diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index 4f189677..9abbddc3 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -28,7 +28,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" @@ -67,7 +67,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" @@ -121,7 +121,7 @@ jobs: CHANGES=$(git log --oneline --no-merges "${{ steps.prev.outputs.sha }}..${{ needs.prep.outputs.sha }}" -- . ':!package-lock.json' | sed 's/^/- /') fi if [ -z "$CHANGES" ]; then - CHANGES="- No new commits since the last beta (or this is the first beta build)." + CHANGES="- No new commits since the last beta." fi cat > BETA_RELEASE_BODY.md << EOF diff --git a/.github/workflows/crowdin-sync.yml b/.github/workflows/crowdin-sync.yml new file mode 100644 index 00000000..8ab185cd --- /dev/null +++ b/.github/workflows/crowdin-sync.yml @@ -0,0 +1,83 @@ +name: Crowdin Sync + +on: + schedule: + - cron: "0 6 * * *" + workflow_dispatch: + inputs: + branch: + description: "Branch to sync translations into" + required: false + type: string + +permissions: + contents: write + +jobs: + crowdin: + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Resolve target branch + id: branch + run: | + BRANCH="${{ inputs.branch }}" + if [ -z "$BRANCH" ]; then + BRANCH="${{ github.event.repository.default_branch }}" + fi + echo "name=$BRANCH" >> "$GITHUB_OUTPUT" + + - name: Checkout branch + uses: actions/checkout@v7 + with: + ref: ${{ steps.branch.outputs.name }} + fetch-depth: 0 + token: ${{ secrets.GHCR_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version-file: ".nvmrc" + + - 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 + 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" + git push origin HEAD:"${{ steps.branch.outputs.name }}" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 212f62cc..6a465bce 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -43,6 +43,7 @@ on: jobs: build: runs-on: blacksmith-8vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v7 diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 324cc50f..3b9d1c82 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -72,7 +72,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" @@ -166,7 +166,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" @@ -380,7 +380,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" @@ -966,7 +966,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index c76952da..54f1486f 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v7 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 2e48e167..123f4dcb 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v7 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf89c5a1..d4b51618 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" @@ -92,7 +92,7 @@ jobs: token: ${{ secrets.GHCR_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" cache: "npm" @@ -144,7 +144,7 @@ jobs: token: ${{ secrets.GHCR_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" @@ -225,7 +225,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" @@ -304,7 +304,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" @@ -420,7 +420,7 @@ jobs: path: termix - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: "termix/.nvmrc" cache: "npm" @@ -513,7 +513,7 @@ jobs: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: ".nvmrc" diff --git a/README.md b/README.md index 6ec99960..b92f6c78 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,20 @@ SSH sessions and tabs stay open across devices/refreshes if enabled in user prof **Languages:** Built-in support ~30 languages (managed by [Crowdin](https://docs.termix.site/translations)). + + + + + +**Session Sharing:** +Share a live terminal, RDP, VNC, or Telnet session with others in real time. Share via a link (joined anonymously, no account needed) or with a specific Termix user, and choose read-only or read-write access. Shares can expire automatically or be revoked at any time, and session sharing can be toggled globally or per-host. + + + + +**Desktop Standalone + 2-Way Sync:** +The Electron desktop app runs fully standalone with its own local backend and database, no server required. Optionally connect it to a remote Termix server for automatic two-way sync of hosts, credentials, snippets, and more, and choose whether SSH connections are started locally or through the remote server. + @@ -293,6 +307,14 @@ networks:
+## Telemetry + +Termix sends a small anonymous usage ping once every 24 hours to help understand how many instances are running and which features are actually used. This only includes a randomly generated instance ID, a count of users and hosts, the app version, and whether certain features (terminal, file manager, tunnels, docker, etc.) were used in the last 24 hours. It never includes usernames, hostnames, IP addresses, credentials, or any other identifying or connection data. + +This is opt-out and enabled by default. You can disable it at any time in Admin Settings under **General**. + +
+ ## Donate Termix is free and open source with no subscriptions or paid plans. If you find it useful, consider donating to help cover server costs, domains, and development time. Donations also help fund the time to research and learn what's needed to build features like SAML, Kubernetes, and Agent support. Track progress and donate below. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e4ab722e..caaa91f2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,70 +1,73 @@ -Revamped RBAC/sharing, session recording & replay, Vault auth for monitors, API key host enrollment, Proxmox guest auto sync, database refactor, plus 30+ bug fixes across terminal, file manager, RDP/VNC, and auth. DO NOT DOWNGRADE FROM THIS VERSION. +Standalone-first Electron desktop app with optional remote sync, shared/multiplayer terminal and remote desktop sessions, improved SSH MFA support, custom key shortcuts, bug fixes across terminal, RDP/VNC, mobile, and auth. -https://youtu.be/c3UD4q2jW_8 +https://youtu.be/g0QjNdV3YYY -- Revamped RBAC/sharing system (new UI, all auth types and host protocols now supported) -- Complete admin control over user information (manage all users hosts, credentials, and snippets) -- Support Vault auth for monitors -- API key host enrollment endpoint -- Allow pinned hosts with name sorting -- Session recording and replay -- Terminal font size shortcuts (ctrl + / -) -- Open File Manager to tab right-click menu -- Proxmox guest auto sync -- Complete database refactor -- 30-day donation reminder and new donation milestones that support research: (donate.termix.site) -- Improve site performance with cache and poll pauses -- Save quick connect sessions as hosts +- Added simple telemetrics to PostHog (user count, total hosts across users, and version metrics). +- Reworked Electron desktop app to run standalone-first with a now optional sync to a remote Termix server. +- Added support for starting connections locally or from the remote server on desktop app. +- Added support for custom key shortcuts. +- Added support for more MFA types (SSH-only). +- Added multiplayer/shared sessions for terminals and remote desktop (share via link or user). +- Added support for logging into SSH hosts that require multi-factor authentication (like Duo or JumpCloud push/TOTP prompts). +- Added the option to convert a Quick Connect session into a saved host after connecting. +- Added an export option for sharing host entries without credentials, so a host list can be shared without leaking passwords or keys. +- Unified the folder picker across hosts, credentials, and snippets so they all use the same searchable, create-in-place selector. +- Allowed pasting into the key recording field from the clipboard. +- Allowed sharing hosts that use authentication type "none". +- Allowed setting authentication type "none" on RDP hosts. +- Added the option to show two or more sidebar panels open at the same time. +- Added global custom themes that can be applied across all hosts instead of per host. +- Added a button to quickly create a Credentials entry from a host's existing authentication details. +- Added persistent split screen, so your layout and assigned tabs are restored after closing and reopening the app. +- Added tag matching to host search, so searching now matches tags as well as hostnames. +- Brought back the ability to collapse snippets. +- Added the ability to assign login credentials to an entire folder of hosts instead of one at a time. +- Added the ability to share terminal, VNC, and RDP sessions with other users, including read-only and read-write modes. +- Added the ability to share entire folders of hosts with other users instead of sharing hosts one by one. +- Added the ability to make folders of hosts available to specific users instead of everyone recreating them. +- Reworked SSH credentials to support both a password and an SSH key on the same credential, with an option to auto fill the password when prompted. +- Added a custom group claim option for OIDC login, useful for identity providers like Zitadel that don't use a plain "groups" claim. -- Syntax highlighting artifacts -- Filter dashboard status hosts -- Persist dashboard service link changes -- Snippet text overflow -- Persist remote desktop credential auth -- Guard language switching failures -- Resolve tunnel source credentials -- Windows file delete command -- Artifact release checkout ref -- Command palette escape in fullscreen -- Alerts and audit log normalization -- macOS VNC protocol negotiation -- Port knocking before SSH connect -- Allow escape to close link confirmation -- Prevent Electron modifier wheel zoom -- Credential auth optional password -- Retry transient terminal DNS lookups -- OIDC redirect forwarded port handling -- Preserve recent open tabs on startup -- Terminal font selection -- Poor font legibility in multiple places -- File manager uploads failing -- Tmux detection for non-POSTIX shells -- OPKSSH js-yaml ESM import -- Android Vietnamese IME input -- Firefox RDP clipboard paste -- Proxmox discovery over HTTPS -- External editor actions in file preview -- Firefox desktop OIDC callback -- Status checks through jump hosts -- Restore sudo password auto fill settings -- Preserve file editor position on save -- Sync cloud preference storage mode -- Render RDP sessions at native pixel density -- Restore database import in embedded desktop mode -- Command autocomplete dropdown poor contrast -- Allow clipboard paste in key recording field -- Fix GitHub/google SSO "not defined" errors +- Fixed the Add Channel dialog failing with "config is required" when adding Webhook or ntfy alert channels. +- Fixed font size and UI scaling being too small even at the largest setting on high resolution displays. +- Fixed the Docker integration not working on hosts using authentication type "none". +- Fixed the latest Russian translation updates from Crowdin not being included in the app. +- Fixed missing Nerd Font symbol support in the Android app. +- Fixed credential changes on RDP hosts not saving properly. +- Fixed credential folders not showing up in the folder dropdown when editing a credential. +- Fixed RDP hosts not using their stored credential and falling back to a direct connection. +- Fixed Cmd + scroll on Mac resizing the terminal instead of scrolling. +- Fixed text repeating itself in the terminal when typing with a wireless keyboard on Android. +- Fixed RDP connections failing when going through a jump host. +- Fixed SSH connections failing when going through a jump host in some setups. +- Fixed OIDC login failing with a database error when the identity provider didn't return a client ID. +- Fixed SSH client-to-server tunnels failing with an authentication error. +- Fixed Host Metrics disk usage only showing the root filesystem and ignoring other mounted disks. +- Fixed Android navigation buttons covering the terminal's top bar keys. +- Fixed Proxmox discovery importing DHCP LXC containers with an IP of 0.0.0.0 instead of their real address. +- Fixed VNC connections to macOS Screen Sharing hanging after the handshake instead of connecting. +- Fixed File Manager delete still failing on Windows hosts running PowerShell 5.1. +- Fixed the terminal dropping characters while typing on iOS. +- Fixed SSH lines like "[username@host]" being wrongly highlighted as a log level and breaking output formatting. +- Fixed RDP touch mode on Android not registering taps as clicks. +- Fixed VNC connections still failing due to a guacd protocol version mismatch. +- Guacamole tab showing "connecting" instead of rendering the desktop. +- Fixed tmux not using Tailscale when starting connections. +- Fixed an invalid websocket frame from causing code 10006 crash triggering restart loop. +- Remove chacha20-poly1305 without native ssh2 binding. +- Corrected SSRF blocklist from false-positive on all IPv4. +- Fixed terminal background image incorrectly displaying. diff --git a/docker/Dockerfile b/docker/Dockerfile index f6ba5f51..f4028795 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -58,7 +58,8 @@ WORKDIR /app ENV DATA_DIR=/app/data \ PORT=8080 \ - NODE_ENV=production + NODE_ENV=production \ + POSTHOG_API_KEY=phc_xM8UznirsFxUkGE68gH4jzeqevf4kh76wGw7Ci7hH2dd RUN apt-get update && apt-get install -y nginx gettext-base openssl ca-certificates gosu wget certbot python3-certbot-dns-cloudflare && \ update-ca-certificates && \ diff --git a/docker/nginx-https.conf b/docker/nginx-https.conf index 9449028a..ce22530d 100644 --- a/docker/nginx-https.conf +++ b/docker/nginx-https.conf @@ -226,6 +226,15 @@ http { proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; } + location ~ ^/sync(/.*)?$ { + 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 $proxy_x_forwarded_proto; + } + location ~ ^/termix-id(/.*)?$ { proxy_pass http://127.0.0.1:30001; proxy_http_version 1.1; @@ -467,6 +476,15 @@ http { proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; } + location ~ ^/session-sharing(/.*)?$ { + 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 $proxy_x_forwarded_proto; + } + location /host/tunnel/ { proxy_pass http://127.0.0.1:30003; proxy_http_version 1.1; diff --git a/docker/nginx.conf b/docker/nginx.conf index 68cff5a6..235218b9 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -215,6 +215,15 @@ http { proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; } + location ~ ^/sync(/.*)?$ { + 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 $proxy_x_forwarded_proto; + } + location ~ ^/termix-id(/.*)?$ { proxy_pass http://127.0.0.1:30001; proxy_http_version 1.1; @@ -456,6 +465,15 @@ http { proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; } + location ~ ^/session-sharing(/.*)?$ { + 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 $proxy_x_forwarded_proto; + } + location /host/tunnel/ { proxy_pass http://127.0.0.1:30003; proxy_http_version 1.1; diff --git a/docs/readme/README-AR.md b/docs/readme/README-AR.md index 9d208394..3aa71869 100644 --- a/docs/readme/README-AR.md +++ b/docs/readme/README-AR.md @@ -189,6 +189,20 @@ Termix هي منصة مفتوحة المصدر ومجانية للأبد وذا **اللغات:** دعم مدمج لحوالي 30 لغة (تُدار بواسطة [Crowdin](https://docs.termix.site/translations)). + + + + + +**مشاركة الجلسة:** +شارك جلسة طرفية أو RDP أو VNC أو Telnet مباشرة مع الآخرين في الوقت الفعلي. شارك عبر رابط (الانضمام بشكل مجهول، بدون الحاجة لحساب) أو مع مستخدم Termix محدد، واختر الوصول للقراءة فقط أو للقراءة والكتابة. يمكن أن تنتهي صلاحية المشاركات تلقائيًا أو يتم إلغاؤها في أي وقت، ويمكن تبديل مشاركة الجلسة عالميًا أو لكل مضيف على حدة. + + + + +**تطبيق سطح مكتب مستقل + مزامنة ثنائية الاتجاه:** +يعمل تطبيق سطح المكتب Electron بشكل مستقل تمامًا مع خلفية وقاعدة بيانات محلية خاصة به، دون الحاجة لخادم. يمكن اختياريًا توصيله بخادم Termix عن بُعد للمزامنة التلقائية ثنائية الاتجاه للمضيفين وبيانات الاعتماد والمقتطفات والمزيد، واختيار ما إذا كانت اتصالات SSH تبدأ محليًا أو عبر الخادم البعيد. + diff --git a/docs/readme/README-CN.md b/docs/readme/README-CN.md index 2290b99b..079026f8 100644 --- a/docs/readme/README-CN.md +++ b/docs/readme/README-CN.md @@ -189,6 +189,20 @@ Termix 是一个开源、永久免费、自托管的一体化服务器管理平 **语言:** 内置支持约 30 种语言(由 [Crowdin](https://docs.termix.site/translations) 管理)。 + + + + + +**会话共享:** +与他人实时共享终端、RDP、VNC 或 Telnet 会话。通过链接分享(匿名加入,无需帐户)或与特定的 Termix 用户分享,并选择只读或读写权限。共享可以自动过期或随时撤销,会话共享可以全局或按主机切换。 + + + + +**桌面独立运行 + 双向同步:** +Electron 桌面应用可完全独立运行,拥有自己的本地后端和数据库,无需服务器。也可以选择连接到远程 Termix 服务器,实现主机、凭据、代码片段等的自动双向同步,并选择 SSH 连接是在本地启动还是通过远程服务器启动。 + diff --git a/docs/readme/README-DE.md b/docs/readme/README-DE.md index 6ca2f9af..be5b9961 100644 --- a/docs/readme/README-DE.md +++ b/docs/readme/README-DE.md @@ -189,6 +189,20 @@ SSH-Sitzungen und Tabs bleiben uber Gerate/Aktualisierungen hinweg offen, wenn i **Sprachen:** Integrierte Unterstutzung fur ca. 30 Sprachen (verwaltet uber [Crowdin](https://docs.termix.site/translations)). + + + + + +**Sitzungsfreigabe:** +Teilen Sie eine Live-Terminal-, RDP-, VNC- oder Telnet-Sitzung in Echtzeit mit anderen. Freigabe uber einen Link (anonymer Beitritt, kein Konto erforderlich) oder mit einem bestimmten Termix-Benutzer, mit Wahl zwischen Nur-Lese- oder Lese-/Schreibzugriff. Freigaben konnen automatisch ablaufen oder jederzeit widerrufen werden, und die Sitzungsfreigabe kann global oder pro Host umgeschaltet werden. + + + + +**Eigenstandiger Desktop + bidirektionale Synchronisierung:** +Die Electron-Desktop-App lauft vollstandig eigenstandig mit eigenem lokalem Backend und eigener Datenbank, kein Server erforderlich. Optional mit einem entfernten Termix-Server verbinden fur automatische bidirektionale Synchronisierung von Hosts, Zugangsdaten, Snippets und mehr, mit der Wahl, ob SSH-Verbindungen lokal oder uber den entfernten Server gestartet werden. + diff --git a/docs/readme/README-ES.md b/docs/readme/README-ES.md index b2689408..9006dfcd 100644 --- a/docs/readme/README-ES.md +++ b/docs/readme/README-ES.md @@ -189,6 +189,20 @@ Las sesiones SSH y pestanas permanecen abiertas entre dispositivos/actualizacion **Idiomas:** Soporte integrado para aproximadamente 30 idiomas (gestionado por [Crowdin](https://docs.termix.site/translations)). + + + + + +**Uso compartido de sesion:** +Comparte una sesion en vivo de terminal, RDP, VNC o Telnet con otras personas en tiempo real. Comparte mediante un enlace (se une de forma anonima, sin necesidad de cuenta) o con un usuario especifico de Termix, y elige acceso de solo lectura o de lectura y escritura. Las comparticiones pueden expirar automaticamente o revocarse en cualquier momento, y el uso compartido de sesiones se puede activar globalmente o por host. + + + + +**Aplicacion de escritorio independiente + sincronizacion bidireccional:** +La aplicacion de escritorio Electron funciona de forma totalmente independiente con su propio backend y base de datos locales, sin necesidad de servidor. Opcionalmente, conectala a un servidor Termix remoto para sincronizacion bidireccional automatica de hosts, credenciales, fragmentos y mas, y elige si las conexiones SSH se inician localmente o a traves del servidor remoto. + diff --git a/docs/readme/README-FR.md b/docs/readme/README-FR.md index 0813165a..48d81313 100644 --- a/docs/readme/README-FR.md +++ b/docs/readme/README-FR.md @@ -189,6 +189,20 @@ Les sessions SSH et les onglets restent ouverts sur tous les appareils/actualisa **Langues:** Support integre d'environ 30 langues (gere par [Crowdin](https://docs.termix.site/translations)). + + + + + +**Partage de session:** +Partagez une session de terminal, RDP, VNC ou Telnet en direct avec d'autres personnes en temps reel. Partagez via un lien (rejoint anonymement, sans compte necessaire) ou avec un utilisateur Termix specifique, et choisissez un acces en lecture seule ou en lecture-ecriture. Les partages peuvent expirer automatiquement ou etre revoques a tout moment, et le partage de session peut etre active globalement ou par hote. + + + + +**Application de bureau autonome + synchronisation bidirectionnelle:** +L'application de bureau Electron fonctionne de maniere totalement autonome avec son propre backend et sa propre base de donnees locale, sans serveur requis. Connectez-la eventuellement a un serveur Termix distant pour une synchronisation bidirectionnelle automatique des hotes, des identifiants, des extraits de code et plus encore, et choisissez si les connexions SSH sont demarrees localement ou via le serveur distant. + diff --git a/docs/readme/README-HI.md b/docs/readme/README-HI.md index 17a1e63f..ecf024d2 100644 --- a/docs/readme/README-HI.md +++ b/docs/readme/README-HI.md @@ -189,6 +189,20 @@ Termix एक ओपन-सोर्स, हमेशा के लिए मु **भाषाएँ:** लगभग 30 भाषाओं का बिल्ट-इन सपोर्ट ([Crowdin](https://docs.termix.site/translations) द्वारा प्रबंधित)। + + + + + +**सेशन शेयरिंग:** +लाइव टर्मिनल, RDP, VNC, या Telnet सेशन को दूसरों के साथ रीयल टाइम में शेयर करें। लिंक के जरिए शेयर करें (गुमनाम रूप से जुड़ें, अकाउंट की जरूरत नहीं) या किसी खास Termix यूजर के साथ, और रीड-ओनली या रीड-राइट एक्सेस चुनें। शेयर अपने आप एक्सपायर हो सकते हैं या कभी भी रद्द किए जा सकते हैं, और सेशन शेयरिंग को ग्लोबली या प्रति होस्ट टॉगल किया जा सकता है। + + + + +**डेस्कटॉप स्टैंडअलोन + 2-वे सिंक:** +Electron डेस्कटॉप ऐप अपने खुद के लोकल बैकएंड और डेटाबेस के साथ पूरी तरह से स्टैंडअलोन चलता है, किसी सर्वर की जरूरत नहीं। चाहें तो इसे किसी रिमोट Termix सर्वर से कनेक्ट करें ताकि होस्ट्स, क्रेडेंशियल्स, स्निपेट्स और अन्य चीज़ों का ऑटोमैटिक 2-वे सिंक हो सके, और चुनें कि SSH कनेक्शन लोकली शुरू हों या रिमोट सर्वर के जरिए। + diff --git a/docs/readme/README-IT.md b/docs/readme/README-IT.md index 91d6884b..97e90f2d 100644 --- a/docs/readme/README-IT.md +++ b/docs/readme/README-IT.md @@ -189,6 +189,20 @@ Le sessioni SSH e le schede rimangono aperte tra dispositivi/aggiornamenti se ab **Lingue:** Supporto integrato per circa 30 lingue (gestito da [Crowdin](https://docs.termix.site/translations)). + + + + + +**Condivisione Sessione:** +Condividi una sessione di terminale, RDP, VNC o Telnet dal vivo con altri in tempo reale. Condividi tramite un link (accesso anonimo, nessun account necessario) o con un utente Termix specifico, e scegli l'accesso in sola lettura o lettura/scrittura. Le condivisioni possono scadere automaticamente o essere revocate in qualsiasi momento, e la condivisione della sessione puo essere attivata globalmente o per singolo host. + + + + +**App Desktop Standalone + Sincronizzazione Bidirezionale:** +L'app desktop Electron funziona in modo completamente autonomo con il proprio backend e database locali, senza bisogno di un server. Facoltativamente, collegala a un server Termix remoto per la sincronizzazione bidirezionale automatica di host, credenziali, snippet e altro, scegliendo se le connessioni SSH vengono avviate localmente o tramite il server remoto. + diff --git a/docs/readme/README-JA.md b/docs/readme/README-JA.md index dbde0588..45c153ed 100644 --- a/docs/readme/README-JA.md +++ b/docs/readme/README-JA.md @@ -189,6 +189,20 @@ Tailnetのデバイスをリストしてホストとしてすばやく追加し **多言語対応:** 約30言語の組み込みサポート([Crowdin](https://docs.termix.site/translations)で管理されています)。 + + + + + +**セッション共有:** +ターミナル、RDP、VNC、Telnetのライブセッションを他のユーザーとリアルタイムで共有できます。リンクで共有(匿名参加、アカウント不要)するか、特定のTermixユーザーと共有し、読み取り専用または読み取り/書き込みアクセスを選択できます。共有は自動的に期限切れになるか、いつでも取り消すことができ、セッション共有はグローバルまたはホストごとに切り替えられます。 + + + + +**デスクトップスタンドアロン + 双方向同期:** +Electronデスクトップアプリは、独自のローカルバックエンドとデータベースを使用して完全にスタンドアロンで動作し、サーバーは不要です。オプションでリモートのTermixサーバーに接続し、ホスト、認証情報、スニペットなどの自動双方向同期を行い、SSH接続をローカルで開始するかリモートサーバー経由で開始するかを選択できます。 + diff --git a/docs/readme/README-KO.md b/docs/readme/README-KO.md index 8382f83b..834dc87b 100644 --- a/docs/readme/README-KO.md +++ b/docs/readme/README-KO.md @@ -189,6 +189,20 @@ Tailscale 네트워크의 기기를 나열하여 호스트로 빠르게 추가 **다국어 지원:** 약 30개 언어 내장 지원([Crowdin](https://docs.termix.site/translations)으로 관리). + + + + + +**세션 공유:** +터미널, RDP, VNC, Telnet 세션을 다른 사람과 실시간으로 공유하세요. 링크를 통해 공유(계정 없이 익명으로 참여)하거나 특정 Termix 사용자와 공유할 수 있으며, 읽기 전용 또는 읽기/쓰기 권한을 선택할 수 있습니다. 공유는 자동으로 만료되거나 언제든지 취소될 수 있으며, 세션 공유는 전역 또는 호스트별로 전환할 수 있습니다. + + + + +**데스크톱 독립 실행 + 양방향 동기화:** +Electron 데스크톱 앱은 자체 로컬 백엔드와 데이터베이스를 사용하여 서버 없이 완전히 독립적으로 실행됩니다. 선택적으로 원격 Termix 서버에 연결하여 호스트, 자격 증명, 스니펫 등을 자동으로 양방향 동기화하고, SSH 연결을 로컬에서 시작할지 원격 서버를 통해 시작할지 선택할 수 있습니다. + diff --git a/docs/readme/README-PT.md b/docs/readme/README-PT.md index c46606cc..052dd2e6 100644 --- a/docs/readme/README-PT.md +++ b/docs/readme/README-PT.md @@ -189,6 +189,20 @@ Sessoes SSH e abas permanecem abertas entre dispositivos/atualizacoes se habilit **Idiomas:** Suporte integrado para aproximadamente 30 idiomas (gerenciado pelo [Crowdin](https://docs.termix.site/translations)). + + + + + +**Compartilhamento de sessao:** +Compartilhe uma sessao de terminal, RDP, VNC ou Telnet ao vivo com outras pessoas em tempo real. Compartilhe por meio de um link (entrada anonima, sem necessidade de conta) ou com um usuario especifico do Termix, e escolha acesso somente leitura ou leitura/gravacao. Os compartilhamentos podem expirar automaticamente ou ser revogados a qualquer momento, e o compartilhamento de sessao pode ser ativado globalmente ou por host. + + + + +**Aplicativo de desktop autonomo + sincronizacao bidirecional:** +O aplicativo de desktop Electron funciona de forma totalmente autonoma com seu proprio backend e banco de dados locais, sem necessidade de servidor. Opcionalmente, conecte-o a um servidor Termix remoto para sincronizacao bidirecional automatica de hosts, credenciais, snippets e muito mais, e escolha se as conexoes SSH sao iniciadas localmente ou por meio do servidor remoto. + diff --git a/docs/readme/README-RU.md b/docs/readme/README-RU.md index 7db61e9f..f6774717 100644 --- a/docs/readme/README-RU.md +++ b/docs/readme/README-RU.md @@ -189,6 +189,20 @@ SSH-сессии и вкладки остаются открытыми на вс **Языки:** Встроенная поддержка около 30 языков (управляется через [Crowdin](https://docs.termix.site/translations)). + + + + + +**Общий доступ к сеансу:** +Делитесь сеансом терминала, RDP, VNC или Telnet с другими в режиме реального времени. Делитесь по ссылке (анонимное присоединение, учетная запись не требуется) или с конкретным пользователем Termix, выбирая доступ только для чтения или для чтения и записи. Общий доступ может автоматически истекать или быть отозван в любое время, а общий доступ к сеансам можно включать глобально или для отдельного хоста. + + + + +**Автономное настольное приложение + двусторонняя синхронизация:** +Настольное приложение на Electron полностью автономно, с собственным локальным бэкендом и базой данных, сервер не требуется. При желании подключите его к удаленному серверу Termix для автоматической двусторонней синхронизации хостов, учетных данных, сниппетов и прочего, и выберите, запускаются ли SSH-соединения локально или через удаленный сервер. + diff --git a/docs/readme/README-TR.md b/docs/readme/README-TR.md index 1ee719b0..b75269f3 100644 --- a/docs/readme/README-TR.md +++ b/docs/readme/README-TR.md @@ -189,6 +189,20 @@ Kullanici profilinde etkinlestirilmisse SSH oturumlari ve sekmeler cihazlar/yeni **Diller:** Yaklasik 30 dil icin yerlesik destek ([Crowdin](https://docs.termix.site/translations) tarafindan yonetilir). + + + + + +**Oturum Paylasimi:** +Canli bir terminal, RDP, VNC veya Telnet oturumunu baskalariyla gercek zamanli olarak paylasin. Bir baglanti uzerinden (anonim olarak katilir, hesap gerekmez) veya belirli bir Termix kullanicisiyla paylasin ve salt okunur veya okuma/yazma erisimi secin. Paylasimlar otomatik olarak sona erebilir veya istediginiz zaman iptal edilebilir; oturum paylasimi genel olarak veya sunucu bazinda acilip kapatilabilir. + + + + +**Bagimsiz Masaustu + Cift Yonlu Senkronizasyon:** +Electron masaustu uygulamasi, kendi yerel arka ucu ve veritabaniyla tamamen bagimsiz calisir, sunucu gerekmez. Istege bagli olarak sunucular, kimlik bilgileri, kod parcaciklari ve daha fazlasinin otomatik cift yonlu senkronizasyonu icin uzak bir Termix sunucusuna baglayin ve SSH baglantilarinin yerel olarak mi yoksa uzak sunucu uzerinden mi baslatilacagini secin. + diff --git a/docs/readme/README-VI.md b/docs/readme/README-VI.md index 106b48c5..d7de3ac1 100644 --- a/docs/readme/README-VI.md +++ b/docs/readme/README-VI.md @@ -189,6 +189,20 @@ Cac phien SSH va tab van mo tren cac thiet bi/lan lam moi neu duoc bat trong ho **Ngon Ngu:** Ho tro tich hop khoang 30 ngon ngu (duoc quan ly boi [Crowdin](https://docs.termix.site/translations)). + + + + + +**Chia Se Phien:** +Chia se mot phien terminal, RDP, VNC, hoac Telnet truc tiep voi nguoi khac theo thoi gian thuc. Chia se qua lien ket (tham gia an danh, khong can tai khoan) hoac voi mot nguoi dung Termix cu the, va chon quyen truy cap chi doc hoac doc/ghi. Cac lien ket chia se co the tu dong het han hoac bi thu hoi bat cu luc nao, va tinh nang chia se phien co the duoc bat/tat toan cuc hoac theo tung host. + + + + +**Ung Dung Desktop Doc Lap + Dong Bo 2 Chieu:** +Ung dung desktop Electron chay hoan toan doc lap voi backend va co so du lieu cuc bo rieng, khong can may chu. Tuy chon ket noi voi may chu Termix tu xa de tu dong dong bo 2 chieu cac host, thong tin dang nhap, doan ma va nhieu hon nua, va chon xem cac ket noi SSH duoc khoi tao cuc bo hay thong qua may chu tu xa. + diff --git a/electron/main.cjs b/electron/main.cjs index 13963207..2e20608e 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -20,6 +20,7 @@ const net = require("net"); const { URL } = require("url"); const { fork, spawn } = require("child_process"); const WebSocket = require("ws"); +const remoteSync = require("./remote-sync.cjs"); // Portable mode: if a `.portable` marker exists next to the executable, // store all data in a `data` folder beside the exe instead of %APPDATA%. @@ -441,7 +442,10 @@ function isInvalidCertificateAllowedForUrl(url) { // fall through } - const config = getServerConfigSync(); + // The only remaining "connected remote server" a self-signed/invalid + // certificate could legitimately apply to is the Remote Sync server + // (also used for C2S tunnel relaying, see getC2SRelayUrl). + const config = remoteSync.getRemoteSyncConfig(); if (!config?.allowInvalidCertificate || !config?.serverUrl) return false; return getOrigin(url) === getOrigin(config.serverUrl); @@ -816,7 +820,62 @@ function getBackendDataDir() { return dataDir; } +function getBackendPidFilePath() { + return path.join(app.getPath("userData"), "backend.pid"); +} + +// If the app was previously killed abnormally (crash, force-quit, Task +// Manager) rather than through the normal quit flow, will-quit never fires +// and stopBackendServer() never runs -- the forked backend child is a +// genuinely separate OS process on Windows/mac/Linux, so it keeps running +// and holding every port the backend binds (30001, 30003-30008, 30010, +// 30012...). Every subsequent launch's own backend then fails outright +// with EADDRINUSE and the app is stuck until something manually kills the +// orphan. Reap any such leftover process, identified by PID file, before +// spawning a new one. +function reapOrphanedBackendProcess() { + const pidFilePath = getBackendPidFilePath(); + let recordedPid; + try { + recordedPid = parseInt(fs.readFileSync(pidFilePath, "utf8").trim(), 10); + } catch { + return; + } + if (!Number.isInteger(recordedPid) || recordedPid <= 0) return; + + try { + // Signal 0 does not kill the process -- it only checks whether a + // process with this PID exists and is signalable, throwing ESRCH if + // not. This avoids killing an unrelated process that happens to have + // reused the same PID since the last run. + process.kill(recordedPid, 0); + } catch { + // No live process at that PID; nothing to reap. + try { + fs.unlinkSync(pidFilePath); + } catch { + // already absent + } + return; + } + + logToFile( + `Found orphaned backend process from a previous session (pid ${recordedPid}), terminating it before starting a new one`, + ); + try { + process.kill(recordedPid, "SIGKILL"); + } catch { + // already gone + } + try { + fs.unlinkSync(pidFilePath); + } catch { + // already absent + } +} + function startBackendServer() { + reapOrphanedBackendProcess(); return new Promise((resolve) => { const { entryPath, backendCwd } = getBackendPaths(); @@ -852,11 +911,17 @@ function startBackendServer() { NODE_ENV: "production", ELECTRON_EMBEDDED: "true", PORT: "30001", + VERSION: app.getVersion(), }, stdio: ["pipe", "pipe", "pipe", "ipc"], }); logToFile("Backend process spawned, pid:", backendProcess.pid); + try { + fs.writeFileSync(getBackendPidFilePath(), String(backendProcess.pid)); + } catch { + // Non-fatal: only means a future crash won't self-heal via reap. + } let resolved = false; const readyTimeout = setTimeout(() => { @@ -888,6 +953,7 @@ function startBackendServer() { backendStartFailed = true; } backendProcess = null; + clearBackendPidFile(); if (!resolved) { resolved = true; clearTimeout(readyTimeout); @@ -907,6 +973,14 @@ function startBackendServer() { }); } +function clearBackendPidFile() { + try { + fs.unlinkSync(getBackendPidFilePath()); + } catch { + // already absent + } +} + function stopBackendServer() { if (!backendProcess) return; @@ -929,6 +1003,7 @@ function stopBackendServer() { backendProcess.on("exit", () => { clearTimeout(forceKillTimeout); backendProcess = null; + clearBackendPidFile(); }); } @@ -1335,7 +1410,6 @@ ipcMain.handle("get-embedded-server-status", () => { return { running: backendProcess !== null && !backendProcess.killed && !backendStartFailed, - embedded: !isDev, dataDir: isDev ? null : getBackendDataDir(), }; }); @@ -1442,6 +1516,80 @@ ipcMain.handle("save-server-config", (event, config) => { } }); +// --- Remote sync (optional desktop <-> self-hosted server sync) --- + +// Surfaces the pre-standalone-rework server-config.json (if a serverUrl was +// ever set in it) so the renderer can prompt upgraded installs to set up +// Remote Sync -- their hosts live on that old server and won't appear +// locally until sync is enabled. A fresh install never had this file, so +// this is naturally false for anyone who never used the old architecture. +ipcMain.handle("get-legacy-server-config", () => { + const config = getServerConfigSync(); + return { serverUrl: config?.serverUrl || null }; +}); + +ipcMain.handle("get-desktop-settings", () => { + return remoteSync.getDesktopSettings(); +}); + +ipcMain.handle("save-desktop-settings", (_event, settings) => { + return remoteSync.saveDesktopSettings(settings); +}); + +ipcMain.handle("get-remote-sync-config", () => { + return remoteSync.getRemoteSyncConfig(); +}); + +ipcMain.handle("save-remote-sync-config", (_event, config) => { + return remoteSync.saveRemoteSyncConfig(config); +}); + +ipcMain.handle("clear-remote-sync-config", async () => { + const result = remoteSync.clearRemoteSyncConfig(); + remoteSync.clearRemoteSyncJwt(); + remoteSync.getRemoteSyncEngine()?.updateStatus({ + connected: false, + syncing: false, + needsReauth: false, + lastError: null, + }); + return result; +}); + +ipcMain.handle("save-remote-sync-jwt", (_event, token) => { + const result = remoteSync.saveRemoteSyncJwt(token); + if (result.success) { + remoteSync.getRemoteSyncEngine()?.updateStatus({ + connected: true, + needsReauth: false, + lastError: null, + }); + remoteSync.getRemoteSyncEngine()?.syncNow(); + } + return result; +}); + +ipcMain.handle("get-remote-sync-jwt", () => { + return remoteSync.getRemoteSyncJwt(); +}); + +ipcMain.handle("clear-remote-sync-jwt", () => { + return remoteSync.clearRemoteSyncJwt(); +}); + +ipcMain.handle("get-remote-sync-status", () => { + return remoteSync.getRemoteSyncEngine()?.status || null; +}); + +ipcMain.handle("remote-sync-now", async () => { + return (await remoteSync.getRemoteSyncEngine()?.syncNow()) || null; +}); + +ipcMain.handle("notify-local-login", (_event, token) => { + remoteSync.getRemoteSyncEngine()?.setLocalJwt(token); + return { success: true }; +}); + function getC2STunnelConfigPath() { return path.join(app.getPath("userData"), "c2s-tunnels.json"); } @@ -1577,36 +1725,33 @@ const C2S_WS_HIGH_WATERMARK = 1024 * 1024; const C2S_WS_LOW_WATERMARK = 256 * 1024; const C2S_STREAM_WRITE_LIMIT = 8 * 1024 * 1024; +// C2S (client-to-server) tunnels relay through a connected, self-hosted +// Termix server -- the same "remote server" concept Remote Sync connects +// to, not the always-local embedded backend. There's no separate C2S +// server-URL setting in the UI; it has always shared whatever remote +// server the rest of the app was pointed at. Before the standalone-first +// rework that was server-config.json; now it's remote-sync-config.json, +// since that's the only remaining notion of "a connected remote server." function getC2SRelayUrl() { - const config = getServerConfigSync(); - const serverUrl = - config?.serverUrl || (!isDev ? "http://127.0.0.1:30003" : null); + const config = remoteSync.getRemoteSyncConfig(); + const serverUrl = config?.serverUrl; if (!serverUrl) { - throw new Error("No Termix server configured"); + throw new Error( + "No remote Termix server connected -- enable Remote Sync first", + ); } const base = serverUrl.replace(/\/$/, ""); - const relayHttpUrl = base.endsWith(":30003") - ? `${base}/ssh/tunnel/c2s/stream` - : `${base}/ssh/tunnel/c2s/stream`; + const relayHttpUrl = `${base}/ssh/tunnel/c2s/stream`; return relayHttpUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:"); } -async function getC2SRelayHeaders(relayUrl) { - if (!mainWindow?.webContents?.session) return {}; - - const cookieUrl = relayUrl - .replace(/^ws:/, "http:") - .replace(/^wss:/, "https:"); - const cookies = await mainWindow.webContents.session.cookies.get({ - url: cookieUrl, - name: "jwt", - }); - const jwt = cookies[0]?.value; +async function getC2SRelayHeaders() { + const jwt = remoteSync.getRemoteSyncJwt(); if (!jwt) return {}; return { - Cookie: `jwt=${encodeURIComponent(jwt)}`, + Authorization: `Bearer ${jwt}`, }; } @@ -1706,7 +1851,7 @@ async function openC2SRelay( ) { const tunnelName = tunnel.name || getC2STunnelName(tunnel); const relayUrl = getC2SRelayUrl(); - const headers = await getC2SRelayHeaders(relayUrl); + const headers = await getC2SRelayHeaders(); logToFile(`[c2s] opening relay for ${tunnelName}`, { relayUrl, targetHost, @@ -1811,7 +1956,7 @@ async function openC2SRelay( async function testC2SRelay(tunnel, targetHost, targetPort) { const relayUrl = getC2SRelayUrl(); - const headers = await getC2SRelayHeaders(relayUrl); + const headers = await getC2SRelayHeaders(); const ws = new WebSocket( relayUrl, getWebSocketOptions(relayUrl, { headers }), @@ -2090,7 +2235,7 @@ async function startC2SRemoteTunnel(tunnel, index = 0) { } const relayUrl = getC2SRelayUrl(); - const headers = await getC2SRelayHeaders(relayUrl); + const headers = await getC2SRelayHeaders(); const ws = new WebSocket( relayUrl, getWebSocketOptions(relayUrl, { headers }), @@ -2772,31 +2917,33 @@ ipcMain.handle("close-external-editor", (_event, editId) => { ipcMain.handle("test-server-connection", async (event, serverUrl) => { try { const normalizedServerUrl = serverUrl.replace(/\/$/, ""); - const healthUrl = `${normalizedServerUrl}/health`; + // This is a best-effort reachability probe, not a hard gate: a reverse + // proxy doing SSO in front of the real server (Pangolin, Authelia, + // Cloudflare Access, etc.) intercepts this unauthenticated request + // before it ever reaches Termix's own /health route, and returns its + // own login page (HTML, or a redirect) instead of {"status":"ok"}. + // That's a legitimate, working setup -- the login iframe shown right + // after this check is what actually proves the server is real, by + // completing an authenticated round-trip. So any response at all here + // (any status code, any body) means "something is there, let the user + // proceed"; only a network-level failure (nothing answered at all) + // blocks continuing. try { const response = await httpFetch(healthUrl, { method: "GET", timeout: 10000, }); - if (response.ok) { - const data = await response.text(); - - if ( - data.includes("") || - data.includes("") - ) { - return { - success: false, - error: - "Server returned HTML instead of JSON. This does not appear to be a Termix server.", - }; - } + const data = await response.text(); + const looksLikeHtml = + data.includes("") || + data.includes(""); + if (response.ok && !looksLikeHtml) { try { const healthData = JSON.parse(data); if ( @@ -2816,64 +2963,27 @@ ipcMain.handle("test-server-connection", async (event, serverUrl) => { console.log("Health endpoint did not return valid JSON"); } } + + // Reachable, but not a recognized Termix health response -- likely a + // proxy/SSO login page in front of the real server. Let the user + // proceed; the login step next will fail clearly if this really + // isn't a Termix server. + return { + success: true, + status: response.status, + testedUrl: healthUrl, + warning: looksLikeHtml + ? "Could not confirm this is a Termix server (the response looked like an HTML page, which can happen behind a login-protected reverse proxy). You can continue, and the next step will fail clearly if this isn't actually a Termix server." + : "Server responded, but not with the expected health check format. Continuing anyway.", + }; } catch (urlError) { console.error("Health check failed:", urlError); + return { + success: false, + error: + "Server is not responding. Please ensure the server is running and accessible.", + }; } - - try { - const versionUrl = `${normalizedServerUrl}/version`; - const response = await httpFetch(versionUrl, { - method: "GET", - timeout: 10000, - }); - - if (response.ok) { - const data = await response.text(); - - if ( - data.includes("") || - data.includes("") - ) { - return { - success: false, - error: - "Server returned HTML instead of JSON. This does not appear to be a Termix server.", - }; - } - - try { - const versionData = JSON.parse(data); - if ( - versionData && - (versionData.status === "up_to_date" || - versionData.status === "requires_update" || - (versionData.localVersion && - versionData.version && - versionData.latest_release)) - ) { - return { - success: true, - status: response.status, - testedUrl: versionUrl, - warning: - "Health endpoint not available, but server appears to be running", - }; - } - } catch (parseError) { - console.log("Version endpoint did not return valid JSON"); - } - } - } catch (versionError) { - console.error("Version check failed:", versionError); - } - - return { - success: false, - error: - "Server is not responding or does not appear to be a valid Termix server. Please ensure the server is running and accessible.", - }; } catch (error) { return { success: false, error: error.message }; } @@ -2967,6 +3077,7 @@ app.whenReady().then(async () => { createTray(); createWindow(); + remoteSync.initRemoteSync(() => mainWindow); logToFile("=== Startup complete ==="); }); diff --git a/electron/preload.js b/electron/preload.js index 6b9cf1c0..43d84eff 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -31,6 +31,13 @@ contextBridge.exposeInMainWorld("electronAPI", { startC2SAutoStartTunnels: () => ipcRenderer.invoke("start-c2s-autostart-tunnels"), + onRemoteSyncStatusChanged: (callback) => { + const listener = (_event, status) => callback(status); + ipcRenderer.on("remote-sync-status-changed", listener); + return () => + ipcRenderer.removeListener("remote-sync-status-changed", listener); + }, + clearSessionCookies: () => ipcRenderer.invoke("clear-session-cookies"), getSessionCookie: (name, targetUrl) => ipcRenderer.invoke("get-session-cookie", name, targetUrl), diff --git a/electron/remote-sync.cjs b/electron/remote-sync.cjs new file mode 100644 index 00000000..b34dd037 --- /dev/null +++ b/electron/remote-sync.cjs @@ -0,0 +1,516 @@ +// Remote sync engine for the desktop app's optional connection to a +// self-hosted Termix server. Runs entirely in the Electron main process: +// - Holds the remote JWT (safeStorage-encrypted on disk, never exposed to +// the renderer's localStorage) and the local embedded backend's JWT +// (cached in memory only, handed over by the renderer at local-login +// time via notify-local-login). +// - On a timer, pulls + pushes each synced entity type between the +// embedded backend (always localhost:30001) and the configured remote +// server, reconciling by syncId with last-write-wins on updatedAt, and +// propagating tombstones (deletions) in both directions. +// - Pushes connection/sync status to the renderer via IPC so the Settings +// UI and a global banner can reflect it without polling. + +const { app, safeStorage } = require("electron"); +const fs = require("fs"); +const path = require("path"); + +const SYNCED_ENTITY_TYPES = [ + "hosts", + "sshCredentials", + "sshFolders", + "snippets", + "snippetFolders", + "vaultProfiles", + "dashboardServiceLinks", + "homepageItems", +]; + +const SYNC_INTERVAL_MS = 90 * 1000; +const EMBEDDED_BASE_URL = "http://127.0.0.1:30001"; + +function dataPath(filename) { + return path.join(app.getPath("userData"), filename); +} + +function readJson(filePath, fallback) { + try { + if (!fs.existsSync(filePath)) return fallback; + return JSON.parse(fs.readFileSync(filePath, "utf8")); + } catch { + return fallback; + } +} + +function writeJson(filePath, value) { + const userDataPath = app.getPath("userData"); + if (!fs.existsSync(userDataPath)) { + fs.mkdirSync(userDataPath, { recursive: true }); + } + fs.writeFileSync(filePath, JSON.stringify(value, null, 2)); +} + +function getDesktopSettingsPath() { + return dataPath("desktop-settings.json"); +} + +function getRemoteSyncConfigPath() { + return dataPath("remote-sync-config.json"); +} + +function getRemoteSyncCredentialPath() { + return dataPath("remote-sync-credential.json"); +} + +function getRemoteSyncStatePath() { + return dataPath("remote-sync-state.json"); +} + +function getDesktopSettings() { + return readJson(getDesktopSettingsPath(), { + defaultConnectionOrigin: "local", + migrationNoticeAcknowledged: false, + }); +} + +function saveDesktopSettings(settings) { + writeJson(getDesktopSettingsPath(), settings); + return { success: true }; +} + +function getRemoteSyncConfig() { + return readJson(getRemoteSyncConfigPath(), null); +} + +function saveRemoteSyncConfig(config) { + writeJson(getRemoteSyncConfigPath(), config); + return { success: true }; +} + +function clearRemoteSyncConfig() { + try { + fs.unlinkSync(getRemoteSyncConfigPath()); + } catch { + // already absent + } + return { success: true }; +} + +function getSafeStorageAvailable() { + try { + return safeStorage.isEncryptionAvailable(); + } catch { + return false; + } +} + +function saveRemoteSyncJwt(token) { + if (!getSafeStorageAvailable()) { + return { success: false, error: "Encryption unavailable on this system" }; + } + writeJson(getRemoteSyncCredentialPath(), { + encrypted: true, + value: safeStorage.encryptString(token).toString("base64"), + obtainedAt: new Date().toISOString(), + }); + return { success: true }; +} + +function getRemoteSyncJwt() { + const record = readJson(getRemoteSyncCredentialPath(), null); + if (!record?.encrypted || !getSafeStorageAvailable()) return null; + try { + return safeStorage.decryptString(Buffer.from(record.value, "base64")); + } catch { + return null; + } +} + +function clearRemoteSyncJwt() { + try { + fs.unlinkSync(getRemoteSyncCredentialPath()); + } catch { + // already absent + } + return { success: true }; +} + +function decodeJwtExpiry(token) { + try { + const payloadB64 = token.split(".")[1]; + const payload = JSON.parse( + Buffer.from(payloadB64, "base64").toString("utf8"), + ); + return typeof payload.exp === "number" ? payload.exp * 1000 : null; + } catch { + return null; + } +} + +function isJwtExpiredOrExpiringSoon(token, marginMs = 60 * 1000) { + const expiresAt = decodeJwtExpiry(token); + if (expiresAt === null) return false; + return Date.now() + marginMs >= expiresAt; +} + +class RemoteSyncEngine { + constructor(getMainWindow) { + this.getMainWindow = getMainWindow; + this.localJwt = null; + this.timer = null; + this.syncing = false; + this.status = { + connected: false, + syncing: false, + lastSyncedAt: null, + lastError: null, + needsReauth: false, + }; + } + + setLocalJwt(token) { + this.localJwt = token || null; + } + + emitStatus() { + const win = this.getMainWindow?.(); + if (!win || win.isDestroyed()) return; + win.webContents.send("remote-sync-status-changed", this.status); + } + + updateStatus(patch) { + this.status = { ...this.status, ...patch }; + this.emitStatus(); + } + + start() { + const config = getRemoteSyncConfig(); + this.status.connected = !!config?.serverUrl; + if (this.timer) clearInterval(this.timer); + this.timer = setInterval(() => this.syncNow(), SYNC_INTERVAL_MS); + if (config?.serverUrl) { + // Fire an initial sync shortly after startup rather than waiting a + // full interval, but don't block app boot on it. + setTimeout(() => this.syncNow(), 5000); + } + } + + stop() { + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + } + + async syncNow() { + if (this.syncing) return this.status; + const config = getRemoteSyncConfig(); + if (!config?.serverUrl) { + this.updateStatus({ connected: false, syncing: false }); + return this.status; + } + + const remoteJwt = getRemoteSyncJwt(); + if (!remoteJwt) { + this.updateStatus({ + connected: true, + syncing: false, + needsReauth: true, + lastError: "Not signed in to remote server", + }); + return this.status; + } + if (isJwtExpiredOrExpiringSoon(remoteJwt)) { + this.updateStatus({ + connected: true, + syncing: false, + needsReauth: true, + lastError: "Remote session expired", + }); + return this.status; + } + if (!this.localJwt) { + // Local login hasn't handed us a token yet -- this is expected for the + // first tick or two right after a cold boot (renderer hasn't finished + // its own session check yet), but if it never arrives (e.g. a gap in + // whichever code path establishes the local session), sync would + // otherwise silently no-op forever with no visible error. Surface it + // as a normal, non-alarming "not synced yet" status rather than + // leaving lastSyncedAt/lastError untouched. + this.updateStatus({ + connected: true, + syncing: false, + lastError: "Waiting for local session", + }); + return this.status; + } + + this.syncing = true; + this.updateStatus({ connected: true, syncing: true, lastError: null }); + + try { + const state = readJson(getRemoteSyncStatePath(), { entities: {} }); + let sawAuthFailure = false; + + for (const entityType of SYNCED_ENTITY_TYPES) { + const entityState = state.entities[entityType] || { + lastPulledAt: null, + lastPushedAt: null, + }; + + const result = await this.syncEntity({ + entityType, + remoteBaseUrl: config.serverUrl.replace(/\/$/, ""), + remoteJwt, + since: entityState.lastPulledAt, + }); + + if (result.authFailure) { + sawAuthFailure = true; + break; + } + + state.entities[entityType] = { + lastPulledAt: result.syncedAt, + lastPushedAt: result.syncedAt, + }; + } + + if (sawAuthFailure) { + this.updateStatus({ + syncing: false, + needsReauth: true, + lastError: "Remote server rejected the session", + }); + return this.status; + } + + writeJson(getRemoteSyncStatePath(), state); + writeJson(getRemoteSyncConfigPath(), { + ...config, + lastSyncedAt: new Date().toISOString(), + lastSyncStatus: "ok", + lastSyncError: null, + }); + + this.updateStatus({ + connected: true, + syncing: false, + needsReauth: false, + lastSyncedAt: new Date().toISOString(), + lastError: null, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + writeJson(getRemoteSyncConfigPath(), { + ...config, + lastSyncStatus: "error", + lastSyncError: message, + }); + this.updateStatus({ syncing: false, lastError: message }); + } finally { + this.syncing = false; + } + + return this.status; + } + + async fetchJson(url, token, options = {}) { + const res = await fetch(url, { + ...options, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + ...(options.headers || {}), + }, + }); + if (res.status === 401 || res.status === 403) { + const err = new Error(`Auth failed (${res.status})`); + err.authFailure = true; + throw err; + } + if (!res.ok) { + throw new Error(`Request failed (${res.status}): ${url}`); + } + + const text = await res.text(); + // A reverse-proxy SSO in front of the remote server (Pangolin, Authelia, + // etc.) can intercept even an authenticated, Bearer-token'd request and + // serve its own login page instead of forwarding to Termix -- that comes + // back as a normal 200 OK, so the status checks above don't catch it. + // This is NOT the same as needsReauth/a bad Termix JWT: sync runs as a + // plain server-to-server fetch() in this main process, with no browser + // cookie jar at all, so re-authenticating through the login iframe (which + // only affects the renderer's browser session) can never fix this -- + // reconnecting would tell the user to do something that doesn't help. + // The proxy has to allow this traffic through some other way (an API + // bypass rule, a separate hostname/port that isn't proxy-gated, etc.), + // so this gets its own distinct, honest error rather than piggybacking + // on needsReauth or a raw JSON.parse crash. + const looksLikeHtml = + text.includes("") || + text.includes(""); + if (looksLikeHtml) { + const err = new Error( + "The reverse proxy in front of this server is blocking sync traffic with its own login page. Reconnecting won't fix this -- the proxy needs to let Termix's API requests through (e.g. an SSO bypass rule for the sync API, or a non-proxied hostname/port for it).", + ); + err.proxyBlocked = true; + throw err; + } + + try { + return JSON.parse(text); + } catch { + throw new Error(`Server returned invalid JSON: ${url}`); + } + } + + async pullSide(baseUrl, token, entityType, since) { + const url = `${baseUrl}/sync/${entityType}${since ? `?since=${encodeURIComponent(since)}` : ""}`; + const data = await this.fetchJson(url, token); + return data.rows || []; + } + + async pullTombstones(baseUrl, token, entityType, since) { + const url = `${baseUrl}/sync/${entityType}/tombstones${since ? `?since=${encodeURIComponent(since)}` : ""}`; + const data = await this.fetchJson(url, token); + return data.tombstones || []; + } + + async pushRow(baseUrl, token, entityType, row) { + await this.fetchJson(`${baseUrl}/sync/${entityType}`, token, { + method: "POST", + body: JSON.stringify({ row }), + }); + } + + async pushTombstone(baseUrl, token, entityType, syncId) { + await this.fetchJson(`${baseUrl}/sync/tombstones`, token, { + method: "POST", + body: JSON.stringify({ entityType, syncId }), + }); + } + + async syncEntity({ entityType, remoteBaseUrl, remoteJwt, since }) { + const syncedAt = new Date().toISOString(); + try { + const [localRows, remoteRows, localTombstones, remoteTombstones] = + await Promise.all([ + this.pullSide(EMBEDDED_BASE_URL, this.localJwt, entityType, since), + this.pullSide(remoteBaseUrl, remoteJwt, entityType, since), + this.pullTombstones( + EMBEDDED_BASE_URL, + this.localJwt, + entityType, + since, + ), + this.pullTombstones(remoteBaseUrl, remoteJwt, entityType, since), + ]); + + const tombstonedSyncIds = new Set([ + ...localTombstones.map((t) => t.syncId), + ...remoteTombstones.map((t) => t.syncId), + ]); + + const localBySyncId = new Map( + localRows.filter((r) => r.syncId).map((r) => [r.syncId, r]), + ); + const remoteBySyncId = new Map( + remoteRows.filter((r) => r.syncId).map((r) => [r.syncId, r]), + ); + const allSyncIds = new Set([ + ...localBySyncId.keys(), + ...remoteBySyncId.keys(), + ]); + + for (const syncId of allSyncIds) { + if (tombstonedSyncIds.has(syncId)) continue; + + const localRow = localBySyncId.get(syncId); + const remoteRow = remoteBySyncId.get(syncId); + + if (localRow && !remoteRow) { + await this.pushRow(remoteBaseUrl, remoteJwt, entityType, localRow); + } else if (remoteRow && !localRow) { + await this.pushRow( + EMBEDDED_BASE_URL, + this.localJwt, + entityType, + remoteRow, + ); + } else if (localRow && remoteRow) { + const localUpdatedAt = new Date(localRow.updatedAt || 0).getTime(); + const remoteUpdatedAt = new Date(remoteRow.updatedAt || 0).getTime(); + if (localUpdatedAt > remoteUpdatedAt) { + await this.pushRow(remoteBaseUrl, remoteJwt, entityType, localRow); + } else if (remoteUpdatedAt > localUpdatedAt) { + await this.pushRow( + EMBEDDED_BASE_URL, + this.localJwt, + entityType, + remoteRow, + ); + } + } + } + + // Apply tombstones to whichever side hasn't already deleted the row. + for (const tombstone of localTombstones) { + if (remoteBySyncId.has(tombstone.syncId)) { + await this.pushTombstone( + remoteBaseUrl, + remoteJwt, + entityType, + tombstone.syncId, + ); + } + } + for (const tombstone of remoteTombstones) { + if (localBySyncId.has(tombstone.syncId)) { + await this.pushTombstone( + EMBEDDED_BASE_URL, + this.localJwt, + entityType, + tombstone.syncId, + ); + } + } + + return { syncedAt }; + } catch (error) { + if (error?.authFailure) { + return { syncedAt, authFailure: true }; + } + throw error; + } + } +} + +let engine = null; + +function initRemoteSync(getMainWindow) { + engine = new RemoteSyncEngine(getMainWindow); + engine.start(); + return engine; +} + +function getRemoteSyncEngine() { + return engine; +} + +module.exports = { + initRemoteSync, + getRemoteSyncEngine, + getDesktopSettings, + saveDesktopSettings, + getRemoteSyncConfig, + saveRemoteSyncConfig, + clearRemoteSyncConfig, + saveRemoteSyncJwt, + getRemoteSyncJwt, + clearRemoteSyncJwt, + isJwtExpiredOrExpiringSoon, + decodeJwtExpiry, +}; diff --git a/index.html b/index.html index da71ea34..79c6e4f5 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,10 @@ - + diff --git a/package-lock.json b/package-lock.json index 4e9b87b5..d9b4c673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "termix", - "version": "2.5.1", + "version": "2.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "termix", - "version": "2.5.1", + "version": "2.6.0", "hasInstallScript": true, "dependencies": { "@simplewebauthn/browser": "^13.3.0", @@ -31,22 +31,22 @@ "ldapjs": "^3.0.7", "motion": "^12.42.2", "multer": "^2.2.0", - "nanoid": "^5.1.16", + "nanoid": "^6.0.0", "qrcode": "^1.5.4", "serialport": "^13.0.0", "socks": "^2.8.7", "speakeasy": "^2.0.0", "ssh2": "^1.17.0", "undici": "^8.7.0", - "ws": "^8.20.0" + "ws": "^8.21.1" }, "devDependencies": { - "@biomejs/biome": "2.5.2", + "@biomejs/biome": "2.5.4", "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.10.4", "@codemirror/search": "^6.7.1", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.43.5", + "@codemirror/view": "^6.43.6", "@commitlint/cli": "^21.0.2", "@commitlint/config-conventional": "^21.0.2", "@deadendjs/swagger-jsdoc": "^8.1.2", @@ -58,23 +58,23 @@ "@fontsource/jetbrains-mono": "^5.2.8", "@fontsource/source-code-pro": "^5.2.7", "@monaco-editor/react": "^4.7.0", - "@radix-ui/react-accordion": "^1.2.15", - "@radix-ui/react-alert-dialog": "^1.1.18", - "@radix-ui/react-checkbox": "^1.3.6", - "@radix-ui/react-dialog": "^1.1.18", - "@radix-ui/react-dropdown-menu": "^2.1.19", - "@radix-ui/react-label": "^2.1.11", - "@radix-ui/react-popover": "^1.1.18", - "@radix-ui/react-progress": "^1.1.11", - "@radix-ui/react-scroll-area": "^1.2.13", - "@radix-ui/react-select": "^2.3.2", - "@radix-ui/react-separator": "^1.1.11", - "@radix-ui/react-slider": "^1.4.2", + "@radix-ui/react-accordion": "^1.2.17", + "@radix-ui/react-alert-dialog": "^1.1.20", + "@radix-ui/react-checkbox": "^1.3.8", + "@radix-ui/react-dialog": "^1.1.20", + "@radix-ui/react-dropdown-menu": "^2.1.21", + "@radix-ui/react-label": "^2.1.12", + "@radix-ui/react-popover": "^1.1.20", + "@radix-ui/react-progress": "^1.1.13", + "@radix-ui/react-scroll-area": "^1.2.15", + "@radix-ui/react-select": "^2.3.4", + "@radix-ui/react-separator": "^1.1.12", + "@radix-ui/react-slider": "^1.4.4", "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-switch": "^1.3.2", - "@radix-ui/react-tabs": "^1.1.16", - "@radix-ui/react-tooltip": "^1.2.11", - "@tailwindcss/vite": "^4.3.2", + "@radix-ui/react-switch": "^1.3.4", + "@radix-ui/react-tabs": "^1.1.18", + "@radix-ui/react-tooltip": "^1.2.13", + "@tailwindcss/vite": "^4.3.3", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -94,12 +94,12 @@ "@types/speakeasy": "^2.0.10", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", - "@uiw/codemirror-extensions-langs": "^4.25.9", - "@uiw/codemirror-theme-github": "^4.25.9", - "@uiw/react-codemirror": "^4.25.9", + "@uiw/codemirror-extensions-langs": "^4.25.11", + "@uiw/codemirror-theme-github": "^4.25.11", + "@uiw/react-codemirror": "^4.25.11", "@vitejs/plugin-react": "^6.0.3", - "@vitest/coverage-v8": "^4.1.9", - "@vitest/ui": "^4.1.9", + "@vitest/coverage-v8": "^4.1.10", + "@vitest/ui": "^4.1.10", "@xterm/addon-clipboard": "^0.2.0", "@xterm/addon-fit": "^0.11.0", "@xterm/addon-unicode11": "^0.9.0", @@ -119,19 +119,19 @@ "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "husky": "^9.1.7", - "i18next": "^26.3.4", + "i18next": "^26.3.6", "i18next-browser-languagedetector": "^8.2.1", "jsdom": "^29.1.1", "lint-staged": "^17.0.8", "lucide-react": "^1.20.0", "prettier": "3.8.4", - "radix-ui": "^1.6.1", + "radix-ui": "^1.6.3", "react": "^19.2.7", "react-cytoscapejs": "^2.0.0", "react-dom": "^19.2.7", "react-h5-audio-player": "^3.10.2", "react-hook-form": "^7.79.0", - "react-i18next": "^17.0.4", + "react-i18next": "^17.0.10", "react-icons": "^5.6.0", "react-markdown": "^10.1.0", "react-pdf": "^10.4.1", @@ -148,7 +148,7 @@ "typescript-eslint": "^8.61.1", "vite": "^8.0.16", "vite-plugin-svgr": "^5.2.0", - "vitest": "^4.1.9" + "vitest": "^4.1.10" }, "engines": { "node": ">=22.12.0", @@ -569,9 +569,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", - "integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.4.tgz", + "integrity": "sha512-xy5FNE5kQJKyK5MR1gJy6ztXYx4WBAbYGlK04lMEgmyPRWKybY9NFwiG9yo0XdzOU8Xvhj41u034J1ywfoWfMw==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -585,20 +585,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.2", - "@biomejs/cli-darwin-x64": "2.5.2", - "@biomejs/cli-linux-arm64": "2.5.2", - "@biomejs/cli-linux-arm64-musl": "2.5.2", - "@biomejs/cli-linux-x64": "2.5.2", - "@biomejs/cli-linux-x64-musl": "2.5.2", - "@biomejs/cli-win32-arm64": "2.5.2", - "@biomejs/cli-win32-x64": "2.5.2" + "@biomejs/cli-darwin-arm64": "2.5.4", + "@biomejs/cli-darwin-x64": "2.5.4", + "@biomejs/cli-linux-arm64": "2.5.4", + "@biomejs/cli-linux-arm64-musl": "2.5.4", + "@biomejs/cli-linux-x64": "2.5.4", + "@biomejs/cli-linux-x64-musl": "2.5.4", + "@biomejs/cli-win32-arm64": "2.5.4", + "@biomejs/cli-win32-x64": "2.5.4" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", - "integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.4.tgz", + "integrity": "sha512-4o3NFRobXHynkgcFVrlZsoDAFtF2ldlEGN8sORSws5ZQqyY4PXnPUIylu4ksfyHuwkfvDREuWh3JK+niRwGq3w==", "cpu": [ "arm64" ], @@ -613,9 +613,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", - "integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.4.tgz", + "integrity": "sha512-D32P5HkU2Y6PySuC/WsVDTOgsDwVFmujzhhhOQjajtATpVWFDXuVd3oRbsWNSEA+aaFzyzZm22szsyydBYlSyQ==", "cpu": [ "x64" ], @@ -630,9 +630,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", - "integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.4.tgz", + "integrity": "sha512-pSEfW7B8kTsXUjUxC1xVVK+y85Ht3C5XxZ9gclmC7/3Ku9Vqz8jmI7k0p/BNIjQ6t4sFERI2sFeH73ybiZl6YQ==", "cpu": [ "arm64" ], @@ -647,9 +647,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", - "integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.4.tgz", + "integrity": "sha512-Rpm5/AT1m+DlJmUoYvS4/vXc+0tXJPJ2NQz25TGPyHVF5JrWy75PE0GH6kVxsKtQDuCH4OgzquZq0R4kj/wCVg==", "cpu": [ "arm64" ], @@ -664,9 +664,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", - "integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.4.tgz", + "integrity": "sha512-FNxojWJkL7EajAuzBgoLe0T2G0y112M4lBrDIFl/DomFTx8yqenYOIdsRLNXvOvBBofE8hJi85LjzLmBDpY7/Q==", "cpu": [ "x64" ], @@ -681,9 +681,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", - "integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.4.tgz", + "integrity": "sha512-aby/PohmmgbShcHqFsZVzG8H6D98+P+A6xRWRrQcLW1pCjabcov5UUlke4UqNQBYTkDQav+jB4zyyDDeKB2GaA==", "cpu": [ "x64" ], @@ -698,9 +698,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", - "integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.4.tgz", + "integrity": "sha512-emoXexPZIPAZkz2RKmA95WJUqK3I5MJNYtwEbL5ESciRzhmFMMyekDhNG8hpeOaK+ZGRDxAU4wvGuA5IHQ0h0w==", "cpu": [ "arm64" ], @@ -715,9 +715,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", - "integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.4.tgz", + "integrity": "sha512-U1jaluLw1qQc2Tx7/CeSoL9N5XcqIH+GWjpUAy1ouB5nVjSCMNO+NNHdY3RAs8zxNurLWAdj6pehQdCA2zyU+Q==", "cpu": [ "x64" ], @@ -1129,9 +1129,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.43.5", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.5.tgz", - "integrity": "sha512-7uT/vUgH6dfXWn3WqOe23KneILMvGy5wQjNMEcRXLKzziJ9NOktpW6tGoyQpwVkBgE5Gj6hKkCcsddbnkaWrOQ==", + "version": "6.43.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.6.tgz", + "integrity": "sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==", "dev": true, "license": "MIT", "dependencies": { @@ -3829,20 +3829,20 @@ "license": "MIT" }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.6.tgz", + "integrity": "sha512-w9hl+724uYEgCGR3bhuRepjBtrNB/6gkhCnAf58Ke+SLbHPPQqVZZB59z60roB+5H+nh3nWTcdJhQdFMEydWmw==", "dev": true, "license": "MIT" }, "node_modules/@radix-ui/react-accessible-icon": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.11.tgz", - "integrity": "sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.12.tgz", + "integrity": "sha512-Y0zhCQ/XUdTom5hAxvE8RlXqR4hZmKGK6g2//LfgHmb88PJFOpXSh9B/7FlfYXezVY5FKGjRYWCYz5FXxZ9WZQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-visually-hidden": "1.2.7" + "@radix-ui/react-visually-hidden": "1.2.8" }, "peerDependencies": { "@types/react": "*", @@ -3860,21 +3860,21 @@ } }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.15.tgz", - "integrity": "sha512-24Zz/0SYx8F2bSVThBnQrdJs2VbKelyuJordcFRRdA0fRAhrq/wSegGCqaQz34VQoiWqSMGYCYXEhynLSlyQlg==", + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.17.tgz", + "integrity": "sha512-l3Dmp+qPPc3SqT8+SPnxIgoWBEU2MMBxcQ7BsoRgak2UT75xY83SFvFcrUkUAWukOV3LFF+BQ9aBIFtZsIG8yQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collapsible": "1.1.15", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collapsible": "1.1.17", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -3892,16 +3892,16 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.18.tgz", - "integrity": "sha512-6c2cXpNlAgHDhKguK24XcWHHayMpK+lk7/WwBXBco+ZJ4Dv7xP++GBM280KgTD/HCRu3jSdfe8WQiZssonYaIA==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.20.tgz", + "integrity": "sha512-Ft1W+jPqSh5BKfSTe4dpq6UYQKKQJ5Tvq3wfux+WVlg7nPwFK/3pIlHTb3Rbe+b/tNurx8YGXD9em91ujmgwuQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dialog": "1.1.18", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dialog": "1.1.20", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { @@ -3920,9 +3920,9 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.11.tgz", - "integrity": "sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.12.tgz", + "integrity": "sha512-ltXCE0glRomMZ9+u10d9o1Go+edqa1aLxufH59JRNNM3Yz1uvaeNWSaS1HeVh1X64agtdBG5JA1W1I6ySqWiwA==", "dev": true, "license": "MIT", "dependencies": { @@ -3944,9 +3944,9 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.11.tgz", - "integrity": "sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.12.tgz", + "integrity": "sha512-Sok2IBJxA1XO4pU3ldzZMwUBMumIt64EY8zOUlVq5CdS+i0FrEbajVslfDB+YGWLMsrjY2kZQB0DgkrZXLZvcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3968,13 +3968,14 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.1.tgz", - "integrity": "sha512-+8PWoLLZv3AVb5m0pvoiOca/bQGzc9vPVb+982HB2x3Un0DpYEPM3zLMl4oqRpBsocJuNqLkiv/HXTnTrlwr4g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.3.tgz", + "integrity": "sha512-peavtnApRB1tABx42tHw+rPU83GSg5tXicMYO/Xi1/lqNcRsF6jkr6L7Njo7gj4q/xtDRDKBkqJvbMtoOMYWtA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.4", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", @@ -3996,19 +3997,18 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.6.tgz", - "integrity": "sha512-eUEUoGMDpfkgHWSE97ZZaUJtzR1M7EKnNIpD1Q16+8JR9NWghcaqMulx9PuCQ720w0UclfYn6FEbCdd5Hx087g==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.8.tgz", + "integrity": "sha512-wfN60IGuxynWK7rP4Ks2p7u9G7gqirzkAiFptuzVbsR1ot2/K+PavNUAtxiKxyRfLOvSbVfvvm9m3rFqLEXz7A==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@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-context": "1.2.0", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { @@ -4027,19 +4027,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.15.tgz", - "integrity": "sha512-8A1zibu5skAQ+UVbaeNH5hVMibiFCRJzgMuM14LTWGttnTZKQL9jwYnhAbHRuxrtCqPXa4JvvnVUq1pTNgyZYw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.17.tgz", + "integrity": "sha512-DJgqGsNXa0df3ifz9PFNgvgj/bzIu5QTVWCt5nQWaUkM6y0EarUv4QG4s6mCoeQdOIyVOT/Q1osFuEGub2TDXQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { @@ -4058,14 +4058,14 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.11.tgz", - "integrity": "sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.12.tgz", + "integrity": "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, @@ -4101,9 +4101,9 @@ } }, "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==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4117,17 +4117,17 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.2.tgz", - "integrity": "sha512-qzsA/ZPhF6yMxBOTIk1nlCkoy2mswSbwYL+ErBa2iP0s4WWrlxmczArYqMcpVfEjmM7KJj/ADPXky0yZfbSxtQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.4.tgz", + "integrity": "sha512-eO9tkvHvo4dNwb+lytEcKWjy8c8To+ttLwNt0f9XzzsVFIaspqt3i1/c0JaaksxBB5G//zPo9CCgn39huWQyBA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-menu": "2.1.19", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-menu": "2.1.21", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -4145,24 +4145,25 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.18.tgz", - "integrity": "sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.20.tgz", + "integrity": "sha512-cngVJcvK0yMvR7wICJpv+1uW3Qw4T7QM5sdbb+oE/lxOdTdvF00oaRpWUjVgmjyXe3J+xh7eZyXZlVF3g2g59g==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.13", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", + "@radix-ui/react-use-layout-effect": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -4198,13 +4199,13 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.14.tgz", - "integrity": "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.16.tgz", + "integrity": "sha512-t45h68IjFx0ccBnPJqk0X6ecv69LkCFWd6DNCFQX56mUnVEXZbNOLCH/u9fHlAjFZ1RrFdl8/m4zev7B7NyhXQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -4226,19 +4227,19 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.19.tgz", - "integrity": "sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==", + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.21.tgz", + "integrity": "sha512-gavFM1iWLmWdxWNdGJHVeWeSQul5WE/0pxfvWWt1QnD71hyyujyMCDVacqBomaSOjdxwDzYB+Ng4+MxOvrFB1A==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-menu": "2.1.19", + "@radix-ui/react-menu": "2.1.21", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -4272,9 +4273,9 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.11.tgz", - "integrity": "sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.13.tgz", + "integrity": "sha512-dE04aPEuP9rvKKT0d0KjSOtTEYNg6bmCYFsoSJpfC+y91Hic28ZfDCGgv6aJ+2Kw/LBXYipMZpyqVj/OD3Z8Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -4298,17 +4299,17 @@ } }, "node_modules/@radix-ui/react-form": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.11.tgz", - "integrity": "sha512-0mTMJHv1gQAuEQoq5VDpTD3MRgmfUFdXAVFhpqR7wBeUr+tyRsof0wv/4XdPHLwQrefhoH2FiGHCggrCJhalIw==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.13.tgz", + "integrity": "sha512-PopvWqiutoZh5TJXk9EV9Wh+khbp+LQ+A0H4uHocIjVcKIi6gMlBy4sAaW15thwUSc6PrR8J62nB2uM+htqrcg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-label": "2.1.11", + "@radix-ui/react-label": "2.1.12", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { @@ -4327,21 +4328,21 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.18.tgz", - "integrity": "sha512-rt+Fx4HoCeEwFL2IdoV2QaPltqDLlzxN77i9nwB3Y70scFlfAHh1QCdE2TXKuFJtA1TNygb0oivnFBZifgtZOw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.20.tgz", + "integrity": "sha512-UPmdiR8NsngWjG/y9mClzFg+Rbbpy8u0p0SKM+t7mfH4V07TiLsuylqR0RhJiRibopsawoTtMQudm/TxwHWa9w==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.16", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -4378,9 +4379,9 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.11.tgz", - "integrity": "sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==", + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.12.tgz", + "integrity": "sha512-dxioNQ7VOrYKKWJIxMRmJPDSWQN0gNCUy3zaqUSBwsuFAiFzI0yLGJr2q3ml07k/HlOk55N8KEfwa1ZgfprJ3w==", "dev": true, "license": "MIT", "dependencies": { @@ -4402,26 +4403,26 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.19.tgz", - "integrity": "sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==", + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.21.tgz", + "integrity": "sha512-2BHtaJHvvoWTECyrja1mOjN6z2dWdpeHL6b8PxqZYgex8J8xakT2KAchpZIaMwNPauIRHH/VlPJYhSSKe8lz2g==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.13", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", + "@radix-ui/react-roving-focus": "1.1.16", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", "aria-hidden": "^1.2.4", @@ -4443,22 +4444,22 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.19.tgz", - "integrity": "sha512-Glt6mebxcgQvLeVkH3HiqV5bgQubE+31ELxLs7q0GlYI5k0XYkOkeuPrhXoylxK8eufvIt9CJjzY1TfFMXK3qw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.21.tgz", + "integrity": "sha512-uQONG1qM4D8FSEt0xRs5yDpzeSWggf8lOKqHa84NvqoVoc1qJ6XN+gdkrJuQCyEY55793gLlQI3wjgWO5A/Oqg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-menu": "2.1.19", + "@radix-ui/react-menu": "2.1.21", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -4476,26 +4477,26 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.17.tgz", - "integrity": "sha512-fYeYQvbeNn5AQk2RBbpO7koLm2YbS00UYxC/IL2sgLlninEH5UNIv+X3E0KJ1Vy4WIo+dhN9w8GNqSHhbHWCIg==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.19.tgz", + "integrity": "sha512-58OVQUrpWx/zGVV3lxGUyAtjX4n0305Z8xIdUAq2QlFO2m2hd1eBS4x1yIVtV8bzCQJja0TJttWcwiPI6y6tmw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.7" + "@radix-ui/react-visually-hidden": "1.2.8" }, "peerDependencies": { "@types/react": "*", @@ -4513,21 +4514,21 @@ } }, "node_modules/@radix-ui/react-one-time-password-field": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.11.tgz", - "integrity": "sha512-Rsgab65u73E5kPVh8OS6PgPwJgPyf08GFfJDGAbMdF4DL7CgDhFOaDnXuk/DiMEVF6kgQwl0oJmFklvipmiOLg==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.13.tgz", + "integrity": "sha512-reLtbZtEBsMcqXkjd/wOga4e8t9uxzFHdX9W/j/ZfGznTNJxLGjRrDNGnGOOWcBazMH1BI/b7Cx+hblSWSD7aw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" @@ -4548,18 +4549,18 @@ } }, "node_modules/@radix-ui/react-password-toggle-field": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.6.tgz", - "integrity": "sha512-pQ3xGp/uemomASPH97Eb3shfXX8QlG11bBJyEvRBV+vwtO4HvQlS06Yj9f31Ao7XepvF98SFrRgVDQ7jv+2xjQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.8.tgz", + "integrity": "sha512-NH9puF7Es5Loh8vFELm+SyayzV27nyBw8kiP/uD9wbkwgq359FfbkKEvccrNk75z0LiSqC4REWk1iL9xdeWJkQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1" }, @@ -4579,25 +4580,25 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.18.tgz", - "integrity": "sha512-qdXDes+eHlnMUGlBAAAe5EG7oOQvqsXuq4mq585diMudg80iB+jHbsSeG3+Q4eWNsogNyhqU2p/3i+Y0iEepqg==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.20.tgz", + "integrity": "sha512-/PYqbsyuDkNj+IxMcRx71qNt6GelnuNulMwdCV7AtFEhUyK6XkbwreEN6CCLydMeTiDozBV4uv5aF5d12dDH7w==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.13", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -4617,16 +4618,16 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.2.tgz", - "integrity": "sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.4.tgz", + "integrity": "sha512-PXnCa3XgTQk0FegMctxgqJXtFLZe4IFJdbUkB7jKSCKEpb6utEO4S9Vog/pkyCfEPdzM331gvE4xpztmBAfMng==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.11", + "@radix-ui/react-arrow": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", @@ -4650,9 +4651,9 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.13.tgz", - "integrity": "sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.14.tgz", + "integrity": "sha512-REwjAGPMa3J9oyDE4cuWkZbwnCbbyky66NurquQklXMSDn67cl6oGFx2gO7KZhPtFNbNw9xTWNrti3VIhgluYw==", "dev": true, "license": "MIT", "dependencies": { @@ -4675,9 +4676,9 @@ } }, "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==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.8.tgz", + "integrity": "sha512-0hhyrQdXMaATgq4ammLG9+iPqsXxzZkgTSIxdrJHdfLnXO4Uo5L7BoO3/Xf0AEaettadGZWGGJMw6ujzQvIpGA==", "dev": true, "license": "MIT", "dependencies": { @@ -4723,13 +4724,13 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.11.tgz", - "integrity": "sha512-KqiGJcFaZDc+BvveAgU3ZhACg2MvSUDrCBx4lRR/ZVRNal0bvt8lBpvnSkep9heeOuF8Qfw3fszLDX4OpQ2NVw==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.13.tgz", + "integrity": "sha512-1dUdKDd63Tz9FfbTw20MVr28ohG4v7HOJ1dsavGBBPBS3KGzLOyLKiMJAC1OdgiY18nTSHpD4fULGK5gsLY/ww==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { @@ -4748,21 +4749,20 @@ } }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.2.tgz", - "integrity": "sha512-W8Uo9riHnlzLLWy+r2mVHUyuEWqD/+be4PZzbEvaGoFSBDHkm+GYWjtcE6u3AmPKNyfanWpnVfpZ2GqPCdzzsw==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.4.tgz", + "integrity": "sha512-OpbUmp/korY+tjEQmHwGyQ+QQ3LBlCPC70z03Q/NSqGaHf2EijuwpjQPnswrH6cZLWyT2J6FmB+kzRoMUtPBig==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { @@ -4781,21 +4781,23 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.14.tgz", - "integrity": "sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.16.tgz", + "integrity": "sha512-w7lLsTSd3940vFYEshKkHw+NGf7H0QDJPHYsy8NRjDCVbO6ZdKW1X/xoJSYHZtttnrdZiYqbN2O/2uHGB0zasw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -4813,18 +4815,18 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.13.tgz", - "integrity": "sha512-7tncSubo2G0UY1e8rk+72qe3XRzrGnOLtZQ1PL1KoBfRUNX0NrJT5akb+0kfwSCc3gVR4wdHqyhAQBDpDNOwDw==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.15.tgz", + "integrity": "sha512-JVBHNfTBbGd9hhq/xZZOgmVnBCXhLs8PJJ8vMzgwI0pLZNsKckW9pkoqHyxokUCt1hoxbwDNvF9DItEeZsG68g==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2" @@ -4845,32 +4847,32 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.2.tgz", - "integrity": "sha512-brXD6C/V0fVK0DDbscLVw6LsXrjQ+ay8jdOBaN+tLb4vsHsAMm6Gt6eT77wHX1Eq8GPtD5rJ+RxFtfDozsb4+Q==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.4.tgz", + "integrity": "sha512-E2JxqAvaTUEhWtBptWo02g8FnLYPymv9ahEvW/cZQPPV4ySeyo0M8n3sXccsLUAIfMbexnfXt91qF7UjTbTMMg==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.13", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.7", + "@radix-ui/react-visually-hidden": "1.2.8", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -4890,9 +4892,9 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.11.tgz", - "integrity": "sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.12.tgz", + "integrity": "sha512-2hezgFBBR5jU3S9L9bIZ9Uag6LnvxuFBNsLCfTR8qx+NshuvFmpL4C72+5zMS3Z6UgHNSU1thOw2UaBBPEDpsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4914,20 +4916,20 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.2.tgz", - "integrity": "sha512-qt5C1ppJz66aUDrH1VccjPrq7aFchK0wBrn6xsxlCHNUyE57dRRQ7lp1QFpF7OscMexZF8MCGBTVBlENHPkNiA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.4.tgz", + "integrity": "sha512-8dUytW34KoJaB22ctfP7hqUCuyYa8xn2w7H8kCneeOtS5oM7UBivcnZtR8P4kPYMgdoAZlkMhE9/qkYZ5MlRzQ==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" @@ -4967,18 +4969,17 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.2.tgz", - "integrity": "sha512-tgRBI3DdNwAJYE4BBZyZcz/HRRCvAsPkRvG1wvKc+41tBGMxPn/a87T/wikXAvyDypNQ9kaZwHbeZe+veHCGpA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.4.tgz", + "integrity": "sha512-7iGMj1SfZBAc6xRiy0Y3Wr/v52viQeDhOmaM3fNRyNf2nbYooZA3kKoEDGLPtrDv8JitpzcueqdZLusVMojLdQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { @@ -4997,20 +4998,20 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.16.tgz", - "integrity": "sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.18.tgz", + "integrity": "sha512-1zq2XkQkK/KfbZn84edytYpOLquhNalra5LXc3NAMKhNRSGtyXqjMv6OyC9jlSuNKpqvQtsb57WKoICNk1v/sQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -5028,24 +5029,24 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.18.tgz", - "integrity": "sha512-YNEnTHV47hPep+U0QvVM02OJNka9uygREc+k4Nh5VSZBg4MmE+myI442x3hCGfRpX7N2WSSYSJKws4gE+Z8lgg==", + "version": "1.2.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.20.tgz", + "integrity": "sha512-S28OtO1IvYSpWfaUBtiYCTTwRLF8doafj+a+uQw8rc8dLINS52uuG3CIPCeZc3Jfdb/S7o7HhlQxLoXlIYRu6g==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.16", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.7" + "@radix-ui/react-visually-hidden": "1.2.8" }, "peerDependencies": { "@types/react": "*", @@ -5063,15 +5064,15 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.13.tgz", - "integrity": "sha512-bI2ILJrzwgmAsH05TsJ9pVrzqQwAip7OM2/krqAdYn0R16bl86UPWbe5VPHsALat0EnqpV01cGtkleaUKPNdNg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.15.tgz", + "integrity": "sha512-tyCejFjhJ51UKFVIG8jh9nTdRIsFPxrgrI4IdlxuJeP+AKTfTko+0gBueyBFLHqsyE71Aj9PKHjMnG+YRPyKhA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -5089,19 +5090,19 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.14.tgz", - "integrity": "sha512-TK1vusNKb8IRhF23FTbRgUNZ9zfs5rGIyI7LfR3h26p9LrQ060i0uW9QWeD8baZMddaaP0DBGlIa6pbZG+mitg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.16.tgz", + "integrity": "sha512-uil+A0Um3LaZQJkMap4nIg0VgqWc0j3iNU4AXf9a/zHOgPHNYWfVk5WVsG2296Y8HLv1bxiN7uQJblHc1+00tw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-toggle": "1.1.13", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-toggle": "1.1.15", + "@radix-ui/react-use-controllable-state": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -5119,19 +5120,19 @@ } }, "node_modules/@radix-ui/react-toolbar": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.14.tgz", - "integrity": "sha512-L/EkWVqlnj3lL2toHh4C7PwH2jxfa7OCq6lGfXSCii99ve2S4Ux5rc9HnOa7LN9exHa/Nl9kmCAmP9BuDPy5UA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.16.tgz", + "integrity": "sha512-ZnvUAH+ftoRYzUzFQ8gqKnQ1lUFYb3amguGu+BXpfjvLIkjmXCcHCJlQeBLBlJCOtGNVtP+wHrZaUCC/zYKQMg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-separator": "1.1.11", - "@radix-ui/react-toggle-group": "1.1.14" + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-separator": "1.1.12", + "@radix-ui/react-toggle-group": "1.1.16" }, "peerDependencies": { "@types/react": "*", @@ -5149,24 +5150,25 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.11.tgz", - "integrity": "sha512-8XZ6Py3y3W2nEzAUGCN5cfVKaUi+CVApcz1d6lrNVVf2hvYEixMRkq8k9ggPKnQUpRRuOV5avt8uvxViH2jLwA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.13.tgz", + "integrity": "sha512-56XPNYGMnGBcPyiBTaEXB7IGPybbsdNkFgSv90SCrHkXnu2Av1HhsyZMegzXlTu/QHA3V6/l22GZCv9iEoiqmQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.16", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-visually-hidden": "1.2.7" + "@radix-ui/react-use-controllable-state": "1.2.4", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-visually-hidden": "1.2.8" }, "peerDependencies": { "@types/react": "*", @@ -5200,12 +5202,13 @@ } }, "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==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.4.tgz", + "integrity": "sha512-cx2DixxmSfjCcEoRvDvy1NLd6SWK94XFcEEOZUcharUlXbmahFQGKCfwdKZL2ub34iIwOPOEFVF80xb+yfLYiA==", "dev": true, "license": "MIT", "dependencies": { + "@radix-ui/primitive": "1.1.6", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, @@ -5344,9 +5347,9 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.7.tgz", - "integrity": "sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.8.tgz", + "integrity": "sha512-FjsQEpkNBJJYiPSat6jh2LGKLPX2jAoDVS3AZSBNX3cOUoEGhw/f+z2FCY8Cf1NkoYIbytJ1f4mlWPQpR+MjVg==", "dev": true, "license": "MIT", "dependencies": { @@ -6286,49 +6289,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", - "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "5.21.6", + "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.2" + "tailwindcss": "4.3.3" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", - "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-x64": "4.3.2", - "@tailwindcss/oxide-freebsd-x64": "4.3.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-x64-musl": "4.3.2", - "@tailwindcss/oxide-wasm32-wasi": "4.3.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", - "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", "cpu": [ "arm64" ], @@ -6343,9 +6346,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", - "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", "cpu": [ "arm64" ], @@ -6360,9 +6363,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", - "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", "cpu": [ "x64" ], @@ -6377,9 +6380,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", - "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", "cpu": [ "x64" ], @@ -6394,9 +6397,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", - "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", "cpu": [ "arm" ], @@ -6411,9 +6414,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", - "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", "cpu": [ "arm64" ], @@ -6428,9 +6431,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", - "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", "cpu": [ "arm64" ], @@ -6445,9 +6448,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", - "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", "cpu": [ "x64" ], @@ -6462,9 +6465,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", - "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", "cpu": [ "x64" ], @@ -6479,9 +6482,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", - "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -6575,9 +6578,9 @@ "optional": true }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", - "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", "cpu": [ "arm64" ], @@ -6592,9 +6595,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", - "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", "cpu": [ "x64" ], @@ -6609,15 +6612,15 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.2.tgz", - "integrity": "sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.2", - "@tailwindcss/oxide": "4.3.2", - "tailwindcss": "4.3.2" + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" @@ -7356,9 +7359,9 @@ } }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.25.10", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.10.tgz", - "integrity": "sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==", + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.11.tgz", + "integrity": "sha512-otyFa+n9IOYtEjaKOxPedHkj15fTPUF21wdR9pv0GpZPfuGl27cvmcv6+tognbRu9VvEcsHKE+ESoszeo3KfTw==", "dev": true, "license": "MIT", "dependencies": { @@ -7384,9 +7387,9 @@ } }, "node_modules/@uiw/codemirror-extensions-langs": { - "version": "4.25.10", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.10.tgz", - "integrity": "sha512-VsfENMb23HrcKG2z0n0RB0KY7d11k+8qzUlH6i36099QXa07D/FEfeffoSnP+QdghhvISNnuMTJsWKqYQq6qlA==", + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.11.tgz", + "integrity": "sha512-RJ6MQTGInT+HBnzEs6PoUmPBgLFbWH3e9ZiF0+H1bdWJXrqN0Kfkiopumz1OCcbWFJ8aVJCD6KPK9mXV/LQ4yA==", "dev": true, "license": "MIT", "dependencies": { @@ -7425,22 +7428,22 @@ } }, "node_modules/@uiw/codemirror-theme-github": { - "version": "4.25.10", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.25.10.tgz", - "integrity": "sha512-iMM2QT4FaebJMO4W7lXmxNkRPIjKzgY26wL0QG0Ugy0gzsnxoNz4zgNeFIblPA8rvrN3vOIhNNh4nk9UOlFKxA==", + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.25.11.tgz", + "integrity": "sha512-3s0LK3gX2mvGI996z3G0tEHZqshbeJRly+QMRsKGAI1Tfr1V475bfaW9NvnoDdINuQHt+RB+ri6q57+WWF8d+A==", "dev": true, "license": "MIT", "dependencies": { - "@uiw/codemirror-themes": "4.25.10" + "@uiw/codemirror-themes": "4.25.11" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" } }, "node_modules/@uiw/codemirror-themes": { - "version": "4.25.10", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.10.tgz", - "integrity": "sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==", + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.11.tgz", + "integrity": "sha512-SBNCOgRsCtewGNocRbmjbCkltGXlFcPJsvhxQ351VynQjnWUiPbUrFcEU/haQ3HanROdAAjWXZJPk5bMBxl2jw==", "dev": true, "license": "MIT", "dependencies": { @@ -7458,9 +7461,9 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.25.10", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.10.tgz", - "integrity": "sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==", + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.11.tgz", + "integrity": "sha512-DYVFAKLX+F/4JS9N/7xexh+TICrlncwkX9HKKInrP1bwO0tSfc3k0GB6oawTYhelVKh20cX3TuRx+NJSkVXuMw==", "dev": true, "license": "MIT", "dependencies": { @@ -7468,7 +7471,7 @@ "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.25.10", + "@uiw/codemirror-extensions-basic-setup": "4.25.11", "codemirror": "^6.0.0" }, "funding": { @@ -7518,14 +7521,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", - "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -7539,8 +7542,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -7549,16 +7552,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -7567,13 +7570,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -7604,9 +7607,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7617,13 +7620,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -7631,14 +7634,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -7647,9 +7650,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -7657,13 +7660,13 @@ } }, "node_modules/@vitest/ui": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.9.tgz", - "integrity": "sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.10.tgz", + "integrity": "sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "fflate": "^0.8.2", "flatted": "^3.4.2", "pathe": "^2.0.3", @@ -7675,17 +7678,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.9" + "vitest": "4.1.10" } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -10210,9 +10213,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.6", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", - "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "version": "5.24.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.2.tgz", + "integrity": "sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==", "dev": true, "license": "MIT", "dependencies": { @@ -11601,9 +11604,9 @@ } }, "node_modules/i18next": { - "version": "26.3.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.4.tgz", - "integrity": "sha512-pa7m0d7pBDqGHZxljT+WPFeyFgQ7P7SciPPo1tTqYuO0z4sqADYhwnBESmmGp/wEof1inwdls/k8ZgTg8rxFHA==", + "version": "26.3.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.6.tgz", + "integrity": "sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==", "dev": true, "funding": [ { @@ -11621,7 +11624,7 @@ ], "license": "MIT", "peerDependencies": { - "typescript": "^5 || ^6" + "typescript": "^5 || ^6 || ^7" }, "peerDependenciesMeta": { "typescript": { @@ -14235,9 +14238,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "5.1.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", - "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-6.0.0.tgz", + "integrity": "sha512-mkUH+rPkwU2qPadJ0oJZOjeZ5Mxn8Q1UhevwkTRWNuUZzyia3h4rhzK39hxaHTk0o2OxB8W2SQ6A8k23ZDi1pQ==", "funding": [ { "type": "github", @@ -14249,7 +14252,7 @@ "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^18 || >=20" + "node": "^22 || ^24 || >=26" } }, "node_modules/napi-build-utils": { @@ -15338,67 +15341,67 @@ } }, "node_modules/radix-ui": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.1.tgz", - "integrity": "sha512-QXDXJtB6sK83mLASONYUZCauatcWb+knFviFpN1EhtdbbmlsRmzCLrbZSKztnNiem2KOHIBbiDbauVB7SORXMw==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.3.tgz", + "integrity": "sha512-KmhSq0NfxIwN9q6ZpEaZ+J0hiVFQcGyrPYYhbxg34q9B8CIrQoccLJ3mJ9znLRslLoaogsP2ml8JKOVoKMXgvQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-accessible-icon": "1.1.11", - "@radix-ui/react-accordion": "1.2.15", - "@radix-ui/react-alert-dialog": "1.1.18", - "@radix-ui/react-arrow": "1.1.11", - "@radix-ui/react-aspect-ratio": "1.1.11", - "@radix-ui/react-avatar": "1.2.1", - "@radix-ui/react-checkbox": "1.3.6", - "@radix-ui/react-collapsible": "1.1.15", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.6", + "@radix-ui/react-accessible-icon": "1.1.12", + "@radix-ui/react-accordion": "1.2.17", + "@radix-ui/react-alert-dialog": "1.1.20", + "@radix-ui/react-arrow": "1.1.12", + "@radix-ui/react-aspect-ratio": "1.1.12", + "@radix-ui/react-avatar": "1.2.3", + "@radix-ui/react-checkbox": "1.3.8", + "@radix-ui/react-collapsible": "1.1.17", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-context-menu": "2.3.2", - "@radix-ui/react-dialog": "1.1.18", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-context-menu": "2.3.4", + "@radix-ui/react-dialog": "1.1.20", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", - "@radix-ui/react-dropdown-menu": "2.1.19", + "@radix-ui/react-dismissable-layer": "1.1.16", + "@radix-ui/react-dropdown-menu": "2.1.21", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", - "@radix-ui/react-form": "0.1.11", - "@radix-ui/react-hover-card": "1.1.18", - "@radix-ui/react-label": "2.1.11", - "@radix-ui/react-menu": "2.1.19", - "@radix-ui/react-menubar": "1.1.19", - "@radix-ui/react-navigation-menu": "1.2.17", - "@radix-ui/react-one-time-password-field": "0.1.11", - "@radix-ui/react-password-toggle-field": "0.1.6", - "@radix-ui/react-popover": "1.1.18", - "@radix-ui/react-popper": "1.3.2", - "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.13", + "@radix-ui/react-form": "0.1.13", + "@radix-ui/react-hover-card": "1.1.20", + "@radix-ui/react-label": "2.1.12", + "@radix-ui/react-menu": "2.1.21", + "@radix-ui/react-menubar": "1.1.21", + "@radix-ui/react-navigation-menu": "1.2.19", + "@radix-ui/react-one-time-password-field": "0.1.13", + "@radix-ui/react-password-toggle-field": "0.1.8", + "@radix-ui/react-popover": "1.1.20", + "@radix-ui/react-popper": "1.3.4", + "@radix-ui/react-portal": "1.1.14", + "@radix-ui/react-presence": "1.1.8", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-progress": "1.1.11", - "@radix-ui/react-radio-group": "1.4.2", - "@radix-ui/react-roving-focus": "1.1.14", - "@radix-ui/react-scroll-area": "1.2.13", - "@radix-ui/react-select": "2.3.2", - "@radix-ui/react-separator": "1.1.11", - "@radix-ui/react-slider": "1.4.2", + "@radix-ui/react-progress": "1.1.13", + "@radix-ui/react-radio-group": "1.4.4", + "@radix-ui/react-roving-focus": "1.1.16", + "@radix-ui/react-scroll-area": "1.2.15", + "@radix-ui/react-select": "2.3.4", + "@radix-ui/react-separator": "1.1.12", + "@radix-ui/react-slider": "1.4.4", "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-switch": "1.3.2", - "@radix-ui/react-tabs": "1.1.16", - "@radix-ui/react-toast": "1.2.18", - "@radix-ui/react-toggle": "1.1.13", - "@radix-ui/react-toggle-group": "1.1.14", - "@radix-ui/react-toolbar": "1.1.14", - "@radix-ui/react-tooltip": "1.2.11", + "@radix-ui/react-switch": "1.3.4", + "@radix-ui/react-tabs": "1.1.18", + "@radix-ui/react-toast": "1.2.20", + "@radix-ui/react-toggle": "1.1.15", + "@radix-ui/react-toggle-group": "1.1.16", + "@radix-ui/react-toolbar": "1.1.16", + "@radix-ui/react-tooltip": "1.2.13", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.4", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-escape-keydown": "1.1.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.7" + "@radix-ui/react-visually-hidden": "1.2.8" }, "peerDependencies": { "@types/react": "*", @@ -15542,9 +15545,9 @@ } }, "node_modules/react-i18next": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.8.tgz", - "integrity": "sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.10.tgz", + "integrity": "sha512-XneHftyYA774MJkkccSkZ5oKrUpCnXIPmxio3wemqrVzCRLWiGXOMbIzObrer03fNDEnm8g8R5yYls4HcE+esg==", "dev": true, "license": "MIT", "dependencies": { @@ -15555,7 +15558,7 @@ "peerDependencies": { "i18next": ">= 26.2.0", "react": ">= 16.8.0", - "typescript": "^5 || ^6" + "typescript": "^5 || ^6 || ^7" }, "peerDependenciesMeta": { "react-dom": { @@ -16842,9 +16845,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", - "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", "dev": true, "license": "MIT" }, @@ -17745,19 +17748,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -17785,12 +17788,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -18027,9 +18030,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 72160103..f66d52a0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "termix", "private": true, - "version": "2.5.1", + "version": "2.6.0", "description": "Self-hosted SSH and remote desktop management.", "author": "Karmaa", "main": "electron/main.cjs", @@ -14,7 +14,7 @@ "format:check": "prettier --check .", "biome:check": "biome check biome.json package.json", "biome:fix": "biome check --write biome.json package.json", - "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-xterm-android-ime.cjs", + "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-guacamole-common-js.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-xterm-android-ime.cjs", "prebuild": "node scripts/write-electron-build-info.cjs", "lint": "eslint .", "lint:fix": "eslint --fix .", @@ -65,22 +65,22 @@ "ldapjs": "^3.0.7", "motion": "^12.42.2", "multer": "^2.2.0", - "nanoid": "^5.1.16", + "nanoid": "^6.0.0", "qrcode": "^1.5.4", "serialport": "^13.0.0", "socks": "^2.8.7", "speakeasy": "^2.0.0", "ssh2": "^1.17.0", "undici": "^8.7.0", - "ws": "^8.20.0" + "ws": "^8.21.1" }, "devDependencies": { - "@biomejs/biome": "2.5.2", + "@biomejs/biome": "2.5.4", "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.10.4", "@codemirror/search": "^6.7.1", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.43.5", + "@codemirror/view": "^6.43.6", "@commitlint/cli": "^21.0.2", "@commitlint/config-conventional": "^21.0.2", "@deadendjs/swagger-jsdoc": "^8.1.2", @@ -92,23 +92,23 @@ "@fontsource/jetbrains-mono": "^5.2.8", "@fontsource/source-code-pro": "^5.2.7", "@monaco-editor/react": "^4.7.0", - "@radix-ui/react-accordion": "^1.2.15", - "@radix-ui/react-alert-dialog": "^1.1.18", - "@radix-ui/react-checkbox": "^1.3.6", - "@radix-ui/react-dialog": "^1.1.18", - "@radix-ui/react-dropdown-menu": "^2.1.19", - "@radix-ui/react-label": "^2.1.11", - "@radix-ui/react-popover": "^1.1.18", - "@radix-ui/react-progress": "^1.1.11", - "@radix-ui/react-scroll-area": "^1.2.13", - "@radix-ui/react-select": "^2.3.2", - "@radix-ui/react-separator": "^1.1.11", - "@radix-ui/react-slider": "^1.4.2", + "@radix-ui/react-accordion": "^1.2.17", + "@radix-ui/react-alert-dialog": "^1.1.20", + "@radix-ui/react-checkbox": "^1.3.8", + "@radix-ui/react-dialog": "^1.1.20", + "@radix-ui/react-dropdown-menu": "^2.1.21", + "@radix-ui/react-label": "^2.1.12", + "@radix-ui/react-popover": "^1.1.20", + "@radix-ui/react-progress": "^1.1.13", + "@radix-ui/react-scroll-area": "^1.2.15", + "@radix-ui/react-select": "^2.3.4", + "@radix-ui/react-separator": "^1.1.12", + "@radix-ui/react-slider": "^1.4.4", "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-switch": "^1.3.2", - "@radix-ui/react-tabs": "^1.1.16", - "@radix-ui/react-tooltip": "^1.2.11", - "@tailwindcss/vite": "^4.3.2", + "@radix-ui/react-switch": "^1.3.4", + "@radix-ui/react-tabs": "^1.1.18", + "@radix-ui/react-tooltip": "^1.2.13", + "@tailwindcss/vite": "^4.3.3", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -128,12 +128,12 @@ "@types/speakeasy": "^2.0.10", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", - "@uiw/codemirror-extensions-langs": "^4.25.9", - "@uiw/codemirror-theme-github": "^4.25.9", - "@uiw/react-codemirror": "^4.25.9", + "@uiw/codemirror-extensions-langs": "^4.25.11", + "@uiw/codemirror-theme-github": "^4.25.11", + "@uiw/react-codemirror": "^4.25.11", "@vitejs/plugin-react": "^6.0.3", - "@vitest/coverage-v8": "^4.1.9", - "@vitest/ui": "^4.1.9", + "@vitest/coverage-v8": "^4.1.10", + "@vitest/ui": "^4.1.10", "@xterm/addon-clipboard": "^0.2.0", "@xterm/addon-fit": "^0.11.0", "@xterm/addon-unicode11": "^0.9.0", @@ -153,19 +153,19 @@ "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "husky": "^9.1.7", - "i18next": "^26.3.4", + "i18next": "^26.3.6", "i18next-browser-languagedetector": "^8.2.1", "jsdom": "^29.1.1", "lint-staged": "^17.0.8", "lucide-react": "^1.20.0", "prettier": "3.8.4", - "radix-ui": "^1.6.1", + "radix-ui": "^1.6.3", "react": "^19.2.7", "react-cytoscapejs": "^2.0.0", "react-dom": "^19.2.7", "react-h5-audio-player": "^3.10.2", "react-hook-form": "^7.79.0", - "react-i18next": "^17.0.4", + "react-i18next": "^17.0.10", "react-icons": "^5.6.0", "react-markdown": "^10.1.0", "react-pdf": "^10.4.1", @@ -182,7 +182,7 @@ "typescript-eslint": "^8.61.1", "vite": "^8.0.16", "vite-plugin-svgr": "^5.2.0", - "vitest": "^4.1.9" + "vitest": "^4.1.10" }, "lint-staged": { "*.{ts,tsx}": [ diff --git a/scripts/patch-guacamole-common-js.cjs b/scripts/patch-guacamole-common-js.cjs new file mode 100644 index 00000000..039071de --- /dev/null +++ b/scripts/patch-guacamole-common-js.cjs @@ -0,0 +1,68 @@ +const fs = require("fs"); +const path = require("path"); + +const packageRoot = path.join( + __dirname, + "..", + "node_modules", + "guacamole-common-js", +); + +const bundlePaths = [ + path.join(packageRoot, "dist", "esm", "guacamole-common.js"), + path.join(packageRoot, "dist", "cjs", "guacamole-common.js"), +]; + +const oldFlushBlock = + " if (window.requestAnimationFrame && document.hasFocus())\n" + + " asyncFlush();\n" + + " else\n" + + " syncFlush();"; + +const newFlushBlock = + " // Electron can throttle or skip requestAnimationFrame() for inactive\n" + + " // windows/tabs even while guacd is still sending display frames. Flush\n" + + " // synchronously so Guacamole connections do not stall while waiting for\n" + + " // a frame callback that may never run.\n" + + " syncFlush();"; + +let patched = false; +let foundBundle = false; + +for (const bundlePath of bundlePaths) { + if (!fs.existsSync(bundlePath)) { + console.log( + `[patch-guacamole-common-js] ${bundlePath} not found, skipping`, + ); + continue; + } + + foundBundle = true; + let content = fs.readFileSync(bundlePath, "utf8"); + if (content.includes(newFlushBlock)) continue; + + if (!content.includes(oldFlushBlock)) { + console.log( + `[patch-guacamole-common-js] Flush target not found in ${bundlePath}, skipping`, + ); + continue; + } + + content = content.replace(oldFlushBlock, newFlushBlock); + fs.writeFileSync(bundlePath, content); + patched = true; +} + +if (!foundBundle) { + console.log("[patch-guacamole-common-js] File not found, skipping"); + process.exit(0); +} + +if (!patched) { + console.log("[patch-guacamole-common-js] Already patched"); + process.exit(0); +} + +console.log( + "[patch-guacamole-common-js] Patched display flush to avoid Electron requestAnimationFrame stalls", +); diff --git a/scripts/patch-guacamole-lite.cjs b/scripts/patch-guacamole-lite.cjs index 67cdca65..b9a76efe 100644 --- a/scripts/patch-guacamole-lite.cjs +++ b/scripts/patch-guacamole-lite.cjs @@ -17,14 +17,27 @@ const cryptPath = path.join( "lib", "Crypt.js", ); +const clientConnectionPath = path.join( + __dirname, + "..", + "node_modules", + "guacamole-lite", + "lib", + "ClientConnection.js", +); -if (!fs.existsSync(guacdClientPath) || !fs.existsSync(cryptPath)) { +if ( + !fs.existsSync(guacdClientPath) || + !fs.existsSync(cryptPath) || + !fs.existsSync(clientConnectionPath) +) { console.log("[patch-guacamole-lite] File not found, skipping"); process.exit(0); } let guacdClientContent = fs.readFileSync(guacdClientPath, "utf8"); let cryptContent = fs.readFileSync(cryptPath, "utf8"); +let clientConnectionContent = fs.readFileSync(clientConnectionPath, "utf8"); // Patch 1: protocol version negotiation. // guacamole-lite originally only accepted 1.0.0/1.1.0. Support the protocol @@ -56,20 +69,26 @@ const newVersionBlock = const oldTimezone = "if (protocolVersion === '1_1_0') {"; const newTimezone = "if (protocolVersion !== '1_0_0') {"; -// Patch 3: send the `name` handshake instruction for protocol >= 1.3.0. -// The Guacamole protocol added the `name` instruction in 1.3.0 (an optional -// human-readable identifier for the joining user). guacd 1.6.0 began requiring -// it during the VNC handshake even when negotiating older protocol versions, -// causing connections to silently drop right after "User joined". See +// Patch 3: send the `name` handshake instruction for all protocol versions >= 1.1.0. +// The Guacamole protocol added `name` in 1.3.0, but guacd 1.6.0 began requiring it +// during the VNC handshake even when negotiating VERSION_1_1_0, causing connections to +// silently drop right after "User joined". Sending it for all non-1.0.0 sessions is +// harmless (guacd ignores unknown handshake instructions for older versions). See // Termix-SSH/Support#567 and #734. const oldConnect = " this.sendInstruction(['connect'].concat(connectArgs));"; -const newConnect = +const oldNameConnect = " if (protocolVersion === '1_3_0' || protocolVersion === '1_5_0') {\n" + " this.sendInstruction(['name', this.connectionSettings.name || 'guacamole-lite']);\n" + " }\n" + "\n" + " this.sendInstruction(['connect'].concat(connectArgs));"; +const newConnect = + " if (protocolVersion !== '1_0_0') {\n" + + " this.sendInstruction(['name', this.connectionSettings.name || 'guacamole-lite']);\n" + + " }\n" + + "\n" + + " this.sendInstruction(['connect'].concat(connectArgs));"; // Patch 4: answer guacd's dynamic argument requests locally. // macOS Screen Sharing can request VNC username/password through the @@ -156,13 +175,16 @@ if (!guacdClientContent.includes(newTimezone)) { } if (!guacdClientContent.includes(newConnect)) { - if (!guacdClientContent.includes(oldConnect)) { + if (guacdClientContent.includes(oldNameConnect)) { + guacdClientContent = guacdClientContent.replace(oldNameConnect, newConnect); + } else if (guacdClientContent.includes(oldConnect)) { + guacdClientContent = guacdClientContent.replace(oldConnect, newConnect); + } else { console.log( "[patch-guacamole-lite] Connect target not found, skipping name patch", ); process.exit(0); } - guacdClientContent = guacdClientContent.replace(oldConnect, newConnect); patched = true; } @@ -259,6 +281,94 @@ if (!cryptContent.includes(newDecryptBlock)) { patched = true; } +// Patch 7: drop client-to-guacd input instructions from read-only session-share +// joins. guacd has no native read-only enforcement in the versions this project +// targets, so Termix must gate here. Denylist (not allowlist) on purpose: an +// unrecognized opcode is far more likely to be protocol plumbing (sync, blob, +// clipboard streams) than a new input vector, so failing open is the safer +// default for a client we already control. +const oldSendMessageToGuacd = + " sendMessageToGuacd(message) {\n" + + " this.lastActivity = Date.now();\n" + + " this.logger.log(LOGLEVEL.DEBUG, '[ >>> # ] Received from WS: ```' + message + '```');\n" + + "\n" + + " if (this.guacdClient) {\n" + + " this.guacdClient.send(message, true);\n" + + " }\n" + + " }"; +const newSendMessageToGuacd = + " sendMessageToGuacd(message) {\n" + + " this.lastActivity = Date.now();\n" + + " this.logger.log(LOGLEVEL.DEBUG, '[ >>> # ] Received from WS: ```' + message + '```');\n" + + "\n" + + " if (this.isReadOnlyJoin() && this.isInputInstruction(message)) {\n" + + " return;\n" + + " }\n" + + "\n" + + " if (this.guacdClient) {\n" + + " this.guacdClient.send(message, true);\n" + + " }\n" + + " }\n" + + "\n" + + " isReadOnlyJoin() {\n" + + " const connection = this.connectionSettings && this.connectionSettings.connection;\n" + + " return !!(connection && connection.join && connection.readOnly === true);\n" + + " }\n" + + "\n" + + " // Termix-only read-only gate, not part of the vendored library: extracts just\n" + + " // the leading opcode from a raw '.,...;' instruction without the\n" + + " // overhead of a full stateful parse.\n" + + " isInputInstruction(message) {\n" + + " const dot = message.indexOf('.');\n" + + " if (dot === -1) return false;\n" + + " const len = parseInt(message.substring(0, dot), 10);\n" + + " if (isNaN(len)) return false;\n" + + " const opcode = message.substring(dot + 1, dot + 1 + len);\n" + + " return ['mouse', 'key', 'touch', 'size'].includes(opcode);\n" + + " }"; + +if (!clientConnectionContent.includes("isReadOnlyJoin()")) { + if (!clientConnectionContent.includes(oldSendMessageToGuacd)) { + console.log( + "[patch-guacamole-lite] sendMessageToGuacd target not found, skipping read-only patch", + ); + process.exit(0); + } + clientConnectionContent = clientConnectionContent.replace( + oldSendMessageToGuacd, + newSendMessageToGuacd, + ); + patched = true; +} + +// Patch 8: mergeConnectionOptions only preserves `join` across the settings +// merge, dropping Termix's `readOnly` flag before sendMessageToGuacd can see it. +const oldPreserveJoin = + " // For join connections, preserve the join property\n" + + " if (this.connectionSettings.connection.join) {\n" + + " compiledSettings.join = this.connectionSettings.connection.join;\n" + + " }"; +const newPreserveJoin = + " // For join connections, preserve the join property\n" + + " if (this.connectionSettings.connection.join) {\n" + + " compiledSettings.join = this.connectionSettings.connection.join;\n" + + " compiledSettings.readOnly = this.connectionSettings.connection.readOnly === true;\n" + + " }"; + +if (!clientConnectionContent.includes("compiledSettings.readOnly")) { + if (!clientConnectionContent.includes(oldPreserveJoin)) { + console.log( + "[patch-guacamole-lite] join-preserve target not found, skipping readOnly propagation patch", + ); + process.exit(0); + } + clientConnectionContent = clientConnectionContent.replace( + oldPreserveJoin, + newPreserveJoin, + ); + patched = true; +} + if (!patched) { console.log("[patch-guacamole-lite] Already patched"); process.exit(0); @@ -266,6 +376,7 @@ if (!patched) { fs.writeFileSync(guacdClientPath, guacdClientContent); fs.writeFileSync(cryptPath, cryptContent); +fs.writeFileSync(clientConnectionPath, clientConnectionContent); console.log( - "[patch-guacamole-lite] Patched protocol VERSION_1_3_0/1_5_0 support, name handshake, required arguments, and UTF-8 token decrypt", + "[patch-guacamole-lite] Patched protocol VERSION_1_3_0/1_5_0 support, name handshake, required arguments, UTF-8 token decrypt, and read-only join input filtering", ); diff --git a/scripts/patch-guacamole-lite.test.ts b/scripts/patch-guacamole-lite.test.ts index d6b8c9eb..70e3ad33 100644 --- a/scripts/patch-guacamole-lite.test.ts +++ b/scripts/patch-guacamole-lite.test.ts @@ -69,6 +69,31 @@ describe("patch-guacamole-lite", () => { ]); }); + it("sends name instruction for VERSION_1_1_0 to fix guacd 1.6.0 VNC drops", () => { + const client = createPatchedClient({ + hostname: "192.0.2.10", + port: 5900, + password: "secret", + width: 1280, + height: 720, + dpi: 96, + }); + + client.sendHandshakeReply(["VERSION_1_1_0", "hostname", "port"]); + + expect(client.sendInstruction).toHaveBeenCalledWith(["timezone"]); + expect(client.sendInstruction).toHaveBeenCalledWith([ + "name", + "guacamole-lite", + ]); + expect(client.sendInstruction).toHaveBeenCalledWith([ + "connect", + "VERSION_1_1_0", + "192.0.2.10", + 5900, + ]); + }); + it("answers required credentials through argument value streams", () => { const client = createPatchedClient({ username: "", diff --git a/scripts/patch-nan.cjs b/scripts/patch-nan.cjs index 5fec17f9..43aad385 100644 --- a/scripts/patch-nan.cjs +++ b/scripts/patch-nan.cjs @@ -39,6 +39,19 @@ const nanHeaderPatched = patchFile(path.join(nanDir, "nan.h"), [ # define __builtin_frame_address(level) _AddressOfReturnAddress() #endif +// v8::External::New()/->Value() gained a mandatory ExternalPointerTypeTag +// argument in V8 15 (Electron 43+). Plain Node (V8 <= 13.x as of Node 24) +// still uses the old 2-arg signatures, so this must be conditional rather +// than assumed - a build can target either header set. +#include +#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >= 15 +# define NAN_EXTERNAL_TAG_ARG , static_cast(0) +# define NAN_EXTERNAL_TAG_PARAM static_cast(0) +#else +# define NAN_EXTERNAL_TAG_ARG +# define NAN_EXTERNAL_TAG_PARAM +#endif + #define NODE_0_10_MODULE_VERSION 11`, }, ]); @@ -63,23 +76,24 @@ const bindingPatched = patchFile(bindingPath, [ }, ]); -// 2. nan_implementation_12_inl.h: replace v8::External::New() with the 3-arg form. -// Electron 42 / V8 13+ requires an ExternalPointerTypeTag as the third argument. +// 2. nan_implementation_12_inl.h: replace v8::External::New() with a form that +// passes NAN_EXTERNAL_TAG_ARG - a macro (defined in the nan.h patch above) +// that expands to the ExternalPointerTypeTag argument only when the target +// V8 headers actually declare it (V8 15+ / Electron 43+). const implPath = path.join(nanDir, "nan_implementation_12_inl.h"); let implPatched = false; if (fs.existsSync(implPath)) { let src = fs.readFileSync(implPath, "utf8"); const before = src; - const TAG = "static_cast(0)"; - if (!src.includes(TAG)) { + if (!src.includes("NAN_EXTERNAL_TAG_ARG")) { src = src.replace( - /v8::External::New\(v8::Isolate::GetCurrent\(\),\s*value\)/g, - `v8::External::New(v8::Isolate::GetCurrent(), value, ${TAG})`, + /v8::External::New\(v8::Isolate::GetCurrent\(\),\s*value(?:,\s*static_cast\(0\))?\)/g, + `v8::External::New(v8::Isolate::GetCurrent(), value NAN_EXTERNAL_TAG_ARG)`, ); src = src.replace( - /v8::External::New\(isolate,\s*reinterpret_cast\(callback\)\)/g, - `v8::External::New(isolate, reinterpret_cast(callback), ${TAG})`, + /v8::External::New\(isolate,\s*reinterpret_cast\(callback\)(?:,\s*static_cast\(0\))?\)/g, + `v8::External::New(isolate, reinterpret_cast(callback) NAN_EXTERNAL_TAG_ARG)`, ); } @@ -89,20 +103,19 @@ if (fs.existsSync(implPath)) { } } -// 3. nan_callbacks_12_inl.h: replace ->Value() with ->Value(tag) on v8::External. -// The new API requires an ExternalPointerTypeTag argument. +// 3. nan_callbacks_12_inl.h: replace ->Value() with ->Value(NAN_EXTERNAL_TAG_PARAM) +// on v8::External, same conditional-tag reasoning as above. const callbacksPath = path.join(nanDir, "nan_callbacks_12_inl.h"); let callbacksPatched = false; if (fs.existsSync(callbacksPath)) { let src = fs.readFileSync(callbacksPath, "utf8"); const before = src; - const TAG = "static_cast(0)"; - if (!src.includes(TAG)) { - // Pattern: .As()->Value()) — always followed by )) + if (!src.includes("NAN_EXTERNAL_TAG_PARAM")) { + // Pattern: .As()->Value()) or ->Value()) src = src.replace( - /\.As\(\)->Value\(\)\)/g, - `.As()->Value(${TAG}))`, + /\.As\(\)->Value\((?:static_cast\(0\))?\)\)/g, + `.As()->Value(NAN_EXTERNAL_TAG_PARAM))`, ); } diff --git a/scripts/patch-xterm-android-ime.cjs b/scripts/patch-xterm-android-ime.cjs index e7738b32..4bbff4a3 100644 --- a/scripts/patch-xterm-android-ime.cjs +++ b/scripts/patch-xterm-android-ime.cjs @@ -14,6 +14,17 @@ const xtermDir = path.join( // xtermjs/xterm.js#3600 remains unresolved upstream. Android IMEs can restart // composition on the previous word and replace it with a shorter value (for // example, Vietnamese "Hoar" -> "Hỏa"). xterm 6.0 otherwise emits nothing. +// +// Also fixes _handleAnyTextareaChanges, which iOS Safari/WKWebView drives +// for ordinary typing (it reports keyCode 229 for all software-keyboard +// input, not just IME composition). That handler diffs the textarea value +// via `newValue.replace(oldValue, "")`, a literal substring removal. When +// keystrokes arrive faster than the function's setTimeout(0) callback runs, +// several overlapping callbacks each capture a stale oldValue, so the +// literal-substring search fails to match and the diff silently comes back +// empty - characters are dropped instead of sent. Swap in the same +// common-prefix diff used for composition-end above so a stale oldValue +// still yields the correct delta. const patches = [ { file: "xterm.mjs", @@ -34,6 +45,10 @@ const patches = [ "e.start+=this._dataAlreadySent.length,this._isComposing?i=this._textarea.value.substring(e.start,this._compositionPosition.start):i=this._textarea.value.substring(e.start),i.length>0&&", "e.start+=this._dataAlreadySent.length;if(this._isComposing)i=this._textarea.value.substring(e.start,this._compositionPosition.start);else{const t=this._textarea.value;if(t.length0&&", ], + [ + '_handleAnyTextareaChanges(){let t=this._textarea.value;setTimeout(()=>{if(!this._isComposing){let e=this._textarea.value,i=e.replace(t,"");this._dataAlreadySent=i,e.length>t.length?this._coreService.triggerDataEvent(i,!0):e.length{if(!this._isComposing){let e=this._textarea.value,r=0;const n=Math.min(e.length,t.length);for(;r0&&this._coreService.triggerDataEvent(i,!0)}},0)}", + ], ], }, { @@ -55,6 +70,10 @@ const patches = [ "e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,this._compositionPosition.start):this._textarea.value.substring(e.start),t.length>0&&", "e.start+=this._dataAlreadySent.length;this._isComposing?t=this._textarea.value.substring(e.start,this._compositionPosition.start):(()=>{const s=this._textarea.value;if(s.length0&&", ], + [ + '_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.length{if(!this._isComposing){const t=this._textarea.value;let r=0;const n=Math.min(t.length,e.length);for(;r0&&this._coreService.triggerDataEvent(i,!0)}}),0)}", + ], ], }, ]; @@ -66,18 +85,24 @@ for (const { file, replacements } of patches) { } let source = fs.readFileSync(filePath, "utf8"); - if (source.includes("_preCompositionValue")) { - console.log(`[patch-xterm-android-ime] ${file} already patched`); - continue; - } + let changed = false; for (const [original, patched] of replacements) { + if (source.includes(patched)) { + continue; + } if (!source.includes(original)) { throw new Error( `[patch-xterm-android-ime] Expected source not found in ${file}`, ); } source = source.replace(original, patched); + changed = true; + } + + if (!changed) { + console.log(`[patch-xterm-android-ime] ${file} already patched`); + continue; } fs.writeFileSync(filePath, source); diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index 66914a15..28e5d051 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -12,6 +12,7 @@ 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 "../hosts/guacamole/routes.js"; +import sessionSharingRoutes from "../hosts/session-sharing/routes.js"; import networkTopologyRoutes from "./routes/network-topology.js"; import rbacRoutes from "./routes/rbac.js"; import openTabsRoutes from "./routes/open-tabs.js"; @@ -22,6 +23,7 @@ import { registerAuditLogRoutes } from "./routes/audit-log-routes.js"; import { registerTailscaleRoutes } from "./routes/tailscale-routes.js"; import vaultRoutes from "./routes/vault.js"; import alertRulesRoutes from "./routes/alert-rules-routes.js"; +import syncRoutes from "./routes/sync.js"; import { createCorsMiddleware } from "../utils/cors-config.js"; import fs from "fs"; import path from "path"; @@ -1737,6 +1739,7 @@ app.use("/c2s-tunnel-presets", c2sTunnelPresetRoutes); app.use("/terminal", terminalRoutes); app.use("/session_logs", sessionLogRoutes); app.use("/guacamole", guacamoleRoutes); +app.use("/session-sharing", sessionSharingRoutes); app.use("/network-topology", networkTopologyRoutes); app.use("/rbac", rbacRoutes); app.use("/open-tabs", openTabsRoutes); @@ -1747,6 +1750,7 @@ registerAuditLogRoutes(app, authenticateJWT); registerTailscaleRoutes(app, authenticateJWT); app.use("/vault", vaultRoutes); app.use("/", alertRulesRoutes); +app.use("/sync", syncRoutes); const frontendDistPaths = [ path.join(__dirname, "../../../dist"), diff --git a/src/backend/database/db/index.ts b/src/backend/database/db/index.ts index cb9c358c..b3f0f97b 100644 --- a/src/backend/database/db/index.ts +++ b/src/backend/database/db/index.ts @@ -390,9 +390,11 @@ async function initializeCompleteDatabase(): Promise { name TEXT NOT NULL, color TEXT, icon TEXT, + credential_id INTEGER, 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 (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (credential_id) REFERENCES ssh_credentials (id) ON DELETE SET NULL ); CREATE TABLE IF NOT EXISTS recent_activity ( @@ -493,6 +495,38 @@ async function initializeCompleteDatabase(): Promise { FOREIGN KEY (access_id) REFERENCES host_access (id) ON DELETE SET NULL ); + CREATE TABLE IF NOT EXISTS session_shares ( + id TEXT PRIMARY KEY, + host_id INTEGER NOT NULL, + owner_user_id TEXT NOT NULL, + protocol TEXT NOT NULL, + session_id TEXT NOT NULL, + tab_instance_id TEXT, + share_type TEXT NOT NULL, + target_user_id TEXT, + link_token TEXT UNIQUE, + permission_level TEXT NOT NULL DEFAULT 'read-only', + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TEXT NOT NULL, + revoked_at TEXT, + last_joined_at TEXT, + join_count INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE, + FOREIGN KEY (owner_user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (target_user_id) REFERENCES users (id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS session_share_participants ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + share_id TEXT NOT NULL, + user_id TEXT, + guest_label TEXT, + joined_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + left_at TEXT, + FOREIGN KEY (share_id) REFERENCES session_shares (id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS api_keys ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, @@ -692,12 +726,16 @@ const addColumnIfNotExists = ( sqlite.exec(`ALTER TABLE ${table} ADD COLUMN "${column}" ${definition};`); } catch (alterError) { - databaseLogger.warn(`Failed to add column ${column} to ${table}`, { - operation: "schema_migration", - table, - column, - error: alterError, - }); + const message = + alterError instanceof Error ? alterError.message : String(alterError); + databaseLogger.warn( + `Failed to add column ${column} to ${table}: ${message}`, + { + operation: "schema_migration", + table, + column, + }, + ); } } }; @@ -736,6 +774,8 @@ const migrateSchema = () => { addColumnIfNotExists("user_preferences", "hidden_rail_tabs", "TEXT"); addColumnIfNotExists("user_preferences", "compact_host_view", "INTEGER"); addColumnIfNotExists("user_preferences", "status_color_scheme", "TEXT"); + addColumnIfNotExists("user_preferences", "custom_themes", "TEXT"); + addColumnIfNotExists("user_preferences", "custom_keybindings", "TEXT"); sqlite.exec(` CREATE TABLE IF NOT EXISTS dashboard_service_links ( @@ -1378,6 +1418,19 @@ const migrateSchema = () => { } } + try { + sqlite.prepare("SELECT credential_id FROM ssh_folders LIMIT 1").get(); + } catch { + try { + sqlite.exec("ALTER TABLE ssh_folders ADD COLUMN credential_id INTEGER REFERENCES ssh_credentials(id) ON DELETE SET NULL"); + } catch (alterError) { + databaseLogger.warn("Failed to add credential_id column to ssh_folders", { + operation: "schema_migration", + error: alterError, + }); + } + } + try { sqlite.prepare("SELECT sudo_password FROM ssh_data LIMIT 1").get(); } catch { @@ -1440,6 +1493,8 @@ const migrateSchema = () => { { column: "rdp_auth_type", sql: "ALTER TABLE ssh_data ADD COLUMN rdp_auth_type TEXT" }, { column: "vnc_auth_type", sql: "ALTER TABLE ssh_data ADD COLUMN vnc_auth_type TEXT" }, { column: "telnet_auth_type", sql: "ALTER TABLE ssh_data ADD COLUMN telnet_auth_type TEXT" }, + { column: "allow_session_sharing", sql: "ALTER TABLE ssh_data ADD COLUMN allow_session_sharing INTEGER NOT NULL DEFAULT 1" }, + { column: "connection_origin", sql: "ALTER TABLE ssh_data ADD COLUMN connection_origin TEXT" }, ]; for (const migration of sshDataMigrations) { @@ -1985,6 +2040,74 @@ const migrateSchema = () => { addColumnIfNotExists("users", "sso_provider_id", "INTEGER"); + try { + const usersTableInfo = sqlite.prepare("PRAGMA table_info(users)").all() as Array<{ + cid: number; + name: string; + type: string; + notnull: number; + dflt_value: string | null; + pk: number; + }>; + const legacyNotNullColumns = new Set([ + "client_id", + "client_secret", + "issuer_url", + "authorization_url", + "token_url", + "identifier_path", + "name_path", + "scopes", + ]); + const hasStaleNotNull = usersTableInfo.some( + (col) => legacyNotNullColumns.has(col.name) && col.notnull === 1, + ); + + if (hasStaleNotNull) { + const tempTableName = "users_temp_migration"; + const columnDefs = usersTableInfo + .map((col) => { + const parts = [`"${col.name}"`, col.type || "TEXT"]; + if (col.pk === 1) parts.push("PRIMARY KEY"); + if (col.notnull === 1 && !legacyNotNullColumns.has(col.name)) { + parts.push("NOT NULL"); + } + if (col.dflt_value !== null) { + parts.push(`DEFAULT ${col.dflt_value}`); + } + return parts.join(" "); + }) + .join(",\n "); + const allColumns = usersTableInfo.map((col) => `"${col.name}"`).join(", "); + + sqlite.exec(`PRAGMA foreign_keys = OFF`); + sqlite.exec(` + CREATE TABLE ${tempTableName} ( + ${columnDefs} + ); + + INSERT INTO ${tempTableName} SELECT ${allColumns} FROM users; + + DROP TABLE users; + + ALTER TABLE ${tempTableName} RENAME TO users; + `); + sqlite.exec(`PRAGMA foreign_keys = ON`); + + databaseLogger.info( + "Successfully migrated users table to remove legacy OIDC NOT NULL constraints", + { + operation: "schema_migration_users_oidc_nullable", + }, + ); + } + } catch (migrationError) { + databaseLogger.warn("Failed to migrate users table legacy OIDC columns", { + operation: "schema_migration", + error: migrationError, + }); + } + // Migrate legacy single oidc_config settings blob into sso_providers table try { const migrationDone = getRawSettingValue("sso_migration_v1"); @@ -2206,6 +2329,174 @@ const migrateSchema = () => { } // --- homepage end --- + try { + sqlite.prepare("SELECT id FROM session_shares LIMIT 1").get(); + } catch { + try { + sqlite.exec(` + CREATE TABLE IF NOT EXISTS session_shares ( + id TEXT PRIMARY KEY, + host_id INTEGER NOT NULL, + owner_user_id TEXT NOT NULL, + protocol TEXT NOT NULL, + session_id TEXT NOT NULL, + tab_instance_id TEXT, + share_type TEXT NOT NULL, + target_user_id TEXT, + link_token TEXT UNIQUE, + permission_level TEXT NOT NULL DEFAULT 'read-only', + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TEXT NOT NULL, + revoked_at TEXT, + last_joined_at TEXT, + join_count INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE, + FOREIGN KEY (owner_user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (target_user_id) REFERENCES users (id) ON DELETE CASCADE + ); + `); + sqlite.exec( + "CREATE INDEX IF NOT EXISTS idx_session_shares_link_token ON session_shares(link_token)", + ); + sqlite.exec( + "CREATE INDEX IF NOT EXISTS idx_session_shares_target_user ON session_shares(target_user_id)", + ); + sqlite.exec( + "CREATE INDEX IF NOT EXISTS idx_session_shares_host ON session_shares(host_id)", + ); + } catch (createError) { + databaseLogger.warn("Failed to create session_shares table", { + operation: "schema_migration", + error: createError, + }); + } + } + + try { + sqlite.prepare("SELECT id FROM session_share_participants LIMIT 1").get(); + } catch { + try { + sqlite.exec(` + CREATE TABLE IF NOT EXISTS session_share_participants ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + share_id TEXT NOT NULL, + user_id TEXT, + guest_label TEXT, + joined_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + left_at TEXT, + FOREIGN KEY (share_id) REFERENCES session_shares (id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE + ); + `); + sqlite.exec( + "CREATE INDEX IF NOT EXISTS idx_session_share_participants_share ON session_share_participants(share_id)", + ); + } catch (createError) { + databaseLogger.warn("Failed to create session_share_participants table", { + operation: "schema_migration", + error: createError, + }); + } + } + + // --- sync begin --- + // Stable per-row identity used to match rows across two independently- + // seeded databases (the embedded desktop backend and a connected remote + // server) during sync. Local autoincrement ids collide across instances, + // so a randomly-generated id is the join key instead. SQLite refuses a + // non-constant DEFAULT (e.g. randomblob()) on ALTER TABLE ADD COLUMN for + // tables with existing constraints ("Cannot add a column with + // non-constant default"), so the column is added as plain nullable TEXT; + // repositories set syncId explicitly on insert going forward, and + // existing rows are backfilled by the UPDATE loop below. + addColumnIfNotExists("ssh_data", "sync_id", "TEXT"); + addColumnIfNotExists("ssh_credentials", "sync_id", "TEXT"); + addColumnIfNotExists("ssh_folders", "sync_id", "TEXT"); + addColumnIfNotExists("snippets", "sync_id", "TEXT"); + addColumnIfNotExists("snippet_folders", "sync_id", "TEXT"); + addColumnIfNotExists("vault_profiles", "sync_id", "TEXT"); + addColumnIfNotExists("dashboard_service_links", "sync_id", "TEXT"); + // SQLite also rejects NOT NULL DEFAULT CURRENT_TIMESTAMP here for the same + // "non-constant default" reason -- add nullable, then backfill from + // created_at below and rely on the repository layer to keep it current. + addColumnIfNotExists("dashboard_service_links", "updated_at", "TEXT"); + try { + sqlite.exec( + "UPDATE dashboard_service_links SET updated_at = created_at WHERE updated_at IS NULL", + ); + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + databaseLogger.warn( + `Failed to backfill dashboard_service_links.updated_at: ${message}`, + { operation: "schema_migration", table: "dashboard_service_links" }, + ); + } + addColumnIfNotExists("homepage_items", "sync_id", "TEXT"); + + const syncIdTables = [ + "ssh_data", + "ssh_credentials", + "ssh_folders", + "snippets", + "snippet_folders", + "vault_profiles", + "dashboard_service_links", + "homepage_items", + ]; + + for (const table of syncIdTables) { + try { + const result = sqlite + .prepare( + `UPDATE ${table} SET sync_id = lower(hex(randomblob(16))) WHERE sync_id IS NULL`, + ) + .run(); + if (result.changes > 0) { + databaseLogger.info( + `Backfilled sync_id for ${result.changes} row(s) in ${table}`, + { operation: "sync_id_backfill", table }, + ); + } + sqlite.exec( + `CREATE UNIQUE INDEX IF NOT EXISTS idx_${table}_sync_id ON ${table}(sync_id)`, + ); + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + databaseLogger.warn( + `Failed to backfill sync_id for ${table}: ${message}`, + { + operation: "sync_id_backfill", + table, + }, + ); + } + } + + try { + sqlite.prepare("SELECT id FROM sync_tombstones LIMIT 1").get(); + } catch { + try { + sqlite.exec(` + CREATE TABLE IF NOT EXISTS sync_tombstones ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + entity_type TEXT NOT NULL, + sync_id TEXT NOT NULL, + deleted_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + `); + sqlite.exec( + "CREATE INDEX IF NOT EXISTS idx_sync_tombstones_user_entity ON sync_tombstones(user_id, entity_type)", + ); + } catch (createError) { + databaseLogger.warn("Failed to create sync_tombstones table", { + operation: "schema_migration", + error: createError, + }); + } + } + // --- sync end --- + 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 6e0a3050..423154b4 100644 --- a/src/backend/database/db/schema.ts +++ b/src/backend/database/db/schema.ts @@ -153,6 +153,9 @@ export const hosts = sqliteTable("ssh_data", { enableSessionLogging: integer("enable_session_logging", { mode: "boolean" }) .notNull() .default(true), + allowSessionSharing: integer("allow_session_sharing", { mode: "boolean" }) + .notNull() + .default(true), enableCommandHistory: integer("enable_command_history", { mode: "boolean" }) .notNull() .default(true), @@ -237,6 +240,12 @@ export const hosts = sqliteTable("ssh_data", { socks5Password: text("socks5_password"), socks5ProxyChain: text("socks5_proxy_chain"), + // null = use the desktop app's global default; "local" | "remote" pins + // this specific host's SSH/Docker-console/Serial connections to originate + // from the embedded local backend or a connected remote sync server. + // Ignored for rdp/vnc/telnet, which always require the remote server. + connectionOrigin: text("connection_origin"), + macAddress: text("mac_address"), wolBroadcastAddress: text("wol_broadcast_address"), portKnockSequence: text("port_knock_sequence"), @@ -248,6 +257,11 @@ export const hosts = sqliteTable("ssh_data", { hostKeyLastVerified: text("host_key_last_verified"), hostKeyChangedCount: integer("host_key_changed_count").default(0), + // Stable identity used to match this row across two independently-seeded + // databases (the embedded backend and a connected remote server) during + // sync -- local autoincrement ids collide across instances. + syncId: text("sync_id").unique(), + createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -354,6 +368,7 @@ export const sshCredentials = sqliteTable("ssh_credentials", { usageCount: integer("usage_count").notNull().default(0), lastUsed: text("last_used"), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -388,6 +403,7 @@ export const snippets = sqliteTable("snippets", { description: text("description"), folder: text("folder"), order: integer("order").notNull().default(0), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -405,6 +421,7 @@ export const snippetFolders = sqliteTable("snippet_folders", { name: text("name").notNull(), color: text("color"), icon: text("icon"), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -462,6 +479,10 @@ export const sshFolders = sqliteTable("ssh_folders", { name: text("name").notNull(), color: text("color"), icon: text("icon"), + credentialId: integer("credential_id").references(() => sshCredentials.id, { + onDelete: "set null", + }), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -673,6 +694,62 @@ export const sessionRecordings = sqliteTable("session_recordings", { terminationReason: text("termination_reason"), }); +export const sessionShares = sqliteTable("session_shares", { + id: text("id").primaryKey(), + + hostId: integer("host_id") + .notNull() + .references(() => hosts.id, { onDelete: "cascade" }), + ownerUserId: text("owner_user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + + protocol: text("protocol").notNull(), + + // Live-session binding: TerminalSessionManager's session.id for SSH, or + // guacd's own guacamoleConnectionId for rdp/vnc/telnet. Neither is a DB + // row (process-local, in-memory) so this intentionally has no FK. + sessionId: text("session_id").notNull(), + tabInstanceId: text("tab_instance_id"), + + shareType: text("share_type").notNull(), // "link" | "user" + targetUserId: text("target_user_id").references(() => users.id, { + onDelete: "cascade", + }), + linkToken: text("link_token").unique(), + + permissionLevel: text("permission_level").notNull().default("read-only"), + + createdAt: text("created_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + expiresAt: text("expires_at").notNull(), + revokedAt: text("revoked_at"), + + lastJoinedAt: text("last_joined_at"), + joinCount: integer("join_count").notNull().default(0), +}); + +export const sessionShareParticipants = sqliteTable( + "session_share_participants", + { + id: integer("id").primaryKey({ autoIncrement: true }), + shareId: text("share_id") + .notNull() + .references(() => sessionShares.id, { onDelete: "cascade" }), + + userId: text("user_id").references(() => users.id, { + onDelete: "cascade", + }), + guestLabel: text("guest_label"), + + joinedAt: text("joined_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + leftAt: text("left_at"), + }, +); + export const opksshTokens = sqliteTable("opkssh_tokens", { id: integer("id").primaryKey({ autoIncrement: true }), userId: text("user_id") @@ -724,6 +801,7 @@ export const vaultProfiles = sqliteTable("vault_profiles", { keyType: text("key_type"), // When true the profile is visible/usable by all users on the server shared: integer("shared", { mode: "boolean" }).notNull().default(false), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -813,6 +891,8 @@ export const userPreferences = sqliteTable("user_preferences", { hiddenRailTabs: text("hidden_rail_tabs"), compactHostView: integer("compact_host_view", { mode: "boolean" }), statusColorScheme: text("status_color_scheme"), + customThemes: text("custom_themes"), + customKeybindings: text("custom_keybindings"), updatedAt: text("updated_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -879,9 +959,13 @@ export const dashboardServiceLinks = sqliteTable("dashboard_service_links", { label: text("label").notNull(), url: text("url").notNull(), order: integer("order").notNull().default(0), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), + updatedAt: text("updated_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), }); // --- termix-id begin --- @@ -1067,6 +1151,7 @@ export const homepageItems = sqliteTable("homepage_items", { title: text("title"), config: text("config").notNull().default("{}"), folderId: integer("folder_id"), + syncId: text("sync_id").unique(), createdAt: text("created_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), @@ -1088,3 +1173,20 @@ export const homepageLayouts = sqliteTable("homepage_layouts", { .default(sql`CURRENT_TIMESTAMP`), }); // --- homepage end --- + +// --- sync begin --- +// Records a delete for a synced entity type so the other side of a sync +// pair (embedded desktop backend <-> connected remote server) learns about +// the deletion instead of re-creating the row on its next pull. +export const syncTombstones = sqliteTable("sync_tombstones", { + id: integer("id").primaryKey({ autoIncrement: true }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + entityType: text("entity_type").notNull(), + syncId: text("sync_id").notNull(), + deletedAt: text("deleted_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), +}); +// --- sync end --- diff --git a/src/backend/database/repositories/credential-repository.ts b/src/backend/database/repositories/credential-repository.ts index 82d0af36..5ec291be 100644 --- a/src/backend/database/repositories/credential-repository.ts +++ b/src/backend/database/repositories/credential-repository.ts @@ -1,4 +1,5 @@ import { and, desc, eq, sql } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { sshCredentials, sshCredentialUsage } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; import { DataCrypto } from "../../utils/data-crypto.js"; @@ -18,7 +19,7 @@ export class CredentialRepository { async create(credential: NewCredentialRecord): Promise { const rows = await this.context.drizzle .insert(sshCredentials) - .values(credential) + .values({ syncId: randomUUID(), ...credential }) .returning(); await this.afterWrite(); return rows[0]; @@ -30,7 +31,11 @@ export class CredentialRepository { ): Promise { const userDataKey = DataCrypto.validateUserAccess(userId); const tempId = credential.id ?? Date.now(); - const dataWithTempId = { ...credential, id: tempId }; + const dataWithTempId = { + syncId: randomUUID(), + ...credential, + id: tempId, + }; const encryptedCredential = this.encryptCredentialRecordForWrite( dataWithTempId, userId, @@ -140,7 +145,7 @@ export class CredentialRepository { ): Promise { const rows = await this.context.drizzle .update(sshCredentials) - .set({ folder: newName }) + .set({ folder: newName, updatedAt: sql`CURRENT_TIMESTAMP` }) .where( and( eq(sshCredentials.userId, userId), @@ -163,7 +168,7 @@ export class CredentialRepository { ): Promise { const rows = await this.context.drizzle .update(sshCredentials) - .set(update) + .set({ ...update, updatedAt: sql`CURRENT_TIMESTAMP` }) .where( and( eq(sshCredentials.id, credentialId), @@ -190,7 +195,7 @@ export class CredentialRepository { const rows = await this.context.drizzle .update(sshCredentials) - .set(encryptedUpdate) + .set({ ...encryptedUpdate, updatedAt: sql`CURRENT_TIMESTAMP` }) .where( and( eq(sshCredentials.id, credentialId), @@ -203,7 +208,10 @@ export class CredentialRepository { return this.decryptOne(rows[0] ?? null, userId); } - async deleteForUser(userId: string, credentialId: number): Promise { + async deleteForUser( + userId: string, + credentialId: number, + ): Promise<{ syncId: string | null } | null> { const rows = await this.context.drizzle .delete(sshCredentials) .where( @@ -212,10 +220,10 @@ export class CredentialRepository { eq(sshCredentials.userId, userId), ), ) - .returning({ id: sshCredentials.id }); + .returning({ syncId: sshCredentials.syncId }); await this.afterWrite(); - return rows.length > 0; + return rows[0] ?? null; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/repositories/dashboard-service-link-repository.ts b/src/backend/database/repositories/dashboard-service-link-repository.ts index a12079e0..b06c2100 100644 --- a/src/backend/database/repositories/dashboard-service-link-repository.ts +++ b/src/backend/database/repositories/dashboard-service-link-repository.ts @@ -1,4 +1,5 @@ import { and, asc, eq } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { dashboardServiceLinks } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; @@ -40,11 +41,13 @@ export class DashboardServiceLinkRepository { const [created] = await this.context.drizzle .insert(dashboardServiceLinks) .values({ + syncId: randomUUID(), userId, label: input.label, url: input.url, order: nextOrder, createdAt, + updatedAt: createdAt, }) .returning(); await this.afterWrite(); @@ -76,7 +79,7 @@ export class DashboardServiceLinkRepository { ): Promise { const [updated] = await this.context.drizzle .update(dashboardServiceLinks) - .set(updates) + .set({ ...updates, updatedAt: new Date().toISOString() }) .where( and( eq(dashboardServiceLinks.id, id), @@ -92,7 +95,10 @@ export class DashboardServiceLinkRepository { return updated ?? null; } - async deleteForUser(userId: string, id: number): Promise { + async deleteForUser( + userId: string, + id: number, + ): Promise<{ syncId: string | null } | null> { const rows = await this.context.drizzle .delete(dashboardServiceLinks) .where( @@ -101,13 +107,11 @@ export class DashboardServiceLinkRepository { eq(dashboardServiceLinks.userId, userId), ), ) - .returning({ id: dashboardServiceLinks.id }); + .returning({ syncId: dashboardServiceLinks.syncId }); - if (rows.length > 0) { - await this.afterWrite(); - } - - return rows.length > 0; + if (rows.length === 0) return null; + await this.afterWrite(); + return rows[0]; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/repositories/factory.ts b/src/backend/database/repositories/factory.ts index 0db7fea1..42dd08d8 100644 --- a/src/backend/database/repositories/factory.ts +++ b/src/backend/database/repositories/factory.ts @@ -27,10 +27,12 @@ import { RecentActivityRepository } from "./recent-activity-repository.js"; import { RoleRepository } from "./role-repository.js"; import { SessionRecordingRepository } from "./session-recording-repository.js"; import { SessionRepository } from "./session-repository.js"; +import { SessionShareRepository } from "./session-share-repository.js"; import { SettingsRepository } from "./settings-repository.js"; import { SharedHostSecretsRepository } from "./shared-host-secrets-repository.js"; import { SnippetRepository } from "./snippet-repository.js"; import { SshCredentialUsageRepository } from "./ssh-credential-usage-repository.js"; +import { SyncTombstoneRepository } from "./sync-tombstone-repository.js"; import { SsoProviderRepository } from "./sso-provider-repository.js"; import { TermixIdentityCaRepository } from "./termix-identity-ca-repository.js"; import { TermixIdentityRepository } from "./termix-identity-repository.js"; @@ -125,6 +127,13 @@ export function createCurrentDashboardServiceLinkRepository(): DashboardServiceL ); } +export function createCurrentSyncTombstoneRepository(): SyncTombstoneRepository { + return new SyncTombstoneRepository( + createCurrentRepositoryContext(), + createCurrentRepositoryWriteHook("sync_tombstone_repository_write"), + ); +} + export function createCurrentDismissedAlertRepository(): DismissedAlertRepository { return new DismissedAlertRepository( createCurrentRepositoryContext(), @@ -253,6 +262,13 @@ export function createCurrentSessionRepository(): SessionRepository { ); } +export function createCurrentSessionShareRepository(): SessionShareRepository { + return new SessionShareRepository( + createCurrentRepositoryContext(), + createCurrentRepositoryWriteHook("session_share_repository_write"), + ); +} + export function createCurrentSettingsRepository(): SettingsRepository { return new SettingsRepository( createCurrentRepositoryContext(), diff --git a/src/backend/database/repositories/homepage-item-repository.ts b/src/backend/database/repositories/homepage-item-repository.ts index 1dc8efea..2be7ebed 100644 --- a/src/backend/database/repositories/homepage-item-repository.ts +++ b/src/backend/database/repositories/homepage-item-repository.ts @@ -1,4 +1,5 @@ import { and, asc, eq } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { homepageItems } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; @@ -37,6 +38,7 @@ export class HomepageItemRepository { const [created] = await this.context.drizzle .insert(homepageItems) .values({ + syncId: randomUUID(), userId, typeId: input.typeId, title: input.title, @@ -82,17 +84,18 @@ export class HomepageItemRepository { return updated ?? null; } - async deleteForUser(userId: string, id: number): Promise { + async deleteForUser( + userId: string, + id: number, + ): Promise<{ syncId: string | null } | null> { const rows = await this.context.drizzle .delete(homepageItems) .where(and(eq(homepageItems.id, id), eq(homepageItems.userId, userId))) - .returning({ id: homepageItems.id }); + .returning({ syncId: homepageItems.syncId }); - if (rows.length > 0) { - await this.afterWrite(); - } - - return rows.length > 0; + if (rows.length === 0) return null; + await this.afterWrite(); + return rows[0]; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/repositories/host-folder-repository.ts b/src/backend/database/repositories/host-folder-repository.ts index 50d0b169..d83df5a3 100644 --- a/src/backend/database/repositories/host-folder-repository.ts +++ b/src/backend/database/repositories/host-folder-repository.ts @@ -1,4 +1,5 @@ import { and, eq, like, or, sql } from "drizzle-orm"; +import { randomUUID } from "crypto"; import type { SQLiteColumn } from "drizzle-orm/sqlite-core"; import { hosts, sshCredentials, sshFolders } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; @@ -72,13 +73,20 @@ export class HostFolderRepository { name: string, color: string | null | undefined, icon: string | null | undefined, + credentialId?: number | null, now = new Date().toISOString(), ): Promise<{ folder: HostFolderRecord; created: boolean }> { const existing = await this.findFolder(userId, name); if (existing) { const [updated] = await this.context.drizzle .update(sshFolders) - .set({ color, icon, updatedAt: now }) + .set({ + color, + icon, + credentialId: + credentialId === undefined ? existing.credentialId : credentialId, + updatedAt: now, + }) .where(and(eq(sshFolders.userId, userId), eq(sshFolders.name, name))) .returning(); @@ -89,10 +97,12 @@ export class HostFolderRepository { const [created] = await this.context.drizzle .insert(sshFolders) .values({ + syncId: randomUUID(), userId, name, color, icon, + credentialId: credentialId ?? null, createdAt: now, updatedAt: now, }) @@ -118,7 +128,7 @@ export class HostFolderRepository { async deleteHostsAndFolderRecords( userId: string, folderName: string, - ): Promise { + ): Promise<{ hostSyncIds: string[]; folderSyncIds: string[] }> { const folderMatch = (col: SQLiteColumn) => or(eq(col, folderName), like(col, `${folderName} / %`)); @@ -129,11 +139,21 @@ export class HostFolderRepository { .where(and(eq(hosts.userId, userId), folderMatch(hosts.folder))); } - await this.context.drizzle + const deletedFolders = await this.context.drizzle .delete(sshFolders) - .where(and(eq(sshFolders.userId, userId), folderMatch(sshFolders.name))); + .where(and(eq(sshFolders.userId, userId), folderMatch(sshFolders.name))) + .returning({ syncId: sshFolders.syncId }); await this.afterWrite(); + + return { + hostSyncIds: hostsToDelete + .map((h) => h.syncId) + .filter((id): id is string => !!id), + folderSyncIds: deletedFolders + .map((f) => f.syncId) + .filter((id): id is string => !!id), + }; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/repositories/host-repository.ts b/src/backend/database/repositories/host-repository.ts index 0cb7a4db..c7d2cf72 100644 --- a/src/backend/database/repositories/host-repository.ts +++ b/src/backend/database/repositories/host-repository.ts @@ -1,4 +1,5 @@ -import { and, eq, inArray } from "drizzle-orm"; +import { and, eq, inArray, sql } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { hostAccess, hosts } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; import { DataCrypto } from "../../utils/data-crypto.js"; @@ -22,7 +23,7 @@ export class HostRepository { async create(host: NewHostRecord): Promise { const rows = await this.context.drizzle .insert(hosts) - .values(host) + .values({ syncId: randomUUID(), ...host }) .returning(); await this.afterWrite(); return rows[0]; @@ -34,7 +35,11 @@ export class HostRepository { ): Promise { const userDataKey = DataCrypto.validateUserAccess(userId); const tempId = host.id ?? Date.now(); - const dataWithTempId = { ...host, id: tempId }; + const dataWithTempId = { + syncId: randomUUID(), + ...host, + id: tempId, + }; const encryptedHost = DataCrypto.encryptRecord( "ssh_data", dataWithTempId, @@ -147,7 +152,7 @@ export class HostRepository { ): Promise { const rows = await this.context.drizzle .update(hosts) - .set(update) + .set({ ...update, updatedAt: sql`CURRENT_TIMESTAMP` }) .where(and(eq(hosts.id, hostId), eq(hosts.userId, userId))) .returning(); @@ -170,7 +175,7 @@ export class HostRepository { const rows = await this.context.drizzle .update(hosts) - .set(encryptedUpdate) + .set({ ...encryptedUpdate, updatedAt: sql`CURRENT_TIMESTAMP` }) .where(and(eq(hosts.id, hostId), eq(hosts.userId, userId))) .returning(); @@ -210,7 +215,7 @@ export class HostRepository { const rows = await this.context.drizzle .update(hosts) - .set(update) + .set({ ...update, updatedAt: sql`CURRENT_TIMESTAMP` }) .where(and(inArray(hosts.id, hostIds), eq(hosts.userId, userId))) .returning({ id: hosts.id }); @@ -221,16 +226,19 @@ export class HostRepository { return rows.length; } - async deleteForUser(userId: string, hostId: number): Promise { + async deleteForUser( + userId: string, + hostId: number, + ): Promise<{ syncId: string | null } | null> { await this.deleteAccessForHost(hostId); const rows = await this.context.drizzle .delete(hosts) .where(and(eq(hosts.id, hostId), eq(hosts.userId, userId))) - .returning({ id: hosts.id }); + .returning({ syncId: hosts.syncId }); await this.afterWrite(); - return rows.length > 0; + return rows[0] ?? null; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/repositories/host-resolution-repository.ts b/src/backend/database/repositories/host-resolution-repository.ts index 61288d58..31926f0b 100644 --- a/src/backend/database/repositories/host-resolution-repository.ts +++ b/src/backend/database/repositories/host-resolution-repository.ts @@ -1,5 +1,5 @@ import { and, eq, inArray, isNotNull } from "drizzle-orm"; -import { hostAccess, hosts, sshCredentials } from "../db/schema.js"; +import { hostAccess, hosts, sshCredentials, sshFolders } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; import { DataCrypto } from "../../utils/data-crypto.js"; @@ -315,6 +315,34 @@ export class HostResolutionRepository { return rows[0]?.overrideCredentialId ?? null; } + /** + * Resolve the nearest assigned credential for a folder path, walking up + * through parent folders (e.g. "Switches / Floor1" falls back to + * "Switches" if the child folder has no credential of its own). + */ + async findFolderCredentialId( + userId: string, + folderPath: string, + ): Promise { + const segments = folderPath.split(" / ").filter(Boolean); + if (segments.length === 0) return null; + + const paths = segments.map((_, i) => segments.slice(0, i + 1).join(" / ")); + const rows = await this.context.drizzle + .select({ name: sshFolders.name, credentialId: sshFolders.credentialId }) + .from(sshFolders) + .where( + and(eq(sshFolders.userId, userId), inArray(sshFolders.name, paths)), + ); + + const byName = new Map(rows.map((row) => [row.name, row.credentialId])); + for (let i = paths.length - 1; i >= 0; i--) { + const credentialId = byName.get(paths[i]); + if (credentialId) return credentialId; + } + return null; + } + private decryptOne>( tableName: "ssh_data" | "ssh_credentials", record: T | undefined, diff --git a/src/backend/database/repositories/session-recording-repository.ts b/src/backend/database/repositories/session-recording-repository.ts index 678a24a1..fa3efc10 100644 --- a/src/backend/database/repositories/session-recording-repository.ts +++ b/src/backend/database/repositories/session-recording-repository.ts @@ -58,7 +58,12 @@ export class SessionRecordingRepository { async updateEnded( id: number, - input: { endedAt: string; duration: number | null }, + input: { + endedAt: string; + duration: number | null; + terminatedByOwner?: boolean; + terminationReason?: string; + }, ): Promise { await this.context.drizzle .update(sessionRecordings) diff --git a/src/backend/database/repositories/session-share-repository.ts b/src/backend/database/repositories/session-share-repository.ts new file mode 100644 index 00000000..016c44f1 --- /dev/null +++ b/src/backend/database/repositories/session-share-repository.ts @@ -0,0 +1,247 @@ +import { and, eq, gt, isNull, lt } from "drizzle-orm"; +import { + hosts, + sessionShareParticipants, + sessionShares, + users, +} from "../db/schema.js"; +import type { DatabaseContext } from "./database-context.js"; + +export type SessionShareRecord = typeof sessionShares.$inferSelect; +export type SessionShareParticipantRecord = + typeof sessionShareParticipants.$inferSelect; + +export type SessionShareType = "link" | "user"; +export type SessionSharePermissionLevel = "read-only" | "read-write"; + +export interface SessionShareCreateInput { + id: string; + hostId: number; + ownerUserId: string; + protocol: string; + sessionId: string; + tabInstanceId?: string | null; + shareType: SessionShareType; + targetUserId?: string | null; + linkToken?: string | null; + permissionLevel: SessionSharePermissionLevel; + expiresAt: string; +} + +export interface SessionShareWithHost extends SessionShareRecord { + hostName: string | null; + ownerUsername: string | null; +} + +export interface SharedWithMeRecord extends SessionShareRecord { + hostName: string | null; + ownerUsername: string | null; +} + +function activeShareFilter(now: string) { + return and(isNull(sessionShares.revokedAt), gt(sessionShares.expiresAt, now)); +} + +export class SessionShareRepository { + constructor( + private readonly context: DatabaseContext, + private readonly onWrite?: () => void | Promise, + ) {} + + async create(input: SessionShareCreateInput): Promise { + const [created] = await this.context.drizzle + .insert(sessionShares) + .values({ + id: input.id, + hostId: input.hostId, + ownerUserId: input.ownerUserId, + protocol: input.protocol, + sessionId: input.sessionId, + tabInstanceId: input.tabInstanceId ?? null, + shareType: input.shareType, + targetUserId: input.targetUserId ?? null, + linkToken: input.linkToken ?? null, + permissionLevel: input.permissionLevel, + expiresAt: input.expiresAt, + }) + .returning(); + + await this.afterWrite(); + return created; + } + + async findById(id: string): Promise { + const rows = await this.context.drizzle + .select() + .from(sessionShares) + .where(eq(sessionShares.id, id)) + .limit(1); + return rows[0] ?? null; + } + + async findActiveById( + id: string, + now = new Date().toISOString(), + ): Promise { + const rows = await this.context.drizzle + .select() + .from(sessionShares) + .where(and(eq(sessionShares.id, id), activeShareFilter(now))) + .limit(1); + return rows[0] ?? null; + } + + async findByLinkToken( + linkToken: string, + now = new Date().toISOString(), + ): Promise { + const rows = await this.context.drizzle + .select() + .from(sessionShares) + .where( + and(eq(sessionShares.linkToken, linkToken), activeShareFilter(now)), + ) + .limit(1); + return rows[0] ?? null; + } + + async findActiveSharesForHost( + hostId: number, + ownerUserId: string, + now = new Date().toISOString(), + ): Promise { + return this.context.drizzle + .select() + .from(sessionShares) + .where( + and( + eq(sessionShares.hostId, hostId), + eq(sessionShares.ownerUserId, ownerUserId), + activeShareFilter(now), + ), + ); + } + + async findSharesTargetingUser( + userId: string, + now = new Date().toISOString(), + ): Promise { + const rows = await this.context.drizzle + .select({ + share: sessionShares, + hostName: hosts.name, + ownerUsername: users.username, + }) + .from(sessionShares) + .leftJoin(hosts, eq(sessionShares.hostId, hosts.id)) + .leftJoin(users, eq(sessionShares.ownerUserId, users.id)) + .where( + and( + eq(sessionShares.shareType, "user"), + eq(sessionShares.targetUserId, userId), + activeShareFilter(now), + ), + ); + + return rows.map((row) => ({ + ...row.share, + hostName: row.hostName, + ownerUsername: row.ownerUsername, + })); + } + + async revoke(shareId: string, requestingUserId: string): Promise { + const rows = await this.context.drizzle + .update(sessionShares) + .set({ revokedAt: new Date().toISOString() }) + .where( + and( + eq(sessionShares.id, shareId), + eq(sessionShares.ownerUserId, requestingUserId), + ), + ) + .returning({ id: sessionShares.id }); + + if (rows.length > 0) { + await this.afterWrite(); + } + return rows.length > 0; + } + + async revokeAsAdmin(shareId: string): Promise { + const rows = await this.context.drizzle + .update(sessionShares) + .set({ revokedAt: new Date().toISOString() }) + .where(eq(sessionShares.id, shareId)) + .returning({ id: sessionShares.id }); + + if (rows.length > 0) { + await this.afterWrite(); + } + return rows.length > 0; + } + + async deleteExpiredShares(now = new Date().toISOString()): Promise { + const rows = await this.context.drizzle + .delete(sessionShares) + .where(lt(sessionShares.expiresAt, now)) + .returning({ id: sessionShares.id }); + + if (rows.length > 0) { + await this.afterWrite(); + } + return rows.length; + } + + async touchShareUsage( + shareId: string, + lastJoinedAt = new Date().toISOString(), + ): Promise { + const current = await this.findById(shareId); + await this.context.drizzle + .update(sessionShares) + .set({ + lastJoinedAt, + joinCount: (current?.joinCount ?? 0) + 1, + }) + .where(eq(sessionShares.id, shareId)); + await this.afterWrite(); + } + + async recordParticipantJoin( + shareId: string, + userId: string | null, + guestLabel: string | null, + ): Promise { + const [created] = await this.context.drizzle + .insert(sessionShareParticipants) + .values({ shareId, userId, guestLabel }) + .returning(); + await this.afterWrite(); + return created; + } + + async recordParticipantLeave(participantId: number): Promise { + await this.context.drizzle + .update(sessionShareParticipants) + .set({ leftAt: new Date().toISOString() }) + .where(eq(sessionShareParticipants.id, participantId)); + await this.afterWrite(); + } + + async deleteSharesForHost(hostId: number): Promise { + const rows = await this.context.drizzle + .delete(sessionShares) + .where(eq(sessionShares.hostId, hostId)) + .returning({ id: sessionShares.id }); + + if (rows.length > 0) { + await this.afterWrite(); + } + return rows.length; + } + + private async afterWrite(): Promise { + await this.onWrite?.(); + } +} diff --git a/src/backend/database/repositories/snippet-repository.ts b/src/backend/database/repositories/snippet-repository.ts index 5196ff38..60e6bf17 100644 --- a/src/backend/database/repositories/snippet-repository.ts +++ b/src/backend/database/repositories/snippet-repository.ts @@ -1,4 +1,5 @@ import { and, asc, eq, sql } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { snippetFolders, snippets } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; @@ -151,6 +152,7 @@ export class SnippetRepository { const rows = await this.context.drizzle .insert(snippets) .values({ + syncId: randomUUID(), userId, name: input.name.trim(), content: input.content.trim(), @@ -343,6 +345,7 @@ export class SnippetRepository { const maxOrder = await this.maxOrderForFolder(userId, folderVal); await this.context.drizzle.insert(snippets).values({ + syncId: randomUUID(), userId, name: snippet.name.trim(), content: snippet.content.trim(), @@ -377,6 +380,7 @@ export class SnippetRepository { const rows = await this.context.drizzle .insert(snippetFolders) .values({ + syncId: randomUUID(), userId, name: name.trim(), color: color?.trim() || null, @@ -452,19 +456,24 @@ export class SnippetRepository { return { status: "renamed" }; } - async deleteFolder(userId: string, name: string): Promise { + async deleteFolder( + userId: string, + name: string, + ): Promise<{ syncId: string | null } | null> { await this.context.drizzle .update(snippets) .set({ folder: null }) .where(and(eq(snippets.userId, userId), eq(snippets.folder, name))); - await this.context.drizzle + const rows = await this.context.drizzle .delete(snippetFolders) .where( and(eq(snippetFolders.userId, userId), eq(snippetFolders.name, name)), - ); + ) + .returning({ syncId: snippetFolders.syncId }); await this.afterWrite(); + return rows[0] ?? null; } private async findFolderByName( diff --git a/src/backend/database/repositories/sync-tombstone-repository.ts b/src/backend/database/repositories/sync-tombstone-repository.ts new file mode 100644 index 00000000..1fe4953f --- /dev/null +++ b/src/backend/database/repositories/sync-tombstone-repository.ts @@ -0,0 +1,70 @@ +import { and, eq, gt } from "drizzle-orm"; +import { syncTombstones } from "../db/schema.js"; +import type { DatabaseContext } from "./database-context.js"; + +export type SyncTombstoneRecord = typeof syncTombstones.$inferSelect; + +export type SyncEntityType = + | "hosts" + | "sshCredentials" + | "sshFolders" + | "snippets" + | "snippetFolders" + | "vaultProfiles" + | "dashboardServiceLinks" + | "homepageItems"; + +export class SyncTombstoneRepository { + constructor( + private readonly context: DatabaseContext, + private readonly onWrite?: () => void | Promise, + ) {} + + async record( + userId: string, + entityType: SyncEntityType, + syncId: string, + ): Promise { + if (!syncId) return; + await this.context.drizzle.insert(syncTombstones).values({ + userId, + entityType, + syncId, + }); + await this.afterWrite(); + } + + async recordMany( + userId: string, + entityType: SyncEntityType, + syncIds: string[], + ): Promise { + const rows = syncIds.filter(Boolean); + if (rows.length === 0) return; + await this.context.drizzle + .insert(syncTombstones) + .values(rows.map((syncId) => ({ userId, entityType, syncId }))); + await this.afterWrite(); + } + + async listSince( + userId: string, + entityType: SyncEntityType, + since: string | null, + ): Promise { + const conditions = [ + eq(syncTombstones.userId, userId), + eq(syncTombstones.entityType, entityType), + ]; + if (since) conditions.push(gt(syncTombstones.deletedAt, since)); + + return this.context.drizzle + .select() + .from(syncTombstones) + .where(and(...conditions)); + } + + private async afterWrite(): Promise { + await this.onWrite?.(); + } +} diff --git a/src/backend/database/repositories/vault-profile-repository.ts b/src/backend/database/repositories/vault-profile-repository.ts index 319a7b4d..64ff3706 100644 --- a/src/backend/database/repositories/vault-profile-repository.ts +++ b/src/backend/database/repositories/vault-profile-repository.ts @@ -1,4 +1,5 @@ import { desc, eq, or } from "drizzle-orm"; +import { randomUUID } from "crypto"; import { vaultProfiles } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; @@ -47,6 +48,7 @@ export class VaultProfileRepository { const [created] = await this.context.drizzle .insert(vaultProfiles) .values({ + syncId: randomUUID(), userId: input.userId, name: input.name, description: input.description, @@ -98,17 +100,15 @@ export class VaultProfileRepository { return updated ?? null; } - async deleteById(id: number): Promise { + async deleteById(id: number): Promise<{ syncId: string | null } | null> { const rows = await this.context.drizzle .delete(vaultProfiles) .where(eq(vaultProfiles.id, id)) - .returning({ id: vaultProfiles.id }); + .returning({ syncId: vaultProfiles.syncId }); - if (rows.length > 0) { - await this.afterWrite(); - } - - return rows.length > 0; + if (rows.length === 0) return null; + await this.afterWrite(); + return rows[0]; } async deleteByUserId(userId: string): Promise { diff --git a/src/backend/database/routes/acme-ssl-routes.ts b/src/backend/database/routes/acme-ssl-routes.ts index 317a08a9..5f3fa681 100644 --- a/src/backend/database/routes/acme-ssl-routes.ts +++ b/src/backend/database/routes/acme-ssl-routes.ts @@ -28,7 +28,7 @@ export type AcmeSettings = { enabled: boolean; domain: string; email: string; - challengeType: "http-webroot" | "dns-cloudflare"; + challengeType: "http-webroot" | "dns-cloudflare" | "manual"; cloudflareToken: string; lastIssuedAt: string | null; certStatus: "none" | "valid" | "expiring" | "expired"; @@ -166,7 +166,7 @@ export function registerAcmeSSLRoutes( * type: string * challengeType: * type: string - * enum: [http-webroot, dns-cloudflare] + * enum: [http-webroot, dns-cloudflare, manual] * cloudflareToken: * type: string * responses: @@ -414,4 +414,159 @@ export function registerAcmeSSLRoutes( res.status(500).json({ error: `Certificate request failed: ${message}` }); } }); + + /** + * @openapi + * /users/manual-ssl-upload: + * post: + * summary: Upload a manual/custom SSL certificate and key (admin only) + * description: Validates and installs a user-supplied PEM certificate and private key as the active Termix SSL certificate. + * tags: + * - Users + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * certificate: + * type: string + * privateKey: + * type: string + * responses: + * 200: + * description: Certificate uploaded and installed successfully. + * 400: + * description: Invalid or missing certificate/key. + * 403: + * description: Not authorized. + * 500: + * description: Certificate installation failed. + */ + router.post("/manual-ssl-upload", authenticateJWT, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + const actor = await getAdminActor(userId); + try { + if (!actor) { + return res.status(403).json({ error: "Not authorized" }); + } + + const { certificate, privateKey } = req.body; + + if ( + typeof certificate !== "string" || + typeof privateKey !== "string" || + !certificate.includes("BEGIN CERTIFICATE") || + !privateKey.includes("PRIVATE KEY") + ) { + return res.status(400).json({ + error: "A valid PEM certificate and private key are required", + }); + } + + await fs.mkdir(SSL_DIR, { recursive: true }); + + const tmpCertFile = path.join(SSL_DIR, ".manual-upload.crt.tmp"); + const tmpKeyFile = path.join(SSL_DIR, ".manual-upload.key.tmp"); + + try { + await fs.writeFile(tmpCertFile, certificate, { mode: 0o644 }); + await fs.writeFile(tmpKeyFile, privateKey, { mode: 0o600 }); + + try { + execFileSync("openssl", ["x509", "-in", tmpCertFile, "-noout"], { + stdio: "pipe", + }); + execFileSync( + "openssl", + ["pkey", "-in", tmpKeyFile, "-noout", "-check"], + { stdio: "pipe" }, + ); + } catch { + return res.status(400).json({ + error: + "The provided certificate or private key is not valid PEM data", + }); + } + + const certPubkey = execFileSync( + "openssl", + ["x509", "-in", tmpCertFile, "-noout", "-pubkey"], + { stdio: "pipe" }, + ); + const keyPubkey = execFileSync( + "openssl", + ["pkey", "-in", tmpKeyFile, "-pubout"], + { stdio: "pipe" }, + ); + + if (!certPubkey.equals(keyPubkey)) { + return res + .status(400) + .json({ error: "The certificate and private key do not match" }); + } + + const certDest = path.join(SSL_DIR, "termix.crt"); + const keyDest = path.join(SSL_DIR, "termix.key"); + await fs.rename(tmpCertFile, certDest); + await fs.rename(tmpKeyFile, keyDest); + await fs.chmod(keyDest, 0o600); + await fs.chmod(certDest, 0o644); + } finally { + await fs.rm(tmpCertFile, { force: true }); + await fs.rm(tmpKeyFile, { force: true }); + } + + const settingsRepository = createCurrentSettingsRepository(); + const existing = await settingsRepository.get("acme_ssl_settings"); + const current = existing ? JSON.parse(existing) : {}; + const updated = { + ...current, + challengeType: "manual", + lastIssuedAt: new Date().toISOString(), + }; + await settingsRepository.set( + "acme_ssl_settings", + JSON.stringify(updated), + ); + + authLogger.info("Manual SSL certificate installed", { + operation: "manual_ssl_installed", + }); + + const { ipAddress, userAgent } = getRequestMeta(req); + await logAudit({ + userId, + username: actor.username ?? userId, + action: "manual_ssl_upload", + resourceType: "setting", + details: JSON.stringify({ success: true }), + ipAddress, + userAgent, + success: true, + }); + + res.json({ success: true, ...(await getAcmeSettings()) }); + } catch (err) { + const message = err instanceof Error ? err.message : "Unknown error"; + authLogger.error("Manual SSL certificate upload failed", err); + + const { ipAddress, userAgent } = getRequestMeta(req); + await logAudit({ + userId, + username: actor?.username ?? userId, + action: "manual_ssl_upload", + resourceType: "setting", + details: JSON.stringify({ error: message }), + ipAddress, + userAgent, + success: false, + }); + + res + .status(500) + .json({ error: `Certificate installation failed: ${message}` }); + } + }); } diff --git a/src/backend/database/routes/credentials.ts b/src/backend/database/routes/credentials.ts index a467426a..bd5d49b3 100644 --- a/src/backend/database/routes/credentials.ts +++ b/src/backend/database/routes/credentials.ts @@ -12,6 +12,7 @@ import { createCurrentHostResolutionRepository, createCurrentHostRepository, createCurrentUserRepository, + createCurrentSyncTombstoneRepository, } from "../repositories/factory.js"; const router = express.Router(); @@ -642,6 +643,13 @@ router.delete( userId, credentialId, ); + if (credentialToDelete.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "sshCredentials", + credentialToDelete.syncId, + ); + } // Shares stay in place; re-snapshot so recipients fall back to whatever // auth the host still has (or lose the stale credential copy). diff --git a/src/backend/database/routes/dashboard-service-links-routes.ts b/src/backend/database/routes/dashboard-service-links-routes.ts index 34829356..9e0c2123 100644 --- a/src/backend/database/routes/dashboard-service-links-routes.ts +++ b/src/backend/database/routes/dashboard-service-links-routes.ts @@ -4,7 +4,10 @@ import { dashboardLogger } from "../../utils/logger.js"; import { DatabaseSaveTrigger } from "../../utils/database-save-trigger.js"; import { isNonEmptyString } from "./host-normalizers.js"; import express from "express"; -import { createCurrentDashboardServiceLinkRepository } from "../repositories/factory.js"; +import { + createCurrentDashboardServiceLinkRepository, + createCurrentSyncTombstoneRepository, +} from "../repositories/factory.js"; export const dashboardServiceLinksRouter = express.Router(); @@ -152,10 +155,18 @@ dashboardServiceLinksRouter.delete( return res.status(404).json({ error: "Not found" }); } - await createCurrentDashboardServiceLinkRepository().deleteForUser( - userId, - id, - ); + const deleted = + await createCurrentDashboardServiceLinkRepository().deleteForUser( + userId, + id, + ); + if (deleted?.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "dashboardServiceLinks", + deleted.syncId, + ); + } DatabaseSaveTrigger.triggerSave("dashboard_service_link_deleted"); res.json({ message: "Service link deleted" }); diff --git a/src/backend/database/routes/desktop-auto-session.ts b/src/backend/database/routes/desktop-auto-session.ts new file mode 100644 index 00000000..e6f54160 --- /dev/null +++ b/src/backend/database/routes/desktop-auto-session.ts @@ -0,0 +1,54 @@ +import type { Request } from "express"; +import type { UserRecord } from "../repositories/user-repository.js"; + +export function isLoopbackRequest(req: Request): boolean { + const ip = req.ip || req.socket?.remoteAddress || ""; + return ( + ip === "127.0.0.1" || + ip === "::1" || + ip === "::ffff:127.0.0.1" || + ip.endsWith(":127.0.0.1") + ); +} + +export function extractBearerOrCookieToken(req: Request): string | undefined { + const cookieToken = (req as Request & { cookies?: Record }) + .cookies?.jwt; + if (cookieToken) return cookieToken; + + const authHeader = req.headers["authorization"]; + if (authHeader?.startsWith("Bearer ")) { + return authHeader.slice("Bearer ".length); + } + return undefined; +} + +/** + * Decides who the desktop auto-session endpoint should silently log in as. + * + * The local embedded backend's trust boundary is machine access (loopback), + * not any individual account's credentials -- anyone who can reach loopback + * already has full filesystem access to the local, encrypted-at-rest + * database and its keys. A login form must never appear for the local + * backend, under any circumstance, including a local database that ended + * up with more than one user (e.g. from repeated manual registration + * during testing, or a household sharing one install) -- a user in that + * state deserves to get into the app they installed, not a confusing, + * unexplained dead end. So this always returns a single, deterministic + * user: the admin account if one exists, else the earliest-registered + * account. It never returns null. + */ +export function resolveDesktopAutoSessionUser( + allUsers: UserRecord[], +): UserRecord | null { + if (allUsers.length === 0) return null; + if (allUsers.length === 1) return allUsers[0]; + + const admin = allUsers.find((user) => user.isAdmin); + if (admin) return admin; + + return [...allUsers].sort( + (a, b) => + new Date(a.registeredAt).getTime() - new Date(b.registeredAt).getTime(), + )[0]; +} diff --git a/src/backend/database/routes/homepage-items-routes.ts b/src/backend/database/routes/homepage-items-routes.ts index 5ea49791..e953e63a 100644 --- a/src/backend/database/routes/homepage-items-routes.ts +++ b/src/backend/database/routes/homepage-items-routes.ts @@ -1,7 +1,10 @@ import type { AuthenticatedRequest } from "../../../types/index.js"; import type { Request, Response } from "express"; import { homepageLogger } from "../../utils/logger.js"; -import { createCurrentHomepageItemRepository } from "../repositories/factory.js"; +import { + createCurrentHomepageItemRepository, + createCurrentSyncTombstoneRepository, +} from "../repositories/factory.js"; import express from "express"; export const homepageItemsRouter = express.Router(); @@ -184,7 +187,14 @@ homepageItemsRouter.delete("/:id", async (req: Request, res: Response) => { return res.status(404).json({ error: "Not found" }); } - await itemRepository.deleteForUser(userId, id); + const deleted = await itemRepository.deleteForUser(userId, id); + if (deleted?.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "homepageItems", + deleted.syncId, + ); + } res.json({ message: "Homepage item deleted" }); } catch (err) { homepageLogger.error("Failed to delete homepage item", err); diff --git a/src/backend/database/routes/host-folder-routes.ts b/src/backend/database/routes/host-folder-routes.ts index dfde1491..933fda99 100644 --- a/src/backend/database/routes/host-folder-routes.ts +++ b/src/backend/database/routes/host-folder-routes.ts @@ -3,6 +3,7 @@ import type { AuthenticatedRequest } from "../../../types/index.js"; import { databaseLogger, sshLogger } from "../../utils/logger.js"; import { createCurrentCommandHistoryRepository, + createCurrentCredentialRepository, createCurrentFileManagerBookmarkRepository, createCurrentHostFolderRepository, createCurrentRecentActivityRepository, @@ -10,6 +11,7 @@ import { createCurrentSshCredentialUsageRepository, createCurrentSessionRecordingRepository, createCurrentTransferRecentRepository, + createCurrentSyncTombstoneRepository, } from "../repositories/factory.js"; import { isNonEmptyString } from "./host-normalizers.js"; @@ -138,7 +140,7 @@ export function registerHostFolderRoutes( * /host/folders/metadata: * put: * summary: Update folder metadata - * description: Updates the metadata (color, icon) of a folder. + * description: Updates the metadata (color, icon, assigned credential) of a folder. * tags: * - SSH * requestBody: @@ -154,6 +156,9 @@ export function registerHostFolderRoutes( * type: string * icon: * type: string + * credentialId: + * type: integer + * nullable: true * responses: * 200: * description: Folder metadata updated successfully. @@ -167,19 +172,46 @@ export function registerHostFolderRoutes( authenticateJWT, async (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; - const { name, color, icon } = req.body; + const { name, color, icon, credentialId } = req.body; if (!isNonEmptyString(userId) || !name) { return res.status(400).json({ error: "Folder name is required" }); } + const normalizedCredentialId = + credentialId === undefined + ? undefined + : credentialId === null || credentialId === "" + ? null + : Number(credentialId); + + if ( + normalizedCredentialId !== undefined && + normalizedCredentialId !== null && + !Number.isInteger(normalizedCredentialId) + ) { + return res.status(400).json({ error: "Invalid credential ID" }); + } + try { + if (normalizedCredentialId) { + const credential = + await createCurrentCredentialRepository().findByIdForUser( + userId, + normalizedCredentialId, + ); + if (!credential) { + return res.status(404).json({ error: "Credential not found" }); + } + } + const { folder, created } = await createCurrentHostFolderRepository().upsertMetadata( userId, name, color, icon, + normalizedCredentialId, ); if (!created) { @@ -287,9 +319,17 @@ export function registerHostFolderRoutes( ); } - await hostFolderRepository.deleteHostsAndFolderRecords( + const { hostSyncIds, folderSyncIds } = + await hostFolderRepository.deleteHostsAndFolderRecords( + userId, + folderName, + ); + const tombstoneRepository = createCurrentSyncTombstoneRepository(); + await tombstoneRepository.recordMany(userId, "hosts", hostSyncIds); + await tombstoneRepository.recordMany( userId, - folderName, + "sshFolders", + folderSyncIds, ); try { diff --git a/src/backend/database/routes/host-normalizers.ts b/src/backend/database/routes/host-normalizers.ts index fd406e54..9c17ac07 100644 --- a/src/backend/database/routes/host-normalizers.ts +++ b/src/backend/database/routes/host-normalizers.ts @@ -225,6 +225,9 @@ export function stripSensitiveFields( result.hasKeyPassword = !!host.keyPassword; result.hasPassword = !!host.password; result.hasSudoPassword = !!host.sudoPassword; + result.hasRdpPassword = !!host.rdpPassword; + result.hasVncPassword = !!host.vncPassword; + result.hasTelnetPassword = !!host.telnetPassword; for (const field of SENSITIVE_FIELDS) { delete result[field]; } diff --git a/src/backend/database/routes/host.ts b/src/backend/database/routes/host.ts index e4a80ddd..c216c3f1 100644 --- a/src/backend/database/routes/host.ts +++ b/src/backend/database/routes/host.ts @@ -14,6 +14,7 @@ import { } from "../../hosts/credential-username.js"; import { createCurrentCommandHistoryRepository, + createCurrentCredentialRepository, createCurrentFileManagerBookmarkRepository, createCurrentOpksshTokenRepository, createCurrentRecentActivityRepository, @@ -25,6 +26,7 @@ import { createCurrentHostResolutionRepository, createCurrentHostRepository, createCurrentUserRepository, + createCurrentSyncTombstoneRepository, } from "../repositories/factory.js"; import { isNonEmptyString, @@ -174,6 +176,7 @@ router.post( enableDocker, enableProxmox, enableTmuxMonitor, + allowSessionSharing, showTerminalInSidebar, showFileManagerInSidebar, showTunnelInSidebar, @@ -199,6 +202,7 @@ router.post( socks5Username, socks5Password, socks5ProxyChain, + connectionOrigin, portKnockSequence, overrideCredentialUsername, macAddress, @@ -287,6 +291,7 @@ router.post( enableDocker: enableDocker ? 1 : 0, enableProxmox: enableProxmox ? 1 : 0, enableTmuxMonitor: enableTmuxMonitor ? 1 : 0, + allowSessionSharing: allowSessionSharing === false ? 0 : 1, showTerminalInSidebar: showTerminalInSidebar ? 1 : 0, showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0, showTunnelInSidebar: showTunnelInSidebar ? 1 : 0, @@ -328,6 +333,10 @@ router.post( socks5ProxyChain: socks5ProxyChain ? JSON.stringify(socks5ProxyChain) : null, + connectionOrigin: + connectionOrigin === "local" || connectionOrigin === "remote" + ? connectionOrigin + : null, macAddress: macAddress || null, wolBroadcastAddress: wolBroadcastAddress || null, portKnockSequence: portKnockSequence @@ -814,6 +823,7 @@ router.put( enableDocker, enableProxmox, enableTmuxMonitor, + allowSessionSharing, showTerminalInSidebar, showFileManagerInSidebar, showTunnelInSidebar, @@ -839,6 +849,7 @@ router.put( socks5Username, socks5Password, socks5ProxyChain, + connectionOrigin, portKnockSequence, overrideCredentialUsername, macAddress, @@ -924,6 +935,7 @@ router.put( enableDocker: enableDocker ? 1 : 0, enableProxmox: enableProxmox ? 1 : 0, enableTmuxMonitor: enableTmuxMonitor ? 1 : 0, + allowSessionSharing: allowSessionSharing === false ? 0 : 1, showTerminalInSidebar: showTerminalInSidebar ? 1 : 0, showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0, showTunnelInSidebar: showTunnelInSidebar ? 1 : 0, @@ -965,6 +977,10 @@ router.put( socks5ProxyChain: socks5ProxyChain ? JSON.stringify(socks5ProxyChain) : null, + connectionOrigin: + connectionOrigin === "local" || connectionOrigin === "remote" + ? connectionOrigin + : null, macAddress: macAddress || null, wolBroadcastAddress: wolBroadcastAddress || null, portKnockSequence: portKnockSequence @@ -1482,7 +1498,7 @@ router.get( * name: field * schema: * type: string - * enum: [password, sudoPassword, vncPassword] + * enum: [password, sudoPassword, rdpPassword, vncPassword, telnetPassword, key, keyPassword] * responses: * 200: * description: The requested password value. @@ -1498,7 +1514,17 @@ router.get( const userId = (req as AuthenticatedRequest).userId; const field = (req.query.field as string) || "password"; - if (!["password", "sudoPassword", "vncPassword"].includes(field)) { + if ( + ![ + "password", + "sudoPassword", + "rdpPassword", + "vncPassword", + "telnetPassword", + "key", + "keyPassword", + ].includes(field) + ) { return res.status(400).json({ error: "Invalid field" }); } @@ -1726,9 +1752,16 @@ router.get( * /host/db/hosts/export: * get: * summary: Export all SSH hosts - * description: Exports all SSH hosts for the current user with decrypted credentials. + * description: Exports all SSH hosts for the current user. By default credentials are decrypted and embedded. With `share=1`, secrets are omitted and credential-authenticated hosts instead reference a scrubbed `credentials` array by alias, suitable for handing off to another user. * tags: * - SSH + * parameters: + * - in: query + * name: share + * required: false + * schema: + * type: string + * description: Set to "1" to export without embedded secrets. * responses: * 200: * description: All exported SSH hosts. @@ -1743,6 +1776,7 @@ router.get( requireDataAccess, async (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; + const shareMode = req.query.share === "1" || req.query.share === "true"; if (!isNonEmptyString(userId)) { return res.status(400).json({ error: "Invalid userId" }); @@ -1753,10 +1787,12 @@ router.get( await createCurrentHostResolutionRepository().findHostsByUserId(userId); const exportedHosts = []; + const usedCredentialIds = new Set(); for (const host of allHosts) { - const resolvedHost = - (await resolveHostCredentials(host, userId)) || host; + const resolvedHost = shareMode + ? host + : (await resolveHostCredentials(host, userId)) || host; const exportedConnectionType = (resolvedHost.connectionType as string) || "ssh"; @@ -1770,7 +1806,7 @@ router.get( ip: resolvedHost.ip, port: resolvedHost.port, username: resolvedHost.username, - password: resolvedHost.password || null, + password: shareMode ? null : resolvedHost.password || null, folder: resolvedHost.folder, tags: typeof resolvedHost.tags === "string" @@ -1793,8 +1829,8 @@ router.get( : { ...baseExportData, authType: resolvedHost.authType, - key: resolvedHost.key || null, - keyPassword: resolvedHost.keyPassword || null, + key: shareMode ? null : resolvedHost.key || null, + keyPassword: shareMode ? null : resolvedHost.keyPassword || null, keyType: resolvedHost.keyType || null, credentialId: resolvedHost.credentialId || null, overrideCredentialUsername: @@ -1811,7 +1847,9 @@ router.get( showDockerInSidebar: !!resolvedHost.showDockerInSidebar, showServerStatsInSidebar: !!resolvedHost.showServerStatsInSidebar, defaultPath: resolvedHost.defaultPath, - sudoPassword: resolvedHost.sudoPassword || null, + sudoPassword: shareMode + ? null + : resolvedHost.sudoPassword || null, tunnelConnections: resolvedHost.tunnelConnections ? JSON.parse(resolvedHost.tunnelConnections as string) : [], @@ -1839,22 +1877,92 @@ router.get( socks5Host: resolvedHost.socks5Host || null, socks5Port: resolvedHost.socks5Port || null, socks5Username: resolvedHost.socks5Username || null, - socks5Password: resolvedHost.socks5Password || null, + socks5Password: shareMode + ? null + : resolvedHost.socks5Password || null, socks5ProxyChain: resolvedHost.socks5ProxyChain ? JSON.parse(resolvedHost.socks5ProxyChain as string) : null, }; + if ( + shareMode && + !isRemoteDesktop && + resolvedHost.authType === "credential" && + resolvedHost.credentialId + ) { + usedCredentialIds.add(resolvedHost.credentialId as number); + } + exportedHosts.push(exportData); } - sshLogger.success("All hosts exported with decrypted credentials", { - operation: "hosts_export_all", + if (!shareMode) { + sshLogger.success("All hosts exported with decrypted credentials", { + operation: "hosts_export_all", + count: exportedHosts.length, + userId, + }); + + return res.json({ hosts: exportedHosts }); + } + + const exportedCredentials: Record[] = []; + if (usedCredentialIds.size > 0) { + const credentialRepository = createCurrentCredentialRepository(); + const ownedCredentials = + await credentialRepository.listDecryptedByUserId(userId); + const credentialById = new Map( + ownedCredentials.map((credential) => [credential.id, credential]), + ); + + for (const host of exportedHosts as Record[]) { + const credentialId = host.credentialId as number | null; + if (!credentialId) continue; + const credential = credentialById.get(credentialId); + if (!credential) continue; + + host.credentialAlias = credential.name; + + if ( + !exportedCredentials.some( + (entry) => entry.alias === credential.name, + ) + ) { + exportedCredentials.push({ + alias: credential.name, + name: credential.name, + description: credential.description || null, + folder: credential.folder || null, + tags: + typeof credential.tags === "string" + ? credential.tags.split(",").filter(Boolean) + : [], + authType: credential.authType, + username: credential.username || null, + keyType: credential.keyType || null, + }); + } + } + } + + for (const host of exportedHosts as Record[]) { + delete host.credentialId; + } + + sshLogger.success("All hosts exported for sharing without secrets", { + operation: "hosts_export_all_share", count: exportedHosts.length, + credentialCount: exportedCredentials.length, userId, }); - res.json({ hosts: exportedHosts }); + res.json({ + version: "1", + exportedAt: new Date().toISOString(), + credentials: exportedCredentials, + hosts: exportedHosts, + }); } catch (err) { sshLogger.error("Failed to export all SSH hosts", err, { operation: "hosts_export_all", @@ -1959,6 +2067,13 @@ router.delete( ); await createCurrentHostRepository().deleteForUser(userId, numericHostId); + if (hostToDelete.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "hosts", + hostToDelete.syncId, + ); + } databaseLogger.success("SSH host deleted", { operation: "host_delete_success", diff --git a/src/backend/database/routes/keybinding-validation.ts b/src/backend/database/routes/keybinding-validation.ts new file mode 100644 index 00000000..1890c328 --- /dev/null +++ b/src/backend/database/routes/keybinding-validation.ts @@ -0,0 +1,49 @@ +const VALID_ACTION_TYPES = [ + "copy", + "paste", + "sendControlCode", + "sendText", + "runSnippet", +]; + +export function isValidKeyCombo(combo: unknown): boolean { + return ( + !!combo && + typeof combo === "object" && + typeof (combo as { key?: unknown }).key === "string" && + typeof (combo as { isCode?: unknown }).isCode === "boolean" && + typeof (combo as { ctrl?: unknown }).ctrl === "boolean" && + typeof (combo as { alt?: unknown }).alt === "boolean" && + typeof (combo as { shift?: unknown }).shift === "boolean" && + typeof (combo as { meta?: unknown }).meta === "boolean" + ); +} + +export function isValidKeybindingAction(action: unknown): boolean { + if (!action || typeof action !== "object") return false; + const type = (action as { type?: unknown }).type; + if (typeof type !== "string" || !VALID_ACTION_TYPES.includes(type)) + return false; + if (type === "sendText") { + return typeof (action as { text?: unknown }).text === "string"; + } + if (type === "sendControlCode") { + const code = (action as { controlCode?: unknown }).controlCode; + return typeof code === "string" && /^[a-zA-Z]$/.test(code); + } + if (type === "runSnippet") { + return typeof (action as { snippetId?: unknown }).snippetId === "string"; + } + return true; +} + +export function isValidKeybinding(entry: unknown): boolean { + return ( + !!entry && + typeof entry === "object" && + typeof (entry as { id?: unknown }).id === "string" && + typeof (entry as { enabled?: unknown }).enabled === "boolean" && + isValidKeyCombo((entry as { combo?: unknown }).combo) && + isValidKeybindingAction((entry as { action?: unknown }).action) + ); +} diff --git a/src/backend/database/routes/open-tabs.ts b/src/backend/database/routes/open-tabs.ts index 6ea7c22e..0247a47a 100644 --- a/src/backend/database/routes/open-tabs.ts +++ b/src/backend/database/routes/open-tabs.ts @@ -7,6 +7,7 @@ import { sessionManager } from "../../hosts/terminal/session-manager.js"; import { getCurrentSettingValue, createCurrentOpenTabRepository, + createCurrentSessionShareRepository, } from "../repositories/factory.js"; const router = express.Router(); @@ -277,12 +278,15 @@ router.delete("/:id", authenticateJWT, async (req: Request, res: Response) => { * /open-tabs/active-sessions: * get: * summary: Get all active backend sessions for the current user - * description: Returns live terminal sessions from the session manager. Used by the Active Connections panel and tab restore logic. + * description: > + * Returns live terminal sessions from the session manager, both sessions the + * caller owns and SSH sessions shared to the caller by another user (via + * an in-app session share). Used by the Active Connections panel and tab restore logic. * tags: * - Open Tabs * responses: * 200: - * description: List of active sessions. + * description: List of active sessions (own and shared-with-me). * content: * application/json: * schema: @@ -302,6 +306,17 @@ router.delete("/:id", authenticateJWT, async (req: Request, res: Response) => { * type: boolean * createdAt: * type: number + * isOwnSession: + * type: boolean + * sharedByUsername: + * type: string + * nullable: true + * permissionLevel: + * type: string + * nullable: true + * shareId: + * type: string + * nullable: true */ router.get( "/active-sessions", @@ -309,17 +324,46 @@ router.get( async (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; try { - const sessions = sessionManager.getUserSessions(userId); - return res.json( - sessions.map((s) => ({ - sessionId: s.id, - hostId: s.hostId, - hostName: s.hostName, - tabInstanceId: s.attachedTabInstanceId ?? s.tabInstanceId ?? null, - isConnected: s.isConnected, - createdAt: s.createdAt, - })), - ); + const ownSessions = sessionManager.getUserSessions(userId); + const result = ownSessions.map((s) => ({ + sessionId: s.id, + hostId: s.hostId, + hostName: s.hostName, + tabInstanceId: s.attachedTabInstanceId ?? s.tabInstanceId ?? null, + isConnected: s.isConnected, + createdAt: s.createdAt, + isOwnSession: true, + sharedByUsername: null as string | null, + permissionLevel: null as string | null, + shareId: null as string | null, + })); + + const sharedWithMe = + await createCurrentSessionShareRepository().findSharesTargetingUser( + userId, + ); + for (const share of sharedWithMe) { + if (share.protocol !== "ssh") continue; + const sharedSession = sessionManager.getSession(share.sessionId); + if (!sharedSession || !sharedSession.isConnected) continue; + result.push({ + sessionId: sharedSession.id, + hostId: sharedSession.hostId, + hostName: sharedSession.hostName, + tabInstanceId: + sharedSession.attachedTabInstanceId ?? + sharedSession.tabInstanceId ?? + null, + isConnected: sharedSession.isConnected, + createdAt: sharedSession.createdAt, + isOwnSession: false, + sharedByUsername: share.ownerUsername, + permissionLevel: share.permissionLevel, + shareId: share.id, + }); + } + + return res.json(result); } catch (e) { databaseLogger.error("Failed to get active sessions", e, { operation: "get_active_sessions", diff --git a/src/backend/database/routes/proxmox.ts b/src/backend/database/routes/proxmox.ts index c3a4b8fa..553c1b20 100644 --- a/src/backend/database/routes/proxmox.ts +++ b/src/backend/database/routes/proxmox.ts @@ -488,16 +488,51 @@ async function discoverProxmoxGuestsForHost( async function resolveIp(g: GuestBase): Promise { if (g.type === "lxc") { + let configIp: string | null = null; 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), config.preferredPrefixes); + configIp = parseLxcIp(JSON.parse(cfgJson), config.preferredPrefixes); } catch { - return null; + configIp = null; } + if (configIp) return configIp; + // Static config parsing found nothing (e.g. net0 uses ip=dhcp). + // Fall back to the live interface list for running containers. + if (g.status === "running") { + try { + const ifRaw = await execCommand( + client, + `pvesh get /nodes/${g.node}/lxc/${g.vmid}/interfaces --output-format json 2>/dev/null`, + 5000, + ); + const data = JSON.parse(ifRaw); + const entries: Array> = Array.isArray(data) + ? data + : []; + const allIps: string[] = []; + for (const entry of entries) { + if (entry.name === "lo") continue; + const inet = entry.inet; + if (typeof inet !== "string") continue; + const m = inet.match(/^(\d{1,3}(?:\.\d{1,3}){3})\/\d+$/); + if (m && !m[1].startsWith("127.")) allIps.push(m[1]); + } + if (allIps.length) { + for (const prefix of config.preferredPrefixes) { + const match = allIps.find((ip) => ip.startsWith(prefix)); + if (match) return match; + } + return allIps[0]; + } + } catch { + // Guest not running or interfaces unavailable + } + } + return null; } if (g.type === "qemu" && g.status === "running") { try { diff --git a/src/backend/database/routes/rbac.ts b/src/backend/database/routes/rbac.ts index 06fbb12e..8be290c4 100644 --- a/src/backend/database/routes/rbac.ts +++ b/src/backend/database/routes/rbac.ts @@ -14,6 +14,7 @@ import { } from "../../utils/permission-catalog.js"; import { createCurrentCredentialRepository, + createCurrentHostFolderRepository, createCurrentHostResolutionRepository, createCurrentRbacAccessRepository, createCurrentRoleRepository, @@ -311,6 +312,225 @@ router.post( }, ); +/** + * @openapi + * /rbac/folder/share: + * post: + * summary: Share all hosts in a folder + * description: Shares every host within a folder (and its subfolders) with one or more users and/or roles at a permission level. Only hosts owned by the caller are shared; skips hosts the caller may not share. + * tags: + * - RBAC + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [folder, targets] + * properties: + * folder: + * type: string + * targets: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * enum: [user, role] + * id: + * oneOf: + * - type: string + * - type: integer + * permissionLevel: + * type: string + * enum: [connect, view, edit, manage] + * durationHours: + * type: number + * responses: + * 200: + * description: Folder shared successfully. + * 400: + * description: Invalid request body. + * 404: + * description: Folder has no hosts. + * 500: + * description: Failed to share folder. + */ +router.post( + "/folder/share", + authenticateJWT, + async (req: AuthenticatedRequest, res: Response) => { + const userId = req.userId!; + const { folder } = req.body ?? {}; + + if (!isNonEmptyString(folder)) { + return res.status(400).json({ error: "Folder name is required" }); + } + + try { + const targets = parseShareTargets(req.body ?? {}); + if (!targets) { + return res.status(400).json({ + error: + "targets must be a non-empty array of { type: 'user'|'role', id } entries", + }); + } + + const { durationHours, permissionLevel = "connect" } = req.body; + + if (!isSharePermissionLevel(permissionLevel)) { + return res.status(400).json({ + error: "Invalid permission level", + validLevels: SHARE_PERMISSION_LEVELS, + }); + } + + const userRepository = createCurrentUserRepository(); + const roleRepository = createCurrentRoleRepository(); + for (const target of targets) { + if (target.type === "user") { + const targetUser = await userRepository.findById(target.id as string); + if (!targetUser) { + return res.status(404).json({ + error: "Target user not found", + targetId: target.id, + }); + } + } else { + const targetRole = await roleRepository.findRoleById( + target.id as number, + ); + if (!targetRole) { + return res.status(404).json({ + error: "Target role not found", + targetId: target.id, + }); + } + } + } + + const hostsInFolder = + await createCurrentHostFolderRepository().listHostsInFolder( + userId, + folder, + ); + if (hostsInFolder.length === 0) { + return res.status(404).json({ error: "Folder has no hosts" }); + } + + const expiresAt = expiryFromDuration(durationHours); + const rbacAccessRepository = createCurrentRbacAccessRepository(); + const { SharedHostSecretsManager } = + await import("../../utils/shared-host-secrets-manager.js"); + const secretsManager = SharedHostSecretsManager.getInstance(); + + const hostResults: Array<{ + hostId: number; + shared: boolean; + reason?: string; + }> = []; + + for (const host of hostsInFolder) { + if (targets.some((t) => t.type === "user" && t.id === host.userId)) { + hostResults.push({ + hostId: host.id, + shared: false, + reason: "owner", + }); + continue; + } + + const sharing = await canManageHostSharing(userId, host.id); + if (!sharing.allowed) { + hostResults.push({ + hostId: host.id, + shared: false, + reason: "forbidden", + }); + continue; + } + + for (const target of targets) { + const accessGrant = await rbacAccessRepository.upsertHostAccess({ + hostId: host.id, + grantedBy: userId, + permissionLevel, + expiresAt, + ...(target.type === "user" + ? { + targetType: "user" as const, + targetUserId: target.id as string, + } + : { + targetType: "role" as const, + targetRoleId: target.id as number, + }), + }); + + try { + if (target.type === "user") { + await secretsManager.snapshotForUser( + accessGrant.id, + host.id, + target.id as string, + host.userId, + ); + } else { + await secretsManager.snapshotForRole( + accessGrant.id, + host.id, + target.id as number, + host.userId, + ); + } + } catch (snapshotError) { + databaseLogger.warn("Share created but secret snapshot failed", { + operation: "rbac_folder_share_snapshot_failed", + hostId: host.id, + accessId: accessGrant.id, + error: + snapshotError instanceof Error + ? snapshotError.message + : "Unknown error", + }); + } + } + + hostResults.push({ hostId: host.id, shared: true }); + } + + const sharedCount = hostResults.filter((r) => r.shared).length; + + databaseLogger.success("Folder shared successfully", { + operation: "rbac_folder_share_success", + userId, + folder, + hostsShared: sharedCount, + targets: targets.length, + permissionLevel, + }); + + res.json({ + success: true, + message: "Folder shared successfully", + permissionLevel, + expiresAt, + hostsShared: sharedCount, + hostsTotal: hostsInFolder.length, + hostResults, + }); + } catch (error) { + databaseLogger.error("Failed to share folder", error, { + operation: "share_folder", + folder, + userId, + }); + res.status(500).json({ error: "Failed to share folder" }); + } + }, +); + /** * @openapi * /rbac/host/{id}/access/{accessId}: diff --git a/src/backend/database/routes/snippets.ts b/src/backend/database/routes/snippets.ts index 2fbc4068..0680ebc7 100644 --- a/src/backend/database/routes/snippets.ts +++ b/src/backend/database/routes/snippets.ts @@ -12,6 +12,7 @@ import { createCurrentRoleRepository, createCurrentSnippetRepository, createCurrentUserRepository, + createCurrentSyncTombstoneRepository, } from "../repositories/factory.js"; const router = express.Router(); @@ -400,7 +401,17 @@ router.delete( try { const folderName = decodeURIComponent(name); - await createCurrentSnippetRepository().deleteFolder(userId, folderName); + const deletedFolder = await createCurrentSnippetRepository().deleteFolder( + userId, + folderName, + ); + if (deletedFolder?.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "snippetFolders", + deletedFolder.syncId, + ); + } authLogger.success( `Snippet folder deleted: ${folderName} by user ${userId}`, @@ -1241,6 +1252,14 @@ router.delete( return res.status(404).json({ error: "Snippet not found" }); } + if (existing.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "snippets", + existing.syncId, + ); + } + databaseLogger.info("Command snippet deleted", { operation: "snippet_delete", userId, diff --git a/src/backend/database/routes/sync.ts b/src/backend/database/routes/sync.ts new file mode 100644 index 00000000..3f1198c4 --- /dev/null +++ b/src/backend/database/routes/sync.ts @@ -0,0 +1,415 @@ +import type { Request, Response } from "express"; +import express from "express"; +import { and, eq, gt } from "drizzle-orm"; +import { + hosts, + sshCredentials, + sshFolders, + snippets, + snippetFolders, + vaultProfiles, + dashboardServiceLinks, + homepageItems, +} from "../db/schema.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import { DataCrypto } from "../../utils/data-crypto.js"; +import { databaseLogger } from "../../utils/logger.js"; +import { DatabaseSaveTrigger } from "../../utils/database-save-trigger.js"; +import type { AuthenticatedRequest } from "../../../types/index.js"; +import { + createCurrentRepositoryContext, + createCurrentSyncTombstoneRepository, +} from "../repositories/factory.js"; +import type { SyncEntityType } from "../repositories/sync-tombstone-repository.js"; + +const router = express.Router(); +const authManager = AuthManager.getInstance(); +const authenticateJWT = authManager.createAuthMiddleware(); + +// Encrypted tables need DataCrypto to translate between the wire payload +// (plaintext) and the stored row (encrypted). Everything else is stored +// and synced as-is. +const ENCRYPTED_ENTITY_TABLES: Partial> = { + hosts: "ssh_data", + sshCredentials: "ssh_credentials", +}; + +interface EntityConfig { + table: + | typeof hosts + | typeof sshCredentials + | typeof sshFolders + | typeof snippets + | typeof snippetFolders + | typeof vaultProfiles + | typeof dashboardServiceLinks + | typeof homepageItems; + // Fields that only make sense on the device that created the row, or + // that are managed elsewhere and must never be overwritten by a sync + // payload from the other side. + readOnlyFields: string[]; +} + +const ENTITY_CONFIG: Record = { + hosts: { + table: hosts, + readOnlyFields: ["connectionOrigin"], + }, + sshCredentials: { table: sshCredentials, readOnlyFields: [] }, + sshFolders: { table: sshFolders, readOnlyFields: [] }, + snippets: { table: snippets, readOnlyFields: [] }, + snippetFolders: { table: snippetFolders, readOnlyFields: [] }, + vaultProfiles: { table: vaultProfiles, readOnlyFields: [] }, + dashboardServiceLinks: { table: dashboardServiceLinks, readOnlyFields: [] }, + homepageItems: { table: homepageItems, readOnlyFields: [] }, +}; + +const VALID_ENTITY_TYPES = new Set(Object.keys(ENTITY_CONFIG)); + +export function isValidEntityType(value: unknown): value is SyncEntityType { + return typeof value === "string" && VALID_ENTITY_TYPES.has(value); +} + +function requireUserDataKey(userId: string): Buffer { + return DataCrypto.validateUserAccess(userId); +} + +function decryptIfNeeded( + entityType: SyncEntityType, + row: Record, + userId: string, +): Record { + const tableName = ENCRYPTED_ENTITY_TABLES[entityType]; + if (!tableName) return row; + const userDataKey = DataCrypto.getUserDataKey(userId); + if (!userDataKey) return row; + return DataCrypto.decryptRecord( + tableName, + row, + userId, + userDataKey, + ) as Record; +} + +function encryptIfNeeded( + entityType: SyncEntityType, + row: Record, + userId: string, +): Record { + const tableName = ENCRYPTED_ENTITY_TABLES[entityType]; + if (!tableName) return row; + const userDataKey = requireUserDataKey(userId); + return DataCrypto.encryptRecord( + tableName, + row, + userId, + userDataKey, + ) as Record; +} + +export function stripWritePayload( + entityType: SyncEntityType, + payload: Record, +): Record { + const { readOnlyFields } = ENTITY_CONFIG[entityType]; + const clean = { ...payload }; + delete clean.id; + delete clean.userId; + delete clean.syncId; + for (const field of readOnlyFields) delete clean[field]; + return clean; +} + +/** + * @openapi + * /sync/{entityType}: + * get: + * summary: Pull synced rows for an entity type + * description: Returns rows owned by the authenticated user whose updatedAt is newer than `since` (or all rows if omitted). Used by the desktop app's remote sync engine to reconcile the embedded backend against a connected remote server. + * tags: + * - Sync + * parameters: + * - in: path + * name: entityType + * required: true + * schema: + * type: string + * - in: query + * name: since + * schema: + * type: string + * responses: + * 200: + * description: Rows updated since the given timestamp. + * 400: + * description: Unknown entity type. + * 500: + * description: Failed to fetch rows. + */ +router.get( + "/:entityType", + authenticateJWT, + async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const entityType = req.params.entityType; + if (!isValidEntityType(entityType)) { + return res.status(400).json({ error: "Unknown entity type" }); + } + const since = + typeof req.query.since === "string" && req.query.since + ? req.query.since + : null; + + try { + const { table } = ENTITY_CONFIG[entityType]; + const context = createCurrentRepositoryContext(); + const conditions = [eq(table.userId, userId)]; + if (since && "updatedAt" in table) { + conditions.push(gt((table as typeof hosts).updatedAt, since)); + } + + const rows = await context.drizzle + .select() + .from(table as typeof hosts) + .where(and(...conditions)); + + const decrypted = rows.map((row) => + decryptIfNeeded(entityType, row as Record, userId), + ); + + res.json({ rows: decrypted }); + } catch (err) { + databaseLogger.error(`Failed to pull sync rows for ${entityType}`, err, { + operation: "sync_pull", + entityType, + userId, + }); + res.status(500).json({ error: "Failed to fetch rows" }); + } + }, +); + +/** + * @openapi + * /sync/{entityType}: + * post: + * summary: Upsert a synced row by syncId + * description: Creates or updates a row by its syncId. Used by the desktop app's remote sync engine to push local-only or newer rows to the other side of a sync pair. + * tags: + * - Sync + * parameters: + * - in: path + * name: entityType + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Row upserted. + * 400: + * description: Unknown entity type or missing syncId. + * 500: + * description: Failed to upsert row. + */ +router.post( + "/:entityType", + authenticateJWT, + async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const entityType = req.params.entityType; + if (!isValidEntityType(entityType)) { + return res.status(400).json({ error: "Unknown entity type" }); + } + const payload = req.body?.row; + const syncId = payload?.syncId; + if (!payload || typeof syncId !== "string" || !syncId) { + return res.status(400).json({ error: "Missing row.syncId" }); + } + + try { + const { table } = ENTITY_CONFIG[entityType]; + const context = createCurrentRepositoryContext(); + + const existingRows = await context.drizzle + .select() + .from(table as typeof hosts) + .where( + and( + eq((table as typeof hosts).syncId, syncId), + eq(table.userId, userId), + ), + ) + .limit(1); + const existing = existingRows[0] as Record | undefined; + + const writePayload = stripWritePayload(entityType, payload); + const encryptedPayload = encryptIfNeeded( + entityType, + writePayload, + userId, + ); + + let resultRow: Record; + if (existing) { + const updatedRows = await context.drizzle + .update(table as typeof hosts) + .set(encryptedPayload) + .where( + and( + eq((table as typeof hosts).id, existing.id as number), + eq(table.userId, userId), + ), + ) + .returning(); + resultRow = updatedRows[0] as Record; + } else { + const insertedRows = await context.drizzle + .insert(table as typeof hosts) + .values({ + ...encryptedPayload, + userId, + syncId, + } as typeof hosts.$inferInsert) + .returning(); + resultRow = insertedRows[0] as Record; + } + + await DatabaseSaveTrigger.forceSave("sync_upsert"); + + res.json({ + row: decryptIfNeeded(entityType, resultRow, userId), + created: !existing, + }); + } catch (err) { + databaseLogger.error(`Failed to upsert sync row for ${entityType}`, err, { + operation: "sync_upsert", + entityType, + userId, + }); + res.status(500).json({ error: "Failed to upsert row" }); + } + }, +); + +/** + * @openapi + * /sync/{entityType}/tombstones: + * get: + * summary: Pull deletion tombstones for an entity type + * description: Returns tombstones recorded since `since` so the other side of a sync pair can apply the same deletions. + * tags: + * - Sync + * parameters: + * - in: path + * name: entityType + * required: true + * schema: + * type: string + * - in: query + * name: since + * schema: + * type: string + * responses: + * 200: + * description: Tombstones recorded since the given timestamp. + * 400: + * description: Unknown entity type. + * 500: + * description: Failed to fetch tombstones. + */ +router.get( + "/:entityType/tombstones", + authenticateJWT, + async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const entityType = req.params.entityType; + if (!isValidEntityType(entityType)) { + return res.status(400).json({ error: "Unknown entity type" }); + } + const since = + typeof req.query.since === "string" && req.query.since + ? req.query.since + : null; + + try { + const tombstones = await createCurrentSyncTombstoneRepository().listSince( + userId, + entityType, + since, + ); + res.json({ tombstones }); + } catch (err) { + databaseLogger.error( + `Failed to fetch sync tombstones for ${entityType}`, + err, + { operation: "sync_tombstones_pull", entityType, userId }, + ); + res.status(500).json({ error: "Failed to fetch tombstones" }); + } + }, +); + +/** + * @openapi + * /sync/tombstones: + * post: + * summary: Report a deletion from the other side of a sync pair + * description: Applies a remote deletion locally (if the row still exists) and records the tombstone so future pulls stay consistent. + * tags: + * - Sync + * responses: + * 200: + * description: Deletion applied (or row already absent). + * 400: + * description: Unknown entity type or missing syncId. + * 500: + * description: Failed to apply deletion. + */ +router.post( + "/tombstones", + authenticateJWT, + async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const entityType = req.body?.entityType; + const syncId = req.body?.syncId; + if ( + !isValidEntityType(entityType) || + typeof syncId !== "string" || + !syncId + ) { + return res.status(400).json({ error: "Missing entityType or syncId" }); + } + + try { + const { table } = ENTITY_CONFIG[entityType]; + const context = createCurrentRepositoryContext(); + + await context.drizzle + .delete(table as typeof hosts) + .where( + and( + eq((table as typeof hosts).syncId, syncId), + eq(table.userId, userId), + ), + ); + + await createCurrentSyncTombstoneRepository().record( + userId, + entityType, + syncId, + ); + await DatabaseSaveTrigger.forceSave("sync_tombstone_applied"); + + res.json({ success: true }); + } catch (err) { + databaseLogger.error("Failed to apply sync tombstone", err, { + operation: "sync_tombstone_apply", + entityType, + userId, + }); + res.status(500).json({ error: "Failed to apply deletion" }); + } + }, +); + +export default router; diff --git a/src/backend/database/routes/user-preferences.ts b/src/backend/database/routes/user-preferences.ts index 87b5ae73..498b92b3 100644 --- a/src/backend/database/routes/user-preferences.ts +++ b/src/backend/database/routes/user-preferences.ts @@ -8,6 +8,7 @@ import type { UserPreferenceRecord, UserPreferenceUpdate, } from "../repositories/user-preference-repository.js"; +import { isValidKeybinding } from "./keybinding-validation.js"; const router = express.Router(); const authManager = AuthManager.getInstance(); @@ -33,6 +34,8 @@ const pickPreferences = (row?: UserPreferenceRecord | null) => ({ hiddenRailTabs: row?.hiddenRailTabs ?? null, compactHostView: row?.compactHostView ?? null, statusColorScheme: row?.statusColorScheme ?? null, + customThemes: row?.customThemes ?? null, + customKeybindings: row?.customKeybindings ?? null, }); /** @@ -106,6 +109,14 @@ const pickPreferences = (row?: UserPreferenceRecord | null) => ({ * statusColorScheme: * type: string * nullable: true + * customThemes: + * type: string + * nullable: true + * description: JSON-encoded array of the user's saved global custom terminal themes. + * customKeybindings: + * type: string + * nullable: true + * description: JSON-encoded array of the user's custom terminal keybindings. */ router.get("/", authenticateJWT, async (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; @@ -175,6 +186,12 @@ router.get("/", authenticateJWT, async (req: Request, res: Response) => { * type: boolean * statusColorScheme: * type: string + * customThemes: + * type: string + * description: JSON-encoded array of the user's saved global custom terminal themes. + * customKeybindings: + * type: string + * description: JSON-encoded array of the user's custom terminal keybindings. * responses: * 200: * description: Preferences updated successfully. @@ -201,6 +218,8 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => { hiddenRailTabs, compactHostView, statusColorScheme, + customThemes, + customKeybindings, } = req.body as { reopenTabsOnLogin?: boolean; theme?: string | null; @@ -221,6 +240,8 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => { hiddenRailTabs?: string | null; compactHostView?: boolean | null; statusColorScheme?: string | null; + customThemes?: string | null; + customKeybindings?: string | null; }; const updates: UserPreferenceUpdate = { @@ -244,12 +265,64 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => { storageMode, hiddenRailTabs, statusColorScheme, + customThemes, + customKeybindings, })) { if (value !== undefined && value !== null && typeof value !== "string") { return res.status(400).json({ error: `${key} must be a string` }); } } + if (customThemes !== undefined && customThemes !== null) { + let parsedThemes: unknown; + try { + parsedThemes = JSON.parse(customThemes); + } catch { + return res + .status(400) + .json({ error: "customThemes must be a JSON-encoded array" }); + } + if (!Array.isArray(parsedThemes) || parsedThemes.length > 100) { + return res.status(400).json({ + error: "customThemes must be a JSON array of at most 100 themes", + }); + } + const isValidTheme = (entry: unknown): boolean => + !!entry && + typeof entry === "object" && + typeof (entry as { id?: unknown }).id === "string" && + typeof (entry as { name?: unknown }).name === "string" && + !!(entry as { colors?: unknown }).colors && + typeof (entry as { colors?: unknown }).colors === "object"; + if (!parsedThemes.every(isValidTheme)) { + return res.status(400).json({ + error: "Each custom theme must have an id, name, and colors object", + }); + } + } + + if (customKeybindings !== undefined && customKeybindings !== null) { + let parsedKeybindings: unknown; + try { + parsedKeybindings = JSON.parse(customKeybindings); + } catch { + return res + .status(400) + .json({ error: "customKeybindings must be a JSON-encoded array" }); + } + if (!Array.isArray(parsedKeybindings) || parsedKeybindings.length > 200) { + return res.status(400).json({ + error: "customKeybindings must be a JSON array of at most 200 bindings", + }); + } + if (!parsedKeybindings.every(isValidKeybinding)) { + return res.status(400).json({ + error: + "Each custom keybinding must have an id, enabled flag, valid combo, and valid action", + }); + } + } + const boolFields: Record = { commandAutocomplete, commandPaletteEnabled, @@ -294,6 +367,9 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => { if (compactHostView !== undefined) updates.compactHostView = compactHostView; if (statusColorScheme !== undefined) updates.statusColorScheme = statusColorScheme; + if (customThemes !== undefined) updates.customThemes = customThemes; + if (customKeybindings !== undefined) + updates.customKeybindings = customKeybindings; if (Object.keys(updates).length === 1) { return res.status(400).json({ error: "No preferences provided" }); diff --git a/src/backend/database/routes/user-settings-routes.ts b/src/backend/database/routes/user-settings-routes.ts index 2c23c107..27819bff 100644 --- a/src/backend/database/routes/user-settings-routes.ts +++ b/src/backend/database/routes/user-settings-routes.ts @@ -519,6 +519,207 @@ export function registerUserSettingsRoutes( }, ); + /** + * @openapi + * /users/analytics-enabled: + * get: + * summary: Get analytics enabled setting + * description: Returns whether anonymous usage telemetry is enabled. + * tags: + * - Users + * responses: + * 200: + * description: Analytics enabled status. + * content: + * application/json: + * schema: + * type: object + * properties: + * enabled: + * type: boolean + */ + router.get("/analytics-enabled", authenticateJWT, async (_req, res) => { + try { + res.json({ + enabled: await createCurrentSettingsRepository().getBoolean( + "analytics_enabled", + true, + ), + }); + } catch (err) { + authLogger.error("Failed to get analytics enabled setting", err); + res + .status(500) + .json({ error: "Failed to get analytics enabled setting" }); + } + }); + + /** + * @openapi + * /users/analytics-enabled: + * patch: + * summary: Update analytics enabled setting (admin only) + * description: Enables or disables the daily anonymous usage telemetry heartbeat. + * 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("/analytics-enabled", authenticateJWT, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const actor = await getAdminActor(userId); + if (!actor) { + 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" }); + } + await createCurrentSettingsRepository().set( + "analytics_enabled", + enabled ? "true" : "false", + ); + + const { ipAddress, userAgent } = getRequestMeta(req); + await logAudit({ + userId, + username: actor.username ?? userId, + action: "update_analytics_enabled", + resourceType: "setting", + details: JSON.stringify({ enabled }), + ipAddress, + userAgent, + success: true, + }); + + res.json({ enabled }); + } catch (err) { + authLogger.error("Failed to update analytics enabled setting", err); + res + .status(500) + .json({ error: "Failed to update analytics enabled setting" }); + } + }); + + /** + * @openapi + * /users/session-sharing-enabled: + * get: + * summary: Get session sharing globally enabled setting + * description: Returns whether live session sharing (terminal/RDP/VNC/Telnet share links and in-app joins) is allowed instance-wide. Overrides every per-host toggle when false. + * tags: + * - Users + * responses: + * 200: + * description: Session sharing enabled status. + * content: + * application/json: + * schema: + * type: object + * properties: + * enabled: + * type: boolean + */ + router.get("/session-sharing-enabled", authenticateJWT, async (_req, res) => { + try { + res.json({ + enabled: await createCurrentSettingsRepository().getBoolean( + "session_sharing_globally_enabled", + true, + ), + }); + } catch (err) { + authLogger.error("Failed to get session sharing enabled setting", err); + res + .status(500) + .json({ error: "Failed to get session sharing enabled setting" }); + } + }); + + /** + * @openapi + * /users/session-sharing-enabled: + * patch: + * summary: Update session sharing globally enabled setting (admin only) + * description: Enables or disables live session sharing instance-wide, overriding every per-host allowSessionSharing toggle. + * 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( + "/session-sharing-enabled", + authenticateJWT, + async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const actor = await getAdminActor(userId); + if (!actor) { + 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" }); + } + await createCurrentSettingsRepository().set( + "session_sharing_globally_enabled", + enabled ? "true" : "false", + ); + + const { ipAddress, userAgent } = getRequestMeta(req); + await logAudit({ + userId, + username: actor.username ?? userId, + action: "update_session_sharing_enabled", + resourceType: "setting", + details: JSON.stringify({ enabled }), + ipAddress, + userAgent, + success: true, + }); + + res.json({ enabled }); + } catch (err) { + authLogger.error( + "Failed to update session sharing enabled setting", + err, + ); + res + .status(500) + .json({ error: "Failed to update session sharing enabled setting" }); + } + }, + ); + /** * @openapi * /users/host-defaults: diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index f58f34b8..51de8958 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -18,6 +18,11 @@ import { isOidcTokenCallback, } from "../../utils/oidc-desktop-callback.js"; import { deleteUserAndRelatedData } from "./delete-user-data.js"; +import { + isLoopbackRequest, + extractBearerOrCookieToken, + resolveDesktopAutoSessionUser, +} from "./desktop-auto-session.js"; import { shouldShowDonationModal } from "./donation-modal-utils.js"; import { getOIDCConfigFromEnv, @@ -1848,9 +1853,13 @@ router.post( * description: Not authenticated. */ router.get("/me/token", authenticateJWT, (req: Request, res: Response) => { - const token = (req as Request & { cookies: Record }).cookies - ?.jwt; - res.json({ token: token || null }); + // authenticateJWT accepts either the jwt cookie or an Authorization: + // Bearer header (see auth-manager.ts's createAuthMiddleware) -- this must + // check both too, or a request that only carried the header (e.g. the + // Electron renderer's own axios interceptor, which always attaches a + // stored localStorage JWT as a Bearer header) would pass authentication + // here but still get back a null token. + res.json({ token: extractBearerOrCookieToken(req) ?? null }); }); /** @@ -1880,6 +1889,80 @@ router.get("/setup-required", async (req, res) => { } }); +/** + * @openapi + * /users/internal/auto-session: + * post: + * summary: Mint a session for the sole local desktop user + * description: Used by the Electron desktop app to skip the login form entirely when running standalone against the embedded local backend. Only available over loopback. Logs in as the sole local user regardless of its credentials; if the local database has more than one user (e.g. repeated manual registration), deterministically logs in as the admin account, or the earliest-registered account if none is admin -- a login form must never appear for the local backend under any circumstance. Only declines if zero local users exist at all, which normal desktop provisioning never produces. Provisions the resolved user's data-encryption key if missing before minting the session, matching every other login path -- self-heals an account that previously ended up with a valid session but no usable encryption key. + * tags: + * - Users + * responses: + * 200: + * description: Session created. + * 403: + * description: Forbidden, or no local users exist. + * 500: + * description: Failed to create session. + */ +router.post("/internal/auto-session", async (req, res) => { + try { + if (!isLoopbackRequest(req)) { + authLogger.warn( + "Rejected non-loopback attempt to access auto-session endpoint", + { source: req.ip }, + ); + return res.status(403).json({ error: "Forbidden" }); + } + + const userRepository = createCurrentUserRepository(); + const allUsers = await userRepository.listAll(); + const userRecord = resolveDesktopAutoSessionUser(allUsers); + if (!userRecord) { + return res.status(403).json({ + error: "No local users exist", + }); + } + await authManager.registerUser(userRecord.id); + const existingToken = extractBearerOrCookieToken(req); + if (existingToken) { + const existingPayload = await authManager.verifyJWTToken(existingToken); + if (existingPayload?.userId === userRecord.id) { + return res.json({ + success: true, + is_admin: !!userRecord.isAdmin, + username: userRecord.username, + token: existingToken, + }); + } + } + + const token = await authManager.generateJWTToken(userRecord.id, { + deviceType: "desktop", + deviceInfo: "Termix Desktop (local)", + rememberMe: true, + }); + + const response = { + success: true, + is_admin: !!userRecord.isAdmin, + username: userRecord.username, + token, + }; + + return res + .cookie( + "jwt", + token, + authManager.getSecureCookieOptions(req, 30 * 24 * 60 * 60 * 1000), + ) + .json(response); + } catch (err) { + authLogger.error("Failed to create auto-session", err); + res.status(500).json({ error: "Failed to create auto-session" }); + } +}); + /** * @openapi * /users/count: diff --git a/src/backend/database/routes/vault.ts b/src/backend/database/routes/vault.ts index d70b3050..0616b73f 100644 --- a/src/backend/database/routes/vault.ts +++ b/src/backend/database/routes/vault.ts @@ -3,6 +3,7 @@ import type { Request, Response } from "express"; import { createCurrentVaultProfileRepository, createCurrentUserRepository, + createCurrentSyncTombstoneRepository, } from "../repositories/factory.js"; import type { VaultProfileUpdateInput } from "../repositories/vault-profile-repository.js"; import type { AuthenticatedRequest } from "../../../types/index.js"; @@ -421,7 +422,14 @@ router.delete( .status(403) .json({ error: "Only the owner can delete this profile" }); } - await repository.deleteById(id); + const deleted = await repository.deleteById(id); + if (deleted?.syncId) { + await createCurrentSyncTombstoneRepository().record( + userId, + "vaultProfiles", + deleted.syncId, + ); + } res.json({ success: true }); } catch (err) { authLogger.error("Failed to delete vault profile", err); diff --git a/src/backend/hosts/auth-manager.ts b/src/backend/hosts/auth-manager.ts index 9aceab71..a1dfe43a 100644 --- a/src/backend/hosts/auth-manager.ts +++ b/src/backend/hosts/auth-manager.ts @@ -117,6 +117,24 @@ export class SSHAuthManager { return; } + // JumpCloud Protect / DUO-style push MFA: a menu choice ("Choose [1] Push, + // or [2] TOTP:") followed by an empty-answerable confirm ("Press enter to + // send Push request:"). Checked before the TOTP regex because the menu + // prompt's own text ("...or [2] TOTP:") would otherwise match it and get + // misrouted into the numeric-code flow. + const pushPromptPattern = + /choose.*push.*totp|press enter.*(push|send)|push notification|authentication by phone/i; + const isPushPrompt = promptTexts.some((p) => pushPromptPattern.test(p)); + + if (isPushPrompt) { + sshLogger.info("Push/menu MFA prompt detected", { + operation: "ssh_keyboard_interactive_push", + hostId: this.context.hostId, + }); + this.handlePasswordAuth(prompts, finish, resolvedCredentials, hostConfig); + return; + } + const totpPromptIndex = prompts.findIndex((p) => /verification code|verification_code|token|otp|2fa|authenticator|google.*auth/i.test( p.prompt, @@ -313,6 +331,10 @@ export class SSHAuthManager { ? passwordPromptIndex : firstUnansweredIndex; + const pushPromptPattern = + /choose.*push.*totp|press enter.*(push|send)|push notification|authentication by phone/i; + const isPushPrompt = pushPromptPattern.test(prompts[promptIndex].prompt); + this.context.keyboardInteractiveFinish = (userResponses: string[]) => { const userInput = (userResponses[0] || "").trim(); @@ -333,22 +355,25 @@ export class SSHAuthManager { clearTimeout(this.context.totpTimeout); } - this.context.totpTimeout = setTimeout(() => { - if (this.context.keyboardInteractiveFinish) { - this.context.keyboardInteractiveFinish = null; - this.context.keyboardInteractiveResponded = false; - sshLogger.warn("Password prompt timeout", { - operation: "password_timeout", - hostId: this.context.hostId, - }); - this.context.ws.send( - JSON.stringify({ - type: "error", - message: "Password verification timeout. Please reconnect.", - }), - ); - } - }, 180000); + this.context.totpTimeout = setTimeout( + () => { + if (this.context.keyboardInteractiveFinish) { + this.context.keyboardInteractiveFinish = null; + this.context.keyboardInteractiveResponded = false; + sshLogger.warn("Password prompt timeout", { + operation: "password_timeout", + hostId: this.context.hostId, + }); + this.context.ws.send( + JSON.stringify({ + type: "error", + message: "Password verification timeout. Please reconnect.", + }), + ); + } + }, + isPushPrompt ? 300000 : 180000, + ); this.sendLog("auth", "info", "Password authentication required"); @@ -356,6 +381,7 @@ export class SSHAuthManager { JSON.stringify({ type: "password_required", prompt: prompts[promptIndex].prompt, + echo: prompts[promptIndex].echo, }), ); return; diff --git a/src/backend/hosts/docker/console.ts b/src/backend/hosts/docker/console.ts index 46a3c9bf..7fb1d003 100644 --- a/src/backend/hosts/docker/console.ts +++ b/src/backend/hosts/docker/console.ts @@ -32,6 +32,12 @@ const wss = new WebSocketServer({ port: 30009, }); +wss.on("error", (error) => { + sshLogger.error("Docker console WebSocket server error", error, { + operation: "wss_error", + }); +}); + async function detectShell( session: SSHSession, containerId: string, diff --git a/src/backend/hosts/docker/routes.ts b/src/backend/hosts/docker/routes.ts index 2cebf272..68e7d741 100644 --- a/src/backend/hosts/docker/routes.ts +++ b/src/backend/hosts/docker/routes.ts @@ -280,6 +280,7 @@ export function registerDockerSshRoutes(app: express.Express): void { if (userProvidedPassword) { resolvedCredentials.password = userProvidedPassword; + resolvedCredentials.authType = "password"; } if (userProvidedSshKey) { resolvedCredentials.sshKey = userProvidedSshKey; diff --git a/src/backend/hosts/guacamole/guacamole-server.ts b/src/backend/hosts/guacamole/guacamole-server.ts index 1d142e21..efc8654d 100644 --- a/src/backend/hosts/guacamole/guacamole-server.ts +++ b/src/backend/hosts/guacamole/guacamole-server.ts @@ -27,12 +27,64 @@ const GUACAMOLE_RECORDINGS_DIR = path.join(DATA_DIR, "session_recordings", "guacamole"); type GuacamoleClientConnection = { + guacamoleConnectionId?: string; connectionSettings?: { - connection?: { type?: string }; + connection?: { type?: string; join?: string; readOnly?: boolean }; recording?: GuacamoleRecordingMetadata; + termixMeta?: { + termixConnectId: string; + hostId: number; + ownerUserId: string; + protocol: string; + }; }; }; +export interface GuacSessionInfo { + guacamoleConnectionId: string; + hostId: number; + ownerUserId: string; + protocol: string; + openedAt: number; +} + +// Keyed by termixConnectId (routes.ts's correlation id), populated once the +// primary connection's guacd handshake completes. +const guacSessionByConnectId = new Map(); +// Keyed by guacd's own guacamoleConnectionId, for join-time lookups. +const guacSessionByGuacamoleId = new Map(); +const pendingConnectResolvers = new Map< + string, + (info: GuacSessionInfo | null) => void +>(); + +export function waitForGuacdOpen( + termixConnectId: string, + timeoutMs = 10000, +): Promise { + const existing = guacSessionByConnectId.get(termixConnectId); + if (existing) return Promise.resolve(existing); + + return new Promise((resolve) => { + let settled = false; + const finish = (info: GuacSessionInfo | null) => { + if (settled) return; + settled = true; + pendingConnectResolvers.delete(termixConnectId); + resolve(info); + }; + + pendingConnectResolvers.set(termixConnectId, finish); + setTimeout(() => finish(null), timeoutMs); + }); +} + +export function getGuacSessionInfo( + guacamoleConnectionId: string, +): GuacSessionInfo | null { + return guacSessionByGuacamoleId.get(guacamoleConnectionId) ?? null; +} + async function persistGuacamoleRecording( clientConnection: GuacamoleClientConnection, ): Promise { @@ -118,7 +170,6 @@ const clientOptions = { vnc: { "swap-red-blue": false, cursor: "remote", - security: "any", width: 1280, height: 720, }, @@ -149,6 +200,25 @@ function createGuacServer(): GuacamoleLite { operation: "guac_connection_open", type: clientConnection.connectionSettings?.connection?.type, }); + + const termixMeta = clientConnection.connectionSettings?.termixMeta; + const guacamoleConnectionId = clientConnection.guacamoleConnectionId; + const isJoin = !!clientConnection.connectionSettings?.connection?.join; + + if (!isJoin && termixMeta && guacamoleConnectionId) { + const info: GuacSessionInfo = { + guacamoleConnectionId, + hostId: termixMeta.hostId, + ownerUserId: termixMeta.ownerUserId, + protocol: termixMeta.protocol, + openedAt: Date.now(), + }; + guacSessionByConnectId.set(termixMeta.termixConnectId, info); + guacSessionByGuacamoleId.set(guacamoleConnectionId, info); + + const resolver = pendingConnectResolvers.get(termixMeta.termixConnectId); + if (resolver) resolver(info); + } }); server.on("close", (clientConnection: GuacamoleClientConnection) => { @@ -156,6 +226,15 @@ function createGuacServer(): GuacamoleLite { operation: "guac_connection_close", type: clientConnection.connectionSettings?.connection?.type, }); + + const isJoin = !!clientConnection.connectionSettings?.connection?.join; + const termixMeta = clientConnection.connectionSettings?.termixMeta; + const guacamoleConnectionId = clientConnection.guacamoleConnectionId; + if (!isJoin && termixMeta && guacamoleConnectionId) { + guacSessionByConnectId.delete(termixMeta.termixConnectId); + guacSessionByGuacamoleId.delete(guacamoleConnectionId); + } + persistGuacamoleRecording(clientConnection).catch((error) => { guacLogger.error("Failed to persist Guacamole recording", error, { operation: "guac_recording_persist_error", diff --git a/src/backend/hosts/guacamole/routes.ts b/src/backend/hosts/guacamole/routes.ts index 0e47c2f9..f5b07d5d 100644 --- a/src/backend/hosts/guacamole/routes.ts +++ b/src/backend/hosts/guacamole/routes.ts @@ -3,16 +3,18 @@ import { GuacamoleTokenService } from "./token-service.js"; import { guacLogger } from "../../utils/logger.js"; import { AuthManager } from "../../utils/auth-manager.js"; import { PermissionManager } from "../../utils/permission-manager.js"; -import { Client } from "ssh2"; import net from "net"; import crypto from "crypto"; import path from "path"; -import type { AuthenticatedRequest } from "../../../types/index.js"; +import type { AuthenticatedRequest, ProxyNode } from "../../../types/index.js"; import { createCurrentHostResolutionRepository, createCurrentSettingsRepository, } from "../../database/repositories/factory.js"; import { resolveGuacdOptions } from "../../utils/guacd-config.js"; +import { createJumpHostChain } from "../jump-host-chain.js"; +import type { SOCKS5Config } from "../../utils/socks5-helper.js"; +import { waitForGuacdOpen } from "./guacamole-server.js"; const router = express.Router(); const tokenService = GuacamoleTokenService.getInstance(); @@ -165,6 +167,12 @@ router.post("/token", async (req, res) => { * type: string * enum: [rdp, vnc, telnet] * description: Override the host's default connection type + * promptedUsername: + * type: string + * description: Username for this connection only, used when the host's RDP auth type is "none". Not persisted. + * promptedPassword: + * type: string + * description: Password for this connection only, used when the host's RDP auth type is "none". Not persisted. * responses: * 200: * description: Connection token generated successfully @@ -176,6 +184,10 @@ router.post("/token", async (req, res) => { * token: * type: string * description: Encrypted connection token + * guacamoleConnectionId: + * type: string + * nullable: true + * description: guacd's own connection id for this session, once the handshake completes. Used to mint session-share join tokens. * 400: * description: Invalid request or unsupported connection type * 403: @@ -421,12 +433,22 @@ router.post( let username: string; let password: string; + const rdpAuthTypeForConnect = isSharedConnection + ? null + : (host.rdpAuthType as string) || + (host.rdpCredentialId ? "credential" : "direct"); + switch (connectionType) { case "rdp": - username = - (host.rdpUser as string) || (host.username as string) || ""; - password = - (host.rdpPassword as string) || (host.password as string) || ""; + if (rdpAuthTypeForConnect === "none") { + username = String(req.body?.promptedUsername || ""); + password = String(req.body?.promptedPassword || ""); + } else { + username = + (host.rdpUser as string) || (host.username as string) || ""; + password = + (host.rdpPassword as string) || (host.password as string) || ""; + } port = (host.rdpPort as number) || port || 3389; break; case "vnc": @@ -463,65 +485,91 @@ router.post( if (jumpHosts.length > 0) { try { - const { resolveHostById } = await import("../host-resolver.js"); - const jumpHost = await resolveHostById(jumpHosts[0].hostId, userId); - if (jumpHost) { - const tunnelPort = await new Promise((resolve, reject) => { - const sshClient = new Client(); - sshClient.on("ready", () => { - const server = net.createServer((sock) => { - sshClient.forwardOut( - "127.0.0.1", - 0, - hostname, - port, - (err, stream) => { - if (err) { - sock.destroy(); - return; - } - sock.pipe(stream).pipe(sock); - }, - ); - }); - server.listen(0, "127.0.0.1", () => { - const addr = server.address() as net.AddressInfo; - // Auto-cleanup after 1 hour - setTimeout( - () => { - server.close(); - sshClient.end(); - }, - 60 * 60 * 1000, - ); - resolve(addr.port); - }); - }); - sshClient.on("error", reject); + let socks5ProxyChain: ProxyNode[] = []; + if (hostRecord.socks5ProxyChain) { + try { + socks5ProxyChain = + typeof hostRecord.socks5ProxyChain === "string" + ? JSON.parse(hostRecord.socks5ProxyChain as string) + : (hostRecord.socks5ProxyChain as ProxyNode[]); + } catch { + socks5ProxyChain = []; + } + } - const connectOpts: Record = { - host: jumpHost.ip, - port: jumpHost.port || 22, - username: jumpHost.username, - readyTimeout: 30000, - }; - if (jumpHost.key) { - connectOpts.privateKey = jumpHost.key; - if (jumpHost.keyPassword) - connectOpts.passphrase = jumpHost.keyPassword; - } else if (jumpHost.password) { - connectOpts.password = jumpHost.password; - } - sshClient.connect(connectOpts); - }); - hostname = "127.0.0.1"; - port = tunnelPort; - guacLogger.info("SSH tunnel established for guacamole", { - operation: "guac_ssh_tunnel", - hostId, - tunnelPort, + const proxyConfig: SOCKS5Config | null = + hostRecord.useSocks5 && + (hostRecord.socks5Host || socks5ProxyChain.length > 0) + ? { + useSocks5: hostRecord.useSocks5 as boolean, + socks5Host: hostRecord.socks5Host as string | undefined, + socks5Port: hostRecord.socks5Port as number | undefined, + socks5Username: hostRecord.socks5Username as + | string + | undefined, + socks5Password: hostRecord.socks5Password as + | string + | undefined, + socks5ProxyChain, + } + : null; + + const jumpClient = await createJumpHostChain( + jumpHosts, + userId, + proxyConfig, + ); + + if (!jumpClient) { + guacLogger.error( + "Failed to establish jump host chain for guacamole", + undefined, + { operation: "guac_ssh_tunnel_error", hostId }, + ); + return res.status(500).json({ + error: "Failed to establish SSH tunnel to remote host", }); } + + const targetHostname = hostname; + const targetPort = port; + const tunnelPort = await new Promise((resolve, reject) => { + const server = net.createServer((sock) => { + jumpClient.forwardOut( + "127.0.0.1", + 0, + targetHostname, + targetPort, + (err, stream) => { + if (err) { + sock.destroy(); + return; + } + sock.pipe(stream).pipe(sock); + }, + ); + }); + server.on("error", reject); + server.listen(0, "127.0.0.1", () => { + const addr = server.address() as net.AddressInfo; + // Auto-cleanup after 1 hour + setTimeout( + () => { + server.close(); + jumpClient.end(); + }, + 60 * 60 * 1000, + ); + resolve(addr.port); + }); + }); + hostname = "127.0.0.1"; + port = tunnelPort; + guacLogger.info("SSH tunnel established for guacamole", { + operation: "guac_ssh_tunnel", + hostId, + tunnelPort, + }); } catch (tunnelError) { guacLogger.error("Failed to establish SSH tunnel", tunnelError, { operation: "guac_ssh_tunnel_error", @@ -541,7 +589,8 @@ router.post( ? { guacdPort: perConnectionGuacdPort } : {}), }; - const recordingEnabled = host.enableSessionLogging !== false; + const recordingEnabled = + connectionType !== "vnc" && host.enableSessionLogging !== false; const recordingName = `${crypto.randomUUID()}.guac`; const recordingPath = process.env.GUACD_RECORDING_PATH || @@ -564,6 +613,14 @@ router.post( guacConfig["recording-include-keys"] = true; } + const termixConnectId = crypto.randomUUID(); + const termixMeta = { + termixConnectId, + hostId, + ownerUserId: userId, + protocol: connectionType as "rdp" | "vnc" | "telnet", + }; + switch (connectionType) { case "rdp": if (guacConfig["enable-drive"] && !guacConfig["drive-path"]) { @@ -591,6 +648,7 @@ router.post( ...guacdOverrides, }, recordingMetadata, + termixMeta, ); break; case "vnc": @@ -600,11 +658,11 @@ router.post( password, { port, - security: "any", ...guacConfig, ...guacdOverrides, }, recordingMetadata, + termixMeta, ); break; case "telnet": @@ -618,13 +676,19 @@ router.post( ...guacdOverrides, }, recordingMetadata, + termixMeta, ); break; default: return res.status(400).json({ error: "Invalid connection type" }); } - res.json({ token }); + const sessionInfo = await waitForGuacdOpen(termixConnectId, 10000); + + res.json({ + token, + guacamoleConnectionId: sessionInfo?.guacamoleConnectionId ?? null, + }); } catch (error) { guacLogger.error("Failed to generate guacamole token for host", error, { operation: "guac_host_token_error", diff --git a/src/backend/hosts/guacamole/token-service.ts b/src/backend/hosts/guacamole/token-service.ts index d2fe23da..a77e6203 100644 --- a/src/backend/hosts/guacamole/token-service.ts +++ b/src/backend/hosts/guacamole/token-service.ts @@ -2,11 +2,13 @@ import crypto from "crypto"; import { guacLogger } from "../../utils/logger.js"; export interface GuacamoleConnectionSettings { - type: "rdp" | "vnc" | "telnet"; + type?: "rdp" | "vnc" | "telnet"; + join?: string; + readOnly?: boolean; guacdHost?: string; guacdPort?: number; settings: { - hostname: string; + hostname?: string; port?: number; username?: string; password?: string; @@ -28,9 +30,17 @@ export interface GuacamoleConnectionSettings { }; } +export interface TermixGuacMeta { + termixConnectId: string; + hostId: number; + ownerUserId: string; + protocol: "rdp" | "vnc" | "telnet"; +} + export interface GuacamoleToken { connection: GuacamoleConnectionSettings; recording?: GuacamoleRecordingMetadata; + termixMeta?: TermixGuacMeta; } export interface GuacamoleRecordingMetadata { @@ -137,6 +147,7 @@ export class GuacamoleTokenService { guacdPort?: number; } = {}, recording?: GuacamoleRecordingMetadata, + termixMeta?: TermixGuacMeta, ): string { const { guacdHost, guacdPort, ...settingsOptions } = options; const token: GuacamoleToken = { @@ -155,6 +166,7 @@ export class GuacamoleTokenService { }, }, recording, + termixMeta, }; return this.encryptToken(token); } @@ -168,6 +180,7 @@ export class GuacamoleTokenService { guacdPort?: number; } = {}, recording?: GuacamoleRecordingMetadata, + termixMeta?: TermixGuacMeta, ): string { const { guacdHost, guacdPort, ...settingsOptions } = options; const token: GuacamoleToken = { @@ -184,6 +197,7 @@ export class GuacamoleTokenService { }, }, recording, + termixMeta, }; return this.encryptToken(token); } @@ -197,6 +211,7 @@ export class GuacamoleTokenService { guacdPort?: number; } = {}, recording?: GuacamoleRecordingMetadata, + termixMeta?: TermixGuacMeta, ): string { const { guacdHost, guacdPort, ...settingsOptions } = options; const token: GuacamoleToken = { @@ -213,6 +228,20 @@ export class GuacamoleTokenService { }, }, recording, + termixMeta, + }; + return this.encryptToken(token); + } + + // join tokens never carry recording params - only the primary connection's + // token should write recording-path/recording-name to guacd. + createJoinToken(guacamoleConnectionId: string, readOnly: boolean): string { + const token: GuacamoleToken = { + connection: { + join: guacamoleConnectionId, + readOnly, + settings: {}, + }, }; return this.encryptToken(token); } diff --git a/src/backend/hosts/host-resolver.ts b/src/backend/hosts/host-resolver.ts index 859d8e09..df5d8bd4 100644 --- a/src/backend/hosts/host-resolver.ts +++ b/src/backend/hosts/host-resolver.ts @@ -122,35 +122,61 @@ export async function resolveHostById( repository, ); if (!resolved) return null; - } else if (host.credentialId) { - try { - const cred = (await repository.findCredentialByIdForUser( - host.credentialId as number, - ownerId, - )) as Record | null; - - if (cred) { - host.password = pickResolvedPassword(host.password, cred.password); - // Prefer the normalised private key; fall back to raw key field - host.key = (cred.privateKey || cred.key) as string | null; - host.keyPassword = cred.keyPassword; - host.keyType = cred.keyType; - // CA-signed certificate for cert-based auth - (host as Record).certPublicKey = - cred.certPublicKey || null; - host.username = pickResolvedUsername( - host.username, - cred.username, - host.overrideCredentialUsername, + } else { + let effectiveCredentialId = host.credentialId as number | null | undefined; + if ( + !effectiveCredentialId && + host.authType === "credential" && + host.folder + ) { + try { + effectiveCredentialId = await repository.findFolderCredentialId( + ownerId, + host.folder as string, ); - host.authType = host.key ? "key" : host.password ? "password" : "none"; + } catch (e) { + sshLogger.warn("Failed to resolve folder credential for host", { + operation: "host_resolver_folder_credential", + hostId, + error: e instanceof Error ? e.message : "Unknown", + }); + } + } + + if (effectiveCredentialId) { + try { + const cred = (await repository.findCredentialByIdForUser( + effectiveCredentialId, + ownerId, + )) as Record | null; + + if (cred) { + host.password = pickResolvedPassword(host.password, cred.password); + // Prefer the normalised private key; fall back to raw key field + host.key = (cred.privateKey || cred.key) as string | null; + host.keyPassword = cred.keyPassword; + host.keyType = cred.keyType; + // CA-signed certificate for cert-based auth + (host as Record).certPublicKey = + cred.certPublicKey || null; + host.username = pickResolvedUsername( + host.username, + cred.username, + host.overrideCredentialUsername, + ); + host.authType = host.key + ? "key" + : host.password + ? "password" + : "none"; + } + } catch (e) { + sshLogger.warn("Failed to resolve credential for host", { + operation: "host_resolver_credential", + hostId, + error: e instanceof Error ? e.message : "Unknown", + }); } - } catch (e) { - sshLogger.warn("Failed to resolve credential for host", { - operation: "host_resolver_credential", - hostId, - error: e instanceof Error ? e.message : "Unknown", - }); } } diff --git a/src/backend/hosts/metrics/index.ts b/src/backend/hosts/metrics/index.ts index b934a34d..8f71f1d9 100644 --- a/src/backend/hosts/metrics/index.ts +++ b/src/backend/hosts/metrics/index.ts @@ -32,6 +32,7 @@ import { collectSystemMetrics } from "./widgets/system-collector.js"; import { collectLoginStats } from "./widgets/login-stats-collector.js"; import { collectPortsMetrics } from "./widgets/ports-collector.js"; import { collectFirewallMetrics } from "./widgets/firewall-collector.js"; +import { collectTemperatureMetrics } from "./widgets/temperature-collector.js"; import { createSocks5Connection, type SOCKS5Config, @@ -146,6 +147,7 @@ const DEFAULT_STATS_CONFIG: StatsConfig = { "processes", "ports", "firewall", + "temperature", ], statusCheckEnabled: true, statusCheckInterval: 60, @@ -1582,6 +1584,21 @@ async function collectMetrics(host: SSHHostWithCredentials): Promise<{ // expected } + let temperature: { + source: "sysfs" | "sensors" | "none"; + highestCelsius: number | null; + sensors: Array<{ label: string; celsius: number }>; + } = { + source: "none", + highestCelsius: null, + sensors: [], + }; + try { + temperature = await collectTemperatureMetrics(client); + } catch { + // expected + } + const result = { cpu, memory, @@ -1593,6 +1610,7 @@ async function collectMetrics(host: SSHHostWithCredentials): Promise<{ login_stats, ports, firewall, + temperature, }; metricsCache.set(host.id, result); diff --git a/src/backend/hosts/metrics/widgets/disk-collector.ts b/src/backend/hosts/metrics/widgets/disk-collector.ts index 79ae76c2..eec042d3 100644 --- a/src/backend/hosts/metrics/widgets/disk-collector.ts +++ b/src/backend/hosts/metrics/widgets/disk-collector.ts @@ -1,6 +1,67 @@ import type { Client } from "ssh2"; import { execCommand, toFixedNum } from "./common-utils.js"; +const PSEUDO_FS_RE = /^(tmpfs|devtmpfs|overlay|udev|none|shm)$/; + +export interface DfRow { + filesystem: string; + mount: string; + parts: string[]; +} + +export function parseDfLines(output: string): DfRow[] { + return output + .split("\n") + .map((l) => l.trim()) + .filter(Boolean) + .map((line) => { + const parts = line.split(/\s+/); + return { filesystem: parts[0] || "", mount: parts[5] || "", parts }; + }) + .filter( + (row) => row.parts.length >= 6 && !PSEUDO_FS_RE.test(row.filesystem), + ); +} + +// Finds the index of the most-utilized real filesystem in a `df -B1`-style +// row set (parts[1] = total bytes, parts[2] = used bytes), so a nearly-full +// secondary mount (e.g. /data) isn't hidden behind a healthy root filesystem. +export function findWorstMountIndex(bytesRows: DfRow[]): { + index: number; + usedBytes: number; + totalBytes: number; +} { + let worstIndex = -1; + let worstUsedBytes = -1; + let worstTotalBytes = 0; + + bytesRows.forEach((row, index) => { + const totalBytes = Number(row.parts[1]); + const usedBytes = Number(row.parts[2]); + if ( + !Number.isFinite(totalBytes) || + !Number.isFinite(usedBytes) || + totalBytes <= 0 + ) { + return; + } + const usedRatio = usedBytes / totalBytes; + const worstRatio = + worstTotalBytes > 0 ? worstUsedBytes / worstTotalBytes : -1; + if (usedRatio > worstRatio) { + worstIndex = index; + worstUsedBytes = usedBytes; + worstTotalBytes = totalBytes; + } + }); + + return { + index: worstIndex, + usedBytes: worstUsedBytes, + totalBytes: worstTotalBytes, + }; +} + export async function collectDiskMetrics(client: Client): Promise<{ percent: number | null; usedHuman: string | null; @@ -14,41 +75,28 @@ export async function collectDiskMetrics(client: Client): Promise<{ try { const [diskOutHuman, diskOutBytes] = await Promise.all([ - execCommand(client, "df -h -P / | tail -n +2"), - execCommand(client, "df -B1 -P / | tail -n +2"), + execCommand(client, "df -h -P | tail -n +2"), + execCommand(client, "df -B1 -P | tail -n +2"), ]); - const humanLine = - diskOutHuman.stdout - .split("\n") - .map((l) => l.trim()) - .filter(Boolean)[0] || ""; - const bytesLine = - diskOutBytes.stdout - .split("\n") - .map((l) => l.trim()) - .filter(Boolean)[0] || ""; + const humanRows = parseDfLines(diskOutHuman.stdout); + const bytesRows = parseDfLines(diskOutBytes.stdout); + const worst = findWorstMountIndex(bytesRows); - const humanParts = humanLine.split(/\s+/); - const bytesParts = bytesLine.split(/\s+/); + if (worst.totalBytes > 0) { + diskPercent = Math.max( + 0, + Math.min(100, (worst.usedBytes / worst.totalBytes) * 100), + ); - if (humanParts.length >= 6 && bytesParts.length >= 6) { - totalHuman = humanParts[1] || null; - usedHuman = humanParts[2] || null; - availableHuman = humanParts[3] || null; - - const totalBytes = Number(bytesParts[1]); - const usedBytes = Number(bytesParts[2]); - - if ( - Number.isFinite(totalBytes) && - Number.isFinite(usedBytes) && - totalBytes > 0 - ) { - diskPercent = Math.max( - 0, - Math.min(100, (usedBytes / totalBytes) * 100), - ); + const humanRow = + humanRows.length === bytesRows.length + ? humanRows[worst.index] + : humanRows.find((row) => row.mount === bytesRows[worst.index].mount); + if (humanRow) { + totalHuman = humanRow.parts[1] || null; + usedHuman = humanRow.parts[2] || null; + availableHuman = humanRow.parts[3] || null; } } } catch { diff --git a/src/backend/hosts/serial.ts b/src/backend/hosts/serial.ts index 7868c2eb..39bd7985 100644 --- a/src/backend/hosts/serial.ts +++ b/src/backend/hosts/serial.ts @@ -21,6 +21,12 @@ const authManager = AuthManager.getInstance(); const wss = new WebSocketServer({ port: 30011 }); +wss.on("error", (error) => { + sshLogger.error("Serial WebSocket server error", error, { + operation: "wss_error", + }); +}); + wss.on("connection", async (ws: WebSocket, req) => { let userId: string | undefined; diff --git a/src/backend/hosts/session-sharing/routes.ts b/src/backend/hosts/session-sharing/routes.ts new file mode 100644 index 00000000..4e37452d --- /dev/null +++ b/src/backend/hosts/session-sharing/routes.ts @@ -0,0 +1,539 @@ +import crypto from "crypto"; +import express from "express"; +import type { Request, Response } from "express"; +import type { AuthenticatedRequest } from "../../../types/index.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import { PermissionManager } from "../../utils/permission-manager.js"; +import { sshLogger } from "../../utils/logger.js"; +import { sessionManager } from "../terminal/session-manager.js"; +import { getGuacSessionInfo } from "../guacamole/guacamole-server.js"; +import { GuacamoleTokenService } from "../guacamole/token-service.js"; +import { + createCurrentSessionShareRepository, + createCurrentSettingsRepository, + createCurrentHostResolutionRepository, +} from "../../database/repositories/factory.js"; + +const router = express.Router(); +const authManager = AuthManager.getInstance(); +const authenticateJWT = authManager.createAuthMiddleware(); +const permissionManager = PermissionManager.getInstance(); +const tokenService = GuacamoleTokenService.getInstance(); + +const DEFAULT_EXPIRY_HOURS = 24; +const MAX_EXPIRY_HOURS = 24 * 30; + +type Protocol = "ssh" | "rdp" | "vnc" | "telnet"; +type PermissionLevel = "read-only" | "read-write"; + +interface ResolveRateEntry { + count: number; + windowStart: number; +} +const resolveAttempts = new Map(); +const RESOLVE_WINDOW_MS = 60 * 1000; +const RESOLVE_MAX_ATTEMPTS = 30; + +function isResolveRateLimited(ip: string): boolean { + const now = Date.now(); + const entry = resolveAttempts.get(ip); + if (!entry || now - entry.windowStart > RESOLVE_WINDOW_MS) { + resolveAttempts.set(ip, { count: 1, windowStart: now }); + return false; + } + entry.count += 1; + return entry.count > RESOLVE_MAX_ATTEMPTS; +} + +setInterval( + () => { + const now = Date.now(); + for (const [ip, entry] of resolveAttempts.entries()) { + if (now - entry.windowStart > RESOLVE_WINDOW_MS) + resolveAttempts.delete(ip); + } + }, + 5 * 60 * 1000, +); + +async function isSharingEnabledForHost(hostId: number): Promise<{ + enabled: boolean; + hostOwnerId: string | null; +}> { + const globalEnabled = await createCurrentSettingsRepository().getBoolean( + "session_sharing_globally_enabled", + true, + ); + if (!globalEnabled) return { enabled: false, hostOwnerId: null }; + + const hostResolutionRepository = createCurrentHostResolutionRepository(); + const hostOwnerId = await hostResolutionRepository.findHostOwnerId(hostId); + if (!hostOwnerId) return { enabled: false, hostOwnerId: null }; + + const host = await hostResolutionRepository.findHostById(hostId, hostOwnerId); + if (!host) return { enabled: false, hostOwnerId: null }; + + return { + enabled: host.allowSessionSharing !== false, + hostOwnerId, + }; +} + +function computeExpiresAt(expiryHours: number | undefined): string { + const hours = Math.min( + Math.max(expiryHours ?? DEFAULT_EXPIRY_HOURS, 1), + MAX_EXPIRY_HOURS, + ); + return new Date(Date.now() + hours * 60 * 60 * 1000).toISOString(); +} + +function isLiveSessionOwnedBy( + protocol: Protocol, + sessionId: string, + userId: string, +): boolean { + if (protocol === "ssh") { + const session = sessionManager.getSession(sessionId); + return !!session && session.isConnected && session.userId === userId; + } + const info = getGuacSessionInfo(sessionId); + return !!info && info.ownerUserId === userId; +} + +function isLiveSession(protocol: Protocol, sessionId: string): boolean { + if (protocol === "ssh") { + const session = sessionManager.getSession(sessionId); + return !!session && session.isConnected; + } + return !!getGuacSessionInfo(sessionId); +} + +/** + * @openapi + * /session-sharing/create: + * post: + * summary: Create a session share (link or targeted user) + * description: Mints a share grant for a live terminal/RDP/VNC/Telnet session. Caller must own the live session. + * tags: + * - Session Sharing + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - hostId + * - sessionId + * - protocol + * - shareType + * - permissionLevel + * properties: + * hostId: + * type: integer + * sessionId: + * type: string + * tabInstanceId: + * type: string + * protocol: + * type: string + * enum: [ssh, rdp, vnc, telnet] + * shareType: + * type: string + * enum: [link, user] + * targetUserId: + * type: string + * permissionLevel: + * type: string + * enum: [read-only, read-write] + * expiryHours: + * type: number + * responses: + * 200: + * description: Share created + * 400: + * description: Invalid request + * 403: + * description: Sharing disabled, or caller does not own the session + * 500: + * description: Server error + */ +router.post("/create", authenticateJWT, async (req: Request, res: Response) => { + try { + const userId = (req as AuthenticatedRequest).userId!; + const { + hostId, + sessionId, + tabInstanceId, + protocol, + shareType, + targetUserId, + permissionLevel, + expiryHours, + } = req.body ?? {}; + + if (!hostId || !sessionId || !protocol || !shareType || !permissionLevel) { + return res.status(400).json({ error: "Missing required fields" }); + } + if (!["ssh", "rdp", "vnc", "telnet"].includes(protocol)) { + return res.status(400).json({ error: "Invalid protocol" }); + } + if (!["link", "user"].includes(shareType)) { + return res.status(400).json({ error: "Invalid shareType" }); + } + if (!["read-only", "read-write"].includes(permissionLevel)) { + return res.status(400).json({ error: "Invalid permissionLevel" }); + } + if (shareType === "user" && !targetUserId) { + return res + .status(400) + .json({ error: "targetUserId is required for user shares" }); + } + + const numericHostId = Number(hostId); + + const { enabled: sharingEnabled } = + await isSharingEnabledForHost(numericHostId); + if (!sharingEnabled) { + return res + .status(403) + .json({ error: "Session sharing is disabled for this host" }); + } + + if (!isLiveSessionOwnedBy(protocol, String(sessionId), userId)) { + return res + .status(403) + .json({ error: "You do not own this live session" }); + } + + if (shareType === "user") { + const accessInfo = await permissionManager.canAccessHost( + targetUserId, + numericHostId, + "connect", + ); + if (!accessInfo.hasAccess) { + return res.status(403).json({ + error: "Target user does not have access to this host", + }); + } + } + + const shareId = crypto.randomUUID(); + const linkToken = + shareType === "link" + ? crypto.randomBytes(24).toString("base64url") + : null; + const expiresAt = computeExpiresAt(expiryHours); + + const created = await createCurrentSessionShareRepository().create({ + id: shareId, + hostId: numericHostId, + ownerUserId: userId, + protocol, + sessionId: String(sessionId), + tabInstanceId: tabInstanceId ?? null, + shareType, + targetUserId: shareType === "user" ? targetUserId : null, + linkToken, + permissionLevel, + expiresAt, + }); + + res.json({ + shareId: created.id, + linkToken: created.linkToken, + expiresAt: created.expiresAt, + }); + } catch (error) { + sshLogger.error("Failed to create session share", error, { + operation: "session_share_create_error", + }); + res.status(500).json({ error: "Failed to create session share" }); + } +}); + +/** + * @openapi + * /session-sharing/host/{hostId}/active: + * get: + * summary: List active session shares for a host + * description: Returns active (non-revoked, non-expired) shares owned by the caller for the given host. + * tags: + * - Session Sharing + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: hostId + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: List of active shares + * 400: + * description: Invalid host id + * 500: + * description: Server error + */ +router.get( + "/host/:hostId/active", + authenticateJWT, + async (req: Request, res: Response) => { + try { + const userId = (req as AuthenticatedRequest).userId!; + const hostId = Number.parseInt(String(req.params.hostId), 10); + if (!hostId || Number.isNaN(hostId)) { + return res.status(400).json({ error: "Invalid host ID" }); + } + + const shares = + await createCurrentSessionShareRepository().findActiveSharesForHost( + hostId, + userId, + ); + + res.json({ shares }); + } catch (error) { + sshLogger.error("Failed to list session shares", error, { + operation: "session_share_list_error", + }); + res.status(500).json({ error: "Failed to list session shares" }); + } + }, +); + +/** + * @openapi + * /session-sharing/{shareId}: + * delete: + * summary: Revoke a session share + * description: Revokes a share. Owner or admin only. Best-effort kick of live SSH participants; guac joins are not force-disconnected in v1. + * tags: + * - Session Sharing + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: shareId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Share revoked + * 403: + * description: Not authorized to revoke this share + * 404: + * description: Share not found + * 500: + * description: Server error + */ +router.delete( + "/:shareId", + authenticateJWT, + async (req: Request, res: Response) => { + try { + const userId = (req as AuthenticatedRequest).userId!; + const shareId = String(req.params.shareId); + + const repository = createCurrentSessionShareRepository(); + const share = await repository.findById(shareId); + if (!share) { + return res.status(404).json({ error: "Share not found" }); + } + + let revoked = await repository.revoke(shareId, userId); + if (!revoked) { + if (await permissionManager.isAdmin(userId)) { + revoked = await repository.revokeAsAdmin(shareId); + } + } + + if (!revoked) { + return res + .status(403) + .json({ error: "Not authorized to revoke this share" }); + } + + // Best-effort kick of live participants. SSH sessions support ending + // just the guests via ownerEndSession; guac joins aren't force-kickable + // from a REST handler (guacamole-lite exposes no kick API), so a revoked + // guac link only blocks *future* resolves until the guest's own socket ends. + if (share.protocol === "ssh") { + try { + sessionManager.ownerEndSession( + share.sessionId, + "Session share revoked by owner", + ); + } catch { + // best-effort only + } + } + + res.json({ success: true }); + } catch (error) { + sshLogger.error("Failed to revoke session share", error, { + operation: "session_share_revoke_error", + }); + res.status(500).json({ error: "Failed to revoke session share" }); + } + }, +); + +/** + * @openapi + * /session-sharing/resolve/{linkToken}: + * get: + * summary: Resolve a guest share link + * description: Public, unauthenticated endpoint for anonymous share-link guests. Never returns host name, IP, username, or hostId. Rate-limited per IP. + * tags: + * - Session Sharing + * parameters: + * - in: path + * name: linkToken + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Resolved share connection info + * 404: + * description: Link not found, expired, revoked, or sharing disabled + * 429: + * description: Too many requests + * 500: + * description: Server error + */ +router.get("/resolve/:linkToken", async (req: Request, res: Response) => { + try { + const ip = req.ip || req.socket.remoteAddress || "unknown"; + if (isResolveRateLimited(ip)) { + return res.status(429).json({ error: "Too many requests" }); + } + + const linkToken = String(req.params.linkToken); + const repository = createCurrentSessionShareRepository(); + const share = await repository.findByLinkToken(linkToken); + if (!share) { + return res.status(404).json({ error: "Link not found or expired" }); + } + + const { enabled: sharingEnabled } = await isSharingEnabledForHost( + share.hostId, + ); + if (!sharingEnabled) { + return res.status(404).json({ error: "Link not found or expired" }); + } + + const protocol = share.protocol as Protocol; + if (!isLiveSession(protocol, share.sessionId)) { + return res.status(404).json({ error: "Session is no longer active" }); + } + + // Field-by-field by design - never spread a host row into this response. + // Anonymous guests must never see hostname/IP/username/hostId (decision #5). + const response: { + protocol: Protocol; + permissionLevel: PermissionLevel; + wsPath: string; + connectParams?: Record; + } = { + protocol, + permissionLevel: share.permissionLevel as PermissionLevel, + wsPath: + protocol === "ssh" + ? `/terminal/ws?shareToken=${encodeURIComponent(linkToken)}` + : "/guacamole/websocket/", + }; + + if (protocol !== "ssh") { + const joinToken = tokenService.createJoinToken( + share.sessionId, + share.permissionLevel === "read-only", + ); + response.connectParams = { token: joinToken }; + } + + try { + await repository.touchShareUsage(share.id); + await repository.recordParticipantJoin(share.id, null, "Guest"); + } catch { + // best-effort, never fail the resolve response over audit bookkeeping + } + + res.json(response); + } catch (error) { + sshLogger.error("Failed to resolve session share link", error, { + operation: "session_share_resolve_error", + }); + res.status(500).json({ error: "Failed to resolve share link" }); + } +}); + +/** + * @openapi + * /session-sharing/{shareId}/end: + * post: + * summary: End a shared session for all participants + * description: Owner-only. Terminates the underlying session and notifies joined participants. Guac protocol kick is best-effort in v1. + * tags: + * - Session Sharing + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: shareId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Session ended + * 403: + * description: Not the owner of this share + * 404: + * description: Share not found + * 500: + * description: Server error + */ +router.post( + "/:shareId/end", + authenticateJWT, + async (req: Request, res: Response) => { + try { + const userId = (req as AuthenticatedRequest).userId!; + const shareId = String(req.params.shareId); + + const repository = createCurrentSessionShareRepository(); + const share = await repository.findById(shareId); + if (!share) { + return res.status(404).json({ error: "Share not found" }); + } + if (share.ownerUserId !== userId) { + return res.status(403).json({ error: "Not the owner of this share" }); + } + + if (share.protocol === "ssh") { + sessionManager.ownerEndSession( + share.sessionId, + "Session ended by owner", + ); + } + // Guac protocols: no kick API available from a REST handler in v1 - see + // DELETE /:shareId for the same limitation. + + res.json({ success: true }); + } catch (error) { + sshLogger.error("Failed to end shared session", error, { + operation: "session_share_end_error", + }); + res.status(500).json({ error: "Failed to end shared session" }); + } + }, +); + +export default router; diff --git a/src/backend/hosts/terminal/index.ts b/src/backend/hosts/terminal/index.ts index f23b2a10..b575c749 100644 --- a/src/backend/hosts/terminal/index.ts +++ b/src/backend/hosts/terminal/index.ts @@ -20,7 +20,14 @@ import { SSHAuthManager } from "../auth-manager.js"; import type { ProxyNode } from "../../../types/index.js"; import { SSHHostKeyVerifier } from "../host-key-verifier.js"; import { createJumpHostChain } from "../jump-host-chain.js"; -import { sessionManager } from "./session-manager.js"; +import { + sessionManager, + isMessageAllowedForParticipant, +} from "./session-manager.js"; +import { + createCurrentSessionShareRepository, + createCurrentSettingsRepository, +} from "../../database/repositories/factory.js"; import { detectTmux, attachOrCreateTmuxSession, @@ -105,10 +112,172 @@ const wss = new WebSocketServer({ port: 30002, }); +wss.on("error", (error) => { + sshLogger.error("WebSocket server error", error, { + operation: "wss_error", + }); +}); + +/** + * Auth path for anonymous share-link guests (?shareToken=). + * Never touches DataCrypto/user credentials - guests join an already-live + * stream and never decrypt stored secrets. + */ +async function handleShareTokenConnection( + ws: WebSocket, + req: import("http").IncomingMessage, + shareToken: string, +): Promise { + const shareRepo = createCurrentSessionShareRepository(); + const share = await shareRepo.findByLinkToken(shareToken); + if (!share) { + ws.close(1008, "Invalid or expired share link"); + return; + } + if (share.protocol !== "ssh") { + ws.close(1008, "Unsupported share protocol"); + return; + } + + const globallyEnabled = await createCurrentSettingsRepository().getBoolean( + "session_sharing_globally_enabled", + true, + ); + if (!globallyEnabled) { + ws.close(1008, "Session sharing is disabled"); + return; + } + + const host = await createCurrentHostResolutionRepository().findHostById( + share.hostId, + share.ownerUserId, + ); + if (!host || host.allowSessionSharing === false) { + ws.close(1008, "Session sharing is disabled for this host"); + return; + } + + const session = sessionManager.getSession(share.sessionId); + if (!session || !session.isConnected) { + ws.close(1008, "Session has ended"); + return; + } + + const permissionLevel = share.permissionLevel as "read-write" | "read-only"; + const joined = sessionManager.joinAsParticipant(share.sessionId, ws, { + userId: null, + permissionLevel, + guestLabel: "Guest", + shareId: share.id, + }); + if (!joined) { + ws.close(1008, "Session is no longer active"); + return; + } + + shareRepo.touchShareUsage(share.id).catch(() => {}); + shareRepo.recordParticipantJoin(share.id, null, "Guest").catch(() => {}); + + const buffered = sessionManager.getBuffer(joined); + if (buffered) { + ws.send(JSON.stringify({ type: "data", data: buffered })); + } + ws.send( + JSON.stringify({ type: "sessionAttached", sessionId: share.sessionId }), + ); + ws.send(JSON.stringify({ type: "connected", message: "Joined session" })); + + const currentSessionId: string = share.sessionId; + + let wsAlive = true; + ws.on("pong", () => { + wsAlive = true; + }); + const wsPingInterval = setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + if (!wsAlive) { + ws.terminate(); + return; + } + wsAlive = false; + ws.ping(); + } else { + clearInterval(wsPingInterval); + } + }, 30000); + + ws.on("close", () => { + clearInterval(wsPingInterval); + sessionManager.removeParticipant(currentSessionId, ws); + sshLogger.info("Guest left shared terminal session", { + operation: "terminal_guest_disconnect", + sessionId: currentSessionId, + shareId: share.id, + }); + }); + + ws.on("message", (msg: RawData) => { + let parsed: WebSocketMessage; + try { + parsed = JSON.parse(msg.toString()) as WebSocketMessage; + } catch { + return; + } + const { type, data } = parsed; + + const liveSession = sessionManager.getSession(currentSessionId); + const participant = liveSession + ? sessionManager.getParticipantForWs(liveSession, ws) + : null; + if (!isMessageAllowedForParticipant(participant, type)) { + return; + } + + switch (type) { + case "input": { + const inputData = data as string; + sessionManager.bufferInput(currentSessionId, inputData); + const inputStream = liveSession?.sshStream; + if (inputStream) { + try { + inputStream.write(Buffer.from(inputData, "utf8")); + } catch { + inputStream.write(Buffer.from(inputData, "latin1")); + } + } + break; + } + case "ping": + ws.send(JSON.stringify({ type: "pong" })); + break; + case "disconnect": + sessionManager.removeParticipant(currentSessionId, ws); + break; + default: + break; + } + }); +} + wss.on("connection", async (ws: WebSocket, req) => { let userId: string | undefined; let sessionId: string | undefined; + ws.on("error", (error) => { + sshLogger.error("WebSocket connection error", error, { + operation: "ws_error", + sessionId, + }); + }); + + const urlObj = new URL(req.url || "", "http://localhost"); + const shareToken = urlObj.searchParams.get("shareToken"); + + if (shareToken) { + await handleShareTokenConnection(ws, req, shareToken); + return; + } + try { let token: string | undefined; @@ -126,7 +295,6 @@ wss.on("connection", async (ws: WebSocket, req) => { } if (!token) { - const urlObj = new URL(req.url || "", "http://localhost"); const qp = urlObj.searchParams.get("token"); if (qp) token = qp; } @@ -242,11 +410,20 @@ wss.on("connection", async (ws: WebSocket, req) => { if (currentSessionId) { const session = sessionManager.getSession(currentSessionId); if (session?.isConnected) { - // Only detach if this WS is still the one attached to the session. - // If a refresh reconnected and reattached a new WS before this close - // event fired, we must not clobber that new attachment. - if (session.attachedWs === ws || session.attachedWs === null) { - sessionManager.detachWs(currentSessionId); + const participant = sessionManager.getParticipantForWs(session, ws); + if (participant && !participant.isOwner) { + sessionManager.removeParticipant(currentSessionId, ws); + } else { + // Only detach if this WS is still the owner's attached socket, or + // no owner is currently attached. If a refresh reconnected and + // reattached a new WS before this close event fired, we must not + // clobber that new attachment. + const ownerStillAttached = Array.from( + session.participants.values(), + ).some((p) => p.isOwner && p.ws !== ws); + if (!ownerStillAttached) { + sessionManager.detachWs(currentSessionId); + } } } else { sessionManager.destroySession(currentSessionId); @@ -295,6 +472,21 @@ wss.on("connection", async (ws: WebSocket, req) => { const { type, data } = parsed; + // Server-side gate: non-owner participants (read-only or read-write + // guests/joiners) may only send input/ping/disconnect - everything else + // (auth flows, tmux, resize, etc.) is owner-only and silently ignored. + if (type !== "joinSharedSession") { + const gateSession = currentSessionId + ? sessionManager.getSession(currentSessionId) + : null; + const gateParticipant = gateSession + ? sessionManager.getParticipantForWs(gateSession, ws) + : null; + if (!isMessageAllowedForParticipant(gateParticipant, type)) { + return; + } + } + switch (type) { case "connectToHost": { const connectData = data as ConnectToHostData; @@ -445,7 +637,20 @@ wss.on("connection", async (ws: WebSocket, req) => { break; } - case "disconnect": + case "disconnect": { + const disconnectSession = currentSessionId + ? sessionManager.getSession(currentSessionId) + : null; + const disconnectParticipant = disconnectSession + ? sessionManager.getParticipantForWs(disconnectSession, ws) + : null; + if (disconnectParticipant && !disconnectParticipant.isOwner) { + if (currentSessionId) { + sessionManager.removeParticipant(currentSessionId, ws); + currentSessionId = null; + } + break; + } if (currentSessionId) { sessionManager.destroySession(currentSessionId); currentSessionId = null; @@ -454,6 +659,7 @@ wss.on("connection", async (ws: WebSocket, req) => { sshConn = null; sshStream = null; break; + } case "get_cwd": { const activeConn = @@ -474,10 +680,8 @@ wss.on("connection", async (ws: WebSocket, req) => { execStream.stderr.on("data", () => {}); execStream.on("close", () => { const cwd = stdout.trim() || "/"; - const attachedWs = - sessionManager.getSession(currentSessionId)?.attachedWs ?? ws; - if (attachedWs.readyState === WebSocket.OPEN) { - attachedWs.send(JSON.stringify({ type: "cwd", path: cwd })); + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: "cwd", path: cwd })); } }); }); @@ -517,10 +721,8 @@ wss.on("connection", async (ws: WebSocket, req) => { execStream.stderr.on("data", () => {}); execStream.on("close", () => { const resolvedPath = stdout.trim() || requestedPath; - const attachedWs = - sessionManager.getSession(currentSessionId)?.attachedWs ?? ws; - if (attachedWs.readyState === WebSocket.OPEN) { - attachedWs.send( + if (ws.readyState === WebSocket.OPEN) { + ws.send( JSON.stringify({ type: "open_file_in_editor", path: resolvedPath, @@ -1001,6 +1203,105 @@ wss.on("connection", async (ws: WebSocket, req) => { break; } + case "joinSharedSession": { + const joinData = data as { shareId: string; tabInstanceId?: string }; + try { + const shareRepo = createCurrentSessionShareRepository(); + const share = await shareRepo.findActiveById(joinData.shareId); + if ( + !share || + share.shareType !== "user" || + share.targetUserId !== userId || + share.protocol !== "ssh" + ) { + ws.send( + JSON.stringify({ + type: "error", + message: "Share not found or not accessible", + }), + ); + break; + } + + const { PermissionManager } = + await import("../../utils/permission-manager.js"); + const access = await PermissionManager.getInstance().canAccessHost( + userId, + share.hostId, + "connect", + ); + if (!access.hasAccess) { + ws.send( + JSON.stringify({ + type: "error", + message: "Share not found or not accessible", + }), + ); + break; + } + + const joinedSession = sessionManager.joinAsParticipant( + share.sessionId, + ws, + { + userId, + permissionLevel: share.permissionLevel as + | "read-write" + | "read-only", + tabInstanceId: joinData.tabInstanceId, + shareId: share.id, + }, + ); + if (!joinedSession) { + ws.send( + JSON.stringify({ + type: "error", + message: "Shared session is no longer active", + }), + ); + break; + } + + currentSessionId = share.sessionId; + sshStream = joinedSession.sshStream; + sshConn = joinedSession.sshConn; + isConnecting = false; + isConnected = true; + + shareRepo.touchShareUsage(share.id).catch(() => {}); + shareRepo + .recordParticipantJoin(share.id, userId, null) + .catch(() => {}); + + const buffered = sessionManager.getBuffer(joinedSession); + if (buffered) { + ws.send(JSON.stringify({ type: "data", data: buffered })); + } + ws.send( + JSON.stringify({ + type: "sessionAttached", + sessionId: share.sessionId, + }), + ); + ws.send( + JSON.stringify({ type: "connected", message: "Joined session" }), + ); + } catch (error) { + sshLogger.error("Failed to join shared session", error, { + operation: "terminal_join_shared_session_error", + userId, + shareId: joinData.shareId, + }); + ws.send( + JSON.stringify({ + type: "error", + message: "Failed to join shared session", + }), + ); + } + break; + } + default: sshLogger.warn("Unknown message type received", { operation: "websocket_message_unknown_type", @@ -1288,39 +1589,56 @@ wss.on("connection", async (ws: WebSocket, req) => { }; } - sendLog("dns", "info", `Starting address resolution of ${ip}`); + const connectsViaJumpHosts = !!( + hostConfig.jumpHosts && + hostConfig.jumpHosts.length > 0 && + hostConfig.userId + ); + let connectHost = ip; - try { - const resolution = await resolveHostForSshConnect(ip); - connectHost = resolution.host; - if (resolution.resolvedAddress && resolution.resolvedAddress !== ip) { - sendLog( - "dns", - "success", - `Resolved ${ip} to ${resolution.resolvedAddress}`, - { attempts: resolution.attempts }, - ); - } - } catch (error) { - const message = error instanceof Error ? error.message : "Unknown error"; - sshLogger.error("SSH hostname resolution failed", error, { - operation: "terminal_dns_resolve", - hostId: id, - ip, - port, - transient: isRetriableDnsError(error), - }); - sendLog("dns", "error", `DNS resolution failed for ${ip}: ${message}`); - ws.send( - JSON.stringify({ - type: "error", - message: isRetriableDnsError(error) - ? "SSH error: DNS lookup temporarily failed. Check the Docker/container DNS configuration or try again." - : "SSH error: Could not resolve hostname from the Termix server container.", - }), + if (connectsViaJumpHosts) { + // The target is only reachable through the jump host's network (e.g. a + // VPN-only address), so DNS must be resolved there, not on this host. + sendLog( + "dns", + "info", + `Skipping local address resolution of ${ip} (resolved by jump host)`, ); - cleanupAuthState(connectionTimeout); - return; + } else { + sendLog("dns", "info", `Starting address resolution of ${ip}`); + try { + const resolution = await resolveHostForSshConnect(ip); + connectHost = resolution.host; + if (resolution.resolvedAddress && resolution.resolvedAddress !== ip) { + sendLog( + "dns", + "success", + `Resolved ${ip} to ${resolution.resolvedAddress}`, + { attempts: resolution.attempts }, + ); + } + } catch (error) { + const message = + error instanceof Error ? error.message : "Unknown error"; + sshLogger.error("SSH hostname resolution failed", error, { + operation: "terminal_dns_resolve", + hostId: id, + ip, + port, + transient: isRetriableDnsError(error), + }); + sendLog("dns", "error", `DNS resolution failed for ${ip}: ${message}`); + ws.send( + JSON.stringify({ + type: "error", + message: isRetriableDnsError(error) + ? "SSH error: DNS lookup temporarily failed. Check the Docker/container DNS configuration or try again." + : "SSH error: Could not resolve hostname from the Termix server container.", + }), + ); + cleanupAuthState(connectionTimeout); + return; + } } sendLog("tcp", "info", `Connecting to ${ip} port ${port}`); @@ -1619,12 +1937,10 @@ wss.on("connection", async (ws: WebSocket, req) => { const session = sessionManager.getSession(boundSessionId); if (session) { sessionManager.bufferOutput(boundSessionId!, utf8String); - - if (session.attachedWs?.readyState === WebSocket.OPEN) { - session.attachedWs.send( - JSON.stringify({ type: "data", data: utf8String }), - ); - } + sessionManager.broadcast(boundSessionId!, { + type: "data", + data: utf8String, + }); } } catch (error) { sshLogger.error("Error encoding terminal data", error, { @@ -1636,34 +1952,28 @@ wss.on("connection", async (ws: WebSocket, req) => { const session = sessionManager.getSession(boundSessionId); if (session) { sessionManager.bufferOutput(boundSessionId!, fallback); - - if (session.attachedWs?.readyState === WebSocket.OPEN) { - session.attachedWs.send( - JSON.stringify({ type: "data", data: fallback }), - ); - } + sessionManager.broadcast(boundSessionId!, { + type: "data", + data: fallback, + }); } } }); stream.on("close", (code: number | null) => { const session = sessionManager.getSession(boundSessionId); - if (session?.attachedWs?.readyState === WebSocket.OPEN) { + if (session) { if (code != null) { - session.attachedWs.send( - JSON.stringify({ - type: "session_ended", - code, - }), - ); + sessionManager.broadcast(boundSessionId!, { + type: "session_ended", + code, + }); } else { - session.attachedWs.send( - JSON.stringify({ - type: "disconnected", - message: "Connection lost", - graceful: true, - }), - ); + sessionManager.broadcast(boundSessionId!, { + type: "disconnected", + message: "Connection lost", + graceful: true, + }); } } if (boundSessionId) { @@ -1683,13 +1993,11 @@ wss.on("connection", async (ws: WebSocket, req) => { username, }); const session = sessionManager.getSession(boundSessionId); - if (session?.attachedWs?.readyState === WebSocket.OPEN) { - session.attachedWs.send( - JSON.stringify({ - type: "error", - message: "SSH stream error: " + err.message, - }), - ); + if (session) { + sessionManager.broadcast(boundSessionId!, { + type: "error", + message: "SSH stream error: " + err.message, + }); } }); @@ -2014,7 +2322,7 @@ wss.on("connection", async (ws: WebSocket, req) => { 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.", + `Tailscale SSH authentication failed for user "${username}". Ensure Tailscale is running on the server, SSH is advertised (tailscale set --ssh), and your ACL policy grants the "${username}" user to your identity (check tailscale.com/s/ssh for the check/action ACL syntax). If your Tailscale identity maps to a different Unix user, update the username on this host.`, ); if (currentSessionId) { sessionManager.destroySession(currentSessionId); @@ -2024,8 +2332,7 @@ wss.on("connection", async (ws: WebSocket, req) => { 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.", + message: `Tailscale SSH authentication failed for user "${username}". Ensure Tailscale is running on the server, SSH is advertised (tailscale set --ssh), and your ACL policy grants the "${username}" user to your identity. If your Tailscale identity maps to a different Unix user, update the username on this host.`, }), ); return; diff --git a/src/backend/hosts/terminal/session-manager.ts b/src/backend/hosts/terminal/session-manager.ts index 1e1d3845..542e8c95 100644 --- a/src/backend/hosts/terminal/session-manager.ts +++ b/src/backend/hosts/terminal/session-manager.ts @@ -15,6 +15,16 @@ const DEFAULT_TIMEOUT_MINUTES = 30; const HEALTH_CHECK_INTERVAL_MS = 60_000; const MAX_SESSIONS_PER_USER = 10; +export interface SessionParticipant { + ws: WebSocket; + userId: string | null; // null for anonymous link guests + permissionLevel: "read-write" | "read-only"; + isOwner: boolean; + guestLabel?: string; + tabInstanceId?: string; + joinedViaShareId?: string; +} + export interface TerminalSession { id: string; userId: string; @@ -32,7 +42,7 @@ export interface TerminalSession { isConnected: boolean; createdAt: number; - attachedWs: WebSocket | null; + participants: Map; lastDetachedAt: number | null; detachTimeout: NodeJS.Timeout | null; @@ -48,6 +58,33 @@ export interface TerminalSession { sessionLoggingEnabled: boolean; sessionStartedAt: number; lastPersistedBytes: number; + terminatedByOwner: boolean; + terminationReason: string | null; +} + +/** Message types a non-owner participant may legally send. */ +const NON_OWNER_ALLOWED_MESSAGE_TYPES = new Set([ + "input", + "ping", + "disconnect", +]); + +/** + * Server-side gate for whether a participant may send a given WS message + * type. The owner may send anything; non-owners are limited to input (if + * read-write), ping, and disconnect. Pure function so read-only enforcement + * is unit-testable without a real WebSocketServer. + */ +export function isMessageAllowedForParticipant( + participant: Pick | null, + messageType: string, +): boolean { + if (!participant || participant.isOwner) return true; + if (!NON_OWNER_ALLOWED_MESSAGE_TYPES.has(messageType)) return false; + if (messageType === "input" && participant.permissionLevel === "read-only") { + return false; + } + return true; } class TerminalSessionManager { @@ -81,7 +118,7 @@ class TerminalSessionManager { const userSessions = this.getUserSessions(userId); if (userSessions.length >= MAX_SESSIONS_PER_USER) { const detached = userSessions - .filter((s) => s.attachedWs === null) + .filter((s) => this.getOwnerParticipant(s) === null) .sort( (a, b) => (a.lastDetachedAt ?? a.createdAt) - @@ -109,7 +146,7 @@ class TerminalSessionManager { operation: "session_tab_duplicate_skip", existingSessionId: existing.id, tabInstanceId, - hasAttachedWs: existing.attachedWs !== null, + hasAttachedWs: this.getOwnerParticipant(existing) !== null, }, ); return existing.id; @@ -151,7 +188,7 @@ class TerminalSessionManager { rows, isConnected: false, createdAt: now, - attachedWs: null, + participants: new Map(), lastDetachedAt: null, detachTimeout: null, outputBuffer: [], @@ -166,6 +203,8 @@ class TerminalSessionManager { sessionLoggingEnabled, sessionStartedAt: now, lastPersistedBytes: 0, + terminatedByOwner: false, + terminationReason: null, }; this.sessions.set(id, session); @@ -199,6 +238,25 @@ class TerminalSessionManager { session.isConnected = true; } + /** Finds the owner's participant entry, if currently attached. */ + private getOwnerParticipant( + session: TerminalSession, + ): SessionParticipant | null { + for (const participant of session.participants.values()) { + if (participant.isOwner) return participant; + } + return null; + } + + private getOwnerEntry( + session: TerminalSession, + ): [string, SessionParticipant] | null { + for (const entry of session.participants.entries()) { + if (entry[1].isOwner) return entry; + } + return null; + } + attachWs( sessionId: string, userId: string, @@ -234,8 +292,9 @@ class TerminalSessionManager { return null; } + const ownerParticipant = this.getOwnerParticipant(session); const isDetached = - !session.attachedWs || session.attachedWs.readyState !== WebSocket.OPEN; + !ownerParticipant || ownerParticipant.ws.readyState !== WebSocket.OPEN; const isOriginalTab = (session.attachedTabInstanceId ?? session.tabInstanceId) === tabInstanceId; @@ -282,9 +341,10 @@ class TerminalSessionManager { ); } - if (session.attachedWs && session.attachedWs !== ws) { + const ownerEntry = this.getOwnerEntry(session); + if (ownerEntry && ownerEntry[1].ws !== ws) { try { - session.attachedWs.send( + ownerEntry[1].ws.send( JSON.stringify({ type: "sessionTakenOver", sessionId, @@ -294,7 +354,7 @@ class TerminalSessionManager { } catch { /* ignore */ } - session.attachedWs = null; + session.participants.delete(ownerEntry[0]); } if (session.detachTimeout) { @@ -302,7 +362,14 @@ class TerminalSessionManager { session.detachTimeout = null; } - session.attachedWs = ws; + const participantId = crypto.randomUUID(); + session.participants.set(participantId, { + ws, + userId, + permissionLevel: "read-write", + isOwner: true, + tabInstanceId, + }); session.attachedTabInstanceId = tabInstanceId; session.lastDetachedAt = null; @@ -316,6 +383,110 @@ class TerminalSessionManager { return session; } + /** + * Adds a non-owner participant (in-app share join or anonymous link guest). + * Purely additive - never evicts the owner or any other participant. + */ + joinAsParticipant( + sessionId: string, + ws: WebSocket, + opts: { + userId: string | null; + permissionLevel: "read-write" | "read-only"; + guestLabel?: string; + tabInstanceId?: string; + shareId?: string; + }, + ): TerminalSession | null { + const session = this.sessions.get(sessionId); + if (!session || !session.isConnected) return null; + + const participantId = crypto.randomUUID(); + session.participants.set(participantId, { + ws, + userId: opts.userId, + permissionLevel: opts.permissionLevel, + isOwner: false, + guestLabel: opts.guestLabel, + tabInstanceId: opts.tabInstanceId, + joinedViaShareId: opts.shareId, + }); + + sshLogger.info("Participant joined shared session", { + operation: "session_join_participant", + sessionId, + userId: opts.userId, + permissionLevel: opts.permissionLevel, + shareId: opts.shareId, + }); + + return session; + } + + /** Fans out a message to every OPEN participant socket; skips closed ones and send failures. */ + broadcast(sessionId: string, message: object): void { + const session = this.sessions.get(sessionId); + if (!session) return; + const payload = JSON.stringify(message); + for (const participant of session.participants.values()) { + if (participant.ws.readyState !== WebSocket.OPEN) continue; + try { + participant.ws.send(payload); + } catch { + /* ignore individual send failures, keep broadcasting to the rest */ + } + } + } + + /** Finds the participant entry (owner or not) for a given socket. */ + getParticipantForWs( + session: TerminalSession, + ws: WebSocket, + ): SessionParticipant | null { + for (const participant of session.participants.values()) { + if (participant.ws === ws) return participant; + } + return null; + } + + /** + * Removes a non-owner participant's socket. No detach timeout or session + * destruction side effects - a guest leaving must never end the session. + */ + removeParticipant(sessionId: string, ws: WebSocket): void { + const session = this.sessions.get(sessionId); + if (!session) return; + for (const [id, participant] of session.participants.entries()) { + if (participant.ws === ws && !participant.isOwner) { + session.participants.delete(id); + sshLogger.info("Participant left shared session", { + operation: "session_leave_participant", + sessionId, + userId: participant.userId, + }); + return; + } + } + } + + /** Broadcasts termination to all guests, then destroys the session. */ + ownerEndSession(sessionId: string, reason: string): void { + const session = this.sessions.get(sessionId); + if (!session) return; + + this.broadcast(sessionId, { type: "sessionTerminatedByOwner", reason }); + session.terminatedByOwner = true; + session.terminationReason = reason; + + sshLogger.info("Owner ended shared session", { + operation: "session_owner_end", + sessionId, + reason, + }); + + this.destroySession(sessionId); + } + detachWs(sessionId: string): void { const session = this.sessions.get(sessionId); if (!session) return; @@ -325,7 +496,10 @@ class TerminalSessionManager { session.detachTimeout = null; } - session.attachedWs = null; + const ownerEntry = this.getOwnerEntry(session); + if (ownerEntry) { + session.participants.delete(ownerEntry[0]); + } session.lastDetachedAt = Date.now(); // Persist log immediately when the user detaches so it appears right away, @@ -365,6 +539,23 @@ class TerminalSessionManager { fs.promises.unlink(session.recordingPath).catch(() => {}); } + for (const participant of session.participants.values()) { + if (participant.isOwner) continue; + if (participant.ws.readyState !== WebSocket.OPEN) continue; + try { + participant.ws.send( + JSON.stringify({ + type: "sessionExpired", + sessionId, + message: "Session has ended", + }), + ); + } catch { + /* ignore */ + } + } + session.participants.clear(); + if (session.sshStream) { try { session.sshStream.end(); @@ -440,12 +631,16 @@ class TerminalSessionManager { recordingPath: session.recordingPath, protocol: "ssh", format: "asciicast", + terminatedByOwner: session.terminatedByOwner || undefined, + terminationReason: session.terminationReason ?? undefined, }); session.recordingId = created.id; } else { await repo.updateEnded(session.recordingId, { endedAt: new Date(endedAt).toISOString(), duration, + terminatedByOwner: session.terminatedByOwner || undefined, + terminationReason: session.terminationReason ?? undefined, }); } } catch (err) { @@ -569,10 +764,10 @@ class TerminalSessionManager { for (const [id, session] of this.sessions) { if (!session.isConnected) continue; - if ( - session.attachedWs && - session.attachedWs.readyState === WebSocket.OPEN - ) { + const hasOpenParticipant = Array.from(session.participants.values()).some( + (p) => p.ws.readyState === WebSocket.OPEN, + ); + if (hasOpenParticipant) { continue; } diff --git a/src/backend/hosts/tunnel/index.ts b/src/backend/hosts/tunnel/index.ts index bbbfc4fb..d4378299 100644 --- a/src/backend/hosts/tunnel/index.ts +++ b/src/backend/hosts/tunnel/index.ts @@ -42,9 +42,21 @@ const c2sRelayWss = new WebSocketServer({ path: "/ssh/tunnel/c2s/stream", }); +c2sRelayWss.on("error", (error) => { + tunnelLogger.error("C2S relay WebSocket server error", error, { + operation: "c2s_relay_wss_error", + }); +}); + c2sRelayWss.on("connection", (ws, req) => { let opened = false; + ws.on("error", (error) => { + tunnelLogger.error("C2S relay WebSocket connection error", error, { + operation: "c2s_relay_ws_error", + }); + }); + ws.once("message", async (raw) => { try { const token = extractRequestToken(req); diff --git a/src/backend/starter.ts b/src/backend/starter.ts index dfc0a8f9..15d75092 100644 --- a/src/backend/starter.ts +++ b/src/backend/starter.ts @@ -15,6 +15,74 @@ import { setGlobalLogLevel, } from "./utils/logger.js"; +async function provisionLocalDesktopUserIfNeeded(): Promise { + const { createCurrentUserRepository, createCurrentRoleRepository } = + await import("./database/repositories/factory.js"); + const { AuthManager } = await import("./utils/auth-manager.js"); + const crypto = await import("crypto"); + + const userRepository = createCurrentUserRepository(); + const existingCount = await userRepository.countAll(); + if (existingCount > 0) { + const allUsers = await userRepository.listAll(); + for (const user of allUsers) { + try { + await AuthManager.getInstance().registerUser(user.id); + } catch (dekError) { + systemLogger.error( + "Failed to verify/provision data-encryption key for existing user", + dekError, + { operation: "desktop_dek_healing", userId: user.id }, + ); + } + } + return; + } + + const id = crypto.randomUUID(); + const { isFirstUser } = await userRepository.createFirstLocalUser({ + id, + username: "local", + passwordHash: "", + isOidc: false, + clientId: "", + clientSecret: "", + issuerUrl: "", + authorizationUrl: "", + tokenUrl: "", + identifierPath: "", + namePath: "", + scopes: "openid email profile", + totpSecret: null, + totpEnabled: false, + totpBackupCodes: null, + }); + + try { + await createCurrentRoleRepository().assignRoleNameToUser({ + userId: id, + roleName: isFirstUser ? "admin" : "user", + grantedBy: id, + }); + } catch (roleError) { + systemLogger.error( + "Failed to assign default role to auto-provisioned local user", + roleError, + { operation: "desktop_auto_provision_role" }, + ); + } + + await AuthManager.getInstance().registerUser( + id, + crypto.randomBytes(32).toString("hex"), + ); + + systemLogger.success("Auto-provisioned local desktop user", { + operation: "desktop_auto_provision", + userId: id, + }); +} + (async () => { const initStartTime = Date.now(); try { @@ -61,6 +129,8 @@ import { } } } + process.env.VERSION = version; + versionLogger.info(`Termix Backend starting - Version: ${version}`, { operation: "startup", version: version, @@ -105,6 +175,10 @@ import { await import("./utils/crypto-migration/shared-host-secrets-migration.js"); await runSharedHostSecretsMigration(); + if (process.env.ELECTRON_EMBEDDED === "true") { + await provisionLocalDesktopUserIfNeeded(); + } + import("./utils/opkssh-binary-manager.js").then( ({ OPKSSHBinaryManager }) => { OPKSSHBinaryManager.ensureBinary().catch((error) => { @@ -170,6 +244,9 @@ import { }); } + const { startAnalyticsHeartbeat } = await import("./utils/analytics.js"); + startAnalyticsHeartbeat(); + systemLogger.success("Termix backend started successfully", { operation: "backend_init_complete", port: process.env.PORT || 4090, diff --git a/src/backend/tests/database/repositories/dashboard-service-link-repository.test.ts b/src/backend/tests/database/repositories/dashboard-service-link-repository.test.ts index e3bf7a67..a68bb268 100644 --- a/src/backend/tests/database/repositories/dashboard-service-link-repository.test.ts +++ b/src/backend/tests/database/repositories/dashboard-service-link-repository.test.ts @@ -32,7 +32,9 @@ describe("DashboardServiceLinkRepository", () => { label TEXT NOT NULL, url TEXT NOT NULL, "order" INTEGER NOT NULL DEFAULT 0, - created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + sync_id TEXT, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); INSERT INTO users (id, username, password_hash) @@ -99,8 +101,10 @@ describe("DashboardServiceLinkRepository", () => { ); expect(writeCount).toBe(2); - expect(await repo.deleteForUser("user-2", link.id)).toBe(false); - expect(await repo.deleteForUser("user-1", link.id)).toBe(true); + expect(await repo.deleteForUser("user-2", link.id)).toBeNull(); + expect(await repo.deleteForUser("user-1", link.id)).toEqual({ + syncId: expect.any(String), + }); expect(writeCount).toBe(3); }); diff --git a/src/backend/tests/database/repositories/homepage-item-repository.test.ts b/src/backend/tests/database/repositories/homepage-item-repository.test.ts index 09a5dc27..3ea1525e 100644 --- a/src/backend/tests/database/repositories/homepage-item-repository.test.ts +++ b/src/backend/tests/database/repositories/homepage-item-repository.test.ts @@ -31,6 +31,7 @@ describe("HomepageItemRepository", () => { title TEXT, config TEXT NOT NULL DEFAULT '{}', folder_id INTEGER, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -107,8 +108,10 @@ describe("HomepageItemRepository", () => { ).toBeNull(); expect(writeCount).toBe(2); - expect(await repo.deleteForUser("user-2", item.id)).toBe(false); - expect(await repo.deleteForUser("user-1", item.id)).toBe(true); + expect(await repo.deleteForUser("user-2", item.id)).toBeNull(); + expect(await repo.deleteForUser("user-1", item.id)).toEqual({ + syncId: expect.any(String), + }); expect(writeCount).toBe(3); }); diff --git a/src/backend/tests/database/repositories/host-credential-repositories.test.ts b/src/backend/tests/database/repositories/host-credential-repositories.test.ts index 3973618f..e97dce23 100644 --- a/src/backend/tests/database/repositories/host-credential-repositories.test.ts +++ b/src/backend/tests/database/repositories/host-credential-repositories.test.ts @@ -55,6 +55,7 @@ describe("HostRepository and CredentialRepository", () => { cert_public_key TEXT, usage_count INTEGER NOT NULL DEFAULT 0, last_used TEXT, + sync_id TEXT, 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 @@ -87,6 +88,7 @@ describe("HostRepository and CredentialRepository", () => { vault_profile_id INTEGER, enable_terminal INTEGER NOT NULL DEFAULT 1, enable_session_logging INTEGER NOT NULL DEFAULT 1, + allow_session_sharing INTEGER NOT NULL DEFAULT 1, enable_command_history INTEGER NOT NULL DEFAULT 1, enable_tunnel INTEGER NOT NULL DEFAULT 1, tunnel_connections TEXT, @@ -150,6 +152,8 @@ describe("HostRepository and CredentialRepository", () => { host_key_first_seen TEXT, host_key_last_verified TEXT, host_key_changed_count INTEGER DEFAULT 0, + connection_origin TEXT, + sync_id TEXT, 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, @@ -216,18 +220,27 @@ describe("HostRepository and CredentialRepository", () => { ).toBe("primary"); expect((await repo.credentials.findById(created.id))?.name).toBe("primary"); + // Backdate updated_at so the update's CURRENT_TIMESTAMP bump is + // deterministically observable regardless of clock resolution -- + // the sync engine's last-write-wins conflict resolution depends on + // every mutating update actually advancing this column. + repo.sqlite + .prepare("UPDATE ssh_credentials SET updated_at = ? WHERE id = ?") + .run("2000-01-01 00:00:00", created.id); + const updated = await repo.credentials.updateForUser("user-1", created.id, { folder: "ops", tags: "linux,admin", }); expect(updated?.folder).toBe("ops"); + expect(updated?.updatedAt).not.toBe("2000-01-01 00:00:00"); expect( await repo.credentials.findByIdForUser("user-2", created.id), ).toBeNull(); - expect(await repo.credentials.deleteForUser("user-1", created.id)).toBe( - true, - ); + expect(await repo.credentials.deleteForUser("user-1", created.id)).toEqual({ + syncId: expect.any(String), + }); expect( await repo.credentials.findByIdForUser("user-1", created.id), ).toBeNull(); @@ -335,15 +348,20 @@ describe("HostRepository and CredentialRepository", () => { expect(raw.password).toBe("user-encrypted-password"); + repo.sqlite + .prepare("UPDATE ssh_credentials SET updated_at = ? WHERE id = ?") + .run("2000-01-01 00:00:00", created.id); + await repo.credentials.updateEncryptedForUser("user-1", created.id, { password: "updated-secret", }); const updatedRaw = repo.sqlite - .prepare("SELECT password FROM ssh_credentials WHERE id = ?") - .get(created.id) as { password: string }; + .prepare("SELECT password, updated_at FROM ssh_credentials WHERE id = ?") + .get(created.id) as { password: string; updated_at: string }; expect(updatedRaw.password).toBe("user-encrypted-password"); + expect(updatedRaw.updated_at).not.toBe("2000-01-01 00:00:00"); expect(DataCrypto.encryptRecord).toHaveBeenCalledWith( "ssh_credentials", expect.objectContaining({ password: "updated-secret" }), @@ -374,7 +392,7 @@ describe("HostRepository and CredentialRepository", () => { const onWrite = vi.fn(); const repo = await createRepositories(onWrite); - await repo.credentials.create({ + const primary = await repo.credentials.create({ userId: "user-1", name: "primary", authType: "password", @@ -392,6 +410,9 @@ describe("HostRepository and CredentialRepository", () => { authType: "password", folder: "prod", }); + repo.sqlite + .prepare("UPDATE ssh_credentials SET updated_at = ? WHERE id = ?") + .run("2000-01-01 00:00:00", primary.id); onWrite.mockClear(); await expect( @@ -401,6 +422,11 @@ describe("HostRepository and CredentialRepository", () => { expect(await repo.credentials.listFolders("user-1")).toEqual(["ops"]); expect(await repo.credentials.listFolders("user-2")).toEqual(["prod"]); expect(onWrite).toHaveBeenCalledTimes(1); + + const renamedRow = repo.sqlite + .prepare("SELECT updated_at FROM ssh_credentials WHERE id = ?") + .get(primary.id) as { updated_at: string }; + expect(renamedRow.updated_at).not.toBe("2000-01-01 00:00:00"); }); it("returns empty credential reads when user data is locked", async () => { @@ -441,14 +467,21 @@ describe("HostRepository and CredentialRepository", () => { (await repo.hosts.listByUserId("user-1")).map((item) => item.id), ).toEqual([host.id]); + repo.sqlite + .prepare("UPDATE ssh_data SET updated_at = ? WHERE id = ?") + .run("2000-01-01 00:00:00", host.id); + const updated = await repo.hosts.updateForUser("user-1", host.id, { name: "web-1-renamed", folder: "prod", }); expect(updated?.name).toBe("web-1-renamed"); + expect(updated?.updatedAt).not.toBe("2000-01-01 00:00:00"); expect(await repo.hosts.findByIdForUser("user-2", host.id)).toBeNull(); - expect(await repo.hosts.deleteForUser("user-1", host.id)).toBe(true); + expect(await repo.hosts.deleteForUser("user-1", host.id)).toEqual({ + syncId: expect.any(String), + }); expect(await repo.hosts.findById(host.id)).toBeNull(); }); @@ -484,15 +517,20 @@ describe("HostRepository and CredentialRepository", () => { expect(raw.password).toBe("encrypted-host-password"); + repo.sqlite + .prepare("UPDATE ssh_data SET updated_at = ? WHERE id = ?") + .run("2000-01-01 00:00:00", created.id); + await repo.hosts.updateEncryptedForUser("user-1", created.id, { password: "updated-secret", }); const updatedRaw = repo.sqlite - .prepare("SELECT password FROM ssh_data WHERE id = ?") - .get(created.id) as { password: string }; + .prepare("SELECT password, updated_at FROM ssh_data WHERE id = ?") + .get(created.id) as { password: string; updated_at: string }; expect(updatedRaw.password).toBe("encrypted-host-password"); + expect(updatedRaw.updated_at).not.toBe("2000-01-01 00:00:00"); expect(DataCrypto.encryptRecord).toHaveBeenCalledWith( "ssh_data", expect.objectContaining({ password: "updated-secret" }), @@ -617,6 +655,9 @@ describe("HostRepository and CredentialRepository", () => { username: "root", authType: "password", }); + repo.sqlite + .prepare("UPDATE ssh_data SET updated_at = ? WHERE id IN (?, ?)") + .run("2000-01-01 00:00:00", first.id, second.id); onWrite.mockClear(); const states = await repo.hosts.listBulkUpdateState("user-1", [ @@ -634,6 +675,12 @@ describe("HostRepository and CredentialRepository", () => { expect((await repo.hosts.findById(first.id))?.folder).toBe("ops"); expect((await repo.hosts.findById(other.id))?.folder).toBeNull(); expect(onWrite).toHaveBeenCalledTimes(1); + expect((await repo.hosts.findById(first.id))?.updatedAt).not.toBe( + "2000-01-01 00:00:00", + ); + expect((await repo.hosts.findById(second.id))?.updatedAt).not.toBe( + "2000-01-01 00:00:00", + ); }); it("records credential usage and increments usage counters", async () => { @@ -686,6 +733,8 @@ describe("HostRepository and CredentialRepository", () => { .run(host.id, "user-2", "user-1"); expect(await repo.hosts.deleteAccessForHost(host.id)).toBe(1); - expect(await repo.hosts.deleteForUser("user-1", host.id)).toBe(true); + expect(await repo.hosts.deleteForUser("user-1", host.id)).toEqual({ + syncId: expect.any(String), + }); }); }); diff --git a/src/backend/tests/database/repositories/host-folder-repository.test.ts b/src/backend/tests/database/repositories/host-folder-repository.test.ts index ca7df0c6..7b94bea4 100644 --- a/src/backend/tests/database/repositories/host-folder-repository.test.ts +++ b/src/backend/tests/database/repositories/host-folder-repository.test.ts @@ -35,6 +35,7 @@ describe("HostFolderRepository", () => { name TEXT NOT NULL, folder TEXT, auth_type TEXT NOT NULL, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -66,6 +67,7 @@ describe("HostFolderRepository", () => { vault_profile_id INTEGER, enable_terminal INTEGER NOT NULL DEFAULT 1, enable_session_logging INTEGER NOT NULL DEFAULT 1, + allow_session_sharing INTEGER NOT NULL DEFAULT 1, enable_command_history INTEGER NOT NULL DEFAULT 1, enable_tunnel INTEGER NOT NULL DEFAULT 1, tunnel_connections TEXT, @@ -129,6 +131,8 @@ describe("HostFolderRepository", () => { host_key_first_seen TEXT, host_key_last_verified TEXT, host_key_changed_count INTEGER DEFAULT 0, + connection_origin TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -139,6 +143,8 @@ describe("HostFolderRepository", () => { name TEXT NOT NULL, color TEXT, icon TEXT, + credential_id INTEGER, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -216,6 +222,7 @@ describe("HostFolderRepository", () => { "prod", "#abcdef", "folder", + undefined, "2026-02-01T00:00:00.000Z", ), ).resolves.toMatchObject({ @@ -228,6 +235,7 @@ describe("HostFolderRepository", () => { "new", null, null, + null, "2026-03-01T00:00:00.000Z", ), ).resolves.toMatchObject({ @@ -237,6 +245,28 @@ describe("HostFolderRepository", () => { expect(writes).toBe(2); }); + it("assigns a credential to a folder and resolves it for nested paths", async () => { + const { repository } = await createRepository(); + + await expect( + repository.upsertMetadata( + "user-1", + "prod", + undefined, + undefined, + 1, + "2026-02-01T00:00:00.000Z", + ), + ).resolves.toMatchObject({ + created: false, + folder: { credentialId: 1 }, + }); + + const folders = await repository.listFolders("user-1"); + const prodFolder = folders.find((f) => f.name === "prod"); + expect(prodFolder?.credentialId).toBe(1); + }); + it("lists and deletes hosts and folder records in a folder tree", async () => { let writes = 0; const { repository, sqlite } = await createRepository(() => { diff --git a/src/backend/tests/database/repositories/host-resolution-repository.test.ts b/src/backend/tests/database/repositories/host-resolution-repository.test.ts index e90eb576..ad9d0351 100644 --- a/src/backend/tests/database/repositories/host-resolution-repository.test.ts +++ b/src/backend/tests/database/repositories/host-resolution-repository.test.ts @@ -61,6 +61,7 @@ describe("HostResolutionRepository", () => { vault_profile_id INTEGER, enable_terminal INTEGER NOT NULL DEFAULT 1, enable_session_logging INTEGER NOT NULL DEFAULT 1, + allow_session_sharing INTEGER NOT NULL DEFAULT 1, enable_command_history INTEGER NOT NULL DEFAULT 1, enable_tunnel INTEGER NOT NULL DEFAULT 1, tunnel_connections TEXT, @@ -124,6 +125,8 @@ describe("HostResolutionRepository", () => { host_key_first_seen TEXT, host_key_last_verified TEXT, host_key_changed_count INTEGER DEFAULT 0, + connection_origin TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -147,6 +150,7 @@ describe("HostResolutionRepository", () => { cert_public_key TEXT, usage_count INTEGER NOT NULL DEFAULT 0, last_used TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -165,6 +169,18 @@ describe("HostResolutionRepository", () => { override_credential_id INTEGER ); + CREATE TABLE ssh_folders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + name TEXT NOT NULL, + color TEXT, + icon TEXT, + credential_id INTEGER, + sync_id TEXT, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + INSERT INTO users (id, username, password_hash) VALUES ('user-1', 'alice', 'hash'), ('user-2', 'bob', 'hash'); INSERT INTO ssh_data ( @@ -185,6 +201,11 @@ describe("HostResolutionRepository", () => { host_id, user_id, granted_by, permission_level, override_credential_id ) VALUES (1, 'user-2', 'user-1', 'execute', 8); + INSERT INTO ssh_folders (user_id, name, credential_id) + VALUES + ('user-1', 'switches', 7), + ('user-1', 'switches / floor1', NULL), + ('user-1', 'no-cred', NULL); `); return new HostResolutionRepository(context, onWrite); @@ -492,4 +513,24 @@ describe("HostResolutionRepository", () => { repository.findOverrideCredentialId(1, "user-1"), ).resolves.toBeNull(); }); + + it("resolves a folder's assigned credential, walking up to parent folders", async () => { + const repository = await createRepository(); + + await expect( + repository.findFolderCredentialId("user-1", "switches"), + ).resolves.toBe(7); + await expect( + repository.findFolderCredentialId("user-1", "switches / floor1"), + ).resolves.toBe(7); + await expect( + repository.findFolderCredentialId("user-1", "no-cred"), + ).resolves.toBeNull(); + await expect( + repository.findFolderCredentialId("user-1", "unknown"), + ).resolves.toBeNull(); + await expect( + repository.findFolderCredentialId("user-1", ""), + ).resolves.toBeNull(); + }); }); diff --git a/src/backend/tests/database/repositories/session-share-repository.test.ts b/src/backend/tests/database/repositories/session-share-repository.test.ts new file mode 100644 index 00000000..a7cc0318 --- /dev/null +++ b/src/backend/tests/database/repositories/session-share-repository.test.ts @@ -0,0 +1,393 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { TestSqliteDatabase } from "./test-support.js"; +import { SessionShareRepository } from "../../../database/repositories/session-share-repository.js"; + +describe("SessionShareRepository", () => { + let adapter: TestSqliteDatabase | null = null; + + afterEach(async () => { + if (adapter) { + await adapter.close(); + adapter = null; + } + }); + + async function createRepository( + onWrite?: () => void | Promise, + ): Promise { + adapter = new TestSqliteDatabase(); + const context = await adapter.connect(); + context.sqlite?.exec(` + CREATE TABLE users ( + id TEXT PRIMARY KEY, + username TEXT NOT NULL, + password_hash TEXT NOT NULL + ); + + CREATE TABLE ssh_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + name TEXT NOT NULL, + ip TEXT + ); + + CREATE TABLE session_shares ( + id TEXT PRIMARY KEY, + host_id INTEGER NOT NULL, + owner_user_id TEXT NOT NULL, + protocol TEXT NOT NULL, + session_id TEXT NOT NULL, + tab_instance_id TEXT, + share_type TEXT NOT NULL, + target_user_id TEXT, + link_token TEXT UNIQUE, + permission_level TEXT NOT NULL DEFAULT 'read-only', + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TEXT NOT NULL, + revoked_at TEXT, + last_joined_at TEXT, + join_count INTEGER NOT NULL DEFAULT 0 + ); + + CREATE TABLE session_share_participants ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + share_id TEXT NOT NULL, + user_id TEXT, + guest_label TEXT, + joined_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + left_at TEXT + ); + + INSERT INTO users (id, username, password_hash) + VALUES ('owner-1', 'alice', 'hash'), ('guest-1', 'bob', 'hash'); + INSERT INTO ssh_data (id, user_id, name, ip) + VALUES (1, 'owner-1', 'host-one', '10.0.0.1'), (2, 'owner-1', 'host-two', '10.0.0.2'); + `); + + return new SessionShareRepository(context, onWrite); + } + + const FAR_FUTURE = "2999-01-01T00:00:00.000Z"; + const FAR_PAST = "2000-01-01T00:00:00.000Z"; + + it("creates a share and finds it by id", async () => { + const repo = await createRepository(); + + const created = await repo.create({ + id: "share-1", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-abc", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + expect(created).toMatchObject({ + id: "share-1", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + linkToken: "token-abc", + permissionLevel: "read-only", + }); + + const found = await repo.findById("share-1"); + expect(found).toMatchObject({ id: "share-1", sessionId: "session-1" }); + }); + + it("findByLinkToken excludes revoked shares", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-revoked", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-revoked", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + expect(await repo.findByLinkToken("token-revoked")).not.toBeNull(); + + await repo.revoke("share-revoked", "owner-1"); + + expect(await repo.findByLinkToken("token-revoked")).toBeNull(); + }); + + it("findByLinkToken excludes expired shares", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-expired", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-expired", + permissionLevel: "read-only", + expiresAt: FAR_PAST, + }); + + expect(await repo.findByLinkToken("token-expired")).toBeNull(); + }); + + it("findByLinkToken returns active, non-expired, non-revoked shares", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-active", + hostId: 1, + ownerUserId: "owner-1", + protocol: "vnc", + sessionId: "guac-session-1", + shareType: "link", + linkToken: "token-active", + permissionLevel: "read-write", + expiresAt: FAR_FUTURE, + }); + + const found = await repo.findByLinkToken("token-active"); + expect(found).toMatchObject({ + id: "share-active", + protocol: "vnc", + permissionLevel: "read-write", + }); + }); + + it("findSharesTargetingUser returns only active user-targeted shares with host/owner metadata", async () => { + const repo = await createRepository(); + + await repo.create({ + id: "share-user-active", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "user", + targetUserId: "guest-1", + permissionLevel: "read-write", + expiresAt: FAR_FUTURE, + }); + + // Expired user share for the same target - must be excluded + await repo.create({ + id: "share-user-expired", + hostId: 2, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-2", + shareType: "user", + targetUserId: "guest-1", + permissionLevel: "read-only", + expiresAt: FAR_PAST, + }); + + // Link share, not targeting a user - must be excluded even though it's active + await repo.create({ + id: "share-link-active", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-3", + shareType: "link", + linkToken: "token-unrelated", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + const shares = await repo.findSharesTargetingUser("guest-1"); + expect(shares).toHaveLength(1); + expect(shares[0]).toMatchObject({ + id: "share-user-active", + hostName: "host-one", + ownerUsername: "alice", + }); + }); + + it("revoke only affects the requesting owner's own share", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-owned", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-owned", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + expect(await repo.revoke("share-owned", "guest-1")).toBe(false); + expect(await repo.revoke("share-owned", "owner-1")).toBe(true); + }); + + it("revokeAsAdmin revokes regardless of owner", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-admin-target", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-admin", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + expect(await repo.revokeAsAdmin("share-admin-target")).toBe(true); + expect(await repo.findByLinkToken("token-admin")).toBeNull(); + }); + + it("deleteExpiredShares removes only expired rows", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-old", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-old", + permissionLevel: "read-only", + expiresAt: FAR_PAST, + }); + await repo.create({ + id: "share-current", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-2", + shareType: "link", + linkToken: "token-current", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + const deletedCount = await repo.deleteExpiredShares(); + expect(deletedCount).toBe(1); + expect(await repo.findById("share-old")).toBeNull(); + expect(await repo.findById("share-current")).not.toBeNull(); + }); + + it("touchShareUsage increments joinCount and sets lastJoinedAt", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-touch", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-touch", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + await repo.touchShareUsage("share-touch", "2026-01-01T00:00:00.000Z"); + let row = await repo.findById("share-touch"); + expect(row?.joinCount).toBe(1); + expect(row?.lastJoinedAt).toBe("2026-01-01T00:00:00.000Z"); + + await repo.touchShareUsage("share-touch", "2026-01-02T00:00:00.000Z"); + row = await repo.findById("share-touch"); + expect(row?.joinCount).toBe(2); + }); + + it("records and closes participant joins", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-participants", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-participants", + permissionLevel: "read-write", + expiresAt: FAR_FUTURE, + }); + + const participant = await repo.recordParticipantJoin( + "share-participants", + null, + "Guest", + ); + expect(participant).toMatchObject({ + shareId: "share-participants", + userId: null, + guestLabel: "Guest", + }); + expect(participant.leftAt).toBeNull(); + + await repo.recordParticipantLeave(participant.id); + }); + + it("write hook fires on mutating operations", async () => { + let writeCount = 0; + const repo = await createRepository(() => { + writeCount += 1; + }); + + await repo.create({ + id: "share-write-hook", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-write-hook", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + expect(writeCount).toBe(1); + + await repo.revoke("share-write-hook", "owner-1"); + expect(writeCount).toBe(2); + }); + + it("deleteSharesForHost removes all shares for a host", async () => { + const repo = await createRepository(); + await repo.create({ + id: "share-host-1a", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "token-h1a", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + await repo.create({ + id: "share-host-1b", + hostId: 1, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-2", + shareType: "link", + linkToken: "token-h1b", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + await repo.create({ + id: "share-host-2", + hostId: 2, + ownerUserId: "owner-1", + protocol: "ssh", + sessionId: "session-3", + shareType: "link", + linkToken: "token-h2", + permissionLevel: "read-only", + expiresAt: FAR_FUTURE, + }); + + expect(await repo.deleteSharesForHost(1)).toBe(2); + expect(await repo.findById("share-host-2")).not.toBeNull(); + }); +}); diff --git a/src/backend/tests/database/repositories/snippet-repository.test.ts b/src/backend/tests/database/repositories/snippet-repository.test.ts index 8dc1d4cf..f4d51377 100644 --- a/src/backend/tests/database/repositories/snippet-repository.test.ts +++ b/src/backend/tests/database/repositories/snippet-repository.test.ts @@ -29,6 +29,7 @@ describe("SnippetRepository", () => { description TEXT, folder TEXT, "order" INTEGER NOT NULL DEFAULT 0, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, host_filter TEXT @@ -40,6 +41,7 @@ describe("SnippetRepository", () => { name TEXT NOT NULL, color TEXT, icon TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); diff --git a/src/backend/tests/database/repositories/sync-tombstone-repository.test.ts b/src/backend/tests/database/repositories/sync-tombstone-repository.test.ts new file mode 100644 index 00000000..cd85f2d9 --- /dev/null +++ b/src/backend/tests/database/repositories/sync-tombstone-repository.test.ts @@ -0,0 +1,138 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { TestSqliteDatabase } from "./test-support.js"; +import { SyncTombstoneRepository } from "../../../database/repositories/sync-tombstone-repository.js"; + +describe("SyncTombstoneRepository", () => { + let adapter: TestSqliteDatabase | null = null; + + afterEach(async () => { + if (adapter) { + await adapter.close(); + adapter = null; + } + }); + + async function createRepository( + onWrite?: () => void | Promise, + ): Promise { + adapter = new TestSqliteDatabase(); + const context = await adapter.connect(); + context.sqlite?.exec(` + CREATE TABLE users ( + id TEXT PRIMARY KEY, + username TEXT NOT NULL, + password_hash TEXT NOT NULL + ); + + CREATE TABLE sync_tombstones ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + entity_type TEXT NOT NULL, + sync_id TEXT NOT NULL, + deleted_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + + INSERT INTO users (id, username, password_hash) + VALUES ('user-1', 'alice', 'hash'), ('user-2', 'bob', 'hash'); + `); + + return new SyncTombstoneRepository(context, onWrite); + } + + it("records a tombstone and lists it back for the owning user", async () => { + let writeCount = 0; + const repo = await createRepository(() => { + writeCount += 1; + }); + + await repo.record("user-1", "hosts", "sync-abc"); + expect(writeCount).toBe(1); + + const rows = await repo.listSince("user-1", "hosts", null); + expect(rows).toHaveLength(1); + expect(rows[0]).toMatchObject({ + userId: "user-1", + entityType: "hosts", + syncId: "sync-abc", + }); + }); + + it("does not record a tombstone for an empty syncId", async () => { + const repo = await createRepository(); + await repo.record("user-1", "hosts", ""); + const rows = await repo.listSince("user-1", "hosts", null); + expect(rows).toHaveLength(0); + }); + + it("recordMany writes multiple tombstones and filters out falsy ids", async () => { + let writeCount = 0; + const repo = await createRepository(() => { + writeCount += 1; + }); + + await repo.recordMany("user-1", "hosts", ["a", "", "b", "c"]); + expect(writeCount).toBe(1); + + const rows = await repo.listSince("user-1", "hosts", null); + expect(rows.map((r) => r.syncId).sort()).toEqual(["a", "b", "c"]); + }); + + it("recordMany is a no-op when given no syncIds", async () => { + let writeCount = 0; + const repo = await createRepository(() => { + writeCount += 1; + }); + + await repo.recordMany("user-1", "hosts", []); + expect(writeCount).toBe(0); + }); + + it("scopes listSince by userId and entityType", async () => { + const repo = await createRepository(); + await repo.record("user-1", "hosts", "sync-1"); + await repo.record("user-1", "snippets", "sync-2"); + await repo.record("user-2", "hosts", "sync-3"); + + const rows = await repo.listSince("user-1", "hosts", null); + expect(rows).toHaveLength(1); + expect(rows[0].syncId).toBe("sync-1"); + }); + + it("filters listSince by the since timestamp", async () => { + const adapterLocal = new TestSqliteDatabase(); + adapter = adapterLocal; + const context = await adapterLocal.connect(); + context.sqlite?.exec(` + CREATE TABLE users ( + id TEXT PRIMARY KEY, + username TEXT NOT NULL, + password_hash TEXT NOT NULL + ); + + CREATE TABLE sync_tombstones ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + entity_type TEXT NOT NULL, + sync_id TEXT NOT NULL, + deleted_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + + INSERT INTO users (id, username, password_hash) + VALUES ('user-1', 'alice', 'hash'); + + INSERT INTO sync_tombstones (user_id, entity_type, sync_id, deleted_at) + VALUES + ('user-1', 'hosts', 'old', '2026-01-01T00:00:00.000Z'), + ('user-1', 'hosts', 'new', '2026-06-01T00:00:00.000Z'); + `); + const repo = new SyncTombstoneRepository(context); + + const rows = await repo.listSince( + "user-1", + "hosts", + "2026-03-01T00:00:00.000Z", + ); + expect(rows).toHaveLength(1); + expect(rows[0].syncId).toBe("new"); + }); +}); diff --git a/src/backend/tests/database/repositories/user-data-export-repository.test.ts b/src/backend/tests/database/repositories/user-data-export-repository.test.ts index d52c2c35..2a2796f3 100644 --- a/src/backend/tests/database/repositories/user-data-export-repository.test.ts +++ b/src/backend/tests/database/repositories/user-data-export-repository.test.ts @@ -49,6 +49,7 @@ describe("UserDataExportRepository", () => { vault_profile_id INTEGER, enable_terminal INTEGER NOT NULL DEFAULT 1, enable_session_logging INTEGER NOT NULL DEFAULT 1, + allow_session_sharing INTEGER NOT NULL DEFAULT 1, enable_command_history INTEGER NOT NULL DEFAULT 1, enable_tunnel INTEGER NOT NULL DEFAULT 1, tunnel_connections TEXT, @@ -112,6 +113,8 @@ describe("UserDataExportRepository", () => { host_key_first_seen TEXT, host_key_last_verified TEXT, host_key_changed_count INTEGER DEFAULT 0, + connection_origin TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -135,6 +138,7 @@ describe("UserDataExportRepository", () => { cert_public_key TEXT, usage_count INTEGER NOT NULL DEFAULT 0, last_used TEXT, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); diff --git a/src/backend/tests/database/repositories/user-preference-repository.test.ts b/src/backend/tests/database/repositories/user-preference-repository.test.ts index 3ff56e19..307fa363 100644 --- a/src/backend/tests/database/repositories/user-preference-repository.test.ts +++ b/src/backend/tests/database/repositories/user-preference-repository.test.ts @@ -47,6 +47,8 @@ describe("UserPreferenceRepository", () => { hidden_rail_tabs TEXT, compact_host_view INTEGER, status_color_scheme TEXT, + custom_themes TEXT, + custom_keybindings TEXT, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); diff --git a/src/backend/tests/database/repositories/vault-profile-repository.test.ts b/src/backend/tests/database/repositories/vault-profile-repository.test.ts index 2c6cdfc9..c0ade51e 100644 --- a/src/backend/tests/database/repositories/vault-profile-repository.test.ts +++ b/src/backend/tests/database/repositories/vault-profile-repository.test.ts @@ -40,6 +40,7 @@ describe("VaultProfileRepository", () => { valid_principals TEXT, key_type TEXT, shared INTEGER NOT NULL DEFAULT 0, + sync_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -119,8 +120,8 @@ describe("VaultProfileRepository", () => { }); expect(await repo.updateById(999, { name: "missing" })).toBeNull(); - expect(await repo.deleteById(1)).toBe(true); - expect(await repo.deleteById(1)).toBe(false); + expect(await repo.deleteById(1)).toEqual({ syncId: null }); + expect(await repo.deleteById(1)).toBeNull(); expect(await repo.findById(1)).toBeNull(); expect(writeCount).toBe(2); }); diff --git a/src/backend/tests/database/routes/desktop-auto-session.test.ts b/src/backend/tests/database/routes/desktop-auto-session.test.ts new file mode 100644 index 00000000..9c11826e --- /dev/null +++ b/src/backend/tests/database/routes/desktop-auto-session.test.ts @@ -0,0 +1,146 @@ +import { describe, expect, it } from "vitest"; +import type { Request } from "express"; +import type { UserRecord } from "../../../database/repositories/user-repository.js"; +import { + isLoopbackRequest, + extractBearerOrCookieToken, + resolveDesktopAutoSessionUser, +} from "../../../database/routes/desktop-auto-session.js"; + +function makeUser(overrides: Partial = {}): UserRecord { + return { + id: "user-1", + username: "local", + passwordHash: "", + isOidc: false, + totpEnabled: false, + isAdmin: false, + registeredAt: "2026-01-01T00:00:00.000Z", + ...overrides, + } as UserRecord; +} + +describe("isLoopbackRequest", () => { + it.each(["127.0.0.1", "::1", "::ffff:127.0.0.1"])( + "accepts %s as loopback", + (ip) => { + expect(isLoopbackRequest({ ip, socket: {} } as unknown as Request)).toBe( + true, + ); + }, + ); + + it("accepts an IPv4-mapped loopback suffix", () => { + expect( + isLoopbackRequest({ + ip: "::ffff:127.0.0.1", + socket: {}, + } as unknown as Request), + ).toBe(true); + }); + + it("rejects a non-loopback IP", () => { + expect( + isLoopbackRequest({ + ip: "192.168.1.50", + socket: {}, + } as unknown as Request), + ).toBe(false); + }); + + it("falls back to socket.remoteAddress when req.ip is empty", () => { + expect( + isLoopbackRequest({ + ip: "", + socket: { remoteAddress: "127.0.0.1" }, + } as unknown as Request), + ).toBe(true); + }); +}); + +describe("extractBearerOrCookieToken", () => { + it("prefers the jwt cookie over the Authorization header", () => { + const req = { + cookies: { jwt: "cookie-token" }, + headers: { authorization: "Bearer header-token" }, + } as unknown as Request; + expect(extractBearerOrCookieToken(req)).toBe("cookie-token"); + }); + + it("falls back to a Bearer Authorization header", () => { + const req = { + cookies: {}, + headers: { authorization: "Bearer header-token" }, + } as unknown as Request; + expect(extractBearerOrCookieToken(req)).toBe("header-token"); + }); + + it("returns undefined when neither is present", () => { + const req = { cookies: {}, headers: {} } as unknown as Request; + expect(extractBearerOrCookieToken(req)).toBeUndefined(); + }); + + it("ignores a non-Bearer Authorization header", () => { + const req = { + cookies: {}, + headers: { authorization: "Basic abc123" }, + } as unknown as Request; + expect(extractBearerOrCookieToken(req)).toBeUndefined(); + }); +}); + +describe("resolveDesktopAutoSessionUser", () => { + it("returns the sole local user regardless of having a real password", () => { + const user = makeUser({ passwordHash: "$2a$10$realbcryptvaluehere" }); + expect(resolveDesktopAutoSessionUser([user])).toBe(user); + }); + + it("returns the sole local user even when OIDC-enabled", () => { + const user = makeUser({ isOidc: true }); + expect(resolveDesktopAutoSessionUser([user])).toBe(user); + }); + + it("returns the sole local user even when TOTP-enabled", () => { + const user = makeUser({ totpEnabled: true }); + expect(resolveDesktopAutoSessionUser([user])).toBe(user); + }); + + it("returns the auto-provisioned passwordless placeholder", () => { + const user = makeUser({ passwordHash: "" }); + expect(resolveDesktopAutoSessionUser([user])).toBe(user); + }); + + it("declines when zero users exist", () => { + expect(resolveDesktopAutoSessionUser([])).toBeNull(); + }); + + it("never declines for a multi-user local database -- prefers the admin account", () => { + const admin = makeUser({ + id: "user-2", + isAdmin: true, + registeredAt: "2026-02-01T00:00:00.000Z", + }); + const result = resolveDesktopAutoSessionUser([ + makeUser({ + id: "user-1", + isAdmin: false, + registeredAt: "2026-01-01T00:00:00.000Z", + }), + admin, + ]); + expect(result).toBe(admin); + }); + + it("falls back to the earliest-registered account when no admin exists", () => { + const earliest = makeUser({ + id: "user-1", + registeredAt: "2026-01-01T00:00:00.000Z", + }); + const result = resolveDesktopAutoSessionUser([ + makeUser({ id: "user-2", registeredAt: "2026-03-01T00:00:00.000Z" }), + earliest, + makeUser({ id: "user-3", registeredAt: "2026-02-01T00:00:00.000Z" }), + ]); + expect(result).toBe(earliest); + }); +}); diff --git a/src/backend/tests/database/routes/host-normalizers.test.ts b/src/backend/tests/database/routes/host-normalizers.test.ts index eb0a863f..812f99bf 100644 --- a/src/backend/tests/database/routes/host-normalizers.test.ts +++ b/src/backend/tests/database/routes/host-normalizers.test.ts @@ -160,6 +160,28 @@ describe("stripSensitiveFields", () => { expect(result.hasPassword).toBe(false); expect(result.hasKey).toBe(false); }); + + it("strips rdp/vnc/telnet passwords and adds their presence flags", () => { + const result = stripSensitiveFields({ + name: "rdp-box", + rdpPassword: "rdp-secret", + vncPassword: "vnc-secret", + telnetPassword: "telnet-secret", + }); + expect(result.rdpPassword).toBeUndefined(); + expect(result.vncPassword).toBeUndefined(); + expect(result.telnetPassword).toBeUndefined(); + expect(result.hasRdpPassword).toBe(true); + expect(result.hasVncPassword).toBe(true); + expect(result.hasTelnetPassword).toBe(true); + }); + + it("marks rdp/vnc/telnet presence flags false when absent", () => { + const result = stripSensitiveFields({ name: "rdp-box" }); + expect(result.hasRdpPassword).toBe(false); + expect(result.hasVncPassword).toBe(false); + expect(result.hasTelnetPassword).toBe(false); + }); }); describe("transformHostResponse", () => { diff --git a/src/backend/tests/database/routes/keybinding-validation.test.ts b/src/backend/tests/database/routes/keybinding-validation.test.ts new file mode 100644 index 00000000..0802be37 --- /dev/null +++ b/src/backend/tests/database/routes/keybinding-validation.test.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from "vitest"; +import { + isValidKeyCombo, + isValidKeybindingAction, + isValidKeybinding, +} from "../../../database/routes/keybinding-validation.js"; + +const validCombo = { + key: "c", + isCode: false, + ctrl: true, + alt: false, + shift: false, + meta: false, +}; + +describe("isValidKeyCombo", () => { + it("accepts a well-formed combo", () => { + expect(isValidKeyCombo(validCombo)).toBe(true); + }); + + it("rejects a combo missing a boolean field", () => { + const { ctrl: _ctrl, ...rest } = validCombo; + expect(isValidKeyCombo(rest)).toBe(false); + }); + + it("rejects a non-object", () => { + expect(isValidKeyCombo("ctrl+c")).toBe(false); + expect(isValidKeyCombo(null)).toBe(false); + }); +}); + +describe("isValidKeybindingAction", () => { + it("accepts copy and paste with no extra fields", () => { + expect(isValidKeybindingAction({ type: "copy" })).toBe(true); + expect(isValidKeybindingAction({ type: "paste" })).toBe(true); + }); + + it("rejects an unknown action type", () => { + expect(isValidKeybindingAction({ type: "explode" })).toBe(false); + }); + + it("requires text for sendText", () => { + expect(isValidKeybindingAction({ type: "sendText" })).toBe(false); + expect(isValidKeybindingAction({ type: "sendText", text: "ls -la" })).toBe( + true, + ); + }); + + it("requires a single-letter controlCode for sendControlCode", () => { + expect( + isValidKeybindingAction({ type: "sendControlCode", controlCode: "w" }), + ).toBe(true); + expect( + isValidKeybindingAction({ type: "sendControlCode", controlCode: "ww" }), + ).toBe(false); + expect( + isValidKeybindingAction({ type: "sendControlCode", controlCode: "1" }), + ).toBe(false); + expect(isValidKeybindingAction({ type: "sendControlCode" })).toBe(false); + }); + + it("requires snippetId for runSnippet", () => { + expect( + isValidKeybindingAction({ type: "runSnippet", snippetId: "42" }), + ).toBe(true); + expect(isValidKeybindingAction({ type: "runSnippet" })).toBe(false); + }); +}); + +describe("isValidKeybinding", () => { + const base = { + id: "kb-1", + enabled: true, + combo: validCombo, + action: { type: "copy" }, + }; + + it("accepts a well-formed keybinding", () => { + expect(isValidKeybinding(base)).toBe(true); + }); + + it("rejects a keybinding missing id", () => { + const { id: _id, ...rest } = base; + expect(isValidKeybinding(rest)).toBe(false); + }); + + it("rejects a keybinding missing enabled", () => { + const { enabled: _enabled, ...rest } = base; + expect(isValidKeybinding(rest)).toBe(false); + }); + + it("rejects a keybinding with an invalid combo", () => { + expect(isValidKeybinding({ ...base, combo: {} })).toBe(false); + }); + + it("rejects a keybinding with an invalid action", () => { + expect(isValidKeybinding({ ...base, action: { type: "sendText" } })).toBe( + false, + ); + }); +}); diff --git a/src/backend/tests/database/routes/sync.test.ts b/src/backend/tests/database/routes/sync.test.ts new file mode 100644 index 00000000..55d27bb2 --- /dev/null +++ b/src/backend/tests/database/routes/sync.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "vitest"; +import { + isValidEntityType, + stripWritePayload, +} from "../../../database/routes/sync.js"; + +describe("isValidEntityType", () => { + it("accepts every whitelisted sync entity type", () => { + for (const type of [ + "hosts", + "sshCredentials", + "sshFolders", + "snippets", + "snippetFolders", + "vaultProfiles", + "dashboardServiceLinks", + "homepageItems", + ]) { + expect(isValidEntityType(type)).toBe(true); + } + }); + + it("rejects unknown or non-string entity types", () => { + expect(isValidEntityType("hostAccess")).toBe(false); + expect(isValidEntityType("")).toBe(false); + expect(isValidEntityType(undefined)).toBe(false); + expect(isValidEntityType(42)).toBe(false); + }); +}); + +describe("stripWritePayload", () => { + it("strips id, userId, and syncId from every entity type", () => { + const payload = { + id: 1, + userId: "user-1", + syncId: "abc", + name: "prod-db", + }; + expect(stripWritePayload("sshFolders", payload)).toEqual({ + name: "prod-db", + }); + }); + + it("also strips desktop-only fields flagged read-only for hosts", () => { + const payload = { + id: 1, + userId: "user-1", + syncId: "abc", + name: "web", + connectionOrigin: "remote", + }; + expect(stripWritePayload("hosts", payload)).toEqual({ name: "web" }); + }); + + it("does not mutate the original payload object", () => { + const payload = { id: 1, userId: "user-1", syncId: "abc", name: "x" }; + stripWritePayload("snippets", payload); + expect(payload).toEqual({ + id: 1, + userId: "user-1", + syncId: "abc", + name: "x", + }); + }); +}); diff --git a/src/backend/tests/hosts/auth-manager.test.ts b/src/backend/tests/hosts/auth-manager.test.ts new file mode 100644 index 00000000..2edcc9d7 --- /dev/null +++ b/src/backend/tests/hosts/auth-manager.test.ts @@ -0,0 +1,204 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("../../database/repositories/factory.js", () => ({ + createCurrentHostResolutionRepository: () => ({ + findCredentialByIdForUser: async () => null, + }), +})); + +vi.mock("../../utils/logger.js", () => ({ + sshLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, + authLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, +})); + +import { SSHAuthManager } from "../../hosts/auth-manager.js"; + +function createManager() { + const sent: Record[] = []; + const ws = { send: (data: string) => sent.push(JSON.parse(data)) } as any; + const manager = new SSHAuthManager({ + userId: "user-1", + ws, + hostId: 1, + isKeyboardInteractive: false, + keyboardInteractiveResponded: false, + keyboardInteractiveFinish: null, + totpPromptSent: false, + warpgateAuthPromptSent: false, + totpTimeout: null, + warpgateAuthTimeout: null, + totpAttempts: 0, + }); + return { manager, sent }; +} + +describe("SSHAuthManager.handleKeyboardInteractive", () => { + beforeEach(() => { + vi.useRealTimers(); + }); + + it("routes a TOTP verification prompt to the totp flow", () => { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "", + "", + "", + [{ prompt: "Verification code: ", echo: true }], + finish, + { username: "root", authType: "none" }, + ); + + expect(sent).toEqual([ + { + type: "connection_log", + data: { + stage: "auth", + level: "info", + message: "TOTP verification required", + }, + }, + { type: "totp_required", prompt: "Verification code: " }, + ]); + expect(finish).not.toHaveBeenCalled(); + }); + + it("forwards echo:true for a JumpCloud-style push/TOTP menu prompt", () => { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "", + "", + "", + [{ prompt: "Choose [1] Push, or [2] TOTP: ", echo: true }], + finish, + { username: "root", authType: "none" }, + ); + + expect(sent).toEqual([ + { + type: "connection_log", + data: { + stage: "auth", + level: "info", + message: "Password authentication required", + }, + }, + { + type: "password_required", + prompt: "Choose [1] Push, or [2] TOTP: ", + echo: true, + }, + ]); + }); + + it("silently auto-answers a plain password prompt when a stored password exists", () => { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "", + "", + "", + [{ prompt: "Password: ", echo: false }], + finish, + { username: "root", password: "hunter2", authType: "password" }, + ); + + expect(finish).toHaveBeenCalledWith(["hunter2"]); + expect(sent).toEqual([]); + }); + + it("prompts the user for a push-confirm prompt and accepts an empty response", () => { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "", + "", + "", + [{ prompt: "Press enter to send Push request: ", echo: true }], + finish, + { username: "root", authType: "none" }, + ); + + expect(sent).toEqual([ + { + type: "connection_log", + data: { + stage: "auth", + level: "info", + message: "Password authentication required", + }, + }, + { + type: "password_required", + prompt: "Press enter to send Push request: ", + echo: true, + }, + ]); + + manager.context.keyboardInteractiveFinish?.([""]); + + expect(finish).toHaveBeenCalledWith([""]); + }); + + it("uses a longer timeout for push-style prompts than generic prompts", () => { + vi.useFakeTimers(); + try { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "", + "", + "", + [{ prompt: "Press enter to send Push request: ", echo: true }], + finish, + { username: "root", authType: "none" }, + ); + + vi.advanceTimersByTime(180001); + expect(sent.some((m) => m.type === "error")).toBe(false); + + vi.advanceTimersByTime(120000); + expect(sent.some((m) => m.type === "error")).toBe(true); + } finally { + vi.useRealTimers(); + } + }); + + it("routes Warpgate prompts to the warpgate flow, not the generic path", () => { + const { manager, sent } = createManager(); + const finish = vi.fn(); + + manager.handleKeyboardInteractive( + "Warpgate Authentication", + "Visit https://warpgate.example.com/auth to continue. Security key: AB12", + "", + [{ prompt: "Press enter once done: ", echo: true }], + finish, + { username: "root", authType: "none" }, + ); + + expect(sent).toEqual([ + { + type: "connection_log", + data: { + stage: "auth", + level: "info", + message: "Warpgate authentication required", + }, + }, + { + type: "warpgate_auth_required", + url: "https://warpgate.example.com/auth", + securityKey: "AB12", + instructions: + "Visit https://warpgate.example.com/auth to continue. Security key: AB12", + }, + ]); + }); +}); diff --git a/src/backend/tests/hosts/guacamole/token-service.test.ts b/src/backend/tests/hosts/guacamole/token-service.test.ts index de3069a5..d6cfc994 100644 --- a/src/backend/tests/hosts/guacamole/token-service.test.ts +++ b/src/backend/tests/hosts/guacamole/token-service.test.ts @@ -65,4 +65,41 @@ describe("GuacamoleTokenService", () => { expect(tokenService.decryptToken(token)?.recording).toEqual(recording); }); + + it("preserves termixMeta through the encrypt/decrypt round trip", () => { + const termixMeta = { + termixConnectId: "connect-1", + hostId: 7, + ownerUserId: "user-1", + protocol: "rdp" as const, + }; + const token = tokenService.createRdpToken( + "windows.example.test", + "Administrator", + "secret", + {}, + undefined, + termixMeta, + ); + + expect(tokenService.decryptToken(token)?.termixMeta).toEqual(termixMeta); + }); + + it("createJoinToken sets connection.join, not connection.type", () => { + const token = tokenService.createJoinToken("guacd-conn-123", true); + const decrypted = tokenService.decryptToken(token); + + expect(decrypted?.connection.join).toBe("guacd-conn-123"); + expect(decrypted?.connection.type).toBeUndefined(); + expect(decrypted?.connection.readOnly).toBe(true); + }); + + it("createJoinToken round-trips a read-write join through decryptToken", () => { + const token = tokenService.createJoinToken("guacd-conn-456", false); + const decrypted = tokenService.decryptToken(token); + + expect(decrypted?.connection.join).toBe("guacd-conn-456"); + expect(decrypted?.connection.readOnly).toBe(false); + expect(decrypted?.recording).toBeUndefined(); + }); }); diff --git a/src/backend/tests/hosts/host-resolver.test.ts b/src/backend/tests/hosts/host-resolver.test.ts index 9729fd97..68a9fc56 100644 --- a/src/backend/tests/hosts/host-resolver.test.ts +++ b/src/backend/tests/hosts/host-resolver.test.ts @@ -8,6 +8,7 @@ const state = vi.hoisted(() => ({ credentials: new Map>(), sharedSecret: null as Record | null, auditCalls: [] as Record[], + folderCredentialId: null as number | null, })); vi.mock("../../database/repositories/factory.js", () => ({ @@ -17,6 +18,7 @@ vi.mock("../../database/repositories/factory.js", () => ({ findOverrideCredentialId: async () => state.overrideCredentialId, findCredentialByIdForUser: async (credentialId: number, userId: string) => state.credentials.get(`${credentialId}:${userId}`) ?? null, + findFolderCredentialId: async () => state.folderCredentialId, }), createCurrentVaultProfileRepository: () => ({ findById: async () => null, @@ -101,6 +103,7 @@ beforeEach(() => { state.credentials.clear(); state.sharedSecret = null; state.auditCalls = []; + state.folderCredentialId = null; }); describe("resolveHostById", () => { @@ -138,6 +141,63 @@ describe("resolveHostById", () => { expect(host.sudoPassword).toBe("owner-sudo"); }); + it("falls back to the host's folder-assigned credential when none is set on the host", async () => { + state.host = baseHost({ + authType: "credential", + credentialId: null, + folder: "switches", + username: "", + password: null, + }); + state.folderCredentialId = 11; + state.credentials.set("11:owner", { + id: 11, + username: "folder-user", + authType: "password", + password: "folder-pass", + privateKey: null, + key: null, + keyPassword: null, + keyType: null, + }); + + const host = (await resolveHostById(42, "owner")) as Record< + string, + unknown + >; + expect(host.password).toBe("folder-pass"); + expect(host.username).toBe("folder-user"); + expect(host.authType).toBe("password"); + }); + + it("prefers the host's own credential over its folder's credential", async () => { + state.host = baseHost({ + authType: "credential", + credentialId: 9, + folder: "switches", + username: "", + password: null, + }); + state.folderCredentialId = 11; + state.credentials.set("9:owner", { + id: 9, + username: "host-user", + authType: "password", + password: "host-pass", + privateKey: null, + key: null, + keyPassword: null, + keyType: null, + }); + + const host = (await resolveHostById(42, "owner")) as Record< + string, + unknown + >; + expect(host.username).toBe("host-user"); + expect(host.password).toBe("host-pass"); + }); + it("uses the share snapshot for a non-owner and strips owner-only secrets", async () => { state.host = baseHost({ username: "" }); state.sharedSecret = { diff --git a/src/backend/tests/hosts/metrics/widgets/disk-collector.test.ts b/src/backend/tests/hosts/metrics/widgets/disk-collector.test.ts new file mode 100644 index 00000000..70d17e30 --- /dev/null +++ b/src/backend/tests/hosts/metrics/widgets/disk-collector.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from "vitest"; +import { + parseDfLines, + findWorstMountIndex, +} from "../../../../hosts/metrics/widgets/disk-collector.js"; + +describe("parseDfLines", () => { + it("parses df -P output into rows", () => { + const output = + "/dev/nvme0n1p2 3848290697216 1046898851840 2606516101120 29% /\n" + + "/dev/nvme1n1p1 15393162788864 15239230844928 153931922841 99% /data\n"; + const rows = parseDfLines(output); + expect(rows).toHaveLength(2); + expect(rows[0].mount).toBe("/"); + expect(rows[1].mount).toBe("/data"); + }); + + it("filters out pseudo filesystems", () => { + const output = + "tmpfs 8000 0 8000 0% /dev/shm\n" + + "overlay 100 50 50 50% /\n" + + "/dev/sda1 100 50 50 50% /mnt/data\n"; + const rows = parseDfLines(output); + expect(rows).toHaveLength(1); + expect(rows[0].mount).toBe("/mnt/data"); + }); +}); + +describe("findWorstMountIndex", () => { + it("picks the most-utilized mount, not just the first row", () => { + const rows = parseDfLines( + "/dev/nvme0n1p2 3848290697216 1046898851840 2606516101120 29% /\n" + + "/dev/nvme1n1p1 15393162788864 15239230844928 153931922841 99% /data\n", + ); + const worst = findWorstMountIndex(rows); + expect(worst.index).toBe(1); + expect(worst.totalBytes).toBe(15393162788864); + expect(worst.usedBytes).toBe(15239230844928); + }); + + it("falls back to the only mount available", () => { + const rows = parseDfLines("/dev/sda1 100 30 70 30% /\n"); + const worst = findWorstMountIndex(rows); + expect(worst.index).toBe(0); + }); + + it("skips rows with invalid or zero totals", () => { + const rows = parseDfLines( + "/dev/sda1 0 0 0 0% /broken\n" + "/dev/sda2 100 40 60 40% /ok\n", + ); + const worst = findWorstMountIndex(rows); + expect(worst.index).toBe(1); + }); + + it("returns index -1 when there are no usable rows", () => { + const worst = findWorstMountIndex([]); + expect(worst.index).toBe(-1); + expect(worst.totalBytes).toBe(0); + }); +}); diff --git a/src/backend/tests/hosts/session-sharing/routes.test.ts b/src/backend/tests/hosts/session-sharing/routes.test.ts new file mode 100644 index 00000000..052d1701 --- /dev/null +++ b/src/backend/tests/hosts/session-sharing/routes.test.ts @@ -0,0 +1,513 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { Request, Response } from "express"; + +const state = vi.hoisted(() => ({ + currentUserId: "user-1", + globalSharingEnabled: true, + hosts: new Map(), + hostOwnerAccess: new Map(), // `${userId}:${hostId}` -> hasAccess + sshSessions: new Map(), + guacSessions: new Map< + string, + { ownerUserId: string; hostId: number; protocol: string } + >(), + shares: new Map>(), + admins: new Set(), +})); + +vi.mock("../../../utils/logger.js", () => ({ + sshLogger: { + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, +})); + +vi.mock("../../../utils/auth-manager.js", () => ({ + AuthManager: { + getInstance: () => ({ + createAuthMiddleware: + () => + (req: Record, _res: unknown, next: () => void) => { + req.userId = state.currentUserId; + next(); + }, + }), + }, +})); + +vi.mock("../../../utils/permission-manager.js", () => ({ + PermissionManager: { + getInstance: () => ({ + canAccessHost: async ( + userId: string, + hostId: number, + _action: string, + ) => ({ + hasAccess: state.hostOwnerAccess.get(`${userId}:${hostId}`) ?? false, + }), + isAdmin: async (userId: string) => state.admins.has(userId), + }), + }, +})); + +vi.mock("../../../hosts/terminal/session-manager.js", () => ({ + sessionManager: { + getSession: (sessionId: string) => { + const session = state.sshSessions.get(sessionId); + if (!session) return null; + return { ...session }; + }, + ownerEndSession: vi.fn(), + }, +})); + +vi.mock("../../../hosts/guacamole/guacamole-server.js", () => ({ + getGuacSessionInfo: (guacamoleConnectionId: string) => + state.guacSessions.get(guacamoleConnectionId) ?? null, +})); + +vi.mock("../../../hosts/guacamole/token-service.js", () => ({ + GuacamoleTokenService: { + getInstance: () => ({ + createJoinToken: (guacamoleConnectionId: string, readOnly: boolean) => + `join-token:${guacamoleConnectionId}:${readOnly}`, + }), + }, +})); + +vi.mock("../../../database/repositories/factory.js", () => ({ + createCurrentSessionShareRepository: () => ({ + create: async (input: Record) => { + const row = { + ...input, + createdAt: "2026-07-20T00:00:00.000Z", + revokedAt: null, + lastJoinedAt: null, + joinCount: 0, + }; + state.shares.set(input.id as string, row); + return row; + }, + findById: async (id: string) => state.shares.get(id) ?? null, + findByLinkToken: async (linkToken: string) => { + for (const share of state.shares.values()) { + if ( + share.linkToken === linkToken && + !share.revokedAt && + (share.expiresAt as string) > new Date().toISOString() + ) { + return share; + } + } + return null; + }, + findActiveSharesForHost: async (hostId: number, ownerUserId: string) => { + return [...state.shares.values()].filter( + (s) => + s.hostId === hostId && s.ownerUserId === ownerUserId && !s.revokedAt, + ); + }, + revoke: async (shareId: string, requestingUserId: string) => { + const share = state.shares.get(shareId); + if (!share || share.ownerUserId !== requestingUserId) return false; + share.revokedAt = "2026-07-20T01:00:00.000Z"; + return true; + }, + revokeAsAdmin: async (shareId: string) => { + const share = state.shares.get(shareId); + if (!share) return false; + share.revokedAt = "2026-07-20T01:00:00.000Z"; + return true; + }, + touchShareUsage: async () => {}, + recordParticipantJoin: async () => ({ id: 1 }), + }), + createCurrentSettingsRepository: () => ({ + getBoolean: async () => state.globalSharingEnabled, + }), + createCurrentHostResolutionRepository: () => ({ + findHostOwnerId: async (hostId: number) => + state.hosts.get(hostId)?.userId ?? null, + findHostById: async (hostId: number) => { + const host = state.hosts.get(hostId); + if (!host) return null; + return { allowSessionSharing: host.allowSessionSharing }; + }, + }), +})); + +const { default: router } = + await import("../../../hosts/session-sharing/routes.js"); + +type RouteLayer = { + route?: { + path: string; + methods: Record; + stack: { + handle: (req: Request, res: Response, next: () => void) => unknown; + }[]; + }; +}; + +function findHandlers(method: string, path: string) { + const layers = (router as unknown as { stack: RouteLayer[] }).stack; + const layer = layers.find( + (l) => l.route?.path === path && l.route.methods[method], + ); + if (!layer?.route) throw new Error(`No route for ${method} ${path}`); + return layer.route.stack.map((s) => s.handle); +} + +function makeReqRes(overrides: { + body?: Record; + params?: Record; + ip?: string; +}) { + const req = { + body: overrides.body ?? {}, + params: overrides.params ?? {}, + headers: {}, + ip: overrides.ip ?? "127.0.0.1", + socket: { remoteAddress: overrides.ip ?? "127.0.0.1" }, + } as unknown as Request; + + const res = { + statusCode: 200, + jsonBody: null as unknown, + status(code: number) { + (this as unknown as { statusCode: number }).statusCode = code; + return this; + }, + json(payload: unknown) { + (this as unknown as { jsonBody: unknown }).jsonBody = payload; + return this; + }, + } as unknown as Response & { statusCode: number; jsonBody: unknown }; + + return { req, res }; +} + +async function invoke( + method: string, + path: string, + overrides: { + body?: Record; + params?: Record; + ip?: string; + } = {}, +) { + const handlers = findHandlers(method, path); + const { req, res } = makeReqRes(overrides); + + for (const handler of handlers) { + let calledNext = false; + await handler(req, res, () => { + calledNext = true; + }); + if (!calledNext) break; + } + + return res as unknown as { + statusCode: number; + jsonBody: Record | null; + }; +} + +beforeEach(() => { + state.currentUserId = "user-1"; + state.globalSharingEnabled = true; + state.hosts = new Map([ + [1, { userId: "user-1", allowSessionSharing: true }], + [2, { userId: "user-1", allowSessionSharing: false }], + ]); + state.hostOwnerAccess = new Map([["user-2:1", true]]); + state.sshSessions = new Map([ + ["session-1", { userId: "user-1", isConnected: true }], + ]); + state.guacSessions = new Map([ + ["guac-conn-1", { ownerUserId: "user-1", hostId: 1, protocol: "vnc" }], + ]); + state.shares = new Map(); + state.admins = new Set(); +}); + +describe("POST /session-sharing/create", () => { + it("rejects a caller who does not own the live session", async () => { + state.currentUserId = "user-2"; + const res = await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + + expect(res.statusCode).toBe(403); + expect(res.jsonBody).toMatchObject({ + error: "You do not own this live session", + }); + }); + + it("creates a link share for the session owner", async () => { + const res = await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.jsonBody).toMatchObject({ shareId: expect.any(String) }); + expect((res.jsonBody as Record).linkToken).toBeTruthy(); + }); + + it("rejects a user share when the target lacks host access", async () => { + const res = await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "user", + targetUserId: "no-access-user", + permissionLevel: "read-write", + }, + }); + + expect(res.statusCode).toBe(403); + expect(res.jsonBody).toMatchObject({ + error: "Target user does not have access to this host", + }); + }); + + it("global kill switch overrides an enabled per-host toggle", async () => { + state.globalSharingEnabled = false; + const res = await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + + expect(res.statusCode).toBe(403); + expect(res.jsonBody).toMatchObject({ + error: "Session sharing is disabled for this host", + }); + }); + + it("rejects when the per-host toggle is off even though global is on", async () => { + const res = await invoke("post", "/create", { + body: { + hostId: 2, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + + expect(res.statusCode).toBe(403); + }); +}); + +describe("GET /session-sharing/resolve/:linkToken", () => { + async function createActiveLinkShare( + overrides: Partial> = {}, + ) { + await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + ...overrides, + }, + }); + const [share] = [...state.shares.values()]; + return share as { linkToken: string; id: string }; + } + + it("never includes hostname, ip, username, or hostId in the response body", async () => { + const share = await createActiveLinkShare(); + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: share.linkToken }, + }); + + expect(res.statusCode).toBe(200); + const body = res.jsonBody as Record; + const serialized = JSON.stringify(body).toLowerCase(); + + expect(body).not.toHaveProperty("hostname"); + expect(body).not.toHaveProperty("ip"); + expect(body).not.toHaveProperty("username"); + expect(body).not.toHaveProperty("hostId"); + expect(body).not.toHaveProperty("hostName"); + expect(serialized).not.toContain("10.0.0"); + expect(serialized).not.toContain("hostname"); + expect(serialized).not.toContain('"ip"'); + expect(serialized).not.toContain("username"); + }); + + it("returns only protocol/permissionLevel/wsPath(/connectParams) for ssh", async () => { + const share = await createActiveLinkShare(); + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: share.linkToken }, + }); + + expect(res.jsonBody).toEqual({ + protocol: "ssh", + permissionLevel: "read-only", + wsPath: `/terminal/ws?shareToken=${encodeURIComponent(share.linkToken)}`, + }); + }); + + it("mints a fresh join token for guac protocols", async () => { + await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "guac-conn-1", + protocol: "vnc", + shareType: "link", + permissionLevel: "read-only", + }, + }); + const [share] = [...state.shares.values()] as { + linkToken: string; + }[]; + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: share.linkToken }, + }); + + expect(res.statusCode).toBe(200); + expect((res.jsonBody as Record).connectParams).toEqual({ + token: "join-token:guac-conn-1:true", + }); + }); + + it("rejects an unknown link token", async () => { + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: "does-not-exist" }, + }); + + expect(res.statusCode).toBe(404); + }); + + it("rejects a revoked link token", async () => { + const share = await createActiveLinkShare(); + await invoke("delete", "/:shareId", { params: { shareId: share.id } }); + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: share.linkToken }, + }); + + expect(res.statusCode).toBe(404); + }); + + it("rejects an expired link token", async () => { + state.shares.set("share-expired", { + id: "share-expired", + hostId: 1, + ownerUserId: "user-1", + protocol: "ssh", + sessionId: "session-1", + shareType: "link", + linkToken: "expired-token", + permissionLevel: "read-only", + expiresAt: "2000-01-01T00:00:00.000Z", + revokedAt: null, + }); + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: "expired-token" }, + }); + + expect(res.statusCode).toBe(404); + }); + + it("re-checks the global kill switch at resolve time, not just at creation time", async () => { + const share = await createActiveLinkShare(); + + state.globalSharingEnabled = false; + + const res = await invoke("get", "/resolve/:linkToken", { + params: { linkToken: share.linkToken }, + }); + + expect(res.statusCode).toBe(404); + }); +}); + +describe("DELETE /session-sharing/:shareId", () => { + it("allows the owner to revoke their own share", async () => { + await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + const [share] = [...state.shares.values()] as { id: string }[]; + + const res = await invoke("delete", "/:shareId", { + params: { shareId: share.id }, + }); + + expect(res.statusCode).toBe(200); + }); + + it("rejects a non-owner, non-admin caller", async () => { + await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + const [share] = [...state.shares.values()] as { id: string }[]; + + state.currentUserId = "user-2"; + const res = await invoke("delete", "/:shareId", { + params: { shareId: share.id }, + }); + + expect(res.statusCode).toBe(403); + }); + + it("allows an admin to revoke someone else's share", async () => { + await invoke("post", "/create", { + body: { + hostId: 1, + sessionId: "session-1", + protocol: "ssh", + shareType: "link", + permissionLevel: "read-only", + }, + }); + const [share] = [...state.shares.values()] as { id: string }[]; + + state.currentUserId = "admin-1"; + state.admins.add("admin-1"); + const res = await invoke("delete", "/:shareId", { + params: { shareId: share.id }, + }); + + expect(res.statusCode).toBe(200); + }); +}); diff --git a/src/backend/tests/hosts/terminal/session-manager.test.ts b/src/backend/tests/hosts/terminal/session-manager.test.ts index 9a369716..5b94ed1e 100644 --- a/src/backend/tests/hosts/terminal/session-manager.test.ts +++ b/src/backend/tests/hosts/terminal/session-manager.test.ts @@ -49,9 +49,19 @@ vi.mock("fs", () => ({ }, })); -const { sessionManager } = +const { sessionManager, isMessageAllowedForParticipant } = await import("../../../hosts/terminal/session-manager.js"); +// Minimal fake WebSocket - only the surface session-manager touches. +function makeFakeWs(readyState = 1 /* OPEN */) { + return { + readyState, + send: vi.fn(), + } as unknown as import("ws").WebSocket; +} +const WS_OPEN = 1; +const WS_CLOSED = 3; + describe("TerminalSessionManager - session logging", () => { beforeEach(() => { vi.clearAllMocks(); @@ -150,3 +160,273 @@ describe("TerminalSessionManager - session logging", () => { sessionManager.destroySession(id); }); }); + +describe("TerminalSessionManager - multiplayer participants", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockMkdir.mockResolvedValue(undefined); + mockWriteFile.mockResolvedValue(undefined); + mockCreate.mockResolvedValue({ id: 1 }); + mockUpdateEnded.mockResolvedValue(undefined); + }); + + function createConnectedSession(): string { + const id = sessionManager.createSession( + "owner-1", + 1, + "host", + 80, + 24, + undefined, + false, + ); + // Mark connected without a real ssh2 stream - only isConnected is read + // by attachWs/joinAsParticipant. + const session = sessionManager.getSession(id)!; + session.isConnected = true; + return id; + } + + it("joinAsParticipant adds a participant without evicting the owner", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + const guestWs = makeFakeWs(); + const session = sessionManager.joinAsParticipant(id, guestWs, { + userId: null, + permissionLevel: "read-only", + guestLabel: "Guest", + }); + + expect(session).not.toBeNull(); + expect(session!.participants.size).toBe(2); + const ownerParticipant = sessionManager.getParticipantForWs( + session!, + ownerWs, + ); + expect(ownerParticipant?.isOwner).toBe(true); + expect(ownerWs.send).not.toHaveBeenCalled(); + + sessionManager.destroySession(id); + }); + + it("joinAsParticipant returns null for a nonexistent or unconnected session", () => { + expect( + sessionManager.joinAsParticipant("does-not-exist", makeFakeWs(), { + userId: null, + permissionLevel: "read-only", + }), + ).toBeNull(); + }); + + it("broadcast sends to all OPEN participant sockets and skips CLOSED ones", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(WS_OPEN); + sessionManager.attachWs(id, "owner-1", ownerWs); + + const openGuestWs = makeFakeWs(WS_OPEN); + const closedGuestWs = makeFakeWs(WS_CLOSED); + sessionManager.joinAsParticipant(id, openGuestWs, { + userId: null, + permissionLevel: "read-only", + }); + sessionManager.joinAsParticipant(id, closedGuestWs, { + userId: null, + permissionLevel: "read-only", + }); + + sessionManager.broadcast(id, { type: "data", data: "hello" }); + + expect(ownerWs.send).toHaveBeenCalledWith( + JSON.stringify({ type: "data", data: "hello" }), + ); + expect(openGuestWs.send).toHaveBeenCalledWith( + JSON.stringify({ type: "data", data: "hello" }), + ); + expect(closedGuestWs.send).not.toHaveBeenCalled(); + + sessionManager.destroySession(id); + }); + + it("broadcast does not throw if a socket's send throws", () => { + const id = createConnectedSession(); + const throwingWs = makeFakeWs(WS_OPEN); + (throwingWs.send as ReturnType).mockImplementation(() => { + throw new Error("send failed"); + }); + sessionManager.attachWs(id, "owner-1", throwingWs); + + expect(() => + sessionManager.broadcast(id, { type: "data", data: "x" }), + ).not.toThrow(); + + sessionManager.destroySession(id); + }); + + it("broadcast is a no-op for a nonexistent session", () => { + expect(() => + sessionManager.broadcast("does-not-exist", { type: "data" }), + ).not.toThrow(); + }); + + it("owner detach arms the idle timeout (existing behavior)", () => { + vi.useFakeTimers(); + try { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + sessionManager.detachWs(id); + const session = sessionManager.getSession(id); + expect(session?.detachTimeout).not.toBeNull(); + expect(session?.lastDetachedAt).not.toBeNull(); + + sessionManager.destroySession(id); + } finally { + vi.useRealTimers(); + } + }); + + it("removeParticipant on a non-owner does not arm a timeout or destroy the session", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + const guestWs = makeFakeWs(); + sessionManager.joinAsParticipant(id, guestWs, { + userId: null, + permissionLevel: "read-write", + }); + + sessionManager.removeParticipant(id, guestWs); + + const session = sessionManager.getSession(id); + expect(session).not.toBeNull(); + expect(session?.detachTimeout).toBeNull(); + expect(session?.participants.size).toBe(1); + expect(sessionManager.getParticipantForWs(session!, guestWs)).toBeNull(); + + sessionManager.destroySession(id); + }); + + it("removeParticipant is a no-op when the ws belongs to the owner", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + sessionManager.removeParticipant(id, ownerWs); + + const session = sessionManager.getSession(id); + expect(session?.participants.size).toBe(1); + expect(sessionManager.getParticipantForWs(session!, ownerWs)?.isOwner).toBe( + true, + ); + + sessionManager.destroySession(id); + }); + + it("destroySession cleans up all participants, not just the owner", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + const guestWs = makeFakeWs(); + sessionManager.joinAsParticipant(id, guestWs, { + userId: null, + permissionLevel: "read-only", + }); + + sessionManager.destroySession(id); + + expect(guestWs.send).toHaveBeenCalled(); + expect(sessionManager.getSession(id)).toBeNull(); + }); + + it("ownerEndSession notifies non-owner participants and destroys the session", () => { + const id = createConnectedSession(); + const ownerWs = makeFakeWs(); + sessionManager.attachWs(id, "owner-1", ownerWs); + + const guestWs = makeFakeWs(); + sessionManager.joinAsParticipant(id, guestWs, { + userId: null, + permissionLevel: "read-write", + }); + + sessionManager.ownerEndSession(id, "owner ended the session"); + + expect(guestWs.send).toHaveBeenCalledWith( + JSON.stringify({ + type: "sessionTerminatedByOwner", + reason: "owner ended the session", + }), + ); + expect(sessionManager.getSession(id)).toBeNull(); + }); +}); + +describe("isMessageAllowedForParticipant", () => { + it("allows any message type for the owner or when there is no participant", () => { + expect(isMessageAllowedForParticipant(null, "connectToHost")).toBe(true); + expect( + isMessageAllowedForParticipant( + { isOwner: true, permissionLevel: "read-write" }, + "resize", + ), + ).toBe(true); + }); + + it("drops input from a read-only participant", () => { + expect( + isMessageAllowedForParticipant( + { isOwner: false, permissionLevel: "read-only" }, + "input", + ), + ).toBe(false); + }); + + it("allows input from a read-write non-owner participant", () => { + expect( + isMessageAllowedForParticipant( + { isOwner: false, permissionLevel: "read-write" }, + "input", + ), + ).toBe(true); + }); + + it("allows ping and disconnect for any non-owner participant", () => { + expect( + isMessageAllowedForParticipant( + { isOwner: false, permissionLevel: "read-only" }, + "ping", + ), + ).toBe(true); + expect( + isMessageAllowedForParticipant( + { isOwner: false, permissionLevel: "read-only" }, + "disconnect", + ), + ).toBe(true); + }); + + it("blocks resize and auth/tmux message types for non-owner participants regardless of permission level", () => { + for (const type of [ + "resize", + "totp_response", + "password_response", + "tmux_attach", + "tmux_detach", + "get_cwd", + "vault_start_auth", + "opkssh_start_auth", + ]) { + expect( + isMessageAllowedForParticipant( + { isOwner: false, permissionLevel: "read-write" }, + type, + ), + ).toBe(false); + } + }); +}); diff --git a/src/backend/tests/utils/analytics.test.ts b/src/backend/tests/utils/analytics.test.ts new file mode 100644 index 00000000..a64b3bdd --- /dev/null +++ b/src/backend/tests/utils/analytics.test.ts @@ -0,0 +1,137 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +const mockGetBoolean = vi.fn(); +const mockGet = vi.fn(); +const mockSet = vi.fn(); +const mockPost = vi.fn(); + +function makeChain(resolveValue: unknown) { + const chain: Record = {}; + const methods = ["from", "where", "groupBy"]; + 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); + return chain; +} + +vi.mock("../../database/repositories/factory.js", () => ({ + createCurrentSettingsRepository: () => ({ + getBoolean: mockGetBoolean, + get: mockGet, + set: mockSet, + }), + createCurrentRepositoryContext: () => ({ + drizzle: { + select: vi.fn(() => makeChain([{ count: 0 }])), + }, + }), +})); + +vi.mock("../../database/db/schema.js", () => ({ + users: {}, + hosts: {}, + recentActivity: { type: "type", timestamp: "timestamp" }, +})); + +vi.mock("../../utils/logger.js", () => ({ + Logger: class { + info = vi.fn(); + warn = vi.fn(); + error = vi.fn(); + }, +})); + +vi.mock("axios", () => ({ + default: { post: mockPost }, +})); + +describe("analytics", () => { + const originalEnv = { ...process.env }; + + beforeEach(() => { + vi.clearAllMocks(); + process.env = { ...originalEnv }; + }); + + afterEach(() => { + process.env = { ...originalEnv }; + }); + + it("isAnalyticsEnabled defaults to true via the settings repository", async () => { + mockGetBoolean.mockResolvedValue(true); + const { isAnalyticsEnabled } = await import("../../utils/analytics.js"); + + const result = await isAnalyticsEnabled(); + + expect(result).toBe(true); + expect(mockGetBoolean).toHaveBeenCalledWith("analytics_enabled", true); + }); + + it("getOrCreateInstanceId returns the existing id without generating one", async () => { + mockGet.mockResolvedValue("existing-id"); + const { getOrCreateInstanceId } = await import("../../utils/analytics.js"); + + const id = await getOrCreateInstanceId(); + + expect(id).toBe("existing-id"); + expect(mockSet).not.toHaveBeenCalled(); + }); + + it("getOrCreateInstanceId generates and persists a new id when absent", async () => { + mockGet.mockResolvedValue(null); + const { getOrCreateInstanceId } = await import("../../utils/analytics.js"); + + const id = await getOrCreateInstanceId(); + + expect(id).toMatch(/^[0-9a-f-]{36}$/); + expect(mockSet).toHaveBeenCalledWith("analytics_instance_id", id); + }); + + it("collectAndSendHeartbeat does not call PostHog when POSTHOG_API_KEY is unset", async () => { + delete process.env.POSTHOG_API_KEY; + const { collectAndSendHeartbeat } = + await import("../../utils/analytics.js"); + + await collectAndSendHeartbeat(); + + expect(mockPost).not.toHaveBeenCalled(); + }); + + it("collectAndSendHeartbeat does not call PostHog when analytics is disabled", async () => { + process.env.POSTHOG_API_KEY = "phc_test"; + mockGetBoolean.mockResolvedValue(false); + const { collectAndSendHeartbeat } = + await import("../../utils/analytics.js"); + + await collectAndSendHeartbeat(); + + expect(mockPost).not.toHaveBeenCalled(); + }); + + it("collectAndSendHeartbeat posts a heartbeat event with the expected shape when enabled", async () => { + process.env.POSTHOG_API_KEY = "phc_test"; + mockGetBoolean.mockResolvedValue(true); + mockGet.mockResolvedValue("instance-123"); + mockPost.mockResolvedValue({}); + const { collectAndSendHeartbeat } = + await import("../../utils/analytics.js"); + + await collectAndSendHeartbeat(); + + expect(mockPost).toHaveBeenCalledTimes(1); + const [url, body] = mockPost.mock.calls[0]; + expect(url).toContain("/capture/"); + expect(body).toMatchObject({ + api_key: "phc_test", + event: "instance_heartbeat", + distinct_id: "instance-123", + properties: expect.objectContaining({ + user_count: 0, + host_count: 0, + used_terminal: 0, + }), + }); + }); +}); diff --git a/src/backend/tests/utils/safe-outbound-fetch.test.ts b/src/backend/tests/utils/safe-outbound-fetch.test.ts new file mode 100644 index 00000000..f20b5406 --- /dev/null +++ b/src/backend/tests/utils/safe-outbound-fetch.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, it, vi } from "vitest"; +import type { LookupAddress, LookupAllOptions } from "dns"; +import { + createDnsLookupHook, + isBlockedAddress, +} from "../../utils/safe-outbound-fetch.js"; + +describe("isBlockedAddress", () => { + it("allows public IPv4 addresses", () => { + expect(isBlockedAddress("8.8.8.8")).toBe(false); + expect(isBlockedAddress("104.21.52.150")).toBe(false); + }); + + it("blocks private/reserved IPv4 ranges", () => { + expect(isBlockedAddress("10.0.0.1")).toBe(true); + expect(isBlockedAddress("172.16.0.1")).toBe(true); + expect(isBlockedAddress("192.168.1.1")).toBe(true); + expect(isBlockedAddress("127.0.0.1")).toBe(true); + expect(isBlockedAddress("169.254.1.1")).toBe(true); + expect(isBlockedAddress("100.64.0.1")).toBe(true); + }); + + it("allows public IPv6 addresses", () => { + expect(isBlockedAddress("2606:4700:3034::ac43:c88d")).toBe(false); + expect(isBlockedAddress("2001:4860:4860::8888")).toBe(false); + }); + + it("blocks private/reserved IPv6 ranges", () => { + expect(isBlockedAddress("::1")).toBe(true); + expect(isBlockedAddress("fc00::1")).toBe(true); + expect(isBlockedAddress("fe80::1")).toBe(true); + }); + + it("blocks IPv4-mapped-IPv6 spoofing of private addresses", () => { + expect(isBlockedAddress("::ffff:127.0.0.1")).toBe(true); + expect(isBlockedAddress("::ffff:192.168.1.1")).toBe(true); + expect(isBlockedAddress("::ffff:10.0.0.1")).toBe(true); + }); + + it("does not block IPv4-mapped-IPv6 form of public addresses", () => { + expect(isBlockedAddress("::ffff:104.21.52.150")).toBe(false); + expect(isBlockedAddress("::ffff:8.8.8.8")).toBe(false); + }); + + it("blocks unparseable input", () => { + expect(isBlockedAddress("not-an-ip")).toBe(true); + }); +}); + +// These exercise createDnsLookupHook directly against a fake resolver, +// bypassing fetch()/undici entirely. That's the actual code path the +// original bug lived in — a public IPv4 address getting misclassified as +// private — and testing it through a real Agent/fetch call would only +// add flakiness (real TCP connects, undici's own quirks) without adding +// coverage of the logic that actually broke. +function runHook( + addresses: LookupAddress[], + error: NodeJS.ErrnoException | null = null, +) { + const fakeLookup = ( + _host: string, + _opts: LookupAllOptions, + cb: (err: NodeJS.ErrnoException | null, addrs: LookupAddress[]) => void, + ) => cb(error, addresses); + + const hook = createDnsLookupHook(fakeLookup); + const callback = vi.fn(); + hook("example.invalid", { all: true }, callback); + return callback; +} + +describe("createDnsLookupHook", () => { + it("allows a public IPv4 address through", () => { + const callback = runHook([{ address: "104.21.52.150", family: 4 }]); + expect(callback).toHaveBeenCalledWith(null, "104.21.52.150", 4); + }); + + it("rejects a private address with the private-destination error", () => { + const callback = runHook([{ address: "192.168.1.1", family: 4 }]); + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + message: "Private destinations are not allowed", + }), + "", + 0, + ); + }); + + it("rejects with a distinct error when DNS returns no addresses", () => { + const callback = runHook([]); + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + message: "DNS resolution returned no addresses", + }), + "", + 0, + ); + }); + + it("propagates a real DNS lookup error untouched", () => { + const dnsError = Object.assign(new Error("getaddrinfo ENOTFOUND"), { + code: "ENOTFOUND", + }); + const callback = runHook([], dnsError); + expect(callback).toHaveBeenCalledWith(dnsError, "", 0); + }); +}); diff --git a/src/backend/utils/analytics.ts b/src/backend/utils/analytics.ts new file mode 100644 index 00000000..9271962a --- /dev/null +++ b/src/backend/utils/analytics.ts @@ -0,0 +1,134 @@ +import crypto from "crypto"; +import axios from "axios"; +import { sql } from "drizzle-orm"; +import { users, hosts, recentActivity } from "../database/db/schema.js"; +import { + createCurrentSettingsRepository, + createCurrentRepositoryContext, +} from "../database/repositories/factory.js"; +import { Logger } from "./logger.js"; + +export const analyticsLogger = new Logger("ANALYTICS", "📈", "#06b6d4"); + +const FEATURE_ACTIVITY_TYPES = [ + "terminal", + "file_manager", + "tunnel", + "docker", + "telnet", + "vnc", + "rdp", + "server_stats", +] as const; + +const POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com"; +const HEARTBEAT_INTERVAL_MS = 24 * 60 * 60 * 1000; + +export async function isAnalyticsEnabled(): Promise { + return createCurrentSettingsRepository().getBoolean( + "analytics_enabled", + true, + ); +} + +export async function getOrCreateInstanceId(): Promise { + const settings = createCurrentSettingsRepository(); + const existing = await settings.get("analytics_instance_id"); + if (existing) return existing; + + const id = crypto.randomUUID(); + await settings.set("analytics_instance_id", id); + return id; +} + +function getAppVersion(): string { + return process.env.VERSION || "unknown"; +} + +async function collectFeatureUsage(): Promise> { + const since = new Date(Date.now() - HEARTBEAT_INTERVAL_MS).toISOString(); + const db = createCurrentRepositoryContext().drizzle; + + const rows = await db + .select({ + type: recentActivity.type, + count: sql`count(*)`, + }) + .from(recentActivity) + .where(sql`${recentActivity.timestamp} >= ${since}`) + .groupBy(recentActivity.type); + + const counts = new Map(rows.map((row) => [row.type, Number(row.count)])); + const usage: Record = {}; + for (const type of FEATURE_ACTIVITY_TYPES) { + usage[`used_${type}`] = counts.get(type) ?? 0; + } + return usage; +} + +async function collectCounts(): Promise<{ + userCount: number; + hostCount: number; +}> { + const db = createCurrentRepositoryContext().drizzle; + + const [userRows, hostRows] = await Promise.all([ + db.select({ count: sql`count(*)` }).from(users), + db.select({ count: sql`count(*)` }).from(hosts), + ]); + + return { + userCount: Number(userRows[0]?.count ?? 0), + hostCount: Number(hostRows[0]?.count ?? 0), + }; +} + +export async function collectAndSendHeartbeat(): Promise { + const apiKey = process.env.POSTHOG_API_KEY; + if (!apiKey) return; + + try { + if (!(await isAnalyticsEnabled())) return; + + const instanceId = await getOrCreateInstanceId(); + const { userCount, hostCount } = await collectCounts(); + const featureUsage = await collectFeatureUsage(); + + await axios.post( + `${POSTHOG_HOST}/capture/`, + { + api_key: apiKey, + event: "instance_heartbeat", + distinct_id: instanceId, + properties: { + version: getAppVersion(), + user_count: userCount, + host_count: hostCount, + ...featureUsage, + }, + }, + { timeout: 10000 }, + ); + + analyticsLogger.info("Sent daily usage heartbeat", { + operation: "analytics_heartbeat_sent", + }); + } catch (err) { + analyticsLogger.warn("Failed to send usage heartbeat", { + operation: "analytics_heartbeat_failed", + error: err instanceof Error ? err.message : "Unknown error", + }); + } +} + +export function startAnalyticsHeartbeat(): void { + if (!process.env.POSTHOG_API_KEY) { + analyticsLogger.info("Analytics disabled: POSTHOG_API_KEY not set", { + operation: "analytics_disabled_no_key", + }); + return; + } + + void collectAndSendHeartbeat(); + setInterval(() => void collectAndSendHeartbeat(), HEARTBEAT_INTERVAL_MS); +} diff --git a/src/backend/utils/safe-outbound-fetch.ts b/src/backend/utils/safe-outbound-fetch.ts index 1448be0e..46f5bbbb 100644 --- a/src/backend/utils/safe-outbound-fetch.ts +++ b/src/backend/utils/safe-outbound-fetch.ts @@ -1,28 +1,54 @@ -import { lookup } from "dns"; +import { lookup, type LookupAddress, type LookupAllOptions } from "dns"; import { BlockList, isIP } from "net"; import { Agent } from "undici"; +type DnsLookupFn = ( + hostname: string, + options: LookupAllOptions, + callback: ( + err: NodeJS.ErrnoException | null, + addresses: LookupAddress[], + ) => void, +) => void; + +type LookupHookCallback = ( + error: NodeJS.ErrnoException | Error | null, + address: string, + family: number, +) => void; + const blockedAddresses = new BlockList(); -for (const [network, prefix] of [ +// Derived, not hand-duplicated: Node's BlockList matches addresses across +// families through their IPv4-mapped-IPv6 form regardless of which `type` +// you pass to check()/addSubnet() (see the addAddress('123.123.123.123') / +// check('::ffff:123.123.123.123') example on +// https://nodejs.org/api/net.html#class-netblocklist). So every IPv4 range +// below needs an "::ffff:" mirror in the IPv6 list, or a spoofed +// literal like "::ffff:127.0.0.1" slips through unblocked. Generating the +// mirror from this list instead of maintaining two lists by hand means the +// two can't drift out of sync the way they did before. +const blockedIpv4Ranges = [ ["0.0.0.0", 8], ["10.0.0.0", 8], - ["100.64.0.0", 10], + ["100.64.0.0", 10], // CGNAT ["127.0.0.0", 8], - ["169.254.0.0", 16], + ["169.254.0.0", 16], // link-local ["172.16.0.0", 12], ["192.168.0.0", 16], - ["198.18.0.0", 15], - ["224.0.0.0", 4], - ["240.0.0.0", 4], -] as const) { + ["198.18.0.0", 15], // benchmarking + ["224.0.0.0", 4], // multicast + ["240.0.0.0", 4], // reserved +] as const; + +for (const [network, prefix] of blockedIpv4Ranges) { blockedAddresses.addSubnet(network, prefix, "ipv4"); + blockedAddresses.addSubnet(`::ffff:${network}`, prefix + 96, "ipv6"); } for (const [network, prefix] of [ ["::", 128], ["::1", 128], - ["::ffff:0:0", 96], ["fc00::", 7], ["fe80::", 10], ["ff00::", 8], @@ -30,7 +56,7 @@ for (const [network, prefix] of [ blockedAddresses.addSubnet(network, prefix, "ipv6"); } -function isBlockedAddress(address: string): boolean { +export function isBlockedAddress(address: string): boolean { const family = isIP(address); return ( family === 0 || @@ -38,6 +64,42 @@ function isBlockedAddress(address: string): boolean { ); } +// Extracted so the blocklist decision can be tested directly against a +// fake DNS resolver, instead of only through a real fetch()/Agent call — +// the actual bug here lived entirely in this callback, several layers +// below where undici's own "fetch failed" wrapping would otherwise hide it. +export function createDnsLookupHook(dnsLookup: DnsLookupFn = lookup) { + return function lookupHook( + host: string, + lookupOptions: LookupAllOptions, + callback: LookupHookCallback, + ): void { + dnsLookup( + host, + { ...lookupOptions, all: true, verbatim: true }, + (error, addresses) => { + if (error) return callback(error, "", 0); + if (!addresses.length) { + return callback( + new Error("DNS resolution returned no addresses"), + "", + 0, + ); + } + if (addresses.some(({ address }) => isBlockedAddress(address))) { + return callback( + new Error("Private destinations are not allowed"), + "", + 0, + ); + } + const selected = addresses[0]; + callback(null, selected.address, selected.family); + }, + ); + }; +} + export async function safeOutboundFetch( rawUrl: string, options: RequestInit, @@ -58,27 +120,7 @@ export async function safeOutboundFetch( const dispatcher = new Agent({ connect: { - lookup(host, lookupOptions, callback) { - lookup( - host, - { ...lookupOptions, all: true, verbatim: true }, - (error, addresses) => { - if (error) return callback(error, "", 0); - if ( - !addresses.length || - addresses.some(({ address }) => isBlockedAddress(address)) - ) { - return callback( - new Error("Private destinations are not allowed"), - "", - 0, - ); - } - const selected = addresses[0]; - callback(null, selected.address, selected.family); - }, - ); - }, + lookup: createDnsLookupHook(), }, }); diff --git a/src/backend/utils/ssh-algorithms.ts b/src/backend/utils/ssh-algorithms.ts index 40a59122..ca643933 100644 --- a/src/backend/utils/ssh-algorithms.ts +++ b/src/backend/utils/ssh-algorithms.ts @@ -31,13 +31,13 @@ try { nativeRequire("ssh2/lib/protocol/crypto/build/Release/sshcrypto.node"); ssh2BindingAvailable = true; } catch { - try { - // ESM fallback: check if chacha20 works via OpenSSL createCipheriv - crypto.createCipheriv("chacha20", Buffer.alloc(32), Buffer.alloc(16)); - ssh2BindingAvailable = true; - } catch { - ssh2BindingAvailable = false; - } + // The pure-JS fallback in ssh2 for chacha20-poly1305@openssh.com is broken and + // corrupts the transport: the target sshd aborts the KEX with + // "ssh_dispatch_run_fatal: ... incomplete message [preauth]" and the client times out. + // A working OpenSSL "chacha20" cipher is NOT sufficient here — only the native + // binding (sshcrypto.node) makes chacha20-poly1305 usable. Keep it disabled otherwise + // so filterCiphers() drops it and the connection negotiates AES-GCM instead. + ssh2BindingAvailable = false; } function filterCiphers(list: CipherAlgorithm[]): CipherAlgorithm[] { diff --git a/src/main.tsx b/src/main.tsx index 1e4c8894..fb07801e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -65,6 +65,12 @@ const ElectronVersionCheck = lazy(() => })), ); +// Anonymous guest view for shared terminal/RDP/VNC/Telnet sessions (?view=shared&token=). +// Rendered outside FullscreenAppGate since guests never have a JWT/cookie to verify. +const SharedSessionView = lazy( + () => import("@/features/session-sharing/SharedSessionView"), +); + type Phase = | "verifying" | "idle-auth" @@ -174,11 +180,15 @@ function App() { stored?.loggedIn ? "verifying" : "idle-auth", ); const [authUsername, setAuthUsername] = useState(stored?.username ?? ""); + const [verifyRetryCount, setVerifyRetryCount] = useState(0); const timerRef = useRef | null>(null); // Track whether fading-in came from a fresh login (vs. session verification on page load). // When session-verified, Auth must not mount during the transition — it would trigger // silent OIDC redirect and cause an infinite refresh loop. const fadingInFromLoginRef = useRef(false); + // Dedupes concurrent handleLogout() calls within the same tick -- see + // handleLogout for why phase state alone isn't sufficient for this. + const loggingOutRef = useRef(false); useEffect(() => { const savedAccent = localStorage.getItem("termix-accent"); @@ -204,7 +214,18 @@ function App() { if (isElectron()) { try { const token = await getCurrentToken(); - if (token) localStorage.setItem("jwt", token); + if (token) { + localStorage.setItem("jwt", token); + // Remote Sync's engine (main process) needs this local JWT to + // authenticate against the embedded backend during sync, same + // as a fresh login provides via handleLogin below -- a session + // restore (the common case on every normal launch) must hand + // it over too, or sync silently never runs after the first + // app restart. + window.electronAPI + ?.invoke?.("notify-local-login", token) + .catch(() => {}); + } } catch { // Non-fatal: WebSocket connections will fall back to cookie auth } @@ -213,36 +234,83 @@ function App() { setPhase("fading-in"); timerRef.current = setTimeout(() => setPhase("idle-app"), 450); }) - .catch(() => { - clearStoredAuth(); - setPhase("idle-auth"); + .catch((err: unknown) => { + // Only treat a genuine auth rejection (401/403) as "not logged in". + // Anything else (network hiccup, backend still starting up, a + // transient 5xx) is not proof the session is invalid -- clearing + // stored auth here would drop the user back to Auth.tsx, which in + // Electron immediately mints a brand-new auto-session, silently + // swapping out the JWT/cookie from under any still-in-flight + // requests and causing spurious "Session expired" toasts. + const status = + (err as { status?: number; response?: { status?: number } }) + ?.status ?? + (err as { response?: { status?: number } })?.response?.status; + if (status === 401 || status === 403) { + clearStoredAuth(); + setPhase("idle-auth"); + return; + } + // Transient failure: retry rather than logging out. In Electron the + // embedded local backend is bundled, always-on infrastructure that + // always eventually comes up (a slow cold boot just takes longer), + // and Auth.tsx never shows a login form for it anyway -- so there's + // no reason to ever give up and manufacture a logout here. Outside + // Electron a genuinely broken backend still needs to surface the + // login screen eventually, so that case keeps a retry cap. + if (!isElectron() && verifyRetryCount >= 5) { + clearStoredAuth(); + setPhase("idle-auth"); + return; + } + const delay = isElectron() + ? Math.min(1000 * 2 ** verifyRetryCount, 10000) + : 3000; + timerRef.current = setTimeout(() => { + setVerifyRetryCount((c) => c + 1); + }, delay); }); - }, [phase]); + }, [phase, verifyRetryCount]); function handleLogin(u: string) { + loggingOutRef.current = false; setAuthUsername(u); fadingInFromLoginRef.current = true; setPhase("fading-in"); timerRef.current = setTimeout(() => setPhase("idle-app"), 450); if (isElectron()) { window.electronAPI?.startC2SAutoStartTunnels?.().catch(() => {}); + const localJwt = localStorage.getItem("jwt"); + if (localJwt) { + window.electronAPI + ?.invoke?.("notify-local-login", localJwt) + .catch(() => {}); + } } } function handleLogout() { + // A single background hiccup can trigger several independent 401s at + // once (e.g. a burst of unrelated polls all failing together in the + // same tick), each calling this. React batches the resulting setPhase + // calls, so checking `phase` here can't distinguish the first call in + // a batch from the second -- both would see the same pre-update value + // and both would proceed, each overwriting timerRef with a fresh + // 450ms timer. A steady trickle of these could keep resetting the + // countdown so the transition never actually completes, which looks + // exactly like "nothing happens." loggingOutRef is synchronous and + // isn't subject to batching, so it correctly dedupes within one tick. + if (loggingOutRef.current) return; + loggingOutRef.current = true; clearStoredAuth(); setPhase("fading-out"); timerRef.current = setTimeout(() => { setAuthUsername(""); setPhase("idle-auth"); + loggingOutRef.current = false; }, 450); } - function handleChangeServer() { - localStorage.setItem("termix_show_server_config", "true"); - handleLogout(); - } - const showApp = phase === "idle-app" || phase === "fading-in" || phase === "fading-out"; const showAuth = @@ -288,11 +356,7 @@ function App() { }} > - + )} @@ -322,6 +386,16 @@ function RootApp() { const searchParams = new URLSearchParams(window.location.search); const isFullscreen = searchParams.has("view"); + // Anonymous guests have no cookie/JWT at all, so this bypasses FullscreenAppGate's + // auth check entirely rather than waiting on a getUserInfo() call that would always fail. + if (searchParams.get("view") === "shared") { + return ( + + + + ); + } + if (isFullscreen) { return ( diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 61a38183..91cc7d4a 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -64,6 +64,15 @@ export interface ElectronAPI { started: number; errors: string[]; }>; + onRemoteSyncStatusChanged?: ( + callback: (status: { + connected: boolean; + syncing: boolean; + lastSyncedAt: string | null; + lastError: string | null; + needsReauth: boolean; + }) => void, + ) => () => void; clearSessionCookies: () => Promise; getSessionCookie: ( name: string, @@ -157,7 +166,6 @@ declare global { interface Window { electronAPI: ElectronAPI; IS_ELECTRON: boolean; - configuredServerUrl?: string | null; electronClipboard?: { writeText(text: string): Promise; readText(): Promise; diff --git a/src/types/guacamole-common-js.d.ts b/src/types/guacamole-common-js.d.ts index 3dbb36c3..24079905 100644 --- a/src/types/guacamole-common-js.d.ts +++ b/src/types/guacamole-common-js.d.ts @@ -97,6 +97,39 @@ declare module "guacamole-common-js" { up: boolean; down: boolean; } + + interface MouseEvent { + state: Mouse.State; + preventDefault(): void; + stopPropagation(): void; + } + + class Touchpad { + constructor(element: HTMLElement); + currentState: Mouse.State; + clickTimingThreshold: number; + clickMoveThreshold: number; + scrollThreshold: number; + onEach( + types: string[], + listener: (event: Mouse.MouseEvent) => void, + ): void; + on(type: string, listener: (event: Mouse.MouseEvent) => void): void; + } + + class Touchscreen { + constructor(element: HTMLElement); + currentState: Mouse.State; + clickTimingThreshold: number; + clickMoveThreshold: number; + scrollThreshold: number; + longPressThreshold: number; + onEach( + types: string[], + listener: (event: Mouse.MouseEvent) => void, + ): void; + on(type: string, listener: (event: Mouse.MouseEvent) => void): void; + } } class Keyboard { diff --git a/src/types/index.ts b/src/types/index.ts index 3b2b9a03..0c1b8c80 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -151,6 +151,7 @@ export interface Host { enableDocker: boolean; enableProxmox: boolean; enableTmuxMonitor: boolean; + allowSessionSharing?: boolean; proxmoxConfig?: ProxmoxConfig | null; showTerminalInSidebar: boolean; showFileManagerInSidebar: boolean; @@ -207,7 +208,7 @@ export interface Host { telnetUser?: string; telnetPassword?: string; telnetCredentialId?: number | null; - rdpAuthType?: "direct" | "credential" | null; + rdpAuthType?: "direct" | "credential" | "none" | null; vncAuthType?: "direct" | "credential" | null; telnetAuthType?: "direct" | "credential" | null; createdAt: string; @@ -272,6 +273,7 @@ export interface HostData { enableDocker?: boolean; enableProxmox?: boolean; enableTmuxMonitor?: boolean; + allowSessionSharing?: boolean; proxmoxConfig?: ProxmoxConfig | Record | null; showTerminalInSidebar?: boolean; showFileManagerInSidebar?: boolean; @@ -329,7 +331,7 @@ export interface HostData { telnetUser?: string; telnetPassword?: string; telnetCredentialId?: number | null; - rdpAuthType?: "direct" | "credential" | null; + rdpAuthType?: "direct" | "credential" | "none" | null; vncAuthType?: "direct" | "credential" | null; telnetAuthType?: "direct" | "credential" | null; } @@ -343,6 +345,7 @@ export interface SSHFolder { name: string; color?: string; icon?: string; + credentialId?: number | null; createdAt: string; updatedAt: string; } diff --git a/src/types/ui-types.ts b/src/types/ui-types.ts index 304d4127..4064ca60 100644 --- a/src/types/ui-types.ts +++ b/src/types/ui-types.ts @@ -71,6 +71,7 @@ export type Host = { useSocks5?: boolean; socks5Host?: string; socks5Port?: number; + connectionOrigin?: "local" | "remote" | null; socks5Username?: string; socks5Password?: string; socks5ProxyChain?: { @@ -152,19 +153,26 @@ export type Host = { vncPort: number; telnetPort: number; + rdpAuthType?: "direct" | "credential" | "none"; rdpCredentialId?: string; rdpUser?: string; rdpPassword?: string; + hasRdpPassword?: boolean; domain?: string; security?: string; ignoreCert?: boolean; + vncAuthType?: "direct" | "credential"; vncCredentialId?: string; vncPassword?: string; + hasVncPassword?: boolean; vncUser?: string; + telnetAuthType?: "direct" | "credential"; + telnetCredentialId?: string; telnetUser?: string; telnetPassword?: string; + hasTelnetPassword?: boolean; guacamoleConfig?: Record; forceKeyboardInteractive?: boolean; @@ -217,6 +225,7 @@ export type HostFolder = { path?: string; color?: string; icon?: string; + credentialId?: number | null; }; export type TabType = @@ -276,6 +285,9 @@ export type Tab = { host?: Host; openedAt: number; restoredSessionId?: string | null; + /** Set when this tab joins someone else's live shared session instead of connecting/attaching its own. */ + joinSharedSessionId?: string | null; + joinShareId?: string | null; initialFilePath?: string; serialConfig?: SerialConfig; terminalRef?: import("react").RefObject<{ @@ -286,6 +298,8 @@ export type Tab = { fit?: () => void; notifyResize?: () => void; getApplicationCursorKeysMode?: () => boolean; + openShareModal?: () => void; + canShare?: () => boolean; } | null>; }; diff --git a/src/ui/AppShell.tsx b/src/ui/AppShell.tsx index 5c905e55..2729ce48 100644 --- a/src/ui/AppShell.tsx +++ b/src/ui/AppShell.tsx @@ -126,10 +126,13 @@ import { getActiveSessions, getUserPreferences, dismissDonationModal, + isElectron, type UserPreferences, type OpenTabRecord, } from "@/main-axios"; import { DonationReminderModal } from "@/user/DonationReminderModal.tsx"; +import { RemoteSyncBanner } from "@/components/RemoteSyncBanner.tsx"; +import { MigrationNoticeDialog } from "@/components/MigrationNoticeDialog.tsx"; import { dbHealthMonitor } from "@/lib/db-health-monitor"; import type { SSHHostWithStatus } from "@/main-axios"; import { ServerStatusProvider } from "@/lib/ServerStatusContext"; @@ -141,7 +144,10 @@ import { quickConnectHostToPayload } from "@/sidebar/quick-connect-host"; function buildHostTree( hosts: SSHHostWithStatus[], - folderMeta?: Map, + folderMeta?: Map< + string, + { color?: string; icon?: string; credentialId?: number | null } + >, ): HostFolder { const root: HostFolder = { name: "root", children: [] }; const folderMap = new Map(); @@ -159,6 +165,7 @@ function buildHostTree( path: accumulated, color: meta?.color, icon: meta?.icon, + credentialId: meta?.credentialId ?? null, children: [], }; folderMap.set(accumulated, folder); @@ -189,11 +196,9 @@ export { tabIcon, renderTabContent } from "@/shell/tabUtils"; export function AppShell({ username, onLogout, - onChangeServer, }: { username: string; onLogout: () => void; - onChangeServer?: () => void; }) { const { t, i18n } = useTranslation(); const { setTheme } = useTheme(); @@ -218,11 +223,14 @@ export function AppShell({ const [splitMode, setSplitMode] = useState( () => (localStorage.getItem("termix_splitMode") as SplitMode) ?? "none", ); - const [paneTabIds, setPaneTabIds] = useState<(string | null)[]>( - () => - JSON.parse(localStorage.getItem("termix_paneTabIds") ?? "null") ?? - Array(6).fill(null), + // paneTabIds holds live tab.id values, which change on every restore, so we + // can't restore it from storage directly. It starts empty and gets filled in + // once by the reconciliation effect below, keyed off the stable instanceId + // values saved in termix_paneInstanceIds. + const [paneTabIds, setPaneTabIds] = useState<(string | null)[]>(() => + Array(6).fill(null), ); + const paneLayoutRestoredRef = useRef(false); useEffect(() => { paneTabIdsRef.current = paneTabIds; }, [paneTabIds]); @@ -231,6 +239,13 @@ export function AppShell({ const [hostsLoading, setHostsLoading] = useState(true); const [allHosts, setAllHosts] = useState([]); const [isAdmin, setIsAdmin] = useState(false); + // Remote sync is not yet configurable (added in a later phase), so this + // is always false for now -- admin/user-management UI stays hidden until + // the desktop app is connected to a remote Termix server, since a + // standalone local install has exactly one implicit user and nothing to + // administer. + const [isRemoteSyncConnected] = useState(false); + const showMultiUserUI = isAdmin && (!isElectron() || isRemoteSyncConnected); const [userId, setUserId] = useState(null); const [showDonationModal, setShowDonationModal] = useState(false); const [backgroundTabRecords, setBackgroundTabRecords] = useState< @@ -239,6 +254,9 @@ export function AppShell({ const [sidebarOpen, setSidebarOpen] = useState(true); const [railView, setRailView] = useState("hosts"); + const [remoteSyncInitialServerUrl, setRemoteSyncInitialServerUrl] = useState< + string | undefined + >(undefined); const [sidebarWidth, setSidebarWidth] = useState(() => { const saved = localStorage.getItem("termix_sidebarWidth"); return saved ? parseInt(saved, 10) : 291; @@ -258,8 +276,15 @@ export function AppShell({ }, [splitMode]); useEffect(() => { - localStorage.setItem("termix_paneTabIds", JSON.stringify(paneTabIds)); - }, [paneTabIds]); + // Don't overwrite the saved layout with the empty initial state before + // reconciliation has had a chance to restore it. + if (!paneLayoutRestoredRef.current) return; + const instanceIds = paneTabIds.map((id) => { + if (id == null) return null; + return tabs.find((t) => t.id === id)?.instanceId ?? null; + }); + localStorage.setItem("termix_paneInstanceIds", JSON.stringify(instanceIds)); + }, [paneTabIds, tabs]); const isMobile = useIsMobile(); @@ -798,11 +823,15 @@ export function AppShell({ ]); const converted = raw.map(sshHostToHost); setAllHosts(converted); - const folderMeta = new Map(); + const folderMeta = new Map< + string, + { color?: string; icon?: string; credentialId?: number | null } + >(); for (const f of folders) { folderMeta.set(f.name, { color: f.color ?? undefined, icon: f.icon ?? undefined, + credentialId: f.credentialId ?? null, }); } setRealHostTree(buildHostTree(raw, folderMeta)); @@ -968,6 +997,35 @@ export function AppShell({ loadSavedTabs(); }, [hostsLoaded, userPrefsLoaded]); + // Restore split-screen pane assignments once tabs are settled. Saved assignments are + // keyed by instanceId (stable across reloads) and remapped to the live tab.id here, + // since tab.id is regenerated every time a tab is (re)opened. + useEffect(() => { + if (!tabsReady || paneLayoutRestoredRef.current) return; + paneLayoutRestoredRef.current = true; + + try { + const savedInstanceIds: (string | null)[] = JSON.parse( + localStorage.getItem("termix_paneInstanceIds") ?? "null", + ); + if (!Array.isArray(savedInstanceIds)) return; + + const restored = savedInstanceIds.map((instanceId) => { + if (instanceId == null) return null; + return tabs.find((t) => t.instanceId === instanceId)?.id ?? null; + }); + if (restored.some((id) => id != null)) { + setPaneTabIds(restored); + } else { + // None of the saved panes could be restored (e.g. reopen-tabs-on-login + // is disabled), so drop back to a single view instead of an empty split. + setSplitMode("none"); + } + } catch { + // silently fail + } + }, [tabsReady, tabs]); + // Debounced tab-order sync: when tab order changes, patch each persistent tab's tabOrder in DB. const orderSyncTimeoutRef = useRef | null>( null, @@ -1006,6 +1064,8 @@ export function AppShell({ savedLabel?: string; initialFilePath?: string; serialConfig?: SerialConfig; + joinSharedSessionId?: string | null; + joinShareId?: string | null; }, ) { const tabId = `${host.name}-${type}-${Date.now()}`; @@ -1022,6 +1082,8 @@ export function AppShell({ const savedLabel = restore?.savedLabel; const initialFilePath = restore?.initialFilePath; const serialConfig = restore?.serialConfig; + const joinSharedSessionId = restore?.joinSharedSessionId ?? null; + const joinShareId = restore?.joinShareId ?? null; // A saved label that doesn't match the bare host name or the auto-numbered pattern is a custom label const isCustomLabel = savedLabel != null && @@ -1043,6 +1105,8 @@ export function AppShell({ openedAt, terminalRef: ref, restoredSessionId: restore?.restoredSessionId ?? null, + joinSharedSessionId, + joinShareId, initialFilePath, serialConfig, }, @@ -1075,6 +1139,8 @@ export function AppShell({ openedAt, terminalRef: ref, restoredSessionId: restore?.restoredSessionId ?? null, + joinSharedSessionId, + joinShareId, initialFilePath, serialConfig, }, @@ -1330,6 +1396,17 @@ export function AppShell({ } } + function openShareForTab(id: string) { + const tab = tabs.find((t) => t.id === id); + if (!tab) return; + const ref = tab.terminalRef?.current; + if (ref?.canShare?.()) { + ref.openShareModal?.(); + } else { + toast.error(t("sessionSharing.notReadyToShare")); + } + } + function closeTab(id: string) { const tab = tabs.find((t) => t.id === id); const confirmEnabled = localStorage.getItem("confirmTabClose") === "true"; @@ -1675,6 +1752,56 @@ export function AppShell({ }} onRenameTab={renameTab} onReorderTabs={setTabs} + onJoinSharedSession={(session) => { + if (!session.shareId) return; + const existingHost = allHosts.find( + (h) => h.id === String(session.hostId), + ); + const host: Host = existingHost ?? { + id: String(session.hostId), + name: session.hostName, + username: "", + ip: "", + port: 0, + folder: "", + online: false, + cpu: null, + ram: null, + lastAccess: new Date().toISOString(), + authType: "none", + enableTerminal: false, + enableCommandHistory: false, + enableTunnel: false, + enableFileManager: false, + enableDocker: false, + enableProxmox: false, + enableTmuxMonitor: false, + enableSsh: false, + enableRdp: false, + enableVnc: false, + enableTelnet: false, + sshPort: 22, + rdpPort: 3389, + vncPort: 5900, + telnetPort: 23, + serverTunnels: [], + quickActions: [], + }; + const instanceId = + typeof crypto.randomUUID === "function" + ? crypto.randomUUID() + : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; + openTab(host, "terminal", { + instanceId, + restoredSessionId: null, + joinSharedSessionId: session.sessionId, + joinShareId: session.shareId, + savedLabel: t("connections.sharedSessionLabel", { + hostName: session.hostName, + }), + }); + if (isMobile) setSidebarOpen(false); + }} /> )} @@ -1690,16 +1817,16 @@ export function AppShell({ setUserPrefs((current) => ({ ...current, ...updates })) } + remoteSyncInitialServerUrl={remoteSyncInitialServerUrl} /> )} - {railView === "admin-settings" && isAdmin && ( + {railView === "admin-settings" && showMultiUserUI && (
-
- {/* Skinny icon rail — desktop only, hidden on mobile */} - - - {/* Desktop: inline resizable sidebar */} - {!isMobile && ( -
- {sidebarHeader} - {sidebarPanelContent} - - {sidebarOpen && !sidebarEditing && ( -
- )} -
- )} - - {/* Mobile: sidebar as overlay sheet */} - {isMobile && ( - - - {sidebarHeader} - {sidebarPanelContent} - - - )} - - {/* Main content area */} -
- {!isMobile && !sidebarOpen && ( - - )} -
- { - const targetTab = tabs.find((t) => t.id === tabId); - if (targetTab?.host) openTab(targetTab.host, "files"); +
+ {isElectron() && ( + <> + { + setRailView("user-profile"); + if (!sidebarOpen) setSidebarOpen(true); }} - isAppFullscreen={isAppFullscreen} - onToggleAppFullscreen={toggleAppFullscreen} /> -
- {/* Split view — always mounted when not mobile, hidden via CSS when inactive */} - {!isMobile && ( -
- -
- )} - - {/* Normal-view container. Tab nodes are appended here (or to pane elements) - by the DOM-placement effect above. React portals each tab's content - into its stable per-tab node so the component is never remounted. - When split is active, shown on top only if the active tab is not in a pane. */} -
- {tabs.map((tab) => { - const tabNode = getTabNode(tab.id, tab.type === "terminal"); - const paneIdx = isSplit ? paneTabIds.indexOf(tab.id) : -1; - const inPane = paneIdx !== -1; - const activeInline = !inPane && tab.id === activeTabId; - return createPortal( - renderTabContent( - tab, - openSingletonTab, - openTab, - closeTab, - inPane || activeInline, - (host, filePath) => - openTab(host, "files", { - instanceId: - typeof crypto.randomUUID === "function" - ? crypto.randomUUID() - : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, - restoredSessionId: null, - initialFilePath: filePath, - }), - (host, _path) => openTab(host, "files"), - (host, path) => - openTab(host, "terminal", { - instanceId: - typeof crypto.randomUUID === "function" - ? crypto.randomUUID() - : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, - restoredSessionId: null, - initialFilePath: path, - }), - renameTab, - saveQuickConnectHost, - ), - tabNode, - tab.id, - ); - })} -
-
-
- - {/* Bottom nav bar — mobile only */} - { + setRemoteSyncInitialServerUrl(url); + setRailView("user-profile"); + if (!sidebarOpen) setSidebarOpen(true); + }} + /> + + )} +
+ {/* Skinny icon rail — desktop only, hidden on mobile */} + + + {/* Desktop: inline resizable sidebar */} + {!isMobile && ( +
+ {sidebarHeader} + {sidebarPanelContent} + + {sidebarOpen && !sidebarEditing && ( +
+ )} +
+ )} + + {/* Mobile: sidebar as overlay sheet */} + {isMobile && ( + + + {sidebarHeader} + {sidebarPanelContent} + + + )} + + {/* Main content area */} +
+ {!isMobile && !sidebarOpen && ( + + )} +
+ { + const targetTab = tabs.find((t) => t.id === tabId); + if (targetTab?.host) openTab(targetTab.host, "files"); + }} + onOpenShare={openShareForTab} + isAppFullscreen={isAppFullscreen} + onToggleAppFullscreen={toggleAppFullscreen} + /> +
+ {/* Split view — always mounted when not mobile, hidden via CSS when inactive */} + {!isMobile && ( +
+ +
+ )} + + {/* Normal-view container. Tab nodes are appended here (or to pane elements) + by the DOM-placement effect above. React portals each tab's content + into its stable per-tab node so the component is never remounted. + When split is active, shown on top only if the active tab is not in a pane. */} +
+ {tabs.map((tab) => { + const tabNode = getTabNode(tab.id, tab.type === "terminal"); + const paneIdx = isSplit ? paneTabIds.indexOf(tab.id) : -1; + const inPane = paneIdx !== -1; + const activeInline = !inPane && tab.id === activeTabId; + return createPortal( + renderTabContent( + tab, + openSingletonTab, + openTab, + closeTab, + inPane || activeInline, + (host, filePath) => + openTab(host, "files", { + instanceId: + typeof crypto.randomUUID === "function" + ? crypto.randomUUID() + : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, + restoredSessionId: null, + initialFilePath: filePath, + }), + (host, _path) => openTab(host, "files"), + (host, path) => + openTab(host, "terminal", { + instanceId: + typeof crypto.randomUUID === "function" + ? crypto.randomUUID() + : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, + restoredSessionId: null, + initialFilePath: path, + }), + renameTab, + saveQuickConnectHost, + ), + tabNode, + tab.id, + ); + })} +
+
+
+ + {/* Bottom nav bar — mobile only */} + +
diff --git a/src/ui/api/acme-ssl-api.ts b/src/ui/api/acme-ssl-api.ts index 64bb286a..c9e9234d 100644 --- a/src/ui/api/acme-ssl-api.ts +++ b/src/ui/api/acme-ssl-api.ts @@ -1,6 +1,6 @@ import { authApi, handleApiError } from "@/main-axios"; -export type AcmeChallengeType = "http-webroot" | "dns-cloudflare"; +export type AcmeChallengeType = "http-webroot" | "dns-cloudflare" | "manual"; export type AcmeSettings = { enabled: boolean; @@ -45,3 +45,15 @@ export async function requestAcmeCertificate(): Promise< handleApiError(error, "request ACME certificate"); } } + +export async function uploadManualSslCertificate(payload: { + certificate: string; + privateKey: string; +}): Promise { + try { + const response = await authApi.post("/users/manual-ssl-upload", payload); + return response.data; + } catch (error) { + handleApiError(error, "upload manual SSL certificate"); + } +} diff --git a/src/ui/api/admin-user-data-api.ts b/src/ui/api/admin-user-data-api.ts index ecac34ea..e6c48dc7 100644 --- a/src/ui/api/admin-user-data-api.ts +++ b/src/ui/api/admin-user-data-api.ts @@ -99,7 +99,14 @@ export async function adminDeleteUserHost( export async function adminGetHostPassword( targetUserId: string, hostId: number, - field: "password" | "sudoPassword" | "vncPassword" = "password", + field: + | "password" + | "sudoPassword" + | "rdpPassword" + | "vncPassword" + | "telnetPassword" + | "key" + | "keyPassword" = "password", ): Promise { try { const response = await sshHostApi.get( diff --git a/src/ui/api/credentials-api.ts b/src/ui/api/credentials-api.ts index 7c0f1e7b..c8362bad 100644 --- a/src/ui/api/credentials-api.ts +++ b/src/ui/api/credentials-api.ts @@ -96,7 +96,14 @@ export async function getSSHHostWithCredentials( export async function getHostPassword( hostId: number, - field: "password" | "sudoPassword" | "vncPassword" = "password", + field: + | "password" + | "sudoPassword" + | "rdpPassword" + | "vncPassword" + | "telnetPassword" + | "key" + | "keyPassword" = "password", ): Promise { try { const response = await sshHostApi.get( @@ -200,6 +207,7 @@ export async function updateFolderMetadata( name: string, color?: string, icon?: string, + credentialId?: number | null, ): Promise { try { sshLogger.info("Updating folder metadata", { @@ -207,12 +215,14 @@ export async function updateFolderMetadata( name, color, icon, + credentialId, }); await authApi.put("/host/folders/metadata", { name, color, icon, + credentialId, }); sshLogger.success("Folder metadata updated successfully", { diff --git a/src/ui/api/guacamole-api.ts b/src/ui/api/guacamole-api.ts index abb59e60..f47dd455 100644 --- a/src/ui/api/guacamole-api.ts +++ b/src/ui/api/guacamole-api.ts @@ -72,6 +72,7 @@ export interface GuacamoleTokenRequest { export interface GuacamoleTokenResponse { token: string; + guacamoleConnectionId?: string | null; } type GuacamoleConfigSource = { @@ -208,12 +209,18 @@ export async function getGuacamoleToken( export async function getGuacamoleTokenFromHost( hostId: number, protocol?: "rdp" | "vnc" | "telnet", + promptedCredentials?: { username?: string; password?: string }, ): Promise { try { - const response = await authApi.post( - `/guacamole/connect-host/${hostId}`, - protocol ? { protocol } : {}, - ); + const response = await authApi.post(`/guacamole/connect-host/${hostId}`, { + ...(protocol ? { protocol } : {}), + ...(promptedCredentials?.username + ? { promptedUsername: promptedCredentials.username } + : {}), + ...(promptedCredentials?.password + ? { promptedPassword: promptedCredentials.password } + : {}), + }); return response.data; } catch (error) { throw handleApiError(error, "get guacamole token from host"); diff --git a/src/ui/api/host-metrics-api.ts b/src/ui/api/host-metrics-api.ts index f45b89e8..f7e06ee3 100644 --- a/src/ui/api/host-metrics-api.ts +++ b/src/ui/api/host-metrics-api.ts @@ -1,6 +1,15 @@ import { handleApiError, statsApi } from "@/main-axios"; import type { HostMetricsLayout } from "@/types/host-metrics"; +// Every function below is keyed by a host's numeric database id, and the +// receiving backend must own that host in its own database -- a synced +// host has a different numeric id on each side (only its syncId matches +// across them). These calls always target the embedded local backend; see +// getAllServerStatuses in host-metrics-status-api.ts for the one metrics +// call that IS safely merged across local + remote (a process-local, +// in-memory aggregate keyed by whichever host ids that process happens to +// know about, not a per-host lookup). + export interface MetricsHistoryRow { ts: string; cpu_percent: number | null; diff --git a/src/ui/api/host-metrics-status-api.ts b/src/ui/api/host-metrics-status-api.ts index 0a6eebe7..5ae0949f 100644 --- a/src/ui/api/host-metrics-status-api.ts +++ b/src/ui/api/host-metrics-status-api.ts @@ -1,8 +1,31 @@ import axios, { type AxiosRequestConfig } from "axios"; -import { handleApiError, statsApi } from "@/main-axios"; +import { + handleApiError, + statsApi, + getRemoteStatsApi, + isElectron, +} from "@/main-axios"; import type { ServerMetrics, ServerStatus } from "@/main-axios"; import { getCachedServerStatuses } from "@/lib/hosts-request-cache"; +// Metrics collection/viewer registration below (startMetricsPolling, +// registerMetricsViewer, etc.) is NOT origin-routed: the backend that +// receives the call must own the target host by numeric database id, and a +// synced host has a different numeric id in each database (only its +// syncId matches across them). Only the aggregate status read is merged +// across local + remote, same as tunnel status. +async function isRemoteSyncConnected(): Promise { + if (!isElectron()) return false; + try { + const config = (await window.electronAPI?.invoke?.( + "get-remote-sync-config", + )) as { serverUrl?: string } | null; + return !!config?.serverUrl; + } catch { + return false; + } +} + type ApiConnectionLog = { type: "info" | "success" | "warning" | "error"; stage: string; @@ -76,6 +99,7 @@ export async function getAllServerStatuses(): Promise< > { return getCachedServerStatuses(async () => { let lastError: unknown = null; + let localStatuses: Record = {}; for (let i = 0; i < STATUS_RETRY_SCHEDULE.length; i++) { const { timeoutMs, pauseAfterMs } = STATUS_RETRY_SCHEDULE[i]; @@ -89,7 +113,9 @@ export async function getAllServerStatuses(): Promise< // blips don't look like real outages. __silentRetry: !isFinalAttempt, } as AxiosRequestConfig & { __silentRetry?: boolean }); - return response.data || {}; + localStatuses = response.data || {}; + lastError = null; + break; } catch (error) { lastError = error; if (!isTransientStatusError(error)) { @@ -102,8 +128,24 @@ export async function getAllServerStatuses(): Promise< } } - handleApiError(lastError, "fetch server statuses"); - return {}; + if (lastError) { + handleApiError(lastError, "fetch server statuses"); + return {}; + } + + if (await isRemoteSyncConnected()) { + try { + const remoteResult = await getRemoteStatsApi().get("/status", { + timeout: 8000, + __silentRetry: true, + } as AxiosRequestConfig & { __silentRetry?: boolean }); + return { ...localStatuses, ...(remoteResult.data || {}) }; + } catch { + // remote unreachable this tick -- fall back to local-only statuses + } + } + + return localStatuses; }); } diff --git a/src/ui/api/open-tabs-api.ts b/src/ui/api/open-tabs-api.ts index e9eb5a7c..747fdb95 100644 --- a/src/ui/api/open-tabs-api.ts +++ b/src/ui/api/open-tabs-api.ts @@ -1,5 +1,7 @@ import { authApi } from "@/main-axios"; import { createTtlRequestCache } from "@/lib/ttl-request-cache"; +import type { TerminalTheme } from "@/lib/terminal-themes"; +import type { CustomKeybinding } from "@/types/keybindings"; // OPEN TABS API // ============================================================================ @@ -41,6 +43,10 @@ export interface ActiveSessionInfo { tabInstanceId: string | null; isConnected: boolean; createdAt: number; + isOwnSession: boolean; + sharedByUsername: string | null; + permissionLevel: string | null; + shareId: string | null; } const activeSessionsCache = createTtlRequestCache(2_000); @@ -82,6 +88,12 @@ export async function getActiveSessions(): Promise { // USER PREFERENCES API // ============================================================================ +export interface SavedCustomTheme { + id: string; + name: string; + colors: TerminalTheme["colors"]; +} + export interface UserPreferences { reopenTabsOnLogin: boolean; theme?: string | null; @@ -102,6 +114,30 @@ export interface UserPreferences { hiddenRailTabs?: string | null; compactHostView?: boolean | null; statusColorScheme?: string | null; + customThemes?: string | null; + customKeybindings?: string | null; +} + +export function parseCustomThemes(raw?: string | null): SavedCustomTheme[] { + if (!raw) return []; + try { + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +export function parseCustomKeybindings( + raw?: string | null, +): CustomKeybinding[] { + if (!raw) return []; + try { + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } } export async function getUserPreferences(): Promise { diff --git a/src/ui/api/rbac-api.ts b/src/ui/api/rbac-api.ts index 63f2553a..808843f8 100644 --- a/src/ui/api/rbac-api.ts +++ b/src/ui/api/rbac-api.ts @@ -124,6 +124,31 @@ export async function shareHost( } } +export async function shareFolder( + folder: string, + shareData: { + targets: ShareTarget[]; + permissionLevel: SharePermissionLevel; + durationHours?: number; + }, +): Promise<{ + success: boolean; + expiresAt: string | null; + hostsShared: number; + hostsTotal: number; + hostResults: Array<{ hostId: number; shared: boolean; reason?: string }>; +}> { + try { + const response = await rbacApi.post("/rbac/folder/share", { + folder, + ...shareData, + }); + return response.data; + } catch (error) { + throw handleApiError(error, "share folder"); + } +} + export async function updateHostAccess( hostId: number, accessId: number, diff --git a/src/ui/api/session-sharing-api.ts b/src/ui/api/session-sharing-api.ts new file mode 100644 index 00000000..343a15bc --- /dev/null +++ b/src/ui/api/session-sharing-api.ts @@ -0,0 +1,192 @@ +import axios from "axios"; +import { getBasePath } from "@/lib/base-path"; +import { isElectron } from "@/lib/electron"; +import { authApi, handleApiError } from "@/main-axios"; + +export interface ResolvedShareLink { + protocol: "ssh" | "rdp" | "vnc" | "telnet"; + permissionLevel: "read-only" | "read-write"; + wsPath: string; + connectParams?: { token: string }; +} + +export type ShareLinkErrorKind = "not-found" | "rate-limited" | "unknown"; + +export class ShareLinkError extends Error { + constructor( + message: string, + public readonly kind: ShareLinkErrorKind, + ) { + super(message); + this.name = "ShareLinkError"; + } +} + +const isDev = (): boolean => + !isElectron() && + process.env.NODE_ENV === "development" && + (window.location.port === "3000" || + window.location.port === "5173" || + window.location.port === ""); + +// Guests have no session/JWT, so this deliberately builds a bare base URL +// rather than going through main-axios's authenticated instances. The +// desktop app always runs its embedded local backend as the source of +// truth, so a share link opened there always resolves against it -- +// joining a session hosted on someone else's remote server isn't +// supported from the desktop app today. +async function resolveApiBaseUrl(): Promise { + if (isDev()) { + const protocol = window.location.protocol === "https:" ? "https" : "http"; + return `${protocol}://localhost:30001`; + } + if (isElectron()) { + return "http://127.0.0.1:30001"; + } + return getBasePath(); +} + +export async function resolveShareLink( + linkToken: string, +): Promise { + const baseUrl = await resolveApiBaseUrl(); + try { + const response = await axios.get( + `${baseUrl}/session-sharing/resolve/${encodeURIComponent(linkToken)}`, + ); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + if (error.response?.status === 404) { + throw new ShareLinkError( + "Share link is invalid, expired, or revoked", + "not-found", + ); + } + if (error.response?.status === 429) { + throw new ShareLinkError( + "Too many attempts, please try again shortly", + "rate-limited", + ); + } + } + throw new ShareLinkError("Failed to resolve share link", "unknown"); + } +} + +// ============================================================================ +// SESSION SHARING (authenticated owner-side API) +// ============================================================================ + +export type SessionShareProtocol = "ssh" | "rdp" | "vnc" | "telnet"; +export type SessionShareType = "link" | "user"; +export type SessionSharePermissionLevel = "read-only" | "read-write"; + +export interface SessionShareRecord { + id: string; + hostId: number; + ownerUserId: string; + protocol: SessionShareProtocol; + sessionId: string; + tabInstanceId: string | null; + shareType: SessionShareType; + targetUserId: string | null; + linkToken: string | null; + permissionLevel: SessionSharePermissionLevel; + createdAt: string; + expiresAt: string; + revokedAt: string | null; + lastJoinedAt: string | null; + joinCount: number; +} + +export interface CreateSessionShareRequest { + hostId: number; + sessionId: string; + tabInstanceId?: string; + protocol: SessionShareProtocol; + shareType: SessionShareType; + targetUserId?: string; + permissionLevel: SessionSharePermissionLevel; + expiryHours?: number; +} + +export interface CreateSessionShareResponse { + shareId: string; + linkToken: string | null; + expiresAt: string; +} + +export async function createSessionShare( + request: CreateSessionShareRequest, +): Promise { + try { + const response = await authApi.post("/session-sharing/create", request); + return response.data; + } catch (error) { + throw handleApiError(error, "create session share"); + } +} + +export async function getActiveSessionShares( + hostId: number, +): Promise<{ shares: SessionShareRecord[] }> { + try { + const response = await authApi.get( + `/session-sharing/host/${hostId}/active`, + ); + return response.data; + } catch (error) { + throw handleApiError(error, "fetch active session shares"); + } +} + +export async function revokeSessionShare( + shareId: string, +): Promise<{ success: true }> { + try { + const response = await authApi.delete(`/session-sharing/${shareId}`); + return response.data; + } catch (error) { + throw handleApiError(error, "revoke session share"); + } +} + +export async function endSessionShareSession( + shareId: string, +): Promise<{ success: true }> { + try { + const response = await authApi.post(`/session-sharing/${shareId}/end`); + return response.data; + } catch (error) { + throw handleApiError(error, "end shared session"); + } +} + +// ============================================================================ +// GLOBAL ADMIN TOGGLE +// ============================================================================ + +export async function getSessionSharingGloballyEnabled(): Promise<{ + enabled: boolean; +}> { + try { + const response = await authApi.get("/users/session-sharing-enabled"); + return response.data; + } catch (error) { + throw handleApiError(error, "fetch session sharing enabled setting"); + } +} + +export async function updateSessionSharingGloballyEnabled( + enabled: boolean, +): Promise<{ enabled: boolean }> { + try { + const response = await authApi.patch("/users/session-sharing-enabled", { + enabled, + }); + return response.data; + } catch (error) { + throw handleApiError(error, "update session sharing enabled setting"); + } +} diff --git a/src/ui/api/settings-api.ts b/src/ui/api/settings-api.ts index 4eeed208..f49f8ff4 100644 --- a/src/ui/api/settings-api.ts +++ b/src/ui/api/settings-api.ts @@ -145,6 +145,32 @@ export async function updateGuacamoleSettings(settings: { } } +// ============================================================================ +// ANALYTICS SETTINGS +// ============================================================================ + +export async function getAnalyticsEnabled(): Promise<{ enabled: boolean }> { + try { + const response = await authApi.get("/users/analytics-enabled"); + return response.data; + } catch (error) { + handleApiError(error, "fetch analytics enabled setting"); + } +} + +export async function updateAnalyticsEnabled( + enabled: boolean, +): Promise<{ enabled: boolean }> { + try { + const response = await authApi.patch("/users/analytics-enabled", { + enabled, + }); + return response.data; + } catch (error) { + handleApiError(error, "update analytics enabled setting"); + } +} + // ============================================================================ // HOST DEFAULTS SETTINGS // ============================================================================ diff --git a/src/ui/api/ssh-file-operations-api.ts b/src/ui/api/ssh-file-operations-api.ts index 082f4c96..f2798fe8 100644 --- a/src/ui/api/ssh-file-operations-api.ts +++ b/src/ui/api/ssh-file-operations-api.ts @@ -1,5 +1,13 @@ import axios from "axios"; -import { authApi, fileManagerApi, handleApiError } from "@/main-axios"; +import { + authApi, + fileManagerApi, + handleApiError, + getFileManagerApiForSession, + setSessionOrigin, + clearSessionOrigin, +} from "@/main-axios"; +import { resolveConnectionOrigin } from "@/lib/connection-origin"; import { fileLogger } from "@/lib/frontend-logger"; import type { SSHHost } from "@/types/index"; @@ -72,7 +80,7 @@ export async function connectSSH( }, ): Promise> { try { - const response = await fileManagerApi.post( + const response = await getFileManagerApiForSession(sessionId).post( "/ssh/connect", { sessionId, ...config }, { timeout: 120000 }, @@ -121,12 +129,15 @@ export async function disconnectSSH( sessionId: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/disconnect", { - sessionId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/disconnect", + { sessionId }, + ); return response.data; } catch (error) { handleApiError(error, "disconnect SSH"); + } finally { + clearSessionOrigin(sessionId); } } @@ -135,10 +146,10 @@ export async function verifySSHTOTP( totpCode: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/connect-totp", { - sessionId, - totpCode, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/connect-totp", + { sessionId, totpCode }, + ); return response.data; } catch (error) { handleApiError(error, "verify SSH TOTP"); @@ -149,9 +160,10 @@ export async function verifySSHWarpgate( sessionId: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/connect-warpgate", { - sessionId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/connect-warpgate", + { sessionId }, + ); return response.data; } catch (error) { handleApiError(error, "verify SSH Warpgate"); @@ -239,9 +251,10 @@ export async function getSSHStatus( sessionId: string, ): Promise<{ connected: boolean }> { try { - const response = await fileManagerApi.get("/ssh/status", { - params: { sessionId }, - }); + const response = await getFileManagerApiForSession(sessionId).get( + "/ssh/status", + { params: { sessionId } }, + ); return response.data; } catch (error) { handleApiError(error, "get SSH status"); @@ -252,9 +265,10 @@ export async function keepSSHAlive( sessionId: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/keepalive", { - sessionId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/keepalive", + { sessionId }, + ); return response.data; } catch (error) { handleApiError(error, "SSH keepalive"); @@ -266,9 +280,10 @@ export async function listSSHFiles( path: string, ): Promise<{ files: unknown[]; path: string }> { try { - const response = await fileManagerApi.get("/ssh/listFiles", { - params: { sessionId, path }, - }); + const response = await getFileManagerApiForSession(sessionId).get( + "/ssh/listFiles", + { params: { sessionId, path } }, + ); return response.data || { files: [], path }; } catch (error) { handleApiError(error, "list SSH files"); @@ -281,9 +296,10 @@ export async function identifySSHSymlink( path: string, ): Promise<{ path: string; target: string; type: "directory" | "file" }> { try { - const response = await fileManagerApi.get("/ssh/identifySymlink", { - params: { sessionId, path }, - }); + const response = await getFileManagerApiForSession(sessionId).get( + "/ssh/identifySymlink", + { params: { sessionId, path } }, + ); return response.data; } catch (error) { handleApiError(error, "identify SSH symlink"); @@ -295,9 +311,10 @@ export async function resolveSSHPath( path: string, ): Promise { try { - const response = await fileManagerApi.get("/ssh/resolvePath", { - params: { sessionId, path }, - }); + const response = await getFileManagerApiForSession(sessionId).get( + "/ssh/resolvePath", + { params: { sessionId, path } }, + ); return response.data?.resolvedPath || path; } catch { return path; @@ -313,9 +330,10 @@ export async function readSSHFile( encoding?: "base64" | "utf8"; }> { try { - const response = await fileManagerApi.get("/ssh/readFile", { - params: { sessionId, path }, - }); + const response = await getFileManagerApiForSession(sessionId).get( + "/ssh/readFile", + { params: { sessionId, path } }, + ); return response.data; } catch (error: unknown) { if (error.response?.status === 404) { @@ -340,13 +358,10 @@ export async function writeSSHFile( userId?: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/writeFile", { - sessionId, - path, - content, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/writeFile", + { sessionId, path, content, hostId, userId }, + ); if ( response.data && @@ -410,7 +425,7 @@ export async function uploadSSHFile( form.append("totalSize", String(file.size)); form.append("chunk", chunkBlob, fileName); - const response = await fileManagerApi.postForm( + const response = await getFileManagerApiForSession(sessionId).postForm( "/ssh/uploadFileChunk", form, { timeout: 0 }, @@ -444,7 +459,7 @@ export async function uploadSSHFile( if (userId !== undefined) form.append("userId", userId); form.append("file", file, fileName); - const response = await fileManagerApi.postForm( + const response = await getFileManagerApiForSession(sessionId).postForm( "/ssh/uploadFileStream", form, { @@ -464,7 +479,7 @@ export async function downloadSSHFile( userId?: string, ): Promise> { try { - const response = await fileManagerApi.post( + const response = await getFileManagerApiForSession(sessionId).post( "/ssh/downloadFile", { sessionId, @@ -484,7 +499,7 @@ export async function downloadSSHFileStream( sessionId: string, filePath: string, ): Promise { - const response = await fileManagerApi.post( + const response = await getFileManagerApiForSession(sessionId).post( "/ssh/downloadFileStream", { sessionId, path: filePath }, { responseType: "blob", timeout: 0 }, @@ -503,14 +518,10 @@ export async function createSSHFile( userId?: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/createFile", { - sessionId, - path, - fileName, - content, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/createFile", + { sessionId, path, fileName, content, hostId, userId }, + ); return response.data; } catch (error) { handleApiError(error, "create SSH file"); @@ -525,13 +536,10 @@ export async function createSSHFolder( userId?: string, ): Promise> { try { - const response = await fileManagerApi.post("/ssh/createFolder", { - sessionId, - path, - folderName, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/createFolder", + { sessionId, path, folderName, hostId, userId }, + ); return response.data; } catch (error) { handleApiError(error, "create SSH folder"); @@ -546,15 +554,18 @@ export async function deleteSSHItem( userId?: string, ): Promise> { try { - const response = await fileManagerApi.delete("/ssh/deleteItem", { - data: { - sessionId, - path, - isDirectory, - hostId, - userId, + const response = await getFileManagerApiForSession(sessionId).delete( + "/ssh/deleteItem", + { + data: { + sessionId, + path, + isDirectory, + hostId, + userId, + }, }, - }); + ); return response.data; } catch (error) { handleApiError(error, "delete SSH item"); @@ -566,7 +577,7 @@ export async function setSudoPassword( password: string, ): Promise { try { - await fileManagerApi.post("/sudo-password", { + await getFileManagerApiForSession(sessionId).post("/sudo-password", { sessionId, password, }); @@ -583,7 +594,7 @@ export async function copySSHItem( userId?: string, ): Promise> { try { - const response = await fileManagerApi.post( + const response = await getFileManagerApiForSession(sessionId).post( "/ssh/copyItem", { sessionId, @@ -611,13 +622,10 @@ export async function renameSSHItem( userId?: string, ): Promise> { try { - const response = await fileManagerApi.put("/ssh/renameItem", { - sessionId, - oldPath, - newName, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).put( + "/ssh/renameItem", + { sessionId, oldPath, newName, hostId, userId }, + ); return response.data; } catch (error) { handleApiError(error, "rename SSH item"); @@ -633,7 +641,7 @@ export async function moveSSHItem( userId?: string, ): Promise> { try { - const response = await fileManagerApi.put( + const response = await getFileManagerApiForSession(sessionId).put( "/ssh/moveItem", { sessionId, @@ -670,13 +678,10 @@ export async function changeSSHPermissions( userId, }); - const response = await fileManagerApi.post("/ssh/changePermissions", { - sessionId, - path, - permissions, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/changePermissions", + { sessionId, path, permissions, hostId, userId }, + ); fileLogger.success("SSH file permissions changed successfully", { operation: "change_permissions", @@ -715,13 +720,10 @@ export async function extractSSHArchive( userId, }); - const response = await fileManagerApi.post("/ssh/extractArchive", { - sessionId, - archivePath, - extractPath, - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/extractArchive", + { sessionId, archivePath, extractPath, hostId, userId }, + ); fileLogger.success("Archive extracted successfully", { operation: "extract_archive", @@ -762,14 +764,17 @@ export async function compressSSHFiles( userId, }); - const response = await fileManagerApi.post("/ssh/compressFiles", { - sessionId, - paths, - archiveName, - format: format || "zip", - hostId, - userId, - }); + const response = await getFileManagerApiForSession(sessionId).post( + "/ssh/compressFiles", + { + sessionId, + paths, + archiveName, + format: format || "zip", + hostId, + userId, + }, + ); fileLogger.success("Files compressed successfully", { operation: "compress_files", @@ -811,6 +816,12 @@ export async function ensureSSHSessionForHost( host: SSHHost, ): Promise { const sessionId = host.id.toString(); + const origin = await resolveConnectionOrigin({ + connectionType: host.connectionType, + connectionOrigin: host.connectionOrigin, + }); + setSessionOrigin(sessionId, origin); + try { const status = await getSSHStatus(sessionId); if (status?.connected) { diff --git a/src/ui/api/system-status-api.ts b/src/ui/api/system-status-api.ts index 63e42b72..50e770d9 100644 --- a/src/ui/api/system-status-api.ts +++ b/src/ui/api/system-status-api.ts @@ -1,10 +1,5 @@ import { AxiosError } from "axios"; -import { - authApi, - handleApiError, - isElectron, - markUserAuthenticated, -} from "@/main-axios"; +import { authApi, handleApiError, markUserAuthenticated } from "@/main-axios"; import type { AuthResponse } from "@/main-axios"; // ALERTS @@ -63,25 +58,6 @@ export async function verifyTOTPLogin( rememberMe, }); - const isInIframe = - typeof window !== "undefined" && window.self !== window.top; - - if (isInIframe && isElectron() && response.data.success) { - try { - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "totp_verify", - platform: "desktop", - timestamp: Date.now(), - }, - window.location.origin, - ); - } catch (e) { - console.error("[main-axios] Error posting message to parent:", e); - } - } - if (response.data.success) { markUserAuthenticated(); } diff --git a/src/ui/api/tunnel-api.ts b/src/ui/api/tunnel-api.ts index 311dc73f..ec445f43 100644 --- a/src/ui/api/tunnel-api.ts +++ b/src/ui/api/tunnel-api.ts @@ -1,5 +1,11 @@ import axios from "axios"; -import { authApi, handleApiError, tunnelApi } from "@/main-axios"; +import { + authApi, + handleApiError, + tunnelApi, + getRemoteTunnelApi, + isElectron, +} from "@/main-axios"; import type { C2STunnelPreset, TunnelConfig, @@ -9,13 +15,46 @@ import type { // TUNNEL MANAGEMENT // ============================================================================ +// +// Tunnel status is a process-local, in-memory view (no DB lookup) so it's +// safe to read from both the embedded backend and a connected remote server +// and merge the results. connectTunnel/disconnectTunnel/cancelTunnel are +// NOT origin-routed: they resolve the target host by numeric database id +// against whichever backend receives the request, and a synced host has a +// different numeric id in each database (only its syncId matches across +// them) -- routing those calls to a remote backend would need a +// local-id-to-remote-id resolution step that doesn't exist yet. They always +// target the embedded local backend for now. + +async function isRemoteSyncConnected(): Promise { + if (!isElectron()) return false; + try { + const config = (await window.electronAPI?.invoke?.( + "get-remote-sync-config", + )) as { serverUrl?: string } | null; + return !!config?.serverUrl; + } catch { + return false; + } +} export async function getTunnelStatuses(): Promise< Record > { try { - const response = await tunnelApi.get("/tunnel/status"); - return response.data || {}; + const [localResult, remoteConnected] = await Promise.all([ + tunnelApi.get("/tunnel/status"), + isRemoteSyncConnected(), + ]); + const localStatuses = localResult.data || {}; + if (!remoteConnected) return localStatuses; + + try { + const remoteResult = await getRemoteTunnelApi().get("/tunnel/status"); + return { ...localStatuses, ...(remoteResult.data || {}) }; + } catch { + return localStatuses; + } } catch (error) { handleApiError(error, "fetch tunnel statuses"); } @@ -30,9 +69,18 @@ export function subscribeTunnelStatuses( withCredentials: true, }); + let latestLocal: Record = {}; + let latestRemote: Record = {}; + let remotePollTimer: ReturnType | null = null; + + const emitMerged = () => { + onStatuses({ ...latestLocal, ...latestRemote }); + }; + source.addEventListener("statuses", (event) => { try { - onStatuses(JSON.parse(event.data) as Record); + latestLocal = JSON.parse(event.data) as Record; + emitMerged(); } catch { onError?.(); } @@ -42,7 +90,27 @@ export function subscribeTunnelStatuses( onError?.(); }; - return () => source.close(); + // Remote tunnel status has no SSE stream exposed to the desktop app yet, + // so poll it at a modest interval when a remote server is connected. + isRemoteSyncConnected().then((connected) => { + if (!connected) return; + const pollRemote = async () => { + try { + const result = await getRemoteTunnelApi().get("/tunnel/status"); + latestRemote = result.data || {}; + emitMerged(); + } catch { + // remote unreachable this tick -- keep last known remote statuses + } + }; + pollRemote(); + remotePollTimer = setInterval(pollRemote, 5000); + }); + + return () => { + source.close(); + if (remotePollTimer) clearInterval(remotePollTimer); + }; } export async function getTunnelStatusByName( diff --git a/src/ui/auth/Auth.tsx b/src/ui/auth/Auth.tsx index b34a4c48..80fc649d 100644 --- a/src/ui/auth/Auth.tsx +++ b/src/ui/auth/Auth.tsx @@ -29,17 +29,13 @@ import { completePasswordReset, getOIDCAuthorizeUrl, verifyTOTPLogin, - getServerConfig, - saveServerConfig, isElectron, - getEmbeddedServerStatus, getCurrentToken, getOidcSilentLoginDefault, + requestDesktopAutoSession, } 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"; import { changeAppLanguage, normalizeLanguageCode } from "@/i18n/i18n"; import { @@ -263,13 +259,23 @@ export function Auth({ onLogin }: AuthProps) { const [firstUser, setFirstUser] = useState(false); const [dbConnectionFailed, setDbConnectionFailed] = useState(false); const [dbHealthChecking, setDbHealthChecking] = useState(true); - - const [showServerConfig, setShowServerConfig] = useState( - null, - ); - const [currentServerUrl, setCurrentServerUrl] = useState(""); const [webviewAuthSuccess, setWebviewAuthSuccess] = useState(false); + // Electron, non-iframed only: the desktop app never shows a login form + // when running standalone -- the embedded backend auto-provisions a + // single local user on first boot, and this component silently exchanges + // that for a session instead of rendering login/register. + // null = probe still in flight (Electron only, blocks rendering below). + // true = probe settled with no auto-login (not applicable outside + // Electron, multiple users exist, or setup is genuinely required) -- + // safe to fall through to the normal form/health-check flow. + // Auto-login success never sets this; it calls onLogin directly and this + // component unmounts. + const [desktopAutoSessionDone, setDesktopAutoSessionDone] = useState< + boolean | null + >(!isElectron() || isInElectronWebView() ? true : null); + const [desktopAutoSessionRetries, setDesktopAutoSessionRetries] = useState(0); + useEffect(() => { try { localStorage.setItem("rememberMe", rememberMe.toString()); @@ -320,7 +326,11 @@ export function Auth({ onLogin }: AuthProps) { }, []); useEffect(() => { - if (showServerConfig) return; + // Runs once the auto-session probe has settled (immediately outside + // Electron, since it starts at true there; after the probe resolves in + // Electron). Waiting avoids flashing a login screen the user is about + // to skip past via auto-login. + if (desktopAutoSessionDone !== true) return; setDbHealthChecking(true); getSetupRequired() .then((res) => { @@ -332,53 +342,59 @@ export function Auth({ onLogin }: AuthProps) { }) .catch(() => setDbConnectionFailed(true)) .finally(() => setDbHealthChecking(false)); - }, [showServerConfig]); + }, [desktopAutoSessionDone]); + // A cold first launch spawns the embedded backend as a separate process + // that can take anywhere from a couple seconds to much longer to finish + // booting (DB init, SSL, antivirus scanning a freshly-unpacked binary, + // slow disks, etc.) -- well after the renderer has already mounted. The + // embedded backend is bundled, always-on infrastructure, not something + // that can be "not there" -- it always eventually comes up. So a + // "retry" outcome (connection error, not a real verdict) is retried + // forever with capped backoff rather than ever giving up and falling + // through to the login form: that form is not a valid destination for a + // standalone install with no remote sync configured, since the only + // local account has no password to log in with. Only a definitive + // "declined" (backend reachable and says no -- multiple users, or the + // sole local user has a real credential) stops retrying and shows the + // real form. useEffect(() => { - const checkElectron = async () => { - if (isInElectronWebView()) { - setShowServerConfig(false); - return; - } - if (isElectron()) { - const forceShow = localStorage.getItem("termix_show_server_config"); - if (forceShow === "true") { - localStorage.removeItem("termix_show_server_config"); - try { - const config = await getServerConfig(); - setCurrentServerUrl(config?.serverUrl || ""); - } catch { - // ignore - } - setShowServerConfig(true); + if (desktopAutoSessionDone !== null) return; + let cancelled = false; + let retryTimer: ReturnType | null = null; + requestDesktopAutoSession() + .then((outcome) => { + if (cancelled) return; + if (outcome.kind === "success") { + storeAuth(outcome.data.username || ""); + onLogin( + outcome.data.username || "", + outcome.data.userId || undefined, + !!outcome.data.is_admin, + ); return; } - try { - const [config, status] = await Promise.all([ - getServerConfig(), - getEmbeddedServerStatus(), - ]); - if ( - status?.embedded && - status?.running && - config && - !config.serverUrl - ) { - setShowServerConfig(false); - setCurrentServerUrl(""); - return; - } - setCurrentServerUrl(config?.serverUrl || ""); - setShowServerConfig(!config || !config.serverUrl); - } catch { - setShowServerConfig(true); + if (outcome.kind === "retry") { + const delay = Math.min(1000 * 2 ** desktopAutoSessionRetries, 10000); + retryTimer = setTimeout(() => { + if (!cancelled) setDesktopAutoSessionRetries((c) => c + 1); + }, delay); + return; } - } else { - setShowServerConfig(false); - } + setDesktopAutoSessionDone(true); + }) + .catch(() => { + if (cancelled) return; + const delay = Math.min(1000 * 2 ** desktopAutoSessionRetries, 10000); + retryTimer = setTimeout(() => { + if (!cancelled) setDesktopAutoSessionRetries((c) => c + 1); + }, delay); + }); + return () => { + cancelled = true; + if (retryTimer) clearTimeout(retryTimer); }; - checkElectron(); - }, []); + }, [desktopAutoSessionDone, desktopAutoSessionRetries, onLogin]); useEffect(() => { if (view === "totp" && totpInputRef.current) totpInputRef.current.focus(); @@ -474,36 +490,6 @@ export function Auth({ onLogin }: AuthProps) { } }, [onLogin, t]); - const handleElectronAuthSuccess = useCallback( - async (token: string | null) => { - try { - if (!token) { - // No token in postMessage — fall back to waiting for the HttpOnly cookie - const cookieReady = await window.electronAPI?.waitForSessionCookie?.( - "jwt", - currentServerUrl, - null, - 5000, - ); - if (cookieReady && !cookieReady.success) - throw new Error(cookieReady.error || "Auth cookie not ready"); - } - const meRes = await getUserInfo(); - if (!meRes) throw new Error("Failed to get user info"); - storeAuth(meRes.username || ""); - onLogin( - meRes.username || "", - meRes.userId || undefined, - !!meRes.is_admin, - ); - toast.success(t("messages.loginSuccess")); - } catch { - toast.error(t("errors.failedUserInfo")); - } - }, - [onLogin, currentServerUrl, t], - ); - function resetAll() { setUsername(""); setPassword(""); @@ -549,11 +535,19 @@ export function Auth({ onLogin }: AuthProps) { return; } if (isInElectronWebView()) { + // The iframe's login request never carries the X-Electron-App header + // (only the top-level Electron renderer's axios instances do), so the + // backend never includes the JWT in the login response body -- it + // only lands in an HttpOnly cookie scoped to this iframe's origin. + // Read it back via /users/me/token, same as the mobile-webview OIDC + // callback below does, so the parent window can persist it. + const token = res?.token ?? (await getCurrentToken()); window.parent.postMessage( { type: "AUTH_SUCCESS", source: "auth_component", platform: "desktop", + token: token ?? null, timestamp: Date.now(), }, "*", @@ -607,6 +601,35 @@ export function Auth({ onLogin }: AuthProps) { setView("totp"); return; } + if (isInMobileWebView()) { + // Native-app requests get the JWT in the login response body. + const token = res?.token ?? ""; + (window as ExtendedWindow).ReactNativeWebView?.postMessage( + JSON.stringify({ type: "AUTH_SUCCESS", token }), + ); + setWebviewAuthSuccess(true); + return; + } + if (isInElectronWebView()) { + // Registration inside the Remote Sync iframe must hand off to the + // parent window the same way handleLogin does -- otherwise this + // component's own onLogin() below fires on the iframe's own, + // independent copy of the app, rendering the full remote AppShell + // inside the small login dialog instead of closing it. + const token = res?.token ?? (await getCurrentToken()); + window.parent.postMessage( + { + type: "AUTH_SUCCESS", + source: "auth_component", + platform: "desktop", + token: token ?? null, + timestamp: Date.now(), + }, + "*", + ); + setWebviewAuthSuccess(true); + return; + } const meRes = await getUserInfo(); storeAuth(meRes.username || username.trim()); toast.success(t("messages.registrationSuccess")); @@ -650,11 +673,16 @@ export function Auth({ onLogin }: AuthProps) { return; } if (isInElectronWebView()) { + // See the equivalent branch in handleLogin: the iframe never sends + // X-Electron-App, so the JWT never lands in the response body here + // either -- read it back from the HttpOnly cookie that was just set. + const token = res?.token ?? (await getCurrentToken()); window.parent.postMessage( { type: "AUTH_SUCCESS", source: "totp_auth_component", platform: "desktop", + token: token ?? null, timestamp: Date.now(), }, "*", @@ -935,46 +963,19 @@ export function Auth({ onLogin }: AuthProps) { oidcSilentLoginDefaultLoaded, ]); - // Electron server config / webview auth success screens - if (isElectron() && !isInElectronWebView()) { - if (showServerConfig === null) - return ( -
-
-
- ); - if (showServerConfig) - return ( -
-
- window.location.reload()} - onUseEmbedded={async () => { - await saveServerConfig({ - serverUrl: "", - lastUpdated: new Date().toISOString(), - }); - setShowServerConfig(false); - setCurrentServerUrl(""); - }} - onCancel={() => setShowServerConfig(false)} - isFirstTime={!currentServerUrl} - /> -
-
- ); - if (!webviewAuthSuccess && showServerConfig === false && currentServerUrl) - return ( -
-
- setShowServerConfig(true)} - /> -
-
- ); + // Electron, non-iframed: wait for the auto-session probe before rendering + // anything, so a standalone desktop install never flashes a login form + // it's about to skip past. + if ( + isElectron() && + !isInElectronWebView() && + desktopAutoSessionDone === null + ) { + return ( +
+
+
+ ); } if (webviewAuthSuccess || (isInElectronWebView() && webviewAuthSuccess)) @@ -1018,30 +1019,11 @@ export function Auth({ onLogin }: AuthProps) { ))}
- {isElectron() && currentServerUrl && ( -
-
- - {t("serverConfig.serverUrl")} - - - {currentServerUrl} - -
- -
- )}
); - if (dbHealthChecking && showServerConfig === false) + if (dbHealthChecking) return (
@@ -1068,21 +1050,6 @@ export function Auth({ onLogin }: AuthProps) { return (
- {isElectron() && !isInElectronWebView() && showServerConfig === false && ( -
- - - {t("serverConfig.localServer")} - -
-
- )}
{/* Left decorative panel */}
diff --git a/src/ui/auth/ElectronLoginForm.tsx b/src/ui/auth/ElectronLoginForm.tsx index b49cb358..3abc0569 100644 --- a/src/ui/auth/ElectronLoginForm.tsx +++ b/src/ui/auth/ElectronLoginForm.tsx @@ -7,6 +7,12 @@ interface ElectronLoginFormProps { serverUrl: string; onAuthSuccess: (token: string | null) => void | Promise; onChangeServer: () => void; + // "local" (default): the app's own login, JWT goes to localStorage like + // every other client. "remoteSync": this iframe is authenticating a + // Settings-triggered connection to a remote Termix server for the sync + // engine -- the JWT is handed to the Electron main process's encrypted + // store instead, never exposed to the renderer's localStorage. + targetPurpose?: "local" | "remoteSync"; } const AUTH_MESSAGE_SOURCES = new Set([ @@ -19,6 +25,7 @@ export function ElectronLoginForm({ serverUrl, onAuthSuccess, onChangeServer, + targetPurpose = "local", }: ElectronLoginFormProps) { const { t } = useTranslation(); const [loading, setLoading] = useState(true); @@ -43,7 +50,11 @@ export function ElectronLoginForm({ try { if (token) { - localStorage.setItem("jwt", token); + if (targetPurpose === "remoteSync") { + await window.electronAPI?.invoke?.("save-remote-sync-jwt", token); + } else { + localStorage.setItem("jwt", token); + } } await onAuthSuccessRef.current(token); } catch { @@ -53,7 +64,7 @@ export function ElectronLoginForm({ hasAuthenticatedRef.current = false; } }, - [t], + [t, targetPurpose], ); // postMessage from server Auth.tsx after the backend has set the HttpOnly cookie. @@ -204,7 +215,7 @@ export function ElectronLoginForm({ const isEmbeddedServer = serverUrl.includes("localhost:30001"); return ( -
+
{isAuthenticating && (
@@ -212,7 +223,7 @@ export function ElectronLoginForm({ )} {!isAuthenticating && ( -
+
-

- {t("serverConfig.embeddedDesc")} -

-
-
- - {t("common.or") || "OR"} - -
-
- - )} - -
-
- -
- handleUrlChange(e.target.value)} - disabled={loading || embeddedLoading} - className={savedUrls.length > 0 ? "pr-9" : ""} - onFocus={() => { - if (savedUrls.length > 0) setDropdownOpen(true); - }} - /> - {savedUrls.length > 0 && ( - - )} - {dropdownOpen && savedUrls.length > 0 && ( -
-

- {t("serverConfig.savedServers")} -

- {savedUrls.map((url) => ( -
- - -
- ))} -
- )} -
-
- - {serverUrl.trim().startsWith("https://") && ( -
-
- -

- {t("serverConfig.allowInvalidCertificateDesc")} -

-
- -
- )} - - {error && ( - - {t("common.error")} - {error} - - )} - -
- {onCancel && !isFirstTime && ( - - )} - -
- -

- {t("serverConfig.helpText")} -

-
-
-
- ); -} diff --git a/src/ui/auth/LoginPage.tsx b/src/ui/auth/LoginPage.tsx deleted file mode 100644 index 4efd8497..00000000 --- a/src/ui/auth/LoginPage.tsx +++ /dev/null @@ -1,1981 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import React, { useState, useEffect, useCallback, useRef } from "react"; -import { Button } from "@/components/button.tsx"; -import { Input } from "@/components/input.tsx"; -import { PasswordInput } from "@/components/password-input.tsx"; -import { Label } from "@/components/label.tsx"; -import { Checkbox } from "@/components/checkbox.tsx"; -import { Alert, AlertDescription, AlertTitle } from "@/components/alert.tsx"; -import { Tabs, TabsList, TabsTrigger } from "@/components/tabs.tsx"; -import { useTranslation } from "react-i18next"; -import { LanguageSwitcher } from "@/user/LanguageSwitcher.tsx"; -import { toast } from "sonner"; -import { Sun, Moon, Monitor } from "lucide-react"; -import { useTheme } from "@/components/theme-provider"; -import { - registerUser, - loginUser, - getUserInfo, - getRegistrationAllowed, - getPasswordLoginAllowed, - getSetupRequired, - initiatePasswordReset, - verifyPasswordResetCode, - completePasswordReset, - getOIDCAuthorizeUrl, - verifyTOTPLogin, - getServerConfig, - saveServerConfig, - isElectron, - getEmbeddedServerStatus, - getCurrentToken, - getOidcSilentLoginDefault, -} from "@/main-axios"; -import { getSSOProviders, ldapLogin } from "@/api/sso-provider-api"; -import { authenticateWithWebAuthn } from "@/api/webauthn-api"; -import type { SSOProviderPublic } from "@/types/index"; -import { ElectronServerConfig as ServerConfigComponent } from "@/auth/ElectronServerConfig.tsx"; -import { ElectronLoginForm } from "@/auth/ElectronLoginForm.tsx"; -import { - removeSilentSigninFromSearch, - shouldTriggerSilentSignin, -} from "./silent-signin"; - -interface ExtendedWindow extends Window { - IS_ELECTRON_WEBVIEW?: boolean; - ReactNativeWebView?: { postMessage: (msg: string) => void }; -} - -const isInMobileWebView = () => - /Termix-Mobile\/(Android|iOS)/.test(navigator.userAgent) || - !!(window as ExtendedWindow).ReactNativeWebView; - -interface AuthProps extends React.ComponentProps<"div"> { - setLoggedIn: (loggedIn: boolean) => void; - setIsAdmin: (isAdmin: boolean) => void; - setUsername: (username: string | null) => void; - setUserId: (userId: string | null) => void; - loggedIn: boolean; - authLoading: boolean; - setDbError: (error: string | null) => void; - onAuthSuccess: (authData: { - isAdmin: boolean; - username: string | null; - userId: string | null; - }) => void; -} - -export function Auth({ - className, - setLoggedIn, - setIsAdmin, - setUsername, - setUserId, - loggedIn, - authLoading, - setDbError, - onAuthSuccess, - ...props -}: AuthProps) { - const { t } = useTranslation(); - const { theme, setTheme } = useTheme(); - - const isDarkMode = - theme === "dark" || - theme === "dracula" || - theme === "gentlemansChoice" || - theme === "midnightEspresso" || - theme === "catppuccinMocha" || - (theme === "system" && - window.matchMedia("(prefers-color-scheme: dark)").matches); - const lineColor = isDarkMode ? "#151517" : "#f9f9f9"; - - const isInElectronWebView = useCallback(() => { - if (isInMobileWebView()) return false; - if ((window as ExtendedWindow).IS_ELECTRON_WEBVIEW) { - return true; - } - try { - if (window.self !== window.top) { - return true; - } - } catch { - return true; - } - return false; - }, []); - - const [tab, setTab] = useState<"login" | "signup" | "reset">("login"); - const [localUsername, setLocalUsername] = useState(""); - const [password, setPassword] = useState(""); - const [signupConfirmPassword, setSignupConfirmPassword] = useState(""); - const [rememberMe, setRememberMe] = useState(() => { - try { - const saved = localStorage.getItem("rememberMe"); - return saved === "true"; - } catch { - return false; - } - }); - const [loading, setLoading] = useState(false); - const [passkeyLoading, setPasskeyLoading] = useState(false); - const [oidcLoading, setOidcLoading] = useState(false); - const [internalLoggedIn, setInternalLoggedIn] = useState(false); - const [firstUser, setFirstUser] = useState(false); - const [firstUserToastShown, setFirstUserToastShown] = useState(false); - const [registrationAllowed, setRegistrationAllowed] = useState(true); - const [passwordLoginAllowed, setPasswordLoginAllowed] = useState(true); - 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 [oidcSilentLoginDefault, setOidcSilentLoginDefault] = useState(false); - const [oidcSilentLoginDefaultLoaded, setOidcSilentLoginDefaultLoaded] = - useState(false); - - const [resetStep, setResetStep] = useState< - "initiate" | "verify" | "newPassword" - >("initiate"); - const [resetCode, setResetCode] = useState(""); - const [newPassword, setNewPassword] = useState(""); - const [confirmPassword, setConfirmPassword] = useState(""); - const [tempToken, setTempToken] = useState(""); - const [resetLoading, setResetLoading] = useState(false); - const [resetSuccess, setResetSuccess] = useState(false); - - const [totpRequired, setTotpRequired] = useState(false); - const [totpCode, setTotpCode] = useState(""); - const [totpTempToken, setTotpTempToken] = useState(""); - const [totpLoading, setTotpLoading] = useState(false); - const [webviewAuthSuccess, setWebviewAuthSuccess] = useState(false); - const totpInputRef = React.useRef(null); - - // Hand the JWT to the native app embedding this page in a React Native WebView. - // The mobile onMessage handler only reads { type, token }. - const postMobileAuthSuccess = useCallback((token: string) => { - (window as ExtendedWindow).ReactNativeWebView?.postMessage( - JSON.stringify({ type: "AUTH_SUCCESS", token }), - ); - setWebviewAuthSuccess(true); - }, []); - - const [showServerConfig, setShowServerConfig] = useState( - null, - ); - const [currentServerUrl, setCurrentServerUrl] = useState(""); - const [dbConnectionFailed, setDbConnectionFailed] = useState(false); - const [dbHealthChecking, setDbHealthChecking] = useState(false); - - const handleElectronAuthSuccess = useCallback(async () => { - try { - // token was stored in localStorage by ElectronLoginForm before this runs, - // so getUserInfo() can authenticate via the cookie interceptor or localStorage jwt. - let retries = 5; - let meRes = null; - while (retries-- > 0) { - try { - meRes = await getUserInfo(); - break; - } catch (err: unknown) { - const isNoServer = - (err as { code?: string })?.code === "NO_SERVER_CONFIGURED" || - (err as Error)?.message?.includes("no-server-configured"); - if (isNoServer && retries > 0) { - await new Promise((r) => setTimeout(r, 500)); - } else { - throw err; - } - } - } - if (!meRes) throw new Error("Failed to get user info"); - setInternalLoggedIn(true); - setLoggedIn(true); - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - onAuthSuccess({ - isAdmin: !!meRes.is_admin, - username: meRes.username || null, - userId: meRes.userId || null, - }); - toast.success(t("messages.loginSuccess")); - } catch { - toast.error(t("errors.failedUserInfo")); - } - }, [ - onAuthSuccess, - setLoggedIn, - setIsAdmin, - setUsername, - setUserId, - t, - setInternalLoggedIn, - ]); - - useEffect(() => { - setInternalLoggedIn(loggedIn); - }, [loggedIn]); - - useEffect(() => { - if (totpRequired && totpInputRef.current) { - totpInputRef.current.focus(); - } - }, [totpRequired]); - - useEffect(() => { - try { - localStorage.setItem("rememberMe", rememberMe.toString()); - } catch { - // expected - localStorage might not be available - } - }, [rememberMe]); - - useEffect(() => { - getRegistrationAllowed().then((res) => { - setRegistrationAllowed(res.allowed); - }); - }, [isInElectronWebView]); - - useEffect(() => { - getPasswordLoginAllowed() - .then((res) => { - setPasswordLoginAllowed(res.allowed); - }) - .catch((err) => { - if (err.code !== "NO_SERVER_CONFIGURED") { - console.error("Failed to fetch password login status:", err); - } - }); - }, []); - - useEffect(() => { - getSSOProviders() - .then((providers) => { - setSsoProviders(providers || []); - }) - .catch(() => { - setSsoProviders([]); - }) - .finally(() => { - setSsoProvidersLoaded(true); - }); - }, []); - - useEffect(() => { - getOidcSilentLoginDefault() - .then((res) => { - setOidcSilentLoginDefault(res.enabled); - }) - .catch(() => {}) - .finally(() => { - setOidcSilentLoginDefaultLoaded(true); - }); - }, []); - - useEffect(() => { - if (showServerConfig) { - return; - } - - setDbHealthChecking(true); - getSetupRequired() - .then((res) => { - if (res.setup_required) { - setFirstUser(true); - setTab("signup"); - if (!firstUserToastShown) { - toast.info(t("auth.firstUserMessage")); - setFirstUserToastShown(true); - } - } else { - setFirstUser(false); - } - setDbError(null); - setDbConnectionFailed(false); - }) - .catch(() => { - setDbConnectionFailed(true); - }) - .finally(() => { - setDbHealthChecking(false); - }); - }, [setDbError, firstUserToastShown, showServerConfig, t]); - - // 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(); - setLoading(true); - - if (!localUsername.trim()) { - toast.error(t("errors.requiredField")); - setLoading(false); - return; - } - - if (!passwordLoginAllowed && !firstUser) { - toast.error(t("errors.passwordLoginDisabled")); - setLoading(false); - return; - } - - try { - let res; - if (tab === "login") { - res = await loginUser(localUsername, password, rememberMe); - } else { - if (password !== signupConfirmPassword) { - toast.error(t("errors.passwordMismatch")); - setLoading(false); - return; - } - if (password.length < 6) { - toast.error(t("errors.minLength", { min: 6 })); - setLoading(false); - return; - } - - await registerUser(localUsername, password); - res = await loginUser(localUsername, password, rememberMe); - } - - if (res.requires_totp) { - setTotpRequired(true); - setTotpTempToken(res.temp_token); - setLoading(false); - return; - } - - if (!res || !res.success) { - throw new Error(t("errors.loginFailed")); - } - - if (isInMobileWebView()) { - // Native-app requests get the JWT in the login response body. - postMobileAuthSuccess(res.token || ""); - return; - } - - if (isInElectronWebView()) { - try { - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "auth_component", - platform: "desktop", - token: res.token || null, - timestamp: Date.now(), - }, - "*", - ); - setWebviewAuthSuccess(true); - return; - } catch (e) { - console.error("Error posting auth success message:", e); - } - } - - const [meRes] = await Promise.all([getUserInfo()]); - - setInternalLoggedIn(true); - setLoggedIn(true); - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - setDbError(null); - onAuthSuccess({ - isAdmin: !!meRes.is_admin, - username: meRes.username || null, - userId: meRes.userId || null, - }); - setInternalLoggedIn(true); - if (tab === "signup") { - setSignupConfirmPassword(""); - toast.success(t("messages.registrationSuccess")); - } else { - toast.success(t("messages.loginSuccess")); - } - setTotpRequired(false); - setTotpCode(""); - setTotpTempToken(""); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { data?: { error?: string } }; - }; - const errorMessage = - error?.response?.data?.error || - error?.message || - t("errors.unknownError"); - toast.error(errorMessage); - setInternalLoggedIn(false); - setLoggedIn(false); - setIsAdmin(false); - setUsername(null); - setUserId(null); - if (error?.response?.data?.error?.includes("Database")) { - setDbConnectionFailed(true); - } else { - setDbError(null); - } - } finally { - setLoading(false); - } - } - - async function handleInitiatePasswordReset() { - setResetLoading(true); - try { - await initiatePasswordReset(localUsername); - setResetStep("verify"); - toast.success(t("messages.resetCodeSent")); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { data?: { error?: string } }; - }; - toast.error( - error?.response?.data?.error || - error?.message || - t("errors.failedPasswordReset"), - ); - } finally { - setResetLoading(false); - } - } - - async function handleVerifyResetCode() { - setResetLoading(true); - try { - const response = await verifyPasswordResetCode(localUsername, resetCode); - setTempToken(response.tempToken); - setResetStep("newPassword"); - toast.success(t("messages.codeVerified")); - } catch (err: unknown) { - const error = err as { - response?: { - data?: { - error?: string; - code?: string; - remainingTime?: number; - remainingAttempts?: number; - }; - }; - }; - const errorCode = error?.response?.data?.code; - const remainingTime = error?.response?.data?.remainingTime; - const remainingAttempts = error?.response?.data?.remainingAttempts; - - let errorMessage = - error?.response?.data?.error || t("errors.failedVerifyCode"); - - if (errorCode === "RESET_CODE_RATE_LIMITED") { - if (remainingTime) { - errorMessage = t("errors.resetCodeRateLimitedWithTime", { - time: remainingTime, - }); - } else { - errorMessage = t("errors.resetCodeRateLimited"); - } - } else if ( - remainingAttempts !== undefined && - remainingAttempts <= 2 && - remainingAttempts > 0 - ) { - errorMessage = `${errorMessage} (${remainingAttempts} ${t("auth.attemptsRemaining")})`; - } - - toast.error(errorMessage); - } finally { - setResetLoading(false); - } - } - - async function handleCompletePasswordReset() { - setResetLoading(true); - - if (newPassword !== confirmPassword) { - toast.error(t("errors.passwordMismatch")); - setResetLoading(false); - return; - } - - if (newPassword.length < 6) { - toast.error(t("errors.minLength", { min: 6 })); - setResetLoading(false); - return; - } - - try { - try { - await completePasswordReset(localUsername, tempToken, newPassword); - } catch (err: unknown) { - const error = err as { - response?: { data?: { code?: string } }; - }; - if (error?.response?.data?.code !== "DATA_WIPE_REQUIRED") { - throw err; - } - if (!window.confirm(t("auth.confirmResetDataWipe"))) { - setResetLoading(false); - return; - } - await completePasswordReset( - localUsername, - tempToken, - newPassword, - true, - ); - } - - setResetStep("initiate"); - setResetCode(""); - setNewPassword(""); - setConfirmPassword(""); - setTempToken(""); - - setResetSuccess(true); - toast.success(t("messages.passwordResetSuccess")); - - setTab("login"); - resetPasswordState(); - } catch (err: unknown) { - const error = err as { response?: { data?: { error?: string } } }; - toast.error( - error?.response?.data?.error || t("errors.failedCompleteReset"), - ); - } finally { - setResetLoading(false); - } - } - - function resetPasswordState() { - setResetStep("initiate"); - setResetCode(""); - setNewPassword(""); - setConfirmPassword(""); - setTempToken(""); - setResetSuccess(false); - setSignupConfirmPassword(""); - } - - function clearFormFields() { - setPassword(""); - setSignupConfirmPassword(""); - } - - async function handleTOTPVerification() { - if (totpCode.length !== 6) { - toast.error(t("auth.enterCode")); - return; - } - - setTotpLoading(true); - - try { - const res = await verifyTOTPLogin(totpTempToken, totpCode, rememberMe); - - if (!res || !res.success) { - throw new Error(t("errors.loginFailed")); - } - - if (isInMobileWebView()) { - // Native-app requests get the JWT in the verify response body. - postMobileAuthSuccess(res.token || ""); - setTotpLoading(false); - return; - } - - if (isInElectronWebView()) { - try { - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "totp_auth_component", - platform: "desktop", - token: res.token || null, - timestamp: Date.now(), - }, - "*", - ); - setWebviewAuthSuccess(true); - setTotpLoading(false); - return; - } catch (e) { - console.error("Error posting auth success message:", e); - } - } - - setLoggedIn(true); - setIsAdmin(!!res.is_admin); - setUsername(res.username || null); - setUserId(res.userId || null); - setDbError(null); - - onAuthSuccess({ - isAdmin: !!res.is_admin, - username: res.username || null, - userId: res.userId || null, - }); - - setInternalLoggedIn(true); - setTotpRequired(false); - setTotpCode(""); - setTotpTempToken(""); - toast.success(t("messages.loginSuccess")); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { - data?: { - code?: string; - error?: string; - remainingTime?: number; - remainingAttempts?: number; - }; - }; - }; - const errorCode = error?.response?.data?.code; - const remainingTime = error?.response?.data?.remainingTime; - const remainingAttempts = error?.response?.data?.remainingAttempts; - - let errorMessage = - error?.response?.data?.error || - error?.message || - t("errors.invalidTotpCode"); - - if (errorCode === "SESSION_EXPIRED") { - setTotpRequired(false); - setTotpCode(""); - setTotpTempToken(""); - setTab("login"); - toast.error(t("errors.sessionExpired")); - } else if (errorCode === "TOTP_RATE_LIMITED") { - if (remainingTime) { - errorMessage = t("errors.totpRateLimitedWithTime", { - time: remainingTime, - }); - } else { - errorMessage = t("errors.totpRateLimited"); - } - toast.error(errorMessage); - } else { - if ( - remainingAttempts !== undefined && - remainingAttempts <= 2 && - remainingAttempts > 0 - ) { - errorMessage = `${errorMessage} (${remainingAttempts} ${t("auth.attemptsRemaining")})`; - } - toast.error(errorMessage); - } - } finally { - setTotpLoading(false); - } - } - - async function handlePasskeyLogin() { - setPasskeyLoading(true); - try { - const res = await authenticateWithWebAuthn( - localUsername, - rememberMe, - "preferred", - ); - - if (res.requires_totp) { - setTotpRequired(true); - setTotpTempToken(res.temp_token || ""); - return; - } - - if (!res || !res.success) { - throw new Error(t("errors.loginFailed")); - } - - if (isInMobileWebView()) { - postMobileAuthSuccess(res.token || ""); - return; - } - - if (isInElectronWebView()) { - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "passkey_auth_component", - platform: "desktop", - token: res.token || null, - timestamp: Date.now(), - }, - "*", - ); - setWebviewAuthSuccess(true); - return; - } - - const meRes = await getUserInfo(); - setInternalLoggedIn(true); - setLoggedIn(true); - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - setDbError(null); - onAuthSuccess({ - isAdmin: !!meRes.is_admin, - username: meRes.username || null, - userId: meRes.userId || null, - }); - toast.success(t("messages.loginSuccess")); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { data?: { error?: string } }; - }; - toast.error( - error?.response?.data?.error || - error?.message || - t("auth.passkeyLoginFailed"), - ); - } finally { - setPasskeyLoading(false); - } - } - - 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")); - } - - 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 meRes = await getUserInfo(); - setInternalLoggedIn(true); - setLoggedIn(true); - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - setDbError(null); - onAuthSuccess({ - isAdmin: !!meRes.is_admin, - username: meRes.username || null, - userId: meRes.userId || null, - }); - toast.success(t("messages.loginSuccess")); - } 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, - onAuthSuccess, - setLoggedIn, - setIsAdmin, - setUsername, - setUserId, - setDbError, - t, - ], - ); - - useEffect(() => { - if (!ssoProvidersLoaded || silentSigninHandledRef.current) return; - if (!oidcSilentLoginDefaultLoaded) return; - - const urlTriggered = shouldTriggerSilentSignin(window.location.search); - if (!urlTriggered && !oidcSilentLoginDefault) return; - - if (urlTriggered) { - const nextSearch = removeSilentSigninFromSearch(window.location.search); - window.history.replaceState( - {}, - document.title, - `${window.location.pathname}${nextSearch}${window.location.hash}`, - ); - } - - silentSigninHandledRef.current = true; - 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; - } - - if (urlTriggered) { - toast.info(t("errors.silentSigninOidcUnavailable")); - } - }, [ - handleOIDCLogin, - ssoProvidersLoaded, - ssoProviders, - t, - oidcSilentLoginDefault, - oidcSilentLoginDefaultLoaded, - ]); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const success = urlParams.get("success"); - const error = urlParams.get("error"); - - if (error) { - if (error === "registration_disabled") { - toast.error(t("messages.registrationDisabled")); - } else if (error === "user_not_allowed") { - toast.error(t("messages.userNotAllowed")); - } else { - toast.error(`${t("errors.oidcAuthFailed")}: ${error}`); - } - setOidcLoading(false); - window.history.replaceState({}, document.title, window.location.pathname); - return; - } - - if (success) { - setOidcLoading(true); - - if (isInMobileWebView()) { - // The OIDC callback authenticated via an HttpOnly cookie on this origin, - // so prefer a token in the URL (termix-mobile:-origin callbacks include - // one), otherwise read it back from the cookie via /users/me/token. - const finish = (token: string) => { - postMobileAuthSuccess(token); - setOidcLoading(false); - window.history.replaceState( - {}, - document.title, - window.location.pathname, - ); - }; - const urlToken = urlParams.get("token"); - if (urlToken) { - finish(urlToken); - } else { - getCurrentToken() - .then((token) => finish(token ?? "")) - .catch(() => finish("")); - } - return; - } - - if (isInElectronWebView()) { - try { - const urlToken = urlParams.get("token"); - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "oidc_callback", - platform: "desktop", - token: urlToken || null, - timestamp: Date.now(), - }, - "*", - ); - setWebviewAuthSuccess(true); - setOidcLoading(false); - window.history.replaceState( - {}, - document.title, - window.location.pathname, - ); - return; - } catch (e) { - console.error("Error posting auth success message:", e); - } - } - - getUserInfo() - .then((meRes) => { - setInternalLoggedIn(true); - setLoggedIn(true); - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - setDbError(null); - onAuthSuccess({ - isAdmin: !!meRes.is_admin, - username: meRes.username || null, - userId: meRes.userId || null, - }); - setInternalLoggedIn(true); - window.history.replaceState( - {}, - document.title, - window.location.pathname, - ); - }) - .catch((err) => { - console.error("Failed to get user info after OIDC callback:", err); - toast.error(t("errors.failedUserInfo")); - setInternalLoggedIn(false); - setLoggedIn(false); - setIsAdmin(false); - setUsername(null); - setUserId(null); - window.history.replaceState( - {}, - document.title, - window.location.pathname, - ); - }) - .finally(() => { - setOidcLoading(false); - }); - } - }, [ - onAuthSuccess, - setDbError, - setIsAdmin, - setLoggedIn, - setUserId, - setUsername, - t, - isInElectronWebView, - ]); - - const Spinner = ( - - - - - ); - - useEffect(() => { - if (dbConnectionFailed) { - toast.error(t("errors.databaseConnection")); - } - }, [dbConnectionFailed, t]); - - useEffect(() => { - const checkServerConfig = async () => { - if (isInElectronWebView()) { - setShowServerConfig(false); - return; - } - - if (isElectron()) { - try { - const [config, status] = await Promise.all([ - getServerConfig(), - getEmbeddedServerStatus(), - ]); - - if ( - status?.embedded && - status?.running && - config && - !config.serverUrl - ) { - setCurrentServerUrl(""); - setShowServerConfig(false); - return; - } - - setCurrentServerUrl(config?.serverUrl || ""); - setShowServerConfig(!config || !config.serverUrl); - } catch { - setShowServerConfig(true); - } - } else { - setShowServerConfig(false); - } - }; - - checkServerConfig(); - }, []); - - if (showServerConfig === null && !isInElectronWebView()) { - return ( -
-
-
-
-
- ); - } - - if (showServerConfig && !isInElectronWebView()) { - return ( -
- { - window.location.reload(); - }} - onUseEmbedded={async () => { - await saveServerConfig({ - serverUrl: "", - lastUpdated: new Date().toISOString(), - }); - setShowServerConfig(false); - setCurrentServerUrl(""); - }} - onCancel={() => { - setShowServerConfig(false); - }} - isFirstTime={!currentServerUrl} - /> -
- ); - } - - if ( - isElectron() && - currentServerUrl && - authLoading && - !isInElectronWebView() - ) { - return ( -
-
-
-
-
-

- {t("common.checkingAuthentication")} -

-
-
-
-
- ); - } - - if (isElectron() && currentServerUrl && !loggedIn && !isInElectronWebView()) { - return ( -
-
- { - setShowServerConfig(true); - }} - /> -
-
- ); - } - - if (dbHealthChecking && !dbConnectionFailed) { - return ( -
-
-
-
-
-

- {t("common.checkingDatabase")} -

-
-
-
-
- ); - } - - if (dbConnectionFailed) { - return ( -
-
-
-

- {t("errors.databaseConnection")} -

-

- {t("messages.databaseConnectionFailed")} -

-
- -
- -
- -
-
- - -
- {isElectron() && currentServerUrl && ( -
-
- -
- {currentServerUrl} -
-
- -
- )} -
-
-
- ); - } - - return ( -
-
-
-
-
- {t("common.appName").toUpperCase()} -
-
- {t("auth.tagline")} -
-
-
- -
-
- {isInElectronWebView() && !webviewAuthSuccess && ( - - - {t("auth.desktopApp")} - - {t("auth.loggingInToDesktopApp")} - - - )} - {(isInElectronWebView() || isInMobileWebView()) && - webviewAuthSuccess && ( -
-
-

- {t("messages.loginSuccess")} -

-

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

-
-
- )} - {!webviewAuthSuccess && totpRequired && ( -
{ - e.preventDefault(); - handleTOTPVerification(); - }} - > -
-

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

-

{t("auth.enterCode")}

-
- -
- - - setTotpCode(e.target.value.replace(/\D/g, "")) - } - disabled={totpLoading} - className="text-center text-2xl tracking-widest font-mono" - autoComplete="one-time-code" - /> -

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

-
- - - - -
- )} - - {!webviewAuthSuccess && - !loggedIn && - !authLoading && - !totpRequired && ( - <> - {(() => { - const hasLogin = passwordLoginAllowed && !firstUser; - const hasSignup = - (passwordLoginAllowed || firstUser) && - registrationAllowed; - const hasPasskey = !firstUser; - const hasSso = ssoProviders.length > 0; - const hasAnyAuth = - hasLogin || hasSignup || hasPasskey || hasSso; - - if (!hasAnyAuth) { - return ( -
-

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

-

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

-
- ); - } - - return ( - <> - { - const newTab = v as "login" | "signup" | "reset"; - setTab(newTab); - if (tab === "reset") resetPasswordState(); - if ( - (tab === "login" && newTab === "signup") || - (tab === "signup" && newTab === "login") - ) { - clearFormFields(); - } - }} - className="w-full mb-8" - > - - {passwordLoginAllowed && ( - - {t("common.login")} - - )} - {(passwordLoginAllowed || firstUser) && - registrationAllowed && ( - - {t("common.register")} - - )} - - - -
-

- {tab === "login" - ? t("auth.loginTitle") - : tab === "signup" - ? t("auth.registerTitle") - : t("auth.forgotPassword")} -

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

{t("auth.resetCodeDesc")}

-
-
-
- - - setLocalUsername(e.target.value) - } - disabled={resetLoading} - /> -
- -
- - )} - - {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" - /> -
- - -
- - )} -
- ) : ( -
- {!passwordLoginAllowed && - !firstUser && - tab === "login" ? ( -
-

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

-
- - - setLocalUsername(e.target.value) - } - disabled={passkeyLoading || loggedIn} - autoComplete="username webauthn" - /> -
- -
- ) : ( - <> -
- - - setLocalUsername(e.target.value) - } - disabled={loading || loggedIn} - autoComplete="username webauthn" - /> -
-
- - - setPassword(e.target.value) - } - disabled={loading || loggedIn} - /> -
- {tab === "login" && ( -
- - setRememberMe(checked === true) - } - disabled={loading || loggedIn} - /> - -
- )} - {tab === "signup" && ( -
- - - setSignupConfirmPassword(e.target.value) - } - disabled={loading || loggedIn} - /> -
- )} - - {tab === "login" && ( - - )} - {tab === "login" && ( - - )} - - )} - - {ssoProviders.length > 0 && !isElectron() && ( -
- {(passwordLoginAllowed || - firstUser || - tab === "signup") && ( -
-
- - {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 ( - - ); - })} -
- )} - - )} - -
-
- - -
- {isElectron() && currentServerUrl && ( -
-
- -
- {currentServerUrl} -
-
- -
- )} -
- - ); - })()} - - )} -
-
-
-
- ); -} diff --git a/src/ui/auth/LoginScreen.tsx b/src/ui/auth/LoginScreen.tsx deleted file mode 100644 index 9fcf715c..00000000 --- a/src/ui/auth/LoginScreen.tsx +++ /dev/null @@ -1,37 +0,0 @@ -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/MigrationNoticeDialog.tsx b/src/ui/components/MigrationNoticeDialog.tsx new file mode 100644 index 00000000..19278554 --- /dev/null +++ b/src/ui/components/MigrationNoticeDialog.tsx @@ -0,0 +1,107 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Server } from "lucide-react"; +import { isElectron } from "@/lib/electron"; +import { Button } from "@/components/button.tsx"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/dialog.tsx"; + +type DesktopSettings = { + defaultConnectionOrigin: "local" | "remote"; + migrationNoticeAcknowledged?: boolean; +}; + +// One-time notice for Electron installs that previously pointed the whole +// app at a remote Termix server (the pre-2.6.0 architecture). That install +// now runs a fully local, standalone backend by default -- the hosts, +// credentials, and snippets that lived on the old remote server won't show +// up here until the user explicitly turns on Remote Sync and reconnects to +// that same server. A fresh install never had a legacy serverUrl, so this +// never fires for anyone who didn't go through the old flow, and it only +// ever shows once per install (tracked in desktop-settings.json). +export function MigrationNoticeDialog({ + onOpenRemoteSync, +}: { + onOpenRemoteSync: (serverUrl: string) => void; +}) { + const { t } = useTranslation(); + const [legacyServerUrl, setLegacyServerUrl] = useState(null); + const [open, setOpen] = useState(false); + + useEffect(() => { + if (!isElectron()) return; + let cancelled = false; + + Promise.all([ + window.electronAPI?.invoke?.("get-legacy-server-config") as Promise<{ + serverUrl: string | null; + } | null>, + window.electronAPI?.invoke?.("get-desktop-settings") as Promise< + DesktopSettings | undefined + >, + ]) + .then(([legacyConfig, settings]) => { + if (cancelled) return; + const serverUrl = legacyConfig?.serverUrl || null; + if (serverUrl && !settings?.migrationNoticeAcknowledged) { + setLegacyServerUrl(serverUrl); + setOpen(true); + } + }) + .catch(() => {}); + + return () => { + cancelled = true; + }; + }, []); + + const acknowledge = async () => { + setOpen(false); + const settings = ((await window.electronAPI?.invoke?.( + "get-desktop-settings", + )) as DesktopSettings | undefined) ?? { defaultConnectionOrigin: "local" }; + await window.electronAPI?.invoke?.("save-desktop-settings", { + ...settings, + migrationNoticeAcknowledged: true, + }); + }; + + const handleSetUpSync = async () => { + const url = legacyServerUrl || ""; + await acknowledge(); + onOpenRemoteSync(url); + }; + + if (!legacyServerUrl) return null; + + return ( + !next && acknowledge()}> + + +
+ + {t("migrationNotice.title")} +
+ +

{t("migrationNotice.body1")}

+

{t("migrationNotice.body2", { url: legacyServerUrl })}

+
+
+ + + + +
+
+ ); +} diff --git a/src/ui/components/RemoteSyncBanner.tsx b/src/ui/components/RemoteSyncBanner.tsx new file mode 100644 index 00000000..f554ac47 --- /dev/null +++ b/src/ui/components/RemoteSyncBanner.tsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { AlertTriangle } from "lucide-react"; +import { isElectron } from "@/lib/electron"; + +interface RemoteSyncStatus { + connected: boolean; + syncing: boolean; + lastSyncedAt: string | null; + lastError: string | null; + needsReauth: boolean; +} + +// Non-blocking banner shown when a connected remote sync server needs +// re-authentication. Never gates or hides any other UI -- the local app +// keeps working fully regardless of remote sync state. +export function RemoteSyncBanner({ onReconnect }: { onReconnect: () => void }) { + const { t } = useTranslation(); + const [status, setStatus] = useState(null); + + useEffect(() => { + if (!isElectron()) return; + window.electronAPI + ?.invoke?.("get-remote-sync-status") + .then((s) => setStatus((s as RemoteSyncStatus) ?? null)) + .catch(() => {}); + const unsubscribe = window.electronAPI?.onRemoteSyncStatusChanged?.( + (nextStatus: RemoteSyncStatus) => setStatus(nextStatus), + ); + return () => unsubscribe?.(); + }, []); + + if (!status?.connected || !status.needsReauth) return null; + + return ( +
+
+ + {t("remoteSync.bannerMessage")} +
+ +
+ ); +} diff --git a/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx b/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx index 7259504a..d823b339 100644 --- a/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx +++ b/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx @@ -121,11 +121,14 @@ export function ProxmoxDiscoverDialog({ const credId = defaultCredentialId ?? discoveredCredentialId; const importAuth = resolveProxmoxImportAuth(defaultAuthType, credId); - const toImport = guests - .filter((g) => selected.has(g.vmid)) + const selectedGuests = guests.filter((g) => selected.has(g.vmid)); + const skippedNoIp = selectedGuests.filter((g) => !g.ip).length; + + const toImport = selectedGuests + .filter((g) => !!g.ip) .map((g) => ({ name: g.name, - ip: g.ip ?? "0.0.0.0", + ip: g.ip as string, port: g.connectionType === "rdp" ? 3389 : 22, username: defaultUsername ?? "root", folder: importFolder, @@ -152,10 +155,15 @@ export function ProxmoxDiscoverDialog({ }, })); - const result = await bulkImportSSHHosts(toImport, false); - const updated = await getSSHHosts(); - onHostsChanged(updated); - window.dispatchEvent(new CustomEvent("termix:hosts-changed")); + const result = toImport.length + ? await bulkImportSSHHosts(toImport, false) + : { success: 0, updated: 0, skipped: 0, failed: 0 }; + + if (toImport.length) { + const updated = await getSSHHosts(); + onHostsChanged(updated); + window.dispatchEvent(new CustomEvent("termix:hosts-changed")); + } const msg = [ result.success @@ -167,6 +175,9 @@ export function ProxmoxDiscoverDialog({ result.failed ? t("hosts.proxmoxResultFailed", { count: result.failed }) : null, + skippedNoIp + ? t("hosts.proxmoxResultSkippedNoIp", { count: skippedNoIp }) + : null, ] .filter(Boolean) .join(", "); diff --git a/src/ui/dashboard/DashboardTab.tsx b/src/ui/dashboard/DashboardTab.tsx index 50154591..fddd09bc 100644 --- a/src/ui/dashboard/DashboardTab.tsx +++ b/src/ui/dashboard/DashboardTab.tsx @@ -43,6 +43,7 @@ import { getServiceLinks, createServiceLink, deleteServiceLink, + isElectron, } from "@/main-axios"; import type { RecentActivityItem, ServiceLink } from "@/main-axios"; import { useTranslation } from "react-i18next"; @@ -1365,7 +1366,13 @@ export function DashboardTab({ load(); getUserInfo() - .then((info) => setIsAdmin(!!info.is_admin)) + .then((info) => { + // Remote sync is not yet configurable (added in a later phase), so + // a standalone desktop install never shows admin/user-management + // UI -- it has exactly one implicit user and nothing to administer. + const isRemoteSyncConnected = false; + setIsAdmin(!!info.is_admin && (!isElectron() || isRemoteSyncConnected)); + }) .catch(() => {}); getUptime() .then((u) => setUptimeFormatted(u.formatted)) diff --git a/src/ui/features/FullScreenAppWrapper.tsx b/src/ui/features/FullScreenAppWrapper.tsx index 8d658204..70b0f6e7 100644 --- a/src/ui/features/FullScreenAppWrapper.tsx +++ b/src/ui/features/FullScreenAppWrapper.tsx @@ -2,9 +2,14 @@ import React, { useEffect, useState } from "react"; import { TabProvider } from "@/shell/TabContext.tsx"; import { CommandHistoryProvider } from "@/features/terminal/command-history/CommandHistoryContext.tsx"; import { SidebarProvider } from "@/components/sidebar.tsx"; -import { getSSHHosts, getUserInfo } from "@/main-axios.ts"; +import { + getSSHHosts, + getUserInfo, + isElectron, + requestDesktopAutoSession, +} from "@/main-axios.ts"; import type { SSHHost } from "@/types"; -import { LoginScreen } from "@/auth/LoginScreen.tsx"; +import { Auth } from "@/auth/Auth.tsx"; import { Toaster } from "@/components/sonner.tsx"; import { dbHealthMonitor } from "@/lib/db-health-monitor.ts"; import { useTranslation } from "react-i18next"; @@ -38,20 +43,70 @@ export const FullScreenAppWrapper: React.FC = ({ }, []); useEffect(() => { - const checkAuth = async () => { + // Popped-out windows (terminal/tunnel/file-manager/etc.) share the main + // window's session rather than owning their own login -- there's no + // login form to fall back to here. On a cold Electron launch this + // window's renderer can start before the embedded backend has finished + // booting, so a bare failure isn't proof of "logged out"; in Electron + // it's retried forever (the embedded backend is bundled, always-on + // infrastructure that always eventually comes up) with a fallback to + // the same auto-session exchange the main window uses, since a login + // form must never appear for the local backend. Outside Electron a + // capped retry still applies, matching the web app's normal behavior. + let cancelled = false; + let retryTimer: ReturnType | null = null; + const maxAttempts = 10; + + const checkAuth = async (attempt: number) => { try { const userInfo = await getUserInfo(); + if (cancelled) return; if (userInfo) { setIsAuthenticated(true); + setAuthLoading(false); + return; } } catch { - setIsAuthenticated(false); - } finally { - setAuthLoading(false); + // fall through to retry/auto-session below } + if (cancelled) return; + + if (isElectron()) { + const outcome = await requestDesktopAutoSession(); + if (cancelled) return; + if (outcome.kind === "success") { + setIsAuthenticated(true); + setAuthLoading(false); + return; + } + if (outcome.kind === "declined") { + setIsAuthenticated(false); + setAuthLoading(false); + return; + } + // "retry": keep retrying indefinitely with capped backoff. + const delay = Math.min(1000 * 2 ** attempt, 10000); + retryTimer = setTimeout(() => { + if (!cancelled) checkAuth(attempt + 1); + }, delay); + return; + } + + if (attempt >= maxAttempts) { + setIsAuthenticated(false); + setAuthLoading(false); + return; + } + retryTimer = setTimeout(() => { + if (!cancelled) checkAuth(attempt + 1); + }, 1000); }; - checkAuth(); + checkAuth(0); + return () => { + cancelled = true; + if (retryTimer) clearTimeout(retryTimer); + }; }, []); useEffect(() => { @@ -84,6 +139,11 @@ export const FullScreenAppWrapper: React.FC = ({ window.location.reload(); }; + // Electron never reaches an unauthenticated render: checkAuth above + // retries and falls back to the same auto-session exchange the main + // window uses, so this branch is web-only there -- a real "not logged + // in" case for a bookmarked/shared full-screen link. + if (authLoading) { return (
= ({ className="w-full h-screen overflow-hidden flex items-center justify-center" style={{ backgroundColor: "var(--bg-base)" }} > - + f.value === terminalConfig.fontFamily, ); const fontFamily = fontConfig?.fallback ?? TERMINAL_FONTS[0].fallback; + ensureTerminalFontsLoaded(fontConfig?.value ?? TERMINAL_FONTS[0].value); terminal.options.cursorBlink = terminalConfig.cursorBlink; terminal.options.cursorStyle = terminalConfig.cursorStyle; @@ -263,7 +269,7 @@ export function ConsoleTerminal({ } }, [terminal]); - const connect = React.useCallback(() => { + const connect = React.useCallback(async () => { if (!terminal || containerState !== "running") { toast.error(t("docker.containerMustBeRunning")); return; @@ -285,20 +291,30 @@ export function ConsoleTerminal({ window.location.port === "5173" || window.location.port === ""); - const baseWsUrl = isDev - ? `${window.location.protocol === "https:" ? "wss" : "ws"}://localhost:30009` - : isElectronApp - ? (() => { - const baseUrl = - (window as { configuredServerUrl?: string }) - .configuredServerUrl || "http://127.0.0.1:30001"; - const wsProtocol = baseUrl.startsWith("https://") - ? "wss://" - : "ws://"; - const wsHost = baseUrl.replace(/^https?:\/\//, ""); - return `${wsProtocol}${wsHost}/docker/console/`; - })() - : `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}${getBasePath()}/docker/console/`; + let baseWsUrl: string; + if (isDev) { + baseWsUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://localhost:30009`; + } else if (isElectronApp) { + const origin = await resolveConnectionOrigin({ + connectionType: "ssh", + connectionOrigin: hostConfig.connectionOrigin, + }); + const resolvedUrl = await buildOriginWsUrl({ + origin, + localPort: 30009, + localPath: "/docker/console/", + remotePath: "/docker/console/", + includeLocalJwt: false, + }); + if (!resolvedUrl) { + setIsConnecting(false); + toast.error(t("errors.remoteServerRequired")); + return; + } + baseWsUrl = resolvedUrl; + } else { + baseWsUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}${getBasePath()}/docker/console/`; + } const ws = new WebSocket(baseWsUrl); diff --git a/src/ui/features/guacamole/GuacamoleApp.tsx b/src/ui/features/guacamole/GuacamoleApp.tsx index 5e98717e..a978557d 100644 --- a/src/ui/features/guacamole/GuacamoleApp.tsx +++ b/src/ui/features/guacamole/GuacamoleApp.tsx @@ -8,33 +8,49 @@ import React, { import { GuacamoleDisplay, type GuacamoleDisplayHandle, + type GuacamoleTouchMode, } from "@/features/guacamole/GuacamoleDisplay.tsx"; import { getGuacamoleTokenFromHost, getGuacdStatus, getSSHHosts, logActivity, + isElectron, } from "@/main-axios.ts"; +import { resolveConnectionOrigin } from "@/lib/connection-origin.ts"; import { useTranslation } from "react-i18next"; import { AlertCircle, RefreshCw } from "lucide-react"; import { GuacamoleToolbar } from "@/features/guacamole/GuacamoleToolbar.tsx"; import { Button } from "@/components/button.tsx"; +import { Input } from "@/components/input.tsx"; +import { PasswordInput } from "@/components/password-input.tsx"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/dialog.tsx"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; +import { ShareSessionModal } from "@/features/session-sharing/ShareSessionModal.tsx"; import type { SSHHost } from "@/types"; interface GuacamoleAppProps { hostId?: string; tabId?: string; protocol?: "rdp" | "vnc" | "telnet"; + isVisible?: boolean; } export interface GuacamoleAppHandle { disconnect: () => void; isConnected: () => boolean; + openShareModal: () => void; + canShare: () => boolean; } const GuacamoleApp = React.forwardRef( - function GuacamoleApp({ hostId, tabId, protocol }, ref) { + function GuacamoleApp({ hostId, tabId, protocol, isVisible = true }, ref) { const { t } = useTranslation(); const [hostConfig, setHostConfig] = useState(null); const [loading, setLoading] = useState(true); @@ -88,6 +104,7 @@ const GuacamoleApp = React.forwardRef( hostName={hostConfig.name || hostConfig.ip || String(hostId)} tabId={tabId} protocol={protocol} + isVisible={isVisible} ref={ref} /> ); @@ -96,60 +113,134 @@ const GuacamoleApp = React.forwardRef( interface GuacamoleAppInnerProps { hostId: number; - hostConfig: Pick; + hostConfig: Pick< + SSHHost, + "connectionType" | "guacamoleConfig" | "rdpAuthType" + >; hostName: string; tabId?: string; protocol?: "rdp" | "vnc" | "telnet"; + isVisible: boolean; } const GuacamoleAppInner = React.forwardRef< GuacamoleAppHandle, GuacamoleAppInnerProps >(function GuacamoleAppInner( - { hostId, hostConfig, hostName, tabId, protocol }, + { hostId, hostConfig, hostName, tabId, protocol, isVisible }, ref, ) { const { t } = useTranslation(); const [token, setToken] = useState(null); + const [guacamoleConnectionId, setGuacamoleConnectionId] = useState< + string | null + >(null); + const [shareModalOpen, setShareModalOpen] = useState(false); const [error, setError] = useState(null); const [connectionError, setConnectionError] = useState(null); const [retryCount, setRetryCount] = useState(0); + const [touchMode, setTouchMode] = useState(() => + typeof window !== "undefined" && + (navigator.maxTouchPoints > 0 || "ontouchstart" in window) + ? "touchscreen" + : null, + ); const displayRef = useRef(null); + const resolvedProtocolForConnect = (protocol ?? + hostConfig.connectionType ?? + "rdp") as "rdp" | "vnc" | "telnet"; + const needsCredentialPrompt = + resolvedProtocolForConnect === "rdp" && hostConfig.rdpAuthType === "none"; + + const [promptedCredentials, setPromptedCredentials] = useState<{ + username: string; + password: string; + } | null>(null); + const [promptOpen, setPromptOpen] = useState(needsCredentialPrompt); + const [promptUsername, setPromptUsername] = useState(""); + const [promptPassword, setPromptPassword] = useState(""); + useImperativeHandle(ref, () => ({ disconnect: () => displayRef.current?.disconnect(), isConnected: () => displayRef.current?.isConnected() === true, + openShareModal: () => setShareModalOpen(true), + canShare: () => guacamoleConnectionId !== null, })); useEffect(() => { + if (needsCredentialPrompt && !promptedCredentials) { + setPromptOpen(true); + return; + } + setToken(null); + setGuacamoleConnectionId(null); setError(null); - getGuacdStatus() - .then((status) => { + + (async () => { + if (isElectron()) { + const origin = await resolveConnectionOrigin({ + connectionType: resolvedProtocolForConnect, + }); + if (origin === "remote") { + const remoteConfig = (await window.electronAPI?.invoke?.( + "get-remote-sync-config", + )) as { serverUrl?: string } | null; + if (!remoteConfig?.serverUrl) { + setError(t("errors.remoteServerRequired")); + return; + } + } + } + + try { + const status = await getGuacdStatus(); if (status.guacd.status !== "connected") { setError(t("guacamole.guacdUnavailable")); return; } - return getGuacamoleTokenFromHost(hostId, protocol); - }) - .then((result) => { + const result = await getGuacamoleTokenFromHost( + hostId, + protocol, + promptedCredentials ?? undefined, + ); if (result) { setToken(result.token); - const resolvedProtocol = (protocol ?? - hostConfig.connectionType ?? - "rdp") as "rdp" | "vnc" | "telnet"; - logActivity(resolvedProtocol, hostId, hostName).catch(() => {}); + setGuacamoleConnectionId(result.guacamoleConnectionId ?? null); + logActivity(resolvedProtocolForConnect, hostId, hostName).catch( + () => {}, + ); } - }) - .catch((err) => setError(err?.message || t("guacamole.failedToConnect"))); - }, [hostConfig.connectionType, hostId, hostName, protocol, retryCount, t]); + } catch (err: unknown) { + const message = + err instanceof Error ? err.message : t("guacamole.failedToConnect"); + setError(message || t("guacamole.failedToConnect")); + } + })(); + }, [ + hostId, + hostName, + protocol, + retryCount, + t, + needsCredentialPrompt, + promptedCredentials, + resolvedProtocolForConnect, + ]); const handleReconnect = useCallback(() => { setConnectionError(null); setError(null); setToken(null); + if (needsCredentialPrompt) { + setPromptedCredentials(null); + setPromptUsername(""); + setPromptPassword(""); + setPromptOpen(true); + } setRetryCount((c) => c + 1); - }, []); + }, [needsCredentialPrompt]); useEffect(() => { if (!tabId) return; @@ -162,6 +253,67 @@ const GuacamoleAppInner = React.forwardRef< window.removeEventListener("termix:refresh-guacamole", handler); }, [tabId, handleReconnect]); + if (promptOpen) { + return ( + { + if (!open) setPromptOpen(false); + }} + > + + + + {t("guacamole.credentialPromptTitle")} + + + {t("guacamole.credentialPromptDescription")} + + +
{ + e.preventDefault(); + setPromptedCredentials({ + username: promptUsername, + password: promptPassword, + }); + setPromptOpen(false); + }} + > +
+ + setPromptUsername(e.target.value)} + /> +
+
+ + setPromptPassword(e.target.value)} + /> +
+
+ +
+
+
+
+ ); + } + if (error) { return (
)} setConnectionError(err)} /> - + + {shareModalOpen && guacamoleConnectionId && ( + setShareModalOpen(false)} + hostId={hostId} + sessionId={guacamoleConnectionId} + protocol={resolvedProtocol} + tabInstanceId={tabId} + /> + )}
); }); diff --git a/src/ui/features/guacamole/GuacamoleDisplay.tsx b/src/ui/features/guacamole/GuacamoleDisplay.tsx index 5a7876e7..671fa6f7 100644 --- a/src/ui/features/guacamole/GuacamoleDisplay.tsx +++ b/src/ui/features/guacamole/GuacamoleDisplay.tsx @@ -8,10 +8,14 @@ import { } from "react"; import Guacamole from "guacamole-common-js"; import { useTranslation } from "react-i18next"; -import { getGuacamoleToken, isElectron, isEmbeddedMode } from "@/main-axios.ts"; +import { getGuacamoleToken, isElectron } from "@/main-axios.ts"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; import { getBasePath } from "@/lib/base-path.ts"; import { buildGuacamoleWebSocketBaseUrl } from "./guacamole-websocket-url.ts"; +import { + resolveConnectionOrigin, + buildOriginWsUrl, +} from "@/lib/connection-origin.ts"; import { isFirefoxBrowser, isPasteShortcut, @@ -44,9 +48,12 @@ export interface GuacamoleDisplayHandle { setClipboard: (data: string) => void; } +export type GuacamoleTouchMode = "touchscreen" | "touchpad"; + interface GuacamoleDisplayProps { connectionConfig: GuacamoleConnectionConfig; isVisible: boolean; + touchMode?: GuacamoleTouchMode | null; onConnect?: () => void; onDisconnect?: () => void; onError?: (error: string) => void; @@ -58,7 +65,7 @@ export const GuacamoleDisplay = forwardRef< GuacamoleDisplayHandle, GuacamoleDisplayProps >(function GuacamoleDisplay( - { connectionConfig, isVisible, onConnect, onDisconnect, onError }, + { connectionConfig, isVisible, touchMode, onConnect, onDisconnect, onError }, ref, ) { const { t } = useTranslation(); @@ -123,11 +130,11 @@ export const GuacamoleDisplay = forwardRef< }, })); - const getWebSocketUrl = useCallback( + const getWebSocketConnection = useCallback( async ( containerWidth: number, containerHeight: number, - ): Promise => { + ): Promise<{ url: string; query: string } | null> => { try { let token: string; const connectionProtocol = @@ -166,15 +173,31 @@ export const GuacamoleDisplay = forwardRef< connectionConfig.dpi, ); - const wsBase = buildGuacamoleWebSocketBaseUrl({ - isDev, - isElectronApp: isElectron(), - isEmbeddedApp: isEmbeddedMode(), - configuredServerUrl: (window as { configuredServerUrl?: string }) - .configuredServerUrl, - basePath: getBasePath(), - location: window.location, - }); + let wsBase: string | null; + if (isElectron()) { + const origin = await resolveConnectionOrigin({ + connectionType: connectionProtocol, + }); + wsBase = await buildOriginWsUrl({ + origin, + localPort: 30008, + localPath: "/guacamole/websocket/", + remotePath: "/guacamole/websocket/", + includeLocalJwt: false, + }); + if (!wsBase) { + onError?.(t("errors.remoteServerRequired")); + return null; + } + } else { + wsBase = buildGuacamoleWebSocketBaseUrl({ + isDev, + isElectronApp: false, + isEmbeddedApp: false, + basePath: getBasePath(), + location: window.location, + }); + } const params = new URLSearchParams({ token, @@ -182,7 +205,7 @@ export const GuacamoleDisplay = forwardRef< height: String(displaySize.height), }); if (displaySize.dpi) params.set("dpi", String(displaySize.dpi)); - return `${wsBase}?${params.toString()}`; + return { url: wsBase, query: params.toString() }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; @@ -190,7 +213,7 @@ export const GuacamoleDisplay = forwardRef< return null; } }, - [connectionConfig, onError], + [connectionConfig, onError, t], ); const refreshKeyboardHandlers = useCallback(() => { @@ -285,10 +308,9 @@ export const GuacamoleDisplay = forwardRef< setIsReady(false); setHasError(false); - // Wait two frames so the container is fully laid out before measuring. - await new Promise((resolve) => - requestAnimationFrame(() => requestAnimationFrame(() => resolve())), - ); + // Let layout settle before measuring without depending on animation frames, + // which may be throttled while Electron windows or tabs are inactive. + await new Promise((resolve) => setTimeout(resolve, 0)); if (!isMountedRef.current) { isConnectingRef.current = false; return; @@ -310,9 +332,7 @@ export const GuacamoleDisplay = forwardRef< (containerWidth < 100 || containerHeight < 100) && attempt < 40; attempt++ ) { - await new Promise((resolve) => - requestAnimationFrame(() => resolve()), - ); + await new Promise((resolve) => setTimeout(resolve, 25)); if (!isMountedRef.current) { isConnectingRef.current = false; return; @@ -325,19 +345,28 @@ export const GuacamoleDisplay = forwardRef< containerHeight = window.innerHeight || 720; } - const wsUrl = await getWebSocketUrl(containerWidth, containerHeight); + const wsConnection = await getWebSocketConnection( + containerWidth, + containerHeight, + ); if (!isMountedRef.current) { isConnectingRef.current = false; return; } - if (!wsUrl) { + if (!wsConnection) { isConnectingRef.current = false; return; } - const tunnel = new Guacamole.WebSocketTunnel(wsUrl); + const tunnel = new Guacamole.WebSocketTunnel(wsConnection.url); const client = new Guacamole.Client(tunnel); clientRef.current = client; + let connectWatchdog: ReturnType | null = null; + const clearConnectWatchdog = () => { + if (!connectWatchdog) return; + clearTimeout(connectWatchdog); + connectWatchdog = null; + }; const display = client.getDisplay(); const displayElement = display.getElement(); @@ -378,7 +407,7 @@ export const GuacamoleDisplay = forwardRef< } display.onresize = () => { - if (!isMountedRef.current) return; + if (!isMountedRef.current || clientRef.current !== client) return; rescaleDisplay(true); setIsReady(true); }; @@ -388,26 +417,46 @@ export const GuacamoleDisplay = forwardRef< setIsReady(true); } - const mouse = new Guacamole.Mouse(displayElement); - const sendMouseState = (state: Guacamole.Mouse.State) => { + const sendMouseEvent = (event: Guacamole.Mouse.MouseEvent) => { displayElement.focus({ preventScroll: true }); const scale = scaleRef.current; - const adjustedX = Math.round(state.x / scale); - const adjustedY = Math.round(state.y / scale); - + const state = event.state; const adjustedState = new Guacamole.Mouse.State( - adjustedX, - adjustedY, + Math.round(state.x / scale), + Math.round(state.y / scale), state.left, state.middle, state.right, state.up, state.down, ) as Guacamole.Mouse.State; - client.sendMouseState(adjustedState); }; - mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = sendMouseState; + + if (touchMode === "touchscreen") { + const touchscreen = new Guacamole.Mouse.Touchscreen(displayElement); + touchscreen.onEach(["mousedown", "mousemove", "mouseup"], sendMouseEvent); + } else if (touchMode === "touchpad") { + const touchpad = new Guacamole.Mouse.Touchpad(displayElement); + touchpad.onEach(["mousedown", "mousemove", "mouseup"], sendMouseEvent); + } else { + const mouse = new Guacamole.Mouse(displayElement); + const sendMouseState = (state: Guacamole.Mouse.State) => { + displayElement.focus({ preventScroll: true }); + const scale = scaleRef.current; + const adjustedState = new Guacamole.Mouse.State( + Math.round(state.x / scale), + Math.round(state.y / scale), + state.left, + state.middle, + state.right, + state.up, + state.down, + ) as Guacamole.Mouse.State; + client.sendMouseState(adjustedState); + }; + mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = sendMouseState; + } const keyboard = new Guacamole.Keyboard(displayElement); keyboardRef.current = keyboard; @@ -425,10 +474,13 @@ export const GuacamoleDisplay = forwardRef< displayElement.addEventListener("focus", handleDisplayFocus); displayElement.addEventListener("blur", handleDisplayBlur); displayElement.addEventListener("mousedown", handleDisplayFocus); + displayElement.addEventListener("touchstart", handleDisplayFocus, { + passive: true, + }); refreshKeyboardHandlers(); client.onstatechange = (state: number) => { - if (!isMountedRef.current) return; + if (!isMountedRef.current || clientRef.current !== client) return; switch (state) { case 0: break; @@ -437,6 +489,7 @@ export const GuacamoleDisplay = forwardRef< case 2: break; case 3: + clearConnectWatchdog(); isConnectingRef.current = false; setIsReady(true); onConnect?.(); @@ -456,16 +509,21 @@ export const GuacamoleDisplay = forwardRef< case 4: break; case 5: + clearConnectWatchdog(); + isConnectingRef.current = false; setIsReady(false); + setHasError(true); hasKeyboardFocusRef.current = false; refreshKeyboardHandlers(); + onError?.(t("guacamole.connectionError")); onDisconnect?.(); break; } }; client.onerror = (error: Guacamole.Status) => { - if (!isMountedRef.current) return; + if (!isMountedRef.current || clientRef.current !== client) return; + clearConnectWatchdog(); const errorMessage = error.message || t("guacamole.connectionError"); setIsReady(false); setHasError(true); @@ -509,8 +567,21 @@ export const GuacamoleDisplay = forwardRef< }; try { - client.connect(); + connectWatchdog = setTimeout(() => { + if ( + !isMountedRef.current || + clientRef.current !== client || + !isConnectingRef.current + ) { + return; + } + + disconnectClient(); + void connect(); + }, 8000); + client.connect(wsConnection.query); } catch (error) { + clearConnectWatchdog(); isConnectingRef.current = false; if (!isMountedRef.current) return; setIsReady(false); @@ -520,15 +591,17 @@ export const GuacamoleDisplay = forwardRef< ); } }, [ - getWebSocketUrl, + getWebSocketConnection, onConnect, onDisconnect, onError, refreshKeyboardHandlers, rescaleDisplay, + disconnectClient, connectionConfig.protocol, connectionConfig.type, connectionConfig.dpi, + touchMode, t, ]); diff --git a/src/ui/features/guacamole/GuacamoleToolbar.tsx b/src/ui/features/guacamole/GuacamoleToolbar.tsx index e28bae0f..2736d688 100644 --- a/src/ui/features/guacamole/GuacamoleToolbar.tsx +++ b/src/ui/features/guacamole/GuacamoleToolbar.tsx @@ -13,6 +13,8 @@ import { ChevronUp, ChevronDown, ChevronsLeftRight, + Touchpad, + MousePointer, } from "lucide-react"; import { Tooltip, @@ -20,13 +22,18 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/tooltip.tsx"; -import type { GuacamoleDisplayHandle } from "@/features/guacamole/GuacamoleDisplay.tsx"; +import type { + GuacamoleDisplayHandle, + GuacamoleTouchMode, +} from "@/features/guacamole/GuacamoleDisplay.tsx"; import { useTranslation } from "react-i18next"; import { cn } from "@/lib/utils"; interface GuacamoleToolbarProps { displayRef: React.RefObject; protocol: "rdp" | "vnc" | "telnet"; + touchMode?: GuacamoleTouchMode | null; + onTouchModeChange?: (mode: GuacamoleTouchMode) => void; } const MODIFIER_KEYSYMS = { @@ -107,6 +114,8 @@ function TipIconBtn({ export const GuacamoleToolbar: React.FC = ({ displayRef, protocol, + touchMode, + onTouchModeChange, }) => { const { t } = useTranslation(); const [position, setPosition] = useState({ x: 0, y: 12 }); @@ -287,6 +296,39 @@ export const GuacamoleToolbar: React.FC = ({ + {/* Touch mode toggle — touch devices only */} + {touchMode != null && onTouchModeChange && ( + <> +
+ + + + + + {touchMode === "touchscreen" + ? t("guacamole.toolbar.switchToTrackpad") + : t("guacamole.toolbar.switchToTouch")} + + + + )} + {/* System combos — RDP/VNC only */} {isRdpVnc && ( <> diff --git a/src/ui/features/serial/Serial.tsx b/src/ui/features/serial/Serial.tsx index 969b0ad6..851abb15 100644 --- a/src/ui/features/serial/Serial.tsx +++ b/src/ui/features/serial/Serial.tsx @@ -10,10 +10,10 @@ import { FitAddon } from "@xterm/addon-fit"; import { useTranslation } from "react-i18next"; import { TriangleAlert } from "lucide-react"; import { isElectron } from "@/lib/electron"; -import { isEmbeddedMode } from "@/main-axios"; import { useTheme } from "@/components/theme-provider"; import { resolveTermixThemeColors } from "@/features/terminal/terminal-theme"; import { DEFAULT_TERMINAL_CONFIG, TERMINAL_FONTS } from "@/lib/terminal-themes"; +import { ensureTerminalFontsLoaded } from "@/features/terminal/terminal-global-styles"; import type { SerialConfig } from "@/types/ui-types"; import type { SerialHandle } from "./serial-types"; @@ -67,6 +67,7 @@ export const Serial = forwardRef(function Serial( const fontConfig = TERMINAL_FONTS.find( (f) => f.value === DEFAULT_TERMINAL_CONFIG.fontFamily, ); + ensureTerminalFontsLoaded(fontConfig?.value ?? TERMINAL_FONTS[0].value); terminal.options.theme = { background: themeColors.background, foreground: themeColors.foreground, @@ -99,30 +100,10 @@ export const Serial = forwardRef(function Serial( // ── WebSocket (Electron) path ────────────────────────────────────────── const buildWsUrl = useCallback(() => { - const isDev = - !isElectron() && - process.env.NODE_ENV === "development" && - (window.location.port === "3000" || - window.location.port === "5173" || - window.location.port === ""); - - if (isDev || isEmbeddedMode()) { - const token = localStorage.getItem("jwt"); - const base = "ws://127.0.0.1:30011"; - return token ? `${base}?token=${encodeURIComponent(token)}` : base; - } - - const configuredUrl = (window as { configuredServerUrl?: string | null }) - .configuredServerUrl; - - if (!configuredUrl) return null; - - const wsProtocol = configuredUrl.startsWith("https://") - ? "wss://" - : "ws://"; - const wsHost = configuredUrl.replace(/^https?:\/\//, "").replace(/\/$/, ""); + // Serial is always local -- the device is physically attached to this + // desktop machine, so it never routes through a remote server. const token = localStorage.getItem("jwt"); - const base = `${wsProtocol}${wsHost}/serial/websocket/`; + const base = "ws://127.0.0.1:30011"; return token ? `${base}?token=${encodeURIComponent(token)}` : base; }, []); diff --git a/src/ui/features/session-sharing/ShareSessionModal.tsx b/src/ui/features/session-sharing/ShareSessionModal.tsx new file mode 100644 index 00000000..082c301c --- /dev/null +++ b/src/ui/features/session-sharing/ShareSessionModal.tsx @@ -0,0 +1,429 @@ +import { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Check, Copy, Link2, Search, Shield, User, Users } from "lucide-react"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { Input } from "@/components/input"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/dropdown-menu"; +import { getUserList } from "@/main-axios"; +import { + createSessionShare, + getActiveSessionShares, + revokeSessionShare, + type SessionShareProtocol, + type SessionSharePermissionLevel, + type SessionShareRecord, +} from "@/api/session-sharing-api"; + +const EXPIRY_PRESETS = [ + { key: "oneHour", hours: 1 }, + { key: "oneDay", hours: 24 }, + { key: "sevenDays", hours: 24 * 7 }, + { key: "thirtyDays", hours: 24 * 30 }, + { key: "custom", hours: undefined }, +] as const; + +type ExpiryPresetKey = (typeof EXPIRY_PRESETS)[number]["key"]; + +export function ShareSessionModal({ + open, + onClose, + hostId, + sessionId, + protocol, + tabInstanceId, +}: { + open: boolean; + onClose: () => void; + hostId: number; + sessionId: string | null; + protocol: SessionShareProtocol; + tabInstanceId?: string; +}) { + const { t } = useTranslation(); + const [mode, setMode] = useState<"link" | "user">("link"); + const [permissionLevel, setPermissionLevel] = + useState("read-only"); + const [expiryPreset, setExpiryPreset] = useState("oneDay"); + const [customHours, setCustomHours] = useState(""); + const [search, setSearch] = useState(""); + const [users, setUsers] = useState<{ id: string; username: string }[]>([]); + const [selectedUserId, setSelectedUserId] = useState(null); + const [submitting, setSubmitting] = useState(false); + const [createdLink, setCreatedLink] = useState(null); + const [shares, setShares] = useState([]); + const [sharesLoaded, setSharesLoaded] = useState(false); + + useEffect(() => { + if (!open) return; + setMode("link"); + setPermissionLevel("read-only"); + setExpiryPreset("oneDay"); + setCustomHours(""); + setSearch(""); + setSelectedUserId(null); + setCreatedLink(null); + setSharesLoaded(false); + setShares([]); + }, [open, sessionId]); + + useEffect(() => { + if (!open || sharesLoaded) return; + setSharesLoaded(true); + Promise.all([ + getUserList().catch(() => ({ users: [] })), + getActiveSessionShares(hostId).catch(() => ({ shares: [] })), + ]).then(([usersRes, sharesRes]) => { + setUsers( + (usersRes.users ?? []).map((u) => ({ + id: String(u.userId), + username: u.username, + })), + ); + setShares(sharesRes.shares ?? []); + }); + }, [open, hostId, sharesLoaded]); + + const filteredUsers = useMemo(() => { + const q = search.trim().toLowerCase(); + return q + ? users.filter((u) => u.username.toLowerCase().includes(q)) + : users; + }, [users, search]); + + const expiryHours = (() => { + if (expiryPreset === "custom") { + const hours = Number(customHours); + return Number.isFinite(hours) && hours > 0 ? hours : undefined; + } + return EXPIRY_PRESETS.find((p) => p.key === expiryPreset)?.hours; + })(); + + async function refreshShares() { + try { + const res = await getActiveSessionShares(hostId); + setShares(res.shares ?? []); + } catch { + // silently ignore + } + } + + async function handleCreate() { + if (!sessionId) return; + if (mode === "user" && !selectedUserId) return; + if (expiryPreset === "custom" && !expiryHours) return; + + setSubmitting(true); + try { + const result = await createSessionShare({ + hostId, + sessionId, + tabInstanceId, + protocol, + shareType: mode, + targetUserId: + mode === "user" ? (selectedUserId ?? undefined) : undefined, + permissionLevel, + expiryHours, + }); + + if (mode === "link" && result.linkToken) { + const url = `${window.location.origin}${window.location.pathname}?view=shared&token=${result.linkToken}`; + setCreatedLink(url); + toast.success(t("sessionSharing.linkCreated")); + } else { + toast.success(t("sessionSharing.shareCreated")); + setSelectedUserId(null); + } + await refreshShares(); + } catch (error) { + const status = (error as { status?: number })?.status; + if (mode === "user" && status === 403) { + toast.error(t("sessionSharing.userLacksHostAccess")); + } else { + toast.error(t("sessionSharing.shareFailed")); + } + } finally { + setSubmitting(false); + } + } + + async function handleCopyLink() { + if (!createdLink) return; + try { + await navigator.clipboard.writeText(createdLink); + toast.success(t("sessionSharing.linkCopied")); + } catch { + // clipboard API unavailable, ignore + } + } + + async function handleRevoke(shareId: string) { + try { + await revokeSessionShare(shareId); + setShares((prev) => prev.filter((s) => s.id !== shareId)); + toast.success(t("sessionSharing.revoked")); + } catch { + toast.error(t("sessionSharing.revokeFailed")); + } + } + + return ( + !next && onClose()}> + + + {t("sessionSharing.modalTitle")} + + {mode === "link" + ? t("sessionSharing.linkModeDescription") + : t("sessionSharing.userModeDescription")} + + + +
+
+ {(["link", "user"] as const).map((m) => ( + + ))} +
+ + {mode === "user" && ( + <> +
+ + setSearch(e.target.value)} + className="pl-8" + /> +
+
+ {filteredUsers.length === 0 ? ( +
+ {t("sessionSharing.noUsersFound")} +
+ ) : ( + filteredUsers.map((user) => { + const isSelected = selectedUserId === user.id; + return ( + + ); + }) + )} +
+ + )} + +
+
+ + {t("sessionSharing.permissionLevel.label")} + + +
+ + + + + + + {EXPIRY_PRESETS.map((preset) => ( + setExpiryPreset(preset.key)} + > + {expiryPreset === preset.key ? ( + + ) : ( + + )} + {t(`hosts.sharing.expiry.${preset.key}`)} + + ))} + + +
+ + {expiryPreset === "custom" && ( + setCustomHours(e.target.value)} + className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + )} + +

+ {permissionLevel === "read-only" + ? t("sessionSharing.permissionLevel.readOnlyDescription") + : t("sessionSharing.permissionLevel.readWriteDescription")} +

+ + + + {createdLink && ( +
+ + +
+ )} + +
+
+ + {t("sessionSharing.activeShares")} + {shares.length > 0 && ( + + ({shares.length}) + + )} +
+
+ {shares.length === 0 && ( +
+ {t("sessionSharing.noActiveShares")} +
+ )} + {shares.map((share) => { + const targetUser = users.find( + (u) => u.id === share.targetUserId, + ); + return ( +
+
+ {share.shareType === "link" ? ( + + ) : ( + + )} +
+ + {share.shareType === "link" + ? t("sessionSharing.linkShareBadge") + : t("sessionSharing.userShareBadge", { + username: + targetUser?.username ?? + share.targetUserId ?? + "?", + })} + + + {share.permissionLevel === "read-write" + ? t("sessionSharing.permissionLevel.readWrite") + : t("sessionSharing.permissionLevel.readOnly")} + {" · "} + {t("sessionSharing.expiresAt", { + date: new Date(share.expiresAt).toLocaleString(), + })} + +
+
+ +
+ ); + })} +
+
+
+
+
+ ); +} diff --git a/src/ui/features/session-sharing/SharedSessionView.tsx b/src/ui/features/session-sharing/SharedSessionView.tsx new file mode 100644 index 00000000..8827b5ca --- /dev/null +++ b/src/ui/features/session-sharing/SharedSessionView.tsx @@ -0,0 +1,320 @@ +import React, { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useXTerm } from "react-xtermjs"; +import { FitAddon } from "@xterm/addon-fit"; +import { AlertCircle, Eye } from "lucide-react"; +import { + resolveShareLink, + type ResolvedShareLink, + type ShareLinkErrorKind, +} from "@/api/session-sharing-api"; +import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; +import { getBasePath } from "@/lib/base-path"; +import { isElectron } from "@/lib/electron"; +import { GuacamoleDisplay } from "@/features/guacamole/GuacamoleDisplay.tsx"; + +const PING_INTERVAL_MS = 30000; + +interface TerminalWsMessage { + type: string; + data?: string; + [key: string]: unknown; +} + +// Mirrors Terminal.tsx's baseWsUrl construction (dev/electron/embedded/prod). +// Duplicated rather than extracted from that file to avoid touching it here. +// A shared session link is always resolved against the desktop app's +// embedded local backend -- joining a session hosted on someone else's +// remote server isn't supported from the desktop app today. +async function resolveTerminalWsBaseUrl(): Promise { + const isDev = + !isElectron() && + process.env.NODE_ENV === "development" && + (window.location.port === "3000" || + window.location.port === "5173" || + window.location.port === ""); + + if (isDev) { + return `${window.location.protocol === "https:" ? "wss" : "ws"}://localhost:30002`; + } + if (isElectron()) { + return "ws://127.0.0.1:30002"; + } + const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws"; + return `${wsProtocol}://${window.location.host}${getBasePath()}/ssh/websocket/`; +} + +function ReadOnlyBadge({ label }: { label: string }) { + return ( +
+ + {label} +
+ ); +} + +function CenteredMessage({ + icon, + message, +}: { + icon: React.ReactNode; + message: string; +}) { + return ( +
+ {icon} +

+ {message} +

+
+ ); +} + +function GuestTerminalView({ + share, + linkToken, +}: { + share: ResolvedShareLink; + linkToken: string; +}) { + const { t } = useTranslation(); + const { instance: terminal, ref: xtermRef } = useXTerm(); + const [ended, setEnded] = useState(null); + const wsRef = useRef(null); + const pingIntervalRef = useRef | null>(null); + + useEffect(() => { + if (!terminal || !xtermRef.current) return; + + terminal.options.theme = { background: "#0c0d0b" }; + + const fitAddon = new FitAddon(); + terminal.loadAddon(fitAddon); + terminal.open(xtermRef.current); + fitAddon.fit(); + + const resizeObserver = new ResizeObserver(() => fitAddon.fit()); + resizeObserver.observe(xtermRef.current); + + let cancelled = false; + let ws: WebSocket | null = null; + + resolveTerminalWsBaseUrl().then((baseWsUrl) => { + if (cancelled) return; + const separator = baseWsUrl.includes("?") ? "&" : "?"; + ws = new WebSocket( + `${baseWsUrl}${separator}shareToken=${encodeURIComponent(linkToken)}`, + ); + wsRef.current = ws; + + ws.onopen = () => { + pingIntervalRef.current = setInterval(() => { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: "ping" })); + } + }, PING_INTERVAL_MS); + }; + + ws.onmessage = (event) => { + let msg: TerminalWsMessage; + try { + msg = JSON.parse(event.data); + } catch { + return; + } + + switch (msg.type) { + case "data": + if (typeof msg.data === "string") terminal.write(msg.data); + break; + case "sessionExpired": + case "sessionTerminatedByOwner": + case "session_ended": + setEnded(t("sessionSharing.guestView.sessionEnded")); + break; + default: + break; + } + }; + + ws.onclose = () => { + setEnded((prev) => prev ?? t("sessionSharing.guestView.sessionEnded")); + }; + + if (share.permissionLevel === "read-write") { + terminal.onData((data) => { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: "input", data })); + } + }); + } + }); + + return () => { + cancelled = true; + resizeObserver.disconnect(); + if (pingIntervalRef.current) clearInterval(pingIntervalRef.current); + ws?.close(); + wsRef.current = null; + }; + // Deliberately runs once terminal mounts - share/token/permission are stable for the view's lifetime. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [terminal, linkToken]); + + return ( +
+ {share.permissionLevel === "read-only" && ( + + )} + {ended && ( +
+ + } + message={ended} + /> +
+ )} +
+
+ ); +} + +function GuestGuacamoleView({ share }: { share: ResolvedShareLink }) { + const { t } = useTranslation(); + const [connectionError, setConnectionError] = useState(null); + + if (!share.connectParams?.token) { + return ( + + } + message={t("sessionSharing.guestView.linkInvalid")} + /> + ); + } + + return ( +
+ {share.permissionLevel === "read-only" && ( + + )} + {connectionError && ( +
+ + } + message={connectionError} + /> +
+ )} + setConnectionError(err)} + /> +
+ ); +} + +export default function SharedSessionView() { + const { t } = useTranslation(); + const [share, setShare] = useState(null); + const [linkToken, setLinkToken] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const token = params.get("token"); + if (!token) { + setError(t("sessionSharing.guestView.linkInvalid")); + setLoading(false); + return; + } + setLinkToken(token); + + resolveShareLink(token) + .then((resolved) => setShare(resolved)) + .catch((err) => { + const kind = (err as { kind?: ShareLinkErrorKind })?.kind; + if (kind === "rate-limited") { + setError(t("sessionSharing.guestView.rateLimited")); + } else { + setError(t("sessionSharing.guestView.linkInvalid")); + } + }) + .finally(() => setLoading(false)); + }, [t]); + + return ( +
+
+ {loading && ( + + )} + {!loading && error && ( + + } + message={error} + /> + )} + {!loading && + !error && + share && + linkToken && + (share.protocol === "ssh" ? ( + + ) : ( + + ))} +
+
+ ); +} diff --git a/src/ui/features/terminal/MobileTerminalKeyboard.tsx b/src/ui/features/terminal/MobileTerminalKeyboard.tsx index 83d0123a..f21364e1 100644 --- a/src/ui/features/terminal/MobileTerminalKeyboard.tsx +++ b/src/ui/features/terminal/MobileTerminalKeyboard.tsx @@ -5,11 +5,13 @@ import { ChevronDown, ChevronLeft, ChevronRight, + Clipboard, Pencil, X, Plus, RotateCcw, } from "lucide-react"; +import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; @@ -252,6 +254,18 @@ export function MobileTerminalKeyboard({ terminalRef.current?.sendInput?.(seq); } + async function handlePaste() { + try { + const text = window.electronClipboard + ? await window.electronClipboard.readText() + : ((await navigator.clipboard?.readText?.()) ?? ""); + if (text) terminalRef.current?.paste?.(text); + else toast.error(t("terminal.clipboardReadFailed")); + } catch { + toast.error(t("terminal.clipboardReadFailed")); + } + } + function toggleCtrl() { setCtrlActive((v) => !v); setShiftActive(false); @@ -322,6 +336,18 @@ export function MobileTerminalKeyboard({ {shiftActive ? t("mobileKeyboard.backTab") : t("mobileKeyboard.tab")} + {/* Paste */} + +
{/* Ctrl */} diff --git a/src/ui/features/terminal/Terminal.tsx b/src/ui/features/terminal/Terminal.tsx index 7eda9981..f13dc7d8 100644 --- a/src/ui/features/terminal/Terminal.tsx +++ b/src/ui/features/terminal/Terminal.tsx @@ -17,16 +17,18 @@ import { Unicode11Addon } from "@xterm/addon-unicode11"; import { WebLinksAddon } from "@xterm/addon-web-links"; import { useTranslation } from "react-i18next"; import { getBasePath } from "@/lib/base-path"; +import { + resolveConnectionOrigin, + buildOriginWsUrl, +} from "@/lib/connection-origin.ts"; import { getCookie, isElectron, - isEmbeddedMode, logActivity, getSnippets, deleteCommandFromHistory, getCommandHistory, getHostPassword, - getServerConfig, } from "@/main-axios.ts"; import { TOTPDialog } from "@/ssh/dialogs/TOTPDialog.tsx"; import { SSHAuthDialog } from "@/ssh/dialogs/SSHAuthDialog.tsx"; @@ -39,7 +41,7 @@ import { DEFAULT_TERMINAL_CONFIG, TERMINAL_FONTS, } from "@/lib/terminal-themes.ts"; -import "./terminal-global-styles.ts"; +import { ensureTerminalFontsLoaded } from "./terminal-global-styles.ts"; import { useTheme } from "@/components/theme-provider.tsx"; import { globalShortcutHandler } from "@/lib/global-shortcut-handler"; import { useCommandTracker } from "@/features/terminal/command-history/useCommandTracker.ts"; @@ -57,11 +59,19 @@ import { toast } from "sonner"; import { Button } from "@/components/button"; import { Save } from "lucide-react"; import { resolveTermixThemeColors } from "./terminal-theme.ts"; +import { ShareSessionModal } from "@/features/session-sharing/ShareSessionModal.tsx"; import type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts"; import { getNextTerminalFontSize, getTerminalFontZoomDirection, } from "./terminal-font-zoom.ts"; +import { + getUserPreferences, + parseCustomKeybindings, +} from "@/api/open-tabs-api"; +import { findMatchingKeybinding } from "@/lib/keybinding-match"; +import { dispatchKeybindingAction } from "@/lib/keybinding-dispatch"; +import type { CustomKeybinding } from "@/types/keybindings"; export type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts"; type HostKeyVerificationData = Omit< @@ -158,12 +168,17 @@ const TerminalInner = forwardRef( : themeColors.background; const fitAddonRef = useRef(null); const webSocketRef = useRef(null); + const customKeybindingsRef = useRef([]); + const cachedSnippetsRef = useRef<{ id: number; content: string }[] | null>( + null, + ); const resizeTimeout = useRef(null); const wasDisconnectedBySSH = useRef(false); const pingIntervalRef = useRef(null); const pongReceivedRef = useRef(true); const pongTimeoutRef = useRef(null); const [isConnected, setIsConnected] = useState(false); + const [shareModalOpen, setShareModalOpen] = useState(false); const [isSavingQuickConnect, setIsSavingQuickConnect] = useState(false); const [isQuickConnectSaved, setIsQuickConnectSaved] = useState(false); const [isConnecting, setIsConnecting] = useState(false); @@ -182,6 +197,10 @@ const TerminalInner = forwardRef( const [totpRequired, setTotpRequired] = useState(false); const [totpPrompt, setTotpPrompt] = useState(""); const [isPasswordPrompt, setIsPasswordPrompt] = useState(false); + const [mfaPromptMode, setMfaPromptMode] = useState< + "totp" | "password" | "menu" | "push" + >("totp"); + const [mfaWaiting, setMfaWaiting] = useState(false); const [showAuthDialog, setShowAuthDialog] = useState(false); const [authDialogReason, setAuthDialogReason] = useState< "no_keyboard" | "auth_failed" | "timeout" @@ -512,17 +531,25 @@ const TerminalInner = forwardRef( } function handleTotpSubmit(code: string) { - if (webSocketRef.current && code) { - if (totpTimeoutRef.current) { - clearTimeout(totpTimeoutRef.current); - totpTimeoutRef.current = null; - } + const isPushMode = mfaPromptMode === "push"; + if (webSocketRef.current && (code || isPushMode)) { webSocketRef.current.send( JSON.stringify({ type: isPasswordPrompt ? "password_response" : "totp_response", data: { code }, }), ); + if (isPushMode) { + // Server blocks until the phone approval completes; keep the dialog + // open in a waiting state rather than closing it immediately. The + // existing timeout continues running until "connected" or "error". + setMfaWaiting(true); + return; + } + if (totpTimeoutRef.current) { + clearTimeout(totpTimeoutRef.current); + totpTimeoutRef.current = null; + } setTotpRequired(false); setTotpPrompt(""); setIsPasswordPrompt(false); @@ -536,6 +563,9 @@ const TerminalInner = forwardRef( } setTotpRequired(false); setTotpPrompt(""); + setIsPasswordPrompt(false); + setMfaPromptMode("totp"); + setMfaWaiting(false); if (onClose) onClose(); } @@ -824,6 +854,9 @@ const TerminalInner = forwardRef( webSocketRef.current.send(JSON.stringify({ type: "input", data })); } }, + paste: (text: string) => { + terminal?.paste(text); + }, notifyResize: () => { try { const cols = terminal?.cols ?? undefined; @@ -846,8 +879,20 @@ const TerminalInner = forwardRef( onOpenFileManager?.("/"); } }, + openShareModal: () => setShareModalOpen(true), + canShare: () => + isConnected && + !isQuickConnect && + !hostConfig.joinShareId && + typeof hostConfig.id === "number", }), - [isConnected, terminal], + [ + isConnected, + terminal, + isQuickConnect, + hostConfig.joinShareId, + hostConfig.id, + ], ); function getUseRightClickCopyPaste() { @@ -956,52 +1001,28 @@ const TerminalInner = forwardRef( if (isDev) { baseWsUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://localhost:30002`; } else if (isElectron()) { - let configuredUrl = (window as { configuredServerUrl?: string | null }) - .configuredServerUrl; - - if (!configuredUrl && !isEmbeddedMode()) { - try { - const serverConfig = await getServerConfig(); - configuredUrl = serverConfig?.serverUrl || null; - if (configuredUrl) { - ( - window as Window & - typeof globalThis & { - configuredServerUrl?: string | null; - } - ).configuredServerUrl = configuredUrl; - } - } catch (error) { - console.error("Failed to resolve Electron server URL:", error); - } - } - - if (isEmbeddedMode()) { - baseWsUrl = "ws://127.0.0.1:30002"; - const storedJwt = localStorage.getItem("jwt"); - if (storedJwt) { - baseWsUrl += `?token=${encodeURIComponent(storedJwt)}`; - } - } else if (!configuredUrl) { - console.error("No configured server URL available for Electron SSH"); + const origin = await resolveConnectionOrigin({ + connectionType: "ssh", + connectionOrigin: hostConfig.connectionOrigin as + | "local" + | "remote" + | null + | undefined, + }); + const resolvedUrl = await buildOriginWsUrl({ + origin, + localPort: 30002, + localPath: "", + remotePath: "/ssh/websocket/", + }); + if (!resolvedUrl) { setIsConnected(false); setIsConnecting(false); - updateConnectionError(t("errors.failedToLoadServer")); + updateConnectionError(t("errors.remoteServerRequired")); isConnectingRef.current = false; return; - } else { - const wsProtocol = configuredUrl.startsWith("https://") - ? "wss://" - : "ws://"; - const wsHost = configuredUrl - .replace(/^https?:\/\//, "") - .replace(/\/$/, ""); - baseWsUrl = `${wsProtocol}${wsHost}/ssh/websocket/`; - const storedJwt = localStorage.getItem("jwt"); - if (storedJwt) { - baseWsUrl += `?token=${encodeURIComponent(storedJwt)}`; - } } + baseWsUrl = resolvedUrl; } else { baseWsUrl = `${getBasePath()}/ssh/websocket/`; } @@ -1076,7 +1097,19 @@ const TerminalInner = forwardRef( const restoredSessionId = pendingRestoredSessionIdRef.current; pendingRestoredSessionIdRef.current = null; - if (restoredSessionId) { + if (hostConfig.joinShareId) { + isAttachingSessionRef.current = true; + + ws.send( + JSON.stringify({ + type: "joinSharedSession", + data: { + shareId: hostConfig.joinShareId, + tabInstanceId: hostConfig.instanceId, + }, + }), + ); + } else if (restoredSessionId) { sessionIdRef.current = restoredSessionId; isAttachingSessionRef.current = true; @@ -1327,6 +1360,8 @@ const TerminalInner = forwardRef( setTotpRequired(true); setTotpPrompt(msg.prompt || t("terminal.totpCodeLabel")); setIsPasswordPrompt(false); + setMfaPromptMode("totp"); + setMfaWaiting(false); if (connectionTimeoutRef.current) { clearTimeout(connectionTimeoutRef.current); connectionTimeoutRef.current = null; @@ -1342,10 +1377,24 @@ const TerminalInner = forwardRef( }, 180000); } else if (msg.type === "totp_retry") { // Existing prompt remains visible while the backend asks for another code. + setMfaWaiting(false); } else if (msg.type === "password_required") { + const promptText: string = msg.prompt || ""; + const pushPromptPattern = + /choose.*push.*totp|press enter.*(push|send)|push notification|authentication by phone/i; + const isPush = pushPromptPattern.test(promptText); + const isMenu = !isPush && msg.echo === true; + const mode: "menu" | "push" | "password" = isPush + ? "push" + : isMenu + ? "menu" + : "password"; + setTotpRequired(true); - setTotpPrompt(msg.prompt || t("common.password")); + setTotpPrompt(promptText || t("common.password")); setIsPasswordPrompt(true); + setMfaPromptMode(mode); + setMfaWaiting(false); if (connectionTimeoutRef.current) { clearTimeout(connectionTimeoutRef.current); connectionTimeoutRef.current = null; @@ -1353,12 +1402,15 @@ const TerminalInner = forwardRef( if (totpTimeoutRef.current) { clearTimeout(totpTimeoutRef.current); } - totpTimeoutRef.current = setTimeout(() => { - setTotpRequired(false); - if (webSocketRef.current) { - webSocketRef.current.close(); - } - }, 180000); + totpTimeoutRef.current = setTimeout( + () => { + setTotpRequired(false); + if (webSocketRef.current) { + webSocketRef.current.close(); + } + }, + isPush ? 300000 : 180000, + ); } else if (msg.type === "warpgate_auth_required") { setWarpgateAuthRequired(true); setWarpgateAuthUrl(msg.url || ""); @@ -1986,6 +2038,7 @@ const TerminalInner = forwardRef( (f) => f.value === config.fontFamily, ); const fontFamily = fontConfig?.fallback || TERMINAL_FONTS[0].fallback; + ensureTerminalFontsLoaded(fontConfig?.value || TERMINAL_FONTS[0].value); // Update terminal options individually to avoid re-initialization flashes terminal.options.cursorBlink = config.cursorBlink; @@ -2053,6 +2106,7 @@ const TerminalInner = forwardRef( (f) => f.value === config.fontFamily, ); const fontFamily = fontConfig?.fallback || TERMINAL_FONTS[0].fallback; + ensureTerminalFontsLoaded(fontConfig?.value || TERMINAL_FONTS[0].value); const activeTheme = previewTheme || config.theme; const themeColors = resolveTermixThemeColors( @@ -2354,14 +2408,76 @@ const TerminalInner = forwardRef( }; }, [hostConfig.id, hostConfig.instanceId]); + useEffect(() => { + let cancelled = false; + const loadKeybindings = () => { + cachedSnippetsRef.current = null; + getUserPreferences() + .then((prefs) => { + if (!cancelled) { + customKeybindingsRef.current = parseCustomKeybindings( + prefs.customKeybindings, + ).filter((kb) => kb.enabled); + } + }) + .catch(() => { + // keep previous/empty bindings on failure, non-fatal + }); + }; + loadKeybindings(); + window.addEventListener("customKeybindingsChanged", loadKeybindings); + return () => { + cancelled = true; + window.removeEventListener("customKeybindingsChanged", loadKeybindings); + }; + }, []); + useEffect(() => { if (!terminal) return; + const getSnippetById = async ( + id: string, + ): Promise<{ content: string } | undefined> => { + try { + if (!cachedSnippetsRef.current) { + cachedSnippetsRef.current = (await getSnippets()) as unknown as { + id: number; + content: string; + }[]; + } + const numericId = Number(id); + return cachedSnippetsRef.current?.find((s) => s.id === numericId); + } catch { + return undefined; + } + }; + const handleCustomKey = (e: KeyboardEvent): boolean => { if (e.type !== "keydown") { return true; } + // Custom user keybindings take priority over built-in defaults, but + // never override autocomplete popup navigation while it is open. + if (!showAutocompleteRef.current) { + const matched = findMatchingKeybinding( + e, + customKeybindingsRef.current, + ); + if (matched) { + e.preventDefault(); + e.stopPropagation(); + dispatchKeybindingAction(matched.action, { + terminal, + webSocketRef, + writeTextToClipboard, + readTextFromClipboard, + getSnippetById, + }); + return false; + } + } + // Forward global app shortcuts to AppShell directly — xterm swallows // all keydown events and synthetic re-dispatch is unreliable. // stopPropagation prevents the same event from also firing the window listener. @@ -2896,6 +3012,8 @@ const TerminalInner = forwardRef( (
, document.body, )} + + {shareModalOpen && typeof hostConfig.id === "number" && ( + setShareModalOpen(false)} + hostId={hostConfig.id} + sessionId={sessionIdRef.current} + protocol="ssh" + tabInstanceId={hostConfig.instanceId} + /> + )}
); }, diff --git a/src/ui/features/terminal/terminal-global-styles.ts b/src/ui/features/terminal/terminal-global-styles.ts index a5a1b60f..c91a890f 100644 --- a/src/ui/features/terminal/terminal-global-styles.ts +++ b/src/ui/features/terminal/terminal-global-styles.ts @@ -46,6 +46,7 @@ style.innerHTML = ` .xterm .xterm-viewport { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.3) transparent; + background-color: transparent !important; } .dark .xterm .xterm-viewport::-webkit-scrollbar-thumb { @@ -74,3 +75,22 @@ style.innerHTML = ` } `; document.head.appendChild(style); + +// Canvas fillText() does not reliably trigger @font-face fetches on every +// browser engine (notably Android WebView) the way rendering real DOM text +// does. xterm.js draws glyphs to a , so without an explicit load the +// terminal can keep painting the fallback font's tofu boxes even after +// document.fonts.ready resolves. Forcing the load here ensures the glyph +// data is actually fetched before the terminal renders with it. +export function ensureTerminalFontsLoaded(fontFamily: string): void { + if (typeof document === "undefined" || !document.fonts) return; + const specs = [ + `400 16px "${fontFamily}"`, + `700 16px "${fontFamily}"`, + `italic 400 16px "${fontFamily}"`, + `italic 700 16px "${fontFamily}"`, + ]; + for (const spec of specs) { + document.fonts.load(spec).catch(() => {}); + } +} diff --git a/src/ui/features/terminal/terminal-types.ts b/src/ui/features/terminal/terminal-types.ts index 3c2c562b..3fe928d8 100644 --- a/src/ui/features/terminal/terminal-types.ts +++ b/src/ui/features/terminal/terminal-types.ts @@ -4,6 +4,9 @@ export interface TerminalHostConfig { id?: number; instanceId?: string; restoredSessionId?: string | null; + /** Set when this tab joins someone else's live shared SSH session instead of connecting/attaching. */ + joinSharedSessionId?: string | null; + joinShareId?: string | null; ip: string; port: number; username: string; @@ -24,7 +27,10 @@ export interface TerminalHandle { fit: () => void; focus: () => void; sendInput: (data: string) => void; + paste: (text: string) => void; notifyResize: () => void; refresh: () => void; getApplicationCursorKeysMode: () => boolean; + openShareModal: () => void; + canShare: () => boolean; } diff --git a/src/ui/features/tmux-monitor/PanePreview.tsx b/src/ui/features/tmux-monitor/PanePreview.tsx index 87b1ed58..d9581948 100644 --- a/src/ui/features/tmux-monitor/PanePreview.tsx +++ b/src/ui/features/tmux-monitor/PanePreview.tsx @@ -70,6 +70,36 @@ export function PanePreview({ setAttachNonce((n) => n + 1); } + // For Tailscale-auth hosts the generic pane-preview attach path can fail + // host-reachability checks because the connection is initiated as a plain + // TCP/SSH dial rather than through the Tailscale transport that was used for + // the interactive terminal. Building the hostConfig with an explicit + // authType and a defensively derived SSH port ensures the backend selects + // the correct Tailscale-aware PTY path for both initial attach and reattach. + let terminalHostConfig: TerminalHostConfig; + if (host.authType === "tailscale") { + // Prefer host.sshPort when set (Tailscale SSH can be on a non-standard + // port); fall back to the general host.port. + const resolvedPort = host.sshPort ?? host.port; + terminalHostConfig = { + ...host, + port: resolvedPort, + sshPort: resolvedPort, + // Carry authType explicitly to guard against accidental omission + // in the spread (e.g. if host object shape changes upstream). + authType: "tailscale", + instanceId: instanceIdRef.current, + } as TerminalHostConfig; + } else { + const resolvedPort = host.sshPort ?? host.port; + terminalHostConfig = { + ...host, + port: resolvedPort, + sshPort: resolvedPort, + instanceId: instanceIdRef.current, + } as TerminalHostConfig; + } + return ( <>
@@ -147,13 +177,7 @@ export function PanePreview({ { + if ( + host.connectionType === "rdp" || + host.connectionType === "vnc" || + host.connectionType === "telnet" + ) { + return "remote"; + } + if (host.connectionType === "serial") { + return "local"; + } + if (!isElectron()) { + return "local"; + } + if (host.connectionOrigin === "local" || host.connectionOrigin === "remote") { + return host.connectionOrigin; + } + + try { + const settings = (await window.electronAPI?.invoke?.( + "get-desktop-settings", + )) as { defaultConnectionOrigin?: ConnectionOrigin } | null; + return settings?.defaultConnectionOrigin === "remote" ? "remote" : "local"; + } catch { + return "local"; + } +} + +export interface RemoteConnectionTarget { + serverUrl: string; + jwt: string | null; +} + +async function getRemoteConnectionTarget(): Promise { + try { + const [config, jwt] = await Promise.all([ + window.electronAPI?.invoke?.("get-remote-sync-config") as Promise<{ + serverUrl?: string; + } | null>, + window.electronAPI?.invoke?.("get-remote-sync-jwt") as Promise< + string | null + >, + ]); + if (!config?.serverUrl) return null; + return { serverUrl: config.serverUrl, jwt: jwt ?? null }; + } catch { + return null; + } +} + +/** + * Builds the base WebSocket URL for an interactive connection protocol, + * given a resolved origin. Returns null when origin is "remote" but no + * remote server is connected -- callers must show a blocking message + * rather than attempting to connect. + */ +export async function buildOriginWsUrl({ + origin, + localPort, + localPath, + remotePath, + includeLocalJwt = true, +}: { + origin: ConnectionOrigin; + localPort: number; + localPath: string; + remotePath: string; + includeLocalJwt?: boolean; +}): Promise { + if (origin === "local") { + let url = `ws://127.0.0.1:${localPort}${localPath}`; + if (includeLocalJwt) { + const token = localStorage.getItem("jwt"); + if (token) url += `?token=${encodeURIComponent(token)}`; + } + return url; + } + + const remote = await getRemoteConnectionTarget(); + if (!remote) return null; + + const wsProtocol = remote.serverUrl.startsWith("https://") + ? "wss://" + : "ws://"; + const wsHost = remote.serverUrl + .replace(/^https?:\/\//, "") + .replace(/\/$/, ""); + let url = `${wsProtocol}${wsHost}${remotePath}`; + if (remote.jwt) url += `?token=${encodeURIComponent(remote.jwt)}`; + return url; +} diff --git a/src/ui/lib/default-keybindings.ts b/src/ui/lib/default-keybindings.ts new file mode 100644 index 00000000..e4c7ec6a --- /dev/null +++ b/src/ui/lib/default-keybindings.ts @@ -0,0 +1,106 @@ +import type { KeyCombo } from "@/types/keybindings"; + +export interface DefaultKeybindingInfo { + id: string; + combo: KeyCombo; + description: string; +} + +export const BUILT_IN_DEFAULTS: DefaultKeybindingInfo[] = [ + { + id: "default-copy-ctrlc", + combo: { + key: "c", + isCode: false, + ctrl: true, + alt: false, + shift: false, + meta: false, + }, + description: "Copy selection (Ctrl+C, only when text is selected)", + }, + { + id: "default-copy-ctrlshiftc", + combo: { + key: "c", + isCode: false, + ctrl: true, + alt: false, + shift: true, + meta: false, + }, + description: "Copy selection (Ctrl+Shift+C)", + }, + { + id: "default-copy-cmdc", + combo: { + key: "c", + isCode: false, + ctrl: false, + alt: false, + shift: false, + meta: true, + }, + description: "Copy selection (Cmd+C)", + }, + { + id: "default-paste-ctrlshiftv", + combo: { + key: "v", + isCode: false, + ctrl: true, + alt: false, + shift: true, + meta: false, + }, + description: "Paste from clipboard (Ctrl+Shift+V)", + }, + { + id: "default-ctrlaltw", + combo: { + key: "w", + isCode: false, + ctrl: true, + alt: true, + shift: false, + meta: false, + }, + description: "Send Ctrl+W to shell (blocked from closing browser tab)", + }, + { + id: "default-ctrlaltt", + combo: { + key: "t", + isCode: false, + ctrl: true, + alt: true, + shift: false, + meta: false, + }, + description: "Send Ctrl+T to shell (blocked from opening browser tab)", + }, + { + id: "default-ctrlaltn", + combo: { + key: "n", + isCode: false, + ctrl: true, + alt: true, + shift: false, + meta: false, + }, + description: "Send Ctrl+N to shell (blocked from opening browser window)", + }, + { + id: "default-ctrlaltq", + combo: { + key: "q", + isCode: false, + ctrl: true, + alt: true, + shift: false, + meta: false, + }, + description: "Send Ctrl+Q to shell (blocked from quitting browser)", + }, +]; diff --git a/src/ui/lib/keybinding-dispatch.ts b/src/ui/lib/keybinding-dispatch.ts new file mode 100644 index 00000000..d02092be --- /dev/null +++ b/src/ui/lib/keybinding-dispatch.ts @@ -0,0 +1,56 @@ +import type { Terminal } from "@xterm/xterm"; +import type { KeybindingAction } from "@/types/keybindings"; + +export interface KeybindingDispatchContext { + terminal: Terminal; + webSocketRef: React.MutableRefObject; + writeTextToClipboard: (text: string) => Promise; + readTextFromClipboard: () => Promise; + getSnippetById: (id: string) => Promise<{ content: string } | undefined>; +} + +export function dispatchKeybindingAction( + action: KeybindingAction, + ctx: KeybindingDispatchContext, +): void { + const sendRaw = (data: string) => { + if (ctx.webSocketRef.current?.readyState === 1) { + ctx.webSocketRef.current.send(JSON.stringify({ type: "input", data })); + } + }; + + switch (action.type) { + case "copy": { + const selection = ctx.terminal.getSelection(); + if (selection) { + ctx.writeTextToClipboard(selection); + ctx.terminal.clearSelection(); + } + return; + } + case "paste": { + ctx.readTextFromClipboard().then((text) => { + if (text) ctx.terminal.paste(text); + }); + return; + } + case "sendControlCode": { + if (!action.controlCode) return; + const code = action.controlCode.toLowerCase().charCodeAt(0) - 96; + if (code >= 1 && code <= 26) sendRaw(String.fromCharCode(code)); + return; + } + case "sendText": { + sendRaw((action.text ?? "") + (action.appendEnter ? "\r" : "")); + return; + } + case "runSnippet": { + if (!action.snippetId) return; + ctx.getSnippetById(action.snippetId).then((snippet) => { + if (snippet) + sendRaw(snippet.content + (action.appendEnter !== false ? "\r" : "")); + }); + return; + } + } +} diff --git a/src/ui/lib/keybinding-match.ts b/src/ui/lib/keybinding-match.ts new file mode 100644 index 00000000..ca9cf979 --- /dev/null +++ b/src/ui/lib/keybinding-match.ts @@ -0,0 +1,17 @@ +import type { CustomKeybinding, KeyCombo } from "@/types/keybindings"; + +export function eventMatchesCombo(e: KeyboardEvent, combo: KeyCombo): boolean { + if (e.ctrlKey !== combo.ctrl) return false; + if (e.altKey !== combo.alt) return false; + if (e.shiftKey !== combo.shift) return false; + if (e.metaKey !== combo.meta) return false; + const actual = combo.isCode ? e.code : e.key.toLowerCase(); + return actual === combo.key; +} + +export function findMatchingKeybinding( + e: KeyboardEvent, + bindings: CustomKeybinding[], +): CustomKeybinding | undefined { + return bindings.find((kb) => eventMatchesCombo(e, kb.combo)); +} diff --git a/src/ui/lib/terminal-syntax-highlighter.ts b/src/ui/lib/terminal-syntax-highlighter.ts index b576514e..3799e59f 100644 --- a/src/ui/lib/terminal-syntax-highlighter.ts +++ b/src/ui/lib/terminal-syntax-highlighter.ts @@ -256,11 +256,11 @@ function highlightPlainText( text: string, activePatterns: HighlightPattern[], activeSgr: string, + protectedRanges: ProtectedRange[], ): string { if (text.length > MAX_LINE_LENGTH || !text.trim()) return text; const matches: MatchResult[] = []; - const protectedRanges = getProtectedRanges(text); for (const pattern of activePatterns) { pattern.regex.lastIndex = 0; @@ -381,13 +381,34 @@ function highlightLine( if (bare.length > MAX_LINE_LENGTH) return line; if (isShellPromptLine(bare)) return line; + // Compute protected ranges (e.g. SSH bracket headings) against the fully + // stripped line rather than per-ANSI-segment text. A colored prompt theme + // (e.g. "[user@host]") splits the heading across + // multiple plain-text segments, so matching per-segment would miss it and + // let a username like "warning" get wrongly highlighted as a log level. + const plainLine = bare.replace(STRIP_ANSI_RE, ""); + const lineProtectedRanges = getProtectedRanges(plainLine); + const segments = parseAnsiSegments(bare); + let plainOffset = 0; const result = segments - .map((s) => - s.isAnsi - ? s.content - : highlightPlainText(s.content, activePatterns, s.activeSgr ?? ""), - ) + .map((s) => { + if (s.isAnsi) return s.content; + const segmentStart = plainOffset; + plainOffset += s.content.length; + const localRanges = lineProtectedRanges + .map((r) => ({ + start: r.start - segmentStart, + end: r.end - segmentStart, + })) + .filter((r) => r.start < s.content.length && r.end > 0); + return highlightPlainText( + s.content, + activePatterns, + s.activeSgr ?? "", + localRanges, + ); + }) .join(""); return cr ? result + "\r" : result; diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index 9e33a4ab..8fc2d478 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -389,6 +389,45 @@ "noSavedServers": "No saved servers", "removeServer": "Remove" }, + "migrationNotice": { + "title": "Termix Desktop now runs standalone", + "body1": "This app used to connect straight to your Termix server. It's been reworked to run fully on its own, storing hosts and credentials locally so it keeps working offline. Two-way sync with a self-hosted Termix server is now optional.", + "body2": "Your existing hosts and credentials are still on {{url}}. Turn on Remote Sync and reconnect to that server to bring them back and keep both in sync going forward.", + "dismiss": "Not now", + "setUpSync": "Set Up Remote Sync" + }, + "remoteSync": { + "title": "Remote Sync", + "description": "Optionally connect this desktop app to a self-hosted Termix server to sync your hosts, credentials, and snippets across devices. The app always works fully offline whether or not you connect.", + "notConnected": "Not connected", + "connected": "Connected", + "connectedTo": "Connected to {{url}}", + "lastSynced": "Last synced {{time}}", + "neverSynced": "Never synced", + "syncError": "Sync error: {{message}}", + "needsReauth": "Sign-in expired", + "connectButton": "Connect to Server", + "disconnectButton": "Disconnect", + "syncNowButton": "Sync Now", + "syncing": "Syncing...", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "connectionTestFailed": "Could not reach a Termix server at that URL", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "savedServers": "Saved Servers", + "removeServer": "Remove", + "continueButton": "Continue", + "cancelButton": "Cancel", + "signInTitle": "Sign in to {{url}}", + "originTitle": "Connection Origin", + "originDescription": "Choose where SSH connections originate from by default. This can be overridden per host.", + "originLocal": "This device (local network)", + "originRemote": "Remote server", + "bannerReconnect": "Reconnect", + "bannerMessage": "Remote sync needs re-authentication" + }, "versionCheck": { "error": "Version Check Error", "checkFailed": "Failed to check for updates", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Override Credential Username", "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", "oidcUsernameHint": "Use $oidc.preferred_username to substitute your OIDC login name.", + "tailscaleUsernameHint": "This must be a Unix user your Tailscale identity is granted in the tailnet's SSH ACL, not necessarily root.", "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", "addKnock": "Add Port", @@ -650,6 +690,11 @@ "delayAfterMs": "Delay After (ms)", "useSocks5Proxy": "Use SOCKS5 Proxy", "useSocks5ProxyDesc": "Route connection through a proxy server", + "connectionOrigin": "Connection Origin", + "connectionOriginDesc": "Where this host's SSH connection originates from. Overrides the desktop app's global default.", + "connectionOriginDefault": "Use default", + "connectionOriginLocal": "This device (local network)", + "connectionOriginRemote": "Remote server", "proxyHost": "Proxy Host", "proxyPort": "Proxy Port", "proxyUsername": "Proxy Username", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Bright Cyan", "customThemeBrightWhite": "Bright White", "customThemeResetTooltip": "Reset to defaults", + "savedThemesLabel": "Saved Themes", + "saveAsGlobalTheme": "Save as Global Theme", + "saveGlobalThemeNamePrompt": "Enter a name for this theme", + "saveGlobalThemeSuccess": "Theme saved", + "saveGlobalThemeError": "Failed to save theme", + "applyGlobalThemeTooltip": "Apply this theme", + "deleteGlobalThemeTooltip": "Delete this theme", + "noSavedThemes": "No saved themes yet", "syntaxHighlightingLabel": "Syntax Highlighting", "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", "syntaxHighlightingCategories": "Highlight Categories", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", "enableSessionLogging": "Session Logging", "enableSessionLoggingDesc": "Record terminal session output for later review", + "allowSessionSharing": "Allow Session Sharing", + "allowSessionSharingDesc": "Let live sessions on this host be shared via link or with other users", "enableCommandHistory": "Command History", "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", "linkClickBehaviorLabel": "Link Click Behavior", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} imported", "proxmoxResultUpdated": "{{count}} updated", "proxmoxResultFailed": "{{count}} failed", + "proxmoxResultSkippedNoIp": "{{count}} skipped (no IP found)", "proxmoxImportComplete": "Proxmox import complete: {{summary}}", "proxmoxDiscoveryFailed": "Discovery failed", "proxmoxImportFailed": "Import failed", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Failed to save credential", "credentialNameRequired": "Please enter a name for the credential", "credentialAuthRequired": "Add a password, an SSH key, or both", + "createCredentialFromHostBtn": "Create Credential", + "createCredentialFromHostTitle": "Create Credential From Host", + "createCredentialFromHostDesc": "Create a reusable, shareable credential entry prefilled with this host's current username, password, and/or SSH key.", "backToHosts": "Back to Hosts", "backToCredentials": "Back to Credentials", "pinned": "Pinned", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Use / to separate levels and create nested folders.", "folderColor": "Color", "folderIcon": "Icon", + "folderCredential": "Credential", + "folderCredentialNone": "No credential assigned", + "folderCredentialHint": "Hosts in this folder that use \"Stored credential\" auth without their own credential selected will inherit this one.", "folderPreview": "Preview", "folderNameFallback": "Untitled folder", "createFolderButton": "Create folder", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tags", "shareHost": "Share Host", "shareHostTitle": "Share: {{name}}", + "shareFolder": "Share Folder", + "shareFolderTitle": "Share folder: {{name}}", + "folderSharedSuccessfully": "Shared {{count}} host(s) in folder", + "failedToShareFolder": "Failed to share folder", "sharing": { "loadError": "Failed to load sharing data. Please try again.", "shareWithSection": "Share with", @@ -1221,6 +1287,7 @@ "shareWithCount": "Share ({{count}})", "currentAccess": "Current access", "noAccessEntries": "This host has not been shared yet", + "folderShareSummary": "Shared {{shared}} of {{total}} host(s) in this folder", "grantedBy": "Granted by", "expires": "Expires", "expired": "Expired", @@ -1242,6 +1309,8 @@ "authMethod": "Auth Method", "authTypeDirect": "Direct", "authTypeCredential": "Credential", + "authTypeNone": "None", + "authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.", "selectCredential": "Select a credential...", "connectionSettings": "Connection Settings", "displaySettings": "Display Settings", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP Port", "username": "Username", "password": "Password", + "passwordSaved": "Password saved, type to change", "domain": "Domain", "securityMode": "Security Mode", "colorDepth": "Color Depth", @@ -1424,7 +1494,60 @@ "expiresIn": "Expires in {{duration}}", "search": "Search connections...", "noSearchResults": "No connections match your search", - "rename": "Rename session" + "rename": "Rename session", + "sectionSharedWithMe": "Shared with me", + "sharedBy": "Shared by {{username}}", + "join": "Join", + "sharedSessionLabel": "{{hostName}} (shared)" + }, + "sessionSharing": { + "guestView": { + "loading": "Connecting to shared session...", + "linkInvalid": "This share link is invalid, expired, or has been revoked", + "rateLimited": "Too many attempts, please try again shortly", + "sessionEnded": "This session has ended", + "readOnlyBadge": "View only" + }, + "modalTitle": "Share session", + "shareButton": "Share", + "notReadyToShare": "Session is not ready to share yet", + "modeTab": { + "link": "Link", + "user": "User" + }, + "linkModeDescription": "Anyone with this link can join, no account required.", + "userModeDescription": "Share with a specific user who already has access to this host. If they do not have access yet, share the host with them first or use a link instead. Once shared, the session appears in their Connections tab.", + "permissionLevel": { + "label": "Permission level", + "readOnly": "Read-only", + "readOnlyDescription": "Can watch the session live but cannot type or interact.", + "readWrite": "Read-write", + "readWriteDescription": "Can type and interact with the session just like the owner." + }, + "expiryLabel": "Link expiry", + "createLinkButton": "Create link", + "createShareButton": "Share with user", + "searchUsersPlaceholder": "Search users...", + "noUsersFound": "No users found", + "linkCreated": "Share link created", + "linkCopied": "Link copied to clipboard", + "copyLink": "Copy link", + "shareCreated": "Session shared. It will appear in their Connections tab.", + "shareFailed": "Failed to create share", + "userLacksHostAccess": "That user does not have access to this host yet. Share the host with them first, or use a link instead.", + "activeShares": "Active shares", + "noActiveShares": "No active shares for this session", + "revoke": "Revoke", + "revokeConfirmTitle": "Revoke this share?", + "revokeConfirmDescription": "Anyone using this share will lose access immediately.", + "revoked": "Share revoked", + "revokeFailed": "Failed to revoke share", + "joinCount": "{{count}} join", + "joinCount_other": "{{count}} joins", + "expiresAt": "Expires {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "User: {{username}}", + "loadSharesFailed": "Failed to load active shares" }, "guacamole": { "connecting": "Connecting to {{type}} session...", @@ -1436,6 +1559,9 @@ "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.", + "credentialPromptTitle": "Enter RDP Credentials", + "credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.", + "connect": "Connect", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Reconnect Session", "collapse": "Collapse toolbar", "expand": "Expand toolbar", - "dragHandle": "Drag to reposition" + "dragHandle": "Drag to reposition", + "switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)", + "switchToTouch": "Switch to touch mode (tap directly where you want to click)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Two-Factor Authentication Required", "totpCodeLabel": "Verification Code", "totpVerify": "Verify", + "mfaPromptRequired": "Authentication Required", + "mfaPushRequired": "Push Authentication Required", + "mfaMenuPlaceholder": "Enter your response", + "mfaWaitingApproval": "Waiting for approval on your device...", + "mfaSendRequest": "Send Request", "warpgateAuthRequired": "Warpgate Authentication Required", "warpgateSecurityKey": "Security Key", "warpgateAuthUrl": "Authentication URL", @@ -2277,7 +2410,8 @@ "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" + "failedToLoadServer": "Failed to load server", + "remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." }, "messages": { "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Failed to save SSL settings", "sslRequiresDomain": "Domain and email are required", "sslInfoNote": "After issuing a certificate, enable SSL in your environment variables (ENABLE_SSL=true) and restart Termix.", + "sslManualOption": "Manual (upload certificate)", + "sslManualCert": "Certificate (PEM)", + "sslManualCertPlaceholder": "-----BEGIN CERTIFICATE-----", + "sslManualKey": "Private Key (PEM)", + "sslManualKeyPlaceholder": "-----BEGIN PRIVATE KEY-----", + "sslManualDesc": "Paste your existing certificate and private key, including a full chain if required by your CA.", + "sslManualUpload": "Upload & Install Certificate", + "sslManualUploadLoading": "Uploading certificate...", + "sslManualUploadSuccess": "Certificate uploaded and installed successfully", + "sslManualUploadFailed": "Certificate upload failed", + "sslManualRequiresFields": "Certificate and private key are required", "auditLogTotal": "{{total}} total entries", "auditLogEmpty": "No audit log entries found", "auditLogSuccess": "Success", @@ -2672,6 +2817,12 @@ "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", + "analyticsEnabled": "Share Anonymous Usage Statistics", + "analyticsEnabledDesc": "Sends an anonymous daily count of users, hosts, and feature usage to help improve Termix. No personal data or connection details are ever included.", + "updateAnalyticsFailed": "Failed to update analytics setting", + "sessionSharingGloballyEnabled": "Allow Session Sharing", + "sessionSharingGloballyEnabledDesc": "Allow live terminal, RDP, VNC, and Telnet sessions to be shared instance-wide. Overrides every per-host sharing toggle when disabled.", + "updateSessionSharingFailed": "Failed to update session sharing setting", "sessionTimeout": "Session Timeout", "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", @@ -3119,6 +3270,47 @@ "executionSuccess": "Success", "executionFailed": "Failed" }, + "keybindings": { + "title": "Keyboard Shortcuts", + "description": "Customize terminal copy, paste, and control shortcuts, or bind keys to send text or run a snippet.", + "defaultsHeading": "Built-in Shortcuts", + "customHeading": "Custom Shortcuts", + "addBinding": "Add Shortcut", + "addBindingTitle": "Add Shortcut", + "editBindingTitle": "Edit Shortcut", + "loading": "Loading...", + "noCustomBindings": "No custom shortcuts yet", + "defaultBadge": "Default", + "customizedBadge": "Customized", + "customize": "Customize", + "resetToDefault": "Reset to default", + "resetAllToDefaults": "Reset all to defaults", + "close": "Close", + "cancel": "Cancel", + "saveBinding": "Save Shortcut", + "saveError": "Failed to save keyboard shortcuts", + "pressKeysToRecord": "Key combination", + "pressKeysPlaceholder": "Click and press keys", + "recording": "Press keys...", + "comboRequiredError": "Press a key combination first", + "textRequiredError": "Enter the text to send", + "controlCodeRequiredError": "Enter a single letter for the control code", + "snippetRequiredError": "Select a snippet", + "conflictWarning": "This combination is already bound to {{combo}}. Saving will make both fire.", + "actionLabel": "Action", + "actionCopy": "Copy selection", + "actionPaste": "Paste", + "actionSendControlCode": "Send Ctrl+letter signal", + "actionSendText": "Send text", + "actionRunSnippet": "Run existing snippet", + "controlCodeLabel": "Letter", + "textLabel": "Text to send", + "appendEnterLabel": "Press Enter after sending", + "snippetLabel": "Snippet", + "selectSnippetPlaceholder": "Select a snippet", + "orphanedSnippetWarning": "snippet not found", + "clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers." + }, "userProfile": { "donateTitle": "Keep Termix alive", "donateDescription": "Termix is free, self-hosted, and built by only a few people in their spare time. If it's saved you time or money, a crypto donation helps keep it going.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Appearance", "sectionSecurity": "Security", "sectionApiKeys": "API Keys", + "sectionData": "Data", "sectionC2sTunnels": "C2S Tunnels", "usernameLabel": "Username", "roleLabel": "Role", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Command Autocomplete", "commandAutocompleteDesc": "Show autocomplete while typing", + "keyboardShortcuts": "Keyboard Shortcuts", + "keyboardShortcutsDescription": "Customize terminal copy, paste, and command shortcuts", + "manageShortcuts": "Manage", "terminalLinkBehavior": "Terminal Link Click", "terminalLinkBehaviorDesc": "Default behavior when clicking links in the terminal", "historyTracking": "History Tracking", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Include your key in the", "apiKeyUsageHintHeader": "header.", "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "exportData": "Export My Data", + "exportDataDesc": "Download a backup of your hosts, credentials, and settings to transfer to another device", + "export": "Export", + "exporting": "Exporting...", + "importData": "Import My Data", + "importDataDesc": "Restore your hosts, credentials, and settings from a .sqlite backup file", + "importDataSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "exportSuccess": "Data exported successfully", + "exportFailed": "Data export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", "roleUser": "User", "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Paste", "editQuickKeys": "Edit quick keys", "quickKeysTitle": "Quick Keys", "quickKeysDesc": "Tap × to remove. Supports up to 8 characters.", diff --git a/src/ui/locales/translated/af_ZA.json b/src/ui/locales/translated/af_ZA.json index c1cc6651..42363f70 100644 --- a/src/ui/locales/translated/af_ZA.json +++ b/src/ui/locales/translated/af_ZA.json @@ -389,6 +389,45 @@ "noSavedServers": "Geen gestoorde bedieners nie", "removeServer": "Verwyder" }, + "migrationNotice": { + "title": "Termix Desktop loop nou alleenstaande", + "body1": "Hierdie toepassing het voorheen direk aan jou Termix-bediener gekoppel. Dit is herontwerp om volledig op sy eie te loop, en stoor gashere en geloofsbriewe plaaslik sodat dit vanlyn kan bly werk. Tweerigting-sinchronisasie met 'n self-gehoste Termix-bediener is nou opsioneel.", + "body2": "Jou bestaande gashere en geloofsbriewe is steeds op {{url}}. Skakel Afstandsinchronisasie aan en koppel weer aan daardie bediener om hulle terug te bring en albei vorentoe gesinchroniseer te hou.", + "dismiss": "Nie nou nie", + "setUpSync": "Stel afstandsinchronisasie op" + }, + "remoteSync": { + "title": "Afstandsinchronisasie", + "description": "Koppel opsioneel hierdie lessenaar-app aan 'n self-gehoste Termix-bediener om jou gashere, geloofsbriewe en brokkies oor toestelle te sinkroniseer. Die app werk altyd ten volle vanlyn, of jy nou koppel of nie.", + "notConnected": "Nie gekoppel nie", + "connected": "Verbonde", + "connectedTo": "Gekoppel aan {{url}}", + "lastSynced": "Laas gesinkroniseer {{time}}", + "neverSynced": "Nooit gesinkroniseer nie", + "syncError": "Sinkronisasiefout: {{message}}", + "needsReauth": "Aanmelding het verval", + "connectButton": "Koppel aan bediener", + "disconnectButton": "Ontkoppel", + "syncNowButton": "Sinkroniseer nou", + "syncing": "Sinkroniseer...", + "serverUrl": "Bediener-URL", + "enterServerUrl": "Voer asseblief 'n bediener-URL in", + "mustIncludeProtocol": "Bediener-URL moet met http:// of https:// begin", + "connectionTestFailed": "Kon nie 'n Termix-bediener by daardie URL bereik nie", + "allowInvalidCertificate": "Laat ongeldige sertifikaat toe", + "allowInvalidCertificateDesc": "Gebruik slegs vir vertroude selfgehoste bedieners met selfgetekende of IP-adres sertifikate.", + "savedServers": "Gestoorde bedieners", + "removeServer": "Verwyder", + "continueButton": "Gaan voort", + "cancelButton": "Kanselleer", + "signInTitle": "Meld aan by {{url}}", + "originTitle": "Verbindingsoorsprong", + "originDescription": "Kies waar SSH-verbindings standaard vandaan kom. Dit kan per gasheer oorskryf word.", + "originLocal": "Hierdie toestel (plaaslike netwerk)", + "originRemote": "Afstandbediener", + "bannerReconnect": "Herkoppel", + "bannerMessage": "Afstandsinchronisasie benodig herverifikasie" + }, "versionCheck": { "error": "Weergawekontrolefout", "checkFailed": "Kon nie vir opdaterings kyk nie", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Oorskryf geloofsbriewe gebruikersnaam", "overrideCredentialUsernameDesc": "Gebruik die gebruikersnaam wat hierbo gespesifiseer is in plaas van die gebruikersnaam van die geloofsbriewe", "oidcUsernameHint": "Gebruik $oidc.preferred_username om jou OIDC-aanmeldnaam te vervang.", + "tailscaleUsernameHint": "Dit moet 'n Unix-gebruiker wees aan wie jou Tailscale-identiteit in die tailnet se SSH ACL toegestaan word, nie noodwendig root nie.", "jumpHostChain": "Springgasheerketting", "portKnocking": "Port Knocking", "addKnock": "Voeg poort by", @@ -650,6 +690,11 @@ "delayAfterMs": "Vertraging na (ms)", "useSocks5Proxy": "Gebruik SOCKS5 Proxy", "useSocks5ProxyDesc": "Roeteer verbinding deur 'n proxy-bediener", + "connectionOrigin": "Verbindingsoorsprong", + "connectionOriginDesc": "Waar hierdie gasheer se SSH-verbinding vandaan kom. Oorskryf die rekenaarprogram se globale verstekwaarde.", + "connectionOriginDefault": "Gebruik verstekwaarde", + "connectionOriginLocal": "Hierdie toestel (plaaslike netwerk)", + "connectionOriginRemote": "Afstandbediener", "proxyHost": "Proxy-gasheer", "proxyPort": "Proxy-poort", "proxyUsername": "Proxy-gebruikersnaam", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Helder Siaan", "customThemeBrightWhite": "Helderwit", "customThemeResetTooltip": "Stel terug na verstekwaardes", + "savedThemesLabel": "Gestoorde temas", + "saveAsGlobalTheme": "Stoor as globale tema", + "saveGlobalThemeNamePrompt": "Voer 'n naam vir hierdie tema in", + "saveGlobalThemeSuccess": "Tema gestoor", + "saveGlobalThemeError": "Kon nie tema stoor nie", + "applyGlobalThemeTooltip": "Pas hierdie tema toe", + "deleteGlobalThemeTooltip": "Vee hierdie tema uit", + "noSavedThemes": "Geen gestoorde temas nog nie", "syntaxHighlightingLabel": "Sintaksis-uitlig", "syntaxHighlightingDesc": "Kleur terminaaluitvoer in (foute, paaie, IP's, tydstempels)", "syntaxHighlightingCategories": "Merk kategorieë uit", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Begin of koppel outomaties aan tmux-sessie", "enableSessionLogging": "Sessie-logging", "enableSessionLoggingDesc": "Neem terminaalsessie-uitvoer op vir latere hersiening", + "allowSessionSharing": "Laat Sessiedeling Toe", + "allowSessionSharingDesc": "Laat regstreekse sessies op hierdie gasheer via skakel of met ander gebruikers gedeel word", "enableCommandHistory": "Opdraggeskiedenis", "enableCommandHistoryDesc": "Rekordopdragte word in hierdie terminaal uitgevoer vir geskiedenis en outovoltooiing", "linkClickBehaviorLabel": "Skakelklikgedrag", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} ingevoer", "proxmoxResultUpdated": "{{count}} opgedateer", "proxmoxResultFailed": "{{count}} het misluk", + "proxmoxResultSkippedNoIp": "{{count}} oorgeslaan (geen IP gevind nie)", "proxmoxImportComplete": "Proxmox-invoer voltooi: {{summary}}", "proxmoxDiscoveryFailed": "Ontdekking het misluk", "proxmoxImportFailed": "Invoer het misluk", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Kon nie geloofsbriewe stoor nie", "credentialNameRequired": "Voer asseblief 'n naam vir die geloofsbrief in", "credentialAuthRequired": "Voeg 'n wagwoord, 'n SSH-sleutel of albei by", + "createCredentialFromHostBtn": "Skep geloofsbriewe", + "createCredentialFromHostTitle": "Skep geloofsbriewe vanaf gasheer", + "createCredentialFromHostDesc": "Skep 'n herbruikbare, deelbare geloofsbriewe-inskrywing wat vooraf ingevul is met hierdie gasheer se huidige gebruikersnaam, wagwoord en/of SSH-sleutel.", "backToHosts": "Terug na gashere", "backToCredentials": "Terug na geloofsbriewe", "pinned": "Vasgepen", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Gebruik / om vlakke te skei en geneste vouers te skep.", "folderColor": "Kleur", "folderIcon": "Ikoon", + "folderCredential": "Geloofsbrief", + "folderCredentialNone": "Geen geloofsbriewe toegeken nie", + "folderCredentialHint": "Gashere in hierdie vouer wat \"Gestoorde geloofsbriewe\"-magtiging gebruik sonder dat hul eie geloofsbriewe gekies is, sal hierdie een erf.", "folderPreview": "Voorskou", "folderNameFallback": "Naamlose vouer", "createFolderButton": "Skep vouer", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Etikette", "shareHost": "Deel gasheer", "shareHostTitle": "Deel: {{name}}", + "shareFolder": "Deel vouer", + "shareFolderTitle": "Deel vouer: {{name}}", + "folderSharedSuccessfully": "Gedeelde {{count}} gasheer(s) in vouer", + "failedToShareFolder": "Kon nie vouer deel nie", "sharing": { "loadError": "Kon nie deeldata laai nie. Probeer asseblief weer.", "shareWithSection": "Deel met", @@ -1221,6 +1287,7 @@ "shareWithCount": "Deel ({{count}})", "currentAccess": "Huidige toegang", "noAccessEntries": "Hierdie gasheer is nog nie gedeel nie", + "folderShareSummary": "Gedeelde {{shared}} van {{total}} gasheer(s) in hierdie vouer", "grantedBy": "Toegeken deur", "expires": "Vervaldatum", "expired": "Verstryk", @@ -1242,6 +1309,8 @@ "authMethod": "Magtigingsmetode", "authTypeDirect": "Direkte", "authTypeCredential": "Geloofsbrief", + "authTypeNone": "Geen", + "authTypeNoneDesc": "Geen aanmeldingsbesonderhede word gestoor nie. Jy sal elke keer as jy koppel vir 'n gebruikersnaam en wagwoord gevra word; hulle word nie gestoor nie.", "selectCredential": "Kies 'n geloofsbrief...", "connectionSettings": "Verbindingsinstellings", "displaySettings": "Skerminstellings", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-poort", "username": "Gebruikersnaam", "password": "Wagwoord", + "passwordSaved": "Wagwoord gestoor, tik om te verander", "domain": "Domein", "securityMode": "Sekuriteitsmodus", "colorDepth": "Kleurdiepte", @@ -1424,7 +1494,60 @@ "expiresIn": "Verval oor {{duration}}", "search": "Soek verbindings...", "noSearchResults": "Geen verbindings stem ooreen met jou soektog nie", - "rename": "Hernoem sessie" + "rename": "Hernoem sessie", + "sectionSharedWithMe": "Met my gedeel", + "sharedBy": "Gedeel deur {{username}}", + "join": "Sluit aan", + "sharedSessionLabel": "{{hostName}} (gedeel)" + }, + "sessionSharing": { + "guestView": { + "loading": "Verbind met gedeelde sessie...", + "linkInvalid": "Hierdie deelskakel is ongeldig, het verval of is herroep", + "rateLimited": "Te veel pogings, probeer asseblief weer binnekort", + "sessionEnded": "Hierdie sessie is verby", + "readOnlyBadge": "Slegs kyk" + }, + "modalTitle": "Deel sessie", + "shareButton": "Deel", + "notReadyToShare": "Sessie is nog nie gereed om te deel nie", + "modeTab": { + "link": "Skakel", + "user": "Gebruiker" + }, + "linkModeDescription": "Enigiemand met hierdie skakel kan aansluit, geen rekening nodig nie.", + "userModeDescription": "Deel met 'n spesifieke gebruiker wat reeds toegang tot hierdie gasheer het. Indien hulle nog nie toegang het nie, deel die gasheer eers met hulle of gebruik eerder 'n skakel. Sodra dit gedeel is, verskyn die sessie in hul Verbindings-oortjie.", + "permissionLevel": { + "label": "Toestemmingsvlak", + "readOnly": "Leesalleen", + "readOnlyDescription": "Kan die sessie regstreeks kyk, maar kan nie tik of interaksie hê nie.", + "readWrite": "Lees-skryf", + "readWriteDescription": "Kan tik en met die sessie interaksie hê net soos die eienaar." + }, + "expiryLabel": "Skakelvervaldatum", + "createLinkButton": "Skep skakel", + "createShareButton": "Deel met gebruiker", + "searchUsersPlaceholder": "Soek gebruikers...", + "noUsersFound": "Geen gebruikers gevind nie", + "linkCreated": "Deel skakel geskep", + "linkCopied": "Skakel gekopieer na knipbord", + "copyLink": "Kopieer skakel", + "shareCreated": "Sessie gedeel. Dit sal in hul Verbindings-oortjie verskyn.", + "shareFailed": "Kon nie deel skep nie", + "userLacksHostAccess": "Daardie gebruiker het nog nie toegang tot hierdie gasheer nie. Deel die gasheer eers met hulle, of gebruik eerder 'n skakel.", + "activeShares": "Aktiewe aandele", + "noActiveShares": "Geen aktiewe delings vir hierdie sessie nie", + "revoke": "Herroep", + "revokeConfirmTitle": "Herroep hierdie deling?", + "revokeConfirmDescription": "Enigiemand wat hierdie deel gebruik, sal onmiddellik toegang verloor.", + "revoked": "Deel herroep", + "revokeFailed": "Kon nie deel herroep nie", + "joinCount": "{{count}} sluit aan", + "joinCount_other": "{{count}} sluit aan", + "expiresAt": "Vervaldatum {{date}}", + "linkShareBadge": "Skakel", + "userShareBadge": "Gebruiker: {{username}}", + "loadSharesFailed": "Kon nie aktiewe delings laai nie" }, "guacamole": { "connecting": "Verbind met {{type}} sessie...", @@ -1436,6 +1559,9 @@ "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.", + "credentialPromptTitle": "Voer RDP-aanbewyse in", + "credentialPromptDescription": "Hierdie gasheer is ingestel om vir geloofsbriewe te vra tydens verbinding. Hulle word slegs vir hierdie sessie gebruik en word nie gestoor nie.", + "connect": "Verbind", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Herkoppel Sessie", "collapse": "Vou nutsbalk in", "expand": "Vou die nutsbalk uit", - "dragHandle": "Sleep om te herposisioneer" + "dragHandle": "Sleep om te herposisioneer", + "switchToTrackpad": "Skakel oor na raakvlakmodus (sleep om wyser te beweeg, tik om te klik)", + "switchToTouch": "Skakel oor na raakmodus (tik direk waar jy wil klik)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Tweefaktor-verifikasie vereis", "totpCodeLabel": "Verifikasiekode", "totpVerify": "Verifieer", + "mfaPromptRequired": "Verifikasie Vereis", + "mfaPushRequired": "Stoot-verifikasie vereis", + "mfaMenuPlaceholder": "Voer jou antwoord in", + "mfaWaitingApproval": "Wag vir goedkeuring op jou toestel...", + "mfaSendRequest": "Stuur versoek", "warpgateAuthRequired": "Warpgate-verifikasie vereis", "warpgateSecurityKey": "Sekuriteitsleutel", "warpgateAuthUrl": "Verifikasie-URL", @@ -2277,7 +2410,8 @@ "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" + "failedToLoadServer": "Kon nie bediener laai nie", + "remoteServerRequired": "Afstandbediener word benodig. Koppel 'n afstandbediener in Instellings om hierdie verbindingstipe te gebruik." }, "messages": { "registrationDisabled": "Nuwe rekeningregistrasie is tans deur 'n administrateur gedeaktiveer. Meld asseblief aan of kontak 'n administrateur.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Kon nie SSL-instellings stoor nie", "sslRequiresDomain": "Domein en e-posadres word vereis", "sslInfoNote": "Nadat u 'n sertifikaat uitgereik het, aktiveer SSL in u omgewingveranderlikes (ENABLE_SSL=true) en herbegin Termix.", + "sslManualOption": "Handleiding (laai sertifikaat op)", + "sslManualCert": "Sertifikaat (PEM)", + "sslManualCertPlaceholder": "-----BEGIN SERTIFIKAAT-----", + "sslManualKey": "Privaatsleutel (PEM)", + "sslManualKeyPlaceholder": "-----BEGIN PRIVAAT SLEUTEL-----", + "sslManualDesc": "Plak jou bestaande sertifikaat en privaat sleutel, insluitend 'n volledige ketting indien vereis deur jou CA.", + "sslManualUpload": "Laai sertifikaat op en installeer dit", + "sslManualUploadLoading": "Laai tans sertifikaat op...", + "sslManualUploadSuccess": "Sertifikaat suksesvol opgelaai en geïnstalleer", + "sslManualUploadFailed": "Sertifikaatoplaai het misluk", + "sslManualRequiresFields": "Sertifikaat en privaat sleutel word vereis", "auditLogTotal": "{{total}} totale inskrywings", "auditLogEmpty": "Geen ouditlogboekinskrywings gevind nie", "auditLogSuccess": "Sukses", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Opdraggeskiedenis", "commandHistoryEnabledDesc": "Laat die opname van opdraggeskiedenis toe. Wanneer dit gedeaktiveer is, word geskiedenis nie gestoor nie, ongeag die instellings per gasheer.", "updateCommandHistoryFailed": "Kon nie die instelling vir opdraggeskiedenis opdateer nie", + "analyticsEnabled": "Deel Anonieme Gebruiksstatistieke", + "analyticsEnabledDesc": "Stuur 'n anonieme daaglikse telling van gebruikers, gashere en funksiegebruik om Termix te help verbeter. Geen persoonlike data of verbindingsbesonderhede word ooit ingesluit nie.", + "updateAnalyticsFailed": "Kon nie analitiese instelling opdateer nie", + "sessionSharingGloballyEnabled": "Laat Sessiedeling Toe", + "sessionSharingGloballyEnabledDesc": "Laat lewendige terminaal-, RDP-, VNC- en Telnet-sessies toe om instansiewyd gedeel te word. Oorskryf elke deelskakelaar per gasheer wanneer dit gedeaktiveer is.", + "updateSessionSharingFailed": "Kon nie sessiedelinginstelling opdateer nie", "sessionTimeout": "Sessie-tydsbeperking", "hours": "ure", "sessionTimeoutRange": "Min 1 uur · Maks 720 uur", @@ -3119,6 +3270,47 @@ "executionSuccess": "Sukses", "executionFailed": "Misluk" }, + "keybindings": { + "title": "Sleutelbordkortpaaie", + "description": "Pasmaak terminaalkopieer-, plak- en beheerkortpaaie, of bind sleutels om teks te stuur of 'n brokkie uit te voer.", + "defaultsHeading": "Ingeboude kortpaaie", + "customHeading": "Pasgemaakte kortpaaie", + "addBinding": "Voeg kortpad by", + "addBindingTitle": "Voeg kortpad by", + "editBindingTitle": "Wysig Kortpad", + "loading": "Laai tans...", + "noCustomBindings": "Geen persoonlike kortpaaie nog nie", + "defaultBadge": "Standaard", + "customizedBadge": "Aangepas", + "customize": "Pasmaak", + "resetToDefault": "Stel terug na verstekwaarde", + "resetAllToDefaults": "Stel alles terug na verstekwaardes", + "close": "Maak toe", + "cancel": "Kanselleer", + "saveBinding": "Stoor Kortpad", + "saveError": "Kon nie sleutelbordkortpaaie stoor nie", + "pressKeysToRecord": "Sleutelkombinasie", + "pressKeysPlaceholder": "Klik en druk sleutels", + "recording": "Druk sleutels...", + "comboRequiredError": "Druk eers 'n sleutelkombinasie", + "textRequiredError": "Voer die teks in om te stuur", + "controlCodeRequiredError": "Voer 'n enkele letter vir die beheerkode in", + "snippetRequiredError": "Kies 'n uittreksel", + "conflictWarning": "Hierdie kombinasie is reeds gebind aan {{combo}}. As jy dit stoor, sal albei aktiveer.", + "actionLabel": "Aksie", + "actionCopy": "Kopieer seleksie", + "actionPaste": "Plak", + "actionSendControlCode": "Stuur Ctrl+letter sein", + "actionSendText": "Stuur SMS", + "actionRunSnippet": "Voer bestaande brokkie uit", + "controlCodeLabel": "Brief", + "textLabel": "SMS om te stuur", + "appendEnterLabel": "Druk Enter na stuur", + "snippetLabel": "Brokkie", + "selectSnippetPlaceholder": "Kies 'n uittreksel", + "orphanedSnippetWarning": "brokkie nie gevind nie", + "clipboardPermissionNote": "Kan in sommige blaaiers met die eerste gebruik vir knipbordtoestemming vra." + }, "userProfile": { "donateTitle": "Hou Termix lewendig", "donateDescription": "Termix is gratis, self-gehost en gebou deur slegs 'n paar mense in hul vrye tyd. As dit jou tyd of geld bespaar het, help 'n kripto-skenking om dit aan die gang te hou.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Voorkoms", "sectionSecurity": "Sekuriteit", "sectionApiKeys": "API-sleutels", + "sectionData": "Data", "sectionC2sTunnels": "C2S-tonnels", "usernameLabel": "Gebruikersnaam", "roleLabel": "Rol", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminaal", "commandAutocomplete": "Opdrag Outomatiese Voltooiing", "commandAutocompleteDesc": "Wys outovoltooiing terwyl jy tik", + "keyboardShortcuts": "Sleutelbordkortpaaie", + "keyboardShortcutsDescription": "Pas terminale kopieer-, plak- en opdragkortpaaie aan", + "manageShortcuts": "Bestuur", "terminalLinkBehavior": "Klik op die terminaalskakel", "terminalLinkBehaviorDesc": "Standaardgedrag wanneer skakels in die terminaal geklik word", "historyTracking": "Geskiedenisopsporing", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Sluit jou sleutel in die", "apiKeyUsageHintHeader": "opskrif.", "apiKeyPermissionsHint": "Sleutels erf die regte van die skeppergebruiker.", + "exportData": "Voer my data uit", + "exportDataDesc": "Laai 'n rugsteun van jou gashere, geloofsbriewe en instellings af om na 'n ander toestel oor te dra", + "export": "Uitvoer", + "exporting": "Uitvoer tans...", + "importData": "Voer my data in", + "importDataDesc": "Herstel jou gashere, geloofsbriewe en instellings vanaf 'n .sqlite-rugsteunlêer", + "importDataSelected": "Gekies: {{name}}", + "selectFile": "Kies Lêer", + "changeFile": "Verandering", + "import": "Invoer", + "importing": "Voer tans in...", + "exportSuccess": "Data suksesvol uitgevoer", + "exportFailed": "Data-uitvoer het misluk", + "importSelectFile": "Kies asseblief eers 'n lêer", + "importCompleted": "Invoer voltooi: {{total}} items ingevoer, {{skipped}} oorgeslaan", + "importFailed": "Invoer het misluk: {{error}}", "roleUser": "Gebruiker", "authMethodDual": "Dubbele Magtiging", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "Bladsy Op", "pageDown": "PgDn", "delete": "Del", + "paste": "Plak", "editQuickKeys": "Wysig vinnige sleutels", "quickKeysTitle": "Vinnige sleutels", "quickKeysDesc": "Tik × om te verwyder. Ondersteun tot 8 karakters.", diff --git a/src/ui/locales/translated/ar_SA.json b/src/ui/locales/translated/ar_SA.json index 2b023682..67d0d554 100644 --- a/src/ui/locales/translated/ar_SA.json +++ b/src/ui/locales/translated/ar_SA.json @@ -1,728 +1,773 @@ { "termixId": { - "title": "معرف Termix", - "loadFailed": "فشل تحميل معرّف Termix", - "claimTitle": "احصل على معرف Termix الخاص بك", - "claimIntro": "اختر اسم مستخدم فريدًا. سيتم نشر مفاتيح SSH العامة الخاصة بك على عنوان URL عام يمكنك إضافته إلى ملف authorized_keys الخاص بأي خادم.", - "handleLabel": "مقبض", - "handlePlaceholder": "أليس", + "title": "Termix ID", + "loadFailed": "فشل تحميل Termix ID", + "claimTitle": "احصل على Termix ID الخاص بك", + "claimIntro": "اختر معرّفًا فريدًا. سيتم نشر مفاتيح SSH العامة الخاصة بك في رابط عام يمكنك إضافته إلى ملف authorized_keys لأي خادم.", + "handleLabel": "المعرّف", + "handlePlaceholder": "alice", "checking": "جارٍ التحقق…", "available": "متاح", - "taken": "مأخوذة", - "invalidHandle": "الأحرف الصغيرة والأرقام والشرطة (-) فقط", + "taken": "مستخدم بالفعل", + "invalidHandle": "أحرف صغيرة، أرقام، - و _ فقط", "descriptionLabel": "الوصف (اختياري)", - "descriptionPlaceholder": "مفاتيح الكمبيوتر المحمول والهاتف الخاص بالعمل", - "create": "إنشاء TermixID", - "created": "تم إنشاء معرف Termix", - "createFailed": "فشل إنشاء معرف Termix", - "deleteConfirm": "هل تريد حذف معرّف Termix الخاص بك وجميع المفاتيح المنشورة؟ ستحتفظ الخوادم التي قمت بتوفيرها بالمفاتيح حتى تقوم بإزالتها يدويًا.", - "deleted": "تم حذف معرف Termix", - "deleteFailed": "فشل حذف معرّف Termix", - "copyFailed": "فشلت عملية النسخ", - "resolverUrlLabel": "عنوان URL للمُحلِّل العام", - "provisionLabel": "قم بتوفير خادم", - "publishTitle": "انشر مفتاحًا عامًا", - "generate": "يولد", - "generateTooltip": "قم بإنشاء زوج مفاتيح Ed25519 — انشر المفتاح العام وقم بتنزيل المفتاح الخاص إلى جهازك", - "saveToVault": "حفظ إلى بيانات الاعتماد", + "descriptionPlaceholder": "مفاتيح حاسوب العمل والهاتف", + "create": "إنشاء Termix ID", + "created": "تم إنشاء Termix ID", + "createFailed": "فشل إنشاء Termix ID", + "deleteConfirm": "حذف Termix ID وجميع المفاتيح المنشورة؟ ستحتفظ الخوادم التي قمت بتجهيزها بالمفاتيح حتى تقوم بإزالتها يدويًا.", + "deleted": "تم حذف Termix ID", + "deleteFailed": "فشل حذف Termix ID", + "copyFailed": "فشل النسخ", + "resolverUrlLabel": "رابط المُحلل العام", + "provisionLabel": "تجهيز خادم", + "publishTitle": "نشر مفتاح عام", + "generate": "توليد", + "generateTooltip": "توليد زوج مفاتيح Ed25519 — ينشر المفتاح العام ويُنزّل المفتاح الخاص إلى جهازك", + "saveToVault": "حفظ في بيانات الاعتماد", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", - "labelPlaceholder": "ملصق (اختياري)", - "add": "يضيف", - "importFromCredential": "أو الاستيراد من بيانات اعتماد مخزنة", + "labelPlaceholder": "وسم (اختياري)", + "add": "إضافة", + "importFromCredential": "أو استيراد من بيانات اعتماد مخزّنة", "keyPublished": "تم نشر المفتاح", "addKeyFailed": "فشل إضافة المفتاح", - "generatedSaved": "تم إنشاء زوج المفاتيح وحفظه في خزنة بيانات الاعتماد الخاصة بك. كما تم تنزيل المفتاح الخاص.", - "generatedOnly": "تم إنشاء زوج المفاتيح — تم تنزيل المفتاح الخاص (يظهر مرة واحدة فقط).", - "generateFailed": "فشل في إنشاء المفتاح", + "generatedSaved": "تم توليد زوج المفاتيح وحفظه في مخزن بيانات الاعتماد. كما تم تنزيل المفتاح الخاص.", + "generatedOnly": "تم توليد زوج المفاتيح — تم تنزيل المفتاح الخاص (يُعرض مرة واحدة فقط).", + "generateFailed": "فشل توليد المفتاح", "imported": "تم استيراد المفتاح من بيانات الاعتماد", "importFailed": "فشل استيراد المفتاح", - "noKeys": "لم يتم نشر أي مفاتيح عامة حتى الآن.", + "noKeys": "لا توجد مفاتيح عامة منشورة بعد.", "keysTitle": "المفاتيح المنشورة", - "published": "نُشر", - "hidden": "مختفي", - "keyRemoved": "تم إزالة المفتاح", - "removeKeyFailed": "فشل في إزالة المفتاح", + "published": "منشور", + "hidden": "مخفي", + "keyRemoved": "تمت إزالة المفتاح", + "removeKeyFailed": "فشل إزالة المفتاح", "updateKeyFailed": "فشل تحديث المفتاح", - "fromVault": "من مخزن بيانات الاعتماد", - "linkedToTermixId": "تم النشر عبر معرف Termix", + "fromVault": "من خزنة بيانات الاعتماد", + "linkedToTermixId": "منشور عبر Termix ID", "selectCredential": "اختر بيانات اعتماد...", - "import": "يستورد", - "caTitle": "جهة إصدار الشهادات", - "caIntro": "قم بتزويد هذا المرجع المصادقة على خادم، وسيقبل أي شهادة تقوم بتوقيعها. قم بتدوير الشهادات لإلغاء جميع الشهادات دفعة واحدة؛ كما تنتهي صلاحية الشهادات تلقائيًا.", - "caEnable": "تفعيل CA", - "caEnabled": "تم تمكين CA", - "caCreateFailed": "فشل تفعيل CA", - "caPublicKeyLabel": "المفتاح العام لهيئة التصديق", - "caTrustLabel": "الثقة في الخادم (تشغيل كجذر)", - "caRotate": "تناوب", - "caRotateConfirm": "هل يجب تغيير جهة إصدار الشهادات؟ ستتوقف كل شهادة موقعة من قبلها عن القبول ويجب إعادة إصدارها.", - "caRotated": "تم تغيير المرجع المصدق - تم إلغاء الشهادات السابقة", - "caRotateFailed": "فشل تدوير CA", - "caDelete": "إزالة CA", - "caDeleteConfirm": "هل تريد إزالة جهة إصدار الشهادات؟", - "caDeleted": "تمت إزالة CA", - "caDeleteFailed": "فشل إزالة شهادة المصادقة", + "import": "استيراد", + "caTitle": "المرجع المصدق (CA)", + "caIntro": "وثق هذا المرجع المصدق على الخادم ليقبل أي شهادة توقعها. دوِّره لإبطال كل الشهادات دفعة واحدة؛ تنتهي صلاحية الشهادات تلقائيًا أيضًا.", + "caEnable": "فعّل المرجع المصدق", + "caEnabled": "تم تفعيل المرجع المصدق", + "caCreateFailed": "فشل تفعيل المرجع المصدق", + "caPublicKeyLabel": "المفتاح العام للمرجع المصدق", + "caTrustLabel": "ثق به على الخادم (تشغيل بصلاحيات الجذر)", + "caRotate": "دوِّر", + "caRotateConfirm": "هل تريد تدوير المرجع المصدق؟ ستتوقف جميع الشهادات التي وقعها عن القبول ويجب إعادة إصدارها.", + "caRotated": "تم تدوير المرجع المصدق — أُبطلت الشهادات السابقة", + "caRotateFailed": "فشل تدوير المرجع المصدق", + "caDelete": "أزل المرجع المصدق", + "caDeleteConfirm": "هل تريد إزالة المرجع المصدق؟", + "caDeleted": "تمت إزالة المرجع المصدق", + "caDeleteFailed": "فشل إزالة المرجع المصدق", "caValidityLabel": "صلاحية الشهادة (بالأيام)", "issueCert": "شهادة", - "issueCertTooltip": "قم بإصدار شهادة SSH لهذا المفتاح، موقعة من قبل جهة إصدار الشهادات الخاصة بك.", + "issueCertTooltip": "إصدار شهادة SSH لهذا المفتاح، موقعة من المرجع المصدق الخاص بك", "certIssued": "تم إصدار الشهادة وتنزيلها", "certIssueFailed": "فشل إصدار الشهادة" }, "credentials": { - "folders": "المجلدات", + "folders": "مجلدات", "folder": "مجلد", "password": "كلمة المرور", "key": "مفتاح", "sshPrivateKey": "مفتاح SSH الخاص", "upload": "رفع", - "keyPassword": "كلمة المرور الرئيسية", + "keyPassword": "كلمة مرور المفتاح", "sshKey": "مفتاح SSH", - "uploadPrivateKeyFile": "قم بتحميل ملف المفتاح الخاص", - "searchCredentials": "بيانات اعتماد البحث...", + "uploadPrivateKeyFile": "رفع ملف المفتاح الخاص", + "searchCredentials": "بحث عن بيانات الاعتماد...", "addCredential": "إضافة بيانات اعتماد", "caCertificate": "شهادة CA (-cert.pub)", - "caCertificateDescription": "اختياري: قم بتحميل أو لصق ملف الشهادة الموقعة من جهة إصدار الشهادات (مثال: id_ed25519-cert.pub). هذا الإجراء مطلوب عندما يستخدم خادم SSH الخاص بك نظام المصادقة القائم على الشهادات.", - "uploadCertFile": "قم بتحميل ملف -cert.pub", - "clearCert": "واضح", + "caCertificateDescription": "اختياري: رفع أو لصق ملف الشهادة الموقعة من CA (مثل id_ed25519-cert.pub). مطلوب عندما يستخدم خادم SSH ترخيصًا قائمًا على الشهادات.", + "uploadCertFile": "رفع ملف -cert.pub", + "clearCert": "مسح", "certLoaded": "تم تحميل الشهادة", "certPublicKeyLabel": "شهادة CA", "certTypeLabel": "نوع الشهادة", - "pasteOrUploadCert": "قم بلصق أو تحميل شهادة -cert.pub...", - "hasCaCert": "حاصل على شهادة CA", - "noCaCert": "لا يوجد شهادة CA", - "noPublicKeyAvailable": "لا يوجد مفتاح عام متاح. افتح محرر بيانات الاعتماد أولاً.", + "pasteOrUploadCert": "لصق أو رفع شهادة -cert.pub...", + "hasCaCert": "لديه شهادة CA", + "noCaCert": "لا توجد شهادة CA", + "noPublicKeyAvailable": "لا يوجد مفتاح عام متوفر. افتح محرر بيانات الاعتماد أولاً.", "deployCommandCopied": "تم نسخ أمر النشر", - "sortCredentials": "فرز بيانات الاعتماد", + "sortCredentials": "ترتيب بيانات الاعتماد", "sortDefault": "الترتيب الافتراضي", - "sortNameAsc": "الاسم (من الألف إلى الياء)", - "sortNameDesc": "الاسم (من Z إلى A)", - "sortUsernameAsc": "اسم المستخدم (من الألف إلى الياء)", + "sortNameAsc": "الاسم (A → Z)", + "sortNameDesc": "الاسم (Z → A)", + "sortUsernameAsc": "اسم المستخدم (A → Z)", "sortUsernameDesc": "اسم المستخدم (Z → A)", - "filterCredentials": "بيانات اعتماد التصفية", - "filterClearAll": "مسح الفلاتر", - "filterTypeGroup": "يكتب", + "filterCredentials": "تصفية بيانات الاعتماد", + "filterClearAll": "مسح عوامل التصفية", + "filterTypeGroup": "النوع", "filterTypePassword": "كلمة المرور", "filterTypeKey": "مفتاح SSH", - "filterTagsGroup": "الوسوم" + "filterTagsGroup": "وسوم" }, "homepage": { "title": "الصفحة الرئيسية", - "addWidget": "إضافة أداة", - "editWidget": "أداة التحرير", - "deleteWidget": "يمسح", + "addWidget": "إضافة ويدجت", + "editWidget": "تعديل ويدجت", + "deleteWidget": "حذف", "widgetTypes": "أنواع الأدوات", "serviceLink": "رابط الخدمة", "folder": "مجلد", - "clock": "ساعة", - "notes": "ملحوظات", + "clock": "الساعة", + "notes": "ملاحظات", "hostStatus": "حالة المضيف", - "bookmarkList": "الإشارات المرجعية", - "zoomIn": "تكبير الصورة", - "zoomOut": "تصغير العرض", - "resetView": "إعادة ضبط العرض", - "lockLayout": "تخطيط القفل", - "unlockLayout": "فتح التخطيط", - "noWidgets": "انقر بزر الماوس الأيمن أو انقر فوق + لإضافة أول عنصر واجهة مستخدم", - "openFullView": "افتح العرض الكامل", + "bookmarkList": "علامات مرجعية", + "zoomIn": "تكبير", + "zoomOut": "تصغير", + "resetView": "إعادة تعيين العرض", + "lockLayout": "قفل التخطيط", + "unlockLayout": "إلغاء قفل التخطيط", + "noWidgets": "انقر بزر الماوس الأيمن أو انقر على + لإضافة أداتك الأولى", + "openFullView": "فتح العرض الكامل", "previewTitle": "معاينة الصفحة الرئيسية", - "cancel": "يلغي", - "save": "يحفظ", - "title_label": "عنوان", + "cancel": "إلغاء", + "save": "حفظ", + "title_label": "العنوان", "widgetTitlePlaceholder": "عنوان الأداة (اختياري)", - "url": "عنوان URL", - "imageUrl": "رابط الصورة المخصصة", - "imageUrlHint": "اترك هذا الحقل فارغًا لاستخدام أيقونة الموقع تلقائيًا", - "showImage": "عرض الصورة", - "description": "وصف", - "color": "لون", - "icon": "رمز", - "expanded": "موسع افتراضيًا", + "url": "URL", + "imageUrl": "URL صورة مخصصة", + "imageUrlHint": "اتركه فارغًا لاستخدام أيقونة الموقع تلقائيًا", + "showImage": "إظهار الصورة", + "description": "الوصف", + "color": "اللون", + "icon": "الأيقونة", + "expanded": "موسَّع بشكل افتراضي", "timezone": "المنطقة الزمنية", "showSeconds": "عرض الثواني", "format12h": "12 ساعة", - "format24h": "خدمة على مدار 24 ساعة", - "content": "محتوى", + "format24h": "24 ساعة", + "content": "المحتوى", "backgroundColor": "لون الخلفية", - "host": "يستضيف", + "host": "المضيف", "showMetrics": "عرض المقاييس", "links": "روابط", "addLink": "إضافة رابط", - "linkLabel": "ملصق", - "linkUrl": "عنوان URL", - "removeLink": "يزيل", + "linkLabel": "التسمية", + "linkUrl": "URL", + "removeLink": "إزالة", "categoryLinks": "روابط", "categoryInfo": "معلومات", - "categorySystem": "نظام", - "widgetServiceLinkDesc": "مربع قابل للنقر يؤدي إلى عنوان URL للخدمة", + "categorySystem": "النظام", + "widgetServiceLinkDesc": "بلاطة قابلة للنقر تؤدي إلى رابط خدمة", "widgetFolderDesc": "حاوية لتجميع الأدوات ذات الصلة", - "widgetClockDesc": "ساعة مباشرة مع إمكانية ضبط المنطقة الزمنية", - "widgetNotesDesc": "أداة ملاحظات ماركداون", - "widgetHostStatusDesc": "يعرض معلومات مباشرة عن وحدة المعالجة المركزية والذاكرة والقرص لمضيف SSH", + "widgetClockDesc": "ساعة مباشرة مع منطقة زمنية قابلة للتكوين", + "widgetNotesDesc": "أداة ملاحظات بتنسيق Markdown", + "widgetHostStatusDesc": "يعرض بيانات المعالج والذاكرة والقرص لخادم SSH مباشرة", "widgetBookmarkListDesc": "قائمة بالروابط السريعة", - "copyLink": "نسخ الوصلة", + "copyLink": "نسخ الرابط", "linkCopied": "تم نسخ الرابط!", - "location": "موقع", - "temperatureUnit": "وحدة قياس درجة الحرارة", - "showForecast": "عرض توقعات الطقس لثلاثة أيام", + "location": "الموقع", + "temperatureUnit": "وحدة درجة الحرارة", + "showForecast": "إظهار توقعات 3 أيام", "scrolling": "السماح بالتمرير", "feedUrl": "رابط التغذية", "maxItems": "الحد الأقصى للعناصر", - "showDescription": "عرض الوصف", - "widgetWeatherName": "طقس", + "showDescription": "إظهار الوصف", + "widgetWeatherName": "الطقس", "widgetWeatherDesc": "حالة الطقس المباشرة لأي موقع", "widgetIframeName": "تضمين iFrame", - "widgetIframeDesc": "قم بتضمين أي عنوان URL في إطار iframe", - "widgetRssName": "موجز RSS", - "widgetRssDesc": "عرض العناصر من موجز RSS أو Atom", - "dragToFolder": "اسحب الأدوات هنا أو استخدم علامة + للإضافة", - "showDisk": "عرض استخدام القرص", - "addToFolder": "أضف الأداة إلى المجلد", + "widgetIframeDesc": "تضمين أي رابط في إطار iFrame", + "widgetRssName": "تغذية RSS", + "widgetRssDesc": "عرض عناصر من تغذية RSS أو Atom", + "dragToFolder": "اسحب الأدوات هنا أو استخدم + للإضافة", + "showDisk": "إظهار استخدام القرص", + "addToFolder": "إضافة أداة إلى المجلد", "noHostSelected": "لم يتم تحديد أي مضيف", "metricsNotAvailable": "المقاييس غير متوفرة", - "selectHost": "اختر مضيفًا...", + "selectHost": "اختيار مضيف...", "displayedMetrics": "المقاييس المعروضة", - "metricCpu": "وحدة المعالجة المركزية", - "metricMemory": "ذاكرة", - "metricDisk": "أدك", + "metricCpu": "CPU", + "metricMemory": "الذاكرة", + "metricDisk": "القرص", "metricUptime": "وقت التشغيل", - "metricSystem": "نظام", + "metricSystem": "النظام", "metricOs": "نظام التشغيل", "metricKernel": "النواة", "metricHostname": "اسم المضيف", - "metricNetwork": "شبكة", + "metricNetwork": "الشبكة", "metricProcesses": "العمليات", - "metricProcessesTotal": "المجموع", - "metricProcessesRunning": "جري", - "categoryMonitoring": "يراقب", - "loading": "تحميل...", - "noData": "لا توجد بيانات متاحة", - "allClear": "الوضع آمن", - "acknowledgeAlert": "يُقرّ", - "noPingUrls": "لم يتم تكوين أي عناوين URL", - "pingLabel": "ملصق", - "addPingUrl": "أضف عنوان URL", - "showLatency": "عرض زمن الاستجابة", + "metricProcessesTotal": "إجمالي", + "metricProcessesRunning": "قيد التشغيل", + "categoryMonitoring": "المراقبة", + "loading": "جار التحميل...", + "noData": "لا توجد بيانات", + "allClear": "كل شيء طبيعي", + "acknowledgeAlert": "إقرار", + "noPingUrls": "لم يتم تكوين عناوين URL", + "pingLabel": "التسمية", + "addPingUrl": "إضافة URL", + "showLatency": "إظهار زمن الانتقال", "refreshInterval": "فترة التحديث", - "seconds": "ثوانٍ", - "filterActivityTypes": "أنواع المرشحات", - "filterTypesHint": "اترك هذا الحقل فارغًا لعرض الكل", - "showTimestamp": "عرض الطابع الزمني", + "seconds": "ثانية", + "filterActivityTypes": "أنواع التصفية", + "filterTypesHint": "اترك فارغاً لإظهار الكل", + "showTimestamp": "إظهار الطابع الزمني", "uptimeUnavailable": "وقت التشغيل غير متوفر", "uptimeLabel": "وقت التشغيل", - "overviewVersion": "إصدار", + "overviewVersion": "الإصدار", "overviewUpdate": "محدّث", - "overviewUpdateAvailable": "يتوفر تحديث", + "overviewUpdateAvailable": "تحديث متوفر", "overviewDatabase": "قاعدة البيانات", "overviewUptime": "وقت التشغيل", - "noHosts": "لم يتم تكوين أي مضيفين", - "hostGridHosts": "المضيفون", - "hostGridAllHint": "اترك هذا الحقل فارغًا لعرض جميع المضيفين", + "noHosts": "لا توجد مضيفات مُهيأة", + "hostGridHosts": "المضيفات", + "hostGridAllHint": "اتركه فارغًا لعرض جميع المضيفات", "columns": "الأعمدة", - "showIp": "عرض عنوان IP", + "showIp": "إظهار عنوان IP", "connectionType": "نوع الاتصال", - "layout": "تَخطِيط", - "showStatus": "عرض الحالة", + "layout": "التخطيط", + "showStatus": "إظهار الحالة", "showHostName": "إظهار اسم المضيف", - "noDockerActivity": "لا يوجد نشاط في Docker", + "noDockerActivity": "لا يوجد نشاط Docker", "noActivity": "لا يوجد نشاط", - "showAcknowledged": "تم الإقرار بالقبول", - "showCurrentValue": "عرض القيمة الحالية", - "chartMetric": "متري", - "metricRange": "يتراوح", + "showAcknowledged": "إظهار المُقر بها", + "showCurrentValue": "إظهار القيمة الحالية", + "chartMetric": "المقياس", + "metricRange": "النطاق", "widgetMetricsChartName": "مخطط المقاييس", - "widgetMetricsChartDesc": "مخطط تاريخي لوحدة المعالجة المركزية والذاكرة والقرص أو الشبكة لمضيف", - "widgetHostGridName": "شبكة الاستضافة", - "widgetHostGridDesc": "عرض شبكي لحالات مضيف SSH", + "widgetMetricsChartDesc": "مخطط تاريخي لوحدة المعالجة المركزية أو الذاكرة أو القرص أو الشبكة لمضيف", + "widgetHostGridName": "شبكة المضيفات", + "widgetHostGridDesc": "عرض شبكي لحالات مضيفات SSH", "widgetAlertFeedName": "موجز التنبيهات", - "widgetAlertFeedDesc": "إطلاق نار تنبيهي مباشر مع تأكيد الدعم", - "widgetPingStatusName": "حالة اختبار الاتصال (ping)", - "widgetPingStatusDesc": "حالة اختبار اتصال HTTP لعنوان URL واحد أو أكثر", + "widgetAlertFeedDesc": "تنبيهات حية مع دعم الإقرار", + "widgetPingStatusName": "حالة Ping", + "widgetPingStatusDesc": "حالة ping HTTP لعنوان URL واحد أو أكثر", "widgetRecentActivityName": "النشاط الأخير", - "widgetRecentActivityDesc": "موجز قابل للتمرير لأنشطة Termix الأخيرة", - "widgetTermixUptimeName": "وقت تشغيل Termix", - "widgetTermixUptimeDesc": "عداد وقت التشغيل المباشر لخادم Termix", + "widgetRecentActivityDesc": "موجز قابل للتمرير للنشاط الأخير في Termix", + "widgetTermixUptimeName": "مدة تشغيل Termix", + "widgetTermixUptimeDesc": "عداد مباشر لمدة تشغيل خادم Termix", "widgetSystemOverviewName": "نظرة عامة على النظام", - "widgetSystemOverviewDesc": "إصدار Termix، وحالة قاعدة البيانات، ووقت التشغيل في لمحة", - "widgetSshQuickConnectName": "اتصال سريع عبر SSH", + "widgetSystemOverviewDesc": "إصدار Termix وصحة قاعدة البيانات ومدة التشغيل في لمحة", + "widgetSshQuickConnectName": "اتصال SSH السريع", "widgetSshQuickConnectDesc": "أزرار بنقرة واحدة لفتح جلسات SSH", "widgetDockerActivityName": "نشاط Docker", - "widgetDockerActivityDesc": "أحداث حاويات Docker الأخيرة عبر جميع المضيفين", - "widgetCalendarName": "تقويم", - "widgetCalendarDesc": "تقويم شهري مع تمييز اليوم", - "widgetCountdownName": "العد التنازلي", - "widgetCountdownDesc": "مؤقت العد التنازلي لتاريخ مستهدف", + "widgetDockerActivityDesc": "أحداث حاويات Docker الأخيرة عبر جميع المضيفات", + "widgetCalendarName": "التقويم", + "widgetCalendarDesc": "تقويم شهري مع إبراز اليوم", + "widgetCountdownName": "عداد تنازلي", + "widgetCountdownDesc": "مؤقت عد تنازلي لتاريخ مستهدف", "widgetSearchBarName": "شريط البحث", - "widgetSearchBarDesc": "أداة البحث السريع على الويب", + "widgetSearchBarDesc": "ودجت بحث سريع على الويب", "widgetTextBannerName": "لافتة نصية", - "widgetTextBannerDesc": "عنوان بارز أو عنوان قسم للوحة القماشية", + "widgetTextBannerDesc": "تسمية عريضة أو رأس قسم للوحة", "widgetImageWidgetName": "صورة", - "widgetImageWidgetDesc": "عرض صورة من عنوان URL", + "widgetImageWidgetDesc": "عرض صورة من رابط URL", "widgetMarkdownNotesName": "ملاحظات ماركداون", - "widgetMarkdownNotesDesc": "ملاحظات غنية مع عرض Markdown مضمن", - "widgetCustomApiName": "واجهة برمجة تطبيقات مخصصة", - "widgetCustomApiDesc": "جلب وعرض البيانات من أي واجهة برمجة تطبيقات JSON", - "widgetServiceGridName": "شبكة الخدمة", - "widgetServiceGridDesc": "شبكة قابلة للتكوين من روابط بلاطات الخدمة", - "widgetDashboardLinksName": "روابط لوحة التحكم", - "widgetDashboardLinksDesc": "اعرض روابط الخدمة التي قمت بتكوينها من لوحة التحكم", + "widgetMarkdownNotesDesc": "ملاحظات غنية مع عرض ماركداون مضمن", + "widgetCustomApiName": "API مخصص", + "widgetCustomApiDesc": "جلب وعرض بيانات من أي JSON API", + "widgetServiceGridName": "شبكة الخدمات", + "widgetServiceGridDesc": "شبكة قابلة للتكوين من روابط مربعات الخدمات", + "widgetDashboardLinksName": "روابط لوحة المعلومات", + "widgetDashboardLinksDesc": "عرض روابط الخدمات المكوّنة من لوحة المعلومات", "widgetSearchLinksName": "اختصارات البحث", - "widgetSearchLinksDesc": "أزرار اختصار البحث السريع مع إدخال مضمن", + "widgetSearchLinksDesc": "أزرار اختصارات بحث سريعة مع إدخال مضمن", "widgetLinkTreeName": "شجرة الروابط", - "widgetLinkTreeDesc": "أقسام مجمعة من الروابط مع عناوين", - "calMon": "شهر", - "calTue": "تو", - "calWed": "نحن", - "calThu": "ذ", - "calFri": "الأب", - "calSat": "سا", - "calSun": "سو", - "startOnMonday": "ابدأ أسبوعك يوم الاثنين", + "widgetLinkTreeDesc": "أقسام مجمّعة من الروابط مع عناوين", + "calMon": "إثنين", + "calTue": "ثلاثاء", + "calWed": "أربعاء", + "calThu": "خميس", + "calFri": "جمعة", + "calSat": "سبت", + "calSun": "أحد", + "startOnMonday": "بدء الأسبوع يوم الإثنين", "countdownNoDate": "لم يتم تحديد تاريخ مستهدف", - "countdownPast": "انتهى الحدث", + "countdownPast": "انقضى الحدث", "countdownDays": "أيام", "countdownHours": "ساعات", - "countdownMinutes": "مين", - "countdownSeconds": "ثانية", - "countdownLabel": "ملصق", + "countdownMinutes": "دقائق", + "countdownSeconds": "ثوانٍ", + "countdownLabel": "تسمية", "countdownLabelPlaceholder": "مثال: يوم الإطلاق", - "countdownShowDays": "أيام العرض", - "countdownShowHours": "مواعيد العرض", + "countdownShowDays": "إظهار الأيام", + "countdownShowHours": "إظهار الساعات", "targetDate": "التاريخ المستهدف", "searchEngine": "محرك البحث", - "customSearchUrl": "عنوان URL مخصص للبحث", - "searchPlaceholder": "يبحث...", - "searchGo": "يذهب", - "searchPlaceholderLabel": "نص توضيحي", + "customSearchUrl": "عنوان URL بحث مخصص", + "searchPlaceholder": "بحث...", + "searchGo": "تنفيذ", + "searchPlaceholderLabel": "نص العنصر النائب", "searchPlaceholderHint": "النص المعروض داخل حقل البحث", - "openInNewTab": "افتح في علامة تبويب جديدة", + "openInNewTab": "فتح في علامة تبويب جديدة", "searchQueryPlaceholder": "أدخل الاستعلام...", - "noSearchShortcuts": "لم يتم تكوين أي اختصارات", + "noSearchShortcuts": "لا توجد اختصارات مكونة", "addSearchShortcut": "إضافة اختصار", "fontSize": "حجم الخط", "textAlign": "محاذاة النص", "fontWeight": "وزن الخط", - "clearColor": "لون شفاف", + "clearColor": "مسح اللون", "imageFit": "ملاءمة الصورة", "imageLinkUrl": "رابط URL", "noImage": "لم يتم تعيين عنوان URL للصورة", "altText": "نص بديل", - "altTextPlaceholder": "صف الصورة", + "altTextPlaceholder": "وصف الصورة", "renderMarkdown": "عرض Markdown", "displayMode": "وضع العرض", "displayField": "حقل العرض", "jsonPath": "مسار JSON", - "customApiLabel": "ملصق", + "customApiLabel": "تسمية", "customApiLabelPlaceholder": "مثال: درجة الحرارة", "customApiUnit": "وحدة", - "customApiNoUrl": "لم يتم تكوين عنوان URL لواجهة برمجة التطبيقات", - "customApiError": "فشل في جلب البيانات", + "customApiNoUrl": "لم يتم تكوين عنوان URL API", + "customApiError": "فشل في الجلب", "customApiNotArray": "الاستجابة ليست مصفوفة", "addService": "إضافة خدمة", - "showLabels": "عرض التصنيفات", + "showLabels": "إظهار التسميات", "iconSize": "حجم الأيقونة", - "noDashboardLinks": "لم يتم تكوين أي روابط للوحة التحكم", - "noLimit": "بلا حدود", + "noDashboardLinks": "لا توجد روابط لوحة تحكم مُعدة", + "noLimit": "غير محدود", "sectionHeading": "عنوان القسم", "addSection": "إضافة قسم", "compactMode": "الوضع المضغوط", - "showDetailed": "عرض التفاصيل", - "selectHosts": "حدد المضيفين", + "showDetailed": "إظهار التفاصيل", + "selectHosts": "اختيار المضيفين", "allHosts": "جميع المضيفين", "listLayout": "قائمة", "gridLayout": "شبكة", - "terminal": "صالة", + "terminal": "الطرفية", "files": "الملفات", - "docker": "عامل ميناء", + "docker": "Docker", "range15m": "15 دقيقة", "range1h": "ساعة واحدة", "range6h": "6 ساعات", "range24h": "24 ساعة", - "metricNetRx": "تنزيل من الإنترنت", - "metricNetTx": "تحميل الشبكة", - "severityFilter": "فلتر الشدة", - "filterAll": "الجميع", - "accentColor": "لون مميز", + "metricNetRx": "تنزيل الشبكة", + "metricNetTx": "رفع الشبكة", + "severityFilter": "مرشح الخطورة", + "filterAll": "الكل", + "accentColor": "لون التمييز", "widgetSshTerminalName": "طرفية SSH", - "widgetSshTerminalDesc": "طرفية SSH مضمنة متصلة بمضيف مُهيأ", - "sshTerminalNoHost": "لم يتم تكوين أي مضيف", - "sshTerminalConnect": "يتصل", - "sshTerminalAutoConnect": "الاتصال التلقائي عند التحميل", - "widgetQuickConnectName": "توصيل سريع", + "widgetSshTerminalDesc": "طرفية SSH مضمنة متصلة بمضيف مُعد", + "sshTerminalNoHost": "لم يتم إعداد مضيف", + "sshTerminalConnect": "اتصل", + "sshTerminalAutoConnect": "اتصال تلقائي عند التحميل", + "widgetQuickConnectName": "اتصال سريع", "widgetQuickConnectDesc": "أزرار تشغيل بنقرة واحدة لأي نوع اتصال عبر مضيفيك", "connectionTypes": "أنواع الاتصال", - "connType_terminal": "صالة", + "connType_terminal": "الطرفية", "connType_files": "مدير الملفات", - "connType_docker": "عامل ميناء", + "connType_docker": "Docker", "connType_tunnel": "نفق", "connType_host-metrics": "مقاييس المضيف", "connType_rdp": "RDP", "connType_vnc": "VNC", - "connType_telnet": "تيلنت", + "connType_telnet": "Telnet", "widgetFileManagerName": "مدير الملفات", - "widgetFileManagerDesc": "مدير ملفات SFTP مدمج لمضيف مُهيأ", + "widgetFileManagerDesc": "مدير ملفات SFTP مدمج لمضيف مكوّن", "widgetDockerName": "مدير Docker", - "widgetDockerDesc": "مدير حاويات Docker مدمج لمضيف مُهيأ", + "widgetDockerDesc": "مدير حاويات Docker مدمج لمضيف مكوّن", "widgetTunnelName": "مدير الأنفاق", - "widgetTunnelDesc": "مدير أنفاق SSH مدمج لمضيف مُهيأ", + "widgetTunnelDesc": "مدير أنفاق SSH مدمج لمضيف مكوّن", "widgetNoHostSelected": "لم يتم تكوين أي مضيف" }, "serverConfig": { - "title": "تكوين الخادم", - "description": "قم بتكوين عنوان URL لخادم Termix للاتصال بخدمات الواجهة الخلفية الخاصة بك", - "serverUrl": "عنوان الخادم", - "enterServerUrl": "الرجاء إدخال عنوان URL للخادم", + "title": "إعدادات الخادم", + "description": "تكوين رابط خادم Termix للاتصال بخدماتك الخلفية", + "serverUrl": "رابط الخادم", + "enterServerUrl": "يرجى إدخال رابط الخادم", "saveFailed": "فشل حفظ الإعدادات", - "saveError": "حدث خطأ أثناء حفظ الإعدادات", - "saving": "توفير...", + "saveError": "خطأ في حفظ الإعدادات", + "saving": "جاري الحفظ...", "saveConfig": "حفظ الإعدادات", - "helpText": "أدخل عنوان URL حيث يعمل خادم Termix الخاص بك (على سبيل المثال، http://localhost:30001 أو https://your-server.com)", + "helpText": "أدخل الرابط حيث يعمل خادم Termix (مثال: http://localhost:30001 أو https://your-server.com)", "changeServer": "تغيير الخادم", - "mustIncludeProtocol": "يجب أن يبدأ عنوان URL للخادم بـ http:// أو https://", + "mustIncludeProtocol": "يجب أن يبدأ رابط الخادم بـ http:// أو https://", "allowInvalidCertificate": "السماح بشهادة غير صالحة", - "allowInvalidCertificateDesc": "استخدمه فقط للخوادم الموثوقة ذاتية الاستضافة التي تحتوي على شهادات موقعة ذاتيًا أو شهادات عناوين IP.", + "allowInvalidCertificateDesc": "استخدم فقط للخوادم الموثوقة ذاتية الاستضافة التي تحمل شهادات موقعة ذاتيًا أو بعنوان IP.", "useEmbedded": "استخدام الخادم المحلي", - "embeddedDesc": "قم بتشغيل Termix باستخدام الخادم المحلي المدمج (لا حاجة لخادم بعيد)", - "embeddedConnecting": "جارٍ الاتصال بالخادم المحلي...", + "embeddedDesc": "تشغيل Termix مع الخادم المحلي المدمج (لا حاجة لخادم بعيد)", + "embeddedConnecting": "جاري الاتصال بالخادم المحلي...", "embeddedNotReady": "الخادم المحلي غير جاهز بعد. يرجى الانتظار قليلاً ثم المحاولة مرة أخرى.", "localServer": "الخادم المحلي", "savedServers": "الخوادم المحفوظة", "noSavedServers": "لا توجد خوادم محفوظة", - "removeServer": "يزيل" + "removeServer": "إزالة" + }, + "migrationNotice": { + "title": "يعمل برنامج Termix Desktop الآن بشكل مستقل", + "body1": "كان هذا التطبيق يتصل مباشرةً بخادم Termix الخاص بك. تم تطويره ليعمل بشكل مستقل تمامًا، حيث يخزن بيانات المضيفين وبيانات الاعتماد محليًا، مما يضمن استمرارية عمله دون اتصال بالإنترنت. أصبح التزامن ثنائي الاتجاه مع خادم Termix مُستضاف ذاتيًا اختياريًا الآن.", + "body2": "لا تزال بيانات المضيفين وبيانات الاعتماد الحالية موجودة على {{url}}. قم بتشغيل المزامنة عن بُعد وأعد الاتصال بهذا الخادم لاستعادتها والحفاظ على تزامنها في المستقبل.", + "dismiss": "ليس الآن", + "setUpSync": "إعداد المزامنة عن بُعد" + }, + "remoteSync": { + "title": "المزامنة عن بعد", + "description": "يمكنك ربط تطبيق سطح المكتب هذا بخادم Termix مُستضاف ذاتيًا لمزامنة بيانات المضيفين وبيانات الاعتماد والقصاصات البرمجية عبر الأجهزة. يعمل التطبيق دائمًا بشكل كامل دون اتصال بالإنترنت سواءً كنت متصلاً أم لا.", + "notConnected": "غير متصل", + "connected": "متصل", + "connectedTo": "متصل بـ {{url}}", + "lastSynced": "آخر مزامنة {{time}}", + "neverSynced": "لم تتم المزامنة مطلقاً", + "syncError": "خطأ في المزامنة: {{message}}", + "needsReauth": "انتهت صلاحية تسجيل الدخول", + "connectButton": "الاتصال بالخادم", + "disconnectButton": "قطع الاتصال", + "syncNowButton": "مزامنة الآن", + "syncing": "جارٍ المزامنة...", + "serverUrl": "عنوان الخادم", + "enterServerUrl": "الرجاء إدخال عنوان URL للخادم", + "mustIncludeProtocol": "يجب أن يبدأ عنوان URL للخادم بـ http:// أو https://", + "connectionTestFailed": "تعذر الوصول إلى خادم Termix على عنوان URL هذا", + "allowInvalidCertificate": "السماح بشهادة غير صالحة", + "allowInvalidCertificateDesc": "استخدمه فقط للخوادم الموثوقة ذاتية الاستضافة التي تحتوي على شهادات موقعة ذاتيًا أو شهادات عناوين IP.", + "savedServers": "الخوادم المحفوظة", + "removeServer": "يزيل", + "continueButton": "يكمل", + "cancelButton": "يلغي", + "signInTitle": "سجّل الدخول إلى {{url}}", + "originTitle": "أصل الاتصالات", + "originDescription": "اختر مصدر اتصالات SSH الافتراضي. يمكن تغيير هذا الإعداد لكل مضيف على حدة.", + "originLocal": "هذا الجهاز (الشبكة المحلية)", + "originRemote": "خادم بعيد", + "bannerReconnect": "إعادة الاتصال", + "bannerMessage": "يتطلب المزامنة عن بُعد إعادة المصادقة" }, "versionCheck": { "error": "خطأ في التحقق من الإصدار", "checkFailed": "فشل التحقق من وجود تحديثات", - "upToDate": "التطبيق محدّث", - "currentVersion": "أنت تستخدم الإصدار {{version}}", - "updateAvailable": "يتوفر تحديث", - "newVersionAvailable": "يتوفر إصدار جديد! أنت تستخدم الإصدار {{current}}، ولكن الإصدار {{latest}} متوفر.", - "betaVersion": "النسخة التجريبية", - "betaVersionDesc": "أنت تستخدم الإصدار {{current}}، وهو أحدث من أحدث إصدار مستقر {{latest}}.", - "releasedOn": "تم إصداره في {{date}}", + "upToDate": "التطبيق محدث", + "currentVersion": "أنت تعمل على الإصدار {{version}}", + "updateAvailable": "تحديث متاح", + "newVersionAvailable": "إصدار جديد متاح! أنت تعمل على {{current}}، لكن {{latest}} متاح.", + "betaVersion": "إصدار تجريبي", + "betaVersionDesc": "أنت تعمل على {{current}}، وهو أحدث من آخر إصدار مستقر {{latest}}.", + "releasedOn": "صدر في {{date}}", "downloadUpdate": "تنزيل التحديث", - "checking": "جارٍ التحقق من وجود تحديثات...", - "checkUpdates": "تحقق من وجود تحديثات", - "checkingUpdates": "جارٍ التحقق من وجود تحديثات...", - "updateRequired": "يلزم التحديث" + "checking": "جارٍ التحقق من التحديثات...", + "checkUpdates": "التحقق من التحديثات", + "checkingUpdates": "جارٍ التحقق من التحديثات...", + "updateRequired": "التحديث مطلوب" }, "common": { - "close": "يغلق", - "minimize": "التقليل", + "close": "إغلاق", + "minimize": "تصغير", "online": "متصل", - "offline": "غير متصل بالإنترنت", - "unknown": "مجهول", - "continue": "يكمل", + "offline": "غير متصل", + "unknown": "غير معروف", + "continue": "متابعة", "maintenance": "صيانة", "degraded": "متدهور", "error": "خطأ", "warning": "تحذير", - "unsavedChanges": "التغييرات غير المحفوظة", - "dismiss": "رفض", - "loading": "تحميل...", - "optional": "خياري", - "connect": "يتصل", + "unsavedChanges": "تغييرات غير محفوظة", + "dismiss": "تجاهل", + "loading": "جارٍ التحميل...", + "optional": "اختياري", + "connect": "اتصل", "copied": "تم النسخ", "connecting": "جارٍ الاتصال...", - "updateAvailable": "يتوفر تحديث", - "appName": "تيرميكس", - "openInNewTab": "افتح في علامة تبويب جديدة", + "updateAvailable": "تحديث متاح", + "appName": "Termix", + "openInNewTab": "فتح في علامة تبويب جديدة", "noReleases": "لا توجد إصدارات", "updatesAndReleases": "التحديثات والإصدارات", "newVersionAvailable": "يتوفر إصدار جديد ({{version}}).", - "failedToFetchUpdateInfo": "فشل في جلب معلومات التحديث", - "preRelease": "إصدار مسبق", - "noReleasesFound": "لم يتم العثور على أي إصدارات.", - "cancel": "يلغي", + "failedToFetchUpdateInfo": "فشل جلب معلومات التحديث", + "preRelease": "ما قبل الإصدار", + "noReleasesFound": "لم يتم العثور على إصدارات.", + "cancel": "إلغاء", "username": "اسم المستخدم", "login": "تسجيل الدخول", "logout": "تسجيل الخروج", - "register": "يسجل", + "register": "تسجيل", "password": "كلمة المرور", "confirmPassword": "تأكيد كلمة المرور", - "back": "خلف", - "save": "يحفظ", - "saving": "توفير...", - "delete": "يمسح", + "back": "رجوع", + "save": "حفظ", + "saving": "جاري الحفظ...", + "delete": "حذف", "rename": "إعادة تسمية", - "edit": "يحرر", - "add": "يضيف", - "confirm": "يتأكد", + "edit": "تحرير", + "add": "إضافة", + "confirm": "تأكيد", "no": "لا", "or": "أو", "next": "التالي", - "previous": "سابق", - "refresh": "ينعش", - "language": "لغة", + "previous": "السابق", + "refresh": "تحديث", + "language": "اللغة", "checking": "جارٍ التحقق...", "checkingDatabase": "جارٍ التحقق من اتصال قاعدة البيانات...", "checkingAuthentication": "جارٍ التحقق من المصادقة...", - "backendReconnected": "تم استعادة اتصال الخادم", - "connectionDegraded": "انقطع الاتصال بالخادم، جارٍ استعادة الاتصال…", - "reload": "أعد تحميل الصفحة", - "remove": "يزيل", - "create": "يخلق", + "backendReconnected": "تمت استعادة الاتصال بالخادم", + "connectionDegraded": "فُقد الاتصال بالخادم، جارٍ الاستعادة...", + "reload": "إعادة تحميل", + "remove": "إزالة", + "create": "إنشاء", "update": "تحديث", - "copy": "ينسخ", + "copy": "نسخ", "copyFailed": "فشل النسخ إلى الحافظة", - "maximize": "تحقيق أقصى استفادة", - "restore": "يعيد", - "of": "ل", - "saved": "أنقذ", - "deleted": "تم الحذف", - "deleteFailed": "فشل الحذف", - "saveFailed": "فشل الحفظ", + "maximize": "تكبير", + "restore": "استعادة", + "of": "من", + "saved": "محفوظ", + "deleted": "محذوف", + "deleteFailed": "فشل في الحذف", + "saveFailed": "فشل في الحفظ", "required": "مطلوب" }, "nav": { - "home": "بيت", - "terminal": "صالة", - "docker": "عامل ميناء", + "home": "الرئيسية", + "terminal": "الطرفية", + "docker": "Docker", "tunnels": "الأنفاق", "fileManager": "مدير الملفات", "serverStats": "مقاييس المضيف", "hostMetrics": "مقاييس المضيف", - "admin": "مسؤل", - "termixId": "بطاقة تعريف", - "userProfile": "ملف تعريف المستخدم", - "splitScreen": "شاشة مقسمة", + "admin": "الإدارة", + "termixId": "معرف", + "userProfile": "ملف المستخدم", + "splitScreen": "تقسيم الشاشة", "confirmClose": "هل تريد إغلاق هذه الجلسة النشطة؟", - "close": "يغلق", - "cancel": "يلغي", + "close": "إغلاق", + "cancel": "إلغاء", "sshManager": "مدير SSH", - "cannotSplitTab": "لا يمكن تقسيم هذه الصفحة", + "cannotSplitTab": "لا يمكن تقسيم علامة التبويب هذه", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "انسخ كلمة المرور", - "copySudoPassword": "انسخ كلمة مرور sudo", + "copyPassword": "نسخ كلمة المرور", + "copySudoPassword": "نسخ كلمة مرور Sudo", "passwordCopied": "تم نسخ كلمة المرور إلى الحافظة", "noPasswordAvailable": "لا توجد كلمة مرور متاحة", "failedToCopyPassword": "فشل نسخ كلمة المرور", "refreshTab": "تحديث الاتصال", "renameTab": "إعادة تسمية علامة التبويب", - "openFileManager": "مدير الملفات المفتوح", - "dashboard": "لوحة التحكم", + "openFileManager": "فتح مدير الملفات", + "dashboard": "لوحة المعلومات", "networkGraph": "رسم بياني للشبكة", - "tmuxMonitor": "شاشة Tmux", + "tmuxMonitor": "مراقب Tmux", "homepage": "الصفحة الرئيسية", - "quickConnect": "توصيل سريع", + "quickConnect": "اتصال سريع", "sshTools": "أدوات SSH", - "history": "تاريخ", + "history": "السجل", "sessionLogs": "سجلات الجلسات", "hosts": "المضيفون", - "snippets": "مقتطفات", - "hostManager": "مدير الاستضافة", - "credentials": "أوراق اعتماد", + "snippets": "المقتطفات", + "hostManager": "مدير المضيفين", + "credentials": "بيانات الاعتماد", "connections": "الاتصالات", "alerts": "التنبيهات", - "serial": "مسلسل", - "roleAdministrator": "المسؤول", + "serial": "تسلسلي", + "roleAdministrator": "مسؤول", "roleUser": "مستخدم" }, "hosts": { "hosts": "المضيفون", - "noHosts": "لا يوجد مضيفون SSH", + "noHosts": "لا توجد مضيفات SSH", "retry": "إعادة المحاولة", - "refresh": "ينعش", - "optional": "خياري", + "refresh": "تحديث", + "optional": "اختياري", "downloadSample": "تنزيل عينة", "failedToDeleteHost": "فشل حذف {{name}}", - "importSkipExisting": "استيراد (تجاهل الموجود)", - "importSSHConfig": "الاستيراد من إعدادات SSH", + "importSkipExisting": "استيراد (تخطي الموجود)", + "importSSHConfig": "استيراد من إعدادات SSH", "connectionDetails": "تفاصيل الاتصال", "ssh": "SSH", - "telnet": "تيلنت", + "telnet": "Telnet", "remoteDesktop": "سطح المكتب البعيد", - "port": "ميناء", + "port": "المنفذ", "username": "اسم المستخدم", - "folder": "مجلد", + "folder": "المجلد", "tags": "الوسوم", - "pin": "دبوس", + "pin": "تثبيت", "addHost": "إضافة مضيف", - "editHost": "تعديل المضيف", - "cloneHost": "مضيف الاستنساخ", - "enableTerminal": "تفعيل الطرفية", - "enableTunnel": "تفعيل النفق", - "enableFileManager": "تفعيل مدير الملفات", - "enableDocker": "تفعيل Docker", + "editHost": "تحرير المضيف", + "cloneHost": "استنساخ المضيف", + "enableTerminal": "تمكين الطرفية", + "enableTunnel": "تمكين النفق", + "enableFileManager": "تمكين مدير الملفات", + "enableDocker": "تمكين Docker", "defaultPath": "المسار الافتراضي", - "connection": "اتصال", + "connection": "الاتصال", "upload": "رفع", "authentication": "المصادقة", "password": "كلمة المرور", - "key": "مفتاح", + "key": "المفتاح", "credential": "بيانات الاعتماد", - "none": "لا أحد", + "none": "لا يوجد", "sshPrivateKey": "مفتاح SSH الخاص", "keyType": "نوع المفتاح", - "uploadFile": "تحميل الملف", + "uploadFile": "رفع ملف", "tabGeneral": "عام", "tabSsh": "SSH", - "tabTerminal": "صالة", + "tabTerminal": "الطرفية", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "الأنفاق", - "tabDocker": "عامل ميناء", + "tabDocker": "Docker", "tabFiles": "الملفات", "tabStats": "مقاييس المضيف", "tabHostMetrics": "مقاييس المضيف", - "tabTelnet": "تيلنت", - "tabSharing": "مشاركة", + "tabTelnet": "Telnet", + "tabSharing": "المشاركة", "tabAuthentication": "المصادقة", - "terminal": "صالة", - "tunnel": "نفق", + "terminal": "الطرفية", + "tunnel": "النفق", "fileManager": "مدير الملفات", "serverStats": "مقاييس المضيف", - "status": "حالة", + "status": "الحالة", "folderRenamed": "تمت إعادة تسمية المجلد \"{{oldName}}\" إلى \"{{newName}}\" بنجاح", - "failedToRenameFolder": "فشل إعادة تسمية المجلد", - "movedToFolder": "تم نقل المضيف (المضيفين) {{count}} إلى \"{{folder}}\"", + "failedToRenameFolder": "فشلت إعادة تسمية المجلد", + "movedToFolder": "تم نقل {{count}} مضيف إلى \"{{folder}}\"", "editHostTooltip": "تعديل المضيف", - "statusChecks": "التحقق من الحالة", - "metricsCollection": "مجموعة المقاييس", + "statusChecks": "فحوصات الحالة", + "metricsCollection": "جمع المقاييس", "metricsInterval": "فترة جمع المقاييس", - "metricsIntervalDesc": "عدد مرات جمع إحصائيات الخادم (5 ثوانٍ - ساعة واحدة)", - "behavior": "سلوك", - "themePreview": "معاينة القالب", - "theme": "سمة", + "metricsIntervalDesc": "مدة جمع إحصائيات الخادم (5s - 1h)", + "behavior": "السلوك", + "themePreview": "معاينة السمة", + "theme": "السمة", "fontFamily": "عائلة الخط", "fontSize": "حجم الخط", "letterSpacing": "تباعد الأحرف", "lineHeight": "ارتفاع السطر", "cursorStyle": "نمط المؤشر", "cursorBlink": "وميض المؤشر", - "scrollbackBuffer": "مخزن التمرير الخلفي", + "scrollbackBuffer": "مخزن التمرير", "bellStyle": "نمط الجرس", - "rightClickSelectsWord": "انقر بزر الماوس الأيمن لتحديد كلمة", + "rightClickSelectsWord": "النقر باليمين يحدد الكلمة", "fastScrollModifier": "مُعدِّل التمرير السريع", "fastScrollSensitivity": "حساسية التمرير السريع", "sshAgentForwarding": "إعادة توجيه وكيل SSH", - "backspaceMode": "وضع الحذف", + "backspaceMode": "نمط الحذف", "startupSnippet": "مقتطف بدء التشغيل", - "selectSnippet": "حدد المقتطف", - "forceKeyboardInteractive": "لوحة مفاتيح تفاعلية", - "overrideCredentialUsername": "اسم مستخدم بيانات الاعتماد المتجاوز", - "overrideCredentialUsernameDesc": "استخدم اسم المستخدم المحدد أعلاه بدلاً من اسم مستخدم بيانات الاعتماد", - "oidcUsernameHint": "استخدم $oidc.preferred_username لاستبدال اسم تسجيل الدخول الخاص بك في OIDC.", - "jumpHostChain": "سلسلة مضيف القفز", - "portKnocking": "طرق الميناء", + "selectSnippet": "اختيار مقتطف", + "forceKeyboardInteractive": "فرض تفاعل لوحة المفاتيح", + "overrideCredentialUsername": "تجاوز اسم مستخدم الاعتماد", + "overrideCredentialUsernameDesc": "استخدم اسم المستخدم المحدد أعلاه بدلاً من اسم مستخدم الاعتماد", + "oidcUsernameHint": "استخدم $oidc.preferred_username لاستبدال اسم تسجيل الدخول الخاص بـ OIDC.", + "tailscaleUsernameHint": "يجب أن يكون هذا مستخدمًا لنظام Unix، حيث يتم منح هوية Tailscale الخاصة بك في قائمة التحكم بالوصول SSH الخاصة بشبكة tailnet، وليس بالضرورة المستخدم الجذر.", + "jumpHostChain": "سلسلة مضيفات الانتقال", + "portKnocking": "طرق المنفذ", "addKnock": "إضافة منفذ", "addProxyNode": "إضافة عقدة", "proxyNode": "عقدة الوكيل", "proxyType": "نوع الوكيل", "quickActions": "إجراءات سريعة", - "sudoPasswordAutoFill": "التعبئة التلقائية لكلمة مرور sudo", - "sudoPassword": "كلمة مرور sudo", - "keepaliveInterval": "فترة الإبقاء على الاتصال (مللي ثانية)", + "sudoPasswordAutoFill": "التعبئة التلقائية لكلمة مرور Sudo", + "sudoPassword": "كلمة مرور Sudo", + "keepaliveInterval": "فاصل الإبقاء (مللي ثانية)", "moshCommand": "أمر MOSH", - "environmentVariables": "المتغيرات البيئية", + "environmentVariables": "متغيرات البيئة", "addVariable": "إضافة متغير", - "docker": "عامل ميناء", - "copyTerminalUrl": "انسخ عنوان URL الخاص بالطرفية", - "copyFileManagerUrl": "انسخ رابط مدير الملفات", - "copyRemoteDesktopUrl": "انسخ عنوان URL لسطح المكتب البعيد", + "docker": "Docker", + "copyTerminalUrl": "نسخ URL الطرفية", + "copyFileManagerUrl": "نسخ URL مدير الملفات", + "copyRemoteDesktopUrl": "نسخ URL سطح المكتب البعيد", "failedToConnect": "فشل الاتصال بوحدة التحكم", - "connect": "يتصل", + "connect": "اتصل", "disconnect": "قطع الاتصال", - "start": "يبدأ", + "start": "بدء", "enableStatusCheck": "تفعيل فحص الحالة", "enableMetrics": "تفعيل المقاييس", - "bulkUpdateFailed": "فشل التحديث المجمع", - "selectAll": "حدد الكل", - "deselectAll": "قم بإلغاء تحديد الكل", + "bulkUpdateFailed": "فشل التحديث الجماعي", + "selectAll": "تحديد الكل", + "deselectAll": "إلغاء تحديد الكل", "protocols": "البروتوكولات", - "secureShell": "صدفة آمنة", + "secureShell": "Secure Shell", "virtualNetwork": "الشبكة الافتراضية", - "unencryptedShell": "غلاف غير مشفر", + "unencryptedShell": "shell غير مشفر", "addressIp": "العنوان / IP", - "friendlyName": "اسم ودود", + "friendlyName": "الاسم المألوف", "macAddress": "عنوان MAC", - "wolBroadcastAddress": "خطاب بث WOL", - "wolBroadcastAddressDesc": "بث موجه اختياري لشبكات Docker/الموجهة (مثل 192.168.1.255). اتركه فارغًا لاستخدام 255.255.255.255.", - "folderAndAdvanced": "المجلد والخيارات المتقدمة", + "wolBroadcastAddress": "عنوان بث WoL", + "wolBroadcastAddressDesc": "بث موجه اختياري لشبكات Docker/الموجهة (مثال: 192.168.1.255). اتركه فارغًا لاستخدام 255.255.255.255.", + "folderAndAdvanced": "المجلد والمتقدم", "privateNotes": "ملاحظات خاصة", "privateNotesPlaceholder": "تفاصيل حول هذا الخادم...", - "pinToTop": "قم بتثبيته في الأعلى", - "pinToTopDesc": "اعرض هذا المضيف دائمًا في أعلى القائمة", - "portKnockingSequence": "تسلسل طرق المنافذ", - "addKnockBtn": "أضف طرقًا", - "noPortKnocking": "لم يتم تكوين خاصية طرق المنافذ.", - "knockPort": "ميناء نوك", - "protocol": "بروتوكول", + "pinToTop": "تثبيت في الأعلى", + "pinToTopDesc": "إظهار هذا المضيف دائمًا في أعلى القائمة", + "portKnockingSequence": "تسلسل طرق المنفذ", + "addKnockBtn": "إضافة طرق", + "noPortKnocking": "لم يتم تكوين أي إجراء لطرق المنافذ.", + "knockPort": "منفذ الطرق", + "protocol": "البروتوكول", "delayAfterMs": "التأخير بعد (مللي ثانية)", - "useSocks5Proxy": "استخدم بروكسي SOCKS5", + "useSocks5Proxy": "استخدام وكيل SOCKS5", "useSocks5ProxyDesc": "توجيه الاتصال عبر خادم وكيل", + "connectionOrigin": "أصل الاتصالات", + "connectionOriginDesc": "مصدر اتصال SSH لهذا المضيف. يتجاوز هذا الإعداد الإعداد الافتراضي العام لتطبيق سطح المكتب.", + "connectionOriginDefault": "استخدم الإعدادات الافتراضية", + "connectionOriginLocal": "هذا الجهاز (الشبكة المحلية)", + "connectionOriginRemote": "خادم بعيد", "proxyHost": "مضيف الوكيل", - "proxyPort": "منفذ بروكسي", + "proxyPort": "منفذ الوكيل", "proxyUsername": "اسم مستخدم الوكيل", "proxyPassword": "كلمة مرور الوكيل", - "proxySingleMode": "وكيل واحد", + "proxySingleMode": "وكيل فردي", "proxyChainMode": "سلسلة الوكلاء", "you": "أنت", - "jumpHostChainLabel": "سلسلة مضيف القفز", - "addJumpBtn": "أضف قفزة", + "jumpHostChainLabel": "سلسلة مضيفات القفز", + "addJumpBtn": "إضافة قفزة", "noJumpHosts": "لم يتم تكوين أي مضيفات قفز.", - "selectAServer": "اختر خادمًا...", + "selectAServer": "اختيار خادم...", "sshPort": "منفذ SSH", "authMethod": "طريقة المصادقة", - "storedCredential": "بيانات الاعتماد المخزنة", - "selectACredential": "اختر بيانات اعتماد...", - "vaultProfile": "ملف تعريف مُوقِّع الخزينة", - "selectAVaultProfile": "حدد ملف تعريف Vault...", - "vaultProfileHint": "تُستمد الإعدادات من الملف الشخصي المشترك؛ وستقوم بتسجيل الدخول إلى Vault عبر OIDC عند الاتصال. لا يتم تخزين أي بيانات سرية.", - "vaultNewProfile": "ملف تعريف جديد", - "vaultManageProfiles": "إدارة ملفات تعريف الخزينة", - "vaultAddrLabel": "عنوان الخزنة", + "storedCredential": "بيانات اعتماد مخزنة", + "selectACredential": "اختيار بيانات اعتماد...", + "vaultProfile": "ملف توقيع Vault", + "selectAVaultProfile": "اختيار ملف توقيع Vault...", + "vaultProfileHint": "تأتي الإعدادات من الملف المشترك؛ ستقوم بتسجيل الدخول إلى Vault عبر OIDC عند الاتصال. لا يتم تخزين أي أسرار.", + "vaultNewProfile": "ملف جديد", + "vaultManageProfiles": "إدارة ملفات Vault", + "vaultAddrLabel": "عنوان Vault", "vaultNamespaceLabel": "مساحة الاسم", - "vaultOidcMountLabel": "OIDC Auth Mount", - "vaultOidcRoleLabel": "دور منظمة تنمية البنية التحتية المتكاملة", + "vaultOidcMountLabel": "تثبيت مصادقة OIDC", + "vaultOidcRoleLabel": "دور OIDC", "vaultSshMountLabel": "تثبيت أسرار SSH", - "vaultSshRoleLabel": "دور مُوقِّع SSH", - "vaultValidPrincipalsLabel": "المبادئ الصحيحة", + "vaultSshRoleLabel": "دور موقع SSH", + "vaultValidPrincipalsLabel": "المستخدمون المسموح بهم", "vaultKeyTypeLabel": "نوع المفتاح المؤقت", - "vaultSharedLabel": "شارك مع جميع المستخدمين", + "vaultSharedLabel": "مشاركة مع جميع المستخدمين", "vaultCreateProfile": "إنشاء ملف تعريف", - "vaultProfileCreated": "تم إنشاء ملف تعريف الخزنة", - "vaultProfileSaved": "تم حفظ ملف تعريف الخزنة", - "vaultProfileDeleted": "تم حذف ملف تعريف الخزنة", + "vaultProfileCreated": "تم إنشاء ملف تعريف Vault", + "vaultProfileSaved": "تم حفظ ملف تعريف Vault", + "vaultProfileDeleted": "تم حذف ملف تعريف Vault", "vaultProfileSaveFailed": "فشل حفظ ملف تعريف Vault", "vaultProfileDeleteFailed": "فشل حذف ملف تعريف Vault", - "vaultSaveProfile": "حفظ الملف الشخصي", - "vaultProfileValidationError": "الاسم وعنوان الخزنة ودور مُوقِّع SSH مطلوبان", - "vaultNoProfiles": "لا توجد ملفات تعريف للخزنة حتى الآن.", - "vaultSharedBadge": "تمت المشاركة", + "vaultSaveProfile": "حفظ ملف التعريف", + "vaultProfileValidationError": "الاسم وعنوان Vault ودور الموقّع SSH مطلوب", + "vaultNoProfiles": "لا توجد ملفات تعريف Vault بعد.", + "vaultSharedBadge": "مشترك", "keyTypeLabel": "نوع المفتاح", - "keyTypeAuto": "الكشف التلقائي", + "keyTypeAuto": "اكتشاف تلقائي", "keyPasteTab": "لصق", "keyUploadTab": "رفع", "keyFileLoaded": "تم تحميل ملف المفتاح", - "keyUploadClick": "انقر لتحميل ملفات بصيغة .pem / .key / .ppk", - "clearKey": "مفتاح واضح", + "keyUploadClick": "انقر لرفع ملف .pem / .key / .ppk", + "clearKey": "مسح المفتاح", "keySaved": "تم حفظ مفتاح SSH", - "keyReplaceNotice": "ألصق مفتاحًا جديدًا أدناه لاستبداله", - "keyPassphraseSaved": "تم حفظ عبارة المرور، اكتب لتغييرها", + "keyReplaceNotice": "الصق مفتاحًا جديدًا أدناه لاستبداله", + "keyPassphraseSaved": "تم حفظ عبارة المرور، اكتب للتغيير", "replaceKey": "استبدال المفتاح", "docsLink": "عرض المستندات", "opksshLabel": "OPKSSH", - "opksshDesc": "سجّل الدخول إلى هذا المضيف باستخدام موفر الهوية الخاص بك بدلاً من كلمة المرور أو المفتاح. يتطلب ذلك تثبيت OPKSSH على الخادم.", - "warpgateLabel": "بوابة الالتواء", - "warpgateDesc": "يتصل هذا المضيف عبر خادم وكيل SSH من Warpgate. وسيتولى Termix عملية الموافقة عبر المتصفح تلقائيًا بعد المصادقة.", + "opksshDesc": "تسجيل الدخول إلى هذا المضيف باستخدام مزود الهوية بدلاً من كلمة المرور أو المفتاح. يتطلب إعداد OPKSSH على الخادم.", + "warpgateLabel": "بوابة Warpgate", + "warpgateDesc": "يتصل هذا المضيف عبر وكيل SSH من Warpgate. سيتولى Termix عملية الموافقة عبر المتصفح تلقائيًا بعد المصادقة.", "agentLabel": "وكيل SSH", - "agentDesc": "قم بالمصادقة باستخدام وكيل SSH يعمل على خادم Termix (Bitwarden، 1Password، gpg-agent، KeeAgent، ssh-agent). يجب أن يكون الوكيل قيد التشغيل على الجهاز الذي يستضيف Termix.", + "agentDesc": "المصادقة باستخدام وكيل SSH يعمل على خادم Termix (مثل Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). يجب أن يكون الوكيل قيد التشغيل على الجهاز الذي يستضيف Termix.", "agentSocketPathLabel": "مسار مقبس الوكيل", "agentSocketPathPlaceholder": "اتركه فارغًا لاستخدام SSH_AUTH_SOCK", - "agentSocketPathHint": "اتركه فارغًا ليتم الكشف عنه تلقائيًا من متغير البيئة SSH_AUTH_SOCK، أو أدخل مسار مقبس مخصص (على سبيل المثال /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "حدد جهاز قياس الذيل", + "agentSocketPathHint": "اتركه فارغًا للاكتشاف التلقائي من متغير البيئة SSH_AUTH_SOCK، أو أدخل مسار مقبس مخصص (مثال: /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "اختيار جهاز Tailscale", "tailscaleDeviceSelectPlaceholder": "اختر جهازًا...", - "tailscaleNoApiKey": "لم يتم تكوين مفتاح واجهة برمجة تطبيقات Tailscale. أضف مفتاحًا في إعدادات المسؤول لتمكين اكتشاف الجهاز.", + "tailscaleNoApiKey": "لم يتم تكوين مفتاح API لـ Tailscale. أضف واحدًا في إعدادات المسؤول لتمكين اكتشاف الأجهزة.", "tailscaleDocsLink": "عرض المستندات", "tailscaleLoadingDevices": "جارٍ تحميل الأجهزة...", - "tailscaleNoDevices": "لم يتم العثور على أي أجهزة في شبكتك المحلية.", - "tailscaleDeviceAutoFill": "سيؤدي اختيار جهاز إلى ملء عنوان IP الخاص بالمضيف تلقائيًا.", - "forceKeyboardInteractiveLabel": "لوحة مفاتيح تفاعلية", - "forceKeyboardInteractiveShortDesc": "فرض إدخال كلمة المرور يدويًا حتى في حالة وجود المفاتيح", + "tailscaleNoDevices": "لم يتم العثور على أجهزة في tailnet الخاص بك.", + "tailscaleDeviceAutoFill": "سيؤدي تحديد جهاز إلى تعبئة عنوان IP للمضيف تلقائيًا.", + "forceKeyboardInteractiveLabel": "فرض التفاعل عبر لوحة المفاتيح", + "forceKeyboardInteractiveShortDesc": "فرض إدخال كلمة المرور يدويًا حتى لو كانت المفاتيح موجودة", "allowLegacyAlgorithmsLabel": "السماح بالخوارزميات القديمة", - "allowLegacyAlgorithmsDesc": "قم بتمكين خوارزميات SSH المهملة (ssh-dss، ssh-rsa، diffie-hellman-group1-sha1، hmac-md5، 3des-cbc) للاتصالات بالأجهزة القديمة التي لا يمكن ترقيتها.", + "allowLegacyAlgorithmsDesc": "تمكين خوارزميات SSH المهملة (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) للاتصال بالأجهزة القديمة التي لا يمكن ترقيتها.", "insecure": "غير آمن", - "terminalAppearance": "مظهر الجهاز", + "terminalAppearance": "مظهر الطرفية", "colorTheme": "نظام الألوان", "fontFamilyLabel": "عائلة الخط", "fontSizeLabel": "حجم الخط", @@ -730,228 +775,239 @@ "letterSpacingPx": "تباعد الأحرف (بكسل)", "lineHeightLabel": "ارتفاع السطر", "bellStyleLabel": "نمط الجرس", - "backspaceModeLabel": "وضع الحذف", + "backspaceModeLabel": "وضع المسح الخلفي", "cursorBlinking": "وميض المؤشر", - "cursorBlinkingDesc": "تفعيل خاصية وميض مؤشر الطرفية", - "rightClickSelectsWordLabel": "النقر بزر الماوس الأيمن يحدد كلمة", - "rightClickSelectsWordShortDesc": "حدد الكلمة الموجودة أسفل المؤشر عند النقر بزر الماوس الأيمن", + "cursorBlinkingDesc": "تمكين تأثير وميض المؤشر في الطرفية", + "rightClickSelectsWordLabel": "تحديد الكلمة بالنقر الأيمن", + "rightClickSelectsWordShortDesc": "تحديد الكلمة تحت المؤشر عند النقر بالزر الأيمن", "backgroundImageLabel": "رابط صورة الخلفية", "backgroundImageDesc": "رابط اختياري لصورة خلفية الطرفية", "backgroundImageOpacityLabel": "شفافية صورة الخلفية", "customThemeColors": "ألوان مخصصة", - "customThemeBackground": "خلفية", + "customThemeBackground": "الخلفية", "customThemeForeground": "المقدمة", "customThemeCursor": "المؤشر", - "customThemeCursorAccent": "علامة المؤشر", - "customThemeSelection": "اختيار", + "customThemeCursorAccent": "تمييز المؤشر", + "customThemeSelection": "التحديد", "customThemeAnsiColors": "ألوان ANSI", "customThemeBlack": "أسود", "customThemeRed": "أحمر", "customThemeGreen": "أخضر", "customThemeYellow": "أصفر", "customThemeBlue": "أزرق", - "customThemeMagenta": "أرجواني", + "customThemeMagenta": "ماجنتا", "customThemeCyan": "سماوي", "customThemeWhite": "أبيض", - "customThemeBrightBlack": "أسود لامع", - "customThemeBrightRed": "أحمر فاقع", - "customThemeBrightGreen": "أخضر فاتح", - "customThemeBrightYellow": "أصفر فاقع", - "customThemeBrightBlue": "أزرق فاتح", - "customThemeBrightMagenta": "لون أرجواني فاتح", - "customThemeBrightCyan": "أزرق فاتح", - "customThemeBrightWhite": "أبيض ناصع", - "customThemeResetTooltip": "إعادة ضبط الإعدادات الافتراضية", - "syntaxHighlightingLabel": "تمييز بناء الجملة", + "customThemeBrightBlack": "أسود ساطع", + "customThemeBrightRed": "أحمر ساطع", + "customThemeBrightGreen": "أخضر ساطع", + "customThemeBrightYellow": "أصفر ساطع", + "customThemeBrightBlue": "أزرق ساطع", + "customThemeBrightMagenta": "ماجنتا ساطع", + "customThemeBrightCyan": "سماوي ساطع", + "customThemeBrightWhite": "أبيض ساطع", + "customThemeResetTooltip": "إعادة تعيين إلى الإعدادات الافتراضية", + "savedThemesLabel": "السمات المحفوظة", + "saveAsGlobalTheme": "حفظ كقالب عام", + "saveGlobalThemeNamePrompt": "أدخل اسمًا لهذا القالب", + "saveGlobalThemeSuccess": "حفظ السمة", + "saveGlobalThemeError": "فشل حفظ السمة", + "applyGlobalThemeTooltip": "قم بتطبيق هذا القالب", + "deleteGlobalThemeTooltip": "احذف هذا القالب", + "noSavedThemes": "لم يتم حفظ أي سمات بعد", + "syntaxHighlightingLabel": "تمييز الصيغة", "syntaxHighlightingDesc": "تلوين مخرجات الطرفية (الأخطاء، المسارات، عناوين IP، الطوابع الزمنية)", - "syntaxHighlightingCategories": "أبرز الفئات", - "syntaxHighlightingCategoriesDesc": "اختر أنواع المحتوى التي تريد تلوينها", + "syntaxHighlightingCategories": "فئات التمييز", + "syntaxHighlightingCategoriesDesc": "اختر أنواع المحتوى المراد تلوينها", "syntaxCategoryLogLevels": "مستويات السجل", - "syntaxCategoryLogLevelsDesc": "خطأ، تحذير، معلومات، تصحيح الأخطاء، خطأ فادح", + "syntaxCategoryLogLevelsDesc": "خطأ، تحذير، معلومات، تنقيح، فادح", "syntaxCategoryPaths": "مسارات الملفات", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "الطوابع الزمنية", - "syntaxCategoryTimestampsDesc": "[12:34:56]، 2024-01-15", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "عناوين IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "عناوين URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "أرقام مُعَلَّمة", - "syntaxCategoryNumbersDesc": "المنفذ 8080، مخرج 1، حالة 404", - "behaviorAndAdvanced": "السلوك والمهارات المتقدمة", - "scrollbackBufferLabel": "مخزن التمرير الخلفي", + "syntaxCategoryNumbers": "أرقام معنونة", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "السلوك والمتقدم", + "scrollbackBufferLabel": "مخزن التمرير المؤقت", "scrollbackMaxLines": "الحد الأقصى لعدد الأسطر المحفوظة في السجل", - "sshAgentForwardingLabel": "إعادة توجيه وكيل SSH", - "sshAgentForwardingShortDesc": "مرر مفاتيح SSH المحلية الخاصة بك إلى هذا المضيف", - "useSSHTitleLabel": "استخدم عنوان نافذة SSH", - "useSSHTitleDesc": "قم بتحديث عنوان علامة التبويب من عنوان نافذة الصدفة بدلاً من اسم المضيف", - "enableAutoMosh": "تفعيل خاصية Auto-Mosh", - "enableAutoMoshDesc": "يُفضّل استخدام Mosh بدلاً من SSH إن كان ذلك متاحاً.", - "enableAutoTmux": "تفعيل خاصية Auto-Tmux", - "enableAutoTmuxDesc": "تشغيل جلسة tmux أو ربطها بها تلقائيًا", + "sshAgentForwardingLabel": "توجيه وكيل SSH", + "sshAgentForwardingShortDesc": "تمرير مفاتيح SSH المحلية إلى هذا المضيف", + "useSSHTitleLabel": "استخدام عنوان نافذة SSH", + "useSSHTitleDesc": "تحديث عنوان التبويب من عنوان نافذة الصدفة بدلاً من اسم المضيف", + "enableAutoMosh": "تمكين Mosh التلقائي", + "enableAutoMoshDesc": "تفضيل Mosh على SSH إذا كان متاحاً", + "enableAutoTmux": "تمكين tmux التلقائي", + "enableAutoTmuxDesc": "تشغيل أو الإلحاق بجلسة tmux تلقائياً", "enableSessionLogging": "تسجيل الجلسات", - "enableSessionLoggingDesc": "سجل مخرجات جلسة الطرفية للمراجعة لاحقًا", + "enableSessionLoggingDesc": "تسجيل مخرجات الجلسة الطرفية للمراجعة لاحقاً", + "allowSessionSharing": "السماح بمشاركة الجلسة", + "allowSessionSharingDesc": "يمكن مشاركة الجلسات المباشرة على هذا المضيف عبر رابط أو مع مستخدمين آخرين", "enableCommandHistory": "سجل الأوامر", - "enableCommandHistoryDesc": "سجل الأوامر التي يتم تنفيذها في هذه المحطة الطرفية للاطلاع على سجلها وميزة الإكمال التلقائي.", + "enableCommandHistoryDesc": "تسجيل الأوامر المنفذة في هذه الطرفية للسجل والإكمال التلقائي", "linkClickBehaviorLabel": "سلوك النقر على الرابط", - "linkClickBehaviorDesc": "يتحكم هذا الخيار في ما يحدث عند النقر على رابط في نافذة الأوامر. استخدم \"افتراضي\" لاتباع إعدادات التطبيق.", - "linkClickBehaviorDefault": "الوضع الافتراضي (اتبع إعدادات التطبيق)", - "linkClickBehaviorConfirm": "أظهر النافذة المنبثقة لفتحها أو نسخها", - "linkClickBehaviorDirect": "افتح مباشرة", - "sudoPasswordAutoFillLabel": "تعبئة كلمة مرور sudo تلقائيًا", - "sudoPasswordAutoFillShortDesc": "قم بتوفير كلمة مرور sudo تلقائيًا عند المطالبة بها.", - "sudoPasswordAutoFillDesc": "قم بتخزين كلمة مرور sudo حتى تتمكن مقاييس المضيف وموجهات الطرفية وغيرها من تشغيل الأوامر ذات الامتيازات تلقائيًا.", + "linkClickBehaviorDesc": "يتحكم في ما يحدث عند النقر على رابط في الطرفية. استخدم 'افتراضي' لاتباع إعداد التطبيق العام.", + "linkClickBehaviorDefault": "افتراضي (اتباع إعداد التطبيق)", + "linkClickBehaviorConfirm": "إظهار نافذة منبثقة للفتح أو النسخ", + "linkClickBehaviorDirect": "فتح مباشرةً", + "sudoPasswordAutoFillLabel": "الملء التلقائي لكلمة مرور sudo", + "sudoPasswordAutoFillShortDesc": "توفير كلمة مرور sudo تلقائياً عند المطالبة", + "sudoPasswordAutoFillDesc": "تخزين كلمة مرور sudo لتشغيل مقاييس المضيف ومطالبات الطرفية وغيرها من الأوامر المميزة تلقائياً.", "sudoPasswordLabel": "كلمة مرور sudo", - "environmentVariablesLabel": "المتغيرات البيئية", + "environmentVariablesLabel": "متغيرات البيئة", "addVariableBtn": "إضافة متغير", - "noEnvVars": "لم يتم تكوين أي متغيرات بيئية.", - "fastScrollModifierLabel": "مُعدِّل التمرير السريع", + "noEnvVars": "لم يتم تكوين أي متغيرات البيئة.", + "fastScrollModifierLabel": "مفتاح التمرير السريع", "fastScrollSensitivityLabel": "حساسية التمرير السريع", - "moshCommandLabel": "موش كوماند", + "moshCommandLabel": "أمر Mosh", "startupSnippetLabel": "مقتطف بدء التشغيل", - "keepaliveIntervalLabel": "فترة الإبقاء على الاتصال (بالثواني)", - "maxKeepaliveMisses": "فشل ماكس في الحفاظ على الحياة", + "keepaliveIntervalLabel": "فترة إبقاء الاتصال (بالثواني)", + "maxKeepaliveMisses": "الحد الأقصى لمرات فقدان إبقاء الاتصال", "tunnelSettings": "إعدادات النفق", - "enableTunneling": "تفعيل النفق", - "enableTunnelingDesc": "قم بتمكين وظيفة نفق SSH لهذا المضيف", + "enableTunneling": "تمكين الأنفاق", + "enableTunnelingDesc": "تمكين وظيفة نفق SSH لهذا المضيف", "serverTunnelsSection": "أنفاق الخادم", "addTunnelBtn": "إضافة نفق", "noTunnelsConfigured": "لم يتم تكوين أي أنفاق.", "tunnelLabel": "نفق {{number}}", "tunnelType": "نوع النفق", - "tunnelModeLocalDesc": "قم بتوجيه منفذ محلي إلى منفذ على الخادم البعيد (أو مضيف يمكن الوصول إليه منه).", - "tunnelModeRemoteDesc": "قم بإعادة توجيه منفذ على الخادم البعيد إلى منفذ محلي على جهازك.", - "tunnelModeDynamicDesc": "قم بإنشاء وكيل SOCKS5 على منفذ محلي لإعادة توجيه المنافذ الديناميكية.", + "tunnelModeLocalDesc": "توجيه منفذ محلي إلى منفذ على الخادم البعيد (أو مضيف يمكن الوصول إليه منه).", + "tunnelModeRemoteDesc": "إعادة توجيه منفذ على الخادم البعيد إلى منفذ محلي على جهازك.", + "tunnelModeDynamicDesc": "إنشاء وكيل SOCKS5 على منفذ محلي لتوجيه المنافذ الديناميكي.", "sameHost": "هذا المضيف (نفق مباشر)", "endpointHost": "مضيف نقطة النهاية", - "endpointHostPlaceholder": "نفس المضيف، أو مضيف SSH، أو مضيف/عنوان IP يمكن الوصول إليه", + "endpointHostPlaceholder": "نفس المضيف، مضيف SSH، أو مضيف/IP يمكن الوصول إليه", "endpointPort": "منفذ نقطة النهاية", - "bindHost": "ربط المضيف", + "bindHost": "مضيف الربط", "sourcePort": "منفذ المصدر", - "maxRetries": "أقصى عدد من المحاولات", - "retryIntervalS": "فترة إعادة المحاولة (s)", - "autoStartLabel": "التشغيل التلقائي", - "autoStartDesc": "قم بتوصيل هذا النفق تلقائيًا عند تحميل المضيف", - "tunnelConnecting": "نفق يربط...", - "tunnelDisconnected": "تم فصل النفق", + "maxRetries": "الحد الأقصى لإعادة المحاولات", + "retryIntervalS": "فترة إعادة المحاولة (بالثواني)", + "autoStartLabel": "تشغيل تلقائي", + "autoStartDesc": "الاتصال بهذا النفق تلقائياً عند تحميل المضيف", + "tunnelConnecting": "جارٍ توصيل النفق...", + "tunnelDisconnected": "تم قطع اتصال النفق", "failedToConnectTunnel": "فشل الاتصال", - "failedToDisconnectTunnel": "فشل في قطع الاتصال", - "dockerIntegration": "تكامل دوكر", - "enableDockerMonitor": "تفعيل Docker", + "failedToDisconnectTunnel": "فشل قطع الاتصال", + "dockerIntegration": "تكامل Docker", + "enableDockerMonitor": "تمكين Docker", "enableDockerMonitorDesc": "مراقبة وإدارة الحاويات على هذا المضيف عبر Docker", "containerRuntime": "بيئة تشغيل الحاويات", "containerRuntimeDesc": "اختر واجهة سطر الأوامر المستخدمة لإدارة الحاويات على هذا المضيف", - "containerRuntimeDocker": "عامل ميناء", - "containerRuntimePodman": "بودمان", - "enableTmuxMonitor": "تفعيل مراقب Tmux", - "enableTmuxMonitorDesc": "اعرض هذا المضيف في شاشة مراقبة Tmux وأضف إجراءات Tmux الخاصة به إلى الشريط الجانبي", - "tabProxmox": "بروكسموكس", + "containerRuntimeDocker": "Docker", + "containerRuntimePodman": "Podman", + "enableTmuxMonitor": "تمكين مراقب Tmux", + "enableTmuxMonitorDesc": "إظهار هذا المضيف في مراقب Tmux وإضافة إجراءات tmux الخاصة به إلى الشريط الجانبي", + "tabProxmox": "Proxmox", "proxmoxIntegration": "تكامل Proxmox", - "enableProxmox": "تفعيل Proxmox", - "enableProxmoxDesc": "قم بتحديد هذا المضيف كعقدة Proxmox. يتيح ذلك اكتشاف الضيوف واستيرادهم مباشرةً من هذا المضيف.", + "enableProxmox": "تمكين Proxmox", + "enableProxmoxDesc": "تعليم هذا المضيف كعقدة Proxmox. يُمكّن اكتشاف الضيوف واستيرادهم مباشرة من هذا المضيف.", "proxmoxDefaultAuthType": "نوع المصادقة الافتراضي", - "proxmoxDefaultAuthTypeDesc": "طريقة المصادقة المطبقة على المضيفين الضيوف المستوردين. اختر نوع المصادقة الذي يتوافق مع طريقة اتصالك بضيوفك.", - "authTypePassword": "كلمة المرور", + "proxmoxDefaultAuthTypeDesc": "طريقة المصادقة المطبقة على المضيفين الضيوف المستوردين. اختر نوع المصادقة الذي يتوافق مع طريقة اتصالك بالضيوف.", + "authTypePassword": "كلمة مرور", "authTypeKey": "مفتاح SSH", - "authTypeCredential": "بيانات الاعتماد", + "authTypeCredential": "بيانات اعتماد", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "لا أحد", + "authTypeNone": "بلا", "proxmoxDefaultCredential": "بيانات الاعتماد الافتراضية", - "proxmoxDefaultCredentialDesc": "بيانات الاعتماد المستخدمة للمضيفين الضيوف المستوردين عندما يتم تعيين نوع المصادقة إلى بيانات الاعتماد.", - "proxmoxWindowsDetection": "الكشف عن نظام التشغيل Windows / بروتوكول سطح المكتب البعيد (RDP)", - "proxmoxWindowsDetectionDesc": "أنماط الأسماء المفصولة بفواصل والتي تؤدي إلى تشغيل RDP بدلاً من SSH (غير حساسة لحالة الأحرف)", + "proxmoxDefaultCredentialDesc": "بيانات الاعتماد المُستخدمة للأجهزة الضيفة المستوردة عند ضبط نوع المصادقة على 'بيانات الاعتماد'.", + "proxmoxWindowsDetection": "اكتشاف Windows / RDP", + "proxmoxWindowsDetectionDesc": "أنماط الأسماء مفصولة بفواصل تُشغّل RDP بدلاً من SSH (غير حسّاسة لحالة الأحرف)", "proxmoxDockerDetection": "اكتشاف Docker", - "proxmoxDockerDetectionDesc": "أنماط أسماء مفصولة بفواصل تُمكّن Docker من مطابقة الضيوف", - "proxmoxPreferredRanges": "نطاقات IP المفضلة", - "proxmoxPreferredRangesDesc": "بادئات مفصولة بفواصل مرتبة حسب الأولوية لاختيار عنوان IP عندما يكون لدى جهاز ضيف عدة واجهات", - "proxmoxAutoSync": "مزامنة الضيوف تلقائيًا", - "proxmoxAutoSyncDesc": "قم باكتشاف عقدة Proxmox هذه بشكل دوري وقم بإنشاء أو تحديث مضيفي الضيوف المستوردين أثناء فتح جلستك.", - "proxmoxSyncInterval": "فاصل المزامنة (بالدقائق)", - "proxmoxSyncIntervalDesc": "الحد الأدنى 5 دقائق. يتخطى المجدول جلسات المستخدمين المقفلة.", - "proxmoxMarkMissing": "مارك الضيوف المفقودين", - "proxmoxMarkMissingDesc": "أضف علامة proxmox-missing عندما يختفي ضيف تم استيراده مسبقًا بدلاً من حذفه.", + "proxmoxDockerDetectionDesc": "أنماط الأسماء مفصولة بفواصل تُمكّن Docker للأجهزة الضيفة المطابقة", + "proxmoxPreferredRanges": "نطاقات IP المُفضّلة", + "proxmoxPreferredRangesDesc": "بادئات مفصولة بفواصل بترتيب الأولوية لاختيار IP عندما يكون للجهاز الضيف واجهات متعددة", + "proxmoxAutoSync": "مزامنة تلقائية للأجهزة الضيفة", + "proxmoxAutoSyncDesc": "اكتشاف عقدة Proxmox هذه بشكل دوري وإنشاء أو تحديث الأجهزة الضيفة المستوردة أثناء فتح جلستك.", + "proxmoxSyncInterval": "فترة المزامنة (بالدقائق)", + "proxmoxSyncIntervalDesc": "الحد الأدنى 5 دقائق. يتخطّى المجدول جلسات المستخدم المقفلة.", + "proxmoxMarkMissing": "تعليم الأجهزة الضيفة المفقودة", + "proxmoxMarkMissingDesc": "أضف وسم proxmox-missing عندما يختفي جهاز ضيف تم استيراده مسبقاً بدلاً من حذفه.", "proxmoxLastSync": "آخر مزامنة", "proxmoxLastSyncNever": "لم تتم المزامنة بعد", - "proxmoxLastSyncNoResult": "لا توجد تفاصيل عن النتائج", - "proxmoxLastSyncSummary": "تم إنشاء {{created}} ، تم تحديث {{updated}} ، مفقود {{markedMissing}} ، تم تخطي {{skipped}}", + "proxmoxLastSyncNoResult": "لا توجد تفاصيل للنتائج", + "proxmoxLastSyncSummary": "{{created}} مُنشأ، {{updated}} مُحدَّث، {{markedMissing}} مفقود، {{skipped}} مُتخطّى", "proxmoxLastSyncStatus": { "success": "نجاح", "error": "فشل", "pending": "قيد الانتظار" }, - "proxmoxDiscoverAction": "اكتشف واستورد ضيوف Proxmox", - "proxmoxImportTitle": "الاستيراد من Proxmox", - "proxmoxSelectHost": "حدد مضيف Proxmox…", - "proxmoxDiscover": "يكتشف", - "proxmoxDiscovering": "اكتشاف…", - "proxmoxDiscoverGuests": "اكتشف الضيوف", - "proxmoxGuestsSelected_one": "{{count}} ضيف — {{selected}} مُختار", - "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", - "proxmoxSelectAll": "حدد الكل", - "proxmoxDeselectAll": "قم بإلغاء تحديد الكل", - "proxmoxNoGuests": "لم يتم العثور على أي ضيوف على عقدة Proxmox هذه.", - "proxmoxImportButton_one": "استيراد {{count}} المضيف", + "proxmoxDiscoverAction": "اكتشاف واستيراد أجهزة Proxmox الضيفة", + "proxmoxImportTitle": "استيراد من Proxmox", + "proxmoxSelectHost": "اختر مضيف Proxmox…", + "proxmoxDiscover": "اكتشف", + "proxmoxDiscovering": "جارٍ الاكتشاف…", + "proxmoxDiscoverGuests": "اكتشف الأجهزة الضيفة", + "proxmoxGuestsSelected_one": "{{count}} جهاز ضيف — {{selected}} مُحدد", + "proxmoxGuestsSelected_other": "{{count}} أجهزة ضيفة — {{selected}} مُحددة", + "proxmoxSelectAll": "تحديد الكل", + "proxmoxDeselectAll": "إلغاء تحديد الكل", + "proxmoxNoGuests": "لم يتم العثور على أجهزة ضيفة على عقدة Proxmox هذه.", + "proxmoxImportButton_one": "استيراد {{count}} مضيف", "proxmoxImportButton_other": "استيراد {{count}} مضيفين", - "proxmoxResultImported": "تم الاستيراد {{count}}", - "proxmoxResultUpdated": "تم التحديث {{count}}", - "proxmoxResultFailed": "فشل {{count}}", + "proxmoxResultImported": "{{count}} تم استيرادها", + "proxmoxResultUpdated": "{{count}} تم تحديثها", + "proxmoxResultFailed": "{{count}} فشلت", + "proxmoxResultSkippedNoIp": "تم تخطي {{count}} (لم يتم العثور على عنوان IP)", "proxmoxImportComplete": "اكتمل استيراد Proxmox: {{summary}}", "proxmoxDiscoveryFailed": "فشل الاكتشاف", - "proxmoxImportFailed": "فشلت عملية الاستيراد", - "enableFileManagerMonitor": "تفعيل مدير الملفات", - "enableFileManagerMonitorDesc": "تصفح الملفات وإدارتها على هذا المضيف عبر بروتوكول SFTP", + "proxmoxImportFailed": "فشل الاستيراد", + "enableFileManagerMonitor": "تمكين مدير الملفات", + "enableFileManagerMonitorDesc": "استعراض وإدارة الملفات على هذا المضيف عبر SFTP", "scpLegacyLabel": "وضع SCP القديم", - "scpLegacyDesc": "استخدم نقل الملفات القديم للخوادم التي لا تدعم نظام SFTP الفرعي (مثل خوادم SSH المدمجة أو البسيطة).", + "scpLegacyDesc": "استخدم وضع النقل القديم للملفات للخوادم التي لا تدعم النظام الفرعي SFTP (مثل خوادم SSH المدمجة أو ذات الإمكانيات المحدودة).", "defaultPathLabel": "المسار الافتراضي", - "fileManagerPathHint": "المجلد الذي سيتم فتحه عند تشغيل مدير الملفات لهذا المضيف.", - "statusChecksLabel": "التحقق من الحالة", - "enableStatusChecks": "تفعيل عمليات التحقق من الحالة", - "enableStatusChecksDesc": "قم بإجراء اختبار اتصال دوري بهذا المضيف للتحقق من توافره.", - "useGlobalInterval": "استخدام الفاصل الزمني العالمي", - "useGlobalIntervalDesc": "تجاوز فاصل فحص الحالة على مستوى الخادم", - "checkIntervalS": "فترة الفحص (أو الفترات الزمنية)", - "checkIntervalDesc": "ثوانٍ بين كل نبضة اتصال", - "metricsCollectionLabel": "مجموعة المقاييس", - "enableMetricsLabel": "تفعيل المقاييس", - "enableMetricsDesc": "قم بجمع بيانات وحدة المعالجة المركزية، وذاكرة الوصول العشوائي، والقرص، وغيرها من المقاييس من هذا المضيف", - "useGlobalMetrics": "استخدام الفاصل الزمني العالمي", - "useGlobalMetricsDesc": "تجاوز الفاصل الزمني للمقاييس على مستوى الخادم", - "metricsIntervalS": "فاصل القياس (ثانية)", - "metricsIntervalDesc2": "الثواني بين لقطات القياس", + "fileManagerPathHint": "الدليل الذي سيتم فتحه عند تشغيل مدير الملفات لهذا المضيف.", + "statusChecksLabel": "فحوصات الحالة", + "enableStatusChecks": "تمكين فحوصات الحالة", + "enableStatusChecksDesc": "إرسال ping دوريًا لهذا المضيف للتحقق من توفره", + "useGlobalInterval": "استخدام الفاصل الزمني العام", + "useGlobalIntervalDesc": "تجاوز باستخدام فترة فحص الحالة على مستوى الخادم", + "checkIntervalS": "فترة الفحص (ثانية)", + "checkIntervalDesc": "عدد الثواني بين كل ping للتحقق من الاتصال", + "metricsCollectionLabel": "تجميع المقاييس", + "enableMetricsLabel": "تمكين المقاييس", + "enableMetricsDesc": "تجميع مقاييس وحدة المعالجة المركزية والذاكرة والقرص وغيرها من هذا المضيف", + "useGlobalMetrics": "استخدام الفاصل الزمني العام", + "useGlobalMetricsDesc": "تجاوز باستخدام فترة المقاييس على مستوى الخادم", + "metricsIntervalS": "فترة المقاييس (ثانية)", + "metricsIntervalDesc2": "الثواني بين لقطات المقاييس", "visibleWidgets": "الأدوات المرئية", - "widgetsMovedToHostMetrics": "تُضاف البطاقات الآن وتُرتب وتُغير أحجامها مباشرةً في علامة تبويب \"مقاييس المضيف\". افتح \"مقاييس المضيف\" لهذا المضيف واستخدم \"تخصيص\" لاختيار البطاقات التي تُريد عرضها.", + "widgetsMovedToHostMetrics": "تتم الآن إضافة البطاقات وترتيبها وتغيير حجمها مباشرةً في علامة تبويب مقاييس المضيف. افتح مقاييس المضيف لهذا المضيف واستخدم تخصيص لاختيار البطاقات التي ستظهر.", "cpuUsageLabel": "استخدام وحدة المعالجة المركزية", - "cpuUsageDesc": "نسبة استخدام وحدة المعالجة المركزية، متوسطات الحمل، رسم بياني مصغر", + "cpuUsageDesc": "نسبة وحدة المعالجة المركزية، متوسطات الحمل، رسم بياني شريطي صغير", "memoryLabel": "استخدام الذاكرة", - "memoryDesc": "استخدام ذاكرة الوصول العشوائي (RAM)، وذاكرة التبديل، والذاكرة المخزنة مؤقتًا", + "memoryDesc": "استخدام RAM، الذاكرة المؤقتة للتبادل (swap)، المحفوظة مؤقتًا", "storageLabel": "استخدام القرص", - "storageDesc": "استخدام القرص لكل نقطة تحميل", + "storageDesc": "استخدام القرص لكل نقطة تركيب", "networkLabel": "واجهات الشبكة", - "networkDesc": "قائمة الواجهات وعرض النطاق الترددي", - "uptimeLabel": "وقت التشغيل", - "uptimeDesc": "وقت تشغيل النظام ووقت بدء التشغيل", + "networkDesc": "قائمة الواجهات وعرض النطاق", + "uptimeLabel": "مدة التشغيل", + "uptimeDesc": "مدة تشغيل النظام ووقت الإقلاع", "systemInfoLabel": "معلومات النظام", - "systemInfoDesc": "نظام التشغيل، النواة، اسم المضيف، البنية", + "systemInfoDesc": "نظام التشغيل، النواة، اسم المضيف، المعمارية", "recentLoginsLabel": "عمليات تسجيل الدخول الأخيرة", "recentLoginsDesc": "أحداث تسجيل الدخول الناجحة والفاشلة", "topProcessesLabel": "أهم العمليات", - "topProcessesDesc": "معرّف العملية، نسبة استخدام وحدة المعالجة المركزية، نسبة استخدام الذاكرة، الأمر", - "listeningPortsLabel": "منافذ الاستماع", - "listeningPortsDesc": "فتح المنافذ باستخدام العملية والحالة", + "topProcessesDesc": "PID، نسبة المعالج، نسبة الذاكرة، الأمر", + "listeningPortsLabel": "المنافذ المستمعة", + "listeningPortsDesc": "المنافذ المفتوحة مع العملية والحالة", "firewallLabel": "جدار الحماية", - "firewallDesc": "حالة جدار الحماية، وAppArmor، وSELinux", + "firewallDesc": "حالة جدار الحماية و AppArmor و SELinux", "quickActionsLabel": "إجراءات سريعة", "quickActionsToolbar": "تظهر الإجراءات السريعة كأزرار في شريط أدوات مقاييس المضيف لتنفيذ الأوامر بنقرة واحدة.", - "noQuickActions": "لم تُتخذ أي إجراءات سريعة حتى الآن.", - "buttonLabel": "ملصق الزر", - "selectSnippetPlaceholder": "حدد المقتطف...", + "noQuickActions": "لا توجد إجراءات سريعة بعد.", + "buttonLabel": "تسمية الزر", + "selectSnippetPlaceholder": "اختر مقتطفًا...", "addActionBtn": "إضافة إجراء", "hostSharedSuccessfully": "تمت مشاركة المضيف بنجاح", - "failedToShareHost": "فشل في مشاركة المضيف", + "failedToShareHost": "فشلت مشاركة المضيف", "accessRevoked": "تم إلغاء الوصول", - "failedToRevokeAccess": "فشل في إلغاء الوصول", - "cancelBtn": "يلغي", - "savingBtn": "توفير...", + "failedToRevokeAccess": "فشل إلغاء الوصول", + "cancelBtn": "إلغاء", + "savingBtn": "جاري الحفظ...", "addHostBtn": "إضافة مضيف", "hostUpdated": "تم تحديث المضيف", "hostCreated": "تم إنشاء المضيف", @@ -959,26 +1015,29 @@ "credentialUpdated": "تم تحديث بيانات الاعتماد", "credentialCreated": "تم إنشاء بيانات الاعتماد", "failedToSaveCredential": "فشل حفظ بيانات الاعتماد", - "credentialNameRequired": "الرجاء إدخال اسم لبيان الاعتماد", + "credentialNameRequired": "الرجاء إدخال اسم لبيانات الاعتماد", "credentialAuthRequired": "أضف كلمة مرور أو مفتاح SSH أو كليهما", + "createCredentialFromHostBtn": "إنشاء بيانات اعتماد", + "createCredentialFromHostTitle": "إنشاء بيانات اعتماد من المضيف", + "createCredentialFromHostDesc": "قم بإنشاء إدخال بيانات اعتماد قابل لإعادة الاستخدام والمشاركة، معبأ مسبقًا باسم المستخدم وكلمة المرور و/أو مفتاح SSH الحالي لهذا المضيف.", "backToHosts": "العودة إلى المضيفين", "backToCredentials": "العودة إلى بيانات الاعتماد", - "pinned": "مثبت", + "pinned": "مثبّت", "noHostsFound": "لم يتم العثور على مضيفين", - "tryDifferentTerm": "جرب مصطلحًا مختلفًا", - "addFirstHost": "أضف أول مضيف لك للبدء", + "tryDifferentTerm": "جرّب مصطلحًا مختلفًا", + "addFirstHost": "أضِف مضيفك الأول للبدء", "noCredentialsFound": "لم يتم العثور على بيانات اعتماد", "addCredentialBtn": "إضافة بيانات اعتماد", - "updateCredentialBtn": "تحديث بيانات الاعتماد", - "features": "سمات", - "noFolder": "(بدون مجلد)", - "deleteSelected": "يمسح", - "exitSelection": "اختيار الخروج", - "importSkip": "استيراد (تجاهل الموجود)", + "updateCredentialBtn": "تحديث بيانات اعتماد", + "features": "الميزات", + "noFolder": "(بلا مجلد)", + "deleteSelected": "حذف", + "exitSelection": "إنهاء التحديد", + "importSkip": "استيراد (تخطّي الموجود)", "importOverwrite": "استيراد (استبدال)", - "collapseBtn": "ينهار", + "collapseBtn": "طي", "importExportBtn": "استيراد / تصدير", - "hostStatusesRefreshed": "تم تحديث حالات المضيف", + "hostStatusesRefreshed": "تم تحديث حالات المضيفين", "failedToRefreshHosts": "فشل تحديث المضيفين", "movedHostTo": "تم نقل {{host}} إلى \"{{folder}}\"", "failedToMoveHost": "فشل نقل المضيف", @@ -986,85 +1045,88 @@ "deletedFolder": "تم حذف المجلد \"{{name}}\"", "failedToDeleteFolder": "فشل حذف المجلد", "deleteAllInFolder": "حذف جميع المضيفين في \"{{name}}\"؟ لا يمكن التراجع عن هذا.", - "folderPickerPlaceholder": "لا يوجد مجلد", + "folderPickerPlaceholder": "بلا مجلد", "folderPickerSearch": "ابحث أو أنشئ (استخدم / للمجلدات الفرعية)...", - "folderPickerNone": "لا يوجد مجلد", + "folderPickerNone": "بلا مجلد", "folderPickerCreate": "إنشاء \"{{path}}\"", "folderPickerEmpty": "لا توجد مجلدات مطابقة", "newFolder": "مجلد جديد", "createFolderTitle": "إنشاء مجلد", - "editFolderTitle": "مجلد التحرير", - "folderDialogDescription": "اختر اسمًا ولونًا وأيقونة. استخدم / لترتيب المجلدات داخل بعضها.", + "editFolderTitle": "تعديل المجلد", + "folderDialogDescription": "اختر اسمًا ولونًا وأيقونة. استخدم / لتداخل المجلدات.", "folderNameLabel": "اسم المجلد", - "folderNamePlaceholder": "مثال: الإنتاج/الويب", + "folderNamePlaceholder": "مثال: إنتاج/ويب", "folderNestingHint": "استخدم / لفصل المستويات وإنشاء مجلدات متداخلة.", "folderColor": "لون", - "folderIcon": "رمز", + "folderIcon": "أيقونة", + "folderCredential": "بيانات الاعتماد", + "folderCredentialNone": "لم يتم تخصيص أي بيانات اعتماد", + "folderCredentialHint": "سترث الأجهزة المضيفة الموجودة في هذا المجلد والتي تستخدم مصادقة \"بيانات الاعتماد المخزنة\" دون تحديد بيانات الاعتماد الخاصة بها هذه البيانات.", "folderPreview": "معاينة", "folderNameFallback": "مجلد بدون عنوان", "createFolderButton": "إنشاء مجلد", "saveFolderButton": "حفظ المجلد", - "cancel": "يلغي", - "iconSearchPlaceholder": "أيقونات البحث...", - "openAllSessions": "افتح جميع الجلسات", - "editFolder": "مجلد التحرير", + "cancel": "إلغاء", + "iconSearchPlaceholder": "بحث عن أيقونات...", + "openAllSessions": "فتح جميع الجلسات", + "editFolder": "تعديل المجلد", "deleteFolder": "حذف المجلد", "folderSaved": "تم حفظ المجلد", "failedToSaveFolder": "فشل حفظ المجلد", "folderDeleted": "تم حذف المجلد \"{{name}}\"", - "deleteFolderConfirm": "هل يمكن حذف \"{{name}}\" ومضيفه {{count}} ؟ لا يمكن التراجع عن هذا الإجراء.", + "deleteFolderConfirm": "حذف \"{{name}}\" ومضيفيه {{count}}؟ لا يمكن التراجع عن هذا.", "failedToMoveHosts": "فشل نقل المضيفين", - "expandAll": "قم بتوسيع جميع المجلدات", + "expandAll": "توسيع جميع المجلدات", "collapseAll": "طي جميع المجلدات", - "moreActions": "أكثر", - "groupBy": "التجميع حسب", - "GroupByFolder": "مجلد", + "moreActions": "المزيد", + "groupBy": "تجميع حسب", + "GroupByFolder": "المجلد", "GroupByTag": "الوسم", - "GroupByStatus": "حالة", - "GroupByProtocol": "بروتوكول", + "GroupByStatus": "الحالة", + "GroupByProtocol": "البروتوكول", "GroupByAuth": "نوع المصادقة", - "groupUngrouped": "غير مصنف", - "deletedHost": "تم الحذف {{name}}", - "copiedToClipboard": "تم نسخها إلى الحافظة", - "terminalUrlCopied": "تم نسخ عنوان URL الخاص بالطرفية", - "fileManagerUrlCopied": "تم نسخ عنوان URL لمدير الملفات", - "tunnelUrlCopied": "تم نسخ عنوان URL للنفق", - "dockerUrlCopied": "تم نسخ عنوان URL الخاص بـ Docker", - "hostMetricsUrlCopied": "تم نسخ عنوان URL الخاص بمقاييس المضيف", - "tmuxMonitorUrlCopied": "تم نسخ عنوان URL الخاص بمراقب Tmux", - "rdpUrlCopied": "تم نسخ عنوان URL الخاص بـ RDP", + "groupUngrouped": "غير مجمعة", + "deletedHost": "تم حذف {{name}}", + "copiedToClipboard": "تم النسخ إلى الحافظة", + "terminalUrlCopied": "تم نسخ رابط الطرفية", + "fileManagerUrlCopied": "تم نسخ رابط مدير الملفات", + "tunnelUrlCopied": "تم نسخ رابط النفق", + "dockerUrlCopied": "تم نسخ رابط Docker", + "hostMetricsUrlCopied": "تم نسخ رابط مقاييس المضيف", + "tmuxMonitorUrlCopied": "تم نسخ رابط Tmux Monitor", + "rdpUrlCopied": "تم نسخ رابط RDP", "vncUrlCopied": "تم نسخ رابط VNC", - "telnetUrlCopied": "تم نسخ عنوان URL الخاص بـ Telnet", - "remoteDesktopUrlCopied": "تم نسخ عنوان URL لسطح المكتب البعيد", + "telnetUrlCopied": "تم نسخ رابط Telnet", + "remoteDesktopUrlCopied": "تم نسخ رابط سطح المكتب البعيد", "expandActions": "توسيع الإجراءات", - "collapseActions": "إجراءات الانهيار", - "wakeOnLanAction": "التنبيه عبر الشبكة المحلية", + "collapseActions": "طي الإجراءات", + "wakeOnLanAction": "إيقاظ عبر الشبكة", "wakeOnLanSuccess": "تم إرسال حزمة سحرية إلى {{name}}", "wakeOnLanError": "فشل إرسال الحزمة السحرية", - "cloneHostAction": "مضيف الاستنساخ", - "copyAddress": "انسخ العنوان", - "copyLink": "نسخ الوصلة", - "copyTerminalUrlAction": "انسخ عنوان URL الخاص بالطرفية", - "copyFileManagerUrlAction": "انسخ رابط مدير الملفات", - "copyTunnelUrlAction": "انسخ رابط النفق", - "copyDockerUrlAction": "رابط نسخ Docker", - "copyHostMetricsUrlAction": "نسخ رابط مقاييس الاستضافة", - "copyTmuxMonitorUrlAction": "انسخ رابط مراقبة Tmux", - "copyRdpUrlAction": "انسخ عنوان URL الخاص بـ RDP", - "copyVncUrlAction": "انسخ رابط VNC", - "copyTelnetUrlAction": "انسخ رابط Telnet", - "copyRemoteDesktopUrlAction": "انسخ عنوان URL لسطح المكتب البعيد", - "deleteCredentialConfirm": "حذف بيانات الاعتماد \"{{name}}\"?", - "deletedCredential": "تم الحذف {{name}}", + "cloneHostAction": "استنساخ المضيف", + "copyAddress": "نسخ العنوان", + "copyLink": "نسخ الرابط", + "copyTerminalUrlAction": "نسخ رابط الطرفية", + "copyFileManagerUrlAction": "نسخ رابط مدير الملفات", + "copyTunnelUrlAction": "نسخ رابط النفق", + "copyDockerUrlAction": "نسخ رابط Docker", + "copyHostMetricsUrlAction": "نسخ رابط مقاييس المضيف", + "copyTmuxMonitorUrlAction": "نسخ رابط Tmux Monitor", + "copyRdpUrlAction": "نسخ رابط RDP", + "copyVncUrlAction": "نسخ رابط VNC", + "copyTelnetUrlAction": "نسخ رابط Telnet", + "copyRemoteDesktopUrlAction": "نسخ رابط سطح المكتب البعيد", + "deleteCredentialConfirm": "هل تريد حذف بيانات الاعتماد \"{{name}}\"؟", + "deletedCredential": "تم حذف {{name}}", "deploySSHKeyTitle": "نشر مفتاح SSH", "deployingBtn": "جارٍ النشر...", - "deployBtn": "انشر", + "deployBtn": "نشر", "failedToDeployKey": "فشل نشر المفتاح", - "deleteHostsConfirm": "حذف المضيف {{count}}{{plural}}؟ لا يمكن التراجع عن هذا الإجراء.", - "movedToRoot": "تم نقله إلى الجذر", + "deleteHostsConfirm": "هل تريد حذف {{count}} مضيف{{plural}}؟ لا يمكن التراجع عن هذا الإجراء.", + "movedToRoot": "تم النقل إلى الجذر", "enableTerminalFeature": "تفعيل الطرفية", - "disableTerminalFeature": "تعطيل الجهاز", - "enableFilesFeature": "تمكين الملفات", + "disableTerminalFeature": "تعطيل الطرفية", + "enableFilesFeature": "تفعيل الملفات", "disableFilesFeature": "تعطيل الملفات", "enableTunnelsFeature": "تفعيل الأنفاق", "disableTunnelsFeature": "تعطيل الأنفاق", @@ -1072,657 +1134,728 @@ "disableDockerFeature": "تعطيل Docker", "enableProxmoxFeature": "تفعيل Proxmox", "disableProxmoxFeature": "تعطيل Proxmox", - "addTagsPlaceholder": "أضف الوسوم...", + "addTagsPlaceholder": "إضافة علامات...", "authDetails": "تفاصيل المصادقة", - "credType": "يكتب", - "generateKeyPairDesc": "قم بإنشاء زوج مفاتيح جديد، وسيتم ملء كل من المفاتيح الخاصة والعامة تلقائيًا.", + "credType": "النوع", + "generateKeyPairDesc": "إنشاء زوج مفاتيح جديد، سيتم ملء كل من المفتاح الخاص والعام تلقائياً.", "generatingKey": "جارٍ التوليد...", - "generateLabel": "إنشاء {{label}}", - "uploadFileBtn": "تحميل الملف", - "keyPassphraseOptional": "عبارة المرور الرئيسية (اختياري)", + "generateLabel": "توليد {{label}}", + "uploadFileBtn": "رفع ملف", + "keyPassphraseOptional": "عبارة مرور المفتاح (اختياري)", "sshPublicKeyOptional": "مفتاح SSH العام (اختياري)", "publicKeyGenerated": "تم إنشاء المفتاح العام", - "failedToGeneratePublicKey": "فشل في استخلاص المفتاح العام", + "failedToGeneratePublicKey": "فشل اشتقاق المفتاح العام", "publicKeyCopied": "تم نسخ المفتاح العام", - "keyPairGenerated": "تم إنشاء زوج المفاتيح {{label}}", - "failedToGenerateKeyPair": "فشل في إنشاء زوج المفاتيح", - "searchHostsPlaceholder": "ابحث عن المضيفين والعناوين والوسوم…", - "searchCredentialsPlaceholder": "بيانات اعتماد البحث…", - "refreshBtn": "ينعش", - "addTag": "أضف الوسوم...", - "deleteConfirmBtn": "يمسح", - "tunnelRequirementsText": "يجب أن يحتوي خادم SSH على GatewayPorts نعم، و AllowTcpForwarding نعم، و PermitRootLogin نعم مضبوطة في /etc/ssh/sshd_config.", - "deleteHostConfirm": "حذف \"{{name}}\"?", - "enableAtLeastOneProtocol": "قم بتمكين بروتوكول واحد على الأقل من البروتوكولات المذكورة أعلاه لضبط إعدادات المصادقة والاتصال.", - "keyPassphrase": "عبارة المرور الرئيسية", - "connectBtn": "يتصل", + "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": "معلومات أساسية", + "basicInformation": "المعلومات الأساسية", "authDetailsSection": "تفاصيل المصادقة", - "credTypeLabel": "يكتب", + "credTypeLabel": "النوع", "hostsTab": "المضيفون", - "credentialsTab": "أوراق اعتماد", - "selectMultiple": "اختر عدة خيارات", - "selectHosts": "حدد المضيفين", + "credentialsTab": "بيانات الاعتماد", + "selectMultiple": "تحديد متعدد", + "selectHosts": "تحديد المضيفين", "connectionLabel": "اتصال", "authenticationLabel": "المصادقة", "generateKeyPairTitle": "إنشاء زوج مفاتيح", - "generateKeyPairDescription": "قم بإنشاء زوج مفاتيح جديد، وسيتم ملء كل من المفاتيح الخاصة والعامة تلقائيًا.", - "generateFromPrivateKey": "يتم توليدها من المفتاح الخاص", - "refreshBtn2": "ينعش", - "exitSelectionTitle": "اختيار الخروج", + "generateKeyPairDescription": "إنشاء زوج مفاتيح جديد، سيتم ملء المفتاحين الخاص والعام تلقائياً.", + "generateFromPrivateKey": "إنشاء من المفتاح الخاص", + "refreshBtn2": "تحديث", + "exitSelectionTitle": "الخروج من التحديد", "exportAll": "تصدير الكل", "exportForSharing": "تصدير للمشاركة", "addHostBtn2": "إضافة مضيف", "addCredentialBtn2": "إضافة بيانات اعتماد", - "checkingHostStatuses": "جارٍ التحقق من حالة المضيف...", + "checkingHostStatuses": "جارٍ التحقق من حالات المضيفين...", "pinnedSection": "مثبت", "hostsExported": "تم تصدير المضيفين بنجاح", "hostsShareExported": "تم تصدير المضيفين القابلين للمشاركة بنجاح", "exportFailed": "فشل تصدير المضيفين", - "sampleDownloaded": "تم تنزيل ملف العينة", + "sampleDownloaded": "تم تنزيل الملف النموذجي", "failedToDeleteCredential2": "فشل حذف بيانات الاعتماد", "noFolderOption": "(بدون مجلد)", - "nSelected": "{{count}} تم الاختيار", - "featuresMenu": "سمات", - "moveMenu": "يتحرك", - "connectSelected": "يتصل", - "cancelSelection": "يلغي", - "deployDialogDesc": "قم بنشر {{name}} إلى authorized_keys الخاص بالمضيف.", - "targetHostLabel": "المضيف المستهدف", - "selectHostOption": "اختر مضيفًا...", + "nSelected": "تم تحديد {{count}}", + "featuresMenu": "الميزات", + "moveMenu": "نقل", + "connectSelected": "اتصال", + "cancelSelection": "إلغاء", + "deployDialogDesc": "نشر {{name}} إلى ملف authorized_keys الخاص بالمضيف.", + "targetHostLabel": "المضيف الهدف", + "selectHostOption": "اختيار مضيف...", "keyDeployedSuccess": "تم نشر المفتاح بنجاح", "failedToDeployKey2": "فشل نشر المفتاح", "deletedCount": "تم حذف {{count}} مضيفين", "failedToDeleteCount": "فشل حذف {{count}} مضيفين", - "duplicatedHost": "مكرر \"{{name}}\"", - "failedToDuplicateHost": "فشل في تكرار المضيف", - "updatedCount": "تم تحديث {{count}} مضيفين", - "friendlyNameLabel": "اسم ودود", - "descriptionLabel": "وصف", + "duplicatedHost": "تم نسخ \"{{name}}\"", + "failedToDuplicateHost": "فشل نسخ المضيف", + "updatedCount": "تم تحديث {{count}} مضيف", + "friendlyNameLabel": "الاسم المألوف", + "descriptionLabel": "الوصف", "loadingHost": "جارٍ تحميل المضيف...", "loadingHosts": "جارٍ تحميل المضيفين...", "loadingCredentials": "جارٍ تحميل بيانات الاعتماد...", - "noHostsYet": "لا يوجد مضيفون حتى الآن", - "noHostsMatchSearch": "لم يتم العثور على أي مضيفين يطابقون بحثك", - "hostNotFound": "لم يتم العثور على المضيف", - "searchHosts": "ابحث عن المضيفين...", - "sortHosts": "فرز المضيفين", + "noHostsYet": "لا يوجد مضيفون بعد", + "noHostsMatchSearch": "لا يوجد مضيفون يطابقون بحثك", + "hostNotFound": "المضيف غير موجود", + "searchHosts": "ابحث عن مضيفين...", + "sortHosts": "ترتيب المضيفين", "sortDefault": "الترتيب الافتراضي", - "sortNameAsc": "الاسم (من الألف إلى الياء)", - "sortNameDesc": "الاسم (Z → A)", + "sortNameAsc": "الاسم (أ → ي)", + "sortNameDesc": "الاسم (ي → أ)", "sortIpAsc": "عنوان IP (تصاعدي)", - "sortIpDesc": "عنوان IP (الوصف)", - "sortOnlineFirst": "عبر الإنترنت أولاً", - "sortOfflineFirst": "العمل دون اتصال بالإنترنت أولاً", - "sortPinnedFirst": "مثبت أولاً", + "sortIpDesc": "عنوان IP (تنازلي)", + "sortOnlineFirst": "المتصل أولاً", + "sortOfflineFirst": "غير المتصل أولاً", + "sortPinnedFirst": "المثبت أولاً", "filterHosts": "تصفية المضيفين", - "filterClearAll": "مسح الفلاتر", - "filterStatusGroup": "حالة", + "filterClearAll": "مسح التصفية", + "filterStatusGroup": "الحالة", "filterOnline": "متصل", - "filterOffline": "غير متصل بالإنترنت", + "filterOffline": "غير متصل", "filterPinned": "مثبت", "filterAuthGroup": "نوع المصادقة", "filterAuthPassword": "كلمة المرور", "filterAuthKey": "مفتاح SSH", "filterAuthCredential": "بيانات الاعتماد", - "filterAuthNone": "لا أحد", + "filterAuthNone": "بلا", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "بروتوكول", + "filterProtocolGroup": "البروتوكول", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "تيلنت", - "filterFeaturesGroup": "سمات", - "filterFeatureTerminal": "صالة", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "الميزات", + "filterFeatureTerminal": "الطرفية", "filterFeatureFileManager": "مدير الملفات", "filterFeatureTunnel": "نفق", - "filterFeatureDocker": "عامل ميناء", + "filterFeatureDocker": "Docker", "filterTagsGroup": "الوسوم", - "shareHost": "مشاركة الاستضافة", + "shareHost": "مشاركة المضيف", "shareHostTitle": "مشاركة: {{name}}", + "shareFolder": "مجلد مشترك", + "shareFolderTitle": "مجلد المشاركة: {{name}}", + "folderSharedSuccessfully": "المضيف (المضيفون) المشترك {{count}} في المجلد", + "failedToShareFolder": "فشل مشاركة المجلد", "sharing": { "loadError": "فشل تحميل بيانات المشاركة. يرجى المحاولة مرة أخرى.", - "shareWithSection": "شارك مع", + "shareWithSection": "مشاركة مع", "usersTab": "المستخدمون", "rolesTab": "الأدوار", - "searchPlaceholder": "ابحث عن المستخدمين أو الأدوار...", - "noMatches": "لم يتم العثور على نتائج", - "permissionLevelLabel": "مستوى الإذن", + "searchPlaceholder": "البحث عن مستخدمين أو أدوار...", + "noMatches": "لا توجد نتائج مطابقة", + "permissionLevelLabel": "مستوى الصلاحية", "levels": { "connect": { - "label": "يتصل", - "description": "الجلسات المفتوحة فقط: الطرفية، سطح المكتب البعيد، مدير الملفات، الأنفاق، وDocker. لا يُسمح بالوصول إلى إعدادات المضيف." + "label": "اتصال", + "description": "فتح الجلسات فقط: الطرفية، سطح المكتب البعيد، مدير الملفات، الأنفاق وDocker. لا يمكن الوصول إلى إعدادات المضيف." }, "view": { - "label": "منظر", - "description": "اتصل، واطلع على إعدادات المضيف. لا يتم عرض أي بيانات سرية." + "label": "عرض", + "description": "الاتصال، بالإضافة إلى عرض إعدادات المضيف. لا تظهر الأسرار أبدًا." }, "edit": { - "label": "يحرر", - "description": "يمكنك عرض المضيف وتعديله. يمكن استبدال البيانات السرية ولكن لا يمكن قراءتها مطلقًا؛ وتبقى بيانات الاعتماد خاصة بالمالك فقط." + "label": "تعديل", + "description": "العرض، بالإضافة إلى تعديل المضيف. يمكن استبدال الأسرار ولكن لا يمكن قراءتها أبدًا؛ تظل تعيينات بيانات الاعتماد للمالك فقط." }, "manage": { - "label": "يدير", - "description": "قم بالتحرير، بالإضافة إلى مشاركة المضيف مع الآخرين، وتغيير مستويات الأذونات وإلغاء الوصول." + "label": "إدارة", + "description": "التعديل، بالإضافة إلى مشاركة المضيف مع الآخرين، وتغيير مستويات الصلاحية وإلغاء الوصول." } }, "expiryLabel": "انتهاء صلاحية الوصول", "expiry": { - "never": "أبداً", + "never": "أبدًا", "oneHour": "ساعة واحدة", "oneDay": "24 ساعة", "sevenDays": "7 أيام", "thirtyDays": "30 يومًا", "custom": "مخصص" }, - "customHoursPlaceholder": "عدد الساعات المتبقية حتى انتهاء صلاحية الوصول", - "shareButton": "يشارك", + "customHoursPlaceholder": "عدد الساعات حتى انتهاء صلاحية الوصول", + "shareButton": "مشاركة", "shareWithCount": "مشاركة ({{count}})", "currentAccess": "الوصول الحالي", "noAccessEntries": "لم تتم مشاركة هذا المضيف بعد", - "grantedBy": "مُنِحَتْ بِـ", - "expires": "تاريخ الانتهاء", - "expired": "منتهي الصلاحية", + "folderShareSummary": "مُشترك {{shared}} من {{total}} مُضيف (مضيفين) في هذا المجلد", + "grantedBy": "ممنوح بواسطة", + "expires": "انتهاء", + "expired": "منتهي", "never": "أبداً", "revoke": "إلغاء", "accessUpdated": "تم تحديث الوصول", "accessUpdateFailed": "فشل تحديث الوصول", "sharedBadge": "مشترك", - "sharedBadgeTooltip": "تمت المشاركة بواسطة {{owner}} ({{level}} الوصول)", - "viewOnlyBanner": "هذا المضيف مُشترك معك بواسطة {{owner}} مع إمكانية عرض الملف. الإعدادات للقراءة فقط.", - "sharedEditBanner": "هذا المضيف مُشترك معك بواسطة {{owner}} مع إمكانية التعديل. تُطبّق التغييرات على المضيف الفعلي؛ ولا يُمكن تغيير بيانات المصادقة إلا من قِبل المالك.", - "ownerOnlyControl": "لا يمكن تغيير هذا إلا من قبل مالك المضيف" + "sharedBadgeTooltip": "مُشارَك بواسطة {{owner}} (مستوى وصول {{level}})", + "viewOnlyBanner": "تمت مشاركة هذا المضيف معك من قبل {{owner}} بصلاحية العرض فقط. التكوين للقراءة فقط.", + "sharedEditBanner": "تمت مشاركة هذا المضيف معك من قبل {{owner}} بصلاحية التعديل. التغييرات تؤثر على المضيف الفعلي؛ لا يمكن تغيير مراجع المصادقة إلا من قبل المالك.", + "ownerOnlyControl": "يمكن لمالك المضيف فقط تغيير هذا" }, "guac": { "connection": "اتصال", "authentication": "المصادقة", "storedCredential": "بيانات الاعتماد المخزنة", - "noCredential": "لا توجد مؤهلات (المؤهلات المباشرة أدناه)", + "noCredential": "لا توجد بيانات اعتماد (بيانات اعتماد مباشرة أدناه)", "authMethod": "طريقة المصادقة", "authTypeDirect": "مباشر", "authTypeCredential": "بيانات الاعتماد", + "authTypeNone": "لا أحد", + "authTypeNoneDesc": "لا يتم تخزين أي بيانات اعتماد. سيُطلب منك إدخال اسم المستخدم وكلمة المرور في كل مرة تتصل فيها؛ فهي غير محفوظة.", "selectCredential": "اختر بيانات اعتماد...", "connectionSettings": "إعدادات الاتصال", "displaySettings": "إعدادات العرض", "audioSettings": "إعدادات الصوت", - "rdpPerformance": "أداء بروتوكول سطح المكتب البعيد (RDP)", - "deviceRedirection": "إعادة توجيه الجهاز", - "session": "حصة", + "rdpPerformance": "أداء RDP", + "deviceRedirection": "إعادة توجيه الأجهزة", + "session": "جلسة", "gateway": "بوابة", - "remoteApp": "تطبيق عن بعد", - "clipboard": "لوحة كتابة", + "remoteApp": "RemoteApp", + "clipboard": "الحافظة", "sessionRecording": "تسجيل الجلسة", - "wakeOnLan": "خاصية Wake-on-LAN", + "wakeOnLan": "الإيقاظ عبر الشبكة", "vncSettings": "إعدادات VNC", - "terminalSettings": "إعدادات المحطة الطرفية", + "terminalSettings": "إعدادات الطرفية", "rdpPort": "منفذ RDP", "username": "اسم المستخدم", "password": "كلمة المرور", - "domain": "اِختِصاص", + "passwordSaved": "تم حفظ كلمة المرور، اكتب لتغييرها", + "domain": "مجال", "securityMode": "وضع الأمان", "colorDepth": "عمق اللون", - "width": "عرض", - "height": "ارتفاع", + "width": "العرض", + "height": "الارتفاع", "dpi": "DPI", "resizeMethod": "طريقة تغيير الحجم", "clientName": "اسم العميل", "initialProgram": "البرنامج الأولي", "serverLayout": "تخطيط الخادم", "timezone": "المنطقة الزمنية", - "loadBalanceInfo": "معلومات رصيد التحميل / ملفات تعريف الارتباط", - "loadBalanceInfoDesc": "ملف تعريف ارتباط وسيط اتصال سطح المكتب البعيد لموازنة تحميل مزرعة RDS (على سبيل المثال tsv://MS Terminal Services Plugin.1.CollectionName)", - "guacdProxy": "بروكسي غواكد", - "guacdHostname": "مضيف غواكد", - "guacdHostnamePlaceholder": "الإعداد الافتراضي العام", - "guacdPort": "ميناء غوادالوبي", - "guacdProxyDesc": "قم بتجاوز مثيل guacd العام لهذا الاتصال. اتركه فارغًا لاستخدام guacd المُكوّن عالميًا.", + "loadBalanceInfo": "معلومات موازنة التحميل / ملف تعريف ارتباط", + "loadBalanceInfoDesc": "ملف تعريف ارتباط وسيط اتصال RD لموازنة تحميل مزرعة RDS (مثل: tsv://MS Terminal Services Plugin.1.CollectionName)", + "guacdProxy": "وكيل guacd", + "guacdHostname": "مضيف guacd", + "guacdHostnamePlaceholder": "الافتراضي العام", + "guacdPort": "منفذ guacd", + "guacdProxyDesc": "تجاوز مثيل guacd العام لهذا الاتصال. اتركه فارغًا لاستخدام إعدادات guacd العمومية.", "gatewayHostname": "اسم مضيف البوابة", - "gatewayPort": "بوابة الميناء", + "gatewayPort": "منفذ البوابة", "gatewayUsername": "اسم مستخدم البوابة", "gatewayPassword": "كلمة مرور البوابة", - "gatewayDomain": "نطاق البوابة", + "gatewayDomain": "مجال البوابة", "remoteAppProgram": "برنامج RemoteApp", "workingDirectory": "دليل العمل", - "arguments": "الحجج", - "normalizeLineEndings": "توحيد نهايات الأسطر", + "arguments": "الوسائط", + "normalizeLineEndings": "تطبيع نهايات الأسطر", "recordingPath": "مسار التسجيل", "recordingName": "اسم التسجيل", "macAddress": "عنوان MAC", - "broadcastAddress": "خطاب إذاعي", + "broadcastAddress": "عنوان البث", "udpPort": "منفذ UDP", - "waitTimeS": "وقت الانتظار (ثوانٍ)", - "driveName": "اسم محرك الأقراص", - "drivePath": "مسار القيادة", + "waitTimeS": "وقت الانتظار (ث)", + "driveName": "اسم القرص", + "drivePath": "مسار القرص", "ignoreCertificate": "تجاهل الشهادة", - "ignoreCertificateDesc": "السماح بالاتصالات مع المضيفين الذين لديهم شهادات موقعة ذاتيًا", - "forceLossless": "قوة بدون فقدان", - "forceLosslessDesc": "فرض ترميز الصور بدون فقدان (جودة أعلى، نطاق ترددي أكبر)", + "ignoreCertificateDesc": "السماح بالاتصال بالمضيفين بشهادات موقعة ذاتيًا", + "forceLossless": "فرض بدون فقدان", + "forceLosslessDesc": "فرض ترميز الصور بدون فقدان (جودة أعلى، استهلاك أكبر للنطاق الترددي)", "disableAudio": "تعطيل الصوت", - "disableAudioDesc": "كتم جميع الأصوات من الجلسة البعيدة", - "enableAudioInput": "تفعيل إدخال الصوت (الميكروفون)", + "disableAudioDesc": "كتم كل الصوت من الجلسة البعيدة", + "enableAudioInput": "تمكين إدخال الصوت (الميكروفون)", "enableAudioInputDesc": "إعادة توجيه الميكروفون المحلي إلى الجلسة البعيدة", - "wallpaper": "ورق جدران", - "wallpaperDesc": "عرض خلفية سطح المكتب (تعطيل هذه الميزة يحسن الأداء)", - "theming": "تصميم الزخارف", - "themingDesc": "تفعيل السمات والأنماط المرئية", - "fontSmoothing": "تنعيم الخط", - "fontSmoothingDesc": "تفعيل عرض خطوط ClearType", - "fullWindowDrag": "سحب ملء الشاشة", - "fullWindowDragDesc": "عرض محتويات النافذة أثناء السحب", - "desktopComposition": "تكوين سطح المكتب", - "desktopCompositionDesc": "تفعيل تأثيرات الزجاج الهوائي", - "menuAnimations": "رسوم متحركة للقوائم", - "menuAnimationsDesc": "تفعيل تأثيرات التلاشي والانزلاق في القوائم", + "wallpaper": "خلفية سطح المكتب", + "wallpaperDesc": "إظهار خلفية سطح المكتب (التعطيل يحسن الأداء)", + "theming": "السمات", + "themingDesc": "تمكين السمات والأنماط المرئية", + "fontSmoothing": "تنعيم الخطوط", + "fontSmoothingDesc": "تمكين عرض خطوط ClearType", + "fullWindowDrag": "سحب النافذة بالكامل", + "fullWindowDragDesc": "إظهار محتويات النافذة أثناء السحب", + "desktopComposition": "تركيب سطح المكتب", + "desktopCompositionDesc": "تمكين تأثيرات Aero الزجاجية", + "menuAnimations": "حركات القوائم", + "menuAnimationsDesc": "تمكين حركات التلاشي والانزلاق للقوائم", "disableBitmapCaching": "تعطيل التخزين المؤقت للصور النقطية", - "disableBitmapCachingDesc": "قم بإيقاف تشغيل ذاكرة التخزين المؤقت للصور النقطية (قد يساعد ذلك في حل المشاكل التقنية).", + "disableBitmapCachingDesc": "إيقاف التخزين المؤقت للصور النقطية (قد يساعد في حل المشكلات)", "disableOffscreenCaching": "تعطيل التخزين المؤقت خارج الشاشة", - "disableOffscreenCachingDesc": "قم بإيقاف تشغيل ذاكرة التخزين المؤقت خارج الشاشة", - "disableGlyphCaching": "تعطيل تخزين الرموز الرسومية مؤقتًا", - "disableGlyphCachingDesc": "قم بإيقاف تشغيل ذاكرة التخزين المؤقت للرموز الرسومية", - "enableGfx": "تفعيل الرسومات", - "enableGfxDesc": "استخدم مسار رسومات RemoteFX", - "enablePrinting": "تفعيل الطباعة", + "disableOffscreenCachingDesc": "إيقاف التخزين المؤقت خارج الشاشة", + "disableGlyphCaching": "تعطيل التخزين المؤقت للأشكال", + "disableGlyphCachingDesc": "إيقاف التخزين المؤقت للأشكال", + "enableGfx": "تمكين GFX", + "enableGfxDesc": "استخدام خط أنابيب رسوميات RemoteFX", + "enablePrinting": "تمكين الطباعة", "enablePrintingDesc": "إعادة توجيه الطابعات المحلية إلى الجلسة البعيدة", - "enableDriveRedirection": "تفعيل إعادة توجيه محركات الأقراص", - "enableDriveRedirectionDesc": "قم بتعيين مجلد محلي كقرص في الجلسة البعيدة", + "enableDriveRedirection": "تمكين إعادة توجيه القرص", + "enableDriveRedirectionDesc": "تعيين مجلد محلي كقرص في الجلسة البعيدة", "createDrivePath": "إنشاء مسار محرك الأقراص", - "createDrivePathDesc": "قم بإنشاء المجلد تلقائيًا إذا لم يكن موجودًا", - "disableDownload": "تعطيل التنزيل", + "createDrivePathDesc": "إنشاء المجلد تلقائياً إذا لم يكن موجوداً", + "disableDownload": "تعطيل التحميل", "disableDownloadDesc": "منع تنزيل الملفات من الجلسة البعيدة", - "disableUpload": "تعطيل التحميل", - "disableUploadDesc": "منع تحميل الملفات إلى الجلسة البعيدة", - "enableTouch": "تفعيل اللمس", - "enableTouchDesc": "تفعيل إعادة توجيه إدخال اللمس", + "disableUpload": "تعطيل الرفع", + "disableUploadDesc": "منع رفع الملفات إلى الجلسة البعيدة", + "enableTouch": "تمكين اللمس", + "enableTouchDesc": "تمكين تمرير إدخالات اللمس", "consoleSession": "جلسة وحدة التحكم", "consoleSessionDesc": "الاتصال بوحدة التحكم (الجلسة 0) بدلاً من جلسة جديدة", "sendWolPacket": "إرسال حزمة WOL", - "sendWolPacketDesc": "أرسل حزمة بيانات سحرية لتنشيط هذا المضيف قبل الاتصال", + "sendWolPacketDesc": "إرسال حزمة سحرية لإيقاظ هذا المضيف قبل الاتصال", "disableCopy": "تعطيل النسخ", "disableCopyDesc": "منع نسخ النص من الجلسة البعيدة", - "disablePaste": "تعطيل خاصية اللصق", - "disablePasteDesc": "منع لصق النصوص في الجلسة البعيدة", - "createPathIfMissing": "أنشئ المسار إذا كان مفقودًا", - "createPathIfMissingDesc": "إنشاء دليل التسجيل تلقائيًا", + "disablePaste": "تعطيل اللصق", + "disablePasteDesc": "منع لصق النص في الجلسة البعيدة", + "createPathIfMissing": "إنشاء المسار إذا كان مفقوداً", + "createPathIfMissingDesc": "إنشاء دليل التسجيل تلقائياً", "excludeOutput": "استبعاد المخرجات", - "excludeOutputDesc": "لا تقم بتسجيل مخرجات الشاشة (البيانات الوصفية فقط)", - "excludeMouse": "استبعاد الماوس", - "excludeMouseDesc": "لا تسجل حركات الماوس", + "excludeOutputDesc": "عدم تسجيل مخرجات الشاشة (البيانات الوصفية فقط)", + "excludeMouse": "استبعاد الفأرة", + "excludeMouseDesc": "عدم تسجيل حركات الفأرة", "includeKeystrokes": "تضمين ضغطات المفاتيح", - "includeKeystrokesDesc": "سجل ضغطات المفاتيح الخام بالإضافة إلى مخرجات الشاشة", + "includeKeystrokesDesc": "تسجيل ضغطات المفاتيح الخام بالإضافة إلى مخرجات الشاشة", "vncPort": "منفذ VNC", "vncPassword": "كلمة مرور VNC", "vncUsernameOptional": "اسم المستخدم (اختياري)", - "vncLeaveBlank": "اترك هذا الحقل فارغًا إذا لم يكن مطلوبًا", + "vncLeaveBlank": "اتركه فارغاً إذا لم يكن مطلوباً", "cursorMode": "وضع المؤشر", "swapRedBlue": "تبديل الأحمر/الأزرق", - "swapRedBlueDesc": "قم بتبديل قناتي اللون الأحمر والأزرق (يحل بعض مشاكل الألوان)", + "swapRedBlueDesc": "تبديل قنوات اللون الأحمر والأزرق (يصلح بعض مشكلات الألوان)", "readOnly": "للقراءة فقط", - "readOnlyDesc": "عرض الشاشة عن بُعد دون إرسال أي مدخلات", - "telnetPort": "منفذ تيلنت", + "readOnlyDesc": "عرض الشاشة البعيدة دون إرسال أي إدخال", + "telnetPort": "منفذ Telnet", "terminalType": "نوع الطرفية", "fontName": "اسم الخط", "fontSize": "حجم الخط", "colorScheme": "نظام الألوان", - "backspaceKey": "مفتاح الحذف", + "backspaceKey": "مفتاح Backspace", "saveHostFirst": "احفظ المضيف أولاً.", - "sharingOptionsAfterSave": "تتوفر خيارات المشاركة بعد حفظ المضيف.", - "permissionLevel": "مستوى الإذن", - "typeHeader": "يكتب", - "targetHeader": "هدف", - "permissionHeader": "إذن", - "cancelBtn": "يلغي", - "savingBtn": "توفير...", + "sharingOptionsAfterSave": "خيارات المشاركة متاحة بعد حفظ المضيف.", + "permissionLevel": "مستوى الصلاحية", + "typeHeader": "النوع", + "targetHeader": "الهدف", + "permissionHeader": "الصلاحية", + "cancelBtn": "إلغاء", + "savingBtn": "جارٍ الحفظ...", "updateHostBtn": "تحديث المضيف", "addHostBtn": "إضافة مضيف" } }, "commandPalette": { - "searchPlaceholder": "ابحث عن المضيفين أو الأوامر أو الإعدادات...", + "searchPlaceholder": "ابحث عن المضيفين، الأوامر، أو الإعدادات...", "quickActions": "إجراءات سريعة", - "hostManager": "مدير الاستضافة", - "hostManagerDesc": "إدارة أو إضافة أو تعديل المضيفين", + "hostManager": "مدير المضيفين", + "hostManagerDesc": "إدارة المضيفين أو إضافتهم أو تعديلهم", "addNewHost": "إضافة مضيف جديد", - "addNewHostDesc": "سجل مضيفًا جديدًا", - "adminSettings": "إعدادات المسؤول", - "adminSettingsDesc": "قم بضبط تفضيلات النظام والمستخدمين", - "userProfile": "ملف تعريف المستخدم", + "addNewHostDesc": "تسجيل مضيف جديد", + "adminSettings": "إعدادات المدير", + "adminSettingsDesc": "تكوين تفضيلات النظام والمستخدمين", + "userProfile": "الملف الشخصي للمستخدم", "userProfileDesc": "إدارة حسابك وتفضيلاتك", - "addCredential": "إضافة بيانات اعتماد", - "addCredentialDesc": "قم بتخزين مفاتيح أو كلمات مرور SSH", - "tmuxMonitor": "شاشة Tmux", - "tmuxMonitorDesc": "راقب جلسات tmux عبر مضيفيك", + "addCredential": "إضافة اعتماد", + "addCredentialDesc": "تخزين مفاتيح SSH أو كلمات المرور", + "tmuxMonitor": "مراقب Tmux", + "tmuxMonitorDesc": "مراقبة جلسات tmux عبر مضيفيك", "recentActivity": "النشاط الأخير", "serversAndHosts": "الخوادم والمضيفون", - "noHostsFound": "لم يتم العثور على أي مضيفين مطابقين لـ \"{{search}}\"", + "noHostsFound": "لم يتم العثور على مضيفين يطابقون \"{{search}}\"", "links": "روابط", - "navigate": "تصفح", - "select": "يختار", - "toggleWith": "التبديل باستخدام" + "navigate": "تنقل", + "select": "تحديد", + "toggleWith": "تبديل باستخدام" }, "splitScreen": { - "paneEmpty": "اللوحة {{index}} - فارغة", + "paneEmpty": "الجزء {{index}} - فارغ", "noTabAssigned": "لم يتم تعيين علامة تبويب", - "focusedPane": "اللوحة النشطة" + "focusedPane": "الجزء النشط" }, "connections": { "noConnections": "لا توجد اتصالات", - "noConnectionsDesc": "افتح نافذة طرفية أو مدير ملفات أو سطح مكتب بعيد لعرض الاتصالات هنا", - "connectedFor": "متصل لمدة {{duration}}", + "noConnectionsDesc": "افتح محطة طرفية، أو مدير ملفات، أو سطح مكتب بعيد لرؤية الاتصالات هنا", + "connectedFor": "متصل منذ {{duration}}", "connected": "متصل", "disconnected": "غير متصل", "closeTab": "إغلاق علامة التبويب", - "closeConnection": "اتصال وثيق", - "forgetTab": "ينسى", - "removeBackground": "يزيل", + "closeConnection": "إغلاق الاتصال", + "forgetTab": "نسيان", + "removeBackground": "إزالة", "reconnect": "إعادة الاتصال", "reopenTab": "إعادة فتح", - "sectionOpen": "يفتح", - "sectionBackground": "خلفية", + "sectionOpen": "مفتوحة", + "sectionBackground": "الخلفية", "backgroundDesc": "تستمر الجلسات لمدة 30 دقيقة بعد قطع الاتصال ويمكن إعادة الاتصال بها.", - "persisted": "استمر في الخلفية", - "expiresIn": "تنتهي صلاحيتها في {{duration}}", - "search": "ابحث عن الاتصالات...", - "noSearchResults": "لا توجد نتائج مطابقة لبحثك", - "rename": "إعادة تسمية الجلسة" + "persisted": "محفوظة في الخلفية", + "expiresIn": "تنتهي خلال {{duration}}", + "search": "بحث في الاتصالات...", + "noSearchResults": "لا توجد اتصالات تطابق بحثك", + "rename": "إعادة تسمية الجلسة", + "sectionSharedWithMe": "تمت مشاركته معي", + "sharedBy": "تمت المشاركة بواسطة {{username}}", + "join": "ينضم", + "sharedSessionLabel": "{{hostName}} (مشترك)" + }, + "sessionSharing": { + "guestView": { + "loading": "جارٍ الاتصال بالجلسة المشتركة...", + "linkInvalid": "رابط المشاركة هذا غير صالح أو منتهي الصلاحية أو تم إلغاؤه", + "rateLimited": "عدد المحاولات كبير جدًا، يرجى المحاولة مرة أخرى قريبًا", + "sessionEnded": "انتهت هذه الجلسة", + "readOnlyBadge": "للعرض فقط" + }, + "modalTitle": "جلسة مشاركة", + "shareButton": "يشارك", + "notReadyToShare": "الجلسة غير جاهزة للمشاركة بعد", + "modeTab": { + "link": "وصلة", + "user": "مستخدم" + }, + "linkModeDescription": "يمكن لأي شخص لديه هذا الرابط الانضمام، ولا يلزم وجود حساب.", + "userModeDescription": "شارك الملف مع مستخدم محدد لديه صلاحية الوصول إلى هذا المضيف. إذا لم يكن لديه صلاحية الوصول بعد، فشارك معه المضيف أولاً أو استخدم رابطًا. بمجرد المشاركة، ستظهر الجلسة في علامة تبويب \"الاتصالات\" الخاصة به.", + "permissionLevel": { + "label": "مستوى الإذن", + "readOnly": "للقراءة فقط", + "readOnlyDescription": "يمكنني مشاهدة الجلسة مباشرة ولكن لا يمكنني الكتابة أو التفاعل.", + "readWrite": "القراءة والكتابة", + "readWriteDescription": "يمكنه الكتابة والتفاعل مع الجلسة تمامًا مثل المالك." + }, + "expiryLabel": "انتهاء صلاحية الرابط", + "createLinkButton": "إنشاء رابط", + "createShareButton": "مشاركة مع المستخدم", + "searchUsersPlaceholder": "ابحث عن المستخدمين...", + "noUsersFound": "لم يتم العثور على مستخدمين", + "linkCreated": "تم إنشاء رابط المشاركة", + "linkCopied": "تم نسخ الرابط إلى الحافظة", + "copyLink": "نسخ الوصلة", + "shareCreated": "تمت مشاركة الجلسة. ستظهر في علامة تبويب \"الاتصالات\" الخاصة بهم.", + "shareFailed": "فشل إنشاء المشاركة", + "userLacksHostAccess": "لا يملك هذا المستخدم صلاحية الوصول إلى هذا المضيف بعد. شارك المضيف معه أولاً، أو استخدم رابطاً بدلاً من ذلك.", + "activeShares": "الأسهم النشطة", + "noActiveShares": "لا توجد أسهم نشطة لهذه الجلسة", + "revoke": "إلغاء", + "revokeConfirmTitle": "إلغاء هذه الحصة؟", + "revokeConfirmDescription": "أي شخص يستخدم هذا الملف المشترك سيفقد الوصول إليه فوراً.", + "revoked": "تم إلغاء الحصة", + "revokeFailed": "فشل في إلغاء المشاركة", + "joinCount": "{{count}} انضم", + "joinCount_other": "{{count}} ينضم", + "expiresAt": "تاريخ الانتهاء {{date}}", + "linkShareBadge": "وصلة", + "userShareBadge": "المستخدم: {{username}}", + "loadSharesFailed": "فشل تحميل المشاركات النشطة" }, "guacamole": { "connecting": "جارٍ الاتصال بجلسة {{type}}...", "connectionError": "خطأ في الاتصال", "connectionFailed": "فشل الاتصال", "failedToConnect": "فشل الحصول على رمز الاتصال", - "hostNotFound": "لم يتم العثور على المضيف", - "noHostSelected": "لم يتم تحديد أي مضيف", + "hostNotFound": "المضيف غير موجود", + "noHostSelected": "لم يتم اختيار مضيف", "reconnect": "إعادة الاتصال", "retry": "إعادة المحاولة", "guacdUnavailable": "خدمة سطح المكتب البعيد (guacd) غير متاحة. يرجى التأكد من تشغيل guacd وإمكانية الوصول إليه وتكوينه بشكل صحيح في إعدادات المسؤول.", + "credentialPromptTitle": "أدخل بيانات اعتماد RDP", + "credentialPromptDescription": "تم ضبط هذا المضيف ليطلب بيانات الاعتماد عند الاتصال. تُستخدم هذه البيانات لهذه الجلسة فقط ولا يتم حفظها.", + "connect": "يتصل", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (شاشة القفل)", - "winKey": "مفتاح ويندوز", - "ctrl": "كنترول", + "winL": "Win+L (قفل الشاشة)", + "winKey": "مفتاح Windows", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "يحول", - "win": "يفوز", - "stickyActive": "{{key}} (مغلق - انقر للتحرير)", - "stickyInactive": "{{key}} (انقر للإغلاق)", - "esc": "يهرب", - "tab": "فاتورة غير مدفوعة", - "home": "بيت", - "end": "نهاية", - "pageUp": "صفحة لأعلى", - "pageDown": "الصفحة لأسفل", - "arrowUp": "السهم لأعلى", - "arrowDown": "السهم لأسفل", - "arrowLeft": "سهم إلى اليسار", - "arrowRight": "السهم إلى اليمين", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (مثبت - انقر لتحرير)", + "stickyInactive": "{{key}} (انقر للتثبيت)", + "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": "مفاتيح الوظائف", "reconnect": "إعادة الاتصال بالجلسة", - "collapse": "تصغير شريط الأدوات", + "collapse": "طي شريط الأدوات", "expand": "توسيع شريط الأدوات", - "dragHandle": "اسحب لإعادة التموضع" + "dragHandle": "اسحب لتغيير الموضع", + "switchToTrackpad": "قم بالتبديل إلى وضع لوحة اللمس (اسحب لتحريك المؤشر، انقر للنقر)", + "switchToTouch": "قم بالتبديل إلى وضع اللمس (انقر مباشرة في المكان الذي تريد النقر فيه)" } }, "terminal": { "connect": "الاتصال بالمضيف", - "clear": "واضح", + "clear": "مسح", "paste": "لصق", "reconnect": "إعادة الاتصال", "connectionLost": "انقطع الاتصال", "connected": "متصل", - "clipboardWriteFailed": "فشلت عملية النسخ إلى الحافظة. تأكد من أن الصفحة تُعرض عبر بروتوكول HTTPS أو الخادم المحلي.", - "clipboardReadFailed": "فشل في القراءة من الحافظة. تأكد من منح أذونات الوصول إلى الحافظة.", - "clipboardHttpWarning": "يتطلب اللصق استخدام بروتوكول HTTPS. استخدم Ctrl+Shift+V أو قم بتشغيل Termix عبر بروتوكول HTTPS.", - "passwordPromptFillTitle": "أدخل كلمة المرور المحفوظة في هذا الحقل؟", + "clipboardWriteFailed": "فشل النسخ إلى الحافظة. تأكد من تقديم الصفحة عبر HTTPS أو localhost.", + "clipboardReadFailed": "فشلت القراءة من الحافظة. تأكد من منح أذونات الحافظة.", + "clipboardHttpWarning": "يتطلب اللصق HTTPS. استخدم Ctrl+Shift+V أو قم بتقديم Termix عبر HTTPS.", + "passwordPromptFillTitle": "هل تريد ملء كلمة المرور المحفوظة في هذه النافذة؟", "unknownError": "حدث خطأ غير معروف", "websocketError": "خطأ في اتصال WebSocket", "connecting": "جارٍ الاتصال...", - "noHostSelected": "لم يتم تحديد أي مضيف", - "reconnecting": "إعادة الاتصال... ({{attempt}}/{{max}})", + "noHostSelected": "لم يتم تحديد مضيف", + "reconnecting": "جارٍ إعادة الاتصال... ({{attempt}}/{{max}})", "reconnected": "تمت إعادة الاتصال بنجاح", "tmuxSessionCreated": "تم إنشاء جلسة tmux: {{name}}", - "tmuxSessionAttached": "تم إرفاق جلسة tmux: {{name}}", - "tmuxUnavailable": "لم يتم تثبيت tmux على المضيف البعيد، لذا سيتم استخدام shell القياسي.", + "tmuxSessionAttached": "تم الالتحاق بجلسة tmux: {{name}}", + "tmuxUnavailable": "tmux غير مثبّت على المضيف البعيد، جارٍ الرجوع إلى الصدفة القياسية", "tmuxSessionPickerTitle": "جلسات tmux", - "tmuxSessionPickerDesc": "تم العثور على جلسات tmux موجودة على هذا المضيف. اختر واحدة لإعادة الاتصال بها أو إنشاء جلسة جديدة.", - "tmuxWindows": "ويندوز", + "tmuxSessionPickerDesc": "تم العثور على جلسات tmux موجودة على هذا المضيف. اختر واحدة لإعادة الاتصال أو أنشئ جلسة جديدة.", + "tmuxWindows": "النوافذ", "tmuxWindowCount": "{{count}} نافذة", - "tmuxAttached": "العملاء المرتبطون", - "tmuxAttachedCount": "مرفق {{count}}", - "tmuxLastActivity": "النشاط الأخير", - "tmuxTimeJustNow": "الآن", - "tmuxTimeMinutes": "{{count}}منذ شهر", - "tmuxTimeHours": "{{count}}منذ ساعة", - "tmuxTimeDays": "{{count}}منذ يوم", + "tmuxAttached": "العملاء المتصلون", + "tmuxAttachedCount": "{{count}} متصل", + "tmuxLastActivity": "آخر نشاط", + "tmuxTimeJustNow": "الآن فقط", + "tmuxTimeMinutes": "منذ {{count}}د", + "tmuxTimeHours": "منذ {{count}}س", + "tmuxTimeDays": "منذ {{count}}ي", "tmuxCreateNew": "بدء جلسة جديدة", - "tmuxCopyHint": "اضبط التحديد واضغط على مفتاح الإدخال لنسخه إلى الحافظة", - "tmuxDetach": "افصل جلسة tmux", - "tmuxDetached": "تم فصل جلسة tmux", + "tmuxCopyHint": "اضبط التحديد واضغط Enter للنسخ إلى الحافظة", + "tmuxDetach": "فصل من جلسة tmux", + "tmuxDetached": "تم الفصل من جلسة tmux", "maxReconnectAttemptsReached": "تم الوصول إلى الحد الأقصى لمحاولات إعادة الاتصال", - "closeTab": "يغلق", - "connectionTimeout": "انقطع الاتصال", + "closeTab": "إغلاق", + "connectionTimeout": "انتهت مهلة الاتصال", "terminalTitle": "الطرفية - {{host}}", "terminalWithPath": "الطرفية - {{host}}:{{path}}", "runTitle": "تشغيل {{command}} - {{host}}", - "totpRequired": "يلزم التحقق بخطوتين", + "totpRequired": "مطلوب المصادقة الثنائية", "totpCodeLabel": "رمز التحقق", - "totpVerify": "يؤكد", - "warpgateAuthRequired": "يلزم التحقق من بوابة الالتواء", + "totpVerify": "تحقق", + "mfaPromptRequired": "المصادقة مطلوبة", + "mfaPushRequired": "يلزم التحقق من صحة الإشعارات", + "mfaMenuPlaceholder": "أدخل ردك", + "mfaWaitingApproval": "في انتظار الموافقة على جهازك...", + "mfaSendRequest": "إرسال الطلب", + "warpgateAuthRequired": "مطلوب مصادقة Warpgate", "warpgateSecurityKey": "مفتاح الأمان", - "warpgateAuthUrl": "عنوان URL للمصادقة", - "warpgateOpenBrowser": "افتح في المتصفح", - "warpgateContinue": "لقد أكملت عملية المصادقة", - "opksshAuthRequired": "يلزم المصادقة عبر OPKSSH", - "opksshAuthDescription": "أكمل عملية المصادقة في متصفحك للمتابعة. ستظل هذه الجلسة صالحة لمدة 24 ساعة.", + "warpgateAuthUrl": "رابط المصادقة", + "warpgateOpenBrowser": "فتح في المتصفح", + "warpgateContinue": "لقد أكملت المصادقة", + "opksshAuthRequired": "مطلوب مصادقة OPKSSH", + "opksshAuthDescription": "أكمل المصادقة في المتصفح للمتابعة. تبقى هذه الجلسة صالحة لمدة 24 ساعة.", "opksshOpenBrowser": "افتح المتصفح للمصادقة", "opksshWaitingForAuth": "في انتظار المصادقة في المتصفح...", "opksshAuthenticating": "جارٍ معالجة المصادقة...", "opksshTimeout": "انتهت مهلة المصادقة. يرجى المحاولة مرة أخرى.", - "opksshAuthFailed": "فشلت عملية المصادقة. يرجى التحقق من بيانات اعتمادك والمحاولة مرة أخرى.", - "opksshSignInWith": "سجّل الدخول باستخدام {{provider}}", - "vaultAuthTitle": "يلزم تسجيل الدخول إلى الخزنة", - "vaultAuthDescription": "تم فتح نافذة لتسجيل الدخول إلى HashiCorp Vault. أكمل عملية تسجيل الدخول هناك؛ وسيتم متابعة هذا الاتصال تلقائيًا.", - "vaultAuthFailed": "فشلت عملية مصادقة الخزنة. يرجى المحاولة مرة أخرى.", + "opksshAuthFailed": "فشلت المصادقة. يرجى التحقق من بيانات الاعتماد والمحاولة مرة أخرى.", + "opksshSignInWith": "تسجيل الدخول باستخدام {{provider}}", + "vaultAuthTitle": "مطلوب تسجيل الدخول إلى Vault", + "vaultAuthDescription": "تم فتح نافذة لتسجيل الدخول إلى HashiCorp Vault. أكمل تسجيل الدخول هناك؛ سيستمر هذا الاتصال تلقائيًا.", + "vaultAuthFailed": "فشلت مصادقة Vault. يرجى المحاولة مرة أخرى.", "vaultReopen": "أعد فتح نافذة تسجيل الدخول", - "sudoPasswordPopupTitle": "أدخل كلمة المرور؟", - "linkDialogTitle": "افتح الرابط", - "linkDialogOpen": "يفتح", - "linkDialogCopy": "ينسخ", - "websocketAbnormalClose": "تم إغلاق الاتصال بشكل غير متوقع. قد يكون هذا بسبب مشكلة في إعدادات الخادم الوكيل العكسي أو بروتوكول SSL. يرجى مراجعة سجلات الخادم.", + "sudoPasswordPopupTitle": "إدراج كلمة المرور؟", + "linkDialogTitle": "فتح الرابط", + "linkDialogOpen": "فتح", + "linkDialogCopy": "نسخ", + "websocketAbnormalClose": "أُغلق الاتصال بشكل غير متوقع. قد يكون السبب مشكلة في الوكيل العكسي أو إعدادات SSL. يرجى مراجعة سجلات الخادم.", "connectionLogTitle": "سجل الاتصال", - "connectionLogCopy": "انسخ السجلات إلى الحافظة", - "connectionLogEmpty": "لا توجد سجلات اتصال حتى الآن", + "connectionLogCopy": "نسخ السجلات إلى الحافظة", + "connectionLogEmpty": "لا توجد سجلات اتصال بعد", "connectionLogWaiting": "في انتظار سجلات الاتصال...", "connectionLogCopied": "تم نسخ سجلات الاتصال إلى الحافظة", "connectionLogCopyFailed": "فشل نسخ السجلات إلى الحافظة", - "connectionRejected": "رفض الخادم الاتصال. يرجى التحقق من بيانات المصادقة وإعدادات الشبكة.", + "connectionRejected": "تم رفض الاتصال من قبل الخادم. يرجى التحقق من المصادقة وإعدادات الشبكة.", "hostKeyRejected": "تم رفض التحقق من مفتاح مضيف SSH. تم إلغاء الاتصال.", "sessionTakenOver": "تم فتح الجلسة في علامة تبويب أخرى. جارٍ إعادة الاتصال...", "split": { - "splitTab": "علامة تبويب مقسمة", - "addToSplit": "أضف إلى التقسيم", - "removeFromSplit": "أزل من التقسيم" + "splitTab": "تقسيم علامة التبويب", + "addToSplit": "إضافة إلى التقسيم", + "removeFromSplit": "إزالة من التقسيم" } }, "fileManager": { - "noHostSelected": "لم يتم تحديد أي مضيف", + "noHostSelected": "لم يتم اختيار مضيف", "initializingEditor": "جارٍ تهيئة المحرر...", "file": "ملف", "folder": "مجلد", - "uploadFile": "تحميل الملف", - "downloadFile": "تحميل", + "uploadFile": "رفع ملف", + "downloadFile": "تنزيل", "extractArchive": "استخراج الأرشيف", - "extractingArchive": "استخراج {{name}}...", + "extractingArchive": "جارٍ استخراج {{name}}...", "archiveExtractedSuccessfully": "تم استخراج {{name}} بنجاح", - "extractFailed": "فشلت عملية الاستخراج", - "compressFile": "ضغط الملف", - "compressFiles": "ضغط الملفات", - "compressFilesDesc": "ضغط {{count}} عنصرًا في أرشيف", + "extractFailed": "فشل الاستخراج", + "compressFile": "ضغط ملف", + "compressFiles": "ضغط ملفات", + "compressFilesDesc": "ضغط {{count}} عنصر في أرشيف", "archiveName": "اسم الأرشيف", "enterArchiveName": "أدخل اسم الأرشيف...", - "compressionFormat": "تنسيق الضغط", + "compressionFormat": "صيغة الضغط", "selectedFiles": "الملفات المحددة", - "andMoreFiles": "والمزيد {{count}}...", + "andMoreFiles": "و {{count}} المزيد...", "compress": "ضغط", - "compressingFiles": "ضغط {{count}} عنصرًا إلى {{name}}...", + "compressingFiles": "جارٍ ضغط {{count}} عنصر في {{name}}...", "filesCompressedSuccessfully": "تم إنشاء {{name}} بنجاح", "compressFailed": "فشل الضغط", - "edit": "يحرر", + "edit": "تحرير", "preview": "معاينة", - "previous": "سابق", + "previous": "السابق", "next": "التالي", - "pageXOfY": "الصفحة {{current}} من {{total}}", - "zoomOut": "تصغير العرض", - "zoomIn": "تكبير الصورة", + "pageXOfY": "صفحة {{current}} من {{total}}", + "zoomOut": "تصغير", + "zoomIn": "تكبير", "newFile": "ملف جديد", "newFolder": "مجلد جديد", "rename": "إعادة تسمية", - "uploading": "جارٍ التحميل...", - "uploadingFile": "جارٍ التحميل {{name}}...", + "uploading": "جارٍ الرفع...", + "uploadingFile": "جارٍ رفع {{name}}...", "fileName": "اسم الملف", "folderName": "اسم المجلد", - "fileUploadedSuccessfully": "تم تحميل الملف \"{{name}}\" بنجاح", - "failedToUploadFile": "فشل تحميل الملف", + "fileUploadedSuccessfully": "تم رفع الملف \"{{name}}\" بنجاح", + "failedToUploadFile": "فشل رفع الملف", "fileDownloadedSuccessfully": "تم تنزيل الملف \"{{name}}\" بنجاح", "failedToDownloadFile": "فشل تنزيل الملف", "fileCreatedSuccessfully": "تم إنشاء الملف \"{{name}}\" بنجاح", "folderCreatedSuccessfully": "تم إنشاء المجلد \"{{name}}\" بنجاح", "failedToCreateItem": "فشل إنشاء العنصر", - "operationFailed": "فشلت العملية {{operation}} لـ {{name}}: {{error}}", - "failedToResolveSymlink": "فشل في حل الرابط الرمزي", - "itemsDeletedSuccessfully": "تم حذف {{count}} عنصرًا بنجاح", + "operationFailed": "فشلت عملية {{operation}} لـ {{name}}: {{error}}", + "failedToResolveSymlink": "فشل حل الرابط الرمزي", + "itemsDeletedSuccessfully": "تم حذف {{count}} عناصر بنجاح", "failedToDeleteItems": "فشل حذف العناصر", "sudoPasswordRequired": "كلمة مرور المسؤول مطلوبة", "enterSudoPassword": "أدخل كلمة مرور sudo لمتابعة هذه العملية", "sudoPassword": "كلمة مرور sudo", "sudoOperationFailed": "فشلت عملية sudo", - "sudoAuthFailed": "فشلت عملية مصادقة sudo", - "dragFilesToUpload": "قم بإسقاط الملفات هنا لتحميلها", + "sudoAuthFailed": "فشلت مصادقة sudo", + "dragFilesToUpload": "أفلت الملفات هنا لرفعها", "emptyFolder": "هذا المجلد فارغ", - "searchFiles": "ملفات البحث...", + "searchFiles": "بحث في الملفات...", "upload": "رفع", "selectHostToStart": "حدد مضيفًا لبدء إدارة الملفات", - "sshRequiredForFileManager": "يتطلب مدير الملفات استخدام SSH. هذا المضيف لا يدعم SSH.", + "sshRequiredForFileManager": "يتطلب مدير الملفات SSH. هذا المضيف لم يتم تمكين SSH عليه.", "failedToConnect": "فشل الاتصال بـ SSH", - "failedToLoadDirectory": "فشل تحميل الدليل", + "failedToLoadDirectory": "فشل تحميل المجلد", "noSSHConnection": "لا يوجد اتصال SSH متاح", - "copy": "ينسخ", - "cut": "يقطع", + "copy": "نسخ", + "cut": "قص", "paste": "لصق", "copyPath": "نسخ المسار", "copyPaths": "نسخ المسارات", - "delete": "يمسح", - "properties": "ملكيات", - "refresh": "ينعش", - "downloadFiles": "قم بتنزيل {{count}} ملفًا إلى المتصفح", + "delete": "حذف", + "properties": "خصائص", + "refresh": "تحديث", + "downloadFiles": "تنزيل {{count}} ملفات إلى المتصفح", "copyFiles": "نسخ {{count}} عناصر", "cutFiles": "قص {{count}} عناصر", - "deleteFiles": "حذف {{count}} عنصرًا", - "filesCopiedToClipboard": "تم نسخ العناصر {{count}} إلى الحافظة", - "filesCutToClipboard": "{{count}} العناصر المقطوعة إلى الحافظة", + "deleteFiles": "حذف {{count}} عناصر", + "filesCopiedToClipboard": "تم نسخ {{count}} عناصر إلى الحافظة", + "filesCutToClipboard": "تم قص {{count}} عناصر إلى الحافظة", "pathCopiedToClipboard": "تم نسخ المسار إلى الحافظة", - "pathsCopiedToClipboard": "{{count}} تم نسخ المسارات إلى الحافظة", + "pathsCopiedToClipboard": "تم نسخ {{count}} مسارات إلى الحافظة", "failedToCopyPath": "فشل نسخ المسار إلى الحافظة", - "copyFolderLink": "انسخ الرابط إلى المجلد", - "copyCurrentFolderLink": "انسخ الرابط إلى المجلد الحالي", + "copyFolderLink": "نسخ رابط المجلد", + "copyCurrentFolderLink": "نسخ رابط المجلد الحالي", "folderLinkCopied": "تم نسخ رابط المجلد إلى الحافظة", "failedToCopyFolderLink": "فشل نسخ رابط المجلد", - "movedItems": "تم نقل {{count}} عنصرًا", + "movedItems": "تم نقل {{count}} عناصر", "failedToDeleteItem": "فشل حذف العنصر", "itemRenamedSuccessfully": "تمت إعادة تسمية {{type}} بنجاح", - "failedToRenameItem": "فشل في إعادة تسمية العنصر", - "download": "تحميل", - "openExternalEditor": "افتح خارجياً", - "chooseExternalEditor": "اختر محررًا خارجيًا", - "externalEditorSelected": "تم اختيار محرر خارجي", - "externalEditorDesktopOnly": "لا يتوفر المحرر الخارجي إلا في تطبيق سطح المكتب", - "externalEditorOpened": "تم فتح الملف في محرر خارجي. سيؤدي الحفظ هناك إلى تحميل التغييرات مرة أخرى إلى الخادم.", + "failedToRenameItem": "فشلت إعادة تسمية العنصر", + "download": "تنزيل", + "openExternalEditor": "فتح خارجيًا", + "chooseExternalEditor": "اختيار محرر خارجي", + "externalEditorSelected": "تم اختيار المحرر الخارجي", + "externalEditorDesktopOnly": "المحرر الخارجي متاح فقط في تطبيق سطح المكتب", + "externalEditorOpened": "تم الفتح في المحرر الخارجي. سيؤدي الحفظ هناك إلى رفع التغييرات إلى الخادم.", "failedToOpenExternalEditor": "فشل فتح المحرر الخارجي", - "failedToSelectExternalEditor": "فشل في تحديد محرر خارجي", - "permissions": "الأذونات", - "size": "مقاس", - "modified": "معدل", - "path": "طريق", - "confirmDelete": "هل أنت متأكد من رغبتك في حذف {{name}}؟", + "failedToSelectExternalEditor": "فشل اختيار المحرر الخارجي", + "permissions": "الصلاحيات", + "size": "الحجم", + "modified": "تاريخ التعديل", + "path": "المسار", + "confirmDelete": "هل أنت متأكد أنك تريد حذف {{name}}؟", "permissionDenied": "تم رفض الإذن", "serverError": "خطأ في الخادم", "fileSavedSuccessfully": "تم حفظ الملف بنجاح", "failedToSaveFile": "فشل حفظ الملف", - "confirmDeleteSingleItem": "هل أنت متأكد من رغبتك في حذف \"{{name}}\" نهائياً؟", - "confirmDeleteMultipleItems": "هل أنت متأكد من رغبتك في حذف العناصر {{count}} نهائياً؟", - "confirmDeleteMultipleItemsWithFolders": "هل أنت متأكد من رغبتك في حذف العناصر {{count}} نهائياً؟ يشمل ذلك المجلدات ومحتوياتها.", - "confirmDeleteFolder": "هل أنت متأكد من رغبتك في حذف المجلد \"{{name}}\" وجميع محتوياته بشكل نهائي؟", - "permanentDeleteWarning": "لا يمكن التراجع عن هذا الإجراء. سيتم حذف العنصر (العناصر) نهائياً من الخادم.", - "recent": "مؤخرًا", - "pinned": "مثبت", + "confirmDeleteSingleItem": "هل أنت متأكد أنك تريد حذف \"{{name}}\" نهائيًا؟", + "confirmDeleteMultipleItems": "هل أنت متأكد أنك تريد حذف {{count}} عناصر نهائيًا؟", + "confirmDeleteMultipleItemsWithFolders": "هل أنت متأكد أنك تريد حذف {{count}} عناصر نهائيًا؟ يتضمن ذلك المجلدات ومحتوياتها.", + "confirmDeleteFolder": "هل أنت متأكد أنك تريد حذف المجلد \"{{name}}\" وجميع محتوياته نهائيًا؟", + "permanentDeleteWarning": "لا يمكن التراجع عن هذا الإجراء. سيتم حذف العنصر (العناصر) نهائيًا من الخادم.", + "recent": "الأخيرة", + "pinned": "المثبتة", "folderShortcuts": "اختصارات المجلدات", "failedToReconnectSSH": "فشل إعادة الاتصال بجلسة SSH", "openTerminalHere": "افتح الطرفية هنا", - "run": "يجري", + "run": "تشغيل", "openTerminalInFolder": "افتح الطرفية في هذا المجلد", "openTerminalInFileLocation": "افتح الطرفية في موقع الملف", - "runningFile": "الجري - {{file}}", - "onlyRunExecutableFiles": "لا يمكن تشغيل سوى الملفات القابلة للتنفيذ", - "directories": "دليل المستخدم", - "removedFromRecentFiles": "تمت إزالة \"{{name}}\" من الملفات الأخيرة", - "removeFailed": "فشلت عملية الإزالة", + "runningFile": "قيد التشغيل - {{file}}", + "onlyRunExecutableFiles": "يمكن تشغيل الملفات التنفيذية فقط", + "directories": "المجلدات", + "removedFromRecentFiles": "تمت إزالة \"{{name}}\" من الملفات الحديثة", + "removeFailed": "فشلت الإزالة", "unpinnedSuccessfully": "تم إلغاء تثبيت \"{{name}}\" بنجاح", - "unpinFailed": "فشلت عملية إلغاء التثبيت", + "unpinFailed": "فشل إلغاء التثبيت", "removedShortcut": "تمت إزالة الاختصار \"{{name}}\"", - "removeShortcutFailed": "فشلت عملية إزالة الاختصار", - "clearedAllRecentFiles": "تم مسح جميع الملفات الأخيرة", - "clearFailed": "فشلت عملية المسح", - "removeFromRecentFiles": "إزالة من الملفات الأخيرة", - "clearAllRecentFiles": "مسح جميع الملفات الأخيرة", + "removeShortcutFailed": "فشلت إزالة الاختصار", + "clearedAllRecentFiles": "تم مسح جميع الملفات الحديثة", + "clearFailed": "فشل المسح", + "removeFromRecentFiles": "إزالة من الملفات الحديثة", + "clearAllRecentFiles": "مسح جميع الملفات الحديثة", "unpinFile": "إلغاء تثبيت الملف", "removeShortcut": "إزالة الاختصار", - "pinFile": "ملف رقم التعريف الشخصي", - "addToShortcuts": "أضف إلى الاختصارات", - "pasteFailed": "فشلت عملية اللصق", - "noUndoableActions": "لا توجد إجراءات قابلة للتراجع", - "undoCopySuccess": "تم التراجع عن عملية النسخ: تم حذف {{count}} الملفات المنسوخة", + "pinFile": "تثبيت الملف", + "addToShortcuts": "إضافة إلى الاختصارات", + "pasteFailed": "فشل اللصق", + "noUndoableActions": "لا توجد إجراءات يمكن التراجع عنها", + "undoCopySuccess": "تم التراجع عن عملية النسخ: حُذف {{count}} ملفًا منسوخًا", "undoCopyFailedDelete": "فشل التراجع: تعذر حذف أي ملفات منسوخة", - "undoCopyFailedNoInfo": "فشل التراجع: تعذر العثور على معلومات الملف المنسوخ", - "undoMoveSuccess": "تم التراجع عن عملية النقل: تم نقل {{count}} ملفًا إلى موقعها الأصلي", - "undoMoveFailedMove": "فشل التراجع: تعذر نقل أي ملفات إلى الخلف", - "undoMoveFailedNoInfo": "فشل التراجع: تعذر العثور على معلومات الملف المنقول", - "undoDeleteNotSupported": "لا يمكن التراجع عن عملية الحذف: تم حذف الملفات نهائياً من الخادم", + "undoCopyFailedNoInfo": "فشل التراجع: تعذر العثور على معلومات الملفات المنسوخة", + "undoMoveSuccess": "تم التراجع عن عملية النقل: تم نقل {{count}} ملفًا إلى الموقع الأصلي", + "undoMoveFailedMove": "فشل التراجع: تعذر نقل أي ملفات للخلف", + "undoMoveFailedNoInfo": "فشل التراجع: تعذر العثور على معلومات الملفات المنقولة", + "undoDeleteNotSupported": "لا يمكن التراجع عن عملية الحذف: تم حذف الملفات نهائيًا من الخادم", "undoTypeNotSupported": "نوع عملية التراجع غير مدعوم", "undoOperationFailed": "فشلت عملية التراجع", "unknownError": "خطأ غير معروف", - "confirm": "يتأكد", - "find": "يجد...", - "replace": "يستبدل", - "downloadInstead": "قم بالتنزيل بدلاً من ذلك", + "confirm": "تأكيد", + "find": "بحث...", + "replace": "استبدال", + "downloadInstead": "تنزيل بدلاً من ذلك", "keyboardShortcuts": "اختصارات لوحة المفاتيح", - "searchAndReplace": "البحث والاستبدال", - "editing": "التحرير", - "search": "يبحث", - "findNext": "ابحث عن التالي", - "findPrevious": "البحث عن السابق", - "save": "يحفظ", - "selectAll": "حدد الكل", - "undo": "التراجع", - "redo": "إعادة العمل", - "moveLineUp": "تغيير التشكيلة", - "moveLineDown": "انقل السطر لأسفل", - "toggleComment": "إظهار/إخفاء التعليقات", + "searchAndReplace": "بحث واستبدال", + "editing": "تعديل", + "search": "بحث", + "findNext": "بحث عن التالي", + "findPrevious": "بحث عن السابق", + "save": "حفظ", + "selectAll": "تحديد الكل", + "undo": "تراجع", + "redo": "إعادة", + "moveLineUp": "نقل السطر لأعلى", + "moveLineDown": "نقل السطر لأسفل", + "toggleComment": "تبديل التعليق", "autoComplete": "إكمال تلقائي", "imageLoadError": "فشل تحميل الصورة", "startTyping": "ابدأ الكتابة...", "unknownSize": "حجم غير معروف", "fileIsEmpty": "الملف فارغ", - "largeFileWarning": "تحذير بشأن الملفات الكبيرة", - "largeFileWarningDesc": "حجم هذا الملف {{size}} ، مما قد يتسبب في مشاكل في الأداء عند فتحه كنص.", - "fileNotFoundAndRemoved": "لم يتم العثور على الملف \"{{name}}\" وتمت إزالته من الملفات الأخيرة/المثبتة", + "largeFileWarning": "تحذير ملف كبير", + "largeFileWarningDesc": "هذا الملف بحجم {{size}}، مما قد يسبب مشكلات في الأداء عند فتحه كنص.", + "fileNotFoundAndRemoved": "الملف \"{{name}}\" غير موجود وتمت إزالته من الملفات الأخيرة/المثبتة", "failedToLoadFile": "فشل تحميل الملف: {{error}}", - "serverErrorOccurred": "حدث خطأ في الخادم. يرجى المحاولة مرة أخرى لاحقاً.", + "serverErrorOccurred": "حدث خطأ في الخادم. يرجى المحاولة لاحقًا.", "autoSaveFailed": "فشل الحفظ التلقائي", "fileAutoSaved": "تم حفظ الملف تلقائيًا", - "moveFileFailed": "فشل في نقل {{name}}", + "moveFileFailed": "فشل نقل {{name}}", "moveOperationFailed": "فشلت عملية النقل", - "canOnlyCompareFiles": "لا يمكن مقارنة سوى ملفين", + "canOnlyCompareFiles": "يمكن مقارنة ملفين فقط", "comparingFiles": "مقارنة الملفات: {{file1}} و {{file2}}", "dragFailed": "فشلت عملية السحب", "filePinnedSuccessfully": "تم تثبيت الملف \"{{name}}\" بنجاح", @@ -1730,1186 +1863,1204 @@ "fileUnpinnedSuccessfully": "تم إلغاء تثبيت الملف \"{{name}}\" بنجاح", "unpinFileFailed": "فشل إلغاء تثبيت الملف", "shortcutAddedSuccessfully": "تمت إضافة اختصار المجلد \"{{name}}\" بنجاح", - "addShortcutFailed": "فشل إضافة الاختصار", - "operationCompletedSuccessfully": "تم إدخال {{operation}} {{count}} عنصر بنجاح", + "addShortcutFailed": "فشلت إضافة الاختصار", + "operationCompletedSuccessfully": "تمت {{operation}} {{count}} عناصر بنجاح", "operationCompleted": "{{operation}} {{count}} عناصر", "downloadFileSuccess": "تم تنزيل الملف {{name}} بنجاح", "downloadFileFailed": "فشل التنزيل", - "moveTo": "انتقل إلى {{name}}", - "diffCompareWith": "قارن الفرق مع {{name}}", - "dragOutsideToDownload": "اسحب خارج النافذة لتنزيل ({{count}} ملفًا)", + "moveTo": "نقل إلى {{name}}", + "diffCompareWith": "مقارنة الاختلافات مع {{name}}", + "dragOutsideToDownload": "اسحب خارج النافذة للتنزيل ({{count}} ملفات)", "newFolderDefault": "مجلد جديد", "newFileDefault": "ملف جديد.txt", - "successfullyMovedItems": "تم نقل {{count}} عنصر بنجاح إلى {{target}}", - "move": "يتحرك", - "searchInFile": "ابحث في الملف (Ctrl+F)", + "successfullyMovedItems": "تم نقل {{count}} عناصر بنجاح إلى {{target}}", + "move": "نقل", + "searchInFile": "بحث في الملف (Ctrl+F)", "showKeyboardShortcuts": "إظهار اختصارات لوحة المفاتيح", - "decreaseFontSize": "قلل حجم الخط", - "increaseFontSize": "قم بزيادة حجم الخط", - "startWritingMarkdown": "ابدأ بكتابة محتوى Markdown الخاص بك...", - "loadingFileComparison": "جارٍ تحميل مقارنة الفيديو...", - "reload": "أعد تحميل الصفحة", - "compare": "يقارن", - "sideBySide": "جنبا إلى جنب", + "decreaseFontSize": "تصغير حجم الخط", + "increaseFontSize": "تكبير حجم الخط", + "startWritingMarkdown": "ابدأ كتابة محتوى Markdown...", + "loadingFileComparison": "جارٍ تحميل مقارنة الملفات...", + "reload": "إعادة تحميل", + "compare": "مقارنة", + "sideBySide": "جنباً إلى جنب", "inline": "مضمن", "fileComparison": "مقارنة الملفات: {{file1}} مقابل {{file2}}", - "fileTooLarge": "حجم الملف كبير جدًا: {{error}}", - "sshConnectionFailed": "فشل الاتصال عبر SSH. يرجى التحقق من اتصالك بـ {{name}} ({{ip}}:{{port}})", + "fileTooLarge": "الملف كبير جداً: {{error}}", + "sshConnectionFailed": "فشل اتصال SSH. يرجى التحقق من الاتصال بـ {{name}} ({{ip}}:{{port}})", "loadFileFailed": "فشل تحميل الملف: {{error}}", "connecting": "جارٍ الاتصال...", "connectedSuccessfully": "تم الاتصال بنجاح", - "totpVerificationFailed": "فشل التحقق من رمز التحقق من نوع TOTP", - "warpgateVerificationFailed": "فشلت عملية مصادقة بوابة الحرب", - "authenticationFailed": "فشلت عملية المصادقة", + "totpVerificationFailed": "فشل التحقق من TOTP", + "warpgateVerificationFailed": "فشلت مصادقة Warpgate", + "authenticationFailed": "فشلت المصادقة", "incorrectPassphrase": "عبارة المرور غير صحيحة. يرجى المحاولة مرة أخرى.", "verificationCodePrompt": "رمز التحقق:", - "changePermissions": "تغيير الأذونات", - "currentPermissions": "الأذونات الحالية", - "owner": "مالك", - "group": "مجموعة", - "others": "آحرون", - "read": "يقرأ", - "write": "يكتب", - "execute": "ينفذ", - "permissionsChangedSuccessfully": "تم تغيير الأذونات بنجاح", - "failedToChangePermissions": "فشل تغيير الأذونات", - "name": "اسم", - "sortByName": "اسم", + "changePermissions": "تغيير الصلاحيات", + "currentPermissions": "الصلاحيات الحالية", + "owner": "المالك", + "group": "المجموعة", + "others": "الآخرون", + "read": "قراءة", + "write": "كتابة", + "execute": "تنفيذ", + "permissionsChangedSuccessfully": "تم تغيير الصلاحيات بنجاح", + "failedToChangePermissions": "فشل تغيير الصلاحيات", + "name": "الاسم", + "sortByName": "الاسم", "sortByDate": "تاريخ التعديل", - "sortBySize": "مقاس", - "ascending": "صعوداً", - "descending": "نزولاً", - "root": "جذر", + "sortBySize": "الحجم", + "ascending": "تصاعدي", + "descending": "تنازلي", + "root": "الجذر", "new": "جديد", - "sortBy": "فرز حسب", - "items": "أغراض", - "selected": "مختار", + "sortBy": "ترتيب حسب", + "items": "عناصر", + "selected": "محدد", "editor": "محرر", "octal": "ثماني", - "storage": "تخزين", - "disk": "أدك", + "storage": "التخزين", + "disk": "القرص", "used": "مستخدم", - "of": "ل", - "toggleSidebar": "تبديل الشريط الجانبي", - "cannotLoadPdf": "لا يمكن تحميل ملف PDF", + "of": "من", + "toggleSidebar": "إظهار/إخفاء الشريط الجانبي", + "cannotLoadPdf": "لا يمكن تحميل PDF", "pdfLoadError": "حدث خطأ أثناء تحميل ملف PDF هذا.", - "loadingPdf": "جارٍ تحميل ملف PDF...", + "loadingPdf": "جارٍ تحميل PDF...", "loadingPage": "جارٍ تحميل الصفحة..." }, "transfer": { - "copyToHost": "انسخ إلى المضيف…", - "moveToHost": "انتقل إلى المضيف…", - "copyItemsToHost": "انسخ {{count}} عنصرًا إلى المضيف…", - "moveItemsToHost": "انقل {{count}} عنصرًا إلى المضيف…", - "noHostsConnected": "لا توجد مضيفات أخرى لإدارة الملفات متاحة.", - "noHostsConnectedHint": "أضف مضيف SSH آخر مع تمكين مدير الملفات في مدير المضيف.", - "selectDestinationHost": "حدد المضيف الوجهة", + "copyToHost": "نسخ إلى المضيف…", + "moveToHost": "نقل إلى المضيف…", + "copyItemsToHost": "نسخ {{count}} عناصر إلى المضيف…", + "moveItemsToHost": "نقل {{count}} عناصر إلى المضيف…", + "noHostsConnected": "لا تتوفر مضيفات أخرى لمدير الملفات.", + "noHostsConnectedHint": "أضف مضيف SSH آخر مع تمكين مدير الملفات في مدير المضيفات.", + "selectDestinationHost": "تحديد المضيف الوجهة", "destinationPath": "مسار الوجهة", - "recentDestinations": "الوجهات السياحية الحديثة", + "recentDestinations": "الوجهات الأخيرة", "collapseRecentDestinations": "طي الوجهات الأخيرة", "expandRecentDestinations": "توسيع الوجهات الأخيرة", "browseFolders": "تصفح مجلدات الوجهة", - "browseDestination": "تصفح أو أدخل المسار", - "confirmCopy": "ينسخ", - "confirmMove": "يتحرك", - "transferring": "نقل…", - "compressing": "ضغط…", - "extracting": "استخراج…", - "transferringItems": "نقل {{current}} من {{total}} عنصر…", - "transferSuccess": "اكتملت عملية النقل", - "transferError": "فشلت عملية النقل", - "transferPartial": "اكتمل النقل بدون أخطاء {{count}}", - "transferPartialHint": "تعذر التحويل: {{paths}}", - "itemsSummary": "{{count}} عناصر", - "destMustBeDirectory": "يجب أن تكون الوجهة دليلاً لعمليات نقل العناصر المتعددة", - "selectThisFolder": "حدد هذا المجلد", - "browsePathWillBeCreated": "هذا المجلد غير موجود بعد. سيتم إنشاؤه عند بدء عملية النقل.", + "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": "مستعد", + "goUp": "للأعلى", + "copyFolderToHost": "نسخ المجلد إلى المضيف…", + "moveFolderToHost": "نقل المجلد إلى المضيف…", + "hostReady": "جاهز", "hostConnecting": "جارٍ الاتصال…", "hostDisconnected": "غير متصل", - "hostAuthRequired": "يلزم المصادقة — افتح مدير الملفات على هذا المضيف أولاً", + "hostAuthRequired": "المصادقة مطلوبة — افتح مدير الملفات على هذا المضيف أولاً", "hostConnectionFailed": "فشل الاتصال", - "metricsTitle": "مواعيد الانتقال", - "metricsPrepare": "تجهيز الوجهة: {{duration}}", - "metricsCompress": "الضغط على المصدر: {{duration}}", - "metricsHopSourceRead": "المصدر ← الخادم: {{throughput}}", + "metricsTitle": "توقيتات النقل", + "metricsPrepare": "تحضير الوجهة: {{duration}}", + "metricsCompress": "ضغط على المصدر: {{duration}}", + "metricsHopSourceRead": "المصدر → الخادم: {{throughput}}", "metricsHopDestSftpWrite": "الخادم → الوجهة (SFTP): {{throughput}}", "metricsHopDestLocalWrite": "الخادم → الوجهة (محلي): {{throughput}}", - "metricsTransfer": "من البداية إلى النهاية: {{throughput}} ({{duration}})", - "metricsExtract": "استخراج البيانات في الوجهة: {{duration}}", + "metricsTransfer": "نقل شامل: {{throughput}} ({{duration}})", + "metricsExtract": "فك على الوجهة: {{duration}}", "metricsSourceDelete": "إزالة من المصدر: {{duration}}", - "metricsTotal": "المجموع: {{duration}}", - "progressCompressing": "الضغط على المضيف المصدر…", - "progressExtracting": "جارٍ الاستخراج في الوجهة…", - "progressTransferring": "نقل البيانات…", - "progressReconnecting": "إعادة الاتصال…", - "parallelSegmentsLabel": "مسارات نقل متوازية", - "parallelSegmentsOption": "{{count}} ممرات", - "parallelSegmentsHint": "يتم تقسيم الملفات الكبيرة إلى أجزاء بحجم 256 ميجابايت. تستخدم المسارات المتعددة اتصالات منفصلة (مثل بدء عمليات نقل متعددة) لزيادة الإنتاجية الإجمالية.", + "metricsTotal": "الإجمالي: {{duration}}", + "progressCompressing": "جارٍ الضغط على المضيف المصدر…", + "progressExtracting": "جارٍ الفك على الوجهة…", + "progressTransferring": "جارٍ نقل البيانات…", + "progressReconnecting": "جارٍ إعادة الاتصال…", + "parallelSegmentsLabel": "مسارات النقل المتوازية", + "parallelSegmentsOption": "{{count}} مسارات", + "parallelSegmentsHint": "تُقسم الملفات الكبيرة إلى أجزاء بحجم 256 ميغابايت. تستخدم المسارات المتعددة اتصالات منفصلة (مثل بدء عدة عمليات نقل) لتحقيق إنتاجية إجمالية أعلى.", "progressTotalSpeed": "{{speed}} إجمالي ({{lanes}} مسارات)", - "progressTransferringItems": "نقل الملفات ({{current}} من {{total}})…", + "progressTransferringItems": "جارٍ نقل الملفات ({{current}} من {{total}})…", "progressBytes": "{{transferred}} / {{total}}", "progressItems": "{{current}} / {{total}} ملفات", - "sourceNotDeletedPartial": "تم الاحتفاظ بالملفات المصدرية (نقل جزئي)", - "jumpHostLimitation": "يجب أن يكون كلا المضيفين قابلين للوصول من خادم Termix. لا يتم دعم التوجيه المباشر بين المضيفين.", - "cancel": "يلغي", + "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 المستخدمة في نسخ الملفات الفردية، واحداً تلو الآخر. يتم تجميع التقدم عبر جميع الملفات، لذا يتحرك الشريط ببطء أثناء نقل الملفات الكبيرة.", + "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 كنسخ الملف الواحد، واحداً تلو الآخر. يُجمع التقدم عبر جميع الملفات، لذا يتحرك الشريط ببطء أثناء الملفات الكبيرة.", "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": "اخترت 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 لكل ملف لهذه المجموعة." }, - "progressCancel": "يلغي", - "progressCancelling": "إلغاء…", + "progressCancel": "إلغاء", + "progressCancelling": "جارٍ الإلغاء…", "progressStalled": "متوقف", - "resumedHint": "تمت إعادة الاتصال بعملية نقل بيانات نشطة بدأت في نافذة أخرى.", - "transferCancelled": "تم إلغاء التحويل", - "transferCancelledCopyHint": "قد تبقى بعض الملفات الجزئية في الوجهة.", - "transferCancelledMoveHint": "قد تبقى بعض الملفات الجزئية في الوجهة. لم تُحذف ملفات المصدر.", - "cleanupDestFiles": "وجهة التنظيف", + "resumedHint": "تمت إعادة الاتصال بنقل نشط بدأ في نافذة أخرى.", + "transferCancelled": "تم إلغاء النقل", + "transferCancelledCopyHint": "قد تبقى ملفات جزئية على الوجهة.", + "transferCancelledMoveHint": "قد تبقى ملفات جزئية على الوجهة. لم يتم حذف ملفات المصدر.", + "cleanupDestFiles": "تنظيف الوجهة", "cleanupDestFilesSuccess": "تمت إزالة الملفات الجزئية من الوجهة", - "cleanupDestFilesPartial": "تعذر حذف بعض الملفات الجزئية", - "cleanupDestFilesNothing": "لا يوجد ما يستدعي التنظيف في الوجهة", - "cleanupDestFilesError": "فشلت عملية التنظيف", + "cleanupDestFilesPartial": "تعذر إزالة بعض الملفات الجزئية", + "cleanupDestFilesNothing": "لا شيء للتنظيف على الوجهة", + "cleanupDestFilesError": "فشل التنظيف", "retryTransfer": "إعادة المحاولة", - "retryTransferError": "فشلت المحاولة", - "transferFailedRetryHint": "تم الاحتفاظ ببيانات جزئية على الوجهة. ستُستأنف المحاولة عند عودة الاتصال." + "retryTransferError": "فشلت إعادة المحاولة", + "transferFailedRetryHint": "تم الاحتفاظ بالبيانات الجزئية على الوجهة. ستُستأنف إعادة المحاولة عند عودة الاتصال." }, "tunnels": { - "noSshTunnels": "ممنوع استخدام أنفاق SSH", - "createFirstTunnelMessage": "لم تقم بإنشاء أي أنفاق SSH حتى الآن. قم بتكوين اتصالات الأنفاق في مدير المضيف للبدء.", + "noSshTunnels": "لا توجد أنفاق SSH", + "createFirstTunnelMessage": "لم تُنشئ أي أنفاق SSH بعد. قم بتكوين اتصالات الأنفاق في مدير المضيف للبدء.", "connected": "متصل", "disconnected": "غير متصل", "connecting": "جارٍ الاتصال...", "error": "خطأ", "canceling": "جارٍ الإلغاء...", - "connect": "يتصل", - "disconnect": "قطع الاتصال", - "cancel": "يلغي", - "port": "ميناء", - "localPort": "الميناء المحلي", - "remotePort": "منفذ بعيد", + "connect": "اتصل", + "disconnect": "فصل", + "cancel": "إلغاء", + "port": "منفذ", + "localPort": "المنفذ المحلي", + "remotePort": "المنفذ البعيد", "currentHostPort": "منفذ المضيف الحالي", - "endpointPort": "منفذ نقطة النهاية", + "endpointPort": "منفذ النهاية", "bindIp": "عنوان IP المحلي", - "endpointSshConfig": "تكوين نقطة النهاية عبر SSH", - "endpointSshHost": "مضيف SSH لنقطة النهاية", - "endpointSshHostPlaceholder": "حدد مضيفًا مُهيأً", - "endpointSshHostRequired": "حدد مضيف SSH لنقطة النهاية لكل نفق عميل.", - "attempt": "المحاولة رقم {{current}} من أصل {{max}}", - "nextRetryIn": "المحاولة التالية خلال {{seconds}} ثانية", + "endpointSshConfig": "إعدادات SSH للنهاية", + "endpointSshHost": "مضيف SSH للنهاية", + "endpointSshHostPlaceholder": "اختر مضيفًا مُعيّنًا", + "endpointSshHostRequired": "اختر مضيف SSH للنهاية لكل نفق عميل.", + "attempt": "محاولة {{current}} من {{max}}", + "nextRetryIn": "إعادة المحاولة التالية في {{seconds}} ثانية", "clientTunnels": "أنفاق العميل", "clientTunnel": "نفق العميل", - "addClientTunnel": "إضافة نفق العميل", - "noClientTunnels": "لم يتم تكوين أي أنفاق عميل على هذا الجهاز.", + "addClientTunnel": "إضافة نفق عميل", + "noClientTunnels": "لا توجد أنفاق عميل مُعدّة على سطح المكتب هذا.", "tunnelName": "اسم النفق", "remoteHost": "المضيف البعيد", - "autoStart": "التشغيل التلقائي", - "clientAutoStartDesc": "يبدأ الاتصال عند فتح برنامج سطح المكتب هذا ويبقى متصلاً.", - "clientManualStartDesc": "استخدم زري \"بدء\" و\"إيقاف\" من هذا السطر. لن يفتح برنامج Termix هذا الملف تلقائيًا.", - "clientRemoteServerNote": "قد يتطلب التوجيه عن بُعد السماح بـ AllowTcpForwarding و GatewayPorts على خادم SSH الخاص بالطرفية. يُغلق المنفذ البعيد عند انقطاع اتصال هذا الجهاز.", - "clientTunnelStarted": "تم بدء تشغيل نفق العميل", + "autoStart": "بدء تلقائي", + "clientAutoStartDesc": "يبدأ عند فتح عميل سطح المكتب هذا ويبقى متصلاً.", + "clientManualStartDesc": "استخدم بدء وإيقاف من هذا الصف. لن يقوم Termix بفتحه تلقائيًا.", + "clientRemoteServerNote": "قد يتطلب التوجيه البعيد AllowTcpForwarding و GatewayPorts على خادم SSH للنهاية. يُغلق المنفذ البعيد عند فصل سطح المكتب هذا.", + "clientTunnelStarted": "تم بدء نفق العميل", "clientTunnelStopped": "تم إيقاف نفق العميل", "tunnelTestSucceeded": "نجح اختبار النفق", "tunnelTestFailed": "فشل اختبار النفق", "localSaved": "تم حفظ أنفاق العميل", - "localSaveError": "فشل حفظ أنفاق العميل المحلي", + "localSaveError": "فشل حفظ أنفاق العميل المحلية", "invalidBindIp": "يجب أن يكون عنوان IP المحلي عنوان IPv4 صالحًا.", - "invalidLocalTargetIp": "يجب أن يكون عنوان IP المستهدف المحلي عنوان IPv4 صالحًا.", - "invalidLocalPort": "يجب أن يكون رقم الميناء المحلي بين 1 و 65535.", - "invalidRemotePort": "يجب أن يكون رقم المنفذ البعيد بين 1 و 65535.", + "invalidLocalTargetIp": "يجب أن يكون عنوان IP الهدف المحلي عنوان IPv4 صالحًا.", + "invalidLocalPort": "يجب أن يكون المنفذ المحلي بين 1 و 65535.", + "invalidRemotePort": "يجب أن يكون المنفذ البعيد بين 1 و 65535.", "invalidLocalTargetPort": "يجب أن يكون منفذ الهدف المحلي بين 1 و 65535.", - "invalidEndpointPort": "يجب أن يكون منفذ نقطة النهاية بين 1 و 65535.", - "duplicateAutoStartBind": "لا يمكن استخدام سوى نفق عميل واحد للتشغيل التلقائي {{bind}}.", + "invalidEndpointPort": "يجب أن يكون منفذ النهاية بين 1 و 65535.", + "duplicateAutoStartBind": "يمكن لنفق عميل تلقائي التشغيل واحد فقط استخدام {{bind}}.", "manualControlError": "فشل تحديث حالة النفق.", - "active": "نشيط", - "start": "يبدأ", - "stop": "قف", - "test": "امتحان", + "active": "نشط", + "start": "تشغيل", + "stop": "إيقاف", + "test": "اختبار", "type": "نوع النفق", "typeLocal": "محلي (-L)", - "typeRemote": "عن بعد (-R)", + "typeRemote": "بعيد (-R)", "typeDynamic": "ديناميكي (-D)", "typeServerLocalDesc": "من المضيف الحالي إلى نقطة النهاية.", - "typeServerRemoteDesc": "نقطة النهاية تعود إلى المضيف الحالي.", - "typeClientLocalDesc": "من الحاسوب المحلي إلى نقطة النهاية.", - "typeClientRemoteDesc": "نقطة النهاية تعود إلى الكمبيوتر المحلي.", - "typeClientDynamicDesc": "برنامج SOCKS على جهاز الكمبيوتر المحلي.", + "typeServerRemoteDesc": "من نقطة النهاية إلى المضيف الحالي.", + "typeClientLocalDesc": "من الكمبيوتر المحلي إلى نقطة النهاية.", + "typeClientRemoteDesc": "من نقطة النهاية إلى الكمبيوتر المحلي.", + "typeClientDynamicDesc": "SOCKS على الكمبيوتر المحلي.", "typeDynamicDesc": "توجيه حركة مرور SOCKS5 CONNECT عبر SSH", - "forwardDescriptionServerLocal": "المضيف الحالي {{sourcePort}} → نقطة النهاية {{endpointPort}}.", - "forwardDescriptionServerRemote": "نقطة النهاية {{endpointPort}} → المضيف الحالي {{sourcePort}}.", + "forwardDescriptionServerLocal": "من المضيف الحالي {{sourcePort}} إلى نقطة النهاية {{endpointPort}}.", + "forwardDescriptionServerRemote": "من نقطة النهاية {{endpointPort}} إلى المضيف الحالي {{sourcePort}}.", "forwardDescriptionServerDynamic": "SOCKS على المضيف الحالي {{sourcePort}}.", - "forwardDescriptionClientLocal": "محلي {{sourcePort}} → بعيد {{endpointPort}}.", - "forwardDescriptionClientRemote": "عن بعد {{sourcePort}} → محلي {{endpointPort}}.", + "forwardDescriptionClientLocal": "من المحلي {{sourcePort}} إلى البعيد {{endpointPort}}.", + "forwardDescriptionClientRemote": "من البعيد {{sourcePort}} إلى المحلي {{endpointPort}}.", "forwardDescriptionClientDynamic": "SOCKS على المنفذ المحلي {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → جوارب عبر {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS عبر {{endpoint}}", "autoNameClientLocal": "محلي {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → محلي {{localPort}}", - "autoNameClientDynamic": "جوارب {{localPort}} عبر {{endpoint}}", - "route": "طريق:", - "lastStarted": "آخر بدء", + "autoNameClientDynamic": "SOCKS {{localPort}} عبر {{endpoint}}", + "route": "المسار:", + "lastStarted": "آخر تشغيل", "lastTested": "آخر اختبار", "lastError": "آخر خطأ", - "maxRetries": "أقصى عدد من المحاولات", - "maxRetriesDescription": "الحد الأقصى لعدد محاولات إعادة المحاولة.", - "retryInterval": "فترة إعادة المحاولة (بالثواني)", - "retryIntervalDescription": "الوقت اللازم للانتظار بين محاولات إعادة المحاولة.", + "maxRetries": "الحد الأقصى لإعادة المحاولات", + "maxRetriesDescription": "الحد الأقصى لعدد مرات إعادة المحاولة", + "retryInterval": "فاصل إعادة المحاولة (ثوانٍ)", + "retryIntervalDescription": "مدة الانتظار بين محاولات إعادة المحاولة", "local": "محلي", - "remote": "بعيد", - "destination": "وجهة", - "host": "يستضيف", - "mode": "وضع", - "noHostSelected": "لم يتم تحديد أي مضيف", - "working": "عمل..." + "remote": "البعيد", + "destination": "الوجهة", + "host": "المضيف", + "mode": "الوضع", + "noHostSelected": "لم يتم تحديد مضيف", + "working": "جارٍ العمل..." }, "cardGrid": { - "dragToMove": "اسحب للتحريك", + "dragToMove": "اسحب للنقل", "dragToResize": "اسحب لتغيير الحجم", "changeWidth": "تغيير العرض", "removeCard": "إزالة {{label}}", - "addCard": "يضيف", + "addCard": "إضافة", "columns": "الأعمدة", - "empty": "لا توجد بطاقات. استخدم زر \"إضافة\" أدناه لوضع البطاقات." + "empty": "لا توجد بطاقات. استخدم زر 'إضافة' أدناه لوضع البطاقات." }, "hostMetrics": { - "cpu": "وحدة المعالجة المركزية", - "memory": "ذاكرة", - "disk": "أدك", - "network": "شبكة", - "uptime": "وقت التشغيل", + "cpu": "المعالج", + "memory": "الذاكرة", + "disk": "القرص", + "network": "الشبكة", + "uptime": "مدة التشغيل", "processes": "العمليات", - "available": "متاح", - "free": "حر", + "available": "المتاح", + "free": "الخالي", "connecting": "جارٍ الاتصال...", "connectionFailed": "فشل الاتصال بالخادم", - "naCpus": "وحدة المعالجة المركزية غير متوفرة", - "cpuCores_one": "{{count}} النواة", - "cpuCores_other": "{{count}} النوى", - "cpuUsage": "استخدام وحدة المعالجة المركزية", + "naCpus": "غير متوفر", + "cpuCores_one": "{{count}} نواة", + "cpuCores_other": "{{count}} نوى", + "cpuUsage": "استخدام المعالج", "memoryUsage": "استخدام الذاكرة", "diskUsage": "استخدام القرص", - "temperature": "درجة حرارة", + "temperature": "درجة الحرارة", "highestTemperature": "أعلى درجة حرارة", - "failedToFetchHostConfig": "فشل في جلب إعدادات المضيف", + "failedToFetchHostConfig": "فشل جلب تكوين المضيف", "serverOffline": "الخادم غير متصل", - "cannotFetchMetrics": "لا يمكن جلب المقاييس من الخادم غير المتصل بالإنترنت", - "totpFailed": "فشل التحقق من رمز التحقق من نوع TOTP", - "noneAuthNotSupported": "لا تدعم مقاييس المضيف نوع المصادقة \"none\".", - "noHostSelected": "لم يتم تحديد أي مضيف", - "load": "حمولة", + "cannotFetchMetrics": "لا يمكن جلب المؤشرات من خادم غير متصل", + "totpFailed": "فشل التحقق من TOTP", + "noneAuthNotSupported": "مقاييس المضيف لا تدعم نوع المصادقة 'none'.", + "noHostSelected": "لم يتم تحديد مضيف", + "load": "الحمل", "systemInfo": "معلومات النظام", "hostname": "اسم المضيف", "operatingSystem": "نظام التشغيل", "kernel": "النواة", "seconds": "ثوانٍ", "networkInterfaces": "واجهات الشبكة", - "noInterfacesFound": "لم يتم العثور على أي واجهات شبكة", - "noProcessesFound": "لم يتم العثور على أي عمليات", - "processesTotal": "المجموع", - "processesRunning": "جري", + "noInterfacesFound": "لم يتم العثور على واجهات شبكة", + "noProcessesFound": "لم يتم العثور على عمليات", + "processesTotal": "الإجمالي", + "processesRunning": "قيد التشغيل", "loginStats": "إحصائيات تسجيل الدخول عبر SSH", "noRecentLoginData": "لا توجد بيانات تسجيل دخول حديثة", - "executingQuickAction": "تنفيذ {{name}}...", - "quickActionSuccess": "{{name}} تم بنجاح", + "executingQuickAction": "جارٍ تنفيذ {{name}}...", + "quickActionSuccess": "تم إكمال {{name}} بنجاح", "quickActionFailed": "فشل {{name}}", "quickActionError": "فشل تنفيذ {{name}}", "ports": { - "title": "منافذ الاستماع", - "protocol": "بروتوكول", - "port": "ميناء", - "address": "عنوان", - "process": "عملية", - "search": "منافذ البحث...", - "allProtocols": "الجميع", - "noData": "لا توجد بيانات منافذ استماع" + "title": "المنافذ المستمعة", + "protocol": "البروتوكول", + "port": "المنفذ", + "address": "العنوان", + "process": "العملية", + "search": "بحث في المنافذ...", + "allProtocols": "الكل", + "noData": "لا توجد بيانات للمنافذ المستمعة" }, "firewall": { "title": "جدار الحماية", "inactive": "غير نشط", - "policy": "سياسة", + "policy": "السياسة", "rules": "قواعد", "noRules": "لا توجد قواعد في هذه السلسلة", - "noData": "لا تتوفر بيانات جدار الحماية", - "action": "فعل", - "protocol": "بروتو", - "port": "ميناء", - "source": "مصدر", - "anywhere": "في أي مكان", + "noData": "لا توجد بيانات لجدار الحماية", + "action": "الإجراء", + "protocol": "البروتوكول", + "port": "المنفذ", + "source": "المصدر", + "anywhere": "أي مكان", "chains": "السلاسل" }, "loadAvg": "متوسط التحميل", - "swap": "تبديل", - "architecture": "بنيان", - "refresh": "ينعش", + "swap": "التبديل", + "architecture": "المعمارية", + "refresh": "تحديث", "retry": "إعادة المحاولة", - "customize": "تخصيص التصميم", - "reset": "إعادة ضبط", - "tabLive": "يعيش", - "editModeInstructions": "اسحب البطاقات لإعادة ترتيبها، واسحب الحافة السفلية لتغيير حجمها، واستخدم زر العرض لتغيير امتداد البطاقة. أضف أو أزل البطاقات من الأسفل.", + "customize": "تخصيص التخطيط", + "reset": "إعادة تعيين", + "tabLive": "مباشر", + "editModeInstructions": "اسحب البطاقات لإعادة ترتيبها، واسحب الحافة السفلية لتغيير حجمها، واستخدم زر العرض لتغيير امتداد البطاقة. أضف البطاقات أو أزلها أدناه.", "managers": { - "services": "خدمات", + "services": "الخدمات", "processInspector": "مفتش العمليات", "logViewer": "عارض السجلات", - "cron": "وظائف Cron", - "packages": "الطرود", + "cron": "مهام كرون", + "packages": "الحزم", "ssl": "شهادات SSL", "firewall": "جدار الحماية", - "users": "المستخدمون والصلاحيات", - "healthCheck": "الفحوصات الصحية", - "diskBreakdown": "خلل القرص", + "users": "المستخدمون والأذونات", + "healthCheck": "فحوصات الصحة", + "diskBreakdown": "تفصيل القرص", "systemdTimers": "المؤقتات", - "topMemory": "أعلى القائمة عن طريق الذاكرة", + "topMemory": "الأعلى استهلاكًا للذاكرة", "noData": "لا توجد بيانات", - "sudoHint": "قم بتعيين كلمة مرور sudo لهذا المضيف في محرر المضيف لتمكين الإجراءات ذات الامتيازات.", - "filter": "فلتر...", - "start": "يبدأ", - "stop": "قف", + "sudoHint": "قم بتعيين كلمة مرور sudo لهذا المضيف في محرر المضيف لتمكين الإجراءات المميزة.", + "filter": "تصفية...", + "start": "بدء", + "stop": "إيقاف", "restart": "إعادة تشغيل", - "actionDone": "تم التحديث {{name}}", - "actionFailed": "فشلت العملية", + "actionDone": "تم تحديث {{name}}", + "actionFailed": "فشل الإجراء", "signalSent": "تم إرسال إشارة إلى PID {{pid}}", - "killHint": "انقر: إنهاء (SIGTERM). انقر بزر الماوس الأيمن: إيقاف إجباري (SIGKILL).", - "working": "عمل...", + "killHint": "نقرة: إنهاء (SIGTERM). نقرة بزر الماوس الأيمن: إنهاء إجباري (SIGKILL).", + "working": "جارٍ العمل...", "update": "تحديث", - "upgradeAll": "قم بترقية الكل", - "allUpToDate": "كل شيء محدّث", - "save": "يحفظ", - "command": "يأمر", - "enabled": "مُفعّل", - "cronSaved": "تم تحديث Crontab", + "upgradeAll": "ترقية الكل", + "allUpToDate": "كل شيء محدث", + "save": "حفظ", + "command": "أمر", + "enabled": "مفعل", + "cronSaved": "تم تحديث crontab", "clients": "العملاء", - "dryRun": "تجربة جافة", + "dryRun": "تشغيل تجريبي", "renew": "تجديد", "noAcmeClient": "لم يتم العثور على عميل ACME (certbot أو acme.sh) على هذا المضيف.", - "addInputRule": "إضافة قاعدة إدخال", - "firewallWarning": "التغييرات مؤقتة فقط حتى يتم حفظها. احرص على عدم فقدان الوصول إلى حسابك.", + "addInputRule": "إضافة قاعدة INPUT", + "firewallWarning": "التغييرات مؤقتة حتى يتم حفظها. احذر من قفل وصولك.", "ruleApplied": "تم تطبيق القاعدة", "invalidPort": "أدخل منفذًا صالحًا (1-65535)", - "newUsername": "اسم المستخدم الجديد", - "addUser": "يضيف", + "newUsername": "اسم مستخدم جديد", + "addUser": "إضافة", "deleteUser": "حذف المستخدم", - "noHealthChecks": "لم يتم إعداد أي فحوصات صحية بعد.", - "follow": "يتبع", - "noLogData": "لا يوجد سجل للمخرجات.", - "tree": "شجرة", - "enableDisable": "تفعيل / تعطيل عند بدء التشغيل", - "grantSudo": "منح صلاحيات sudo", - "revokeSudo": "إلغاء صلاحيات المستخدم الجذر (sudo)", - "sslIssueCert": "إصدار الشهادة", - "sslExpired": "منتهي الصلاحية", - "sslInDays": "في {{days}}د", + "noHealthChecks": "لم يتم تكوين فحوصات الصحة بعد.", + "follow": "متابعة", + "noLogData": "لا توجد مخرجات سجل.", + "tree": "شجري", + "enableDisable": "تفعيل / تعطيل عند الإقلاع", + "grantSudo": "منح sudo", + "revokeSudo": "سحب sudo", + "sslIssueCert": "إصدار شهادة", + "sslExpired": "منتهية الصلاحية", + "sslInDays": "خلال {{days}} يومًا", "sslNeedDomain": "أدخل نطاقًا واحدًا على الأقل", "sslIssued": "تم إصدار الشهادة", - "sslDomainsPlaceholder": "example.com، www.example.com", - "sslHttpStandalone": "بروتوكول نقل النص التشعبي (مستقل)", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (مستقل)", "sslHttpWebroot": "HTTP (webroot)", - "sslDns": "نظام أسماء النطاقات (DNS)", - "sslDnsProvider": "موفر خدمة نظام أسماء النطاقات (مثل كلاود فلير)", - "sslIssueHint": "يجب أن تكون بيانات اعتماد موفر نظام أسماء النطاقات (DNS) مُهيأة مسبقًا على المضيف.", + "sslDns": "DNS", + "sslDnsProvider": "مزود DNS (مثل cloudflare)", + "sslIssueHint": "يجب أن تكون بيانات اعتماد مزود DNS قد تم تكوينها مسبقًا على المضيف.", "sslRevoke": "إلغاء الشهادة", "sslRevoked": "تم إلغاء الشهادة", - "sslRevokeConfirm": "هل تريد إلغاء وإزالة الشهادة \"{{name}}\"؟ لا يمكن التراجع عن هذا الإجراء.", - "healthRun": "يجري", - "healthName": "اسم", + "sslRevokeConfirm": "هل تريد إلغاء الشهادة \"{{name}}\" وإزالتها؟ لا يمكن التراجع عن هذا الإجراء.", + "healthRun": "تشغيل", + "healthName": "الاسم", "healthTarget": "المضيف / العنوان", - "healthAddCheck": "أضف علامة صح", - "healthSaved": "تم حفظ الفحوصات الصحية", - "healthMissingFields": "يحتاج كل فحص إلى اسم وهدف", + "healthAddCheck": "إضافة فحص", + "healthSaved": "تم حفظ فحوصات الصحة", + "healthMissingFields": "يتطلب كل فحص اسمًا وهدفًا", "logFile": "ملف", "logUnit": "وحدة", - "logCustomPath": "مسار مخصص ضمن /var/log (اختياري)", - "logGrep": "خطوط التصفية...", - "firewallPersist": "الالتزام بالقواعد", - "firewallPersisted": "تم الاحتفاظ بقواعد جدار الحماية", - "wireguard": "شبكة سلكية", - "tailscale": "مقياس الذيل", - "wgNotInstalled": "لم يتم تثبيت برنامج WireGuard على هذا المضيف", - "wgNoInterfaces": "لم يتم تكوين أي واجهات WireGuard", - "wgInterfaceUp": "ارفع", - "wgInterfaceDown": "إسقاط", - "wgBringingUp": "جارٍ تشغيل {{name}}...", - "wgBringingDown": "إسقاط {{name}}...", - "wgInterfaceUpDone": "{{name}} مُفعّل", - "wgInterfaceDownDone": "{{name}} معطل", - "wgListenPort": "ميناء", + "logCustomPath": "مسار مخصص تحت /var/log (اختياري)", + "logGrep": "تصفية السطور...", + "firewallPersist": "حفظ القواعد", + "firewallPersisted": "تم حفظ قواعد جدار الحماية", + "wireguard": "WireGuard", + "tailscale": "Tailscale", + "wgNotInstalled": "WireGuard غير مثبت على هذا المضيف", + "wgNoInterfaces": "لا توجد واجهات WireGuard مُعدّة", + "wgInterfaceUp": "رفع", + "wgInterfaceDown": "إنزال", + "wgBringingUp": "جارٍ رفع {{name}}...", + "wgBringingDown": "جارٍ إنزال {{name}}...", + "wgInterfaceUpDone": "{{name}} مرفوع", + "wgInterfaceDownDone": "{{name}} منزّل", + "wgListenPort": "المنفذ", "wgPublicKey": "المفتاح العام", "wgEndpoint": "نقطة النهاية", "wgAllowedIPs": "عناوين IP المسموح بها", - "wgLastHandshake": "المصافحة الأخيرة", - "wgHandshakeNever": "أبداً", - "wgTransfer": "تحويل", - "tsNotInstalled": "لم يتم تثبيت Tailscale على هذا المضيف", - "tsRunning": "جري", - "tsStopped": "توقف", - "tsEnable": "يتصل", - "tsDisable": "قطع الاتصال", + "wgLastHandshake": "آخر مصافحة", + "wgHandshakeNever": "أبدًا", + "wgTransfer": "نقل", + "tsNotInstalled": "Tailscale غير مثبت على هذا المضيف", + "tsRunning": "يعمل", + "tsStopped": "متوقف", + "tsEnable": "اتصل", + "tsDisable": "افصل", "tsEnabling": "جارٍ الاتصال بـ Tailscale...", "tsDisabling": "جارٍ قطع الاتصال بـ Tailscale...", - "tsEnabled": "مقياس الذيل المتصل", - "tsDisabled": "تم فصل مقياس الذيل", - "tsIPs": "عناوين IP ذات نطاق تنازلي", + "tsEnabled": "Tailscale متصل", + "tsDisabled": "Tailscale غير متصل", + "tsIPs": "عناوين IP لـ Tailscale", "tsPeers": "الأقران", "tsOnline": "متصل", - "tsOffline": "غير متصل بالإنترنت", - "tsExitNode": "عقدة الخروج", - "tsExitNodeActive": "عقدة الخروج نشطة", + "tsOffline": "غير متصل", + "tsExitNode": "عقدة خروج", + "tsExitNodeActive": "عقدة خروج نشطة", "tsHostname": "اسم المضيف" } }, "auth": { - "tagline": "إدارة SSH وسطح المكتب عن بُعد ذاتية الاستضافة", + "tagline": "إدارة SSH وسطح المكتب البعيد ذاتية الاستضافة", "loginTitle": "مرحبًا بعودتك", "registerTitle": "إنشاء حساب", - "forgotPassword": "هل نسيت كلمة السر؟", - "rememberMe": "تذكر الجهاز لمدة 30 يومًا (يشمل رمز التحقق من الجهاز)", - "noAccount": "ليس لديك حساب؟", + "forgotPassword": "نسيت كلمة المرور؟", + "rememberMe": "تذكر الجهاز لمدة 30 يومًا (يتضمن TOTP)", + "noAccount": "لا تملك حسابًا؟", "hasAccount": "هل لديك حساب بالفعل؟", "twoFactorAuth": "المصادقة الثنائية", "enterCode": "أدخل رمز التحقق", "backupCode": "أو استخدم رمز النسخ الاحتياطي", - "verifyCode": "رمز التحقق", - "redirectingToApp": "إعادة التوجيه إلى التطبيق...", - "sshAuthenticationRequired": "يلزم المصادقة عبر SSH", + "verifyCode": "تحقق من الرمز", + "redirectingToApp": "جارٍ إعادة التوجيه إلى التطبيق...", + "sshAuthenticationRequired": "مصادقة SSH مطلوبة", "sshNoKeyboardInteractive": "المصادقة التفاعلية عبر لوحة المفاتيح غير متاحة", - "sshAuthenticationFailed": "فشلت عملية المصادقة", - "sshAuthenticationTimeout": "مهلة المصادقة", - "sshNoKeyboardInteractiveDescription": "لا يدعم الخادم المصادقة التفاعلية عبر لوحة المفاتيح. يرجى تقديم كلمة المرور أو مفتاح SSH الخاص بك.", - "sshAuthFailedDescription": "البيانات المُدخلة غير صحيحة. يُرجى المحاولة مرة أخرى باستخدام بيانات صحيحة.", + "sshAuthenticationFailed": "فشلت المصادقة", + "sshAuthenticationTimeout": "انتهت مهلة المصادقة", + "sshNoKeyboardInteractiveDescription": "الخادم لا يدعم المصادقة التفاعلية عبر لوحة المفاتيح. يرجى تقديم كلمة المرور أو مفتاح SSH.", + "sshAuthFailedDescription": "بيانات الاعتماد المقدمة غير صحيحة. يرجى المحاولة مرة أخرى باستخدام بيانات اعتماد صالحة.", "sshTimeoutDescription": "انتهت مهلة محاولة المصادقة. يرجى المحاولة مرة أخرى.", "sshProvideCredentialsDescription": "يرجى تقديم بيانات اعتماد SSH الخاصة بك للاتصال بهذا الخادم.", - "sshPasswordDescription": "أدخل كلمة المرور الخاصة باتصال SSH هذا.", - "sshKeyPasswordDescription": "إذا كان مفتاح SSH الخاص بك مشفرًا، فأدخل عبارة المرور هنا.", - "passphraseRequired": "عبارة مرور مطلوبة", - "passphraseRequiredDescription": "مفتاح SSH مشفر. يرجى إدخال عبارة المرور لفك تشفيره.", - "back": "خلف", + "sshPasswordDescription": "أدخل كلمة المرور لاتصال SSH هذا.", + "sshKeyPasswordDescription": "إذا كان مفتاح SSH مشفرًا، أدخل عبارة المرور هنا.", + "passphraseRequired": "عبارة المرور مطلوبة", + "passphraseRequiredDescription": "مفتاح SSH مشفر. الرجاء إدخال عبارة المرور لفك تشفيره.", + "back": "رجوع", "firstUser": "المستخدم الأول", - "firstUserMessage": "أنت المستخدم الأول وسيتم تعيينك كمسؤول. يمكنك الاطلاع على إعدادات المسؤول من القائمة المنسدلة للمستخدم في الشريط الجانبي. إذا كنت تعتقد أن هذا خطأ، فراجع سجلات Docker أو أنشئ مشكلة على GitHub.", + "firstUserMessage": "أنت المستخدم الأول وستصبح مسؤولاً. يمكنك عرض إعدادات المسؤول في القائمة المنسدلة للمستخدم في الشريط الجانبي. إذا كنت تعتقد أن هذا خطأ، تحقق من سجلات docker، أو أنشئ مشكلة على GitHub.", "external": "خارجي", "loginWithExternal": "تسجيل الدخول باستخدام مزود خارجي", - "loginWithExternalDesc": "قم بتسجيل الدخول باستخدام موفر الهوية الخارجي الذي قمت بتكوينه", - "externalNotSupportedInElectron": "لا يدعم تطبيق Electron المصادقة الخارجية حاليًا. يُرجى استخدام نسخة الويب لتسجيل الدخول عبر OIDC.", - "loginWithProvider": "سجّل الدخول باستخدام {{name}}", - "orContinueWith": "أو تابع مع", + "loginWithExternalDesc": "تسجيل الدخول باستخدام مزود الهوية الخارجي المكون", + "externalNotSupportedInElectron": "المصادقة الخارجية غير مدعومة في تطبيق Electron بعد. الرجاء استخدام إصدار الويب لتسجيل الدخول عبر OIDC.", + "loginWithProvider": "تسجيل الدخول باستخدام {{name}}", + "orContinueWith": "أو المتابعة باستخدام", "ldapUsername": "اسم مستخدم LDAP", "ldapPassword": "كلمة مرور LDAP", "ldapSignIn": "تسجيل الدخول", - "ldapLoginFailed": "فشل تسجيل الدخول إلى LDAP", + "ldapLoginFailed": "فشل تسجيل الدخول عبر LDAP", "resetPasswordButton": "إعادة تعيين كلمة المرور", - "sendResetCode": "إرسال رمز إعادة الضبط", - "resetCodeDesc": "أدخل اسم المستخدم الخاص بك لتلقي رمز إعادة تعيين كلمة المرور. سيتم تسجيل الرمز في سجلات حاوية Docker.", - "resetCode": "رمز إعادة الضبط", - "verifyCodeButton": "رمز التحقق", - "enterResetCode": "أدخل الرمز المكون من 6 أرقام من سجلات حاوية Docker للمستخدم:", + "sendResetCode": "إرسال رمز إعادة التعيين", + "resetCodeDesc": "أدخل اسم المستخدم لتلقي رمز إعادة تعيين كلمة المرور. سيتم تسجيل الرمز في سجلات حاوية docker.", + "resetCode": "رمز إعادة التعيين", + "verifyCodeButton": "التحقق من الرمز", + "enterResetCode": "أدخل الرمز المكون من 6 أرقام من سجلات حاوية docker للمستخدم:", "newPassword": "كلمة المرور الجديدة", "confirmNewPassword": "تأكيد كلمة المرور", "enterNewPassword": "أدخل كلمة المرور الجديدة للمستخدم:", - "signUp": "اشتراك", + "signUp": "إنشاء حساب", "desktopApp": "تطبيق سطح المكتب", "loggingInToDesktopApp": "تسجيل الدخول إلى تطبيق سطح المكتب", "loadingServer": "جارٍ تحميل الخادم...", - "dataLossWarning": "سيؤدي إعادة تعيين كلمة مرورك بهذه الطريقة إلى حذف جميع مضيفي SSH المحفوظين، وبيانات الاعتماد، والبيانات المشفرة الأخرى. لا يمكن التراجع عن هذا الإجراء. استخدم هذه الطريقة فقط إذا نسيت كلمة مرورك ولم تكن مسجلاً الدخول.", - "authenticationDisabled": "تم تعطيل المصادقة", - "authenticationDisabledDesc": "جميع طرق المصادقة معطلة حاليًا. يرجى الاتصال بمسؤول النظام.", - "passwordLoginDisabledDesc": "تم تعطيل تسجيل الدخول باستخدام كلمة المرور. استخدم رمز مرور أو موفر مصادقة خارجي.", - "signInWithPasskey": "سجّل الدخول باستخدام رمز المرور", - "passkeyLoginFailed": "فشل تسجيل الدخول باستخدام مفتاح المرور", - "attemptsRemaining": "عدد المحاولات المتبقية: {{count}}", - "confirmResetDataWipe": "لم يتم تسجيل الدخول إلى هذا الحساب منذ تحديث التشفير، لذا لا يمكن استعادة بياناته المخزنة بدون كلمة المرور القديمة. ستؤدي إعادة التعيين إلى حذف مضيفيه وبيانات اعتماده ومقتطفاته نهائيًا. هل تريد المتابعة؟" + "dataLossWarning": "ستؤدي إعادة تعيين كلمة المرور بهذه الطريقة إلى حذف جميع مضيفي SSH المحفوظين وبيانات الاعتماد والبيانات المشفرة الأخرى. لا يمكن التراجع عن هذا الإجراء. استخدم هذا فقط إذا نسيت كلمة المرور ولم تقم بتسجيل الدخول.", + "authenticationDisabled": "المصادقة معطلة", + "authenticationDisabledDesc": "جميع طرق المصادقة معطلة حاليًا. الرجاء الاتصال بالمسؤول.", + "passwordLoginDisabledDesc": "تسجيل الدخول بكلمة المرور معطل. استخدم مفتاح مرور أو مزود مصادقة خارجي.", + "signInWithPasskey": "تسجيل الدخول باستخدام مفتاح المرور", + "passkeyLoginFailed": "فشل تسجيل الدخول بمفتاح المرور", + "attemptsRemaining": "{{count}} محاولات متبقية", + "confirmResetDataWipe": "لم يتم تسجيل الدخول إلى هذا الحساب منذ ترقية التشفير، لذا لا يمكن استرداد البيانات المخزنة بدون كلمة المرور القديمة. ستؤدي إعادة التعيين إلى حذف المضيفات وبيانات الاعتماد والمقتطفات بشكل دائم. هل تريد المتابعة؟" }, "hostKey": { "verifyNewHost": "تحقق من مفتاح مضيف SSH", "keyChangedWarning": "تم تغيير مفتاح مضيف SSH", - "firstConnectionTitle": "هذه أول مرة أتصل فيها بهذا المضيف", - "firstConnectionDescription": "لا يمكن التحقق من صحة هذا المضيف. تأكد من تطابق بصمة الإصبع مع ما تتوقعه.", - "keyChangedDescription": "لقد تغير مفتاح المضيف لهذا الخادم منذ آخر اتصال لك. قد يشير هذا إلى وجود مشكلة أمنية.", + "firstConnectionTitle": "أول اتصال بهذا المضيف", + "firstConnectionDescription": "لا يمكن التحقق من صحة هذا المضيف. تحقق من تطابق البصمة مع ما تتوقعه.", + "keyChangedDescription": "لقد تغير مفتاح المضيف لهذا الخادم منذ آخر اتصال. قد يشير ذلك إلى مشكلة أمنية.", "previousKey": "المفتاح السابق", - "newFingerprint": "بصمة إصبع جديدة", - "fingerprint": "بصمة", - "verifyInstructions": "إذا كنت تثق بهذا المضيف، فانقر فوق \"موافق\" للمتابعة وحفظ هذه البصمة للاتصالات المستقبلية.", + "newFingerprint": "البصمة الجديدة", + "fingerprint": "البصمة", + "verifyInstructions": "إذا كنت تثق بهذا المضيف، فانقر على قبول للمتابعة وحفظ هذه البصمة للاتصالات المستقبلية.", "securityWarning": "تحذير أمني", - "acceptAndContinue": "موافق ومتابعة", - "acceptNewKey": "اقبل المفتاح الجديد وتابع." + "acceptAndContinue": "قبول ومتابعة", + "acceptNewKey": "قبول المفتاح الجديد والمتابعة" }, "errors": { "databaseConnection": "تعذر الاتصال بقاعدة البيانات", "unknownError": "خطأ غير معروف", "loginFailed": "فشل تسجيل الدخول", - "failedPasswordReset": "فشل بدء عملية إعادة تعيين كلمة المرور", + "failedPasswordReset": "فشل بدء إعادة تعيين كلمة المرور", "failedVerifyCode": "فشل التحقق من رمز إعادة التعيين", - "failedCompleteReset": "فشل في إكمال عملية إعادة تعيين كلمة المرور", + "failedCompleteReset": "فشل إكمال إعادة تعيين كلمة المرور", "invalidTotpCode": "رمز TOTP غير صالح", - "failedOidcLogin": "فشل بدء تسجيل الدخول إلى OIDC", - "silentSigninOidcUnavailable": "تم طلب تسجيل الدخول الصامت، ولكن تسجيل الدخول إلى OIDC غير متاح.", + "failedOidcLogin": "فشل بدء تسجيل الدخول عبر OIDC", + "silentSigninOidcUnavailable": "تم طلب تسجيل الدخول الصامت، لكن تسجيل الدخول عبر OIDC غير متاح.", "failedUserInfo": "فشل الحصول على معلومات المستخدم بعد تسجيل الدخول", - "oidcAuthFailed": "فشلت عملية مصادقة OIDC", - "invalidAuthUrl": "تم استلام عنوان URL غير صالح للتفويض من الواجهة الخلفية", - "requiredField": "هذه الخانة مطلوبه", + "oidcAuthFailed": "فشلت مصادقة OIDC", + "invalidAuthUrl": "تم استلام عنوان URL تفويض غير صالح من الخادم الخلفي", + "requiredField": "هذا الحقل مطلوب", "minLength": "الحد الأدنى للطول هو {{min}}", "passwordMismatch": "كلمات المرور غير متطابقة", - "passwordLoginDisabled": "تم تعطيل تسجيل الدخول باستخدام اسم المستخدم/كلمة المرور حاليًا", - "sessionExpired": "انتهت صلاحية الجلسة - يرجى تسجيل الدخول مرة أخرى", - "totpRateLimited": "تم تجاوز الحد المسموح به: عدد محاولات التحقق من رمز TOTP كبير جدًا. يرجى المحاولة مرة أخرى لاحقًا.", - "totpRateLimitedWithTime": "تم تجاوز الحد المسموح به: عدد محاولات التحقق من رمز TOTP كبير جدًا. يُرجى الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", - "resetCodeRateLimited": "تم تجاوز الحد المسموح به: عدد محاولات التحقق كبير جدًا. يرجى المحاولة مرة أخرى لاحقًا.", - "resetCodeRateLimitedWithTime": "تم تجاوز الحد المسموح به: عدد محاولات التحقق كبير جدًا. يُرجى الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", - "authTokenSaveFailed": "فشل حفظ رمز المصادقة", - "failedToLoadServer": "فشل تحميل الخادم" + "passwordLoginDisabled": "تسجيل الدخول باسم المستخدم وكلمة المرور معطل حالياً", + "sessionExpired": "انتهت الجلسة - يرجى تسجيل الدخول مرة أخرى", + "totpRateLimited": "تم تقييد المعدل: عدد كبير جدًا من محاولات التحقق من TOTP. يرجى المحاولة مرة أخرى لاحقًا.", + "totpRateLimitedWithTime": "تم تقييد المعدل: عدد كبير جدًا من محاولات التحقق من TOTP. يرجى الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", + "resetCodeRateLimited": "تم تقييد المعدل: عدد كبير جدًا من محاولات التحقق. يرجى المحاولة مرة أخرى لاحقًا.", + "resetCodeRateLimitedWithTime": "محدودية المعدل: عدد كبير جداً من محاولات التحقق. يرجى الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", + "authTokenSaveFailed": "فشل في حفظ رمز المصادقة", + "failedToLoadServer": "فشل في تحميل الخادم", + "remoteServerRequired": "يلزم وجود خادم بعيد. قم بتوصيل خادم بعيد في الإعدادات لاستخدام نوع الاتصال هذا." }, "messages": { - "registrationDisabled": "تم تعطيل تسجيل الحسابات الجديدة حاليًا من قِبل أحد المسؤولين. يُرجى تسجيل الدخول أو الاتصال بأحد المسؤولين.", - "userNotAllowed": "حسابك غير مصرح له بالتسجيل. يرجى الاتصال بأحد المسؤولين.", + "registrationDisabled": "تسجيل الحسابات الجديدة معطل حالياً من قبل المسؤول. يرجى تسجيل الدخول أو الاتصال بالمسؤول.", + "userNotAllowed": "حسابك غير مخول بالتسجيل. يرجى الاتصال بالمسؤول.", "databaseConnectionFailed": "فشل الاتصال بخادم قاعدة البيانات", - "resetCodeSent": "تم إرسال رمز إعادة الضبط إلى سجلات Docker", + "resetCodeSent": "تم إرسال رمز إعادة التعيين إلى سجلات Docker", "codeVerified": "تم التحقق من الرمز بنجاح", "passwordResetSuccess": "تمت إعادة تعيين كلمة المرور بنجاح", "loginSuccess": "تم تسجيل الدخول بنجاح", "registrationSuccess": "تم التسجيل بنجاح" }, "profile": { - "c2sTunnelConfigDesc": "أنفاق سطح المكتب المحلية التي تستهدف مضيفي SSH المُهيئين.", - "c2sTunnelPresets": "إعدادات مسبقة لأنفاق العميل", - "c2sTunnelPresetsDesc": "احفظ قائمة الأنفاق المحلية لعميل سطح المكتب هذا كإعداد مسبق للخادم المسمى، أو قم بتحميل إعداد مسبق مرة أخرى إلى هذا العميل.", - "c2sTunnelPresetsUnavailable": "تتوفر إعدادات نفق العميل المسبقة فقط في برنامج سطح المكتب.", - "c2sPresetName": "اسم الإعداد المسبق", - "c2sPresetNamePlaceholder": "اسم العميل المُسبق", - "c2sPresetToLoad": "تم ضبطه مسبقًا للتحميل", - "c2sNoPresetSelected": "لم يتم تحديد أي إعداد مسبق", - "c2sNoPresets": "لم يتم حفظ أي إعدادات مسبقة", - "c2sLoadPreset": "حمولة", - "c2sCurrentLocalConfig": "{{count}} تم تكوين نفق (أنفاق) العميل المحلي على سطح المكتب هذا.", - "c2sPresetSyncNote": "الإعدادات المسبقة عبارة عن لقطات صريحة؛ تحميل إحداها يحل محل قائمة أنفاق العميل المحلي لعميل سطح المكتب هذا.", - "c2sPresetSaved": "تم حفظ الإعدادات المسبقة لنفق العميل", - "c2sPresetLoaded": "تم تحميل إعدادات نفق العميل محليًا", - "c2sPresetRenamed": "تمت إعادة تسمية الإعداد المسبق لنفق العميل", - "c2sPresetDeleted": "تم حذف الإعداد المسبق لنفق العميل", - "c2sPresetLoadError": "فشل تحميل إعدادات نفق العميل" + "c2sTunnelConfigDesc": "أنفاق سطح المكتب المحلية التي تستهدف مضيفي SSH المكونين.", + "c2sTunnelPresets": "إعدادات النفق المُسبَقة للعميل", + "c2sTunnelPresetsDesc": "احفظ قائمة الأنفاق المحلية لعميل سطح المكتب هذا كإعداد مُسبَق مسمى على الخادم، أو حمّل إعداداً مُسبَقاً إلى هذا العميل.", + "c2sTunnelPresetsUnavailable": "إعدادات النفق المُسبَقة للعميل متاحة فقط في تطبيق سطح المكتب.", + "c2sPresetName": "اسم الإعداد المُسبَق", + "c2sPresetNamePlaceholder": "اسم الإعداد المُسبَق للعميل", + "c2sPresetToLoad": "الإعداد المُسبَق للتحميل", + "c2sNoPresetSelected": "لم يتم تحديد إعداد مُسبَق", + "c2sNoPresets": "لا توجد إعدادات مُسبَقة محفوظة", + "c2sLoadPreset": "تحميل", + "c2sCurrentLocalConfig": "تم تكوين {{count}} نفق محلي للعميل على سطح المكتب هذا.", + "c2sPresetSyncNote": "الإعدادات المُسبَقة هي لقطات واضحة؛ تحميل أحدها يستبدل قائمة الأنفاق المحلية لعميل سطح المكتب هذا.", + "c2sPresetSaved": "تم حفظ الإعداد المُسبَق لنفق العميل", + "c2sPresetLoaded": "تم تحميل الإعداد المُسبَق لنفق العميل محلياً", + "c2sPresetRenamed": "تمت إعادة تسمية الإعداد المُسبَق لنفق العميل", + "c2sPresetDeleted": "تم حذف الإعداد المُسبَق لنفق العميل", + "c2sPresetLoadError": "فشل في تحميل إعدادات نفق العميل المُسبَقة" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "لغة", - "keyPassword": "كلمة المرور الرئيسية", - "pastePrivateKey": "ألصق مفتاحك الخاص هنا...", - "localListenerHost": "127.0.0.1 (الاستماع محليًا)", + "language": "اللغة", + "keyPassword": "كلمة مرور المفتاح", + "pastePrivateKey": "الصق مفتاحك الخاص هنا...", + "localListenerHost": "127.0.0.1 (استماع محلي)", "localTargetHost": "127.0.0.1 (الهدف على هذا الكمبيوتر)", "socksListenerHost": "127.0.0.1 (مستمع SOCKS)", - "enterPassword": "أدخل كلمة المرور الخاصة بك", + "enterPassword": "أدخل كلمة المرور", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "لوحة التحكم", "loading": "جارٍ تحميل لوحة التحكم...", - "github": "جيت هاب", - "support": "يدعم", - "discord": "ديسكورد", + "github": "GitHub", + "support": "الدعم", + "discord": "Discord", "docs": "المستندات", - "donate": "يتبرع", + "donate": "تبرع", "serverOverview": "نظرة عامة على الخادم", - "version": "إصدار", - "upToDate": "محدّث", - "updateAvailable": "يتوفر تحديث", + "version": "الإصدار", + "upToDate": "مُحدَّث", + "updateAvailable": "تحديث متاح", "beta": "بيتا", - "uptime": "وقت التشغيل", + "uptime": "مدة التشغيل", "database": "قاعدة البيانات", - "healthy": "صحيح", + "healthy": "سليم", "error": "خطأ", "totalHosts": "إجمالي المضيفين", - "totalTunnels": "توتال تونلز", - "totalCredentials": "المؤهلات الكاملة", + "totalTunnels": "إجمالي الأنفاق", + "totalCredentials": "إجمالي بيانات الاعتماد", "recentActivity": "النشاط الأخير", - "reset": "إعادة ضبط", + "reset": "إعادة تعيين", "loadingRecentActivity": "جارٍ تحميل النشاط الأخير...", - "noRecentActivity": "لا يوجد نشاط حديث", + "noRecentActivity": "لا يوجد نشاط أخير", "quickActions": "إجراءات سريعة", "addHost": "إضافة مضيف", "addCredential": "إضافة بيانات اعتماد", "adminSettings": "إعدادات المسؤول", - "userProfile": "ملف تعريف المستخدم", + "userProfile": "ملف المستخدم", "serverStats": "إحصائيات الخادم", "loadingServerStats": "جارٍ تحميل إحصائيات الخادم...", "noServerData": "لا تتوفر بيانات الخادم", - "cpu": "وحدة المعالجة المركزية", - "ram": "كبش", + "cpu": "المعالج", + "ram": "الذاكرة", "customizeLayout": "تخصيص لوحة التحكم", "dashboardSettings": "إعدادات لوحة التحكم", - "enableDisableCards": "تفعيل/تعطيل البطاقات", - "resetLayout": "إعادة الضبط إلى الوضع الافتراضي", + "enableDisableCards": "تمكين/تعطيل البطاقات", + "resetLayout": "استعادة التخطيط الافتراضي", "serverOverviewCard": "نظرة عامة على الخادم", "recentActivityCard": "النشاط الأخير", - "networkGraphCard": "رسم بياني للشبكة", - "networkGraph": "رسم بياني للشبكة", + "networkGraphCard": "الرسم البياني للشبكة", + "networkGraph": "الرسم البياني للشبكة", "quickActionsCard": "إجراءات سريعة", "serverStatsCard": "إحصائيات الخادم", - "panelMain": "رئيسي", - "panelSide": "جانب", - "justNow": "الآن", - "serviceLinks": "روابط الخدمة", + "panelMain": "الرئيسية", + "panelSide": "الجانبية", + "justNow": "للتو", + "serviceLinks": "روابط الخدمات", "homepagePreview": "معاينة الصفحة الرئيسية" }, "donation": { - "title": "هل تستمتع باستخدام Termix؟", - "body": "Termix برنامج مجاني ومفتوح المصدر، تم تطويره وصيانته بواسطة فريق مكون من شخصين في أوقات فراغهم. إذا حلّ Termix محل أداة تجارية كنت ستدفع ثمنها، فإن تبرعك يساعد في تغطية تكاليف الاستضافة واستمرار التطوير. حاليًا، التبرعات متاحة بالعملات الرقمية فقط.", - "milestones": "تساهم التبرعات أيضاً في تمويل الوقت اللازم للبحث والتعلم اللازمين لتطوير ميزات مثل دعم SAML وKubernetes. تابعوا التقدم المحرز على صفحة التبرعات.", - "cta": "يتبرع", - "dismiss": "ربما لاحقاً" + "title": "هل تستمتع بـ Termix؟", + "body": "Termix هو برنامج مجاني ومفتوح المصدر، تم بناؤه وصيانته بواسطة فريق مكون من شخصين في وقت فراغنا. إذا كان قد استبدل أداة تجارية كنت ستضطر لدفع ثمنها، فالتبرع يساعد في تغطية تكاليف الاستضافة ويحافظ على استمرار التطوير. حتى الآن، التبرعات مقبولة بالعملات المشفرة فقط.", + "milestones": "تساعد التبرعات أيضًا في تمويل الوقت اللازم للبحث والتعلم لبناء ميزات مثل دعم SAML و Kubernetes. يمكنك الاطلاع على التقدم في صفحة التبرع.", + "cta": "تبرع", + "dismiss": "ربما لاحقًا" }, "dashboardTab": { "stable": "مستقر", - "hostsOnline": "المضيفون عبر الإنترنت", + "hostsOnline": "المضيفون المتصلون", "activeTunnels": "الأنفاق النشطة", - "registerNewServer": "قم بتسجيل خادم جديد", - "storeSshKeysOrPasswords": "قم بتخزين مفاتيح أو كلمات مرور SSH", + "registerNewServer": "تسجيل خادم جديد", + "storeSshKeysOrPasswords": "تخزين مفاتيح SSH أو كلمات المرور", "manageUsersAndRoles": "إدارة المستخدمين والأدوار", "manageYourAccount": "إدارة حسابك", "hostStatus": "حالة المضيف", "noHostsConfigured": "لم يتم تكوين أي مضيفين", "online": "متصل", - "offline": "عبر الإنترنت", - "checking": "التحقق", + "offline": "غير متصل", + "checking": "جارٍ التحقق", "onlineLower": "متصل", - "nodes": "{{count}} عقد", - "add": "يضيف:", + "nodes": "{{count}} عقدة", + "add": "إضافة:", "commandPalette": "لوحة الأوامر", - "done": "منتهي", - "editModeInstructions": "اسحب البطاقات لإعادة ترتيبها · اسحب فاصل الأعمدة لتغيير حجم الأعمدة · اسحب الحافة السفلية للبطاقة لتغيير ارتفاعها · اسحب علامة الحذف لإزالتها", + "done": "تم", + "editModeInstructions": "اسحب البطاقات لإعادة الترتيب · اسحب مقسم العمود لتغيير حجم الأعمدة · اسحب الحافة السفلية للبطاقة لتغيير ارتفاعها · اسحب إلى سلة المهملات للإزالة", "empty": "فارغ", - "clear": "واضح", - "serviceLinksTitle": "روابط الخدمة", - "serviceLinksEmpty": "لا توجد روابط خدمة حتى الآن", - "serviceLinksAddLabel": "ملصق", - "serviceLinksAddUrl": "عنوان URL", - "serviceLinksAdd": "يضيف", + "clear": "مسح", + "serviceLinksTitle": "روابط الخدمات", + "serviceLinksEmpty": "لا توجد روابط خدمات بعد", + "serviceLinksAddLabel": "التسمية", + "serviceLinksAddUrl": "URL", + "serviceLinksAdd": "إضافة", "serviceLinksLabelPlaceholder": "خدمتي", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "أدخل عنوان ويب صالحًا", - "serviceLinksAddFailed": "فشل إضافة رابط الخدمة", - "disk": "أدك", + "serviceLinksInvalidUrl": "أدخل عنوان ويب صالح", + "serviceLinksAddFailed": "فشل في إضافة رابط الخدمة", + "disk": "القرص", "viewServerDetails": "عرض تفاصيل الخادم" }, "sessionLogs": { "title": "سجلات الجلسات", - "noLogs": "لا توجد سجلات جلسات حتى الآن", - "noLogsDesc": "قم بتمكين تسجيل الجلسات على المضيف لبدء التسجيل", - "duration": "مدة", + "noLogs": "لا توجد سجلات جلسات بعد", + "noLogsDesc": "فعّل تسجيل الجلسات على مضيف لبدء التسجيل", + "duration": "المدة", "viewLog": "عرض السجل", - "downloadLog": "تحميل", - "deleteLog": "يمسح", - "confirmDelete": "هل تريد حذف سجل هذه الجلسة؟", + "downloadLog": "تنزيل", + "deleteLog": "حذف", + "confirmDelete": "حذف سجل الجلسة هذا؟", "confirmDeleteDesc": "لا يمكن التراجع عن هذا الإجراء.", - "copyContent": "ينسخ", + "copyContent": "نسخ", "copied": "تم النسخ!", - "loadError": "فشل تحميل سجلات الجلسة", - "deleteError": "فشل حذف سجل الجلسة", + "loadError": "فشل في تحميل سجلات الجلسات", + "deleteError": "فشل في حذف سجل الجلسة", "filterByHost": "تصفية حسب المضيف..." }, "networkGraph": { "addHost": "إضافة مضيف", "addGroup": "إضافة مجموعة", "addLink": "إضافة رابط", - "zoomIn": "تكبير الصورة", - "zoomOut": "تصغير العرض", + "zoomIn": "تكبير", + "zoomOut": "تصغير", "resetView": "إعادة ضبط العرض", - "selectHost": "حدد المضيف", + "selectHost": "تحديد مضيف", "chooseHost": "اختر مضيفًا...", - "parentGroup": "مجموعة أولياء الأمور", - "noGroup": "لا توجد مجموعة", + "parentGroup": "المجموعة الأم", + "noGroup": "بدون مجموعة", "groupName": "اسم المجموعة", - "color": "لون", - "source": "مصدر", - "target": "هدف", - "moveToGroup": "الانتقال إلى المجموعة", - "selectGroup": "حدد المجموعة...", + "color": "اللون", + "source": "المصدر", + "target": "الهدف", + "moveToGroup": "نقل إلى مجموعة", + "selectGroup": "تحديد مجموعة...", "addConnection": "إضافة اتصال", "hostDetails": "تفاصيل المضيف", "removeFromGroup": "إزالة من المجموعة", - "addHostHere": "أضف المضيف هنا", - "editGroup": "مجموعة التحرير", - "delete": "يمسح", - "add": "يضيف", - "create": "يخلق", - "move": "يتحرك", - "connect": "يتصل", + "addHostHere": "إضافة مضيف هنا", + "editGroup": "تعديل المجموعة", + "delete": "حذف", + "add": "إضافة", + "create": "إنشاء", + "move": "نقل", + "connect": "اتصل", "createGroup": "إنشاء مجموعة", - "selectSourcePlaceholder": "حدد المصدر...", - "selectTargetPlaceholder": "حدد الهدف...", + "selectSourcePlaceholder": "تحديد المصدر...", + "selectTargetPlaceholder": "تحديد الهدف...", "invalidFile": "ملف غير صالح", - "hostAlreadyExists": "المضيف موجود بالفعل في بنية الشبكة", + "hostAlreadyExists": "المضيف موجود بالفعل في الطوبولوجيا", "connectionExists": "الاتصال موجود بالفعل", - "unknown": "مجهول", - "name": "اسم", - "ip": "الملكية الفكرية", - "status": "حالة", - "failedToAddNode": "فشل إضافة العقدة", + "unknown": "غير معروف", + "name": "الاسم", + "ip": "IP", + "status": "الحالة", + "failedToAddNode": "فشل في إضافة العقدة", "sourceDifferentFromTarget": "يجب أن يكون المصدر والهدف مختلفين", "exportJSON": "تصدير JSON", - "importJSON": "استيراد بيانات JSON", - "terminal": "صالة", + "importJSON": "استيراد JSON", + "terminal": "الطرفية", "fileManager": "مدير الملفات", "tunnel": "نفق", - "docker": "عامل ميناء", + "docker": "Docker", "serverStats": "مقاييس المضيف", "hostMetrics": "مقاييس المضيف", - "noNodes": "لا توجد عقد حتى الآن" + "noNodes": "لا توجد عُقد بعد" }, "docker": { - "notEnabled": "Docker غير مفعل لهذا المضيف", - "validating": "جارٍ التحقق من صحة Docker...", + "notEnabled": "Docker غير مفعّل لهذا المضيف", + "validating": "جارٍ التحقق من Docker...", "connecting": "جارٍ الاتصال...", "error": "خطأ", "version": "Docker {{version}}", "connectionFailed": "فشل الاتصال بـ Docker", - "containerStarted": "تم تشغيل الحاوية {{name}}", - "failedToStartContainer": "فشل بدء تشغيل الحاوية {{name}}", - "containerStopped": "تم إيقاف الحاوية {{name}}", + "containerStarted": "بدأت الحاوية {{name}}", + "failedToStartContainer": "فشل تشغيل الحاوية {{name}}", + "containerStopped": "توقفت الحاوية {{name}}", "failedToStopContainer": "فشل إيقاف الحاوية {{name}}", - "containerRestarted": "تمت إعادة تشغيل الحاوية {{name}}", + "containerRestarted": "أعيد تشغيل الحاوية {{name}}", "failedToRestartContainer": "فشل إعادة تشغيل الحاوية {{name}}", - "containerPaused": "الحاوية {{name}} متوقفة مؤقتًا", - "containerUnpaused": "الحاوية {{name}} غير متوقفة مؤقتًا", + "containerPaused": "تم إيقاف الحاوية {{name}} مؤقتاً", + "containerUnpaused": "تم استئناف الحاوية {{name}}", "failedToTogglePauseContainer": "فشل تبديل حالة الإيقاف المؤقت للحاوية {{name}}", "containerRemoved": "تمت إزالة الحاوية {{name}}", - "failedToRemoveContainer": "فشل في إزالة الحاوية {{name}}", + "failedToRemoveContainer": "فشلت إزالة الحاوية {{name}}", "image": "صورة", - "ports": "الموانئ", + "ports": "منافذ", "noPorts": "لا توجد منافذ", - "start": "يبدأ", + "start": "ابدأ", "confirmRemoveContainer": "هل أنت متأكد من رغبتك في إزالة الحاوية '{{name}}'؟ لا يمكن التراجع عن هذا الإجراء.", - "runningContainerWarning": "تحذير: هذه الحاوية قيد التشغيل حاليًا. سيؤدي حذفها إلى إيقاف الحاوية أولًا.", + "runningContainerWarning": "تحذير: هذه الحاوية قيد التشغيل حالياً. ستؤدي إزالتها إلى إيقافها أولاً.", "loadingContainers": "جارٍ تحميل الحاويات...", "manager": "مدير Docker", - "autoRefresh": "التحديث التلقائي", + "autoRefresh": "تحديث تلقائي", "timestamps": "الطوابع الزمنية", - "lines": "خطوط", + "lines": "الأسطر", "filterLogs": "تصفية السجلات...", - "refresh": "ينعش", - "download": "تحميل", - "clear": "واضح", + "refresh": "تحديث", + "download": "تنزيل", + "clear": "مسح", "logsDownloaded": "تم تنزيل السجلات بنجاح", "last50": "آخر 50", "last100": "آخر 100", "last500": "آخر 500", "last1000": "آخر 1000", - "allLogs": "جميع السجلات", - "noLogsMatching": "لا توجد سجلات مطابقة لـ \"{{query}}\"", + "allLogs": "كل السجلات", + "noLogsMatching": "لا توجد سجلات تطابق \"{{query}}\"", "noLogsAvailable": "لا توجد سجلات متاحة", - "noContainersFound": "لم يتم العثور على أي حاويات", + "noContainersFound": "لا توجد حاويات", "noContainersFoundHint": "لا توجد حاويات Docker متاحة على هذا المضيف", - "searchPlaceholder": "حاويات البحث...", + "searchPlaceholder": "البحث عن حاويات...", "allStatuses": "جميع الحالات", - "stateRunning": "جري", - "statePaused": "توقف مؤقتًا", - "stateExited": "خرج", + "stateRunning": "قيد التشغيل", + "statePaused": "معلق", + "stateExited": "متوقف", "stateRestarting": "إعادة التشغيل", - "noContainersMatchFilters": "لا توجد حاويات تطابق معايير التصفية الخاصة بك", - "noContainersMatchFiltersHint": "حاول تعديل معايير البحث أو التصفية", - "failedToFetchStats": "فشل في جلب إحصائيات الحاوية", - "containerNotRunning": "الحاوية لا تعمل", - "startContainerToViewStats": "ابدأ تشغيل الحاوية لعرض الإحصائيات", + "noContainersMatchFilters": "لا توجد حاويات تطابق عوامل التصفية الخاصة بك", + "noContainersMatchFiltersHint": "جرّب تعديل معايير البحث أو التصفية", + "failedToFetchStats": "فشل جلب إحصائيات الحاوية", + "containerNotRunning": "الحاوية غير قيد التشغيل", + "startContainerToViewStats": "ابدأ الحاوية لعرض الإحصائيات", "loadingStats": "جارٍ تحميل الإحصائيات...", - "errorLoadingStats": "إحصائيات أخطاء التحميل", + "errorLoadingStats": "خطأ في تحميل الإحصائيات", "noStatsAvailable": "لا توجد إحصائيات متاحة", - "cpuUsage": "استخدام وحدة المعالجة المركزية", - "current": "حاضِر", + "cpuUsage": "استخدام CPU", + "current": "الحالي", "memoryUsage": "استخدام الذاكرة", - "networkIo": "إدخال/إخراج الشبكة", - "input": "مدخل", - "output": "الناتج", - "blockIo": "إدخال/إخراج الكتلة", - "read": "يقرأ", - "write": "يكتب", - "pids": "معرّفات PID", + "networkIo": "الإدخال/الإخراج للشبكة", + "input": "إدخال", + "output": "المخرجات", + "blockIo": "الإدخال/الإخراج الكتلي", + "read": "قراءة", + "write": "كتابة", + "pids": "PIDs", "containerInformation": "معلومات الحاوية", - "name": "اسم", - "id": "بطاقة تعريف", - "state": "ولاية", - "containerMustBeRunning": "يجب أن يكون الحاوية قيد التشغيل للوصول إلى وحدة التحكم", + "name": "الاسم", + "id": "المعرف", + "state": "الحالة", + "containerMustBeRunning": "يجب أن تكون الحاوية قيد التشغيل للوصول إلى وحدة التحكم", "verificationCodePrompt": "أدخل رمز التحقق", - "totpVerificationFailed": "فشلت عملية التحقق من رمز TOTP. يرجى المحاولة مرة أخرى.", - "warpgateVerificationFailed": "فشلت عملية المصادقة على بوابة Warpgate. يرجى المحاولة مرة أخرى.", + "totpVerificationFailed": "فشل التحقق من TOTP. يرجى المحاولة مرة أخرى.", + "warpgateVerificationFailed": "فشلت مصادقة Warpgate. يرجى المحاولة مرة أخرى.", "connectedTo": "متصل بـ {{containerName}}", - "disconnected": "غير متصل", + "disconnected": "تم قطع الاتصال", "consoleError": "خطأ في وحدة التحكم", "errorMessage": "خطأ: {{message}}", "failedToConnect": "فشل الاتصال بالحاوية", "console": "وحدة التحكم", - "selectShell": "حدد الصدفة", - "bash": "سحق", - "sh": "ش", - "ash": "رماد", - "connect": "يتصل", + "selectShell": "اختر الصدفة", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "اتصل", "disconnect": "قطع الاتصال", "notConnected": "غير متصل", - "clickToConnect": "انقر على \"اتصال\" لبدء جلسة طرفية", + "clickToConnect": "انقر فوق 'اتصل' لبدء جلسة صدفة", "connectingTo": "جارٍ الاتصال بـ {{containerName}}...", - "containerNotFound": "لم يتم العثور على الحاوية", - "backToList": "العودة إلى القائمة", - "logs": "سجلات", + "containerNotFound": "الحاوية غير موجودة", + "backToList": "عودة إلى القائمة", + "logs": "السجلات", "stats": "الإحصائيات", "consoleTab": "وحدة التحكم", - "startContainerToAccess": "قم بتشغيل الحاوية للوصول إلى وحدة التحكم" + "startContainerToAccess": "ابدأ تشغيل الحاوية للوصول إلى وحدة التحكم" }, "admin": { "sectionGeneral": "عام", "sectionOidc": "OIDC", - "sectionSso": "موفرو خدمات تسجيل الدخول الموحد", - "ssoAddProvider": "إضافة موفر الخدمة", + "sectionSso": "مزودو SSO", + "ssoAddProvider": "إضافة مزود", "ssoDocsLink": "عرض المستندات", "ssoProviderDocsLink": "عرض المستندات", - "ssoNoProviders": "لم يتم تكوين أي موفر خدمة تسجيل دخول موحد (SSO).", - "ssoProviderName": "اسم العرض", - "ssoProviderType": "نوع المزوّد", - "ssoDeleteProvider": "حذف الموفر", - "ssoDeleteConfirm": "هل تريد حذف هذا الموفر؟ لن يتمكن المستخدمون المرتبطون به من تسجيل الدخول.", + "ssoNoProviders": "لم يتم تكوين أي مزود SSO.", + "ssoProviderName": "الاسم المعروض", + "ssoProviderType": "نوع المزود", + "ssoDeleteProvider": "حذف المزود", + "ssoDeleteConfirm": "حذف هذا المزود؟ لن يتمكن المستخدمون المرتبطون من تسجيل الدخول.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", - "ssoTypeGithub": "جيت هاب", - "ssoTypeGoogle": "جوجل", - "ssoEnabled": "مُفعّل", - "ssoDisabled": "عاجز", - "ssoSaveProvider": "حفظ المزوّد", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "مُمكّن", + "ssoDisabled": "معطل", + "ssoSaveProvider": "حفظ المزود", "ssoTestConnection": "اختبار الاتصال", - "ssoEditProvider": "تعديل المزوّد", + "ssoEditProvider": "تعديل المزود", "ldapHost": "مضيف LDAP", - "ldapPort": "ميناء", - "ldapUseTls": "استخدم بروتوكول TLS (LDAPS)", - "ldapBindDn": "ربط DN", - "ldapBindPassword": "ربط كلمة المرور", + "ldapPort": "المنفذ", + "ldapUseTls": "استخدام TLS (LDAPS)", + "ldapBindDn": "اسم مميز للربط", + "ldapBindPassword": "كلمة مرور الارتباط", "ldapUserSearchBase": "قاعدة بحث المستخدمين", - "ldapUserSearchFilter": "فلتر بحث المستخدم", + "ldapUserSearchFilter": "مرشح بحث المستخدمين", "ldapUsernameAttr": "سمة اسم المستخدم", "ldapDisplayNameAttr": "سمة اسم العرض", - "ldapGroupSearchBase": "قاعدة بيانات البحث الجماعي", - "ldapAdminGroup": "مجموعة الإدارة", - "ldapAllowedUsers": "المستخدمون المسموح لهم", + "ldapGroupSearchBase": "قاعدة بحث المجموعات", + "ldapAdminGroup": "مجموعة المديرين", + "ldapAllowedUsers": "المستخدمون المسموح بهم", "sectionUsers": "المستخدمون", "sectionSessions": "الجلسات", "sectionRoles": "الأدوار", "sectionDatabase": "قاعدة البيانات", - "sectionApiKeys": "مفاتيح واجهة برمجة التطبيقات", + "sectionApiKeys": "مفاتيح API", "sectionAuditLog": "سجل التدقيق", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "إصدار وتجديد شهادة SSL موثوقة من Let's Encrypt تلقائيًا. يتطلب ذلك نطاقًا عامًا ومنفذ 80 أو الوصول إلى نظام أسماء النطاقات (DNS).", - "sslDocsLink": "عرض وثائق SSL", - "sslDomain": "اِختِصاص", + "sslDescription": "إصدار وتجديد شهادة SSL موثوقة تلقائيًا من Let's Encrypt. يتطلب نطاقًا عامًا والمنفذ 80 أو وصول DNS.", + "sslDocsLink": "عرض مستندات SSL", + "sslDomain": "النطاق", "sslDomainPlaceholder": "termix.example.com", "sslDomainDesc": "اسم النطاق العام للشهادة.", - "sslEmail": "بريد إلكتروني", + "sslEmail": "البريد الإلكتروني", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "البريد الإلكتروني للتواصل بخصوص إشعارات Let's Encrypt والحساب.", + "sslEmailDesc": "بريد الاتصال لإشعارات Let's Encrypt والحساب.", "sslChallengeType": "نوع التحدي", - "sslChallengeTypeDesc": "كيفية إثبات ملكية النطاق لشركة Let's Encrypt.", - "sslChallengeHttp": "بروتوكول HTTP (جذر الويب) - يتطلب منفذ 80 يمكن الوصول إليه من الإنترنت", - "sslChallengeDns": "نظام أسماء النطاقات (Cloudflare) - يتطلب رمز API من Cloudflare", - "sslCloudflareToken": "رمز واجهة برمجة تطبيقات Cloudflare", - "sslCloudflareTokenPlaceholder": "أدخل الرمز المميز...", - "sslCloudflareTokenDesc": "رمز مميز ذو نطاق محدد مع إذن Zone:DNS:Edit لنطاقك.", + "sslChallengeTypeDesc": "كيفية إثبات ملكية النطاق لـ Let's Encrypt.", + "sslChallengeHttp": "HTTP (webroot) - يتطلب المنفذ 80 متاحًا من الإنترنت", + "sslChallengeDns": "DNS (Cloudflare) - يتطلب رمز Cloudflare API", + "sslCloudflareToken": "رمز Cloudflare API", + "sslCloudflareTokenPlaceholder": "أدخل الرمز...", + "sslCloudflareTokenDesc": "رمز مقيد بصلاحية Zone:DNS:Edit للنطاق الخاص بك.", "sslCertStatus": "حالة الشهادة", "sslCertStatusNone": "لا توجد شهادة", - "sslCertStatusValid": "صالح", - "sslCertStatusExpiring": "ينتهي العرض قريباً", - "sslCertStatusExpired": "منتهي الصلاحية", - "sslCertExpiresAt": "تاريخ الانتهاء {{date}}", + "sslCertStatusValid": "صالحة", + "sslCertStatusExpiring": "ستنتهي قريبًا", + "sslCertStatusExpired": "منتهية", + "sslCertExpiresAt": "تنتهي في {{date}}", "sslLastIssued": "آخر إصدار {{date}}", "sslRequestCert": "إصدار / تجديد الشهادة", - "sslRequestCertLoading": "طلب الشهادة...", + "sslRequestCertLoading": "جارٍ طلب الشهادة...", "sslRequestCertSuccess": "تم إصدار الشهادة وتثبيتها بنجاح", "sslRequestCertFailed": "فشل طلب الشهادة", "sslSave": "حفظ الإعدادات", "sslSaved": "تم حفظ إعدادات SSL", "sslSaveFailed": "فشل حفظ إعدادات SSL", - "sslRequiresDomain": "يلزم توفير اسم النطاق والبريد الإلكتروني", - "sslInfoNote": "بعد إصدار الشهادة، قم بتمكين SSL في متغيرات البيئة الخاصة بك (ENABLE_SSL=true) وأعد تشغيل Termix.", - "auditLogTotal": "{{total}} إجمالي عدد الإدخالات", - "auditLogEmpty": "لم يتم العثور على أي إدخالات في سجل التدقيق", + "sslRequiresDomain": "النطاق والبريد الإلكتروني مطلوبان", + "sslInfoNote": "بعد إصدار الشهادة، فعّل SSL في متغيرات البيئة (ENABLE_SSL=true) وأعد تشغيل Termix.", + "sslManualOption": "يدوي (تحميل الشهادة)", + "sslManualCert": "شهادة (PEM)", + "sslManualCertPlaceholder": "-----بداية الشهادة-----", + "sslManualKey": "المفتاح الخاص (PEM)", + "sslManualKeyPlaceholder": "-----بداية المفتاح الخاص-----", + "sslManualDesc": "ألصق شهادتك الحالية ومفتاحك الخاص، بما في ذلك سلسلة كاملة إذا طلبت منك جهة إصدار الشهادات الخاصة بك ذلك.", + "sslManualUpload": "تحميل وتثبيت الشهادة", + "sslManualUploadLoading": "جارٍ تحميل الشهادة...", + "sslManualUploadSuccess": "تم تحميل الشهادة وتثبيتها بنجاح", + "sslManualUploadFailed": "فشل تحميل الشهادة", + "sslManualRequiresFields": "الشهادة والمفتاح الخاص مطلوبان", + "auditLogTotal": "{{total}} إجمالي السجلات", + "auditLogEmpty": "لا توجد إدخالات في سجل التدقيق", "auditLogSuccess": "نجاح", "auditLogFailed": "فشل", "auditLogClearFilters": "مسح الفلاتر", - "auditLogPage": "الصفحة {{page}} من {{totalPages}} (المجموع{{total}})", - "auditLogIp": "الملكية الفكرية", - "auditLogResourceId": "معرّف المورد", + "auditLogPage": "صفحة {{page}} من {{totalPages}} (إجمالي {{total}})", + "auditLogIp": "IP", + "auditLogResourceId": "معرف المورد", "auditLogFilterUser": "مستخدم", - "auditLogFilterAction": "فعل", + "auditLogFilterAction": "الإجراء", "auditLogFilterResourceType": "نوع المورد", - "auditLogFilterStatus": "حالة", + "auditLogFilterStatus": "الحالة", "auditLogFilterFrom": "من", - "auditLogFilterTo": "ل", - "auditLogFilterAll": "الجميع", - "allowRegistration": "السماح بتسجيل المستخدم", - "allowRegistrationDesc": "اسمح للمستخدمين الجدد بالتسجيل الذاتي باستخدام اسم المستخدم وكلمة المرور", - "allowPasswordLogin": "السماح بتسجيل الدخول باستخدام كلمة المرور", - "allowPasswordLoginDesc": "تسجيل الدخول باستخدام اسم المستخدم/كلمة المرور", + "auditLogFilterTo": "إلى", + "auditLogFilterAll": "الكل", + "allowRegistration": "السماح بتسجيل المستخدمين", + "allowRegistrationDesc": "يسمح للمستخدمين الجدد بالتسجيل الذاتي باستخدام اسم مستخدم وكلمة مرور", + "allowPasswordLogin": "السماح بتسجيل الدخول بكلمة المرور", + "allowPasswordLoginDesc": "تسجيل الدخول باسم المستخدم/كلمة المرور", "oidcAutoProvision": "التزويد التلقائي لـ OIDC", - "oidcAutoProvisionDesc": "إنشاء حسابات تلقائية لمستخدمي OIDC/SSO عند تسجيل الدخول لأول مرة (بشكل مستقل عن مفتاح تبديل التسجيل)", - "oidcSilentLoginDefault": "تسجيل دخول OIDC الصامت افتراضيًا", - "oidcSilentLoginDefaultDesc": "إعادة التوجيه تلقائيًا إلى صفحة تسجيل الدخول إلى OIDC في كل زيارة، مع تخطي نموذج تسجيل الدخول بالكامل.", + "oidcAutoProvisionDesc": "إنشاء الحسابات تلقائياً لمستخدمي OIDC/SSO عند تسجيل الدخول الأول (بغض النظر عن مفتاح التسجيل)", + "oidcSilentLoginDefault": "تسجيل الدخول الصامت لـ OIDC بشكل افتراضي", + "oidcSilentLoginDefaultDesc": "إعادة التوجيه تلقائياً إلى تسجيل الدخول OIDC في كل زيارة، وتخطي نموذج تسجيل الدخول بالكامل", "allowPasswordReset": "السماح بإعادة تعيين كلمة المرور", - "allowPasswordResetDesc": "إعادة ضبط الكود عبر سجلات Docker", + "allowPasswordResetDesc": "رمز إعادة التعيين عبر سجلات Docker", "commandHistoryEnabled": "سجل الأوامر", - "commandHistoryEnabledDesc": "السماح بتسجيل سجل الأوامر. عند تعطيل هذه الخاصية، لا يتم حفظ السجل بغض النظر عن إعدادات كل مضيف.", - "updateCommandHistoryFailed": "فشل تحديث إعدادات سجل الأوامر", - "sessionTimeout": "انتهاء مهلة الجلسة", + "commandHistoryEnabledDesc": "السماح بتسجيل سجل الأوامر. عند التعطيل، لا يتم حفظ السجل بغض النظر عن إعدادات كل جهاز.", + "updateCommandHistoryFailed": "فشل تحديث إعداد سجل الأوامر", + "analyticsEnabled": "مشاركة إحصائيات الاستخدام المجهولة", + "analyticsEnabledDesc": "يرسل النظام إحصاءً يومياً مجهولاً للمستخدمين والمضيفين واستخدام الميزات للمساعدة في تحسين Termix. ولا يتم تضمين أي بيانات شخصية أو تفاصيل اتصال على الإطلاق.", + "updateAnalyticsFailed": "فشل تحديث إعدادات التحليلات", + "sessionSharingGloballyEnabled": "السماح بمشاركة الجلسة", + "sessionSharingGloballyEnabledDesc": "يسمح هذا الخيار بمشاركة جلسات الطرفية المباشرة، وجلسات RDP، وVNC، وTelnet على مستوى الجهاز. ويتجاوز هذا الخيار جميع إعدادات المشاركة الخاصة بكل مضيف عند تعطيله.", + "updateSessionSharingFailed": "فشل تحديث إعدادات مشاركة الجلسة", + "sessionTimeout": "مهلة الجلسة", "hours": "ساعات", - "sessionTimeoutRange": "الحد الأدنى ساعة واحدة · الحد الأقصى 720 ساعة", - "monitoringDefaults": "مراقبة الإعدادات الافتراضية", - "statusCheck": "التحقق من الحالة", - "metrics": "المقاييس", + "sessionTimeoutRange": "الحد الأدنى 1 ساعة · الحد الأقصى 720 ساعة", + "monitoringDefaults": "إعدادات المراقبة الافتراضية", + "statusCheck": "فحص الحالة", + "metrics": "مقاييس", "sec": "ثانية", "logLevel": "مستوى السجل", - "enableGuacamole": "تفعيل جواكامولي", + "enableGuacamole": "تمكين Guacamole", "enableGuacamoleDesc": "سطح المكتب البعيد RDP/VNC", "enableGuacamoleDocsLink": "عرض المستندات", - "guacdUrl": "رابط غواسد", - "tailscaleApiKey": "مفتاح واجهة برمجة تطبيقات Tailscale", - "tailscaleApiKeyDescription": "يُستخدم لاكتشاف الأجهزة في محرر المضيف. أنشئ مفتاحًا على الرابط tailscale.com/admin/settings/keys.", + "guacdUrl": "رابط guacd", + "tailscaleApiKey": "مفتاح Tailscale API", + "tailscaleApiKeyDescription": "يُستخدم لاكتشاف الأجهزة في محرر المضيف. قم بإنشاء مفتاح من tailscale.com/admin/settings/keys.", "tailscaleApiKeyDocsLink": "عرض المستندات", - "oidcDescription": "قم بتهيئة OpenID Connect لتسجيل الدخول الموحد. الحقول المميزة بعلامة * مطلوبة.", + "oidcDescription": "تكوين OpenID Connect لتسجيل الدخول الموحّد (SSO). الحقول المميزة بـ * مطلوبة.", "oidcDocsLink": "عرض المستندات", "oidcClientId": "معرف العميل", "oidcClientSecret": "سر العميل", "oidcAuthUrl": "رابط التفويض", "oidcIssuerUrl": "رابط المُصدر", "oidcTokenUrl": "رابط الرمز المميز", - "oidcUserIdentifier": "مسار مُعرّف المستخدم", + "oidcUserIdentifier": "مسار معرّف المستخدم", "oidcDisplayName": "مسار اسم العرض", "oidcScopes": "النطاقات", - "oidcUserinfoUrl": "تجاوز عنوان URL لمعلومات المستخدم", + "oidcUserinfoUrl": "تجاوز رابط Userinfo", "oidcAllowedUsers": "المستخدمون المسموح لهم", - "oidcAllowedUsersDesc": "بريد إلكتروني واحد لكل سطر. اتركه فارغًا للسماح للجميع.", - "oidcAdminGroup": "مجموعة الإدارة", - "oidcAdminGroupDesc": "يُمنح المستخدمون في هذه المجموعة صلاحيات المسؤول. اترك هذا الحقل فارغًا لتعطيل مزامنة المجموعة.", - "oidcGroupClaim": "مطالبة جماعية", - "oidcGroupClaimDesc": "اختياري. مسار المطالبة الذي يحتوي على مجموعات المستخدم. القيمة الافتراضية هي المجموعات، ثم الأدوار، ثم المجموعة. استخدم هذا الخيار لموفري الخدمات الذين لديهم مطالبة مخصصة (مثل Zitadel).", + "oidcAllowedUsersDesc": "بريد إلكتروني واحد في كل سطر. اتركه فارغًا للسماح للجميع.", + "oidcAdminGroup": "مجموعة المسؤولين", + "oidcAdminGroupDesc": "يُمنح المستخدمون في هذه المجموعة صلاحيات المسؤول. اتركه فارغًا لتعطيل مزامنة المجموعة.", + "oidcGroupClaim": "مطالبة المجموعة", + "oidcGroupClaimDesc": "اختياري. مسار المطالبة الذي يحتوي على مجموعات المستخدم. القيمة الافتراضية هي groups، ثم roles، ثم group. استخدم هذا للموفّرين الذين لديهم مطالبة مخصصة (مثل Zitadel).", "oidcCaCert": "شهادة CA مخصصة", - "oidcCaCertDesc": "اختياري. شهادة مرجع مصدق مشفرة بصيغة PEM لموفري OIDC الذين يستخدمون مرجع مصدق خاص أو موقع ذاتيًا. اترك هذا الحقل فارغًا لاستخدام مخزن الثقة الخاص بالنظام.", - "removeOidc": "يزيل", - "usersCount": "{{count}} مستخدمين", - "createUser": "يخلق", + "oidcCaCertDesc": "اختياري. شهادة CA بترميز PEM لموفّري OIDC الذين يستخدمون شهادة CA خاصة أو موقّعة ذاتيًا. اتركه فارغًا لاستخدام مخزن الثقة في النظام.", + "removeOidc": "إزالة", + "usersCount": "{{count}} مستخدم", + "createUser": "إنشاء", "newRole": "دور جديد", - "roleName": "اسم", + "roleName": "الاسم", "roleDisplayName": "اسم العرض", - "roleDescription": "وصف", - "rolesCount": "{{count}} الأدوار", - "createRole": "يخلق", + "roleDescription": "الوصف", + "rolesCount": "{{count}} دور", + "createRole": "إنشاء", "creating": "جارٍ الإنشاء...", "exportDatabase": "تصدير قاعدة البيانات", - "exportDatabaseDesc": "قم بتنزيل نسخة احتياطية من جميع المضيفين وبيانات الاعتماد والإعدادات", - "export": "يصدّر", + "exportDatabaseDesc": "تنزيل نسخة احتياطية لجميع المضيفين وبيانات الاعتماد والإعدادات", + "export": "تصدير", "exporting": "جارٍ التصدير...", "importDatabase": "استيراد قاعدة البيانات", "importDatabaseDesc": "استعادة من ملف نسخ احتياطي بصيغة .sqlite", - "importDatabaseSelected": "تم الاختيار: {{name}}", - "selectFile": "حدد ملفًا", - "changeFile": "يتغير", - "import": "يستورد", + "importDatabaseSelected": "المحدد: {{name}}", + "selectFile": "اختيار ملف", + "changeFile": "تغيير", + "import": "استيراد", "importing": "جارٍ الاستيراد...", - "apiKeysCount": "{{count}} مفاتيح", + "apiKeysCount": "{{count}} مفتاح", "apiKeysDocsLink": "عرض المستندات", "newApiKey": "مفتاح API جديد", - "apiKeyCreatedWarning": "تم إنشاء المفتاح - انسخه الآن، لن يظهر مرة أخرى.", - "apiKeyName": "اسم", - "apiKeyUser": "مستخدم", - "apiKeySelectUser": "اختر مستخدمًا...", + "apiKeyCreatedWarning": "تم إنشاء المفتاح - انسخه الآن، لن يُعرض مرة أخرى.", + "apiKeyName": "الاسم", + "apiKeyUser": "المستخدم", + "apiKeySelectUser": "اختيار مستخدم...", "apiKeyExpiresAt": "تاريخ الانتهاء", "createKey": "إنشاء مفتاح", - "apiKeyNoExpiry": "بدون تاريخ انتهاء صلاحية", - "revokedBadge": "ملغي", - "authTypeDual": "المصادقة المزدوجة", + "apiKeyNoExpiry": "بدون انتهاء صلاحية", + "revokedBadge": "ملغى", + "authTypeDual": "مصادقة مزدوجة", "authTypeOidc": "OIDC", "authTypeLocal": "محلي", - "adminStatusAdministrator": "المسؤول", + "adminStatusAdministrator": "مدير", "adminStatusRegularUser": "مستخدم عادي", - "adminBadge": "مسؤل", - "systemBadge": "SYS", + "adminBadge": "مدير", + "systemBadge": "نظام", "customBadge": "مخصص", "youBadge": "أنت", - "sessionsActive": "{{count}} نشط", - "sessionActive": "نشط: {{time}}", - "sessionExpires": "تاريخ الانتهاء: {{time}}", - "revokeAll": "الجميع", - "revokeAllSessionsSuccess": "تم إلغاء جميع جلسات المستخدم", - "revokeAllSessionsFailed": "فشل في إلغاء الجلسات", - "revokeSessionFailed": "فشل إلغاء الجلسة", + "sessionsActive": "{{count}} نشطة", + "sessionActive": "نشطة: {{time}}", + "sessionExpires": "تنتهي: {{time}}", + "revokeAll": "الكل", + "revokeAllSessionsSuccess": "تم إبطال جميع جلسات المستخدم", + "revokeAllSessionsFailed": "فشل إبطال الجلسات", + "revokeSessionFailed": "فشل إبطال الجلسة", "addRole": "إضافة دور", - "noCustomRoles": "لم يتم تعريف أي أدوار مخصصة", - "removeRoleFailed": "فشل في إزالة الدور", - "assignRoleFailed": "فشل في تعيين الدور", + "noCustomRoles": "لا توجد أدوار مخصصة مُعرَّفة", + "removeRoleFailed": "فشل إزالة الدور", + "assignRoleFailed": "فشل تعيين الدور", "deleteRoleFailed": "فشل حذف الدور", - "userAdminAccess": "المسؤول", - "userAdminAccessDesc": "الوصول الكامل إلى جميع إعدادات المسؤول", + "userAdminAccess": "مدير", + "userAdminAccessDesc": "وصول كامل إلى جميع إعدادات المدير", "userRoles": "الأدوار", - "revokeAllUserSessions": "إلغاء جميع الجلسات", - "revokeAllUserSessionsDesc": "فرض إعادة تسجيل الدخول على جميع الأجهزة", - "revoke": "إلغاء", - "deleteUserWarning": "حذف هذا المستخدم نهائي.", + "revokeAllUserSessions": "إبطال جميع الجلسات", + "revokeAllUserSessionsDesc": "إجبار تسجيل الدخول مجددًا على جميع الأجهزة", + "revoke": "إبطال", + "deleteUserWarning": "حذف هذا المستخدم دائم.", "deleteUser": "حذف {{username}}", - "deleting": "جارٍ الحذف...", + "deleting": "جاري الحذف...", "deleteUserFailed": "فشل حذف المستخدم", "deleteUserSuccess": "تم حذف المستخدم \"{{username}}\"", "deleteRoleSuccess": "تم حذف الدور \"{{name}}\"", - "revokeKeySuccess": "تم إلغاء المفتاح \"{{name}}\"", - "revokeKeyFailed": "فشل إلغاء المفتاح", - "copiedToClipboard": "تم نسخها إلى الحافظة", - "done": "منتهي", - "createUserTitle": "إنشاء مستخدمين", - "createUserDesc": "أنشئ حسابًا محليًا جديدًا.", + "revokeKeySuccess": "تم إبطال المفتاح \"{{name}}\"", + "revokeKeyFailed": "فشل إبطال المفتاح", + "copiedToClipboard": "تم النسخ إلى الحافظة", + "done": "تم", + "createUserTitle": "إنشاء مستخدم", + "createUserDesc": "إنشاء حساب محلي جديد.", "createUserUsername": "اسم المستخدم", "createUserPassword": "كلمة المرور", - "createUserPasswordHint": "الحد الأدنى 6 أحرف.", + "createUserPasswordHint": "6 أحرف على الأقل.", "createUserEnterUsername": "أدخل اسم المستخدم", "createUserEnterPassword": "أدخل كلمة المرور", - "createUserSubmit": "إنشاء مستخدمين", + "createUserSubmit": "إنشاء مستخدم", "editUserTitle": "إدارة المستخدم: {{username}}", - "editUserDesc": "تعديل الأدوار، وحالة المسؤول، والجلسات، وإعدادات الحساب.", + "editUserDesc": "تعديل الأدوار، وحالة المدير، والجلسات، وإعدادات الحساب.", "editUserUsername": "اسم المستخدم", "editUserAuthType": "نوع المصادقة", "editUserAdminStatus": "حالة المسؤول", - "editUserUserId": "معرف المستخدم", + "editUserUserId": "معرّف المستخدم", "linkAccountTitle": "ربط الحسابات", - "linkAccountDesc": "قم بدمج حساب OIDC {{username}} مع حساب محلي موجود.", - "linkAccountDescLocal": "قم بربط حساب محلي {{username}} بحساب OIDC موجود فقط.", + "linkAccountDesc": "دمج حساب OIDC {{username}} مع حساب محلي موجود.", + "linkAccountDescLocal": "ربط حساب محلي {{username}} بحساب OIDC فقط موجود.", "linkAccountWarningTitle": "سيؤدي هذا إلى:", - "linkAccountEffect1": "احذف الحساب المخصص لـ OIDC فقط", - "linkAccountEffect2": "أضف بيانات تسجيل الدخول إلى OIDC إلى الحساب المستهدف", - "linkAccountEffect3": "السماح بتسجيل الدخول باستخدام كل من OIDC وكلمة المرور", + "linkAccountEffect1": "حذف حساب OIDC فقط", + "linkAccountEffect2": "إضافة تسجيل دخول OIDC إلى الحساب الهدف", + "linkAccountEffect3": "السماح بتسجيل الدخول عبر OIDC وكلمة المرور", "linkAccountTargetUsername": "اسم مستخدم الحساب المحلي", "linkAccountTargetPlaceholder": "أدخل اسم مستخدم الحساب المحلي للربط به", "linkAccountOidcUsername": "اسم مستخدم حساب OIDC", "linkAccountOidcPlaceholder": "أدخل اسم مستخدم حساب OIDC فقط للدمج", - "linkAccountOidcNotFound": "لم يتم العثور على أي حساب مخصص لـ OIDC فقط بهذا الاسم المستخدم", + "linkAccountOidcNotFound": "لم يتم العثور على حساب OIDC فقط بهذا الاسم", "linkAccounts": "ربط الحسابات", "linkAccountSuccess": "تم ربط الحسابات بنجاح", "linkAccountFailed": "فشل ربط الحسابات", "linkAccountInProgress": "جارٍ الربط...", "unlinkAccountTitle": "إلغاء ربط OIDC", - "unlinkAccountDesc": "قم بإزالة مصادقة OIDC من {{username}}. لن يتمكنوا من تسجيل الدخول إلا باستخدام كلمة المرور الخاصة بهم.", - "unlinkAccountWarning": "سيؤدي هذا إلى إزالة تسجيل الدخول إلى OIDC من هذا الحساب. يجب على المستخدم تعيين كلمة مرور لمتابعة تسجيل الدخول.", + "unlinkAccountDesc": "إزالة مصادقة OIDC من {{username}}. سيتمكن من تسجيل الدخول باستخدام كلمة المرور فقط.", + "unlinkAccountWarning": "سيؤدي هذا إلى إزالة تسجيل دخول OIDC من هذا الحساب. يجب أن يكون لدى المستخدم كلمة مرور لمواصلة تسجيل الدخول.", "unlinkAccount": "إلغاء ربط OIDC", "unlinkAccountInProgress": "جارٍ إلغاء الربط...", - "unlinkAccountSuccess": "تم فصل OIDC بنجاح", - "unlinkAccountFailed": "فشل في فصل OIDC", - "saving": "توفير...", - "updateRegistrationFailed": "فشل تحديث إعدادات التسجيل", - "updatePasswordLoginFailed": "فشل تحديث إعدادات تسجيل الدخول بكلمة المرور", - "cannotDisablePasswordLoginWithTotp": "لا يمكن تعطيل تسجيل الدخول بكلمة المرور أثناء تفعيل المصادقة الثنائية لمستخدم واحد أو أكثر. قم بتعطيل المصادقة الثنائية أولاً.", + "unlinkAccountSuccess": "تم إلغاء ربط OIDC بنجاح", + "unlinkAccountFailed": "فشل إلغاء ربط OIDC", + "saving": "جارٍ الحفظ...", + "updateRegistrationFailed": "فشل تحديث إعداد التسجيل", + "updatePasswordLoginFailed": "فشل تحديث إعداد تسجيل الدخول بكلمة المرور", + "cannotDisablePasswordLoginWithTotp": "لا يمكن تعطيل تسجيل الدخول بكلمة المرور أثناء تمكين المصادقة الثنائية (2FA) لمستخدم واحد أو أكثر. قم بتعطيل المصادقة الثنائية أولاً.", "updateOidcAutoProvisionFailed": "فشل تحديث إعداد التزويد التلقائي لـ OIDC", - "updateOidcSilentLoginDefaultFailed": "فشل تحديث إعدادات تسجيل الدخول الصامت لـ OIDC", - "updatePasswordResetFailed": "فشل تحديث إعدادات إعادة تعيين كلمة المرور", - "sessionTimeoutRange2": "يجب أن تكون مهلة الجلسة بين ساعة واحدة و720 ساعة", - "sessionTimeoutSaved": "حفظ مهلة الجلسة", + "updateOidcSilentLoginDefaultFailed": "فشل تحديث إعداد تسجيل الدخول الصامت لـ OIDC", + "updatePasswordResetFailed": "فشل تحديث إعداد إعادة تعيين كلمة المرور", + "sessionTimeoutRange2": "يجب أن تكون مهلة الجلسة بين 1 و 720 ساعة", + "sessionTimeoutSaved": "تم حفظ مهلة الجلسة", "sessionTimeoutSaveFailed": "فشل حفظ مهلة الجلسة", - "monitoringIntervalInvalid": "قيم فترات غير صالحة", + "monitoringIntervalInvalid": "قيم الفاصل الزمني غير صالحة", "monitoringSaved": "تم حفظ إعدادات المراقبة", "monitoringSaveFailed": "فشل حفظ إعدادات المراقبة", "metricsHistoryRetention": "الاحتفاظ بسجل المقاييس", - "metricsHistoryRetentionRange": "من يوم واحد إلى 90 يومًا", + "metricsHistoryRetentionRange": "من 1 إلى 90 يومًا", "days": "أيام", - "guacamoleSaved": "تم حفظ إعدادات الجواكامولي", - "guacamoleSaveFailed": "فشل حفظ إعدادات الجواكامولي", - "guacamoleUpdateFailed": "فشل تحديث إعدادات الجواكامولي", - "tailscaleSettingsSaved": "تم حفظ إعدادات مقياس الذيل", - "tailscaleSettingsSaveFailed": "فشل حفظ إعدادات مقياس الذيل", + "guacamoleSaved": "تم حفظ إعدادات Guacamole", + "guacamoleSaveFailed": "فشل حفظ إعدادات Guacamole", + "guacamoleUpdateFailed": "فشل تحديث إعداد Guacamole", + "tailscaleSettingsSaved": "تم حفظ إعدادات Tailscale", + "tailscaleSettingsSaveFailed": "فشل حفظ إعدادات Tailscale", "logLevelUpdateFailed": "فشل تحديث مستوى السجل", - "oidcSaved": "تم حفظ إعدادات OIDC", - "oidcSaveFailed": "فشل حفظ إعدادات OIDC", + "oidcSaved": "تم حفظ تكوين OIDC", + "oidcSaveFailed": "فشل حفظ تكوين OIDC", "oidcRemoved": "تمت إزالة تكوين OIDC", - "oidcRemoveFailed": "فشل في إزالة إعدادات OIDC", + "oidcRemoveFailed": "فشلت إزالة تكوين OIDC", "createUserRequired": "اسم المستخدم وكلمة المرور مطلوبان", "createUserPasswordTooShort": "يجب أن تتكون كلمة المرور من 6 أحرف على الأقل", "createUserSuccess": "تم إنشاء المستخدم \"{{username}}\"", "createUserFailed": "فشل إنشاء المستخدم", "updateAdminStatusFailed": "فشل تحديث حالة المسؤول", - "allSessionsRevoked": "تم إلغاء جميع الجلسات", - "revokeSessionsFailed": "فشل في إلغاء الجلسات", + "allSessionsRevoked": "تم إبطال جميع الجلسات", + "revokeSessionsFailed": "فشل إبطال الجلسات", "manageUserData": "إدارة بيانات المستخدم", "backToUsers": "العودة إلى المستخدمين", - "manageTabAccount": "حساب", + "manageTabAccount": "الحساب", "manageTabHosts": "المضيفون", - "manageTabCredentials": "أوراق اعتماد", - "manageTabSnippets": "مقتطفات", + "manageTabCredentials": "بيانات الاعتماد", + "manageTabSnippets": "المقتطفات", "manageTabSessions": "الجلسات", "manageTabDanger": "خطر", "manageEditorBack": "العودة إلى بيانات {{username}}", - "dataLockedBadge": "مغلق", - "dataLockedNotice": "تبقى بيانات المستخدم {{username}}مقفلة حتى يقوم بتسجيل الدخول مرة أخرى. ولا يمكن عرض أو تعديل مضيفيه وبيانات اعتماده ومقتطفاته حتى ذلك الحين.", + "dataLockedBadge": "مقفل", + "dataLockedNotice": "تظل بيانات {{username}} مقفلة حتى تسجيل الدخول التالي. لا يمكن عرض أو تعديل مضيفيه وبيانات اعتماده ومقاطع حتى ذلك الحين.", "resetPasswordTitle": "إعادة تعيين كلمة المرور", - "resetPasswordOidcOnly": "يقوم هذا المستخدم بتسجيل الدخول من خلال مزود خدمة خارجي وليس لديه كلمة مرور.", - "resetPasswordPlaceholder": "كلمة المرور الجديدة", - "resetPasswordBtn": "إعادة ضبط", - "resetPasswordWorking": "جارٍ إعادة الضبط...", - "resetPasswordSuccess": "إعادة تعيين كلمة المرور", - "resetPasswordSuccessWiped": "تمت إعادة تعيين كلمة المرور. تم مسح بيانات المستخدم المشفرة.", - "resetPasswordFailed": "فشل إعادة تعيين كلمة المرور", - "resetPasswordConfirmWipe": "لم يسجّل المستخدم {{username}} دخوله منذ تحديث التشفير، لذا لا يمكن استعادة بياناته. إعادة التعيين الآن ستحذف مضيفيه وبيانات اعتماده وبياناته الشخصية. هل تريد المتابعة؟", + "resetPasswordOidcOnly": "يسجل هذا المستخدم الدخول عبر مزود خارجي ولا يملك كلمة مرور.", + "resetPasswordPlaceholder": "كلمة مرور جديدة", + "resetPasswordBtn": "إعادة تعيين", + "resetPasswordWorking": "جارٍ إعادة التعيين...", + "resetPasswordSuccess": "تمت إعادة تعيين كلمة المرور", + "resetPasswordSuccessWiped": "تمت إعادة تعيين كلمة المرور. تم حذف البيانات المشفرة للمستخدم.", + "resetPasswordFailed": "فشلت إعادة تعيين كلمة المرور", + "resetPasswordConfirmWipe": "لم يسجل {{username}} الدخول منذ ترقية التشفير، لذا لا يمكن استرداد بياناته. ستؤدي إعادة التعيين الآن إلى حذف مضيفيه وبيانات اعتماده ومقاطع. هل تريد المتابعة؟", "totpSectionTitle": "المصادقة الثنائية", - "totpStatusEnabled": "تم تفعيل رمز TOTP لهذا المستخدم", - "totpStatusDisabled": "لم يتم تفعيل رمز التحقق من المصدر (TOTP) لهذا المستخدم", - "disableTotp": "إبطال", - "disableTotpConfirm": "هل يجب تعطيل المصادقة الثنائية للمستخدم {{username}}؟ سيتمكنون من تسجيل الدخول باستخدام كلمة المرور فقط.", - "totpDisabledSuccess": "تم تعطيل رمز التحقق من الهاتف", - "totpDisableFailed": "فشل تعطيل بروتوكول TOTP", - "manageApiKeys": "مفاتيح واجهة برمجة التطبيقات", - "apiKeyNamePlaceholder": "الاسم الرئيسي", + "totpStatusEnabled": "تم تمكين TOTP لهذا المستخدم", + "totpStatusDisabled": "لم يتم تمكين TOTP لهذا المستخدم", + "disableTotp": "تعطيل", + "disableTotpConfirm": "تعطيل المصادقة الثنائية لـ {{username}}؟ سيكون بمقدورهم تسجيل الدخول باستخدام كلمة المرور فقط.", + "totpDisabledSuccess": "تم تعطيل TOTP", + "totpDisableFailed": "فشل تعطيل TOTP", + "manageApiKeys": "مفاتيح API", + "apiKeyNamePlaceholder": "اسم المفتاح", "apiKeyCopyNotice": "انسخ هذا المفتاح الآن، فلن يظهر مرة أخرى.", "noApiKeysForUser": "لا توجد مفاتيح API", "apiKeyDeleteFailed": "فشل حذف مفتاح API", "exportUserData": "تصدير البيانات", - "exportUserDataDesc": "قم بتنزيل بيانات المضيفين وبيانات الاعتماد وبيانات مدير الملفات الخاصة بهذا المستخدم بصيغة JSON. يتم فك تشفير البيانات السرية.", + "exportUserDataDesc": "تنزيل مضيفات هذا المستخدم وبيانات اعتماده وبيانات مدير الملفات بصيغة JSON. يتم فك تشفير الأسرار.", "exportUserDataSuccess": "تم تصدير بيانات المستخدم", "exportUserDataFailed": "فشل تصدير بيانات المستخدم", - "hostsCount": "{{count}} المضيفون", + "hostsCount": "{{count}} مضيفات", "addHostForUser": "إضافة مضيف", - "noHostsForUser": "لا يملك هذا المستخدم أي مضيفين", - "connectToHost": "يتصل", - "deleteHostConfirm": "حذف المضيف \"{{name}}\" التابع لـ \" {{username}}\"؟", + "noHostsForUser": "لا يملك هذا المستخدم أي مضيفات", + "connectToHost": "اتصال", + "deleteHostConfirm": "حذف المضيف \"{{name}}\" التابع لـ {{username}}؟", "hostDeletedSuccess": "تم حذف المضيف", "hostDeleteFailed": "فشل حذف المضيف", - "credentialsCount": "بيانات الاعتماد {{count}}", + "credentialsCount": "{{count}} بيانات اعتماد", "addCredentialForUser": "إضافة بيانات اعتماد", - "noCredentialsForUser": "لا يملك هذا المستخدم بيانات اعتماد", - "deleteCredentialConfirm": "حذف بيانات الاعتماد \"{{name}}\" التابعة لـ \" {{username}}\"؟", + "noCredentialsForUser": "هذا المستخدم ليس لديه بيانات اعتماد", + "deleteCredentialConfirm": "حذف بيانات الاعتماد \"{{name}}\" الخاصة بـ {{username}}؟", "credentialDeletedSuccess": "تم حذف بيانات الاعتماد", "credentialDeleteFailed": "فشل حذف بيانات الاعتماد", - "snippetsCount": "مقتطفات {{count}}", + "snippetsCount": "{{count}} مقتطفات", "addSnippetForUser": "إضافة مقتطف", - "noSnippetsForUser": "لا يملك هذا المستخدم أي مقتطفات", - "snippetRequiredFields": "اسم المقتطف ومحتواه مطلوبان", + "noSnippetsForUser": "هذا المستخدم ليس لديه مقتطفات", + "snippetRequiredFields": "اسم المقتطف والمحتوى مطلوبان", "snippetNamePlaceholder": "اسم المقتطف", "snippetContentPlaceholder": "محتوى الأمر", - "snippetFolderPlaceholder": "مجلد (اختياري)", + "snippetFolderPlaceholder": "المجلد (اختياري)", "snippetSaved": "تم حفظ المقتطف", "snippetSaveFailed": "فشل حفظ المقتطف", - "deleteSnippetConfirm": "حذف المقتطف \"{{name}}\" التابع لـ {{username}}؟", + "deleteSnippetConfirm": "حذف المقتطف \"{{name}}\" الخاص بـ {{username}}؟", "snippetDeletedSuccess": "تم حذف المقتطف", "snippetDeleteFailed": "فشل حذف المقتطف", "noSessionsForUser": "لا توجد جلسات نشطة", - "deleteUserDangerDesc": "احذف نهائيًا {{username}} وجميع بياناتها (المضيفين، بيانات الاعتماد، المقتطفات، السجل). لا يمكن التراجع عن هذا الإجراء.", - "deleteUserConfirm": "هل تريد حذف {{username}} وجميع بياناتهم بشكل دائم؟", - "deleteUserAdminBlocked": "قم بإزالة صلاحيات المسؤول قبل حذف هذا المستخدم.", + "deleteUserDangerDesc": "حذف {{username}} وجميع بياناته بشكل دائم (المضيفين، بيانات الاعتماد، المقتطفات، السجل). لا يمكن التراجع عن هذا الإجراء.", + "deleteUserConfirm": "حذف {{username}} وجميع بياناته بشكل دائم؟", + "deleteUserAdminBlocked": "إزالة صلاحية المسؤول قبل حذف هذا المستخدم.", "createRoleRequired": "الاسم واسم العرض مطلوبان", "createRoleSuccess": "تم إنشاء الدور \"{{name}}\"", "createRoleFailed": "فشل إنشاء الدور", @@ -2918,145 +3069,145 @@ "apiKeyCreatedSuccess": "تم إنشاء مفتاح API \"{{name}}\"", "apiKeyCreateFailed": "فشل إنشاء مفتاح API", "exportSuccess": "تم تصدير قاعدة البيانات بنجاح", - "exportFailed": "فشلت عملية تصدير قاعدة البيانات", + "exportFailed": "فشل تصدير قاعدة البيانات", "importSelectFile": "يرجى تحديد ملف أولاً", - "importCompleted": "اكتمل الاستيراد: تم استيراد {{total}} عنصرًا، وتم تخطي {{skipped}} عنصرًا", + "importCompleted": "اكتمل الاستيراد: تم استيراد {{total}} عنصر، وتم تخطي {{skipped}}", "importFailed": "فشل الاستيراد: {{error}}", - "importError": "فشلت عملية استيراد قاعدة البيانات", - "sectionHostDefaults": "الإعدادات الافتراضية للمضيف", - "hostDefaultsDesc": "يتم تطبيق الإعدادات تلقائيًا عند إنشاء مضيف جديد. ولا يزال بإمكان المضيفين الأفراد تجاوز هذه الإعدادات.", - "hostDefaultsSocks5": "بروكسي SOCKS5", - "hostDefaultsUseSocks5": "تفعيل بروكسي SOCKS5", - "hostDefaultsUseSocks5Desc": "قم بتعبئة وكيل SOCKS5 مسبقًا على جميع المضيفين الجدد", - "hostDefaultsSocks5Host": "خادم الوكيل / المنفذ", + "importError": "فشل استيراد قاعدة البيانات", + "sectionHostDefaults": "الإعدادات الافتراضية للمضيفين", + "hostDefaultsDesc": "الإعدادات التي يتم تطبيقها تلقائيًا عند إنشاء مضيف جديد. لا يزال بإمكان كل مضيف تجاوز هذه الإعدادات.", + "hostDefaultsSocks5": "وكيل SOCKS5", + "hostDefaultsUseSocks5": "تمكين وكيل SOCKS5", + "hostDefaultsUseSocks5Desc": "ملء وكيل SOCKS5 مسبقًا على جميع المضيفين الجدد", + "hostDefaultsSocks5Host": "مضيف الوكيل / المنفذ", "hostDefaultsSocks5Username": "اسم مستخدم الوكيل", "hostDefaultsSocks5Password": "كلمة مرور الوكيل", "hostDefaultsMetrics": "مقاييس المضيف", - "hostDefaultsMetricsEnabled": "تفعيل المقاييس", - "hostDefaultsMetricsEnabledDesc": "يتم جمع بيانات وحدة المعالجة المركزية والذاكرة وغيرها من الإحصائيات على الأجهزة المضيفة الجديدة بشكل افتراضي.", - "hostDefaultsStatusCheckEnabled": "تفعيل فحص الحالة", - "hostDefaultsStatusCheckEnabledDesc": "يتم استطلاع حالة الاتصال بالإنترنت/عدم الاتصال على المضيفين الجدد بشكل افتراضي.", - "hostDefaultsTerminal": "صالة", + "hostDefaultsMetricsEnabled": "تمكين المقاييس", + "hostDefaultsMetricsEnabledDesc": "جمع إحصائيات المعالج والذاكرة وغيرها على المضيفين الجدد افتراضيًا", + "hostDefaultsStatusCheckEnabled": "تمكين فحص الحالة", + "hostDefaultsStatusCheckEnabledDesc": "استطلاع حالة الاتصال أو الانفصال على المضيفين الجدد افتراضيًا", + "hostDefaultsTerminal": "الطرفية", "hostDefaultsSessionLogging": "تسجيل الجلسات", - "hostDefaultsSessionLoggingDesc": "يتم تسجيل جلسات الطرفية على المضيفين الجدد افتراضيًا", + "hostDefaultsSessionLoggingDesc": "تسجيل جلسات الطرفية على المضيفين الجدد افتراضيًا", "hostDefaultsCommandHistory": "سجل الأوامر", - "hostDefaultsCommandHistoryDesc": "يتم تتبع سجل الأوامر على الأجهزة المضيفة الجديدة افتراضيًا", + "hostDefaultsCommandHistoryDesc": "تتبع سجل الأوامر على المضيفين الجدد افتراضيًا", "hostDefaultsSaved": "تم حفظ الإعدادات الافتراضية للمضيف", "hostDefaultsSaveFailed": "فشل حفظ الإعدادات الافتراضية للمضيف", "rolePermissions": { - "count": "{{count}} الأذونات", - "editAction": "تعديل الأذونات", - "loadError": "فشل تحميل كتالوج الأذونات", - "saved": "تم حفظ أذونات الدور", - "saveError": "فشل حفظ أذونات الدور", - "save": "يحفظ", - "saving": "توفير..." + "count": "{{count}} صلاحيات", + "editAction": "تحرير الصلاحيات", + "loadError": "فشل تحميل كتالوج الصلاحيات", + "saved": "تم حفظ صلاحيات الدور", + "saveError": "فشل حفظ صلاحيات الدور", + "save": "حفظ", + "saving": "جارٍ الحفظ..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "يستضيف", + "hostLabel": "المضيف", "hostPlaceholder": "192.168.1.1 أو example.com", - "portLabel": "ميناء", + "portLabel": "المنفذ", "portPlaceholder": "22", "usernameLabel": "اسم المستخدم", - "usernamePlaceholder": "اسم المستخدم", + "usernamePlaceholder": "username", "authLabel": "المصادقة", "passwordLabel": "كلمة المرور", - "passwordPlaceholder": "كلمة المرور", + "passwordPlaceholder": "password", "privateKeyLabel": "المفتاح الخاص", - "privateKeyPlaceholder": "ألصق المفتاح الخاص...", + "privateKeyPlaceholder": "الصق المفتاح الخاص...", "credentialLabel": "بيانات الاعتماد", - "credentialPlaceholder": "حدد بيانات اعتماد محفوظة", - "connectToTerminal": "الاتصال بالطرفية", - "connectToFiles": "الاتصال بالملفات" + "credentialPlaceholder": "اختر بيانات اعتماد محفوظة", + "connectToTerminal": "اتصل بالطرفية", + "connectToFiles": "اتصل بالملفات" }, "history": { - "noTerminalSelected": "لا يوجد طرفية ج", + "noTerminalSelected": "لا توجد طرفية محددة", "noTerminalSelectedHint": "افتح علامة تبويب طرفية SSH لعرض سجل الأوامر الخاص بها", - "searchPlaceholder": "سجل البحث...", + "searchPlaceholder": "ابحث في السجل...", "clearAll": "مسح الكل", - "noHistoryEntries": "لا توجد سجلات تاريخية", - "trackingDisabled": "تم تعطيل خاصية تتبع السجل", - "trackingDisabledHint": "قم بتفعيله في إعدادات طرفية الجهاز المضيف." + "noHistoryEntries": "لا توجد مدخلات سجل", + "trackingDisabled": "تتبع السجل معطل", + "trackingDisabledHint": "قم بتمكينه في إعدادات الطرفية الخاصة بالمضيف." }, "sshTools": { - "keyRecordingTitle": "تسجيل رئيسي", - "recordToTerminals": "التسجيل في المحطات الطرفية", - "selectAll": "الجميع", - "selectNone": "لا أحد", + "keyRecordingTitle": "تسجيل المفاتيح", + "recordToTerminals": "التسجيل إلى الطرفيات", + "selectAll": "الكل", + "selectNone": "بلا", "noTerminalTabsOpen": "لا توجد علامات تبويب طرفية مفتوحة", - "selectTerminalsAbove": "حدد المحطات الطرفية أعلاه", + "selectTerminalsAbove": "اختر الطرفيات أعلاه", "broadcastInputPlaceholder": "اكتب هنا لبث ضغطات المفاتيح...", - "fillPassword": "أدخل كلمة المرور", - "fillPasswordSuccess": "أدخل كلمة المرور في {{count}} الطرفية (الطرفيات)", - "fillPasswordMissing": "لا توجد كلمة مرور محفوظة للمحطة (المحطات) المحددة {{count}}", - "stopRecording": "أوقف التسجيل", - "startRecording": "ابدأ التسجيل", - "settingsTitle": "إعدادات", - "enableRightClickCopyPaste": "تفعيل خاصية النسخ/اللصق بالنقر بزر الماوس الأيمن" + "fillPassword": "املأ كلمة المرور", + "fillPasswordSuccess": "تم ملء كلمة المرور في {{count}} طرفية", + "fillPasswordMissing": "لا توجد كلمة مرور محفوظة لـ {{count}} طرفية محددة", + "stopRecording": "إيقاف التسجيل", + "startRecording": "بدء التسجيل", + "settingsTitle": "الإعدادات", + "enableRightClickCopyPaste": "تمكين النسخ واللصق بالزر الأيمن" }, "splitScreen": { - "layoutTitle": "تَخطِيط", - "selectLayoutAbove": "اختر تخطيطًا من الأعلى", - "selectLayoutHint": "اختر عدد الأجزاء التي تريد عرضها", - "panesTitle": "ألواح", - "openTabsTitle": "فتح علامات التبويب", - "dragTabsHint": "اسحب علامات التبويب إلى الأجزاء العلوية، أو استخدم خاصية التعيين السريع.", - "dropHere": "انزل هنا", + "layoutTitle": "التخطيط", + "selectLayoutAbove": "اختر تخطيطًا أعلاه", + "selectLayoutHint": "اختر عدد الأجزاء المراد عرضها", + "panesTitle": "الأجزاء", + "openTabsTitle": "علامات التبويب المفتوحة", + "dragTabsHint": "اسحب علامات التبويب إلى الأجزاء أعلاه، أو استخدم التعيين السريع", + "dropHere": "أسقط هنا", "emptyPane": "فارغ", "dashboard": "لوحة التحكم", - "clearSplitScreen": "شاشة مقسمة واضحة", - "quickAssign": "التعيين السريع", + "clearSplitScreen": "مسح تقسيم الشاشة", + "quickAssign": "تعيين سريع", "alreadyAssigned": "لوحة {{index}}", - "splitTab": "علامة تبويب مقسمة", - "addToSplit": "أضف إلى التقسيم", - "removeFromSplit": "أزل من التقسيم", - "assignToPane": "تعيين إلى اللوحة", + "splitTab": "تقسيم التبويب", + "addToSplit": "إضافة إلى التقسيم", + "removeFromSplit": "إزالة من التقسيم", + "assignToPane": "تعيين للوحة", "hotkeysTitle": "اختصارات لوحة المفاتيح", - "hotkeysSplitRight": "تبديل تقسيم الشاشة إلى قسمين", - "hotkeysSplitBelow": "تبديل تقسيم الشاشة إلى ثلاثة أقسام", - "hotkeysNavigatePane": "التنقل بين الأجزاء", - "hotkeysNextTab": "علامة التبويب التالية", - "hotkeysPrevTab": "علامة التبويب السابقة" + "hotkeysSplitRight": "تبديل التقسيم الثنائي", + "hotkeysSplitBelow": "تبديل التقسيم الثلاثي", + "hotkeysNavigatePane": "التنقل بين اللوحات", + "hotkeysNextTab": "التبويب التالي", + "hotkeysPrevTab": "التبويب السابق" }, "snippets": { - "title": "مقتطفات", - "createSnippetTitle": "إنشاء جزء من التعليمات البرمجية", - "createSnippetDescription": "أنشئ جزءًا جديدًا من الأوامر لتنفيذ سريع", - "nameLabel": "اسم", - "namePlaceholder": "مثال: أعد تشغيل Nginx", - "descriptionLabel": "وصف", + "title": "المقتطفات", + "createSnippetTitle": "إنشاء مقتطفة", + "createSnippetDescription": "إنشاء مقتطفة أمر جديدة للتنفيذ السريع", + "nameLabel": "الاسم", + "namePlaceholder": "مثال: إعادة تشغيل Nginx", + "descriptionLabel": "الوصف", "descriptionPlaceholder": "وصف اختياري", - "optional": "خياري", + "optional": "اختياري", "folderLabel": "مجلد", - "noFolder": "لا يوجد مجلد (غير مصنف)", - "commandLabel": "يأمر", - "commandPlaceholder": "مثال: sudo systemctl restart nginx", - "cancel": "يلغي", - "createSnippetButton": "إنشاء جزء من التعليمات البرمجية", + "noFolder": "بدون مجلد (غير مصنف)", + "commandLabel": "الأمر", + "commandPlaceholder": "مثلاً: sudo systemctl restart nginx", + "cancel": "إلغاء", + "createSnippetButton": "إنشاء مقتطفة", "createFolderTitle": "إنشاء مجلد", - "createFolderDescription": "قم بتنظيم مقتطفاتك في مجلدات", + "createFolderDescription": "تنظيم المقتطفات في مجلدات", "folderNameLabel": "اسم المجلد", - "folderNamePlaceholder": "على سبيل المثال، أوامر النظام، نصوص دوكر البرمجية", + "folderNamePlaceholder": "مثلاً: أوامر النظام، نصوص Docker", "folderColorLabel": "لون المجلد", "folderIconLabel": "أيقونة المجلد", "previewLabel": "معاينة", "folderNameFallback": "اسم المجلد", "createFolderButton": "إنشاء مجلد", - "targetTerminals": "محطات الهدف", - "selectAll": "الجميع", - "selectNone": "لا أحد", + "targetTerminals": "الطرفيات المستهدفة", + "selectAll": "الكل", + "selectNone": "لا شيء", "noTerminalTabsOpen": "لا توجد علامات تبويب طرفية مفتوحة", - "searchPlaceholder": "نتائج البحث...", + "searchPlaceholder": "ابحث في المقتطفات...", "newSnippet": "مقتطف جديد", "newFolder": "مجلد جديد", - "run": "يجري", + "run": "تشغيل", "noSnippetsInFolder": "لا توجد مقتطفات في هذا المجلد", "uncategorized": "غير مصنف", - "editSnippetTitle": "تعديل المقتطف", - "editSnippetDescription": "قم بتحديث مقتطف الأمر هذا", + "editSnippetTitle": "تحرير المقتطف", + "editSnippetDescription": "تحديث مقتطف الأمر هذا", "saveSnippetButton": "حفظ التغييرات", "createSuccess": "تم إنشاء المقتطف بنجاح", "createFailed": "فشل إنشاء المقتطف", @@ -3066,7 +3217,7 @@ "folderCreateSuccess": "تم إنشاء المجلد بنجاح", "folderCreateFailed": "فشل إنشاء المجلد", "editFolderTitle": "تحرير المجلد", - "editFolderDescription": "أعد تسمية هذا المجلد أو غيّر مظهره", + "editFolderDescription": "إعادة تسمية أو تغيير مظهر هذا المجلد", "saveFolderButton": "حفظ التغييرات", "editFolder": "تحرير المجلد", "deleteFolder": "حذف المجلد", @@ -3074,137 +3225,182 @@ "folderDeleteFailed": "فشل حذف المجلد", "folderEditSuccess": "تم تحديث المجلد بنجاح", "folderEditFailed": "فشل تحديث المجلد", - "confirmRunMessage": "تشغيل \"{{name}}\"?", - "confirmRunButton": "يجري", - "runSuccess": "تم تشغيل \"{{name}}\" في {{count}} طرفية(ات)", + "confirmRunMessage": "تشغيل \"{{name}}\"؟", + "confirmRunButton": "تشغيل", + "runSuccess": "تم تشغيل \"{{name}}\" في {{count}} طرفية", "copySuccess": "تم نسخ \"{{name}}\" إلى الحافظة", "shareTitle": "مشاركة المقتطف", "shareUser": "مستخدم", "shareRole": "دور", "selectUser": "اختر مستخدمًا...", "selectRole": "اختر دورًا...", - "shareSuccess": "تمت مشاركة المقتطف بنجاح", - "shareFailed": "فشل في مشاركة المقتطف", - "revokeSuccess": "تم إلغاء الوصول", - "revokeFailed": "فشل في إلغاء الوصول", + "shareSuccess": "تمت مشاركة الجزء البرمجي بنجاح", + "shareFailed": "فشلت مشاركة الجزء البرمجي", + "revokeSuccess": "تم إبطال الوصول", + "revokeFailed": "فشل إبطال الوصول", "currentAccess": "الوصول الحالي", "shareLoadError": "فشل تحميل بيانات المشاركة", - "loading": "تحميل...", - "close": "يغلق", - "reorderFailed": "فشل حفظ ترتيب المقتطفات", + "loading": "جارٍ التحميل...", + "close": "إغلاق", + "reorderFailed": "فشل حفظ ترتيب الأجزاء البرمجية", "importExport": "استيراد / تصدير", "exportBtn": "تصدير JSON", - "importBtn": "استيراد بيانات JSON", - "exportSuccess": "تم تصدير المقتطفات بنجاح", - "exportFailed": "فشل تصدير المقتطفات", - "importTitle": "مقتطفات الاستيراد", - "importDescription": "استورد أجزاءً ومجلدات من ملف JSON تم تصديره بواسطة Termix", - "importDropOrClick": "أسقط ملف JSON هنا أو انقر للتصفح", - "importSelectedFile": "تم الاختيار: {{name}}", - "importOverwrite": "استبدل المقاطع البرمجية الموجودة بنفس الاسم والمجلد", - "importStartBtn": "يستورد", - "importSuccess": "اكتمل الاستيراد: تمت إضافة {{snippets}} مقطع برمجي، وتم تحديث {{updated}} ، وتم تخطي {{skipped}} ، وتمت إضافة {{folders}} مجلد/مجلدات", - "importFailed": "فشل استيراد المقتطفات", - "importInvalidFile": "ملف غير صالح: كان من المتوقع وجود كائن JSON يحتوي على مصفوفات من المقاطع أو المجلدات.", - "targetHostsLabel": "المضيفون المستهدفون", - "targetHostsHint": "قم بتعيين المضيفين لتشغيل هذا المقتطف البرمجي مباشرةً دون فتح نافذة طرفية.", - "noHostsAvailable": "لم يتم تكوين أي مضيفين", + "importBtn": "استيراد JSON", + "exportSuccess": "تم تصدير الأجزاء البرمجية بنجاح", + "exportFailed": "فشل تصدير الأجزاء البرمجية", + "importTitle": "استيراد الأجزاء البرمجية", + "importDescription": "استيراد الأجزاء البرمجية والمجلدات من ملف JSON تم تصديره بواسطة Termix", + "importDropOrClick": "أفلت ملف JSON هنا أو انقر للاستعراض", + "importSelectedFile": "المحدد: {{name}}", + "importOverwrite": "الكتابة فوق الأجزاء البرمجية الموجودة بنفس الاسم والمجلد", + "importStartBtn": "استيراد", + "importSuccess": "اكتمل الاستيراد: تمت إضافة {{snippets}} جزء برمجي، وتحديث {{updated}}، وتخطي {{skipped}}، وإضافة {{folders}} مجلد", + "importFailed": "فشل استيراد الأجزاء البرمجية", + "importInvalidFile": "ملف غير صالح: يُتوقع كائن JSON يحتوي على مصفوفات من الأجزاء البرمجية أو المجلدات", + "targetHostsLabel": "المضيفات المستهدفة", + "targetHostsHint": "تعيين مضيفات لتشغيل هذا الجزء البرمجي مباشرة بدون فتح طرفية.", + "noHostsAvailable": "لم يتم إعداد مضيفات", "clearTargetHosts": "مسح الكل", - "hasTargetHosts": "لديه مضيفون مستهدفون", - "runOnTargets": "ركض نحو الأهداف", - "directRunSuccess": "تم تشغيل \"{{name}}\" على {{count}} مضيف(مضيفين)", - "directRunPartialFail": "فشلت عملية \"{{name}}\" على مضيف واحد أو أكثر", + "hasTargetHosts": "لديه مضيفات مستهدفة", + "runOnTargets": "تشغيل على المضيفات", + "directRunSuccess": "تم تشغيل \"{{name}}\" على {{count}} مضيف", + "directRunPartialFail": "فشل \"{{name}}\" على واحد أو أكثر من المضيفات", "executionResultTitle": "نتائج التنفيذ: {{name}}", - "executionResultDescription": "مخرجات تشغيل المقتطف البرمجي على كل مضيف مستهدف.", + "executionResultDescription": "المخرجات الناتجة عن تشغيل المقتطف على كل مضيف مستهدف.", "executionSuccess": "نجاح", "executionFailed": "فشل" }, + "keybindings": { + "title": "اختصارات لوحة المفاتيح", + "description": "قم بتخصيص اختصارات النسخ واللصق والتحكم في الطرفية، أو اربط المفاتيح لإرسال نص أو تشغيل جزء من التعليمات البرمجية.", + "defaultsHeading": "اختصارات مدمجة", + "customHeading": "اختصارات مخصصة", + "addBinding": "إضافة اختصار", + "addBindingTitle": "إضافة اختصار", + "editBindingTitle": "تحرير الاختصار", + "loading": "تحميل...", + "noCustomBindings": "لا توجد اختصارات مخصصة حتى الآن", + "defaultBadge": "تقصير", + "customizedBadge": "مخصص", + "customize": "تخصيص", + "resetToDefault": "إعادة الضبط إلى الوضع الافتراضي", + "resetAllToDefaults": "إعادة ضبط جميع الإعدادات إلى الوضع الافتراضي", + "close": "يغلق", + "cancel": "يلغي", + "saveBinding": "حفظ الاختصار", + "saveError": "فشل حفظ اختصارات لوحة المفاتيح", + "pressKeysToRecord": "مجموعة المفاتيح", + "pressKeysPlaceholder": "انقر واضغط على المفاتيح", + "recording": "اضغط على المفاتيح...", + "comboRequiredError": "اضغط على مجموعة مفاتيح أولاً", + "textRequiredError": "أدخل النص المراد إرساله", + "controlCodeRequiredError": "أدخل حرفًا واحدًا لرمز التحكم", + "snippetRequiredError": "حدد جزءًا من التعليمات البرمجية", + "conflictWarning": "هذا المزيج مرتبط بالفعل بـ {{combo}}. سيؤدي الحفظ إلى إطلاق النار من كليهما.", + "actionLabel": "فعل", + "actionCopy": "تحديد النص", + "actionPaste": "لصق", + "actionSendControlCode": "إرسال إشارة Ctrl+حرف", + "actionSendText": "أرسل رسالة نصية", + "actionRunSnippet": "تشغيل المقتطف البرمجي الحالي", + "controlCodeLabel": "خطاب", + "textLabel": "نص لإرساله", + "appendEnterLabel": "اضغط على زر الإدخال بعد الإرسال", + "snippetLabel": "مقتطف", + "selectSnippetPlaceholder": "حدد جزءًا من التعليمات البرمجية", + "orphanedSnippetWarning": "لم يتم العثور على المقتطف", + "clipboardPermissionNote": "قد يطلب منك بعض المتصفحات منح إذن الوصول إلى الحافظة عند الاستخدام الأول." + }, "userProfile": { - "donateTitle": "حافظ على حياة Termix", - "donateDescription": "Termix مجاني، ويُستضاف ذاتيًا، وقد طُوّر بواسطة عدد قليل من الأشخاص في أوقات فراغهم. إذا وفّر لك الوقت أو المال، فإن التبرع بالعملات الرقمية يُساعد في استمراره.", - "donateMilestones": "تساهم التبرعات في تمويل الوقت اللازم للبحث والتعلم حول ما يلزم لبناء دعم SAML وKubernetes وAgent. تابعوا التقدم وتبرعوا.", + "donateTitle": "ضمان استمرارية Termix", + "donateDescription": "Termix مجاني، وذاتي الاستضافة، وقد أنشأه عدد قليل من الأشخاص في أوقات فراغهم. إذا وفر عليك الوقت أو المال، فإن التبرع بالعملات الرقمية يساعد في استمراريته.", + "donateMilestones": "التبرعات تساعد في تمويل الوقت للبحث والتعلم عما يلزم لبناء دعم SAML و Kubernetes و Agent. شاهد التقدم وتبرع.", "donateButton": "تبرع بالعملات الرقمية", "storageModeLocal": "المتصفح", "storageModeCloud": "قاعدة البيانات", - "storageModeDescription": "يخزن المتصفح الإعدادات في هذا المتصفح فقط. تتم مزامنة قاعدة البيانات مع الخادم ويتم تحميلها على أي جهاز.", - "resetToDefaults": "إعادة ضبط الإعدادات الافتراضية", - "resetToDefaultsSuccess": "تمت إعادة ضبط الإعدادات إلى الوضع الافتراضي.", + "storageModeDescription": "المتصفح يخزن الإعدادات في هذا المتصفح فقط. قاعدة البيانات تتم مزامنتها مع الخادم وتحميلها على أي جهاز.", + "resetToDefaults": "استعادة الإعدادات الافتراضية", + "resetToDefaultsSuccess": "تمت إعادة تعيين الإعدادات إلى الوضع الافتراضي.", "storageModeSwitch": "تخزين التفضيلات", - "sectionAccount": "حساب", - "sectionAppearance": "مظهر", - "sectionSecurity": "حماية", - "sectionApiKeys": "مفاتيح واجهة برمجة التطبيقات", + "sectionAccount": "الحساب", + "sectionAppearance": "المظهر", + "sectionSecurity": "الأمان", + "sectionApiKeys": "مفاتيح API", + "sectionData": "بيانات", "sectionC2sTunnels": "أنفاق C2S", "usernameLabel": "اسم المستخدم", - "roleLabel": "دور", - "roleAdministrator": "المسؤول", + "roleLabel": "الدور", + "roleAdministrator": "مسؤول", "authMethodLabel": "طريقة المصادقة", "authMethodLocal": "محلي", "twoFaLabel": "المصادقة الثنائية", - "twoFaOn": "على", - "twoFaOff": "عن", - "versionLabel": "إصدار", - "betaProgramTitle": "برنامج تجريبي", - "betaProgramDescription": "جرّب الميزات القادمة مبكراً باستخدام علامة Docker :beta الأسبوعية المتجددة. غير مستقر، غير مناسب للاستخدام في بيئات الإنتاج.", - "betaProgramFeedback": "هل وجدت خطأً برمجياً؟ أبلغ عنه هنا.", - "betaProgramLearnMore": "يتعلم أكثر", + "twoFaOn": "مفعلة", + "twoFaOff": "غير مفعلة", + "versionLabel": "الإصدار", + "betaProgramTitle": "برنامج بيتا", + "betaProgramDescription": "جرب الميزات القادمة مبكراً مع الوسم الأسبوعي المتجدد :beta لـ Docker. غير مستقر، وغير مخصص للإنتاج.", + "betaProgramFeedback": "وجدت خطأ؟ أبلغ عنه هنا.", + "betaProgramLearnMore": "معرفة المزيد", "deleteAccount": "حذف الحساب", - "deleteAccountDescription": "احذف حسابك نهائياً", - "changeServerDescription": "قم بالتبديل إلى خادم Termix خلفي مختلف", - "deleteButton": "يمسح", + "deleteAccountDescription": "حذف حسابك بشكل دائم", + "changeServerDescription": "التبديل إلى خادم Termix خلفي آخر", + "deleteButton": "حذف", "deleteAccountPermanent": "هذا الإجراء دائم ولا يمكن التراجع عنه.", "deleteAccountWarning": "سيتم حذف جميع الجلسات والمضيفين وبيانات الاعتماد والإعدادات بشكل دائم.", - "confirmPasswordDeletePlaceholder": "أدخل كلمة المرور الخاصة بك للتأكيد", - "languageLabel": "لغة", - "themeLabel": "سمة", + "confirmPasswordDeletePlaceholder": "أدخل كلمة المرور للتأكيد", + "languageLabel": "اللغة", + "themeLabel": "المظهر", "fontSizeLabel": "حجم الخط", - "accentColorLabel": "لون مميز", - "settingsTerminal": "صالة", - "commandAutocomplete": "إكمال الأوامر تلقائيًا", + "accentColorLabel": "لون التمييز", + "settingsTerminal": "الطرفية", + "commandAutocomplete": "الإكمال التلقائي للأوامر", "commandAutocompleteDesc": "إظهار الإكمال التلقائي أثناء الكتابة", - "terminalLinkBehavior": "انقر على رابط المحطة الطرفية", + "keyboardShortcuts": "اختصارات لوحة المفاتيح", + "keyboardShortcutsDescription": "تخصيص اختصارات النسخ واللصق والأوامر في الطرفية", + "manageShortcuts": "يدير", + "terminalLinkBehavior": "سلوك النقر على الروابط في الطرفية", "terminalLinkBehaviorDesc": "السلوك الافتراضي عند النقر على الروابط في الطرفية", - "historyTracking": "تتبع التاريخ", + "historyTracking": "تتبع السجل", "historyTrackingDesc": "تتبع أوامر الطرفية", "commandPalette": "لوحة الأوامر", - "commandPaletteDesc": "تفعيل اختصار لوحة المفاتيح", - "reopenTabsOnLogin": "أعد فتح علامات التبويب عند تسجيل الدخول", - "reopenTabsOnLoginDesc": "استعد علامات التبويب المفتوحة عند تسجيل الدخول أو تحديث الصفحة، حتى من جهاز آخر.", + "commandPaletteDesc": "تمكين اختصار لوحة المفاتيح", + "reopenTabsOnLogin": "إعادة فتح علامات التبويب عند تسجيل الدخول", + "reopenTabsOnLoginDesc": "استعادة علامات التبويب المفتوحة عند تسجيل الدخول أو تحديث الصفحة، حتى من جهاز آخر", "confirmTabClose": "تأكيد إغلاق علامة التبويب", - "confirmTabCloseDesc": "استأذن قبل إغلاق علامات تبويب الطرفية", + "confirmTabCloseDesc": "طلب التأكيد قبل إغلاق علامات تبويب الطرفية", "settingsSidebar": "الشريط الجانبي", - "showHostTags": "علامات مقدم البرنامج", - "showHostTagsDesc": "عرض العلامات في قائمة المضيفين", - "hostTrayOnClick": "انقر لتوسيع إجراءات المضيف", - "hostTrayOnClickDesc": "أظهر أزرار الاتصال دائمًا؛ انقر لتوسيع خيارات الإدارة بدلاً من التمرير فوقها", - "compactHostView": "عرض المضيف المصغر", - "compactHostViewDesc": "قم بطي كل مضيف إلى سطر واحد يعرض اسمه وعنوانه فقط", - "statusColors": "ألوان المكانة الحقيقية", - "statusColorsDesc": "استخدم اللون الأخضر/الأحمر للدلالة على حالة الاتصال بالإنترنت/عدم الاتصال بدلاً من لون التمييز", - "pinAppRail": "دبوس سكة التطبيق", - "pinAppRailDesc": "حافظ على شريط التطبيق الجانبي الأيسر مفتوحًا دائمًا بدلاً من توسيعه عند تمرير المؤشر فوقه.", - "expandAppRailOnHover": "قم بتوسيع شريط التطبيقات عند التمرير", - "expandAppRailOnHoverDesc": "اسمح لشريط التطبيق الجانبي الأيسر بالتمدد عند تحريك المؤشر فوقه", - "settingsNavigation": "ملاحة", - "navigationTabsDesc": "اختر علامات التبويب التي تظهر في شريط التطبيق", - "settingsSnippets": "مقتطفات", - "foldersCollapsed": "المجلدات مطوية", - "foldersCollapsedDesc": "يتم طي المجلدات افتراضيًا", + "showHostTags": "إظهار وسوم المضيفين", + "showHostTagsDesc": "عرض الوسوم في قائمة المضيفين", + "hostTrayOnClick": "النقر لتوسيع إجراءات المضيف", + "hostTrayOnClickDesc": "إظهار أزرار الاتصال دائمًا؛ النقر لتوسيع خيارات الإدارة بدلاً من التحويم", + "compactHostView": "عرض مضغوط للمضيفين", + "compactHostViewDesc": "طي كل مضيف إلى سطر واحد يعرض الاسم والعنوان فقط", + "statusColors": "ألوان الحالة الحقيقية", + "statusColorsDesc": "استخدام الأخضر/الأحمر لحالة الاتصال/عدم الاتصال بدلاً من لون التمييز", + "pinAppRail": "تثبيت شريط التطبيقات", + "pinAppRailDesc": "إبقاء شريط التطبيقات الأيسر موسعًا دائمًا بدلاً من التوسيع عند التحويم", + "expandAppRailOnHover": "توسيع شريط التطبيقات عند التحويم", + "expandAppRailOnHoverDesc": "السماح بتوسيع شريط التطبيقات الأيسر عند تحريك المؤشر فوقه", + "settingsNavigation": "التنقل", + "navigationTabsDesc": "اختيار علامات التبويب التي تظهر في شريط التطبيق", + "settingsSnippets": "المقتطفات", + "foldersCollapsed": "المجلدات المطوية", + "foldersCollapsedDesc": "طي المجلدات افتراضيًا", "confirmExecution": "تأكيد التنفيذ", - "confirmExecutionDesc": "تأكد قبل تشغيل المقاطع البرمجية", + "confirmExecutionDesc": "طلب التأكيد قبل تشغيل المقتطفات", "settingsUpdates": "التحديثات", - "disableUpdateChecks": "تعطيل عمليات التحقق من التحديثات", - "disableUpdateChecksDesc": "توقف عن التحقق من التحديثات", - "totpAuthenticator": "تطبيق مصادقة TOTP", - "totpEnabled": "تم تفعيل المصادقة الثنائية", - "totpDisabled": "أضف أمان تسجيل دخول إضافي", - "disable": "إبطال", - "enable": "يُمكَِن", - "setupTotp": "إعداد رمز التحقق من الهاتف (TOTP)", + "disableUpdateChecks": "تعطيل التحقق من التحديثات", + "disableUpdateChecksDesc": "إيقاف التحقق من التحديثات", + "totpAuthenticator": "مُوثِّق TOTP", + "totpEnabled": "التحقق بخطوتين ممكّن", + "totpDisabled": "إضافة أمان إضافي لتسجيل الدخول", + "disable": "تعطيل", + "enable": "تمكين", + "setupTotp": "إعداد TOTP", "qrCode": "رمز الاستجابة السريعة", - "totpInstructions": "امسح رمز الاستجابة السريعة أو أدخل كلمة المرور السرية في تطبيق المصادقة الخاص بك، ثم أدخل الرمز المكون من 6 أرقام", + "totpInstructions": "امسح رمز الاستجابة السريعة أو أدخل السر في تطبيق الموثق، ثم أدخل الرمز المكون من 6 أرقام", "totpCodePlaceholder": "000000", - "verify": "يؤكد", + "verify": "تحقق", "changePassword": "تغيير كلمة المرور", "currentPasswordLabel": "كلمة المرور الحالية", "currentPasswordPlaceholder": "كلمة المرور الحالية", @@ -3214,35 +3410,51 @@ "confirmPasswordPlaceholder": "تأكيد كلمة المرور الجديدة", "updatePassword": "تحديث كلمة المرور", "createApiKeyTitle": "إنشاء مفتاح API", - "createApiKeyDescription": "قم بإنشاء مفتاح API جديد للوصول البرمجي.", - "apiKeyNameLabel": "اسم", - "apiKeyNamePlaceholder": "مثال: خط أنابيب التكامل المستمر", - "expiryDateLabel": "تاريخ انتهاء الصلاحية", - "optional": "خياري", - "cancel": "يلغي", + "createApiKeyDescription": "توليد مفتاح API جديد للوصول البرمجي.", + "apiKeyNameLabel": "الاسم", + "apiKeyNamePlaceholder": "مثال: مسار CI", + "expiryDateLabel": "تاريخ الانتهاء", + "optional": "اختياري", + "cancel": "إلغاء", "createKey": "إنشاء مفتاح", - "apiKeyCount": "{{count}} مفاتيح", + "apiKeyCount": "{{count}} مفتاح", "newKey": "مفتاح جديد", - "noApiKeys": "لا توجد مفاتيح API حتى الآن.", - "apiKeyActive": "نشيط", - "apiKeyUsageHint": "ضع مفتاحك في", - "apiKeyUsageHintHeader": "العنوان.", + "noApiKeys": "لا توجد مفاتيح API بعد.", + "apiKeyActive": "نشط", + "apiKeyUsageHint": "قم بتضمين مفتاحك في", + "apiKeyUsageHintHeader": "رأس الطلب.", "apiKeyPermissionsHint": "ترث المفاتيح صلاحيات المستخدم المنشئ.", + "exportData": "تصدير بياناتي", + "exportDataDesc": "قم بتنزيل نسخة احتياطية من ملفات المضيفين وبيانات الاعتماد والإعدادات الخاصة بك لنقلها إلى جهاز آخر.", + "export": "يصدّر", + "exporting": "جارٍ التصدير...", + "importData": "استيراد بياناتي", + "importDataDesc": "استعد بيانات المضيفين وبيانات الاعتماد والإعدادات من ملف نسخ احتياطي بصيغة .sqlite", + "importDataSelected": "تم الاختيار: {{name}}", + "selectFile": "حدد ملفًا", + "changeFile": "يتغير", + "import": "يستورد", + "importing": "جارٍ الاستيراد...", + "exportSuccess": "تم تصدير البيانات بنجاح", + "exportFailed": "فشل تصدير البيانات", + "importSelectFile": "يرجى تحديد ملف أولاً", + "importCompleted": "اكتمل الاستيراد: تم استيراد {{total}} عنصرًا، وتم تخطي {{skipped}} عنصرًا", + "importFailed": "فشل الاستيراد: {{error}}", "roleUser": "مستخدم", - "authMethodDual": "المصادقة المزدوجة", + "authMethodDual": "مصادقة مزدوجة", "authMethodOidc": "OIDC", "totpSetupFailed": "فشل بدء إعداد TOTP", - "totpEnter6Digits": "أدخل رمزًا مكونًا من 6 أرقام", - "totpEnabledSuccess": "تم تفعيل المصادقة الثنائية", + "totpEnter6Digits": "أدخل رمزًا من 6 أرقام", + "totpEnabledSuccess": "تم تمكين المصادقة الثنائية", "totpInvalidCode": "رمز غير صالح، يرجى المحاولة مرة أخرى", - "totpDisableInputRequired": "أدخل رمز TOTP أو كلمة المرور الخاصة بك", + "totpDisableInputRequired": "أدخل رمز TOTP أو كلمة المرور", "totpDisabledSuccess": "تم تعطيل المصادقة الثنائية", "totpDisableFailed": "فشل تعطيل المصادقة الثنائية", "totpDisableTitle": "تعطيل المصادقة الثنائية", "totpDisablePlaceholder": "أدخل رمز TOTP أو كلمة المرور", "totpDisableConfirm": "تعطيل المصادقة الثنائية", - "totpContinueVerify": "تابع عملية التحقق", - "totpVerifyTitle": "رمز التحقق", + "totpContinueVerify": "متابعة للتحقق", + "totpVerifyTitle": "التحقق من الرمز", "totpBackupTitle": "رموز النسخ الاحتياطي", "totpDownloadBackup": "تنزيل رموز النسخ الاحتياطي", "passkeys": "مفاتيح المرور", @@ -3250,223 +3462,224 @@ "passkeyName": "اسم مفتاح المرور", "passkeyUvPreferred": "مفضل", "passkeyUvRequired": "مطلوب", - "passkeyUvDiscouraged": "محبط", - "addPasskey": "إضافة مفتاح المرور", - "noPasskeys": "لم يتم تسجيل أي مفاتيح مرور.", + "passkeyUvDiscouraged": "غير مشجع", + "addPasskey": "إضافة مفتاح مرور", + "noPasskeys": "لا توجد مفاتيح مرور مسجلة.", "passkeyAdded": "تمت إضافة مفتاح المرور", "passkeyAddFailed": "فشل إضافة مفتاح المرور", "passkeyDeleted": "تم حذف مفتاح المرور", "passkeyDeleteFailed": "فشل حذف مفتاح المرور", - "done": "منتهي", + "done": "تم", "secretCopied": "تم نسخ السر إلى الحافظة", "apiKeyNameRequired": "اسم المفتاح مطلوب", "apiKeyCreated": "تم إنشاء مفتاح API \"{{name}}\"", "apiKeyCreateFailed": "فشل إنشاء مفتاح API", - "apiKeyUser": "مستخدم", - "apiKeyExpires": "تاريخ الانتهاء", - "apiKeyRevoked": "تم إلغاء مفتاح API \"{{name}}\"", - "apiKeyRevokeFailed": "فشل إلغاء مفتاح API", - "passwordFieldsRequired": "يلزم إدخال كلمات المرور الحالية والجديدة", + "apiKeyUser": "المستخدم", + "apiKeyExpires": "ينتهي", + "apiKeyRevoked": "تم إبطال مفتاح API \"{{name}}\"", + "apiKeyRevokeFailed": "فشل إبطال مفتاح API", + "passwordFieldsRequired": "كلمة المرور الحالية والجديدة مطلوبة", "passwordMismatch": "كلمات المرور غير متطابقة", "passwordTooShort": "يجب أن تتكون كلمة المرور من 6 أحرف على الأقل", "passwordUpdated": "تم تحديث كلمة المرور بنجاح", "passwordUpdateFailed": "فشل تحديث كلمة المرور", - "deletePasswordRequired": "يلزم إدخال كلمة المرور لحذف حسابك", + "deletePasswordRequired": "كلمة المرور مطلوبة لحذف حسابك", "deleteFailed": "فشل حذف الحساب", "deleting": "جارٍ الحذف...", - "colorPickerTooltip": "افتح منتقي الألوان", - "themeSystem": "نظام", - "themeLight": "ضوء", - "themeDark": "مظلم", + "colorPickerTooltip": "فتح منتقي الألوان", + "themeSystem": "النظام", + "themeLight": "فاتح", + "themeDark": "داكن", "themeDracula": "دراكولا", - "themeCatppuccin": "كاتبوتشي", + "themeCatppuccin": "كاتبوسين", "themeNord": "نورد", - "themeSolarized": "تم تشميسها", - "themeTokyoNight": "ليلة طوكيو", - "themeOneDark": "ظلام واحد", - "themeGruvbox": "جروفوكس" + "themeSolarized": "سولارايزد", + "themeTokyoNight": "طوكيو نايت", + "themeOneDark": "ون دارك", + "themeGruvbox": "جروفبوكس" } } }, "tmuxMonitor": { - "title": "شاشة Tmux", - "failedToLoadHosts": "فشل تحميل المضيفين", + "title": "مراقب Tmux", + "failedToLoadHosts": "فشل تحميل الأجهزة المضيفة", "failedToLoad": "فشل تحميل جلسات tmux", - "tmuxUnavailable": "لم يتم تثبيت tmux على هذا المضيف", - "noSessions": "لا توجد جلسات tmux على هذا المضيف", - "noHostSelected": "لم يتم تحديد أي مضيف", - "attached": "مُرفَق", - "detached": "منفصل", - "attach": "أرفق", - "editTags": "تعديل الوسوم", - "tagsHint": "الكلمات المفتاحية المفصولة بفواصل (مثل: YOLO، lab، training)", + "tmuxUnavailable": "tmux غير مثبت على هذا الجهاز المضيف", + "noSessions": "لا توجد جلسات tmux على هذا الجهاز المضيف", + "noHostSelected": "لم يتم تحديد أي جهاز مضيف", + "attached": "متصلة", + "detached": "منفصلة", + "attach": "اتصال", + "editTags": "تعديل العلامات", + "tagsHint": "علامات مفصولة بفواصل (مثال: YOLO, lab, training)", "tagsSaved": "تم حفظ العلامات", "tagsSaveFailed": "فشل حفظ العلامات", - "searchPlaceholder": "نتائج البحث عبر الجلسات...", - "searchResults": "{{count}} يطابق", + "searchPlaceholder": "بحث في المخرجات عبر الجلسات...", + "searchResults": "{{count}} مطابقة", "searchFailed": "فشل البحث", - "selectPaneHint": "حدد جزءًا لمعاينة مخرجاته", + "selectPaneHint": "حدد إطارًا لمعاينة مخرجاته", "closePreview": "إغلاق المعاينة", "newSession": "جلسة جديدة", - "newSessionHint": "اسم الجلسة (أحرف، أرقام، _ @ % + = -)", - "newSessionPlaceholder": "جلستي", - "create": "يخلق", + "newSessionHint": "اسم الجلسة (حروف، أرقام، _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "إنشاء", "sessionCreated": "تم إنشاء الجلسة \"{{name}}\"", "sessionCreateFailed": "فشل إنشاء الجلسة", - "splitRight": "انقسم إلى اليمين", - "splitDown": "انقسام", - "splitFailed": "فشل تقسيم اللوحة", + "splitRight": "تقسيم لليمين", + "splitDown": "تقسيم للأسفل", + "splitFailed": "فشل تقسيم الإطار", "newWindow": "نافذة جديدة", "windowCreateFailed": "فشل إنشاء النافذة", - "attachSessionTooltip": "أرفق بـ {{session}}", - "refresh": "ينعش", + "attachSessionTooltip": "اتصل بـ {{session}}", + "refresh": "تحديث", "refreshFailed": "فشل تحديث الجلسات", "collapseAll": "طي الكل", - "expandAll": "عرض الكل", - "moreActions": "المزيد من الإجراءات", + "expandAll": "توسيع الكل", + "moreActions": "إجراءات أخرى", "sessionStats": "إحصائيات الجلسة", "renameSessionTitle": "إعادة تسمية الجلسة \"{{name}}\"", "rename": "إعادة تسمية", "sessionRenamed": "تمت إعادة تسمية الجلسة إلى \"{{name}}\"", "sessionRenameFailed": "فشل إعادة تسمية الجلسة", - "editTagsTitle": "قم بتعديل الوسوم لـ \"{{name}}\"", - "killPane": "نافذة القتل", - "resizeTree": "اسحب لتغيير الحجم — انقر نقراً مزدوجاً لإعادة الضبط", - "reattach": "إعادة الربط (يصلح العرض المشوش)", - "killWindow": "نافذة القتل", - "killWindowTitle": "هل تريد إنهاء نافذة {{index}} الخاصة بـ \"{{session}}\"؟", - "killWindowBody": "سيتم إنهاء جميع الأجزاء والعمليات في هذه النافذة. إنهاء النافذة الأخيرة ينهي الجلسة.", - "windowKillFailed": "فشل في إغلاق النافذة", + "editTagsTitle": "تعديل العلامات لـ \"{{name}}\"", + "killPane": "إنهاء الجزء", + "resizeTree": "اسحب لتغيير الحجم — انقر نقرًا مزدوجًا لإعادة الضبط", + "reattach": "إعادة الارتباط (لإصلاح العرض المشوش)", + "killWindow": "إنهاء النافذة", + "killWindowTitle": "هل تريد إنهاء النافذة {{index}} من الجلسة \"{{session}}\"؟", + "killWindowBody": "سيتم إنهاء كل جزء وعملية في هذه النافذة. إنهاء آخر نافذة ينهي الجلسة.", + "windowKillFailed": "فشل إنهاء النافذة", "statusActivity": "نشاط", - "statusWindows": "ويندوز", - "statusPanes": "ألواح", - "statusTags": "الوسوم", - "killPaneTitle": "نافذة الإيقاف {{id}}؟", - "killPaneBody": "سيتم إنهاء العملية الجارية في هذه النافذة. يؤدي إغلاق النافذة الأخيرة إلى إغلاق نافذتها.", - "paneKillFailed": "فشل في إغلاق النافذة", - "killSessionTitle": "إنهاء الجلسة \"{{name}}\"؟", - "killSessionBody": "سيتم إنهاء جميع النوافذ والعمليات الجارية في هذه الجلسة. لا يمكن التراجع عن هذا الإجراء.", - "kill": "قتل", + "statusWindows": "نوافذ", + "statusPanes": "أجزاء", + "statusTags": "وسوم", + "killPaneTitle": "هل تريد إنهاء الجزء {{id}}؟", + "killPaneBody": "سيتم إنهاء العملية الجارية في هذا الجزء. إنهاء آخر جزء يغلق نافذته.", + "paneKillFailed": "فشل إنهاء الجزء", + "killSessionTitle": "هل تريد إنهاء الجلسة \"{{name}}\"؟", + "killSessionBody": "سيتم إنهاء جميع النوافذ والعمليات الجارية في هذه الجلسة. لا يمكن التراجع عن ذلك.", + "kill": "إنهاء", "sessionKilled": "تم إنهاء الجلسة \"{{name}}\"", "sessionKillFailed": "فشل إنهاء الجلسة", - "hostUnreachable": "تعذر الاتصال بالخادم. تأكد من أنه متصل بالإنترنت ويمكن الوصول إليه.", + "hostUnreachable": "تعذر الاتصال بالمضيف. تأكد من أنه متصل وقابل للوصول.", "noServer": "لا يوجد خادم tmux يعمل على هذا المضيف.", - "searchTruncated": "نتائج جزئية - يغطي البحث آخر {{lines}} سطر من كل جزء وعلى الأكثر {{panes}} أجزاء.", + "searchTruncated": "نتائج جزئية — يشمل البحث آخر {{lines}} سطرًا من كل جزء وبحد أقصى {{panes}} جزءًا.", "closeSearchResults": "إغلاق نتائج البحث", "retry": "إعادة المحاولة", "noHosts": "لا توجد مضيفات SSH متاحة", - "noHostsHint": "قم بتمكين خيار مراقبة Tmux على مضيف SSH (مدير المضيف → علامة تبويب المحطة الطرفية) لمراقبة جلسات tmux الخاصة به.", - "tmuxInstallHint": "قم بتثبيته على الجهاز المضيف باستخدام:", - "attachTooltip": "افتح نافذة طرفية إلى {{host}}", - "attachTooltipPane": "افتح نافذة طرفية إلى {{host}} — جلسة tmux {{session}}", + "noHostsHint": "قم بتمكين خيار Tmux Monitor على مضيف SSH (مدير المضيفات → تبويب الطرفية) لمراقبة جلسات tmux الخاصة به.", + "tmuxInstallHint": "قم بتثبيته على المضيف باستخدام:", + "attachTooltip": "افتح طرفية إلى {{host}}", + "attachTooltipPane": "افتح طرفية إلى {{host}} — جلسة tmux {{session}}", "timeJustNow": "الآن", - "timeMinutes": "{{count}}منذ شهر", - "timeHours": "{{count}}منذ ساعة", - "timeDays": "{{count}}منذ يوم" + "timeMinutes": "قبل {{count}}m", + "timeHours": "قبل {{count}}h", + "timeDays": "قبل {{count}}d" }, "mobileKeyboard": { - "shift": "يحول", - "ctrl": "كنترول", - "esc": "خروج", - "tab": "فاتورة غير مدفوعة", + "shift": "Shift", + "ctrl": "Ctrl", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "السهم لأعلى", - "arrowDown": "السهم لأسفل", - "arrowLeft": "سهم إلى اليسار", - "arrowRight": "السهم إلى اليمين", - "home": "بيت", - "end": "نهاية", - "pageUp": "صفحة لأعلى", - "pageDown": "صفحة", - "delete": "ديل", - "editQuickKeys": "تحرير مفاتيح الاختصار", - "quickKeysTitle": "مفاتيح الاختصار", - "quickKeysDesc": "انقر على علامة × للإزالة. يدعم حتى 8 أحرف.", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "home": "Home", + "end": "End", + "pageUp": "PgUp", + "pageDown": "PgDn", + "delete": "Del", + "paste": "لصق", + "editQuickKeys": "تحرير المفاتيح السريعة", + "quickKeysTitle": "المفاتيح السريعة", + "quickKeysDesc": "اضغط × للإزالة. يدعم حتى 8 أحرف.", "quickKeyPlaceholder": "مثال: sudo ", - "addQuickKey": "يضيف", - "removeQuickKey": "يزيل", - "resetDefaults": "إعادة ضبط الإعدادات الافتراضية", - "done": "منتهي" + "addQuickKey": "إضافة", + "removeQuickKey": "إزالة", + "resetDefaults": "استعادة الإعدادات الافتراضية", + "done": "تم" }, "serial": { - "title": "مسلسل", - "portLabel": "ميناء", - "portPlaceholder": "/dev/ttyUSB0 أو COM3", + "title": "منفذ تسلسلي", + "portLabel": "المنفذ", + "portPlaceholder": "/dev/ttyUSB0 or COM3", "baudRateLabel": "معدل الباود", - "dataBitsLabel": "بيانات", - "stopBitsLabel": "قف", + "dataBitsLabel": "البيانات", + "stopBitsLabel": "التوقف", "parityLabel": "التكافؤ", - "parityNone": "لا أحد", - "parityEven": "حتى", - "parityOdd": "غريب", - "connect": "الاتصال بالمنفذ التسلسلي", + "parityNone": "بلا", + "parityEven": "زوجي", + "parityOdd": "فردي", + "connect": "اتصال", "disconnect": "قطع الاتصال", - "refreshPorts": "منافذ التحديث", + "refreshPorts": "تحديث المنافذ", "connected": "متصل بـ {{path}} بسرعة {{baud}} باود", - "disconnected": "تم فصل المنفذ التسلسلي", - "connectionError": "فشل فتح المنفذ التسلسلي", + "disconnected": "تم قطع الاتصال بالمنفذ التسلسلي", + "connectionError": "فشل في فتح المنفذ التسلسلي", "wsError": "خطأ في WebSocket", "errorNoServerUrl": "لم يتم تكوين عنوان URL للخادم", - "notSupportedTitle": "الرقم التسلسلي غير مدعوم", - "notSupported": "تتطلب الاتصالات التسلسلية متصفحًا يدعم واجهة برمجة تطبيقات الويب التسلسلية (Chrome أو Edge أو Firefox 151+)، أو تطبيق Termix لسطح المكتب.", - "hideHint": "يمكنك إخفاء علامة التبويب \"الرقم التسلسلي\" ضمن ملف تعريف المستخدم > المظهر > الشريط الجانبي > التنقل.", - "browserPickerHint": "انقر فوق \"اتصال\" وسيفتح متصفحك أداة اختيار المنفذ لاختيار الجهاز." + "notSupportedTitle": "المنفذ التسلسلي غير مدعوم", + "notSupported": "تتطلب الاتصالات التسلسلية متصفحًا يدعم Web Serial API (Chrome، أو Edge، أو Firefox 151+)، أو تطبيق Termix لسطح المكتب.", + "hideHint": "يمكنك إخفاء تبويب المنفذ التسلسلي من الملف الشخصي > المظهر > الشريط الجانبي > التنقل.", + "browserPickerHint": "انقر على Connect وسيفتح متصفحك نافذة اختيار المنفذ لتحديد الجهاز." }, "metricsHistory": { "title": "سجل المقاييس", - "historySuffix": "تاريخ", + "historySuffix": "السجل", "cpuMemoryDisk": "وحدة المعالجة المركزية / الذاكرة / القرص", - "network": "شبكة", - "download": "وصفة طبية", - "upload": "تكساس", - "noData": "لا تتوفر بيانات تاريخية لهذه الفترة الزمنية.", + "network": "الشبكة", + "download": "استقبال", + "upload": "إرسال", + "noData": "لا توجد بيانات تاريخية متاحة لهذا النطاق الزمني.", "custom": "مخصص", - "to": "ل", - "apply": "يتقدم", + "to": "إلى", + "apply": "تطبيق", "viewHistory": "عرض السجل" }, "alerts": { "tabFirings": "التنبيهات", - "tabRules": "قواعد", + "tabRules": "القواعد", "tabChannels": "القنوات", - "noFirings": "لا توجد تنبيهات غير مُعترف بها", - "noRules": "لم يتم تكوين أي قواعد تنبيه", - "noChannels": "لم يتم تكوين أي قنوات إشعارات", - "rulesDesc": "تؤدي قواعد التنبيه إلى إرسال الإشعارات", - "channelsDesc": "أين يتم إرسال إشعارات التنبيه", - "acknowledge": "يُقرّ", - "ackAll": "إقرار بالجميع", - "allAcknowledged": "تم تأكيد جميع التنبيهات", - "ackFailed": "فشل في تأكيد التنبيه", - "ackAllFailed": "فشل في تأكيد جميع التنبيهات", - "showAcknowledged": "عرض الكل", - "hideAcknowledged": "إخفاء القيد", + "noFirings": "لا توجد تنبيهات غير مُقَرَّة", + "noRules": "لم يتم تكوين قواعد تنبيه", + "noChannels": "لم يتم تكوين قنوات إشعار", + "rulesDesc": "تقوم قواعد التنبيه بتشغيل الإشعارات", + "channelsDesc": "مكان إرسال إشعارات التنبيه", + "acknowledge": "إقرار", + "ackAll": "إقرار الكل", + "allAcknowledged": "تم إقرار جميع التنبيهات", + "ackFailed": "فشل في إقرار التنبيه", + "ackAllFailed": "فشل في إقرار جميع التنبيهات", + "showAcknowledged": "إظهار الكل", + "hideAcknowledged": "إخفاء المُقَرَّة", "addChannel": "إضافة قناة", - "editChannel": "قناة التحرير", - "channelName": "اسم", - "channelType": "يكتب", + "editChannel": "تعديل القناة", + "channelName": "الاسم", + "channelType": "النوع", "channelNameRequired": "الاسم مطلوب", - "webhookUrl": "عنوان URL", - "webhookUrlRequired": "عنوان URL الخاص بـ Webhook مطلوب", - "webhookDesc": "أرسل حمولة JSON إلى هذا الرابط في كل تنبيه يتم إطلاقه", - "ntfyServer": "عنوان الخادم", - "ntfyTopic": "عنوان", + "webhookUrl": "URL", + "webhookUrlRequired": "رابط Webhook مطلوب", + "webhookDesc": "يُرسل حمولة JSON عبر طلب POST إلى عنوان URL هذا عند كل تنبيه", + "ntfyServer": "عنوان URL للخادم", + "ntfyTopic": "الموضوع", "ntfyTopicRequired": "الموضوع مطلوب", "ntfyToken": "رمز الوصول (اختياري)", "channelSaveFailed": "فشل حفظ القناة", - "test": "امتحان", + "test": "اختبار", "testSent": "تم إرسال إشعار الاختبار", "testFailed": "فشل إشعار الاختبار", "addRule": "إضافة قاعدة تنبيه", - "editRule": "تعديل قاعدة التنبيه", + "editRule": "تعديل قاعدة تنبيه", "ruleName": "اسم القاعدة", "ruleNameRequired": "الاسم مطلوب", - "triggerType": "مشغل", - "thresholdValue": "عتبة (٪)", - "durationSeconds": "المدة (بالثواني، 0 = إطلاق النار فوراً)", + "triggerType": "المشغّل", + "thresholdValue": "الحد (%)", + "durationSeconds": "المدة (بالثواني، 0 = إطلاق فوري)", "cooldownMinutes": "فترة التهدئة (بالدقائق)", "channels": "قنوات الإشعارات", - "noChannelsHint": "أضف القنوات في علامة تبويب القنوات أولاً", + "noChannelsHint": "أضف قنوات في تبويب القنوات أولاً", "ruleSaveFailed": "فشل حفظ القاعدة" } } diff --git a/src/ui/locales/translated/bg_BG.json b/src/ui/locales/translated/bg_BG.json index c1dd527c..de153062 100644 --- a/src/ui/locales/translated/bg_BG.json +++ b/src/ui/locales/translated/bg_BG.json @@ -389,6 +389,45 @@ "noSavedServers": "Няма запазени сървъри", "removeServer": "Премахване" }, + "migrationNotice": { + "title": "Termix Desktop вече работи самостоятелно", + "body1": "Това приложение се свързваше директно с вашия Termix сървър. То е преработено, за да работи напълно самостоятелно, съхранявайки хостове и идентификационни данни локално, така че да продължи да работи офлайн. Двупосочната синхронизация със самостоятелно хостван Termix сървър вече е по избор.", + "body2": "Вашите съществуващи хостове и идентификационни данни все още са на {{url}}. Включете отдалечената синхронизация и се свържете отново с този сървър, за да ги върнете и да поддържате синхронизирани занапред.", + "dismiss": "Не сега", + "setUpSync": "Настройване на отдалечена синхронизация" + }, + "remoteSync": { + "title": "Дистанционна синхронизация", + "description": "По желание свържете това настолно приложение към самостоятелно хостван Termix сървър, за да синхронизирате вашите хостове, идентификационни данни и фрагменти на различни устройства. Приложението винаги работи напълно офлайн, независимо дали се свързвате или не.", + "notConnected": "Няма връзка", + "connected": "Свързан", + "connectedTo": "Свързано с {{url}}", + "lastSynced": "Последна синхронизация {{time}}", + "neverSynced": "Никога не е синхронизирано", + "syncError": "Грешка при синхронизиране: {{message}}", + "needsReauth": "Входът е изтекъл", + "connectButton": "Свързване със сървъра", + "disconnectButton": "Изключване", + "syncNowButton": "Синхронизирай сега", + "syncing": "Синхронизиране...", + "serverUrl": "URL адрес на сървъра", + "enterServerUrl": "Моля, въведете URL адрес на сървъра", + "mustIncludeProtocol": "URL адресът на сървъра трябва да започва с http:// или https://", + "connectionTestFailed": "Не можа да се свърже със сървър на Termix на този URL адрес", + "allowInvalidCertificate": "Разрешаване на невалиден сертификат", + "allowInvalidCertificateDesc": "Използвайте само за надеждни самостоятелно хоствани сървъри със самоподписани или IP-адресни сертификати.", + "savedServers": "Запазени сървъри", + "removeServer": "Премахване", + "continueButton": "Продължи", + "cancelButton": "Отказ", + "signInTitle": "Влезте в {{url}}", + "originTitle": "Произход на връзката", + "originDescription": "Изберете откъде произлизат SSH връзките по подразбиране. Това може да се промени за всеки хост.", + "originLocal": "Това устройство (локална мрежа)", + "originRemote": "Отдалечен сървър", + "bannerReconnect": "Свържете се отново", + "bannerMessage": "Дистанционната синхронизация изисква повторно удостоверяване" + }, "versionCheck": { "error": "Грешка при проверка на версията", "checkFailed": "Проверката за актуализации не бе успешна", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Замяна на потребителско име за идентификационни данни", "overrideCredentialUsernameDesc": "Използвайте посоченото по-горе потребителско име вместо потребителското име от идентификационните данни", "oidcUsernameHint": "Използвайте $oidc.preferred_username, за да заместите вашето OIDC потребителско име.", + "tailscaleUsernameHint": "Това трябва да е Unix потребител, чиято самоличност на Tailscale е предоставена в SSH ACL на tailnet, не е задължително root.", "jumpHostChain": "Верига за прескачане на хост", "portKnocking": "Порт Нокинг", "addKnock": "Добавяне на порт", @@ -650,6 +690,11 @@ "delayAfterMs": "Закъснение след (ms)", "useSocks5Proxy": "Използвайте SOCKS5 прокси", "useSocks5ProxyDesc": "Маршрутизиране на връзката през прокси сървър", + "connectionOrigin": "Произход на връзката", + "connectionOriginDesc": "Откъде произлиза SSH връзката на този хост. Заменя глобалната настройка по подразбиране на настолното приложение.", + "connectionOriginDefault": "Използвай по подразбиране", + "connectionOriginLocal": "Това устройство (локална мрежа)", + "connectionOriginRemote": "Отдалечен сървър", "proxyHost": "Прокси хост", "proxyPort": "Прокси порт", "proxyUsername": "Потребителско име на прокси", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Ярко циан", "customThemeBrightWhite": "Ярко бяло", "customThemeResetTooltip": "Възстановяване на фабричните настройки", + "savedThemesLabel": "Запазени теми", + "saveAsGlobalTheme": "Запазване като глобална тема", + "saveGlobalThemeNamePrompt": "Въведете име за тази тема", + "saveGlobalThemeSuccess": "Темата е запазена", + "saveGlobalThemeError": "Запазването на темата не бе успешно", + "applyGlobalThemeTooltip": "Приложете тази тема", + "deleteGlobalThemeTooltip": "Изтрий тази тема", + "noSavedThemes": "Все още няма запазени теми", "syntaxHighlightingLabel": "Подчертаване на синтаксиса", "syntaxHighlightingDesc": "Оцветяване на изхода на терминала (грешки, пътища, IP адреси, времеви отпечатъци)", "syntaxHighlightingCategories": "Маркирайте категориите", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Автоматично стартиране или прикачване към tmux сесия", "enableSessionLogging": "Записване на сесии", "enableSessionLoggingDesc": "Записване на изхода от терминалната сесия за по-късен преглед", + "allowSessionSharing": "Разрешаване на споделяне на сесия", + "allowSessionSharingDesc": "Позволете споделянето на сесиите на живо на този хост чрез линк или с други потребители", "enableCommandHistory": "История на командите", "enableCommandHistoryDesc": "Запис на команди, изпълнявани в този терминал, за история и автоматично довършване", "linkClickBehaviorLabel": "Поведение при кликване върху връзки", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} импортирано", "proxmoxResultUpdated": "{{count}} актуализирано", "proxmoxResultFailed": "{{count}} неуспешно", + "proxmoxResultSkippedNoIp": "{{count}} пропуснато (не е намерен IP адрес)", "proxmoxImportComplete": "Импортирането на Proxmox е завършено: {{summary}}", "proxmoxDiscoveryFailed": "Откриването не беше успешно", "proxmoxImportFailed": "Импортирането не бе успешно", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Запазването на идентификационните данни не бе успешно", "credentialNameRequired": "Моля, въведете име за идентификационните данни", "credentialAuthRequired": "Добавете парола, SSH ключ или и двете", + "createCredentialFromHostBtn": "Създаване на идентификационни данни", + "createCredentialFromHostTitle": "Създаване на идентификационни данни от хост", + "createCredentialFromHostDesc": "Създайте запис за многократна употреба и споделяне на идентификационни данни, предварително попълнен с текущото потребителско име, парола и/или SSH ключ на този хост.", "backToHosts": "Обратно към Домакини", "backToCredentials": "Обратно към удостоверенията", "pinned": "Закачено", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Използвайте / за разделяне на нивата и създаване на вложени папки.", "folderColor": "Цвят", "folderIcon": "Икона", + "folderCredential": "Пълномощия", + "folderCredentialNone": "Няма присвоени идентификационни данни", + "folderCredentialHint": "Хостовете в тази папка, които използват удостоверяване „Съхранени идентификационни данни“, без да са избрани собствени идентификационни данни, ще наследят тези.", "folderPreview": "Преглед", "folderNameFallback": "Неозаглавена папка", "createFolderButton": "Създаване на папка", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Етикети", "shareHost": "Споделяне на хост", "shareHostTitle": "Споделяне: {{name}}", + "shareFolder": "Споделяне на папка", + "shareFolderTitle": "Споделяне на папка: {{name}}", + "folderSharedSuccessfully": "Споделени {{count}} хост(ове) в папка", + "failedToShareFolder": "Споделянето на папката не бе успешно", "sharing": { "loadError": "Зареждането на данните за споделяне не бе успешно. Моля, опитайте отново.", "shareWithSection": "Сподели с", @@ -1221,6 +1287,7 @@ "shareWithCount": "Споделяне ({{count}})", "currentAccess": "Текущ достъп", "noAccessEntries": "Този хост все още не е споделен", + "folderShareSummary": "Споделени {{shared}} от {{total}} хост(ове) в тази папка", "grantedBy": "Предоставено от", "expires": "Изтича", "expired": "Изтекъл", @@ -1242,6 +1309,8 @@ "authMethod": "Метод за удостоверяване", "authTypeDirect": "Директно", "authTypeCredential": "Пълномощия", + "authTypeNone": "Няма", + "authTypeNoneDesc": "Не се съхраняват идентификационни данни. Ще бъдете подканени да въведете потребителско име и парола всеки път, когато се свържете; те не се запазват.", "selectCredential": "Изберете идентификационен номер...", "connectionSettings": "Настройки за връзка", "displaySettings": "Настройки на дисплея", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP порт", "username": "Потребителско име", "password": "Парола", + "passwordSaved": "Паролата е запазена, въведете, за да я промените", "domain": "Домейн", "securityMode": "Режим на сигурност", "colorDepth": "Дълбочина на цвета", @@ -1424,7 +1494,60 @@ "expiresIn": "Изтича след {{duration}}", "search": "Търсене на връзки...", "noSearchResults": "Няма връзки, които да отговарят на търсенето ви", - "rename": "Преименуване на сесия" + "rename": "Преименуване на сесия", + "sectionSharedWithMe": "Споделено с мен", + "sharedBy": "Споделено от {{username}}", + "join": "Присъединете се", + "sharedSessionLabel": "{{hostName}} (споделено)" + }, + "sessionSharing": { + "guestView": { + "loading": "Свързване със споделена сесия...", + "linkInvalid": "Този линк за споделяне е невалиден, изтекъл или е отменен", + "rateLimited": "Твърде много опити, моля опитайте отново скоро", + "sessionEnded": "Тази сесия приключи", + "readOnlyBadge": "Само преглед" + }, + "modalTitle": "Споделяне на сесия", + "shareButton": "Споделяне", + "notReadyToShare": "Сесията все още не е готова за споделяне", + "modeTab": { + "link": "Връзка", + "user": "Потребител" + }, + "linkModeDescription": "Всеки с този линк може да се присъедини, не е необходим акаунт.", + "userModeDescription": "Споделете с конкретен потребител, който вече има достъп до този хост. Ако все още няма достъп, първо споделете хоста с него или използвайте връзка. След като бъде споделена, сесията се показва в раздела му „Връзки“.", + "permissionLevel": { + "label": "Ниво на разрешение", + "readOnly": "Само за четене", + "readOnlyDescription": "Може да гледа сесията на живо, но не може да пише или да взаимодейства.", + "readWrite": "Четене и запис", + "readWriteDescription": "Може да пише и да взаимодейства със сесията точно като собственика." + }, + "expiryLabel": "Изтичане на срока на валидност на връзката", + "createLinkButton": "Създаване на връзка", + "createShareButton": "Споделяне с потребителя", + "searchUsersPlaceholder": "Търсене на потребители...", + "noUsersFound": "Не са намерени потребители", + "linkCreated": "Линкът за споделяне е създаден", + "linkCopied": "Връзката е копирана в буферната памет", + "copyLink": "Копиране на връзката", + "shareCreated": "Сесията е споделена. Тя ще се покаже в раздела им „Връзки“.", + "shareFailed": "Създаването на споделено съдържание не бе успешно", + "userLacksHostAccess": "Тозият потребител все още няма достъп до този хост. Първо споделете хоста с него или използвайте линк.", + "activeShares": "Активни акции", + "noActiveShares": "Няма активни споделяния за тази сесия", + "revoke": "Отмяна", + "revokeConfirmTitle": "Да се отмени ли това споделяне?", + "revokeConfirmDescription": "Всеки, който използва това споделяне, ще загуби достъп незабавно.", + "revoked": "Акцията е отменена", + "revokeFailed": "Неуспешно отменяне на споделянето", + "joinCount": "{{count}} присъединяване", + "joinCount_other": "{{count}} се присъединява", + "expiresAt": "Изтича {{date}}", + "linkShareBadge": "Връзка", + "userShareBadge": "Потребител: {{username}}", + "loadSharesFailed": "Зареждането на активните споделяния не бе успешно" }, "guacamole": { "connecting": "Свързване към сесия {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Свържете се отново", "retry": "Опитай отново", "guacdUnavailable": "Услугата за отдалечен работен плот (guacd) не е налична. Моля, уверете се, че guacd работи, е достъпна и е конфигурирана правилно в администраторските настройки.", + "credentialPromptTitle": "Въведете RDP идентификационни данни", + "credentialPromptDescription": "Този хост е настроен да изисква идентификационни данни при свързване. Те се използват само за тази сесия и не се запазват.", + "connect": "Свържете се", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Сесия за повторно свързване", "collapse": "Свиване на лентата с инструменти", "expand": "Разгъване на лентата с инструменти", - "dragHandle": "Плъзнете, за да препозиционирате" + "dragHandle": "Плъзнете, за да препозиционирате", + "switchToTrackpad": "Превключване в режим на тракпад (плъзнете, за да преместите курсора, докоснете, за да щракнете)", + "switchToTouch": "Превключване към режим на докосване (докоснете директно там, където искате да щракнете)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Изисква се двуфакторно удостоверяване", "totpCodeLabel": "Код за потвърждение", "totpVerify": "Проверка", + "mfaPromptRequired": "Изисква се удостоверяване", + "mfaPushRequired": "Изисква се удостоверяване чрез push", + "mfaMenuPlaceholder": "Въведете отговора си", + "mfaWaitingApproval": "Чака се одобрение на вашето устройство...", + "mfaSendRequest": "Изпрати заявка", "warpgateAuthRequired": "Изисква се удостоверяване на Warpgate", "warpgateSecurityKey": "Ключ за сигурност", "warpgateAuthUrl": "URL адрес за удостоверяване", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Ограничена скорост: Твърде много опити за потвърждение. Моля, опитайте отново по-късно.", "resetCodeRateLimitedWithTime": "Ограничена скорост: Твърде много опити за потвърждение. Моля, изчакайте {{time}} секунди, преди да опитате отново.", "authTokenSaveFailed": "Запазването на токена за удостоверяване не бе успешно", - "failedToLoadServer": "Зареждането на сървъра не бе успешно" + "failedToLoadServer": "Зареждането на сървъра не бе успешно", + "remoteServerRequired": "Изисква се отдалечен сървър. Свържете се с отдалечен сървър в „Настройки“, за да използвате този тип връзка." }, "messages": { "registrationDisabled": "Регистрацията на нов акаунт в момента е деактивирана от администратор. Моля, влезте или се свържете с администратор.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Запазването на настройките за SSL не бе успешно", "sslRequiresDomain": "Домейнът и имейлът са задължителни", "sslInfoNote": "След издаване на сертификат, активирайте SSL в променливите на средата (ENABLE_SSL=true) и рестартирайте Termix.", + "sslManualOption": "Ръководство (сертификат за качване)", + "sslManualCert": "Сертификат (PEM)", + "sslManualCertPlaceholder": "-----ЗАПОЧВАНЕ НА СЕРТИФИКАТ-----", + "sslManualKey": "Частен ключ (PEM)", + "sslManualKeyPlaceholder": "-----ЗАПОЧВАНЕ НА ЛИЧЕН КЛЮЧ-----", + "sslManualDesc": "Поставете съществуващия си сертификат и частен ключ, включително пълната верига, ако се изисква от вашия CA.", + "sslManualUpload": "Сертификат за качване и инсталиране", + "sslManualUploadLoading": "Качване на сертификат...", + "sslManualUploadSuccess": "Сертификатът е качен и инсталиран успешно", + "sslManualUploadFailed": "Качването на сертификата не бе успешно", + "sslManualRequiresFields": "Изискват се сертификат и частен ключ", "auditLogTotal": "{{total}} общо записи", "auditLogEmpty": "Няма намерени записи в журнала за одит", "auditLogSuccess": "Успех", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "История на командите", "commandHistoryEnabledDesc": "Разрешаване на записване на историята на командите. Когато е деактивирано, историята не се запазва, независимо от настройките за всеки хост.", "updateCommandHistoryFailed": "Актуализирането на настройката за историята на командите не бе успешно", + "analyticsEnabled": "Споделяне на анонимни статистически данни за употреба", + "analyticsEnabledDesc": "Изпраща анонимен дневен брой потребители, хостове и използване на функции, за да помогне за подобряването на Termix. Никога не се включват лични данни или подробности за връзката.", + "updateAnalyticsFailed": "Актуализирането на настройката за анализ не бе успешно", + "sessionSharingGloballyEnabled": "Разрешаване на споделяне на сесия", + "sessionSharingGloballyEnabledDesc": "Разрешава споделянето на сесии на живо на терминал, RDP, VNC и Telnet в целия екземпляр. Заменя всяко превключвател за споделяне на хост, когато е деактивиран.", + "updateSessionSharingFailed": "Актуализирането на настройката за споделяне на сесията не бе успешно", "sessionTimeout": "Време за изчакване на сесията", "hours": "часове", "sessionTimeoutRange": "Мин. 1 ч. · Макс. 720 ч.", @@ -3119,6 +3270,47 @@ "executionSuccess": "Успех", "executionFailed": "Неуспешно" }, + "keybindings": { + "title": "Клавишни комбинации", + "description": "Персонализирайте клавишните комбинации за копиране, поставяне и управление на терминала или обвържете клавишите за изпращане на текст или изпълнение на фрагмент.", + "defaultsHeading": "Вградени преки пътища", + "customHeading": "Персонализирани преки пътища", + "addBinding": "Добавяне на пряк път", + "addBindingTitle": "Добавяне на пряк път", + "editBindingTitle": "Редактиране на пряк път", + "loading": "Зареждане...", + "noCustomBindings": "Все още няма персонализирани преки пътища", + "defaultBadge": "По подразбиране", + "customizedBadge": "Персонализирано", + "customize": "Персонализиране", + "resetToDefault": "Възстановяване на фабричните настройки", + "resetAllToDefaults": "Възстановяване на всички настройки по подразбиране", + "close": "Затвори", + "cancel": "Отказ", + "saveBinding": "Запазване на пряк път", + "saveError": "Запазването на клавишните комбинации не бе успешно", + "pressKeysToRecord": "Комбинация от клавиши", + "pressKeysPlaceholder": "Щракнете и натиснете клавиши", + "recording": "Натиснете клавишите...", + "comboRequiredError": "Първо натиснете клавишна комбинация", + "textRequiredError": "Въведете текста за изпращане", + "controlCodeRequiredError": "Въведете една буква за контролния код", + "snippetRequiredError": "Изберете фрагмент", + "conflictWarning": "Тази комбинация вече е свързана с {{combo}}. Запазването ще доведе до задействане и на двете.", + "actionLabel": "Действие", + "actionCopy": "Копиране на селекцията", + "actionPaste": "Паста", + "actionSendControlCode": "Изпрати сигнал Ctrl+буква", + "actionSendText": "Изпращане на текст", + "actionRunSnippet": "Изпълнете съществуващия фрагмент", + "controlCodeLabel": "Писмо", + "textLabel": "Текст за изпращане", + "appendEnterLabel": "Натиснете Enter след изпращане", + "snippetLabel": "Фрагмент", + "selectSnippetPlaceholder": "Изберете фрагмент", + "orphanedSnippetWarning": "фрагмент не е намерен", + "clipboardPermissionNote": "Може да поиска разрешение за достъп до клипборда при първото използване в някои браузъри." + }, "userProfile": { "donateTitle": "Пази Термикс жив", "donateDescription": "Termix е безплатен, самостоятелно хостван и изграден само от няколко души в свободното им време. Ако ви е спестил време или пари, дарението от крипто помага да продължи да функционира.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Външен вид", "sectionSecurity": "Сигурност", "sectionApiKeys": "API ключове", + "sectionData": "Данни", "sectionC2sTunnels": "Тунели C2S", "usernameLabel": "Потребителско име", "roleLabel": "Роля", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Терминал", "commandAutocomplete": "Автоматично довършване на команди", "commandAutocompleteDesc": "Показване на автоматично довършване при въвеждане", + "keyboardShortcuts": "Клавишни комбинации", + "keyboardShortcutsDescription": "Персонализиране на клавишните комбинации за копиране, поставяне и команди в терминала", + "manageShortcuts": "Управление", "terminalLinkBehavior": "Щракване върху връзката на терминала", "terminalLinkBehaviorDesc": "Поведение по подразбиране при кликване върху връзки в терминала", "historyTracking": "Проследяване на историята", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Включете ключа си в", "apiKeyUsageHintHeader": "заглавка.", "apiKeyPermissionsHint": "Ключовете наследяват разрешенията на потребителя, който ги е създал.", + "exportData": "Експортиране на моите данни", + "exportDataDesc": "Изтеглете резервно копие на вашите хостове, идентификационни данни и настройки, за да ги прехвърлите на друго устройство", + "export": "Експорт", + "exporting": "Експортиране...", + "importData": "Импортиране на моите данни", + "importDataDesc": "Възстановете вашите хостове, идентификационни данни и настройки от .sqlite архивен файл", + "importDataSelected": "Избрано: {{name}}", + "selectFile": "Изберете файл", + "changeFile": "Промяна", + "import": "Внос", + "importing": "Импортиране...", + "exportSuccess": "Данните са експортирани успешно", + "exportFailed": "Експортирането на данни не бе успешно", + "importSelectFile": "Моля, първо изберете файл", + "importCompleted": "Импортирането е завършено: {{total}} елемента са импортирани, {{skipped}} са пропуснати", + "importFailed": "Импортирането не бе успешно: {{error}}", "roleUser": "Потребител", "authMethodDual": "Двойно удостоверяване", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Дел", + "paste": "Паста", "editQuickKeys": "Редактиране на бързи клавиши", "quickKeysTitle": "Бързи клавиши", "quickKeysDesc": "Докоснете × за премахване. Поддържа до 8 знака.", diff --git a/src/ui/locales/translated/bn_BD.json b/src/ui/locales/translated/bn_BD.json index b28b62b8..84678a37 100644 --- a/src/ui/locales/translated/bn_BD.json +++ b/src/ui/locales/translated/bn_BD.json @@ -389,6 +389,45 @@ "noSavedServers": "কোনো সংরক্ষিত সার্ভার নেই", "removeServer": "অপসারণ করুন" }, + "migrationNotice": { + "title": "টার্মিক্স ডেস্কটপ এখন স্বতন্ত্রভাবে চলে।", + "body1": "এই অ্যাপটি আগে সরাসরি আপনার টারমিক্স সার্ভারের সাথে সংযুক্ত হতো। এটিকে এখন সম্পূর্ণ স্বাধীনভাবে চলার জন্য নতুন করে তৈরি করা হয়েছে, যা হোস্ট এবং ক্রেডেনশিয়াল স্থানীয়ভাবে সংরক্ষণ করে, ফলে এটি অফলাইনেও কাজ করতে থাকে। সেলফ-হোস্টেড টারমিক্স সার্ভারের সাথে দ্বিমুখী সিঙ্ক এখন ঐচ্ছিক।", + "body2": "আপনার বর্তমান হোস্ট এবং ক্রেডেনশিয়াল এখনও {{url}}-এ রয়েছে। সেগুলোকে ফিরিয়ে আনতে এবং ভবিষ্যতে উভয়কে সিঙ্কে রাখতে রিমোট সিঙ্ক চালু করুন এবং সেই সার্ভারে পুনরায় সংযোগ করুন।", + "dismiss": "এখন নয়", + "setUpSync": "রিমোট সিঙ্ক সেট আপ করুন" + }, + "remoteSync": { + "title": "রিমোট সিঙ্ক", + "description": "ঐচ্ছিকভাবে, আপনার হোস্ট, ক্রেডেনশিয়াল এবং স্নিপেটগুলো বিভিন্ন ডিভাইসে সিঙ্ক করার জন্য এই ডেস্কটপ অ্যাপটিকে একটি সেলফ-হোস্টেড টারমিক্স সার্ভারের সাথে সংযুক্ত করুন। আপনি সংযুক্ত করুন বা না করুন, অ্যাপটি সর্বদা সম্পূর্ণ অফলাইনে কাজ করে।", + "notConnected": "সংযুক্ত নয়", + "connected": "সংযুক্ত", + "connectedTo": "{{url}} এর সাথে সংযুক্ত", + "lastSynced": "সর্বশেষ সিঙ্ক করা হয়েছে {{time}}", + "neverSynced": "কখনো সিঙ্ক করা হয়নি", + "syncError": "সিঙ্ক ত্রুটি: {{message}}", + "needsReauth": "সাইন-ইন এর মেয়াদ শেষ হয়ে গেছে", + "connectButton": "সার্ভারে সংযোগ করুন", + "disconnectButton": "সংযোগ বিচ্ছিন্ন করুন", + "syncNowButton": "এখন সিঙ্ক করুন", + "syncing": "সিঙ্ক হচ্ছে...", + "serverUrl": "সার্ভার ইউআরএল", + "enterServerUrl": "অনুগ্রহ করে একটি সার্ভার URL লিখুন", + "mustIncludeProtocol": "সার্ভার URL অবশ্যই http:// অথবা https:// দিয়ে শুরু হতে হবে।", + "connectionTestFailed": "ঐ URL-এ কোনো Termix সার্ভারে পৌঁছানো যায়নি।", + "allowInvalidCertificate": "অবৈধ সার্টিফিকেট অনুমোদন করুন", + "allowInvalidCertificateDesc": "শুধুমাত্র সেলফ-সাইন্ড বা আইপি-অ্যাড্রেস সার্টিফিকেটযুক্ত বিশ্বস্ত সেলফ-হোস্টেড সার্ভারের জন্য ব্যবহার করুন।", + "savedServers": "সংরক্ষিত সার্ভার", + "removeServer": "অপসারণ করুন", + "continueButton": "চালিয়ে যান", + "cancelButton": "বাতিল করুন", + "signInTitle": "{{url}} এ সাইন ইন করুন", + "originTitle": "সংযোগের উৎস", + "originDescription": "ডিফল্টরূপে SSH সংযোগগুলি কোথা থেকে শুরু হবে তা বেছে নিন। এটি প্রতিটি হোস্টের জন্য আলাদাভাবে পরিবর্তন করা যেতে পারে।", + "originLocal": "এই ডিভাইস (স্থানীয় নেটওয়ার্ক)", + "originRemote": "রিমোট সার্ভার", + "bannerReconnect": "পুনরায় সংযোগ করুন", + "bannerMessage": "রিমোট সিঙ্কের জন্য পুনরায় প্রমাণীকরণ প্রয়োজন।" + }, "versionCheck": { "error": "সংস্করণ যাচাই ত্রুটি", "checkFailed": "আপডেট পরীক্ষা করতে ব্যর্থ হয়েছে", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "ক্রেডেনশিয়াল ব্যবহারকারীর নাম ওভাররাইড করুন", "overrideCredentialUsernameDesc": "ক্রেডেনশিয়ালের ইউজারনেমের পরিবর্তে উপরে উল্লেখিত ইউজারনেমটি ব্যবহার করুন।", "oidcUsernameHint": "আপনার OIDC লগইন নামের পরিবর্তে $oidc.preferred_username ব্যবহার করুন।", + "tailscaleUsernameHint": "এটি অবশ্যই একজন ইউনিক্স ব্যবহারকারী হতে হবে, যার মাধ্যমে আপনার টেইলনেট-এর SSH ACL-এ আপনার টেইলস্কেল পরিচয়কে অনুমতি দেওয়া হয়; এটি রুট হওয়া আবশ্যক নয়।", "jumpHostChain": "জাম্প হোস্ট চেইন", "portKnocking": "পোর্ট নকিং", "addKnock": "পোর্ট যোগ করুন", @@ -650,6 +690,11 @@ "delayAfterMs": "বিলম্বের পর (মিলিসেকেন্ড)", "useSocks5Proxy": "SOCKS5 প্রক্সি ব্যবহার করুন", "useSocks5ProxyDesc": "প্রক্সি সার্ভারের মাধ্যমে সংযোগ স্থাপন করুন", + "connectionOrigin": "সংযোগের উৎস", + "connectionOriginDesc": "এই হোস্টের SSH সংযোগটি কোথা থেকে শুরু হয়। এটি ডেস্কটপ অ্যাপের গ্লোবাল ডিফল্টকে ওভাররাইড করে।", + "connectionOriginDefault": "ডিফল্ট ব্যবহার করুন", + "connectionOriginLocal": "এই ডিভাইস (স্থানীয় নেটওয়ার্ক)", + "connectionOriginRemote": "রিমোট সার্ভার", "proxyHost": "প্রক্সি হোস্ট", "proxyPort": "প্রক্সি পোর্ট", "proxyUsername": "প্রক্সি ব্যবহারকারীর নাম", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "উজ্জ্বল সায়ান", "customThemeBrightWhite": "উজ্জ্বল সাদা", "customThemeResetTooltip": "ডিফল্টে রিসেট করুন", + "savedThemesLabel": "সংরক্ষিত থিম", + "saveAsGlobalTheme": "গ্লোবাল থিম হিসেবে সংরক্ষণ করুন", + "saveGlobalThemeNamePrompt": "এই থিমের জন্য একটি নাম লিখুন", + "saveGlobalThemeSuccess": "থিম সংরক্ষিত", + "saveGlobalThemeError": "থিম সংরক্ষণ করতে ব্যর্থ হয়েছে", + "applyGlobalThemeTooltip": "এই থিমটি প্রয়োগ করুন", + "deleteGlobalThemeTooltip": "এই থিমটি মুছে ফেলুন", + "noSavedThemes": "এখনো কোনো থিম সংরক্ষণ করা হয়নি", "syntaxHighlightingLabel": "সিনট্যাক্স হাইলাইটিং", "syntaxHighlightingDesc": "টার্মিনাল আউটপুট রঙিন করুন (ত্রুটি, পাথ, আইপি, টাইমস্ট্যাম্প)", "syntaxHighlightingCategories": "হাইলাইট বিভাগ", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "স্বয়ংক্রিয়ভাবে tmux সেশন চালু করুন বা সংযুক্ত করুন", "enableSessionLogging": "সেশন লগিং", "enableSessionLoggingDesc": "পরবর্তী পর্যালোচনার জন্য টার্মিনাল সেশনের আউটপুট রেকর্ড করুন।", + "allowSessionSharing": "সেশন শেয়ারিং অনুমোদন করুন", + "allowSessionSharingDesc": "এই হোস্টের লাইভ সেশনগুলো লিঙ্কের মাধ্যমে বা অন্যান্য ব্যবহারকারীদের সাথে শেয়ার করা যাবে।", "enableCommandHistory": "কমান্ডের ইতিহাস", "enableCommandHistoryDesc": "ইতিহাস এবং স্বয়ংক্রিয় সম্পূর্ণতার জন্য এই টার্মিনালে চালানো কমান্ডগুলো রেকর্ড করুন।", "linkClickBehaviorLabel": "লিঙ্ক ক্লিকের আচরণ", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} আমদানি করা হয়েছে", "proxmoxResultUpdated": "{{count}} আপডেট করা হয়েছে", "proxmoxResultFailed": "{{count}} ব্যর্থ হয়েছে", + "proxmoxResultSkippedNoIp": "{{count}} বাদ দেওয়া হয়েছে (কোন আইপি পাওয়া যায়নি)", "proxmoxImportComplete": "প্রক্সমক্স আমদানি সম্পন্ন: {{summary}}", "proxmoxDiscoveryFailed": "আবিষ্কার ব্যর্থ হয়েছে", "proxmoxImportFailed": "আমদানি ব্যর্থ হয়েছে", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "পরিচয়পত্র সংরক্ষণ করতে ব্যর্থ হয়েছে", "credentialNameRequired": "অনুগ্রহ করে পরিচয়পত্রের জন্য একটি নাম লিখুন।", "credentialAuthRequired": "একটি পাসওয়ার্ড, একটি SSH কী, অথবা উভয়ই যোগ করুন।", + "createCredentialFromHostBtn": "পরিচয়পত্র তৈরি করুন", + "createCredentialFromHostTitle": "হোস্ট থেকে ক্রেডেনশিয়াল তৈরি করুন", + "createCredentialFromHostDesc": "এই হোস্টের বর্তমান ইউজারনেম, পাসওয়ার্ড এবং/অথবা SSH কী দিয়ে আগে থেকেই পূরণ করা একটি পুনঃব্যবহারযোগ্য ও শেয়ারযোগ্য ক্রেডেনশিয়াল এন্ট্রি তৈরি করুন।", "backToHosts": "হোস্টদের কাছে ফিরে যান", "backToCredentials": "পরিচয়পত্রে ফিরে যান", "pinned": "পিন করা", @@ -1000,6 +1059,9 @@ "folderNestingHint": "স্তর আলাদা করতে এবং নেস্টেড ফোল্ডার তৈরি করতে / ব্যবহার করুন।", "folderColor": "রঙ", "folderIcon": "আইকন", + "folderCredential": "শংসাপত্র", + "folderCredentialNone": "কোনো পরিচয়পত্র বরাদ্দ করা হয়নি", + "folderCredentialHint": "এই ফোল্ডারের যে হোস্টগুলো নিজেদের ক্রেডেনশিয়াল নির্বাচন না করে 'সংরক্ষিত ক্রেডেনশিয়াল' প্রমাণীকরণ ব্যবহার করে, তারা উত্তরাধিকারসূত্রে এটিই পাবে।", "folderPreview": "প্রিভিউ", "folderNameFallback": "শিরোনামহীন ফোল্ডার", "createFolderButton": "ফোল্ডার তৈরি করুন", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "ট্যাগ", "shareHost": "হোস্ট শেয়ার করুন", "shareHostTitle": "শেয়ার করুন: {{name}}", + "shareFolder": "শেয়ার ফোল্ডার", + "shareFolderTitle": "শেয়ার ফোল্ডার: {{name}}", + "folderSharedSuccessfully": "ফোল্ডারে শেয়ার করা {{count}} হোস্ট(গুলি)", + "failedToShareFolder": "ফোল্ডার শেয়ার করতে ব্যর্থ হয়েছে", "sharing": { "loadError": "শেয়ারিং ডেটা লোড করা সম্ভব হয়নি। অনুগ্রহ করে আবার চেষ্টা করুন।", "shareWithSection": "শেয়ার করুন", @@ -1221,6 +1287,7 @@ "shareWithCount": "শেয়ার করুন ({{count}})", "currentAccess": "বর্তমান অ্যাক্সেস", "noAccessEntries": "এই হোস্টটি এখনও শেয়ার করা হয়নি।", + "folderShareSummary": "এই ফোল্ডারে {{total}} টি {{shared}} টি শেয়ার করা হোস্ট।", "grantedBy": "মঞ্জুর করেছেন", "expires": "মেয়াদ শেষ", "expired": "মেয়াদোত্তীর্ণ", @@ -1242,6 +1309,8 @@ "authMethod": "প্রমাণীকরণ পদ্ধতি", "authTypeDirect": "সরাসরি", "authTypeCredential": "শংসাপত্র", + "authTypeNone": "কোনোটিই না", + "authTypeNoneDesc": "কোনো পরিচয়পত্র সংরক্ষিত থাকে না। প্রতিবার সংযোগ করার সময় আপনাকে একটি ইউজারনেম এবং পাসওয়ার্ড দিতে বলা হবে; সেগুলি সংরক্ষণ করা হয় না।", "selectCredential": "একটি শংসাপত্র নির্বাচন করুন...", "connectionSettings": "সংযোগ সেটিংস", "displaySettings": "ডিসপ্লে সেটিংস", @@ -1259,6 +1328,7 @@ "rdpPort": "আরডিপি পোর্ট", "username": "ব্যবহারকারীর নাম", "password": "পাসওয়ার্ড", + "passwordSaved": "পাসওয়ার্ড সংরক্ষিত আছে, পরিবর্তন করতে টাইপ করুন", "domain": "ডোমেইন", "securityMode": "নিরাপত্তা মোড", "colorDepth": "রঙের গভীরতা", @@ -1424,7 +1494,60 @@ "expiresIn": "{{duration}}-এ মেয়াদ শেষ হবে", "search": "সংযোগগুলি অনুসন্ধান করুন...", "noSearchResults": "আপনার অনুসন্ধানের সাথে কোনো সংযোগ মেলেনি।", - "rename": "পুনঃনামকরণ অধিবেশন" + "rename": "পুনঃনামকরণ অধিবেশন", + "sectionSharedWithMe": "আমার সাথে শেয়ার করা হয়েছে", + "sharedBy": "{{username}} দ্বারা শেয়ার করা হয়েছে", + "join": "যোগদান করুন", + "sharedSessionLabel": "{{hostName}} (ভাগ করা)" + }, + "sessionSharing": { + "guestView": { + "loading": "শেয়ার করা সেশনে সংযোগ স্থাপন করা হচ্ছে...", + "linkInvalid": "এই শেয়ার লিঙ্কটি অবৈধ, মেয়াদোত্তীর্ণ, বা বাতিল করা হয়েছে।", + "rateLimited": "অনেকবার চেষ্টা করা হয়েছে, অনুগ্রহ করে কিছুক্ষণ পরে আবার চেষ্টা করুন।", + "sessionEnded": "এই অধিবেশন শেষ হয়েছে", + "readOnlyBadge": "শুধুমাত্র দেখার জন্য" + }, + "modalTitle": "সেশন শেয়ার করুন", + "shareButton": "শেয়ার", + "notReadyToShare": "সেশনটি এখনও শেয়ার করার জন্য প্রস্তুত নয়।", + "modeTab": { + "link": "লিঙ্ক", + "user": "ব্যবহারকারী" + }, + "linkModeDescription": "এই লিঙ্কের মাধ্যমে যে কেউ যোগ দিতে পারবেন, কোনো অ্যাকাউন্টের প্রয়োজন নেই।", + "userModeDescription": "এমন একজন নির্দিষ্ট ব্যবহারকারীর সাথে শেয়ার করুন যার এই হোস্টে আগে থেকেই অ্যাক্সেস আছে। যদি তাদের এখনও অ্যাক্সেস না থাকে, তাহলে প্রথমে তাদের সাথে হোস্টটি শেয়ার করুন অথবা এর পরিবর্তে একটি লিঙ্ক ব্যবহার করুন। শেয়ার করা হয়ে গেলে, সেশনটি তাদের 'কানেকশনস' ট্যাবে দেখা যাবে।", + "permissionLevel": { + "label": "অনুমতি স্তর", + "readOnly": "শুধুমাত্র পঠনযোগ্য", + "readOnlyDescription": "সেশনটি সরাসরি দেখা যাবে কিন্তু টাইপ বা অংশগ্রহণ করা যাবে না।", + "readWrite": "পড়া-লেখা", + "readWriteDescription": "মালিকের মতোই সেশনে টাইপ করতে এবং ইন্টারঅ্যাক্ট করতে পারবেন।" + }, + "expiryLabel": "লিঙ্কের মেয়াদ শেষ", + "createLinkButton": "লিঙ্ক তৈরি করুন", + "createShareButton": "ব্যবহারকারীর সাথে শেয়ার করুন", + "searchUsersPlaceholder": "ব্যবহারকারীদের অনুসন্ধান করুন...", + "noUsersFound": "কোন ব্যবহারকারী খুঁজে পাওয়া যায়নি", + "linkCreated": "শেয়ার লিঙ্ক তৈরি করা হয়েছে", + "linkCopied": "লিঙ্ক ক্লিপবোর্ডে কপি করা হয়েছে", + "copyLink": "লিঙ্ক কপি করুন", + "shareCreated": "সেশনটি শেয়ার করা হয়েছে। এটি তাদের কানেকশনস ট্যাবে দেখা যাবে।", + "shareFailed": "শেয়ার তৈরি করতে ব্যর্থ হয়েছে", + "userLacksHostAccess": "ওই ব্যবহারকারীর এখনও এই হোস্টে অ্যাক্সেস নেই। প্রথমে তাদের সাথে হোস্টটি শেয়ার করুন, অথবা এর পরিবর্তে একটি লিঙ্ক ব্যবহার করুন।", + "activeShares": "সক্রিয় শেয়ার", + "noActiveShares": "এই সেশনের জন্য কোনো সক্রিয় শেয়ার নেই", + "revoke": "বাতিল করুন", + "revokeConfirmTitle": "এই শেয়ারটি প্রত্যাহার করবেন?", + "revokeConfirmDescription": "যে কেউ এই শেয়ারটি ব্যবহার করলে, তিনি অবিলম্বে অ্যাক্সেস হারাবেন।", + "revoked": "শেয়ার প্রত্যাহার করা হয়েছে", + "revokeFailed": "শেয়ার প্রত্যাহার করতে ব্যর্থ হয়েছে", + "joinCount": "{{count}} যোগদান করুন", + "joinCount_other": "{{count}} যোগদান করে", + "expiresAt": "মেয়াদ শেষ হবে {{date}}", + "linkShareBadge": "লিঙ্ক", + "userShareBadge": "ব্যবহারকারী: {{username}}", + "loadSharesFailed": "সক্রিয় শেয়ারগুলি লোড করতে ব্যর্থ হয়েছে" }, "guacamole": { "connecting": "{{type}} সেশনে সংযোগ স্থাপন করা হচ্ছে...", @@ -1436,6 +1559,9 @@ "reconnect": "পুনরায় সংযোগ করুন", "retry": "পুনরায় চেষ্টা করুন", "guacdUnavailable": "রিমোট ডেস্কটপ সার্ভিস (guacd) উপলব্ধ নেই। অনুগ্রহ করে নিশ্চিত করুন যে guacd চালু ও অ্যাক্সেসযোগ্য আছে এবং অ্যাডমিন সেটিংসে সঠিকভাবে কনফিগার করা আছে।", + "credentialPromptTitle": "RDP পরিচয়পত্র প্রবেশ করান", + "credentialPromptDescription": "এই হোস্টটি সংযোগের সময় ক্রেডেনশিয়াল চাওয়ার জন্য সেট করা আছে। এগুলি শুধুমাত্র এই সেশনের জন্য ব্যবহৃত হয় এবং সংরক্ষিত হয় না।", + "connect": "সংযোগ করুন", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "পুনরায় সংযোগ সেশন", "collapse": "টুলবার সংকুচিত করুন", "expand": "টুলবার প্রসারিত করুন", - "dragHandle": "অবস্থান পরিবর্তন করতে টেনে আনুন" + "dragHandle": "অবস্থান পরিবর্তন করতে টেনে আনুন", + "switchToTrackpad": "ট্র্যাকপ্যাড মোডে যান (কার্সার সরাতে ড্র্যাগ করুন, ক্লিক করতে ট্যাপ করুন)", + "switchToTouch": "টাচ মোডে যান (যেখানে ক্লিক করতে চান সেখানে সরাসরি ট্যাপ করুন)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "দ্বি-স্তরীয় প্রমাণীকরণ আবশ্যক", "totpCodeLabel": "যাচাইকরণ কোড", "totpVerify": "যাচাই করুন", + "mfaPromptRequired": "প্রমাণীকরণ আবশ্যক", + "mfaPushRequired": "পুশ প্রমাণীকরণ আবশ্যক", + "mfaMenuPlaceholder": "আপনার প্রতিক্রিয়া লিখুন", + "mfaWaitingApproval": "আপনার ডিভাইসে অনুমোদনের জন্য অপেক্ষা করা হচ্ছে...", + "mfaSendRequest": "অনুরোধ পাঠান", "warpgateAuthRequired": "ওয়ার্পগেট প্রমাণীকরণ আবশ্যক", "warpgateSecurityKey": "নিরাপত্তা চাবি", "warpgateAuthUrl": "প্রমাণীকরণ ইউআরএল", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "রেট সীমিত: অনেক বেশি যাচাইকরণ প্রচেষ্টা করা হয়েছে। অনুগ্রহ করে পরে আবার চেষ্টা করুন।", "resetCodeRateLimitedWithTime": "রেট সীমিত: অনেক বেশি যাচাইকরণ প্রচেষ্টা করা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করার আগে {{time}} সেকেন্ড অপেক্ষা করুন।", "authTokenSaveFailed": "প্রমাণীকরণ টোকেন সংরক্ষণ করতে ব্যর্থ হয়েছে", - "failedToLoadServer": "সার্ভার লোড করতে ব্যর্থ হয়েছে" + "failedToLoadServer": "সার্ভার লোড করতে ব্যর্থ হয়েছে", + "remoteServerRequired": "রিমোট সার্ভার প্রয়োজন। এই সংযোগের ধরণটি ব্যবহার করতে সেটিংস-এ একটি রিমোট সার্ভার সংযুক্ত করুন।" }, "messages": { "registrationDisabled": "অ্যাডমিন কর্তৃক নতুন অ্যাকাউন্ট নিবন্ধন বর্তমানে বন্ধ রাখা হয়েছে। অনুগ্রহ করে লগ ইন করুন অথবা কোনো প্রশাসকের সাথে যোগাযোগ করুন।", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "SSL সেটিংস সংরক্ষণ করতে ব্যর্থ হয়েছে", "sslRequiresDomain": "ডোমেইন এবং ইমেল প্রয়োজন", "sslInfoNote": "সার্টিফিকেট ইস্যু করার পর, আপনার এনভায়রনমেন্ট ভেরিয়েবলে SSL সক্রিয় করুন (ENABLE_SSL=true) এবং Termix রিস্টার্ট করুন।", + "sslManualOption": "ম্যানুয়াল (শংসাপত্র আপলোড করুন)", + "sslManualCert": "সার্টিফিকেট (পিইএম)", + "sslManualCertPlaceholder": "-----সনদপত্র শুরু-----", + "sslManualKey": "ব্যক্তিগত কী (পিইএম)", + "sslManualKeyPlaceholder": "-----ব্যক্তিগত কী শুরু-----", + "sslManualDesc": "আপনার বিদ্যমান সার্টিফিকেট এবং প্রাইভেট কী পেস্ট করুন, আপনার CA-এর প্রয়োজন হলে সম্পূর্ণ চেইনও অন্তর্ভুক্ত করুন।", + "sslManualUpload": "সার্টিফিকেট আপলোড ও ইনস্টল করুন", + "sslManualUploadLoading": "সার্টিফিকেট আপলোড করা হচ্ছে...", + "sslManualUploadSuccess": "সার্টিফিকেট সফলভাবে আপলোড এবং ইনস্টল করা হয়েছে।", + "sslManualUploadFailed": "সার্টিফিকেট আপলোড ব্যর্থ হয়েছে", + "sslManualRequiresFields": "সার্টিফিকেট এবং প্রাইভেট কী প্রয়োজন", "auditLogTotal": "{{total}} মোট এন্ট্রি", "auditLogEmpty": "কোনো অডিট লগ এন্ট্রি পাওয়া যায়নি", "auditLogSuccess": "সাফল্য", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "কমান্ডের ইতিহাস", "commandHistoryEnabledDesc": "কমান্ড হিস্ট্রি রেকর্ডিংয়ের অনুমতি দিন। এটি নিষ্ক্রিয় করা থাকলে, হোস্ট-ভিত্তিক সেটিংস নির্বিশেষে হিস্ট্রি সংরক্ষিত হয় না।", "updateCommandHistoryFailed": "কমান্ড হিস্ট্রি সেটিং আপডেট করতে ব্যর্থ হয়েছে", + "analyticsEnabled": "বেনামী ব্যবহারের পরিসংখ্যান শেয়ার করুন", + "analyticsEnabledDesc": "টারমিক্সকে উন্নত করতে সাহায্য করার জন্য ব্যবহারকারী, হোস্ট এবং ফিচার ব্যবহারের একটি বেনামী দৈনিক গণনা পাঠায়। এতে কোনো ব্যক্তিগত তথ্য বা সংযোগের বিবরণ অন্তর্ভুক্ত করা হয় না।", + "updateAnalyticsFailed": "অ্যানালিটিক্স সেটিং আপডেট করতে ব্যর্থ হয়েছে", + "sessionSharingGloballyEnabled": "সেশন শেয়ারিং অনুমোদন করুন", + "sessionSharingGloballyEnabledDesc": "লাইভ টার্মিনাল, RDP, VNC, এবং টেলনেট সেশনগুলোকে ইনস্ট্যান্স-ব্যাপী শেয়ার করার অনুমতি দিন। এটি নিষ্ক্রিয় করা হলে, প্রতি-হোস্ট শেয়ারিং টগলকে ওভাররাইড করে।", + "updateSessionSharingFailed": "সেশন শেয়ারিং সেটিং আপডেট করতে ব্যর্থ হয়েছে", "sessionTimeout": "সেশন টাইমআউট", "hours": "ঘন্টা", "sessionTimeoutRange": "সর্বনিম্ন ১ ঘণ্টা · সর্বোচ্চ ৭২০ ঘণ্টা", @@ -3119,6 +3270,47 @@ "executionSuccess": "সাফল্য", "executionFailed": "ব্যর্থ" }, + "keybindings": { + "title": "কীবোর্ড শর্টকাট", + "description": "টার্মিনালের কপি, পেস্ট ও কন্ট্রোল শর্টকাটগুলো নিজের মতো করে সাজিয়ে নিন, অথবা টেক্সট পাঠাতে বা কোনো স্নিপেট চালাতে কী-গুলো বাইন্ড করুন।", + "defaultsHeading": "অন্তর্নির্মিত শর্টকাট", + "customHeading": "কাস্টম শর্টকাট", + "addBinding": "শর্টকাট যোগ করুন", + "addBindingTitle": "শর্টকাট যোগ করুন", + "editBindingTitle": "সম্পাদনা শর্টকাট", + "loading": "লোড হচ্ছে...", + "noCustomBindings": "এখনো কোনো কাস্টম শর্টকাট নেই", + "defaultBadge": "ডিফল্ট", + "customizedBadge": "কাস্টমাইজড", + "customize": "কাস্টমাইজ করুন", + "resetToDefault": "ডিফল্টে রিসেট করুন", + "resetAllToDefaults": "সবকিছু ডিফল্টে রিসেট করুন", + "close": "বন্ধ করুন", + "cancel": "বাতিল করুন", + "saveBinding": "সংরক্ষণ শর্টকাট", + "saveError": "কিবোর্ড শর্টকাট সংরক্ষণ করা যায়নি", + "pressKeysToRecord": "চাবির সংমিশ্রণ", + "pressKeysPlaceholder": "ক্লিক করুন এবং কী টিপুন", + "recording": "কীগুলো চাপুন...", + "comboRequiredError": "প্রথমে একটি কী-কম্বিনেশন চাপুন", + "textRequiredError": "পাঠানোর জন্য টেক্সটটি লিখুন।", + "controlCodeRequiredError": "কন্ট্রোল কোডের জন্য একটি অক্ষর লিখুন।", + "snippetRequiredError": "একটি স্নিপেট নির্বাচন করুন", + "conflictWarning": "এই সংমিশ্রণটি ইতিমধ্যেই {{combo}}এর সাথে আবদ্ধ। সংরক্ষণ করলে উভয়ই সক্রিয় হবে।", + "actionLabel": "পদক্ষেপ", + "actionCopy": "কপি নির্বাচন", + "actionPaste": "পেস্ট", + "actionSendControlCode": "Ctrl+অক্ষর সংকেত পাঠান", + "actionSendText": "টেক্সট পাঠান", + "actionRunSnippet": "বিদ্যমান কোড চালান", + "controlCodeLabel": "চিঠি", + "textLabel": "পাঠানোর জন্য টেক্সট", + "appendEnterLabel": "পাঠানোর পর এন্টার চাপুন", + "snippetLabel": "স্নিপেট", + "selectSnippetPlaceholder": "একটি স্নিপেট নির্বাচন করুন", + "orphanedSnippetWarning": "স্নিপেটটি খুঁজে পাওয়া যায়নি", + "clipboardPermissionNote": "কিছু ব্রাউজারে প্রথমবার ব্যবহারের সময় ক্লিপবোর্ডের অনুমতি চাইতে পারে।" + }, "userProfile": { "donateTitle": "টারমিক্সকে বাঁচিয়ে রাখুন", "donateDescription": "টারমিক্স বিনামূল্যে, সেলফ-হোস্টেড এবং মাত্র কয়েকজন ব্যক্তি তাদের অবসর সময়ে এটি তৈরি করেছেন। যদি এটি আপনার সময় বা অর্থ বাঁচিয়ে থাকে, তবে একটি ক্রিপ্টো অনুদান এটিকে চালিয়ে যেতে সাহায্য করে।", @@ -3134,6 +3326,7 @@ "sectionAppearance": "চেহারা", "sectionSecurity": "নিরাপত্তা", "sectionApiKeys": "এপিআই কী", + "sectionData": "ডেটা", "sectionC2sTunnels": "সি২এস টানেল", "usernameLabel": "ব্যবহারকারীর নাম", "roleLabel": "ভূমিকা", @@ -3162,6 +3355,9 @@ "settingsTerminal": "টার্মিনাল", "commandAutocomplete": "কমান্ড অটোকমপ্লিট", "commandAutocompleteDesc": "টাইপ করার সময় স্বয়ংক্রিয় সম্পূর্ণতা দেখান", + "keyboardShortcuts": "কীবোর্ড শর্টকাট", + "keyboardShortcutsDescription": "টার্মিনাল কপি, পেস্ট এবং কমান্ড শর্টকাট কাস্টমাইজ করুন", + "manageShortcuts": "পরিচালনা করুন", "terminalLinkBehavior": "টার্মিনাল লিঙ্ক ক্লিক", "terminalLinkBehaviorDesc": "টার্মিনালে লিঙ্কে ক্লিক করার সময় ডিফল্ট আচরণ", "historyTracking": "ইতিহাস ট্র্যাকিং", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "আপনার চাবিটি অন্তর্ভুক্ত করুন", "apiKeyUsageHintHeader": "হেডার।", "apiKeyPermissionsHint": "কী-গুলো সৃষ্টিকারী ব্যবহারকারীর অনুমতি উত্তরাধিকারসূত্রে লাভ করে।", + "exportData": "আমার ডেটা রপ্তানি করুন", + "exportDataDesc": "অন্য ডিভাইসে স্থানান্তর করার জন্য আপনার হোস্ট, ক্রেডেনশিয়াল এবং সেটিংসের একটি ব্যাকআপ ডাউনলোড করুন।", + "export": "রপ্তানি", + "exporting": "রপ্তানি করা হচ্ছে...", + "importData": "আমার ডেটা আমদানি করুন", + "importDataDesc": "একটি .sqlite ব্যাকআপ ফাইল থেকে আপনার হোস্ট, ক্রেডেনশিয়াল এবং সেটিংস পুনরুদ্ধার করুন।", + "importDataSelected": "নির্বাচিত: {{name}}", + "selectFile": "ফাইল নির্বাচন করুন", + "changeFile": "পরিবর্তন", + "import": "আমদানি", + "importing": "আমদানি করা হচ্ছে...", + "exportSuccess": "ডেটা সফলভাবে রপ্তানি করা হয়েছে", + "exportFailed": "ডেটা রপ্তানি ব্যর্থ হয়েছে", + "importSelectFile": "অনুগ্রহ করে প্রথমে একটি ফাইল নির্বাচন করুন।", + "importCompleted": "আমদানি সম্পন্ন হয়েছে: {{total}} টি আইটেম আমদানি করা হয়েছে, {{skipped}} টি বাদ দেওয়া হয়েছে", + "importFailed": "আমদানি ব্যর্থ হয়েছে: {{error}}", "roleUser": "ব্যবহারকারী", "authMethodDual": "দ্বৈত প্রমাণীকরণ", "authMethodOidc": "ওআইডিসি", @@ -3379,6 +3591,7 @@ "pageUp": "পিজিআপ", "pageDown": "পিজিডিএন", "delete": "ডেল", + "paste": "পেস্ট", "editQuickKeys": "দ্রুত কী সম্পাদনা করুন", "quickKeysTitle": "দ্রুত চাবি", "quickKeysDesc": "মুছে ফেলতে × চাপুন। সর্বোচ্চ ৮টি অক্ষর সমর্থন করে।", diff --git a/src/ui/locales/translated/ca_ES.json b/src/ui/locales/translated/ca_ES.json index a3990f3f..034fac7b 100644 --- a/src/ui/locales/translated/ca_ES.json +++ b/src/ui/locales/translated/ca_ES.json @@ -389,6 +389,45 @@ "noSavedServers": "Cap servidor desat", "removeServer": "Elimina" }, + "migrationNotice": { + "title": "Termix Desktop ara funciona de manera autònoma", + "body1": "Aquesta aplicació abans es connectava directament al vostre servidor Termix. S'ha reelaborat per funcionar completament per si sola, emmagatzemant els hosts i les credencials localment per tal que continuï funcionant fora de línia. La sincronització bidireccional amb un servidor Termix autoallotjat ara és opcional.", + "body2": "Els vostres amfitrions i credencials existents encara són a {{url}}. Activeu la sincronització remota i torneu a connectar-vos a aquest servidor per tornar-los a sincronitzar i mantenir-los tots dos sincronitzats en el futur.", + "dismiss": "Ara no", + "setUpSync": "Configura la sincronització remota" + }, + "remoteSync": { + "title": "Sincronització remota", + "description": "Opcionalment, podeu connectar aquesta aplicació d'escriptori a un servidor Termix autoallotjat per sincronitzar els vostres hosts, credencials i fragments entre dispositius. L'aplicació sempre funciona completament fora de línia, tant si us connecteu com si no.", + "notConnected": "No connectat", + "connected": "Connectat", + "connectedTo": "Connectat a {{url}}", + "lastSynced": "Última sincronització {{time}}", + "neverSynced": "Mai sincronitzat", + "syncError": "Error de sincronització: {{message}}", + "needsReauth": "L'inici de sessió ha caducat", + "connectButton": "Connecta't al servidor", + "disconnectButton": "Desconnecta", + "syncNowButton": "Sincronitza ara", + "syncing": "Sincronitzant...", + "serverUrl": "URL del servidor", + "enterServerUrl": "Si us plau, introduïu una URL de servidor", + "mustIncludeProtocol": "L'URL del servidor ha de començar per http:// o https://", + "connectionTestFailed": "No s'ha pogut connectar amb un servidor Termix en aquesta URL.", + "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.", + "savedServers": "Servidors desats", + "removeServer": "Elimina", + "continueButton": "Continua", + "cancelButton": "Cancel·la", + "signInTitle": "Inicia la sessió a {{url}}", + "originTitle": "Origen de la connexió", + "originDescription": "Trieu l'origen de les connexions SSH per defecte. Això es pot anul·lar per cada host.", + "originLocal": "Aquest dispositiu (xarxa local)", + "originRemote": "Servidor remot", + "bannerReconnect": "Reconnecta", + "bannerMessage": "La sincronització remota necessita una reautenticació" + }, "versionCheck": { "error": "Error de comprovació de versió", "checkFailed": "No s'ha pogut comprovar si hi ha actualitzacions", @@ -602,6 +641,7 @@ "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", "oidcUsernameHint": "Feu servir $oidc.preferred_username per substituir el vostre nom d'usuari d'inici de sessió OIDC.", + "tailscaleUsernameHint": "Aquest ha de ser un usuari d'Unix; la teva identitat Tailscale està atorgada a l'ACL SSH de la xarxa principal, no necessàriament un usuari root.", "jumpHostChain": "Cadena d'amfitrió de salt", "portKnocking": "Cop de port", "addKnock": "Afegeix un port", @@ -650,6 +690,11 @@ "delayAfterMs": "Retard després (ms)", "useSocks5Proxy": "Utilitza el servidor intermediari SOCKS5", "useSocks5ProxyDesc": "Enrutar la connexió a través d'un servidor proxy", + "connectionOrigin": "Origen de la connexió", + "connectionOriginDesc": "D'on s'origina la connexió SSH d'aquest amfitrió. Substitueix el valor predeterminat global de l'aplicació d'escriptori.", + "connectionOriginDefault": "Utilitza el valor per defecte", + "connectionOriginLocal": "Aquest dispositiu (xarxa local)", + "connectionOriginRemote": "Servidor remot", "proxyHost": "Amfitrió intermediari", "proxyPort": "Port de proxy", "proxyUsername": "Nom d'usuari del proxy", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Cian brillant", "customThemeBrightWhite": "Blanc brillant", "customThemeResetTooltip": "Restableix els valors predeterminats", + "savedThemesLabel": "Temes desats", + "saveAsGlobalTheme": "Desa com a tema global", + "saveGlobalThemeNamePrompt": "Introdueix un nom per a aquest tema", + "saveGlobalThemeSuccess": "Tema desat", + "saveGlobalThemeError": "No s'ha pogut desar el tema", + "applyGlobalThemeTooltip": "Aplica aquest tema", + "deleteGlobalThemeTooltip": "Suprimeix aquest tema", + "noSavedThemes": "Encara no hi ha temes desats", "syntaxHighlightingLabel": "Ressaltat de sintaxi", "syntaxHighlightingDesc": "Coloritzar la sortida del terminal (errors, camins, IP, marques de temps)", "syntaxHighlightingCategories": "Categories destacades", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Inicia o connecta automàticament a la sessió de tmux", "enableSessionLogging": "Registre de sessions", "enableSessionLoggingDesc": "Enregistra la sortida de la sessió de terminal per a una revisió posterior", + "allowSessionSharing": "Permetre la compartició de sessions", + "allowSessionSharingDesc": "Permet que les sessions en directe d'aquest amfitrió es comparteixin mitjançant un enllaç o amb altres usuaris", "enableCommandHistory": "Historial de comandaments", "enableCommandHistoryDesc": "Enregistra les ordres executades en aquest terminal per a l'historial i l'autocompleció", "linkClickBehaviorLabel": "Comportament dels clics d'enllaç", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importat", "proxmoxResultUpdated": "{{count}} actualitzat", "proxmoxResultFailed": "{{count}} ha fallat", + "proxmoxResultSkippedNoIp": "{{count}} omès (no s'ha trobat cap IP)", "proxmoxImportComplete": "Importació de Proxmox completada: {{summary}}", "proxmoxDiscoveryFailed": "El descobriment ha fallat", "proxmoxImportFailed": "La importació ha fallat", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "No s'ha pogut desar la credencial", "credentialNameRequired": "Si us plau, introduïu un nom per a la credencial", "credentialAuthRequired": "Afegiu una contrasenya, una clau SSH o totes dues", + "createCredentialFromHostBtn": "Crea credencials", + "createCredentialFromHostTitle": "Crea credencials des de l'amfitrió", + "createCredentialFromHostDesc": "Crea una entrada de credencials reutilitzable i compartible preomplerta amb el nom d'usuari, la contrasenya i/o la clau SSH actuals d'aquest amfitrió.", "backToHosts": "Torna a Amfitrions", "backToCredentials": "Torna a les credencials", "pinned": "Fixat", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Feu servir / per separar nivells i crear carpetes imbricades.", "folderColor": "Color", "folderIcon": "Icona", + "folderCredential": "Credencial", + "folderCredentialNone": "Cap credencial assignada", + "folderCredentialHint": "Els amfitrions d'aquesta carpeta que utilitzen l'autenticació \"Credencial emmagatzemada\" sense tenir les seves pròpies credencials seleccionades heretaran aquesta.", "folderPreview": "Vista prèvia", "folderNameFallback": "Carpeta sense títol", "createFolderButton": "Crea una carpeta", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Etiquetes", "shareHost": "Comparteix l'amfitrió", "shareHostTitle": "Comparteix: {{name}}", + "shareFolder": "Compartir carpeta", + "shareFolderTitle": "Compartir carpeta: {{name}}", + "folderSharedSuccessfully": "Host(s) compartit(s) {{count}} a la carpeta", + "failedToShareFolder": "No s'ha pogut compartir la carpeta", "sharing": { "loadError": "No s'han pogut carregar les dades de compartició. Torna-ho a provar.", "shareWithSection": "Comparteix amb", @@ -1221,6 +1287,7 @@ "shareWithCount": "Comparteix ({{count}})", "currentAccess": "Accés actual", "noAccessEntries": "Aquest amfitrió encara no s'ha compartit", + "folderShareSummary": "S'han compartit {{shared}} de {{total}} amfitrions en aquesta carpeta", "grantedBy": "Atorgat per", "expires": "Caduca", "expired": "Caducat", @@ -1242,6 +1309,8 @@ "authMethod": "Mètode d'autenticació", "authTypeDirect": "Directe", "authTypeCredential": "Credencial", + "authTypeNone": "Cap", + "authTypeNoneDesc": "No s'emmagatzemen les credencials. Se us demanarà un nom d'usuari i una contrasenya cada vegada que us connecteu; no es desen.", "selectCredential": "Selecciona una credencial...", "connectionSettings": "Configuració de connexió", "displaySettings": "Configuració de la pantalla", @@ -1259,6 +1328,7 @@ "rdpPort": "Port RDP", "username": "Nom d'usuari", "password": "Contrasenya", + "passwordSaved": "Contrasenya desada, escriu per canviar-la", "domain": "Domini", "securityMode": "Mode de seguretat", "colorDepth": "Profunditat de color", @@ -1424,7 +1494,60 @@ "expiresIn": "Caduca en {{duration}}", "search": "Cerca connexions...", "noSearchResults": "No hi ha connexions que coincideixin amb la teva cerca", - "rename": "Canvia el nom de la sessió" + "rename": "Canvia el nom de la sessió", + "sectionSharedWithMe": "Compartit amb mi", + "sharedBy": "Compartit per {{username}}", + "join": "Uneix-te", + "sharedSessionLabel": "{{hostName}} (compartit)" + }, + "sessionSharing": { + "guestView": { + "loading": "Connectant a la sessió compartida...", + "linkInvalid": "Aquest enllaç per compartir no és vàlid, ha caducat o s'ha revocat.", + "rateLimited": "Massa intents, si us plau, torneu-ho a intentar en breu", + "sessionEnded": "Aquesta sessió ha finalitzat", + "readOnlyBadge": "Només visualització" + }, + "modalTitle": "Comparteix la sessió", + "shareButton": "Comparteix", + "notReadyToShare": "La sessió encara no està a punt per compartir", + "modeTab": { + "link": "Enllaç", + "user": "Usuari" + }, + "linkModeDescription": "Qualsevol persona amb aquest enllaç s'hi pot unir, no cal tenir cap compte.", + "userModeDescription": "Comparteix amb un usuari específic que ja tingui accés a aquest amfitrió. Si encara no hi té accés, comparteix primer l'amfitrió amb ell o utilitza un enllaç. Un cop compartida, la sessió apareix a la pestanya Connexions.", + "permissionLevel": { + "label": "Nivell de permís", + "readOnly": "Només lectura", + "readOnlyDescription": "Puc veure la sessió en directe però no puc escriure ni interactuar.", + "readWrite": "Lectura i escriptura", + "readWriteDescription": "Pot escriure i interactuar amb la sessió igual que el propietari." + }, + "expiryLabel": "Caducitat de l'enllaç", + "createLinkButton": "Crea un enllaç", + "createShareButton": "Comparteix amb l'usuari", + "searchUsersPlaceholder": "Cerca usuaris...", + "noUsersFound": "No s'han trobat usuaris", + "linkCreated": "Enllaç de compartició creat", + "linkCopied": "Enllaç copiat al porta-retalls", + "copyLink": "Copia l'enllaç", + "shareCreated": "Sessió compartida. Apareixerà a la pestanya Connexions.", + "shareFailed": "No s'ha pogut crear la compartició", + "userLacksHostAccess": "Aquest usuari encara no té accés a aquest amfitrió. Primer comparteix l'amfitrió amb ell o utilitza un enllaç.", + "activeShares": "Accions actives", + "noActiveShares": "No hi ha cap compartició activa per a aquesta sessió", + "revoke": "Revocar", + "revokeConfirmTitle": "Voleu revocar aquesta compartició?", + "revokeConfirmDescription": "Qualsevol persona que utilitzi aquesta opció compartida perdrà l'accés immediatament.", + "revoked": "Compartir revocat", + "revokeFailed": "No s'ha pogut revocar la compartició", + "joinCount": "{{count}} uneix-te", + "joinCount_other": "{{count}} s'uneix", + "expiresAt": "Caduca {{date}}", + "linkShareBadge": "Enllaç", + "userShareBadge": "Usuari: {{username}}", + "loadSharesFailed": "No s'han pogut carregar les comparticions actives" }, "guacamole": { "connecting": "Connectant a la sessió {{type}}...", @@ -1436,6 +1559,9 @@ "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.", + "credentialPromptTitle": "Introduïu les credencials RDP", + "credentialPromptDescription": "Aquest amfitrió està configurat per demanar credencials en connectar-se. Només s'utilitzen per a aquesta sessió i no es desen.", + "connect": "Connecta", "ctrlAltDel": "Ctrl+Alt+Supr", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Supr", @@ -1461,7 +1587,9 @@ "reconnect": "Sessió de reconnexió", "collapse": "Replega la barra d'eines", "expand": "Expandeix la barra d'eines", - "dragHandle": "Arrossega per reposicionar" + "dragHandle": "Arrossega per reposicionar", + "switchToTrackpad": "Canvia al mode trackpad (arrossega per moure el cursor, toca per fer clic)", + "switchToTouch": "Canvia al mode tàctil (toca directament on vols fer clic)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Cal autenticació de dos factors", "totpCodeLabel": "Codi de verificació", "totpVerify": "Verifica", + "mfaPromptRequired": "Cal autenticació", + "mfaPushRequired": "Autenticació push obligatòria", + "mfaMenuPlaceholder": "Introdueix la teva resposta", + "mfaWaitingApproval": "Esperant aprovació al dispositiu...", + "mfaSendRequest": "Enviar sol·licitud", "warpgateAuthRequired": "Cal autenticació de Warpgate", "warpgateSecurityKey": "Clau de seguretat", "warpgateAuthUrl": "URL d'autenticació", @@ -2277,7 +2410,8 @@ "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" + "failedToLoadServer": "No s'ha pogut carregar el servidor", + "remoteServerRequired": "Cal un servidor remot. Connecteu un servidor remot a Configuració per utilitzar aquest tipus de connexió." }, "messages": { "registrationDisabled": "El registre de comptes nous està desactivat actualment per un administrador. Si us plau, inicia la sessió o contacta amb un administrador.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "No s'ha pogut desar la configuració SSL", "sslRequiresDomain": "Cal el domini i el correu electrònic", "sslInfoNote": "Després d'emetre un certificat, activeu SSL a les variables d'entorn (ENABLE_SSL=true) i reinicieu Termix.", + "sslManualOption": "Manual (pujar certificat)", + "sslManualCert": "Certificat (PEM)", + "sslManualCertPlaceholder": "-----CERTIFICAT D'INICI-----", + "sslManualKey": "Clau privada (PEM)", + "sslManualKeyPlaceholder": "-----INICI DE LA CLAU PRIVADA-----", + "sslManualDesc": "Enganxeu el certificat i la clau privada existents, incloent-hi una cadena completa si la vostra autoritat de certificació ho requereix.", + "sslManualUpload": "Pujar i instal·lar el certificat", + "sslManualUploadLoading": "S'està carregant el certificat...", + "sslManualUploadSuccess": "El certificat s'ha carregat i instal·lat correctament", + "sslManualUploadFailed": "Ha fallat la càrrega del certificat", + "sslManualRequiresFields": "Cal un certificat i una clau privada", "auditLogTotal": "{{total}} total d'entrades", "auditLogEmpty": "No s'han trobat entrades del registre d'auditoria", "auditLogSuccess": "Èxit", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Historial de comandaments", "commandHistoryEnabledDesc": "Permet l'enregistrament de l'historial d'ordres. Quan està desactivat, l'historial no es desa independentment de la configuració de cada host.", "updateCommandHistoryFailed": "No s'ha pogut actualitzar la configuració de l'historial d'ordres", + "analyticsEnabled": "Comparteix estadístiques d'ús anònimes", + "analyticsEnabledDesc": "Envia un recompte diari anònim d'usuaris, amfitrions i ús de funcions per ajudar a millorar Termix. No s'hi inclouen mai dades personals ni detalls de connexió.", + "updateAnalyticsFailed": "No s'ha pogut actualitzar la configuració d'analítica", + "sessionSharingGloballyEnabled": "Permetre la compartició de sessions", + "sessionSharingGloballyEnabledDesc": "Permet que les sessions de terminal, RDP, VNC i Telnet en directe es comparteixin a tota la instància. Anul·la tots els activables i desactivables per compartir per host quan estan desactivats.", + "updateSessionSharingFailed": "No s'ha pogut actualitzar la configuració de compartició de sessions", "sessionTimeout": "Temps d'espera de la sessió", "hours": "hores", "sessionTimeoutRange": "Mín 1 h · Màx 720 h", @@ -3119,6 +3270,47 @@ "executionSuccess": "Èxit", "executionFailed": "Fallat" }, + "keybindings": { + "title": "Dreceres de teclat", + "description": "Personalitza les dreceres de copiar, enganxar i controlar del terminal, o vincula tecles per enviar text o executar un fragment.", + "defaultsHeading": "Dreceres integrades", + "customHeading": "Dreceres personalitzades", + "addBinding": "Afegeix una drecera", + "addBindingTitle": "Afegeix una drecera", + "editBindingTitle": "Edita la drecera", + "loading": "S'està carregant...", + "noCustomBindings": "Encara no hi ha dreceres personalitzades", + "defaultBadge": "Per defecte", + "customizedBadge": "Personalitzat", + "customize": "Personalitza", + "resetToDefault": "Restableix els valors predeterminats", + "resetAllToDefaults": "Restableix-ho tot als valors predeterminats", + "close": "Tanca", + "cancel": "Cancel·la", + "saveBinding": "Desa la drecera", + "saveError": "No s'han pogut desar les dreceres de teclat", + "pressKeysToRecord": "Combinació de tecles", + "pressKeysPlaceholder": "Feu clic i premeu les tecles", + "recording": "Premeu les tecles...", + "comboRequiredError": "Premeu primer una combinació de tecles", + "textRequiredError": "Introduïu el text per enviar", + "controlCodeRequiredError": "Introduïu una sola lletra per al codi de control", + "snippetRequiredError": "Selecciona un fragment", + "conflictWarning": "Aquesta combinació ja està vinculada a {{combo}}. Si deseu, totes dues es dispararan.", + "actionLabel": "Acció", + "actionCopy": "Copia la selecció", + "actionPaste": "Enganxa", + "actionSendControlCode": "Envia el senyal Ctrl+lletra", + "actionSendText": "Enviar text", + "actionRunSnippet": "Executa el fragment existent", + "controlCodeLabel": "Carta", + "textLabel": "Text per enviar", + "appendEnterLabel": "Premeu Intro després d'enviar", + "snippetLabel": "Fragment", + "selectSnippetPlaceholder": "Selecciona un fragment", + "orphanedSnippetWarning": "fragment no trobat", + "clipboardPermissionNote": "Pot demanar permisos al porta-retalls en el primer ús en alguns navegadors." + }, "userProfile": { "donateTitle": "Mantingueu viva la Termix", "donateDescription": "Termix és gratuït, s'allotja per si mateix i només l'han creat unes poques persones en el seu temps lliure. Si t'ha estalviat temps o diners, una donació de criptomonedes t'ajudarà a mantenir-lo en marxa.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Aspecte", "sectionSecurity": "Seguretat", "sectionApiKeys": "Claus API", + "sectionData": "Dades", "sectionC2sTunnels": "Túnels C2S", "usernameLabel": "Nom d'usuari", "roleLabel": "Rol", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Autocompletar ordres", "commandAutocompleteDesc": "Mostra l'autocompleció mentre s'escriu", + "keyboardShortcuts": "Dreceres de teclat", + "keyboardShortcutsDescription": "Personalitza les dreceres de comandament i copiar, enganxar i copiar al terminal", + "manageShortcuts": "Gestiona", "terminalLinkBehavior": "Clic d'enllaç del terminal", "terminalLinkBehaviorDesc": "Comportament predeterminat en fer clic a enllaços al terminal", "historyTracking": "Seguiment de l'historial", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Inclou la teva clau a la", "apiKeyUsageHintHeader": "capçalera.", "apiKeyPermissionsHint": "Les claus hereten els permisos de l'usuari que les crea.", + "exportData": "Exporta les meves dades", + "exportDataDesc": "Baixeu una còpia de seguretat dels vostres amfitrions, credencials i configuració per transferir-la a un altre dispositiu", + "export": "Exporta", + "exporting": "Exportant...", + "importData": "Importa les meves dades", + "importDataDesc": "Restaura els teus hosts, credencials i configuració des d'un fitxer de còpia de seguretat .sqlite", + "importDataSelected": "Seleccionat: {{name}}", + "selectFile": "Selecciona un fitxer", + "changeFile": "Canvi", + "import": "Importa", + "importing": "Important...", + "exportSuccess": "Dades exportades correctament", + "exportFailed": "L'exportació 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}}", "roleUser": "Usuari", "authMethodDual": "Doble autenticació", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "Re Pàg", "pageDown": "Av Pàg", "delete": "Eliminar", + "paste": "Enganxa", "editQuickKeys": "Edita les tecles ràpides", "quickKeysTitle": "Tecles ràpides", "quickKeysDesc": "Toca × per eliminar. Admet fins a 8 caràcters.", diff --git a/src/ui/locales/translated/cs_CZ.json b/src/ui/locales/translated/cs_CZ.json index 27c8d253..0f2e385e 100644 --- a/src/ui/locales/translated/cs_CZ.json +++ b/src/ui/locales/translated/cs_CZ.json @@ -389,6 +389,45 @@ "noSavedServers": "Žádné uložené servery", "removeServer": "Odstranit" }, + "migrationNotice": { + "title": "Termix Desktop nyní běží samostatně", + "body1": "Tato aplikace se dříve připojovala přímo k vašemu serveru Termix. Byla přepracována tak, aby běžela plně samostatně a lokálně ukládala hostitele a přihlašovací údaje, takže fungovala i offline. Obousměrná synchronizace se samostatně hostovaným serverem Termix je nyní volitelná.", + "body2": "Vaši stávající hostitelé a přihlašovací údaje jsou stále na {{url}}. Zapněte vzdálenou synchronizaci a znovu se připojte k danému serveru, abyste je obnovili a udrželi obojí synchronizované i v budoucnu.", + "dismiss": "Teď ne", + "setUpSync": "Nastavení vzdálené synchronizace" + }, + "remoteSync": { + "title": "Vzdálená synchronizace", + "description": "Volitelně můžete tuto desktopovou aplikaci připojit k serveru Termix hostovanému na vlastním serveru a synchronizovat hostitele, přihlašovací údaje a úryvky kódu napříč zařízeními. Aplikace vždy funguje plně offline, ať už se připojíte, nebo ne.", + "notConnected": "Nepřipojeno", + "connected": "Připojeno", + "connectedTo": "Připojeno k {{url}}", + "lastSynced": "Naposledy synchronizováno {{time}}", + "neverSynced": "Nikdy nesynchronizováno", + "syncError": "Chyba synchronizace: {{message}}", + "needsReauth": "Platnost přihlášení vypršela", + "connectButton": "Připojení k serveru", + "disconnectButton": "Odpojit", + "syncNowButton": "Synchronizovat nyní", + "syncing": "Synchronizace...", + "serverUrl": "URL serveru", + "enterServerUrl": "Zadejte prosím URL adresu serveru", + "mustIncludeProtocol": "URL serveru musí začínat http:// nebo https://", + "connectionTestFailed": "Na dané URL adrese se nepodařilo spojit se se serverem Termix.", + "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.", + "savedServers": "Uložené servery", + "removeServer": "Odstranit", + "continueButton": "Pokračovat", + "cancelButton": "Zrušit", + "signInTitle": "Přihlaste se do {{url}}", + "originTitle": "Původ připojení", + "originDescription": "Ve výchozím nastavení vyberte, odkud SSH připojení pocházejí. Toto nastavení lze u jednotlivých hostitelů přepsat.", + "originLocal": "Toto zařízení (lokální síť)", + "originRemote": "Vzdálený server", + "bannerReconnect": "Znovu se připojte", + "bannerMessage": "Vzdálená synchronizace vyžaduje opětovné ověření" + }, "versionCheck": { "error": "Chyba kontroly verze", "checkFailed": "Nepodařilo se zkontrolovat aktualizace", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Přepsat uživatelské jméno pro přihlašovací údaje", "overrideCredentialUsernameDesc": "Použijte výše uvedené uživatelské jméno místo uživatelského jména z přihlašovacích údajů.", "oidcUsernameHint": "Použijte $oidc.preferred_username jako náhradu za vaše přihlašovací jméno OIDC.", + "tailscaleUsernameHint": "Musí se jednat o uživatele Unixu, kterému je vaše identita Tailscale udělena v SSH ACL tailnetu, nikoli nutně o uživatele root.", "jumpHostChain": "Přejít k hostitelskému řetězci", "portKnocking": "Klepnutí na přístav", "addKnock": "Přidat port", @@ -650,6 +690,11 @@ "delayAfterMs": "Zpoždění po (ms)", "useSocks5Proxy": "Použít proxy SOCKS5", "useSocks5ProxyDesc": "Směrování připojení přes proxy server", + "connectionOrigin": "Původ připojení", + "connectionOriginDesc": "Odkud pochází SSH připojení tohoto hostitele. Přepíše globální výchozí nastavení desktopové aplikace.", + "connectionOriginDefault": "Použít výchozí nastavení", + "connectionOriginLocal": "Toto zařízení (lokální síť)", + "connectionOriginRemote": "Vzdálený server", "proxyHost": "Proxy hostitel", "proxyPort": "Proxy port", "proxyUsername": "Uživatelské jméno proxy serveru", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Jasně azurová", "customThemeBrightWhite": "Jasně bílá", "customThemeResetTooltip": "Obnovit na výchozí nastavení", + "savedThemesLabel": "Uložená témata", + "saveAsGlobalTheme": "Uložit jako globální motiv", + "saveGlobalThemeNamePrompt": "Zadejte název tohoto motivu", + "saveGlobalThemeSuccess": "Téma uloženo", + "saveGlobalThemeError": "Uložení motivu se nezdařilo", + "applyGlobalThemeTooltip": "Použít toto téma", + "deleteGlobalThemeTooltip": "Smazat toto téma", + "noSavedThemes": "Zatím žádné uložené motivy", "syntaxHighlightingLabel": "Zvýrazňování syntaxe", "syntaxHighlightingDesc": "Obarvení výstupu terminálu (chyby, cesty, IP adresy, časová razítka)", "syntaxHighlightingCategories": "Zvýraznit kategorie", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Automatické spuštění nebo připojení k relaci tmux", "enableSessionLogging": "Protokolování relací", "enableSessionLoggingDesc": "Zaznamenejte výstup terminálové relace pro pozdější kontrolu", + "allowSessionSharing": "Povolit sdílení relace", + "allowSessionSharingDesc": "Umožnit sdílení živých relací na tomto hostiteli prostřednictvím odkazu nebo s ostatními uživateli", "enableCommandHistory": "Historie příkazů", "enableCommandHistoryDesc": "Zaznamenávejte příkazy spouštěné v tomto terminálu pro historii a automatické dokončování", "linkClickBehaviorLabel": "Chování při kliknutí na odkaz", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importováno", "proxmoxResultUpdated": "{{count}} aktualizováno", "proxmoxResultFailed": "{{count}} selhalo", + "proxmoxResultSkippedNoIp": "{{count}} přeskočeno (nenalezena žádná IP adresa)", "proxmoxImportComplete": "Import Proxmoxu dokončen: {{summary}}", "proxmoxDiscoveryFailed": "Objev selhal", "proxmoxImportFailed": "Import se nezdařil", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Uložení přihlašovacích údajů se nezdařilo", "credentialNameRequired": "Zadejte prosím název pro přihlašovací údaje", "credentialAuthRequired": "Přidejte heslo, SSH klíč nebo obojí", + "createCredentialFromHostBtn": "Vytvořit pověření", + "createCredentialFromHostTitle": "Vytvořit přihlašovací údaje od hostitele", + "createCredentialFromHostDesc": "Vytvořte opakovaně použitelný a sdílený záznam o přihlašovacích údajích, který bude předvyplněn aktuálním uživatelským jménem, heslem a/nebo SSH klíčem tohoto hostitele.", "backToHosts": "Zpět k hostitelům", "backToCredentials": "Zpět k pověřovacím dokumentům", "pinned": "Připnuto", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Pomocí kláves / oddělte úrovně a vytvořte vnořené složky.", "folderColor": "Barva", "folderIcon": "Ikona", + "folderCredential": "Pověření", + "folderCredentialNone": "Nebyly přiřazeny žádné pověření", + "folderCredentialHint": "Hostitelé v této složce, kteří používají ověřování „Uložené přihlašovací údaje“, aniž by měli vybrány vlastní přihlašovací údaje, zdědí tento.", "folderPreview": "Náhled", "folderNameFallback": "Nepojmenovaná složka", "createFolderButton": "Vytvořit složku", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Štítky", "shareHost": "Sdílení hostitele", "shareHostTitle": "Sdílet: {{name}}", + "shareFolder": "Sdílet složku", + "shareFolderTitle": "Sdílená složka: {{name}}", + "folderSharedSuccessfully": "Sdílených {{count}} hostitelů ve složce", + "failedToShareFolder": "Sdílení složky se nezdařilo", "sharing": { "loadError": "Nepodařilo se načíst data sdílení. Zkuste to prosím znovu.", "shareWithSection": "Sdílet s", @@ -1221,6 +1287,7 @@ "shareWithCount": "Sdílet ({{count}})", "currentAccess": "Aktuální přístup", "noAccessEntries": "Tento hostitel ještě nebyl sdílen", + "folderShareSummary": "Sdílené {{shared}} z {{total}} hostitelů v této složce", "grantedBy": "Uděleno", "expires": "Platnost vyprší", "expired": "Platnost vypršela", @@ -1242,6 +1309,8 @@ "authMethod": "Metoda ověřování", "authTypeDirect": "Řídit", "authTypeCredential": "Pověření", + "authTypeNone": "Žádný", + "authTypeNoneDesc": "Nejsou uloženy žádné přihlašovací údaje. Při každém připojení budete vyzváni k zadání uživatelského jména a hesla; tyto údaje se neukládají.", "selectCredential": "Vyberte pověření...", "connectionSettings": "Nastavení připojení", "displaySettings": "Nastavení displeje", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP port", "username": "Uživatelské jméno", "password": "Heslo", + "passwordSaved": "Heslo uloženo, zadejte heslo pro změnu", "domain": "Doména", "securityMode": "Bezpečnostní režim", "colorDepth": "Barevná hloubka", @@ -1424,7 +1494,60 @@ "expiresIn": "Platnost vyprší za {{duration}}", "search": "Hledat spojení...", "noSearchResults": "Vašemu vyhledávání neodpovídají žádná spojení", - "rename": "Přejmenovat relaci" + "rename": "Přejmenovat relaci", + "sectionSharedWithMe": "Sdíleno se mnou", + "sharedBy": "Sdíleno uživatelem {{username}}", + "join": "Připojit se", + "sharedSessionLabel": "{{hostName}} (sdílené)" + }, + "sessionSharing": { + "guestView": { + "loading": "Připojování ke sdílené relaci...", + "linkInvalid": "Tento odkaz pro sdílení je neplatný, jeho platnost vypršela nebo byl zrušen.", + "rateLimited": "Příliš mnoho pokusů, zkuste to prosím znovu brzy", + "sessionEnded": "Tato relace skončila", + "readOnlyBadge": "Pouze zobrazení" + }, + "modalTitle": "Sdílení relace", + "shareButton": "Podíl", + "notReadyToShare": "Relace ještě není připravena ke sdílení", + "modeTab": { + "link": "Odkaz", + "user": "Uživatel" + }, + "linkModeDescription": "Připojit se může kdokoli s tímto odkazem, účet není potřeba.", + "userModeDescription": "Sdílejte s konkrétním uživatelem, který již má k tomuto hostiteli přístup. Pokud ještě nemá přístup, nejprve s ním hostitele sdílejte nebo použijte odkaz. Po sdílení se relace zobrazí na jeho kartě Připojení.", + "permissionLevel": { + "label": "Úroveň oprávnění", + "readOnly": "Pouze pro čtení", + "readOnlyDescription": "Může sledovat relaci živě, ale nemůže psát ani interagovat.", + "readWrite": "Čtení a zápis", + "readWriteDescription": "Může psát a interagovat s relací stejně jako její vlastník." + }, + "expiryLabel": "Platnost vypršení platnosti odkazu", + "createLinkButton": "Vytvořit odkaz", + "createShareButton": "Sdílet s uživatelem", + "searchUsersPlaceholder": "Hledat uživatele...", + "noUsersFound": "Nenaleženi žádní uživatelé", + "linkCreated": "Odkaz pro sdílení vytvořen", + "linkCopied": "Odkaz zkopírován do schránky", + "copyLink": "Kopírovat odkaz", + "shareCreated": "Relace sdílena. Zobrazí se na jejich kartě Připojení.", + "shareFailed": "Vytvoření sdílené položky se nezdařilo", + "userLacksHostAccess": "Tento uživatel zatím nemá přístup k tomuto hostiteli. Nejprve s ním hostitele sdílejte nebo použijte odkaz.", + "activeShares": "Aktivní akcie", + "noActiveShares": "Pro tuto relaci nejsou žádné aktivní sdílení", + "revoke": "Zrušit", + "revokeConfirmTitle": "Zrušit toto sdílení?", + "revokeConfirmDescription": "Každý, kdo tuto sdílenou složku používá, okamžitě ztratí přístup.", + "revoked": "Podíl zrušen", + "revokeFailed": "Zrušení sdílení se nezdařilo", + "joinCount": "{{count}} připojit se", + "joinCount_other": "{{count}} se připojí", + "expiresAt": "Platnost vyprší {{date}}", + "linkShareBadge": "Odkaz", + "userShareBadge": "Uživatel: {{username}}", + "loadSharesFailed": "Nepodařilo se načíst aktivní sdílení" }, "guacamole": { "connecting": "Připojování k relaci {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Znovu se připojte", "retry": "Zkusit znovu", "guacdUnavailable": "Služba vzdálené plochy (guacd) není k dispozici. Ujistěte se prosím, že je guacd spuštěná, přístupná a správně nakonfigurovaná v nastavení administrátora.", + "credentialPromptTitle": "Zadejte přihlašovací údaje RDP", + "credentialPromptDescription": "Tento hostitel je nastaven tak, aby při připojení vyžadoval přihlašovací údaje. Tyto přihlašovací údaje se používají pouze pro tuto relaci a neukládají se.", + "connect": "Připojit", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Znovu připojit relaci", "collapse": "Sbalit panel nástrojů", "expand": "Rozbalit panel nástrojů", - "dragHandle": "Přetažením změníte pozici" + "dragHandle": "Přetažením změníte pozici", + "switchToTrackpad": "Přepnout do režimu trackpadu (tažením přesunete kurzor, klepnutím kliknete)", + "switchToTouch": "Přepnout do dotykového režimu (klepněte přímo na místo, kam chcete kliknout)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Vyžadováno dvoufaktorové ověřování", "totpCodeLabel": "Ověřovací kód", "totpVerify": "Ověřit", + "mfaPromptRequired": "Vyžadováno ověření", + "mfaPushRequired": "Vyžadováno ověřování push transakcí", + "mfaMenuPlaceholder": "Zadejte svou odpověď", + "mfaWaitingApproval": "Čekání na schválení na vašem zařízení...", + "mfaSendRequest": "Odeslat požadavek", "warpgateAuthRequired": "Vyžadováno ověřování Warpgate", "warpgateSecurityKey": "Bezpečnostní klíč", "warpgateAuthUrl": "URL ověřování", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Omezená rychlost: Příliš mnoho pokusů o ověření. Zkuste to prosím znovu později.", "resetCodeRateLimitedWithTime": "Omezená rychlost: Příliš mnoho pokusů o ověření. Před dalším pokusem prosím počkejte {{time}} sekund.", "authTokenSaveFailed": "Uložení ověřovacího tokenu se nezdařilo", - "failedToLoadServer": "Nepodařilo se načíst server" + "failedToLoadServer": "Nepodařilo se načíst server", + "remoteServerRequired": "Je vyžadován vzdálený server. Pro použití tohoto typu připojení se připojte ke vzdálenému serveru v Nastavení." }, "messages": { "registrationDisabled": "Registrace nového účtu je momentálně zakázána administrátorem. Přihlaste se prosím nebo kontaktujte administrátora.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Nepodařilo se uložit nastavení SSL", "sslRequiresDomain": "Doména a e-mail jsou povinné", "sslInfoNote": "Po vystavení certifikátu povolte SSL v proměnných prostředí (ENABLE_SSL=true) a restartujte Termix.", + "sslManualOption": "Manuál (nahrání certifikátu)", + "sslManualCert": "Certifikát (PEM)", + "sslManualCertPlaceholder": "-----ZAČÁTEK CERTIFIKÁTU-----", + "sslManualKey": "Soukromý klíč (PEM)", + "sslManualKeyPlaceholder": "-----ZAČÁTEK SOUKROMÉHO KLÍČE-----", + "sslManualDesc": "Vložte svůj stávající certifikát a soukromý klíč, včetně celého řetězce, pokud to vaše certifikační autorita vyžaduje.", + "sslManualUpload": "Nahrát a nainstalovat certifikát", + "sslManualUploadLoading": "Nahrávání certifikátu...", + "sslManualUploadSuccess": "Certifikát byl úspěšně nahrán a nainstalován.", + "sslManualUploadFailed": "Nahrávání certifikátu se nezdařilo", + "sslManualRequiresFields": "Je vyžadován certifikát a soukromý klíč", "auditLogTotal": "{{total}} celkem záznamů", "auditLogEmpty": "Nenalezeny žádné položky protokolu auditu", "auditLogSuccess": "Úspěch", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Historie příkazů", "commandHistoryEnabledDesc": "Povolit záznam historie příkazů. Pokud je tato možnost zakázána, historie se neukládá bez ohledu na nastavení pro jednotlivé hostitele.", "updateCommandHistoryFailed": "Nepodařilo se aktualizovat nastavení historie příkazů.", + "analyticsEnabled": "Sdílení anonymních statistik používání", + "analyticsEnabledDesc": "Odesílá anonymní denní počet uživatelů, hostitelů a využití funkcí, aby pomohl vylepšit Termix. Nikdy neobsahuje žádné osobní údaje ani podrobnosti o připojení.", + "updateAnalyticsFailed": "Nepodařilo se aktualizovat nastavení analytických nástrojů", + "sessionSharingGloballyEnabled": "Povolit sdílení relace", + "sessionSharingGloballyEnabledDesc": "Povolit sdílení relací živého terminálu, RDP, VNC a Telnet v rámci celé instance. Pokud je tato možnost zakázána, přepíše všechny přepínače sdílení pro jednotlivé hostitele.", + "updateSessionSharingFailed": "Nepodařilo se aktualizovat nastavení sdílení relace", "sessionTimeout": "Časový limit relace", "hours": "hodiny", "sessionTimeoutRange": "Min. 1h · Max. 720h", @@ -3119,6 +3270,47 @@ "executionSuccess": "Úspěch", "executionFailed": "Selhalo" }, + "keybindings": { + "title": "Klávesové zkratky", + "description": "Přizpůsobte si klávesové zkratky pro kopírování, vkládání a ovládání v terminálu nebo propojte klávesy pro odeslání textu nebo spuštění úryvku kódu.", + "defaultsHeading": "Vestavěné zkratky", + "customHeading": "Vlastní zkratky", + "addBinding": "Přidat zkratku", + "addBindingTitle": "Přidat zkratku", + "editBindingTitle": "Upravit zkratku", + "loading": "Načítání...", + "noCustomBindings": "Zatím žádné vlastní zkratky", + "defaultBadge": "Výchozí", + "customizedBadge": "Přizpůsobeno", + "customize": "Přizpůsobit", + "resetToDefault": "Obnovit výchozí nastavení", + "resetAllToDefaults": "Obnovit vše na výchozí nastavení", + "close": "Blízko", + "cancel": "Zrušit", + "saveBinding": "Uložit zástupce", + "saveError": "Uložení klávesových zkratek se nezdařilo", + "pressKeysToRecord": "Kombinace kláves", + "pressKeysPlaceholder": "Klikněte a stiskněte klávesy", + "recording": "Stiskněte klávesy...", + "comboRequiredError": "Nejprve stiskněte kombinaci kláves", + "textRequiredError": "Zadejte text k odeslání", + "controlCodeRequiredError": "Zadejte jedno písmeno pro kontrolní kód", + "snippetRequiredError": "Vyberte úryvek", + "conflictWarning": "Tato kombinace je již vázána na {{combo}}. Uložením se obě spustí.", + "actionLabel": "Akce", + "actionCopy": "Kopírovat výběr", + "actionPaste": "Pasta", + "actionSendControlCode": "Odeslat signál Ctrl+písmeno", + "actionSendText": "Odeslat text", + "actionRunSnippet": "Spustit existující úryvek kódu", + "controlCodeLabel": "Dopis", + "textLabel": "Text k odeslání", + "appendEnterLabel": "Po odeslání stiskněte Enter", + "snippetLabel": "Úryvek", + "selectSnippetPlaceholder": "Vyberte úryvek", + "orphanedSnippetWarning": "úryvek nenalezen", + "clipboardPermissionNote": "V některých prohlížečích se může při prvním použití zobrazit výzva k zadání povolení ke schránce." + }, "userProfile": { "donateTitle": "Udrž Termixe naživu", "donateDescription": "Termix je zdarma, hostovaný na vlastních platformách a vyvíjí ho jen několik lidí ve svém volném čase. Pokud vám to ušetřilo čas nebo peníze, dar kryptoměn vám pomůže udržet ho v chodu.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Vzhled", "sectionSecurity": "Zabezpečení", "sectionApiKeys": "Klíče API", + "sectionData": "Data", "sectionC2sTunnels": "Tunely C2S", "usernameLabel": "Uživatelské jméno", "roleLabel": "Role", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminál", "commandAutocomplete": "Automatické dokončování příkazů", "commandAutocompleteDesc": "Zobrazit automatické doplňování při psaní", + "keyboardShortcuts": "Klávesové zkratky", + "keyboardShortcutsDescription": "Přizpůsobení zkratek pro kopírování, vkládání a příkazy v terminálu", + "manageShortcuts": "Spravovat", "terminalLinkBehavior": "Kliknutí na odkaz terminálu", "terminalLinkBehaviorDesc": "Výchozí chování při kliknutí na odkazy v terminálu", "historyTracking": "Sledování historie", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Vložte svůj klíč do", "apiKeyUsageHintHeader": "záhlaví.", "apiKeyPermissionsHint": "Klíče dědí oprávnění uživatele, který je vytvořil.", + "exportData": "Exportovat má data", + "exportDataDesc": "Stáhněte si zálohu svých hostitelů, přihlašovacích údajů a nastavení pro přenos do jiného zařízení", + "export": "Vývozní", + "exporting": "Export...", + "importData": "Importovat má data", + "importDataDesc": "Obnovte své hostitele, přihlašovací údaje a nastavení ze záložního souboru .sqlite", + "importDataSelected": "Vybráno: {{name}}", + "selectFile": "Vybrat soubor", + "changeFile": "Přeměna", + "import": "Importovat", + "importing": "Import...", + "exportSuccess": "Data byla úspěšně exportována", + "exportFailed": "Export dat se nezdařil", + "importSelectFile": "Nejprve prosím vyberte soubor", + "importCompleted": "Import dokončen: {{total}} položek importováno, {{skipped}} přeskočeno", + "importFailed": "Import se nezdařil: {{error}}", "roleUser": "Uživatel", "authMethodDual": "Duální ověřování", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Pasta", "editQuickKeys": "Upravit rychlé klávesy", "quickKeysTitle": "Rychlé klávesy", "quickKeysDesc": "Klepnutím na × odstraníte. Podporuje až 8 znaků.", diff --git a/src/ui/locales/translated/da_DK.json b/src/ui/locales/translated/da_DK.json index afa4b973..55da78ee 100644 --- a/src/ui/locales/translated/da_DK.json +++ b/src/ui/locales/translated/da_DK.json @@ -389,6 +389,45 @@ "noSavedServers": "Ingen gemte servere", "removeServer": "Fjerne" }, + "migrationNotice": { + "title": "Termix Desktop kører nu selvstændigt", + "body1": "Denne app plejede at oprette forbindelse direkte til din Termix-server. Den er blevet omarbejdet til at køre fuldt ud alene og gemmer værter og legitimationsoplysninger lokalt, så den fortsætter med at fungere offline. Tovejssynkronisering med en selvhostet Termix-server er nu valgfri.", + "body2": "Dine eksisterende værter og loginoplysninger er stadig på {{url}}. Aktivér fjernsynkronisering, og opret forbindelse til serveren igen for at få dem tilbage og holde begge synkroniseret fremadrettet.", + "dismiss": "Ikke nu", + "setUpSync": "Opsæt fjernsynkronisering" + }, + "remoteSync": { + "title": "Fjernsynkronisering", + "description": "Du kan eventuelt tilslutte denne desktop-app til en selvhostet Termix-server for at synkronisere dine værter, legitimationsoplysninger og kodestykker på tværs af enheder. Appen fungerer altid fuldt offline, uanset om du opretter forbindelse eller ej.", + "notConnected": "Ikke forbundet", + "connected": "Forbundet", + "connectedTo": "Forbundet til {{url}}", + "lastSynced": "Sidst synkroniseret {{time}}", + "neverSynced": "Aldrig synkroniseret", + "syncError": "Synkroniseringsfejl: {{message}}", + "needsReauth": "Login udløbet", + "connectButton": "Opret forbindelse til serveren", + "disconnectButton": "Afbryde", + "syncNowButton": "Synkroniser nu", + "syncing": "Synkroniserer...", + "serverUrl": "Server-URL", + "enterServerUrl": "Indtast venligst en server-URL", + "mustIncludeProtocol": "Server-URL skal starte med http:// eller https://", + "connectionTestFailed": "Kunne ikke nå en Termix-server på den URL", + "allowInvalidCertificate": "Tillad ugyldigt certifikat", + "allowInvalidCertificateDesc": "Brug kun til betroede, selvhostede servere med selvsignerede certifikater eller IP-adressecertifikater.", + "savedServers": "Gemte servere", + "removeServer": "Fjerne", + "continueButton": "Fortsætte", + "cancelButton": "Ophæve", + "signInTitle": "Log ind på {{url}}", + "originTitle": "Forbindelsens oprindelse", + "originDescription": "Vælg som standard, hvor SSH-forbindelser stammer fra. Dette kan tilsidesættes pr. vært.", + "originLocal": "Denne enhed (lokalt netværk)", + "originRemote": "Fjernserver", + "bannerReconnect": "Genopret forbindelse", + "bannerMessage": "Fjernsynkronisering skal godkendes igen" + }, "versionCheck": { "error": "Versionskontrolfejl", "checkFailed": "Kunne ikke søge efter opdateringer", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Tilsidesæt brugernavn til legitimationsoplysninger", "overrideCredentialUsernameDesc": "Brug det ovenfor angivne brugernavn i stedet for legitimationsoplysningernes brugernavn", "oidcUsernameHint": "Brug $oidc.preferred_username til at erstatte dit OIDC-loginnavn.", + "tailscaleUsernameHint": "Dette skal være en Unix-bruger, som din Tailscale-identitet er tildelt i tailnets SSH ACL, ikke nødvendigvis root.", "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", "addKnock": "Tilføj port", @@ -650,6 +690,11 @@ "delayAfterMs": "Forsinkelse efter (ms)", "useSocks5Proxy": "Brug SOCKS5 Proxy", "useSocks5ProxyDesc": "Rut forbindelsen via en proxyserver", + "connectionOrigin": "Forbindelsens oprindelse", + "connectionOriginDesc": "Hvor denne værts SSH-forbindelse stammer fra. Tilsidesætter desktop-appens globale standard.", + "connectionOriginDefault": "Brug standard", + "connectionOriginLocal": "Denne enhed (lokalt netværk)", + "connectionOriginRemote": "Fjernserver", "proxyHost": "Proxy-vært", "proxyPort": "Proxyport", "proxyUsername": "Proxy-brugernavn", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Lys cyan", "customThemeBrightWhite": "Klar hvid", "customThemeResetTooltip": "Nulstil til standardindstillinger", + "savedThemesLabel": "Gemte temaer", + "saveAsGlobalTheme": "Gem som globalt tema", + "saveGlobalThemeNamePrompt": "Indtast et navn til dette tema", + "saveGlobalThemeSuccess": "Tema gemt", + "saveGlobalThemeError": "Temaet kunne ikke gemmes", + "applyGlobalThemeTooltip": "Anvend dette tema", + "deleteGlobalThemeTooltip": "Slet dette tema", + "noSavedThemes": "Ingen gemte temaer endnu", "syntaxHighlightingLabel": "Syntaksfremhævning", "syntaxHighlightingDesc": "Farvlæg terminaloutput (fejl, stier, IP'er, tidsstempler)", "syntaxHighlightingCategories": "Fremhæv kategorier", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Start eller tilslut automatisk til tmux-session", "enableSessionLogging": "Sessionslogning", "enableSessionLoggingDesc": "Optag output fra terminalsessionen til senere gennemgang", + "allowSessionSharing": "Tillad deling af sessioner", + "allowSessionSharingDesc": "Tillad live-sessioner på denne vært at blive delt via link eller med andre brugere", "enableCommandHistory": "Kommandohistorik", "enableCommandHistoryDesc": "Optagelseskommandoer køres i denne terminal for historik og autofuldførelse", "linkClickBehaviorLabel": "Linkkliksadfærd", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importeret", "proxmoxResultUpdated": "{{count}} opdateret", "proxmoxResultFailed": "{{count}} mislykkedes", + "proxmoxResultSkippedNoIp": "{{count}} sprunget over (ingen IP fundet)", "proxmoxImportComplete": "Proxmox-import fuldført: {{summary}}", "proxmoxDiscoveryFailed": "Opdagelsen mislykkedes", "proxmoxImportFailed": "Importen mislykkedes", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Kunne ikke gemme legitimationsoplysninger", "credentialNameRequired": "Indtast venligst et navn til legitimationsoplysningerne", "credentialAuthRequired": "Tilføj en adgangskode, en SSH-nøgle eller begge dele", + "createCredentialFromHostBtn": "Opret legitimationsoplysninger", + "createCredentialFromHostTitle": "Opret legitimationsoplysninger fra vært", + "createCredentialFromHostDesc": "Opret en genanvendelig, delbar legitimationsoplysningspost, der er forudfyldt med denne værts nuværende brugernavn, adgangskode og/eller SSH-nøgle.", "backToHosts": "Tilbage til værter", "backToCredentials": "Tilbage til legitimationsoplysninger", "pinned": "Fastgjort", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Brug / til at adskille niveauer og oprette indbyggede mapper.", "folderColor": "Farve", "folderIcon": "Ikon", + "folderCredential": "Legitimationsoplysninger", + "folderCredentialNone": "Ingen legitimationsoplysninger tildelt", + "folderCredentialHint": "Værter i denne mappe, der bruger godkendelsen \"Gemte legitimationsoplysninger\" uden at have valgt deres egne legitimationsoplysninger, arver denne.", "folderPreview": "Forhåndsvisning", "folderNameFallback": "Unavngivet mappe", "createFolderButton": "Opret mappe", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tags", "shareHost": "Del vært", "shareHostTitle": "Del: {{name}}", + "shareFolder": "Del mappe", + "shareFolderTitle": "Del mappe: {{name}}", + "folderSharedSuccessfully": "Delte {{count}} vært(er) i mappe", + "failedToShareFolder": "Mappen kunne ikke deles", "sharing": { "loadError": "Delingsdataene kunne ikke indlæses. Prøv igen.", "shareWithSection": "Del med", @@ -1221,6 +1287,7 @@ "shareWithCount": "Del ({{count}})", "currentAccess": "Nuværende adgang", "noAccessEntries": "Denne vært er ikke blevet delt endnu", + "folderShareSummary": "Delte {{shared}} af {{total}} vært(er) i denne mappe", "grantedBy": "Tildelt af", "expires": "Udløber", "expired": "Udløbet", @@ -1242,6 +1309,8 @@ "authMethod": "Godkendelsesmetode", "authTypeDirect": "Direkte", "authTypeCredential": "Legitimationsoplysninger", + "authTypeNone": "Ingen", + "authTypeNoneDesc": "Ingen loginoplysninger gemmes. Du vil blive bedt om et brugernavn og en adgangskode, hver gang du opretter forbindelse; de gemmes ikke.", "selectCredential": "Vælg en legitimationsoplysninger...", "connectionSettings": "Forbindelsesindstillinger", "displaySettings": "Skærmindstillinger", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-port", "username": "Brugernavn", "password": "Adgangskode", + "passwordSaved": "Adgangskode gemt, skriv for at ændre", "domain": "Domæne", "securityMode": "Sikkerhedstilstand", "colorDepth": "Farvedybde", @@ -1424,7 +1494,60 @@ "expiresIn": "Udløber om {{duration}}", "search": "Søg efter forbindelser...", "noSearchResults": "Ingen forbindelser matcher din søgning", - "rename": "Omdøb session" + "rename": "Omdøb session", + "sectionSharedWithMe": "Delt med mig", + "sharedBy": "Delt af {{username}}", + "join": "Deltag", + "sharedSessionLabel": "{{hostName}} (delt)" + }, + "sessionSharing": { + "guestView": { + "loading": "Forbinder til delt session...", + "linkInvalid": "Dette delingslink er ugyldigt, udløbet eller er blevet tilbagekaldt", + "rateLimited": "For mange forsøg, prøv igen om lidt", + "sessionEnded": "Denne session er afsluttet", + "readOnlyBadge": "Kun visning" + }, + "modalTitle": "Del session", + "shareButton": "Dele", + "notReadyToShare": "Sessionen er ikke klar til deling endnu", + "modeTab": { + "link": "Forbindelse", + "user": "Bruger" + }, + "linkModeDescription": "Alle med dette link kan tilmelde sig, ingen konto kræves.", + "userModeDescription": "Del med en bestemt bruger, der allerede har adgang til denne vært. Hvis de ikke har adgang endnu, skal du først dele værten med dem eller bruge et link i stedet. Når sessionen er delt, vises den under deres fane Forbindelser.", + "permissionLevel": { + "label": "Tilladelsesniveau", + "readOnly": "Skrivebeskyttet", + "readOnlyDescription": "Kan se sessionen live, men kan ikke skrive eller interagere.", + "readWrite": "Læs-skriv", + "readWriteDescription": "Kan skrive og interagere med sessionen ligesom ejeren." + }, + "expiryLabel": "Linkudløb", + "createLinkButton": "Opret link", + "createShareButton": "Del med brugeren", + "searchUsersPlaceholder": "Søg efter brugere...", + "noUsersFound": "Ingen brugere fundet", + "linkCreated": "Delingslink oprettet", + "linkCopied": "Link kopieret til udklipsholder", + "copyLink": "Kopiér link", + "shareCreated": "Session delt. Den vises i fanen Forbindelser.", + "shareFailed": "Kunne ikke oprette deling", + "userLacksHostAccess": "Den bruger har endnu ikke adgang til denne vært. Del værten med dem først, eller brug et link i stedet.", + "activeShares": "Aktive aktier", + "noActiveShares": "Ingen aktive delinger for denne session", + "revoke": "Tilbagekald", + "revokeConfirmTitle": "Tilbagekald denne deling?", + "revokeConfirmDescription": "Enhver, der bruger denne deling, mister adgangen med det samme.", + "revoked": "Deling tilbagekaldt", + "revokeFailed": "Delingen kunne ikke tilbagekaldes", + "joinCount": "{{count}} tilmeld dig", + "joinCount_other": "{{count}} deltager", + "expiresAt": "Udløber {{date}}", + "linkShareBadge": "Forbindelse", + "userShareBadge": "Bruger: {{username}}", + "loadSharesFailed": "Kunne ikke indlæse aktive delinger" }, "guacamole": { "connecting": "Forbinder til {{type}} session...", @@ -1436,6 +1559,9 @@ "reconnect": "Genopret forbindelse", "retry": "Prøv igen", "guacdUnavailable": "Fjernskrivebordstjenesten (guacd) er ikke tilgængelig. Sørg for, at guacd kører, er tilgængelig og konfigureret korrekt i administratorindstillingerne.", + "credentialPromptTitle": "Indtast RDP-legitimationsoplysninger", + "credentialPromptDescription": "Denne vært er indstillet til at bede om legitimationsoplysninger ved forbindelse. De bruges kun til denne session og gemmes ikke.", + "connect": "Forbinde", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Genopret forbindelse til sessionen", "collapse": "Skjul værktøjslinjen", "expand": "Udvid værktøjslinjen", - "dragHandle": "Træk for at flytte" + "dragHandle": "Træk for at flytte", + "switchToTrackpad": "Skift til trackpad-tilstand (træk for at flytte markøren, tryk for at klikke)", + "switchToTouch": "Skift til berøringstilstand (tryk direkte der, hvor du vil klikke)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Tofaktorgodkendelse påkrævet", "totpCodeLabel": "Bekræftelseskode", "totpVerify": "Verificere", + "mfaPromptRequired": "Godkendelse påkrævet", + "mfaPushRequired": "Push-godkendelse påkrævet", + "mfaMenuPlaceholder": "Indtast dit svar", + "mfaWaitingApproval": "Venter på godkendelse på din enhed...", + "mfaSendRequest": "Send anmodning", "warpgateAuthRequired": "Warpgate-godkendelse påkrævet", "warpgateSecurityKey": "Sikkerhedsnøgle", "warpgateAuthUrl": "Godkendelses-URL", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Begrænset hastighed: For mange bekræftelsesforsøg. Prøv igen senere.", "resetCodeRateLimitedWithTime": "Begrænset hastighed: For mange bekræftelsesforsøg. Vent {{time}} sekunder, før du prøver igen.", "authTokenSaveFailed": "Kunne ikke gemme godkendelsestoken", - "failedToLoadServer": "Kunne ikke indlæse serveren" + "failedToLoadServer": "Kunne ikke indlæse serveren", + "remoteServerRequired": "Fjernserver kræves. Opret forbindelse til en fjernserver i Indstillinger for at bruge denne forbindelsestype." }, "messages": { "registrationDisabled": "Registrering af nye kontoer er i øjeblikket deaktiveret af en administrator. Log venligst ind eller kontakt en administrator.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Kunne ikke gemme SSL-indstillinger", "sslRequiresDomain": "Domæne og e-mail er påkrævet", "sslInfoNote": "Efter udstedelse af et certifikat, skal du aktivere SSL i dine miljøvariabler (ENABLE_SSL=true) og genstarte Termix.", + "sslManualOption": "Manual (upload certifikat)", + "sslManualCert": "Certifikat (PEM)", + "sslManualCertPlaceholder": "-----STARTCERTIFIKAT-----", + "sslManualKey": "Privat nøgle (PEM)", + "sslManualKeyPlaceholder": "-----START PRIVAT NØGLE-----", + "sslManualDesc": "Indsæt dit eksisterende certifikat og din private nøgle, inklusive en fuld kæde, hvis det kræves af din CA.", + "sslManualUpload": "Upload og installer certifikat", + "sslManualUploadLoading": "Uploader certifikat...", + "sslManualUploadSuccess": "Certifikatet er uploadet og installeret", + "sslManualUploadFailed": "Upload af certifikat mislykkedes", + "sslManualRequiresFields": "Certifikat og privat nøgle er påkrævet", "auditLogTotal": "{{total}} samlede poster", "auditLogEmpty": "Ingen poster i revisionsloggen fundet", "auditLogSuccess": "Succes", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Kommandohistorik", "commandHistoryEnabledDesc": "Tillad optagelse af kommandohistorik. Når den er deaktiveret, gemmes historikken ikke uanset indstillingerne for den enkelte vært.", "updateCommandHistoryFailed": "Kunne ikke opdatere indstillingen for kommandohistorik", + "analyticsEnabled": "Del anonym brugsstatistik", + "analyticsEnabledDesc": "Sender en anonym daglig optælling af brugere, værter og funktionsbrug for at forbedre Termix. Ingen personlige data eller forbindelsesoplysninger inkluderes nogensinde.", + "updateAnalyticsFailed": "Analyseindstillingen kunne ikke opdateres", + "sessionSharingGloballyEnabled": "Tillad deling af sessioner", + "sessionSharingGloballyEnabledDesc": "Tillad deling af live terminal-, RDP-, VNC- og Telnet-sessioner på tværs af instanser. Tilsidesætter alle delingsindstillinger pr. vært, når de er deaktiveret.", + "updateSessionSharingFailed": "Indstillingen for deling af session kunne ikke opdateres", "sessionTimeout": "Sessionstimeout", "hours": "timer", "sessionTimeoutRange": "Min. 1 time · Maks. 720 timer", @@ -3119,6 +3270,47 @@ "executionSuccess": "Succes", "executionFailed": "Mislykkedes" }, + "keybindings": { + "title": "Tastaturgenveje", + "description": "Tilpas genveje til kopiering, indsættelse og styring af terminaler, eller bind taster for at sende tekst eller køre et kodestykke.", + "defaultsHeading": "Indbyggede genveje", + "customHeading": "Brugerdefinerede genveje", + "addBinding": "Tilføj genvej", + "addBindingTitle": "Tilføj genvej", + "editBindingTitle": "Rediger genvej", + "loading": "Indlæser...", + "noCustomBindings": "Ingen brugerdefinerede genveje endnu", + "defaultBadge": "Misligholdelse", + "customizedBadge": "Tilpasset", + "customize": "Tilpas", + "resetToDefault": "Nulstil til standard", + "resetAllToDefaults": "Nulstil alle til standardindstillingerne", + "close": "Tæt", + "cancel": "Ophæve", + "saveBinding": "Gem genvej", + "saveError": "Kunne ikke gemme tastaturgenveje", + "pressKeysToRecord": "Tastekombination", + "pressKeysPlaceholder": "Klik og tryk på tasterne", + "recording": "Tryk på tasterne...", + "comboRequiredError": "Tryk først på en tastekombination", + "textRequiredError": "Indtast den tekst, der skal sendes", + "controlCodeRequiredError": "Indtast et enkelt bogstav for kontrolkoden", + "snippetRequiredError": "Vælg et uddrag", + "conflictWarning": "Denne kombination er allerede bundet til {{combo}}. Hvis du gemmer dem, aktiveres begge.", + "actionLabel": "Handling", + "actionCopy": "Kopiér valg", + "actionPaste": "Indsæt", + "actionSendControlCode": "Send Ctrl+bogstavsignal", + "actionSendText": "Send sms", + "actionRunSnippet": "Kør eksisterende kodestykke", + "controlCodeLabel": "Bogstav", + "textLabel": "Tekst der skal sendes", + "appendEnterLabel": "Tryk Enter efter afsendelse", + "snippetLabel": "Uddrag", + "selectSnippetPlaceholder": "Vælg et uddrag", + "orphanedSnippetWarning": "uddrag ikke fundet", + "clipboardPermissionNote": "Kan bede om tilladelse til udklipsholder ved første brug i nogle browsere." + }, "userProfile": { "donateTitle": "Hold Termix i live", "donateDescription": "Termix er gratis, selvhostet og bygget af kun få personer i deres fritid. Hvis det har sparet dig tid eller penge, kan en kryptodonation hjælpe med at holde det kørende.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Udseende", "sectionSecurity": "Sikkerhed", "sectionApiKeys": "API-nøgler", + "sectionData": "Data", "sectionC2sTunnels": "C2S-tunneler", "usernameLabel": "Brugernavn", "roleLabel": "Rolle", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Kommando Autofuldførelse", "commandAutocompleteDesc": "Vis autofuldførelse under indtastning", + "keyboardShortcuts": "Tastaturgenveje", + "keyboardShortcutsDescription": "Tilpas genveje til terminalkopiering, -indsætning og -kommandoer", + "manageShortcuts": "Styre", "terminalLinkBehavior": "Klik på terminallink", "terminalLinkBehaviorDesc": "Standardadfærd ved klik på links i terminalen", "historyTracking": "Historiksporing", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Medtag din nøgle i", "apiKeyUsageHintHeader": "overskrift.", "apiKeyPermissionsHint": "Nøgler arver tilladelserne fra den oprettende bruger.", + "exportData": "Eksporter mine data", + "exportDataDesc": "Download en sikkerhedskopi af dine værter, loginoplysninger og indstillinger for at overføre dem til en anden enhed.", + "export": "Eksportere", + "exporting": "Eksporterer...", + "importData": "Importer mine data", + "importDataDesc": "Gendan dine værter, legitimationsoplysninger og indstillinger fra en .sqlite-backupfil", + "importDataSelected": "Valgt: {{name}}", + "selectFile": "Vælg fil", + "changeFile": "Forandring", + "import": "Importere", + "importing": "Importerer...", + "exportSuccess": "Data eksporteret", + "exportFailed": "Dataeksport mislykkedes", + "importSelectFile": "Vælg venligst en fil først", + "importCompleted": "Import fuldført: {{total}} elementer importeret, {{skipped}} sprunget over", + "importFailed": "Import mislykkedes: {{error}}", "roleUser": "Bruger", "authMethodDual": "Dobbeltgodkendelse", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "Side op", "pageDown": "PgDn", "delete": "Del", + "paste": "Indsæt", "editQuickKeys": "Rediger genvejstaster", "quickKeysTitle": "Hurtigtaster", "quickKeysDesc": "Tryk på × for at fjerne. Understøtter op til 8 tegn.", diff --git a/src/ui/locales/translated/de_DE.json b/src/ui/locales/translated/de_DE.json index 0de5e9ad..26556355 100644 --- a/src/ui/locales/translated/de_DE.json +++ b/src/ui/locales/translated/de_DE.json @@ -1,72 +1,72 @@ { "termixId": { - "title": "Termix-ID", - "loadFailed": "Termix-ID konnte nicht geladen werden", + "title": "Termix ID", + "loadFailed": "Fehler beim Laden der Termix-ID", "claimTitle": "Termix-ID beanspruchen", - "claimIntro": "Wählen Sie einen eindeutigen Benutzernamen. Ihre öffentlichen SSH-Schlüssel werden unter einer öffentlichen URL veröffentlicht, die Sie der authorized_keys-Datei eines beliebigen Servers hinzufügen können.", - "handleLabel": "Handle", + "claimIntro": "Wählen Sie einen eindeutigen Benutzernamen. Ihre öffentlichen SSH-Schlüssel werden unter einer öffentlichen URL veröffentlicht, die Sie in die authorized_keys-Datei eines beliebigen Servers einfügen können.", + "handleLabel": "Benutzername", "handlePlaceholder": "alice", - "checking": "Überprüfung…", + "checking": "Wird überprüft…", "available": "Verfügbar", "taken": "Bereits vergeben", - "invalidHandle": "Nur Kleinbuchstaben, Zahlen, - und _", + "invalidHandle": "Nur Kleinbuchstaben, Zahlen, - und _ erlaubt", "descriptionLabel": "Beschreibung (optional)", - "descriptionPlaceholder": "Arbeitslaptop- und Telefonschlüssel", + "descriptionPlaceholder": "Arbeits-Laptop & Telefonschlüssel", "create": "Termix-ID erstellen", "created": "Termix-ID erstellt", - "createFailed": "Termix-ID konnte nicht erstellt werden", - "deleteConfirm": "Ihre Termix-ID und alle veröffentlichten Schlüssel löschen? Die von Ihnen eingerichteten Server behalten die Schlüssel, bis Sie sie manuell entfernen.", + "createFailed": "Fehler beim Erstellen der Termix-ID", + "deleteConfirm": "Termix-ID und alle veröffentlichten Schlüssel löschen? Bereits bereitgestellte Server behalten die Schlüssel, bis Sie sie manuell entfernen.", "deleted": "Termix-ID gelöscht", - "deleteFailed": "Termix-ID konnte nicht gelöscht werden", + "deleteFailed": "Fehler beim Löschen der Termix-ID", "copyFailed": "Kopieren fehlgeschlagen", "resolverUrlLabel": "Öffentliche Resolver-URL", - "provisionLabel": "Einen Server bereitstellen", + "provisionLabel": "Server bereitstellen", "publishTitle": "Öffentlichen Schlüssel veröffentlichen", - "generate": "Erzeugen", - "generateTooltip": "Generieren Sie ein Ed25519-Schlüsselpaar – der öffentliche Schlüssel wird veröffentlicht und der private Schlüssel auf Ihr Gerät heruntergeladen.", - "saveToVault": "Unter Zugangsdaten speichern", + "generate": "Generieren", + "generateTooltip": "Ed25519-Schlüsselpaar generieren — veröffentlicht den öffentlichen Schlüssel und lädt den privaten Schlüssel auf Ihr Gerät herunter", + "saveToVault": "In Anmeldeinformationen speichern", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Bezeichnung (optional)", "add": "Hinzufügen", - "importFromCredential": "Oder aus gespeicherten Zugangsdaten importieren", + "importFromCredential": "Oder aus gespeicherten Anmeldeinformationen importieren", "keyPublished": "Schlüssel veröffentlicht", - "addKeyFailed": "Schlüssel konnte nicht hinzugefügt werden", - "generatedSaved": "Schlüsselpaar generiert und im Zugangsdaten-Tresor gespeichert. Der private Schlüssel wurde ebenfalls heruntergeladen.", - "generatedOnly": "Schlüsselpaar generiert – privater Schlüssel heruntergeladen (wird nur einmal angezeigt).", - "generateFailed": "Schlüssel konnte nicht generiert werden", - "imported": "Schlüssel aus Zugangsdaten importiert", - "importFailed": "Schlüssel konnte nicht importiert werden", - "noKeys": "Es wurden noch keine öffentlichen Schlüssel veröffentlicht.", + "addKeyFailed": "Fehler beim Hinzufügen des Schlüssels", + "generatedSaved": "Schlüsselpaar generiert und in Ihrem Anmeldeinformations-Tresor gespeichert. Privater Schlüssel ebenfalls heruntergeladen.", + "generatedOnly": "Schlüsselpaar generiert — privater Schlüssel heruntergeladen (wird nur einmal angezeigt).", + "generateFailed": "Fehler beim Generieren des Schlüssels", + "imported": "Schlüssel aus Anmeldeinformationen importiert", + "importFailed": "Schlüsselimport fehlgeschlagen", + "noKeys": "Noch keine öffentlichen Schlüssel veröffentlicht.", "keysTitle": "Veröffentlichte Schlüssel", "published": "Veröffentlicht", - "hidden": "Versteckt", + "hidden": "Ausgeblendet", "keyRemoved": "Schlüssel entfernt", - "removeKeyFailed": "Schlüssel konnte nicht entfernt werden", - "updateKeyFailed": "Schlüssel konnte nicht aktualisiert werden", - "fromVault": "Aus dem Zugangsdaten-Tresor", + "removeKeyFailed": "Schlüsselentfernung fehlgeschlagen", + "updateKeyFailed": "Schlüsselaktualisierung fehlgeschlagen", + "fromVault": "Aus dem Anmeldedaten-Tresor", "linkedToTermixId": "Veröffentlicht über Termix ID", - "selectCredential": "Zugangsdaten auswählen...", - "import": "Import", + "selectCredential": "Anmeldedaten auswählen...", + "import": "Importieren", "caTitle": "Zertifizierungsstelle", - "caIntro": "Vertrauen Sie dieser Zertifizierungsstelle auf einem Server, dann akzeptiert er jedes von Ihnen signierte Zertifikat. Durch regelmäßiges Rotieren können Sie alle Zertifikate gleichzeitig widerrufen; Zertifikate laufen außerdem automatisch ab.", + "caIntro": "Vertrauen Sie dieser CA auf einem Server, und sie akzeptiert jedes von Ihnen signierte Zertifikat. Durch Rotieren widerrufen Sie alles auf einmal; Zertifikate laufen auch automatisch ab.", "caEnable": "CA aktivieren", "caEnabled": "CA aktiviert", - "caCreateFailed": "CA konnte nicht aktiviert werden", - "caPublicKeyLabel": "CA-öffentlicher Schlüssel", - "caTrustLabel": "Auf einem Server als vertrauenswürdig hinterlegen (als Root ausführen)", - "caRotate": "Wechseln", - "caRotateConfirm": "Zertifizierungsstelle wechseln? Alle von ihr signierten Zertifikate werden nicht mehr akzeptiert und müssen neu ausgestellt werden.", - "caRotated": "Zertifizierungsstelle gewechselt – vorherige Zertifikate widerrufen", - "caRotateFailed": "CA konnte nicht rotiert werden", + "caCreateFailed": "Aktivierung der CA fehlgeschlagen", + "caPublicKeyLabel": "Öffentlicher Schlüssel der CA", + "caTrustLabel": "Vertrauen auf einem Server (als root ausführen)", + "caRotate": "Rotieren", + "caRotateConfirm": "CA rotieren? Alle von ihr signierten Zertifikate werden nicht mehr akzeptiert und müssen neu ausgestellt werden.", + "caRotated": "CA rotiert — vorherige Zertifikate widerrufen", + "caRotateFailed": "Rotieren der CA fehlgeschlagen", "caDelete": "CA entfernen", - "caDeleteConfirm": "Die Zertifizierungsstelle entfernen?", + "caDeleteConfirm": "Zertifizierungsstelle entfernen?", "caDeleted": "CA entfernt", - "caDeleteFailed": "CA konnte nicht entfernt werden", - "caValidityLabel": "Gültigkeit des Zertifikats (Tage)", + "caDeleteFailed": "Entfernen der CA fehlgeschlagen", + "caValidityLabel": "Zertifikatsgültigkeit (Tage)", "issueCert": "Zertifikat", - "issueCertTooltip": "Stellen Sie ein SSH-Zertifikat für diesen Schlüssel aus, das von Ihrer Zertifizierungsstelle signiert ist.", + "issueCertTooltip": "Ein SSH-Zertifikat für diesen Schlüssel ausstellen, signiert von Ihrer CA", "certIssued": "Zertifikat ausgestellt und heruntergeladen", - "certIssueFailed": "Zertifikat konnte nicht ausgestellt werden" + "certIssueFailed": "Ausstellung des Zertifikats fehlgeschlagen" }, "credentials": { "folders": "Ordner", @@ -75,30 +75,30 @@ "key": "Schlüssel", "sshPrivateKey": "SSH-Privatschlüssel", "upload": "Hochladen", - "keyPassword": "Schlüsselpasswort", + "keyPassword": "Schlüssel-Passwort", "sshKey": "SSH-Schlüssel", - "uploadPrivateKeyFile": "Datei mit privatem Schlüssel hochladen", - "searchCredentials": "Zugangsdaten suchen...", - "addCredential": "Zugangsdaten hinzufügen", + "uploadPrivateKeyFile": "Private-Key-Datei hochladen", + "searchCredentials": "Anmeldedaten durchsuchen...", + "addCredential": "Anmeldedaten hinzufügen", "caCertificate": "CA-Zertifikat (-cert.pub)", - "caCertificateDescription": "Optional: Laden Sie die von der Zertifizierungsstelle signierte Zertifikatsdatei (z. B. id_ed25519-cert.pub) hoch oder fügen Sie sie ein. Dies ist erforderlich, wenn Ihr SSH-Server zertifikatbasierte Authentifizierung verwendet.", - "uploadCertFile": "Datei „-cert.pub“ hochladen", - "clearCert": "Entfernen", + "caCertificateDescription": "Optional: Laden Sie die CA-signierte Zertifikatsdatei hoch oder fügen Sie sie ein (z. B. id_ed25519-cert.pub). Erforderlich, wenn Ihr SSH-Server zertifikatbasierte Autorisierung verwendet.", + "uploadCertFile": "Datei -cert.pub hochladen", + "clearCert": "Löschen", "certLoaded": "Zertifikat geladen", "certPublicKeyLabel": "CA-Zertifikat", "certTypeLabel": "Zertifikatstyp", - "pasteOrUploadCert": "Fügen Sie ein -cert.pub-Zertifikat ein oder laden Sie es hoch...", - "hasCaCert": "Besitzt CA-Zertifikat", + "pasteOrUploadCert": "-cert.pub-Zertifikat einfügen oder hochladen...", + "hasCaCert": "CA-Zertifikat vorhanden", "noCaCert": "Kein CA-Zertifikat", - "noPublicKeyAvailable": "Kein öffentlicher Schlüssel verfügbar. Öffnen Sie zuerst den Zugangsdaten-Editor.", + "noPublicKeyAvailable": "Kein öffentlicher Schlüssel verfügbar. Öffnen Sie zuerst den Anmeldedaten-Editor.", "deployCommandCopied": "Bereitstellungsbefehl kopiert", - "sortCredentials": "Zugangsdaten sortieren", + "sortCredentials": "Anmeldedaten sortieren", "sortDefault": "Standardreihenfolge", "sortNameAsc": "Name (A → Z)", "sortNameDesc": "Name (Z → A)", "sortUsernameAsc": "Benutzername (A → Z)", "sortUsernameDesc": "Benutzername (Z → A)", - "filterCredentials": "Zugangsdaten filtern", + "filterCredentials": "Anmeldedaten filtern", "filterClearAll": "Filter zurücksetzen", "filterTypeGroup": "Typ", "filterTypePassword": "Passwort", @@ -115,23 +115,23 @@ "folder": "Ordner", "clock": "Uhr", "notes": "Notizen", - "hostStatus": "Hoststatus", + "hostStatus": "Host-Status", "bookmarkList": "Lesezeichen", "zoomIn": "Vergrößern", "zoomOut": "Verkleinern", "resetView": "Ansicht zurücksetzen", "lockLayout": "Layout sperren", "unlockLayout": "Layout entsperren", - "noWidgets": "Klicken Sie mit der rechten Maustaste oder auf +, um Ihr erstes Widget hinzuzufügen.", + "noWidgets": "Klicken Sie mit der rechten Maustaste oder auf +, um Ihr erstes Widget hinzuzufügen", "openFullView": "Vollansicht öffnen", - "previewTitle": "Startseitenvorschau", + "previewTitle": "Homepage-Vorschau", "cancel": "Abbrechen", "save": "Speichern", "title_label": "Titel", "widgetTitlePlaceholder": "Widget-Titel (optional)", "url": "URL", "imageUrl": "Benutzerdefinierte Bild-URL", - "imageUrlHint": "Lassen Sie dieses Feld leer, um das Website-Favicon automatisch zu verwenden.", + "imageUrlHint": "Leer lassen, um automatisch das Favicon der Website zu verwenden", "showImage": "Bild anzeigen", "description": "Beschreibung", "color": "Farbe", @@ -139,8 +139,8 @@ "expanded": "Standardmäßig erweitert", "timezone": "Zeitzone", "showSeconds": "Sekunden anzeigen", - "format12h": "12-Stunden-Format", - "format24h": "24-Stunden-Format", + "format12h": "12-Stunden", + "format24h": "24-Stunden", "content": "Inhalt", "backgroundColor": "Hintergrundfarbe", "host": "Host", @@ -153,37 +153,37 @@ "categoryLinks": "Links", "categoryInfo": "Info", "categorySystem": "System", - "widgetServiceLinkDesc": "Eine anklickbare Kachel, die zu einer Dienst-URL verlinkt.", - "widgetFolderDesc": "Ein Container zur Gruppierung zusammengehöriger Widgets.", + "widgetServiceLinkDesc": "Anklickbare Kachel, die auf eine Service-URL verlinkt", + "widgetFolderDesc": "Ein Container, um verwandte Widgets zu gruppieren", "widgetClockDesc": "Eine Live-Uhr mit konfigurierbarer Zeitzone", "widgetNotesDesc": "Ein Markdown-Notizen-Widget", - "widgetHostStatusDesc": "Zeigt die aktuelle CPU-, Speicher- und Festplattenauslastung eines SSH-Hosts an.", - "widgetBookmarkListDesc": "Eine Liste mit Schnelllinks", + "widgetHostStatusDesc": "Zeigt Live-CPU-, Arbeitsspeicher- und Festplattenauslastung für einen SSH-Host an", + "widgetBookmarkListDesc": "Eine Liste von Schnelllinks", "copyLink": "Link kopieren", "linkCopied": "Link kopiert!", "location": "Standort", "temperatureUnit": "Temperatureinheit", "showForecast": "3-Tage-Vorhersage anzeigen", - "scrolling": "Scrollen zulassen", + "scrolling": "Scrollen erlauben", "feedUrl": "Feed-URL", - "maxItems": "Maximale Anzahl an Einträgen", + "maxItems": "Max. Einträge", "showDescription": "Beschreibung anzeigen", "widgetWeatherName": "Wetter", - "widgetWeatherDesc": "Live-Wetterdaten für jeden beliebigen Ort", + "widgetWeatherDesc": "Live-Wetter für jeden Standort", "widgetIframeName": "iFrame-Einbettung", - "widgetIframeDesc": "Beliebige URL in einen iFrame einbetten", + "widgetIframeDesc": "Bettet eine beliebige URL in einen iFrame ein", "widgetRssName": "RSS-Feed", - "widgetRssDesc": "Elemente aus einem RSS- oder Atom-Feed anzeigen", - "dragToFolder": "Ziehen Sie Widgets hierher oder verwenden Sie + zum Hinzufügen.", + "widgetRssDesc": "Zeigt Einträge aus einem RSS- oder Atom-Feed an", + "dragToFolder": "Widgets hierher ziehen oder + zum Hinzufügen verwenden", "showDisk": "Festplattennutzung anzeigen", "addToFolder": "Widget zum Ordner hinzufügen", "noHostSelected": "Kein Host ausgewählt", "metricsNotAvailable": "Metriken nicht verfügbar", - "selectHost": "Wählen Sie einen Host aus...", + "selectHost": "Host auswählen...", "displayedMetrics": "Angezeigte Metriken", "metricCpu": "CPU", "metricMemory": "Arbeitsspeicher", - "metricDisk": "Datenträger", + "metricDisk": "Festplatte", "metricUptime": "Betriebszeit", "metricSystem": "System", "metricOs": "Betriebssystem", @@ -192,7 +192,7 @@ "metricNetwork": "Netzwerk", "metricProcesses": "Prozesse", "metricProcessesTotal": "gesamt", - "metricProcessesRunning": "aktiv", + "metricProcessesRunning": "laufend", "categoryMonitoring": "Überwachung", "loading": "Wird geladen...", "noData": "Keine Daten verfügbar", @@ -205,7 +205,7 @@ "refreshInterval": "Aktualisierungsintervall", "seconds": "Sekunden", "filterActivityTypes": "Filtertypen", - "filterTypesHint": "Lassen Sie das Feld leer, um alle anzuzeigen", + "filterTypesHint": "Leer lassen, um alle anzuzeigen", "showTimestamp": "Zeitstempel anzeigen", "uptimeUnavailable": "Betriebszeit nicht verfügbar", "uptimeLabel": "Betriebszeit", @@ -216,59 +216,59 @@ "overviewUptime": "Betriebszeit", "noHosts": "Keine Hosts konfiguriert", "hostGridHosts": "Hosts", - "hostGridAllHint": "Lassen Sie dieses Feld leer, um alle Hosts anzuzeigen.", + "hostGridAllHint": "Leer lassen, um alle Hosts anzuzeigen", "columns": "Spalten", "showIp": "IP-Adresse anzeigen", "connectionType": "Verbindungstyp", "layout": "Layout", "showStatus": "Status anzeigen", - "showHostName": "Hostnamen anzeigen", + "showHostName": "Hostname anzeigen", "noDockerActivity": "Keine Docker-Aktivität", "noActivity": "Keine Aktivität", "showAcknowledged": "Bestätigte anzeigen", "showCurrentValue": "Aktuellen Wert anzeigen", "chartMetric": "Metrik", - "metricRange": "Zeitraum", - "widgetMetricsChartName": "Metriken-Diagramm", - "widgetMetricsChartDesc": "Historisches Diagramm zu CPU, Arbeitsspeicher, Festplatte oder Netzwerk für einen Host", + "metricRange": "Bereich", + "widgetMetricsChartName": "Metrikdiagramm", + "widgetMetricsChartDesc": "Historisches CPU-, Arbeitsspeicher-, Festplatten- oder Netzwerkdiagramm für einen Host", "widgetHostGridName": "Host-Raster", - "widgetHostGridDesc": "Rasteransicht der SSH-Hoststatus", - "widgetAlertFeedName": "Warnmeldungsfeed", - "widgetAlertFeedDesc": "Aktive Warnmeldungen mit Bestätigungsfunktion", + "widgetHostGridDesc": "Rasteransicht der SSH-Host-Status", + "widgetAlertFeedName": "Alarm-Feed", + "widgetAlertFeedDesc": "Live-Alarmauslösungen mit Bestätigungsfunktion", "widgetPingStatusName": "Ping-Status", "widgetPingStatusDesc": "HTTP-Ping-Status für eine oder mehrere URLs", - "widgetRecentActivityName": "Aktuelle Aktivitäten", + "widgetRecentActivityName": "Letzte Aktivität", "widgetRecentActivityDesc": "Scrollbarer Feed der letzten Termix-Aktivitäten", "widgetTermixUptimeName": "Termix-Betriebszeit", - "widgetTermixUptimeDesc": "Live-Zähler für die Betriebszeit des Termix-Servers", + "widgetTermixUptimeDesc": "Live-Betriebszeitzähler für den Termix-Server", "widgetSystemOverviewName": "Systemübersicht", - "widgetSystemOverviewDesc": "Termix-Version, Datenbankzustand und Verfügbarkeit auf einen Blick", + "widgetSystemOverviewDesc": "Termix-Version, Datenbankzustand und Betriebszeit auf einen Blick", "widgetSshQuickConnectName": "SSH-Schnellverbindung", - "widgetSshQuickConnectDesc": "SSH-Sitzungen mit einem Klick öffnen", + "widgetSshQuickConnectDesc": "Ein-Klick-Schaltflächen zum Öffnen von SSH-Sitzungen", "widgetDockerActivityName": "Docker-Aktivität", - "widgetDockerActivityDesc": "Aktuelle Docker-Container-Ereignisse auf allen Hosts", + "widgetDockerActivityDesc": "Letzte Docker-Container-Ereignisse über alle Hosts hinweg", "widgetCalendarName": "Kalender", - "widgetCalendarDesc": "Ein Monatskalender, in dem der heutige Tag hervorgehoben ist", + "widgetCalendarDesc": "Ein Monatskalender mit hervorgehobenem heutigem Datum", "widgetCountdownName": "Countdown", - "widgetCountdownDesc": "Countdown-Timer bis zu einem Zieldatum", + "widgetCountdownDesc": "Countdown-Timer zu einem Zieldatum", "widgetSearchBarName": "Suchleiste", - "widgetSearchBarDesc": "Widget für die schnelle Websuche", + "widgetSearchBarDesc": "Schnelles Websuche-Widget", "widgetTextBannerName": "Textbanner", - "widgetTextBannerDesc": "Eine aussagekräftige Beschriftung oder Abschnittsüberschrift für die Leinwand", + "widgetTextBannerDesc": "Ein fettgedrucktes Label oder eine Abschnittsüberschrift für die Leinwand", "widgetImageWidgetName": "Bild", "widgetImageWidgetDesc": "Ein Bild von einer URL anzeigen", "widgetMarkdownNotesName": "Markdown-Notizen", "widgetMarkdownNotesDesc": "Formatierte Notizen mit Inline-Markdown-Darstellung", "widgetCustomApiName": "Benutzerdefinierte API", - "widgetCustomApiDesc": "Daten von beliebigen JSON-APIs abrufen und anzeigen", + "widgetCustomApiDesc": "Daten von einer beliebigen JSON-API abrufen und anzeigen", "widgetServiceGridName": "Service-Raster", - "widgetServiceGridDesc": "Ein konfigurierbares Raster von Service-Kachel-Links", + "widgetServiceGridDesc": "Ein konfigurierbares Raster aus Service-Kachel-Links", "widgetDashboardLinksName": "Dashboard-Links", - "widgetDashboardLinksDesc": "Zeigen Sie die konfigurierten Service-Links aus Ihrem Dashboard an.", - "widgetSearchLinksName": "Such-Shortcuts", - "widgetSearchLinksDesc": "Schaltflächen für die Schnellsuche mit integriertem Eingabefeld", - "widgetLinkTreeName": "Linkbaum", - "widgetLinkTreeDesc": "Gruppierte Linkabschnitte mit Überschriften", + "widgetDashboardLinksDesc": "Konfigurierte Dienst-Links aus dem Dashboard anzeigen", + "widgetSearchLinksName": "Such-Verknüpfungen", + "widgetSearchLinksDesc": "Schnelle Such-Verknüpfungsschaltflächen mit Inline-Eingabe", + "widgetLinkTreeName": "Link-Baum", + "widgetLinkTreeDesc": "Gruppierte Link-Abschnitte mit Überschriften", "calMon": "Mo", "calTue": "Di", "calWed": "Mi", @@ -276,52 +276,52 @@ "calFri": "Fr", "calSat": "Sa", "calSun": "So", - "startOnMonday": "Die Woche beginnt am Montag.", + "startOnMonday": "Woche am Montag beginnen", "countdownNoDate": "Kein Zieldatum festgelegt", "countdownPast": "Ereignis ist vorbei", "countdownDays": "Tage", "countdownHours": "Std.", - "countdownMinutes": "min", + "countdownMinutes": "Min.", "countdownSeconds": "Sek.", - "countdownLabel": "Bezeichnung", - "countdownLabelPlaceholder": "z. B. Starttag", + "countdownLabel": "Label", + "countdownLabelPlaceholder": "z.B. Starttag", "countdownShowDays": "Tage anzeigen", "countdownShowHours": "Stunden anzeigen", "targetDate": "Zieldatum", "searchEngine": "Suchmaschine", "customSearchUrl": "Benutzerdefinierte Such-URL", "searchPlaceholder": "Suchen...", - "searchGo": "Suchen", + "searchGo": "Los", "searchPlaceholderLabel": "Platzhaltertext", - "searchPlaceholderHint": "Im Suchfeld angezeigter Text", + "searchPlaceholderHint": "Text, der im Suchfeld angezeigt wird", "openInNewTab": "In neuem Tab öffnen", - "searchQueryPlaceholder": "Suchanfrage eingeben...", - "noSearchShortcuts": "Keine Suchverknüpfungen konfiguriert", - "addSearchShortcut": "Verknüpfung hinzufügen", + "searchQueryPlaceholder": "Suchbegriff eingeben...", + "noSearchShortcuts": "Keine Shortcuts konfiguriert", + "addSearchShortcut": "Shortcut hinzufügen", "fontSize": "Schriftgröße", "textAlign": "Textausrichtung", "fontWeight": "Schriftstärke", - "clearColor": "Farbe zurücksetzen", + "clearColor": "Farbe löschen", "imageFit": "Bildanpassung", "imageLinkUrl": "Link-URL", "noImage": "Keine Bild-URL festgelegt", "altText": "Alternativtext", - "altTextPlaceholder": "Beschreiben Sie das Bild", + "altTextPlaceholder": "Bild beschreiben", "renderMarkdown": "Markdown rendern", "displayMode": "Anzeigemodus", "displayField": "Anzeigefeld", "jsonPath": "JSON-Pfad", "customApiLabel": "Bezeichnung", - "customApiLabelPlaceholder": "z. B. Temperatur", + "customApiLabelPlaceholder": "z.B. Temperatur", "customApiUnit": "Einheit", "customApiNoUrl": "Keine API-URL konfiguriert", - "customApiError": "Abruf fehlgeschlagen", - "customApiNotArray": "Die Antwort ist kein Array.", + "customApiError": "Fehler beim Abrufen", + "customApiNotArray": "Antwort ist kein Array", "addService": "Dienst hinzufügen", - "showLabels": "Bezeichnungen anzeigen", + "showLabels": "Beschriftungen anzeigen", "iconSize": "Symbolgröße", - "noDashboardLinks": "Es sind keine Dashboard-Links konfiguriert.", - "noLimit": "Unbegrenzt", + "noDashboardLinks": "Keine Dashboard-Links konfiguriert", + "noLimit": "Keine Begrenzung", "sectionHeading": "Abschnittsüberschrift", "addSection": "Abschnitt hinzufügen", "compactMode": "Kompaktmodus", @@ -343,12 +343,12 @@ "filterAll": "Alle", "accentColor": "Akzentfarbe", "widgetSshTerminalName": "SSH-Terminal", - "widgetSshTerminalDesc": "Ein Inline-SSH-Terminal, das mit einem konfigurierten Host verbunden ist", + "widgetSshTerminalDesc": "Ein eingebettetes SSH-Terminal, das mit einem konfigurierten Host verbunden ist", "sshTerminalNoHost": "Kein Host konfiguriert", "sshTerminalConnect": "Verbinden", "sshTerminalAutoConnect": "Beim Laden automatisch verbinden", "widgetQuickConnectName": "Schnellverbindung", - "widgetQuickConnectDesc": "Schaltflächen zum Öffnen beliebiger Verbindungstypen auf Ihren Hosts mit nur einem Klick", + "widgetQuickConnectDesc": "Ein-Klick-Schaltflächen für alle Verbindungstypen auf Ihren Hosts", "connectionTypes": "Verbindungstypen", "connType_terminal": "Terminal", "connType_files": "Dateimanager", @@ -362,48 +362,87 @@ "widgetFileManagerDesc": "Eingebetteter SFTP-Dateimanager für einen konfigurierten Host", "widgetDockerName": "Docker-Manager", "widgetDockerDesc": "Eingebetteter Docker-Container-Manager für einen konfigurierten Host", - "widgetTunnelName": "Tunnelmanager", - "widgetTunnelDesc": "Eingebetteter SSH-Tunnelmanager für einen konfigurierten Host", + "widgetTunnelName": "Tunnel-Manager", + "widgetTunnelDesc": "Eingebetteter SSH-Tunnel-Manager für einen konfigurierten Host", "widgetNoHostSelected": "Kein Host konfiguriert" }, "serverConfig": { "title": "Serverkonfiguration", - "description": "Konfigurieren Sie die Termix-Server-URL, um eine Verbindung zu Ihren Backend-Diensten herzustellen.", + "description": "Konfigurieren Sie die Termix-Server-URL, um eine Verbindung zu Ihren Backend-Diensten herzustellen", "serverUrl": "Server-URL", - "enterServerUrl": "Bitte geben Sie eine Server-URL ein.", - "saveFailed": "Konfiguration konnte nicht gespeichert werden", + "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, unter der Ihr Termix-Server läuft (z. B. http://localhost:30001 oder https://your-server.com).", + "helpText": "Geben Sie die URL ein, unter der Ihr Termix-Server läuft (z. B. http://localhost:30001 oder https://your-server.com)", "changeServer": "Server wechseln", - "mustIncludeProtocol": "Die Server-URL muss mit http:// oder https:// beginnen.", + "mustIncludeProtocol": "Die 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.", + "allowInvalidCertificateDesc": "Nur für vertrauenswürdige selbst gehostete Server mit selbstsignierten oder IP-Adress-Zertifikaten verwenden.", "useEmbedded": "Lokalen Server verwenden", - "embeddedDesc": "Termix mit dem integrierten lokalen Server betreiben (kein Remote-Server erforderlich).", - "embeddedConnecting": "Verbindung zum lokalen Server wird hergestellt...", + "embeddedDesc": "Termix mit dem integrierten lokalen Server ausführen (kein Remote-Server erforderlich)", + "embeddedConnecting": "Verbinde mit lokalem Server...", "embeddedNotReady": "Der lokale Server ist noch nicht bereit. Bitte warten Sie einen Moment und versuchen Sie es erneut.", "localServer": "Lokaler Server", "savedServers": "Gespeicherte Server", "noSavedServers": "Keine gespeicherten Server", "removeServer": "Entfernen" }, + "migrationNotice": { + "title": "Termix Desktop läuft jetzt eigenständig", + "body1": "Diese App verband sich früher direkt mit Ihrem Termix-Server. Sie wurde überarbeitet und läuft nun vollständig eigenständig. Hosts und Zugangsdaten werden lokal gespeichert, sodass die App auch offline funktioniert. Die bidirektionale Synchronisierung mit einem selbst gehosteten Termix-Server ist jetzt optional.", + "body2": "Ihre bestehenden Hosts und Anmeldeinformationen befinden sich noch auf {{url}}. Aktivieren Sie die Remote-Synchronisierung und stellen Sie die Verbindung zu diesem Server wieder her, um sie wiederherzustellen und zukünftig synchron zu halten.", + "dismiss": "Nicht jetzt.", + "setUpSync": "Fernsynchronisierung einrichten" + }, + "remoteSync": { + "title": "Fernsynchronisierung", + "description": "Sie können diese Desktop-App optional mit einem selbst gehosteten Termix-Server verbinden, um Ihre Hosts, Anmeldeinformationen und Code-Snippets geräteübergreifend zu synchronisieren. Die App funktioniert aber auch offline einwandfrei, unabhängig davon, ob eine Verbindung besteht oder nicht.", + "notConnected": "Nicht verbunden", + "connected": "Verbunden", + "connectedTo": "Verbunden mit {{url}}", + "lastSynced": "Zuletzt synchronisiert {{time}}", + "neverSynced": "Nie synchronisiert", + "syncError": "Synchronisierungsfehler: {{message}}", + "needsReauth": "Anmeldung abgelaufen", + "connectButton": "Verbindung zum Server herstellen", + "disconnectButton": "Trennen", + "syncNowButton": "Jetzt synchronisieren", + "syncing": "Synchronisierung läuft...", + "serverUrl": "Server-URL", + "enterServerUrl": "Bitte geben Sie eine Server-URL ein.", + "mustIncludeProtocol": "Die Server-URL muss mit http:// oder https:// beginnen.", + "connectionTestFailed": "Unter dieser URL konnte kein Termix-Server erreicht werden.", + "allowInvalidCertificate": "Ungültiges Zertifikat zulassen", + "allowInvalidCertificateDesc": "Nur für vertrauenswürdige, selbstgehostete Server mit selbstsignierten oder IP-Adresszertifikaten verwenden.", + "savedServers": "Gespeicherte Server", + "removeServer": "Entfernen", + "continueButton": "Weitermachen", + "cancelButton": "Stornieren", + "signInTitle": "Melden Sie sich an bei {{url}}", + "originTitle": "Verbindungsursprung", + "originDescription": "Wählen Sie den Standardort für SSH-Verbindungen aus. Diese Einstellung kann für jeden Host individuell angepasst werden.", + "originLocal": "Dieses Gerät (lokales Netzwerk)", + "originRemote": "Remote-Server", + "bannerReconnect": "Wiederverbinden", + "bannerMessage": "Für die Remote-Synchronisierung ist eine erneute Authentifizierung erforderlich." + }, "versionCheck": { - "error": "Versionsprüfungsfehler", - "checkFailed": "Fehler beim Prüfen auf Aktualisierungen", - "upToDate": "Die App ist auf dem neuesten Stand.", + "error": "Fehler bei der Versionsprüfung", + "checkFailed": "Fehler bei der Update-Prüfung", + "upToDate": "App ist auf dem neuesten Stand", "currentVersion": "Sie verwenden Version {{version}}", "updateAvailable": "Update verfügbar", - "newVersionAvailable": "Eine neue Version ist verfügbar! Sie verwenden derzeit {{current}}, aber {{latest}} ist verfügbar.", - "betaVersion": "Betaversion", - "betaVersionDesc": "Sie verwenden {{current}}, das neuer ist als die aktuellste stabile Version {{latest}}.", + "newVersionAvailable": "Eine neue Version ist verfügbar! Sie verwenden {{current}}, aber {{latest}} steht zur Verfügung.", + "betaVersion": "Beta-Version", + "betaVersionDesc": "Sie verwenden {{current}}, das neuer ist als die letzte stabile Version {{latest}}.", "releasedOn": "Veröffentlicht am {{date}}", "downloadUpdate": "Update herunterladen", "checking": "Suche nach Updates...", - "checkUpdates": "Auf Aktualisierungen prüfen", + "checkUpdates": "Auf Updates prüfen", "checkingUpdates": "Suche nach Updates...", - "updateRequired": "Aktualisierung erforderlich" + "updateRequired": "Update erforderlich" }, "common": { "close": "Schließen", @@ -413,25 +452,25 @@ "unknown": "Unbekannt", "continue": "Weiter", "maintenance": "Wartung", - "degraded": "Beeinträchtigt", + "degraded": "Eingeschränkt", "error": "Fehler", "warning": "Warnung", - "unsavedChanges": "Nicht gespeicherte Änderungen", - "dismiss": "Ausblenden", - "loading": "Wird geladen...", + "unsavedChanges": "Ungespeicherte Änderungen", + "dismiss": "Verwerfen", + "loading": "Laden...", "optional": "Optional", "connect": "Verbinden", "copied": "Kopiert", - "connecting": "Verbindung wird hergestellt...", + "connecting": "Verbinden...", "updateAvailable": "Update verfügbar", "appName": "Termix", "openInNewTab": "In neuem Tab öffnen", "noReleases": "Keine Veröffentlichungen", - "updatesAndReleases": "Aktualisierungen & Veröffentlichungen", + "updatesAndReleases": "Updates & Veröffentlichungen", "newVersionAvailable": "Eine neue Version ({{version}}) ist verfügbar.", - "failedToFetchUpdateInfo": "Aktualisierungsinformationen konnten nicht abgerufen werden.", + "failedToFetchUpdateInfo": "Fehler beim Abrufen der Update-Informationen", "preRelease": "Vorabversion", - "noReleasesFound": "Keine Veröffentlichungen gefunden.", + "noReleasesFound": "Keine Versionen gefunden.", "cancel": "Abbrechen", "username": "Benutzername", "login": "Anmelden", @@ -441,7 +480,7 @@ "confirmPassword": "Passwort bestätigen", "back": "Zurück", "save": "Speichern", - "saving": "Wird gespeichert...", + "saving": "Speichern...", "delete": "Löschen", "rename": "Umbenennen", "edit": "Bearbeiten", @@ -450,20 +489,20 @@ "no": "Nein", "or": "ODER", "next": "Weiter", - "previous": "Zurück", + "previous": "Vorherige", "refresh": "Aktualisieren", "language": "Sprache", - "checking": "Wird geprüft...", + "checking": "Prüfen...", "checkingDatabase": "Datenbankverbindung wird geprüft...", "checkingAuthentication": "Authentifizierung wird geprüft...", "backendReconnected": "Serververbindung wiederhergestellt", - "connectionDegraded": "Serververbindung unterbrochen, Wiederherstellung läuft…", + "connectionDegraded": "Serververbindung verloren, Wiederherstellung...", "reload": "Neu laden", "remove": "Entfernen", "create": "Erstellen", "update": "Aktualisieren", "copy": "Kopieren", - "copyFailed": "Fehler beim Kopieren in die Zwischenablage", + "copyFailed": "Kopieren in die Zwischenablage fehlgeschlagen", "maximize": "Maximieren", "restore": "Wiederherstellen", "of": "von", @@ -485,11 +524,11 @@ "termixId": "ID", "userProfile": "Benutzerprofil", "splitScreen": "Geteilter Bildschirm", - "confirmClose": "Diese aktive Sitzung beenden?", + "confirmClose": "Diese aktive Sitzung schließen?", "close": "Schließen", "cancel": "Abbrechen", "sshManager": "SSH-Manager", - "cannotSplitTab": "Dieser Tab kann nicht geteilt werden", + "cannotSplitTab": "Diese Registerkarte kann nicht geteilt werden", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "Passwort kopieren", "copySudoPassword": "Sudo-Passwort kopieren", @@ -497,7 +536,7 @@ "noPasswordAvailable": "Kein Passwort verfügbar", "failedToCopyPassword": "Passwort konnte nicht kopiert werden", "refreshTab": "Verbindung aktualisieren", - "renameTab": "Tab umbenennen", + "renameTab": "Registerkarte umbenennen", "openFileManager": "Dateimanager öffnen", "dashboard": "Dashboard", "networkGraph": "Netzwerkdiagramm", @@ -510,10 +549,10 @@ "hosts": "Hosts", "snippets": "Snippets", "hostManager": "Host-Manager", - "credentials": "Anmeldeinformationen", + "credentials": "Zugangsdaten", "connections": "Verbindungen", - "alerts": "Warnmeldungen", - "serial": "Seriennummer", + "alerts": "Benachrichtigungen", + "serial": "Seriell", "roleAdministrator": "Administrator", "roleUser": "Benutzer" }, @@ -524,13 +563,13 @@ "refresh": "Aktualisieren", "optional": "Optional", "downloadSample": "Beispiel herunterladen", - "failedToDeleteHost": "Fehler beim Löschen von {{name}}", + "failedToDeleteHost": "Löschen von {{name}} fehlgeschlagen", "importSkipExisting": "Importieren (vorhandene überspringen)", - "importSSHConfig": "Import aus der SSH-Konfiguration", + "importSSHConfig": "Aus SSH-Konfiguration importieren", "connectionDetails": "Verbindungsdetails", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Remote-Desktop", + "remoteDesktop": "Remotedesktop", "port": "Port", "username": "Benutzername", "folder": "Ordner", @@ -549,9 +588,9 @@ "authentication": "Authentifizierung", "password": "Passwort", "key": "Schlüssel", - "credential": "Zugangsdaten", + "credential": "Anmeldedaten", "none": "Keine", - "sshPrivateKey": "SSH-Privatschlüssel", + "sshPrivateKey": "Privater SSH-Schlüssel", "keyType": "Schlüsseltyp", "uploadFile": "Datei hochladen", "tabGeneral": "Allgemein", @@ -565,43 +604,44 @@ "tabStats": "Host-Metriken", "tabHostMetrics": "Host-Metriken", "tabTelnet": "Telnet", - "tabSharing": "Teilen", + "tabSharing": "Freigabe", "tabAuthentication": "Authentifizierung", "terminal": "Terminal", "tunnel": "Tunnel", "fileManager": "Dateimanager", "serverStats": "Host-Metriken", "status": "Status", - "folderRenamed": "Ordner \"{{oldName}}\" erfolgreich umbenannt in \"{{newName}}\"", - "failedToRenameFolder": "Ordner konnte nicht umbenannt werden", - "movedToFolder": "{{count}} Host(s) nach \"{{folder}}\" verschoben", + "folderRenamed": "Ordner \"{{oldName}}\" erfolgreich in \"{{newName}}\" umbenannt", + "failedToRenameFolder": "Fehler beim Umbenennen des Ordners", + "movedToFolder": "{{count}} Host(s) nach „{{folder}}“ verschoben", "editHostTooltip": "Host bearbeiten", "statusChecks": "Statusprüfungen", - "metricsCollection": "Metrikensammlung", - "metricsInterval": "Metrikenerfassungsintervall", - "metricsIntervalDesc": "Wie oft sollen Serverstatistiken erfasst werden (5 Sekunden - 1 Stunde)?", + "metricsCollection": "Metrikerfassung", + "metricsInterval": "Metrikerfassungsintervall", + "metricsIntervalDesc": "Wie oft Serverstatistiken erfasst werden sollen (5s - 1h)", "behavior": "Verhalten", "themePreview": "Designvorschau", "theme": "Design", "fontFamily": "Schriftfamilie", "fontSize": "Schriftgröße", - "letterSpacing": "Buchstabenabstand", + "letterSpacing": "Zeichenabstand", "lineHeight": "Zeilenhöhe", - "cursorStyle": "Cursorform", - "cursorBlink": "Blinkender Cursor", + "cursorStyle": "Cursor-Stil", + "cursorBlink": "Cursor-Blinken", "scrollbackBuffer": "Scrollback-Puffer", "bellStyle": "Signaltonart", "rightClickSelectsWord": "Rechtsklick wählt Wort aus", - "fastScrollModifier": "Modifikatortaste für schnelles Scrollen", - "fastScrollSensitivity": "Empfindlichkeit für schnelles Scrollen", + "fastScrollModifier": "Schnellscroll-Modifikator", + "fastScrollSensitivity": "Schnellscroll-Empfindlichkeit", "sshAgentForwarding": "SSH-Agent-Weiterleitung", - "backspaceMode": "Rücktaste-Modus", + "backspaceMode": "Backspace-Modus", "startupSnippet": "Start-Snippet", "selectSnippet": "Snippet auswählen", - "forceKeyboardInteractive": "Tastaturinteraktion erzwingen", - "overrideCredentialUsername": "Benutzernamen der Zugangsdaten überschreiben", - "overrideCredentialUsernameDesc": "Verwenden Sie den oben angegebenen Benutzernamen anstelle des Benutzernamens der Anmeldeinformationen.", + "forceKeyboardInteractive": "Keyboard-Interactive erzwingen", + "overrideCredentialUsername": "Benutzernamen der Anmeldeinformationen überschreiben", + "overrideCredentialUsernameDesc": "Den oben angegebenen Benutzernamen anstelle des Benutzernamens der Anmeldeinformationen verwenden", "oidcUsernameHint": "Verwenden Sie $oidc.preferred_username, um Ihren OIDC-Anmeldenamen zu ersetzen.", + "tailscaleUsernameHint": "Es muss sich um einen Unix-Benutzer handeln, dem Ihre Tailscale-Identität in der SSH-ACL des Tailnets gewährt wird, nicht unbedingt um den Root-Benutzer.", "jumpHostChain": "Jump-Host-Kette", "portKnocking": "Port-Knocking", "addKnock": "Port hinzufügen", @@ -609,17 +649,17 @@ "proxyNode": "Proxy-Knoten", "proxyType": "Proxy-Typ", "quickActions": "Schnellaktionen", - "sudoPasswordAutoFill": "sudo-Passwort automatisch ausfüllen", + "sudoPasswordAutoFill": "Sudo-Passwort automatisch ausfüllen", "sudoPassword": "Sudo-Passwort", "keepaliveInterval": "Keepalive-Intervall (ms)", - "moshCommand": "Mosh-Befehl", + "moshCommand": "MOSH-Befehl", "environmentVariables": "Umgebungsvariablen", "addVariable": "Variable hinzufügen", "docker": "Docker", "copyTerminalUrl": "Terminal-URL kopieren", - "copyFileManagerUrl": "URL des Dateimanagers kopieren", + "copyFileManagerUrl": "Dateimanager-URL kopieren", "copyRemoteDesktopUrl": "Remote-Desktop-URL kopieren", - "failedToConnect": "Verbindung zur Konsole fehlgeschlagen.", + "failedToConnect": "Verbindung zur Konsole fehlgeschlagen", "connect": "Verbinden", "disconnect": "Trennen", "start": "Starten", @@ -627,122 +667,127 @@ "enableMetrics": "Metriken aktivieren", "bulkUpdateFailed": "Massenaktualisierung fehlgeschlagen", "selectAll": "Alle auswählen", - "deselectAll": "Alle abwählen", + "deselectAll": "Auswahl aufheben", "protocols": "Protokolle", "secureShell": "Secure Shell", "virtualNetwork": "Virtuelles Netzwerk", "unencryptedShell": "Unverschlüsselte Shell", - "addressIp": "Adresse / IP-Adresse", + "addressIp": "Adresse / IP", "friendlyName": "Anzeigename", "macAddress": "MAC-Adresse", "wolBroadcastAddress": "WoL-Broadcast-Adresse", - "wolBroadcastAddressDesc": "Optionale, gerichtete Broadcast-Adresse für Docker/geroutete Netzwerke (z. B. 192.168.1.255). Feld leer lassen, um 255.255.255.255 zu verwenden.", - "folderAndAdvanced": "Ordner & erweiterte Einstellungen", + "wolBroadcastAddressDesc": "Optionale gerichtete Broadcast-Adresse für Docker/geroutete Netzwerke (z. B. 192.168.1.255). Leer lassen, um 255.255.255.255 zu verwenden.", + "folderAndAdvanced": "Ordner & Erweitert", "privateNotes": "Private Notizen", - "privateNotesPlaceholder": "Details zu diesem Server...", - "pinToTop": "Anheften", - "pinToTopDesc": "Diesen Host immer ganz oben in der Liste anzeigen", + "privateNotesPlaceholder": "Details zu diesem Server …", + "pinToTop": "Oben anheften", + "pinToTopDesc": "Diesen Host immer oben in der Liste anzeigen", "portKnockingSequence": "Port-Knocking-Sequenz", - "addKnockBtn": "Klopfen hinzufügen", - "noPortKnocking": "Es ist kein Port-Knocking konfiguriert.", + "addKnockBtn": "Knock hinzufügen", + "noPortKnocking": "Kein Port-Knocking konfiguriert.", "knockPort": "Knock-Port", "protocol": "Protokoll", - "delayAfterMs": "Anschließende Verzögerung (ms)", + "delayAfterMs": "Verzögerung nach (ms)", "useSocks5Proxy": "SOCKS5-Proxy verwenden", - "useSocks5ProxyDesc": "Die Verbindung wird über einen Proxy-Server geleitet.", + "useSocks5ProxyDesc": "Verbindung über einen Proxy-Server leiten", + "connectionOrigin": "Verbindungsursprung", + "connectionOriginDesc": "Hier wird der Ursprung der SSH-Verbindung dieses Hosts angezeigt. Überschreibt die globale Standardeinstellung der Desktop-App.", + "connectionOriginDefault": "Standardwert verwenden", + "connectionOriginLocal": "Dieses Gerät (lokales Netzwerk)", + "connectionOriginRemote": "Remote-Server", "proxyHost": "Proxy-Host", "proxyPort": "Proxy-Port", "proxyUsername": "Proxy-Benutzername", "proxyPassword": "Proxy-Passwort", - "proxySingleMode": "Einzelproxy", + "proxySingleMode": "Einzelner Proxy", "proxyChainMode": "Proxy-Kette", - "you": "Sie", - "jumpHostChainLabel": "Jump-Host-Kette", - "addJumpBtn": "Sprung hinzufügen", - "noJumpHosts": "Es sind keine Jump-Hosts konfiguriert.", - "selectAServer": "Wählen Sie einen Server aus...", + "you": "Lokal", + "jumpHostChainLabel": "Sprungserver-Kette", + "addJumpBtn": "Sprungserver hinzufügen", + "noJumpHosts": "Keine Sprungserver konfiguriert.", + "selectAServer": "Server auswählen...", "sshPort": "SSH-Port", "authMethod": "Authentifizierungsmethode", - "storedCredential": "Gespeicherte Anmeldeinformationen", - "selectACredential": "Wählen Sie eine Anmeldeinformation aus...", - "vaultProfile": "Vault-Signaturprofil", - "selectAVaultProfile": "Wählen Sie ein Vault-Profil aus...", - "vaultProfileHint": "Die Einstellungen stammen aus dem freigegebenen Profil; Sie melden sich beim Verbinden über OIDC bei Vault an. Es werden keine Geheimnisse gespeichert.", + "storedCredential": "Gespeicherte Anmeldedaten", + "selectACredential": "Anmeldedaten auswählen...", + "vaultProfile": "Vault-Signer-Profil", + "selectAVaultProfile": "Vault-Profil auswählen...", + "vaultProfileHint": "Die Einstellungen stammen aus dem freigegebenen Profil; Sie melden sich beim Verbinden per OIDC bei Vault an. Es werden keine Geheimnisse gespeichert.", "vaultNewProfile": "Neues Profil", "vaultManageProfiles": "Vault-Profile verwalten", "vaultAddrLabel": "Vault-Adresse", - "vaultNamespaceLabel": "Namensraum", - "vaultOidcMountLabel": "OIDC-Authentifizierungs-Mount", + "vaultNamespaceLabel": "Namespace", + "vaultOidcMountLabel": "OIDC-Auth-Mount", "vaultOidcRoleLabel": "OIDC-Rolle", "vaultSshMountLabel": "SSH-Secrets-Mount", - "vaultSshRoleLabel": "SSH-Signiererrolle", - "vaultValidPrincipalsLabel": "Gültige Prinzipale", + "vaultSshRoleLabel": "SSH-Signer-Rolle", + "vaultValidPrincipalsLabel": "Gültige Principals", "vaultKeyTypeLabel": "Ephemerer Schlüsseltyp", - "vaultSharedLabel": "Mit allen Nutzern teilen", + "vaultSharedLabel": "Mit allen Benutzern teilen", "vaultCreateProfile": "Profil erstellen", "vaultProfileCreated": "Vault-Profil erstellt", "vaultProfileSaved": "Vault-Profil gespeichert", "vaultProfileDeleted": "Vault-Profil gelöscht", - "vaultProfileSaveFailed": "Das Speichern des Vault-Profils ist fehlgeschlagen.", - "vaultProfileDeleteFailed": "Das Löschen des Vault-Profils ist fehlgeschlagen.", + "vaultProfileSaveFailed": "Fehler beim Speichern des Vault-Profils", + "vaultProfileDeleteFailed": "Fehler beim Löschen des Vault-Profils", "vaultSaveProfile": "Profil speichern", - "vaultProfileValidationError": "Name, Vault-Adresse und SSH-Signaturrolle sind erforderlich", + "vaultProfileValidationError": "Name, Vault-Adresse und SSH-Signer-Rolle sind erforderlich", "vaultNoProfiles": "Noch keine Vault-Profile.", - "vaultSharedBadge": "freigegeben", + "vaultSharedBadge": "geteilt", "keyTypeLabel": "Schlüsseltyp", - "keyTypeAuto": "Automatische Erkennung", + "keyTypeAuto": "Automatisch erkennen", "keyPasteTab": "Einfügen", "keyUploadTab": "Hochladen", "keyFileLoaded": "Schlüsseldatei geladen", - "keyUploadClick": "Klicken Sie hier, um .pem-, .key- oder .ppk-Dateien hochzuladen.", - "clearKey": "Schlüssel löschen", + "keyUploadClick": "Zum Hochladen von .pem / .key / .ppk klicken", + "clearKey": "Schlüssel entfernen", "keySaved": "SSH-Schlüssel gespeichert", - "keyReplaceNotice": "Fügen Sie unten einen neuen Schlüssel ein, um ihn zu ersetzen.", + "keyReplaceNotice": "Fügen Sie unten einen neuen Schlüssel ein, um ihn zu ersetzen", "keyPassphraseSaved": "Passphrase gespeichert, zum Ändern eingeben", "replaceKey": "Schlüssel ersetzen", - "docsLink": "Dokumente ansehen", + "docsLink": "Dokumentation anzeigen", "opksshLabel": "OPKSSH", - "opksshDesc": "Melden Sie sich bei diesem Host mit Ihrem Identitätsanbieter anstelle eines Passworts oder Schlüssels an. OPKSSH muss auf dem Server eingerichtet sein.", - "warpgateLabel": "Warpgate-Gateway", - "warpgateDesc": "Dieser Host verbindet sich über einen Warpgate-SSH-Proxy. Termix übernimmt nach der Authentifizierung automatisch den browserbasierten Genehmigungsprozess.", + "opksshDesc": "Melden Sie sich mit Ihrem Identitätsanbieter an diesem Host an, anstatt mit einem Passwort oder Schlüssel. Erfordert, dass OPKSSH auf dem Server eingerichtet ist.", + "warpgateLabel": "Warpgate Gateway", + "warpgateDesc": "Dieser Host verbindet sich über einen Warpgate SSH-Proxy. Termix übernimmt nach der Authentifizierung automatisch den browserbasierten Genehmigungsablauf.", "agentLabel": "SSH-Agent", - "agentDesc": "Authentifizieren Sie sich mithilfe eines SSH-Agenten, der auf dem Termix-Server ausgeführt wird (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Der Agent muss auf dem Rechner laufen, auf dem Termix gehostet wird.", - "agentSocketPathLabel": "Pfad zum SSH-Agent-Socket", - "agentSocketPathPlaceholder": "Lassen Sie dieses Feld leer, um SSH_AUTH_SOCK zu verwenden.", - "agentSocketPathHint": "Lassen Sie das Feld leer, um die automatische Erkennung anhand der Umgebungsvariablen SSH_AUTH_SOCK zu ermöglichen, oder geben Sie einen benutzerdefinierten Socket-Pfad ein (z. B. /run/user/1000/gnupg/S.gpg-agent.ssh).", + "agentDesc": "Authentifizieren Sie sich mit einem SSH-Agenten, der auf dem Termix-Server läuft (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Der Agent muss auf dem Rechner laufen, auf dem Termix gehostet wird.", + "agentSocketPathLabel": "Agent-Socket-Pfad", + "agentSocketPathPlaceholder": "Leer lassen, um SSH_AUTH_SOCK zu verwenden", + "agentSocketPathHint": "Leer lassen, um automatisch über die Umgebungsvariable SSH_AUTH_SOCK zu erkennen, oder geben Sie einen benutzerdefinierten Socket-Pfad ein (z. B. /run/user/1000/gnupg/S.gpg-agent.ssh).", "tailscaleDeviceSelect": "Tailscale-Gerät auswählen", - "tailscaleDeviceSelectPlaceholder": "Wählen Sie ein Gerät aus...", - "tailscaleNoApiKey": "Es ist kein Tailscale-API-Schlüssel konfiguriert. Fügen Sie einen in den Administratoreinstellungen hinzu, um die Geräteerkennung zu aktivieren.", - "tailscaleDocsLink": "Dokumente ansehen", + "tailscaleDeviceSelectPlaceholder": "Gerät auswählen...", + "tailscaleNoApiKey": "Kein Tailscale API-Schlüssel konfiguriert. Fügen Sie einen in den Admin-Einstellungen hinzu, um die Geräteerkennung zu aktivieren.", + "tailscaleDocsLink": "Dokumentation anzeigen", "tailscaleLoadingDevices": "Geräte werden geladen...", - "tailscaleNoDevices": "In Ihrem Tailnet wurden keine Geräte gefunden.", - "tailscaleDeviceAutoFill": "Wenn Sie ein Gerät auswählen, wird die Host-IP-Adresse automatisch ausgefüllt.", - "forceKeyboardInteractiveLabel": "Tastaturinteraktion erzwingen", + "tailscaleNoDevices": "Keine Geräte in Ihrem Tailnet gefunden.", + "tailscaleDeviceAutoFill": "Die Auswahl eines Geräts füllt die Host-IP-Adresse automatisch aus.", + "forceKeyboardInteractiveLabel": "Keyboard Interactive erzwingen", "forceKeyboardInteractiveShortDesc": "Manuelle Passworteingabe erzwingen, auch wenn Schlüssel vorhanden sind", "allowLegacyAlgorithmsLabel": "Veraltete Algorithmen zulassen", - "allowLegacyAlgorithmsDesc": "Aktivieren Sie veraltete SSH-Algorithmen (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) für Verbindungen zu alten Geräten, die nicht aktualisiert werden können.", + "allowLegacyAlgorithmsDesc": "Veraltete SSH-Algorithmen aktivieren (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) für Verbindungen zu alten Geräten, die nicht aktualisiert werden können.", "insecure": "Unsicher", - "terminalAppearance": "Terminalaussehen", - "colorTheme": "Farbthema", + "terminalAppearance": "Terminal-Erscheinungsbild", + "colorTheme": "Farbschema", "fontFamilyLabel": "Schriftfamilie", "fontSizeLabel": "Schriftgröße", - "cursorStyleLabel": "Cursorform", + "cursorStyleLabel": "Cursor-Stil", "letterSpacingPx": "Buchstabenabstand (px)", "lineHeightLabel": "Zeilenhöhe", - "bellStyleLabel": "Signaltonart", - "backspaceModeLabel": "Rücktaste-Modus", - "cursorBlinking": "Blinkender Cursor", - "cursorBlinkingDesc": "Blinkanimation für den Terminalcursor aktivieren", + "bellStyleLabel": "Signalton-Stil", + "backspaceModeLabel": "Backspace-Modus", + "cursorBlinking": "Cursor-Blinken", + "cursorBlinkingDesc": "Blinkanimation für den Terminal-Cursor aktivieren", "rightClickSelectsWordLabel": "Rechtsklick wählt Wort aus", - "rightClickSelectsWordShortDesc": "Wählen Sie das Wort unter dem Cursor per Rechtsklick aus.", - "backgroundImageLabel": "URL des Hintergrundbildes", + "rightClickSelectsWordShortDesc": "Wort unter dem Cursor bei Rechtsklick auswählen", + "backgroundImageLabel": "Hintergrundbild-URL", "backgroundImageDesc": "Optionale URL für ein Terminal-Hintergrundbild", - "backgroundImageOpacityLabel": "Hintergrundbilddeckkraft", + "backgroundImageOpacityLabel": "Hintergrundbild-Deckkraft", "customThemeColors": "Benutzerdefinierte Farben", "customThemeBackground": "Hintergrund", "customThemeForeground": "Vordergrund", "customThemeCursor": "Cursor", - "customThemeCursorAccent": "Cursorakzent", + "customThemeCursorAccent": "Cursor-Akzent", "customThemeSelection": "Auswahl", "customThemeAnsiColors": "ANSI-Farben", "customThemeBlack": "Schwarz", @@ -758,15 +803,23 @@ "customThemeBrightGreen": "Hellgrün", "customThemeBrightYellow": "Hellgelb", "customThemeBrightBlue": "Hellblau", - "customThemeBrightMagenta": "Helles Magenta", - "customThemeBrightCyan": "Helles Cyan", + "customThemeBrightMagenta": "Hellmagenta", + "customThemeBrightCyan": "Hellcyan", "customThemeBrightWhite": "Hellweiß", "customThemeResetTooltip": "Auf Standardeinstellungen zurücksetzen", + "savedThemesLabel": "Gespeicherte Designs", + "saveAsGlobalTheme": "Als globales Design speichern", + "saveGlobalThemeNamePrompt": "Geben Sie einen Namen für dieses Thema ein.", + "saveGlobalThemeSuccess": "Thema gespeichert", + "saveGlobalThemeError": "Das Speichern des Designs ist fehlgeschlagen.", + "applyGlobalThemeTooltip": "Dieses Design anwenden", + "deleteGlobalThemeTooltip": "Dieses Design löschen", + "noSavedThemes": "Noch keine gespeicherten Designs", "syntaxHighlightingLabel": "Syntaxhervorhebung", - "syntaxHighlightingDesc": "Terminalausgabe farbig darstellen (Fehler, Pfade, IPs, Zeitstempel)", + "syntaxHighlightingDesc": "Terminalausgabe einfärben (Fehler, Pfade, IPs, Zeitstempel)", "syntaxHighlightingCategories": "Hervorhebungskategorien", - "syntaxHighlightingCategoriesDesc": "Wählen Sie aus, welche Inhaltstypen farblich hervorgehoben werden sollen.", - "syntaxCategoryLogLevels": "Protokollierungsstufen", + "syntaxHighlightingCategoriesDesc": "Wählen Sie, welche Inhaltstypen eingefärbt werden sollen", + "syntaxCategoryLogLevels": "Log-Level", "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "Dateipfade", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", @@ -778,69 +831,71 @@ "syntaxCategoryUrlsDesc": "https://example.com", "syntaxCategoryNumbers": "Beschriftete Zahlen", "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", - "behaviorAndAdvanced": "Verhalten & erweiterte Einstellungen", - "scrollbackBufferLabel": "Scrollback-Puffer", + "behaviorAndAdvanced": "Verhalten & Erweitert", + "scrollbackBufferLabel": "Rückrollpuffer", "scrollbackMaxLines": "Maximale Anzahl der im Verlauf gespeicherten Zeilen", "sshAgentForwardingLabel": "SSH-Agent-Weiterleitung", - "sshAgentForwardingShortDesc": "Ihre lokalen SSH-Schlüssel an diesen Host weiterleiten", + "sshAgentForwardingShortDesc": "Lokale SSH-Schlüssel an diesen Host weiterleiten", "useSSHTitleLabel": "SSH-Fenstertitel verwenden", - "useSSHTitleDesc": "Aktualisieren Sie den Tab-Titel anhand des Fenstertitels der Shell anstelle des Hostnamens.", + "useSSHTitleDesc": "Aktualisieren Sie den Tab-Titel vom Fenstertitel der Shell anstelle des Hostnamens", "enableAutoMosh": "Auto-Mosh aktivieren", - "enableAutoMoshDesc": "Mosh bevorzugen, falls verfügbar", + "enableAutoMoshDesc": "Mosh gegenüber SSH bevorzugen, falls verfügbar", "enableAutoTmux": "Auto-Tmux aktivieren", - "enableAutoTmuxDesc": "Automatisch eine tmux-Sitzung starten oder an eine bestehende anhängen", + "enableAutoTmuxDesc": "Automatisch eine tmux-Sitzung starten oder anhängen", "enableSessionLogging": "Sitzungsprotokollierung", - "enableSessionLoggingDesc": "Terminalausgabe zur späteren Überprüfung aufzeichnen", - "enableCommandHistory": "Befehlshistorie", - "enableCommandHistoryDesc": "In diesem Terminal ausgeführte Befehle für den Verlauf und die Autovervollständigung aufzeichnen", - "linkClickBehaviorLabel": "Link-Klickverhalten", - "linkClickBehaviorDesc": "Steuert das Verhalten beim Klicken auf einen Link im Terminal. Verwenden Sie „Standard“, um die Anwendungseinstellung zu übernehmen.", - "linkClickBehaviorDefault": "Standardeinstellung (entsprechend den App-Einstellungen)", + "enableSessionLoggingDesc": "Terminal-Sitzungsausgabe zur späteren Überprüfung aufzeichnen", + "allowSessionSharing": "Sitzungsfreigabe zulassen", + "allowSessionSharingDesc": "Ermöglichen Sie es, Live-Sitzungen auf diesem Host per Link oder mit anderen Benutzern zu teilen.", + "enableCommandHistory": "Befehlsverlauf", + "enableCommandHistoryDesc": "In diesem Terminal ausgeführte Befehle für Verlauf und Autovervollständigung aufzeichnen", + "linkClickBehaviorLabel": "Verhalten beim Linkklick", + "linkClickBehaviorDesc": "Legt fest, was passiert, wenn Sie im Terminal auf einen Link klicken. Verwenden Sie 'Standard', um die App-Einstellung zu übernehmen.", + "linkClickBehaviorDefault": "Standard (App-Einstellung folgen)", "linkClickBehaviorConfirm": "Popup zum Öffnen oder Kopieren anzeigen", "linkClickBehaviorDirect": "Direkt öffnen", - "sudoPasswordAutoFillLabel": "sudo-Passwort automatisch ausfüllen", - "sudoPasswordAutoFillShortDesc": "sudo-Passwort bei Aufforderung automatisch eingeben", - "sudoPasswordAutoFillDesc": "Speichern Sie ein sudo-Passwort, damit Host-Metriken, Terminal-Eingabeaufforderungen und andere Funktionen privilegierte Befehle automatisch ausführen können.", - "sudoPasswordLabel": "sudo-Passwort", + "sudoPasswordAutoFillLabel": "Sudo-Passwort automatisch ausfüllen", + "sudoPasswordAutoFillShortDesc": "Sudo-Passwort bei Aufforderung automatisch bereitstellen", + "sudoPasswordAutoFillDesc": "Sudo-Passwort speichern, damit Hostmetriken, Terminal-Eingabeaufforderungen und andere privilegierte Befehle automatisch ausgeführt werden können.", + "sudoPasswordLabel": "Sudo-Passwort", "environmentVariablesLabel": "Umgebungsvariablen", "addVariableBtn": "Variable hinzufügen", - "noEnvVars": "Es sind keine Umgebungsvariablen konfiguriert.", - "fastScrollModifierLabel": "Modifikatortaste für schnelles Scrollen", - "fastScrollSensitivityLabel": "Empfindlichkeit für schnelles Scrollen", + "noEnvVars": "Keine Umgebungsvariablen konfiguriert.", + "fastScrollModifierLabel": "Schnellscroll-Modifikator", + "fastScrollSensitivityLabel": "Schnellscroll-Empfindlichkeit", "moshCommandLabel": "Mosh-Befehl", - "startupSnippetLabel": "Start-Snippet", + "startupSnippetLabel": "Startup-Snippet", "keepaliveIntervalLabel": "Keepalive-Intervall (Sekunden)", - "maxKeepaliveMisses": "Max. Anzahl verpasster Keepalives", - "tunnelSettings": "Tunneleinstellungen", - "enableTunneling": "Tunneling aktivieren", - "enableTunnelingDesc": "Aktivieren Sie die SSH-Tunnelfunktionalität für diesen Host.", + "maxKeepaliveMisses": "Max. Keepalive-Fehlschläge", + "tunnelSettings": "Tunnel-Einstellungen", + "enableTunneling": "Tunnel aktivieren", + "enableTunnelingDesc": "SSH-Tunnel-Funktionalität für diesen Host aktivieren", "serverTunnelsSection": "Server-Tunnel", "addTunnelBtn": "Tunnel hinzufügen", - "noTunnelsConfigured": "Es sind keine Tunnel konfiguriert.", + "noTunnelsConfigured": "Keine Tunnel konfiguriert.", "tunnelLabel": "Tunnel {{number}}", "tunnelType": "Tunneltyp", - "tunnelModeLocalDesc": "Leiten Sie einen lokalen Port an einen Port auf dem Remote-Server (oder an einen von dort erreichbaren Host) weiter.", - "tunnelModeRemoteDesc": "Leiten Sie einen Port auf dem Remote-Server an einen lokalen Port auf Ihrem Rechner weiter.", - "tunnelModeDynamicDesc": "Erstellen Sie einen SOCKS5-Proxy auf einem lokalen Port für die dynamische Portweiterleitung.", + "tunnelModeLocalDesc": "Leitet einen lokalen Port an einen Port auf dem Remote-Server (oder einen von dort erreichbaren Host) weiter.", + "tunnelModeRemoteDesc": "Leitet einen Port auf dem Remote-Server zurück zu einem lokalen Port auf Ihrem Rechner weiter.", + "tunnelModeDynamicDesc": "Erstellt einen SOCKS5-Proxy auf einem lokalen Port für dynamische Portweiterleitung.", "sameHost": "Dieser Host (direkter Tunnel)", "endpointHost": "Endpunkt-Host", - "endpointHostPlaceholder": "Derselbe Host, ein SSH-Host oder ein erreichbarer Host bzw. eine erreichbare IP-Adresse", + "endpointHostPlaceholder": "Gleicher Host, SSH-Host oder erreichbarer Host/IP", "endpointPort": "Endpunkt-Port", - "bindHost": "Bind-Adresse", + "bindHost": "Bind-Host", "sourcePort": "Quellport", - "maxRetries": "Maximale Wiederholungsversuche", + "maxRetries": "Max. Wiederholungen", "retryIntervalS": "Wiederholungsintervall (s)", - "autoStartLabel": "Automatischer Start", - "autoStartDesc": "Dieser Tunnel wird automatisch verbunden, sobald der Host geladen ist.", + "autoStartLabel": "Autostart", + "autoStartDesc": "Diesen Tunnel automatisch verbinden, wenn der Host geladen wird", "tunnelConnecting": "Tunnel wird verbunden...", "tunnelDisconnected": "Tunnel getrennt", - "failedToConnectTunnel": "Verbindung fehlgeschlagen", - "failedToDisconnectTunnel": "Trennen fehlgeschlagen", + "failedToConnectTunnel": "Verbindungsaufbau fehlgeschlagen", + "failedToDisconnectTunnel": "Trennung fehlgeschlagen", "dockerIntegration": "Docker-Integration", "enableDockerMonitor": "Docker aktivieren", - "enableDockerMonitorDesc": "Überwachen und verwalten Sie Container auf diesem Host über Docker.", - "containerRuntime": "Container-Laufzeit", - "containerRuntimeDesc": "Wählen Sie die CLI aus, die für die Containerverwaltung auf diesem Host verwendet wird.", + "enableDockerMonitorDesc": "Container auf diesem Host über Docker überwachen und verwalten", + "containerRuntime": "Container-Runtime", + "containerRuntimeDesc": "Wählen Sie die CLI für die Container-Verwaltung auf diesem Host", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", "enableTmuxMonitor": "Tmux-Monitor aktivieren", @@ -848,84 +903,85 @@ "tabProxmox": "Proxmox", "proxmoxIntegration": "Proxmox-Integration", "enableProxmox": "Proxmox aktivieren", - "enableProxmoxDesc": "Markieren Sie diesen Host als Proxmox-Knoten. Dadurch wird die Gasterkennung und der Import direkt von diesem Host ermöglicht.", + "enableProxmoxDesc": "Diesen Host als Proxmox-Knoten markieren. Ermöglicht die Erkennung und den Import von Gästen direkt von diesem Host.", "proxmoxDefaultAuthType": "Standard-Authentifizierungstyp", - "proxmoxDefaultAuthTypeDesc": "Authentifizierungsmethode für importierte Gast-Hosts. Wählen Sie den Authentifizierungstyp, mit dem Sie sich bei Ihren Gastsystemen verbinden.", + "proxmoxDefaultAuthTypeDesc": "Authentifizierungsmethode, die auf importierte Gast-Hosts angewendet wird. Wählen Sie den Authentifizierungstyp, der Ihrer Verbindungsmethode zu den Gästen entspricht.", "authTypePassword": "Passwort", "authTypeKey": "SSH-Schlüssel", - "authTypeCredential": "Anmeldeinformationen", + "authTypeCredential": "Anmeldedaten", "authTypeOpkssh": "OPKSSH", "authTypeNone": "Keine", - "proxmoxDefaultCredential": "Standardanmeldeinformationen", - "proxmoxDefaultCredentialDesc": "Anmeldeinformationen, die für importierte Gasthosts verwendet werden, wenn der Authentifizierungstyp auf „Anmeldeinformationen“ eingestellt ist.", + "proxmoxDefaultCredential": "Standard-Anmeldedaten", + "proxmoxDefaultCredentialDesc": "Anmeldedaten, die für importierte Gastsysteme verwendet werden, wenn der Authentifizierungstyp auf Anmeldedaten gesetzt ist.", "proxmoxWindowsDetection": "Windows-/RDP-Erkennung", - "proxmoxWindowsDetectionDesc": "Durch Kommas getrennte Namensmuster, die RDP anstelle von SSH auslösen (Groß-/Kleinschreibung wird nicht beachtet)", + "proxmoxWindowsDetectionDesc": "Kommagetrennte Namensmuster, die RDP statt SSH auslösen (Groß-/Kleinschreibung wird nicht beachtet).", "proxmoxDockerDetection": "Docker-Erkennung", - "proxmoxDockerDetectionDesc": "Durch Kommas getrennte Namensmuster, die Docker für passende Gäste aktivieren", + "proxmoxDockerDetectionDesc": "Kommagetrennte Namensmuster, die Docker für passende Gastsysteme aktivieren.", "proxmoxPreferredRanges": "Bevorzugte IP-Bereiche", - "proxmoxPreferredRangesDesc": "Durch Kommas getrennte Präfixe in Prioritätsreihenfolge für die IP-Auswahl, wenn ein Gast über mehrere Schnittstellen verfügt.", - "proxmoxAutoSync": "Gäste automatisch synchronisieren", - "proxmoxAutoSyncDesc": "Ermitteln Sie diesen Proxmox-Knoten regelmäßig und erstellen oder aktualisieren Sie importierte Gasthosts, während Ihre Sitzung entsperrt ist.", + "proxmoxPreferredRangesDesc": "Kommagetrennte Präfixe in Prioritätsreihenfolge für die IP-Auswahl, wenn ein Gastsystem mehrere Schnittstellen hat.", + "proxmoxAutoSync": "Gastsysteme automatisch synchronisieren", + "proxmoxAutoSyncDesc": "Diesen Proxmox-Knoten periodisch erkennen und importierte Gastsysteme erstellen oder aktualisieren, während Ihre Sitzung entsperrt ist.", "proxmoxSyncInterval": "Synchronisierungsintervall (Minuten)", "proxmoxSyncIntervalDesc": "Mindestens 5 Minuten. Der Scheduler überspringt gesperrte Benutzersitzungen.", - "proxmoxMarkMissing": "Mark vermisste Gäste", - "proxmoxMarkMissingDesc": "Füge ein proxmox-missing-Tag hinzu, wenn ein zuvor importierter Gast verschwindet, anstatt ihn zu löschen.", + "proxmoxMarkMissing": "Fehlende Gastsysteme markieren", + "proxmoxMarkMissingDesc": "Ein proxmox-missing-Tag hinzufügen, wenn ein zuvor importiertes Gastsystem verschwindet, anstatt es zu löschen.", "proxmoxLastSync": "Letzte Synchronisierung", "proxmoxLastSyncNever": "Noch nicht synchronisiert", "proxmoxLastSyncNoResult": "Keine Ergebnisdetails", - "proxmoxLastSyncSummary": "{{created}} erstellt, {{updated}} aktualisiert, {{markedMissing}} fehlt, {{skipped}} übersprungen", + "proxmoxLastSyncSummary": "{{created}} erstellt, {{updated}} aktualisiert, {{markedMissing}} fehlend, {{skipped}} übersprungen", "proxmoxLastSyncStatus": { "success": "Erfolg", "error": "Fehlgeschlagen", "pending": "Ausstehend" }, - "proxmoxDiscoverAction": "Proxmox-Gäste entdecken und importieren", - "proxmoxImportTitle": "Import von Proxmox", - "proxmoxSelectHost": "Wählen Sie einen Proxmox-Host aus…", - "proxmoxDiscover": "Ermitteln", - "proxmoxDiscovering": "Wird ermittelt…", - "proxmoxDiscoverGuests": "Gäste ermitteln", + "proxmoxDiscoverAction": "Proxmox-Gastsysteme erkennen & importieren", + "proxmoxImportTitle": "Von Proxmox importieren", + "proxmoxSelectHost": "Proxmox-Host auswählen…", + "proxmoxDiscover": "Erkennen", + "proxmoxDiscovering": "Erkennen…", + "proxmoxDiscoverGuests": "Gastsysteme erkennen", "proxmoxGuestsSelected_one": "{{count}} Gast — {{selected}} ausgewählt", "proxmoxGuestsSelected_other": "{{count}} Gäste — {{selected}} ausgewählt", "proxmoxSelectAll": "Alle auswählen", "proxmoxDeselectAll": "Alle abwählen", - "proxmoxNoGuests": "Auf diesem Proxmox-Knoten wurden keine Gäste gefunden.", + "proxmoxNoGuests": "Keine Gastsysteme auf diesem Proxmox-Knoten gefunden.", "proxmoxImportButton_one": "{{count}} Host importieren", "proxmoxImportButton_other": "{{count}} Hosts importieren", "proxmoxResultImported": "{{count}} importiert", "proxmoxResultUpdated": "{{count}} aktualisiert", "proxmoxResultFailed": "{{count}} fehlgeschlagen", + "proxmoxResultSkippedNoIp": "{{count}} übersprungen (keine IP-Adresse gefunden)", "proxmoxImportComplete": "Proxmox-Import abgeschlossen: {{summary}}", - "proxmoxDiscoveryFailed": "Ermittlung fehlgeschlagen", + "proxmoxDiscoveryFailed": "Erkennung fehlgeschlagen", "proxmoxImportFailed": "Import fehlgeschlagen", "enableFileManagerMonitor": "Dateimanager aktivieren", "enableFileManagerMonitorDesc": "Dateien auf diesem Host über SFTP durchsuchen und verwalten", "scpLegacyLabel": "SCP-Legacy-Modus", - "scpLegacyDesc": "Verwenden Sie die herkömmliche Dateiübertragung für Server, die das SFTP-Subsystem nicht unterstützen (z. B. eingebettete oder minimale SSH-Server).", + "scpLegacyDesc": "Verwenden Sie die Legacy-Dateiübertragung für Server, die das SFTP-Subsystem nicht unterstützen (z. B. eingebettete oder minimale SSH-Server).", "defaultPathLabel": "Standardpfad", - "fileManagerPathHint": "Das Verzeichnis, das beim Start des Dateimanagers für diesen Host geöffnet werden soll.", + "fileManagerPathHint": "Das Verzeichnis, das geöffnet wird, wenn der Dateimanager für diesen Host gestartet wird.", "statusChecksLabel": "Statusprüfungen", "enableStatusChecks": "Statusprüfungen aktivieren", - "enableStatusChecksDesc": "Verfügbarkeit dieses Hosts regelmäßig per Ping prüfen", + "enableStatusChecksDesc": "Diesen Host regelmäßig anpingen, um die Verfügbarkeit zu überprüfen", "useGlobalInterval": "Globales Intervall verwenden", - "useGlobalIntervalDesc": "Stattdessen das serverweite Statusprüfungsintervall verwenden", + "useGlobalIntervalDesc": "Mit dem serverweiten Statusprüfungsintervall überschreiben", "checkIntervalS": "Prüfintervall (s)", - "checkIntervalDesc": "Sekunden zwischen jedem Verbindungsping", - "metricsCollectionLabel": "Metrikensammlung", + "checkIntervalDesc": "Sekunden zwischen den einzelnen Verbindungspings", + "metricsCollectionLabel": "Metrikerfassung", "enableMetricsLabel": "Metriken aktivieren", - "enableMetricsDesc": "Erfassen Sie CPU-, RAM-, Festplatten- und andere Metriken von diesem Host", + "enableMetricsDesc": "CPU, RAM, Festplatten und andere Metriken von diesem Host erfassen", "useGlobalMetrics": "Globales Intervall verwenden", - "useGlobalMetricsDesc": "Serverweites Metrikintervall verwenden", + "useGlobalMetricsDesc": "Mit dem serverweiten Metrikintervall überschreiben", "metricsIntervalS": "Metrikintervall (s)", - "metricsIntervalDesc2": "Sekunden zwischen Metrik-Snapshots", + "metricsIntervalDesc2": "Sekunden zwischen Metrik-Momentaufnahmen", "visibleWidgets": "Sichtbare Widgets", - "widgetsMovedToHostMetrics": "Karten können jetzt direkt im Tab „Hostmetriken“ hinzugefügt, angeordnet und in der Größe angepasst werden. Öffnen Sie die Hostmetriken für diesen Host und wählen Sie über „Anpassen“ aus, welche Karten angezeigt werden sollen.", + "widgetsMovedToHostMetrics": "Karten werden jetzt direkt im Tab „Host-Metriken“ hinzugefügt, angeordnet und in der Größe verändert. Öffnen Sie die Host-Metriken für diesen Host und verwenden Sie „Anpassen“, um auszuwählen, welche Karten angezeigt werden.", "cpuUsageLabel": "CPU-Auslastung", - "cpuUsageDesc": "CPU-Auslastung, Lastdurchschnitte, Sparkline-Diagramm", - "memoryLabel": "Arbeitsspeicherauslastung", - "memoryDesc": "RAM-Nutzung, Auslagerungsspeicher, Cache", + "cpuUsageDesc": "CPU-Prozent, Lastdurchschnitt, Sparkline-Diagramm", + "memoryLabel": "Speicherauslastung", + "memoryDesc": "RAM-Nutzung, Swap, zwischengespeichert", "storageLabel": "Festplattennutzung", - "storageDesc": "Festplattennutzung pro Einhängepunkt", + "storageDesc": "Festplattennutzung pro Mount-Point", "networkLabel": "Netzwerkschnittstellen", "networkDesc": "Schnittstellenliste und Bandbreite", "uptimeLabel": "Betriebszeit", @@ -936,12 +992,12 @@ "recentLoginsDesc": "Erfolgreiche und fehlgeschlagene Anmeldeereignisse", "topProcessesLabel": "Top-Prozesse", "topProcessesDesc": "PID, CPU%, MEM%, Befehl", - "listeningPortsLabel": "Lauschende Ports", + "listeningPortsLabel": "Offene Ports", "listeningPortsDesc": "Offene Ports mit Prozess und Status", "firewallLabel": "Firewall", - "firewallDesc": "Firewall-, AppArmor- und SELinux-Status", + "firewallDesc": "Status von Firewall, AppArmor, SELinux", "quickActionsLabel": "Schnellaktionen", - "quickActionsToolbar": "Schnellaktionen werden als Schaltflächen in der Symbolleiste der Host-Metriken angezeigt, um Befehle mit einem Klick auszuführen.", + "quickActionsToolbar": "Schnellaktionen erscheinen als Schaltflächen in der Host-Metriken-Symbolleiste zur Ausführung von Befehlen mit einem Klick.", "noQuickActions": "Noch keine Schnellaktionen.", "buttonLabel": "Schaltflächenbeschriftung", "selectSnippetPlaceholder": "Snippet auswählen...", @@ -959,62 +1015,68 @@ "credentialUpdated": "Anmeldeinformationen aktualisiert", "credentialCreated": "Anmeldeinformationen erstellt", "failedToSaveCredential": "Anmeldeinformationen konnten nicht gespeichert werden", - "credentialNameRequired": "Bitte geben Sie einen Namen für die Zugangsdaten ein.", - "credentialAuthRequired": "Fügen Sie ein Passwort, einen SSH-Schlüssel oder beides hinzu.", - "backToHosts": "Zurück zu den Hosts", - "backToCredentials": "Zurück zu den Anmeldeinformationen", - "pinned": "Angepinnt", + "credentialNameRequired": "Bitte geben Sie einen Namen für die Anmeldeinformationen ein", + "credentialAuthRequired": "Passwort, SSH-Schlüssel oder beides hinzufügen", + "createCredentialFromHostBtn": "Anmeldeinformationen erstellen", + "createCredentialFromHostTitle": "Anmeldeinformationen vom Host erstellen", + "createCredentialFromHostDesc": "Erstellen Sie einen wiederverwendbaren, teilbaren Anmeldeinformationseintrag, der mit dem aktuellen Benutzernamen, Passwort und/oder SSH-Schlüssel dieses Hosts vorausgefüllt ist.", + "backToHosts": "Zurück zu Hosts", + "backToCredentials": "Zurück zu Anmeldedaten", + "pinned": "Angeheftet", "noHostsFound": "Keine Hosts gefunden", - "tryDifferentTerm": "Versuchen Sie einen anderen Begriff", - "addFirstHost": "Fügen Sie zunächst Ihren ersten Host hinzu.", - "noCredentialsFound": "Es wurden keine Anmeldeinformationen gefunden.", - "addCredentialBtn": "Anmeldeinformationen hinzufügen", - "updateCredentialBtn": "Anmeldeinformationen aktualisieren", + "tryDifferentTerm": "Versuchen Sie einen anderen Suchbegriff", + "addFirstHost": "Fügen Sie Ihren ersten Host hinzu, um loszulegen", + "noCredentialsFound": "Keine Anmeldedaten gefunden", + "addCredentialBtn": "Anmeldedaten hinzufügen", + "updateCredentialBtn": "Anmeldedaten aktualisieren", "features": "Funktionen", "noFolder": "(Kein Ordner)", "deleteSelected": "Löschen", - "exitSelection": "Auswahl beenden", - "importSkip": "Importieren (vorhandene überspringen)", - "importOverwrite": "Importieren (überschreiben)", + "exitSelection": "Auswahl verlassen", + "importSkip": "Importieren (Bestehende überspringen)", + "importOverwrite": "Importieren (Überschreiben)", "collapseBtn": "Einklappen", - "importExportBtn": "Import / Export", - "hostStatusesRefreshed": "Hoststatus aktualisiert", - "failedToRefreshHosts": "Fehler beim Aktualisieren der Hosts", + "importExportBtn": "Importieren / Exportieren", + "hostStatusesRefreshed": "Host-Status aktualisiert", + "failedToRefreshHosts": "Aktualisieren der Hosts fehlgeschlagen", "movedHostTo": "{{host}} nach \"{{folder}}\" verschoben", - "failedToMoveHost": "Fehler beim Verschieben des Hosts", + "failedToMoveHost": "Verschieben des Hosts fehlgeschlagen", "folderRenamedTo": "Ordner in \"{{name}}\" umbenannt", "deletedFolder": "Ordner \"{{name}}\" gelöscht", - "failedToDeleteFolder": "Ordner konnte nicht gelöscht werden", - "deleteAllInFolder": "Alle Hosts in \"{{name}}\" löschen? Dies kann nicht rückgängig gemacht werden.", + "failedToDeleteFolder": "Löschen des Ordners fehlgeschlagen", + "deleteAllInFolder": "Alle Hosts in \"{{name}}\" löschen? Dieser Vorgang kann nicht rückgängig gemacht werden.", "folderPickerPlaceholder": "Kein Ordner", - "folderPickerSearch": "Suchen oder erstellen (verwenden Sie / für Unterordner)...", + "folderPickerSearch": "Suchen oder erstellen (mit / Unterordner anlegen)...", "folderPickerNone": "Kein Ordner", "folderPickerCreate": "\"{{path}}\" erstellen", "folderPickerEmpty": "Keine passenden Ordner", "newFolder": "Neuer Ordner", "createFolderTitle": "Ordner erstellen", "editFolderTitle": "Ordner bearbeiten", - "folderDialogDescription": "Wählen Sie einen Namen, eine Farbe und ein Symbol. Verwenden Sie / zum Verschachteln von Ordnern.", + "folderDialogDescription": "Wählen Sie einen Namen, eine Farbe und ein Symbol. Verwenden Sie /, um Ordner zu verschachteln.", "folderNameLabel": "Ordnername", - "folderNamePlaceholder": "z. B. Produktion/Web", - "folderNestingHint": "Verwenden Sie / um Ebenen zu trennen und verschachtelte Ordner zu erstellen.", + "folderNamePlaceholder": "z.B. Produktion/Web", + "folderNestingHint": "Verwenden Sie /, um Ebenen zu trennen und verschachtelte Ordner zu erstellen.", "folderColor": "Farbe", "folderIcon": "Symbol", + "folderCredential": "Berechtigung", + "folderCredentialNone": "Keine Zugangsdaten zugewiesen", + "folderCredentialHint": "Hosts in diesem Ordner, die die Authentifizierung mit \"Gespeicherten Anmeldeinformationen\" verwenden, ohne eigene Anmeldeinformationen ausgewählt zu haben, erben diese.", "folderPreview": "Vorschau", "folderNameFallback": "Unbenannter Ordner", "createFolderButton": "Ordner erstellen", "saveFolderButton": "Ordner speichern", "cancel": "Abbrechen", - "iconSearchPlaceholder": "Symbole suchen...", + "iconSearchPlaceholder": "Symbole durchsuchen...", "openAllSessions": "Alle Sitzungen öffnen", "editFolder": "Ordner bearbeiten", "deleteFolder": "Ordner löschen", "folderSaved": "Ordner gespeichert", - "failedToSaveFolder": "Ordner konnte nicht gespeichert werden", + "failedToSaveFolder": "Fehler beim Speichern des Ordners", "folderDeleted": "Ordner \"{{name}}\" gelöscht", - "deleteFolderConfirm": "\"{{name}}\" und die darin enthaltenen {{count}} Host(s) löschen? Dies kann nicht rückgängig gemacht werden.", + "deleteFolderConfirm": "„{{name}}“ und die dazugehörigen {{count}} Host(s) löschen? Dies kann nicht rückgängig gemacht werden.", "failedToMoveHosts": "Fehler beim Verschieben der Hosts", - "expandAll": "Alle Ordner erweitern", + "expandAll": "Alle Ordner ausklappen", "collapseAll": "Alle Ordner einklappen", "moreActions": "Mehr", "groupBy": "Gruppieren nach", @@ -1022,16 +1084,16 @@ "GroupByTag": "Tag", "GroupByStatus": "Status", "GroupByProtocol": "Protokoll", - "GroupByAuth": "Authentifizierungstyp", - "groupUngrouped": "Nicht gruppiert", + "GroupByAuth": "Auth-Typ", + "groupUngrouped": "Ohne Gruppe", "deletedHost": "{{name}} gelöscht", "copiedToClipboard": "In die Zwischenablage kopiert", "terminalUrlCopied": "Terminal-URL kopiert", "fileManagerUrlCopied": "Dateimanager-URL kopiert", "tunnelUrlCopied": "Tunnel-URL kopiert", "dockerUrlCopied": "Docker-URL kopiert", - "hostMetricsUrlCopied": "Host Metrics-URL kopiert", - "tmuxMonitorUrlCopied": "Tmux Monitor-URL kopiert", + "hostMetricsUrlCopied": "Host-Metriken-URL kopiert", + "tmuxMonitorUrlCopied": "Tmux-Monitor-URL kopiert", "rdpUrlCopied": "RDP-URL kopiert", "vncUrlCopied": "VNC-URL kopiert", "telnetUrlCopied": "Telnet-URL kopiert", @@ -1039,35 +1101,35 @@ "expandActions": "Aktionen erweitern", "collapseActions": "Aktionen einklappen", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Magisches Paket an {{name}} gesendet", - "wakeOnLanError": "Das Senden des Magic Packets ist fehlgeschlagen.", + "wakeOnLanSuccess": "Magic Packet an {{name}} gesendet", + "wakeOnLanError": "Fehler beim Senden des Magic Packets", "cloneHostAction": "Host klonen", "copyAddress": "Adresse kopieren", "copyLink": "Link kopieren", "copyTerminalUrlAction": "Terminal-URL kopieren", - "copyFileManagerUrlAction": "URL des Dateimanagers kopieren", + "copyFileManagerUrlAction": "Dateimanager-URL kopieren", "copyTunnelUrlAction": "Tunnel-URL kopieren", "copyDockerUrlAction": "Docker-URL kopieren", - "copyHostMetricsUrlAction": "URL für Host-Metriken kopieren", - "copyTmuxMonitorUrlAction": "Tmux Monitor-URL kopieren", + "copyHostMetricsUrlAction": "Host-Metriken-URL kopieren", + "copyTmuxMonitorUrlAction": "Tmux-Monitor-URL kopieren", "copyRdpUrlAction": "RDP-URL kopieren", "copyVncUrlAction": "VNC-URL kopieren", "copyTelnetUrlAction": "Telnet-URL kopieren", "copyRemoteDesktopUrlAction": "Remote-Desktop-URL kopieren", - "deleteCredentialConfirm": "Zugangsdaten „{{name}}“ löschen?", - "deletedCredential": "Gelöscht {{name}}", + "deleteCredentialConfirm": "Anmeldedaten \"{{name}}\" löschen?", + "deletedCredential": "{{name}} gelöscht", "deploySSHKeyTitle": "SSH-Schlüssel bereitstellen", - "deployingBtn": "Wird bereitgestellt...", + "deployingBtn": "Wird bereitgestellt…", "deployBtn": "Bereitstellen", "failedToDeployKey": "Fehler beim Bereitstellen des Schlüssels", "deleteHostsConfirm": "{{count}} Host{{plural}} löschen? Dies kann nicht rückgängig gemacht werden.", - "movedToRoot": "Nach Stammverzeichnis verschoben", + "movedToRoot": "Zum Stammverzeichnis verschoben", "enableTerminalFeature": "Terminal aktivieren", "disableTerminalFeature": "Terminal deaktivieren", "enableFilesFeature": "Dateien aktivieren", "disableFilesFeature": "Dateien deaktivieren", - "enableTunnelsFeature": "Tunnel aktivieren", - "disableTunnelsFeature": "Tunnel deaktivieren", + "enableTunnelsFeature": "Tunnels aktivieren", + "disableTunnelsFeature": "Tunnels deaktivieren", "enableDockerFeature": "Docker aktivieren", "disableDockerFeature": "Docker deaktivieren", "enableProxmoxFeature": "Proxmox aktivieren", @@ -1075,74 +1137,74 @@ "addTagsPlaceholder": "Tags hinzufügen...", "authDetails": "Authentifizierungsdetails", "credType": "Typ", - "generateKeyPairDesc": "Es wird ein neues Schlüsselpaar generiert, wobei sowohl der private als auch der öffentliche Schlüssel automatisch ausgefüllt werden.", - "generatingKey": "Wird generiert...", + "generateKeyPairDesc": "Ein neues Schlüsselpaar generieren. Der private und der öffentliche Schlüssel werden automatisch ausgefüllt.", + "generatingKey": "Generiere...", "generateLabel": "{{label}} generieren", "uploadFileBtn": "Datei hochladen", - "keyPassphraseOptional": "Schlüsselpasswort (optional)", + "keyPassphraseOptional": "Schlüsselpassphrase (optional)", "sshPublicKeyOptional": "Öffentlicher SSH-Schlüssel (optional)", "publicKeyGenerated": "Öffentlicher Schlüssel generiert", - "failedToGeneratePublicKey": "Öffentlicher Schlüssel konnte nicht abgeleitet werden", + "failedToGeneratePublicKey": "Ableitung des öffentlichen Schlüssels fehlgeschlagen", "publicKeyCopied": "Öffentlicher Schlüssel kopiert", "keyPairGenerated": "{{label}}-Schlüsselpaar generiert", - "failedToGenerateKeyPair": "Schlüsselpaar konnte nicht generiert werden", - "searchHostsPlaceholder": "Hosts, Adressen und Tags durchsuchen…", - "searchCredentialsPlaceholder": "Anmeldeinformationen suchen…", + "failedToGenerateKeyPair": "Schlüsselpaar-Generierung fehlgeschlagen", + "searchHostsPlaceholder": "Hosts, Adressen, Tags durchsuchen…", + "searchCredentialsPlaceholder": "Anmeldedaten durchsuchen…", "refreshBtn": "Aktualisieren", "addTag": "Tags hinzufügen...", "deleteConfirmBtn": "Löschen", - "tunnelRequirementsText": "Der SSH-Server muss in der Datei /etc/ssh/sshd_config die Einstellungen GatewayPorts yes, AllowTcpForwarding yes und PermitRootLogin yes haben.", - "deleteHostConfirm": "Löschen \"{{name}}\"?", - "enableAtLeastOneProtocol": "Aktivieren Sie mindestens eines der oben genannten Protokolle, um die Authentifizierungs- und Verbindungseinstellungen zu konfigurieren.", - "keyPassphrase": "Schlüsselpasswort", + "tunnelRequirementsText": "Der SSH-Server muss in /etc/ssh/sshd_config die Optionen GatewayPorts yes, AllowTcpForwarding yes und PermitRootLogin yes gesetzt haben.", + "deleteHostConfirm": "\"{{name}}\" löschen?", + "enableAtLeastOneProtocol": "Aktivieren Sie mindestens ein Protokoll oben, um Authentifizierungs- und Verbindungseinstellungen zu konfigurieren.", + "keyPassphrase": "Schlüsselpassphrase", "connectBtn": "Verbinden", "disconnectBtn": "Trennen", "basicInformation": "Grundlegende Informationen", "authDetailsSection": "Authentifizierungsdetails", "credTypeLabel": "Typ", "hostsTab": "Hosts", - "credentialsTab": "Anmeldeinformationen", + "credentialsTab": "Zugangsdaten", "selectMultiple": "Mehrere auswählen", "selectHosts": "Hosts auswählen", "connectionLabel": "Verbindung", "authenticationLabel": "Authentifizierung", "generateKeyPairTitle": "Schlüsselpaar generieren", - "generateKeyPairDescription": "Es wird ein neues Schlüsselpaar generiert, wobei sowohl der private als auch der öffentliche Schlüssel automatisch ausgefüllt werden.", + "generateKeyPairDescription": "Ein neues Schlüsselpaar generieren, sowohl privater als auch öffentlicher Schlüssel werden automatisch ausgefüllt.", "generateFromPrivateKey": "Aus privatem Schlüssel generieren", "refreshBtn2": "Aktualisieren", - "exitSelectionTitle": "Auswahl beenden", + "exitSelectionTitle": "Auswahl verlassen", "exportAll": "Alle exportieren", - "exportForSharing": "Für die Freigabe exportieren", + "exportForSharing": "Zum Teilen exportieren", "addHostBtn2": "Host hinzufügen", - "addCredentialBtn2": "Anmeldeinformationen hinzufügen", - "checkingHostStatuses": "Hoststatus werden überprüft...", - "pinnedSection": "Angepinnt", + "addCredentialBtn2": "Zugangsdaten hinzufügen", + "checkingHostStatuses": "Host-Status werden überprüft...", + "pinnedSection": "Angeheftet", "hostsExported": "Hosts erfolgreich exportiert", - "hostsShareExported": "Freigebbare Hosts erfolgreich exportiert", - "exportFailed": "Hosts konnten nicht exportiert werden", + "hostsShareExported": "Teilbare Hosts erfolgreich exportiert", + "exportFailed": "Export der Hosts fehlgeschlagen", "sampleDownloaded": "Beispieldatei heruntergeladen", - "failedToDeleteCredential2": "Anmeldeinformationen konnten nicht gelöscht werden", + "failedToDeleteCredential2": "Löschen der Zugangsdaten fehlgeschlagen", "noFolderOption": "(Kein Ordner)", "nSelected": "{{count}} ausgewählt", "featuresMenu": "Funktionen", "moveMenu": "Verschieben", "connectSelected": "Verbinden", "cancelSelection": "Abbrechen", - "deployDialogDesc": "{{name}} in der Datei authorized_keys eines Hosts bereitstellen.", + "deployDialogDesc": "{{name}} auf die authorized_keys eines Hosts übertragen.", "targetHostLabel": "Zielhost", - "selectHostOption": "Wählen Sie einen Host aus...", - "keyDeployedSuccess": "Schlüssel erfolgreich bereitgestellt", - "failedToDeployKey2": "Fehler beim Bereitstellen des Schlüssels", + "selectHostOption": "Host auswählen...", + "keyDeployedSuccess": "Schlüssel erfolgreich übertragen", + "failedToDeployKey2": "Übertragung des Schlüssels fehlgeschlagen", "deletedCount": "{{count}} Hosts gelöscht", - "failedToDeleteCount": "{{count}} Hosts konnten nicht gelöscht werden", - "duplicatedHost": "\"{{name}}\" dupliziert", - "failedToDuplicateHost": "Fehler beim Duplizieren des Hosts", + "failedToDeleteCount": "Löschen von {{count}} Hosts fehlgeschlagen", + "duplicatedHost": "Dupliziert \"{{name}}\"", + "failedToDuplicateHost": "Host konnte nicht dupliziert werden", "updatedCount": "{{count}} Hosts aktualisiert", "friendlyNameLabel": "Anzeigename", "descriptionLabel": "Beschreibung", "loadingHost": "Host wird geladen...", "loadingHosts": "Hosts werden geladen...", - "loadingCredentials": "Anmeldeinformationen werden geladen...", + "loadingCredentials": "Anmeldedaten werden geladen...", "noHostsYet": "Noch keine Hosts", "noHostsMatchSearch": "Keine Hosts entsprechen Ihrer Suche", "hostNotFound": "Host nicht gefunden", @@ -1155,17 +1217,17 @@ "sortIpDesc": "IP-Adresse (absteigend)", "sortOnlineFirst": "Online zuerst", "sortOfflineFirst": "Offline zuerst", - "sortPinnedFirst": "Angeheftete zuerst", + "sortPinnedFirst": "Angeheftet zuerst", "filterHosts": "Hosts filtern", - "filterClearAll": "Filter löschen", + "filterClearAll": "Alle Filter löschen", "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Angepinnt", + "filterPinned": "Angeheftet", "filterAuthGroup": "Authentifizierungstyp", "filterAuthPassword": "Passwort", "filterAuthKey": "SSH-Schlüssel", - "filterAuthCredential": "Anmeldeinformationen", + "filterAuthCredential": "Anmeldeinformation", "filterAuthNone": "Keine", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protokoll", @@ -1181,8 +1243,12 @@ "filterTagsGroup": "Tags", "shareHost": "Host teilen", "shareHostTitle": "Teilen: {{name}}", + "shareFolder": "Freigegebener Ordner", + "shareFolderTitle": "Freigebener Ordner: {{name}}", + "folderSharedSuccessfully": "Gemeinsame {{count}} Hosts im Ordner", + "failedToShareFolder": "Ordner konnte nicht freigegeben werden", "sharing": { - "loadError": "Die Freigabedaten konnten nicht geladen werden. Bitte versuchen Sie es erneut.", + "loadError": "Fehler beim Laden der Freigabedaten. Bitte versuchen Sie es erneut.", "shareWithSection": "Teilen mit", "usersTab": "Benutzer", "rolesTab": "Rollen", @@ -1192,57 +1258,60 @@ "levels": { "connect": { "label": "Verbinden", - "description": "Nur offene Sitzungen: Terminal, Remote-Desktop, Dateimanager, Tunnel und Docker. Kein Zugriff auf die Host-Konfiguration." + "description": "Nur Sitzungen öffnen: Terminal, Remotedesktop, Dateimanager, Tunnel und Docker. Kein Zugriff auf die Hostkonfiguration." }, "view": { - "label": "Sicht", - "description": "Stellen Sie eine Verbindung her und sehen Sie sich die Host-Konfiguration an. Geheimnisse werden niemals angezeigt." + "label": "Anzeigen", + "description": "Verbinden sowie Hostkonfiguration anzeigen. Geheimnisse werden nie angezeigt." }, "edit": { "label": "Bearbeiten", - "description": "Hostdaten können angezeigt und geändert werden. Geheimnisse können ersetzt, aber niemals gelesen werden; Zugangsdaten bleiben nur dem Besitzer zugänglich." + "description": "Anzeigen sowie Host bearbeiten. Geheimnisse können ersetzt, aber nie gelesen werden; Zugangsdaten-Zuweisungen bleiben nur dem Besitzer vorbehalten." }, "manage": { "label": "Verwalten", - "description": "Bearbeiten und den Host mit anderen teilen, Berechtigungsstufen ändern und den Zugriff widerrufen." + "description": "Bearbeiten sowie Host für andere freigeben, Berechtigungsstufen ändern und Zugriff entziehen." } }, - "expiryLabel": "Ablauf des Zugriffs", + "expiryLabel": "Zugriffsablauf", "expiry": { - "never": "Niemals", + "never": "Nie", "oneHour": "1 Stunde", "oneDay": "24 Stunden", "sevenDays": "7 Tage", "thirtyDays": "30 Tage", - "custom": "Brauch" + "custom": "Benutzerdefiniert" }, - "customHoursPlaceholder": "Zeit bis zum Ablauf des Zugangs", - "shareButton": "Aktie", + "customHoursPlaceholder": "Stunden bis zum Zugriffsablauf", + "shareButton": "Teilen", "shareWithCount": "Teilen ({{count}})", "currentAccess": "Aktueller Zugriff", - "noAccessEntries": "Dieser Host wurde noch nicht geteilt.", - "grantedBy": "Erteilt von", + "noAccessEntries": "Dieser Host wurde noch nicht freigegeben", + "folderShareSummary": "Gemeinsam genutzt {{shared}} von {{total}} Hosts in diesem Ordner", + "grantedBy": "Freigegeben von", "expires": "Läuft ab", "expired": "Abgelaufen", - "never": "Niemals", + "never": "Nie", "revoke": "Widerrufen", "accessUpdated": "Zugriff aktualisiert", "accessUpdateFailed": "Zugriff konnte nicht aktualisiert werden", - "sharedBadge": "Gemeinsam", - "sharedBadgeTooltip": "Gemeinsam genutzt von {{owner}} ({{level}} Zugriff)", - "viewOnlyBanner": "Dieser Host wird Ihnen von {{owner}} mit Lesezugriff zur Verfügung gestellt. Die Konfiguration ist schreibgeschützt.", - "sharedEditBanner": "Dieser Host wird Ihnen von {{owner}} mit Bearbeitungsrechten zur Verfügung gestellt. Änderungen wirken sich auf den eigentlichen Host aus; Authentifizierungsreferenzen können nur vom Eigentümer geändert werden.", - "ownerOnlyControl": "Nur der Host-Besitzer kann dies ändern." + "sharedBadge": "Freigegeben", + "sharedBadgeTooltip": "Freigegeben von {{owner}} ({{level}}-Zugriff)", + "viewOnlyBanner": "Dieser Host wurde von {{owner}} mit Lesezugriff für Sie freigegeben. Die Konfiguration ist schreibgeschützt.", + "sharedEditBanner": "Dieser Host wurde von {{owner}} mit Bearbeitungszugriff für Sie freigegeben. Änderungen wirken sich auf den tatsächlichen Host aus; Authentifizierungsreferenzen können nur vom Besitzer geändert werden.", + "ownerOnlyControl": "Nur der Host-Besitzer kann dies ändern" }, "guac": { "connection": "Verbindung", "authentication": "Authentifizierung", - "storedCredential": "Gespeicherte Anmeldeinformationen", - "noCredential": "Keine Anmeldeinformationen (direkte Anmeldeinformationen siehe unten)", + "storedCredential": "Gespeicherte Anmeldeinformation", + "noCredential": "Keine Anmeldeinformation (direkte Anmeldedaten unten)", "authMethod": "Authentifizierungsmethode", "authTypeDirect": "Direkt", - "authTypeCredential": "Zugangsdaten", - "selectCredential": "Wählen Sie eine Anmeldeinformation aus...", + "authTypeCredential": "Anmeldeinformation", + "authTypeNone": "Keiner", + "authTypeNoneDesc": "Es werden keine Zugangsdaten gespeichert. Sie werden bei jeder Verbindung zur Eingabe eines Benutzernamens und eines Passworts aufgefordert; diese werden nicht gespeichert.", + "selectCredential": "Anmeldeinformation auswählen...", "connectionSettings": "Verbindungseinstellungen", "displaySettings": "Anzeigeeinstellungen", "audioSettings": "Audioeinstellungen", @@ -1259,24 +1328,25 @@ "rdpPort": "RDP-Port", "username": "Benutzername", "password": "Passwort", + "passwordSaved": "Passwort gespeichert, zum Ändern eingeben", "domain": "Domäne", "securityMode": "Sicherheitsmodus", "colorDepth": "Farbtiefe", "width": "Breite", "height": "Höhe", "dpi": "DPI", - "resizeMethod": "Methode zum Ändern der Größe", - "clientName": "Clientname", + "resizeMethod": "Skalierungsmethode", + "clientName": "Client-Name", "initialProgram": "Startprogramm", - "serverLayout": "Server-Layout", + "serverLayout": "Tastaturlayout", "timezone": "Zeitzone", - "loadBalanceInfo": "Load-Balancing-Informationen / Cookie", - "loadBalanceInfoDesc": "RD Connection Broker-Cookie für den Lastausgleich in der RDS-Farm (z. B. tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "Lastverteilungsinfo / Cookie", + "loadBalanceInfoDesc": "RD-Verbindungsbroker-Cookie für den Lastausgleich einer RDS-Farm (z.B. tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacd-Proxy", "guacdHostname": "guacd-Host", - "guacdHostnamePlaceholder": "Globaler Standardwert", + "guacdHostnamePlaceholder": "Globaler Standard", "guacdPort": "guacd-Port", - "guacdProxyDesc": "Überschreiben Sie die globale guacd-Instanz für diese Verbindung. Lassen Sie das Feld leer, um die global konfigurierte guacd-Instanz zu verwenden.", + "guacdProxyDesc": "Überschreibt die globale guacd-Instanz für diese Verbindung. Leer lassen, um die global konfigurierte guacd zu verwenden.", "gatewayHostname": "Gateway-Hostname", "gatewayPort": "Gateway-Port", "gatewayUsername": "Gateway-Benutzername", @@ -1285,9 +1355,9 @@ "remoteAppProgram": "RemoteApp-Programm", "workingDirectory": "Arbeitsverzeichnis", "arguments": "Argumente", - "normalizeLineEndings": "Zeilenumbrüche normalisieren", + "normalizeLineEndings": "Zeilenenden normalisieren", "recordingPath": "Aufzeichnungspfad", - "recordingName": "Aufnahmename", + "recordingName": "Aufzeichnungsname", "macAddress": "MAC-Adresse", "broadcastAddress": "Broadcast-Adresse", "udpPort": "UDP-Port", @@ -1295,78 +1365,78 @@ "driveName": "Laufwerksname", "drivePath": "Laufwerkspfad", "ignoreCertificate": "Zertifikat ignorieren", - "ignoreCertificateDesc": "Verbindungen zu Hosts mit selbstsignierten Zertifikaten zulassen", - "forceLossless": "Verlustfreie Kodierung erzwingen", - "forceLosslessDesc": "Verlustfreie Bildkodierung erzwingen (höhere Qualität, mehr Bandbreite)", + "ignoreCertificateDesc": "Verbindungen zu Hosts mit selbstsignierten Zertifikaten erlauben", + "forceLossless": "Verlustfreie Übertragung erzwingen", + "forceLosslessDesc": "Verlustfreie Bildcodierung erzwingen (höhere Qualität, mehr Bandbreite)", "disableAudio": "Audio deaktivieren", - "disableAudioDesc": "Schalten Sie alle Audiosignale der Remote-Sitzung stumm.", - "enableAudioInput": "Audioeingang aktivieren (Mikrofon)", + "disableAudioDesc": "Gesamten Ton der Remote-Sitzung stummschalten", + "enableAudioInput": "Audioeingabe aktivieren (Mikrofon)", "enableAudioInputDesc": "Lokales Mikrofon an die Remote-Sitzung weiterleiten", "wallpaper": "Hintergrundbild", "wallpaperDesc": "Desktop-Hintergrundbild anzeigen (Deaktivierung verbessert die Leistung)", - "theming": "Design", + "theming": "Designs", "themingDesc": "Visuelle Designs und Stile aktivieren", "fontSmoothing": "Schriftglättung", - "fontSmoothingDesc": "ClearType-Schriftwiedergabe aktivieren", - "fullWindowDrag": "Fensterinhalt beim Ziehen", + "fontSmoothingDesc": "ClearType-Schriftdarstellung aktivieren", + "fullWindowDrag": "Vollständiges Fensterziehen", "fullWindowDragDesc": "Fensterinhalt beim Ziehen anzeigen", - "desktopComposition": "Desktop-Komposition", + "desktopComposition": "Desktopkomposition", "desktopCompositionDesc": "Aero-Glaseffekte aktivieren", "menuAnimations": "Menüanimationen", - "menuAnimationsDesc": "Einblend- und Schiebeanimationen für Menüs aktivieren", + "menuAnimationsDesc": "Ein- und Ausblendeanimationen für Menüs aktivieren", "disableBitmapCaching": "Bitmap-Caching deaktivieren", - "disableBitmapCachingDesc": "Bitmap-Cache deaktivieren (kann bei Darstellungsfehlern helfen).", + "disableBitmapCachingDesc": "Bitmap-Cache deaktivieren (kann bei Grafikfehlern helfen)", "disableOffscreenCaching": "Offscreen-Caching deaktivieren", "disableOffscreenCachingDesc": "Offscreen-Cache deaktivieren", "disableGlyphCaching": "Glyphen-Caching deaktivieren", - "disableGlyphCachingDesc": "Glyphencache deaktivieren", + "disableGlyphCachingDesc": "Glyphen-Cache deaktivieren", "enableGfx": "GFX aktivieren", "enableGfxDesc": "RemoteFX-Grafikpipeline verwenden", "enablePrinting": "Drucken aktivieren", - "enablePrintingDesc": "Lokale Drucker an die Remotesitzung umleiten", + "enablePrintingDesc": "Lokale Drucker an die Remote-Sitzung umleiten", "enableDriveRedirection": "Laufwerksumleitung aktivieren", - "enableDriveRedirectionDesc": "Einen lokalen Ordner als Laufwerk in der Remotesitzung einbinden", + "enableDriveRedirectionDesc": "Lokalen Ordner als Laufwerk in der Remote-Sitzung einbinden", "createDrivePath": "Laufwerkspfad erstellen", - "createDrivePathDesc": "Der Ordner wird automatisch erstellt, falls er nicht existiert.", + "createDrivePathDesc": "Ordner automatisch erstellen, falls nicht vorhanden", "disableDownload": "Download deaktivieren", - "disableDownloadDesc": "Verhindern, dass Dateien aus der Remotesitzung heruntergeladen werden", + "disableDownloadDesc": "Herunterladen von Dateien aus der entfernten Sitzung verhindern", "disableUpload": "Upload deaktivieren", - "disableUploadDesc": "Das Hochladen von Dateien in die Remotesitzung verhindern", + "disableUploadDesc": "Hochladen von Dateien in die entfernte Sitzung verhindern", "enableTouch": "Touch aktivieren", "enableTouchDesc": "Touch-Eingabeweiterleitung aktivieren", "consoleSession": "Konsolensitzung", "consoleSessionDesc": "Mit der Konsole (Sitzung 0) statt mit einer neuen Sitzung verbinden", "sendWolPacket": "WOL-Paket senden", - "sendWolPacketDesc": "Senden Sie ein Magic Packet, um diesen Host aufzuwecken, bevor Sie die Verbindung herstellen.", + "sendWolPacketDesc": "Magic Packet senden, um diesen Host vor dem Verbinden aufzuwecken", "disableCopy": "Kopieren deaktivieren", - "disableCopyDesc": "Verhindern, dass Text aus der Remotesitzung kopiert wird", + "disableCopyDesc": "Kopieren von Text aus der entfernten Sitzung verhindern", "disablePaste": "Einfügen deaktivieren", - "disablePasteDesc": "Verhindern, dass Text in die Remotesitzung eingefügt wird", - "createPathIfMissing": "Pfad erstellen, falls fehlt", - "createPathIfMissingDesc": "Automatisches Erstellen des Aufnahmeverzeichnisses", + "disablePasteDesc": "Einfügen von Text in die entfernte Sitzung verhindern", + "createPathIfMissing": "Pfad erstellen, falls nicht vorhanden", + "createPathIfMissingDesc": "Aufzeichnungsverzeichnis automatisch erstellen", "excludeOutput": "Ausgabe ausschließen", - "excludeOutputDesc": "Bildschirmausgabe nicht aufzeichnen (nur Metadaten).", + "excludeOutputDesc": "Bildschirmausgabe nicht aufzeichnen (nur Metadaten)", "excludeMouse": "Maus ausschließen", "excludeMouseDesc": "Mausbewegungen nicht aufzeichnen", - "includeKeystrokes": "Tastatureingaben einbeziehen", - "includeKeystrokesDesc": "Zusätzlich zur Bildschirmausgabe auch rohe Tastatureingaben aufzeichnen", + "includeKeystrokes": "Tastatureingaben einschließen", + "includeKeystrokesDesc": "Rohe Tastatureingaben zusätzlich zur Bildschirmausgabe aufzeichnen", "vncPort": "VNC-Port", "vncPassword": "VNC-Passwort", "vncUsernameOptional": "Benutzername (optional)", - "vncLeaveBlank": "Lassen Sie dieses Feld leer, falls nicht erforderlich.", + "vncLeaveBlank": "Leer lassen, falls nicht erforderlich", "cursorMode": "Cursor-Modus", "swapRedBlue": "Rot/Blau tauschen", - "swapRedBlueDesc": "Die roten und blauen Farbkanäle vertauschen (behebt einige Farbprobleme)", - "readOnly": "Nur lesbar", - "readOnlyDesc": "Den Remote-Bildschirm anzeigen, ohne Eingaben zu senden.", + "swapRedBlueDesc": "Die roten und blauen Farbkanäle tauschen (behebt einige Farbprobleme)", + "readOnly": "Schreibgeschützt", + "readOnlyDesc": "Bildschirm anzeigen, ohne Eingaben zu senden", "telnetPort": "Telnet-Port", "terminalType": "Terminaltyp", - "fontName": "Schriftartname", + "fontName": "Schriftname", "fontSize": "Schriftgröße", "colorScheme": "Farbschema", - "backspaceKey": "Rücktaste", - "saveHostFirst": "Speichern Sie zuerst den Host.", - "sharingOptionsAfterSave": "Die Freigabeoptionen stehen zur Verfügung, nachdem der Host gespeichert wurde.", + "backspaceKey": "Backspace-Taste", + "saveHostFirst": "Host zuerst speichern.", + "sharingOptionsAfterSave": "Freigabeoptionen stehen nach dem Speichern des Hosts zur Verfügung.", "permissionLevel": "Berechtigungsstufe", "typeHeader": "Typ", "targetHeader": "Ziel", @@ -1391,24 +1461,24 @@ "addCredential": "Zugangsdaten hinzufügen", "addCredentialDesc": "SSH-Schlüssel oder Passwörter speichern", "tmuxMonitor": "Tmux-Monitor", - "tmuxMonitorDesc": "Überwachen Sie tmux-Sitzungen auf Ihren Hosts.", - "recentActivity": "Letzte Aktivitäten", - "serversAndHosts": "Server und Hosts", - "noHostsFound": "Keine Hosts gefunden, die „{{search}}“ entsprechen", + "tmuxMonitorDesc": "Tmux-Sitzungen hostübergreifend überwachen", + "recentActivity": "Letzte Aktivität", + "serversAndHosts": "Server & Hosts", + "noHostsFound": "Keine Hosts gefunden, die \"{{search}}\" entsprechen", "links": "Links", "navigate": "Navigieren", "select": "Auswählen", "toggleWith": "Umschalten mit" }, "splitScreen": { - "paneEmpty": "Bereich {{index}} – leer", + "paneEmpty": "Bereich {{index}} - leer", "noTabAssigned": "Kein Tab zugewiesen", "focusedPane": "Aktiver Bereich" }, "connections": { "noConnections": "Keine Verbindungen", - "noConnectionsDesc": "Öffnen Sie ein Terminal, einen Dateimanager oder eine Remote-Desktop-Verbindung, um die Verbindungen hier anzuzeigen.", - "connectedFor": "Seit {{duration}} verbunden", + "noConnectionsDesc": "Öffnen Sie ein Terminal, einen Dateimanager oder einen Remote-Desktop, um hier Verbindungen anzuzeigen", + "connectedFor": "Verbunden seit {{duration}}", "connected": "Verbunden", "disconnected": "Getrennt", "closeTab": "Tab schließen", @@ -1419,35 +1489,91 @@ "reopenTab": "Erneut öffnen", "sectionOpen": "Offen", "sectionBackground": "Hintergrund", - "backgroundDesc": "Die Sitzungen bleiben nach der Trennung noch 30 Minuten bestehen und können wiederhergestellt werden.", - "persisted": "Im Hintergrund fortgeführt", + "backgroundDesc": "Sitzungen bleiben nach der Trennung 30 Minuten lang bestehen und können erneut verbunden werden.", + "persisted": "Im Hintergrund beibehalten", "expiresIn": "Läuft ab in {{duration}}", - "search": "Verbindungen suchen...", + "search": "Verbindungen durchsuchen...", "noSearchResults": "Keine Verbindungen entsprechen Ihrer Suche", - "rename": "Sitzung umbenennen" + "rename": "Sitzung umbenennen", + "sectionSharedWithMe": "Mit mir geteilt", + "sharedBy": "Geteilt von {{username}}", + "join": "Verbinden", + "sharedSessionLabel": "{{hostName}} (gemeinsam genutzt)" + }, + "sessionSharing": { + "guestView": { + "loading": "Verbindung zur gemeinsamen Sitzung wird hergestellt...", + "linkInvalid": "Dieser Freigabelink ist ungültig, abgelaufen oder wurde widerrufen.", + "rateLimited": "Zu viele Versuche, bitte versuchen Sie es in Kürze erneut.", + "sessionEnded": "Diese Sitzung ist beendet.", + "readOnlyBadge": "Nur Ansicht" + }, + "modalTitle": "Sitzung teilen", + "shareButton": "Aktie", + "notReadyToShare": "Die Sitzung ist noch nicht zur Freigabe bereit.", + "modeTab": { + "link": "Link", + "user": "Benutzer" + }, + "linkModeDescription": "Jeder mit diesem Link kann beitreten, ein Konto ist nicht erforderlich.", + "userModeDescription": "Teilen Sie die Sitzung mit einem bestimmten Benutzer, der bereits Zugriff auf diesen Host hat. Falls er noch keinen Zugriff hat, teilen Sie ihm den Host zuerst mit oder verwenden Sie stattdessen einen Link. Nach der Freigabe erscheint die Sitzung im Tab „Verbindungen“ des Benutzers.", + "permissionLevel": { + "label": "Berechtigungsstufe", + "readOnly": "Nur lesbar", + "readOnlyDescription": "Ich kann die Sitzung live verfolgen, aber nicht tippen oder interagieren.", + "readWrite": "Lesen und Schreiben", + "readWriteDescription": "Kann in der Sitzung genauso tippen und mit ihr interagieren wie der Besitzer." + }, + "expiryLabel": "Linkablauf", + "createLinkButton": "Verknüpfung erstellen", + "createShareButton": "Mit dem Nutzer teilen", + "searchUsersPlaceholder": "Benutzer suchen...", + "noUsersFound": "Keine Benutzer gefunden", + "linkCreated": "Link zum Teilen erstellt", + "linkCopied": "Link in die Zwischenablage kopiert", + "copyLink": "Link kopieren", + "shareCreated": "Die Sitzung wurde freigegeben. Sie wird im Tab „Verbindungen“ des Benutzers angezeigt.", + "shareFailed": "Fehler beim Erstellen der Freigabe", + "userLacksHostAccess": "Dieser Benutzer hat noch keinen Zugriff auf diesen Host. Teilen Sie ihm den Host zuerst mit oder verwenden Sie stattdessen einen Link.", + "activeShares": "Aktive Anteile", + "noActiveShares": "Keine aktiven Anteile für diese Sitzung", + "revoke": "Widerrufen", + "revokeConfirmTitle": "Diese Aktie widerrufen?", + "revokeConfirmDescription": "Wer diese Freigabe nutzt, verliert sofort den Zugriff.", + "revoked": "Anteil widerrufen", + "revokeFailed": "Aktienwiderruf fehlgeschlagen", + "joinCount": "{{count}} beitreten", + "joinCount_other": "{{count}} verbindet sich", + "expiresAt": "Läuft ab {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "Benutzer: {{username}}", + "loadSharesFailed": "Fehler beim Laden der aktiven Anteile" }, "guacamole": { - "connecting": "Verbindung zur {{type}}-Sitzung wird hergestellt...", + "connecting": "Verbinde mit {{type}}-Sitzung...", "connectionError": "Verbindungsfehler", "connectionFailed": "Verbindung fehlgeschlagen", - "failedToConnect": "Verbindungstoken konnte nicht abgerufen werden", + "failedToConnect": "Fehler beim Abrufen des Verbindungstokens", "hostNotFound": "Host nicht gefunden", "noHostSelected": "Kein Host ausgewählt", - "reconnect": "Wiederverbinden", + "reconnect": "Erneut verbinden", "retry": "Wiederholen", - "guacdUnavailable": "Der Remotedesktopdienst (guacd) ist nicht verfügbar. Bitte stellen Sie sicher, dass guacd ausgeführt wird, erreichbar ist und in den Administratoreinstellungen korrekt konfiguriert wurde.", + "guacdUnavailable": "Der Remote-Desktop-Dienst (guacd) ist nicht verfügbar. Bitte stellen Sie sicher, dass guacd ausgeführt wird, erreichbar ist und in den Admin-Einstellungen richtig konfiguriert ist.", + "credentialPromptTitle": "Geben Sie die RDP-Anmeldeinformationen ein.", + "credentialPromptDescription": "Dieser Host ist so konfiguriert, dass er beim Verbindungsaufbau zur Eingabe von Anmeldeinformationen auffordert. Diese werden nur für diese Sitzung verwendet und nicht gespeichert.", + "connect": "Verbinden", "ctrlAltDel": "Strg+Alt+Entf", "toolbar": { "ctrlAltDel": "Strg+Alt+Entf", - "winL": "Win+L (Sperrbildschirm)", + "winL": "Win+L (Bildschirm sperren)", "winKey": "Windows-Taste", "ctrl": "Strg", "alt": "Alt", "shift": "Umschalt", "win": "Win", - "stickyActive": "{{key}} (verriegelt - zum Entriegeln klicken)", - "stickyInactive": "{{key}} (zum Verriegeln klicken)", - "esc": "Esc", + "stickyActive": "{{key}} (eingerastet – zum Lösen klicken)", + "stickyInactive": "{{key}} (zum Einrasten klicken)", + "esc": "Escape", "tab": "Tab", "home": "Pos1", "end": "Ende", @@ -1458,91 +1584,98 @@ "arrowLeft": "Pfeil nach links", "arrowRight": "Pfeil nach rechts", "fnToggle": "Funktionstasten", - "reconnect": "Sitzung wiederverbinden", - "collapse": "Symbolleiste ausblenden", - "expand": "Symbolleiste erweitern", - "dragHandle": "Zum Verschieben ziehen" + "reconnect": "Sitzung erneut verbinden", + "collapse": "Symbolleiste einklappen", + "expand": "Symbolleiste ausklappen", + "dragHandle": "Zum Neupositionieren ziehen", + "switchToTrackpad": "In den Trackpad-Modus wechseln (Zum Bewegen des Cursors ziehen, zum Klicken tippen)", + "switchToTouch": "Wechseln Sie in den Touch-Modus (tippen Sie direkt auf die Stelle, an der Sie klicken möchten)." } }, "terminal": { - "connect": "Verbindung zum Host herstellen", - "clear": "Leeren", + "connect": "Mit Host verbinden", + "clear": "Löschen", "paste": "Einfügen", "reconnect": "Erneut verbinden", - "connectionLost": "Verbindung unterbrochen", + "connectionLost": "Verbindung verloren", "connected": "Verbunden", - "clipboardWriteFailed": "Fehler beim Kopieren in die Zwischenablage. Stellen Sie sicher, dass die Seite über HTTPS oder localhost bereitgestellt wird.", - "clipboardReadFailed": "Fehler beim Lesen der Zwischenablage. Stellen Sie sicher, dass die Berechtigungen für die Zwischenablage erteilt sind.", - "clipboardHttpWarning": "Zum Einfügen ist HTTPS erforderlich. Verwenden Sie Strg+Umschalt+V oder stellen Sie Termix über HTTPS bereit.", + "clipboardWriteFailed": "Kopieren in die Zwischenablage fehlgeschlagen. Stellen Sie sicher, dass die Seite über HTTPS oder localhost bereitgestellt wird.", + "clipboardReadFailed": "Lesen aus der Zwischenablage fehlgeschlagen. Stellen Sie sicher, dass die Zwischenablageberechtigungen erteilt wurden.", + "clipboardHttpWarning": "Einfügen erfordert HTTPS. Verwenden Sie Strg+Umschalt+V oder stellen Sie Termix über HTTPS bereit.", "passwordPromptFillTitle": "Gespeichertes Passwort in diese Eingabeaufforderung einfügen?", - "unknownError": "Es ist ein unbekannter Fehler aufgetreten.", + "unknownError": "Unbekannter Fehler aufgetreten", "websocketError": "WebSocket-Verbindungsfehler", - "connecting": "Verbindung wird hergestellt...", + "connecting": "Verbinde...", "noHostSelected": "Kein Host ausgewählt", - "reconnecting": "Verbindung wird wiederhergestellt... ({{attempt}}/{{max}})", - "reconnected": "Verbindung erfolgreich wiederhergestellt", + "reconnecting": "Wiederverbinden... ({{attempt}}/{{max}})", + "reconnected": "Erfolgreich wieder verbunden", "tmuxSessionCreated": "tmux-Sitzung erstellt: {{name}}", - "tmuxSessionAttached": "tmux-Sitzung angehängt: {{name}}", - "tmuxUnavailable": "tmux ist auf dem Remote-Host nicht installiert. Stattdessen wird die Standard-Shell verwendet.", + "tmuxSessionAttached": "tmux-Sitzung verbunden: {{name}}", + "tmuxUnavailable": "tmux ist auf dem Remote-Host nicht installiert, Fallback auf die Standard-Shell", "tmuxSessionPickerTitle": "tmux-Sitzungen", - "tmuxSessionPickerDesc": "Auf diesem Host wurden vorhandene tmux-Sitzungen gefunden. Wählen Sie eine aus, um sie erneut anzuhängen, oder erstellen Sie eine neue Sitzung.", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "Bestehende tmux-Sitzungen auf diesem Host gefunden. Wählen Sie eine aus, um sie erneut zu verbinden, oder erstellen Sie eine neue.", + "tmuxWindows": "Fenster", "tmuxWindowCount": "{{count}} Fenster", "tmuxAttached": "Verbundene Clients", "tmuxAttachedCount": "{{count}} verbunden", "tmuxLastActivity": "Letzte Aktivität", - "tmuxTimeJustNow": "soeben", + "tmuxTimeJustNow": "Soeben", "tmuxTimeMinutes": "vor {{count}} Min.", "tmuxTimeHours": "vor {{count}} Std.", - "tmuxTimeDays": "vor {{count}} T.", + "tmuxTimeDays": "vor {{count}} Tagen", "tmuxCreateNew": "Neue Sitzung starten", - "tmuxCopyHint": "Auswahl anpassen und Eingabetaste drücken, um in die Zwischenablage zu kopieren", - "tmuxDetach": "tmux-Sitzung trennen", - "tmuxDetached": "Die Verbindung zur tmux-Sitzung wurde getrennt.", - "maxReconnectAttemptsReached": "Maximale Anzahl an Wiederverbindungsversuchen erreicht", + "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 Wiederverbindungsversuche erreicht", "closeTab": "Schließen", - "connectionTimeout": "Verbindungstimeout", + "connectionTimeout": "Verbindungs-Timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "{{command}} wird ausgeführt – {{host}}", + "runTitle": "Ausführung: {{command}} - {{host}}", "totpRequired": "Zwei-Faktor-Authentifizierung erforderlich", - "totpCodeLabel": "Bestätigungscode", + "totpCodeLabel": "Verifizierungscode", "totpVerify": "Verifizieren", + "mfaPromptRequired": "Authentifizierung erforderlich", + "mfaPushRequired": "Push-Authentifizierung erforderlich", + "mfaMenuPlaceholder": "Geben Sie Ihre Antwort ein", + "mfaWaitingApproval": "Warten auf die Genehmigung Ihres Geräts...", + "mfaSendRequest": "Anfrage senden", "warpgateAuthRequired": "Warpgate-Authentifizierung erforderlich", "warpgateSecurityKey": "Sicherheitsschlüssel", "warpgateAuthUrl": "Authentifizierungs-URL", "warpgateOpenBrowser": "Im Browser öffnen", "warpgateContinue": "Authentifizierung abgeschlossen", "opksshAuthRequired": "OPKSSH-Authentifizierung erforderlich", - "opksshAuthDescription": "Bitte authentifizieren Sie sich in Ihrem Browser, um fortzufahren. Diese Sitzung bleibt 24 Stunden lang gültig.", - "opksshOpenBrowser": "Browser zur Authentifizierung öffnen", + "opksshAuthDescription": "Schließen Sie die Authentifizierung in Ihrem Browser ab, um fortzufahren. Diese Sitzung bleibt 24 Stunden lang gültig.", + "opksshOpenBrowser": "Browser zum Authentifizieren öffnen", "opksshWaitingForAuth": "Warten auf Authentifizierung im Browser...", "opksshAuthenticating": "Authentifizierung wird verarbeitet...", - "opksshTimeout": "Zeitüberschreitung bei der Authentifizierung. Bitte versuchen Sie es erneut.", - "opksshAuthFailed": "Die Authentifizierung ist fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten und versuchen Sie es erneut.", - "opksshSignInWith": "Melden Sie sich mit {{provider}} an.", + "opksshTimeout": "Authentifizierung abgelaufen. Bitte versuchen Sie es erneut.", + "opksshAuthFailed": "Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihre Anmeldeinformationen und versuchen Sie es erneut.", + "opksshSignInWith": "Anmelden mit {{provider}}", "vaultAuthTitle": "Vault-Anmeldung erforderlich", - "vaultAuthDescription": "Ein Fenster zur Anmeldung bei HashiCorp Vault wurde geöffnet. Schließen Sie dort die Anmeldung ab; diese Verbindung wird anschließend automatisch fortgesetzt.", - "vaultAuthFailed": "Die Vault-Authentifizierung ist fehlgeschlagen. Bitte versuchen Sie es erneut.", + "vaultAuthDescription": "Ein Fenster zur Anmeldung bei HashiCorp Vault wurde geöffnet. Schließen Sie die Anmeldung dort ab; diese Verbindung wird automatisch fortgesetzt.", + "vaultAuthFailed": "Vault-Authentifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.", "vaultReopen": "Anmeldefenster erneut öffnen", "sudoPasswordPopupTitle": "Passwort eingeben?", "linkDialogTitle": "Link öffnen", "linkDialogOpen": "Öffnen", "linkDialogCopy": "Kopieren", - "websocketAbnormalClose": "Die Verbindung wurde unerwartet geschlossen. Dies kann an einem Problem mit der Reverse-Proxy- oder SSL-Konfiguration liegen. Bitte überprüfen Sie die Serverprotokolle.", + "websocketAbnormalClose": "Verbindung unerwartet geschlossen. Dies kann auf ein Problem mit dem Reverse-Proxy oder der SSL-Konfiguration zurückzuführen sein. Bitte überprüfen Sie die Server-Logs.", "connectionLogTitle": "Verbindungsprotokoll", "connectionLogCopy": "Protokolle in die Zwischenablage kopieren", "connectionLogEmpty": "Noch keine Verbindungsprotokolle", "connectionLogWaiting": "Warten auf Verbindungsprotokolle...", "connectionLogCopied": "Verbindungsprotokolle in die Zwischenablage kopiert", "connectionLogCopyFailed": "Fehler beim Kopieren der Protokolle in die Zwischenablage", - "connectionRejected": "Die Verbindung wurde vom Server abgelehnt. Bitte überprüfen Sie Ihre Authentifizierungs- und Netzwerkkonfiguration.", - "hostKeyRejected": "Die Überprüfung des SSH-Hostschlüssels wurde abgelehnt. Verbindung abgebrochen.", - "sessionTakenOver": "Die Sitzung wurde in einem anderen Tab geöffnet. Verbindung wird wiederhergestellt…", + "connectionRejected": "Verbindung vom Server abgelehnt. Bitte überprüfen Sie Ihre Authentifizierung und Netzwerkkonfiguration.", + "hostKeyRejected": "SSH-Hostschlüssel-Überprüfung abgelehnt. Verbindung abgebrochen.", + "sessionTakenOver": "Sitzung wurde in einem anderen Tab geöffnet. Verbindung wird wiederhergestellt...", "split": { "splitTab": "Tab teilen", - "addToSplit": "Zur geteilten Ansicht hinzufügen", - "removeFromSplit": "Aus der geteilten Ansicht entfernen" + "addToSplit": "Zur Teilung hinzufügen", + "removeFromSplit": "Aus Teilung entfernen" } }, "fileManager": { @@ -1552,22 +1685,22 @@ "folder": "Ordner", "uploadFile": "Datei hochladen", "downloadFile": "Herunterladen", - "extractArchive": "Archiv extrahieren", - "extractingArchive": "{{name}} wird extrahiert...", + "extractArchive": "Archiv entpacken", + "extractingArchive": "Extrahiere {{name}}...", "archiveExtractedSuccessfully": "{{name}} erfolgreich extrahiert", - "extractFailed": "Extraktion fehlgeschlagen", + "extractFailed": "Extrahieren fehlgeschlagen", "compressFile": "Datei komprimieren", "compressFiles": "Dateien komprimieren", "compressFilesDesc": "{{count}} Elemente in ein Archiv komprimieren", "archiveName": "Archivname", - "enterArchiveName": "Archivnamen eingeben...", + "enterArchiveName": "Archivname eingeben...", "compressionFormat": "Komprimierungsformat", "selectedFiles": "Ausgewählte Dateien", - "andMoreFiles": "und {{count}} mehr...", + "andMoreFiles": "und {{count}} weitere...", "compress": "Komprimieren", - "compressingFiles": "{{count}} Elemente werden in {{name}} komprimiert...", + "compressingFiles": "Komprimiere {{count}} Elemente in {{name}}...", "filesCompressedSuccessfully": "{{name}} erfolgreich erstellt", - "compressFailed": "Kompression fehlgeschlagen", + "compressFailed": "Komprimierung fehlgeschlagen", "edit": "Bearbeiten", "preview": "Vorschau", "previous": "Zurück", @@ -1578,34 +1711,34 @@ "newFile": "Neue Datei", "newFolder": "Neuer Ordner", "rename": "Umbenennen", - "uploading": "Wird hochgeladen...", - "uploadingFile": "{{name}} wird hochgeladen...", + "uploading": "Lade hoch...", + "uploadingFile": "Lade {{name}} hoch...", "fileName": "Dateiname", "folderName": "Ordnername", "fileUploadedSuccessfully": "Datei \"{{name}}\" erfolgreich hochgeladen", - "failedToUploadFile": "Datei konnte nicht hochgeladen werden", + "failedToUploadFile": "Hochladen der Datei fehlgeschlagen", "fileDownloadedSuccessfully": "Datei \"{{name}}\" erfolgreich heruntergeladen", - "failedToDownloadFile": "Datei konnte nicht heruntergeladen werden", + "failedToDownloadFile": "Herunterladen der Datei fehlgeschlagen", "fileCreatedSuccessfully": "Datei \"{{name}}\" erfolgreich erstellt", "folderCreatedSuccessfully": "Ordner \"{{name}}\" erfolgreich erstellt", - "failedToCreateItem": "Element konnte nicht erstellt werden", - "operationFailed": "Vorgang „{{operation}}“ für {{name}} fehlgeschlagen: {{error}}", - "failedToResolveSymlink": "Symlink konnte nicht aufgelöst werden", + "failedToCreateItem": "Fehler beim Erstellen des Elements", + "operationFailed": "{{operation}}-Operation für {{name}} fehlgeschlagen: {{error}}", + "failedToResolveSymlink": "Fehler beim Auflösen des Symlinks", "itemsDeletedSuccessfully": "{{count}} Elemente erfolgreich gelöscht", "failedToDeleteItems": "Fehler beim Löschen der Elemente", - "sudoPasswordRequired": "Administratorpasswort erforderlich", - "enterSudoPassword": "Geben Sie das sudo-Passwort ein, um diesen Vorgang fortzusetzen.", + "sudoPasswordRequired": "Sudo-Passwort erforderlich", + "enterSudoPassword": "Sudo-Passwort eingeben, um diesen Vorgang fortzusetzen", "sudoPassword": "Sudo-Passwort", - "sudoOperationFailed": "Sudo-Operation fehlgeschlagen", + "sudoOperationFailed": "Sudo-Vorgang fehlgeschlagen", "sudoAuthFailed": "Sudo-Authentifizierung fehlgeschlagen", - "dragFilesToUpload": "Dateien zum Hochladen hier ablegen", + "dragFilesToUpload": "Dateien hier ablegen, um sie hochzuladen", "emptyFolder": "Dieser Ordner ist leer", "searchFiles": "Dateien durchsuchen...", "upload": "Hochladen", - "selectHostToStart": "Wählen Sie einen Host aus, um die Dateiverwaltung zu starten.", - "sshRequiredForFileManager": "Der Dateimanager benötigt SSH. Auf diesem Host ist SSH nicht aktiviert.", - "failedToConnect": "SSH-Verbindung fehlgeschlagen", - "failedToLoadDirectory": "Verzeichnis konnte nicht geladen werden", + "selectHostToStart": "Wählen Sie einen Host aus, um die Dateiverwaltung zu starten", + "sshRequiredForFileManager": "Dateimanager erfordert SSH. SSH ist auf diesem Host nicht aktiviert.", + "failedToConnect": "Verbindung zu SSH fehlgeschlagen", + "failedToLoadDirectory": "Fehler beim Laden des Verzeichnisses", "noSSHConnection": "Keine SSH-Verbindung verfügbar", "copy": "Kopieren", "cut": "Ausschneiden", @@ -1615,7 +1748,7 @@ "delete": "Löschen", "properties": "Eigenschaften", "refresh": "Aktualisieren", - "downloadFiles": "{{count}} Dateien in den Browser herunterladen", + "downloadFiles": "{{count}} Dateien über den Browser herunterladen", "copyFiles": "{{count}} Elemente kopieren", "cutFiles": "{{count}} Elemente ausschneiden", "deleteFiles": "{{count}} Elemente löschen", @@ -1623,11 +1756,11 @@ "filesCutToClipboard": "{{count}} Elemente in die Zwischenablage ausgeschnitten", "pathCopiedToClipboard": "Pfad in die Zwischenablage kopiert", "pathsCopiedToClipboard": "{{count}} Pfade in die Zwischenablage kopiert", - "failedToCopyPath": "Pfad konnte nicht in die Zwischenablage kopiert werden", + "failedToCopyPath": "Fehler beim Kopieren des Pfads in die Zwischenablage", "copyFolderLink": "Link zum Ordner kopieren", "copyCurrentFolderLink": "Link zum aktuellen Ordner kopieren", - "folderLinkCopied": "Ordnerlink in die Zwischenablage kopiert", - "failedToCopyFolderLink": "Fehler beim Kopieren des Ordnerlinks", + "folderLinkCopied": "Ordner-Link in die Zwischenablage kopiert", + "failedToCopyFolderLink": "Fehler beim Kopieren des Ordner-Links", "movedItems": "{{count}} Elemente verschoben", "failedToDeleteItem": "Fehler beim Löschen des Elements", "itemRenamedSuccessfully": "{{type}} erfolgreich umbenannt", @@ -1636,74 +1769,74 @@ "openExternalEditor": "Extern öffnen", "chooseExternalEditor": "Externen Editor auswählen", "externalEditorSelected": "Externer Editor ausgewählt", - "externalEditorDesktopOnly": "Der externe Editor ist nur in der Desktop-App verfügbar.", - "externalEditorOpened": "In einem externen Editor geöffnet. Wenn Sie dort speichern, werden die Änderungen zurück auf den Server hochgeladen.", - "failedToOpenExternalEditor": "Externer Editor konnte nicht geöffnet werden", - "failedToSelectExternalEditor": "Externer Editor konnte nicht ausgewählt werden", + "externalEditorDesktopOnly": "Externer Editor ist nur in der Desktop-App verfügbar", + "externalEditorOpened": "Im externen Editor geöffnet. Beim Speichern werden die Änderungen auf den Server hochgeladen.", + "failedToOpenExternalEditor": "Fehler beim Öffnen des externen Editors", + "failedToSelectExternalEditor": "Fehler bei der Auswahl des externen Editors", "permissions": "Berechtigungen", "size": "Größe", "modified": "Geändert", "path": "Pfad", - "confirmDelete": "Möchten Sie {{name}} wirklich löschen?", + "confirmDelete": "Sind Sie sicher, dass Sie {{name}} löschen möchten?", "permissionDenied": "Zugriff verweigert", "serverError": "Serverfehler", "fileSavedSuccessfully": "Datei erfolgreich gespeichert", - "failedToSaveFile": "Datei konnte nicht gespeichert werden", - "confirmDeleteSingleItem": "Möchten Sie „{{name}}“ wirklich endgültig löschen?", - "confirmDeleteMultipleItems": "Möchten Sie {{count}} Elemente wirklich endgültig löschen?", - "confirmDeleteMultipleItemsWithFolders": "Sind Sie sicher, dass Sie {{count}} Elemente endgültig löschen möchten? Dies umfasst Ordner und deren Inhalt.", + "failedToSaveFile": "Fehler beim Speichern der Datei", + "confirmDeleteSingleItem": "Sind Sie sicher, dass Sie \"{{name}}\" endgültig löschen möchten?", + "confirmDeleteMultipleItems": "Sind Sie sicher, dass Sie {{count}} Elemente endgültig löschen möchten?", + "confirmDeleteMultipleItemsWithFolders": "Sind Sie sicher, dass Sie {{count}} Elemente endgültig löschen möchten? Dies beinhaltet auch Ordner und deren Inhalte.", "confirmDeleteFolder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt endgültig löschen möchten?", - "permanentDeleteWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Die betreffenden Elemente werden endgültig vom Server gelöscht.", + "permanentDeleteWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Die Elemente werden endgültig vom Server gelöscht.", "recent": "Zuletzt verwendet", "pinned": "Angepinnt", - "folderShortcuts": "Ordnerverknüpfungen", - "failedToReconnectSSH": "Verbindung zur SSH-Sitzung konnte nicht wiederhergestellt werden", + "folderShortcuts": "Ordner-Verknüpfungen", + "failedToReconnectSSH": "Fehler beim Wiederherstellen der SSH-Sitzung", "openTerminalHere": "Terminal hier öffnen", "run": "Ausführen", "openTerminalInFolder": "Terminal in diesem Ordner öffnen", "openTerminalInFileLocation": "Terminal am Dateispeicherort öffnen", - "runningFile": "Wird ausgeführt – {{file}}", - "onlyRunExecutableFiles": "Es können nur ausführbare Dateien ausgeführt werden.", + "runningFile": "Läuft - {{file}}", + "onlyRunExecutableFiles": "Nur ausführbare Dateien können ausgeführt werden", "directories": "Verzeichnisse", - "removedFromRecentFiles": "„{{name}}“ aus den zuletzt verwendeten Dateien entfernt", + "removedFromRecentFiles": "\"{{name}}\" aus den zuletzt verwendeten Dateien entfernt", "removeFailed": "Entfernen fehlgeschlagen", - "unpinnedSuccessfully": "Anheftung von „{{name}}“ erfolgreich aufgehoben", - "unpinFailed": "Entpinnung fehlgeschlagen", - "removedShortcut": "Verknüpfung „{{name}}“ entfernt", - "removeShortcutFailed": "Verknüpfung konnte nicht entfernt werden", - "clearedAllRecentFiles": "Alle zuletzt verwendeten Dateien wurden gelöscht", + "unpinnedSuccessfully": "\"{{name}}\" erfolgreich gelöst", + "unpinFailed": "Lösen fehlgeschlagen", + "removedShortcut": "Verknüpfung \"{{name}}\" entfernt", + "removeShortcutFailed": "Entfernen der Verknüpfung fehlgeschlagen", + "clearedAllRecentFiles": "Alle zuletzt verwendeten Dateien gelöscht", "clearFailed": "Löschen fehlgeschlagen", - "removeFromRecentFiles": "Aus den zuletzt verwendeten Dateien entfernen", + "removeFromRecentFiles": "Aus zuletzt verwendeten Dateien entfernen", "clearAllRecentFiles": "Alle zuletzt verwendeten Dateien löschen", - "unpinFile": "Anheftung der Datei aufheben", + "unpinFile": "Datei lösen", "removeShortcut": "Verknüpfung entfernen", "pinFile": "Datei anheften", "addToShortcuts": "Zu Verknüpfungen hinzufügen", "pasteFailed": "Einfügen fehlgeschlagen", "noUndoableActions": "Keine rückgängig zu machenden Aktionen", "undoCopySuccess": "Kopiervorgang rückgängig gemacht: {{count}} kopierte Dateien gelöscht", - "undoCopyFailedDelete": "Rückgängigmachen fehlgeschlagen: Es konnten keine kopierten Dateien gelöscht werden.", - "undoCopyFailedNoInfo": "Rückgängigmachen fehlgeschlagen: Kopierte Dateiinformationen konnten nicht gefunden werden.", - "undoMoveSuccess": "Verschiebevorgang rückgängig gemacht: {{count}} Dateien zurück an ihren ursprünglichen Speicherort verschoben", - "undoMoveFailedMove": "Rückgängigmachen fehlgeschlagen: Es konnten keine Dateien zurückverschoben werden.", - "undoMoveFailedNoInfo": "Rückgängigmachen fehlgeschlagen: Verschobene Dateiinformationen konnten nicht gefunden werden.", - "undoDeleteNotSupported": "Der Löschvorgang kann nicht rückgängig gemacht werden: Die Dateien wurden endgültig vom Server gelöscht.", - "undoTypeNotSupported": "Nicht unterstützter Vorgang zum Rückgängigmachen", - "undoOperationFailed": "Rückgängigmachen fehlgeschlagen", + "undoCopyFailedDelete": "Rückgängig fehlgeschlagen: Konnte keine kopierten Dateien löschen", + "undoCopyFailedNoInfo": "Rückgängig fehlgeschlagen: Informationen zu kopierten Dateien nicht gefunden", + "undoMoveSuccess": "Verschiebevorgang rückgängig gemacht: {{count}} Dateien an den ursprünglichen Ort zurückverschoben", + "undoMoveFailedMove": "Rückgängig fehlgeschlagen: Konnte keine Dateien zurückverschieben", + "undoMoveFailedNoInfo": "Rückgängig fehlgeschlagen: Informationen zu verschobenen Dateien nicht gefunden", + "undoDeleteNotSupported": "Löschvorgang kann nicht rückgängig gemacht werden: Dateien wurden dauerhaft vom Server gelöscht", + "undoTypeNotSupported": "Nicht unterstützter Typ für Rückgängig-Vorgang", + "undoOperationFailed": "Rückgängig-Vorgang fehlgeschlagen", "unknownError": "Unbekannter Fehler", "confirm": "Bestätigen", "find": "Suchen...", "replace": "Ersetzen", "downloadInstead": "Stattdessen herunterladen", - "keyboardShortcuts": "Tastenkombinationen", - "searchAndReplace": "Suchen und Ersetzen", - "editing": "Wird bearbeitet", + "keyboardShortcuts": "Tastaturkürzel", + "searchAndReplace": "Suchen & Ersetzen", + "editing": "Bearbeiten", "search": "Suchen", "findNext": "Weitersuchen", - "findPrevious": "Rückwärts suchen", + "findPrevious": "Vorheriges suchen", "save": "Speichern", - "selectAll": "Alle auswählen", - "undo": "Rückgängig machen", + "selectAll": "Alles auswählen", + "undo": "Rückgängig", "redo": "Wiederholen", "moveLineUp": "Zeile nach oben verschieben", "moveLineDown": "Zeile nach unten verschieben", @@ -1712,39 +1845,39 @@ "imageLoadError": "Bild konnte nicht geladen werden", "startTyping": "Mit der Eingabe beginnen...", "unknownSize": "Unbekannte Größe", - "fileIsEmpty": "Die Datei ist leer.", - "largeFileWarning": "Warnung vor großer Datei", - "largeFileWarningDesc": "Diese Datei ist {{size}} groß, was beim Öffnen als Textdatei zu Leistungsproblemen führen kann.", - "fileNotFoundAndRemoved": "Die Datei \"{{name}}\" wurde nicht gefunden und aus den zuletzt verwendeten/angehefteten Dateien entfernt.", + "fileIsEmpty": "Datei ist leer", + "largeFileWarning": "Warnung bei großen Dateien", + "largeFileWarningDesc": "Diese Datei ist {{size}} groß, was beim Öffnen als Text zu Leistungsproblemen führen kann.", + "fileNotFoundAndRemoved": "Datei \"{{name}}\" nicht gefunden und wurde aus den zuletzt verwendeten/angehefteten Dateien entfernt.", "failedToLoadFile": "Datei konnte nicht geladen werden: {{error}}", - "serverErrorOccurred": "Es ist ein Serverfehler aufgetreten. Bitte versuchen Sie es später erneut.", + "serverErrorOccurred": "Ein Serverfehler ist aufgetreten. Bitte versuchen Sie es später erneut.", "autoSaveFailed": "Automatisches Speichern fehlgeschlagen", "fileAutoSaved": "Datei automatisch gespeichert", - "moveFileFailed": "Fehler beim Verschieben von {{name}}", - "moveOperationFailed": "Verschiebungsvorgang fehlgeschlagen", - "canOnlyCompareFiles": "Es können nur zwei Dateien verglichen werden.", - "comparingFiles": "Dateien werden verglichen: {{file1}} und {{file2}}", + "moveFileFailed": "Verschieben von {{name}} fehlgeschlagen", + "moveOperationFailed": "Verschiebevorgang fehlgeschlagen", + "canOnlyCompareFiles": "Es können nur zwei Dateien verglichen werden", + "comparingFiles": "Vergleiche Dateien: {{file1}} und {{file2}}", "dragFailed": "Ziehvorgang fehlgeschlagen", - "filePinnedSuccessfully": "Datei \"{{name}}\" erfolgreich angeheftet", - "pinFileFailed": "Datei konnte nicht angeheftet werden", - "fileUnpinnedSuccessfully": "Anheftung der Datei „{{name}}“ erfolgreich aufgehoben", - "unpinFileFailed": "Datei konnte nicht gelöst werden", + "filePinnedSuccessfully": "Datei \"{{name}}\" wurde erfolgreich angeheftet", + "pinFileFailed": "Anheften der Datei fehlgeschlagen", + "fileUnpinnedSuccessfully": "Datei \"{{name}}\" wurde erfolgreich abgeheftet", + "unpinFileFailed": "Loslösen der Datei fehlgeschlagen", "shortcutAddedSuccessfully": "Ordnerverknüpfung \"{{name}}\" erfolgreich hinzugefügt", - "addShortcutFailed": "Verknüpfung konnte nicht hinzugefügt werden", - "operationCompletedSuccessfully": "{{operation}} für {{count}} Elemente erfolgreich abgeschlossen", - "operationCompleted": "{{operation}}: {{count}} Elemente", + "addShortcutFailed": "Hinzufügen der Verknüpfung fehlgeschlagen", + "operationCompletedSuccessfully": "{{operation}} {{count}} Elemente erfolgreich abgeschlossen", + "operationCompleted": "{{operation}} {{count}} Elemente", "downloadFileSuccess": "Datei {{name}} erfolgreich heruntergeladen", "downloadFileFailed": "Download fehlgeschlagen", - "moveTo": "Nach {{name}} verschieben", - "diffCompareWith": "Diff mit {{name}} vergleichen", + "moveTo": "Verschieben nach {{name}}", + "diffCompareWith": "Diff-Vergleich mit {{name}}", "dragOutsideToDownload": "Zum Herunterladen aus dem Fenster ziehen ({{count}} Dateien)", - "newFolderDefault": "NeuerOrdner", - "newFileDefault": "NeueDatei.txt", - "successfullyMovedItems": "{{count}} Elemente erfolgreich nach {{target}} verschoben.", + "newFolderDefault": "Neuer Ordner", + "newFileDefault": "Neue Datei.txt", + "successfullyMovedItems": "{{count}} Elemente erfolgreich nach {{target}} verschoben", "move": "Verschieben", "searchInFile": "In Datei suchen (Strg+F)", - "showKeyboardShortcuts": "Tastenkombinationen anzeigen", - "decreaseFontSize": "Schriftgröße verringern", + "showKeyboardShortcuts": "Tastaturkürzel anzeigen", + "decreaseFontSize": "Schriftgröße verkleinern", "increaseFontSize": "Schriftgröße vergrößern", "startWritingMarkdown": "Beginnen Sie mit der Eingabe Ihres Markdown-Inhalts...", "loadingFileComparison": "Dateivergleich wird geladen...", @@ -1752,20 +1885,20 @@ "compare": "Vergleichen", "sideBySide": "Nebeneinander", "inline": "Inline", - "fileComparison": "Dateivergleich: {{file1}} mit {{file2}}", + "fileComparison": "Dateivergleich: {{file1}} vs. {{file2}}", "fileTooLarge": "Datei zu groß: {{error}}", "sshConnectionFailed": "SSH-Verbindung fehlgeschlagen. Bitte überprüfen Sie Ihre Verbindung zu {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Datei konnte nicht geladen werden: {{error}}", + "loadFileFailed": "Fehler beim Laden der Datei: {{error}}", "connecting": "Verbindung wird hergestellt...", - "connectedSuccessfully": "Verbindung erfolgreich hergestellt", - "totpVerificationFailed": "TOTP-Verifizierung fehlgeschlagen", + "connectedSuccessfully": "Erfolgreich verbunden", + "totpVerificationFailed": "TOTP-Überprüfung fehlgeschlagen", "warpgateVerificationFailed": "Warpgate-Authentifizierung fehlgeschlagen", "authenticationFailed": "Authentifizierung fehlgeschlagen", "incorrectPassphrase": "Falsche Passphrase. Bitte versuchen Sie es erneut.", - "verificationCodePrompt": "Bestätigungscode:", + "verificationCodePrompt": "Verifizierungscode:", "changePermissions": "Berechtigungen ändern", "currentPermissions": "Aktuelle Berechtigungen", - "owner": "Eigentümer", + "owner": "Besitzer", "group": "Gruppe", "others": "Andere", "read": "Lesen", @@ -1779,7 +1912,7 @@ "sortBySize": "Größe", "ascending": "Aufsteigend", "descending": "Absteigend", - "root": "Root", + "root": "Stammverzeichnis", "new": "Neu", "sortBy": "Sortieren nach", "items": "Elemente", @@ -1790,119 +1923,119 @@ "disk": "Datenträger", "used": "Belegt", "of": "von", - "toggleSidebar": "Seitenleiste ein-/ausblenden", + "toggleSidebar": "Seitenleiste umschalten", "cannotLoadPdf": "PDF kann nicht geladen werden", "pdfLoadError": "Beim Laden dieser PDF-Datei ist ein Fehler aufgetreten.", "loadingPdf": "PDF wird geladen...", "loadingPage": "Seite wird geladen..." }, "transfer": { - "copyToHost": "Zum Host kopieren…", - "moveToHost": "Auf einen Host verschieben…", - "copyItemsToHost": "{{count}} Elemente auf einen Host kopieren…", - "moveItemsToHost": "{{count}} Elemente auf Host verschieben…", - "noHostsConnected": "Es sind keine anderen Dateimanager-Hosts verfügbar.", - "noHostsConnectedHint": "Fügen Sie im Host-Manager einen weiteren SSH-Host mit aktiviertem Dateimanager hinzu.", + "copyToHost": "Auf Host kopieren…", + "moveToHost": "Auf Host verschieben…", + "copyItemsToHost": "{{count}} Elemente auf Host kopieren…", + "moveItemsToHost": "{{count}} Elemente zum Host verschieben…", + "noHostsConnected": "Keine weiteren Dateimanager-Hosts verfügbar.", + "noHostsConnectedHint": "Fügen Sie einen weiteren SSH-Host mit aktiviertem Dateimanager im Host-Manager hinzu.", "selectDestinationHost": "Zielhost auswählen", "destinationPath": "Zielpfad", - "recentDestinations": "Zuletzt verwendete Ziele", - "collapseRecentDestinations": "Zuletzt verwendete Ziele einklappen", - "expandRecentDestinations": "Zuletzt verwendete Ziele ausklappen", + "recentDestinations": "Letzte Ziele", + "collapseRecentDestinations": "Letzte Ziele einklappen", + "expandRecentDestinations": "Letzte Ziele ausklappen", "browseFolders": "Zielordner durchsuchen", "browseDestination": "Pfad durchsuchen oder eingeben", "confirmCopy": "Kopieren", "confirmMove": "Verschieben", - "transferring": "Wird übertragen…", - "compressing": "Wird komprimiert…", - "extracting": "Wird extrahiert…", - "transferringItems": "Übertrage {{current}} von {{total}} Elementen…", + "transferring": "Übertragen …", + "compressing": "Komprimieren …", + "extracting": "Entpacken …", + "transferringItems": "{{current}} von {{total}} Elementen übertragen …", "transferSuccess": "Übertragung abgeschlossen", "transferError": "Übertragung fehlgeschlagen", "transferPartial": "Übertragung mit {{count}} Fehlern abgeschlossen", - "transferPartialHint": "Übertragung fehlgeschlagen: {{paths}}", + "transferPartialHint": "Konnte nicht übertragen werden: {{paths}}", "itemsSummary": "{{count}} Elemente", - "destMustBeDirectory": "Das Ziel muss bei der Übertragung mehrerer Elemente ein Verzeichnis sein", - "selectThisFolder": "Wählen Sie diesen Ordner aus", - "browsePathWillBeCreated": "Dieser Ordner existiert noch nicht. Er wird erstellt, sobald die Übertragung beginnt.", + "destMustBeDirectory": "Bei Übertragung mehrerer Elemente muss das Ziel ein Verzeichnis sein.", + "selectThisFolder": "Diesen Ordner auswählen", + "browsePathWillBeCreated": "Dieser Ordner existiert noch nicht. Er wird beim Start der Übertragung erstellt.", "browsePathError": "Dieser Pfad konnte auf dem Zielhost nicht geöffnet werden.", "goUp": "Nach oben", - "copyFolderToHost": "Ordner auf Host kopieren…", - "moveFolderToHost": "Ordner auf Host verschieben…", + "copyFolderToHost": "Ordner zum Host kopieren …", + "moveFolderToHost": "Ordner zum Host verschieben …", "hostReady": "Bereit", - "hostConnecting": "Verbindung wird hergestellt…", + "hostConnecting": "Verbinden …", "hostDisconnected": "Nicht verbunden", - "hostAuthRequired": "Authentifizierung erforderlich – öffnen Sie zuerst den Dateimanager auf diesem Host.", + "hostAuthRequired": "Authentifizierung erforderlich — öffnen Sie zuerst den Dateimanager auf diesem Host", "hostConnectionFailed": "Verbindung fehlgeschlagen", "metricsTitle": "Übertragungszeiten", "metricsPrepare": "Ziel vorbereiten: {{duration}}", - "metricsCompress": "Komprimierung auf der Quellseite: {{duration}}", + "metricsCompress": "Komprimieren 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": "Am Ziel extrahieren: {{duration}}", - "metricsSourceDelete": "Aus der Quelle entfernen: {{duration}}", + "metricsTransfer": "Ende-zu-Ende: {{throughput}} ({{duration}})", + "metricsExtract": "Entpacken auf dem Ziel: {{duration}}", + "metricsSourceDelete": "Löschen von der Quelle: {{duration}}", "metricsTotal": "Gesamt: {{duration}}", - "progressCompressing": "Komprimierung auf dem Quellhost…", - "progressExtracting": "Extraktion am Ziel…", - "progressTransferring": "Datenübertragung…", - "progressReconnecting": "Verbindung wird wiederhergestellt…", + "progressCompressing": "Komprimieren auf dem Quellhost…", + "progressExtracting": "Entpacken auf dem Zielhost…", + "progressTransferring": "Daten übertragen…", + "progressReconnecting": "Neu verbinden…", "parallelSegmentsLabel": "Parallele Übertragungskanäle", "parallelSegmentsOption": "{{count}} Kanäle", - "parallelSegmentsHint": "Große Dateien werden in 256-MB-Blöcke aufgeteilt. Mehrere Kanäle verwenden separate Verbindungen (wie mehrere gleichzeitig gestartete Übertragungen), um einen höheren Gesamtdurchsatz zu erzielen.", - "progressTotalSpeed": "{{speed}} insgesamt ({{lanes}} Kanäle)", - "progressTransferringItems": "Dateien werden übertragen ({{current}} von {{total}})…", + "parallelSegmentsHint": "Große Dateien werden in 256-MB-Blöcke aufgeteilt. Mehrere Kanäle verwenden eigene Verbindungen (ähnlich wie mehrere gleichzeitige Übertragungen) für einen höheren Gesamtdurchsatz.", + "progressTotalSpeed": "{{speed}} gesamt ({{lanes}} Kanäle)", + "progressTransferringItems": "Übertragen von Dateien ({{current}} von {{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": "Abbrechen", "methodLabel": "Übertragungsmethode", - "methodAuto": "Auto", + "methodAuto": "Automatisch", "methodTar": "Tar-Archiv", - "methodItemSftp": "SFTP pro Datei", - "methodAutoHint": "Wählt anhand von Dateianzahl, Größe und Komprimierbarkeit zwischen einem tar-Archiv und SFTP pro Datei. Einzelne Dateien werden immer per SFTP-Streaming ü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.", + "methodItemSftp": "Dateiweise SFTP", + "methodAutoHint": "Wählt Tar-Archiv oder dateiweise SFTP basierend auf Dateianzahl, Größe und Komprimierbarkeit. Einzelne Dateien verwenden immer Streaming-SFTP.", + "methodTarHint": "Komprimieren auf der Quelle, ein Archiv übertragen, auf dem Ziel entpacken. Erfordert tar auf beiden Unix-Hosts.", + "methodItemSftpHint": "Jede Datei einzeln per SFTP übertragen. Funktioniert auf allen Hosts einschließlich Windows.", "methodPreviewLoading": "Übertragungsmethode wird berechnet…", - "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 wird nacheinander über denselben SFTP-Stream wie bei einer Einzeldateikopie übertragen. Der Fortschritt wird über alle Dateien zusammengefasst, sodass sich der Balken bei großen Dateien nur langsam bewegt.", + "methodPreviewError": "Vorschau der Übertragungsmethode nicht möglich. Der Server wählt beim Start trotzdem eine Methode.", + "methodPreviewWillUseTar": "Verwendet: Tar-Archiv", + "methodPreviewWillUseItemSftp": "Verwendet: Dateiweise SFTP", + "methodPreviewScanSummary": "{{fileCount}} Dateien, {{totalSize}} gesamt (auf Quellhost gescannt).", + "methodItemSftpLimitation": "Jede Datei verwendet denselben SFTP-Stream wie bei einer Einzeldatei-Kopie, eine nach der anderen. Der Fortschritt wird über alle Dateien kombiniert, sodass der Balken bei großen Dateien nur langsam voranschreitet.", "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, darunter eine große Datei ({{largestSize}}), mit komprimierbaren Daten – tar fasst sie zu einer Übertragung zusammen.", - "auto_single_large_in_archive": "Auto: eine große Datei ({{largestSize}}) in dieser Auswahl – SFTP pro Datei.", - "auto_many_incompressible": "Auto: größtenteils nicht komprimierbare Daten – SFTP pro Datei.", - "auto_many_files": "Auto: viele Dateien ({{fileCount}}) – tar reduziert den Aufwand pro Datei.", - "auto_default": "Auto: SFTP pro Datei für diese Auswahl." + "user_item_sftp": "Sie haben dateiweises SFTP ausgewählt.", + "user_tar": "Sie haben tar-Archiv ausgewählt.", + "tar_unavailable": "Tar ist auf einem oder beiden Hosts nicht verfügbar – stattdessen wird dateiweises SFTP verwendet.", + "windows_host": "Ein Windows-Host ist beteiligt – tar wird nicht verwendet.", + "auto_multi_large": "Automatisch: mehrere Dateien, darunter eine große Datei ({{largestSize}}) mit komprimierbaren Daten – tar bündelt sie in eine Übertragung.", + "auto_single_large_in_archive": "Automatisch: eine große Datei ({{largestSize}}) in diesem Satz – dateiweises SFTP.", + "auto_many_incompressible": "Automatisch: überwiegend nicht komprimierbare Daten – dateiweises SFTP.", + "auto_many_files": "Automatisch: viele Dateien ({{fileCount}}) – tar reduziert den Overhead pro Datei.", + "auto_default": "Automatisch: dateiweises SFTP für diesen Satz." }, "progressCancel": "Abbrechen", "progressCancelling": "Wird abgebrochen…", - "progressStalled": "Ins Stocken geraten", - "resumedHint": "Die Verbindung zu einer in einem anderen Fenster gestarteten aktiven Übertragung wurde wiederhergestellt.", + "progressStalled": "Stockt", + "resumedHint": "Erneut mit einer aktiven Übertragung verbunden, die in einem anderen Fenster gestartet wurde.", "transferCancelled": "Übertragung abgebrochen", - "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": "Zieldateien bereinigen", - "cleanupDestFilesSuccess": "Unvollständige Dateien vom Ziel entfernt", + "transferCancelledCopyHint": "Unvollständige Dateien können am Zielort verbleiben.", + "transferCancelledMoveHint": "Unvollständige Dateien können am Zielort verbleiben. Quelldateien wurden nicht entfernt.", + "cleanupDestFiles": "Ziel bereinigen", + "cleanupDestFilesSuccess": "Unvollständige Dateien vom Ziel entfernt.", "cleanupDestFilesPartial": "Einige unvollständige Dateien konnten nicht entfernt werden.", - "cleanupDestFilesNothing": "Am Zielort gibt es nichts aufzuräumen.", - "cleanupDestFilesError": "Bereinigung fehlgeschlagen", + "cleanupDestFilesNothing": "Nichts zum Bereinigen am Ziel.", + "cleanupDestFilesError": "Bereinigung fehlgeschlagen.", "retryTransfer": "Wiederholen", - "retryTransferError": "Wiederholungsversuch fehlgeschlagen", - "transferFailedRetryHint": "Unvollständige Daten wurden am Ziel beibehalten. Nach Wiederherstellung der Verbindung wird die Übertragung beim erneuten Versuch fortgesetzt." + "retryTransferError": "Wiederholung fehlgeschlagen.", + "transferFailedRetryHint": "Unvollständige Daten wurden am Ziel beibehalten. Die Wiederholung wird fortgesetzt, sobald die Verbindung wiederhergestellt ist." }, "tunnels": { "noSshTunnels": "Keine SSH-Tunnel", - "createFirstTunnelMessage": "Sie haben noch keine SSH-Tunnel erstellt. Konfigurieren Sie Tunnelverbindungen im Host-Manager, um loszulegen.", + "createFirstTunnelMessage": "Sie haben noch keine SSH-Tunnel erstellt. Konfigurieren Sie Tunnelverbindungen im Host-Manager, um zu beginnen.", "connected": "Verbunden", "disconnected": "Getrennt", - "connecting": "Verbindung wird hergestellt...", + "connecting": "Verbinde...", "error": "Fehler", "canceling": "Wird abgebrochen...", "connect": "Verbinden", @@ -1913,78 +2046,78 @@ "remotePort": "Remote-Port", "currentHostPort": "Aktueller Host-Port", "endpointPort": "Endpunkt-Port", - "bindIp": "Lokale IP-Adresse", - "endpointSshConfig": "SSH-Endpunktkonfiguration", - "endpointSshHost": "SSH-Host des Endpunkts", - "endpointSshHostPlaceholder": "Wählen Sie einen konfigurierten Host aus.", - "endpointSshHostRequired": "Wählen Sie für jeden Client-Tunnel einen SSH-Endpunkthost aus.", + "bindIp": "Lokale IP", + "endpointSshConfig": "Endpunkt-SSH-Konfiguration", + "endpointSshHost": "Endpunkt-SSH-Host", + "endpointSshHostPlaceholder": "Konfigurierten Host auswählen", + "endpointSshHostRequired": "Wählen Sie einen Endpunkt-SSH-Host für jeden Client-Tunnel aus.", "attempt": "Versuch {{current}} von {{max}}", - "nextRetryIn": "Nächster Versuch in {{seconds}} Sekunden", + "nextRetryIn": "Nächster Wiederholungsversuch in {{seconds}} Sekunden", "clientTunnels": "Client-Tunnel", "clientTunnel": "Client-Tunnel", "addClientTunnel": "Client-Tunnel hinzufügen", - "noClientTunnels": "Auf diesem Desktop sind keine Client-Tunnel konfiguriert.", + "noClientTunnels": "Keine Client-Tunnel auf diesem Desktop konfiguriert.", "tunnelName": "Tunnelname", "remoteHost": "Remote-Host", - "autoStart": "Automatischer Start", - "clientAutoStartDesc": "Startet, sobald dieser Desktop-Client geöffnet wird, und bleibt verbunden.", - "clientManualStartDesc": "Verwenden Sie in dieser Zeile „Start“ und „Stopp“. Termix öffnet den Tunnel nicht automatisch.", - "clientRemoteServerNote": "Remote-Weiterleitungen erfordern auf dem SSH-Server des Endpunkts möglicherweise AllowTcpForwarding und GatewayPorts. Der Remote-Port wird geschlossen, sobald die Verbindung dieses Desktops getrennt wird.", + "autoStart": "Autostart", + "clientAutoStartDesc": "Startet automatisch, wenn dieser Desktop-Client geöffnet wird und die Verbindung aufrechterhalten wird.", + "clientManualStartDesc": "Verwenden Sie Start und Stopp in dieser Zeile. Termix startet den Tunnel nicht automatisch.", + "clientRemoteServerNote": "Remote-Weiterleitung erfordert möglicherweise AllowTcpForwarding und GatewayPorts auf dem Endpunkt-SSH-Server. Der entfernte Port wird geschlossen, wenn dieser Desktop die Verbindung trennt.", "clientTunnelStarted": "Client-Tunnel gestartet", "clientTunnelStopped": "Client-Tunnel gestoppt", "tunnelTestSucceeded": "Tunneltest erfolgreich", "tunnelTestFailed": "Tunneltest fehlgeschlagen", "localSaved": "Client-Tunnel gespeichert", - "localSaveError": "Lokale Client-Tunnel konnten nicht gespeichert werden.", - "invalidBindIp": "Die lokale IP-Adresse muss eine gültige IPv4-Adresse sein.", - "invalidLocalTargetIp": "Die lokale Ziel-IP-Adresse 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": "Der lokale Zielport muss zwischen 1 und 65535 liegen.", - "invalidEndpointPort": "Der Endpunktport muss zwischen 1 und 65535 liegen.", - "duplicateAutoStartBind": "Nur ein Client-Tunnel mit automatischem Start kann {{bind}} verwenden.", - "manualControlError": "Tunnelstatus konnte nicht aktualisiert werden.", + "localSaveError": "Fehler beim Speichern der lokalen Client-Tunnel", + "invalidBindIp": "Lokale IP muss eine gültige IPv4-Adresse sein.", + "invalidLocalTargetIp": "Lokale Ziel-IP muss eine gültige IPv4-Adresse sein.", + "invalidLocalPort": "Lokaler Port muss zwischen 1 und 65535 liegen.", + "invalidRemotePort": "Remote-Port muss zwischen 1 und 65535 liegen.", + "invalidLocalTargetPort": "Lokaler Zielport muss zwischen 1 und 65535 liegen.", + "invalidEndpointPort": "Endpunkt-Port muss zwischen 1 und 65535 liegen.", + "duplicateAutoStartBind": "Nur ein automatisch startender Client-Tunnel kann {{bind}} verwenden.", + "manualControlError": "Fehler beim Aktualisieren des Tunnelstatus.", "active": "Aktiv", "start": "Start", - "stop": "Stoppen", - "test": "Prüfen", + "stop": "Stop", + "test": "Test", "type": "Tunneltyp", "typeLocal": "Lokal (-L)", "typeRemote": "Remote (-R)", "typeDynamic": "Dynamisch (-D)", "typeServerLocalDesc": "Vom aktuellen Host zum Endpunkt.", - "typeServerRemoteDesc": "Endpunkt zurück zum aktuellen Host.", - "typeClientLocalDesc": "Lokaler Computer zum Endpunkt.", - "typeClientRemoteDesc": "Endpunkt zurück zum lokalen Computer.", + "typeServerRemoteDesc": "Vom Endpunkt zurück zum aktuellen Host.", + "typeClientLocalDesc": "Vom lokalen Computer zum Endpunkt.", + "typeClientRemoteDesc": "Vom Endpunkt zurück zum lokalen Computer.", "typeClientDynamicDesc": "SOCKS auf dem lokalen Computer.", - "typeDynamicDesc": "SOCKS5-CONNECT-Datenverkehr über SSH weiterleiten", + "typeDynamicDesc": "SOCKS5 CONNECT-Verkehr über SSH weiterleiten.", "forwardDescriptionServerLocal": "Aktueller Host {{sourcePort}} → Endpunkt {{endpointPort}}.", "forwardDescriptionServerRemote": "Endpunkt {{endpointPort}} → aktueller Host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS auf dem aktuellen Host {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokal {{sourcePort}} → Remote {{endpointPort}}.", - "forwardDescriptionClientRemote": "Remote {{sourcePort}} → lokal {{endpointPort}}.", + "forwardDescriptionServerDynamic": "SOCKS auf aktuellem Host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Lokal {{sourcePort}} → entfernt {{endpointPort}}.", + "forwardDescriptionClientRemote": "Entfernt {{sourcePort}} → lokal {{endpointPort}}.", "forwardDescriptionClientDynamic": "SOCKS auf lokalem Port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS über {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", "autoNameClientLocal": "Lokal {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokal {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} über {{endpoint}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", "route": "Route:", "lastStarted": "Zuletzt gestartet", "lastTested": "Zuletzt getestet", "lastError": "Letzter Fehler", - "maxRetries": "Maximale Wiederholungsversuche", - "maxRetriesDescription": "Maximale Anzahl an Wiederholungsversuchen.", + "maxRetries": "Max. Wiederholungen", + "maxRetriesDescription": "Maximale Anzahl von Wiederholungsversuchen.", "retryInterval": "Wiederholungsintervall (Sekunden)", - "retryIntervalDescription": "Wartezeit zwischen den Wiederholungsversuchen.", + "retryIntervalDescription": "Wartezeit zwischen Wiederholungsversuchen.", "local": "Lokal", "remote": "Remote", "destination": "Ziel", "host": "Host", "mode": "Modus", "noHostSelected": "Kein Host ausgewählt", - "working": "Wird ausgeführt..." + "working": "In Bearbeitung..." }, "cardGrid": { "dragToMove": "Zum Verschieben ziehen", @@ -1993,59 +2126,59 @@ "removeCard": "{{label}} entfernen", "addCard": "Hinzufügen", "columns": "Spalten", - "empty": "Keine Karten. Verwenden Sie unten „Hinzufügen“, um Karten zu platzieren." + "empty": "Keine Karten. Verwenden Sie „Hinzufügen“ unten, um Karten zu platzieren." }, "hostMetrics": { "cpu": "CPU", "memory": "Arbeitsspeicher", - "disk": "Datenträger", + "disk": "Festplatte", "network": "Netzwerk", "uptime": "Betriebszeit", "processes": "Prozesse", "available": "Verfügbar", "free": "Frei", - "connecting": "Verbindung wird hergestellt...", + "connecting": "Verbinden...", "connectionFailed": "Verbindung zum Server fehlgeschlagen", - "naCpus": "CPU(s): n. v.", + "naCpus": "N/A CPU(s)", "cpuCores_one": "{{count}} Kern", "cpuCores_other": "{{count}} Kerne", "cpuUsage": "CPU-Auslastung", - "memoryUsage": "Arbeitsspeichernutzung", - "diskUsage": "Festplattennutzung", + "memoryUsage": "Arbeitsspeicherauslastung", + "diskUsage": "Festplattenauslastung", "temperature": "Temperatur", "highestTemperature": "Höchste Temperatur", - "failedToFetchHostConfig": "Hostkonfiguration konnte nicht abgerufen werden", + "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": "Host-Metriken unterstützen den Authentifizierungstyp „none“ nicht.", + "totpFailed": "TOTP-Überprüfung fehlgeschlagen", + "noneAuthNotSupported": "Host Metrics unterstützt den Authentifizierungstyp 'none' nicht.", "noHostSelected": "Kein Host ausgewählt", - "load": "Laden", + "load": "Auslastung", "systemInfo": "Systeminformationen", "hostname": "Hostname", "operatingSystem": "Betriebssystem", "kernel": "Kernel", "seconds": "Sekunden", "networkInterfaces": "Netzwerkschnittstellen", - "noInterfacesFound": "Es wurden keine Netzwerkschnittstellen gefunden.", + "noInterfacesFound": "Keine Netzwerkschnittstellen gefunden", "noProcessesFound": "Keine Prozesse gefunden", "processesTotal": "gesamt", - "processesRunning": "aktiv", + "processesRunning": "laufend", "loginStats": "SSH-Anmeldestatistiken", "noRecentLoginData": "Keine aktuellen Anmeldedaten", "executingQuickAction": "{{name}} wird ausgeführt...", "quickActionSuccess": "{{name}} erfolgreich abgeschlossen", "quickActionFailed": "{{name}} fehlgeschlagen", - "quickActionError": "Ausführung von {{name}} fehlgeschlagen", + "quickActionError": "Fehler beim Ausführen von {{name}}", "ports": { - "title": "Offene Ports", + "title": "Lauschende Ports", "protocol": "Protokoll", "port": "Port", "address": "Adresse", "process": "Prozess", "search": "Ports durchsuchen...", "allProtocols": "Alle", - "noData": "Keine Daten zu offenen Ports" + "noData": "Keine Daten zu lauschenden Ports" }, "firewall": { "title": "Firewall", @@ -2055,13 +2188,13 @@ "noRules": "Keine Regeln in dieser Kette", "noData": "Keine Firewall-Daten verfügbar", "action": "Aktion", - "protocol": "Protokoll", + "protocol": "Proto", "port": "Port", "source": "Quelle", "anywhere": "Überall", "chains": "Ketten" }, - "loadAvg": "Durchschnittliche Last", + "loadAvg": "Lastdurchschnitt", "swap": "Swap", "architecture": "Architektur", "refresh": "Aktualisieren", @@ -2069,76 +2202,76 @@ "customize": "Layout anpassen", "reset": "Zurücksetzen", "tabLive": "Live", - "editModeInstructions": "Ziehen Sie Karten per Drag & Drop, um sie neu anzuordnen, ziehen Sie am unteren Rand, um die Größe anzupassen, und verwenden Sie die Breitenschaltfläche, um die Kartenbreite zu ändern. Fügen Sie unten Karten hinzu oder entfernen Sie sie.", + "editModeInstructions": "Ziehen Sie Karten, um sie neu anzuordnen, ziehen Sie die untere Kante, um die Größe zu ändern, und verwenden Sie die Breitenschaltfläche, um die Breite einer Karte anzupassen. Fügen Sie Karten unten hinzu oder entfernen Sie sie.", "managers": { "services": "Dienste", "processInspector": "Prozessinspektor", - "logViewer": "Log-Anzeige", + "logViewer": "Protokollbetrachter", "cron": "Cron-Jobs", "packages": "Pakete", "ssl": "SSL-Zertifikate", "firewall": "Firewall", - "users": "Benutzer und Berechtigungen", - "healthCheck": "Zustandsprüfungen", - "diskBreakdown": "Datenträgerbelegung", + "users": "Benutzer & Berechtigungen", + "healthCheck": "Gesundheitsprüfungen", + "diskBreakdown": "Datenträgeraufschlüsselung", "systemdTimers": "Timer", - "topMemory": "Höchster Arbeitsspeicherverbrauch", + "topMemory": "Top nach Speicher", "noData": "Keine Daten", - "sudoHint": "Um privilegierte Aktionen zu ermöglichen, muss im Host-Editor ein sudo-Passwort für diesen Host festgelegt werden.", - "filter": "Filter...", + "sudoHint": "Legen Sie im Host-Editor ein sudo-Passwort für diesen Host fest, um privilegierte Aktionen zu ermöglichen.", + "filter": "Filtern...", "start": "Starten", "stop": "Stoppen", - "restart": "Neu starten", + "restart": "Neustarten", "actionDone": "{{name}} aktualisiert", "actionFailed": "Aktion fehlgeschlagen", "signalSent": "Signal an PID {{pid}} gesendet", - "killHint": "Klicken: beenden (SIGTERM). Rechtsklick: Beenden erzwingen (SIGKILL).", - "working": "Wird ausgeführt...", + "killHint": "Klicken: Beenden (SIGTERM). Rechtsklick: Erzwungenes Beenden (SIGKILL).", + "working": "In Bearbeitung...", "update": "Aktualisieren", - "upgradeAll": "Alle aktualisieren", - "allUpToDate": "Alles ist auf dem neuesten Stand.", + "upgradeAll": "Alle upgraden", + "allUpToDate": "Alles ist auf dem neuesten Stand", "save": "Speichern", "command": "Befehl", "enabled": "Aktiviert", "cronSaved": "Crontab aktualisiert", "clients": "Clients", - "dryRun": "Testlauf", + "dryRun": "Trockenlauf", "renew": "Erneuern", - "noAcmeClient": "Auf diesem Host wurde kein ACME-Client (certbot oder acme.sh) gefunden.", + "noAcmeClient": "Kein ACME-Client (certbot oder acme.sh) auf diesem Host gefunden.", "addInputRule": "INPUT-Regel hinzufügen", - "firewallWarning": "Änderungen werden nur zur Laufzeit wirksam, bis sie gespeichert werden. Achten Sie darauf, sich nicht selbst auszusperren.", + "firewallWarning": "Änderungen sind nur zur Laufzeit wirksam, bis sie gespeichert werden. Achten Sie darauf, sich nicht auszusperren.", "ruleApplied": "Regel angewendet", - "invalidPort": "Geben Sie einen gültigen Port ein (1-65535)", + "invalidPort": "Gültigen Port eingeben (1–65535)", "newUsername": "Neuer Benutzername", "addUser": "Hinzufügen", "deleteUser": "Benutzer löschen", "noHealthChecks": "Noch keine Zustandsprüfungen konfiguriert.", - "follow": "Fortlaufend anzeigen", + "follow": "Folgen", "noLogData": "Keine Protokollausgabe.", "tree": "Baum", - "enableDisable": "Beim Systemstart aktivieren/deaktivieren", - "grantSudo": "sudo-Berechtigung erteilen", - "revokeSudo": "sudo-Berechtigung entziehen", + "enableDisable": "Beim Start aktivieren / deaktivieren", + "grantSudo": "sudo gewähren", + "revokeSudo": "sudo entziehen", "sslIssueCert": "Zertifikat ausstellen", "sslExpired": "Abgelaufen", - "sslInDays": "in {{days}} Tagen", - "sslNeedDomain": "Geben Sie mindestens eine Domäne ein", + "sslInDays": "in {{days}} T", + "sslNeedDomain": "Geben Sie mindestens eine Domain ein", "sslIssued": "Zertifikat ausgestellt", "sslDomainsPlaceholder": "example.com, www.example.com", - "sslHttpStandalone": "HTTP (eigenständig)", + "sslHttpStandalone": "HTTP (Standalone)", "sslHttpWebroot": "HTTP (Webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNS-Anbieter (z. B. Cloudflare)", - "sslIssueHint": "Die Zugangsdaten des DNS-Anbieters müssen bereits auf dem Host konfiguriert sein.", + "sslDnsProvider": "DNS-Anbieter (z. B. cloudflare)", + "sslIssueHint": "Anmeldedaten des DNS-Anbieters müssen bereits auf dem Host konfiguriert sein.", "sslRevoke": "Zertifikat widerrufen", - "sslRevoked": "Zertifikat widerrufen", - "sslRevokeConfirm": "Das Zertifikat \"{{name}}\" widerrufen und entfernen? Dies kann nicht rückgängig gemacht werden.", + "sslRevoked": "Zertifikat wurde widerrufen", + "sslRevokeConfirm": "Zertifikat \"{{name}}\" widerrufen und entfernen? Dies kann nicht rückgängig gemacht werden.", "healthRun": "Ausführen", "healthName": "Name", "healthTarget": "Host / Adresse", "healthAddCheck": "Prüfung hinzufügen", - "healthSaved": "Systemprüfungen gespeichert", - "healthMissingFields": "Jede Prüfung benötigt einen Namen und ein Ziel.", + "healthSaved": "Statusprüfungen gespeichert", + "healthMissingFields": "Jede Prüfung benötigt einen Namen und ein Ziel", "logFile": "Datei", "logUnit": "Unit", "logCustomPath": "Benutzerdefinierter Pfad unter /var/log (optional)", @@ -2147,143 +2280,144 @@ "firewallPersisted": "Firewall-Regeln dauerhaft gespeichert", "wireguard": "WireGuard", "tailscale": "Tailscale", - "wgNotInstalled": "WireGuard ist auf diesem Host nicht installiert.", + "wgNotInstalled": "WireGuard ist auf diesem Host nicht installiert", "wgNoInterfaces": "Keine WireGuard-Schnittstellen konfiguriert", "wgInterfaceUp": "Aktivieren", "wgInterfaceDown": "Deaktivieren", "wgBringingUp": "{{name}} wird aktiviert...", "wgBringingDown": "{{name}} wird deaktiviert...", "wgInterfaceUpDone": "{{name}} ist aktiv", - "wgInterfaceDownDone": "{{name}} ist inaktiv", + "wgInterfaceDownDone": "{{name}} ist deaktiviert", "wgListenPort": "Port", "wgPublicKey": "Öffentlicher Schlüssel", "wgEndpoint": "Endpunkt", - "wgAllowedIPs": "Zulässige IPs", + "wgAllowedIPs": "Erlaubte IPs", "wgLastHandshake": "Letzter Handshake", - "wgHandshakeNever": "Niemals", + "wgHandshakeNever": "Nie", "wgTransfer": "Übertragung", - "tsNotInstalled": "Tailscale ist auf diesem Host nicht installiert.", - "tsRunning": "Läuft", - "tsStopped": "Angehalten", + "tsNotInstalled": "Tailscale ist auf diesem Host nicht installiert", + "tsRunning": "Aktiv", + "tsStopped": "Gestoppt", "tsEnable": "Verbinden", "tsDisable": "Trennen", - "tsEnabling": "Verbindung zu Tailscale wird hergestellt...", - "tsDisabling": "Verbindung zu Tailscale wird getrennt...", + "tsEnabling": "Verbinde mit Tailscale...", + "tsDisabling": "Trenne von Tailscale...", "tsEnabled": "Tailscale verbunden", "tsDisabled": "Tailscale getrennt", "tsIPs": "Tailscale-IPs", "tsPeers": "Peers", "tsOnline": "Online", "tsOffline": "Offline", - "tsExitNode": "Exit-Node", - "tsExitNodeActive": "Exit-Node aktiv", + "tsExitNode": "Exit-Knoten", + "tsExitNodeActive": "Exit-Knoten aktiv", "tsHostname": "Hostname" } }, "auth": { - "tagline": "Selbstgehostete SSH- und Remote-Desktop-Verwaltung", + "tagline": "Selbst gehostete SSH- und Remote-Desktop-Verwaltung", "loginTitle": "Willkommen zurück", - "registerTitle": "Benutzerkonto erstellen", + "registerTitle": "Konto erstellen", "forgotPassword": "Passwort vergessen?", - "rememberMe": "Gerät 30 Tage lang speichern (einschließlich TOTP)", - "noAccount": "Sie haben noch kein Konto?", - "hasAccount": "Sie haben bereits ein Konto?", + "rememberMe": "Gerät für 30 Tage merken (inkl. TOTP)", + "noAccount": "Kein Konto?", + "hasAccount": "Bereits ein Konto?", "twoFactorAuth": "Zwei-Faktor-Authentifizierung", - "enterCode": "Geben Sie den Bestätigungscode ein.", - "backupCode": "Oder einen Backup-Code verwenden", - "verifyCode": "Code überprüfen", + "enterCode": "Bestätigungscode eingeben", + "backupCode": "Oder Backup-Code verwenden", + "verifyCode": "Code bestätigen", "redirectingToApp": "Weiterleitung zur App...", "sshAuthenticationRequired": "SSH-Authentifizierung erforderlich", - "sshNoKeyboardInteractive": "Tastaturinteraktive Authentifizierung nicht verfügbar", + "sshNoKeyboardInteractive": "Keyboard-Interactive-Authentifizierung nicht verfügbar", "sshAuthenticationFailed": "Authentifizierung fehlgeschlagen", - "sshAuthenticationTimeout": "Zeitüberschreitung bei der Authentifizierung", - "sshNoKeyboardInteractiveDescription": "Der Server unterstützt keine Authentifizierung per Tastatur. Bitte geben Sie Ihr Passwort oder Ihren SSH-Schlüssel ein.", - "sshAuthFailedDescription": "Die angegebenen Zugangsdaten waren falsch. Bitte versuchen Sie es erneut mit gültigen Zugangsdaten.", - "sshTimeoutDescription": "Beim Authentifizierungsversuch ist eine Zeitüberschreitung aufgetreten. Bitte versuchen Sie es erneut.", - "sshProvideCredentialsDescription": "Bitte geben Sie Ihre SSH-Zugangsdaten an, um eine Verbindung zu diesem Server herzustellen.", + "sshAuthenticationTimeout": "Authentifizierungs-Timeout", + "sshNoKeyboardInteractiveDescription": "Der Server unterstützt keine Keyboard-Interactive-Authentifizierung. Bitte geben Sie Ihr Passwort oder Ihren SSH-Schlüssel an.", + "sshAuthFailedDescription": "Die angegebenen Anmeldedaten waren falsch. Bitte versuchen Sie es mit gültigen Anmeldedaten erneut.", + "sshTimeoutDescription": "Der Authentifizierungsversuch hat die Zeit überschritten. Bitte versuchen Sie es erneut.", + "sshProvideCredentialsDescription": "Bitte geben Sie Ihre SSH-Anmeldedaten ein, um eine Verbindung zu diesem Server herzustellen.", "sshPasswordDescription": "Geben Sie das Passwort für diese SSH-Verbindung ein.", "sshKeyPasswordDescription": "Falls 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.", + "passphraseRequiredDescription": "Der SSH-Schlüssel ist verschlüsselt. Bitte geben Sie die Passphrase ein, um ihn zu entschlüsseln.", "back": "Zurück", "firstUser": "Erster Benutzer", - "firstUserMessage": "Sie sind der erste Benutzer und werden zum Administrator ernannt. Die Administratoreinstellungen finden Sie im Benutzermenü in der Seitenleiste. Sollten Sie der Meinung sein, dass dies ein Fehler ist, überprüfen Sie bitte die Docker-Logs oder erstellen Sie ein GitHub-Issue.", + "firstUserMessage": "Sie sind der erste Benutzer und werden zum Administrator ernannt. Die Admin-Einstellungen finden Sie im Benutzer-Dropdown der Seitenleiste. Falls Sie dies für einen Fehler halten, überprüfen Sie die Docker-Protokolle oder erstellen Sie ein GitHub-Issue.", "external": "Extern", - "loginWithExternal": "Mit externem Anbieter anmelden", - "loginWithExternalDesc": "Melden Sie sich mit Ihrem konfigurierten externen Identitätsanbieter an.", - "externalNotSupportedInElectron": "Die externe Authentifizierung wird in der Electron-App noch nicht unterstützt. Bitte verwenden Sie die Webversion für die OIDC-Anmeldung.", - "loginWithProvider": "Anmelden mit {{name}}", + "loginWithExternal": "Anmeldung mit externem Anbieter", + "loginWithExternalDesc": "Melden Sie sich mit Ihrem konfigurierten externen Identitätsanbieter an", + "externalNotSupportedInElectron": "Externe Authentifizierung wird in der Electron-App derzeit nicht unterstützt. Bitte verwenden Sie die Web-Version für die OIDC-Anmeldung.", + "loginWithProvider": "Anmeldung mit {{name}}", "orContinueWith": "oder fortfahren mit", "ldapUsername": "LDAP-Benutzername", "ldapPassword": "LDAP-Passwort", "ldapSignIn": "Anmelden", "ldapLoginFailed": "LDAP-Anmeldung fehlgeschlagen", "resetPasswordButton": "Passwort zurücksetzen", - "sendResetCode": "Code zum Zurücksetzen senden", - "resetCodeDesc": "Geben Sie Ihren Benutzernamen ein, um einen Code zum Zurücksetzen Ihres Passworts zu erhalten. Der Code wird in den Docker-Container-Logs ausgegeben.", + "sendResetCode": "Reset-Code senden", + "resetCodeDesc": "Geben Sie Ihren Benutzernamen ein, um einen Code zum Zurücksetzen des Passworts zu erhalten. Der Code wird in den Docker-Container-Protokollen ausgegeben.", "resetCode": "Reset-Code", "verifyCodeButton": "Code überprüfen", - "enterResetCode": "Geben Sie den 6-stelligen Code aus den Docker-Container-Protokollen für den Benutzer ein:", + "enterResetCode": "Geben Sie den 6-stelligen Code aus den Docker-Container-Protokollen für Benutzer ein:", "newPassword": "Neues Passwort", "confirmNewPassword": "Passwort bestätigen", - "enterNewPassword": "Geben Sie Ihr neues Passwort für den Benutzer ein:", + "enterNewPassword": "Geben Sie Ihr neues Passwort für Benutzer ein:", "signUp": "Registrieren", "desktopApp": "Desktop-App", "loggingInToDesktopApp": "Anmeldung bei der Desktop-App", "loadingServer": "Server wird geladen...", - "dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Zugangsdaten und andere verschlüsselte Daten gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden. Verwenden Sie diese Methode nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind.", + "dataLossWarning": "Das Zurücksetzen Ihres Passworts auf diese Weise löscht alle gespeicherten SSH-Hosts, Anmeldedaten und andere verschlüsselte Daten. 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 wenden Sie sich an Ihren Administrator.", - "passwordLoginDisabledDesc": "Die Anmeldung mit Passwort ist deaktiviert. Verwenden Sie einen Passkey oder einen externen Authentifizierungsanbieter.", + "passwordLoginDisabledDesc": "Passwort-Anmeldung ist deaktiviert. Verwenden Sie einen Passkey oder einen externen Authentifizierungsanbieter.", "signInWithPasskey": "Mit Passkey anmelden", - "passkeyLoginFailed": "Anmeldung mit Passkey fehlgeschlagen", - "attemptsRemaining": "{{count}} verbleibende Versuche", - "confirmResetDataWipe": "Dieses Konto wurde seit dem Upgrade der Verschlüsselung nicht mehr verwendet. Die gespeicherten Daten können daher ohne das alte Passwort nicht wiederhergestellt werden. Durch das Zurücksetzen werden Hosts, Anmeldeinformationen und Snippets endgültig gelöscht. Fortfahren?" + "passkeyLoginFailed": "Passkey-Anmeldung fehlgeschlagen", + "attemptsRemaining": "{{count}} Versuche übrig", + "confirmResetDataWipe": "Dieses Konto hat sich seit dem Verschlüsselungs-Upgrade nicht angemeldet, daher können die gespeicherten Daten ohne das alte Passwort nicht wiederhergestellt werden. Ein Zurücksetzen löscht Hosts, Anmeldedaten und Snippets endgültig. Fortfahren?" }, "hostKey": { - "verifyNewHost": "SSH-Hostschlüssel überprüfen", - "keyChangedWarning": "SSH-Hostschlüssel geändert", + "verifyNewHost": "SSH-Host-Key überprüfen", + "keyChangedWarning": "SSH-Host-Key geändert", "firstConnectionTitle": "Erstmalige Verbindung zu diesem Host", - "firstConnectionDescription": "Die Authentizität dieses Hosts kann nicht bestätigt werden. Überprüfen Sie, ob der Fingerabdruck Ihren Erwartungen entspricht.", - "keyChangedDescription": "Der Hostschlüssel für diesen Server hat sich seit Ihrer letzten Verbindung geändert. Dies könnte auf ein Sicherheitsproblem hinweisen.", - "previousKey": "Vorheriger Schlüssel", + "firstConnectionDescription": "Die Authentizität dieses Hosts kann nicht überprüft werden. Bitte den Fingerabdruck mit dem erwarteten Wert abgleichen.", + "keyChangedDescription": "Der Host-Key für diesen Server hat sich seit der letzten Verbindung geändert. Dies könnte auf ein Sicherheitsproblem hinweisen.", + "previousKey": "Vorheriger Key", "newFingerprint": "Neuer Fingerabdruck", "fingerprint": "Fingerabdruck", - "verifyInstructions": "Wenn Sie diesem Host vertrauen, klicken Sie auf „Akzeptieren“, um fortzufahren und diesen Fingerabdruck für zukünftige Verbindungen zu speichern.", + "verifyInstructions": "Wenn Sie diesem Host vertrauen, klicken Sie auf Akzeptieren, um fortzufahren und diesen Fingerabdruck für zukünftige Verbindungen zu speichern.", "securityWarning": "Sicherheitswarnung", - "acceptAndContinue": "Akzeptieren und fortfahren", - "acceptNewKey": "Neuen Schlüssel akzeptieren und fortfahren" + "acceptAndContinue": "Akzeptieren & Fortfahren", + "acceptNewKey": "Neuen Key akzeptieren & Fortfahren" }, "errors": { - "databaseConnection": "Es konnte keine Verbindung zur Datenbank hergestellt werden.", + "databaseConnection": "Verbindung zur Datenbank konnte nicht hergestellt werden", "unknownError": "Unbekannter Fehler", "loginFailed": "Anmeldung fehlgeschlagen", - "failedPasswordReset": "Passwortzurücksetzung konnte nicht initiiert werden", - "failedVerifyCode": "Zurücksetzungscode konnte nicht überprüft werden", - "failedCompleteReset": "Passwortzurücksetzung konnte nicht abgeschlossen werden", + "failedPasswordReset": "Passwort-Zurücksetzung konnte nicht gestartet werden", + "failedVerifyCode": "Bestätigungscode konnte nicht überprüft werden", + "failedCompleteReset": "Passwort-Zurücksetzung konnte nicht abgeschlossen werden", "invalidTotpCode": "Ungültiger TOTP-Code", - "failedOidcLogin": "OIDC-Anmeldung konnte nicht gestartet werden.", - "silentSigninOidcUnavailable": "Eine automatische Anmeldung im Hintergrund wurde angefordert, die OIDC-Anmeldung ist jedoch nicht verfügbar.", - "failedUserInfo": "Benutzerinformationen konnten nach der Anmeldung nicht abgerufen werden", + "failedOidcLogin": "OIDC-Anmeldung konnte nicht gestartet werden", + "silentSigninOidcUnavailable": "Stille Anmeldung wurde angefordert, aber OIDC-Anmeldung ist nicht verfügbar.", + "failedUserInfo": "Benutzerinfo nach Anmeldung konnte nicht abgerufen werden", "oidcAuthFailed": "OIDC-Authentifizierung fehlgeschlagen", - "invalidAuthUrl": "Ungültige Autorisierungs-URL vom Backend empfangen", + "invalidAuthUrl": "Ungültige Autorisierungs-URL vom Backend erhalten", "requiredField": "Dieses Feld ist erforderlich", - "minLength": "Die Mindestlänge beträgt {{min}}", - "passwordMismatch": "Die Passwörter stimmen nicht überein.", - "passwordLoginDisabled": "Die Anmeldung mit Benutzername/Passwort ist derzeit deaktiviert.", - "sessionExpired": "Ihre Sitzung ist abgelaufen – bitte melden Sie sich erneut an.", - "totpRateLimited": "Zu viele TOTP-Bestätigungsversuche. Bitte versuchen Sie es später erneut.", - "totpRateLimitedWithTime": "Zu viele TOTP-Verifizierungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", - "resetCodeRateLimited": "Zu viele Überprüfungsversuche. Bitte versuchen Sie es später erneut.", - "resetCodeRateLimitedWithTime": "Zu viele Bestätigungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", - "authTokenSaveFailed": "Speichern des Authentifizierungstokens fehlgeschlagen", - "failedToLoadServer": "Server konnte nicht geladen werden" + "minLength": "Mindestlänge ist {{min}}", + "passwordMismatch": "Passwörter stimmen nicht überein", + "passwordLoginDisabled": "Benutzername/Passwort-Anmeldung ist derzeit deaktiviert", + "sessionExpired": "Sitzung abgelaufen – bitte erneut anmelden", + "totpRateLimited": "Ratenbegrenzung: Zu viele TOTP-Überprüfungsversuche. Bitte versuchen Sie es später erneut.", + "totpRateLimitedWithTime": "Ratenbegrenzung: Zu viele TOTP-Überprüfungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", + "resetCodeRateLimited": "Ratenbegrenzung: Zu viele Überprüfungsversuche. Bitte versuchen Sie es später erneut.", + "resetCodeRateLimitedWithTime": "Ratenbegrenzung: Zu viele Verifizierungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", + "authTokenSaveFailed": "Fehler beim Speichern des Authentifizierungstokens", + "failedToLoadServer": "Server konnte nicht geladen werden", + "remoteServerRequired": "Remote-Server erforderlich. Um diesen Verbindungstyp zu verwenden, verbinden Sie einen Remote-Server in den Einstellungen." }, "messages": { - "registrationDisabled": "Die Registrierung neuer Konten ist derzeit von einem Administrator deaktiviert. Bitte melden Sie sich an oder kontaktieren Sie einen Administrator.", - "userNotAllowed": "Ihr Konto ist nicht zur Registrierung berechtigt. Bitte kontaktieren Sie einen Administrator.", + "registrationDisabled": "Die Registrierung neuer Konten ist derzeit durch einen Administrator deaktiviert. Bitte melden Sie sich an oder wenden Sie sich an einen Administrator.", + "userNotAllowed": "Ihr Konto ist nicht zur Registrierung berechtigt. Bitte wenden Sie sich an einen Administrator.", "databaseConnectionFailed": "Verbindung zum Datenbankserver fehlgeschlagen", - "resetCodeSent": "Code zum Zurücksetzen in Docker-Logs ausgegeben", + "resetCodeSent": "Zurücksetzungscode an Docker-Protokolle gesendet", "codeVerified": "Code erfolgreich verifiziert", "passwordResetSuccess": "Passwort erfolgreich zurückgesetzt", "loginSuccess": "Anmeldung erfolgreich", @@ -2292,16 +2426,16 @@ "profile": { "c2sTunnelConfigDesc": "Lokale Desktop-Tunnel, die auf konfigurierte SSH-Hosts abzielen.", "c2sTunnelPresets": "Client-Tunnel-Voreinstellungen", - "c2sTunnelPresetsDesc": "Speichern Sie die lokale Tunnelliste dieses Desktop-Clients als benannte Servervoreinstellung oder laden Sie eine Voreinstellung wieder in diesen Client.", + "c2sTunnelPresetsDesc": "Speichern Sie die lokale Tunnel-Liste dieses Desktop-Clients als benannte Server-Voreinstellung oder laden Sie eine Voreinstellung zurück in diesen Client.", "c2sTunnelPresetsUnavailable": "Client-Tunnel-Voreinstellungen sind nur im Desktop-Client verfügbar.", - "c2sPresetName": "Name der Voreinstellung", + "c2sPresetName": "Voreinstellungsname", "c2sPresetNamePlaceholder": "Name der Client-Voreinstellung", - "c2sPresetToLoad": "Voreinstellung zum Laden", + "c2sPresetToLoad": "Zu ladende Voreinstellung", "c2sNoPresetSelected": "Keine Voreinstellung ausgewählt", "c2sNoPresets": "Keine Voreinstellungen gespeichert", "c2sLoadPreset": "Laden", "c2sCurrentLocalConfig": "{{count}} lokale Client-Tunnel auf diesem Desktop konfiguriert.", - "c2sPresetSyncNote": "Voreinstellungen sind explizite Momentaufnahmen; das Laden einer solchen Voreinstellung ersetzt die lokale Client-Tunnelliste dieses Desktop-Clients.", + "c2sPresetSyncNote": "Voreinstellungen sind explizite Snapshots; das Laden einer Voreinstellung ersetzt die lokale Client-Tunnel-Liste dieses Desktop-Clients.", "c2sPresetSaved": "Client-Tunnel-Voreinstellung gespeichert", "c2sPresetLoaded": "Client-Tunnel-Voreinstellung lokal geladen", "c2sPresetRenamed": "Client-Tunnel-Voreinstellung umbenannt", @@ -2313,11 +2447,11 @@ "retryInterval": "10", "language": "Sprache", "keyPassword": "Schlüsselpasswort", - "pastePrivateKey": "Fügen Sie hier Ihren privaten Schlüssel ein...", - "localListenerHost": "127.0.0.1 (lokaler Listener)", + "pastePrivateKey": "Privaten Schlüssel hier einfügen...", + "localListenerHost": "127.0.0.1 (lokal lauschen)", "localTargetHost": "127.0.0.1 (Ziel auf diesem Computer)", "socksListenerHost": "127.0.0.1 (SOCKS-Listener)", - "enterPassword": "Geben Sie Ihr Passwort ein", + "enterPassword": "Passwort eingeben", "defaultPort": "22", "defaultEndpointPort": "224" }, @@ -2329,52 +2463,52 @@ "discord": "Discord", "docs": "Dokumentation", "donate": "Spenden", - "serverOverview": "Serverübersicht", + "serverOverview": "Server-Übersicht", "version": "Version", "upToDate": "Aktuell", "updateAvailable": "Update verfügbar", "beta": "Beta", "uptime": "Betriebszeit", "database": "Datenbank", - "healthy": "Fehlerfrei", + "healthy": "Gesund", "error": "Fehler", - "totalHosts": "Gesamtzahl der Hosts", - "totalTunnels": "Tunnel insgesamt", - "totalCredentials": "Gesamtzahl der Zugangsdaten", + "totalHosts": "Hosts gesamt", + "totalTunnels": "Tunnel gesamt", + "totalCredentials": "Zugangsdaten gesamt", "recentActivity": "Letzte Aktivitäten", "reset": "Zurücksetzen", - "loadingRecentActivity": "Aktuelle Aktivitäten werden geladen...", - "noRecentActivity": "Keine kürzlichen Aktivitäten", + "loadingRecentActivity": "Letzte Aktivitäten werden geladen...", + "noRecentActivity": "Keine letzten Aktivitäten", "quickActions": "Schnellaktionen", "addHost": "Host hinzufügen", "addCredential": "Zugangsdaten hinzufügen", - "adminSettings": "Administratoreinstellungen", + "adminSettings": "Admin-Einstellungen", "userProfile": "Benutzerprofil", - "serverStats": "Serverstatistiken", - "loadingServerStats": "Serverstatistiken werden geladen...", + "serverStats": "Server-Statistiken", + "loadingServerStats": "Server-Statistiken werden geladen...", "noServerData": "Keine Serverdaten verfügbar", "cpu": "CPU", "ram": "RAM", "customizeLayout": "Dashboard anpassen", "dashboardSettings": "Dashboard-Einstellungen", "enableDisableCards": "Karten aktivieren/deaktivieren", - "resetLayout": "Auf Standardwerte zurücksetzen", + "resetLayout": "Auf Standard zurücksetzen", "serverOverviewCard": "Serverübersicht", - "recentActivityCard": "Aktuelle Aktivitäten", + "recentActivityCard": "Letzte Aktivitäten", "networkGraphCard": "Netzwerkdiagramm", "networkGraph": "Netzwerkdiagramm", "quickActionsCard": "Schnellaktionen", "serverStatsCard": "Serverstatistiken", "panelMain": "Hauptbereich", - "panelSide": "Seitlich", - "justNow": "soeben", + "panelSide": "Seitenbereich", + "justNow": "gerade eben", "serviceLinks": "Service-Links", - "homepagePreview": "Startseitenvorschau" + "homepagePreview": "Homepage-Vorschau" }, "donation": { "title": "Gefällt Ihnen Termix?", - "body": "Termix ist kostenlos und Open Source und wird von einem Zweierteam in unserer Freizeit entwickelt und gepflegt. Wenn es ein kommerzielles Tool ersetzt, für das Sie sonst bezahlen müssten, hilft eine Spende, die Hostingkosten zu decken und die Weiterentwicklung zu sichern. Aktuell sind Spenden nur in Kryptowährung möglich.", - "milestones": "Spenden tragen auch dazu bei, die Zeit für Recherchen und das Erlernen der notwendigen Funktionen wie SAML- und Kubernetes-Unterstützung zu finanzieren. Den Fortschritt können Sie auf der Spendenseite verfolgen.", + "body": "Termix ist kostenlos und Open Source, entwickelt und gewartet von einem Zwei-Personen-Team in unserer Freizeit. Wenn es ein kommerzielles Tool ersetzt hat, für das Sie sonst bezahlen würden, hilft eine Spende, die Hosting-Kosten zu decken und die Entwicklung fortzusetzen. Derzeit sind Spenden nur in Kryptowährung möglich.", + "milestones": "Spenden helfen auch, die Zeit für Forschung und Lernen zu finanzieren, die nötig ist, um Funktionen wie SAML- und Kubernetes-Unterstützung zu entwickeln. Den Fortschritt sehen Sie auf der Spendenseite.", "cta": "Spenden", "dismiss": "Vielleicht später" }, @@ -2386,35 +2520,35 @@ "storeSshKeysOrPasswords": "SSH-Schlüssel oder Passwörter speichern", "manageUsersAndRoles": "Benutzer und Rollen verwalten", "manageYourAccount": "Ihr Konto verwalten", - "hostStatus": "Hoststatus", + "hostStatus": "Host-Status", "noHostsConfigured": "Keine Hosts konfiguriert", "online": "ONLINE", "offline": "OFFLINE", - "checking": "WIRD GEPRÜFT", + "checking": "PRÜFEN", "onlineLower": "Online", "nodes": "{{count}} Knoten", "add": "Hinzufügen:", "commandPalette": "Befehlspalette", - "done": "Erledigt", - "editModeInstructions": "Karten per Drag & Drop neu anordnen · Spaltentrenner per Drag & Drop ziehen, um die Spaltenbreite anzupassen · Unterkante einer Karte per Drag & Drop ziehen, um ihre Höhe zu ändern · Zum Entfernen in den Papierkorb klicken", + "done": "Fertig", + "editModeInstructions": "Karten ziehen, um sie neu anzuordnen · Spaltentrenner ziehen, um Spalten zu skalieren · Untere Kante einer Karte ziehen, um die Höhe anzupassen · Papierkorb zum Entfernen", "empty": "Leer", "clear": "Leeren", "serviceLinksTitle": "Service-Links", - "serviceLinksEmpty": "Noch keine Service-Links.", + "serviceLinksEmpty": "Noch keine Service-Links", "serviceLinksAddLabel": "Bezeichnung", "serviceLinksAddUrl": "URL", "serviceLinksAdd": "Hinzufügen", - "serviceLinksLabelPlaceholder": "Mein Service", + "serviceLinksLabelPlaceholder": "Mein Dienst", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "Geben Sie eine gültige Webadresse ein.", - "serviceLinksAddFailed": "Service-Link konnte nicht hinzugefügt werden", + "serviceLinksInvalidUrl": "Geben Sie eine gültige Webadresse ein", + "serviceLinksAddFailed": "Hinzufügen des Service-Links fehlgeschlagen", "disk": "Datenträger", "viewServerDetails": "Serverdetails anzeigen" }, "sessionLogs": { "title": "Sitzungsprotokolle", - "noLogs": "Noch keine Sitzungsprotokolle.", - "noLogsDesc": "Aktivieren Sie die Sitzungsprotokollierung auf einem Host, um die Aufzeichnung zu starten.", + "noLogs": "Noch keine Sitzungsprotokolle", + "noLogsDesc": "Aktivieren Sie die Sitzungsprotokollierung auf einem Host, um die Aufzeichnung zu starten", "duration": "Dauer", "viewLog": "Protokoll anzeigen", "downloadLog": "Herunterladen", @@ -2423,8 +2557,8 @@ "confirmDeleteDesc": "Diese Aktion kann nicht rückgängig gemacht werden.", "copyContent": "Kopieren", "copied": "Kopiert!", - "loadError": "Sitzungsprotokolle konnten nicht geladen werden", - "deleteError": "Sitzungsprotokoll konnte nicht gelöscht werden", + "loadError": "Fehler beim Laden der Sitzungsprotokolle", + "deleteError": "Fehler beim Löschen des Sitzungsprotokolls", "filterByHost": "Nach Host filtern..." }, "networkGraph": { @@ -2445,9 +2579,9 @@ "moveToGroup": "In Gruppe verschieben", "selectGroup": "Gruppe auswählen...", "addConnection": "Verbindung hinzufügen", - "hostDetails": "Hostdetails", + "hostDetails": "Host-Details", "removeFromGroup": "Aus Gruppe entfernen", - "addHostHere": "Hier Host hinzufügen", + "addHostHere": "Host hier hinzufügen", "editGroup": "Gruppe bearbeiten", "delete": "Löschen", "add": "Hinzufügen", @@ -2458,15 +2592,15 @@ "selectSourcePlaceholder": "Quelle auswählen...", "selectTargetPlaceholder": "Ziel auswählen...", "invalidFile": "Ungültige Datei", - "hostAlreadyExists": "Der Host befindet sich bereits in der Topologie.", - "connectionExists": "Es besteht bereits eine Verbindung.", + "hostAlreadyExists": "Host ist bereits in der Topologie", + "connectionExists": "Verbindung ist bereits vorhanden", "unknown": "Unbekannt", "name": "Name", "ip": "IP", "status": "Status", "failedToAddNode": "Knoten konnte nicht hinzugefügt werden", - "sourceDifferentFromTarget": "Quelle und Ziel müssen unterschiedlich sein.", - "exportJSON": "Als JSON exportieren", + "sourceDifferentFromTarget": "Quelle und Ziel müssen unterschiedlich sein", + "exportJSON": "JSON exportieren", "importJSON": "JSON importieren", "terminal": "Terminal", "fileManager": "Dateimanager", @@ -2474,104 +2608,104 @@ "docker": "Docker", "serverStats": "Host-Metriken", "hostMetrics": "Host-Metriken", - "noNodes": "Noch keine Knoten." + "noNodes": "Noch keine Knoten" }, "docker": { - "notEnabled": "Docker ist für diesen Host nicht aktiviert.", + "notEnabled": "Docker ist für diesen Host nicht aktiviert", "validating": "Docker wird überprüft...", "connecting": "Verbindung wird hergestellt...", "error": "Fehler", "version": "Docker {{version}}", "connectionFailed": "Verbindung zu Docker fehlgeschlagen", "containerStarted": "Container {{name}} gestartet", - "failedToStartContainer": "Container {{name}} konnte nicht gestartet werden.", + "failedToStartContainer": "Starten des Containers {{name}} fehlgeschlagen", "containerStopped": "Container {{name}} gestoppt", - "failedToStopContainer": "Container {{name}} konnte nicht gestoppt werden.", - "containerRestarted": "Container {{name}} wurde neu gestartet", + "failedToStopContainer": "Stoppen des Containers {{name}} fehlgeschlagen", + "containerRestarted": "Container {{name}} neu gestartet", "failedToRestartContainer": "Neustart des Containers {{name}} fehlgeschlagen", "containerPaused": "Container {{name}} pausiert", "containerUnpaused": "Container {{name}} fortgesetzt", - "failedToTogglePauseContainer": "Fehler beim Umschalten des Pausenstatus für Container {{name}}", + "failedToTogglePauseContainer": "Umschalten des Pause-Zustands für Container {{name}} fehlgeschlagen", "containerRemoved": "Container {{name}} entfernt", - "failedToRemoveContainer": "Container {{name}} konnte nicht entfernt werden.", + "failedToRemoveContainer": "Entfernen des Containers {{name}} fehlgeschlagen", "image": "Image", "ports": "Ports", "noPorts": "Keine Ports", - "start": "Starten", - "confirmRemoveContainer": "Möchten Sie den Container '{{name}}' wirklich entfernen? Diese Aktion kann nicht rückgängig gemacht werden.", - "runningContainerWarning": "Warnung: Dieser Container wird derzeit ausgeführt. Durch das Entfernen wird der Container zuerst gestoppt.", + "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. Beim Entfernen wird der Container zuerst gestoppt.", "loadingContainers": "Container werden geladen...", "manager": "Docker-Manager", "autoRefresh": "Automatische Aktualisierung", "timestamps": "Zeitstempel", "lines": "Zeilen", - "filterLogs": "Protokolle filtern...", + "filterLogs": "Logs filtern...", "refresh": "Aktualisieren", "download": "Herunterladen", - "clear": "Leeren", - "logsDownloaded": "Protokolle erfolgreich heruntergeladen", - "last50": "Die letzten 50", - "last100": "Die letzten 100", - "last500": "Die letzten 500", - "last1000": "Die letzten 1000", - "allLogs": "Alle Protokolle", - "noLogsMatching": "Keine Protokolleinträge entsprechen „{{query}}“", - "noLogsAvailable": "Keine Protokolle verfügbar", + "clear": "Löschen", + "logsDownloaded": "Logs erfolgreich heruntergeladen", + "last50": "Letzte 50", + "last100": "Letzte 100", + "last500": "Letzte 500", + "last1000": "Letzte 1000", + "allLogs": "Alle Logs", + "noLogsMatching": "Keine Logs passend zu \"{{query}}\"", + "noLogsAvailable": "Keine Logs verfügbar", "noContainersFound": "Keine Container gefunden", - "noContainersFoundHint": "Auf diesem Host sind keine Docker-Container verfügbar.", + "noContainersFoundHint": "Auf diesem Host sind keine Docker-Container verfügbar", "searchPlaceholder": "Container durchsuchen...", - "allStatuses": "Alle Statuswerte", + "allStatuses": "Alle Status", "stateRunning": "Läuft", "statePaused": "Pausiert", "stateExited": "Beendet", "stateRestarting": "Wird neu gestartet", - "noContainersMatchFilters": "Keine Container entsprechen Ihren Filtern.", + "noContainersMatchFilters": "Keine Container entsprechen Ihren Filtern", "noContainersMatchFiltersHint": "Versuchen Sie, Ihre Such- oder Filterkriterien anzupassen.", - "failedToFetchStats": "Fehler beim Abrufen der Containerstatistiken", + "failedToFetchStats": "Container-Statistiken konnten nicht abgerufen werden", "containerNotRunning": "Container läuft nicht", - "startContainerToViewStats": "Starten Sie den Container, um Statistiken anzuzeigen.", + "startContainerToViewStats": "Starten Sie den Container, um Statistiken anzuzeigen", "loadingStats": "Statistiken werden geladen...", "errorLoadingStats": "Fehler beim Laden der Statistiken", "noStatsAvailable": "Keine Statistiken verfügbar", "cpuUsage": "CPU-Auslastung", "current": "Aktuell", - "memoryUsage": "Arbeitsspeicherauslastung", - "networkIo": "Netzwerk-E/A", - "input": "Eingang", + "memoryUsage": "Speicherauslastung", + "networkIo": "Netzwerk-I/O", + "input": "Eingabe", "output": "Ausgabe", - "blockIo": "Block-E/A", + "blockIo": "Block-I/O", "read": "Lesen", "write": "Schreiben", "pids": "PIDs", - "containerInformation": "Containerinformationen", + "containerInformation": "Container-Informationen", "name": "Name", "id": "ID", - "state": "Zustand", - "containerMustBeRunning": "Der Container muss ausgeführt werden, um auf die Konsole zugreifen zu können.", - "verificationCodePrompt": "Geben Sie den Bestätigungscode ein.", - "totpVerificationFailed": "Die TOTP-Verifizierung ist fehlgeschlagen. Bitte versuchen Sie es erneut.", - "warpgateVerificationFailed": "Die Warpgate-Authentifizierung ist fehlgeschlagen. Bitte versuchen Sie es erneut.", + "state": "Status", + "containerMustBeRunning": "Der Container muss ausgeführt werden, um auf die Konsole zuzugreifen", + "verificationCodePrompt": "Bestätigungscode eingeben", + "totpVerificationFailed": "TOTP-Überprüfung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "warpgateVerificationFailed": "Warpgate-Authentifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.", "connectedTo": "Verbunden mit {{containerName}}", "disconnected": "Getrennt", "consoleError": "Konsolenfehler", "errorMessage": "Fehler: {{message}}", "failedToConnect": "Verbindung zum Container fehlgeschlagen", "console": "Konsole", - "selectShell": "Wählen Sie die Shell aus.", + "selectShell": "Shell auswählen", "bash": "Bash", "sh": "sh", "ash": "ash", "connect": "Verbinden", "disconnect": "Trennen", "notConnected": "Nicht verbunden", - "clickToConnect": "Klicken Sie auf „Verbinden“, um eine Shell-Sitzung zu starten.", - "connectingTo": "Verbindung zu {{containerName}} wird hergestellt ...", + "clickToConnect": "Klicken Sie auf 'Verbinden', um eine Shell-Sitzung zu starten", + "connectingTo": "Verbinden mit {{containerName}}...", "containerNotFound": "Container nicht gefunden", "backToList": "Zurück zur Liste", "logs": "Protokolle", "stats": "Statistiken", "consoleTab": "Konsole", - "startContainerToAccess": "Starten Sie den Container, um auf die Konsole zuzugreifen." + "startContainerToAccess": "Starten Sie den Container, um auf die Konsole zuzugreifen" }, "admin": { "sectionGeneral": "Allgemein", @@ -2580,7 +2714,7 @@ "ssoAddProvider": "Anbieter hinzufügen", "ssoDocsLink": "Dokumentation anzeigen", "ssoProviderDocsLink": "Dokumentation anzeigen", - "ssoNoProviders": "Es sind keine SSO-Anbieter konfiguriert.", + "ssoNoProviders": "Keine SSO-Anbieter konfiguriert.", "ssoProviderName": "Anzeigename", "ssoProviderType": "Anbietertyp", "ssoDeleteProvider": "Anbieter löschen", @@ -2596,60 +2730,71 @@ "ssoEditProvider": "Anbieter bearbeiten", "ldapHost": "LDAP-Host", "ldapPort": "Port", - "ldapUseTls": "TLS (LDAPS) verwenden", + "ldapUseTls": "TLS verwenden (LDAPS)", "ldapBindDn": "Bind-DN", "ldapBindPassword": "Bind-Passwort", "ldapUserSearchBase": "Benutzer-Suchbasis", - "ldapUserSearchFilter": "Benutzer-Suchfilter", - "ldapUsernameAttr": "Benutzername-Attribut", + "ldapUserSearchFilter": "Benutzersuchfilter", + "ldapUsernameAttr": "Benutzernamen-Attribut", "ldapDisplayNameAttr": "Anzeigename-Attribut", - "ldapGroupSearchBase": "Gruppensuchbasis", - "ldapAdminGroup": "Administratorgruppe", - "ldapAllowedUsers": "Zugelassene Benutzer", + "ldapGroupSearchBase": "Gruppen-Suchbasis", + "ldapAdminGroup": "Admin-Gruppe", + "ldapAllowedUsers": "Erlaubte Benutzer", "sectionUsers": "Benutzer", "sectionSessions": "Sitzungen", "sectionRoles": "Rollen", "sectionDatabase": "Datenbank", "sectionApiKeys": "API-Schlüssel", - "sectionAuditLog": "Audit-Protokoll", + "sectionAuditLog": "Audit-Log", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Automatische Ausstellung und Erneuerung eines vertrauenswürdigen SSL-Zertifikats von Let's Encrypt. Erfordert eine öffentliche Domain und Zugriff auf Port 80 oder DNS.", - "sslDocsLink": "SSL-Dokumentation ansehen", + "sslDescription": "Automatisch ein vertrauenswürdiges SSL-Zertifikat von Let's Encrypt ausstellen und erneuern. Erfordert eine öffentliche Domain und Port 80 oder DNS-Zugriff.", + "sslDocsLink": "SSL-Dokumentation anzeigen", "sslDomain": "Domain", "sslDomainPlaceholder": "termix.example.com", "sslDomainDesc": "Der öffentliche Domainname für das Zertifikat.", "sslEmail": "E-Mail", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Kontakt-E-Mail-Adresse für Let's Encrypt-Benachrichtigungen und Konto.", + "sslEmailDesc": "Kontakt-E-Mail für Let's Encrypt-Benachrichtigungen und das Konto.", "sslChallengeType": "Challenge-Typ", - "sslChallengeTypeDesc": "Methode zum Nachweis des Domainbesitzes gegenüber Let’s Encrypt.", - "sslChallengeHttp": "HTTP (Webroot) – benötigt Port 80, der aus dem Internet erreichbar sein muss.", - "sslChallengeDns": "DNS (Cloudflare) – erfordert ein Cloudflare-API-Token", - "sslCloudflareToken": "Cloudflare API-Token", + "sslChallengeTypeDesc": "Wie der Domainbesitz gegenüber Let's Encrypt nachgewiesen wird.", + "sslChallengeHttp": "HTTP (Webroot) – erfordert einen aus dem Internet erreichbaren Port 80", + "sslChallengeDns": "DNS (Cloudflare) – erfordert einen Cloudflare-API-Token", + "sslCloudflareToken": "Cloudflare-API-Token", "sslCloudflareTokenPlaceholder": "Token eingeben...", - "sslCloudflareTokenDesc": "Token mit der Berechtigung Zone:DNS:Edit, das auf Ihre Domain beschränkt ist.", + "sslCloudflareTokenDesc": "Bereichsbezogener Token mit Zone:DNS:Edit-Berechtigung für Ihre Domain.", "sslCertStatus": "Zertifikatsstatus", "sslCertStatusNone": "Kein Zertifikat", "sslCertStatusValid": "Gültig", "sslCertStatusExpiring": "Läuft bald ab", "sslCertStatusExpired": "Abgelaufen", - "sslCertExpiresAt": "Läuft am {{date}} ab", - "sslLastIssued": "Zuletzt ausgestellt: {{date}}", - "sslRequestCert": "Zertifikat ausstellen/erneuern", + "sslCertExpiresAt": "Läuft ab {{date}}", + "sslLastIssued": "Zuletzt ausgestellt {{date}}", + "sslRequestCert": "Zertifikat ausstellen / erneuern", "sslRequestCertLoading": "Zertifikat wird angefordert...", "sslRequestCertSuccess": "Zertifikat erfolgreich ausgestellt und installiert", "sslRequestCertFailed": "Zertifikatsanforderung fehlgeschlagen", "sslSave": "Einstellungen speichern", "sslSaved": "SSL-Einstellungen gespeichert", - "sslSaveFailed": "SSL-Einstellungen konnten nicht gespeichert werden", - "sslRequiresDomain": "Domain und E-Mail-Adresse sind erforderlich.", - "sslInfoNote": "Nach der Ausstellung des Zertifikats aktivieren Sie SSL in Ihren Umgebungsvariablen (ENABLE_SSL=true) und starten Sie Termix neu.", + "sslSaveFailed": "Fehler beim Speichern der SSL-Einstellungen", + "sslRequiresDomain": "Domain und E-Mail sind erforderlich", + "sslInfoNote": "Nachdem ein Zertifikat ausgestellt wurde, aktivieren Sie SSL in Ihren Umgebungsvariablen (ENABLE_SSL=true) und starten Sie Termix neu.", + "sslManualOption": "Handbuch (Zertifikat hochladen)", + "sslManualCert": "Zertifikat (PEM)", + "sslManualCertPlaceholder": "-----BEGINN DES ZERTIFIKATS-----", + "sslManualKey": "Privater Schlüssel (PEM)", + "sslManualKeyPlaceholder": "-----BEGINN DES PRIVATEN SCHLÜSSELS-----", + "sslManualDesc": "Fügen Sie Ihr bestehendes Zertifikat und Ihren privaten Schlüssel ein, gegebenenfalls einschließlich der vollständigen Schlüsselkette, falls von Ihrer Zertifizierungsstelle gefordert.", + "sslManualUpload": "Zertifikat hochladen und installieren", + "sslManualUploadLoading": "Zertifikat wird hochgeladen...", + "sslManualUploadSuccess": "Zertifikat erfolgreich hochgeladen und installiert", + "sslManualUploadFailed": "Zertifikat-Upload fehlgeschlagen", + "sslManualRequiresFields": "Zertifikat und privater Schlüssel erforderlich", "auditLogTotal": "{{total}} Einträge insgesamt", - "auditLogEmpty": "Keine Einträge im Audit-Protokoll gefunden", + "auditLogEmpty": "Keine Audit-Log-Einträge gefunden", "auditLogSuccess": "Erfolg", "auditLogFailed": "Fehlgeschlagen", - "auditLogClearFilters": "Filter zurücksetzen", - "auditLogPage": "Seite {{page}} von {{totalPages}} ({{total}} insgesamt)", + "auditLogClearFilters": "Filter löschen", + "auditLogPage": "Seite {{page}} von {{totalPages}} ({{total}} gesamt)", "auditLogIp": "IP", "auditLogResourceId": "Ressourcen-ID", "auditLogFilterUser": "Benutzer", @@ -2659,53 +2804,59 @@ "auditLogFilterFrom": "Von", "auditLogFilterTo": "Bis", "auditLogFilterAll": "Alle", - "allowRegistration": "Benutzerregistrierung zulassen", - "allowRegistrationDesc": "Neuen Benutzern die Selbstregistrierung mit Benutzername und Passwort erlauben", - "allowPasswordLogin": "Passwortanmeldung zulassen", - "allowPasswordLoginDesc": "Anmeldung mit Benutzername/Passwort", - "oidcAutoProvision": "OIDC-Auto-Provisionierung", - "oidcAutoProvisionDesc": "Konten für OIDC-/SSO-Benutzer bei der ersten Anmeldung automatisch erstellen (unabhängig von der Registrierungsoption)", - "oidcSilentLoginDefault": "Standardmäßig automatische OIDC-Anmeldung im Hintergrund", - "oidcSilentLoginDefaultDesc": "Bei jedem Besuch automatisch zur OIDC-Anmeldung weiterleiten und das Anmeldeformular vollständig überspringen", - "allowPasswordReset": "Passwort zurücksetzen erlauben", - "allowPasswordResetDesc": "Code zum Zurücksetzen über Docker-Logs", - "commandHistoryEnabled": "Befehlshistorie", - "commandHistoryEnabledDesc": "Befehlsverlaufsaufzeichnung zulassen. Wenn diese Option deaktiviert ist, wird der Verlauf unabhängig von den hostspezifischen Einstellungen nicht gespeichert.", - "updateCommandHistoryFailed": "Fehler beim Aktualisieren der Befehlsverlaufseinstellung", - "sessionTimeout": "Sitzungstimeout", - "hours": "Std.", - "sessionTimeoutRange": "Mindestens 1 Stunde · Maximal 720 Stunden", - "monitoringDefaults": "Standardwerte für die Überwachung", + "allowRegistration": "Benutzerregistrierung erlauben", + "allowRegistrationDesc": "Neuen Benutzern die Selbstregistrierung mit Benutzernamen und Passwort erlauben", + "allowPasswordLogin": "Passwort-Login erlauben", + "allowPasswordLoginDesc": "Login mit Benutzernamen/Passwort", + "oidcAutoProvision": "OIDC-Automatische Bereitstellung", + "oidcAutoProvisionDesc": "Konten für OIDC/SSO-Benutzer bei der ersten Anmeldung automatisch erstellen (unabhängig vom Registrierungs-Schalter)", + "oidcSilentLoginDefault": "Stiller OIDC-Login standardmäßig", + "oidcSilentLoginDefaultDesc": "Bei jedem Besuch automatisch zum OIDC-Login weiterleiten, das Anmeldeformular vollständig überspringen", + "allowPasswordReset": "Passwort-Zurücksetzen erlauben", + "allowPasswordResetDesc": "Reset-Code über Docker-Logs", + "commandHistoryEnabled": "Befehlsverlauf", + "commandHistoryEnabledDesc": "Aufzeichnung des Befehlsverlaufs erlauben. Wenn deaktiviert, wird der Verlauf unabhängig von Host-Einstellungen nicht gespeichert.", + "updateCommandHistoryFailed": "Fehler beim Aktualisieren der Einstellung für den Befehlsverlauf", + "analyticsEnabled": "Anonyme Nutzungsstatistiken teilen", + "analyticsEnabledDesc": "Sendet täglich anonyme Zählungen von Nutzern, Hosts und Funktionsnutzung, um Termix zu verbessern. Es werden niemals personenbezogene Daten oder Verbindungsdaten übermittelt.", + "updateAnalyticsFailed": "Die Aktualisierung der Analyseeinstellungen ist fehlgeschlagen.", + "sessionSharingGloballyEnabled": "Sitzungsfreigabe zulassen", + "sessionSharingGloballyEnabledDesc": "Ermöglicht die instanzweite Freigabe von Live-Terminal-, RDP-, VNC- und Telnet-Sitzungen. Überschreibt alle Freigabeeinstellungen pro Host, wenn diese deaktiviert sind.", + "updateSessionSharingFailed": "Fehler beim Aktualisieren der Sitzungsfreigabeeinstellung", + "sessionTimeout": "Sitzungszeitlimit", + "hours": "Stunden", + "sessionTimeoutRange": "Min. 1 Std. · Max. 720 Std.", + "monitoringDefaults": "Überwachungsvoreinstellungen", "statusCheck": "Statusprüfung", "metrics": "Metriken", "sec": "Sek.", - "logLevel": "Protokollierungsstufe", + "logLevel": "Log-Level", "enableGuacamole": "Guacamole aktivieren", - "enableGuacamoleDesc": "RDP/VNC-Fernzugriff", + "enableGuacamoleDesc": "RDP/VNC-Remotedesktop", "enableGuacamoleDocsLink": "Dokumentation anzeigen", "guacdUrl": "guacd-URL", - "tailscaleApiKey": "Tailscale API-Schlüssel", - "tailscaleApiKeyDescription": "Wird zur Geräteerkennung im Host-Editor verwendet. Generieren Sie einen Schlüssel unter tailscale.com/admin/settings/keys.", + "tailscaleApiKey": "Tailscale-API-Schlüssel", + "tailscaleApiKeyDescription": "Wird zur Geräteerkennung im Host-Editor verwendet. Erstellen Sie einen Schlüssel unter tailscale.com/admin/settings/keys.", "tailscaleApiKeyDocsLink": "Dokumentation anzeigen", - "oidcDescription": "Konfigurieren Sie OpenID Connect für SSO. Felder mit * sind Pflichtfelder.", + "oidcDescription": "OpenID Connect für SSO konfigurieren. Mit * gekennzeichnete Felder sind erforderlich.", "oidcDocsLink": "Dokumentation anzeigen", "oidcClientId": "Client-ID", "oidcClientSecret": "Client-Secret", "oidcAuthUrl": "Autorisierungs-URL", - "oidcIssuerUrl": "Aussteller-URL", + "oidcIssuerUrl": "Issuer-URL", "oidcTokenUrl": "Token-URL", - "oidcUserIdentifier": "Benutzeridentifikationspfad", - "oidcDisplayName": "Pfad zum Anzeigenamen", + "oidcUserIdentifier": "Benutzerkennungspfad", + "oidcDisplayName": "Anzeigename-Pfad", "oidcScopes": "Scopes", - "oidcUserinfoUrl": "Benutzerinfo-URL überschreiben", + "oidcUserinfoUrl": "Userinfo-URL überschreiben", "oidcAllowedUsers": "Zugelassene Benutzer", - "oidcAllowedUsersDesc": "Eine E-Mail-Adresse pro Zeile. Lassen Sie das Feld leer, um alle Benutzer zuzulassen.", - "oidcAdminGroup": "Administratorgruppe", - "oidcAdminGroupDesc": "Benutzer dieser Gruppe erhalten Administratorrechte. Lassen Sie das Feld leer, um die Gruppensynchronisierung zu deaktivieren.", + "oidcAllowedUsersDesc": "Eine E-Mail pro Zeile. Leer lassen, um alle zuzulassen.", + "oidcAdminGroup": "Admin-Gruppe", + "oidcAdminGroupDesc": "Benutzer in dieser Gruppe erhalten Admin-Berechtigungen. Leer lassen, um die Gruppensynchronisierung zu deaktivieren.", "oidcGroupClaim": "Gruppen-Claim", - "oidcGroupClaimDesc": "Optional. Der Claim-Pfad, der die Gruppen des Benutzers enthält. Standardmäßig werden nacheinander groups, roles und group verwendet. Nutzen Sie diese Option für Anbieter mit einem benutzerdefinierten Claim (z. B. Zitadel).", + "oidcGroupClaimDesc": "Optional. Der Claim-Pfad, der die Gruppen des Benutzers enthält. Standardmäßig groups, roles, dann group. Verwenden Sie dies für Anbieter mit einem benutzerdefinierten Claim (z. B. Zitadel).", "oidcCaCert": "Benutzerdefiniertes CA-Zertifikat", - "oidcCaCertDesc": "Optional. PEM-kodiertes CA-Zertifikat für OIDC-Anbieter mit einer privaten oder selbstsignierten CA. Lassen Sie das Feld leer, um den Vertrauensspeicher des Systems zu verwenden.", + "oidcCaCertDesc": "Optional. PEM-kodiertes CA-Zertifikat für OIDC-Anbieter, die eine private oder selbstsignierte Zertifizierungsstelle verwenden. Leer lassen, um den System-Vertrauensspeicher zu verwenden.", "removeOidc": "Entfernen", "usersCount": "{{count}} Benutzer", "createUser": "Erstellen", @@ -2717,26 +2868,26 @@ "createRole": "Erstellen", "creating": "Wird erstellt...", "exportDatabase": "Datenbank exportieren", - "exportDatabaseDesc": "Laden Sie eine Sicherung aller Hosts, Zugangsdaten und Einstellungen herunter.", + "exportDatabaseDesc": "Sicherung aller Hosts, Anmeldeinformationen und Einstellungen herunterladen", "export": "Exportieren", - "exporting": "Wird exportiert...", + "exporting": "Exportiere...", "importDatabase": "Datenbank importieren", - "importDatabaseDesc": "Wiederherstellung aus einer .sqlite-Sicherungsdatei", + "importDatabaseDesc": "Aus einer .sqlite-Sicherungsdatei wiederherstellen", "importDatabaseSelected": "Ausgewählt: {{name}}", "selectFile": "Datei auswählen", "changeFile": "Ändern", "import": "Importieren", - "importing": "Wird importiert...", + "importing": "Importiere...", "apiKeysCount": "{{count}} Schlüssel", - "apiKeysDocsLink": "Dokumentation anzeigen", + "apiKeysDocsLink": "Dokumentation ansehen", "newApiKey": "Neuer API-Schlüssel", - "apiKeyCreatedWarning": "Schlüssel erstellt – kopieren Sie ihn jetzt; er wird nicht erneut angezeigt.", + "apiKeyCreatedWarning": "Schlüssel erstellt – jetzt kopieren, er wird nicht erneut angezeigt.", "apiKeyName": "Name", "apiKeyUser": "Benutzer", - "apiKeySelectUser": "Wählen Sie einen Benutzer aus...", - "apiKeyExpiresAt": "Läuft ab am", + "apiKeySelectUser": "Benutzer auswählen...", + "apiKeyExpiresAt": "Ablaufdatum", "createKey": "Schlüssel erstellen", - "apiKeyNoExpiry": "Kein Verfallsdatum", + "apiKeyNoExpiry": "Kein Ablauf", "revokedBadge": "WIDERRUFEN", "authTypeDual": "Duale Authentifizierung", "authTypeOidc": "OIDC", @@ -2744,213 +2895,213 @@ "adminStatusAdministrator": "Administrator", "adminStatusRegularUser": "Normaler Benutzer", "adminBadge": "ADMIN", - "systemBadge": "SYSTEM", - "customBadge": "INDIVIDUELL", + "systemBadge": "SYS", + "customBadge": "CUSTOM", "youBadge": "DU", "sessionsActive": "{{count}} aktiv", "sessionActive": "Aktiv: {{time}}", - "sessionExpires": "Läuft ab: {{time}}", + "sessionExpires": "Ablauf: {{time}}", "revokeAll": "Alle", - "revokeAllSessionsSuccess": "Alle Sitzungen für Benutzer widerrufen", - "revokeAllSessionsFailed": "Sitzungen konnten nicht widerrufen werden", - "revokeSessionFailed": "Sitzung konnte nicht widerrufen werden", + "revokeAllSessionsSuccess": "Alle Sitzungen des Benutzers wurden widerrufen.", + "revokeAllSessionsFailed": "Widerrufen der Sitzungen fehlgeschlagen.", + "revokeSessionFailed": "Widerrufen der Sitzung fehlgeschlagen.", "addRole": "Rolle hinzufügen", - "noCustomRoles": "Es sind keine benutzerdefinierten Rollen definiert.", - "removeRoleFailed": "Rolle konnte nicht entfernt werden", - "assignRoleFailed": "Rolle konnte nicht zugewiesen werden", - "deleteRoleFailed": "Rolle konnte nicht gelöscht werden", + "noCustomRoles": "Keine benutzerdefinierten Rollen definiert", + "removeRoleFailed": "Entfernen der Rolle fehlgeschlagen", + "assignRoleFailed": "Zuweisen der Rolle fehlgeschlagen", + "deleteRoleFailed": "Löschen der Rolle fehlgeschlagen", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Vollständiger Zugriff auf alle Administratoreinstellungen", + "userAdminAccessDesc": "Voller Zugriff auf alle Administratoreinstellungen", "userRoles": "Rollen", "revokeAllUserSessions": "Alle Sitzungen widerrufen", "revokeAllUserSessionsDesc": "Erneute Anmeldung auf allen Geräten erzwingen", "revoke": "Widerrufen", "deleteUserWarning": "Das Löschen dieses Benutzers ist endgültig.", - "deleteUser": "„{{username}}“ löschen", - "deleting": "Wird gelöscht...", - "deleteUserFailed": "Benutzer konnte nicht gelöscht werden", - "deleteUserSuccess": "Benutzer \"{{username}}\" gelöscht", - "deleteRoleSuccess": "Rolle \"{{name}}\" gelöscht", - "revokeKeySuccess": "Schlüssel \"{{name}}\" widerrufen", - "revokeKeyFailed": "Schlüsselwiderruf fehlgeschlagen", + "deleteUser": "{{username}} löschen", + "deleting": "Löschen...", + "deleteUserFailed": "Löschen des Benutzers fehlgeschlagen", + "deleteUserSuccess": "Benutzer „{{username}}“ gelöscht", + "deleteRoleSuccess": "Rolle „{{name}}“ gelöscht", + "revokeKeySuccess": "Schlüssel „{{name}}“ widerrufen", + "revokeKeyFailed": "Widerrufen des Schlüssels fehlgeschlagen", "copiedToClipboard": "In die Zwischenablage kopiert", - "done": "Erledigt", + "done": "Fertig", "createUserTitle": "Benutzer erstellen", - "createUserDesc": "Erstellen Sie ein neues lokales Konto.", + "createUserDesc": "Neues lokales Konto erstellen.", "createUserUsername": "Benutzername", "createUserPassword": "Passwort", "createUserPasswordHint": "Mindestens 6 Zeichen.", - "createUserEnterUsername": "Benutzernamen eingeben", + "createUserEnterUsername": "Benutzername eingeben", "createUserEnterPassword": "Passwort eingeben", "createUserSubmit": "Benutzer erstellen", "editUserTitle": "Benutzer verwalten: {{username}}", - "editUserDesc": "Bearbeiten Sie Rollen, Administratorstatus, Sitzungen und Kontoeinstellungen.", + "editUserDesc": "Rollen, Administratorstatus, Sitzungen und Kontoeinstellungen bearbeiten.", "editUserUsername": "Benutzername", "editUserAuthType": "Authentifizierungstyp", - "editUserAdminStatus": "Administratorstatus", + "editUserAdminStatus": "Admin-Status", "editUserUserId": "Benutzer-ID", "linkAccountTitle": "Konten verknüpfen", - "linkAccountDesc": "OIDC-Konto {{username}} mit einem vorhandenen lokalen Konto zusammenführen.", - "linkAccountDescLocal": "Verknüpfen Sie das lokale Konto {{username}} mit einem vorhandenen Konto mit ausschließlicher OIDC-Anmeldung.", + "linkAccountDesc": "Das OIDC-Konto {{username}} mit einem vorhandenen lokalen Konto zusammenführen.", + "linkAccountDescLocal": "Ein lokales Konto {{username}} mit einem vorhandenen Nur-OIDC-Konto verknüpfen.", "linkAccountWarningTitle": "Dies wird:", - "linkAccountEffect1": "Das reine OIDC-Konto löschen", + "linkAccountEffect1": "Das Nur-OIDC-Konto löschen", "linkAccountEffect2": "OIDC-Anmeldung zum Zielkonto hinzufügen", - "linkAccountEffect3": "Sowohl OIDC- als auch Passwortanmeldung zulassen", + "linkAccountEffect3": "Sowohl OIDC- als auch Passwort-Anmeldung erlauben", "linkAccountTargetUsername": "Benutzername des lokalen Kontos", - "linkAccountTargetPlaceholder": "Geben Sie den Benutzernamen des lokalen Kontos ein, das verknüpft werden soll.", - "linkAccountOidcUsername": "OIDC-Kontobenutzername", - "linkAccountOidcPlaceholder": "Geben Sie den Benutzernamen des Kontos mit ausschließlicher OIDC-Anmeldung ein, das zusammengeführt werden soll.", - "linkAccountOidcNotFound": "Kein reines OIDC-Konto mit diesem Benutzernamen gefunden", + "linkAccountTargetPlaceholder": "Geben Sie den Benutzernamen des lokalen Kontos ein, mit dem verknüpft werden soll", + "linkAccountOidcUsername": "Benutzername des OIDC-Kontos", + "linkAccountOidcPlaceholder": "Geben Sie den Benutzernamen des Nur-OIDC-Kontos ein, das zusammengeführt werden soll", + "linkAccountOidcNotFound": "Kein Nur-OIDC-Konto mit diesem Benutzernamen gefunden", "linkAccounts": "Konten verknüpfen", "linkAccountSuccess": "Konten erfolgreich verknüpft", - "linkAccountFailed": "Konten konnten nicht verknüpft werden", - "linkAccountInProgress": "Wird verknüpft...", - "unlinkAccountTitle": "OIDC entkoppeln", - "unlinkAccountDesc": "Entfernen Sie die OIDC-Authentifizierung von {{username}}. Der Benutzer kann sich danach nur noch mit seinem Passwort anmelden.", - "unlinkAccountWarning": "Dadurch wird die OIDC-Anmeldung für dieses Konto entfernt. Der Benutzer muss ein Passwort festgelegt haben, um sich weiterhin anmelden zu können.", - "unlinkAccount": "OIDC entkoppeln", + "linkAccountFailed": "Fehler beim Verknüpfen der Konten", + "linkAccountInProgress": "Verknüpfen...", + "unlinkAccountTitle": "OIDC-Verknüpfung aufheben", + "unlinkAccountDesc": "OIDC-Authentifizierung von {{username}} entfernen. Sie können sich dann nur noch mit ihrem Passwort anmelden.", + "unlinkAccountWarning": "Dadurch wird die OIDC-Anmeldung von diesem Konto entfernt. Der Benutzer muss über ein Passwort verfügen, um sich weiterhin anmelden zu können.", + "unlinkAccount": "OIDC-Verknüpfung aufheben", "unlinkAccountInProgress": "Verknüpfung wird aufgehoben...", - "unlinkAccountSuccess": "OIDC erfolgreich getrennt", - "unlinkAccountFailed": "OIDC konnte nicht getrennt werden", - "saving": "Wird gespeichert...", - "updateRegistrationFailed": "Registrierungseinstellungen konnten nicht aktualisiert werden", - "updatePasswordLoginFailed": "Fehler beim Aktualisieren der Passwort-Anmeldeeinstellungen", - "cannotDisablePasswordLoginWithTotp": "Die Passwortanmeldung kann nicht deaktiviert werden, solange die Zwei-Faktor-Authentifizierung (2FA) für einen oder mehrere Benutzer aktiviert ist. Deaktivieren Sie daher zuerst die 2FA.", - "updateOidcAutoProvisionFailed": "Die Aktualisierung der OIDC-Auto-Provisionierungseinstellung ist fehlgeschlagen.", - "updateOidcSilentLoginDefaultFailed": "Fehler beim Aktualisieren der Einstellung für die stille OIDC-Anmeldung", - "updatePasswordResetFailed": "Fehler beim Aktualisieren der Einstellungen zum Zurücksetzen des Passworts", - "sessionTimeoutRange2": "Die Sitzungsdauer muss zwischen 1 und 720 Stunden liegen.", + "unlinkAccountSuccess": "OIDC-Verknüpfung erfolgreich aufgehoben", + "unlinkAccountFailed": "Fehler beim Aufheben der OIDC-Verknüpfung", + "saving": "Speichern...", + "updateRegistrationFailed": "Fehler beim Aktualisieren der Registrierungseinstellung", + "updatePasswordLoginFailed": "Fehler beim Aktualisieren der Passwort-Anmeldeeinstellung", + "cannotDisablePasswordLoginWithTotp": "Passwort-Anmeldung kann nicht deaktiviert werden, solange 2FA für einen oder mehrere Benutzer aktiviert ist. Deaktivieren Sie zuerst 2FA.", + "updateOidcAutoProvisionFailed": "Fehler beim Aktualisieren der OIDC-Autoprovisionierungseinstellung", + "updateOidcSilentLoginDefaultFailed": "Fehler beim Aktualisieren der Einstellung für stille OIDC-Anmeldung", + "updatePasswordResetFailed": "Fehler beim Aktualisieren der Passwort-Zurücksetzen-Einstellung", + "sessionTimeoutRange2": "Sitzungszeitüberschreitung muss zwischen 1 und 720 Stunden liegen", "sessionTimeoutSaved": "Sitzungstimeout gespeichert", - "sessionTimeoutSaveFailed": "Sitzungstimeout konnte nicht gespeichert werden", + "sessionTimeoutSaveFailed": "Fehler beim Speichern des Sitzungstimeouts", "monitoringIntervalInvalid": "Ungültige Intervallwerte", "monitoringSaved": "Überwachungseinstellungen gespeichert", - "monitoringSaveFailed": "Die Überwachungseinstellungen konnten nicht gespeichert werden.", - "metricsHistoryRetention": "Aufbewahrungsdauer des Metrikverlaufs", + "monitoringSaveFailed": "Fehler beim Speichern der Überwachungseinstellungen", + "metricsHistoryRetention": "Aufbewahrungsdauer der Metriken", "metricsHistoryRetentionRange": "1 bis 90 Tage", "days": "Tage", "guacamoleSaved": "Guacamole-Einstellungen gespeichert", - "guacamoleSaveFailed": "Guacamole-Einstellungen konnten nicht gespeichert werden", - "guacamoleUpdateFailed": "Guacamole-Einstellung konnte nicht aktualisiert werden", + "guacamoleSaveFailed": "Fehler beim Speichern der Guacamole-Einstellungen", + "guacamoleUpdateFailed": "Fehler beim Aktualisieren der Guacamole-Einstellung", "tailscaleSettingsSaved": "Tailscale-Einstellungen gespeichert", - "tailscaleSettingsSaveFailed": "Tailscale-Einstellungen konnten nicht gespeichert werden", - "logLevelUpdateFailed": "Protokollierungsstufe konnte nicht aktualisiert werden", + "tailscaleSettingsSaveFailed": "Fehler beim Speichern der Tailscale-Einstellungen", + "logLevelUpdateFailed": "Fehler beim Aktualisieren des Log-Levels", "oidcSaved": "OIDC-Konfiguration gespeichert", - "oidcSaveFailed": "OIDC-Konfiguration konnte nicht gespeichert werden", + "oidcSaveFailed": "Fehler beim Speichern der OIDC-Konfiguration", "oidcRemoved": "OIDC-Konfiguration entfernt", - "oidcRemoveFailed": "OIDC-Konfiguration konnte nicht entfernt werden", - "createUserRequired": "Benutzername und Passwort sind erforderlich.", - "createUserPasswordTooShort": "Das Passwort muss mindestens 6 Zeichen lang sein.", + "oidcRemoveFailed": "Fehler beim Entfernen der OIDC-Konfiguration", + "createUserRequired": "Benutzername und Passwort sind erforderlich", + "createUserPasswordTooShort": "Das Passwort muss mindestens 6 Zeichen lang sein", "createUserSuccess": "Benutzer \"{{username}}\" erstellt", - "createUserFailed": "Benutzer konnte nicht erstellt werden", - "updateAdminStatusFailed": "Fehler beim Aktualisieren des Administratorstatus", + "createUserFailed": "Fehler beim Erstellen des Benutzers", + "updateAdminStatusFailed": "Fehler beim Aktualisieren des Admin-Status", "allSessionsRevoked": "Alle Sitzungen widerrufen", - "revokeSessionsFailed": "Sitzungen konnten nicht widerrufen werden", + "revokeSessionsFailed": "Fehler beim Widerrufen der Sitzungen", "manageUserData": "Benutzerdaten verwalten", - "backToUsers": "Zurück zu den Benutzern", + "backToUsers": "Zurück zu Benutzern", "manageTabAccount": "Konto", - "manageTabHosts": "Gastgeber", - "manageTabCredentials": "Anmeldeinformationen", - "manageTabSnippets": "Ausschnitte", + "manageTabHosts": "Hosts", + "manageTabCredentials": "Anmeldedaten", + "manageTabSnippets": "Snippets", "manageTabSessions": "Sitzungen", "manageTabDanger": "Gefahr", - "manageEditorBack": "Zurück zu den Daten von {{username}}", + "manageEditorBack": "Zurück zu {{username}}s Daten", "dataLockedBadge": "GESPERRT", - "dataLockedNotice": "Die Daten von {{username}}bleiben gesperrt, bis sie sich das nächste Mal anmelden. Ihre Hosts, Anmeldeinformationen und Snippets können bis dahin weder angezeigt noch bearbeitet werden.", + "dataLockedNotice": "{{username}}s Daten bleiben gesperrt, bis die nächste Anmeldung erfolgt. Hosts, Zugangsdaten und Snippets können bis dahin nicht eingesehen oder bearbeitet werden.", "resetPasswordTitle": "Passwort zurücksetzen", "resetPasswordOidcOnly": "Dieser Benutzer meldet sich über einen externen Anbieter an und hat kein Passwort.", "resetPasswordPlaceholder": "Neues Passwort", "resetPasswordBtn": "Zurücksetzen", - "resetPasswordWorking": "Zurücksetzen...", - "resetPasswordSuccess": "Passwort zurücksetzen", + "resetPasswordWorking": "Wird zurückgesetzt...", + "resetPasswordSuccess": "Passwort zurückgesetzt", "resetPasswordSuccessWiped": "Passwort zurückgesetzt. Die verschlüsselten Daten des Benutzers wurden gelöscht.", - "resetPasswordFailed": "Passwort konnte nicht zurückgesetzt werden", - "resetPasswordConfirmWipe": "{{username}} hat sich seit dem Verschlüsselungs-Upgrade nicht mehr angemeldet, daher können seine Daten nicht wiederhergestellt werden. Durch das Zurücksetzen werden seine Hosts, Anmeldeinformationen und Snippets gelöscht. Fortfahren?", + "resetPasswordFailed": "Passwort zurücksetzen fehlgeschlagen", + "resetPasswordConfirmWipe": "{{username}} hat sich seit dem Verschlüsselungs-Upgrade nicht angemeldet, daher können die Daten nicht wiederhergestellt werden. Durch das Zurücksetzen werden die Hosts, Zugangsdaten und Snippets gelöscht. Fortfahren?", "totpSectionTitle": "Zwei-Faktor-Authentifizierung", - "totpStatusEnabled": "TOTP ist für diesen Benutzer aktiviert.", - "totpStatusDisabled": "TOTP ist für diesen Benutzer nicht aktiviert.", + "totpStatusEnabled": "TOTP ist für diesen Benutzer aktiviert", + "totpStatusDisabled": "TOTP ist für diesen Benutzer nicht aktiviert", "disableTotp": "Deaktivieren", - "disableTotpConfirm": "Die Zwei-Faktor-Authentifizierung für {{username}}deaktivieren? Sie können sich dann nur mit ihrem Passwort anmelden.", + "disableTotpConfirm": "Zwei-Faktor-Authentifizierung für {{username}} deaktivieren? Die Anmeldung ist dann nur mit dem Passwort möglich.", "totpDisabledSuccess": "TOTP deaktiviert", - "totpDisableFailed": "TOTP konnte nicht deaktiviert werden.", + "totpDisableFailed": "TOTP deaktivieren fehlgeschlagen", "manageApiKeys": "API-Schlüssel", "apiKeyNamePlaceholder": "Schlüsselname", - "apiKeyCopyNotice": "Kopieren Sie diesen Schlüssel jetzt, er wird nicht wieder angezeigt.", + "apiKeyCopyNotice": "Kopieren Sie diesen Schlüssel jetzt. Er wird nicht erneut angezeigt.", "noApiKeysForUser": "Keine API-Schlüssel", - "apiKeyDeleteFailed": "API-Schlüssel konnte nicht gelöscht werden", + "apiKeyDeleteFailed": "API-Schlüssel löschen fehlgeschlagen", "exportUserData": "Datenexport", - "exportUserDataDesc": "Laden Sie die Hosts, Anmeldeinformationen und Dateimanagerdaten dieses Benutzers als JSON herunter. Geheimnisse werden entschlüsselt.", + "exportUserDataDesc": "Laden Sie die Hosts, Zugangsdaten und Dateimanager-Daten dieses Benutzers als JSON herunter. Geheimnisse werden entschlüsselt.", "exportUserDataSuccess": "Benutzerdaten exportiert", - "exportUserDataFailed": "Fehler beim Exportieren der Benutzerdaten", + "exportUserDataFailed": "Benutzerdaten exportieren fehlgeschlagen", "hostsCount": "{{count}} Hosts", "addHostForUser": "Host hinzufügen", - "noHostsForUser": "Dieser Benutzer hat keine Hosts.", + "noHostsForUser": "Dieser Benutzer hat keine Hosts", "connectToHost": "Verbinden", - "deleteHostConfirm": "Host \"{{name}}\" zu {{username}} löschen?", + "deleteHostConfirm": "Host \"{{name}}\" von {{username}} löschen?", "hostDeletedSuccess": "Host gelöscht", - "hostDeleteFailed": "Host konnte nicht gelöscht werden", - "credentialsCount": "{{count}} Anmeldeinformationen", - "addCredentialForUser": "Anmeldeinformationen hinzufügen", - "noCredentialsForUser": "Dieser Benutzer besitzt keine Anmeldeinformationen.", - "deleteCredentialConfirm": "Anmeldeinformationen \"{{name}}\" zu {{username}} löschen?", - "credentialDeletedSuccess": "Anmeldeinformationen gelöscht", - "credentialDeleteFailed": "Anmeldeinformationen konnten nicht gelöscht werden", - "snippetsCount": "{{count}} Ausschnitte", - "addSnippetForUser": "Code-Snippet hinzufügen", - "noSnippetsForUser": "Dieser Benutzer hat keine Snippets.", - "snippetRequiredFields": "Name und Inhalt des Snippets sind erforderlich.", + "hostDeleteFailed": "Host löschen fehlgeschlagen", + "credentialsCount": "{{count}} Zugangsdaten", + "addCredentialForUser": "Zugangsdaten hinzufügen", + "noCredentialsForUser": "Dieser Benutzer hat keine Anmeldeinformationen", + "deleteCredentialConfirm": "Anmeldeinformation \"{{name}}\" von {{username}} löschen?", + "credentialDeletedSuccess": "Anmeldeinformation gelöscht", + "credentialDeleteFailed": "Löschen der Anmeldeinformation fehlgeschlagen", + "snippetsCount": "{{count}} Snippets", + "addSnippetForUser": "Snippet hinzufügen", + "noSnippetsForUser": "Dieser Benutzer hat keine Snippets", + "snippetRequiredFields": "Name und Inhalt des Snippets sind erforderlich", "snippetNamePlaceholder": "Snippet-Name", "snippetContentPlaceholder": "Befehlsinhalt", "snippetFolderPlaceholder": "Ordner (optional)", - "snippetSaved": "Ausschnitt gespeichert", - "snippetSaveFailed": "Fehler beim Speichern des Ausschnitts", - "deleteSnippetConfirm": "Ausschnitt \"{{name}}\" zu {{username}} löschen?", - "snippetDeletedSuccess": "Ausschnitt gelöscht", - "snippetDeleteFailed": "Fehler beim Löschen des Ausschnitts", + "snippetSaved": "Snippet gespeichert", + "snippetSaveFailed": "Fehler beim Speichern des Snippets", + "deleteSnippetConfirm": "Snippet \"{{name}}\" von {{username}} löschen?", + "snippetDeletedSuccess": "Snippet gelöscht", + "snippetDeleteFailed": "Fehler beim Löschen des Snippets", "noSessionsForUser": "Keine aktiven Sitzungen", - "deleteUserDangerDesc": "{{username}} und alle zugehörigen Daten (Hosts, Anmeldeinformationen, Snippets, Verlauf) werden endgültig gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden.", + "deleteUserDangerDesc": "{{username}} und alle zugehörigen Daten (Hosts, Anmeldeinformationen, Snippets, Verlauf) dauerhaft löschen. Dies kann nicht rückgängig gemacht werden.", "deleteUserConfirm": "{{username}} und alle zugehörigen Daten dauerhaft löschen?", - "deleteUserAdminBlocked": "Entfernen Sie den Administratorstatus, bevor Sie diesen Benutzer löschen.", - "createRoleRequired": "Name und Anzeigename sind erforderlich.", + "deleteUserAdminBlocked": "Administrator-Status entfernen, bevor dieser Benutzer gelöscht wird.", + "createRoleRequired": "Name und Anzeigename sind erforderlich", "createRoleSuccess": "Rolle \"{{name}}\" erstellt", - "createRoleFailed": "Rolle konnte nicht erstellt werden", - "apiKeyNameRequired": "Ein Schlüsselname ist erforderlich.", + "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", + "apiKeyCreateFailed": "Fehler beim Erstellen des API-Schlüssels", "exportSuccess": "Datenbank erfolgreich exportiert", "exportFailed": "Datenbankexport fehlgeschlagen", - "importSelectFile": "Bitte wählen Sie zuerst eine Datei aus.", + "importSelectFile": "Bitte wählen Sie zuerst eine Datei aus", "importCompleted": "Import abgeschlossen: {{total}} Elemente importiert, {{skipped}} übersprungen", "importFailed": "Import fehlgeschlagen: {{error}}", "importError": "Datenbankimport fehlgeschlagen", - "sectionHostDefaults": "Host-Standardeinstellungen", - "hostDefaultsDesc": "Die Einstellungen werden beim Erstellen eines neuen Hosts automatisch angewendet. Einzelne Hosts können diese jedoch weiterhin überschreiben.", + "sectionHostDefaults": "Host-Standardwerte", + "hostDefaultsDesc": "Einstellungen, die beim Erstellen eines neuen Hosts automatisch angewendet werden. Einzelne Hosts können diese überschreiben.", "hostDefaultsSocks5": "SOCKS5-Proxy", "hostDefaultsUseSocks5": "SOCKS5-Proxy aktivieren", - "hostDefaultsUseSocks5Desc": "SOCKS5-Proxy bei allen neuen Hosts vorbelegen", + "hostDefaultsUseSocks5Desc": "SOCKS5-Proxy für alle neuen Hosts vorbelegen", "hostDefaultsSocks5Host": "Proxy-Host / Port", "hostDefaultsSocks5Username": "Proxy-Benutzername", "hostDefaultsSocks5Password": "Proxy-Passwort", "hostDefaultsMetrics": "Host-Metriken", "hostDefaultsMetricsEnabled": "Metriken aktivieren", - "hostDefaultsMetricsEnabledDesc": "CPU-, Arbeitsspeicher- und andere Statistiken bei neuen Hosts standardmäßig erfassen", - "hostDefaultsStatusCheckEnabled": "Statusprüfung aktivieren", - "hostDefaultsStatusCheckEnabledDesc": "Online-/Offline-Status bei neuen Hosts standardmäßig abfragen", + "hostDefaultsMetricsEnabledDesc": "CPU, Arbeitsspeicher und andere Statistiken standardmäßig auf neuen Hosts erfassen", + "hostDefaultsStatusCheckEnabled": "Statusüberprüfung aktivieren", + "hostDefaultsStatusCheckEnabledDesc": "Online-/Offline-Status standardmäßig auf neuen Hosts abfragen", "hostDefaultsTerminal": "Terminal", "hostDefaultsSessionLogging": "Sitzungsprotokollierung", - "hostDefaultsSessionLoggingDesc": "Terminal-Sitzungen bei neuen Hosts standardmäßig aufzeichnen", + "hostDefaultsSessionLoggingDesc": "Terminalsitzungen standardmäßig auf neuen Hosts aufzeichnen", "hostDefaultsCommandHistory": "Befehlshistorie", - "hostDefaultsCommandHistoryDesc": "Befehlsverlauf bei neuen Hosts standardmäßig aufzeichnen", - "hostDefaultsSaved": "Host-Standardeinstellungen gespeichert", - "hostDefaultsSaveFailed": "Fehler beim Speichern der Host-Standardeinstellungen", + "hostDefaultsCommandHistoryDesc": "Befehlshistorie standardmäßig auf neuen Hosts verfolgen", + "hostDefaultsSaved": "Hoststandards gespeichert", + "hostDefaultsSaveFailed": "Fehler beim Speichern der Hoststandards", "rolePermissions": { "count": "{{count}} Berechtigungen", - "editAction": "Bearbeitungsberechtigungen", - "loadError": "Der Berechtigungskatalog konnte nicht geladen werden.", + "editAction": "Berechtigungen bearbeiten", + "loadError": "Fehler beim Laden des Berechtigungskatalogs", "saved": "Rollenberechtigungen gespeichert", - "saveError": "Rollenberechtigungen konnten nicht gespeichert werden", + "saveError": "Fehler beim Speichern der Rollenberechtigungen", "save": "Speichern", - "saving": "Speichern..." + "saving": "Wird gespeichert …" } }, "newUi": { @@ -2961,25 +3112,25 @@ "portLabel": "Port", "portPlaceholder": "22", "usernameLabel": "Benutzername", - "usernamePlaceholder": "Benutzername", + "usernamePlaceholder": "benutzername", "authLabel": "Authentifizierung", "passwordLabel": "Passwort", - "passwordPlaceholder": "Passwort", + "passwordPlaceholder": "passwort", "privateKeyLabel": "Privater Schlüssel", "privateKeyPlaceholder": "Privaten Schlüssel einfügen...", - "credentialLabel": "Zugangsdaten", - "credentialPlaceholder": "Gespeicherte Zugangsdaten auswählen", - "connectToTerminal": "Verbindung zum Terminal herstellen", - "connectToFiles": "Verbindung zu Dateien herstellen" + "credentialLabel": "Anmeldeinformation", + "credentialPlaceholder": "Gespeicherte Anmeldeinformation auswählen", + "connectToTerminal": "Mit Terminal verbinden", + "connectToFiles": "Mit Dateien verbinden" }, "history": { "noTerminalSelected": "Kein Terminal ausgewählt", - "noTerminalSelectedHint": "Öffnen Sie einen SSH-Terminal-Tab, um dessen Befehlsverlauf anzuzeigen.", + "noTerminalSelectedHint": "Öffnen Sie einen SSH-Terminal-Tab, um den Befehlsverlauf anzuzeigen.", "searchPlaceholder": "Verlauf durchsuchen...", - "clearAll": "Alles löschen", + "clearAll": "Alle löschen", "noHistoryEntries": "Keine Verlaufseinträge", - "trackingDisabled": "Verlaufsprotokollierung ist deaktiviert", - "trackingDisabledHint": "Aktivieren Sie es in den Terminaleinstellungen des Hosts." + "trackingDisabled": "Verlaufsaufzeichnung ist deaktiviert", + "trackingDisabledHint": "Aktivieren Sie sie in den Terminal-Einstellungen des Hosts." }, "sshTools": { "keyRecordingTitle": "Tastenaufzeichnung", @@ -2987,46 +3138,46 @@ "selectAll": "Alle", "selectNone": "Keine", "noTerminalTabsOpen": "Keine Terminal-Tabs geöffnet", - "selectTerminalsAbove": "Oben Terminals auswählen", - "broadcastInputPlaceholder": "Hier tippen, um Tastatureingaben zu übertragen...", - "fillPassword": "Passwort eingeben", - "fillPasswordSuccess": "Passwort in {{count}} Terminal(s) eingegeben", - "fillPasswordMissing": "Für {{count}} ausgewählte Terminal(s) ist kein Passwort gespeichert.", - "stopRecording": "Aufnahme stoppen", - "startRecording": "Aufnahme starten", + "selectTerminalsAbove": "Oben angezeigte Terminals auswählen", + "broadcastInputPlaceholder": "Hier tippen, um Tastenanschläge zu senden...", + "fillPassword": "Passwort einfügen", + "fillPasswordSuccess": "Passwort in {{count}} Terminal(s) eingefügt", + "fillPasswordMissing": "Kein gespeichertes Passwort für {{count}} ausgewählte(s) Terminal(s)", + "stopRecording": "Aufzeichnung beenden", + "startRecording": "Aufzeichnung starten", "settingsTitle": "Einstellungen", - "enableRightClickCopyPaste": "Kopieren/Einfügen per Rechtsklick aktivieren" + "enableRightClickCopyPaste": "Rechtsklick-Kopieren/Einfügen aktivieren" }, "splitScreen": { "layoutTitle": "Layout", - "selectLayoutAbove": "Wählen Sie oben ein Layout aus.", - "selectLayoutHint": "Wählen Sie die Anzahl der anzuzeigenden Bereiche aus.", + "selectLayoutAbove": "Wählen Sie oben ein Layout aus", + "selectLayoutHint": "Wählen Sie, wie viele Fensterbereiche angezeigt werden sollen", "panesTitle": "Bereiche", - "openTabsTitle": "Geöffnete Tabs", - "dragTabsHint": "Ziehen Sie Tabs in die Bereiche oben oder verwenden Sie die Schnellzuweisung.", + "openTabsTitle": "Offene Tabs", + "dragTabsHint": "Ziehen Sie Tabs in die oberen Bereiche oder verwenden Sie Schnellzuweisung", "dropHere": "Hier ablegen", "emptyPane": "Leer", "dashboard": "Dashboard", - "clearSplitScreen": "Geteilte Ansicht leeren", - "quickAssign": "Schnellzuweisung", + "clearSplitScreen": "Aufteilung aufheben", + "quickAssign": "Schnell zuweisen", "alreadyAssigned": "Bereich {{index}}", "splitTab": "Tab teilen", - "addToSplit": "Zur geteilten Ansicht hinzufügen", - "removeFromSplit": "Aus geteilter Ansicht entfernen", - "assignToPane": "Dem Bereich zuweisen", + "addToSplit": "Zur Aufteilung hinzufügen", + "removeFromSplit": "Aus der Aufteilung entfernen", + "assignToPane": "Bereich zuweisen", "hotkeysTitle": "Tastenkombinationen", - "hotkeysSplitRight": "Zweiteilung umschalten", - "hotkeysSplitBelow": "Dreifachteilung umschalten", - "hotkeysNavigatePane": "Zwischen Bereichen navigieren", + "hotkeysSplitRight": "2-fach-Aufteilung umschalten", + "hotkeysSplitBelow": "3-fach-Aufteilung umschalten", + "hotkeysNavigatePane": "Zwischen Bereichen wechseln", "hotkeysNextTab": "Nächster Tab", "hotkeysPrevTab": "Vorheriger Tab" }, "snippets": { "title": "Snippets", "createSnippetTitle": "Snippet erstellen", - "createSnippetDescription": "Erstellen Sie ein neues Befehls-Snippet zur schnellen Ausführung.", + "createSnippetDescription": "Einen neuen Befehlsschnipsel für die schnelle Ausführung erstellen", "nameLabel": "Name", - "namePlaceholder": "z. B. Nginx neu starten", + "namePlaceholder": "z. B. Nginx neustarten", "descriptionLabel": "Beschreibung", "descriptionPlaceholder": "Optionale Beschreibung", "optional": "Optional", @@ -3037,7 +3188,7 @@ "cancel": "Abbrechen", "createSnippetButton": "Snippet erstellen", "createFolderTitle": "Ordner erstellen", - "createFolderDescription": "Organisieren Sie Ihre Snippets in Ordnern.", + "createFolderDescription": "Organisieren Sie Ihre Snippets in Ordnern", "folderNameLabel": "Ordnername", "folderNamePlaceholder": "z. B. Systembefehle, Docker-Skripte", "folderColorLabel": "Ordnerfarbe", @@ -3045,7 +3196,7 @@ "previewLabel": "Vorschau", "folderNameFallback": "Ordnername", "createFolderButton": "Ordner erstellen", - "targetTerminals": "Zielterminals", + "targetTerminals": "Zielterminale", "selectAll": "Alle", "selectNone": "Keine", "noTerminalTabsOpen": "Keine Terminal-Tabs geöffnet", @@ -3059,81 +3210,123 @@ "editSnippetDescription": "Dieses Befehls-Snippet aktualisieren", "saveSnippetButton": "Änderungen speichern", "createSuccess": "Snippet erfolgreich erstellt", - "createFailed": "Fehler beim Erstellen des Snippets", + "createFailed": "Erstellen des Snippets fehlgeschlagen", "updateSuccess": "Snippet erfolgreich aktualisiert", - "updateFailed": "Snippet konnte nicht aktualisiert werden", - "deleteFailed": "Snippet konnte nicht gelöscht werden", + "updateFailed": "Aktualisieren des Snippets fehlgeschlagen", + "deleteFailed": "Löschen des Snippets fehlgeschlagen", "folderCreateSuccess": "Ordner erfolgreich erstellt", - "folderCreateFailed": "Ordner konnte nicht erstellt werden", + "folderCreateFailed": "Erstellen des Ordners fehlgeschlagen", "editFolderTitle": "Ordner bearbeiten", - "editFolderDescription": "Benennen Sie diesen Ordner um oder ändern Sie sein Aussehen.", + "editFolderDescription": "Diesen Ordner umbenennen oder das Aussehen ändern", "saveFolderButton": "Änderungen speichern", "editFolder": "Ordner bearbeiten", "deleteFolder": "Ordner löschen", "folderDeleteSuccess": "Ordner \"{{name}}\" gelöscht", - "folderDeleteFailed": "Ordner konnte nicht gelöscht werden", + "folderDeleteFailed": "Löschen des Ordners fehlgeschlagen", "folderEditSuccess": "Ordner erfolgreich aktualisiert", - "folderEditFailed": "Ordner konnte nicht aktualisiert werden", + "folderEditFailed": "Aktualisieren des Ordners fehlgeschlagen", "confirmRunMessage": "„{{name}}“ ausführen?", "confirmRunButton": "Ausführen", "runSuccess": "„{{name}}“ in {{count}} Terminal(s) ausgeführt", - "copySuccess": "\"{{name}}\" in die Zwischenablage kopiert", + "copySuccess": "„{{name}}“ in die Zwischenablage kopiert", "shareTitle": "Snippet teilen", "shareUser": "Benutzer", "shareRole": "Rolle", "selectUser": "Benutzer auswählen...", "selectRole": "Rolle auswählen...", "shareSuccess": "Snippet erfolgreich geteilt", - "shareFailed": "Snippet konnte nicht freigegeben werden", + "shareFailed": "Snippet konnte nicht geteilt werden", "revokeSuccess": "Zugriff widerrufen", "revokeFailed": "Zugriff konnte nicht widerrufen werden", "currentAccess": "Aktueller Zugriff", - "shareLoadError": "Die Freigabedaten konnten nicht geladen werden.", - "loading": "Wird geladen...", + "shareLoadError": "Teilungsdaten konnten nicht geladen werden", + "loading": "Laden...", "close": "Schließen", - "reorderFailed": "Snippet-Reihenfolge konnte nicht gespeichert werden", + "reorderFailed": "Reihenfolge der Snippets konnte nicht gespeichert werden", "importExport": "Import / Export", "exportBtn": "JSON exportieren", "importBtn": "JSON importieren", "exportSuccess": "Snippets erfolgreich exportiert", "exportFailed": "Snippets konnten nicht exportiert werden", "importTitle": "Snippets importieren", - "importDescription": "Importieren Sie Snippets und Ordner aus einer von Termix exportierten JSON-Datei.", - "importDropOrClick": "JSON-Datei hier ablegen oder zum Auswählen klicken", + "importDescription": "Snippets und Ordner aus einer von Termix exportierten JSON-Datei importieren", + "importDropOrClick": "JSON-Datei hier ablegen oder zum Durchsuchen klicken", "importSelectedFile": "Ausgewählt: {{name}}", - "importOverwrite": "Vorhandene Snippets mit demselben Namen und Ordner überschreiben", + "importOverwrite": "Bestehende Snippets mit gleichem Namen und Ordner überschreiben", "importStartBtn": "Importieren", - "importSuccess": "Import abgeschlossen: {{snippets}} Snippets hinzugefügt, {{updated}} aktualisiert, {{skipped}} übersprungen, {{folders}} Ordner hinzugefügt", + "importSuccess": "Import abgeschlossen: {{snippets}} Snippet(s) hinzugefügt, {{updated}} aktualisiert, {{skipped}} übersprungen, {{folders}} Ordner hinzugefügt", "importFailed": "Snippets konnten nicht importiert werden", - "importInvalidFile": "Ungültige Datei: Es wurde ein JSON-Objekt mit Snippets- oder Ordner-Arrays erwartet.", + "importInvalidFile": "Ungültige Datei: Ein JSON-Objekt mit Snippets- oder Ordner-Arrays erwartet", "targetHostsLabel": "Zielhosts", - "targetHostsHint": "Weisen Sie Hosts zu, auf denen dieses Snippet direkt ohne geöffnetes Terminal ausgeführt werden soll.", + "targetHostsHint": "Hosts zuweisen, um dieses Snippet direkt ohne offenes Terminal auszuführen.", "noHostsAvailable": "Keine Hosts konfiguriert", - "clearTargetHosts": "Alles löschen", - "hasTargetHosts": "Mit Zielhosts", - "runOnTargets": "Auf Zielsystemen ausführen", - "directRunSuccess": "„{{name}}“ auf {{count}} Host(s) ausgeführt", - "directRunPartialFail": "\"{{name}}\" ist auf einem oder mehreren Hosts fehlgeschlagen.", + "clearTargetHosts": "Alle löschen", + "hasTargetHosts": "Hat Zielhosts", + "runOnTargets": "Auf Zielen ausführen", + "directRunSuccess": "\"{{name}}\" auf {{count}} Host(s) ausgeführt", + "directRunPartialFail": "\"{{name}}\" auf einem oder mehreren Hosts fehlgeschlagen", "executionResultTitle": "Ausführungsergebnisse: {{name}}", - "executionResultDescription": "Ausgabe der Snippet-Ausführung auf den einzelnen Zielhosts.", - "executionSuccess": "Erfolg", + "executionResultDescription": "Ausgabe der Snippet-Ausführung auf jedem Zielhost.", + "executionSuccess": "Erfolgreich", "executionFailed": "Fehlgeschlagen" }, + "keybindings": { + "title": "Tastenkombinationen", + "description": "Passen Sie die Tastenkombinationen für Kopieren, Einfügen und Steuerung im Terminal an oder belegen Sie Tasten mit Befehlen zum Senden von Text oder zum Ausführen eines Code-Snippets.", + "defaultsHeading": "Integrierte Tastenkombinationen", + "customHeading": "Benutzerdefinierte Tastenkombinationen", + "addBinding": "Verknüpfung hinzufügen", + "addBindingTitle": "Verknüpfung hinzufügen", + "editBindingTitle": "Bearbeitungs-Shortcut", + "loading": "Laden...", + "noCustomBindings": "Noch keine benutzerdefinierten Tastenkombinationen", + "defaultBadge": "Standard", + "customizedBadge": "Maßgeschneidert", + "customize": "Anpassen", + "resetToDefault": "Auf Standardeinstellungen zurücksetzen", + "resetAllToDefaults": "Alles auf Standardeinstellungen zurücksetzen", + "close": "Schließen", + "cancel": "Stornieren", + "saveBinding": "Verknüpfung speichern", + "saveError": "Tastenkombinationen konnten nicht gespeichert werden", + "pressKeysToRecord": "Tastenkombination", + "pressKeysPlaceholder": "Klicken und drücken Sie die Tasten", + "recording": "Tasten drücken...", + "comboRequiredError": "Drücken Sie zuerst eine Tastenkombination", + "textRequiredError": "Geben Sie den zu sendenden Text ein.", + "controlCodeRequiredError": "Geben Sie einen einzelnen Buchstaben als Kontrollcode ein.", + "snippetRequiredError": "Wählen Sie einen Ausschnitt aus", + "conflictWarning": "Diese Kombination ist bereits an {{combo}}gebunden. Durch Speichern werden beide ausgelöst.", + "actionLabel": "Aktion", + "actionCopy": "Auswahl kopieren", + "actionPaste": "Paste", + "actionSendControlCode": "Sende das Signal Strg+Buchstabe", + "actionSendText": "SMS senden", + "actionRunSnippet": "Vorhandenen Codeausschnitt ausführen", + "controlCodeLabel": "Brief", + "textLabel": "Text zum Senden", + "appendEnterLabel": "Nach dem Absenden die Eingabetaste drücken", + "snippetLabel": "Ausschnitt", + "selectSnippetPlaceholder": "Wählen Sie einen Ausschnitt aus", + "orphanedSnippetWarning": "Ausschnitt nicht gefunden", + "clipboardPermissionNote": "Bei einigen Browsern kann es bei der ersten Verwendung zu einer Anfrage nach der Berechtigung zum Ablegen der Zwischenablage kommen." + }, "userProfile": { - "donateTitle": "Termix am Leben erhalten", - "donateDescription": "Termix ist kostenlos, selbstgehostet und wird von wenigen Personen in ihrer Freizeit entwickelt. Wenn es Ihnen Zeit oder Geld gespart hat, hilft eine Krypto-Spende, das Projekt weiterzuführen.", - "donateMilestones": "Spenden helfen uns, die nötige Zeit für Recherche und das Erlernen der erforderlichen Kenntnisse für die Entwicklung von SAML-, Kubernetes- und Agent-Unterstützung zu finanzieren. Verfolgen Sie den Fortschritt und spenden Sie.", - "donateButton": "Spenden Sie Kryptowährung", + "donateTitle": "Termix unterstützen", + "donateDescription": "Termix ist kostenlos, selbst gehostet und wird von nur wenigen Personen in ihrer Freizeit entwickelt. Wenn es Ihnen Zeit oder Geld gespart hat, hilft eine Krypto-Spende, es am Laufen zu halten.", + "donateMilestones": "Spenden finanzieren die Zeit, um zu erforschen, was für den Aufbau von SAML-, Kubernetes- und Agent-Unterstützung nötig ist. Verfolgen Sie den Fortschritt und spenden Sie.", + "donateButton": "Krypto spenden", "storageModeLocal": "Browser", "storageModeCloud": "Datenbank", - "storageModeDescription": "Der Browser speichert die Einstellungen nur in diesem Browser. Die Datenbank wird mit dem Server synchronisiert und auf jedem Gerät geladen.", + "storageModeDescription": "Browser speichert die Einstellungen nur in diesem Browser. Die Datenbank synchronisiert mit dem Server und lädt sie auf jedem Gerät.", "resetToDefaults": "Auf Standardeinstellungen zurücksetzen", - "resetToDefaultsSuccess": "Einstellungen auf Standardwerte zurückgesetzt.", + "resetToDefaultsSuccess": "Einstellungen wurden auf die Standardwerte zurückgesetzt.", "storageModeSwitch": "Einstellungsspeicher", "sectionAccount": "Konto", "sectionAppearance": "Erscheinungsbild", "sectionSecurity": "Sicherheit", "sectionApiKeys": "API-Schlüssel", + "sectionData": "Daten", "sectionC2sTunnels": "C2S-Tunnel", "usernameLabel": "Benutzername", "roleLabel": "Rolle", @@ -3145,55 +3338,58 @@ "twoFaOff": "Aus", "versionLabel": "Version", "betaProgramTitle": "Beta-Programm", - "betaProgramDescription": "Testen Sie kommende Funktionen frühzeitig mit dem wöchentlich aktualisierten :beta-Docker-Tag. Instabil, nicht für den Produktiveinsatz geeignet.", - "betaProgramFeedback": "Einen Fehler gefunden? Melden Sie ihn hier.", + "betaProgramDescription": "Probieren Sie kommende Funktionen frühzeitig mit dem wöchentlich rollierenden :beta Docker-Tag aus. Instabil, nicht für den Produktiveinsatz geeignet.", + "betaProgramFeedback": "Fehler gefunden? Melden Sie ihn hier.", "betaProgramLearnMore": "Mehr erfahren", "deleteAccount": "Konto löschen", - "deleteAccountDescription": "Konto dauerhaft löschen", - "changeServerDescription": "Wechseln Sie zu einem anderen Termix-Backend-Server.", + "deleteAccountDescription": "Ihr Konto dauerhaft löschen", + "changeServerDescription": "Zu einem anderen Termix-Backend-Server wechseln", "deleteButton": "Löschen", "deleteAccountPermanent": "Diese Aktion ist endgültig und kann nicht rückgängig gemacht werden.", - "deleteAccountWarning": "Alle Sitzungen, Hosts, Zugangsdaten und Einstellungen werden dauerhaft gelöscht.", - "confirmPasswordDeletePlaceholder": "Geben Sie Ihr Passwort zur Bestätigung ein.", + "deleteAccountWarning": "Alle Sitzungen, Hosts, Anmeldeinformationen und Einstellungen werden endgültig gelöscht.", + "confirmPasswordDeletePlaceholder": "Geben Sie Ihr Passwort zur Bestätigung ein", "languageLabel": "Sprache", "themeLabel": "Design", "fontSizeLabel": "Schriftgröße", "accentColorLabel": "Akzentfarbe", "settingsTerminal": "Terminal", - "commandAutocomplete": "Befehlsvervollständigung", + "commandAutocomplete": "Befehlsautovervollständigung", "commandAutocompleteDesc": "Autovervollständigung während der Eingabe anzeigen", - "terminalLinkBehavior": "Klickverhalten für Terminal-Links", + "keyboardShortcuts": "Tastenkombinationen", + "keyboardShortcutsDescription": "Terminal-Kopieren, Einfügen und Befehls-Shortcuts anpassen", + "manageShortcuts": "Verwalten", + "terminalLinkBehavior": "Terminal-Link-Klick", "terminalLinkBehaviorDesc": "Standardverhalten beim Klicken auf Links im Terminal", - "historyTracking": "Verlaufsverfolgung", - "historyTrackingDesc": "Terminalbefehle verfolgen", + "historyTracking": "Verlaufsprotokollierung", + "historyTrackingDesc": "Terminalbefehle protokollieren", "commandPalette": "Befehlspalette", "commandPaletteDesc": "Tastenkombination 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": "Schließen von Tabs bestätigen", - "confirmTabCloseDesc": "Vor dem Schließen der Terminal-Tabs fragen.", + "reopenTabsOnLogin": "Tabs bei Anmeldung wieder öffnen", + "reopenTabsOnLoginDesc": "Offene Tabs bei Anmeldung oder Seitenaktualisierung wiederherstellen, auch von anderen Geräten", + "confirmTabClose": "Tab-Schließen bestätigen", + "confirmTabCloseDesc": "Vor dem Schließen von Terminal-Tabs nachfragen", "settingsSidebar": "Seitenleiste", "showHostTags": "Host-Tags anzeigen", - "showHostTagsDesc": "Tags in der Hostliste anzeigen", - "hostTrayOnClick": "Klicken, um Host-Aktionen einzublenden", - "hostTrayOnClickDesc": "Verbindungsschaltflächen immer anzeigen; zum Erweitern der Verwaltungsoptionen klicken statt mit der Maus darüberfahren", + "showHostTagsDesc": "Tags in Host-Liste anzeigen", + "hostTrayOnClick": "Host-Aktionen per Klick erweitern", + "hostTrayOnClickDesc": "Verbindungsschaltflächen immer anzeigen; Managementoptionen per Klick statt Hover erweitern", "compactHostView": "Kompakte Host-Ansicht", - "compactHostViewDesc": "Reduzieren Sie jeden Host auf eine einzelne Zeile, die nur seinen Namen und seine Adresse anzeigt.", + "compactHostViewDesc": "Jeden Host auf eine Zeile reduzieren, die nur Name und Adresse anzeigt", "statusColors": "Echte Statusfarben", - "statusColorsDesc": "Verwenden Sie Grün/Rot für den Online-/Offline-Status anstelle der Akzentfarbe.", + "statusColorsDesc": "Grün/Rot für Online-/Offline-Status anstelle der Akzentfarbe verwenden", "pinAppRail": "App-Leiste anheften", - "pinAppRailDesc": "Die App-Leiste in der linken Seitenleiste immer ausgeklappt lassen, statt sie erst beim Darüberfahren zu erweitern.", - "expandAppRailOnHover": "App-Leiste beim Überfahren erweitern", - "expandAppRailOnHoverDesc": "Ermöglichen Sie es, dass sich die linke Seitenleiste beim Überfahren mit dem Mauszeiger automatisch erweitert.", + "pinAppRailDesc": "Linke Seitenleisten-App-Leiste immer erweitert lassen, anstatt sie bei Hover zu erweitern", + "expandAppRailOnHover": "App-Leiste bei Hover erweitern", + "expandAppRailOnHoverDesc": "App-Leiste beim Überfahren mit der Maus erweitern", "settingsNavigation": "Navigation", - "navigationTabsDesc": "Wählen Sie aus, welche Tabs in der App-Leiste angezeigt werden sollen.", + "navigationTabsDesc": "Wählen Sie aus, welche Registerkarten in der App-Leiste angezeigt werden", "settingsSnippets": "Snippets", "foldersCollapsed": "Ordner eingeklappt", "foldersCollapsedDesc": "Ordner standardmäßig einklappen", "confirmExecution": "Ausführung bestätigen", "confirmExecutionDesc": "Vor dem Ausführen von Snippets bestätigen", - "settingsUpdates": "Aktualisierungen", - "disableUpdateChecks": "Updateprüfungen deaktivieren", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Update-Prüfungen deaktivieren", "disableUpdateChecksDesc": "Nicht mehr nach Updates suchen", "totpAuthenticator": "TOTP-Authentifikator", "totpEnabled": "2FA ist aktiviert", @@ -3202,9 +3398,9 @@ "enable": "Aktivieren", "setupTotp": "TOTP einrichten", "qrCode": "QR-Code", - "totpInstructions": "Scannen Sie den QR-Code oder geben Sie den geheimen Schlüssel in Ihrer Authentifizierungs-App ein. Geben Sie anschließend den 6-stelligen Code ein.", + "totpInstructions": "Scannen Sie den QR-Code oder geben Sie den geheimen Schlüssel in Ihrer Authentifizierungs-App ein und geben Sie dann den 6-stelligen Code ein.", "totpCodePlaceholder": "000000", - "verify": "Überprüfen", + "verify": "Verifizieren", "changePassword": "Passwort ändern", "currentPasswordLabel": "Aktuelles Passwort", "currentPasswordPlaceholder": "Aktuelles Passwort", @@ -3226,27 +3422,43 @@ "noApiKeys": "Noch keine API-Schlüssel.", "apiKeyActive": "Aktiv", "apiKeyUsageHint": "Fügen Sie Ihren Schlüssel in den", - "apiKeyUsageHintHeader": "Header.", - "apiKeyPermissionsHint": "Schlüssel übernehmen die Berechtigungen des Benutzers, der sie erstellt hat.", + "apiKeyUsageHintHeader": "Header ein.", + "apiKeyPermissionsHint": "Schlüssel erben die Berechtigungen des erstellenden Benutzers.", + "exportData": "Meine Daten exportieren", + "exportDataDesc": "Laden Sie eine Sicherungskopie Ihrer Hosts, Anmeldeinformationen und Einstellungen herunter, um sie auf ein anderes Gerät zu übertragen.", + "export": "Export", + "exporting": "Exportieren...", + "importData": "Meine Daten importieren", + "importDataDesc": "Stellen Sie Ihre Hosts, Anmeldeinformationen und Einstellungen aus einer .sqlite-Sicherungsdatei wieder her.", + "importDataSelected": "Ausgewählt: {{name}}", + "selectFile": "Datei auswählen", + "changeFile": "Ändern", + "import": "Import", + "importing": "Importiert...", + "exportSuccess": "Daten erfolgreich exportiert", + "exportFailed": "Datenexport fehlgeschlagen", + "importSelectFile": "Bitte wählen Sie zuerst eine Datei aus.", + "importCompleted": "Import abgeschlossen: {{total}} Elemente importiert, {{skipped}} übersprungen", + "importFailed": "Import fehlgeschlagen: {{error}}", "roleUser": "Benutzer", "authMethodDual": "Duale Authentifizierung", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP-Setup konnte nicht gestartet werden", + "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": "Geben Sie Ihren TOTP-Code oder Ihr Passwort ein.", + "totpInvalidCode": "Ungültiger Code, bitte versuchen Sie es erneut", + "totpDisableInputRequired": "Geben Sie Ihren TOTP-Code oder Ihr Passwort ein", "totpDisabledSuccess": "Zwei-Faktor-Authentifizierung deaktiviert", - "totpDisableFailed": "Die Deaktivierung der Zwei-Faktor-Authentifizierung ist fehlgeschlagen.", - "totpDisableTitle": "Zwei-Faktor-Authentifizierung deaktivieren", - "totpDisablePlaceholder": "Geben Sie den TOTP-Code oder das Passwort ein.", - "totpDisableConfirm": "Zwei-Faktor-Authentifizierung deaktivieren", - "totpContinueVerify": "Weiter zur Überprüfung", - "totpVerifyTitle": "Code überprüfen", + "totpDisableFailed": "Fehler beim Deaktivieren der 2FA", + "totpDisableTitle": "2FA deaktivieren", + "totpDisablePlaceholder": "TOTP-Code oder Passwort eingeben", + "totpDisableConfirm": "2FA deaktivieren", + "totpContinueVerify": "Weiter zur Verifizierung", + "totpVerifyTitle": "Code verifizieren", "totpBackupTitle": "Backup-Codes", "totpDownloadBackup": "Backup-Codes herunterladen", "passkeys": "Passkeys", - "passkeysDesc": "Verwenden Sie WebAuthn-/FIDO2-Anmeldeinformationen, um sich ohne Passwort anzumelden.", + "passkeysDesc": "WebAuthn/FIDO2-Anmeldedaten für eine Anmeldung ohne Passwort verwenden", "passkeyName": "Passkey-Name", "passkeyUvPreferred": "Bevorzugt", "passkeyUvRequired": "Erforderlich", @@ -3254,26 +3466,26 @@ "addPasskey": "Passkey hinzufügen", "noPasskeys": "Keine Passkeys registriert.", "passkeyAdded": "Passkey hinzugefügt", - "passkeyAddFailed": "Passkey konnte nicht hinzugefügt werden", + "passkeyAddFailed": "Fehler beim Hinzufügen des Passkeys", "passkeyDeleted": "Passkey gelöscht", - "passkeyDeleteFailed": "Fehler beim Löschen des Passkeys", + "passkeyDeleteFailed": "Löschen des Passkeys fehlgeschlagen", "done": "Fertig", - "secretCopied": "Geheimschlüssel in die Zwischenablage kopiert", - "apiKeyNameRequired": "Ein Schlüsselname ist erforderlich.", + "secretCopied": "Secret in die Zwischenablage kopiert", + "apiKeyNameRequired": "Schlüsselname ist erforderlich", "apiKeyCreated": "API-Schlüssel \"{{name}}\" erstellt", - "apiKeyCreateFailed": "API-Schlüssel konnte nicht erstellt werden", + "apiKeyCreateFailed": "Erstellen des API-Schlüssels fehlgeschlagen", "apiKeyUser": "Benutzer", "apiKeyExpires": "Läuft ab", "apiKeyRevoked": "API-Schlüssel \"{{name}}\" widerrufen", - "apiKeyRevokeFailed": "API-Schlüssel konnte nicht widerrufen werden", - "passwordFieldsRequired": "Aktuelle und neue Passwörter sind erforderlich.", - "passwordMismatch": "Die Passwörter stimmen nicht überein.", - "passwordTooShort": "Das Passwort muss mindestens 6 Zeichen lang sein.", + "apiKeyRevokeFailed": "Widerrufen des API-Schlüssels fehlgeschlagen", + "passwordFieldsRequired": "Aktuelles und neues Passwort sind erforderlich", + "passwordMismatch": "Passwörter stimmen nicht überein", + "passwordTooShort": "Passwort muss mindestens 6 Zeichen lang sein", "passwordUpdated": "Passwort erfolgreich aktualisiert", - "passwordUpdateFailed": "Passwort konnte nicht aktualisiert werden", - "deletePasswordRequired": "Zum Löschen Ihres Kontos ist ein Passwort erforderlich.", - "deleteFailed": "Konto konnte nicht gelöscht werden", - "deleting": "Wird gelöscht...", + "passwordUpdateFailed": "Aktualisieren des Passworts fehlgeschlagen", + "deletePasswordRequired": "Passwort ist zum Löschen Ihres Kontos erforderlich", + "deleteFailed": "Löschen des Kontos fehlgeschlagen", + "deleting": "Löschen...", "colorPickerTooltip": "Farbauswahl öffnen", "themeSystem": "System", "themeLight": "Hell", @@ -3290,76 +3502,76 @@ }, "tmuxMonitor": { "title": "Tmux-Monitor", - "failedToLoadHosts": "Hosts konnten nicht geladen werden", - "failedToLoad": "tmux-Sitzungen konnten nicht geladen werden", - "tmuxUnavailable": "tmux ist auf diesem Host nicht installiert.", - "noSessions": "Auf diesem Host sind keine tmux-Sitzungen aktiv.", + "failedToLoadHosts": "Laden der Hosts fehlgeschlagen", + "failedToLoad": "Laden der tmux-Sitzungen fehlgeschlagen", + "tmuxUnavailable": "tmux ist auf diesem Host nicht installiert", + "noSessions": "Keine tmux-Sitzungen auf diesem Host", "noHostSelected": "Kein Host ausgewählt", "attached": "Verbunden", "detached": "Getrennt", - "attach": "Anhängen", + "attach": "Verbinden", "editTags": "Tags bearbeiten", - "tagsHint": "Durch Kommas getrennte Schlagwörter (z. B. YOLO, Labor, Training)", + "tagsHint": "Durch Kommas getrennte Tags (z. B. YOLO, lab, training)", "tagsSaved": "Tags gespeichert", - "tagsSaveFailed": "Tags konnten nicht gespeichert werden", - "searchPlaceholder": "Ausgaben sitzungsübergreifend durchsuchen...", - "searchResults": "{{count}} Übereinstimmungen", + "tagsSaveFailed": "Fehler beim Speichern der Tags", + "searchPlaceholder": "Ausgabe in allen Sitzungen durchsuchen...", + "searchResults": "{{count}} Treffer", "searchFailed": "Suche fehlgeschlagen", - "selectPaneHint": "Wählen Sie einen Bereich aus, um dessen Ausgabe in der Vorschau anzuzeigen.", + "selectPaneHint": "Wählen Sie einen Bereich aus, um dessen Ausgabe anzuzeigen", "closePreview": "Vorschau schließen", "newSession": "Neue Sitzung", "newSessionHint": "Sitzungsname (Buchstaben, Ziffern, _ @ % + = -)", - "newSessionPlaceholder": "meine-sitzung", + "newSessionPlaceholder": "my-session", "create": "Erstellen", "sessionCreated": "Sitzung \"{{name}}\" erstellt", - "sessionCreateFailed": "Sitzung konnte nicht erstellt werden", - "splitRight": "Nach rechts teilen", - "splitDown": "Nach unten teilen", - "splitFailed": "Bereich konnte nicht geteilt werden", + "sessionCreateFailed": "Fehler beim Erstellen der Sitzung", + "splitRight": "Rechts teilen", + "splitDown": "Unten teilen", + "splitFailed": "Fehler beim Teilen des Bereichs", "newWindow": "Neues Fenster", - "windowCreateFailed": "Fenster konnte nicht erstellt werden", - "attachSessionTooltip": "An {{session}} anhängen", + "windowCreateFailed": "Fehler beim Erstellen des Fensters", + "attachSessionTooltip": "Mit {{session}} verbinden", "refresh": "Aktualisieren", - "refreshFailed": "Sitzungen konnten nicht aktualisiert werden", - "collapseAll": "Alle ausblenden", - "expandAll": "Alle erweitern", + "refreshFailed": "Fehler beim Aktualisieren der Sitzungen", + "collapseAll": "Alle einklappen", + "expandAll": "Alle ausklappen", "moreActions": "Weitere Aktionen", "sessionStats": "Sitzungsstatistiken", "renameSessionTitle": "Sitzung \"{{name}}\" umbenennen", "rename": "Umbenennen", - "sessionRenamed": "Sitzung in \"{{name}}\" umbenannt", - "sessionRenameFailed": "Sitzung konnte nicht umbenannt werden", + "sessionRenamed": "Sitzung umbenannt in \"{{name}}\"", + "sessionRenameFailed": "Fehler beim Umbenennen der Sitzung", "editTagsTitle": "Tags für \"{{name}}\" bearbeiten", - "killPane": "Bereich schließen", - "resizeTree": "Ziehen zum Ändern der Größe – Doppelklick zum Zurücksetzen", - "reattach": "Wieder anhängen (behebt eine fehlerhafte Ansicht)", - "killWindow": "Fenster schließen", - "killWindowTitle": "Fenster {{index}} von \"{{session}}\" schließen?", - "killWindowBody": "Alle Bereiche und Prozesse in diesem Fenster werden beendet. Durch Schließen des letzten Fensters wird die Sitzung beendet.", - "windowKillFailed": "Fenster konnte nicht geschlossen werden", + "killPane": "Pane beenden", + "resizeTree": "Ziehen zum Ändern der Größe — Doppelklick zum Zurücksetzen", + "reattach": "Erneut anhängen (behebt eine verzerrte Darstellung)", + "killWindow": "Fenster beenden", + "killWindowTitle": "Fenster {{index}} von \"{{session}}\" beenden?", + "killWindowBody": "Alle Panes und Prozesse in diesem Fenster werden beendet. Wird das letzte Fenster beendet, wird die Sitzung beendet.", + "windowKillFailed": "Fenster konnte nicht beendet werden", "statusActivity": "Aktivität", "statusWindows": "Fenster", - "statusPanes": "Bereiche", + "statusPanes": "Panes", "statusTags": "Tags", - "killPaneTitle": "Bereich {{id}} schließen?", - "killPaneBody": "Der in diesem Bereich laufende Prozess wird beendet. Durch das Schließen des letzten Bereichs wird das zugehörige Fenster geschlossen.", - "paneKillFailed": "Bereich konnte nicht geschlossen werden", + "killPaneTitle": "Pane {{id}} beenden?", + "killPaneBody": "Der in dieser Pane laufende Prozess wird beendet. Wird die letzte Pane beendet, wird das Fenster geschlossen.", + "paneKillFailed": "Pane konnte nicht beendet werden", "killSessionTitle": "Sitzung \"{{name}}\" beenden?", - "killSessionBody": "Alle Fenster und laufenden Prozesse dieser Sitzung werden beendet. Dies kann nicht rückgängig gemacht werden.", + "killSessionBody": "Alle Fenster und laufenden Prozesse in dieser Sitzung werden beendet. Dies kann nicht rückgängig gemacht werden.", "kill": "Beenden", "sessionKilled": "Sitzung \"{{name}}\" beendet", "sessionKillFailed": "Sitzung konnte nicht beendet werden", - "hostUnreachable": "Es konnte keine Verbindung zum Host hergestellt werden. Bitte überprüfen Sie, ob er online und erreichbar ist.", + "hostUnreachable": "Verbindung zum Host konnte nicht hergestellt werden. Überprüfen Sie, ob er online und erreichbar ist.", "noServer": "Auf diesem Host läuft kein tmux-Server.", - "searchTruncated": "Teilergebnisse — die Suche umfasst die letzten {{lines}} Zeilen jedes Bereichs und höchstens {{panes}} Bereiche.", + "searchTruncated": "Teilergebnisse — die Suche umfasst die letzten {{lines}} Zeilen jeder Pane und maximal {{panes}} Panes.", "closeSearchResults": "Suchergebnisse schließen", - "retry": "Wiederholen", + "retry": "Erneut versuchen", "noHosts": "Keine SSH-Hosts verfügbar", - "noHostsHint": "Aktivieren Sie auf einem SSH-Host die Option „Tmux-Monitor“ (Host-Manager → Tab „Terminal“), um dessen tmux-Sitzungen zu überwachen.", + "noHostsHint": "Aktivieren Sie die Option Tmux Monitor für einen SSH-Host (Host-Manager → Terminal-Tab), um dessen tmux-Sitzungen zu überwachen.", "tmuxInstallHint": "Installieren Sie es auf dem Host mit:", - "attachTooltip": "Terminal für {{host}} öffnen", - "attachTooltipPane": "Terminal für {{host}} öffnen — tmux-Sitzung {{session}}", - "timeJustNow": "soeben", + "attachTooltip": "Terminal zu {{host}} öffnen", + "attachTooltipPane": "Terminal zu {{host}} öffnen — tmux-Sitzung {{session}}", + "timeJustNow": "gerade eben", "timeMinutes": "vor {{count}} Min.", "timeHours": "vor {{count}} Std.", "timeDays": "vor {{count}} T." @@ -3377,96 +3589,97 @@ "home": "Pos1", "end": "Ende", "pageUp": "Bild auf", - "pageDown": "PgDn", - "delete": "Del", - "editQuickKeys": "Schnellzugriffstasten bearbeiten", - "quickKeysTitle": "Schnellzugriffstasten", - "quickKeysDesc": "Tippen Sie zum Entfernen auf ×. Es werden bis zu 8 Zeichen unterstützt.", + "pageDown": "Bild ab", + "delete": "Entf", + "paste": "Paste", + "editQuickKeys": "Schnelltasten bearbeiten", + "quickKeysTitle": "Schnelltasten", + "quickKeysDesc": "Tippen Sie auf × zum Entfernen. Unterstützt bis zu 8 Zeichen.", "quickKeyPlaceholder": "z. B. sudo ", "addQuickKey": "Hinzufügen", "removeQuickKey": "Entfernen", - "resetDefaults": "Auf Standardeinstellungen zurücksetzen", + "resetDefaults": "Auf Standard zurücksetzen", "done": "Fertig" }, "serial": { - "title": "Serielle Verbindung", + "title": "Seriell", "portLabel": "Port", "portPlaceholder": "/dev/ttyUSB0 oder COM3", "baudRateLabel": "Baudrate", "dataBitsLabel": "Daten", - "stopBitsLabel": "Stoppbits", + "stopBitsLabel": "Stop", "parityLabel": "Parität", "parityNone": "Keine", "parityEven": "Gerade", "parityOdd": "Ungerade", - "connect": "Serielle Verbindung herstellen", + "connect": "Mit Seriell verbinden", "disconnect": "Trennen", "refreshPorts": "Ports aktualisieren", "connected": "Verbunden mit {{path}} bei {{baud}} Baud", "disconnected": "Serielle Schnittstelle getrennt", - "connectionError": "Serielle Schnittstelle konnte nicht geöffnet werden", + "connectionError": "Fehler beim Öffnen der seriellen Schnittstelle", "wsError": "WebSocket-Fehler", "errorNoServerUrl": "Keine Server-URL konfiguriert", - "notSupportedTitle": "Serielle Verbindungen nicht unterstützt", - "notSupported": "Für serielle Verbindungen wird ein Browser mit Unterstützung für die Web Serial API benötigt (Chrome, Edge oder Firefox 151+), alternativ die Termix-Desktop-App.", - "hideHint": "Sie können den Tab „Seriell“ unter Benutzerprofil > Darstellung > Seitenleiste > Navigation ausblenden.", - "browserPickerHint": "Klicken Sie auf „Verbinden“, und Ihr Browser öffnet eine Portauswahl, in der Sie das Gerät auswählen können." + "notSupportedTitle": "Seriell nicht unterstützt", + "notSupported": "Serielle Verbindungen erfordern einen Browser mit Web Serial API-Unterstützung (Chrome, Edge oder Firefox 151+) oder die Termix-Desktop-App.", + "hideHint": "Sie können den Seriell-Tab unter Benutzerprofil > Erscheinungsbild > Seitenleiste > Navigation ausblenden.", + "browserPickerHint": "Klicken Sie auf Verbinden, und Ihr Browser öffnet eine Port-Auswahl, um das Gerät auszuwählen." }, "metricsHistory": { - "title": "Metrikverlauf", + "title": "Metrikenverlauf", "historySuffix": "Verlauf", - "cpuMemoryDisk": "CPU / Arbeitsspeicher / Festplatte", + "cpuMemoryDisk": "CPU / RAM / Festplatte", "network": "Netzwerk", "download": "RX", "upload": "TX", - "noData": "Für diesen Zeitraum sind keine historischen Daten verfügbar.", + "noData": "Keine Verlaufsdaten für diesen Zeitraum verfügbar.", "custom": "Benutzerdefiniert", "to": "bis", "apply": "Anwenden", "viewHistory": "Verlauf anzeigen" }, "alerts": { - "tabFirings": "Warnmeldungen", + "tabFirings": "Alarme", "tabRules": "Regeln", "tabChannels": "Kanäle", - "noFirings": "Keine unbestätigten Warnmeldungen", - "noRules": "Keine Warnregeln konfiguriert", - "noChannels": "Es sind keine Benachrichtigungskanäle konfiguriert.", - "rulesDesc": "Warnregeln lösen Benachrichtigungen aus", - "channelsDesc": "Ziel für Warnmeldungen", - "acknowledge": "Bestätigen", - "ackAll": "Alle bestätigen", - "allAcknowledged": "Alle Warnmeldungen bestätigt", - "ackFailed": "Warnmeldung konnte nicht bestätigt werden", - "ackAllFailed": "Warnmeldungen konnten nicht alle bestätigt werden", + "noFirings": "Keine unbestätigten Alarme", + "noRules": "Keine Alarmregeln konfiguriert", + "noChannels": "Keine Benachrichtigungskanäle konfiguriert", + "rulesDesc": "Alarmregeln lösen Benachrichtigungen aus", + "channelsDesc": "Wohin Alarmbenachrichtigungen gesendet werden", + "acknowledge": "Quittieren", + "ackAll": "Alle quittieren", + "allAcknowledged": "Alle Alarme quittiert", + "ackFailed": "Quittieren des Alarms fehlgeschlagen", + "ackAllFailed": "Quittieren aller Alarme fehlgeschlagen", "showAcknowledged": "Alle anzeigen", - "hideAcknowledged": "Bestätigte ausblenden", + "hideAcknowledged": "Quittierte ausblenden", "addChannel": "Kanal hinzufügen", "editChannel": "Kanal bearbeiten", "channelName": "Name", "channelType": "Typ", - "channelNameRequired": "Name erforderlich", + "channelNameRequired": "Name ist erforderlich", "webhookUrl": "URL", - "webhookUrlRequired": "Die Webhook-URL ist erforderlich.", - "webhookDesc": "Bei jeder Alarmauslösung wird eine JSON-Payload an diese URL gesendet.", + "webhookUrlRequired": "Webhook-URL ist erforderlich", + "webhookDesc": "Bei jeder Alarmauslösung eine JSON-Nutzlast per POST an diese URL senden", "ntfyServer": "Server-URL", "ntfyTopic": "Topic", "ntfyTopicRequired": "Topic ist erforderlich", "ntfyToken": "Zugriffstoken (optional)", "channelSaveFailed": "Kanal konnte nicht gespeichert werden", - "test": "Prüfen", + "test": "Test", "testSent": "Testbenachrichtigung gesendet", "testFailed": "Testbenachrichtigung fehlgeschlagen", - "addRule": "Warnregel hinzufügen", - "editRule": "Warnregel bearbeiten", + "addRule": "Alarmregel hinzufügen", + "editRule": "Alarmregel bearbeiten", "ruleName": "Regelname", - "ruleNameRequired": "Name erforderlich", + "ruleNameRequired": "Name ist erforderlich", "triggerType": "Auslöser", - "thresholdValue": "Schwelle (%)", - "durationSeconds": "Dauer (Sekunden, 0 = sofortiges Auslösen)", + "thresholdValue": "Schwellenwert (%)", + "durationSeconds": "Dauer (Sekunden, 0 = sofort auslösen)", "cooldownMinutes": "Abklingzeit (Minuten)", "channels": "Benachrichtigungskanäle", - "noChannelsHint": "Fügen Sie zuerst Kanäle auf der Registerkarte „Kanäle“ hinzu.", + "noChannelsHint": "Fügen Sie zuerst Kanäle im Tab 'Kanäle' hinzu", "ruleSaveFailed": "Regel konnte nicht gespeichert werden" } } diff --git a/src/ui/locales/translated/el_GR.json b/src/ui/locales/translated/el_GR.json index 13c66583..4500c33f 100644 --- a/src/ui/locales/translated/el_GR.json +++ b/src/ui/locales/translated/el_GR.json @@ -389,6 +389,45 @@ "noSavedServers": "Δεν υπάρχουν αποθηκευμένοι διακομιστές", "removeServer": "Αφαιρώ" }, + "migrationNotice": { + "title": "Το Termix Desktop λειτουργεί πλέον αυτόνομα.", + "body1": "Αυτή η εφαρμογή συνδεόταν απευθείας με τον διακομιστή Termix. Έχει αναδιαμορφωθεί ώστε να λειτουργεί πλήρως αυτόνομα, αποθηκεύοντας τοπικά τους κεντρικούς υπολογιστές και τα διαπιστευτήρια, ώστε να συνεχίζει να λειτουργεί εκτός σύνδεσης. Ο αμφίδρομος συγχρονισμός με έναν αυτο-φιλοξενούμενο διακομιστή Termix είναι πλέον προαιρετικός.", + "body2": "Οι υπάρχοντες κεντρικοί υπολογιστές και τα διαπιστευτήριά σας εξακολουθούν να βρίσκονται στη θέση {{url}}. Ενεργοποιήστε τον Απομακρυσμένο συγχρονισμό και συνδεθείτε ξανά σε αυτόν τον διακομιστή για να τα επαναφέρετε και να διατηρήσετε και τα δύο συγχρονισμένα στο μέλλον.", + "dismiss": "Όχι τώρα", + "setUpSync": "Ρύθμιση απομακρυσμένου συγχρονισμού" + }, + "remoteSync": { + "title": "Απομακρυσμένος συγχρονισμός", + "description": "Προαιρετικά, συνδέστε αυτήν την εφαρμογή για υπολογιστές σε έναν αυτο-φιλοξενούμενο διακομιστή Termix για να συγχρονίσετε τους κεντρικούς υπολογιστές, τα διαπιστευτήρια και τα αποσπάσματα σε όλες τις συσκευές. Η εφαρμογή λειτουργεί πάντα πλήρως εκτός σύνδεσης, ανεξάρτητα από το αν συνδεθείτε ή όχι.", + "notConnected": "Δεν είναι συνδεδεμένο", + "connected": "Συνδεδεμένος", + "connectedTo": "Συνδεδεμένο με {{url}}", + "lastSynced": "Τελευταίος συγχρονισμός {{time}}", + "neverSynced": "Δεν συγχρονίστηκε ποτέ", + "syncError": "Σφάλμα συγχρονισμού: {{message}}", + "needsReauth": "Η σύνδεση έληξε", + "connectButton": "Σύνδεση με διακομιστή", + "disconnectButton": "Αποσυνδέω", + "syncNowButton": "Συγχρονισμός τώρα", + "syncing": "Συγχρονισμός...", + "serverUrl": "URL διακομιστή", + "enterServerUrl": "Παρακαλώ εισάγετε μια διεύθυνση URL διακομιστή", + "mustIncludeProtocol": "Η διεύθυνση URL του διακομιστή πρέπει να ξεκινά με http:// ή https://", + "connectionTestFailed": "Δεν ήταν δυνατή η επικοινωνία με έναν διακομιστή Termix σε αυτήν τη διεύθυνση URL", + "allowInvalidCertificate": "Να επιτρέπεται το μη έγκυρο πιστοποιητικό", + "allowInvalidCertificateDesc": "Χρησιμοποιήστε μόνο για αξιόπιστους αυτο-φιλοξενούμενους διακομιστές με αυτο-υπογεγραμμένα πιστοποιητικά ή πιστοποιητικά διεύθυνσης IP.", + "savedServers": "Αποθηκευμένοι διακομιστές", + "removeServer": "Αφαιρώ", + "continueButton": "Συνεχίζω", + "cancelButton": "Ματαίωση", + "signInTitle": "Συνδεθείτε στο {{url}}", + "originTitle": "Προέλευση σύνδεσης", + "originDescription": "Επιλέξτε από προεπιλογή την προέλευση των συνδέσεων SSH. Αυτό μπορεί να παρακαμφθεί ανά κεντρικό υπολογιστή.", + "originLocal": "Αυτή η συσκευή (τοπικό δίκτυο)", + "originRemote": "Απομακρυσμένος διακομιστής", + "bannerReconnect": "Επανασύνδεση", + "bannerMessage": "Ο απομακρυσμένος συγχρονισμός απαιτεί εκ νέου έλεγχο ταυτότητας" + }, "versionCheck": { "error": "Σφάλμα ελέγχου έκδοσης", "checkFailed": "Αποτυχία ελέγχου για ενημερώσεις", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Παράκαμψη ονόματος χρήστη διαπιστευτηρίων", "overrideCredentialUsernameDesc": "Χρησιμοποιήστε το όνομα χρήστη που καθορίστηκε παραπάνω αντί για το όνομα χρήστη του πιστοποιητικού", "oidcUsernameHint": "Χρησιμοποιήστε το $oidc.preferred_username για να αντικαταστήσετε το όνομα σύνδεσης OIDC.", + "tailscaleUsernameHint": "Αυτός πρέπει να είναι χρήστης Unix. Η ταυτότητα Tailscale σας έχει εκχωρηθεί στο SSH ACL του tailnet, όχι απαραίτητα root.", "jumpHostChain": "Αλυσίδα υποδοχής άλματος", "portKnocking": "Χτύπημα στο λιμάνι", "addKnock": "Προσθήκη θύρας", @@ -650,6 +690,11 @@ "delayAfterMs": "Καθυστέρηση μετά (ms)", "useSocks5Proxy": "Χρήση διακομιστή μεσολάβησης SOCKS5", "useSocks5ProxyDesc": "Δρομολόγηση σύνδεσης μέσω διακομιστή μεσολάβησης", + "connectionOrigin": "Προέλευση σύνδεσης", + "connectionOriginDesc": "Από πού προέρχεται η σύνδεση SSH αυτού του κεντρικού υπολογιστή. Παρακάμπτει την καθολική προεπιλογή της εφαρμογής για υπολογιστές.", + "connectionOriginDefault": "Χρήση προεπιλογής", + "connectionOriginLocal": "Αυτή η συσκευή (τοπικό δίκτυο)", + "connectionOriginRemote": "Απομακρυσμένος διακομιστής", "proxyHost": "Κεντρικός υπολογιστής μεσολάβησης", "proxyPort": "Θύρα διακομιστή μεσολάβησης", "proxyUsername": "Όνομα χρήστη διακομιστή μεσολάβησης", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Φωτεινό κυανό", "customThemeBrightWhite": "Φωτεινό λευκό", "customThemeResetTooltip": "Επαναφορά στις προεπιλογές", + "savedThemesLabel": "Αποθηκευμένα Θέματα", + "saveAsGlobalTheme": "Αποθήκευση ως καθολικό θέμα", + "saveGlobalThemeNamePrompt": "Εισαγάγετε ένα όνομα για αυτό το θέμα", + "saveGlobalThemeSuccess": "Το θέμα αποθηκεύτηκε", + "saveGlobalThemeError": "Η αποθήκευση του θέματος απέτυχε", + "applyGlobalThemeTooltip": "Εφαρμογή αυτού του θέματος", + "deleteGlobalThemeTooltip": "Διαγραφή αυτού του θέματος", + "noSavedThemes": "Δεν υπάρχουν ακόμη αποθηκευμένα θέματα", "syntaxHighlightingLabel": "Επισήμανση σύνταξης", "syntaxHighlightingDesc": "Χρωματισμός εξόδου τερματικού (σφάλματα, διαδρομές, IP, χρονικές σημάνσεις)", "syntaxHighlightingCategories": "Επισημάνετε κατηγορίες", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Αυτόματη εκκίνηση ή σύνδεση σε συνεδρία tmux", "enableSessionLogging": "Καταγραφή συνεδρίας", "enableSessionLoggingDesc": "Καταγραφή εξόδου συνεδρίας τερματικού για μελλοντική αναθεώρηση", + "allowSessionSharing": "Επιτρέψτε την κοινή χρήση περιόδου σύνδεσης", + "allowSessionSharingDesc": "Να επιτρέπεται η κοινοποίηση ζωντανών συνεδριών σε αυτόν τον πάροχο μέσω συνδέσμου ή με άλλους χρήστες", "enableCommandHistory": "Ιστορικό εντολών", "enableCommandHistoryDesc": "Οι εντολές εγγραφής εκτελούνται σε αυτό το τερματικό για ιστορικό και αυτόματη συμπλήρωση", "linkClickBehaviorLabel": "Συμπεριφορά κλικ σε σύνδεσμο", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} εισήχθη", "proxmoxResultUpdated": "{{count}} ενημερώθηκε", "proxmoxResultFailed": "{{count}} απέτυχε", + "proxmoxResultSkippedNoIp": "{{count}} παραλείφθηκε (δεν βρέθηκε IP)", "proxmoxImportComplete": "Η εισαγωγή Proxmox ολοκληρώθηκε: {{summary}}", "proxmoxDiscoveryFailed": "Η ανακάλυψη απέτυχε", "proxmoxImportFailed": "Η εισαγωγή απέτυχε.", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Αποτυχία αποθήκευσης διαπιστευτηρίων", "credentialNameRequired": "Παρακαλώ εισάγετε ένα όνομα για τα διαπιστευτήρια", "credentialAuthRequired": "Προσθήκη κωδικού πρόσβασης, κλειδιού SSH ή και των δύο", + "createCredentialFromHostBtn": "Δημιουργία διαπιστευτηρίων", + "createCredentialFromHostTitle": "Δημιουργία διαπιστευτηρίων από τον κεντρικό υπολογιστή", + "createCredentialFromHostDesc": "Δημιουργήστε μια επαναχρησιμοποιήσιμη και κοινόχρηστη καταχώριση διαπιστευτηρίων, προσυμπληρωμένη με το τρέχον όνομα χρήστη, τον κωδικό πρόσβασης ή/και το κλειδί SSH αυτού του κεντρικού υπολογιστή.", "backToHosts": "Πίσω στους οικοδεσπότες", "backToCredentials": "Επιστροφή στα Διαπιστευτήρια", "pinned": "Καρφιτσωμένο", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Χρησιμοποιήστε τα / για να διαχωρίσετε τα επίπεδα και να δημιουργήσετε ένθετους φακέλους.", "folderColor": "Χρώμα", "folderIcon": "Εικόνισμα", + "folderCredential": "Πιστοποιητικό", + "folderCredentialNone": "Δεν έχουν εκχωρηθεί πιστοποιητικά", + "folderCredentialHint": "Οι κεντρικοί υπολογιστές σε αυτόν τον φάκελο που χρησιμοποιούν έλεγχο ταυτότητας \"Αποθηκευμένα διαπιστευτήρια\" χωρίς να έχουν επιλέξει τα δικά τους διαπιστευτήρια θα κληρονομήσουν αυτό το ένα.", "folderPreview": "Πρεμιέρα", "folderNameFallback": "Φάκελος χωρίς τίτλο", "createFolderButton": "Δημιουργία φακέλου", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Ετικέτες", "shareHost": "Κοινή χρήση κεντρικού υπολογιστή", "shareHostTitle": "Κοινή χρήση: {{name}}", + "shareFolder": "Κοινή χρήση φακέλου", + "shareFolderTitle": "Κοινόχρηστος φάκελος: {{name}}", + "folderSharedSuccessfully": "Κοινόχρηστοι {{count}} κεντρικοί υπολογιστές στον φάκελο", + "failedToShareFolder": "Αποτυχία κοινής χρήσης φακέλου", "sharing": { "loadError": "Αποτυχία φόρτωσης δεδομένων κοινής χρήσης. Δοκιμάστε ξανά.", "shareWithSection": "Κοινοποίηση με", @@ -1221,6 +1287,7 @@ "shareWithCount": "Κοινή χρήση ({{count}})", "currentAccess": "Τρέχουσα πρόσβαση", "noAccessEntries": "Αυτός ο κεντρικός υπολογιστής δεν έχει κοινοποιηθεί ακόμη", + "folderShareSummary": "Κοινόχρηστο {{shared}} από {{total}} κεντρικούς υπολογιστές σε αυτόν τον φάκελο", "grantedBy": "Χορηγήθηκε από", "expires": "Λήγει", "expired": "Λήξη", @@ -1242,6 +1309,8 @@ "authMethod": "Μέθοδος Εξουσιοδότησης", "authTypeDirect": "Απευθείας", "authTypeCredential": "Πιστοποιητικό", + "authTypeNone": "Κανένας", + "authTypeNoneDesc": "Δεν αποθηκεύονται διαπιστευτήρια. Θα σας ζητείται όνομα χρήστη και κωδικός πρόσβασης κάθε φορά που συνδέεστε. Δεν αποθηκεύονται.", "selectCredential": "Επιλέξτε ένα πιστοποιητικό...", "connectionSettings": "Ρυθμίσεις σύνδεσης", "displaySettings": "Ρυθμίσεις οθόνης", @@ -1259,6 +1328,7 @@ "rdpPort": "Θύρα RDP", "username": "Όνομα χρήστη", "password": "Σύνθημα", + "passwordSaved": "Ο κωδικός πρόσβασης αποθηκεύτηκε, πληκτρολογήστε για να τον αλλάξετε", "domain": "Πεδίο ορισμού", "securityMode": "Λειτουργία ασφαλείας", "colorDepth": "Βάθος χρώματος", @@ -1424,7 +1494,60 @@ "expiresIn": "Λήγει σε {{duration}}", "search": "Αναζήτηση συνδέσεων...", "noSearchResults": "Δεν υπάρχουν συνδέσεις που να αντιστοιχούν στην αναζήτησή σας", - "rename": "Μετονομασία συνεδρίας" + "rename": "Μετονομασία συνεδρίας", + "sectionSharedWithMe": "Κοινοποιήθηκε σε εμένα", + "sharedBy": "Κοινοποίηση από {{username}}", + "join": "Ενώνω", + "sharedSessionLabel": "{{hostName}} (κοινόχρηστο)" + }, + "sessionSharing": { + "guestView": { + "loading": "Σύνδεση σε κοινόχρηστη συνεδρία...", + "linkInvalid": "Αυτός ο σύνδεσμος κοινής χρήσης δεν είναι έγκυρος, έχει λήξει ή έχει ανακληθεί", + "rateLimited": "Πάρα πολλές προσπάθειες, δοκιμάστε ξανά σύντομα.", + "sessionEnded": "Αυτή η συνεδρία έληξε", + "readOnlyBadge": "Μόνο προβολή" + }, + "modalTitle": "Κοινή χρήση περιόδου σύνδεσης", + "shareButton": "Μερίδιο", + "notReadyToShare": "Η συνεδρία δεν είναι ακόμη έτοιμη για κοινή χρήση", + "modeTab": { + "link": "Σύνδεσμος", + "user": "Μεταχειριζόμενος" + }, + "linkModeDescription": "Οποιοσδήποτε έχει αυτόν τον σύνδεσμο μπορεί να συμμετάσχει, δεν απαιτείται λογαριασμός.", + "userModeDescription": "Κοινοποίηση με έναν συγκεκριμένο χρήστη που έχει ήδη πρόσβαση σε αυτόν τον κεντρικό υπολογιστή. Εάν δεν έχει ακόμη πρόσβαση, μοιραστείτε πρώτα τον κεντρικό υπολογιστή μαζί του ή χρησιμοποιήστε έναν σύνδεσμο. Μόλις κοινοποιηθεί, η συνεδρία εμφανίζεται στην καρτέλα Συνδέσεις.", + "permissionLevel": { + "label": "Επίπεδο δικαιωμάτων", + "readOnly": "Μόνο για ανάγνωση", + "readOnlyDescription": "Μπορώ να παρακολουθήσω την συνεδρία ζωντανά, αλλά δεν μπορώ να πληκτρολογήσω ή να αλληλεπιδράσω.", + "readWrite": "Ανάγνωση-εγγραφή", + "readWriteDescription": "Μπορεί να πληκτρολογήσει και να αλληλεπιδράσει με την περίοδο λειτουργίας όπως ο κάτοχος." + }, + "expiryLabel": "Λήξη σύνδεσης", + "createLinkButton": "Δημιουργία συνδέσμου", + "createShareButton": "Κοινοποίηση με χρήστη", + "searchUsersPlaceholder": "Αναζήτηση χρηστών...", + "noUsersFound": "Δεν βρέθηκαν χρήστες", + "linkCreated": "Ο σύνδεσμος κοινής χρήσης δημιουργήθηκε", + "linkCopied": "Ο σύνδεσμος αντιγράφηκε στο πρόχειρο", + "copyLink": "Αντιγραφή συνδέσμου", + "shareCreated": "Η συνεδρία κοινοποιήθηκε. Θα εμφανιστεί στην καρτέλα \"Συνδέσεις\".", + "shareFailed": "Η δημιουργία κοινής χρήσης απέτυχε", + "userLacksHostAccess": "Αυτός ο χρήστης δεν έχει ακόμη πρόσβαση σε αυτόν τον κεντρικό υπολογιστή. Κοινοποιήστε πρώτα τον κεντρικό υπολογιστή μαζί του ή χρησιμοποιήστε έναν σύνδεσμο.", + "activeShares": "Ενεργές κοινοποιήσεις", + "noActiveShares": "Δεν υπάρχουν ενεργές κοινοποιήσεις για αυτήν την περίοδο σύνδεσης", + "revoke": "Ανακαλώ", + "revokeConfirmTitle": "Ανάκληση αυτής της κοινοποίησης;", + "revokeConfirmDescription": "Όποιος χρησιμοποιεί αυτήν την κοινοποίηση θα χάσει αμέσως την πρόσβαση.", + "revoked": "Η κοινοποίηση ανακλήθηκε", + "revokeFailed": "Η ανάκληση της κοινής χρήσης απέτυχε", + "joinCount": "{{count}} συμμετοχή", + "joinCount_other": "{{count}} ενώσεις", + "expiresAt": "Λήγει {{date}}", + "linkShareBadge": "Σύνδεσμος", + "userShareBadge": "Χρήστης: {{username}}", + "loadSharesFailed": "Αποτυχία φόρτωσης ενεργών κοινόχρηστων στοιχείων" }, "guacamole": { "connecting": "Σύνδεση σε συνεδρία {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Επανασύνδεση", "retry": "Δοκιμάζω πάλι", "guacdUnavailable": "Η υπηρεσία απομακρυσμένης επιφάνειας εργασίας (guacd) δεν είναι διαθέσιμη. Βεβαιωθείτε ότι το guacd εκτελείται και είναι προσβάσιμο, καθώς και ότι έχει ρυθμιστεί σωστά στις ρυθμίσεις διαχειριστή.", + "credentialPromptTitle": "Εισαγάγετε τα διαπιστευτήρια RDP", + "credentialPromptDescription": "Αυτός ο κεντρικός υπολογιστής έχει ρυθμιστεί να ζητά διαπιστευτήρια κατά τη σύνδεση. Χρησιμοποιούνται μόνο για αυτήν την περίοδο σύνδεσης και δεν αποθηκεύονται.", + "connect": "Συνδέω", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Επανασύνδεση περιόδου σύνδεσης", "collapse": "Σύμπτυξη γραμμής εργαλείων", "expand": "Ανάπτυξη γραμμής εργαλείων", - "dragHandle": "Σύρετε για αλλαγή θέσης" + "dragHandle": "Σύρετε για αλλαγή θέσης", + "switchToTrackpad": "Μετάβαση σε λειτουργία trackpad (σύρσιμο για μετακίνηση του κέρσορα, άγγιγμα για κλικ)", + "switchToTouch": "Μετάβαση σε λειτουργία αφής (πατήστε απευθείας εκεί που θέλετε να κάνετε κλικ)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Απαιτείται έλεγχος ταυτότητας δύο παραγόντων", "totpCodeLabel": "Κωδικός επαλήθευσης", "totpVerify": "Επαληθεύω", + "mfaPromptRequired": "Απαιτείται έλεγχος ταυτότητας", + "mfaPushRequired": "Απαιτείται έλεγχος ταυτότητας push", + "mfaMenuPlaceholder": "Εισαγάγετε την απάντησή σας", + "mfaWaitingApproval": "Αναμονή για έγκριση στη συσκευή σας...", + "mfaSendRequest": "Αποστολή αιτήματος", "warpgateAuthRequired": "Απαιτείται έλεγχος ταυτότητας Warpgate", "warpgateSecurityKey": "Κλειδί ασφαλείας", "warpgateAuthUrl": "URL ελέγχου ταυτότητας", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Περιορισμένο ποσοστό: Πάρα πολλές προσπάθειες επαλήθευσης. Δοκιμάστε ξανά αργότερα.", "resetCodeRateLimitedWithTime": "Περιορισμένος ρυθμός: Πάρα πολλές προσπάθειες επαλήθευσης. Παρακαλώ περιμένετε {{time}} δευτερόλεπτα πριν προσπαθήσετε ξανά.", "authTokenSaveFailed": "Αποτυχία αποθήκευσης διακριτικού ελέγχου ταυτότητας", - "failedToLoadServer": "Αποτυχία φόρτωσης διακομιστή" + "failedToLoadServer": "Αποτυχία φόρτωσης διακομιστή", + "remoteServerRequired": "Απαιτείται απομακρυσμένος διακομιστής. Συνδέστε έναν απομακρυσμένο διακομιστή στις Ρυθμίσεις για να χρησιμοποιήσετε αυτόν τον τύπο σύνδεσης." }, "messages": { "registrationDisabled": "Η εγγραφή νέου λογαριασμού έχει απενεργοποιηθεί αυτήν τη στιγμή από έναν διαχειριστή. Συνδεθείτε ή επικοινωνήστε με έναν διαχειριστή.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Αποτυχία αποθήκευσης ρυθμίσεων SSL", "sslRequiresDomain": "Απαιτούνται ο τομέας και το email", "sslInfoNote": "Αφού εκδώσετε ένα πιστοποιητικό, ενεργοποιήστε το SSL στις μεταβλητές περιβάλλοντος (ENABLE_SSL=true) και επανεκκινήστε το Termix.", + "sslManualOption": "Εγχειρίδιο (μεταφόρτωση πιστοποιητικού)", + "sslManualCert": "Πιστοποιητικό (PEM)", + "sslManualCertPlaceholder": "-----ΕΝΑΡΞΗ ΠΙΣΤΟΠΟΙΗΤΙΚΟΥ-----", + "sslManualKey": "Ιδιωτικό Κλειδί (PEM)", + "sslManualKeyPlaceholder": "-----ΕΝΑΡΞΗ ΙΔΙΩΤΙΚΟΥ ΚΛΕΙΔΙΟΥ-----", + "sslManualDesc": "Επικολλήστε το υπάρχον πιστοποιητικό και το ιδιωτικό κλειδί σας, συμπεριλαμβανομένης μιας πλήρους αλυσίδας, εάν απαιτείται από την CA σας.", + "sslManualUpload": "Μεταφόρτωση και εγκατάσταση πιστοποιητικού", + "sslManualUploadLoading": "Μεταφόρτωση πιστοποιητικού...", + "sslManualUploadSuccess": "Το πιστοποιητικό μεταφορτώθηκε και εγκαταστάθηκε με επιτυχία.", + "sslManualUploadFailed": "Η μεταφόρτωση του πιστοποιητικού απέτυχε.", + "sslManualRequiresFields": "Απαιτείται πιστοποιητικό και ιδιωτικό κλειδί", "auditLogTotal": "{{total}} συνολικές καταχωρήσεις", "auditLogEmpty": "Δεν βρέθηκαν καταχωρήσεις αρχείου καταγραφής ελέγχου", "auditLogSuccess": "Επιτυχία", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Ιστορικό εντολών", "commandHistoryEnabledDesc": "Να επιτρέπεται η καταγραφή ιστορικού εντολών. Όταν είναι απενεργοποιημένη, το ιστορικό δεν αποθηκεύεται ανεξάρτητα από τις ρυθμίσεις ανά κεντρικό υπολογιστή.", "updateCommandHistoryFailed": "Αποτυχία ενημέρωσης της ρύθμισης ιστορικού εντολών", + "analyticsEnabled": "Κοινοποίηση ανώνυμων στατιστικών χρήσης", + "analyticsEnabledDesc": "Αποστέλλει έναν ανώνυμο ημερήσιο αριθμό χρηστών, κεντρικών υπολογιστών και χρήσης λειτουργιών για να βοηθήσει στη βελτίωση του Termix. Δεν περιλαμβάνονται ποτέ προσωπικά δεδομένα ή λεπτομέρειες σύνδεσης.", + "updateAnalyticsFailed": "Η ενημέρωση της ρύθμισης αναλυτικών στοιχείων απέτυχε", + "sessionSharingGloballyEnabled": "Επιτρέψτε την κοινή χρήση περιόδου σύνδεσης", + "sessionSharingGloballyEnabledDesc": "Επιτρέπεται η κοινή χρήση των ζωντανών συνεδριών τερματικού, RDP, VNC και Telnet σε ολόκληρη την παρουσία. Παρακάμπτει κάθε εναλλαγή κοινής χρήσης ανά κεντρικό υπολογιστή όταν είναι απενεργοποιημένη.", + "updateSessionSharingFailed": "Αποτυχία ενημέρωσης ρύθμισης κοινής χρήσης περιόδου σύνδεσης", "sessionTimeout": "Χρονικό όριο συνεδρίας", "hours": "ώρες", "sessionTimeoutRange": "Ελάχ. 1 ώρα · Μέγ. 720 ώρες", @@ -3119,6 +3270,47 @@ "executionSuccess": "Επιτυχία", "executionFailed": "Αποτυχημένος" }, + "keybindings": { + "title": "Συντομεύσεις πληκτρολογίου", + "description": "Προσαρμόστε τις συντομεύσεις αντιγραφής, επικόλλησης και ελέγχου τερματικού ή συνδέστε τα πλήκτρα για αποστολή κειμένου ή εκτέλεση ενός αποσπάσματος.", + "defaultsHeading": "Ενσωματωμένες συντομεύσεις", + "customHeading": "Προσαρμοσμένες συντομεύσεις", + "addBinding": "Προσθήκη συντόμευσης", + "addBindingTitle": "Προσθήκη συντόμευσης", + "editBindingTitle": "Επεξεργασία συντόμευσης", + "loading": "Φόρτωση...", + "noCustomBindings": "Δεν υπάρχουν ακόμη προσαρμοσμένες συντομεύσεις", + "defaultBadge": "Αθέτηση", + "customizedBadge": "Προσαρμοσμένη", + "customize": "Προσαρμογή", + "resetToDefault": "Επαναφορά στις προεπιλογές", + "resetAllToDefaults": "Επαναφορά όλων στις προεπιλογές", + "close": "Κοντά", + "cancel": "Ματαίωση", + "saveBinding": "Αποθήκευση συντόμευσης", + "saveError": "Η αποθήκευση των συντομεύσεων πληκτρολογίου απέτυχε", + "pressKeysToRecord": "Συνδυασμός πλήκτρων", + "pressKeysPlaceholder": "Κάντε κλικ και πατήστε τα πλήκτρα", + "recording": "Πατήστε τα πλήκτρα...", + "comboRequiredError": "Πατήστε πρώτα έναν συνδυασμό πλήκτρων", + "textRequiredError": "Εισαγάγετε το κείμενο που θέλετε να στείλετε", + "controlCodeRequiredError": "Εισαγάγετε ένα μόνο γράμμα για τον κωδικό ελέγχου", + "snippetRequiredError": "Επιλέξτε ένα απόσπασμα", + "conflictWarning": "Αυτός ο συνδυασμός είναι ήδη συνδεδεμένος με {{combo}}. Η αποθήκευση θα ενεργοποιήσει και τα δύο.", + "actionLabel": "Δράση", + "actionCopy": "Αντιγραφή επιλογής", + "actionPaste": "Πάστα", + "actionSendControlCode": "Αποστολή σήματος Ctrl+γράμμα", + "actionSendText": "Αποστολή κειμένου", + "actionRunSnippet": "Εκτέλεση υπάρχοντος αποσπάσματος", + "controlCodeLabel": "Επιστολή", + "textLabel": "Μήνυμα προς αποστολή", + "appendEnterLabel": "Πατήστε Enter μετά την αποστολή", + "snippetLabel": "Απόσπασμα", + "selectSnippetPlaceholder": "Επιλέξτε ένα απόσπασμα", + "orphanedSnippetWarning": "το απόσπασμα δεν βρέθηκε", + "clipboardPermissionNote": "Ενδέχεται να ζητηθεί άδεια για το πρόχειρο κατά την πρώτη χρήση σε ορισμένα προγράμματα περιήγησης." + }, "userProfile": { "donateTitle": "Κρατήστε το Termix ζωντανό", "donateDescription": "Το Termix είναι δωρεάν, αυτο-φιλοξενείται και δημιουργείται από λίγα μόνο άτομα στον ελεύθερο χρόνο τους. Αν σας έχει εξοικονομήσει χρόνο ή χρήματα, μια δωρεά κρυπτονομισμάτων βοηθάει στη συνέχιση της λειτουργίας του.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Εμφάνιση", "sectionSecurity": "Ασφάλεια", "sectionApiKeys": "Κλειδιά API", + "sectionData": "Δεδομένα", "sectionC2sTunnels": "Σήραγγες C2S", "usernameLabel": "Όνομα χρήστη", "roleLabel": "Ρόλος", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Τερματικό", "commandAutocomplete": "Αυτόματη συμπλήρωση εντολών", "commandAutocompleteDesc": "Εμφάνιση αυτόματης συμπλήρωσης κατά την πληκτρολόγηση", + "keyboardShortcuts": "Συντομεύσεις πληκτρολογίου", + "keyboardShortcutsDescription": "Προσαρμόστε τις συντομεύσεις αντιγραφής, επικόλλησης και εντολών στο τερματικό", + "manageShortcuts": "Διαχειρίζομαι", "terminalLinkBehavior": "Κλικ στον σύνδεσμο τερματικού", "terminalLinkBehaviorDesc": "Προεπιλεγμένη συμπεριφορά κατά το κλικ σε συνδέσμους στο τερματικό", "historyTracking": "Παρακολούθηση ιστορικού", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Συμπεριλάβετε το κλειδί σας στο", "apiKeyUsageHintHeader": "επί κεφαλής.", "apiKeyPermissionsHint": "Τα κλειδιά κληρονομούν τα δικαιώματα του χρήστη που τα δημιούργησε.", + "exportData": "Εξαγωγή των δεδομένων μου", + "exportDataDesc": "Λήψη αντιγράφου ασφαλείας των κεντρικών υπολογιστών, των διαπιστευτηρίων και των ρυθμίσεών σας για μεταφορά σε άλλη συσκευή", + "export": "Εξαγωγή", + "exporting": "Εξαγωγή...", + "importData": "Εισαγωγή των δεδομένων μου", + "importDataDesc": "Επαναφέρετε τους κεντρικούς υπολογιστές, τα διαπιστευτήρια και τις ρυθμίσεις σας από ένα αρχείο αντιγράφου ασφαλείας .sqlite", + "importDataSelected": "Επιλεγμένα: {{name}}", + "selectFile": "Επιλογή Αρχείου", + "changeFile": "Αλλαγή", + "import": "Εισαγωγή", + "importing": "Εισαγωγή...", + "exportSuccess": "Τα δεδομένα εξήχθησαν με επιτυχία.", + "exportFailed": "Η εξαγωγή δεδομένων απέτυχε.", + "importSelectFile": "Επιλέξτε πρώτα ένα αρχείο", + "importCompleted": "Η εισαγωγή ολοκληρώθηκε: {{total}} στοιχεία εισήχθησαν, {{skipped}} παραλείφθηκαν", + "importFailed": "Η εισαγωγή απέτυχε: {{error}}", "roleUser": "Μεταχειριζόμενος", "authMethodDual": "Διπλή Εξουσιοδότηση", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Διαγραφή", + "paste": "Πάστα", "editQuickKeys": "Επεξεργασία γρήγορων πλήκτρων", "quickKeysTitle": "Γρήγορα πλήκτρα", "quickKeysDesc": "Πατήστε × για κατάργηση. Υποστηρίζει έως και 8 χαρακτήρες.", diff --git a/src/ui/locales/translated/es_ES.json b/src/ui/locales/translated/es_ES.json index e22e5637..438bed59 100644 --- a/src/ui/locales/translated/es_ES.json +++ b/src/ui/locales/translated/es_ES.json @@ -1,115 +1,115 @@ { "termixId": { - "title": "ID de Termix", - "loadFailed": "No se pudo cargar el ID de Termix.", - "claimTitle": "Reclama tu ID de Termix", - "claimIntro": "Elige un identificador único. Tus claves públicas SSH se publicarán en una URL pública que podrás añadir al archivo authorized_keys de cualquier servidor.", - "handleLabel": "Manejar", - "handlePlaceholder": "Alicia", + "title": "Termix ID", + "loadFailed": "Error al cargar Termix ID", + "claimTitle": "Reclama tu Termix ID", + "claimIntro": "Elige un identificador único. Tus claves públicas SSH se publicarán en una URL pública que puedes añadir al archivo authorized_keys de cualquier servidor.", + "handleLabel": "Identificador", + "handlePlaceholder": "alice", "checking": "Comprobando…", "available": "Disponible", - "taken": "Ya apartadas", + "taken": "Ya está en uso", "invalidHandle": "Solo letras minúsculas, números, - y _", "descriptionLabel": "Descripción (opcional)", - "descriptionPlaceholder": "Teclado del portátil y del teléfono del trabajo", - "create": "Crear ID de Termix", - "created": "ID de Termix creado", - "createFailed": "No se pudo crear el ID de Termix.", - "deleteConfirm": "¿Debes eliminar tu ID de Termix y todas las claves publicadas? Los servidores que hayas configurado conservarán las claves hasta que las elimines manualmente.", - "deleted": "ID de Termix eliminado", - "deleteFailed": "No se pudo eliminar el ID de Termix.", + "descriptionPlaceholder": "Claves del portátil de trabajo y del teléfono", + "create": "Crear Termix ID", + "created": "Termix ID creado", + "createFailed": "Error al crear Termix ID", + "deleteConfirm": "¿Eliminar tu Termix ID y todas las claves publicadas? Los servidores que aprovisionaste conservarán las claves hasta que las elimines manualmente.", + "deleted": "Termix ID eliminado", + "deleteFailed": "Error al eliminar Termix ID", "copyFailed": "Error al copiar", - "resolverUrlLabel": "URL del resolvedor público", + "resolverUrlLabel": "URL pública de resolución", "provisionLabel": "Aprovisionar un servidor", "publishTitle": "Publicar una clave pública", "generate": "Generar", - "generateTooltip": "Genera un par de claves Ed25519: publica la clave pública y descarga la clave privada en tu dispositivo.", + "generateTooltip": "Generar un par de claves Ed25519: publica la clave pública y descarga la clave privada en tu dispositivo", "saveToVault": "Guardar en credenciales", - "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... usuario@host", + "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Etiqueta (opcional)", - "add": "Agregar", - "importFromCredential": "O importar desde una credencial almacenada.", + "add": "Añadir", + "importFromCredential": "O importar desde una credencial almacenada", "keyPublished": "Clave publicada", - "addKeyFailed": "Error al agregar la clave", - "generatedSaved": "Se generó y guardó el par de claves en su bóveda de credenciales. También se descargó la clave privada.", + "addKeyFailed": "Error al añadir clave", + "generatedSaved": "Par de claves generado y guardado en tu almacén de credenciales. También se descargó la clave privada.", "generatedOnly": "Par de claves generado: clave privada descargada (se muestra solo una vez).", - "generateFailed": "No se pudo generar la clave.", - "imported": "Clave importada de la credencial", + "generateFailed": "Error al generar clave", + "imported": "Clave importada desde la credencial", "importFailed": "Error al importar la clave", - "noKeys": "Aún no se han publicado las claves públicas.", + "noKeys": "Aún no se han publicado claves públicas.", "keysTitle": "Claves publicadas", - "published": "Publicado", - "hidden": "Oculto", - "keyRemoved": "Llave extraída", - "removeKeyFailed": "No se pudo extraer la clave.", + "published": "Publicada", + "hidden": "Oculta", + "keyRemoved": "Clave eliminada", + "removeKeyFailed": "Error al eliminar la clave", "updateKeyFailed": "Error al actualizar la clave", - "fromVault": "Desde el repositorio de credenciales", - "linkedToTermixId": "Publicado a través de Termix ID", - "selectCredential": "Seleccione una credencial...", + "fromVault": "Del almacén de credenciales", + "linkedToTermixId": "Publicada mediante Termix ID", + "selectCredential": "Seleccionar una credencial...", "import": "Importar", - "caTitle": "Autoridad certificadora", - "caIntro": "Confía en esta CA en un servidor y aceptará cualquier certificado que firmes. Rota los certificados para revocarlos todos a la vez; además, los certificados caducan automáticamente.", - "caEnable": "Habilitar CA", - "caEnabled": "CA habilitado", - "caCreateFailed": "No se pudo habilitar CA", - "caPublicKeyLabel": "clave pública de CA", - "caTrustLabel": "Confianza en un servidor (ejecutado como root)", - "caRotate": "Girar", - "caRotateConfirm": "¿Cambiar la CA? Todos los certificados que haya firmado dejarán de ser aceptados y deberán ser reemitidos.", - "caRotated": "La CA rotó; los certificados anteriores fueron revocados.", - "caRotateFailed": "No se pudo rotar CA", + "caTitle": "Autoridad de certificación", + "caIntro": "Confíe en esta CA en un servidor y aceptará cualquier certificado que firme. Rote para revocar todo a la vez; los certificados también caducan por sí solos.", + "caEnable": "Activar CA", + "caEnabled": "CA activada", + "caCreateFailed": "Error al activar la CA", + "caPublicKeyLabel": "Clave pública de la CA", + "caTrustLabel": "Confiar en un servidor (ejecutar como root)", + "caRotate": "Rotar", + "caRotateConfirm": "¿Rotar la CA? Todos los certificados que ha firmado dejarán de ser aceptados y deberán volver a emitirse.", + "caRotated": "CA rotada — certificados anteriores revocados", + "caRotateFailed": "Error al rotar la CA", "caDelete": "Eliminar CA", "caDeleteConfirm": "¿Eliminar la autoridad de certificación?", - "caDeleted": "CA eliminó", - "caDeleteFailed": "No se pudo eliminar CA", + "caDeleted": "CA eliminada", + "caDeleteFailed": "Error al eliminar la CA", "caValidityLabel": "Validez del certificado (días)", "issueCert": "Certificado", - "issueCertTooltip": "Emita un certificado SSH para esta clave, firmado por su CA.", + "issueCertTooltip": "Emitir un certificado SSH para esta clave, firmado por su CA", "certIssued": "Certificado emitido y descargado", - "certIssueFailed": "No se pudo emitir el certificado." + "certIssueFailed": "Error al emitir el certificado" }, "credentials": { "folders": "Carpetas", "folder": "Carpeta", "password": "Contraseña", - "key": "Llave", + "key": "Clave", "sshPrivateKey": "Clave privada SSH", "upload": "Subir", "keyPassword": "Contraseña de la clave", "sshKey": "Clave SSH", "uploadPrivateKeyFile": "Subir archivo de clave privada", - "searchCredentials": "Credenciales de búsqueda...", - "addCredential": "Agregar credencial", + "searchCredentials": "Buscar credenciales...", + "addCredential": "Añadir credencial", "caCertificate": "Certificado CA (-cert.pub)", - "caCertificateDescription": "Opcional: Cargue o pegue el archivo del certificado firmado por la CA (por ejemplo, id_ed25519-cert.pub). Esto es necesario si su servidor SSH utiliza autorización basada en certificados.", + "caCertificateDescription": "Opcional: Suba o pegue el archivo de certificado firmado por CA (p. ej., id_ed25519-cert.pub). Necesario cuando su servidor SSH utiliza autorización basada en certificados.", "uploadCertFile": "Subir archivo -cert.pub", - "clearCert": "Claro", + "clearCert": "Limpiar", "certLoaded": "Certificado cargado", "certPublicKeyLabel": "Certificado CA", "certTypeLabel": "Tipo de certificado", - "pasteOrUploadCert": "Pegue o cargue un certificado -cert.pub...", - "hasCaCert": "Posee certificado CA", + "pasteOrUploadCert": "Pegar o subir un certificado -cert.pub...", + "hasCaCert": "Tiene certificado CA", "noCaCert": "Sin certificado CA", "noPublicKeyAvailable": "No hay clave pública disponible. Abra primero el editor de credenciales.", "deployCommandCopied": "Comando de despliegue copiado", "sortCredentials": "Ordenar credenciales", - "sortDefault": "Orden predeterminada", + "sortDefault": "Orden predeterminado", "sortNameAsc": "Nombre (A → Z)", "sortNameDesc": "Nombre (Z → A)", - "sortUsernameAsc": "Nombre de usuario (A → Z)", - "sortUsernameDesc": "Nombre de usuario (Z → A)", + "sortUsernameAsc": "Usuario (A → Z)", + "sortUsernameDesc": "Usuario (Z → A)", "filterCredentials": "Filtrar credenciales", - "filterClearAll": "Borrar filtros", + "filterClearAll": "Limpiar filtros", "filterTypeGroup": "Tipo", "filterTypePassword": "Contraseña", "filterTypeKey": "Clave SSH", "filterTagsGroup": "Etiquetas" }, "homepage": { - "title": "Página principal", - "addWidget": "Agregar widget", + "title": "Inicio", + "addWidget": "Añadir widget", "editWidget": "Editar widget", - "deleteWidget": "Borrar", + "deleteWidget": "Eliminar", "widgetTypes": "Tipos de widgets", "serviceLink": "Enlace de servicio", "folder": "Carpeta", @@ -117,21 +117,21 @@ "notes": "Notas", "hostStatus": "Estado del host", "bookmarkList": "Marcadores", - "zoomIn": "Dar un golpe de zoom", - "zoomOut": "Aleja la imagen", + "zoomIn": "Acercar", + "zoomOut": "Alejar", "resetView": "Restablecer vista", - "lockLayout": "Diseño de la cerradura", + "lockLayout": "Bloquear diseño", "unlockLayout": "Desbloquear diseño", - "noWidgets": "Haz clic con el botón derecho o haz clic en + para añadir tu primer widget.", + "noWidgets": "Haz clic derecho o pulsa + para añadir tu primer widget", "openFullView": "Abrir vista completa", "previewTitle": "Vista previa de la página de inicio", "cancel": "Cancelar", - "save": "Ahorrar", + "save": "Guardar", "title_label": "Título", "widgetTitlePlaceholder": "Título del widget (opcional)", "url": "URL", "imageUrl": "URL de imagen personalizada", - "imageUrlHint": "Déjelo en blanco para usar el favicon del sitio automáticamente.", + "imageUrlHint": "Déjalo en blanco para usar el favicon del sitio automáticamente", "showImage": "Mostrar imagen", "description": "Descripción", "color": "Color", @@ -143,281 +143,320 @@ "format24h": "24 horas", "content": "Contenido", "backgroundColor": "Color de fondo", - "host": "Anfitrión", + "host": "Host", "showMetrics": "Mostrar métricas", - "links": "Campo de golf", - "addLink": "Agregar enlace", + "links": "Enlaces", + "addLink": "Añadir enlace", "linkLabel": "Etiqueta", "linkUrl": "URL", "removeLink": "Eliminar", - "categoryLinks": "Campo de golf", + "categoryLinks": "Enlaces", "categoryInfo": "Información", "categorySystem": "Sistema", - "widgetServiceLinkDesc": "Un mosaico en el que se puede hacer clic y que enlaza con la URL de un servicio.", - "widgetFolderDesc": "Un contenedor para agrupar widgets relacionados.", - "widgetClockDesc": "Un reloj en tiempo real con zona horaria configurable", - "widgetNotesDesc": "Un widget de notas Markdown", - "widgetHostStatusDesc": "Muestra en tiempo real la CPU, la memoria y el disco de un host SSH.", + "widgetServiceLinkDesc": "Un mosaico interactivo que enlaza a una URL de servicio", + "widgetFolderDesc": "Un contenedor para agrupar widgets relacionados", + "widgetClockDesc": "Un reloj en vivo con zona horaria configurable", + "widgetNotesDesc": "Un widget de notas en Markdown", + "widgetHostStatusDesc": "Muestra uso de CPU, memoria y disco en vivo de un host SSH", "widgetBookmarkListDesc": "Una lista de enlaces rápidos", "copyLink": "Copiar enlace", "linkCopied": "¡Enlace copiado!", "location": "Ubicación", "temperatureUnit": "Unidad de temperatura", - "showForecast": "Mostrar pronóstico para 3 días", + "showForecast": "Mostrar pronóstico de 3 días", "scrolling": "Permitir desplazamiento", "feedUrl": "URL del feed", - "maxItems": "Número máximo de artículos", + "maxItems": "Máx. de elementos", "showDescription": "Mostrar descripción", - "widgetWeatherName": "Clima", - "widgetWeatherDesc": "Información meteorológica en directo para cualquier ubicación.", - "widgetIframeName": "Inserción de iFrame", - "widgetIframeDesc": "Inserta cualquier URL en un iframe.", - "widgetRssName": "Canal RSS", - "widgetRssDesc": "Mostrar elementos de una fuente RSS o Atom", - "dragToFolder": "Arrastra los widgets aquí o usa + para añadir", - "showDisk": "Mostrar uso del disco", - "addToFolder": "Agregar widget a la carpeta", - "noHostSelected": "No se ha seleccionado ningún host.", + "widgetWeatherName": "Tiempo", + "widgetWeatherDesc": "El tiempo en vivo para cualquier ubicación", + "widgetIframeName": "Incrustar iFrame", + "widgetIframeDesc": "Incrusta cualquier URL en un iframe", + "widgetRssName": "Fuente RSS", + "widgetRssDesc": "Muestra elementos de una fuente RSS o Atom", + "dragToFolder": "Arrastra widgets aquí o usa + para añadir", + "showDisk": "Mostrar uso de disco", + "addToFolder": "Añadir widget a la carpeta", + "noHostSelected": "Ningún host seleccionado", "metricsNotAvailable": "Métricas no disponibles", - "selectHost": "Seleccione un anfitrión...", + "selectHost": "Seleccionar un host...", "displayedMetrics": "Métricas mostradas", - "metricCpu": "UPC", + "metricCpu": "CPU", "metricMemory": "Memoria", "metricDisk": "Disco", "metricUptime": "Tiempo de actividad", "metricSystem": "Sistema", - "metricOs": "Sistema operativo", - "metricKernel": "Núcleo", - "metricHostname": "nombre de host", + "metricOs": "SO", + "metricKernel": "Kernel", + "metricHostname": "Nombre de host", "metricNetwork": "Red", "metricProcesses": "Procesos", "metricProcessesTotal": "total", - "metricProcessesRunning": "correr", - "categoryMonitoring": "Escucha", + "metricProcessesRunning": "en ejecución", + "categoryMonitoring": "Monitorización", "loading": "Cargando...", - "noData": "No hay datos disponibles", - "allClear": "Todo despejado", + "noData": "Sin datos disponibles", + "allClear": "Todo correcto", "acknowledgeAlert": "Reconocer", - "noPingUrls": "No se han configurado URL.", + "noPingUrls": "No hay URLs configuradas", "pingLabel": "Etiqueta", - "addPingUrl": "Agregar URL", + "addPingUrl": "Añadir URL", "showLatency": "Mostrar latencia", "refreshInterval": "Intervalo de actualización", - "seconds": "artículos de segunda clase", - "filterActivityTypes": "Tipos de filtro", - "filterTypesHint": "Déjelo vacío para mostrar todo", + "seconds": "segundos", + "filterActivityTypes": "Filtrar tipos", + "filterTypesHint": "Dejar vacío para mostrar todo", "showTimestamp": "Mostrar marca de tiempo", "uptimeUnavailable": "Tiempo de actividad no disponible", "uptimeLabel": "Tiempo de actividad", "overviewVersion": "Versión", - "overviewUpdate": "A hoy", + "overviewUpdate": "Actualizado", "overviewUpdateAvailable": "Actualización disponible", "overviewDatabase": "Base de datos", "overviewUptime": "Tiempo de actividad", - "noHosts": "No hay hosts configurados", - "hostGridHosts": "Anfitriones", - "hostGridAllHint": "Déjelo en blanco para mostrar todos los hosts.", + "noHosts": "Sin hosts configurados", + "hostGridHosts": "Hosts", + "hostGridAllHint": "Dejar vacío para mostrar todos los hosts", "columns": "Columnas", "showIp": "Mostrar dirección IP", "connectionType": "Tipo de conexión", - "layout": "Disposición", + "layout": "Diseño", "showStatus": "Mostrar estado", - "showHostName": "Nombre del presentador del programa", - "noDockerActivity": "No hay actividad de Docker", - "noActivity": "No hay actividad", - "showAcknowledged": "Mostrar reconocimiento", + "showHostName": "Mostrar nombre de host", + "noDockerActivity": "Sin actividad de Docker", + "noActivity": "Sin actividad", + "showAcknowledged": "Mostrar reconocidos", "showCurrentValue": "Mostrar valor actual", - "chartMetric": "Métrico", + "chartMetric": "Métrica", "metricRange": "Rango", - "widgetMetricsChartName": "Tabla de métricas", + "widgetMetricsChartName": "Gráfico de métricas", "widgetMetricsChartDesc": "Gráfico histórico de CPU, memoria, disco o red para un host", - "widgetHostGridName": "Red de hosts", + "widgetHostGridName": "Cuadrícula de hosts", "widgetHostGridDesc": "Vista en cuadrícula de los estados de los hosts SSH", - "widgetAlertFeedName": "Fuente de alertas", - "widgetAlertFeedDesc": "Disparos de alerta en vivo con confirmación de asistencia", - "widgetPingStatusName": "Estado del ping", - "widgetPingStatusDesc": "Estado del ping HTTP para una o más URL.", + "widgetAlertFeedName": "Feed de alertas", + "widgetAlertFeedDesc": "Alertas en tiempo real con soporte para reconocimiento", + "widgetPingStatusName": "Estado de ping", + "widgetPingStatusDesc": "Estado de ping HTTP para una o más URLs", "widgetRecentActivityName": "Actividad reciente", - "widgetRecentActivityDesc": "Feed desplazable de la actividad reciente de Termix", + "widgetRecentActivityDesc": "Feed desplazable de actividad reciente de Termix", "widgetTermixUptimeName": "Tiempo de actividad de Termix", - "widgetTermixUptimeDesc": "Contador de tiempo de actividad en tiempo real para el servidor Termix", - "widgetSystemOverviewName": "Descripción general del sistema", - "widgetSystemOverviewDesc": "Versión de Termix, estado de la base de datos y tiempo de actividad de un vistazo", + "widgetTermixUptimeDesc": "Contador en tiempo real del tiempo de actividad del servidor Termix", + "widgetSystemOverviewName": "Resumen del sistema", + "widgetSystemOverviewDesc": "Versión de Termix, salud de la base de datos y tiempo de actividad de un vistazo", "widgetSshQuickConnectName": "Conexión rápida SSH", - "widgetSshQuickConnectDesc": "Botones de un solo clic para abrir sesiones SSH", + "widgetSshQuickConnectDesc": "Botones de un clic para abrir sesiones SSH", "widgetDockerActivityName": "Actividad de Docker", "widgetDockerActivityDesc": "Eventos recientes de contenedores Docker en todos los hosts", "widgetCalendarName": "Calendario", - "widgetCalendarDesc": "Un calendario mensual con el día de hoy resaltado.", + "widgetCalendarDesc": "Un calendario mensual con el día actual resaltado", "widgetCountdownName": "Cuenta atrás", - "widgetCountdownDesc": "Temporizador de cuenta regresiva hasta una fecha objetivo", + "widgetCountdownDesc": "Temporizador de cuenta atrás hacia una fecha objetivo", "widgetSearchBarName": "Barra de búsqueda", "widgetSearchBarDesc": "Widget de búsqueda web rápida", "widgetTextBannerName": "Banner de texto", - "widgetTextBannerDesc": "Una etiqueta en negrita o un encabezado de sección para el lienzo.", + "widgetTextBannerDesc": "Una etiqueta en negrita o encabezado de sección para el lienzo", "widgetImageWidgetName": "Imagen", "widgetImageWidgetDesc": "Mostrar una imagen desde una URL", - "widgetMarkdownNotesName": "Notas de Markdown", - "widgetMarkdownNotesDesc": "Notas enriquecidas con renderizado Markdown en línea", + "widgetMarkdownNotesName": "Notas Markdown", + "widgetMarkdownNotesDesc": "Notas enriquecidas con renderizado inline de Markdown", "widgetCustomApiName": "API personalizada", - "widgetCustomApiDesc": "Obtén y muestra datos de cualquier API JSON.", - "widgetServiceGridName": "Red de servicios", - "widgetServiceGridDesc": "Una cuadrícula configurable de enlaces de mosaicos de servicio", - "widgetDashboardLinksName": "Enlaces del panel de control", - "widgetDashboardLinksDesc": "Visualiza los enlaces de servicio configurados desde el panel de control.", + "widgetCustomApiDesc": "Obtener y mostrar datos desde cualquier API JSON", + "widgetServiceGridName": "Cuadrícula de servicios", + "widgetServiceGridDesc": "Una cuadrícula configurable de enlaces en mosaico de servicios", + "widgetDashboardLinksName": "Enlaces del panel", + "widgetDashboardLinksDesc": "Muestra tus enlaces de servicio configurados desde el panel", "widgetSearchLinksName": "Atajos de búsqueda", - "widgetSearchLinksDesc": "Botones de acceso directo a la búsqueda rápida con entrada en línea", + "widgetSearchLinksDesc": "Botones de atajo de búsqueda rápida con entrada inline", "widgetLinkTreeName": "Árbol de enlaces", "widgetLinkTreeDesc": "Secciones agrupadas de enlaces con encabezados", - "calMon": "Mes", - "calTue": "Tu", - "calWed": "Nosotros", - "calThu": "El", - "calFri": "Padre", + "calMon": "Lu", + "calTue": "Ma", + "calWed": "Mi", + "calThu": "Ju", + "calFri": "Vi", "calSat": "Sa", - "calSun": "Su", - "startOnMonday": "La semana comienza el lunes.", - "countdownNoDate": "No se ha fijado una fecha límite.", - "countdownPast": "El evento ha terminado.", + "calSun": "Do", + "startOnMonday": "Comenzar la semana el lunes", + "countdownNoDate": "No se ha fijado una fecha objetivo", + "countdownPast": "El evento ya ha pasado", "countdownDays": "días", - "countdownHours": "horas", + "countdownHours": "h", "countdownMinutes": "min", - "countdownSeconds": "segundo", + "countdownSeconds": "s", "countdownLabel": "Etiqueta", - "countdownLabelPlaceholder": "Por ejemplo, el día del lanzamiento.", - "countdownShowDays": "Días de espectáculos", - "countdownShowHours": "Horario del espectáculo", + "countdownLabelPlaceholder": "p. ej. Día de lanzamiento", + "countdownShowDays": "Mostrar días", + "countdownShowHours": "Mostrar horas", "targetDate": "Fecha objetivo", "searchEngine": "Motor de búsqueda", "customSearchUrl": "URL de búsqueda personalizada", "searchPlaceholder": "Buscar...", "searchGo": "Ir", - "searchPlaceholderLabel": "Texto de marcador de posición", - "searchPlaceholderHint": "Texto que se muestra dentro del campo de búsqueda.", - "openInNewTab": "Abrir en una pestaña nueva", - "searchQueryPlaceholder": "Introduzca la consulta...", - "noSearchShortcuts": "No se han configurado accesos directos", - "addSearchShortcut": "Agregar acceso directo", + "searchPlaceholderLabel": "Texto del marcador de posición", + "searchPlaceholderHint": "Texto mostrado dentro del campo de búsqueda", + "openInNewTab": "Abrir en nueva pestaña", + "searchQueryPlaceholder": "Introducir consulta...", + "noSearchShortcuts": "No hay accesos directos configurados", + "addSearchShortcut": "Añadir acceso directo", "fontSize": "Tamaño de fuente", - "textAlign": "Alineación de texto", - "fontWeight": "Peso de la fuente", - "clearColor": "Color transparente", + "textAlign": "Alineación del texto", + "fontWeight": "Grosor de la fuente", + "clearColor": "Limpiar color", "imageFit": "Ajuste de imagen", "imageLinkUrl": "URL del enlace", - "noImage": "No se ha establecido ninguna URL de imagen.", + "noImage": "No se ha establecido una URL de imagen", "altText": "Texto alternativo", - "altTextPlaceholder": "Describe la imagen", - "renderMarkdown": "Renderizar Markdown", + "altTextPlaceholder": "Describir la imagen", + "renderMarkdown": "Procesar Markdown", "displayMode": "Modo de visualización", "displayField": "Campo de visualización", "jsonPath": "Ruta JSON", "customApiLabel": "Etiqueta", "customApiLabelPlaceholder": "p. ej. Temperatura", "customApiUnit": "Unidad", - "customApiNoUrl": "No se ha configurado ninguna URL de API.", - "customApiError": "No se pudo obtener", - "customApiNotArray": "La respuesta no es una matriz", - "addService": "Agregar servicio", + "customApiNoUrl": "No se ha configurado ninguna URL de API", + "customApiError": "Error al obtener", + "customApiNotArray": "La respuesta no es un array", + "addService": "Añadir servicio", "showLabels": "Mostrar etiquetas", "iconSize": "Tamaño del icono", - "noDashboardLinks": "No hay enlaces del panel de control configurados.", + "noDashboardLinks": "Sin enlaces de panel configurados", "noLimit": "Sin límite", "sectionHeading": "Encabezado de sección", - "addSection": "Agregar sección", + "addSection": "Añadir sección", "compactMode": "Modo compacto", - "showDetailed": "Mostrar detalles", + "showDetailed": "Mostrar detallado", "selectHosts": "Seleccionar hosts", - "allHosts": "Todos los anfitriones", + "allHosts": "Todos los hosts", "listLayout": "Lista", - "gridLayout": "Red", + "gridLayout": "Cuadrícula", "terminal": "Terminal", "files": "Archivos", - "docker": "Estibador", + "docker": "Docker", "range15m": "15 minutos", "range1h": "1 hora", "range6h": "6 horas", "range24h": "24 horas", - "metricNetRx": "Descarga neta", - "metricNetTx": "Carga de red", - "severityFilter": "Filtro de gravedad", - "filterAll": "Todo", + "metricNetRx": "Descarga de red", + "metricNetTx": "Subida de red", + "severityFilter": "Filtro de severidad", + "filterAll": "Todos", "accentColor": "Color de acento", "widgetSshTerminalName": "Terminal SSH", - "widgetSshTerminalDesc": "Un terminal SSH en línea conectado a un host configurado.", - "sshTerminalNoHost": "No hay ningún host configurado.", + "widgetSshTerminalDesc": "Un terminal SSH integrado conectado a un host configurado", + "sshTerminalNoHost": "Sin host configurado", "sshTerminalConnect": "Conectar", - "sshTerminalAutoConnect": "Conexión automática al cargar", + "sshTerminalAutoConnect": "Conectar automáticamente al cargar", "widgetQuickConnectName": "Conexión rápida", - "widgetQuickConnectDesc": "Botones de inicio con un solo clic para cualquier tipo de conexión en todos tus hosts.", + "widgetQuickConnectDesc": "Botones de inicio con un clic para cualquier tipo de conexión en tus hosts", "connectionTypes": "Tipos de conexión", "connType_terminal": "Terminal", "connType_files": "Gestor de archivos", - "connType_docker": "Estibador", + "connType_docker": "Docker", "connType_tunnel": "Túnel", - "connType_host-metrics": "Métricas del anfitrión", + "connType_host-metrics": "Métricas del host", "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", "widgetFileManagerName": "Gestor de archivos", - "widgetFileManagerDesc": "Administrador de archivos SFTP integrado para un host configurado.", - "widgetDockerName": "Administrador de Docker", - "widgetDockerDesc": "Gestor de contenedores Docker integrado para un host configurado.", - "widgetTunnelName": "Gerente de túneles", - "widgetTunnelDesc": "Administrador de túneles SSH integrado para un host configurado.", - "widgetNoHostSelected": "No hay ningún host configurado." + "widgetFileManagerDesc": "Gestor de archivos SFTP integrado para un host configurado", + "widgetDockerName": "Gestor de Docker", + "widgetDockerDesc": "Gestor de contenedores Docker integrado para un host configurado", + "widgetTunnelName": "Gestor de túneles", + "widgetTunnelDesc": "Gestor de túneles SSH integrado para un host configurado", + "widgetNoHostSelected": "Sin host configurado" }, "serverConfig": { "title": "Configuración del servidor", - "description": "Configure la URL del servidor Termix para conectarse a sus servicios de backend.", + "description": "Configure la URL del servidor Termix para conectarse a sus servicios backend", "serverUrl": "URL del servidor", - "enterServerUrl": "Por favor, introduzca una URL del servidor.", - "saveFailed": "No se pudo guardar la configuración.", + "enterServerUrl": "Introduzca una URL del servidor", + "saveFailed": "No se pudo guardar la configuración", "saveError": "Error al guardar la configuración", - "saving": "Ahorro...", + "saving": "Guardando...", "saveConfig": "Guardar configuración", - "helpText": "Introduce la URL donde se ejecuta tu servidor Termix (por ejemplo, http://localhost:30001 o https://tu-servidor.com).", + "helpText": "Introduzca la URL donde 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.", + "allowInvalidCertificateDesc": "Usar solo para servidores autoalojados de confianza con certificados autofirmados o de dirección IP.", "useEmbedded": "Usar servidor local", - "embeddedDesc": "Ejecuta Termix con el servidor local integrado (no se necesita servidor remoto).", - "embeddedConnecting": "Conectando con el servidor local...", - "embeddedNotReady": "El servidor local aún no está listo. Por favor, espere un momento e inténtelo de nuevo.", + "embeddedDesc": "Ejecutar Termix con el servidor local integrado (no se necesita servidor remoto)", + "embeddedConnecting": "Conectando al servidor local...", + "embeddedNotReady": "El servidor local aún no está listo. Espere un momento e inténtelo de nuevo.", "localServer": "Servidor local", "savedServers": "Servidores guardados", "noSavedServers": "No hay servidores guardados", "removeServer": "Eliminar" }, + "migrationNotice": { + "title": "Termix Desktop ahora se ejecuta de forma independiente.", + "body1": "Esta aplicación solía conectarse directamente a tu servidor Termix. Se ha rediseñado para funcionar de forma totalmente independiente, almacenando los hosts y las credenciales localmente para que siga funcionando sin conexión. La sincronización bidireccional con un servidor Termix autohospedado ahora es opcional.", + "body2": "Tus hosts y credenciales actuales siguen en {{url}}. Activa la sincronización remota y vuelve a conectarte a ese servidor para recuperarlos y mantenerlos sincronizados en adelante.", + "dismiss": "Ahora no", + "setUpSync": "Configurar la sincronización remota" + }, + "remoteSync": { + "title": "Sincronización remota", + "description": "Opcionalmente, conecta esta aplicación de escritorio a un servidor Termix autogestionado para sincronizar tus hosts, credenciales y fragmentos de código en todos tus dispositivos. La aplicación siempre funciona sin conexión, independientemente de si te conectas o no.", + "notConnected": "No conectado", + "connected": "Conectado", + "connectedTo": "Conectado a {{url}}", + "lastSynced": "Última sincronización {{time}}", + "neverSynced": "Nunca sincronizado", + "syncError": "Error de sincronización: {{message}}", + "needsReauth": "La sesión ha caducado.", + "connectButton": "Conéctese al servidor", + "disconnectButton": "Desconectar", + "syncNowButton": "Sincronizar ahora", + "syncing": "Sincronizando...", + "serverUrl": "URL del servidor", + "enterServerUrl": "Por favor, introduzca una URL del servidor.", + "mustIncludeProtocol": "La URL del servidor debe comenzar con http:// o https://", + "connectionTestFailed": "No se pudo conectar con un servidor Termix en esa URL.", + "allowInvalidCertificate": "Permitir certificado no válido", + "allowInvalidCertificateDesc": "Utilizar únicamente en servidores autogestionados de confianza con certificados autofirmados o basados en direcciones IP.", + "savedServers": "Servidores guardados", + "removeServer": "Eliminar", + "continueButton": "Continuar", + "cancelButton": "Cancelar", + "signInTitle": "Iniciar sesión en {{url}}", + "originTitle": "Origen de la conexión", + "originDescription": "Se elige de forma predeterminada el origen de las conexiones SSH. Esta configuración se puede modificar para cada host.", + "originLocal": "Este dispositivo (red local)", + "originRemote": "Servidor remoto", + "bannerReconnect": "Reconectar", + "bannerMessage": "La sincronización remota requiere una nueva autenticación." + }, "versionCheck": { - "error": "Error de comprobación de versión", - "checkFailed": "No se pudo comprobar si hay actualizaciones.", - "upToDate": "La aplicación está actualizada.", - "currentVersion": "Estás ejecutando la versión {{version}}", + "error": "Error de verificación de versión", + "checkFailed": "Fallo al comprobar actualizaciones", + "upToDate": "La aplicación está actualizada", + "currentVersion": "Estás usando la versión {{version}}", "updateAvailable": "Actualización disponible", - "newVersionAvailable": "¡Hay una nueva versión disponible! Estás ejecutando {{current}}, pero {{latest}} está disponible.", - "betaVersion": "Versión beta", - "betaVersionDesc": "Estás ejecutando {{current}}, que es más reciente que la última versión estable {{latest}}.", + "newVersionAvailable": "¡Hay una nueva versión disponible! Estás usando la {{current}}, pero la {{latest}} está disponible.", + "betaVersion": "Versión Beta", + "betaVersionDesc": "Estás usando {{current}}, que es más reciente que la última versión estable {{latest}}.", "releasedOn": "Publicado el {{date}}", "downloadUpdate": "Descargar actualización", - "checking": "Buscando actualizaciones...", - "checkUpdates": "Comprobar si hay actualizaciones", - "checkingUpdates": "Buscando actualizaciones...", - "updateRequired": "Se requiere actualización" + "checking": "Comprobando actualizaciones...", + "checkUpdates": "Buscar actualizaciones", + "checkingUpdates": "Comprobando actualizaciones...", + "updateRequired": "Actualización necesaria" }, "common": { - "close": "Cerca", + "close": "Cerrar", "minimize": "Minimizar", "online": "En línea", - "offline": "Desconectado", + "offline": "Sin conexión", "unknown": "Desconocido", "continue": "Continuar", "maintenance": "Mantenimiento", "degraded": "Degradado", "error": "Error", "warning": "Advertencia", - "unsavedChanges": "Cambios no guardados", - "dismiss": "Despedir", + "unsavedChanges": "Cambios sin guardar", + "dismiss": "Descartar", "loading": "Cargando...", "optional": "Opcional", "connect": "Conectar", @@ -425,109 +464,109 @@ "connecting": "Conectando...", "updateAvailable": "Actualización disponible", "appName": "Termix", - "openInNewTab": "Abrir en una pestaña nueva", + "openInNewTab": "Abrir en nueva pestaña", "noReleases": "Sin lanzamientos", "updatesAndReleases": "Actualizaciones y lanzamientos", - "newVersionAvailable": "Hay una nueva versión ({{version}}) disponible.", - "failedToFetchUpdateInfo": "No se pudo obtener la información de actualización.", - "preRelease": "Lanzamiento anticipado", - "noReleasesFound": "No se encontraron lanzamientos.", + "newVersionAvailable": "Está disponible una nueva versión ({{version}}).", + "failedToFetchUpdateInfo": "Error al obtener la información de actualización", + "preRelease": "Versión preliminar", + "noReleasesFound": "No se encontraron versiones.", "cancel": "Cancelar", - "username": "Nombre de usuario", - "login": "Acceso", + "username": "Usuario", + "login": "Iniciar sesión", "logout": "Cerrar sesión", - "register": "Registro", + "register": "Registrarse", "password": "Contraseña", - "confirmPassword": "confirmar Contraseña", + "confirmPassword": "Confirmar contraseña", "back": "Atrás", - "save": "Ahorrar", - "saving": "Ahorro...", - "delete": "Borrar", - "rename": "Rebautizar", + "save": "Guardar", + "saving": "Guardando...", + "delete": "Eliminar", + "rename": "Renombrar", "edit": "Editar", - "add": "Agregar", + "add": "Añadir", "confirm": "Confirmar", "no": "No", "or": "O", - "next": "Próximo", + "next": "Siguiente", "previous": "Anterior", "refresh": "Refrescar", "language": "Idioma", - "checking": "De cheques...", - "checkingDatabase": "Comprobando la conexión a la base de datos...", - "checkingAuthentication": "Comprobando la autenticación...", - "backendReconnected": "Se ha restablecido la conexión con el servidor.", - "connectionDegraded": "Se perdió la conexión con el servidor, recuperando…", + "checking": "Comprobando...", + "checkingDatabase": "Comprobando conexión a la base de datos...", + "checkingAuthentication": "Comprobando autenticación...", + "backendReconnected": "Conexión al servidor restablecida", + "connectionDegraded": "Conexión con el servidor perdida, recuperando…", "reload": "Recargar", - "remove": "Eliminar", + "remove": "Quitar", "create": "Crear", "update": "Actualizar", "copy": "Copiar", - "copyFailed": "No se pudo copiar al portapapeles.", + "copyFailed": "Error al copiar al portapapeles", "maximize": "Maximizar", "restore": "Restaurar", "of": "de", "saved": "Guardado", "deleted": "Eliminado", "deleteFailed": "Error al eliminar", - "saveFailed": "No se pudo guardar", + "saveFailed": "Error al guardar", "required": "Requerido" }, "nav": { - "home": "Hogar", + "home": "Inicio", "terminal": "Terminal", - "docker": "Estibador", + "docker": "Docker", "tunnels": "Túneles", "fileManager": "Gestor de archivos", - "serverStats": "Métricas del anfitrión", - "hostMetrics": "Métricas del anfitrión", - "admin": "Administración", - "termixId": "IDENTIFICACIÓN", + "serverStats": "Métricas del host", + "hostMetrics": "Métricas del host", + "admin": "Admin", + "termixId": "ID", "userProfile": "Perfil de usuario", "splitScreen": "Pantalla dividida", "confirmClose": "¿Cerrar esta sesión activa?", - "close": "Cerca", + "close": "Cerrar", "cancel": "Cancelar", - "sshManager": "Administrador SSH", - "cannotSplitTab": "No se puede dividir esta pestaña.", + "sshManager": "Gestor SSH", + "cannotSplitTab": "No se puede dividir esta pestaña", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "Copiar contraseña", "copySudoPassword": "Copiar contraseña de sudo", "passwordCopied": "Contraseña copiada al portapapeles", "noPasswordAvailable": "No hay contraseña disponible", - "failedToCopyPassword": "No se pudo copiar la contraseña", + "failedToCopyPassword": "Error al copiar la contraseña", "refreshTab": "Actualizar conexión", - "renameTab": "Cambiar nombre de la pestaña", - "openFileManager": "Abrir el administrador de archivos", - "dashboard": "Panel", - "networkGraph": "Grafo de red", - "tmuxMonitor": "Monitor Tmux", - "homepage": "Página principal", + "renameTab": "Renombrar pestaña", + "openFileManager": "Abrir gestor de archivos", + "dashboard": "Panel de control", + "networkGraph": "Gráfico de red", + "tmuxMonitor": "Monitor de Tmux", + "homepage": "Inicio", "quickConnect": "Conexión rápida", "sshTools": "Herramientas SSH", - "history": "Historia", + "history": "Historial", "sessionLogs": "Registros de sesión", - "hosts": "Anfitriones", + "hosts": "Hosts", "snippets": "Fragmentos", - "hostManager": "Gerente anfitrión", - "credentials": "Cartas credenciales", + "hostManager": "Gestor de hosts", + "credentials": "Credenciales", "connections": "Conexiones", "alerts": "Alertas", - "serial": "De serie", + "serial": "Serial", "roleAdministrator": "Administrador", "roleUser": "Usuario" }, "hosts": { - "hosts": "Anfitriones", - "noHosts": "No hay hosts SSH", - "retry": "Rever", - "refresh": "Refrescar", + "hosts": "Hosts", + "noHosts": "Sin hosts SSH", + "retry": "Reintentar", + "refresh": "Actualizar", "optional": "Opcional", "downloadSample": "Descargar muestra", - "failedToDeleteHost": "No se pudo eliminar {{name}}", - "importSkipExisting": "Importar (omitir existente)", - "importSSHConfig": "Importar desde la configuración SSH", - "connectionDetails": "Detalles de conexión", + "failedToDeleteHost": "Error al eliminar {{name}}", + "importSkipExisting": "Importar (omitir existentes)", + "importSSHConfig": "Importar desde configuración SSH", + "connectionDetails": "Detalles de la conexión", "ssh": "SSH", "telnet": "Telnet", "remoteDesktop": "Escritorio remoto", @@ -535,20 +574,20 @@ "username": "Nombre de usuario", "folder": "Carpeta", "tags": "Etiquetas", - "pin": "Alfiler", - "addHost": "Agregar host", + "pin": "Anclar", + "addHost": "Añadir host", "editHost": "Editar host", - "cloneHost": "Host de clonación", - "enableTerminal": "Habilitar terminal", - "enableTunnel": "Habilitar túnel", - "enableFileManager": "Habilitar el administrador de archivos", - "enableDocker": "Habilitar Docker", + "cloneHost": "Clonar host", + "enableTerminal": "Activar terminal", + "enableTunnel": "Activar túnel", + "enableFileManager": "Activar gestor de archivos", + "enableDocker": "Activar Docker", "defaultPath": "Ruta predeterminada", "connection": "Conexión", "upload": "Subir", "authentication": "Autenticación", "password": "Contraseña", - "key": "Llave", + "key": "Clave", "credential": "Credencial", "none": "Ninguno", "sshPrivateKey": "Clave privada SSH", @@ -560,26 +599,26 @@ "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Túneles", - "tabDocker": "Estibador", + "tabDocker": "Docker", "tabFiles": "Archivos", - "tabStats": "Métricas del anfitrión", - "tabHostMetrics": "Métricas del anfitrión", + "tabStats": "Métricas del host", + "tabHostMetrics": "Métricas del host", "tabTelnet": "Telnet", - "tabSharing": "Intercambio", + "tabSharing": "Compartir", "tabAuthentication": "Autenticación", "terminal": "Terminal", "tunnel": "Túnel", "fileManager": "Gestor de archivos", - "serverStats": "Métricas del anfitrión", + "serverStats": "Métricas del host", "status": "Estado", - "folderRenamed": "La carpeta \"{{oldName}}\" se ha renombrado correctamente a \"{{newName}}\"", - "failedToRenameFolder": "Error al cambiar el nombre de la carpeta", - "movedToFolder": "Se movieron {{count}} hosts a \"{{folder}}\"", + "folderRenamed": "Carpeta \"{{oldName}}\" renombrada a \"{{newName}}\" correctamente", + "failedToRenameFolder": "Error al renombrar la carpeta", + "movedToFolder": "Movido(s) {{count}} host(s) a \"{{folder}}\"", "editHostTooltip": "Editar host", "statusChecks": "Comprobaciones de estado", "metricsCollection": "Recopilación de métricas", "metricsInterval": "Intervalo de recopilación de métricas", - "metricsIntervalDesc": "Con qué frecuencia se deben recopilar las estadísticas del servidor (5 s - 1 h)", + "metricsIntervalDesc": "Frecuencia de recopilación de estadísticas del servidor (5s - 1h)", "behavior": "Comportamiento", "themePreview": "Vista previa del tema", "theme": "Tema", @@ -590,159 +629,165 @@ "cursorStyle": "Estilo del cursor", "cursorBlink": "Parpadeo del cursor", "scrollbackBuffer": "Búfer de desplazamiento", - "bellStyle": "Estilo campana", - "rightClickSelectsWord": "Haga clic con el botón derecho y seleccione la palabra.", + "bellStyle": "Estilo de campana", + "rightClickSelectsWord": "Clic derecho selecciona palabra", "fastScrollModifier": "Modificador de desplazamiento rápido", "fastScrollSensitivity": "Sensibilidad de desplazamiento rápido", - "sshAgentForwarding": "Reenvío de agente SSH", + "sshAgentForwarding": "Reenvío del agente SSH", "backspaceMode": "Modo de retroceso", "startupSnippet": "Fragmento de inicio", "selectSnippet": "Seleccionar fragmento", - "forceKeyboardInteractive": "Forzar interacción con el teclado", - "overrideCredentialUsername": "Nombre de usuario de credenciales anuladas", - "overrideCredentialUsernameDesc": "Utilice el nombre de usuario especificado anteriormente en lugar del nombre de usuario de la credencial.", - "oidcUsernameHint": "Utilice $oidc.preferred_username para sustituir su nombre de inicio de sesión de OIDC.", - "jumpHostChain": "Cadena de host de salto", - "portKnocking": "Llamada al puerto", - "addKnock": "Agregar puerto", - "addProxyNode": "Agregar nodo", + "forceKeyboardInteractive": "Forzar autenticación interactiva por teclado", + "overrideCredentialUsername": "Anular nombre de usuario de la credencial", + "overrideCredentialUsernameDesc": "Usar el nombre de usuario especificado arriba en lugar del nombre de usuario de la credencial", + "oidcUsernameHint": "Usar $oidc.preferred_username para sustituir tu nombre de inicio de sesión OIDC.", + "tailscaleUsernameHint": "Debe tratarse de un usuario de Unix cuya identidad de Tailscale se concede en la ACL SSH de tailnet, no necesariamente como root.", + "jumpHostChain": "Cadena de hosts de salto", + "portKnocking": "Port Knocking", + "addKnock": "Añadir puerto", + "addProxyNode": "Añadir nodo", "proxyNode": "Nodo proxy", "proxyType": "Tipo de proxy", "quickActions": "Acciones rápidas", - "sudoPasswordAutoFill": "Autocompletar contraseñas con Sudo", + "sudoPasswordAutoFill": "Autocompletado de contraseña de sudo", "sudoPassword": "Contraseña de sudo", - "keepaliveInterval": "Intervalo de mantenimiento de conexión (ms)", + "keepaliveInterval": "Intervalo de keepalive (ms)", "moshCommand": "Comando MOSH", - "environmentVariables": "Variables ambientales", - "addVariable": "Agregar variable", - "docker": "Estibador", + "environmentVariables": "Variables de entorno", + "addVariable": "Añadir variable", + "docker": "Docker", "copyTerminalUrl": "Copiar URL del terminal", - "copyFileManagerUrl": "Copiar URL del administrador de archivos", - "copyRemoteDesktopUrl": "Copiar URL de Escritorio remoto", - "failedToConnect": "No se pudo conectar a la consola.", + "copyFileManagerUrl": "Copiar URL del gestor de archivos", + "copyRemoteDesktopUrl": "Copiar URL del escritorio remoto", + "failedToConnect": "Error al conectar a la consola", "connect": "Conectar", "disconnect": "Desconectar", - "start": "Comenzar", - "enableStatusCheck": "Habilitar comprobación de estado", + "start": "Iniciar", + "enableStatusCheck": "Habilitar verificación de estado", "enableMetrics": "Habilitar métricas", - "bulkUpdateFailed": "La actualización masiva falló.", + "bulkUpdateFailed": "Error en la actualización masiva", "selectAll": "Seleccionar todo", "deselectAll": "Deseleccionar todo", "protocols": "Protocolos", - "secureShell": "Concha segura", + "secureShell": "Shell seguro", "virtualNetwork": "Red virtual", - "unencryptedShell": "shell sin cifrar", - "addressIp": "Dirección IP", + "unencryptedShell": "Shell sin cifrar", + "addressIp": "Dirección / IP", "friendlyName": "Nombre descriptivo", "macAddress": "Dirección MAC", - "wolBroadcastAddress": "Discurso de transmisión de WoL", - "wolBroadcastAddressDesc": "Difusión dirigida opcional para Docker/redes enrutadas (por ejemplo, 192.168.1.255). Déjelo en blanco para usar 255.255.255.255.", - "folderAndAdvanced": "Carpeta y Avanzado", + "wolBroadcastAddress": "Dirección de difusión WoL", + "wolBroadcastAddressDesc": "Difusión dirigida opcional para Docker o redes enrutadas (p. ej., 192.168.1.255). Dejar vacío para usar 255.255.255.255.", + "folderAndAdvanced": "Carpeta y opciones avanzadas", "privateNotes": "Notas privadas", "privateNotesPlaceholder": "Detalles sobre este servidor...", - "pinToTop": "Anclar a la parte superior", - "pinToTopDesc": "Mostrar siempre este host en la parte superior de la lista.", - "portKnockingSequence": "Secuencia de apertura de puertos", - "addKnockBtn": "Agregar golpe", - "noPortKnocking": "No hay configuración de port knocking.", - "knockPort": "Puerto de golpeo", + "pinToTop": "Fijar en la parte superior", + "pinToTopDesc": "Mostrar siempre este equipo al principio de la lista", + "portKnockingSequence": "Secuencia de port knocking", + "addKnockBtn": "Añadir knock", + "noPortKnocking": "No port knocking configurado.", + "knockPort": "Puerto de knocking", "protocol": "Protocolo", - "delayAfterMs": "Retraso después (ms)", - "useSocks5Proxy": "Utilice un proxy SOCKS5.", + "delayAfterMs": "Espera después (ms)", + "useSocks5Proxy": "Usar proxy SOCKS5", "useSocks5ProxyDesc": "Enrutar la conexión a través de un servidor proxy", - "proxyHost": "Host proxy", - "proxyPort": "Puerto proxy", - "proxyUsername": "Nombre de usuario proxy", - "proxyPassword": "Contraseña de proxy", + "connectionOrigin": "Origen de la conexión", + "connectionOriginDesc": "De dónde se origina la conexión SSH de este host. Anula la configuración predeterminada global de la aplicación de escritorio.", + "connectionOriginDefault": "Usar valores predeterminados", + "connectionOriginLocal": "Este dispositivo (red local)", + "connectionOriginRemote": "Servidor remoto", + "proxyHost": "Host del proxy", + "proxyPort": "Puerto del proxy", + "proxyUsername": "Nombre de usuario del proxy", + "proxyPassword": "Contraseña del proxy", "proxySingleMode": "Proxy único", - "proxyChainMode": "Cadena proxy", + "proxyChainMode": "Cadena de proxies", "you": "Tú", - "jumpHostChainLabel": "Cadena de hosts de salto", - "addJumpBtn": "Agregar salto", - "noJumpHosts": "No hay hosts de salto configurados.", - "selectAServer": "Seleccione un servidor...", + "jumpHostChainLabel": "Cadena de jump hosts", + "addJumpBtn": "Añadir salto", + "noJumpHosts": "No jump hosts configurados.", + "selectAServer": "Seleccionar un servidor...", "sshPort": "Puerto SSH", "authMethod": "Método de autenticación", "storedCredential": "Credencial almacenada", - "selectACredential": "Seleccione una credencial...", - "vaultProfile": "Perfil del firmante de la bóveda", - "selectAVaultProfile": "Seleccione un perfil de Vault...", - "vaultProfileHint": "La configuración proviene del perfil compartido; al conectarse, iniciará sesión en Vault mediante OIDC. No se almacenan secretos.", + "selectACredential": "Seleccionar una credencial...", + "vaultProfile": "Perfil de firmante de Vault", + "selectAVaultProfile": "Seleccionar un perfil de Vault...", + "vaultProfileHint": "La configuración proviene del perfil compartido; se iniciará sesión en Vault a través de OIDC al conectarse. No se almacenan secretos.", "vaultNewProfile": "Nuevo perfil", "vaultManageProfiles": "Administrar perfiles de Vault", - "vaultAddrLabel": "Dirección de la bóveda", + "vaultAddrLabel": "Dirección de Vault", "vaultNamespaceLabel": "Espacio de nombres", "vaultOidcMountLabel": "Montaje de autenticación OIDC", - "vaultOidcRoleLabel": "Función de OIDC", + "vaultOidcRoleLabel": "Rol OIDC", "vaultSshMountLabel": "Montaje de secretos SSH", "vaultSshRoleLabel": "Rol de firmante SSH", - "vaultValidPrincipalsLabel": "Principios válidos", + "vaultValidPrincipalsLabel": "Principals válidos", "vaultKeyTypeLabel": "Tipo de clave efímera", "vaultSharedLabel": "Compartir con todos los usuarios", "vaultCreateProfile": "Crear perfil", - "vaultProfileCreated": "Perfil de bóveda creado", - "vaultProfileSaved": "Perfil de bóveda guardado", - "vaultProfileDeleted": "Perfil de bóveda eliminado", - "vaultProfileSaveFailed": "No se pudo guardar el perfil de Vault.", - "vaultProfileDeleteFailed": "No se pudo eliminar el perfil de Vault.", + "vaultProfileCreated": "Perfil de Vault creado", + "vaultProfileSaved": "Perfil de Vault guardado", + "vaultProfileDeleted": "Perfil de Vault eliminado", + "vaultProfileSaveFailed": "Error al guardar el perfil de Vault", + "vaultProfileDeleteFailed": "Error al eliminar el perfil de Vault", "vaultSaveProfile": "Guardar perfil", - "vaultProfileValidationError": "Se requieren nombre, dirección de Vault y rol de firmante SSH.", + "vaultProfileValidationError": "Se requieren el nombre, la dirección de Vault y el rol de firmante SSH", "vaultNoProfiles": "Aún no hay perfiles de Vault.", "vaultSharedBadge": "compartido", "keyTypeLabel": "Tipo de clave", "keyTypeAuto": "Detección automática", - "keyPasteTab": "Pasta", + "keyPasteTab": "Pegar", "keyUploadTab": "Subir", - "keyFileLoaded": "Archivo clave cargado", - "keyUploadClick": "Haz clic para subir archivos .pem / .key / .ppk", - "clearKey": "Clave clara", + "keyFileLoaded": "Archivo de clave cargado", + "keyUploadClick": "Haga clic para subir .pem / .key / .ppk", + "clearKey": "Borrar clave", "keySaved": "Clave SSH guardada", - "keyReplaceNotice": "Pegue una nueva clave a continuación para reemplazarla.", - "keyPassphraseSaved": "Contraseña guardada, escriba para cambiarla", - "replaceKey": "Reemplazar llave", + "keyReplaceNotice": "pegue una nueva clave a continuación para reemplazarla", + "keyPassphraseSaved": "Frase de contraseña guardada, escriba para cambiarla", + "replaceKey": "Reemplazar clave", "docsLink": "Ver documentación", "opksshLabel": "OPKSSH", - "opksshDesc": "Inicie sesión en este servidor utilizando su proveedor de identidad en lugar de una contraseña o clave. Requiere que OPKSSH esté configurado en el servidor.", - "warpgateLabel": "Portal de teletransporte", - "warpgateDesc": "Este host se conecta a través de un proxy SSH Warpgate. Termix gestionará automáticamente el proceso de aprobación basado en el navegador tras la autenticación.", + "opksshDesc": "Inicie sesión en este host usando su proveedor de identidad en lugar de una contraseña o clave. Requiere OPKSSH configurado en el servidor.", + "warpgateLabel": "Warpgate Gateway", + "warpgateDesc": "Este host se conecta a través de un proxy SSH de Warpgate. Termix gestionará el flujo de aprobación basado en navegador automáticamente después de autenticarse.", "agentLabel": "Agente SSH", - "agentDesc": "Autentícate mediante un agente SSH que se ejecute en el servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). El agente debe estar ejecutándose en la máquina donde está alojado Termix.", + "agentDesc": "Autentíquese usando un agente SSH que se ejecuta en el servidor de Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). El agente debe estar ejecutándose en la máquina donde está alojado Termix.", "agentSocketPathLabel": "Ruta del socket del agente", - "agentSocketPathPlaceholder": "Déjelo en blanco para usar SSH_AUTH_SOCK", - "agentSocketPathHint": "Déjelo en blanco para que se detecte automáticamente a partir de la variable de entorno SSH_AUTH_SOCK, o introduzca una ruta de socket personalizada (por ejemplo, /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "Seleccione el dispositivo Tailscale", + "agentSocketPathPlaceholder": "Dejar vacío para usar SSH_AUTH_SOCK", + "agentSocketPathHint": "Dejar vacío para detectar automáticamente desde la variable de entorno SSH_AUTH_SOCK, o introduzca una ruta de socket personalizada (p. ej. /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Seleccionar dispositivo Tailscale", "tailscaleDeviceSelectPlaceholder": "Seleccione un dispositivo...", - "tailscaleNoApiKey": "No hay ninguna clave API de Tailscale configurada. Añada una en la configuración de administración para habilitar la detección de dispositivos.", + "tailscaleNoApiKey": "No se ha configurado ninguna clave de API de Tailscale. Añada una en Ajustes de administración para habilitar el descubrimiento de dispositivos.", "tailscaleDocsLink": "Ver documentación", "tailscaleLoadingDevices": "Cargando dispositivos...", - "tailscaleNoDevices": "No se encontraron dispositivos en su red de cola.", - "tailscaleDeviceAutoFill": "Al seleccionar un dispositivo, la dirección IP del host se rellenará automáticamente.", - "forceKeyboardInteractiveLabel": "Teclado interactivo de fuerza", - "forceKeyboardInteractiveShortDesc": "Obligar a introducir la contraseña manualmente aunque haya llaves presentes.", + "tailscaleNoDevices": "No se han encontrado dispositivos en tu tailnet.", + "tailscaleDeviceAutoFill": "Al seleccionar un dispositivo se rellenará automáticamente la dirección IP del host.", + "forceKeyboardInteractiveLabel": "Forzar teclado interactivo", + "forceKeyboardInteractiveShortDesc": "Forzar entrada manual de contraseña aunque haya claves presentes", "allowLegacyAlgorithmsLabel": "Permitir algoritmos heredados", - "allowLegacyAlgorithmsDesc": "Habilite los algoritmos SSH obsoletos (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) para las conexiones a dispositivos antiguos que no se pueden actualizar.", + "allowLegacyAlgorithmsDesc": "Habilitar algoritmos SSH obsoletos (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) para conexiones a dispositivos antiguos que no se pueden actualizar.", "insecure": "Inseguro", - "terminalAppearance": "Apariencia terminal", + "terminalAppearance": "Apariencia del terminal", "colorTheme": "Tema de color", "fontFamilyLabel": "Familia de fuentes", "fontSizeLabel": "Tamaño de fuente", "cursorStyleLabel": "Estilo del cursor", "letterSpacingPx": "Espaciado entre letras (px)", "lineHeightLabel": "Altura de línea", - "bellStyleLabel": "Estilo campana", + "bellStyleLabel": "Estilo de aviso sonoro", "backspaceModeLabel": "Modo de retroceso", - "cursorBlinking": "Cursor parpadeando", - "cursorBlinkingDesc": "Habilitar la animación de parpadeo para el cursor de la terminal.", - "rightClickSelectsWordLabel": "Clic derecho Selecciona Word", - "rightClickSelectsWordShortDesc": "Seleccione la palabra que se encuentra debajo del cursor haciendo clic con el botón derecho.", + "cursorBlinking": "Parpadeo del cursor", + "cursorBlinkingDesc": "Activar la animación de parpadeo para el cursor del terminal", + "rightClickSelectsWordLabel": "Clic derecho selecciona palabra", + "rightClickSelectsWordShortDesc": "Selecciona la palabra bajo el cursor al hacer clic derecho", "backgroundImageLabel": "URL de la imagen de fondo", - "backgroundImageDesc": "URL opcional para una imagen de fondo de terminal", + "backgroundImageDesc": "URL opcional para una imagen de fondo del terminal", "backgroundImageOpacityLabel": "Opacidad de la imagen de fondo", "customThemeColors": "Colores personalizados", "customThemeBackground": "Fondo", "customThemeForeground": "Primer plano", "customThemeCursor": "Cursor", - "customThemeCursorAccent": "acento del cursor", + "customThemeCursorAccent": "Acento del cursor", "customThemeSelection": "Selección", "customThemeAnsiColors": "Colores ANSI", "customThemeBlack": "Negro", @@ -754,7 +799,7 @@ "customThemeCyan": "Cian", "customThemeWhite": "Blanco", "customThemeBrightBlack": "Negro brillante", - "customThemeBrightRed": "Lacre", + "customThemeBrightRed": "Rojo brillante", "customThemeBrightGreen": "Verde brillante", "customThemeBrightYellow": "Amarillo brillante", "customThemeBrightBlue": "Azul brillante", @@ -762,95 +807,105 @@ "customThemeBrightCyan": "Cian brillante", "customThemeBrightWhite": "Blanco brillante", "customThemeResetTooltip": "Restablecer valores predeterminados", + "savedThemesLabel": "Temas guardados", + "saveAsGlobalTheme": "Guardar como tema global", + "saveGlobalThemeNamePrompt": "Introduce un nombre para este tema.", + "saveGlobalThemeSuccess": "Tema guardado", + "saveGlobalThemeError": "No se pudo guardar el tema.", + "applyGlobalThemeTooltip": "Aplicar este tema", + "deleteGlobalThemeTooltip": "Eliminar este tema", + "noSavedThemes": "Aún no hay temas guardados", "syntaxHighlightingLabel": "Resaltado de sintaxis", - "syntaxHighlightingDesc": "Colorear la salida del terminal (errores, rutas, direcciones IP, marcas de tiempo)", - "syntaxHighlightingCategories": "Categorías destacadas", + "syntaxHighlightingDesc": "Colorear la salida del terminal (errores, rutas, IPs, marcas de tiempo)", + "syntaxHighlightingCategories": "Categorías de resaltado", "syntaxHighlightingCategoriesDesc": "Elige qué tipos de contenido colorear", "syntaxCategoryLogLevels": "Niveles de registro", - "syntaxCategoryLogLevelsDesc": "error, advertencia, información, depuración, fatal", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "Rutas de archivo", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "Marcas de tiempo", - "syntaxCategoryTimestampsDesc": "[12:34:56], 15/01/2024", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "Direcciones IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URLs", - "syntaxCategoryUrlsDesc": "https://ejemplo.com", + "syntaxCategoryUrlsDesc": "https://example.com", "syntaxCategoryNumbers": "Números etiquetados", - "syntaxCategoryNumbersDesc": "Puerto 8080, salida 1, estado 404", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", "behaviorAndAdvanced": "Comportamiento y avanzado", - "scrollbackBufferLabel": "Búfer de desplazamiento", + "scrollbackBufferLabel": "Búfer de retroceso", "scrollbackMaxLines": "Número máximo de líneas conservadas en el historial", - "sshAgentForwardingLabel": "Reenvío de agente SSH", - "sshAgentForwardingShortDesc": "Pasa tus claves SSH locales a este host.", + "sshAgentForwardingLabel": "Reenvío del agente SSH", + "sshAgentForwardingShortDesc": "Reenviar tus claves SSH locales a este host", "useSSHTitleLabel": "Usar título de ventana SSH", - "useSSHTitleDesc": "Actualiza el título de la pestaña con el título de la ventana del shell en lugar del nombre del host.", + "useSSHTitleDesc": "Actualizar el título de la pestaña desde el título de la ventana del shell en lugar del nombre del host", "enableAutoMosh": "Habilitar Auto-Mosh", - "enableAutoMoshDesc": "Prefiera Mosh a SSH si está disponible.", + "enableAutoMoshDesc": "Preferir Mosh sobre SSH si está disponible", "enableAutoTmux": "Habilitar Auto-Tmux", - "enableAutoTmuxDesc": "Iniciar o conectarse automáticamente a una sesión de tmux", - "enableSessionLogging": "Registro de sesiones", - "enableSessionLoggingDesc": "Registre la salida de la sesión del terminal para su posterior revisión.", + "enableAutoTmuxDesc": "Iniciar o adjuntar automáticamente a una sesión tmux", + "enableSessionLogging": "Registro de sesión", + "enableSessionLoggingDesc": "Registrar la salida de la sesión de terminal para su revisión posterior", + "allowSessionSharing": "Permitir compartir la sesión", + "allowSessionSharingDesc": "Permitir que las sesiones en vivo en este servidor se compartan mediante un enlace o con otros usuarios.", "enableCommandHistory": "Historial de comandos", - "enableCommandHistoryDesc": "Registra los comandos ejecutados en esta terminal para el historial y la función de autocompletar.", - "linkClickBehaviorLabel": "Comportamiento al hacer clic en el enlace", - "linkClickBehaviorDesc": "Controla lo que sucede al hacer clic en un enlace en la terminal. Usa 'Predeterminado' para seguir la configuración de la aplicación.", - "linkClickBehaviorDefault": "Predeterminado (seguir la configuración de la aplicación)", + "enableCommandHistoryDesc": "Registrar los comandos ejecutados en este terminal para el historial y autocompletado", + "linkClickBehaviorLabel": "Comportamiento al hacer clic en enlaces", + "linkClickBehaviorDesc": "Controla lo que sucede al hacer clic en un enlace en el terminal. Use 'Predeterminado' para seguir la configuración de la aplicación.", + "linkClickBehaviorDefault": "Predeterminado (seguir configuración de la aplicación)", "linkClickBehaviorConfirm": "Mostrar ventana emergente para abrir o copiar", "linkClickBehaviorDirect": "Abrir directamente", - "sudoPasswordAutoFillLabel": "Autocompletar contraseñas con Sudo", - "sudoPasswordAutoFillShortDesc": "Proporcionar automáticamente la contraseña de sudo cuando se solicite.", - "sudoPasswordAutoFillDesc": "Almacena una contraseña de sudo para que las métricas del host, las indicaciones de la terminal y otros sistemas puedan ejecutar comandos con privilegios automáticamente.", - "sudoPasswordLabel": "Contraseña de sudo", - "environmentVariablesLabel": "Variables ambientales", - "addVariableBtn": "Agregar variable", - "noEnvVars": "No se han configurado variables de entorno.", + "sudoPasswordAutoFillLabel": "Autocompletado de contraseña sudo", + "sudoPasswordAutoFillShortDesc": "Proporcionar automáticamente la contraseña sudo cuando se solicite", + "sudoPasswordAutoFillDesc": "Almacenar una contraseña sudo para que las métricas del host, las solicitudes del terminal y otros puedan ejecutar comandos privilegiados automáticamente.", + "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": "Comando Mosh", "startupSnippetLabel": "Fragmento de inicio", - "keepaliveIntervalLabel": "Intervalo de mantenimiento de conexión (segundos)", - "maxKeepaliveMisses": "Máximo fallos de Keepalive", - "tunnelSettings": "Configuración del túnel", - "enableTunneling": "Habilitar la tunelización", - "enableTunnelingDesc": "Habilitar la funcionalidad de túnel SSH para este host.", - "serverTunnelsSection": "Túneles de servidor", - "addTunnelBtn": "Agregar túnel", + "keepaliveIntervalLabel": "Intervalo de keepalive (segundos)", + "maxKeepaliveMisses": "Máximo de fallos de keepalive", + "tunnelSettings": "Configuración de túnel", + "enableTunneling": "Habilitar tunelización", + "enableTunnelingDesc": "Habilitar la funcionalidad de túnel SSH para este host", + "serverTunnelsSection": "Túneles del servidor", + "addTunnelBtn": "Añadir túnel", "noTunnelsConfigured": "No hay túneles configurados.", "tunnelLabel": "Túnel {{number}}", - "tunnelType": "Tipo túnel", - "tunnelModeLocalDesc": "Reenvía un puerto local a un puerto del servidor remoto (o a un host accesible desde él).", - "tunnelModeRemoteDesc": "Reenvía un puerto del servidor remoto a un puerto local de tu máquina.", - "tunnelModeDynamicDesc": "Cree un proxy SOCKS5 en un puerto local para el reenvío dinámico de puertos.", + "tunnelType": "Tipo de túnel", + "tunnelModeLocalDesc": "Reenviar un puerto local a un puerto en el servidor remoto (o a un host accesible desde él).", + "tunnelModeRemoteDesc": "Reenviar un puerto del servidor remoto a un puerto local en tu equipo.", + "tunnelModeDynamicDesc": "Crear un proxy SOCKS5 en un puerto local para el reenvío dinámico de puertos.", "sameHost": "Este host (túnel directo)", - "endpointHost": "Host de punto final", - "endpointHostPlaceholder": "Mismo host, host SSH o host/IP accesible.", - "endpointPort": "Puerto de punto final", + "endpointHost": "Host de destino", + "endpointHostPlaceholder": "Mismo host, host SSH o host/dirección IP accesible", + "endpointPort": "Puerto de destino", "bindHost": "Host de enlace", - "sourcePort": "Puerto de origen", + "sourcePort": "Puerto origen", "maxRetries": "Reintentos máximos", "retryIntervalS": "Intervalo de reintento (s)", - "autoStartLabel": "Arranque automático", - "autoStartDesc": "Conectar automáticamente este túnel cuando el host esté cargado.", - "tunnelConnecting": "Túnel que conecta...", + "autoStartLabel": "Inicio automático", + "autoStartDesc": "Conectar automáticamente este túnel cuando se cargue el host.", + "tunnelConnecting": "Túnel conectando...", "tunnelDisconnected": "Túnel desconectado", - "failedToConnectTunnel": "No se pudo conectar", - "failedToDisconnectTunnel": "No se pudo desconectar", - "dockerIntegration": "Integración de Docker", - "enableDockerMonitor": "Habilitar Docker", - "enableDockerMonitorDesc": "Supervise y gestione los contenedores en este host mediante Docker.", - "containerRuntime": "Entorno de ejecución de contenedores", - "containerRuntimeDesc": "Seleccione la interfaz de línea de comandos (CLI) utilizada para la administración de contenedores en este host.", - "containerRuntimeDocker": "Estibador", + "failedToConnectTunnel": "Error al conectar", + "failedToDisconnectTunnel": "Error al desconectar", + "dockerIntegration": "Integración con Docker", + "enableDockerMonitor": "Activar Docker", + "enableDockerMonitorDesc": "Supervisar y gestionar contenedores en este host mediante Docker.", + "containerRuntime": "Runtime de contenedores", + "containerRuntimeDesc": "Elegir la CLI utilizada para la gestión de contenedores en este host.", + "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "Habilitar el monitor de Tmux", - "enableTmuxMonitorDesc": "Muestra este host en el Monitor de Tmux y añade sus acciones de tmux a la barra lateral.", + "enableTmuxMonitor": "Activar Tmux Monitor", + "enableTmuxMonitorDesc": "Mostrar este host en el Tmux Monitor y añadir sus acciones de tmux a la barra lateral.", "tabProxmox": "Proxmox", "proxmoxIntegration": "Integración con Proxmox", - "enableProxmox": "Habilitar Proxmox", - "enableProxmoxDesc": "Marca este host como un nodo Proxmox. Esto permite la detección de invitados y la importación directa desde este host.", + "enableProxmox": "Activar Proxmox", + "enableProxmoxDesc": "Marcar este host como un nodo Proxmox. Permite la detección e importación de invitados directamente desde este host.", "proxmoxDefaultAuthType": "Tipo de autenticación predeterminado", - "proxmoxDefaultAuthTypeDesc": "Método de autenticación aplicado a los anfitriones invitados importados. Elija el tipo de autenticación que coincida con la forma en que se conecta con sus invitados.", + "proxmoxDefaultAuthTypeDesc": "Método de autenticación aplicado a los hosts invitados importados. Elija el tipo de autenticación que coincida con cómo se conecta a sus invitados.", "authTypePassword": "Contraseña", "authTypeKey": "Clave SSH", "authTypeCredential": "Credencial", @@ -858,162 +913,169 @@ "authTypeNone": "Ninguno", "proxmoxDefaultCredential": "Credencial predeterminada", "proxmoxDefaultCredentialDesc": "Credencial utilizada para los hosts invitados importados cuando el tipo de autenticación está configurado como Credencial.", - "proxmoxWindowsDetection": "Detección de Windows/RDP", - "proxmoxWindowsDetectionDesc": "Patrones de nombres separados por comas que activan RDP en lugar de SSH (sin distinción entre mayúsculas y minúsculas)", - "proxmoxDockerDetection": "detección de Docker", - "proxmoxDockerDetectionDesc": "Patrones de nombres separados por comas que permiten a Docker hacer coincidir los invitados.", + "proxmoxWindowsDetection": "Detección de Windows / RDP", + "proxmoxWindowsDetectionDesc": "Patrones de nombres separados por comas que activan RDP en lugar de SSH (sin distinción de mayúsculas/minúsculas)", + "proxmoxDockerDetection": "Detección de Docker", + "proxmoxDockerDetectionDesc": "Patrones de nombres separados por comas que habilitan Docker para los invitados coincidentes", "proxmoxPreferredRanges": "Rangos de IP preferidos", - "proxmoxPreferredRangesDesc": "Prefijos separados por comas en orden de prioridad para la selección de IP cuando un invitado tiene múltiples interfaces.", + "proxmoxPreferredRangesDesc": "Prefijos separados por comas en orden de prioridad para la selección de IP cuando un invitado tiene múltiples interfaces", "proxmoxAutoSync": "Sincronización automática de invitados", - "proxmoxAutoSyncDesc": "Descubre periódicamente este nodo Proxmox y crea o actualiza los hosts invitados importados mientras tu sesión esté desbloqueada.", + "proxmoxAutoSyncDesc": "Descubrir periódicamente este nodo Proxmox y crear o actualizar los hosts invitados importados mientras la sesión está desbloqueada.", "proxmoxSyncInterval": "Intervalo de sincronización (minutos)", - "proxmoxSyncIntervalDesc": "Mínimo 5 minutos. El programador omite las sesiones de usuario bloqueadas.", - "proxmoxMarkMissing": "Mark, huéspedes desaparecidos", - "proxmoxMarkMissingDesc": "Cuando un invitado importado previamente desaparece, se añade la etiqueta proxmox-missing en lugar de eliminarlo.", + "proxmoxSyncIntervalDesc": "Mínimo 5 minutos. El planificador omite las sesiones de usuario bloqueadas.", + "proxmoxMarkMissing": "Marcar invitados faltantes", + "proxmoxMarkMissingDesc": "Añadir una etiqueta proxmox-missing cuando un invitado previamente importado desaparece en lugar de eliminarlo.", "proxmoxLastSync": "Última sincronización", "proxmoxLastSyncNever": "Aún no sincronizado", - "proxmoxLastSyncNoResult": "No hay detalles del resultado", - "proxmoxLastSyncSummary": "{{created}} creado, {{updated}} actualizado, {{markedMissing}} faltante, {{skipped}} omitido", + "proxmoxLastSyncNoResult": "Sin detalles del resultado", + "proxmoxLastSyncSummary": "{{created}} creados, {{updated}} actualizados, {{markedMissing}} faltantes, {{skipped}} omitidos", "proxmoxLastSyncStatus": { - "success": "Éxito", + "success": "Correcto", "error": "Fallido", "pending": "Pendiente" }, - "proxmoxDiscoverAction": "Descubre e importa invitados de Proxmox", + "proxmoxDiscoverAction": "Descubrir e importar invitados Proxmox", "proxmoxImportTitle": "Importar desde Proxmox", - "proxmoxSelectHost": "Seleccione un host Proxmox…", + "proxmoxSelectHost": "Seleccionar un host Proxmox…", "proxmoxDiscover": "Descubrir", "proxmoxDiscovering": "Descubriendo…", - "proxmoxDiscoverGuests": "Descubra a los huéspedes", + "proxmoxDiscoverGuests": "Descubrir invitados", "proxmoxGuestsSelected_one": "{{count}} invitado — {{selected}} seleccionado", "proxmoxGuestsSelected_other": "{{count}} invitados — {{selected}} seleccionados", - "proxmoxSelectAll": "Seleccionar todo", - "proxmoxDeselectAll": "Deseleccionar todo", + "proxmoxSelectAll": "Seleccionar todos", + "proxmoxDeselectAll": "Deseleccionar todos", "proxmoxNoGuests": "No se encontraron invitados en este nodo Proxmox.", "proxmoxImportButton_one": "Importar {{count}} host", "proxmoxImportButton_other": "Importar {{count}} hosts", - "proxmoxResultImported": "{{count}} importado", - "proxmoxResultUpdated": "{{count}} actualizado", - "proxmoxResultFailed": "{{count}} falló", + "proxmoxResultImported": "{{count}} importados", + "proxmoxResultUpdated": "{{count}} actualizados", + "proxmoxResultFailed": "{{count}} fallidos", + "proxmoxResultSkippedNoIp": "{{count}} omitido (no se encontró ninguna IP)", "proxmoxImportComplete": "Importación de Proxmox completada: {{summary}}", - "proxmoxDiscoveryFailed": "El descubrimiento fracasó", - "proxmoxImportFailed": "Falló la importación", - "enableFileManagerMonitor": "Habilitar el administrador de archivos", - "enableFileManagerMonitorDesc": "Explore y gestione archivos en este host a través de SFTP.", - "scpLegacyLabel": "Modo heredado de SCP", - "scpLegacyDesc": "Utilice la transferencia de archivos heredada para servidores que no admitan el subsistema SFTP (por ejemplo, servidores SSH integrados o mínimos).", + "proxmoxDiscoveryFailed": "Descubrimiento fallido", + "proxmoxImportFailed": "Importación fallida", + "enableFileManagerMonitor": "Habilitar Gestor de archivos", + "enableFileManagerMonitorDesc": "Explorar y gestionar archivos en este host mediante SFTP", + "scpLegacyLabel": "Modo SCP heredado", + "scpLegacyDesc": "Usar transferencia de archivos heredada para servidores que no admiten el subsistema SFTP (p. ej., servidores SSH integrados o mínimos).", "defaultPathLabel": "Ruta predeterminada", - "fileManagerPathHint": "El directorio que se abrirá cuando se inicie el gestor de archivos para este host.", + "fileManagerPathHint": "Directorio que se abre al iniciar el gestor de archivos para este host.", "statusChecksLabel": "Comprobaciones de estado", "enableStatusChecks": "Habilitar comprobaciones de estado", - "enableStatusChecksDesc": "Haz ping periódicamente a este host para verificar su disponibilidad.", - "useGlobalInterval": "Utilizar intervalo global", - "useGlobalIntervalDesc": "Anular con el intervalo de comprobación de estado de todo el servidor", + "enableStatusChecksDesc": "Hacer ping periódicamente a este host para verificar su disponibilidad.", + "useGlobalInterval": "Usar intervalo global", + "useGlobalIntervalDesc": "Sobrescribir con el intervalo de comprobación de estado global del servidor.", "checkIntervalS": "Intervalo de comprobación (s)", - "checkIntervalDesc": "Segundos entre cada ping de conectividad", + "checkIntervalDesc": "Segundos entre cada ping de conectividad.", "metricsCollectionLabel": "Recopilación de métricas", "enableMetricsLabel": "Habilitar métricas", - "enableMetricsDesc": "Recopilar métricas de CPU, RAM, disco y otras de este host.", - "useGlobalMetrics": "Utilizar intervalo global", - "useGlobalMetricsDesc": "Anular con el intervalo de métricas de todo el servidor", + "enableMetricsDesc": "Recopilar CPU, RAM, disco y otras métricas de este host.", + "useGlobalMetrics": "Usar intervalo global", + "useGlobalMetricsDesc": "Sobrescribir con el intervalo de métricas global del servidor.", "metricsIntervalS": "Intervalo de métricas (s)", - "metricsIntervalDesc2": "Segundos entre instantáneas de métricas", + "metricsIntervalDesc2": "Segundos entre instantáneas de métricas.", "visibleWidgets": "Widgets visibles", - "widgetsMovedToHostMetrics": "Ahora, las tarjetas se agregan, organizan y redimensionan directamente en la pestaña Métricas del host. Abra Métricas del host para este host y use Personalizar para elegir qué tarjetas se muestran.", - "cpuUsageLabel": "Uso de la CPU", - "cpuUsageDesc": "Porcentaje de CPU, carga promedio, gráfico de minilíneas", + "widgetsMovedToHostMetrics": "Ahora las tarjetas se añaden, organizan y redimensionan directamente en la pestaña Métricas del host. Abre Métricas del host para este host y usa Personalizar para elegir qué tarjetas se muestran.", + "cpuUsageLabel": "Uso de CPU", + "cpuUsageDesc": "Porcentaje de CPU, carga media, gráfico de líneas", "memoryLabel": "Uso de memoria", - "memoryDesc": "Uso de RAM, intercambio, caché", - "storageLabel": "Uso del disco", - "storageDesc": "Uso del disco por punto de montaje", + "memoryDesc": "Uso de RAM, swap, caché", + "storageLabel": "Uso de disco", + "storageDesc": "Uso de disco por punto de montaje", "networkLabel": "Interfaces de red", "networkDesc": "Lista de interfaces y ancho de banda", "uptimeLabel": "Tiempo de actividad", - "uptimeDesc": "Tiempo de actividad del sistema y tiempo de arranque", + "uptimeDesc": "Tiempo de actividad del sistema y hora de arranque", "systemInfoLabel": "Información del sistema", - "systemInfoDesc": "Sistema operativo, núcleo, nombre de host, arquitectura", + "systemInfoDesc": "SO, kernel, nombre de host, arquitectura", "recentLoginsLabel": "Inicios de sesión recientes", "recentLoginsDesc": "Eventos de inicio de sesión exitosos y fallidos", - "topProcessesLabel": "Procesos superiores", - "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Puertos de escucha", + "topProcessesLabel": "Procesos principales", + "topProcessesDesc": "PID, %CPU, %MEM, comando", + "listeningPortsLabel": "Puertos en escucha", "listeningPortsDesc": "Puertos abiertos con proceso y estado", "firewallLabel": "Cortafuegos", - "firewallDesc": "Estado del firewall, AppArmor y SELinux", + "firewallDesc": "Estado del cortafuegos, AppArmor, SELinux", "quickActionsLabel": "Acciones rápidas", - "quickActionsToolbar": "Las acciones rápidas aparecen como botones en la barra de herramientas de Métricas del host para ejecutar comandos con un solo clic.", - "noQuickActions": "Aún no se han tomado medidas rápidas.", + "quickActionsToolbar": "Las acciones rápidas aparecen como botones en la barra de herramientas de Métricas del host 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": "Agregar acción", - "hostSharedSuccessfully": "El anfitrión compartió la información correctamente.", - "failedToShareHost": "No se pudo compartir el host", + "addActionBtn": "Añadir acción", + "hostSharedSuccessfully": "Host compartido correctamente", + "failedToShareHost": "Error al compartir el host", "accessRevoked": "Acceso revocado", - "failedToRevokeAccess": "No se pudo revocar el acceso.", + "failedToRevokeAccess": "Error al revocar el acceso", "cancelBtn": "Cancelar", - "savingBtn": "Ahorro...", - "addHostBtn": "Agregar host", + "savingBtn": "Guardando...", + "addHostBtn": "Añadir host", "hostUpdated": "Host actualizado", "hostCreated": "Host creado", - "failedToSave": "No se pudo guardar el host.", - "credentialUpdated": "Credenciales actualizadas", + "failedToSave": "Error al guardar el host", + "credentialUpdated": "Credencial actualizada", "credentialCreated": "Credencial creada", - "failedToSaveCredential": "No se pudo guardar la credencial.", - "credentialNameRequired": "Por favor, introduzca un nombre para la credencial.", - "credentialAuthRequired": "Agregue una contraseña, una clave SSH o ambas.", - "backToHosts": "Volver a los anfitriones", - "backToCredentials": "Volver a Credenciales", - "pinned": "Fijado", - "noHostsFound": "No se encontraron hosts", - "tryDifferentTerm": "Prueba con un término diferente.", - "addFirstHost": "Agrega tu primer anfitrión para comenzar.", + "failedToSaveCredential": "Error al guardar la credencial", + "credentialNameRequired": "Por favor, introduzca un nombre para la credencial", + "credentialAuthRequired": "Añada una contraseña, una clave SSH o ambas", + "createCredentialFromHostBtn": "Crear credenciales", + "createCredentialFromHostTitle": "Crear credenciales desde el host", + "createCredentialFromHostDesc": "Crea una entrada de credenciales reutilizable y compartible, precargada con el nombre de usuario, la contraseña y/o la clave SSH actuales de este host.", + "backToHosts": "Volver a equipos", + "backToCredentials": "Volver a credenciales", + "pinned": "Fijados", + "noHostsFound": "No se encontraron equipos", + "tryDifferentTerm": "Pruebe con otro término", + "addFirstHost": "Añada su primer equipo para comenzar", "noCredentialsFound": "No se encontraron credenciales", - "addCredentialBtn": "Agregar credencial", - "updateCredentialBtn": "Actualizar credenciales", + "addCredentialBtn": "Añadir credencial", + "updateCredentialBtn": "Actualizar credencial", "features": "Características", "noFolder": "(Sin carpeta)", - "deleteSelected": "Borrar", - "exitSelection": "Selección de salida", - "importSkip": "Importar (omitir existente)", + "deleteSelected": "Eliminar", + "exitSelection": "Salir de la selección", + "importSkip": "Importar (omitir existentes)", "importOverwrite": "Importar (sobrescribir)", - "collapseBtn": "Colapsar", - "importExportBtn": "Importación / Exportación", - "hostStatusesRefreshed": "Estado del host actualizado", - "failedToRefreshHosts": "No se pudo actualizar el host.", - "movedHostTo": "Movido {{host}} a \"{{folder}}\"", - "failedToMoveHost": "No se pudo mover el host", + "collapseBtn": "Contraer", + "importExportBtn": "Importar / Exportar", + "hostStatusesRefreshed": "Estados de los equipos actualizados", + "failedToRefreshHosts": "Error al actualizar los equipos", + "movedHostTo": "Se ha movido {{host}} a \"{{folder}}\"", + "failedToMoveHost": "Error al mover el equipo", "folderRenamedTo": "Carpeta renombrada a \"{{name}}\"", - "deletedFolder": "Carpeta eliminada \"{{name}}\"", - "failedToDeleteFolder": "No se pudo eliminar la carpeta", - "deleteAllInFolder": "¿Eliminar todos los hosts en \"{{name}}\"? Esto no se puede deshacer.", - "folderPickerPlaceholder": "No hay carpeta", - "folderPickerSearch": "Buscar o crear (use / para subcarpetas)...", - "folderPickerNone": "No hay carpeta", + "deletedFolder": "Se ha eliminado la carpeta \"{{name}}\"", + "failedToDeleteFolder": "Error al eliminar la carpeta", + "deleteAllInFolder": "¿Eliminar todos los equipos en \"{{name}}\"? Esta acción no se puede deshacer.", + "folderPickerPlaceholder": "Sin carpeta", + "folderPickerSearch": "Buscar o crear (usar / para subcarpetas)...", + "folderPickerNone": "Sin carpeta", "folderPickerCreate": "Crear \"{{path}}\"", - "folderPickerEmpty": "No hay carpetas coincidentes", + "folderPickerEmpty": "Sin carpetas coincidentes", "newFolder": "Nueva carpeta", "createFolderTitle": "Crear carpeta", - "editFolderTitle": "Carpeta de edición", - "folderDialogDescription": "Elige un nombre, un color y un icono. Usa / para anidar carpetas.", + "editFolderTitle": "Editar carpeta", + "folderDialogDescription": "Elija un nombre, color e icono. Utilice / para anidar carpetas.", "folderNameLabel": "Nombre de la carpeta", "folderNamePlaceholder": "p. ej. Producción/Web", - "folderNestingHint": "Utilice / para separar niveles y crear carpetas anidadas.", + "folderNestingHint": "Usa / para separar niveles y crear carpetas anidadas.", "folderColor": "Color", "folderIcon": "Icono", - "folderPreview": "Avance", + "folderCredential": "Credencial", + "folderCredentialNone": "No se ha asignado ninguna credencial.", + "folderCredentialHint": "Los hosts de esta carpeta que utilicen la autenticación con \"credenciales almacenadas\" sin haber seleccionado sus propias credenciales heredarán esta.", + "folderPreview": "Vista previa", "folderNameFallback": "Carpeta sin título", "createFolderButton": "Crear carpeta", "saveFolderButton": "Guardar carpeta", "cancel": "Cancelar", - "iconSearchPlaceholder": "Iconos de búsqueda...", + "iconSearchPlaceholder": "Buscar iconos...", "openAllSessions": "Abrir todas las sesiones", - "editFolder": "Carpeta de edición", + "editFolder": "Editar carpeta", "deleteFolder": "Eliminar carpeta", "folderSaved": "Carpeta guardada", - "failedToSaveFolder": "No se pudo guardar la carpeta.", + "failedToSaveFolder": "No se pudo guardar la carpeta", "folderDeleted": "Carpeta eliminada \"{{name}}\"", - "deleteFolderConfirm": "¿Eliminar \"{{name}}\" y su(s) host(s) {{count}} ? Esto no se puede deshacer.", - "failedToMoveHosts": "No se pudo cambiar de host.", + "deleteFolderConfirm": "¿Eliminar \"{{name}}\" y sus {{count}} hosts? Esta acción no se puede deshacer.", + "failedToMoveHosts": "No se pudieron mover los hosts", "expandAll": "Expandir todas las carpetas", "collapseAll": "Contraer todas las carpetas", "moreActions": "Más", @@ -1026,142 +1088,142 @@ "groupUngrouped": "Sin agrupar", "deletedHost": "Eliminado {{name}}", "copiedToClipboard": "Copiado al portapapeles", - "terminalUrlCopied": "URL del terminal copiada", - "fileManagerUrlCopied": "URL del administrador de archivos copiada", + "terminalUrlCopied": "URL de terminal copiada", + "fileManagerUrlCopied": "URL del gestor de archivos copiada", "tunnelUrlCopied": "URL del túnel copiada", "dockerUrlCopied": "URL de Docker copiada", "hostMetricsUrlCopied": "URL de métricas del host copiada", - "tmuxMonitorUrlCopied": "URL del monitor Tmux copiada", + "tmuxMonitorUrlCopied": "URL de Tmux Monitor copiada", "rdpUrlCopied": "URL de RDP copiada", "vncUrlCopied": "URL de VNC copiada", "telnetUrlCopied": "URL de Telnet copiada", - "remoteDesktopUrlCopied": "URL de Escritorio remoto copiada", - "expandActions": "Ampliar acciones", - "collapseActions": "Acciones de colapso", - "wakeOnLanAction": "Activar la LAN", + "remoteDesktopUrlCopied": "URL del escritorio remoto copiada", + "expandActions": "Expandir acciones", + "collapseActions": "Contraer acciones", + "wakeOnLanAction": "Wake on LAN", "wakeOnLanSuccess": "Paquete mágico enviado a {{name}}", "wakeOnLanError": "Error al enviar el paquete mágico", - "cloneHostAction": "Host de clonación", + "cloneHostAction": "Clonar host", "copyAddress": "Copiar dirección", "copyLink": "Copiar enlace", "copyTerminalUrlAction": "Copiar URL del terminal", - "copyFileManagerUrlAction": "Copiar URL del administrador de archivos", + "copyFileManagerUrlAction": "Copiar URL del gestor de archivos", "copyTunnelUrlAction": "Copiar URL del túnel", "copyDockerUrlAction": "Copiar URL de Docker", "copyHostMetricsUrlAction": "Copiar URL de métricas del host", - "copyTmuxMonitorUrlAction": "Copiar la URL del monitor de Tmux", + "copyTmuxMonitorUrlAction": "Copiar URL de Tmux Monitor", "copyRdpUrlAction": "Copiar URL de RDP", "copyVncUrlAction": "Copiar URL de VNC", "copyTelnetUrlAction": "Copiar URL de Telnet", - "copyRemoteDesktopUrlAction": "Copiar URL de Escritorio remoto", + "copyRemoteDesktopUrlAction": "Copiar URL del escritorio remoto", "deleteCredentialConfirm": "¿Eliminar credencial \"{{name}}\"?", - "deletedCredential": "Eliminado {{name}}", - "deploySSHKeyTitle": "Implementar clave SSH", + "deletedCredential": "Eliminada {{name}}", + "deploySSHKeyTitle": "Desplegar clave SSH", "deployingBtn": "Desplegando...", "deployBtn": "Desplegar", "failedToDeployKey": "Error al desplegar la clave", - "deleteHostsConfirm": "¿Eliminar el host {{count}}{{plural}}? Esto no se puede deshacer.", - "movedToRoot": "Trasladado a la raíz", + "deleteHostsConfirm": "¿Eliminar {{count}} host{{plural}}? Esta acción no se puede deshacer.", + "movedToRoot": "Movido a la raíz", "enableTerminalFeature": "Habilitar terminal", "disableTerminalFeature": "Deshabilitar terminal", "enableFilesFeature": "Habilitar archivos", "disableFilesFeature": "Deshabilitar archivos", - "enableTunnelsFeature": "Habilitar túneles", - "disableTunnelsFeature": "Deshabilitar túneles", - "enableDockerFeature": "Habilitar Docker", - "disableDockerFeature": "Deshabilitar Docker", - "enableProxmoxFeature": "Habilitar Proxmox", - "disableProxmoxFeature": "Deshabilitar Proxmox", - "addTagsPlaceholder": "Agregar etiquetas...", + "enableTunnelsFeature": "Activar túneles", + "disableTunnelsFeature": "Desactivar túneles", + "enableDockerFeature": "Activar Docker", + "disableDockerFeature": "Desactivar Docker", + "enableProxmoxFeature": "Activar Proxmox", + "disableProxmoxFeature": "Desactivar Proxmox", + "addTagsPlaceholder": "Añadir etiquetas...", "authDetails": "Detalles de autenticación", "credType": "Tipo", - "generateKeyPairDesc": "Genera un nuevo par de claves; tanto la clave privada como la pública se completarán automáticamente.", + "generateKeyPairDesc": "Generar un nuevo par de claves, se rellenarán automáticamente la clave privada y la pública.", "generatingKey": "Generando...", "generateLabel": "Generar {{label}}", "uploadFileBtn": "Subir archivo", - "keyPassphraseOptional": "Contraseña clave (opcional)", + "keyPassphraseOptional": "Contraseña de la clave (opcional)", "sshPublicKeyOptional": "Clave pública SSH (opcional)", "publicKeyGenerated": "Clave pública generada", - "failedToGeneratePublicKey": "No se pudo obtener la clave pública.", + "failedToGeneratePublicKey": "Error al derivar la clave pública", "publicKeyCopied": "Clave pública copiada", - "keyPairGenerated": "{{label}} par de claves generado", - "failedToGenerateKeyPair": "No se pudo generar el par de claves", + "keyPairGenerated": "Par de claves {{label}} generado", + "failedToGenerateKeyPair": "Error al generar el par de claves", "searchHostsPlaceholder": "Buscar hosts, direcciones, etiquetas…", "searchCredentialsPlaceholder": "Buscar credenciales…", - "refreshBtn": "Refrescar", - "addTag": "Agregar etiquetas...", - "deleteConfirmBtn": "Borrar", - "tunnelRequirementsText": "El servidor SSH debe tener las opciones GatewayPorts yes, AllowTcpForwarding yes y PermitRootLogin yes configuradas en /etc/ssh/sshd_config.", + "refreshBtn": "Actualizar", + "addTag": "Añadir etiquetas...", + "deleteConfirmBtn": "Eliminar", + "tunnelRequirementsText": "El servidor SSH debe tener GatewayPorts yes, AllowTcpForwarding yes y PermitRootLogin yes configurados en /etc/ssh/sshd_config.", "deleteHostConfirm": "¿Eliminar \"{{name}}\"?", - "enableAtLeastOneProtocol": "Habilite al menos uno de los protocolos anteriores para configurar los ajustes de autenticación y conexión.", - "keyPassphrase": "Contraseña clave", + "enableAtLeastOneProtocol": "Active al menos un protocolo de los anteriores para configurar los ajustes de autenticación y conexión.", + "keyPassphrase": "Contraseña de la clave", "connectBtn": "Conectar", "disconnectBtn": "Desconectar", "basicInformation": "Información básica", "authDetailsSection": "Detalles de autenticación", "credTypeLabel": "Tipo", - "hostsTab": "Anfitriones", - "credentialsTab": "Cartas credenciales", - "selectMultiple": "Seleccione varios", + "hostsTab": "Hosts", + "credentialsTab": "Credenciales", + "selectMultiple": "Seleccionar varios", "selectHosts": "Seleccionar hosts", "connectionLabel": "Conexión", "authenticationLabel": "Autenticación", "generateKeyPairTitle": "Generar par de claves", - "generateKeyPairDescription": "Genera un nuevo par de claves; tanto la clave privada como la pública se completarán automáticamente.", - "generateFromPrivateKey": "Generar a partir de la clave privada", - "refreshBtn2": "Refrescar", - "exitSelectionTitle": "Selección de salida", + "generateKeyPairDescription": "Generar un nuevo par de claves, se rellenarán automáticamente las claves privada y pública.", + "generateFromPrivateKey": "Generar a partir de clave privada", + "refreshBtn2": "Actualizar", + "exitSelectionTitle": "Salir de la selección", "exportAll": "Exportar todo", "exportForSharing": "Exportar para compartir", - "addHostBtn2": "Agregar host", - "addCredentialBtn2": "Agregar credencial", - "checkingHostStatuses": "Comprobando el estado de los hosts...", - "pinnedSection": "Fijado", + "addHostBtn2": "Añadir host", + "addCredentialBtn2": "Añadir credencial", + "checkingHostStatuses": "Comprobando estados de los hosts...", + "pinnedSection": "Fijados", "hostsExported": "Hosts exportados correctamente", "hostsShareExported": "Hosts compartibles exportados correctamente", - "exportFailed": "Error al exportar los hosts.", + "exportFailed": "Error al exportar hosts", "sampleDownloaded": "Archivo de muestra descargado", - "failedToDeleteCredential2": "Error al eliminar las credenciales", + "failedToDeleteCredential2": "Error al eliminar credencial", "noFolderOption": "(Sin carpeta)", - "nSelected": "{{count}} seleccionado", - "featuresMenu": "Características", + "nSelected": "{{count}} seleccionados", + "featuresMenu": "Funcionalidades", "moveMenu": "Mover", "connectSelected": "Conectar", "cancelSelection": "Cancelar", - "deployDialogDesc": "Implementa {{name}} en las claves autorizadas de un host.", - "targetHostLabel": "Anfitrión objetivo", - "selectHostOption": "Seleccione un anfitrión...", + "deployDialogDesc": "Desplegar {{name}} en authorized_keys de un host.", + "targetHostLabel": "Host destino", + "selectHostOption": "Seleccionar un host...", "keyDeployedSuccess": "Clave desplegada correctamente", - "failedToDeployKey2": "Error al desplegar la clave", - "deletedCount": "Hosts eliminados {{count}}", - "failedToDeleteCount": "No se pudo eliminar {{count}} hosts", + "failedToDeployKey2": "Error al desplegar clave", + "deletedCount": "{{count}} hosts eliminados", + "failedToDeleteCount": "Error al eliminar {{count}} hosts", "duplicatedHost": "Duplicado \"{{name}}\"", "failedToDuplicateHost": "Error al duplicar el host", - "updatedCount": "Hosts actualizados {{count}}", + "updatedCount": "{{count}} hosts actualizados", "friendlyNameLabel": "Nombre descriptivo", "descriptionLabel": "Descripción", "loadingHost": "Cargando host...", "loadingHosts": "Cargando hosts...", "loadingCredentials": "Cargando credenciales...", - "noHostsYet": "Aún no hay anfitriones", - "noHostsMatchSearch": "No se encontraron hosts que coincidan con su búsqueda.", + "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", + "sortDefault": "Orden predeterminado", "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", + "sortIpDesc": "Dirección IP (Desc)", + "sortOnlineFirst": "En línea primero", + "sortOfflineFirst": "Sin conexión primero", + "sortPinnedFirst": "Fijados primero", "filterHosts": "Filtrar hosts", - "filterClearAll": "Borrar filtros", + "filterClearAll": "Limpiar filtros", "filterStatusGroup": "Estado", "filterOnline": "En línea", - "filterOffline": "Desconectado", - "filterPinned": "Fijado", + "filterOffline": "Sin conexión", + "filterPinned": "Fijados", "filterAuthGroup": "Tipo de autenticación", "filterAuthPassword": "Contraseña", "filterAuthKey": "Clave SSH", @@ -1177,34 +1239,38 @@ "filterFeatureTerminal": "Terminal", "filterFeatureFileManager": "Gestor de archivos", "filterFeatureTunnel": "Túnel", - "filterFeatureDocker": "Estibador", + "filterFeatureDocker": "Docker", "filterTagsGroup": "Etiquetas", - "shareHost": "Compartir anfitrión", + "shareHost": "Compartir host", "shareHostTitle": "Compartir: {{name}}", + "shareFolder": "Carpeta compartida", + "shareFolderTitle": "Carpeta compartida: {{name}}", + "folderSharedSuccessfully": "Host(s) compartido(s) {{count}} en la carpeta", + "failedToShareFolder": "Error al compartir la carpeta", "sharing": { - "loadError": "No se pudieron cargar los datos para compartir. Inténtelo de nuevo.", + "loadError": "Error al cargar los datos de compartición. Inténtelo de nuevo.", "shareWithSection": "Compartir con", "usersTab": "Usuarios", "rolesTab": "Roles", "searchPlaceholder": "Buscar usuarios o roles...", - "noMatches": "No se encontraron coincidencias", + "noMatches": "No se han encontrado coincidencias", "permissionLevelLabel": "Nivel de permiso", "levels": { "connect": { "label": "Conectar", - "description": "Solo se permiten sesiones abiertas: terminal, escritorio remoto, administrador de archivos, túneles y Docker. No se permite el acceso a la configuración del host." + "description": "Abrir solo sesiones: terminal, escritorio remoto, gestor de archivos, túneles y Docker. Sin acceso a la configuración del host." }, "view": { - "label": "Vista", - "description": "Conéctate y consulta la configuración del host. Las claves secretas nunca se muestran." + "label": "Ver", + "description": "Conectar, además de ver la configuración del host. Los secretos nunca se muestran." }, "edit": { "label": "Editar", - "description": "Visualice y modifique el host. Los secretos se pueden reemplazar, pero nunca leer; las asignaciones de credenciales permanecen solo para el propietario." + "description": "Ver, además de modificar el host. Los secretos se pueden reemplazar pero nunca leer; las asignaciones de credenciales permanecen solo para el propietario." }, "manage": { - "label": "Administrar", - "description": "Edita, comparte el host con otros, cambia los niveles de permisos y revoca el acceso." + "label": "Gestionar", + "description": "Editar, además de compartir el host con otros, cambiar niveles de permiso y revocar el acceso." } }, "expiryLabel": "Caducidad del acceso", @@ -1214,351 +1280,418 @@ "oneDay": "24 horas", "sevenDays": "7 días", "thirtyDays": "30 días", - "custom": "Costumbre" + "custom": "Personalizado" }, "customHoursPlaceholder": "Horas hasta que caduque el acceso", "shareButton": "Compartir", "shareWithCount": "Compartir ({{count}})", "currentAccess": "Acceso actual", - "noAccessEntries": "Este host aún no se ha compartido.", + "noAccessEntries": "Este host aún no se ha compartido", + "folderShareSummary": "Hosts compartidos {{shared}} de {{total}} en esta carpeta", "grantedBy": "Concedido por", "expires": "Caduca", - "expired": "Venció", + "expired": "Expirado", "never": "Nunca", "revoke": "Revocar", "accessUpdated": "Acceso actualizado", - "accessUpdateFailed": "No se pudo actualizar el acceso.", + "accessUpdateFailed": "Error al actualizar el acceso", "sharedBadge": "Compartido", - "sharedBadgeTooltip": "Compartido por {{owner}} ({{level}} acceso)", - "viewOnlyBanner": "Este host es compartido contigo por {{owner}} con acceso de visualización. La configuración es de solo lectura.", - "sharedEditBanner": "Este host es compartido contigo por {{owner}} con permisos de edición. Los cambios se aplican al host real; las referencias de autenticación solo pueden ser modificadas por el propietario.", - "ownerOnlyControl": "Solo el propietario del host puede cambiar esto." + "sharedBadgeTooltip": "Compartido por {{owner}} (acceso {{level}})", + "viewOnlyBanner": "Este host ha sido compartido contigo por {{owner}} con acceso de solo lectura. La configuración es de solo lectura.", + "sharedEditBanner": "Este host ha sido compartido contigo por {{owner}} con acceso de edición. Los cambios se aplican al host real; las referencias de autenticación solo pueden ser modificadas por el propietario.", + "ownerOnlyControl": "Solo el propietario del host puede modificar esto" }, "guac": { "connection": "Conexión", "authentication": "Autenticación", "storedCredential": "Credencial almacenada", - "noCredential": "Sin credenciales (credenciales directas a continuación)", + "noCredential": "Sin credencial (credenciales directas a continuación)", "authMethod": "Método de autenticación", - "authTypeDirect": "Directo", + "authTypeDirect": "Directa", "authTypeCredential": "Credencial", - "selectCredential": "Seleccione una credencial...", + "authTypeNone": "Ninguno", + "authTypeNoneDesc": "No se almacenan credenciales. Se le solicitará un nombre de usuario y una contraseña cada vez que se conecte; estos no se guardan.", + "selectCredential": "Seleccionar una credencial...", "connectionSettings": "Configuración de conexión", "displaySettings": "Configuración de pantalla", "audioSettings": "Configuración de audio", - "rdpPerformance": "Rendimiento de RDP", + "rdpPerformance": "Rendimiento RDP", "deviceRedirection": "Redirección de dispositivos", "session": "Sesión", - "gateway": "Puerta", - "remoteApp": "Aplicación remota", + "gateway": "Gateway", + "remoteApp": "RemoteApp", "clipboard": "Portapapeles", - "sessionRecording": "Grabación de la sesión", + "sessionRecording": "Grabación de sesión", "wakeOnLan": "Wake-on-LAN", "vncSettings": "Configuración de VNC", - "terminalSettings": "Configuración del terminal", + "terminalSettings": "Configuración de terminal", "rdpPort": "Puerto RDP", "username": "Nombre de usuario", "password": "Contraseña", + "passwordSaved": "Contraseña guardada, escriba para cambiarla", "domain": "Dominio", "securityMode": "Modo de seguridad", "colorDepth": "Profundidad de color", "width": "Ancho", - "height": "Altura", + "height": "Alto", "dpi": "DPI", "resizeMethod": "Método de redimensionamiento", "clientName": "Nombre del cliente", "initialProgram": "Programa inicial", - "serverLayout": "Diseño del servidor", + "serverLayout": "Disposición del teclado", "timezone": "Zona horaria", - "loadBalanceInfo": "Información sobre el equilibrio de carga / Cookies", - "loadBalanceInfoDesc": "Cookie de RD Connection Broker para el equilibrio de carga de la granja de RDS (por ejemplo, tsv://MS Terminal Services Plugin.1.CollectionName)", - "guacdProxy": "guacd Proxy", - "guacdHostname": "Host guacd", - "guacdHostnamePlaceholder": "Configuración predeterminada global", - "guacdPort": "Puerto guacd", - "guacdProxyDesc": "Anule la instancia global de guacd para esta conexión. Deje este campo en blanco para usar la instancia de guacd configurada globalmente.", + "loadBalanceInfo": "Información de balanceo de carga / Cookie", + "loadBalanceInfoDesc": "Cookie del agente de conexiones a Escritorio Remoto para el balanceo de carga de la granja RDS (p. ej. tsv://MS Terminal Services Plugin.1.CollectionName)", + "guacdProxy": "Proxy de guacd", + "guacdHostname": "Host de guacd", + "guacdHostnamePlaceholder": "Predeterminado global", + "guacdPort": "Puerto de guacd", + "guacdProxyDesc": "Sobrescribir la instancia global de guacd para esta conexión. Dejar en blanco para usar la configuración global de guacd.", "gatewayHostname": "Nombre de host de la puerta de enlace", - "gatewayPort": "Puerto de entrada", + "gatewayPort": "Puerto de la puerta de enlace", "gatewayUsername": "Nombre de usuario de la puerta de enlace", - "gatewayPassword": "Contraseña de puerta de enlace", - "gatewayDomain": "Dominio de puerta de enlace", + "gatewayPassword": "Contraseña de la puerta de enlace", + "gatewayDomain": "Dominio de la puerta de enlace", "remoteAppProgram": "Programa RemoteApp", "workingDirectory": "Directorio de trabajo", "arguments": "Argumentos", - "normalizeLineEndings": "Normalizar los finales de línea", + "normalizeLineEndings": "Normalizar finales de línea", "recordingPath": "Ruta de grabación", "recordingName": "Nombre de la grabación", "macAddress": "Dirección MAC", - "broadcastAddress": "Discurso de difusión", + "broadcastAddress": "Dirección de difusión", "udpPort": "Puerto UDP", "waitTimeS": "Tiempo de espera (s)", "driveName": "Nombre de la unidad", - "drivePath": "Camino de acceso", + "drivePath": "Ruta de la unidad", "ignoreCertificate": "Ignorar certificado", - "ignoreCertificateDesc": "Permitir conexiones a hosts con certificados autofirmados.", - "forceLossless": "Fuerza sin pérdidas", - "forceLosslessDesc": "Codificación de imagen sin pérdidas (mayor calidad, mayor ancho de banda)", + "ignoreCertificateDesc": "Permitir conexiones a hosts con certificados autofirmados", + "forceLossless": "Forzar sin pérdida", + "forceLosslessDesc": "Forzar codificación de imagen sin pérdida (más calidad, más ancho de banda)", "disableAudio": "Desactivar audio", "disableAudioDesc": "Silenciar todo el audio de la sesión remota", - "enableAudioInput": "Habilitar entrada de audio (micrófono)", + "enableAudioInput": "Activar entrada de audio (micrófono)", "enableAudioInputDesc": "Reenviar el micrófono local a la sesión remota", - "wallpaper": "Papel pintado", + "wallpaper": "Fondo de pantalla", "wallpaperDesc": "Mostrar fondo de escritorio (desactivarlo mejora el rendimiento)", - "theming": "Tematización", - "themingDesc": "Habilitar temas y estilos visuales", + "theming": "Temas", + "themingDesc": "Activar temas y estilos visuales", "fontSmoothing": "Suavizado de fuentes", - "fontSmoothingDesc": "Habilitar la representación de fuentes ClearType", + "fontSmoothingDesc": "Activar renderizado de fuentes ClearType", "fullWindowDrag": "Arrastre de ventana completa", - "fullWindowDragDesc": "Mostrar el contenido de la ventana mientras se arrastra", + "fullWindowDragDesc": "Mostrar el contenido de la ventana al arrastrar", "desktopComposition": "Composición de escritorio", - "desktopCompositionDesc": "Habilitar los efectos de cristal Aero", + "desktopCompositionDesc": "Activar efectos Aero Glass", "menuAnimations": "Animaciones de menú", - "menuAnimationsDesc": "Habilitar animaciones de desvanecimiento y deslizamiento del menú", - "disableBitmapCaching": "Deshabilitar el almacenamiento en caché de mapas de bits", - "disableBitmapCachingDesc": "Desactiva la caché de mapas de bits (esto puede ayudar a solucionar problemas técnicos).", - "disableOffscreenCaching": "Deshabilitar el almacenamiento en caché fuera de pantalla", + "menuAnimationsDesc": "Activar animaciones de fundido y deslizamiento de menú", + "disableBitmapCaching": "Desactivar caché de mapa de bits", + "disableBitmapCachingDesc": "Desactivar la caché de mapa de bits (puede ayudar con fallos)", + "disableOffscreenCaching": "Desactivar caché fuera de pantalla", "disableOffscreenCachingDesc": "Desactivar la caché fuera de pantalla", - "disableGlyphCaching": "Deshabilitar el almacenamiento en caché de glifos", + "disableGlyphCaching": "Desactivar caché de glifos", "disableGlyphCachingDesc": "Desactivar la caché de glifos", - "enableGfx": "Habilitar GFX", - "enableGfxDesc": "Utilice la canalización gráfica de RemoteFX.", - "enablePrinting": "Habilitar impresión", - "enablePrintingDesc": "Redirigir las impresoras locales a la sesión remota.", - "enableDriveRedirection": "Habilitar la redirección de unidades", - "enableDriveRedirectionDesc": "Asignar una carpeta local como una unidad en la sesión remota.", - "createDrivePath": "Crear ruta de acceso", - "createDrivePathDesc": "Crear automáticamente la carpeta si no existe.", + "enableGfx": "Activar GFX", + "enableGfxDesc": "Usar la canalización de gráficos RemoteFX", + "enablePrinting": "Activar impresión", + "enablePrintingDesc": "Redirigir las impresoras locales a la sesión remota", + "enableDriveRedirection": "Activar redirección de unidades", + "enableDriveRedirectionDesc": "Asignar 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": "Deshabilitar descarga", - "disableDownloadDesc": "Impedir la descarga de archivos desde la sesión remota.", - "disableUpload": "Deshabilitar la carga", - "disableUploadDesc": "Impedir la carga de archivos a la sesión remota.", - "enableTouch": "Habilitar la función táctil", + "disableDownloadDesc": "Impedir la descarga de archivos de la sesión remota", + "disableUpload": "Deshabilitar subida", + "disableUploadDesc": "Impedir la subida de archivos a la sesión remota", + "enableTouch": "Habilitar táctil", "enableTouchDesc": "Habilitar el reenvío de entrada táctil", "consoleSession": "Sesión de consola", - "consoleSessionDesc": "Conéctese a la consola (sesión 0) en lugar de iniciar una nueva sesión.", + "consoleSessionDesc": "Conectar a la consola (sesión 0) en lugar de una nueva sesión", "sendWolPacket": "Enviar paquete WOL", - "sendWolPacketDesc": "Envía un paquete mágico para despertar a este host antes de conectarte.", + "sendWolPacketDesc": "Enviar un paquete mágico para encender este equipo antes de conectar", "disableCopy": "Deshabilitar copia", - "disableCopyDesc": "Impedir la copia de texto desde la sesión remota.", - "disablePaste": "Deshabilitar pegar", - "disablePasteDesc": "Impedir pegar texto en la sesión remota.", - "createPathIfMissing": "Crear ruta si falta", + "disableCopyDesc": "Impedir la copia de texto de la sesión remota", + "disablePaste": "Deshabilitar pegado", + "disablePasteDesc": "Impedir el pegado de texto en la sesión remota", + "createPathIfMissing": "Crear ruta si no existe", "createPathIfMissingDesc": "Crear automáticamente el directorio de grabación", "excludeOutput": "Excluir salida", - "excludeOutputDesc": "No grabar la salida de pantalla (solo metadatos).", + "excludeOutputDesc": "No grabar la salida de pantalla (solo metadatos)", "excludeMouse": "Excluir ratón", "excludeMouseDesc": "No grabar los movimientos del ratón", "includeKeystrokes": "Incluir pulsaciones de teclas", - "includeKeystrokesDesc": "Registra las pulsaciones de teclas sin procesar además de la salida de pantalla.", + "includeKeystrokesDesc": "Grabar las pulsaciones de teclas en bruto además de la salida de pantalla", "vncPort": "Puerto VNC", "vncPassword": "Contraseña VNC", "vncUsernameOptional": "Nombre de usuario (opcional)", - "vncLeaveBlank": "Déjelo en blanco si no es necesario.", - "cursorMode": "Modo cursor", + "vncLeaveBlank": "Dejar en blanco si no es necesario", + "cursorMode": "Modo de cursor", "swapRedBlue": "Intercambiar rojo/azul", - "swapRedBlueDesc": "Intercambia los canales de color rojo y azul (soluciona algunos problemas de color).", + "swapRedBlueDesc": "Intercambiar los canales de color rojo y azul (corrige algunos problemas de color)", "readOnly": "Solo lectura", - "readOnlyDesc": "Visualice la pantalla remota sin enviar ninguna entrada.", + "readOnlyDesc": "Ver la pantalla remota sin enviar ninguna entrada", "telnetPort": "Puerto Telnet", "terminalType": "Tipo de terminal", - "fontName": "Nombre de la fuente", + "fontName": "Nombre de fuente", "fontSize": "Tamaño de fuente", - "colorScheme": "Combinación de colores", + "colorScheme": "Esquema de color", "backspaceKey": "Tecla de retroceso", - "saveHostFirst": "Primero, salva al anfitrión.", - "sharingOptionsAfterSave": "Las opciones para compartir estarán disponibles una vez que se haya guardado el host.", + "saveHostFirst": "Primero guarde el host.", + "sharingOptionsAfterSave": "Las opciones de compartición están disponibles después de guardar el host.", "permissionLevel": "Nivel de permiso", "typeHeader": "Tipo", - "targetHeader": "Objetivo", + "targetHeader": "Destino", "permissionHeader": "Permiso", "cancelBtn": "Cancelar", - "savingBtn": "Ahorro...", - "updateHostBtn": "Host de actualización", - "addHostBtn": "Agregar host" + "savingBtn": "Guardando...", + "updateHostBtn": "Actualizar host", + "addHostBtn": "Añadir host" } }, "commandPalette": { - "searchPlaceholder": "Buscar hosts, comandos o configuraciones...", + "searchPlaceholder": "Buscar hosts, comandos o configuración...", "quickActions": "Acciones rápidas", - "hostManager": "Gerente anfitrión", - "hostManagerDesc": "Administrar, agregar o editar hosts", - "addNewHost": "Agregar nuevo host", - "addNewHostDesc": "Registra un nuevo anfitrión", + "hostManager": "Gestor de hosts", + "hostManagerDesc": "Gestionar, añadir o editar hosts", + "addNewHost": "Añadir nuevo host", + "addNewHostDesc": "Registrar un nuevo host", "adminSettings": "Configuración de administrador", - "adminSettingsDesc": "Configurar las preferencias del sistema y los usuarios", + "adminSettingsDesc": "Configurar preferencias del sistema y usuarios", "userProfile": "Perfil de usuario", - "userProfileDesc": "Gestiona tu cuenta y tus preferencias.", - "addCredential": "Agregar credencial", - "addCredentialDesc": "Almacenar claves o contraseñas SSH", - "tmuxMonitor": "Monitor Tmux", - "tmuxMonitorDesc": "Supervisa las sesiones de tmux en todos tus hosts.", + "userProfileDesc": "Gestionar su cuenta y preferencias", + "addCredential": "Añadir credencial", + "addCredentialDesc": "Almacenar claves SSH o contraseñas", + "tmuxMonitor": "Monitor de Tmux", + "tmuxMonitorDesc": "Supervisar sesiones de tmux en sus hosts", "recentActivity": "Actividad reciente", - "serversAndHosts": "Servidores y Hosts", - "noHostsFound": "No se encontraron hosts que coincidieran con \"{{search}}\"", - "links": "Campo de golf", - "navigate": "Navegar por", + "serversAndHosts": "Servidores y hosts", + "noHostsFound": "No se encontraron hosts que coincidan con \"{{search}}\"", + "links": "Enlaces", + "navigate": "Navegar", "select": "Seleccionar", "toggleWith": "Alternar con" }, "splitScreen": { "paneEmpty": "Panel {{index}} - vacío", - "noTabAssigned": "No se ha asignado ninguna pestaña.", + "noTabAssigned": "Ninguna pestaña asignada", "focusedPane": "Panel activo" }, "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}}", + "noConnectionsDesc": "Abre una terminal, gestor de archivos o escritorio remoto para ver las conexiones aquí", + "connectedFor": "Conectado durante {{duration}}", "connected": "Conectado", "disconnected": "Desconectado", "closeTab": "Cerrar pestaña", - "closeConnection": "Conexión estrecha", + "closeConnection": "Cerrar conexión", "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", + "sectionBackground": "En segundo plano", + "backgroundDesc": "Las sesiones persisten durante 30 minutos tras la desconexión y pueden reconectarse.", + "persisted": "Persistente en segundo plano", "expiresIn": "Caduca en {{duration}}", "search": "Buscar conexiones...", - "noSearchResults": "No se encontraron conexiones que coincidan con su búsqueda.", - "rename": "Cambiar nombre de la sesión" + "noSearchResults": "Ninguna conexión coincide con tu búsqueda", + "rename": "Renombrar sesión", + "sectionSharedWithMe": "Compartido conmigo", + "sharedBy": "Compartido por {{username}}", + "join": "Unirse", + "sharedSessionLabel": "{{hostName}} (compartido)" + }, + "sessionSharing": { + "guestView": { + "loading": "Conectando con la sesión compartida...", + "linkInvalid": "Este enlace para compartir no es válido, ha caducado o ha sido revocado.", + "rateLimited": "Demasiados intentos, por favor inténtelo de nuevo en breve.", + "sessionEnded": "Esta sesión ha finalizado.", + "readOnlyBadge": "Solo lectura" + }, + "modalTitle": "Compartir sesión", + "shareButton": "Compartir", + "notReadyToShare": "La sesión aún no está lista para compartirse.", + "modeTab": { + "link": "Enlace", + "user": "Usuario" + }, + "linkModeDescription": "Cualquier persona con este enlace puede unirse, no se requiere cuenta.", + "userModeDescription": "Comparte la sesión con un usuario específico que ya tenga acceso a este host. Si aún no tiene acceso, comparte primero el host con él o utiliza un enlace. Una vez compartida, la sesión aparecerá en su pestaña Conexiones.", + "permissionLevel": { + "label": "Nivel de permiso", + "readOnly": "Solo lectura", + "readOnlyDescription": "Se puede ver la sesión en directo, pero no se puede escribir ni interactuar.", + "readWrite": "Leer y escribir", + "readWriteDescription": "Puede escribir e interactuar con la sesión igual que el propietario." + }, + "expiryLabel": "Caducidad del enlace", + "createLinkButton": "Crear enlace", + "createShareButton": "Compartir con el usuario", + "searchUsersPlaceholder": "Buscar usuarios...", + "noUsersFound": "No se encontraron usuarios", + "linkCreated": "Enlace para compartir creado", + "linkCopied": "Enlace copiado al portapapeles", + "copyLink": "Copiar enlace", + "shareCreated": "Sesión compartida. Aparecerá en su pestaña Conexiones.", + "shareFailed": "No se pudo crear el recurso compartido", + "userLacksHostAccess": "Ese usuario aún no tiene acceso a este servidor. Primero, comparte el servidor con él o utiliza un enlace.", + "activeShares": "Acciones activas", + "noActiveShares": "No hay acciones activas para esta sesión.", + "revoke": "Revocar", + "revokeConfirmTitle": "¿Revocar esta acción?", + "revokeConfirmDescription": "Cualquier persona que utilice este recurso compartido perderá el acceso inmediatamente.", + "revoked": "Acción revocada", + "revokeFailed": "No se pudo revocar la participación.", + "joinCount": "{{count}} unirse", + "joinCount_other": "{{count}} se une", + "expiresAt": "Caduca {{date}}", + "linkShareBadge": "Enlace", + "userShareBadge": "Usuario: {{username}}", + "loadSharesFailed": "No se pudieron cargar los recursos compartidos activos." }, "guacamole": { - "connecting": "Conectando con la sesión {{type}}...", + "connecting": "Conectando a la sesión {{type}}...", "connectionError": "Error de conexión", - "connectionFailed": "Falló la conexión.", - "failedToConnect": "No se pudo obtener el token de conexión.", + "connectionFailed": "Conexión fallida", + "failedToConnect": "Error al obtener el token de conexión", "hostNotFound": "Host no encontrado", - "noHostSelected": "No se ha seleccionado ningún host.", + "noHostSelected": "Ningún host seleccionado", "reconnect": "Reconectar", - "retry": "Rever", - "guacdUnavailable": "El servicio de escritorio remoto (guacd) no está disponible. Asegúrese de que guacd esté en ejecución, sea accesible y esté configurado correctamente en la configuración de administración.", - "ctrlAltDel": "Ctrl+Alt+Supr", + "retry": "Reintentar", + "guacdUnavailable": "El servicio de escritorio remoto (guacd) no está disponible. Por favor, asegúrate de que guacd se está ejecutando, es accesible y está correctamente configurado en los ajustes de administración.", + "credentialPromptTitle": "Introduzca las credenciales de RDP", + "credentialPromptDescription": "Este host está configurado para solicitar credenciales al conectarse. Estas se utilizan únicamente para esta sesión y no se guardan.", + "connect": "Conectar", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl+Alt+Supr", - "winL": "Win+L (Pantalla de bloqueo)", - "winKey": "Tecla de Windows", - "ctrl": "Control", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Bloquear pantalla)", + "winKey": "Tecla Windows", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "Cambio", - "win": "Ganar", - "stickyActive": "{{key}} (bloqueado - haga clic para liberar)", - "stickyInactive": "{{key}} (haz clic para enganchar)", - "esc": "Escapar", - "tab": "Pestaña", - "home": "Hogar", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (activado - clic para soltar)", + "stickyInactive": "{{key}} (clic para activar)", + "esc": "Escape", + "tab": "Tab", + "home": "Inicio", "end": "Fin", - "pageUp": "Página arriba", - "pageDown": "Página abajo", - "arrowUp": "Flecha hacia arriba", - "arrowDown": "Flecha hacia abajo", + "pageUp": "Re Pág", + "pageDown": "Av Pág", + "arrowUp": "Flecha arriba", + "arrowDown": "Flecha abajo", "arrowLeft": "Flecha izquierda", "arrowRight": "Flecha derecha", "fnToggle": "Teclas de función", - "reconnect": "Sesión de reconexión", + "reconnect": "Reconectar sesión", "collapse": "Contraer barra de herramientas", "expand": "Expandir barra de herramientas", - "dragHandle": "Arrastra para reposicionar" + "dragHandle": "Arrastrar para reposicionar", + "switchToTrackpad": "Cambia al modo panel táctil (arrastra para mover el cursor, toca para hacer clic).", + "switchToTouch": "Cambia al modo táctil (pulsa directamente donde quieras hacer clic)." } }, "terminal": { - "connect": "Conéctese con el host", - "clear": "Claro", - "paste": "Pasta", + "connect": "Conectar al host", + "clear": "Limpiar", + "paste": "Pegar", "reconnect": "Reconectar", - "connectionLost": "Se perdió la conexión.", + "connectionLost": "Conexión perdida", "connected": "Conectado", - "clipboardWriteFailed": "No se pudo copiar al portapapeles. Asegúrese de que la página se sirva a través de HTTPS o localhost.", - "clipboardReadFailed": "No se pudo leer el contenido del portapapeles. Asegúrese de que se hayan otorgado los permisos necesarios para acceder al portapapeles.", - "clipboardHttpWarning": "Para pegar se requiere HTTPS. Use Ctrl+Shift+V o ejecute Termix a través de HTTPS.", - "passwordPromptFillTitle": "¿Desea introducir la contraseña guardada en este campo?", - "unknownError": "Se produjo un error desconocido.", + "clipboardWriteFailed": "Error al copiar al portapapeles. Asegúrese de que la página se sirva a través de HTTPS o en localhost.", + "clipboardReadFailed": "No se pudo leer del portapapeles. Asegúrese de que los permisos del portapapeles estén concedidos.", + "clipboardHttpWarning": "Pegar requiere HTTPS. Use Ctrl+Shift+V o sirva Termix a través de HTTPS.", + "passwordPromptFillTitle": "¿Rellenar este campo con la contraseña guardada?", + "unknownError": "Se ha producido un error desconocido.", "websocketError": "Error de conexión WebSocket", "connecting": "Conectando...", - "noHostSelected": "No se ha seleccionado ningún host.", + "noHostSelected": "Ningún host seleccionado", "reconnecting": "Reconectando... ({{attempt}}/{{max}})", - "reconnected": "Conexión restablecida con éxito", - "tmuxSessionCreated": "Sesión de tmux creada: {{name}}", - "tmuxSessionAttached": "Sesión de tmux adjunta: {{name}}", - "tmuxUnavailable": "tmux no está instalado en el host remoto, por lo que se recurrirá al intérprete de comandos estándar.", - "tmuxSessionPickerTitle": "Sesiones de tmux", - "tmuxSessionPickerDesc": "Se han encontrado sesiones de tmux existentes en este host. Seleccione una para volver a conectarse o cree una nueva sesión.", - "tmuxWindows": "Windows", + "reconnected": "Reconectado correctamente", + "tmuxSessionCreated": "Sesión tmux creada: {{name}}", + "tmuxSessionAttached": "Sesión tmux adjuntada: {{name}}", + "tmuxUnavailable": "tmux no está instalado en el host remoto, recurriendo al intérprete de comandos estándar", + "tmuxSessionPickerTitle": "Sesiones tmux", + "tmuxSessionPickerDesc": "Se encontraron sesiones tmux existentes en este host. Seleccione una para readjuntar o cree una nueva sesión.", + "tmuxWindows": "Ventanas", "tmuxWindowCount": "{{count}} ventana", "tmuxAttached": "Clientes adjuntos", "tmuxAttachedCount": "{{count}} adjunto", "tmuxLastActivity": "Última actividad", - "tmuxTimeJustNow": "En este momento", - "tmuxTimeMinutes": "{{count}}hace m", - "tmuxTimeHours": "{{count}}hace h", - "tmuxTimeDays": "{{count}}hace d", + "tmuxTimeJustNow": "ahora mismo", + "tmuxTimeMinutes": "hace {{count}}m", + "tmuxTimeHours": "hace {{count}}h", + "tmuxTimeDays": "hace {{count}}d", "tmuxCreateNew": "Iniciar nueva sesión", - "tmuxCopyHint": "Ajusta la selección y pulsa Intro para copiar al portapapeles.", - "tmuxDetach": "Desconectarse de la sesión de tmux", - "tmuxDetached": "Desconectado de la sesión de tmux", - "maxReconnectAttemptsReached": "Se ha alcanzado el número máximo de intentos de reconexión.", - "closeTab": "Cerca", - "connectionTimeout": "Tiempo de espera de conexión agotado", + "tmuxCopyHint": "Ajuste la selección y pulse Intro para copiar al portapapeles", + "tmuxDetach": "Desconectar de la sesión tmux", + "tmuxDetached": "Desconectado de la sesión tmux", + "maxReconnectAttemptsReached": "Número máximo de intentos de reconexión alcanzado", + "closeTab": "Cerrar", + "connectionTimeout": "Timeout de conexión", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Corriendo {{command}} - {{host}}", - "totpRequired": "Se requiere autenticación de dos factores.", + "runTitle": "Ejecutando {{command}} - {{host}}", + "totpRequired": "Autenticación de dos factores requerida", "totpCodeLabel": "Código de verificación", "totpVerify": "Verificar", - "warpgateAuthRequired": "Se requiere autenticación de Warpgate.", + "mfaPromptRequired": "Se requiere autenticación", + "mfaPushRequired": "Se requiere autenticación push.", + "mfaMenuPlaceholder": "Introduzca su respuesta", + "mfaWaitingApproval": "Esperando la aprobación en su dispositivo...", + "mfaSendRequest": "Enviar solicitud", + "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": "Se requiere autenticación OPKSSH", - "opksshAuthDescription": "Para continuar, complete la autenticación en su navegador. Esta sesión tendrá una validez de 24 horas.", - "opksshOpenBrowser": "Abra el navegador para autenticarse.", + "warpgateContinue": "He completado la autenticación", + "opksshAuthRequired": "Autenticación OPKSSH requerida", + "opksshAuthDescription": "Completa la autenticación en tu navegador para continuar. Esta sesión seguirá siendo válida durante 24 horas.", + "opksshOpenBrowser": "Abrir navegador para autenticarse", "opksshWaitingForAuth": "Esperando autenticación en el navegador...", "opksshAuthenticating": "Procesando autenticación...", - "opksshTimeout": "Se ha agotado el tiempo de espera de autenticación. Inténtalo de nuevo.", - "opksshAuthFailed": "Error de autenticación. Por favor, compruebe sus credenciales e inténtelo de nuevo.", + "opksshTimeout": "La autenticación expiró. Inténtalo de nuevo.", + "opksshAuthFailed": "La autenticación falló. Revisa tus credenciales e inténtalo de nuevo.", "opksshSignInWith": "Iniciar sesión con {{provider}}", - "vaultAuthTitle": "Se requiere iniciar sesión en la bóveda.", - "vaultAuthDescription": "Se ha abierto una ventana para iniciar sesión en HashiCorp Vault. Complete el inicio de sesión; la conexión se reanudará automáticamente.", - "vaultAuthFailed": "Falló la autenticación de la bóveda. Inténtelo de nuevo.", - "vaultReopen": "Volver a abrir la ventana de inicio de sesión", + "vaultAuthTitle": "Inicio de sesión en Vault requerido", + "vaultAuthDescription": "Se ha abierto una ventana para iniciar sesión en HashiCorp Vault. Completa el inicio de sesión allí; esta conexión continuará automáticamente.", + "vaultAuthFailed": "La autenticación de Vault falló. Inténtalo de nuevo.", + "vaultReopen": "Reabrir ventana de inicio de sesión", "sudoPasswordPopupTitle": "¿Introducir contraseña?", "linkDialogTitle": "Abrir enlace", - "linkDialogOpen": "Abierto", + "linkDialogOpen": "Abrir", "linkDialogCopy": "Copiar", - "websocketAbnormalClose": "La conexión se cerró inesperadamente. Esto puede deberse a un problema con el proxy inverso o la configuración SSL. Por favor, revise los registros del servidor.", + "websocketAbnormalClose": "La conexión se cerró de forma inesperada. Esto puede deberse a un problema con el proxy inverso o la configuración SSL. Revisa 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...", + "connectionLogEmpty": "Aún no hay registros de conexión", + "connectionLogWaiting": "Esperando registros de conexión...", "connectionLogCopied": "Registros de conexión copiados al portapapeles", - "connectionLogCopyFailed": "No se pudieron copiar los registros al portapapeles.", - "connectionRejected": "El servidor ha rechazado la conexión. Por favor, compruebe su autenticación y la configuración de su red.", - "hostKeyRejected": "Se rechazó la verificación de la clave de host SSH. Conexión cancelada.", + "connectionLogCopyFailed": "Error al copiar los registros al portapapeles", + "connectionRejected": "Conexión rechazada por el servidor. Revisa tu autenticación y configuración de red.", + "hostKeyRejected": "Verificación de clave de host SSH rechazada. Conexión cancelada.", "sessionTakenOver": "La sesión se abrió en otra pestaña. Reconectando...", "split": { - "splitTab": "Pestaña dividida", - "addToSplit": "Agregar a Dividir", - "removeFromSplit": "Eliminar de Split" + "splitTab": "Dividir pestaña", + "addToSplit": "Añadir a la división", + "removeFromSplit": "Quitar de la división" } }, "fileManager": { - "noHostSelected": "No se ha seleccionado ningún host.", - "initializingEditor": "Inicializando el editor...", + "noHostSelected": "Ningún host seleccionado", + "initializingEditor": "Inicializando editor...", "file": "Archivo", "folder": "Carpeta", "uploadFile": "Subir archivo", "downloadFile": "Descargar", - "extractArchive": "Archivo de extracción", + "extractArchive": "Extraer archivo", "extractingArchive": "Extrayendo {{name}}...", "archiveExtractedSuccessfully": "{{name}} extraído correctamente", - "extractFailed": "La extracción falló.", + "extractFailed": "Extracción fallida", "compressFile": "Comprimir archivo", "compressFiles": "Comprimir archivos", - "compressFilesDesc": "Comprime {{count}} elementos en un archivo", + "compressFilesDesc": "Comprimir {{count}} elementos en un archivo", "archiveName": "Nombre del archivo", "enterArchiveName": "Introduzca el nombre del archivo...", "compressionFormat": "Formato de compresión", @@ -1567,55 +1700,55 @@ "compress": "Comprimir", "compressingFiles": "Comprimiendo {{count}} elementos en {{name}}...", "filesCompressedSuccessfully": "{{name}} creado correctamente", - "compressFailed": "Falló la compresión", + "compressFailed": "La compresión falló", "edit": "Editar", - "preview": "Avance", + "preview": "Vista previa", "previous": "Anterior", - "next": "Próximo", + "next": "Siguiente", "pageXOfY": "Página {{current}} de {{total}}", - "zoomOut": "Aleja la imagen", - "zoomIn": "Dar un golpe de zoom", + "zoomOut": "Alejar", + "zoomIn": "Acercar", "newFile": "Nuevo archivo", "newFolder": "Nueva carpeta", - "rename": "Rebautizar", + "rename": "Renombrar", "uploading": "Subiendo...", "uploadingFile": "Subiendo {{name}}...", "fileName": "Nombre del archivo", "folderName": "Nombre de la carpeta", - "fileUploadedSuccessfully": "El archivo \"{{name}}\" se cargó correctamente", - "failedToUploadFile": "Error al cargar el archivo", - "fileDownloadedSuccessfully": "El archivo \"{{name}}\" se descargó correctamente", - "failedToDownloadFile": "No se pudo descargar el archivo.", + "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": "No se pudo crear el elemento.", + "failedToCreateItem": "No se pudo crear el elemento", "operationFailed": "La operación {{operation}} falló para {{name}}: {{error}}", - "failedToResolveSymlink": "No se pudo resolver el enlace simbólico.", + "failedToResolveSymlink": "No se pudo resolver el enlace simbólico", "itemsDeletedSuccessfully": "{{count}} elementos eliminados correctamente", - "failedToDeleteItems": "No se pudieron eliminar los elementos.", - "sudoPasswordRequired": "Se requiere contraseña de administrador.", - "enterSudoPassword": "Introduzca la contraseña de sudo para continuar con esta operación.", - "sudoPassword": "Contraseña de sudo", - "sudoOperationFailed": "La operación de sudo falló.", - "sudoAuthFailed": "Falló la autenticación de sudo", - "dragFilesToUpload": "Arrastra los archivos aquí para subirlos", + "failedToDeleteItems": "No se pudieron eliminar los elementos", + "sudoPasswordRequired": "Se requiere contraseña de administrador", + "enterSudoPassword": "Introduzca la contraseña sudo para continuar esta operación", + "sudoPassword": "Contraseña sudo", + "sudoOperationFailed": "La operación sudo falló", + "sudoAuthFailed": "La autenticación sudo falló", + "dragFilesToUpload": "Suelte los archivos aquí para subirlos", "emptyFolder": "Esta carpeta está vacía", "searchFiles": "Buscar archivos...", "upload": "Subir", - "selectHostToStart": "Seleccione un host para iniciar la gestión de archivos.", - "sshRequiredForFileManager": "El administrador de archivos requiere SSH. Este host no tiene SSH habilitado.", - "failedToConnect": "No se pudo conectar a SSH.", - "failedToLoadDirectory": "No se pudo cargar el directorio.", + "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": "No se pudo cargar el directorio", "noSSHConnection": "No hay conexión SSH disponible", "copy": "Copiar", "cut": "Cortar", - "paste": "Pasta", + "paste": "Pegar", "copyPath": "Copiar ruta", "copyPaths": "Copiar rutas", - "delete": "Borrar", + "delete": "Eliminar", "properties": "Propiedades", - "refresh": "Refrescar", - "downloadFiles": "Descarga los archivos {{count}} al navegador.", + "refresh": "Actualizar", + "downloadFiles": "Descargar {{count}} archivos al navegador", "copyFiles": "Copiar {{count}} elementos", "cutFiles": "Cortar {{count}} elementos", "deleteFiles": "Eliminar {{count}} elementos", @@ -1623,167 +1756,167 @@ "filesCutToClipboard": "{{count}} elementos cortados al portapapeles", "pathCopiedToClipboard": "Ruta copiada al portapapeles", "pathsCopiedToClipboard": "{{count}} rutas copiadas al portapapeles", - "failedToCopyPath": "No se pudo copiar la ruta al portapapeles.", + "failedToCopyPath": "Error al copiar la ruta al portapapeles", "copyFolderLink": "Copiar enlace a la carpeta", "copyCurrentFolderLink": "Copiar enlace a la carpeta actual", - "folderLinkCopied": "Enlace de carpeta copiado al portapapeles", - "failedToCopyFolderLink": "Error al copiar el enlace de la carpeta", - "movedItems": "Se movieron {{count}} elementos", - "failedToDeleteItem": "No se pudo eliminar el elemento.", - "itemRenamedSuccessfully": "{{type}} renombrado correctamente", - "failedToRenameItem": "No se pudo cambiar el nombre del elemento.", + "folderLinkCopied": "Enlace a la carpeta copiado al portapapeles", + "failedToCopyFolderLink": "Error al copiar el enlace a la carpeta", + "movedItems": "{{count}} elementos movidos", + "failedToDeleteItem": "Error al eliminar el elemento", + "itemRenamedSuccessfully": "{{type}} renombrado con éxito", + "failedToRenameItem": "Error al renombrar el elemento", "download": "Descargar", "openExternalEditor": "Abrir externamente", - "chooseExternalEditor": "Seleccione un editor externo", + "chooseExternalEditor": "Elegir editor externo", "externalEditorSelected": "Editor externo seleccionado", - "externalEditorDesktopOnly": "El editor externo solo está disponible en la aplicación de escritorio.", - "externalEditorOpened": "Abierto en un editor externo. Al guardar allí, los cambios se subirán al servidor.", - "failedToOpenExternalEditor": "No se pudo abrir el editor externo.", - "failedToSelectExternalEditor": "No se pudo seleccionar el editor externo.", + "externalEditorDesktopOnly": "El editor externo solo está disponible en la aplicación de escritorio", + "externalEditorOpened": "Abierto en el editor externo. Al guardar allí se subirán los cambios al servidor.", + "failedToOpenExternalEditor": "Error al abrir el editor externo", + "failedToSelectExternalEditor": "Error al seleccionar el editor externo", "permissions": "Permisos", "size": "Tamaño", "modified": "Modificado", - "path": "Camino", - "confirmDelete": "¿Estás seguro de que quieres eliminar {{name}}?", + "path": "Ruta", + "confirmDelete": "¿Está seguro de que desea eliminar {{name}}?", "permissionDenied": "Permiso denegado", "serverError": "Error del servidor", - "fileSavedSuccessfully": "Archivo guardado correctamente", - "failedToSaveFile": "No se pudo guardar el archivo.", - "confirmDeleteSingleItem": "¿Estás seguro de que quieres eliminar permanentemente \"{{name}}\"?", - "confirmDeleteMultipleItems": "¿Estás seguro de que quieres eliminar permanentemente {{count}} elementos?", - "confirmDeleteMultipleItemsWithFolders": "¿Estás seguro de que quieres eliminar permanentemente {{count}} elementos? Esto incluye carpetas y su contenido.", - "confirmDeleteFolder": "¿Estás seguro de que quieres eliminar permanentemente la carpeta \"{{name}}\" y todo su contenido?", - "permanentDeleteWarning": "Esta acción es irreversible. El/los elemento(s) se eliminarán permanentemente del servidor.", - "recent": "Reciente", - "pinned": "Fijado", + "fileSavedSuccessfully": "Archivo guardado con éxito", + "failedToSaveFile": "Error al guardar el archivo", + "confirmDeleteSingleItem": "¿Está seguro de que desea eliminar permanentemente \"{{name}}\"?", + "confirmDeleteMultipleItems": "¿Está seguro de que desea eliminar permanentemente {{count}} elementos?", + "confirmDeleteMultipleItemsWithFolders": "¿Está seguro de que desea eliminar permanentemente {{count}} elementos? 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. Los elementos se eliminarán permanentemente del servidor.", + "recent": "Recientes", + "pinned": "Fijados", "folderShortcuts": "Accesos directos a carpetas", - "failedToReconnectSSH": "No se pudo reconectar la sesión SSH.", + "failedToReconnectSSH": "Error al reconectar la sesión SSH", "openTerminalHere": "Abrir terminal aquí", - "run": "Correr", + "run": "Ejecutar", "openTerminalInFolder": "Abrir terminal en esta carpeta", - "openTerminalInFileLocation": "Abra la terminal en la ubicación del archivo.", - "runningFile": "Corriendo - {{file}}", - "onlyRunExecutableFiles": "Solo puede ejecutar archivos ejecutables.", + "openTerminalInFileLocation": "Abrir terminal en la ubicación del archivo", + "runningFile": "Ejecutando - {{file}}", + "onlyRunExecutableFiles": "Solo se pueden ejecutar archivos ejecutables", "directories": "Directorios", "removedFromRecentFiles": "Se eliminó \"{{name}}\" de los archivos recientes", - "removeFailed": "Eliminar fallido", - "unpinnedSuccessfully": "Desanclado \"{{name}}\" correctamente", + "removeFailed": "Error al eliminar", + "unpinnedSuccessfully": "Se desancló \"{{name}}\" correctamente", "unpinFailed": "Error al desanclar", "removedShortcut": "Se eliminó el acceso directo \"{{name}}\"", "removeShortcutFailed": "Error al eliminar el acceso directo", - "clearedAllRecentFiles": "Se han borrado todos los archivos recientes.", + "clearedAllRecentFiles": "Se borraron todos los archivos recientes", "clearFailed": "Error al borrar", "removeFromRecentFiles": "Eliminar de archivos recientes", "clearAllRecentFiles": "Borrar todos los archivos recientes", "unpinFile": "Desanclar archivo", "removeShortcut": "Eliminar acceso directo", - "pinFile": "Archivo PIN", - "addToShortcuts": "Agregar a accesos directos", + "pinFile": "Anclar archivo", + "addToShortcuts": "Añadir a accesos directos", "pasteFailed": "Error al pegar", - "noUndoableActions": "No hay acciones deshacer", - "undoCopySuccess": "Operación de copia deshecha: Se eliminaron {{count}} archivos copiados", - "undoCopyFailedDelete": "Error al deshacer: No se pudo eliminar ningún archivo copiado.", - "undoCopyFailedNoInfo": "Error al deshacer: No se pudo encontrar la información del archivo copiado.", - "undoMoveSuccess": "Operación de movimiento deshecha: Los archivos {{count}} se han movido de nuevo a su ubicación original.", - "undoMoveFailedMove": "Error al deshacer: No se pudo mover ningún archivo.", - "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.", + "noUndoableActions": "No hay acciones para deshacer", + "undoCopySuccess": "Se deshizo la copia: se eliminaron {{count}} archivos copiados", + "undoCopyFailedDelete": "Error al deshacer: no se pudo eliminar ningún archivo copiado", + "undoCopyFailedNoInfo": "Error al deshacer: no se encontró información de los archivos copiados", + "undoMoveSuccess": "Se deshizo el movimiento: se movieron {{count}} archivos a su ubicación original", + "undoMoveFailedMove": "Error al deshacer: no se pudo mover ningún archivo de vuelta", + "undoMoveFailedNoInfo": "Error al deshacer: no se encontró información de los archivos movidos", + "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 compatible", - "undoOperationFailed": "La operación de deshacer falló.", + "undoOperationFailed": "Error en la operación de deshacer", "unknownError": "Error desconocido", "confirm": "Confirmar", - "find": "Encontrar...", + "find": "Buscar...", "replace": "Reemplazar", "downloadInstead": "Descargar en su lugar", "keyboardShortcuts": "Atajos de teclado", "searchAndReplace": "Buscar y reemplazar", "editing": "Edición", "search": "Buscar", - "findNext": "Encontrar siguiente", - "findPrevious": "Encontrar anterior", - "save": "Ahorrar", + "findNext": "Buscar siguiente", + "findPrevious": "Buscar anterior", + "save": "Guardar", "selectAll": "Seleccionar todo", "undo": "Deshacer", "redo": "Rehacer", - "moveLineUp": "Mover la fila", - "moveLineDown": "Mover la línea hacia abajo", + "moveLineUp": "Mover línea hacia arriba", + "moveLineDown": "Mover línea hacia abajo", "toggleComment": "Alternar comentario", "autoComplete": "Autocompletar", - "imageLoadError": "No se pudo cargar la imagen.", - "startTyping": "Empieza a escribir...", + "imageLoadError": "Error al cargar la imagen", + "startTyping": "Empiece a escribir...", "unknownSize": "Tamaño desconocido", "fileIsEmpty": "El archivo está vacío", - "largeFileWarning": "Advertencia sobre archivos grandes", - "largeFileWarningDesc": "Este archivo tiene un tamaño de {{size}} , lo que puede causar problemas de rendimiento al abrirlo como texto.", - "fileNotFoundAndRemoved": "El archivo \"{{name}}\" no se encontró y se eliminó de los archivos recientes/anclados.", + "largeFileWarning": "Advertencia de archivo grande", + "largeFileWarningDesc": "Este archivo tiene un tamaño de {{size}}, lo que puede causar problemas de rendimiento al abrirlo como texto.", + "fileNotFoundAndRemoved": "El archivo \"{{name}}\" no se encontró y se ha eliminado de los archivos recientes/fijados.", "failedToLoadFile": "Error al cargar el archivo: {{error}}", - "serverErrorOccurred": "Se ha producido un error del servidor. Inténtalo de nuevo más tarde.", - "autoSaveFailed": "Falló el guardado automático", - "fileAutoSaved": "Archivo guardado automáticamente", - "moveFileFailed": "No se pudo mover {{name}}", - "moveOperationFailed": "La operación de traslado falló.", - "canOnlyCompareFiles": "Solo se pueden comparar dos archivos.", + "serverErrorOccurred": "Se ha producido un error en el servidor. Vuelva a intentarlo más tarde.", + "autoSaveFailed": "Error en el autoguardado", + "fileAutoSaved": "Archivo autoguardado", + "moveFileFailed": "Error al mover {{name}}", + "moveOperationFailed": "Error en la operación de mover", + "canOnlyCompareFiles": "Solo se pueden comparar dos archivos", "comparingFiles": "Comparando archivos: {{file1}} y {{file2}}", - "dragFailed": "La operación de arrastre falló.", - "filePinnedSuccessfully": "El archivo \"{{name}}\" se ha fijado correctamente", - "pinFileFailed": "No se pudo fijar el archivo.", - "fileUnpinnedSuccessfully": "El archivo \"{{name}}\" se ha desanclado correctamente.", - "unpinFileFailed": "No se pudo desanclar el archivo.", - "shortcutAddedSuccessfully": "Acceso directo a la carpeta \"{{name}}\" agregado correctamente", - "addShortcutFailed": "No se pudo agregar el acceso directo", - "operationCompletedSuccessfully": "{{operation}} {{count}} elementos exitosos", + "dragFailed": "Error en la operación de arrastre", + "filePinnedSuccessfully": "Archivo \"{{name}}\" fijado correctamente", + "pinFileFailed": "Error al fijar el archivo", + "fileUnpinnedSuccessfully": "Archivo \"{{name}}\" desfijado correctamente", + "unpinFileFailed": "No se pudo desanclar el archivo", + "shortcutAddedSuccessfully": "Acceso directo a la carpeta \"{{name}}\" añadido correctamente", + "addShortcutFailed": "No se pudo añadir el acceso directo", + "operationCompletedSuccessfully": "{{operation}} {{count}} elementos correctamente", "operationCompleted": "{{operation}} {{count}} elementos", - "downloadFileSuccess": "El archivo {{name}} se descargó correctamente.", - "downloadFileFailed": "Falló la descarga.", + "downloadFileSuccess": "Archivo {{name}} descargado correctamente", + "downloadFileFailed": "Error en la descarga", "moveTo": "Mover a {{name}}", - "diffCompareWith": "Diferencia comparativa con {{name}}", - "dragOutsideToDownload": "Arrastra fuera de la ventana para descargar ({{count}} archivos)", - "newFolderDefault": "Nueva carpeta", + "diffCompareWith": "Comparar diferencias con {{name}}", + "dragOutsideToDownload": "Arrastrar fuera de la ventana para descargar ({{count}} archivos)", + "newFolderDefault": "NuevaCarpeta", "newFileDefault": "NuevoArchivo.txt", - "successfullyMovedItems": "Se movieron correctamente {{count}} elementos a {{target}}", + "successfullyMovedItems": "{{count}} elementos movidos correctamente a {{target}}", "move": "Mover", - "searchInFile": "Buscar en el archivo (Ctrl+F)", + "searchInFile": "Buscar en archivo (Ctrl+F)", "showKeyboardShortcuts": "Mostrar atajos de teclado", - "decreaseFontSize": "Disminuir el tamaño de la fuente", - "increaseFontSize": "Aumentar el tamaño de la fuente", - "startWritingMarkdown": "Empieza a escribir tu contenido en formato Markdown...", + "decreaseFontSize": "Reducir tamaño de fuente", + "increaseFontSize": "Aumentar tamaño de fuente", + "startWritingMarkdown": "Empiece a escribir su contenido Markdown...", "loadingFileComparison": "Cargando comparación de archivos...", "reload": "Recargar", "compare": "Comparar", - "sideBySide": "Juntos", + "sideBySide": "Lado a lado", "inline": "En línea", "fileComparison": "Comparación de archivos: {{file1}} vs {{file2}}", "fileTooLarge": "Archivo demasiado grande: {{error}}", - "sshConnectionFailed": "Falló la conexión SSH. Por favor, compruebe su conexión a {{name}} ({{ip}}:{{port}})", + "sshConnectionFailed": "Error de conexión SSH. Verifique su conexión a {{name}} ({{ip}}:{{port}})", "loadFileFailed": "Error al cargar el archivo: {{error}}", "connecting": "Conectando...", - "connectedSuccessfully": "Conexión establecida con éxito", - "totpVerificationFailed": "Falló la verificación TOTP.", - "warpgateVerificationFailed": "Falló la autenticación de Warpgate.", - "authenticationFailed": "Falló la autenticación", - "incorrectPassphrase": "Contraseña incorrecta. Inténtalo de nuevo.", + "connectedSuccessfully": "Conectado correctamente", + "totpVerificationFailed": "Error en la verificación TOTP", + "warpgateVerificationFailed": "Error de autenticación de Warpgate", + "authenticationFailed": "Error de autenticación", + "incorrectPassphrase": "Frase de contraseña incorrecta. Inténtelo de nuevo.", "verificationCodePrompt": "Código de verificación:", "changePermissions": "Cambiar permisos", "currentPermissions": "Permisos actuales", - "owner": "Dueño", + "owner": "Propietario", "group": "Grupo", "others": "Otros", - "read": "Leer", - "write": "Escribir", - "execute": "Ejecutar", - "permissionsChangedSuccessfully": "Los permisos se modificaron correctamente.", - "failedToChangePermissions": "No se pudieron cambiar los permisos.", + "read": "Lectura", + "write": "Escritura", + "execute": "Ejecución", + "permissionsChangedSuccessfully": "Permisos cambiados correctamente", + "failedToChangePermissions": "Error al cambiar los permisos", "name": "Nombre", "sortByName": "Nombre", "sortByDate": "Fecha de modificación", "sortBySize": "Tamaño", "ascending": "Ascendente", - "descending": "Descendiendo", + "descending": "Descendente", "root": "Raíz", "new": "Nuevo", "sortBy": "Ordenar por", "items": "Elementos", - "selected": "Seleccionado", + "selected": "Seleccionados", "editor": "Editor", "octal": "Octal", "storage": "Almacenamiento", @@ -1792,38 +1925,38 @@ "of": "de", "toggleSidebar": "Alternar barra lateral", "cannotLoadPdf": "No se puede cargar el PDF", - "pdfLoadError": "Se ha producido un error al cargar este archivo PDF.", + "pdfLoadError": "Se produjo un error al cargar este archivo PDF.", "loadingPdf": "Cargando PDF...", "loadingPage": "Cargando página..." }, "transfer": { "copyToHost": "Copiar al host…", "moveToHost": "Mover al host…", - "copyItemsToHost": "Copia {{count}} elementos al host…", + "copyItemsToHost": "Copiar {{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", + "noHostsConnected": "No hay otros hosts de gestor de archivos disponibles.", + "noHostsConnectedHint": "Añade otro host SSH con el Gestor de Archivos habilitado en el Administrador de Hosts.", + "selectDestinationHost": "Seleccionar 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", + "collapseRecentDestinations": "Contraer destinos recientes", + "expandRecentDestinations": "Expandir destinos recientes", + "browseFolders": "Examinar carpetas de destino", + "browseDestination": "Examinar o introducir ruta", "confirmCopy": "Copiar", "confirmMove": "Mover", - "transferring": "Transferencia…", + "transferring": "Transfiriendo…", "compressing": "Comprimiendo…", "extracting": "Extrayendo…", - "transferringItems": "Transferencia de {{current}} de {{total}} elementos…", + "transferringItems": "Transfiriendo {{current}} de {{total}} elementos…", "transferSuccess": "Transferencia completada", - "transferError": "La transferencia falló.", + "transferError": "Transferencia fallida", "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.", + "destMustBeDirectory": "El destino debe ser un directorio para transferencias de múltiples elementos", + "selectThisFolder": "Seleccionar esta carpeta", + "browsePathWillBeCreated": "Esta carpeta no existe aún. Se creará al iniciar la transferencia.", "browsePathError": "No se pudo abrir esta ruta en el host de destino.", "goUp": "Subir", "copyFolderToHost": "Copiar carpeta al host…", @@ -1831,757 +1964,758 @@ "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", + "hostAuthRequired": "Se requiere autenticación — abre primero el Gestor de Archivos en este host", + "hostConnectionFailed": "Conexión fallida", + "metricsTitle": "Tiempos de transferencia", "metricsPrepare": "Preparar destino: {{duration}}", - "metricsCompress": "Comprimir en la fuente: {{duration}}", - "metricsHopSourceRead": "Fuente → servidor: {{throughput}}", + "metricsCompress": "Compresión en origen: {{duration}}", + "metricsHopSourceRead": "Origen → 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}}", + "metricsTransfer": "Extremo a extremo: {{throughput}} ({{duration}})", + "metricsExtract": "Extracción en destino: {{duration}}", + "metricsSourceDelete": "Eliminación del origen: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Comprimiendo en el host de origen…", + "progressCompressing": "Comprimiendo en el equipo de origen…", "progressExtracting": "Extrayendo en el destino…", - "progressTransferring": "Transferencia de datos…", + "progressTransferring": "Transfiriendo 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}})…", + "parallelSegmentsLabel": "Vías de transferencia en paralelo", + "parallelSegmentsOption": "{{count}} vías", + "parallelSegmentsHint": "Los archivos grandes se dividen en fragmentos de 256 MB. Varias vías utilizan conexiones separadas (como iniciar varias transferencias) para un mayor rendimiento total.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} vías)", + "progressTransferringItems": "Transfiriendo archivos ({{current}} de {{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.", + "jumpHostLimitation": "Ambos equipos deben ser accesibles desde el servidor de Termix. No se admite el enrutamiento directo entre equipos.", "cancel": "Cancelar", "methodLabel": "Método de transferencia", - "methodAuto": "Auto", + "methodAuto": "Automático", "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.", + "methodAutoHint": "Elige tar o SFTP por archivo según el número de archivos, tamaño y compresibilidad. Los archivos individuales siempre usan SFTP por transmisión continua.", + "methodTarHint": "Comprime en origen, transfiere un archivo, extrae en destino. Necesita tar en ambos equipos Unix.", + "methodItemSftpHint": "Transfiere cada archivo individualmente por SFTP. Funciona en todos los equipos, 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.", + "methodPreviewWillUseTar": "Se usará: Archivo Tar", + "methodPreviewWillUseItemSftp": "Se usará: SFTP por archivo", + "methodPreviewScanSummary": "{{fileCount}} archivos, {{totalSize}} total (analizado en el equipo de origen).", + "methodItemSftpLimitation": "Cada archivo usa el mismo flujo SFTP que una copia de un solo archivo, uno tras otro. El progreso se combina en todos los archivos, por lo que la barra se mueve lentamente durante archivos grandes.", "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": "Has elegido SFTP por archivo.", + "user_tar": "Has elegido archivo tar.", + "tar_unavailable": "Tar no está disponible en uno o ambos hosts; se usará SFTP por archivo.", + "windows_host": "Hay un host Windows implicado; no se usa tar.", + "auto_multi_large": "Auto: múltiples archivos incluido uno grande ({{largestSize}}) con datos comprimibles; tar los agrupa en una sola transferencia.", + "auto_single_large_in_archive": "Auto: un archivo grande ({{largestSize}}) en este conjunto; SFTP por archivo.", + "auto_many_incompressible": "Auto: datos mayoritariamente incompresibles; SFTP por archivo.", + "auto_many_files": "Auto: muchos archivos ({{fileCount}}); tar reduce la sobrecarga por archivo.", + "auto_default": "Auto: SFTP por archivo para este conjunto." }, "progressCancel": "Cancelar", "progressCancelling": "Cancelando…", "progressStalled": "Estancado", - "resumedHint": "Se ha restablecido la conexión con una transferencia activa iniciada en otra ventana.", + "resumedHint": "Reconectado a 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ó", + "transferCancelledCopyHint": "Pueden quedar archivos parciales en el destino.", + "transferCancelledMoveHint": "Pueden quedar archivos parciales en el destino. Los archivos de origen no se eliminaron.", + "cleanupDestFiles": "Limpiar destino", + "cleanupDestFilesSuccess": "Se eliminaron los archivos parciales del destino", + "cleanupDestFilesPartial": "Algunos archivos parciales no se pudieron eliminar", + "cleanupDestFilesNothing": "Nada que limpiar en el destino", + "cleanupDestFilesError": "Error al limpiar", + "retryTransfer": "Reintentar", + "retryTransferError": "Error al reintentar", "transferFailedRetryHint": "Se conservaron datos parciales en el destino. El reintento se reanudará cuando se restablezca la conexión." }, "tunnels": { "noSshTunnels": "Sin túneles SSH", - "createFirstTunnelMessage": "Aún no has creado ningún túnel SSH. Configura las conexiones de túnel en el Administrador de host para comenzar.", + "createFirstTunnelMessage": "Aún no has creado ningún túnel SSH. Configura las conexiones de túnel en el Administrador de hosts para empezar.", "connected": "Conectado", "disconnected": "Desconectado", "connecting": "Conectando...", "error": "Error", - "canceling": "Cancelado...", + "canceling": "Cancelando...", "connect": "Conectar", "disconnect": "Desconectar", "cancel": "Cancelar", "port": "Puerto", "localPort": "Puerto local", "remotePort": "Puerto remoto", - "currentHostPort": "Puerto host actual", - "endpointPort": "Puerto de punto final", + "currentHostPort": "Puerto del host actual", + "endpointPort": "Puerto del endpoint", "bindIp": "IP local", - "endpointSshConfig": "Configuración SSH del punto final", - "endpointSshHost": "Host SSH de punto final", - "endpointSshHostPlaceholder": "Seleccione un host configurado.", - "endpointSshHostRequired": "Seleccione un host SSH de punto final para cada túnel de cliente.", + "endpointSshConfig": "Configuración SSH del endpoint", + "endpointSshHost": "Host SSH del endpoint", + "endpointSshHostPlaceholder": "Seleccionar un host configurado", + "endpointSshHostRequired": "Seleccione un host SSH del endpoint para cada túnel de cliente.", "attempt": "Intento {{current}} de {{max}}", "nextRetryIn": "Próximo reintento en {{seconds}} segundos", - "clientTunnels": "Túneles de clientes", - "clientTunnel": "Túnel del cliente", - "addClientTunnel": "Agregar túnel de cliente", - "noClientTunnels": "No hay túneles de cliente configurados en este equipo.", + "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 escritorio.", "tunnelName": "Nombre del túnel", "remoteHost": "Host remoto", - "autoStart": "Arranque automático", - "clientAutoStartDesc": "Se inicia cuando se abre este cliente de escritorio y permanece conectado.", - "clientManualStartDesc": "Utilice los botones de Inicio y Detener de esta fila. Termix no la abrirá automáticamente.", - "clientRemoteServerNote": "El reenvío remoto puede requerir las opciones AllowTcpForwarding y GatewayPorts en el servidor SSH del extremo. El puerto remoto se cierra cuando este equipo se desconecta.", - "clientTunnelStarted": "El túnel del cliente ha comenzado.", - "clientTunnelStopped": "El túnel del cliente se detuvo.", - "tunnelTestSucceeded": "La prueba del túnel fue exitosa.", - "tunnelTestFailed": "La prueba del túnel falló.", + "autoStart": "Inicio automático", + "clientAutoStartDesc": "Se inicia al abrir este cliente de escritorio y permanece conectado.", + "clientManualStartDesc": "Use Iniciar y Detener desde esta fila. Termix no lo iniciará automáticamente.", + "clientRemoteServerNote": "El reenvío remoto puede requerir AllowTcpForwarding y GatewayPorts en el servidor SSH del endpoint. 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": "No se pudieron guardar los túneles del cliente local.", + "localSaveError": "Error al guardar los túneles de cliente locales", "invalidBindIp": "La IP local debe ser una dirección IPv4 válida.", - "invalidLocalTargetIp": "La dirección IP de destino local debe ser una dirección IPv4 válida.", + "invalidLocalTargetIp": "La IP de destino local debe ser una dirección IPv4 válida.", "invalidLocalPort": "El puerto local debe estar entre 1 y 65535.", "invalidRemotePort": "El puerto remoto debe estar entre 1 y 65535.", "invalidLocalTargetPort": "El puerto de destino local debe estar entre 1 y 65535.", - "invalidEndpointPort": "El puerto del punto final debe estar entre 1 y 65535.", + "invalidEndpointPort": "El puerto del endpoint debe estar entre 1 y 65535.", "duplicateAutoStartBind": "Solo un túnel de cliente de inicio automático puede usar {{bind}}.", - "manualControlError": "No se pudo actualizar el estado del túnel.", + "manualControlError": "Error al actualizar el estado del túnel.", "active": "Activo", - "start": "Comenzar", + "start": "Iniciar", "stop": "Detener", - "test": "Prueba", - "type": "Tipo túnel", + "test": "Probar", + "type": "Tipo de túnel", "typeLocal": "Local (-L)", "typeRemote": "Remoto (-R)", "typeDynamic": "Dinámico (-D)", - "typeServerLocalDesc": "Host actual al punto final.", - "typeServerRemoteDesc": "El punto final vuelve al host actual.", - "typeClientLocalDesc": "Computadora local al punto final.", - "typeClientRemoteDesc": "El punto final vuelve al ordenador local.", - "typeClientDynamicDesc": "SOCKS en el ordenador local.", - "typeDynamicDesc": "Reenviar el tráfico SOCKS5 CONNECT a través de SSH", - "forwardDescriptionServerLocal": "Host actual {{sourcePort}} → punto final {{endpointPort}}.", - "forwardDescriptionServerRemote": "Punto final {{endpointPort}} → host actual {{sourcePort}}.", + "typeServerLocalDesc": "Del host actual al endpoint.", + "typeServerRemoteDesc": "Del endpoint al host actual.", + "typeClientLocalDesc": "Del equipo local al endpoint.", + "typeClientRemoteDesc": "Del endpoint al equipo local.", + "typeClientDynamicDesc": "SOCKS en el equipo local.", + "typeDynamicDesc": "Reenviar tráfico SOCKS5 CONNECT a través de SSH", + "forwardDescriptionServerLocal": "Host actual {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host actual {{sourcePort}}.", "forwardDescriptionServerDynamic": "SOCKS en el host actual {{sourcePort}}.", "forwardDescriptionClientLocal": "Local {{sourcePort}} → remoto {{endpointPort}}.", "forwardDescriptionClientRemote": "Remoto {{sourcePort}} → local {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS en el puerto local {{sourcePort}}.", + "forwardDescriptionClientDynamic": "SOCKS en puerto local {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → CALCETINES vía {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS a través de {{endpoint}}", "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", - "autoNameClientDynamic": "CALCETINES {{localPort}} vía {{endpoint}}", + "autoNameClientDynamic": "SOCKS {{localPort}} a través de {{endpoint}}", "route": "Ruta:", "lastStarted": "Último inicio", - "lastTested": "Última prueba realizada", + "lastTested": "Última prueba", "lastError": "Último error", - "maxRetries": "Reintentos máximos", - "maxRetriesDescription": "Cantidad máxima de intentos de reintento.", + "maxRetries": "Reintentos máx.", + "maxRetriesDescription": "Número máximo de intentos de reintento.", "retryInterval": "Intervalo de reintento (segundos)", - "retryIntervalDescription": "Tiempo de espera entre cada intento de reintento.", + "retryIntervalDescription": "Tiempo de espera entre intentos de reintento.", "local": "Local", "remote": "Remoto", "destination": "Destino", - "host": "Anfitrión", + "host": "Host", "mode": "Modo", - "noHostSelected": "No se ha seleccionado ningún host.", - "working": "Laboral..." + "noHostSelected": "Ningún host seleccionado", + "working": "Trabajando..." }, "cardGrid": { - "dragToMove": "Arrastra para mover", - "dragToResize": "Arrastra para cambiar el tamaño", + "dragToMove": "Arrastrar para mover", + "dragToResize": "Arrastrar para redimensionar", "changeWidth": "Cambiar ancho", "removeCard": "Eliminar {{label}}", - "addCard": "Agregar", + "addCard": "Añadir", "columns": "Columnas", - "empty": "No se permiten tarjetas. Utilice la opción \"Añadir\" a continuación para colocar tarjetas." + "empty": "No hay tarjetas. Use Añadir abajo para colocar tarjetas." }, "hostMetrics": { - "cpu": "UPC", + "cpu": "CPU", "memory": "Memoria", "disk": "Disco", "network": "Red", "uptime": "Tiempo de actividad", "processes": "Procesos", "available": "Disponible", - "free": "Gratis", + "free": "Libre", "connecting": "Conectando...", - "connectionFailed": "No se pudo conectar con el servidor.", - "naCpus": "N/A CPU(s)", - "cpuCores_one": "{{count}} Núcleo", - "cpuCores_other": "{{count}} Núcleos", - "cpuUsage": "Uso de la CPU", + "connectionFailed": "Error al conectar con el servidor", + "naCpus": "CPU(s) N/D", + "cpuCores_one": "{{count}} núcleo", + "cpuCores_other": "{{count}} núcleos", + "cpuUsage": "Uso de CPU", "memoryUsage": "Uso de memoria", "diskUsage": "Uso del disco", "temperature": "Temperatura", - "highestTemperature": "Temperatura máxima", - "failedToFetchHostConfig": "No se pudo obtener la configuración del host.", + "highestTemperature": "Temperatura más alta", + "failedToFetchHostConfig": "Error al obtener la configuración del host", "serverOffline": "Servidor fuera de línea", - "cannotFetchMetrics": "No se pueden obtener las métricas del servidor sin conexión.", - "totpFailed": "Falló la verificación TOTP.", - "noneAuthNotSupported": "Host Metrics no admite el tipo de autenticación 'ninguno'.", - "noHostSelected": "No se ha seleccionado ningún host.", + "cannotFetchMetrics": "No se pueden obtener métricas de un servidor fuera de línea", + "totpFailed": "Error en la verificación TOTP", + "noneAuthNotSupported": "Host Metrics no admite el tipo de autenticación 'none'.", + "noHostSelected": "Ningún host seleccionado", "load": "Carga", "systemInfo": "Información del sistema", - "hostname": "nombre de host", + "hostname": "Nombre de host", "operatingSystem": "Sistema operativo", - "kernel": "Núcleo", - "seconds": "artículos de segunda clase", + "kernel": "Kernel", + "seconds": "segundos", "networkInterfaces": "Interfaces de red", - "noInterfacesFound": "No se encontraron interfaces de red.", + "noInterfacesFound": "No se encontraron interfaces de red", "noProcessesFound": "No se encontraron procesos", "processesTotal": "total", - "processesRunning": "correr", + "processesRunning": "en ejecución", "loginStats": "Estadísticas de inicio de sesión SSH", "noRecentLoginData": "No hay datos de inicio de sesión recientes", "executingQuickAction": "Ejecutando {{name}}...", - "quickActionSuccess": "{{name}} completado con éxito", + "quickActionSuccess": "{{name}} completado correctamente", "quickActionFailed": "{{name}} falló", "quickActionError": "Error al ejecutar {{name}}", "ports": { - "title": "Puertos de escucha", + "title": "Puertos en escucha", "protocol": "Protocolo", "port": "Puerto", - "address": "DIRECCIÓN", + "address": "Dirección", "process": "Proceso", "search": "Buscar puertos...", - "allProtocols": "Todo", - "noData": "No hay datos de puertos de escucha" + "allProtocols": "Todos", + "noData": "No hay datos de puertos en escucha" }, "firewall": { "title": "Cortafuegos", "inactive": "Inactivo", "policy": "Política", - "rules": "normas", + "rules": "reglas", "noRules": "No hay reglas en esta cadena", - "noData": "No hay datos de cortafuegos disponibles", + "noData": "No hay datos del cortafuegos disponibles", "action": "Acción", "protocol": "Proto", "port": "Puerto", - "source": "Fuente", - "anywhere": "En cualquier lugar", + "source": "Origen", + "anywhere": "Cualquier origen", "chains": "cadenas" }, - "loadAvg": "Carga promedio", - "swap": "Intercambio", + "loadAvg": "Carga Promedio", + "swap": "Swap", "architecture": "Arquitectura", "refresh": "Refrescar", - "retry": "Rever", - "customize": "Personalizar el diseño", - "reset": "Reiniciar", - "tabLive": "Vivir", - "editModeInstructions": "Arrastra las tarjetas para reorganizarlas, arrastra el borde inferior para cambiar su tamaño y usa el botón de ancho para modificar el tamaño de una tarjeta. Agrega o elimina tarjetas a continuación.", + "retry": "Reintentar", + "customize": "Personalizar diseño", + "reset": "Restablecer", + "tabLive": "En vivo", + "editModeInstructions": "Arrastre las tarjetas para reorganizarlas, arrastre el borde inferior para redimensionarlas, y use el botón de ancho para cambiar el ancho de una tarjeta. Añada o elimine tarjetas a continuación.", "managers": { "services": "Servicios", "processInspector": "Inspector de procesos", "logViewer": "Visor de registros", - "cron": "Tareas programadas (Cron Jobs)", + "cron": "Tareas Cron", "packages": "Paquetes", "ssl": "Certificados SSL", - "firewall": "Cortafuegos", + "firewall": "Firewall", "users": "Usuarios y permisos", - "healthCheck": "Chequeos de salud", - "diskBreakdown": "Desmontaje del disco", + "healthCheck": "Comprobaciones de estado", + "diskBreakdown": "Desglose de disco", "systemdTimers": "Temporizadores", - "topMemory": "Lo mejor de la memoria", - "noData": "No hay datos", - "sudoHint": "Establezca una contraseña de sudo para este host en el editor de hosts para habilitar acciones con privilegios.", + "topMemory": "Top por memoria", + "noData": "Sin datos", + "sudoHint": "Establezca una contraseña de sudo para este host en el editor del host para habilitar acciones privilegiadas.", "filter": "Filtrar...", - "start": "Comenzar", + "start": "Iniciar", "stop": "Detener", - "restart": "Reanudar", + "restart": "Reiniciar", "actionDone": "{{name}} actualizado", - "actionFailed": "La acción fracasó", - "signalSent": "Señal enviada al PID {{pid}}", - "killHint": "Clic: finalizar (SIGTERM). Clic derecho: forzar finalización (SIGKILL).", - "working": "Laboral...", + "actionFailed": "Acción fallida", + "signalSent": "Señal enviada a PID {{pid}}", + "killHint": "Clic: terminar (SIGTERM). Clic derecho: forzar cierre (SIGKILL).", + "working": "Trabajando...", "update": "Actualizar", "upgradeAll": "Actualizar todo", "allUpToDate": "Todo está actualizado", - "save": "Ahorrar", - "command": "Dominio", + "save": "Guardar", + "command": "Comando", "enabled": "Activado", "cronSaved": "Crontab actualizado", - "clients": "Clientela", - "dryRun": "Ensayo general", + "clients": "Clientes", + "dryRun": "Simulación", "renew": "Renovar", - "noAcmeClient": "No se ha encontrado ningún cliente ACME (certbot o acme.sh) en este host.", - "addInputRule": "Agregar regla de ENTRADA", - "firewallWarning": "Los cambios solo se aplican durante la ejecución hasta que se guarden. Tenga cuidado de no bloquearse el acceso.", + "noAcmeClient": "No se encontró ningún cliente ACME (certbot o acme.sh) en este host.", + "addInputRule": "Añadir regla INPUT", + "firewallWarning": "Los cambios son solo en tiempo de ejecución hasta que se persisten. Tenga cuidado de no quedarse bloqueado fuera.", "ruleApplied": "Regla aplicada", - "invalidPort": "Ingrese un puerto válido (1-65535)", + "invalidPort": "Introduzca un puerto válido (1-65535)", "newUsername": "Nuevo nombre de usuario", - "addUser": "Agregar", + "addUser": "Añadir", "deleteUser": "Eliminar usuario", "noHealthChecks": "Aún no se han configurado comprobaciones de estado.", "follow": "Seguir", - "noLogData": "No hay salida de registro.", + "noLogData": "Sin salida de registro.", "tree": "Árbol", - "enableDisable": "Habilitar/deshabilitar al arrancar", - "grantSudo": "Grant sudo", + "enableDisable": "Activar / desactivar al arrancar", + "grantSudo": "Conceder sudo", "revokeSudo": "Revocar sudo", "sslIssueCert": "Emitir certificado", - "sslExpired": "Venció", + "sslExpired": "Caducado", "sslInDays": "en {{days}}d", - "sslNeedDomain": "Introduzca al menos un dominio.", + "sslNeedDomain": "Introduzca al menos un dominio", "sslIssued": "Certificado emitido", - "sslDomainsPlaceholder": "ejemplo.com, www.example.com", - "sslHttpStandalone": "HTTP (autónomo)", - "sslHttpWebroot": "HTTP (raíz web)", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "Proveedor de DNS (por ejemplo, Cloudflare)", + "sslDnsProvider": "Proveedor DNS (p. ej. cloudflare)", "sslIssueHint": "Las credenciales del proveedor DNS ya deben estar configuradas en el host.", "sslRevoke": "Revocar certificado", "sslRevoked": "Certificado revocado", - "sslRevokeConfirm": "¿Revocar y eliminar el certificado \"{{name}}\"? Esto no se puede deshacer.", - "healthRun": "Correr", + "sslRevokeConfirm": "¿Revocar y eliminar el certificado \"{{name}}\"? Esta acción no se puede deshacer.", + "healthRun": "Ejecutar", "healthName": "Nombre", "healthTarget": "Host / dirección", - "healthAddCheck": "Agregar comprobación", - "healthSaved": "Se guardaron los controles de salud", - "healthMissingFields": "Cada cheque necesita un nombre y un objetivo.", + "healthAddCheck": "Añadir verificación", + "healthSaved": "Verificaciones de estado guardadas", + "healthMissingFields": "Cada verificación necesita un nombre y un destino", "logFile": "Archivo", "logUnit": "Unidad", - "logCustomPath": "Ruta personalizada en /var/log (opcional)", - "logGrep": "Líneas de filtro...", - "firewallPersist": "Reglas persistentes", - "firewallPersisted": "Las reglas del firewall persistieron.", + "logCustomPath": "Ruta personalizada bajo /var/log (opcional)", + "logGrep": "Filtrar líneas...", + "firewallPersist": "Persistir reglas", + "firewallPersisted": "Reglas del firewall persistidas", "wireguard": "WireGuard", - "tailscale": "Escama de cola", - "wgNotInstalled": "WireGuard no está instalado en este host.", + "tailscale": "Tailscale", + "wgNotInstalled": "WireGuard no está instalado en este host", "wgNoInterfaces": "No hay interfaces WireGuard configuradas", - "wgInterfaceUp": "Sacar un tema", - "wgInterfaceDown": "Reducir", - "wgBringingUp": "Trayendo {{name}} arriba...", - "wgBringingDown": "Derribando {{name}}...", - "wgInterfaceUpDone": "{{name}} está arriba", - "wgInterfaceDownDone": "{{name}} está caído", + "wgInterfaceUp": "Activar", + "wgInterfaceDown": "Desactivar", + "wgBringingUp": "Activando {{name}}...", + "wgBringingDown": "Desactivando {{name}}...", + "wgInterfaceUpDone": "{{name}} está activado", + "wgInterfaceDownDone": "{{name}} está desactivado", "wgListenPort": "Puerto", "wgPublicKey": "Clave pública", - "wgEndpoint": "Punto final", - "wgAllowedIPs": "Direcciones IP permitidas", - "wgLastHandshake": "Último apretón de manos", + "wgEndpoint": "Endpoint", + "wgAllowedIPs": "IPs permitidas", + "wgLastHandshake": "Último handshake", "wgHandshakeNever": "Nunca", - "wgTransfer": "Transferir", - "tsNotInstalled": "Tailscale no está instalado en este host.", - "tsRunning": "Correr", - "tsStopped": "Interrumpido", + "wgTransfer": "Transferencia", + "tsNotInstalled": "Tailscale no está instalado en este host", + "tsRunning": "Ejecutándose", + "tsStopped": "Detenido", "tsEnable": "Conectar", "tsDisable": "Desconectar", - "tsEnabling": "Conectando con Tailscale...", + "tsEnabling": "Conectando a Tailscale...", "tsDisabling": "Desconectando de Tailscale...", - "tsEnabled": "Escama de cola conectada", - "tsDisabled": "Escama de cola desconectada", - "tsIPs": "Propiedades intelectuales de Tailscale", - "tsPeers": "Colegas", + "tsEnabled": "Tailscale conectado", + "tsDisabled": "Tailscale desconectado", + "tsIPs": "IPs de Tailscale", + "tsPeers": "Pares", "tsOnline": "En línea", "tsOffline": "Desconectado", "tsExitNode": "Nodo de salida", "tsExitNodeActive": "Nodo de salida activo", - "tsHostname": "nombre de host" + "tsHostname": "Nombre de host" } }, "auth": { - "tagline": "Gestión de escritorio remoto y SSH autogestionada", + "tagline": "Gestión autoalojada de SSH y escritorio remoto", "loginTitle": "Bienvenido de nuevo", - "registerTitle": "Crear una cuenta", - "forgotPassword": "¿Has olvidado tu contraseña?", - "rememberMe": "Recordatorio del dispositivo durante 30 días (incluye TOTP)", - "noAccount": "¿No tienes una cuenta?", - "hasAccount": "¿Ya tienes una cuenta?", + "registerTitle": "Crear cuenta", + "forgotPassword": "¿Olvidó la contraseña?", + "rememberMe": "Recordar dispositivo durante 30 días (incluye TOTP)", + "noAccount": "¿No tiene cuenta?", + "hasAccount": "¿Ya tiene cuenta?", "twoFactorAuth": "Autenticación de dos factores", "enterCode": "Introduzca el código de verificación", - "backupCode": "O utilice el código de respaldo.", - "verifyCode": "Código de verificación", - "redirectingToApp": "Redireccionando a la aplicación...", - "sshAuthenticationRequired": "Se requiere autenticación SSH.", - "sshNoKeyboardInteractive": "Autenticación interactiva mediante teclado no disponible", - "sshAuthenticationFailed": "Falló la autenticación", - "sshAuthenticationTimeout": "Tiempo de espera de autenticación", - "sshNoKeyboardInteractiveDescription": "El servidor no admite la autenticación interactiva mediante teclado. Proporcione su contraseña o clave SSH.", + "backupCode": "O use un código de respaldo", + "verifyCode": "Verificar código", + "redirectingToApp": "Redirigiendo a la aplicación...", + "sshAuthenticationRequired": "Autenticación SSH requerida", + "sshNoKeyboardInteractive": "Autenticación interactiva por teclado no disponible", + "sshAuthenticationFailed": "Autenticación fallida", + "sshAuthenticationTimeout": "Tiempo de espera de autenticación agotado", + "sshNoKeyboardInteractiveDescription": "El servidor no admite la autenticación por teclado interactivo. Proporcione su contraseña o clave SSH.", "sshAuthFailedDescription": "Las credenciales proporcionadas son incorrectas. Inténtelo de nuevo con credenciales válidas.", - "sshTimeoutDescription": "El intento de autenticación ha caducado. Inténtalo de nuevo.", - "sshProvideCredentialsDescription": "Por favor, proporcione sus credenciales SSH para conectarse a este servidor.", + "sshTimeoutDescription": "El intento de autenticación ha caducado. Inténtelo de nuevo.", + "sshProvideCredentialsDescription": "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 aquí la contraseña.", - "passphraseRequired": "Se requiere contraseña", - "passphraseRequiredDescription": "La clave SSH está cifrada. Introduzca la contraseña para desbloquearla.", - "back": "Atrás", + "sshKeyPasswordDescription": "Si su clave SSH está cifrada, introduzca la frase de contraseña.", + "passphraseRequired": "Frase de contraseña requerida", + "passphraseRequiredDescription": "La clave SSH está cifrada. Introduzca la frase de contraseña para desbloquearla.", + "back": "Volver", "firstUser": "Primer usuario", - "firstUserMessage": "Eres el primer usuario y se te otorgarán permisos de administrador. Puedes ver la configuración de administrador en el menú desplegable de usuarios de la barra lateral. Si crees que se trata de un error, revisa los registros de Docker o crea una incidencia en GitHub.", + "firstUserMessage": "Usted es el primer usuario y se le otorgarán permisos de administrador. Puede ver la configuración de administrador en el menú desplegable de usuario de la barra lateral. Si cree que esto es un error, revise los registros de Docker o cree una incidencia en GitHub.", "external": "Externo", - "loginWithExternal": "Iniciar sesión con un proveedor externo", + "loginWithExternal": "Iniciar sesión con proveedor externo", "loginWithExternalDesc": "Inicie sesión utilizando su proveedor de identidad externo configurado.", - "externalNotSupportedInElectron": "La autenticación externa aún no es compatible con la aplicación Electron. Utilice la versión web para iniciar sesión mediante OIDC.", + "externalNotSupportedInElectron": "La autenticación externa aún no es compatible con la aplicación Electron. Utilice la versión web para iniciar sesión con OIDC.", "loginWithProvider": "Iniciar sesión con {{name}}", "orContinueWith": "o continuar con", "ldapUsername": "Nombre de usuario LDAP", "ldapPassword": "Contraseña LDAP", "ldapSignIn": "Iniciar sesión", - "ldapLoginFailed": "Falló el inicio de sesión LDAP", + "ldapLoginFailed": "Error de inicio de sesión LDAP", "resetPasswordButton": "Restablecer contraseña", - "sendResetCode": "Enviar código de reinicio", - "resetCodeDesc": "Introduce tu nombre de usuario para recibir un código de restablecimiento de contraseña. El código quedará registrado en los registros del contenedor Docker.", - "resetCode": "Código de reinicio", - "verifyCodeButton": "Código de verificación", - "enterResetCode": "Introduzca el código de 6 dígitos de los registros del contenedor Docker para el usuario:", + "sendResetCode": "Enviar código de restablecimiento", + "resetCodeDesc": "Introduzca su nombre de usuario para recibir un código de restablecimiento de contraseña. El código aparecerá en los registros del contenedor de Docker.", + "resetCode": "Código de restablecimiento", + "verifyCodeButton": "Verificar código", + "enterResetCode": "Introduzca el código de 6 dígitos de los registros del contenedor de Docker para el usuario:", "newPassword": "Nueva contraseña", - "confirmNewPassword": "confirmar Contraseña", + "confirmNewPassword": "Confirmar contraseña", "enterNewPassword": "Introduzca su nueva contraseña para el usuario:", - "signUp": "Inscribirse", + "signUp": "Registrarse", "desktopApp": "Aplicación de escritorio", - "loggingInToDesktopApp": "Iniciar sesión en la aplicación de escritorio", + "loggingInToDesktopApp": "Iniciando sesión en la aplicación de escritorio", "loadingServer": "Cargando servidor...", - "dataLossWarning": "Restablecer tu contraseña de esta manera eliminará todos tus hosts SSH guardados, credenciales y demás datos cifrados. Esta acción es irreversible. Úsala únicamente si has olvidado tu contraseña y no has iniciado sesión.", - "authenticationDisabled": "Autenticación deshabilitada", - "authenticationDisabledDesc": "Actualmente, todos los métodos de autenticación están deshabilitados. Póngase en contacto con su administrador.", - "passwordLoginDisabledDesc": "El inicio de sesión con contraseña está deshabilitado. Utilice una clave de acceso o un proveedor de autenticación externo.", - "signInWithPasskey": "Iniciar sesión con la clave de acceso", - "passkeyLoginFailed": "Falló el inicio de sesión con clave de acceso.", + "dataLossWarning": "Restablecer la contraseña de esta forma eliminará todos los hosts SSH guardados, credenciales y otros datos cifrados. Esta acción no se puede deshacer. Utilícela solo si ha olvidado la contraseña y no ha iniciado sesión.", + "authenticationDisabled": "Autenticación desactivada", + "authenticationDisabledDesc": "Todos los métodos de autenticación están actualmente desactivados. Póngase en contacto con su administrador.", + "passwordLoginDisabledDesc": "El inicio de sesión con contraseña está desactivado. Utilice una clave de acceso o un proveedor de autenticación externo.", + "signInWithPasskey": "Iniciar sesión con clave de acceso", + "passkeyLoginFailed": "Error al iniciar sesión con clave de acceso", "attemptsRemaining": "{{count}} intentos restantes", - "confirmResetDataWipe": "Esta cuenta no ha iniciado sesión desde la actualización del cifrado, por lo que sus datos almacenados no se pueden recuperar sin la contraseña anterior. Restablecerla eliminará permanentemente sus hosts, credenciales y fragmentos de código. ¿Desea continuar?" + "confirmResetDataWipe": "Esta cuenta no ha iniciado sesión desde la actualización de cifrado, por lo que sus datos almacenados no se pueden recuperar sin la contraseña antigua. Al restablecer se eliminarán permanentemente sus hosts, credenciales y fragmentos. ¿Continuar?" }, "hostKey": { - "verifyNewHost": "Verificar la clave de host SSH", - "keyChangedWarning": "Se ha cambiado la clave de host SSH.", - "firstConnectionTitle": "Primera vez que me conecto a este host.", - "firstConnectionDescription": "No se puede establecer la autenticidad de este host. Verifique que la huella digital coincida con lo que espera.", + "verifyNewHost": "Verificar clave de host SSH", + "keyChangedWarning": "Clave de host SSH cambiada", + "firstConnectionTitle": "Primera conexión a este host", + "firstConnectionDescription": "No se puede establecer la autenticidad de este host. Verifique que la huella digital coincida con lo esperado.", "keyChangedDescription": "La clave de host de 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 servidor, haga clic en Aceptar para continuar y guardar esta huella digital para futuras conexiones.", + "newFingerprint": "Nueva huella digital", + "fingerprint": "Huella digital", + "verifyInstructions": "Si confía en este host, haga clic en Aceptar para continuar y guardar esta huella digital para futuras conexiones.", "securityWarning": "Advertencia de seguridad", "acceptAndContinue": "Aceptar y continuar", "acceptNewKey": "Aceptar nueva clave y continuar" }, "errors": { - "databaseConnection": "No se pudo conectar con la base de datos.", + "databaseConnection": "No se pudo conectar a la base de datos", "unknownError": "Error desconocido", - "loginFailed": "error de inicio de sesion", - "failedPasswordReset": "No se pudo iniciar el restablecimiento de contraseña.", - "failedVerifyCode": "No se pudo verificar el código de reinicio.", - "failedCompleteReset": "No se pudo completar el restablecimiento de contraseña.", + "loginFailed": "Inicio de sesión fallido", + "failedPasswordReset": "Error al iniciar el restablecimiento de contraseña", + "failedVerifyCode": "Error al verificar el código de restablecimiento", + "failedCompleteReset": "Error al completar el restablecimiento de contraseña", "invalidTotpCode": "Código TOTP no válido", - "failedOidcLogin": "No se pudo iniciar el inicio de sesión de OIDC.", - "silentSigninOidcUnavailable": "Se solicitó el inicio de sesión silencioso, pero el inicio de sesión OIDC no está disponible.", - "failedUserInfo": "No se pudo obtener la información del usuario después de iniciar sesión.", - "oidcAuthFailed": "Falló la autenticación OIDC.", - "invalidAuthUrl": "Se recibió una URL de autorización no válida del servidor.", + "failedOidcLogin": "Error al iniciar sesión OIDC", + "silentSigninOidcUnavailable": "Se solicitó un inicio de sesión silencioso, pero el inicio de sesión OIDC no está disponible.", + "failedUserInfo": "Error al obtener la información del usuario después del inicio de sesión", + "oidcAuthFailed": "Error de autenticación OIDC", + "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": "El inicio de sesión con nombre de usuario/contraseña está actualmente deshabilitado.", - "sessionExpired": "La sesión ha caducado; vuelva a iniciar sesión.", - "totpRateLimited": "Límite de intentos: Se han realizado demasiados intentos de verificación TOTP. Inténtelo de nuevo más tarde.", - "totpRateLimitedWithTime": "Límite de intentos: Demasiados intentos de verificación TOTP. Espere {{time}} segundos antes de volver a intentarlo.", - "resetCodeRateLimited": "Límite de intentos: Se han realizado demasiados intentos de verificación. Inténtelo de nuevo más tarde.", - "resetCodeRateLimitedWithTime": "Límite de intentos: Demasiados intentos de verificación. Por favor, espere {{time}} segundos antes de volver a intentarlo.", - "authTokenSaveFailed": "No se pudo guardar el token de autenticación.", - "failedToLoadServer": "Error al cargar el servidor" + "passwordLoginDisabled": "El inicio de sesión con usuario/contraseña está actualmente deshabilitado", + "sessionExpired": "Sesión caducada - inicie sesión de nuevo", + "totpRateLimited": "Límite de velocidad: Demasiados intentos de verificación TOTP. Inténtelo de nuevo más tarde.", + "totpRateLimitedWithTime": "Límite de velocidad: Demasiados intentos de verificación TOTP. Espere {{time}} segundos antes de intentarlo de nuevo.", + "resetCodeRateLimited": "Límite de velocidad: Demasiados intentos de verificación. Inténtelo de nuevo más tarde.", + "resetCodeRateLimitedWithTime": "Límite de intentos: Demasiados intentos de verificación. Espere {{time}} segundos antes de volver a intentarlo.", + "authTokenSaveFailed": "Error al guardar el token de autenticación", + "failedToLoadServer": "Error al cargar el servidor", + "remoteServerRequired": "Se requiere un servidor remoto. Conecte un servidor remoto en Configuración para usar este tipo de conexión." }, "messages": { - "registrationDisabled": "El registro de nuevas cuentas está desactivado temporalmente por un administrador. Inicie sesión o póngase en contacto con un administrador.", - "userNotAllowed": "Tu cuenta no está autorizada para registrarse. Ponte en contacto con un administrador.", - "databaseConnectionFailed": "No se pudo conectar con el servidor de la base de datos.", - "resetCodeSent": "Código de reinicio enviado a los registros de Docker", + "registrationDisabled": "El registro de nuevas cuentas está deshabilitado por un administrador. Inicie sesión o contacte con un administrador.", + "userNotAllowed": "Su cuenta no está autorizada para registrarse. Contacte con un administrador.", + "databaseConnectionFailed": "Error al conectar con el servidor de base de datos", + "resetCodeSent": "Código de restablecimiento enviado a los registros de Docker", "codeVerified": "Código verificado correctamente", "passwordResetSuccess": "Contraseña restablecida correctamente", - "loginSuccess": "Inicio de sesión correcto", + "loginSuccess": "Inicio de sesión exitoso", "registrationSuccess": "Registro exitoso" }, "profile": { - "c2sTunnelConfigDesc": "Túneles de escritorio locales que apuntan a hosts SSH configurados.", - "c2sTunnelPresets": "Ajustes predefinidos del túnel del cliente", - "c2sTunnelPresetsDesc": "Guarde la lista de túneles locales de este cliente de escritorio como un preajuste de servidor con nombre, o cargue un preajuste en este cliente.", - "c2sTunnelPresetsUnavailable": "Los ajustes preestablecidos del túnel del cliente solo están disponibles en el cliente de escritorio.", + "c2sTunnelConfigDesc": "Túneles de escritorio locales hacia los hosts SSH configurados.", + "c2sTunnelPresets": "Preajustes de túneles de cliente", + "c2sTunnelPresetsDesc": "Guarde la lista de túneles locales de este cliente de escritorio como un preajuste de servidor con nombre, o cargue un preajuste de nuevo en este cliente.", + "c2sTunnelPresetsUnavailable": "Los preajustes de túneles de cliente solo están disponibles en el cliente de escritorio.", "c2sPresetName": "Nombre del preajuste", - "c2sPresetNamePlaceholder": "Nombre de configuración predefinida del cliente", - "c2sPresetToLoad": "Preestablecer para cargar", - "c2sNoPresetSelected": "No se ha seleccionado ningún ajuste preestablecido.", - "c2sNoPresets": "No se guardaron preajustes", - "c2sLoadPreset": "Carga", + "c2sPresetNamePlaceholder": "Nombre del preajuste de cliente", + "c2sPresetToLoad": "Preajuste a cargar", + "c2sNoPresetSelected": "Ningún preajuste seleccionado", + "c2sNoPresets": "No hay preajustes guardados", + "c2sLoadPreset": "Cargar", "c2sCurrentLocalConfig": "{{count}} túnel(es) de cliente local configurado(s) en este escritorio.", - "c2sPresetSyncNote": "Los preajustes son instantáneas explícitas; al cargar uno, se reemplaza la lista de túneles de cliente local de este cliente de escritorio.", - "c2sPresetSaved": "Configuración predefinida del túnel del cliente guardada", - "c2sPresetLoaded": "Configuración predefinida del túnel del cliente cargada localmente", - "c2sPresetRenamed": "Se cambió el nombre del preajuste del túnel del cliente.", - "c2sPresetDeleted": "Se eliminó la configuración preestablecida del túnel del cliente.", - "c2sPresetLoadError": "No se pudieron cargar los ajustes preestablecidos del túnel del cliente." + "c2sPresetSyncNote": "Los preajustes son instantáneas explícitas; cargar uno reemplaza la lista de túneles de cliente local de este cliente de escritorio.", + "c2sPresetSaved": "Preajuste de túnel de cliente guardado", + "c2sPresetLoaded": "Preajuste de túnel de cliente cargado localmente", + "c2sPresetRenamed": "Preajuste de túnel de cliente renombrado", + "c2sPresetDeleted": "Preajuste de túnel de cliente eliminado", + "c2sPresetLoadError": "Error al cargar los preajustes de túneles de cliente" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "Idioma", - "keyPassword": "contraseña clave", - "pastePrivateKey": "Pegue aquí su clave privada...", + "keyPassword": "contraseña de la clave", + "pastePrivateKey": "Pegue su clave privada aquí...", "localListenerHost": "127.0.0.1 (escuchar localmente)", "localTargetHost": "127.0.0.1 (destino en este equipo)", - "socksListenerHost": "127.0.0.1 (oyente SOCKS)", + "socksListenerHost": "127.0.0.1 (escucha SOCKS)", "enterPassword": "Introduce tu contraseña", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Panel", + "title": "Panel de control", "loading": "Cargando panel de control...", "github": "GitHub", - "support": "Apoyo", - "discord": "Discordia", - "docs": "Documentos", + "support": "Soporte", + "discord": "Discord", + "docs": "Documentación", "donate": "Donar", - "serverOverview": "Descripción general del servidor", + "serverOverview": "Resumen del servidor", "version": "Versión", - "upToDate": "A hoy", + "upToDate": "Actualizado", "updateAvailable": "Actualización disponible", "beta": "Beta", "uptime": "Tiempo de actividad", "database": "Base de datos", - "healthy": "Saludable", + "healthy": "Correcto", "error": "Error", - "totalHosts": "Total de anfitriones", - "totalTunnels": "Túneles Totales", - "totalCredentials": "Credenciales totales", + "totalHosts": "Total de hosts", + "totalTunnels": "Total de túneles", + "totalCredentials": "Total de credenciales", "recentActivity": "Actividad reciente", - "reset": "Reiniciar", + "reset": "Restablecer", "loadingRecentActivity": "Cargando actividad reciente...", - "noRecentActivity": "No hay actividad reciente", + "noRecentActivity": "Sin actividad reciente", "quickActions": "Acciones rápidas", - "addHost": "Agregar host", - "addCredential": "Agregar credencial", + "addHost": "Añadir host", + "addCredential": "Añadir credencial", "adminSettings": "Configuración de administrador", "userProfile": "Perfil de usuario", "serverStats": "Estadísticas del servidor", "loadingServerStats": "Cargando estadísticas del servidor...", "noServerData": "No hay datos del servidor disponibles", - "cpu": "UPC", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizar el panel de control", - "dashboardSettings": "Configuración del panel de control", - "enableDisableCards": "Habilitar/Deshabilitar tarjetas", - "resetLayout": "Restablecer a valores predeterminados", - "serverOverviewCard": "Descripción general del servidor", + "customizeLayout": "Personalizar panel", + "dashboardSettings": "Configuración del panel", + "enableDisableCards": "Activar/Desactivar tarjetas", + "resetLayout": "Restablecer diseño predeterminado", + "serverOverviewCard": "Resumen del servidor", "recentActivityCard": "Actividad reciente", - "networkGraphCard": "Grafo de red", - "networkGraph": "Grafo de red", + "networkGraphCard": "Gráfico de red", + "networkGraph": "Gráfico de red", "quickActionsCard": "Acciones rápidas", "serverStatsCard": "Estadísticas del servidor", "panelMain": "Principal", - "panelSide": "Lado", - "justNow": "En este momento", + "panelSide": "Lateral", + "justNow": "ahora mismo", "serviceLinks": "Enlaces de servicio", "homepagePreview": "Vista previa de la página de inicio" }, "donation": { - "title": "¿Te gusta Termix?", - "body": "Termix es gratuito y de código abierto, desarrollado y mantenido por un equipo de dos personas en nuestro tiempo libre. Si ha reemplazado una herramienta comercial por la que de otro modo pagarías, una donación ayuda a cubrir los costos de alojamiento y a mantener el desarrollo. Por el momento, solo se aceptan donaciones en criptomonedas.", - "milestones": "Las donaciones también ayudan a financiar el tiempo necesario para investigar y aprender lo que se requiere para desarrollar funciones como la compatibilidad con SAML y Kubernetes. Consulta el progreso en la página de donaciones.", + "title": "¿Disfrutando de Termix?", + "body": "Termix es gratuito y de código abierto, creado y mantenido por un equipo de dos personas en nuestro tiempo libre. Si has sustituido una herramienta comercial por la que estarías pagando, una donación ayuda a cubrir los costes de alojamiento y a que el desarrollo continúe. Actualmente, las donaciones son solo en criptomonedas.", + "milestones": "Las donaciones también ayudan a financiar el tiempo para investigar y aprender lo necesario para crear funcionalidades como el soporte para SAML y Kubernetes. Ve el progreso en la página de donaciones.", "cta": "Donar", "dismiss": "Quizás más tarde" }, "dashboardTab": { "stable": "ESTABLE", - "hostsOnline": "Anfitriones en línea", + "hostsOnline": "Hosts en línea", "activeTunnels": "Túneles activos", - "registerNewServer": "Registra un nuevo servidor", - "storeSshKeysOrPasswords": "Almacenar claves o contraseñas SSH", + "registerNewServer": "Registrar un nuevo servidor", + "storeSshKeysOrPasswords": "Almacenar claves SSH o contraseñas", "manageUsersAndRoles": "Gestionar usuarios y roles", - "manageYourAccount": "Gestiona tu cuenta", + "manageYourAccount": "Gestionar tu cuenta", "hostStatus": "Estado del host", "noHostsConfigured": "No hay hosts configurados", "online": "EN LÍNEA", "offline": "DESCONECTADO", - "checking": "DE CHEQUES", + "checking": "COMPROBANDO", "onlineLower": "En línea", "nodes": "{{count}} nodos", - "add": "Agregar:", + "add": "Añadir:", "commandPalette": "Paleta de comandos", "done": "Hecho", - "editModeInstructions": "Arrastra las tarjetas para reordenarlas · Arrastra el separador de columnas para cambiar el tamaño de las columnas · Arrastra el borde inferior de una tarjeta para cambiar su altura · Papelera para eliminar", + "editModeInstructions": "Arrastra las tarjetas para reordenar · Arrastra el divisor de columnas para redimensionar las columnas · Arrastra el borde inferior de una tarjeta para cambiar su altura · Arrastra a la papelera para eliminar", "empty": "Vacío", - "clear": "Claro", + "clear": "Limpiar", "serviceLinksTitle": "Enlaces de servicio", "serviceLinksEmpty": "Aún no hay enlaces de servicio", "serviceLinksAddLabel": "Etiqueta", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "Agregar", + "serviceLinksAdd": "Añadir", "serviceLinksLabelPlaceholder": "Mi servicio", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "Introduzca una dirección web válida", - "serviceLinksAddFailed": "No se pudo agregar el enlace del servicio.", + "serviceLinksInvalidUrl": "Introduce una dirección web válida", + "serviceLinksAddFailed": "Error al añadir el enlace de servicio", "disk": "Disco", "viewServerDetails": "Ver detalles del servidor" }, "sessionLogs": { "title": "Registros de sesión", - "noLogs": "Aún no hay registros de sesión.", - "noLogsDesc": "Habilite el registro de sesiones en un host para comenzar a grabar.", + "noLogs": "Aún no hay registros de sesión", + "noLogsDesc": "Activa el registro de sesiones en un host para empezar a grabar", "duration": "Duración", "viewLog": "Ver registro", "downloadLog": "Descargar", - "deleteLog": "Borrar", + "deleteLog": "Eliminar", "confirmDelete": "¿Eliminar este registro de sesión?", "confirmDeleteDesc": "Esta acción no se puede deshacer.", "copyContent": "Copiar", "copied": "¡Copiado!", - "loadError": "No se pudieron cargar los registros de la sesión.", - "deleteError": "No se pudo eliminar el registro de sesión.", + "loadError": "Error al cargar los registros de sesión", + "deleteError": "Error al eliminar el registro de sesión", "filterByHost": "Filtrar por host..." }, "networkGraph": { - "addHost": "Agregar host", - "addGroup": "Agregar grupo", - "addLink": "Agregar enlace", - "zoomIn": "Dar un golpe de zoom", - "zoomOut": "Aleja la imagen", + "addHost": "Añadir host", + "addGroup": "Añadir grupo", + "addLink": "Añadir enlace", + "zoomIn": "Acercar", + "zoomOut": "Alejar", "resetView": "Restablecer vista", - "selectHost": "Seleccionar anfitrión", - "chooseHost": "Elige un anfitrión...", - "parentGroup": "Grupo de padres", - "noGroup": "Ningún grupo", + "selectHost": "Seleccionar host", + "chooseHost": "Elegir un host...", + "parentGroup": "Grupo padre", + "noGroup": "Sin grupo", "groupName": "Nombre del grupo", "color": "Color", - "source": "Fuente", - "target": "Objetivo", - "moveToGroup": "Trasladarse al grupo", + "source": "Origen", + "target": "Destino", + "moveToGroup": "Mover al grupo", "selectGroup": "Seleccionar grupo...", - "addConnection": "Agregar conexión", - "hostDetails": "Detalles del anfitrión", - "removeFromGroup": "Eliminar del grupo", - "addHostHere": "Agregar host aquí", + "addConnection": "Añadir conexión", + "hostDetails": "Detalles del host", + "removeFromGroup": "Quitar del grupo", + "addHostHere": "Añadir host aquí", "editGroup": "Editar grupo", - "delete": "Borrar", - "add": "Agregar", + "delete": "Eliminar", + "add": "Añadir", "create": "Crear", "move": "Mover", "connect": "Conectar", "createGroup": "Crear grupo", - "selectSourcePlaceholder": "Seleccionar fuente...", - "selectTargetPlaceholder": "Seleccionar objetivo...", + "selectSourcePlaceholder": "Seleccionar origen...", + "selectTargetPlaceholder": "Seleccionar destino...", "invalidFile": "Archivo no válido", - "hostAlreadyExists": "El host ya está en la topología.", - "connectionExists": "La conexión ya existe.", + "hostAlreadyExists": "El host ya está en la topología", + "connectionExists": "La conexión ya existe", "unknown": "Desconocido", "name": "Nombre", "ip": "IP", "status": "Estado", - "failedToAddNode": "No se pudo agregar el nodo.", - "sourceDifferentFromTarget": "El origen y el destino deben ser diferentes.", + "failedToAddNode": "Error al añadir nodo", + "sourceDifferentFromTarget": "El origen y el destino deben ser diferentes", "exportJSON": "Exportar JSON", "importJSON": "Importar JSON", "terminal": "Terminal", "fileManager": "Gestor de archivos", "tunnel": "Túnel", - "docker": "Estibador", - "serverStats": "Métricas del anfitrión", - "hostMetrics": "Métricas del anfitrión", + "docker": "Docker", + "serverStats": "Métricas del host", + "hostMetrics": "Métricas del host", "noNodes": "Aún no hay nodos" }, "docker": { - "notEnabled": "Docker no está habilitado para este host.", + "notEnabled": "Docker no está habilitado para este host", "validating": "Validando Docker...", "connecting": "Conectando...", "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "No se pudo conectar con Docker.", + "connectionFailed": "Error al conectar con Docker", "containerStarted": "Contenedor {{name}} iniciado", "failedToStartContainer": "Error al iniciar el contenedor {{name}}", - "containerStopped": "El contenedor {{name}} se detuvo", - "failedToStopContainer": "No se pudo detener el contenedor {{name}}", + "containerStopped": "Contenedor {{name}} detenido", + "failedToStopContainer": "Error al detener el contenedor {{name}}", "containerRestarted": "Contenedor {{name}} reiniciado", "failedToRestartContainer": "Error al reiniciar el contenedor {{name}}", - "containerPaused": "Contenedor {{name}} en pausa", + "containerPaused": "Contenedor {{name}} pausado", "containerUnpaused": "Contenedor {{name}} reanudado", - "failedToTogglePauseContainer": "No se pudo cambiar el estado de pausa para el contenedor {{name}}", + "failedToTogglePauseContainer": "Error al cambiar el estado de pausa del contenedor {{name}}", "containerRemoved": "Contenedor {{name}} eliminado", - "failedToRemoveContainer": "No se pudo eliminar el contenedor {{name}}", + "failedToRemoveContainer": "Error al eliminar el contenedor {{name}}", "image": "Imagen", "ports": "Puertos", - "noPorts": "No hay puertos", - "start": "Comenzar", + "noPorts": "Sin puertos", + "start": "Iniciar", "confirmRemoveContainer": "¿Está seguro de que desea eliminar el contenedor '{{name}}'? Esta acción no se puede deshacer.", - "runningContainerWarning": "Advertencia: Este contenedor se está ejecutando actualmente. Si lo elimina, primero se detendrá.", + "runningContainerWarning": "Advertencia: Este contenedor está actualmente en ejecución. Al eliminarlo, se detendrá primero.", "loadingContainers": "Cargando contenedores...", "manager": "Administrador de Docker", "autoRefresh": "Actualización automática", "timestamps": "Marcas de tiempo", - "lines": "Pauta", + "lines": "Líneas", "filterLogs": "Filtrar registros...", - "refresh": "Refrescar", + "refresh": "Actualizar", "download": "Descargar", - "clear": "Claro", - "logsDownloaded": "Los registros se descargaron correctamente.", + "clear": "Limpiar", + "logsDownloaded": "Registros descargados correctamente", "last50": "Últimos 50", "last100": "Últimos 100", "last500": "Últimos 500", "last1000": "Últimos 1000", "allLogs": "Todos los registros", - "noLogsMatching": "No se encontraron registros que coincidieran con \"{{query}}\"", + "noLogsMatching": "No hay registros que coincidan con \"{{query}}\"", "noLogsAvailable": "No hay registros disponibles", - "noContainersFound": "No se encontraron contenedores.", - "noContainersFoundHint": "No hay contenedores Docker disponibles en este host.", - "searchPlaceholder": "Contenedores de búsqueda...", + "noContainersFound": "No se encontraron contenedores", + "noContainersFoundHint": "No hay contenedores Docker disponibles en este host", + "searchPlaceholder": "Buscar contenedores...", "allStatuses": "Todos los estados", - "stateRunning": "Correr", - "statePaused": "Pausa", - "stateExited": "Salió", + "stateRunning": "En ejecución", + "statePaused": "En pausa", + "stateExited": "Detenido", "stateRestarting": "Reiniciando", - "noContainersMatchFilters": "No hay contenedores que coincidan con sus filtros.", - "noContainersMatchFiltersHint": "Intenta ajustar tus criterios de búsqueda o filtro.", - "failedToFetchStats": "No se pudieron obtener las estadísticas del contenedor.", - "containerNotRunning": "El contenedor no se está ejecutando", - "startContainerToViewStats": "Inicie el contenedor para ver las estadísticas.", + "noContainersMatchFilters": "Ningún contenedor coincide con los filtros", + "noContainersMatchFiltersHint": "Intente ajustar los criterios de búsqueda o filtro", + "failedToFetchStats": "Error al obtener las estadísticas del contenedor", + "containerNotRunning": "El contenedor no está en ejecución", + "startContainerToViewStats": "Inicie el contenedor para ver las estadísticas", "loadingStats": "Cargando estadísticas...", - "errorLoadingStats": "Estadísticas de carga de errores", + "errorLoadingStats": "Error al cargar las estadísticas", "noStatsAvailable": "No hay estadísticas disponibles", - "cpuUsage": "Uso de la CPU", + "cpuUsage": "Uso de CPU", "current": "Actual", "memoryUsage": "Uso de memoria", "networkIo": "E/S de red", - "input": "Aporte", - "output": "Producción", - "blockIo": "Bloque E/S", - "read": "Leer", - "write": "Escribir", + "input": "Entrada", + "output": "Salida", + "blockIo": "E/S de bloques", + "read": "Lectura", + "write": "Escritura", "pids": "PIDs", "containerInformation": "Información del contenedor", "name": "Nombre", - "id": "IDENTIFICACIÓN", + "id": "ID", "state": "Estado", - "containerMustBeRunning": "El contenedor debe estar en ejecución para acceder a la consola.", + "containerMustBeRunning": "El contenedor debe estar en ejecución para acceder a la consola", "verificationCodePrompt": "Introduzca el código de verificación", - "totpVerificationFailed": "La verificación TOTP falló. Inténtalo de nuevo.", - "warpgateVerificationFailed": "Falló la autenticación de Warpgate. Inténtalo de nuevo.", + "totpVerificationFailed": "La verificación TOTP falló. Inténtelo de nuevo.", + "warpgateVerificationFailed": "La autenticación Warpgate falló. Inténtelo de nuevo.", "connectedTo": "Conectado a {{containerName}}", "disconnected": "Desconectado", "consoleError": "Error de consola", "errorMessage": "Error: {{message}}", - "failedToConnect": "No se pudo conectar con el contenedor.", + "failedToConnect": "No se pudo conectar al contenedor", "console": "Consola", "selectShell": "Seleccionar shell", - "bash": "Intento", + "bash": "Bash", "sh": "sh", - "ash": "ceniza", + "ash": "ash", "connect": "Conectar", "disconnect": "Desconectar", "notConnected": "No conectado", - "clickToConnect": "Haz clic en conectar para iniciar una sesión de terminal.", - "connectingTo": "Conectando con {{containerName}}...", + "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": "Inicie el contenedor para acceder a la consola." + "startContainerToAccess": "Inicie el contenedor para acceder a la consola" }, "admin": { "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionSso": "Proveedores de inicio de sesión único (SSO)", - "ssoAddProvider": "Agregar proveedor", + "sectionSso": "Proveedores SSO", + "ssoAddProvider": "Añadir proveedor", "ssoDocsLink": "Ver documentación", "ssoProviderDocsLink": "Ver documentación", - "ssoNoProviders": "No hay proveedores de SSO configurados.", - "ssoProviderName": "Nombre para mostrar", + "ssoNoProviders": "No hay proveedores SSO configurados.", + "ssoProviderName": "Nombre visible", "ssoProviderType": "Tipo de proveedor", "ssoDeleteProvider": "Eliminar proveedor", "ssoDeleteConfirm": "¿Eliminar este proveedor? Los usuarios asociados no podrán iniciar sesión.", @@ -2591,20 +2725,20 @@ "ssoTypeGoogle": "Google", "ssoEnabled": "Activado", "ssoDisabled": "Desactivado", - "ssoSaveProvider": "Proveedor de ahorro", + "ssoSaveProvider": "Guardar proveedor", "ssoTestConnection": "Probar conexión", "ssoEditProvider": "Editar proveedor", "ldapHost": "Host LDAP", "ldapPort": "Puerto", - "ldapUseTls": "Utilice TLS (LDAPS)", - "ldapBindDn": "Enlace DN", + "ldapUseTls": "Usar TLS (LDAPS)", + "ldapBindDn": "DN de enlace", "ldapBindPassword": "Contraseña de enlace", "ldapUserSearchBase": "Base de búsqueda de usuarios", - "ldapUserSearchFilter": "Filtro de búsqueda de usuario", + "ldapUserSearchFilter": "Filtro de búsqueda de usuarios", "ldapUsernameAttr": "Atributo de nombre de usuario", - "ldapDisplayNameAttr": "Atributo de nombre para mostrar", + "ldapDisplayNameAttr": "Atributo de nombre visible", "ldapGroupSearchBase": "Base de búsqueda de grupos", - "ldapAdminGroup": "Grupo de administración", + "ldapAdminGroup": "Grupo de administradores", "ldapAllowedUsers": "Usuarios permitidos", "sectionUsers": "Usuarios", "sectionSessions": "Sesiones", @@ -2613,41 +2747,52 @@ "sectionApiKeys": "Claves API", "sectionAuditLog": "Registro de auditoría", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Emite y renueva automáticamente un certificado SSL de confianza de Let's Encrypt. Requiere un dominio público y acceso al puerto 80 o DNS.", + "sslDescription": "Emitir y renovar automáticamente un certificado SSL de confianza de Let's Encrypt. Requiere un dominio público y acceso al puerto 80 o DNS.", "sslDocsLink": "Ver documentación SSL", "sslDomain": "Dominio", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "El nombre de dominio público del certificado.", + "sslDomainDesc": "El nombre de dominio público para el certificado.", "sslEmail": "Correo electrónico", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Correo electrónico de contacto para notificaciones y gestión de la cuenta de Let's Encrypt.", + "sslEmailDesc": "Correo electrónico de contacto para notificaciones y cuenta de Let's Encrypt.", "sslChallengeType": "Tipo de desafío", "sslChallengeTypeDesc": "Cómo demostrar la propiedad del dominio a Let's Encrypt.", - "sslChallengeHttp": "HTTP (webroot): requiere el puerto 80 accesible desde Internet.", - "sslChallengeDns": "DNS (Cloudflare): requiere un token de API de Cloudflare.", + "sslChallengeHttp": "HTTP (webroot) - requiere el puerto 80 accesible desde internet", + "sslChallengeDns": "DNS (Cloudflare) - requiere un token de API de Cloudflare", "sslCloudflareToken": "Token de API de Cloudflare", - "sslCloudflareTokenPlaceholder": "Introduzca el token...", - "sslCloudflareTokenDesc": "Token con ámbito y permiso Zone:DNS:Edit para su dominio.", + "sslCloudflareTokenPlaceholder": "Introducir token...", + "sslCloudflareTokenDesc": "Token con permiso Zone:DNS:Edit limitado para tu dominio.", "sslCertStatus": "Estado del certificado", "sslCertStatusNone": "Sin certificado", "sslCertStatusValid": "Válido", "sslCertStatusExpiring": "Caduca pronto", - "sslCertStatusExpired": "Venció", + "sslCertStatusExpired": "Caducado", "sslCertExpiresAt": "Caduca {{date}}", "sslLastIssued": "Última emisión {{date}}", - "sslRequestCert": "Emisión/Renovación de Certificado", + "sslRequestCert": "Emitir / Renovar certificado", "sslRequestCertLoading": "Solicitando certificado...", "sslRequestCertSuccess": "Certificado emitido e instalado correctamente", - "sslRequestCertFailed": "Falló la solicitud de certificado.", + "sslRequestCertFailed": "La solicitud del certificado falló", "sslSave": "Guardar configuración", "sslSaved": "Configuración SSL guardada", - "sslSaveFailed": "No se pudieron guardar los ajustes SSL.", - "sslRequiresDomain": "Se requiere dominio y correo electrónico.", - "sslInfoNote": "Tras emitir un certificado, habilite SSL en sus variables de entorno (ENABLE_SSL=true) y reinicie Termix.", - "auditLogTotal": "{{total}} entradas totales", - "auditLogEmpty": "No se encontraron entradas en el registro de auditoría.", + "sslSaveFailed": "Error al guardar la configuración SSL", + "sslRequiresDomain": "Se requieren dominio y correo electrónico", + "sslInfoNote": "Después de emitir un certificado, habilita SSL en las variables de entorno (ENABLE_SSL=true) y reinicia Termix.", + "sslManualOption": "Manual (subir certificado)", + "sslManualCert": "Certificado (PEM)", + "sslManualCertPlaceholder": "-----INICIO DEL CERTIFICADO-----", + "sslManualKey": "Clave privada (PEM)", + "sslManualKeyPlaceholder": "-----COMIENZA LA CLAVE PRIVADA-----", + "sslManualDesc": "Pegue su certificado y clave privada existentes, incluyendo la cadena completa si así lo requiere su CA.", + "sslManualUpload": "Cargar e instalar certificado", + "sslManualUploadLoading": "Subiendo certificado...", + "sslManualUploadSuccess": "Certificado cargado e instalado correctamente", + "sslManualUploadFailed": "Falló la carga del certificado.", + "sslManualRequiresFields": "Se requiere certificado y clave privada.", + "auditLogTotal": "{{total}} entradas en total", + "auditLogEmpty": "No se han encontrado entradas de auditoría", "auditLogSuccess": "Éxito", - "auditLogFailed": "Fallido", + "auditLogFailed": "Error", "auditLogClearFilters": "Borrar filtros", "auditLogPage": "Página {{page}} de {{totalPages}} ({{total}} en total)", "auditLogIp": "IP", @@ -2656,56 +2801,62 @@ "auditLogFilterAction": "Acción", "auditLogFilterResourceType": "Tipo de recurso", "auditLogFilterStatus": "Estado", - "auditLogFilterFrom": "De", - "auditLogFilterTo": "A", - "auditLogFilterAll": "Todo", - "allowRegistration": "Permitir el registro de usuarios", - "allowRegistrationDesc": "Permitir que los nuevos usuarios se registren por sí mismos con un nombre de usuario y una contraseña.", + "auditLogFilterFrom": "Desde", + "auditLogFilterTo": "Hasta", + "auditLogFilterAll": "Todos", + "allowRegistration": "Permitir registro de usuarios", + "allowRegistrationDesc": "Permite a los nuevos usuarios registrarse con un nombre de usuario y contraseña", "allowPasswordLogin": "Permitir inicio de sesión con contraseña", - "allowPasswordLoginDesc": "Nombre de usuario/contraseña para iniciar sesión", - "oidcAutoProvision": "Aprovisionamiento automático de OIDC", - "oidcAutoProvisionDesc": "Crear automáticamente cuentas para usuarios de OIDC/SSO en el primer inicio de sesión (independientemente de la opción de registro).", - "oidcSilentLoginDefault": "Inicio de sesión OIDC silencioso por defecto", - "oidcSilentLoginDefaultDesc": "Redirigir automáticamente al inicio de sesión de OIDC en cada visita, omitiendo por completo el formulario de inicio de sesión.", + "allowPasswordLoginDesc": "Inicio de sesión con usuario/contraseña", + "oidcAutoProvision": "Aprovisionamiento automático OIDC", + "oidcAutoProvisionDesc": "Crear automáticamente cuentas para usuarios OIDC/SSO en el primer inicio de sesión (independiente de la opción de registro)", + "oidcSilentLoginDefault": "Inicio de sesión silencioso OIDC por defecto", + "oidcSilentLoginDefaultDesc": "Redirige automáticamente al inicio de sesión OIDC en cada visita, omitiendo por completo el formulario de inicio de sesión", "allowPasswordReset": "Permitir restablecimiento de contraseña", - "allowPasswordResetDesc": "Restablecer el código a través de los registros de Docker", + "allowPasswordResetDesc": "Código de restablecimiento a través de los registros de Docker", "commandHistoryEnabled": "Historial de comandos", - "commandHistoryEnabledDesc": "Permitir el registro del historial de comandos. Cuando está desactivado, el historial no se guarda, independientemente de la configuración de cada host.", - "updateCommandHistoryFailed": "Error al actualizar la configuración del historial de comandos.", - "sessionTimeout": "Tiempo de espera de la sesión", + "commandHistoryEnabledDesc": "Permitir el registro del historial de comandos. Si se desactiva, el historial no se guarda independientemente de la configuración por host.", + "updateCommandHistoryFailed": "Error al actualizar la configuración del historial de comandos", + "analyticsEnabled": "Compartir estadísticas de uso anónimo", + "analyticsEnabledDesc": "Envía un recuento diario anónimo de usuarios, hosts y uso de funciones para ayudar a mejorar Termix. No se incluyen datos personales ni detalles de conexión.", + "updateAnalyticsFailed": "No se pudo actualizar la configuración de análisis.", + "sessionSharingGloballyEnabled": "Permitir compartir la sesión", + "sessionSharingGloballyEnabledDesc": "Permite compartir sesiones de terminal en vivo, RDP, VNC y Telnet en toda la instancia. Cuando está desactivado, anula cualquier configuración de uso compartido por host.", + "updateSessionSharingFailed": "No se pudo actualizar la configuración de uso compartido de la sesión.", + "sessionTimeout": "Tiempo de expiración de sesión", "hours": "horas", - "sessionTimeoutRange": "Mínimo 1 h · Máximo 720 h", - "monitoringDefaults": "Monitoreo de valores predeterminados", - "statusCheck": "Verificación de estado", - "metrics": "Métrica", - "sec": "segundo", + "sessionTimeoutRange": "Mín 1h · Máx 720h", + "monitoringDefaults": "Configuración predeterminada de monitorización", + "statusCheck": "Comprobación de estado", + "metrics": "Métricas", + "sec": "seg", "logLevel": "Nivel de registro", - "enableGuacamole": "Habilitar guacamole", + "enableGuacamole": "Activar Guacamole", "enableGuacamoleDesc": "Escritorio remoto RDP/VNC", "enableGuacamoleDocsLink": "Ver documentación", "guacdUrl": "URL de guacd", - "tailscaleApiKey": "Clave API de Tailscale", - "tailscaleApiKeyDescription": "Se utiliza para la detección de dispositivos en el editor de host. Genere una clave en tailscale.com/admin/settings/keys.", + "tailscaleApiKey": "Clave de API de Tailscale", + "tailscaleApiKeyDescription": "Se usa para la detección de dispositivos en el editor de hosts. Genere una clave en tailscale.com/admin/settings/keys.", "tailscaleApiKeyDocsLink": "Ver documentación", - "oidcDescription": "Configure OpenID Connect para SSO. Los campos marcados con * son obligatorios.", + "oidcDescription": "Configurar OpenID Connect para inicio de sesión único (SSO). Los campos marcados con * son obligatorios.", "oidcDocsLink": "Ver documentación", "oidcClientId": "ID de cliente", - "oidcClientSecret": "Secreto del cliente", + "oidcClientSecret": "Secreto de cliente", "oidcAuthUrl": "URL de autorización", "oidcIssuerUrl": "URL del emisor", "oidcTokenUrl": "URL del token", "oidcUserIdentifier": "Ruta del identificador de usuario", "oidcDisplayName": "Ruta del nombre para mostrar", - "oidcScopes": "Alcances", - "oidcUserinfoUrl": "Anular la URL de información del usuario", + "oidcScopes": "Ámbitos", + "oidcUserinfoUrl": "Sobrescribir URL de Userinfo", "oidcAllowedUsers": "Usuarios permitidos", - "oidcAllowedUsersDesc": "Un correo electrónico por línea. Déjelo en blanco para permitir todos.", - "oidcAdminGroup": "Grupo de administración", - "oidcAdminGroupDesc": "Los usuarios de este grupo tienen permisos de administrador. Déjelo en blanco para desactivar la sincronización de grupos.", - "oidcGroupClaim": "Reclamación colectiva", - "oidcGroupClaimDesc": "Opcional. La ruta de reclamación que contiene los grupos del usuario. Por defecto, se utiliza la ruta grupos, roles y luego grupo. Úsela para proveedores con una reclamación personalizada (por ejemplo, Zitadel).", + "oidcAllowedUsersDesc": "Un correo electrónico por línea. Déjelo vacío para permitir todos.", + "oidcAdminGroup": "Grupo de administradores", + "oidcAdminGroupDesc": "Los usuarios de este grupo reciben permisos de administrador. Déjelo vacío para desactivar la sincronización de grupos.", + "oidcGroupClaim": "Claim de grupo", + "oidcGroupClaimDesc": "Opcional. La ruta del claim que contiene los grupos del usuario. Por defecto, se usan 'groups', 'roles' y luego 'group'. Úselo para proveedores con un claim personalizado (p. ej. Zitadel).", "oidcCaCert": "Certificado CA personalizado", - "oidcCaCertDesc": "Opcional. Certificado de CA codificado en PEM para proveedores OIDC que utilizan una CA privada o autofirmada. Déjelo en blanco para usar el almacén de confianza del sistema.", + "oidcCaCertDesc": "Opcional. Certificado CA codificado en PEM para proveedores OIDC que utilicen una CA privada o autofirmada. Déjelo vacío para usar el almacén de confianza del sistema.", "removeOidc": "Eliminar", "usersCount": "{{count}} usuarios", "createUser": "Crear", @@ -2717,246 +2868,246 @@ "createRole": "Crear", "creating": "Creando...", "exportDatabase": "Exportar base de datos", - "exportDatabaseDesc": "Descarga una copia de seguridad de todos los hosts, credenciales y configuraciones.", + "exportDatabaseDesc": "Descargar una copia de seguridad de todos los hosts, credenciales y configuraciones", "export": "Exportar", - "exporting": "Exportador...", + "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": "Importador...", - "apiKeysCount": "{{count}} teclas", + "importing": "Importando...", + "apiKeysCount": "{{count}} claves", "apiKeysDocsLink": "Ver documentación", "newApiKey": "Nueva clave API", - "apiKeyCreatedWarning": "Clave creada: cópiela ahora, no volverá a aparecer.", + "apiKeyCreatedWarning": "Clave creada: cópiala ahora, no se volverá a mostrar.", "apiKeyName": "Nombre", "apiKeyUser": "Usuario", - "apiKeySelectUser": "Seleccione un usuario...", - "apiKeyExpiresAt": "Caduca a las", + "apiKeySelectUser": "Seleccionar un usuario...", + "apiKeyExpiresAt": "Caduca", "createKey": "Crear clave", - "apiKeyNoExpiry": "Sin fecha de caducidad", - "revokedBadge": "REVOCADO", + "apiKeyNoExpiry": "Sin caducidad", + "revokedBadge": "REVOCADA", "authTypeDual": "Autenticación dual", "authTypeOidc": "OIDC", "authTypeLocal": "Local", "adminStatusAdministrator": "Administrador", - "adminStatusRegularUser": "Usuario habitual", - "adminBadge": "ADMINISTRACIÓN", - "systemBadge": "SISTEMA", - "customBadge": "COSTUMBRE", - "youBadge": "TÚ", - "sessionsActive": "{{count}} activo", - "sessionActive": "Activo: {{time}}", - "sessionExpires": "Exp: {{time}}", - "revokeAll": "Todo", - "revokeAllSessionsSuccess": "Todas las sesiones del usuario han sido revocadas.", - "revokeAllSessionsFailed": "No se pudieron revocar las sesiones.", - "revokeSessionFailed": "No se pudo revocar la sesión.", - "addRole": "Agregar rol", - "noCustomRoles": "No se han definido roles personalizados", - "removeRoleFailed": "No se pudo eliminar el rol", - "assignRoleFailed": "No se pudo asignar el rol.", - "deleteRoleFailed": "No se pudo eliminar el rol.", + "adminStatusRegularUser": "Usuario normal", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} activas", + "sessionActive": "Activa: {{time}}", + "sessionExpires": "Caduca: {{time}}", + "revokeAll": "Revocar todo", + "revokeAllSessionsSuccess": "Todas las sesiones del usuario revocadas", + "revokeAllSessionsFailed": "Error al revocar las sesiones", + "revokeSessionFailed": "Error al revocar la sesión", + "addRole": "Añadir rol", + "noCustomRoles": "No hay roles personalizados definidos", + "removeRoleFailed": "Error al eliminar el rol", + "assignRoleFailed": "Error al asignar el rol", + "deleteRoleFailed": "Error al eliminar el rol", "userAdminAccess": "Administrador", - "userAdminAccessDesc": "Acceso completo a todas las configuraciones de administrador.", + "userAdminAccessDesc": "Acceso completo a todos los ajustes de administración", "userRoles": "Roles", "revokeAllUserSessions": "Revocar todas las sesiones", - "revokeAllUserSessionsDesc": "Forzar el inicio de sesión en todos los dispositivos.", + "revokeAllUserSessionsDesc": "Forzar inicio de sesión de nuevo en todos los dispositivos", "revoke": "Revocar", - "deleteUserWarning": "Eliminar a este usuario es una medida permanente.", + "deleteUserWarning": "La eliminación de este usuario es permanente.", "deleteUser": "Eliminar {{username}}", "deleting": "Eliminando...", - "deleteUserFailed": "No se pudo eliminar el usuario.", + "deleteUserFailed": "Error al eliminar el usuario", "deleteUserSuccess": "Usuario \"{{username}}\" eliminado", "deleteRoleSuccess": "Rol \"{{name}}\" eliminado", "revokeKeySuccess": "Clave \"{{name}}\" revocada", - "revokeKeyFailed": "No se pudo revocar la clave.", + "revokeKeyFailed": "Error al revocar la clave", "copiedToClipboard": "Copiado al portapapeles", "done": "Hecho", "createUserTitle": "Crear usuario", - "createUserDesc": "Crea una nueva cuenta local.", + "createUserDesc": "Crear una nueva cuenta local.", "createUserUsername": "Nombre de usuario", "createUserPassword": "Contraseña", "createUserPasswordHint": "Mínimo 6 caracteres.", - "createUserEnterUsername": "Ingrese su nombre de usuario", - "createUserEnterPassword": "Introduzca la contraseña", + "createUserEnterUsername": "Introducir nombre de usuario", + "createUserEnterPassword": "Introducir contraseña", "createUserSubmit": "Crear usuario", - "editUserTitle": "Administrar usuario: {{username}}", - "editUserDesc": "Edita roles, estado de administrador, sesiones y configuración de la cuenta.", + "editUserTitle": "Gestionar usuario: {{username}}", + "editUserDesc": "Editar roles, estado de administrador, sesiones y ajustes de la cuenta.", "editUserUsername": "Nombre de usuario", "editUserAuthType": "Tipo de autenticación", - "editUserAdminStatus": "Estado administrativo", + "editUserAdminStatus": "Estado de administrador", "editUserUserId": "ID de usuario", "linkAccountTitle": "Vincular cuentas", "linkAccountDesc": "Fusionar la cuenta OIDC {{username}} con una cuenta local existente.", - "linkAccountDescLocal": "Vincula una cuenta local {{username}} con una cuenta existente que solo utilice OIDC.", - "linkAccountWarningTitle": "Esto hará:", - "linkAccountEffect1": "Eliminar la cuenta exclusiva de OIDC", - "linkAccountEffect2": "Agregar inicio de sesión OIDC a la cuenta de destino", - "linkAccountEffect3": "Permitir tanto el inicio de sesión OIDC como el inicio de sesión con contraseña.", + "linkAccountDescLocal": "Vincular una cuenta local {{username}} con una cuenta OIDC-only existente.", + "linkAccountWarningTitle": "Esto hará lo siguiente:", + "linkAccountEffect1": "Eliminar la cuenta OIDC-only", + "linkAccountEffect2": "Añadir inicio de sesión OIDC a la cuenta de destino", + "linkAccountEffect3": "Permitir tanto el inicio de sesión OIDC como por contraseña", "linkAccountTargetUsername": "Nombre de usuario de la cuenta local", - "linkAccountTargetPlaceholder": "Ingrese el nombre de usuario de la cuenta local para vincularla", + "linkAccountTargetPlaceholder": "Introduzca el nombre de usuario de la cuenta local a vincular", "linkAccountOidcUsername": "Nombre de usuario de la cuenta OIDC", - "linkAccountOidcPlaceholder": "Ingrese el nombre de usuario de la cuenta solo de OIDC para fusionar en", - "linkAccountOidcNotFound": "No se encontró ninguna cuenta exclusiva de OIDC con ese nombre de usuario.", + "linkAccountOidcPlaceholder": "Introduzca el nombre de usuario de la cuenta OIDC-only a fusionar", + "linkAccountOidcNotFound": "No se encontró ninguna cuenta OIDC-only con ese nombre de usuario", "linkAccounts": "Vincular cuentas", "linkAccountSuccess": "Cuentas vinculadas correctamente", - "linkAccountFailed": "No se pudieron vincular las cuentas.", - "linkAccountInProgress": "Enlace...", + "linkAccountFailed": "Error al vincular cuentas", + "linkAccountInProgress": "Vinculando...", "unlinkAccountTitle": "Desvincular OIDC", - "unlinkAccountDesc": "Eliminar la autenticación OIDC de {{username}}. Solo podrán iniciar sesión con su contraseña.", - "unlinkAccountWarning": "Esto eliminará el inicio de sesión de OIDC de esta cuenta. El usuario debe tener una contraseña configurada para continuar iniciando sesión.", + "unlinkAccountDesc": "Eliminar la autenticación OIDC de {{username}}. Solo podrá iniciar sesión con su contraseña.", + "unlinkAccountWarning": "Esto eliminará el inicio de sesión OIDC de esta cuenta. El usuario debe tener una contraseña establecida para poder seguir iniciando sesión.", "unlinkAccount": "Desvincular OIDC", "unlinkAccountInProgress": "Desvinculando...", - "unlinkAccountSuccess": "OIDC se desvinculó correctamente", - "unlinkAccountFailed": "No se pudo desvincular OIDC.", - "saving": "Ahorro...", - "updateRegistrationFailed": "No se pudo actualizar la configuración de registro.", - "updatePasswordLoginFailed": "No se pudo actualizar la configuración de inicio de sesión de contraseña.", - "cannotDisablePasswordLoginWithTotp": "No se puede deshabilitar el inicio de sesión con contraseña mientras la autenticación de dos factores (2FA) esté habilitada para uno o más usuarios. Desactive primero la 2FA.", - "updateOidcAutoProvisionFailed": "No se pudo actualizar la configuración de aprovisionamiento automático de OIDC.", - "updateOidcSilentLoginDefaultFailed": "Error al actualizar la configuración de inicio de sesión silencioso de OIDC.", - "updatePasswordResetFailed": "No se pudo actualizar la configuración de restablecimiento de contraseña.", - "sessionTimeoutRange2": "El tiempo de espera de la sesión debe estar entre 1 y 720 horas.", - "sessionTimeoutSaved": "Se ha guardado el tiempo de espera de la sesión.", - "sessionTimeoutSaveFailed": "No se pudo guardar el tiempo de espera de la sesión.", + "unlinkAccountSuccess": "OIDC desvinculado correctamente", + "unlinkAccountFailed": "Error al desvincular OIDC", + "saving": "Guardando...", + "updateRegistrationFailed": "Error al actualizar la configuración de registro", + "updatePasswordLoginFailed": "Error al actualizar la configuración de inicio de sesión por contraseña", + "cannotDisablePasswordLoginWithTotp": "No se puede deshabilitar el inicio de sesión por contraseña mientras la 2FA esté activada para uno o más usuarios. Desactive primero la 2FA.", + "updateOidcAutoProvisionFailed": "Error al actualizar la configuración de aprovisionamiento automático OIDC", + "updateOidcSilentLoginDefaultFailed": "Error al actualizar la configuración de inicio de sesión OIDC silencioso", + "updatePasswordResetFailed": "Error al actualizar la configuración de restablecimiento de contraseña", + "sessionTimeoutRange2": "El tiempo de espera de sesión debe estar entre 1 y 720 horas", + "sessionTimeoutSaved": "Timeout de sesión guardado", + "sessionTimeoutSaveFailed": "Error al guardar el timeout de sesión", "monitoringIntervalInvalid": "Valores de intervalo no válidos", - "monitoringSaved": "Configuración de monitorización guardada", - "monitoringSaveFailed": "No se pudieron guardar los ajustes de monitorización.", + "monitoringSaved": "Ajustes de monitorización guardados", + "monitoringSaveFailed": "Error al guardar los ajustes de monitorización", "metricsHistoryRetention": "Retención del historial de métricas", "metricsHistoryRetentionRange": "De 1 a 90 días", "days": "días", - "guacamoleSaved": "Configuración de guacamole guardada", - "guacamoleSaveFailed": "No se pudieron guardar los ajustes de Guacamole.", - "guacamoleUpdateFailed": "No se pudo actualizar la configuración de Guacamole.", - "tailscaleSettingsSaved": "Se han guardado los ajustes de escala de cola.", - "tailscaleSettingsSaveFailed": "No se pudieron guardar los ajustes de Tailscale.", - "logLevelUpdateFailed": "No se pudo actualizar el nivel de registro.", + "guacamoleSaved": "Ajustes de Guacamole guardados", + "guacamoleSaveFailed": "Error al guardar los ajustes de Guacamole", + "guacamoleUpdateFailed": "Error al actualizar el ajuste de Guacamole", + "tailscaleSettingsSaved": "Ajustes de Tailscale guardados", + "tailscaleSettingsSaveFailed": "Error al guardar los ajustes de Tailscale", + "logLevelUpdateFailed": "Error al actualizar el nivel de registro", "oidcSaved": "Configuración OIDC guardada", - "oidcSaveFailed": "No se pudo guardar la configuración de OIDC.", - "oidcRemoved": "Se eliminó la configuración de OIDC", - "oidcRemoveFailed": "No se pudo eliminar la configuración de OIDC.", - "createUserRequired": "Se requiere nombre de usuario y contraseña.", - "createUserPasswordTooShort": "La contraseña debe tener al menos 6 caracteres.", + "oidcSaveFailed": "Error al guardar la configuración OIDC", + "oidcRemoved": "Configuración OIDC eliminada", + "oidcRemoveFailed": "Error al eliminar la configuración OIDC", + "createUserRequired": "El nombre de usuario y la contraseña son obligatorios", + "createUserPasswordTooShort": "La contraseña debe tener al menos 6 caracteres", "createUserSuccess": "Usuario \"{{username}}\" creado", "createUserFailed": "Error al crear el usuario", - "updateAdminStatusFailed": "No se pudo actualizar el estado del administrador.", + "updateAdminStatusFailed": "Error al actualizar el estado de administrador", "allSessionsRevoked": "Todas las sesiones revocadas", - "revokeSessionsFailed": "No se pudieron revocar las sesiones.", - "manageUserData": "Gestionar datos de usuario", - "backToUsers": "Volver a los usuarios", + "revokeSessionsFailed": "Error al revocar las sesiones", + "manageUserData": "Gestionar datos del usuario", + "backToUsers": "Volver a usuarios", "manageTabAccount": "Cuenta", - "manageTabHosts": "Anfitriones", - "manageTabCredentials": "Cartas credenciales", - "manageTabSnippets": "Fragmentos", + "manageTabHosts": "Hosts", + "manageTabCredentials": "Credenciales", + "manageTabSnippets": "Snippets", "manageTabSessions": "Sesiones", "manageTabDanger": "Peligro", "manageEditorBack": "Volver a los datos de {{username}}", "dataLockedBadge": "BLOQUEADO", - "dataLockedNotice": "Los datos de {{username}}permanecen bloqueados hasta que inicien sesión nuevamente. Sus hosts, credenciales y fragmentos no se pueden ver ni editar hasta entonces.", + "dataLockedNotice": "Los datos de {{username}} permanecen bloqueados hasta que inicien sesión de nuevo. Sus hosts, credenciales y fragmentos no se pueden ver ni editar hasta entonces.", "resetPasswordTitle": "Restablecer contraseña", "resetPasswordOidcOnly": "Este usuario inicia sesión a través de un proveedor externo y no tiene contraseña.", "resetPasswordPlaceholder": "Nueva contraseña", - "resetPasswordBtn": "Reiniciar", - "resetPasswordWorking": "Reiniciando...", - "resetPasswordSuccess": "Restablecimiento de contraseña", - "resetPasswordSuccessWiped": "Se ha restablecido la contraseña. Se han borrado los datos cifrados del usuario.", - "resetPasswordFailed": "No se pudo restablecer la contraseña.", - "resetPasswordConfirmWipe": "{{username}} no ha iniciado sesión desde la actualización del cifrado, por lo que sus datos no se pueden recuperar. Reiniciar ahora eliminará sus hosts, credenciales y fragmentos. ¿Continuar?", + "resetPasswordBtn": "Restablecer", + "resetPasswordWorking": "Restableciendo...", + "resetPasswordSuccess": "Contraseña restablecida", + "resetPasswordSuccessWiped": "Contraseña restablecida. Los datos cifrados del usuario fueron eliminados.", + "resetPasswordFailed": "Error al restablecer la contraseña", + "resetPasswordConfirmWipe": "{{username}} no ha iniciado sesión desde la actualización del cifrado, por lo que sus datos no se pueden recuperar. Restablecer ahora eliminará sus hosts, credenciales y fragmentos. ¿Continuar?", "totpSectionTitle": "Autenticación de dos factores", - "totpStatusEnabled": "TOTP está habilitado para este usuario.", - "totpStatusDisabled": "TOTP no está habilitado para este usuario.", - "disableTotp": "Desactivar", - "disableTotpConfirm": "Deshabilitar la autenticación de dos factores para {{username}}? Podrán iniciar sesión solo con su contraseña.", + "totpStatusEnabled": "TOTP está habilitado para este usuario", + "totpStatusDisabled": "TOTP no está habilitado para este usuario", + "disableTotp": "Deshabilitar", + "disableTotpConfirm": "¿Deshabilitar la autenticación de dos factores para {{username}}? Podrá iniciar sesión solo con su contraseña.", "totpDisabledSuccess": "TOTP deshabilitado", - "totpDisableFailed": "No se pudo desactivar TOTP.", + "totpDisableFailed": "Error al deshabilitar TOTP", "manageApiKeys": "Claves API", "apiKeyNamePlaceholder": "Nombre de la clave", - "apiKeyCopyNotice": "Copia esta clave ahora, no volverá a aparecer.", - "noApiKeysForUser": "Sin claves API", + "apiKeyCopyNotice": "Copie esta clave ahora, no se mostrará de nuevo.", + "noApiKeysForUser": "No hay claves API", "apiKeyDeleteFailed": "Error al eliminar la clave API", - "exportUserData": "Exportación de datos", - "exportUserDataDesc": "Descarga los hosts, las credenciales y los datos del administrador de archivos de este usuario en formato JSON. Los secretos están descifrados.", + "exportUserData": "Exportar datos", + "exportUserDataDesc": "Descargar hosts, credenciales y datos del gestor de archivos de este usuario como JSON. Los secretos se descifran.", "exportUserDataSuccess": "Datos de usuario exportados", - "exportUserDataFailed": "Error al exportar los datos del usuario.", - "hostsCount": "{{count}} anfitriones", - "addHostForUser": "Agregar host", - "noHostsForUser": "Este usuario no tiene hosts.", + "exportUserDataFailed": "Error al exportar los datos del usuario", + "hostsCount": "{{count}} hosts", + "addHostForUser": "Añadir host", + "noHostsForUser": "Este usuario no tiene hosts", "connectToHost": "Conectar", "deleteHostConfirm": "¿Eliminar el host \"{{name}}\" perteneciente a {{username}}?", "hostDeletedSuccess": "Host eliminado", - "hostDeleteFailed": "No se pudo eliminar el host", + "hostDeleteFailed": "Error al eliminar el host", "credentialsCount": "{{count}} credenciales", - "addCredentialForUser": "Agregar credencial", - "noCredentialsForUser": "Este usuario no tiene credenciales.", - "deleteCredentialConfirm": "¿Eliminar la credencial \"{{name}}\" perteneciente a {{username}}?", + "addCredentialForUser": "Añadir credencial", + "noCredentialsForUser": "Este usuario no tiene credenciales", + "deleteCredentialConfirm": "¿Eliminar credencial \"{{name}}\" de {{username}}?", "credentialDeletedSuccess": "Credencial eliminada", - "credentialDeleteFailed": "Error al eliminar las credenciales", + "credentialDeleteFailed": "Error al eliminar credencial", "snippetsCount": "{{count}} fragmentos", - "addSnippetForUser": "Agregar fragmento", + "addSnippetForUser": "Añadir fragmento", "noSnippetsForUser": "Este usuario no tiene fragmentos", - "snippetRequiredFields": "Se requiere el nombre y el contenido del fragmento.", + "snippetRequiredFields": "El nombre y el contenido del fragmento son obligatorios", "snippetNamePlaceholder": "Nombre del fragmento", "snippetContentPlaceholder": "Contenido del comando", "snippetFolderPlaceholder": "Carpeta (opcional)", "snippetSaved": "Fragmento guardado", - "snippetSaveFailed": "No se pudo guardar el fragmento.", - "deleteSnippetConfirm": "¿Eliminar el fragmento \"{{name}}\" perteneciente a {{username}}?", + "snippetSaveFailed": "Error al guardar fragmento", + "deleteSnippetConfirm": "¿Eliminar fragmento \"{{name}}\" de {{username}}?", "snippetDeletedSuccess": "Fragmento eliminado", - "snippetDeleteFailed": "No se pudo eliminar el fragmento.", + "snippetDeleteFailed": "Error al eliminar fragmento", "noSessionsForUser": "No hay sesiones activas", - "deleteUserDangerDesc": "Eliminar permanentemente {{username}} y todos sus datos (hosts, credenciales, fragmentos, historial). Esta acción es irreversible.", - "deleteUserConfirm": "¿Eliminar permanentemente {{username}} y todos sus datos?", - "deleteUserAdminBlocked": "Elimine el estado de administrador antes de borrar a este usuario.", - "createRoleRequired": "Se requiere nombre y nombre para mostrar.", + "deleteUserDangerDesc": "Eliminar permanentemente a {{username}} y todos sus datos (equipos, credenciales, fragmentos, historial). Esta acción no se puede deshacer.", + "deleteUserConfirm": "¿Eliminar permanentemente a {{username}} y todos sus datos?", + "deleteUserAdminBlocked": "Elimine el estado de administrador antes de eliminar este usuario.", + "createRoleRequired": "El nombre y el nombre para mostrar son obligatorios", "createRoleSuccess": "Rol \"{{name}}\" creado", - "createRoleFailed": "No se pudo crear el rol.", - "apiKeyNameRequired": "Se requiere el nombre de la clave.", - "apiKeyUserRequired": "Se requiere ID de usuario.", + "createRoleFailed": "Error al crear rol", + "apiKeyNameRequired": "El nombre de la clave es obligatorio", + "apiKeyUserRequired": "El ID de usuario es obligatorio", "apiKeyCreatedSuccess": "Clave API \"{{name}}\" creada", - "apiKeyCreateFailed": "Error al crear la clave API", - "exportSuccess": "La base de datos se exportó correctamente.", - "exportFailed": "Falló la exportación de la base de datos.", - "importSelectFile": "Por favor, seleccione primero un archivo.", - "importCompleted": "Importación completada: {{total}} artículos importados, {{skipped}} omitidos", - "importFailed": "Error al importar: {{error}}", - "importError": "Falló la importación de la base de datos.", - "sectionHostDefaults": "Valores predeterminados del host", - "hostDefaultsDesc": "La configuración se aplica automáticamente al crear un nuevo host. Los hosts individuales pueden modificarla.", + "apiKeyCreateFailed": "Error al crear clave API", + "exportSuccess": "Base de datos exportada correctamente", + "exportFailed": "Error al exportar la base de datos", + "importSelectFile": "Por favor, seleccione un archivo primero", + "importCompleted": "Importación completada: {{total}} elementos importados, {{skipped}} omitidos", + "importFailed": "Error de importación: {{error}}", + "importError": "Error al importar la base de datos", + "sectionHostDefaults": "Valores predeterminados de equipos", + "hostDefaultsDesc": "Ajustes aplicados automáticamente al crear un nuevo equipo. Los equipos individuales pueden anular estos valores.", "hostDefaultsSocks5": "Proxy SOCKS5", "hostDefaultsUseSocks5": "Habilitar proxy SOCKS5", - "hostDefaultsUseSocks5Desc": "Configurar previamente el proxy SOCKS5 en todos los nuevos hosts.", - "hostDefaultsSocks5Host": "Host/Puerto proxy", - "hostDefaultsSocks5Username": "Nombre de usuario proxy", - "hostDefaultsSocks5Password": "Contraseña de proxy", - "hostDefaultsMetrics": "Métricas del anfitrión", + "hostDefaultsUseSocks5Desc": "Prellenar el proxy SOCKS5 en todos los hosts nuevos", + "hostDefaultsSocks5Host": "Host / Puerto del proxy", + "hostDefaultsSocks5Username": "Nombre de usuario del proxy", + "hostDefaultsSocks5Password": "Contraseña del proxy", + "hostDefaultsMetrics": "Métricas del host", "hostDefaultsMetricsEnabled": "Habilitar métricas", - "hostDefaultsMetricsEnabledDesc": "Recopilar estadísticas de CPU, memoria y otras en los nuevos hosts de forma predeterminada", - "hostDefaultsStatusCheckEnabled": "Habilitar comprobación de estado", - "hostDefaultsStatusCheckEnabledDesc": "Consultar el estado en línea/fuera de línea en los nuevos hosts por defecto", + "hostDefaultsMetricsEnabledDesc": "Recopilar CPU, memoria y otras estadísticas en hosts nuevos de forma predeterminada", + "hostDefaultsStatusCheckEnabled": "Habilitar verificación de estado", + "hostDefaultsStatusCheckEnabledDesc": "Consultar estado en línea/fuera de línea en hosts nuevos de forma predeterminada", "hostDefaultsTerminal": "Terminal", - "hostDefaultsSessionLogging": "Registro de sesiones", - "hostDefaultsSessionLoggingDesc": "Registrar las sesiones de terminal en los nuevos hosts de forma predeterminada", + "hostDefaultsSessionLogging": "Registro de sesión", + "hostDefaultsSessionLoggingDesc": "Grabar sesiones de terminal en hosts nuevos de forma predeterminada", "hostDefaultsCommandHistory": "Historial de comandos", - "hostDefaultsCommandHistoryDesc": "Registrar el historial de comandos en los nuevos hosts de forma predeterminada", - "hostDefaultsSaved": "Configuración predeterminada del host guardada", - "hostDefaultsSaveFailed": "No se pudieron guardar los valores predeterminados del host.", + "hostDefaultsCommandHistoryDesc": "Registrar historial de comandos en hosts nuevos de forma predeterminada", + "hostDefaultsSaved": "Configuración predeterminada de host guardada", + "hostDefaultsSaveFailed": "Error al guardar la configuración predeterminada de host", "rolePermissions": { "count": "{{count}} permisos", - "editAction": "Modificar permisos", - "loadError": "No se pudo cargar el catálogo de permisos.", - "saved": "Permisos de rol guardados", - "saveError": "No se pudieron guardar los permisos de rol.", - "save": "Ahorrar", - "saving": "Ahorro..." + "editAction": "Editar permisos", + "loadError": "Error al cargar el catálogo de permisos", + "saved": "Permisos del rol guardados", + "saveError": "Error al guardar los permisos del rol", + "save": "Guardar", + "saving": "Guardando..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Anfitrión", + "hostLabel": "Host", "hostPlaceholder": "192.168.1.1 o example.com", "portLabel": "Puerto", "portPlaceholder": "22", @@ -2966,193 +3117,235 @@ "passwordLabel": "Contraseña", "passwordPlaceholder": "contraseña", "privateKeyLabel": "Clave privada", - "privateKeyPlaceholder": "Pegue la clave privada...", + "privateKeyPlaceholder": "Pegar clave privada...", "credentialLabel": "Credencial", - "credentialPlaceholder": "Seleccione una credencial guardada", - "connectToTerminal": "Conéctese al terminal", - "connectToFiles": "Conectar a archivos" + "credentialPlaceholder": "Seleccionar credencial guardada", + "connectToTerminal": "Conectar a Terminal", + "connectToFiles": "Conectar a Archivos" }, "history": { - "noTerminalSelected": "No se ha seleccionado ningún terminal.", - "noTerminalSelectedHint": "Abra una pestaña de terminal SSH para ver su historial de comandos.", - "searchPlaceholder": "Historial de búsqueda...", + "noTerminalSelected": "Ningún terminal seleccionado", + "noTerminalSelectedHint": "Abra una pestaña de terminal SSH para ver su historial de comandos", + "searchPlaceholder": "Buscar en el historial...", "clearAll": "Borrar todo", - "noHistoryEntries": "No hay entradas de historial", - "trackingDisabled": "El seguimiento del historial está desactivado.", - "trackingDisabledHint": "Habilítalo en la configuración del terminal del host." + "noHistoryEntries": "Sin entradas de historial", + "trackingDisabled": "El seguimiento del historial está desactivado", + "trackingDisabledHint": "Actívelo en la configuración del terminal del host." }, "sshTools": { - "keyRecordingTitle": "Grabación de claves", + "keyRecordingTitle": "Grabación de teclas", "recordToTerminals": "Grabar en terminales", - "selectAll": "Todo", + "selectAll": "Todos", "selectNone": "Ninguno", - "noTerminalTabsOpen": "No hay pestañas de terminal abiertas", - "selectTerminalsAbove": "Seleccione los terminales de arriba", - "broadcastInputPlaceholder": "Escribe aquí para transmitir las pulsaciones de teclas...", - "fillPassword": "Introduzca la contraseña", - "fillPasswordSuccess": "Contraseña introducida en la(s) terminal(es) {{count}}", - "fillPasswordMissing": "No se ha guardado ninguna contraseña para {{count}} terminal(es) seleccionada(s)", - "stopRecording": "Detener la grabación", + "noTerminalTabsOpen": "Sin pestañas de terminal abiertas", + "selectTerminalsAbove": "Seleccionar terminales arriba", + "broadcastInputPlaceholder": "Escriba aquí para transmitir pulsaciones...", + "fillPassword": "Rellenar contraseña", + "fillPasswordSuccess": "Contraseña rellenada en {{count}} terminal(es)", + "fillPasswordMissing": "No hay contraseña guardada para {{count}} terminal(es) seleccionado(s)", + "stopRecording": "Detener grabación", "startRecording": "Iniciar grabación", - "settingsTitle": "Ajustes", - "enableRightClickCopyPaste": "Habilitar copiar/pegar con el botón derecho del ratón" + "settingsTitle": "Configuración", + "enableRightClickCopyPaste": "Activar copiar/pegar con clic derecho" }, "splitScreen": { - "layoutTitle": "Disposición", + "layoutTitle": "Diseño", "selectLayoutAbove": "Seleccione un diseño arriba", - "selectLayoutHint": "Elige cuántos paneles mostrar", + "selectLayoutHint": "Elija 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": "Déjelo aquí", + "openTabsTitle": "Pestañas abiertas", + "dragTabsHint": "Arrastre pestañas a los paneles superiores o use Asignación rápida", + "dropHere": "Soltar aquí", "emptyPane": "Vacío", "dashboard": "Panel", - "clearSplitScreen": "Pantalla dividida transparente", + "clearSplitScreen": "Quitar 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", + "splitTab": "Dividir pestaña", + "addToSplit": "Agregar a división", + "removeFromSplit": "Eliminar de división", + "assignToPane": "Asignar a panel", "hotkeysTitle": "Atajos de teclado", - "hotkeysSplitRight": "Alternar división bidireccional", - "hotkeysSplitBelow": "Alternar división en 3 vías", - "hotkeysNavigatePane": "Navegar por los paneles", + "hotkeysSplitRight": "Alternar división doble", + "hotkeysSplitBelow": "Alternar división triple", + "hotkeysNavigatePane": "Navegar entre paneles", "hotkeysNextTab": "Siguiente pestaña", "hotkeysPrevTab": "Pestaña anterior" }, "snippets": { "title": "Fragmentos", "createSnippetTitle": "Crear fragmento", - "createSnippetDescription": "Crea un nuevo fragmento de comando para una ejecución rápida.", + "createSnippetDescription": "Crear un nuevo fragmento de comando para ejecución rápida", "nameLabel": "Nombre", - "namePlaceholder": "Por ejemplo, reiniciar Nginx", + "namePlaceholder": "p. ej., Reiniciar Nginx", "descriptionLabel": "Descripción", "descriptionPlaceholder": "Descripción opcional", "optional": "Opcional", "folderLabel": "Carpeta", - "noFolder": "Sin carpeta (Sin categorizar)", - "commandLabel": "Dominio", - "commandPlaceholder": "Por ejemplo, sudo systemctl restart nginx", + "noFolder": "Sin carpeta (Sin categoría)", + "commandLabel": "Comando", + "commandPlaceholder": "p. ej., sudo systemctl restart nginx", "cancel": "Cancelar", "createSnippetButton": "Crear fragmento", "createFolderTitle": "Crear carpeta", - "createFolderDescription": "Organiza tus fragmentos en carpetas.", + "createFolderDescription": "Organiza tus fragmentos en carpetas", "folderNameLabel": "Nombre de la carpeta", - "folderNamePlaceholder": "Por ejemplo, comandos del sistema, scripts de Docker.", + "folderNamePlaceholder": "p. ej., Comandos del sistema, Scripts Docker", "folderColorLabel": "Color de la carpeta", - "folderIconLabel": "Icono de carpeta", - "previewLabel": "Avance", + "folderIconLabel": "Icono de la carpeta", + "previewLabel": "Vista previa", "folderNameFallback": "Nombre de la carpeta", "createFolderButton": "Crear carpeta", "targetTerminals": "Terminales de destino", - "selectAll": "Todo", + "selectAll": "Todos", "selectNone": "Ninguno", "noTerminalTabsOpen": "No hay pestañas de terminal abiertas", - "searchPlaceholder": "Fragmentos de búsqueda...", + "searchPlaceholder": "Buscar fragmentos...", "newSnippet": "Nuevo fragmento", "newFolder": "Nueva carpeta", - "run": "Correr", + "run": "Ejecutar", "noSnippetsInFolder": "No hay fragmentos en esta carpeta", - "uncategorized": "Sin categoría", + "uncategorized": "Sin categorizar", "editSnippetTitle": "Editar fragmento", - "editSnippetDescription": "Actualiza este fragmento de comando.", + "editSnippetDescription": "Actualizar este fragmento de comando", "saveSnippetButton": "Guardar cambios", "createSuccess": "Fragmento creado correctamente", - "createFailed": "No se pudo crear el fragmento.", + "createFailed": "No se pudo crear el fragmento", "updateSuccess": "Fragmento actualizado correctamente", - "updateFailed": "Error al actualizar el fragmento", - "deleteFailed": "No se pudo eliminar el fragmento.", + "updateFailed": "No se pudo actualizar el fragmento", + "deleteFailed": "No se pudo eliminar el fragmento", "folderCreateSuccess": "Carpeta creada correctamente", - "folderCreateFailed": "Error al crear la carpeta", + "folderCreateFailed": "No se pudo crear la carpeta", "editFolderTitle": "Editar carpeta", - "editFolderDescription": "Cambiar el nombre o la apariencia de esta carpeta.", + "editFolderDescription": "Renombrar o cambiar la apariencia de esta carpeta", "saveFolderButton": "Guardar cambios", - "editFolder": "Carpeta de edición", + "editFolder": "Editar carpeta", "deleteFolder": "Eliminar carpeta", "folderDeleteSuccess": "Carpeta \"{{name}}\" eliminada", "folderDeleteFailed": "No se pudo eliminar la carpeta", - "folderEditSuccess": "La carpeta se actualizó correctamente.", - "folderEditFailed": "Error al actualizar la carpeta", - "confirmRunMessage": "Ejecutar \"{{name}}\"?", - "confirmRunButton": "Correr", + "folderEditSuccess": "Carpeta actualizada correctamente", + "folderEditFailed": "No se pudo actualizar la carpeta", + "confirmRunMessage": "¿Ejecutar \"{{name}}\"?", + "confirmRunButton": "Ejecutar", "runSuccess": "Se ejecutó \"{{name}}\" en {{count}} terminal(es)", - "copySuccess": "Se copió \"{{name}}\" al portapapeles", + "copySuccess": "\"{{name}}\" copiado al portapapeles", "shareTitle": "Compartir fragmento", "shareUser": "Usuario", - "shareRole": "Role", - "selectUser": "Seleccione un usuario...", - "selectRole": "Selecciona un rol...", - "shareSuccess": "Fragmento compartido con éxito", - "shareFailed": "No se pudo compartir el fragmento.", + "shareRole": "Rol", + "selectUser": "Seleccionar un usuario...", + "selectRole": "Seleccionar un rol...", + "shareSuccess": "Fragmento compartido correctamente", + "shareFailed": "Error al compartir el fragmento", "revokeSuccess": "Acceso revocado", - "revokeFailed": "No se pudo revocar el acceso.", + "revokeFailed": "Error al revocar el acceso", "currentAccess": "Acceso actual", - "shareLoadError": "No se pudo cargar los datos compartidos.", + "shareLoadError": "Error al cargar los datos de compartición", "loading": "Cargando...", - "close": "Cerca", - "reorderFailed": "No se pudo guardar el orden del fragmento.", - "importExport": "Importación / Exportación", + "close": "Cerrar", + "reorderFailed": "Error al guardar el orden de los fragmentos", + "importExport": "Importar / Exportar", "exportBtn": "Exportar JSON", "importBtn": "Importar JSON", "exportSuccess": "Fragmentos exportados correctamente", - "exportFailed": "Error al exportar los fragmentos.", + "exportFailed": "Error al exportar los fragmentos", "importTitle": "Importar fragmentos", - "importDescription": "Importa fragmentos y carpetas desde un archivo JSON exportado por Termix.", - "importDropOrClick": "Arrastre un archivo JSON aquí o haga clic para explorar.", + "importDescription": "Importar fragmentos y carpetas desde un archivo JSON exportado por Termix", + "importDropOrClick": "Suelta un archivo JSON aquí o haz clic para explorar", "importSelectedFile": "Seleccionado: {{name}}", - "importOverwrite": "Sobrescribir los fragmentos existentes con el mismo nombre y carpeta.", + "importOverwrite": "Sobrescribir fragmentos existentes con el mismo nombre y carpeta", "importStartBtn": "Importar", - "importSuccess": "Importación completada: {{snippets}} fragmento(s) añadido(s), {{updated}} actualizado(s), {{skipped}} omitido(s), {{folders}} carpeta(s) añadida(s)", + "importSuccess": "Importación completa: {{snippets}} fragmento(s) añadido(s), {{updated}} actualizado(s), {{skipped}} omitido(s), {{folders}} carpeta(s) añadida(s)", "importFailed": "Error al importar los fragmentos", - "importInvalidFile": "Archivo no válido: se esperaba un objeto JSON con fragmentos o matrices de carpetas.", - "targetHostsLabel": "Anfitriones objetivo", - "targetHostsHint": "Asigne hosts para ejecutar este fragmento directamente sin tener una terminal abierta.", + "importInvalidFile": "Archivo no válido: se esperaba un objeto JSON con arrays de fragmentos o carpetas", + "targetHostsLabel": "Hosts de destino", + "targetHostsHint": "Asignar hosts para ejecutar este fragmento directamente sin una terminal abierta.", "noHostsAvailable": "No hay hosts configurados", - "clearTargetHosts": "Borrar todo", - "hasTargetHosts": "Tiene hosts objetivo", - "runOnTargets": "Carrera por objetivos", + "clearTargetHosts": "Limpiar todo", + "hasTargetHosts": "Tiene hosts de destino", + "runOnTargets": "Ejecutar en destinos", "directRunSuccess": "Se ejecutó \"{{name}}\" en {{count}} host(s)", "directRunPartialFail": "\"{{name}}\" falló en uno o más hosts", - "executionResultTitle": "Resultados de la ejecución: {{name}}", - "executionResultDescription": "Resultado de la ejecución del fragmento de código en cada host de destino.", + "executionResultTitle": "Resultados de ejecución: {{name}}", + "executionResultDescription": "Salida de la ejecución del snippet en cada host de destino.", "executionSuccess": "Éxito", "executionFailed": "Fallido" }, + "keybindings": { + "title": "Atajos de teclado", + "description": "Personaliza los atajos de teclado para copiar, pegar y controlar funciones del terminal, o asigna teclas para enviar texto o ejecutar un fragmento de código.", + "defaultsHeading": "Atajos integrados", + "customHeading": "Atajos personalizados", + "addBinding": "Agregar acceso directo", + "addBindingTitle": "Agregar acceso directo", + "editBindingTitle": "Editar acceso directo", + "loading": "Cargando...", + "noCustomBindings": "Aún no hay accesos directos personalizados.", + "defaultBadge": "Por defecto", + "customizedBadge": "Personalizado", + "customize": "Personalizar", + "resetToDefault": "Restablecer a valores predeterminados", + "resetAllToDefaults": "Restablecer todo a los valores predeterminados", + "close": "Cerca", + "cancel": "Cancelar", + "saveBinding": "Guardar acceso directo", + "saveError": "No se pudieron guardar los atajos de teclado.", + "pressKeysToRecord": "Combinación de teclas", + "pressKeysPlaceholder": "Haz clic y pulsa las teclas", + "recording": "Pulsa las teclas...", + "comboRequiredError": "Primero, pulse una combinación de teclas.", + "textRequiredError": "Introduce el texto que deseas enviar", + "controlCodeRequiredError": "Introduzca una sola letra para el código de control.", + "snippetRequiredError": "Seleccione un fragmento", + "conflictWarning": "Esta combinación ya está vinculada a {{combo}}. Guardar hará que ambos se activen.", + "actionLabel": "Acción", + "actionCopy": "Copiar selección", + "actionPaste": "Pasta", + "actionSendControlCode": "Enviar señal Ctrl+letra", + "actionSendText": "Enviar mensaje de texto", + "actionRunSnippet": "Ejecutar el fragmento existente", + "controlCodeLabel": "Carta", + "textLabel": "Texto para enviar", + "appendEnterLabel": "Presione Enter después de enviar", + "snippetLabel": "Retazo", + "selectSnippetPlaceholder": "Seleccione un fragmento", + "orphanedSnippetWarning": "Fragmento no encontrado", + "clipboardPermissionNote": "En algunos navegadores, puede solicitar permiso para copiar y pegar el contenido del portapapeles la primera vez que se utilice." + }, "userProfile": { - "donateTitle": "Mantén Termix con vida", - "donateDescription": "Termix es gratuito, autoalojado y desarrollado por un pequeño grupo de personas en su tiempo libre. Si te ha ahorrado tiempo o dinero, una donación en criptomonedas ayuda a que siga funcionando.", - "donateMilestones": "Las donaciones ayudan a financiar el tiempo necesario para investigar y aprender lo que se requiere para desarrollar compatibilidad con SAML, Kubernetes y agentes. Sigue el progreso y dona.", - "donateButton": "Dona criptomonedas", + "donateTitle": "Mantener Termix vivo", + "donateDescription": "Termix es gratuito, autoalojado y construido por solo unas pocas personas en su tiempo libre. Si te ha ahorrado tiempo o dinero, una donación en criptomonedas ayuda a mantenerlo.", + "donateMilestones": "Las donaciones ayudan a financiar el tiempo para investigar y aprender lo necesario para crear soporte para SAML, Kubernetes y Agent. Consulta el progreso y dona.", + "donateButton": "Donar criptomonedas", "storageModeLocal": "Navegador", "storageModeCloud": "Base de datos", - "storageModeDescription": "El navegador guarda la configuración únicamente en este navegador. La base de datos se sincroniza con el servidor y se carga en cualquier dispositivo.", - "resetToDefaults": "Restablecer valores predeterminados", - "resetToDefaultsSuccess": "La configuración se ha restablecido a los valores predeterminados.", + "storageModeDescription": "El navegador guarda los ajustes solo en este navegador. La base de datos los sincroniza con el servidor y los carga en cualquier dispositivo.", + "resetToDefaults": "Restablecer predeterminados", + "resetToDefaultsSuccess": "Ajustes restablecidos a los valores predeterminados.", "storageModeSwitch": "Almacenamiento de preferencias", "sectionAccount": "Cuenta", "sectionAppearance": "Apariencia", "sectionSecurity": "Seguridad", "sectionApiKeys": "Claves API", + "sectionData": "Datos", "sectionC2sTunnels": "Túneles C2S", "usernameLabel": "Nombre de usuario", - "roleLabel": "Role", + "roleLabel": "Rol", "roleAdministrator": "Administrador", "authMethodLabel": "Método de autenticación", "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "En", - "twoFaOff": "Apagado", + "twoFaOn": "Activado", + "twoFaOff": "Desactivado", "versionLabel": "Versión", "betaProgramTitle": "Programa Beta", - "betaProgramDescription": "Prueba las próximas funciones antes que nadie con la etiqueta Docker :beta, que se actualiza semanalmente. Es inestable y no apta para producción.", - "betaProgramFeedback": "¿Has encontrado un error? Infórmanos aquí.", + "betaProgramDescription": "Prueba las próximas funciones con la etiqueta Docker :beta de lanzamiento semanal continuo. Inestable, no apto para producción.", + "betaProgramFeedback": "¿Encontraste un error? Infórmalo aquí.", "betaProgramLearnMore": "Más información", "deleteAccount": "Eliminar cuenta", "deleteAccountDescription": "Eliminar permanentemente tu cuenta", - "changeServerDescription": "Cambia a un servidor backend de Termix diferente.", - "deleteButton": "Borrar", - "deleteAccountPermanent": "Esta acción es permanente e irreversible.", + "changeServerDescription": "Cambiar a un servidor backend de Termix diferente", + "deleteButton": "Eliminar", + "deleteAccountPermanent": "Esta acción es permanente y no se puede deshacer.", "deleteAccountWarning": "Todas las sesiones, hosts, credenciales y configuraciones se eliminarán permanentemente.", "confirmPasswordDeletePlaceholder": "Introduce tu contraseña para confirmar", "languageLabel": "Idioma", @@ -3160,52 +3353,55 @@ "fontSizeLabel": "Tamaño de fuente", "accentColorLabel": "Color de acento", "settingsTerminal": "Terminal", - "commandAutocomplete": "Autocompletar comandos", - "commandAutocompleteDesc": "Mostrar autocompletar mientras se escribe", - "terminalLinkBehavior": "Enlace de terminal Haga clic en", - "terminalLinkBehaviorDesc": "Comportamiento predeterminado al hacer clic en enlaces en la terminal.", + "commandAutocomplete": "Autocompletado de comandos", + "commandAutocompleteDesc": "Mostrar autocompletado al escribir", + "keyboardShortcuts": "Atajos de teclado", + "keyboardShortcutsDescription": "Personaliza los accesos directos para copiar, pegar y usar comandos en la terminal.", + "manageShortcuts": "Administrar", + "terminalLinkBehavior": "Clic en enlace de terminal", + "terminalLinkBehaviorDesc": "Comportamiento predeterminado al hacer clic en enlaces del terminal", "historyTracking": "Seguimiento del historial", - "historyTrackingDesc": "Seguimiento de comandos de terminal", + "historyTrackingDesc": "Registrar comandos del terminal", "commandPalette": "Paleta de comandos", - "commandPaletteDesc": "Habilitar el acceso directo al teclado", + "commandPaletteDesc": "Habilitar 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.", + "reopenTabsOnLoginDesc": "Restaurar las pestañas abiertas al iniciar sesión o actualizar la página, incluso desde otro dispositivo", "confirmTabClose": "Confirmar cierre de pestaña", - "confirmTabCloseDesc": "Pregunta antes de cerrar las pestañas del terminal.", + "confirmTabCloseDesc": "Preguntar antes de cerrar pestañas del terminal", "settingsSidebar": "Barra lateral", - "showHostTags": "Etiquetas del presentador del programa", + "showHostTags": "Mostrar etiquetas de hosts", "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.", - "compactHostView": "Vista compacta del host", - "compactHostViewDesc": "Colapsa cada host a una sola línea que muestre solo su nombre y dirección.", - "statusColors": "Colores de estado real", - "statusColorsDesc": "Utilice el verde/rojo para indicar el estado en línea/fuera de línea en lugar del color de acento.", - "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Mantener siempre expandida la barra lateral izquierda en lugar de expandirla al pasar el cursor sobre ella.", - "expandAppRailOnHover": "Expandir la barra de la aplicación al pasar el cursor sobre ella.", - "expandAppRailOnHoverDesc": "Permitir que la barra lateral izquierda de la aplicación se expanda cuando el puntero se mueva sobre ella.", + "hostTrayOnClick": "Clic para expandir acciones del host", + "hostTrayOnClickDesc": "Mostrar siempre los botones de conexión; clic para expandir opciones de gestión en lugar de pasar el cursor", + "compactHostView": "Vista compacta de hosts", + "compactHostViewDesc": "Contraer cada host a una sola línea mostrando solo su nombre y dirección", + "statusColors": "Colores de estado reales", + "statusColorsDesc": "Usar verde/rojo para estado en línea/desconectado en lugar del color de acento", + "pinAppRail": "Fijar barra de aplicaciones", + "pinAppRailDesc": "Mantener la barra lateral izquierda de aplicaciones siempre expandida en lugar de expandirse al pasar el cursor", + "expandAppRailOnHover": "Expandir barra de aplicaciones al pasar el cursor", + "expandAppRailOnHoverDesc": "Permitir que la barra lateral izquierda de aplicaciones se expanda cuando el puntero se mueve sobre ella", "settingsNavigation": "Navegación", - "navigationTabsDesc": "Elige qué pestañas aparecen en la barra de la aplicación.", + "navigationTabsDesc": "Elige qué pestañas aparecen en la barra de navegación", "settingsSnippets": "Fragmentos", - "foldersCollapsed": "Carpetas colapsadas", + "foldersCollapsed": "Carpetas contraídas", "foldersCollapsedDesc": "Contraer carpetas por defecto", "confirmExecution": "Confirmar ejecución", - "confirmExecutionDesc": "Confirma antes de ejecutar los fragmentos de código.", + "confirmExecutionDesc": "Confirmar antes de ejecutar fragmentos", "settingsUpdates": "Actualizaciones", - "disableUpdateChecks": "Deshabilitar las comprobaciones de actualización", - "disableUpdateChecksDesc": "Deja de comprobar si hay actualizaciones", + "disableUpdateChecks": "Desactivar comprobación de actualizaciones", + "disableUpdateChecksDesc": "Dejar de buscar actualizaciones", "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "La autenticación de dos factores (2FA) está habilitada.", - "totpDisabled": "Añada seguridad adicional al inicio de sesión.", + "totpEnabled": "2FA activada", + "totpDisabled": "Añadir seguridad adicional al inicio de sesión", "disable": "Desactivar", - "enable": "Permitir", + "enable": "Activar", "setupTotp": "Configurar TOTP", "qrCode": "Código QR", - "totpInstructions": "Escanea el código QR o introduce la clave secreta en tu aplicación de autenticación y, a continuación, introduce el código de 6 dígitos.", + "totpInstructions": "Escanea el código QR o introduce el secreto en tu aplicación de autenticación y luego introduce el código de 6 dígitos", "totpCodePlaceholder": "000000", "verify": "Verificar", - "changePassword": "Cambiar la contraseña", + "changePassword": "Cambiar contraseña", "currentPasswordLabel": "Contraseña actual", "currentPasswordPlaceholder": "Contraseña actual", "newPasswordLabel": "Nueva contraseña", @@ -3214,76 +3410,92 @@ "confirmPasswordPlaceholder": "Confirmar nueva contraseña", "updatePassword": "Actualizar contraseña", "createApiKeyTitle": "Crear clave API", - "createApiKeyDescription": "Genera una nueva clave API para acceso programático.", + "createApiKeyDescription": "Generar una nueva clave API para acceso programático.", "apiKeyNameLabel": "Nombre", - "apiKeyNamePlaceholder": "Por ejemplo, canalización de CI", + "apiKeyNamePlaceholder": "p. ej., CI Pipeline", "expiryDateLabel": "Fecha de caducidad", "optional": "opcional", "cancel": "Cancelar", "createKey": "Crear clave", - "apiKeyCount": "{{count}} teclas", + "apiKeyCount": "{{count}} claves", "newKey": "Nueva clave", "noApiKeys": "Aún no hay claves API.", - "apiKeyActive": "Activo", - "apiKeyUsageHint": "Incluya su llave en el", - "apiKeyUsageHintHeader": "encabezamiento.", - "apiKeyPermissionsHint": "Las claves heredan los permisos del usuario que las creó.", + "apiKeyActive": "Activa", + "apiKeyUsageHint": "Incluye tu clave en el", + "apiKeyUsageHintHeader": "cabecera.", + "apiKeyPermissionsHint": "Las claves heredan los permisos del usuario que las crea.", + "exportData": "Exportar mis datos", + "exportDataDesc": "Descarga una copia de seguridad de tus hosts, credenciales y configuraciones para transferirla a otro dispositivo.", + "export": "Exportar", + "exporting": "Exportador...", + "importData": "Importar mis datos", + "importDataDesc": "Restaure sus hosts, credenciales y configuraciones desde un archivo de copia de seguridad .sqlite.", + "importDataSelected": "Seleccionado: {{name}}", + "selectFile": "Seleccionar archivo", + "changeFile": "Cambiar", + "import": "Importar", + "importing": "Importador...", + "exportSuccess": "Los datos se exportaron correctamente.", + "exportFailed": "Falló la exportación de datos.", + "importSelectFile": "Por favor, seleccione primero un archivo.", + "importCompleted": "Importación completada: {{total}} artículos importados, {{skipped}} omitidos", + "importFailed": "Error al importar: {{error}}", "roleUser": "Usuario", "authMethodDual": "Autenticación dual", "authMethodOidc": "OIDC", - "totpSetupFailed": "No se pudo iniciar la configuración de TOTP.", - "totpEnter6Digits": "Introduzca un código de 6 dígitos.", - "totpEnabledSuccess": "Autenticación de dos factores habilitada", - "totpInvalidCode": "Código no válido, inténtelo de nuevo.", - "totpDisableInputRequired": "Ingrese su código TOTP o contraseña.", + "totpSetupFailed": "No se pudo iniciar la configuración de TOTP", + "totpEnter6Digits": "Introduce un código de 6 dígitos", + "totpEnabledSuccess": "Autenticación de dos factores activada", + "totpInvalidCode": "Código no válido, inténtalo de nuevo", + "totpDisableInputRequired": "Introduce tu código TOTP o contraseña", "totpDisabledSuccess": "Autenticación de dos factores desactivada", - "totpDisableFailed": "No se pudo desactivar la autenticación de dos factores (2FA).", - "totpDisableTitle": "Desactivar la autenticación de dos factores (2FA)", - "totpDisablePlaceholder": "Introduzca el código TOTP o la contraseña.", - "totpDisableConfirm": "Desactivar la autenticación de dos factores (2FA)", - "totpContinueVerify": "Continuar verificando", - "totpVerifyTitle": "Código de verificación", + "totpDisableFailed": "No se pudo desactivar 2FA", + "totpDisableTitle": "Desactivar 2FA", + "totpDisablePlaceholder": "Introduce código TOTP o contraseña", + "totpDisableConfirm": "Desactivar 2FA", + "totpContinueVerify": "Continuar para verificar", + "totpVerifyTitle": "Verificar código", "totpBackupTitle": "Códigos de respaldo", "totpDownloadBackup": "Descargar códigos de respaldo", - "passkeys": "Contraseñas", - "passkeysDesc": "Utilice las credenciales WebAuthn/FIDO2 para iniciar sesión sin contraseña.", - "passkeyName": "nombre de la clave de acceso", - "passkeyUvPreferred": "Privilegiado", - "passkeyUvRequired": "Requerido", - "passkeyUvDiscouraged": "Desanimado", - "addPasskey": "Agregar clave de acceso", - "noPasskeys": "No hay contraseñas registradas.", - "passkeyAdded": "Clave de acceso añadida", - "passkeyAddFailed": "No se pudo agregar la clave de acceso.", - "passkeyDeleted": "Clave de acceso eliminada", + "passkeys": "Passkeys", + "passkeysDesc": "Usa credenciales WebAuthn/FIDO2 para iniciar sesión sin contraseña", + "passkeyName": "Nombre de la passkey", + "passkeyUvPreferred": "Preferida", + "passkeyUvRequired": "Requerida", + "passkeyUvDiscouraged": "Desaconsejada", + "addPasskey": "Añadir passkey", + "noPasskeys": "No hay passkeys registradas.", + "passkeyAdded": "Passkey añadida", + "passkeyAddFailed": "No se pudo añadir la passkey", + "passkeyDeleted": "Passkey eliminada", "passkeyDeleteFailed": "Error al eliminar la clave de acceso", - "done": "Hecho", + "done": "Listo", "secretCopied": "Secreto copiado al portapapeles", - "apiKeyNameRequired": "Se requiere el nombre de la clave.", + "apiKeyNameRequired": "El nombre de la clave es obligatorio", "apiKeyCreated": "Clave API \"{{name}}\" creada", "apiKeyCreateFailed": "Error al crear la clave API", "apiKeyUser": "Usuario", - "apiKeyExpires": "Caduca", + "apiKeyExpires": "Expira", "apiKeyRevoked": "Clave API \"{{name}}\" revocada", - "apiKeyRevokeFailed": "No se pudo revocar la clave API.", - "passwordFieldsRequired": "Se requieren contraseñas nuevas y actuales.", + "apiKeyRevokeFailed": "Error al revocar la clave API", + "passwordFieldsRequired": "Se requieren las contraseñas actual y nueva", "passwordMismatch": "Las contraseñas no coinciden", - "passwordTooShort": "La contraseña debe tener al menos 6 caracteres.", + "passwordTooShort": "La contraseña debe tener al menos 6 caracteres", "passwordUpdated": "Contraseña actualizada correctamente", - "passwordUpdateFailed": "No se pudo actualizar la contraseña", - "deletePasswordRequired": "Se requiere contraseña para eliminar su cuenta.", - "deleteFailed": "No se pudo eliminar la cuenta.", + "passwordUpdateFailed": "Error al actualizar la contraseña", + "deletePasswordRequired": "Se requiere la contraseña para eliminar la cuenta", + "deleteFailed": "Error al eliminar la cuenta", "deleting": "Eliminando...", "colorPickerTooltip": "Abrir selector de color", "themeSystem": "Sistema", - "themeLight": "Luz", + "themeLight": "Claro", "themeDark": "Oscuro", - "themeDracula": "Drácula", - "themeCatppuccin": "Catpuccin", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizado", - "themeTokyoNight": "Noche de Tokio", - "themeOneDark": "Una noche", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } @@ -3291,182 +3503,183 @@ "tmuxMonitor": { "title": "Monitor Tmux", "failedToLoadHosts": "Error al cargar los hosts", - "failedToLoad": "No se pudieron cargar las sesiones de tmux.", - "tmuxUnavailable": "tmux no está instalado en este host.", - "noSessions": "No hay sesiones de tmux en este host.", - "noHostSelected": "No se ha seleccionado ningún host.", - "attached": "Adjunto", - "detached": "Separado", - "attach": "Adjuntar", + "failedToLoad": "Error al cargar las sesiones de tmux", + "tmuxUnavailable": "tmux no está instalado en este host", + "noSessions": "No hay sesiones de tmux en este host", + "noHostSelected": "Ningún host seleccionado", + "attached": "Conectado", + "detached": "Desconectado", + "attach": "Conectar", "editTags": "Editar etiquetas", - "tagsHint": "Etiquetas separadas por comas (por ejemplo, YOLO, lab, training)", + "tagsHint": "Etiquetas separadas por comas (ej. YOLO, lab, training)", "tagsSaved": "Etiquetas guardadas", - "tagsSaveFailed": "No se pudieron guardar las etiquetas", - "searchPlaceholder": "Resultados de búsqueda en todas las sesiones...", + "tagsSaveFailed": "Error al guardar etiquetas", + "searchPlaceholder": "Buscar en la salida de todas las sesiones...", "searchResults": "{{count}} coincidencias", - "searchFailed": "La búsqueda falló.", - "selectPaneHint": "Seleccione un panel para previsualizar su resultado.", - "closePreview": "Cerrar vista previa", + "searchFailed": "Búsqueda fallida", + "selectPaneHint": "Seleccione un panel para previsualizar su salida", + "closePreview": "Cerrar previsualización", "newSession": "Nueva sesión", "newSessionHint": "Nombre de la sesión (letras, dígitos, _ @ % + = -)", - "newSessionPlaceholder": "mi-sesión", + "newSessionPlaceholder": "my-session", "create": "Crear", "sessionCreated": "Sesión \"{{name}}\" creada", - "sessionCreateFailed": "No se pudo crear la sesión.", + "sessionCreateFailed": "Error al crear la sesión", "splitRight": "Dividir a la derecha", - "splitDown": "Dividido", - "splitFailed": "No se pudo dividir el panel.", + "splitDown": "Dividir abajo", + "splitFailed": "Error al dividir el panel", "newWindow": "Nueva ventana", "windowCreateFailed": "Error al crear la ventana", - "attachSessionTooltip": "Adjuntar a {{session}}", - "refresh": "Refrescar", - "refreshFailed": "No se pudieron actualizar las sesiones.", + "attachSessionTooltip": "Conectar a {{session}}", + "refresh": "Actualizar", + "refreshFailed": "Error al actualizar las sesiones", "collapseAll": "Colapsar todo", "expandAll": "Expandir todo", "moreActions": "Más acciones", "sessionStats": "Estadísticas de la sesión", - "renameSessionTitle": "Cambiar nombre de la sesión \"{{name}}\"", - "rename": "Rebautizar", + "renameSessionTitle": "Renombrar sesión \"{{name}}\"", + "rename": "Renombrar", "sessionRenamed": "Sesión renombrada a \"{{name}}\"", - "sessionRenameFailed": "No se pudo cambiar el nombre de la sesión.", + "sessionRenameFailed": "Error al renombrar la sesión", "editTagsTitle": "Editar etiquetas para \"{{name}}\"", - "killPane": "panel de matar", - "resizeTree": "Arrastra para cambiar el tamaño; haz doble clic para restablecer.", - "reattach": "Volver a adjuntar (corrige una vista distorsionada)", - "killWindow": "Ventana de eliminación", - "killWindowTitle": "¿Ventana de eliminación {{index}} de \"{{session}}\"?", - "killWindowBody": "Todos los paneles y procesos de esta ventana se cerrarán. Al cerrar la última ventana, se finalizará la sesión.", - "windowKillFailed": "Error al cerrar la ventana", + "killPane": "Finalizar panel", + "resizeTree": "Arrastrar para redimensionar — doble clic para restablecer", + "reattach": "Reconectar (soluciona una vista distorsionada)", + "killWindow": "Finalizar ventana", + "killWindowTitle": "¿Finalizar ventana {{index}} de \"{{session}}\"?", + "killWindowBody": "Todos los paneles y procesos de esta ventana serán terminados. Finalizar la última ventana finaliza la sesión.", + "windowKillFailed": "Error al finalizar la ventana", "statusActivity": "Actividad", - "statusWindows": "Windows", + "statusWindows": "Ventanas", "statusPanes": "paneles", "statusTags": "Etiquetas", - "killPaneTitle": "¿Eliminar panel {{id}}?", - "killPaneBody": "El proceso que se ejecuta en este panel finalizará. Al cerrar el último panel, se cerrará su ventana.", - "paneKillFailed": "No se pudo eliminar el panel", - "killSessionTitle": "¿Cancelar sesión \"{{name}}\"?", - "killSessionBody": "Todas las ventanas y los procesos en ejecución en esta sesión se cerrarán. Esta acción es irreversible.", - "kill": "Matar", - "sessionKilled": "Sesión \"{{name}}\" eliminada", - "sessionKillFailed": "No se pudo finalizar la sesión.", - "hostUnreachable": "No se pudo conectar con el servidor. Compruebe que esté en línea y accesible.", + "killPaneTitle": "¿Finalizar panel {{id}}?", + "killPaneBody": "El proceso en ejecución en este panel será terminado. Finalizar el último panel cierra su ventana.", + "paneKillFailed": "Error al finalizar el panel", + "killSessionTitle": "¿Finalizar sesión \"{{name}}\"?", + "killSessionBody": "Todas las ventanas y procesos en ejecución de esta sesión serán terminados. Esta acción no se puede deshacer.", + "kill": "Finalizar", + "sessionKilled": "Sesión \"{{name}}\" finalizada", + "sessionKillFailed": "Error al finalizar la sesión", + "hostUnreachable": "No se pudo conectar al host. Compruebe que esté en línea y accesible.", "noServer": "No hay ningún servidor tmux en ejecución en este host.", - "searchTruncated": "Resultados parciales: la búsqueda abarca las últimas {{lines}} líneas de cada panel y como máximo {{panes}} paneles.", + "searchTruncated": "Resultados parciales — la búsqueda cubre las últimas {{lines}} líneas de cada panel y un máximo de {{panes}} paneles.", "closeSearchResults": "Cerrar resultados de búsqueda", - "retry": "Rever", + "retry": "Reintentar", "noHosts": "No hay hosts SSH disponibles", - "noHostsHint": "Habilite la opción Monitor de Tmux en un host SSH (Administrador de hosts → pestaña Terminal) para supervisar sus sesiones de tmux.", + "noHostsHint": "Habilite la opción Tmux Monitor en un host SSH (Gestor de hosts → pestaña Terminal) para monitorizar sus sesiones tmux.", "tmuxInstallHint": "Instálelo en el host con:", - "attachTooltip": "Abre una terminal para {{host}}", - "attachTooltipPane": "Abre una terminal para {{host}} — sesión tmux {{session}}", - "timeJustNow": "En este momento", - "timeMinutes": "{{count}}hace m", - "timeHours": "{{count}}hace h", - "timeDays": "{{count}}hace d" + "attachTooltip": "Abrir un terminal en {{host}}", + "attachTooltipPane": "Abrir un terminal en {{host}} — sesión tmux {{session}}", + "timeJustNow": "ahora mismo", + "timeMinutes": "hace {{count}}m", + "timeHours": "hace {{count}}h", + "timeDays": "hace {{count}}d" }, "mobileKeyboard": { - "shift": "Cambio", - "ctrl": "Control", + "shift": "Mayús", + "ctrl": "Ctrl", "esc": "Esc", - "tab": "Pestaña", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "Flecha hacia arriba", - "arrowDown": "Flecha hacia abajo", + "arrowUp": "Flecha arriba", + "arrowDown": "Flecha abajo", "arrowLeft": "Flecha izquierda", "arrowRight": "Flecha derecha", - "home": "Hogar", + "home": "Inicio", "end": "Fin", - "pageUp": "Re Pág", - "pageDown": "PgDn", - "delete": "Del", + "pageUp": "Av Pág", + "pageDown": "Re Pág", + "delete": "Supr", + "paste": "Pasta", "editQuickKeys": "Editar teclas rápidas", "quickKeysTitle": "Teclas rápidas", - "quickKeysDesc": "Pulsa × para eliminar. Admite hasta 8 caracteres.", + "quickKeysDesc": "Toca × para eliminar. Admite hasta 8 caracteres.", "quickKeyPlaceholder": "p. ej. sudo ", - "addQuickKey": "Agregar", + "addQuickKey": "Añadir", "removeQuickKey": "Eliminar", - "resetDefaults": "Restablecer valores predeterminados", + "resetDefaults": "Restablecer", "done": "Hecho" }, "serial": { - "title": "De serie", + "title": "Serial", "portLabel": "Puerto", - "portPlaceholder": "/dev/ttyUSB0 o COM3", - "baudRateLabel": "Tasa de baudios", + "portPlaceholder": "/dev/ttyUSB0 or COM3", + "baudRateLabel": "Baudios", "dataBitsLabel": "Datos", - "stopBitsLabel": "Detener", + "stopBitsLabel": "Parada", "parityLabel": "Paridad", - "parityNone": "Ninguno", - "parityEven": "Incluso", - "parityOdd": "Extraño", - "connect": "Conectar a serie", + "parityNone": "Ninguna", + "parityEven": "Par", + "parityOdd": "Impar", + "connect": "Conectar a Serial", "disconnect": "Desconectar", "refreshPorts": "Actualizar puertos", "connected": "Conectado a {{path}} a {{baud}} baudios", "disconnected": "Puerto serie desconectado", - "connectionError": "No se pudo abrir el puerto serie.", - "wsError": "Error de WebSocket", - "errorNoServerUrl": "No se ha configurado ninguna URL de servidor.", - "notSupportedTitle": "Número de serie no compatible", - "notSupported": "Las conexiones en serie requieren un navegador compatible con la API Web Serial (Chrome, Edge o Firefox 151 o superior) o la aplicación de escritorio Termix.", + "connectionError": "Error al abrir el puerto serie", + "wsError": "Error WebSocket", + "errorNoServerUrl": "No se ha configurado URL de servidor", + "notSupportedTitle": "Serial no compatible", + "notSupported": "Las conexiones serie requieren un navegador compatible con Web Serial API (Chrome, Edge o Firefox 151+), o la aplicación de escritorio Termix.", "hideHint": "Puedes ocultar la pestaña Serial en Perfil de usuario > Apariencia > Barra lateral > Navegación.", - "browserPickerHint": "Haz clic en Conectar y tu navegador abrirá un selector de puertos para elegir el dispositivo." + "browserPickerHint": "Haz clic en Conectar y el navegador abrirá un selector de puertos para elegir el dispositivo." }, "metricsHistory": { - "title": "Historial de métricas", - "historySuffix": "Historia", + "title": "Historial de Métricas", + "historySuffix": "Historial", "cpuMemoryDisk": "CPU / Memoria / Disco", "network": "Red", "download": "RX", - "upload": "Texas", - "noData": "No hay datos históricos disponibles para este intervalo de tiempo.", - "custom": "Costumbre", + "upload": "TX", + "noData": "No hay datos de historial disponibles para este rango de tiempo.", + "custom": "Personalizado", "to": "a", "apply": "Aplicar", - "viewHistory": "Ver historial" + "viewHistory": "Ver Historial" }, "alerts": { "tabFirings": "Alertas", - "tabRules": "Normas", + "tabRules": "Reglas", "tabChannels": "Canales", - "noFirings": "No hay alertas no confirmadas.", - "noRules": "No se han configurado reglas de alerta.", - "noChannels": "No se han configurado canales de notificación.", - "rulesDesc": "Las reglas de alerta activan notificaciones.", + "noFirings": "No hay alertas sin reconocer", + "noRules": "No hay reglas de alerta configuradas", + "noChannels": "No hay canales de notificación configurados", + "rulesDesc": "Las reglas de alerta disparan notificaciones", "channelsDesc": "Dónde se envían las notificaciones de alerta", "acknowledge": "Reconocer", - "ackAll": "Ack All", - "allAcknowledged": "Todas las alertas han sido reconocidas.", - "ackFailed": "No se pudo confirmar la alerta.", - "ackAllFailed": "No se respondieron todas las alertas.", - "showAcknowledged": "Mostrar todo", - "hideAcknowledged": "Ocultar Ack", - "addChannel": "Agregar canal", + "ackAll": "Reconocer todo", + "allAcknowledged": "Todas las alertas reconocidas", + "ackFailed": "Error al reconocer la alerta", + "ackAllFailed": "Error al reconocer todas las alertas", + "showAcknowledged": "Mostrar todas", + "hideAcknowledged": "Ocultar reconocidas", + "addChannel": "Añadir canal", "editChannel": "Editar canal", "channelName": "Nombre", "channelType": "Tipo", - "channelNameRequired": "Se requiere el nombre", + "channelNameRequired": "El nombre es obligatorio", "webhookUrl": "URL", - "webhookUrlRequired": "Se requiere la URL del webhook.", - "webhookDesc": "Enviar la carga útil JSON a esta URL en cada activación de alerta.", + "webhookUrlRequired": "La URL del webhook es obligatoria", + "webhookDesc": "Envía una carga JSON mediante POST a esta URL cada vez que se active una alerta", "ntfyServer": "URL del servidor", "ntfyTopic": "Tema", - "ntfyTopicRequired": "Se requiere un tema", + "ntfyTopicRequired": "El tema es obligatorio", "ntfyToken": "Token de acceso (opcional)", - "channelSaveFailed": "No se pudo guardar el canal.", - "test": "Prueba", + "channelSaveFailed": "No se pudo guardar el canal", + "test": "Probar", "testSent": "Notificación de prueba enviada", - "testFailed": "Falló la notificación de prueba", - "addRule": "Agregar regla de alerta", + "testFailed": "La notificación de prueba falló", + "addRule": "Añadir regla de alerta", "editRule": "Editar regla de alerta", "ruleName": "Nombre de la regla", - "ruleNameRequired": "Se requiere el nombre", - "triggerType": "Desencadenar", - "thresholdValue": "Límite (%)", - "durationSeconds": "Duración (segundos, 0 = disparo inmediato)", - "cooldownMinutes": "Tiempo de enfriamiento (minutos)", + "ruleNameRequired": "El nombre es obligatorio", + "triggerType": "Disparador", + "thresholdValue": "Umbral (%)", + "durationSeconds": "Duración (segundos, 0 = disparar inmediatamente)", + "cooldownMinutes": "Tiempo de espera (minutos)", "channels": "Canales de notificación", - "noChannelsHint": "Primero, agrega los canales en la pestaña Canales.", + "noChannelsHint": "Añade primero canales en la pestaña Canales", "ruleSaveFailed": "No se pudo guardar la regla" } } diff --git a/src/ui/locales/translated/fi_FI.json b/src/ui/locales/translated/fi_FI.json index 77468743..39cd7228 100644 --- a/src/ui/locales/translated/fi_FI.json +++ b/src/ui/locales/translated/fi_FI.json @@ -389,6 +389,45 @@ "noSavedServers": "Ei tallennettuja palvelimia", "removeServer": "Poistaa" }, + "migrationNotice": { + "title": "Termix Desktop toimii nyt itsenäisesti", + "body1": "Tämä sovellus muodosti aiemmin yhteyden suoraan Termix-palvelimeesi. Se on uudistettu toimimaan täysin itsenäisesti tallentaen isännät ja tunnistetiedot paikallisesti, joten se toimii edelleen offline-tilassa. Kaksisuuntainen synkronointi itse isännöidyn Termix-palvelimen kanssa on nyt valinnainen.", + "body2": "Nykyiset isäntäsi ja tunnistetietosi ovat edelleen {{url}}. Ota etäsynkronointi käyttöön ja muodosta yhteys kyseiseen palvelimeen uudelleen palauttaaksesi ne ja pitääksesi molemmat synkronoituina jatkossa.", + "dismiss": "Ei nyt", + "setUpSync": "Etäsynkronoinnin määrittäminen" + }, + "remoteSync": { + "title": "Etäsynkronointi", + "description": "Voit halutessasi yhdistää tämän työpöytäsovelluksen itse isännöityyn Termix-palvelimeen synkronoidaksesi isännöitsijäsi, tunnistetietosi ja koodinpätkäsi eri laitteiden välillä. Sovellus toimii aina täysin offline-tilassa riippumatta siitä, oletko yhteydessä palvelimeen vai et.", + "notConnected": "Ei yhdistetty", + "connected": "Yhdistetty", + "connectedTo": "Yhdistetty kohteeseen {{url}}", + "lastSynced": "Viimeksi synkronoitu {{time}}", + "neverSynced": "Ei koskaan synkronoitu", + "syncError": "Synkronointivirhe: {{message}}", + "needsReauth": "Kirjautuminen vanhentunut", + "connectButton": "Yhdistä palvelimeen", + "disconnectButton": "Katkaise yhteys", + "syncNowButton": "Synkronoi nyt", + "syncing": "Synkronoidaan...", + "serverUrl": "Palvelimen URL-osoite", + "enterServerUrl": "Anna palvelimen URL-osoite", + "mustIncludeProtocol": "Palvelimen URL-osoitteen on alettava http:// tai https://", + "connectionTestFailed": "Termix-palvelimeen ei saatu yhteyttä kyseisessä URL-osoitteessa", + "allowInvalidCertificate": "Salli virheellinen varmenne", + "allowInvalidCertificateDesc": "Käytä vain luotettaville itse isännöidyille palvelimille, joissa on itse allekirjoitetut tai IP-osoitevarmenteet.", + "savedServers": "Tallennetut palvelimet", + "removeServer": "Poistaa", + "continueButton": "Jatkaa", + "cancelButton": "Peruuttaa", + "signInTitle": "Kirjaudu sisään palveluun {{url}}", + "originTitle": "Yhteyden alkuperä", + "originDescription": "Valitse oletusarvoisesti SSH-yhteyksien lähtöpaikka. Tämä voidaan ohittaa isäntäkohtaisesti.", + "originLocal": "Tämä laite (lähiverkko)", + "originRemote": "Etäpalvelin", + "bannerReconnect": "Yhdistä uudelleen", + "bannerMessage": "Etäsynkronointi vaatii uudelleentunnistuksen" + }, "versionCheck": { "error": "Versiotarkistusvirhe", "checkFailed": "Päivitysten tarkistaminen epäonnistui", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Ohita tunnistetiedot Käyttäjätunnus", "overrideCredentialUsernameDesc": "Käytä yllä määritettyä käyttäjätunnusta tunnistetiedon käyttäjätunnuksen sijaan.", "oidcUsernameHint": "Käytä OIDC-kirjautumisnimesi sijaan muotoa $oidc.preferred_username.", + "tailscaleUsernameHint": "Tämän on oltava Unix-käyttäjä, jolle Tailscale-identiteettisi on myönnetty tailnetin SSH ACL:ssä, ei välttämättä pääkäyttäjä.", "jumpHostChain": "Hyppyisäntäketju", "portKnocking": "Portin koputus", "addKnock": "Lisää portti", @@ -650,6 +690,11 @@ "delayAfterMs": "Viive jälkeen (ms)", "useSocks5Proxy": "Käytä SOCKS5-välityspalvelinta", "useSocks5ProxyDesc": "Reititä yhteys välityspalvelimen kautta", + "connectionOrigin": "Yhteyden alkuperä", + "connectionOriginDesc": "Mistä tämän isännän SSH-yhteys on peräisin. Ohittaa työpöytäsovelluksen yleisen oletusasetuksen.", + "connectionOriginDefault": "Käytä oletusarvoa", + "connectionOriginLocal": "Tämä laite (lähiverkko)", + "connectionOriginRemote": "Etäpalvelin", "proxyHost": "Välityspalvelin", "proxyPort": "Välityspalvelimen portti", "proxyUsername": "Välityspalvelimen käyttäjätunnus", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Kirkkaan syaani", "customThemeBrightWhite": "Kirkkaan valkoinen", "customThemeResetTooltip": "Palauta oletusasetukset", + "savedThemesLabel": "Tallennetut teemat", + "saveAsGlobalTheme": "Tallenna globaalina teemana", + "saveGlobalThemeNamePrompt": "Anna teemalle nimi", + "saveGlobalThemeSuccess": "Teema tallennettu", + "saveGlobalThemeError": "Teeman tallentaminen epäonnistui", + "applyGlobalThemeTooltip": "Käytä tätä teemaa", + "deleteGlobalThemeTooltip": "Poista tämä teema", + "noSavedThemes": "Ei vielä tallennettuja teemoja", "syntaxHighlightingLabel": "Syntaksin korostus", "syntaxHighlightingDesc": "Väritä päätelaitteen tuloste (virheet, polut, IP-osoitteet, aikaleimat)", "syntaxHighlightingCategories": "Korosta kategoriat", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Käynnistä automaattisesti tai liitä tmux-istuntoon", "enableSessionLogging": "Istuntojen lokitiedot", "enableSessionLoggingDesc": "Tallenna pääteistunnon tuloste myöhempää tarkastelua varten", + "allowSessionSharing": "Salli istunnon jakaminen", + "allowSessionSharingDesc": "Salli live-istuntojen jakaminen tällä isännällä linkin kautta tai muiden käyttäjien kanssa", "enableCommandHistory": "Komentohistoria", "enableCommandHistoryDesc": "Tallenna tässä terminaalissa suoritettavat komennot historiaa ja automaattista täydennystä varten", "linkClickBehaviorLabel": "Linkin klikkauskäyttäytyminen", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} tuotu", "proxmoxResultUpdated": "{{count}} päivitetty", "proxmoxResultFailed": "{{count}} epäonnistui", + "proxmoxResultSkippedNoIp": "{{count}} ohitettu (IP-osoitetta ei löytynyt)", "proxmoxImportComplete": "Proxmox-tuonti valmis: {{summary}}", "proxmoxDiscoveryFailed": "Löytö epäonnistui", "proxmoxImportFailed": "Tuonti epäonnistui", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Tunnusten tallentaminen epäonnistui", "credentialNameRequired": "Anna tunnisteelle nimi", "credentialAuthRequired": "Lisää salasana, SSH-avain tai molemmat", + "createCredentialFromHostBtn": "Luo tunnistetiedot", + "createCredentialFromHostTitle": "Luo tunnistetiedot isännältä", + "createCredentialFromHostDesc": "Luo uudelleenkäytettävä, jaettava tunnistetietomerkintä, joka on esitäytetty tämän isännän nykyisellä käyttäjätunnuksella, salasanalla ja/tai SSH-avaimella.", "backToHosts": "Takaisin isäntiin", "backToCredentials": "Takaisin tunnistetietoihin", "pinned": "Kiinnitetty", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Käytä /-merkkiä tasojen erottamiseen ja sisäkkäisten kansioiden luomiseen.", "folderColor": "Väri", "folderIcon": "Kuvake", + "folderCredential": "Valtakirja", + "folderCredentialNone": "Ei tunnistetietoja", + "folderCredentialHint": "Tämän kansion isännät, jotka käyttävät \"Tallennetut tunnistetiedot\" -todennusta ilman omia tunnistetietojaan, perivät tämän.", "folderPreview": "Esikatselu", "folderNameFallback": "Nimetön kansio", "createFolderButton": "Luo kansio", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tunnisteet", "shareHost": "Jaa isäntä", "shareHostTitle": "Jaa: {{name}}", + "shareFolder": "Jaa kansio", + "shareFolderTitle": "Jaa kansio: {{name}}", + "folderSharedSuccessfully": "Jaetut {{count}} isäntä(t) kansiossa", + "failedToShareFolder": "Kansion jakaminen epäonnistui", "sharing": { "loadError": "Jakamistietojen lataaminen epäonnistui. Yritä uudelleen.", "shareWithSection": "Jaa kanssa", @@ -1221,6 +1287,7 @@ "shareWithCount": "Jaa ({{count}})", "currentAccess": "Nykyinen käyttöoikeus", "noAccessEntries": "Tätä isäntää ei ole vielä jaettu", + "folderShareSummary": "Jaettu {{shared}} / {{total}} isäntäkoneesta tässä kansiossa", "grantedBy": "Myöntänyt", "expires": "Vanhenee", "expired": "Vanhentunut", @@ -1242,6 +1309,8 @@ "authMethod": "Todennustapa", "authTypeDirect": "Suoraan", "authTypeCredential": "Valtakirja", + "authTypeNone": "Ei mitään", + "authTypeNoneDesc": "Tunnuksia ei tallenneta. Sinulta kysytään käyttäjätunnusta ja salasanaa aina, kun muodostat yhteyden; niitä ei tallenneta.", "selectCredential": "Valitse tunnistetiedot...", "connectionSettings": "Yhteysasetukset", "displaySettings": "Näyttöasetukset", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-portti", "username": "Käyttäjätunnus", "password": "Salasana", + "passwordSaved": "Salasana tallennettu, kirjoita muuttaaksesi", "domain": "Verkkotunnus", "securityMode": "Suojaustila", "colorDepth": "Värisyvyys", @@ -1424,7 +1494,60 @@ "expiresIn": "Vanhenee {{duration}} kuluttua", "search": "Hae yhteyksiä...", "noSearchResults": "Ei hakuasi vastaavia yhteyksiä", - "rename": "Nimeä istunto uudelleen" + "rename": "Nimeä istunto uudelleen", + "sectionSharedWithMe": "Jaettu kanssani", + "sharedBy": "Jakanut {{username}}", + "join": "Liity", + "sharedSessionLabel": "{{hostName}} (jaettu)" + }, + "sessionSharing": { + "guestView": { + "loading": "Yhdistetään jaettuun istuntoon...", + "linkInvalid": "Tämä jakolinkki on virheellinen, vanhentunut tai se on peruutettu.", + "rateLimited": "Liikaa yrityksiä, yritä uudelleen pian", + "sessionEnded": "Tämä istunto on päättynyt", + "readOnlyBadge": "Vain katselu" + }, + "modalTitle": "Jaa istunto", + "shareButton": "Jakaa", + "notReadyToShare": "Istunto ei ole vielä valmis jaettavaksi", + "modeTab": { + "link": "Linkki", + "user": "Käyttäjä" + }, + "linkModeDescription": "Kuka tahansa, jolla on tämä linkki, voi liittyä, tiliä ei tarvita.", + "userModeDescription": "Jaa tietyn käyttäjän kanssa, jolla on jo pääsy tähän isäntään. Jos heillä ei vielä ole pääsyä, jaa isäntä ensin heidän kanssaan tai käytä linkkiä. Jakamisen jälkeen istunto näkyy heidän Yhteydet-välilehdellään.", + "permissionLevel": { + "label": "Käyttöoikeustaso", + "readOnly": "Vain lukuoikeus", + "readOnlyDescription": "Voit katsoa istuntoa suorana, mutta et voi kirjoittaa tai olla vuorovaikutuksessa.", + "readWrite": "Lue ja kirjoita", + "readWriteDescription": "Voi kirjoittaa ja olla vuorovaikutuksessa istunnon kanssa aivan kuten omistaja." + }, + "expiryLabel": "Linkin vanheneminen", + "createLinkButton": "Luo linkki", + "createShareButton": "Jaa käyttäjän kanssa", + "searchUsersPlaceholder": "Hae käyttäjiä...", + "noUsersFound": "Ei käyttäjiä löytynyt", + "linkCreated": "Jaa linkki luotu", + "linkCopied": "Linkki kopioitu leikepöydälle", + "copyLink": "Kopioi linkki", + "shareCreated": "Istunto jaettu. Se näkyy heidän Yhteydet-välilehdellä.", + "shareFailed": "Jaon luominen epäonnistui", + "userLacksHostAccess": "Käyttäjällä ei ole vielä pääsyä tähän isäntään. Jaa isäntä ensin heidän kanssaan tai käytä linkkiä.", + "activeShares": "Aktiiviset jaot", + "noActiveShares": "Ei aktiivisia jakoja tällä istunnolla", + "revoke": "Peruuttaa", + "revokeConfirmTitle": "Perutaanko tämä jako?", + "revokeConfirmDescription": "Kuka tahansa tätä jakoa käyttävä menettää käyttöoikeuden välittömästi.", + "revoked": "Jako peruttu", + "revokeFailed": "Jakamisen peruuttaminen epäonnistui", + "joinCount": "{{count}} liity", + "joinCount_other": "{{count}} liitokset", + "expiresAt": "Vanhenee {{date}}", + "linkShareBadge": "Linkki", + "userShareBadge": "Käyttäjä: {{username}}", + "loadSharesFailed": "Aktiivisten jakojen lataaminen epäonnistui" }, "guacamole": { "connecting": "Yhdistetään {{type}} -istuntoon...", @@ -1436,6 +1559,9 @@ "reconnect": "Yhdistä uudelleen", "retry": "Yritä uudelleen", "guacdUnavailable": "Etätyöpöytäpalvelu (guacd) ei ole käytettävissä. Varmista, että guacd on käynnissä, käytettävissä ja määritetty oikein järjestelmänvalvojan asetuksissa.", + "credentialPromptTitle": "Syötä RDP-tunnistetiedot", + "credentialPromptDescription": "Tämä isäntä on asetettu kysymään tunnistetietoja yhteyden muodostamisen yhteydessä. Niitä käytetään vain tässä istunnossa, eikä niitä tallenneta.", + "connect": "Yhdistä", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Yhdistä istunto uudelleen", "collapse": "Pienennä työkalupalkki", "expand": "Laajenna työkalupalkkia", - "dragHandle": "Vedä siirtääksesi" + "dragHandle": "Vedä siirtääksesi", + "switchToTrackpad": "Vaihda ohjauslevytilaan (vedä siirtääksesi kohdistinta, napauta napsauttamalla)", + "switchToTouch": "Vaihda kosketustilaan (napauta suoraan haluamaasi kohtaan)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Kaksivaiheinen todennus vaaditaan", "totpCodeLabel": "Vahvistuskoodi", "totpVerify": "Vahvista", + "mfaPromptRequired": "Todennus vaaditaan", + "mfaPushRequired": "Push-todennus vaaditaan", + "mfaMenuPlaceholder": "Kirjoita vastauksesi", + "mfaWaitingApproval": "Odotetaan hyväksyntää laitteellasi...", + "mfaSendRequest": "Lähetä pyyntö", "warpgateAuthRequired": "Warpgate-todennus vaaditaan", "warpgateSecurityKey": "Suojausavain", "warpgateAuthUrl": "Todennus-URL-osoite", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Nopeus rajoitettu: Liian monta vahvistusyritystä. Yritä myöhemmin uudelleen.", "resetCodeRateLimitedWithTime": "Nopeus rajoitettu: Liian monta vahvistusyritystä. Odota {{time}} sekuntia ennen kuin yrität uudelleen.", "authTokenSaveFailed": "Todennustunnuksen tallennus epäonnistui", - "failedToLoadServer": "Palvelimen lataus epäonnistui" + "failedToLoadServer": "Palvelimen lataus epäonnistui", + "remoteServerRequired": "Etäpalvelin vaaditaan. Yhdistä etäpalvelin asetuksissa käyttääksesi tätä yhteystyyppiä." }, "messages": { "registrationDisabled": "Ylläpitäjä on tällä hetkellä poistanut käytöstä uuden tilin rekisteröinnin. Kirjaudu sisään tai ota yhteyttä ylläpitäjään.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "SSL-asetusten tallentaminen epäonnistui", "sslRequiresDomain": "Verkkotunnus ja sähköpostiosoite vaaditaan", "sslInfoNote": "Kun olet myöntänyt varmenteen, ota SSL käyttöön ympäristömuuttujissasi (ENABLE_SSL=true) ja käynnistä Termix uudelleen.", + "sslManualOption": "Manuaalinen (lataa varmenne)", + "sslManualCert": "Todistus (PEM)", + "sslManualCertPlaceholder": "-----ALKUTODISTUS-----", + "sslManualKey": "Yksityinen avain (PEM)", + "sslManualKeyPlaceholder": "-----ALKU YKSITYINEN AVAIN-----", + "sslManualDesc": "Liitä olemassa oleva varmenne ja yksityinen avaimesi, mukaan lukien koko ketju, jos varmentaja sitä vaatii.", + "sslManualUpload": "Lataa ja asenna varmenne", + "sslManualUploadLoading": "Varmennetta ladataan...", + "sslManualUploadSuccess": "Varmenne ladattiin ja asennettiin onnistuneesti", + "sslManualUploadFailed": "Varmenteen lataus epäonnistui", + "sslManualRequiresFields": "Varmenne ja yksityinen avain vaaditaan", "auditLogTotal": "{{total}} merkintöjä yhteensä", "auditLogEmpty": "Ei tarkastuslokin merkintöjä", "auditLogSuccess": "Menestys", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Komentohistoria", "commandHistoryEnabledDesc": "Salli komentohistorian tallennus. Kun tämä on poistettu käytöstä, historiaa ei tallenneta isäntäkohtaisista asetuksista riippumatta.", "updateCommandHistoryFailed": "Komentohistoria-asetuksen päivittäminen epäonnistui", + "analyticsEnabled": "Jaa anonyymit käyttötilastot", + "analyticsEnabledDesc": "Lähettää nimettömän päivittäisen määrän käyttäjistä, isännöistä ja ominaisuuksien käytöstä Termixin parantamiseksi. Henkilötietoja tai yhteystietoja ei koskaan sisällytetä mukaan.", + "updateAnalyticsFailed": "Analytiikka-asetuksen päivittäminen epäonnistui", + "sessionSharingGloballyEnabled": "Salli istunnon jakaminen", + "sessionSharingGloballyEnabledDesc": "Salli reaaliaikaisten pääte-, RDP-, VNC- ja Telnet-istuntojen jakaminen koko instanssin kesken. Poistamalla tämän asetuksen käytöstä ohitetaan kaikki isäntäkohtaisesti jakamiseen liittyvät asetukset.", + "updateSessionSharingFailed": "Istunnon jakamisasetuksen päivittäminen epäonnistui", "sessionTimeout": "Istunnon aikakatkaisu", "hours": "tuntia", "sessionTimeoutRange": "Vähintään 1 h · Enintään 720 h", @@ -3119,6 +3270,47 @@ "executionSuccess": "Menestys", "executionFailed": "Epäonnistui" }, + "keybindings": { + "title": "Pikanäppäimet", + "description": "Mukauta päätelaitteen kopiointi-, liittämis- ja ohjauspikakuvakkeita tai sido näppäimiä tekstin lähettämiseen tai koodinpätkän suorittamiseen.", + "defaultsHeading": "Sisäänrakennetut pikavalinnat", + "customHeading": "Mukautetut pikavalinnat", + "addBinding": "Lisää pikakuvake", + "addBindingTitle": "Lisää pikakuvake", + "editBindingTitle": "Muokkaa pikakuvaketta", + "loading": "Ladataan...", + "noCustomBindings": "Ei vielä mukautettuja pikanäppäimiä", + "defaultBadge": "Oletus", + "customizedBadge": "Räätälöity", + "customize": "Mukauta", + "resetToDefault": "Palauta oletusarvot", + "resetAllToDefaults": "Palauta kaikki oletusasetuksiin", + "close": "Lähellä", + "cancel": "Peruuttaa", + "saveBinding": "Tallenna pikakuvake", + "saveError": "Pikanäppäinten tallentaminen epäonnistui", + "pressKeysToRecord": "Näppäinyhdistelmä", + "pressKeysPlaceholder": "Napsauta ja paina näppäimiä", + "recording": "Paina näppäimiä...", + "comboRequiredError": "Paina ensin näppäinyhdistelmää", + "textRequiredError": "Kirjoita lähetettävä teksti", + "controlCodeRequiredError": "Syötä yksi kirjain ohjauskoodille", + "snippetRequiredError": "Valitse katkelma", + "conflictWarning": "Tämä yhdistelmä on jo sidottu kohtaan {{combo}}. Tallentaminen käynnistää molemmat.", + "actionLabel": "Toiminta", + "actionCopy": "Kopioi valinta", + "actionPaste": "Liitä", + "actionSendControlCode": "Lähetä Ctrl+kirjain-signaali", + "actionSendText": "Lähetä tekstiviesti", + "actionRunSnippet": "Suorita olemassa oleva koodinpätkä", + "controlCodeLabel": "Kirje", + "textLabel": "Lähetettävä tekstiviesti", + "appendEnterLabel": "Paina Enter lähetyksen jälkeen", + "snippetLabel": "Katkelma", + "selectSnippetPlaceholder": "Valitse katkelma", + "orphanedSnippetWarning": "katkelmaa ei löytynyt", + "clipboardPermissionNote": "Saattaa pyytää leikepöydän käyttöoikeutta ensimmäisellä käyttökerralla joissakin selaimissa." + }, "userProfile": { "donateTitle": "Pidä Termix hengissä", "donateDescription": "Termix on ilmainen, itse isännöity ja vain muutaman ihmisen vapaa-ajallaan rakentama palvelu. Jos se on säästänyt aikaasi tai rahaasi, kryptovaluutan lahjoitus auttaa pitämään sen toiminnassa.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Ulkonäkö", "sectionSecurity": "Turvallisuus", "sectionApiKeys": "API-avaimet", + "sectionData": "Data", "sectionC2sTunnels": "C2S-tunnelit", "usernameLabel": "Käyttäjätunnus", "roleLabel": "Rooli", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminaali", "commandAutocomplete": "Komennon automaattinen täydennys", "commandAutocompleteDesc": "Näytä automaattinen täydennys kirjoitettaessa", + "keyboardShortcuts": "Pikanäppäimet", + "keyboardShortcutsDescription": "Mukauta päätelaitteen kopiointi-, liittämis- ja komento-pikakuvakkeita", + "manageShortcuts": "Hallitse", "terminalLinkBehavior": "Päätelinkin napsautus", "terminalLinkBehaviorDesc": "Oletuskäyttäytyminen linkkejä napsautettaessa terminaalissa", "historyTracking": "Historian seuranta", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Sisällytä avaimesi mukaan", "apiKeyUsageHintHeader": "otsikko.", "apiKeyPermissionsHint": "Avaimet perivät luovan käyttäjän oikeudet.", + "exportData": "Vie tietoni", + "exportDataDesc": "Lataa varmuuskopio isännistäsi, tunnistetiedoistasi ja asetuksistasi siirtääksesi ne toiseen laitteeseen", + "export": "Viedä", + "exporting": "Vienti...", + "importData": "Tuo omat tiedot", + "importDataDesc": "Palauta isäntäpalvelimet, tunnistetiedot ja asetukset .sqlite-varmuuskopiotiedostosta", + "importDataSelected": "Valittu: {{name}}", + "selectFile": "Valitse tiedosto", + "changeFile": "Muuttaa", + "import": "Tuoda", + "importing": "Tuodaan...", + "exportSuccess": "Tiedot vietiin onnistuneesti", + "exportFailed": "Tietojen vienti epäonnistui", + "importSelectFile": "Valitse ensin tiedosto", + "importCompleted": "Tuonti valmis: {{total}} kohdetta tuotu, {{skipped}} ohitettu", + "importFailed": "Tuonti epäonnistui: {{error}}", "roleUser": "Käyttäjä", "authMethodDual": "Kaksoistutkimus", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "Sivu ylös", "pageDown": "PgDn", "delete": "Poista", + "paste": "Liitä", "editQuickKeys": "Muokkaa pikanäppäimiä", "quickKeysTitle": "Pikanäppäimet", "quickKeysDesc": "Poista napauttamalla ×. Tukee enintään 8 merkkiä.", diff --git a/src/ui/locales/translated/fr_FR.json b/src/ui/locales/translated/fr_FR.json index f46614d1..e539c0d2 100644 --- a/src/ui/locales/translated/fr_FR.json +++ b/src/ui/locales/translated/fr_FR.json @@ -1,71 +1,71 @@ { "termixId": { - "title": "ID Termix", - "loadFailed": "Échec du chargement de l'identifiant Termix", - "claimTitle": "Réclamez votre identifiant Termix", + "title": "Termix ID", + "loadFailed": "Échec du chargement de Termix ID", + "claimTitle": "Obtenez votre Termix ID", "claimIntro": "Choisissez un identifiant unique. Vos clés publiques SSH seront publiées à une URL publique que vous pourrez ajouter au fichier authorized_keys de n'importe quel serveur.", - "handleLabel": "Poignée", + "handleLabel": "Identifiant", "handlePlaceholder": "alice", "checking": "Vérification…", "available": "Disponible", "taken": "Déjà pris", - "invalidHandle": "Lettres minuscules, chiffres, - et _ seulement", - "descriptionLabel": "Description (facultatif)", - "descriptionPlaceholder": "Clés de l'ordinateur portable et du téléphone professionnel", - "create": "Créer un identifiant Termix", - "created": "Identifiant Termix créé", - "createFailed": "Échec de la création de l'identifiant Termix", - "deleteConfirm": "Supprimer votre identifiant Termix et toutes les clés publiées ? Les serveurs que vous avez provisionnés conserveront les clés jusqu’à ce que vous les supprimiez manuellement.", - "deleted": "Identifiant Termix supprimé", - "deleteFailed": "Échec de la suppression de l'identifiant Termix", + "invalidHandle": "Lettres minuscules, chiffres, - et _ uniquement", + "descriptionLabel": "Description (facultative)", + "descriptionPlaceholder": "Clés pour ordinateur portable pro et téléphone", + "create": "Créer un Termix ID", + "created": "Termix ID créé", + "createFailed": "Échec de la création de Termix ID", + "deleteConfirm": "Supprimer votre Termix ID et toutes les clés publiées ? Les serveurs que vous avez provisionnés conserveront les clés jusqu'à ce que vous les supprimiez manuellement.", + "deleted": "Termix ID supprimé", + "deleteFailed": "Échec de la suppression de Termix ID", "copyFailed": "Échec de la copie", - "resolverUrlLabel": "URL du résolveur public", - "provisionLabel": "Configurer un serveur", + "resolverUrlLabel": "URL de résolution publique", + "provisionLabel": "Provisionner un serveur", "publishTitle": "Publier une clé publique", "generate": "Générer", - "generateTooltip": "Générez une paire de clés Ed25519 : publiez la clé publique et téléchargez la clé privée sur votre appareil.", + "generateTooltip": "Générer une paire de clés Ed25519 — publie la clé publique et télécharge la clé privée sur votre appareil", "saveToVault": "Enregistrer dans les identifiants", - "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... utilisateur@hôte", - "labelPlaceholder": "Étiquette (facultatif)", + "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", + "labelPlaceholder": "Libellé (facultatif)", "add": "Ajouter", - "importFromCredential": "Ou importez à partir d'une information d'identification enregistrée", + "importFromCredential": "Ou importer depuis un identifiant stocké", "keyPublished": "Clé publiée", - "addKeyFailed": "Impossible d'ajouter la clé", - "generatedSaved": "La paire de clés a été générée et enregistrée dans votre coffre-fort numérique. La clé privée a également été téléchargée.", + "addKeyFailed": "Échec de l'ajout de la clé", + "generatedSaved": "Paire de clés générée et enregistrée dans votre coffre-fort d'identifiants. Clé privée également téléchargée.", "generatedOnly": "Paire de clés générée — clé privée téléchargée (affichée une seule fois).", "generateFailed": "Échec de la génération de la clé", - "imported": "Clé importée à partir des informations d'identification", + "imported": "Clé importée depuis l'identifiant", "importFailed": "Échec de l'importation de la clé", - "noKeys": "Aucune clé publique n'a encore été publiée.", + "noKeys": "Aucune clé publique publiée pour le moment.", "keysTitle": "Clés publiées", - "published": "Publié", - "hidden": "Caché", - "keyRemoved": "Clé retirée", - "removeKeyFailed": "Impossible de retirer la clé", + "published": "Publiée", + "hidden": "Masquée", + "keyRemoved": "Clé supprimée", + "removeKeyFailed": "Échec de la suppression de la clé", "updateKeyFailed": "Échec de la mise à jour de la clé", - "fromVault": "Depuis le coffre à identifiants", - "linkedToTermixId": "Publié via Termix ID", - "selectCredential": "Sélectionnez une qualification...", + "fromVault": "Du coffre-fort des identifiants", + "linkedToTermixId": "Publiée via Termix ID", + "selectCredential": "Sélectionner un identifiant...", "import": "Importer", "caTitle": "Autorité de certification", - "caIntro": "Faites confiance à cette autorité de certification sur un serveur : elle acceptera tout certificat que vous signerez. Révoquez-les tous en une seule fois ; les certificats expirent automatiquement.", - "caEnable": "Activer l'autorité de certification", - "caEnabled": "CA activé", - "caCreateFailed": "Échec de l'activation de l'autorité de certification", - "caPublicKeyLabel": "Clé publique de l'autorité de certification", - "caTrustLabel": "Faire confiance à un serveur (exécuté en tant que root)", - "caRotate": "Tourner", - "caRotateConfirm": "Faut-il faire tourner l'autorité de certification ? Tous les certificats qu'elle a signés cesseront d'être acceptés et devront être réémis.", - "caRotated": "CA renouvelée — les certificats précédents ont été révoqués", - "caRotateFailed": "Échec de la rotation CA", - "caDelete": "Supprimer CA", - "caDeleteConfirm": "Supprimer l'autorité de certification ?", - "caDeleted": "CA supprimé", - "caDeleteFailed": "Échec de la suppression de l'autorité de certification", + "caIntro": "Faites confiance à cette AC sur un serveur et il acceptera tout certificat que vous signez. Renouvelez pour tout révoquer d'un coup ; les certificats expirent également d'eux-mêmes.", + "caEnable": "Activer l'AC", + "caEnabled": "AC activée", + "caCreateFailed": "Échec de l'activation de l'AC", + "caPublicKeyLabel": "Clé publique de l'AC", + "caTrustLabel": "Confiance sur un serveur (exécuter en tant que root)", + "caRotate": "Renouveler", + "caRotateConfirm": "Renouveler l'AC ? Tous les certificats qu'elle a signés ne seront plus acceptés et devront être réémis.", + "caRotated": "AC renouvelée — certificats précédents révoqués", + "caRotateFailed": "Échec du renouvellement de l'AC", + "caDelete": "Supprimer l'AC", + "caDeleteConfirm": "Supprimer l'autorité de certification ?", + "caDeleted": "AC supprimée", + "caDeleteFailed": "Échec de la suppression de l'AC", "caValidityLabel": "Validité du certificat (jours)", "issueCert": "Certificat", - "issueCertTooltip": "Émettez un certificat SSH pour cette clé, signé par votre autorité de certification", - "certIssued": "Certificat délivré et téléchargé", + "issueCertTooltip": "Émettre un certificat SSH pour cette clé, signé par votre AC", + "certIssued": "Certificat émis et téléchargé", "certIssueFailed": "Échec de l'émission du certificat" }, "credentials": { @@ -74,64 +74,64 @@ "password": "Mot de passe", "key": "Clé", "sshPrivateKey": "Clé privée SSH", - "upload": "Télécharger", - "keyPassword": "Mot de passe clé", + "upload": "Téléverser", + "keyPassword": "Mot de passe de la clé", "sshKey": "Clé SSH", "uploadPrivateKeyFile": "Téléverser le fichier de clé privée", - "searchCredentials": "Identifiants de recherche...", + "searchCredentials": "Rechercher des identifiants...", "addCredential": "Ajouter un identifiant", - "caCertificate": "Certificat d'autorité de certification (-cert.pub)", - "caCertificateDescription": "Facultatif : Téléversez ou collez le fichier de certificat signé par l’autorité de certification (par exemple, id_ed25519-cert.pub). Requis si votre serveur SSH utilise l’authentification par certificat.", + "caCertificate": "Certificat CA (-cert.pub)", + "caCertificateDescription": "Facultatif : Téléversez ou collez le fichier de certificat signé par une CA (par ex. id_ed25519-cert.pub). Requis lorsque votre serveur SSH utilise une autorisation basée sur des certificats.", "uploadCertFile": "Téléverser le fichier -cert.pub", - "clearCert": "Clair", + "clearCert": "Effacer", "certLoaded": "Certificat chargé", "certPublicKeyLabel": "Certificat CA", "certTypeLabel": "Type de certificat", - "pasteOrUploadCert": "Collez ou téléchargez un certificat -cert.pub...", - "hasCaCert": "Possède un certificat CA", + "pasteOrUploadCert": "Collez ou téléversez un certificat -cert.pub...", + "hasCaCert": "Avec certificat CA", "noCaCert": "Aucun certificat CA", - "noPublicKeyAvailable": "Aucune clé publique disponible. Ouvrez d'abord l'éditeur d'identifiants.", + "noPublicKeyAvailable": "Aucune clé publique disponible. Ouvrez d’abord l’éditeur d’identifiants.", "deployCommandCopied": "Commande de déploiement copiée", - "sortCredentials": "Identifiants de tri", + "sortCredentials": "Trier les identifiants", "sortDefault": "Ordre par défaut", "sortNameAsc": "Nom (A → Z)", "sortNameDesc": "Nom (Z → A)", - "sortUsernameAsc": "Nom d'utilisateur (A → Z)", - "sortUsernameDesc": "Nom d'utilisateur (Z → A)", + "sortUsernameAsc": "Nom d’utilisateur (A → Z)", + "sortUsernameDesc": "Nom d’utilisateur (Z → A)", "filterCredentials": "Filtrer les identifiants", - "filterClearAll": "Filtres transparents", - "filterTypeGroup": "Taper", + "filterClearAll": "Effacer les filtres", + "filterTypeGroup": "Type", "filterTypePassword": "Mot de passe", "filterTypeKey": "Clé SSH", "filterTagsGroup": "Étiquettes" }, "homepage": { - "title": "Page d'accueil", + "title": "Accueil", "addWidget": "Ajouter un widget", "editWidget": "Modifier le widget", "deleteWidget": "Supprimer", "widgetTypes": "Types de widgets", - "serviceLink": "Lien vers les services", + "serviceLink": "Lien de service", "folder": "Dossier", "clock": "Horloge", "notes": "Notes", - "hostStatus": "État de l'hôte", - "bookmarkList": "Signets", - "zoomIn": "Zoomer", - "zoomOut": "Dézoomer", - "resetView": "Réinitialiser l'affichage", - "lockLayout": "Disposition du verrou", + "hostStatus": "Statut de l'hôte", + "bookmarkList": "Favoris", + "zoomIn": "Zoom avant", + "zoomOut": "Zoom arrière", + "resetView": "Réinitialiser la vue", + "lockLayout": "Verrouiller la disposition", "unlockLayout": "Déverrouiller la disposition", "noWidgets": "Faites un clic droit ou cliquez sur + pour ajouter votre premier widget", "openFullView": "Ouvrir la vue complète", "previewTitle": "Aperçu de la page d'accueil", "cancel": "Annuler", - "save": "Sauvegarder", + "save": "Enregistrer", "title_label": "Titre", "widgetTitlePlaceholder": "Titre du widget (facultatif)", "url": "URL", - "imageUrl": "URL de l'image personnalisée", - "imageUrlHint": "Laissez ce champ vide pour utiliser automatiquement le favicon du site", + "imageUrl": "URL d'image personnalisée", + "imageUrlHint": "Laissez vide pour utiliser automatiquement le favicon du site", "showImage": "Afficher l'image", "description": "Description", "color": "Couleur", @@ -139,190 +139,190 @@ "expanded": "Développé par défaut", "timezone": "Fuseau horaire", "showSeconds": "Afficher les secondes", - "format12h": "12 heures", - "format24h": "24 heures", + "format12h": "Format 12 heures", + "format24h": "Format 24 heures", "content": "Contenu", - "backgroundColor": "Couleur de fond", + "backgroundColor": "Couleur d'arrière-plan", "host": "Hôte", - "showMetrics": "Afficher les statistiques", - "links": "Links", + "showMetrics": "Afficher les métriques", + "links": "Liens", "addLink": "Ajouter un lien", - "linkLabel": "Étiquette", + "linkLabel": "Libellé", "linkUrl": "URL", - "removeLink": "Retirer", - "categoryLinks": "Links", + "removeLink": "Supprimer", + "categoryLinks": "Liens", "categoryInfo": "Info", "categorySystem": "Système", "widgetServiceLinkDesc": "Une tuile cliquable renvoyant vers une URL de service", - "widgetFolderDesc": "Un conteneur pour regrouper les widgets associés", + "widgetFolderDesc": "Un conteneur pour regrouper des widgets liés", "widgetClockDesc": "Une horloge en temps réel avec fuseau horaire configurable", "widgetNotesDesc": "Un widget de notes Markdown", - "widgetHostStatusDesc": "Affiche en temps réel le processeur, la mémoire et le disque d'un hôte SSH", - "widgetBookmarkListDesc": "Liste de liens rapides", + "widgetHostStatusDesc": "Affiche en direct le CPU, la mémoire et le disque d'un hôte SSH", + "widgetBookmarkListDesc": "Une liste de liens rapides", "copyLink": "Copier le lien", "linkCopied": "Lien copié !", - "location": "Emplacement", + "location": "Localisation", "temperatureUnit": "Unité de température", - "showForecast": "Afficher les prévisions à 3 jours", + "showForecast": "Afficher les prévisions sur 3 jours", "scrolling": "Autoriser le défilement", "feedUrl": "URL du flux", - "maxItems": "Nombre maximal d'articles", + "maxItems": "Nombre max d'éléments", "showDescription": "Afficher la description", "widgetWeatherName": "Météo", - "widgetWeatherDesc": "Météo en direct pour n'importe quel endroit", + "widgetWeatherDesc": "Météo en direct pour n'importe quel lieu", "widgetIframeName": "Intégration iFrame", - "widgetIframeDesc": "Intégrez n'importe quelle URL dans une iframe", + "widgetIframeDesc": "Intègre n'importe quelle URL dans une iframe", "widgetRssName": "Flux RSS", - "widgetRssDesc": "Afficher les éléments d'un flux RSS ou Atom", - "dragToFolder": "Faites glisser des widgets ici ou utilisez + pour ajouter", + "widgetRssDesc": "Affiche les éléments d'un flux RSS ou Atom", + "dragToFolder": "Faites glisser les widgets ici ou utilisez + pour ajouter", "showDisk": "Afficher l'utilisation du disque", "addToFolder": "Ajouter le widget au dossier", "noHostSelected": "Aucun hôte sélectionné", "metricsNotAvailable": "Métriques non disponibles", - "selectHost": "Sélectionnez un hôte...", + "selectHost": "Sélectionner un hôte…", "displayedMetrics": "Métriques affichées", - "metricCpu": "Processeur", + "metricCpu": "CPU", "metricMemory": "Mémoire", "metricDisk": "Disque", - "metricUptime": "Disponibilité", + "metricUptime": "Uptime", "metricSystem": "Système", - "metricOs": "Système d'exploitation", + "metricOs": "OS", "metricKernel": "Noyau", - "metricHostname": "Nom d'hôte", + "metricHostname": "Nom d’hôte", "metricNetwork": "Réseau", "metricProcesses": "Processus", "metricProcessesTotal": "total", - "metricProcessesRunning": "en cours d'exécution", + "metricProcessesRunning": "en cours", "categoryMonitoring": "Surveillance", - "loading": "Chargement...", + "loading": "Chargement…", "noData": "Aucune donnée disponible", - "allClear": "Tout est clair", - "acknowledgeAlert": "Reconnaître", + "allClear": "Aucune alerte", + "acknowledgeAlert": "Acquitter", "noPingUrls": "Aucune URL configurée", - "pingLabel": "Étiquette", + "pingLabel": "Libellé", "addPingUrl": "Ajouter une URL", "showLatency": "Afficher la latence", - "refreshInterval": "Intervalle d'actualisation", + "refreshInterval": "Intervalle de rafraîchissement", "seconds": "secondes", - "filterActivityTypes": "Types de filtres", + "filterActivityTypes": "Filtrer les types", "filterTypesHint": "Laisser vide pour tout afficher", - "showTimestamp": "Afficher l'horodatage", - "uptimeUnavailable": "Disponibilité indisponible", - "uptimeLabel": "Disponibilité", + "showTimestamp": "Afficher l’horodatage", + "uptimeUnavailable": "Uptime non disponible", + "uptimeLabel": "Uptime", "overviewVersion": "Version", "overviewUpdate": "À jour", "overviewUpdateAvailable": "Mise à jour disponible", "overviewDatabase": "Base de données", - "overviewUptime": "Disponibilité", + "overviewUptime": "Uptime", "noHosts": "Aucun hôte configuré", "hostGridHosts": "Hôtes", - "hostGridAllHint": "Laisser vide pour afficher tous les hôtes", + "hostGridAllHint": "Laissez vide pour afficher tous les hôtes", "columns": "Colonnes", "showIp": "Afficher l'adresse IP", "connectionType": "Type de connexion", - "layout": "Mise en page", - "showStatus": "Afficher l'état", - "showHostName": "Afficher le nom de l'hôte", + "layout": "Disposition", + "showStatus": "Afficher le statut", + "showHostName": "Afficher le nom d'hôte", "noDockerActivity": "Aucune activité Docker", "noActivity": "Aucune activité", - "showAcknowledged": "Afficher la confirmation", + "showAcknowledged": "Afficher les acquittés", "showCurrentValue": "Afficher la valeur actuelle", "chartMetric": "Métrique", - "metricRange": "Gamme", - "widgetMetricsChartName": "Graphique des indicateurs", - "widgetMetricsChartDesc": "Graphique historique du processeur, de la mémoire, du disque ou du réseau pour un hôte", - "widgetHostGridName": "Grille hôte", - "widgetHostGridDesc": "Affichage sous forme de grille des états des hôtes SSH", + "metricRange": "Plage", + "widgetMetricsChartName": "Graphique de métriques", + "widgetMetricsChartDesc": "Graphique historique CPU, mémoire, disque ou réseau pour un hôte", + "widgetHostGridName": "Grille d'hôtes", + "widgetHostGridDesc": "Vue en grille des statuts des hôtes SSH", "widgetAlertFeedName": "Flux d'alertes", - "widgetAlertFeedDesc": "Tirs d'alerte en direct avec accusé de réception", - "widgetPingStatusName": "État du ping", - "widgetPingStatusDesc": "statut ping HTTP pour une ou plusieurs URL", + "widgetAlertFeedDesc": "Déclenchements d'alertes en direct avec acquittement", + "widgetPingStatusName": "Statut du ping", + "widgetPingStatusDesc": "Statut du ping HTTP pour une ou plusieurs URL", "widgetRecentActivityName": "Activité récente", - "widgetRecentActivityDesc": "Flux déroulant des activités récentes de Termix", + "widgetRecentActivityDesc": "Flux défilant de l'activité récente de Termix", "widgetTermixUptimeName": "Temps de disponibilité de Termix", - "widgetTermixUptimeDesc": "Compteur de disponibilité en temps réel pour le serveur Termix", - "widgetSystemOverviewName": "Présentation du système", - "widgetSystemOverviewDesc": "Aperçu de la version de Termix, de l'état de la base de données et de la disponibilité", + "widgetTermixUptimeDesc": "Compteur en direct du temps de disponibilité du serveur Termix", + "widgetSystemOverviewName": "Vue d'ensemble du système", + "widgetSystemOverviewDesc": "Version de Termix, santé de la base de données et temps de disponibilité en un coup d'œil", "widgetSshQuickConnectName": "Connexion rapide SSH", - "widgetSshQuickConnectDesc": "Boutons permettant d'ouvrir des sessions SSH en un clic", + "widgetSshQuickConnectDesc": "Boutons en un clic pour ouvrir des sessions SSH", "widgetDockerActivityName": "Activité Docker", - "widgetDockerActivityDesc": "Événements récents liés aux conteneurs Docker sur tous les hôtes", + "widgetDockerActivityDesc": "Événements récents des conteneurs Docker sur tous les hôtes", "widgetCalendarName": "Calendrier", - "widgetCalendarDesc": "Un calendrier mensuel avec la date du jour mise en évidence.", + "widgetCalendarDesc": "Un calendrier mensuel avec la date du jour en surbrillance", "widgetCountdownName": "Compte à rebours", "widgetCountdownDesc": "Compte à rebours jusqu'à une date cible", "widgetSearchBarName": "Barre de recherche", - "widgetSearchBarDesc": "Widget de recherche rapide sur le Web", + "widgetSearchBarDesc": "Widget de recherche rapide sur le web", "widgetTextBannerName": "Bannière de texte", - "widgetTextBannerDesc": "Une étiquette ou un titre de section en gras pour la toile", + "widgetTextBannerDesc": "Un libellé en gras ou un en-tête de section pour le canevas", "widgetImageWidgetName": "Image", "widgetImageWidgetDesc": "Afficher une image à partir d'une URL", "widgetMarkdownNotesName": "Notes Markdown", - "widgetMarkdownNotesDesc": "Notes enrichies avec rendu Markdown intégré", + "widgetMarkdownNotesDesc": "Notes enrichies avec rendu markdown en ligne", "widgetCustomApiName": "API personnalisée", - "widgetCustomApiDesc": "Récupérer et afficher des données à partir de n'importe quelle API JSON", + "widgetCustomApiDesc": "Récupérer et afficher des données depuis n'importe quelle API JSON", "widgetServiceGridName": "Grille de services", - "widgetServiceGridDesc": "Une grille configurable de liens vers les tuiles de service", + "widgetServiceGridDesc": "Une grille configurable de tuiles de liens de service", "widgetDashboardLinksName": "Liens du tableau de bord", - "widgetDashboardLinksDesc": "Affichez vos liens de service configurés depuis le tableau de bord", + "widgetDashboardLinksDesc": "Afficher vos liens de service configurés depuis le tableau de bord", "widgetSearchLinksName": "Raccourcis de recherche", "widgetSearchLinksDesc": "Boutons de raccourci de recherche rapide avec saisie intégrée", - "widgetLinkTreeName": "Arbre de liens", - "widgetLinkTreeDesc": "Sections de liens regroupées avec des titres", + "widgetLinkTreeName": "Arborescence de liens", + "widgetLinkTreeDesc": "Sections groupées de liens avec des titres", "calMon": "Lu", "calTue": "Ma", "calWed": "Me", - "calThu": "Jeu", + "calThu": "Je", "calFri": "Ve", "calSat": "Sa", "calSun": "Di", - "startOnMonday": "Début de la semaine lundi", - "countdownNoDate": "Aucune date cible n'a été fixée", + "startOnMonday": "Commencer la semaine le lundi", + "countdownNoDate": "Aucune date cible définie", "countdownPast": "L'événement est passé", "countdownDays": "jours", - "countdownHours": "heures", + "countdownHours": "h", "countdownMinutes": "min", - "countdownSeconds": "seconde", - "countdownLabel": "Étiquette", - "countdownLabelPlaceholder": "ex: Le jour du lancement", - "countdownShowDays": "Journées d'exposition", - "countdownShowHours": "Horaires d'ouverture", + "countdownSeconds": "s", + "countdownLabel": "Libellé", + "countdownLabelPlaceholder": "ex. : Jour de lancement", + "countdownShowDays": "Afficher les jours", + "countdownShowHours": "Afficher les heures", "targetDate": "Date cible", "searchEngine": "Moteur de recherche", "customSearchUrl": "URL de recherche personnalisée", - "searchPlaceholder": "Recherche...", - "searchGo": "Aller", - "searchPlaceholderLabel": "Texte d'espace réservé", + "searchPlaceholder": "Rechercher...", + "searchGo": "OK", + "searchPlaceholderLabel": "Texte indicatif", "searchPlaceholderHint": "Texte affiché dans le champ de recherche", "openInNewTab": "Ouvrir dans un nouvel onglet", - "searchQueryPlaceholder": "Saisissez votre requête...", + "searchQueryPlaceholder": "Saisir une requête...", "noSearchShortcuts": "Aucun raccourci configuré", "addSearchShortcut": "Ajouter un raccourci", "fontSize": "Taille de la police", "textAlign": "Alignement du texte", - "fontWeight": "Poids de la police", - "clearColor": "Couleur transparente", + "fontWeight": "Graisse de la police", + "clearColor": "Effacer la couleur", "imageFit": "Ajustement de l'image", "imageLinkUrl": "URL du lien", "noImage": "Aucune URL d'image définie", "altText": "Texte alternatif", - "altTextPlaceholder": "Décrivez l'image", - "renderMarkdown": "Afficher Markdown", + "altTextPlaceholder": "Décrire l'image", + "renderMarkdown": "Rendu Markdown", "displayMode": "Mode d'affichage", "displayField": "Champ d'affichage", "jsonPath": "Chemin JSON", "customApiLabel": "Étiquette", - "customApiLabelPlaceholder": "ex: La température", + "customApiLabelPlaceholder": "ex. : Température", "customApiUnit": "Unité", "customApiNoUrl": "Aucune URL d'API configurée", "customApiError": "Échec de la récupération", "customApiNotArray": "La réponse n'est pas un tableau", "addService": "Ajouter un service", "showLabels": "Afficher les étiquettes", - "iconSize": "Taille de l'icône", - "noDashboardLinks": "Aucun lien vers le tableau de bord n'est configuré", + "iconSize": "Taille des icônes", + "noDashboardLinks": "Aucun lien de tableau de bord configuré", "noLimit": "Aucune limite", - "sectionHeading": "Titre de section", + "sectionHeading": "Titre de la section", "addSection": "Ajouter une section", "compactMode": "Mode compact", "showDetailed": "Afficher les détails", @@ -337,8 +337,8 @@ "range1h": "1 heure", "range6h": "6 heures", "range24h": "24 heures", - "metricNetRx": "Téléchargement en ligne", - "metricNetTx": "Téléchargement réseau", + "metricNetRx": "Téléchargement réseau", + "metricNetTx": "Téléversement réseau", "severityFilter": "Filtre de gravité", "filterAll": "Tous", "accentColor": "Couleur d'accent", @@ -346,9 +346,9 @@ "widgetSshTerminalDesc": "Un terminal SSH intégré connecté à un hôte configuré", "sshTerminalNoHost": "Aucun hôte configuré", "sshTerminalConnect": "Connecter", - "sshTerminalAutoConnect": "Connexion automatique en charge", + "sshTerminalAutoConnect": "Connexion automatique au chargement", "widgetQuickConnectName": "Connexion rapide", - "widgetQuickConnectDesc": "Boutons de lancement en un clic pour tout type de connexion sur vos hôtes", + "widgetQuickConnectDesc": "Boutons de lancement en un clic pour tout type de connexion à vos hôtes", "connectionTypes": "Types de connexion", "connType_terminal": "Terminal", "connType_files": "Gestionnaire de fichiers", @@ -362,32 +362,71 @@ "widgetFileManagerDesc": "Gestionnaire de fichiers SFTP intégré pour un hôte configuré", "widgetDockerName": "Gestionnaire Docker", "widgetDockerDesc": "Gestionnaire de conteneurs Docker intégré pour un hôte configuré", - "widgetTunnelName": "Gestionnaire de tunnel", - "widgetTunnelDesc": "Gestionnaire de tunnel SSH intégré pour un hôte configuré", + "widgetTunnelName": "Gestionnaire de tunnels", + "widgetTunnelDesc": "Gestionnaire de tunnels SSH intégré pour un hôte configuré", "widgetNoHostSelected": "Aucun hôte configuré" }, "serverConfig": { "title": "Configuration du serveur", - "description": "Configurez l'URL du serveur Termix pour vous connecter à vos services backend", + "description": "Configurer l'URL du serveur Termix pour se connecter à vos services backend", "serverUrl": "URL du serveur", - "enterServerUrl": "Veuillez saisir une URL de serveur", + "enterServerUrl": "Veuillez saisir l'URL du serveur", "saveFailed": "Échec de l'enregistrement de la configuration", "saveError": "Erreur lors de l'enregistrement de la configuration", - "saving": "Économie...", + "saving": "Enregistrement...", "saveConfig": "Enregistrer la configuration", - "helpText": "Saisissez l'URL où votre serveur Termix est exécuté (par exemple, http://localhost:30001 ou https://votre-serveur.com)", + "helpText": "Entrez l'URL où votre serveur Termix est en cours d'exécution (par ex., 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 les certificats invalides", + "allowInvalidCertificateDesc": "À utiliser uniquement pour les serveurs auto-hébergés de confiance avec des certificats auto-signés ou 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", + "savedServers": "Serveurs enregistrés", + "noSavedServers": "Aucun serveur enregistré", + "removeServer": "Supprimer" + }, + "migrationNotice": { + "title": "Termix Desktop fonctionne désormais de manière autonome.", + "body1": "Cette application se connectait auparavant directement à votre serveur Termix. Elle a été repensée pour fonctionner de manière totalement autonome, en stockant localement les hôtes et les identifiants afin de rester opérationnelle hors ligne. La synchronisation bidirectionnelle avec un serveur Termix auto-hébergé est désormais optionnelle.", + "body2": "Vos hôtes et identifiants actuels sont toujours sur {{url}}. Activez la synchronisation à distance et reconnectez-vous à ce serveur pour les restaurer et les maintenir synchronisés.", + "dismiss": "Pas maintenant", + "setUpSync": "Configurer la synchronisation à distance" + }, + "remoteSync": { + "title": "Synchronisation à distance", + "description": "Vous pouvez connecter cette application de bureau à un serveur Termix auto-hébergé pour synchroniser vos hôtes, identifiants et extraits de code sur tous vos appareils. L'application fonctionne toujours entièrement hors ligne, que vous soyez connecté ou non.", + "notConnected": "Non connecté", + "connected": "Connecté", + "connectedTo": "Connecté à {{url}}", + "lastSynced": "Dernière synchronisation {{time}}", + "neverSynced": "Jamais synchronisé", + "syncError": "Erreur de synchronisation : {{message}}", + "needsReauth": "Connexion expirée", + "connectButton": "Se connecter au serveur", + "disconnectButton": "Déconnecter", + "syncNowButton": "Synchroniser maintenant", + "syncing": "Synchronisation en cours...", + "serverUrl": "URL du serveur", + "enterServerUrl": "Veuillez saisir une URL de serveur", + "mustIncludeProtocol": "L'URL du serveur doit commencer par http:// ou https://", + "connectionTestFailed": "Impossible de joindre un serveur Termix à cette URL.", "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écutez Termix avec le serveur local intégré (aucun serveur distant n'est nécessaire)", - "embeddedConnecting": "Connexion au serveur local...", - "embeddedNotReady": "Le serveur local n'est pas encore prêt. Veuillez patienter quelques instants et réessayer.", - "localServer": "Serveur local", "savedServers": "Serveurs sauvegardés", - "noSavedServers": "Aucun serveur enregistré", - "removeServer": "Retirer" + "removeServer": "Retirer", + "continueButton": "Continuer", + "cancelButton": "Annuler", + "signInTitle": "Connectez-vous à {{url}}", + "originTitle": "Origine de la connexion", + "originDescription": "Choisissez l'origine par défaut des connexions SSH. Ce paramètre peut être modifié pour chaque hôte.", + "originLocal": "Cet appareil (réseau local)", + "originRemote": "Serveur distant", + "bannerReconnect": "Reconnect", + "bannerMessage": "La synchronisation à distance nécessite une réauthentification." }, "versionCheck": { "error": "Erreur de vérification de version", @@ -395,55 +434,55 @@ "upToDate": "L'application est à jour", "currentVersion": "Vous utilisez la version {{version}}", "updateAvailable": "Mise à jour disponible", - "newVersionAvailable": "Une nouvelle version est disponible ! Vous utilisez la version {{current}}, mais la version {{latest}} est disponible.", + "newVersionAvailable": "Une nouvelle version est disponible ! Vous utilisez {{current}}, mais la version {{latest}} est disponible.", "betaVersion": "Version bêta", - "betaVersionDesc": "Vous utilisez {{current}}, qui est plus récent que la dernière version stable {{latest}}.", - "releasedOn": "Sorti le {{date}}", + "betaVersionDesc": "Vous utilisez {{current}}, qui est plus récente que la dernière version stable {{latest}}.", + "releasedOn": "Publiée le {{date}}", "downloadUpdate": "Télécharger la mise à jour", - "checking": "Recherche de mises à jour...", - "checkUpdates": "Vérifier les mises à jour", - "checkingUpdates": "Recherche de mises à jour...", + "checking": "Recherche des mises à jour...", + "checkUpdates": "Rechercher des mises à jour", + "checkingUpdates": "Recherche des mises à jour...", "updateRequired": "Mise à jour requise" }, "common": { "close": "Fermer", - "minimize": "Minimiser", + "minimize": "Réduire", "online": "En ligne", "offline": "Hors ligne", "unknown": "Inconnu", "continue": "Continuer", - "maintenance": "Entretien", + "maintenance": "Maintenance", "degraded": "Dégradé", "error": "Erreur", "warning": "Avertissement", "unsavedChanges": "Modifications non enregistrées", - "dismiss": "Rejeter", + "dismiss": "Ignorer", "loading": "Chargement...", "optional": "Facultatif", "connect": "Connecter", "copied": "Copié", - "connecting": "De liaison...", + "connecting": "Connexion en cours...", "updateAvailable": "Mise à jour disponible", "appName": "Termix", "openInNewTab": "Ouvrir dans un nouvel onglet", - "noReleases": "Aucune sortie", - "updatesAndReleases": "Mises à jour et nouvelles versions", + "noReleases": "Aucune version disponible", + "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é-lancement", + "failedToFetchUpdateInfo": "Échec de la récupération des informations de mise à jour", + "preRelease": "Préversion", "noReleasesFound": "Aucune version trouvée.", "cancel": "Annuler", "username": "Nom d'utilisateur", "login": "Se connecter", - "logout": "Déconnexion", - "register": "Registre", + "logout": "Se déconnecter", + "register": "S'inscrire", "password": "Mot de passe", - "confirmPassword": "Confirmez le mot de passe", - "back": "Dos", - "save": "Sauvegarder", - "saving": "Économie...", + "confirmPassword": "Confirmer le mot de passe", + "back": "Retour", + "save": "Enregistrer", + "saving": "Enregistrement...", "delete": "Supprimer", - "rename": "Rebaptiser", + "rename": "Renommer", "edit": "Modifier", "add": "Ajouter", "confirm": "Confirmer", @@ -451,41 +490,41 @@ "or": "OU", "next": "Suivant", "previous": "Précédent", - "refresh": "Rafraîchir", + "refresh": "Actualiser", "language": "Langue", "checking": "Vérification...", "checkingDatabase": "Vérification de la connexion à la base de données...", "checkingAuthentication": "Vérification de l'authentification...", - "backendReconnected": "La connexion au serveur a été rétablie.", + "backendReconnected": "Connexion au serveur rétablie", "connectionDegraded": "Connexion au serveur perdue, récupération en cours…", "reload": "Recharger", "remove": "Retirer", "create": "Créer", - "update": "Mise à jour", - "copy": "Copie", - "copyFailed": "Impossible de copier dans le presse-papiers", - "maximize": "Maximiser", + "update": "Mettre à jour", + "copy": "Copier", + "copyFailed": "Échec de la copie dans le presse-papiers.", + "maximize": "Agrandir", "restore": "Restaurer", - "of": "de", + "of": "sur", "saved": "Enregistré", "deleted": "Supprimé", "deleteFailed": "Échec de la suppression", - "saveFailed": "Échec de la sauvegarde", - "required": "Requis" + "saveFailed": "Échec de l'enregistrement", + "required": "Obligatoire" }, "nav": { - "home": "Maison", + "home": "Accueil", "terminal": "Terminal", "docker": "Docker", "tunnels": "Tunnels", "fileManager": "Gestionnaire de fichiers", "serverStats": "Métriques de l'hôte", "hostMetrics": "Métriques de l'hôte", - "admin": "Administrateur", - "termixId": "IDENTIFIANT", + "admin": "Administration", + "termixId": "ID", "userProfile": "Profil utilisateur", - "splitScreen": "Écran partagé", - "confirmClose": "Fermer cette session active ?", + "splitScreen": "Diviser l'écran", + "confirmClose": "Fermer cette session active ?", "close": "Fermer", "cancel": "Annuler", "sshManager": "Gestionnaire SSH", @@ -498,22 +537,22 @@ "failedToCopyPassword": "Échec de la copie du mot de passe", "refreshTab": "Actualiser la connexion", "renameTab": "Renommer l'onglet", - "openFileManager": "Gestionnaire de fichiers ouvert", + "openFileManager": "Ouvrir le gestionnaire de fichiers", "dashboard": "Tableau de bord", - "networkGraph": "Graphe de réseau", + "networkGraph": "Graphique réseau", "tmuxMonitor": "Moniteur Tmux", - "homepage": "Page d'accueil", + "homepage": "Accueil", "quickConnect": "Connexion rapide", "sshTools": "Outils SSH", - "history": "Histoire", + "history": "Historique", "sessionLogs": "Journaux de session", "hosts": "Hôtes", "snippets": "Extraits", "hostManager": "Gestionnaire d'hôtes", - "credentials": "Informations d'identification", - "connections": "Relations", + "credentials": "Identifiants", + "connections": "Connexions", "alerts": "Alertes", - "serial": "En série", + "serial": "Série", "roleAdministrator": "Administrateur", "roleUser": "Utilisateur" }, @@ -521,13 +560,13 @@ "hosts": "Hôtes", "noHosts": "Aucun hôte SSH", "retry": "Réessayer", - "refresh": "Rafraîchir", - "optional": "Facultatif", + "refresh": "Actualiser", + "optional": "Optionnel", "downloadSample": "Télécharger un exemple", "failedToDeleteHost": "Échec de la suppression de {{name}}", - "importSkipExisting": "Importer (ignorer les éléments existants)", + "importSkipExisting": "Importer (ignorer les existants)", "importSSHConfig": "Importer depuis la configuration SSH", - "connectionDetails": "Détails de connexion", + "connectionDetails": "Détails de la connexion", "ssh": "SSH", "telnet": "Telnet", "remoteDesktop": "Bureau à distance", @@ -535,21 +574,21 @@ "username": "Nom d'utilisateur", "folder": "Dossier", "tags": "Étiquettes", - "pin": "Épingle", + "pin": "Épingler", "addHost": "Ajouter un hôte", "editHost": "Modifier l'hôte", - "cloneHost": "Hôte clone", + "cloneHost": "Dupliquer 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": "Connexion", - "upload": "Télécharger", + "upload": "Téléverser", "authentication": "Authentification", "password": "Mot de passe", "key": "Clé", - "credential": "Attestation", + "credential": "Identifiant", "none": "Aucun", "sshPrivateKey": "Clé privée SSH", "keyType": "Type de clé", @@ -572,14 +611,14 @@ "fileManager": "Gestionnaire de fichiers", "serverStats": "Métriques de l'hôte", "status": "Statut", - "folderRenamed": "Le dossier «{{oldName}}» a été renommé en «{{newName}}» avec succès", - "failedToRenameFolder": "Impossible de renommer le dossier", - "movedToFolder": "Déplacement de {{count}} hôte(s) vers \"{{folder}}\"", + "folderRenamed": "Dossier « {{oldName}} » renommé en « {{newName}} » avec succès", + "failedToRenameFolder": "Échec du renommage du dossier", + "movedToFolder": "{{count}} hôte(s) déplacé(s) vers \"{{folder}}\"", "editHostTooltip": "Modifier l'hôte", - "statusChecks": "Vérifications de statut", - "metricsCollection": "Collecte de mesures", + "statusChecks": "Vérifications d'état", + "metricsCollection": "Collecte de métriques", "metricsInterval": "Intervalle de collecte des métriques", - "metricsIntervalDesc": "Fréquence de collecte des statistiques du serveur (5s - 1h)", + "metricsIntervalDesc": "Fréquence de collecte des statistiques du serveur (5 s - 1 h)", "behavior": "Comportement", "themePreview": "Aperçu du thème", "theme": "Thème", @@ -590,20 +629,21 @@ "cursorStyle": "Style de curseur", "cursorBlink": "Clignotement du curseur", "scrollbackBuffer": "Tampon de défilement arrière", - "bellStyle": "Style de cloche", - "rightClickSelectsWord": "Clic droit Sélectionner le mot", + "bellStyle": "Style de sonnerie", + "rightClickSelectsWord": "Sélection de mot par clic droit", "fastScrollModifier": "Modificateur de défilement rapide", - "fastScrollSensitivity": "Sensibilité au défilement rapide", + "fastScrollSensitivity": "Sensibilité du défilement rapide", "sshAgentForwarding": "Transfert d'agent SSH", - "backspaceMode": "Mode de retour arrière", + "backspaceMode": "Mode Retour arrière", "startupSnippet": "Extrait de démarrage", "selectSnippet": "Sélectionner un extrait", - "forceKeyboardInteractive": "Forcer l'interaction au clavier", - "overrideCredentialUsername": "Remplacer le nom d'utilisateur d'identification", - "overrideCredentialUsernameDesc": "Utilisez le nom d'utilisateur indiqué ci-dessus au lieu du nom d'utilisateur des identifiants", + "forceKeyboardInteractive": "Forcer Keyboard-Interactive", + "overrideCredentialUsername": "Remplacer le nom d'utilisateur des identifiants", + "overrideCredentialUsernameDesc": "Utiliser le nom d'utilisateur spécifié ci-dessus au lieu de celui des identifiants", "oidcUsernameHint": "Utilisez $oidc.preferred_username pour remplacer votre nom de connexion OIDC.", - "jumpHostChain": "Chaîne d'hôtes de saut", - "portKnocking": "Tocage à la Porte", + "tailscaleUsernameHint": "Il doit s'agir d'un utilisateur Unix auquel votre identité Tailscale est attribuée dans la liste de contrôle d'accès SSH du réseau Tailnet, pas nécessairement root.", + "jumpHostChain": "Chaîne de Jump Hosts", + "portKnocking": "Port Knocking", "addKnock": "Ajouter un port", "addProxyNode": "Ajouter un nœud", "proxyNode": "Nœud proxy", @@ -611,133 +651,138 @@ "quickActions": "Actions rapides", "sudoPasswordAutoFill": "Remplissage automatique du mot de passe sudo", "sudoPassword": "Mot de passe sudo", - "keepaliveInterval": "Intervalle de maintien de connexion (ms)", - "moshCommand": "Commandement MOSH", - "environmentVariables": "Variables environnementales", + "keepaliveInterval": "Intervalle keepalive (ms)", + "moshCommand": "Commande MOSH", + "environmentVariables": "Variables d'environnement", "addVariable": "Ajouter une variable", "docker": "Docker", "copyTerminalUrl": "Copier l'URL du terminal", "copyFileManagerUrl": "Copier l'URL du gestionnaire de fichiers", - "copyRemoteDesktopUrl": "Copier l'URL du bureau à distance", - "failedToConnect": "Impossible de se connecter à la console", + "copyRemoteDesktopUrl": "Copier l'URL Remote Desktop", + "failedToConnect": "Échec de la connexion à la console", "connect": "Connecter", "disconnect": "Déconnecter", - "start": "Commencer", + "start": "Démarrer", "enableStatusCheck": "Activer la vérification de l'état", - "enableMetrics": "Activer les indicateurs", + "enableMetrics": "Activer les métriques", "bulkUpdateFailed": "Échec de la mise à jour groupée", "selectAll": "Tout sélectionner", - "deselectAll": "Désélectionnez tout", + "deselectAll": "Tout désélectionner", "protocols": "Protocoles", - "secureShell": "Coquille sécurisée", + "secureShell": "Secure Shell", "virtualNetwork": "Réseau virtuel", "unencryptedShell": "Shell non chiffré", "addressIp": "Adresse / IP", - "friendlyName": "Nom amical", + "friendlyName": "Nom convivial", "macAddress": "Adresse MAC", "wolBroadcastAddress": "Adresse de diffusion WoL", - "wolBroadcastAddressDesc": "Diffusion dirigée optionnelle pour les réseaux Docker/routés (ex. : 192.168.1.255). Laisser vide pour utiliser 255.255.255.255.", - "folderAndAdvanced": "Dossier et options avancées", + "wolBroadcastAddressDesc": "Diffusion dirigée optionnelle pour les réseaux Docker/routés (ex. 192.168.1.255). Laissez vide pour utiliser 255.255.255.255.", + "folderAndAdvanced": "Dossier et Avancé", "privateNotes": "Notes privées", - "privateNotesPlaceholder": "Détails concernant ce serveur...", + "privateNotesPlaceholder": "Détails concernant ce serveur…", "pinToTop": "Épingler en haut", - "pinToTopDesc": "Afficher toujours cet hôte en haut de la liste", - "portKnockingSequence": "Séquence de frappe aux ports", - "addKnockBtn": "Ajouter Knock", - "noPortKnocking": "Aucune configuration de port knocking.", - "knockPort": "Port de frappe", + "pinToTopDesc": "Toujours afficher cet hôte en haut de la liste", + "portKnockingSequence": "Séquence de Port Knocking", + "addKnockBtn": "Ajouter un knock", + "noPortKnocking": "Aucun port knocking configuré.", + "knockPort": "Port de knock", "protocol": "Protocole", "delayAfterMs": "Délai après (ms)", "useSocks5Proxy": "Utiliser un proxy SOCKS5", - "useSocks5ProxyDesc": "Connexion acheminée via un serveur proxy", + "useSocks5ProxyDesc": "Acheminer la connexion via un serveur proxy", + "connectionOrigin": "Origine de la connexion", + "connectionOriginDesc": "Origine de la connexion SSH de cet hôte. Remplace la valeur par défaut globale de l'application de bureau.", + "connectionOriginDefault": "Utiliser la valeur par défaut", + "connectionOriginLocal": "Cet appareil (réseau local)", + "connectionOriginRemote": "Serveur distant", "proxyHost": "Hôte proxy", "proxyPort": "Port proxy", - "proxyUsername": "Nom d'utilisateur du proxy", - "proxyPassword": "Mot de passe du proxy", - "proxySingleMode": "Procuration unique", + "proxyUsername": "Nom d'utilisateur proxy", + "proxyPassword": "Mot de passe proxy", + "proxySingleMode": "Proxy unique", "proxyChainMode": "Chaîne de proxy", - "you": "Toi", - "jumpHostChainLabel": "Chaîne d'hôtes de saut", - "addJumpBtn": "Ajouter un saut", - "noJumpHosts": "Aucun serveur de rebond configuré.", - "selectAServer": "Sélectionnez un serveur...", + "you": "Vous", + "jumpHostChainLabel": "Chaîne de rebonds", + "addJumpBtn": "Ajouter un rebond", + "noJumpHosts": "Aucun hôte de rebond configuré.", + "selectAServer": "Sélectionner un serveur...", "sshPort": "Port SSH", "authMethod": "Méthode d'authentification", - "storedCredential": "Identifiants stockés", - "selectACredential": "Sélectionnez une qualification...", - "vaultProfile": "Profil du signataire du coffre-fort", - "selectAVaultProfile": "Sélectionnez un profil Vault...", - "vaultProfileHint": "Les paramètres proviennent du profil partagé ; vous vous connecterez à Vault via OIDC. Aucun secret n'est stocké.", + "storedCredential": "Identifiant enregistré", + "selectACredential": "Sélectionner un identifiant...", + "vaultProfile": "Profil de signature Vault", + "selectAVaultProfile": "Sélectionner un profil Vault...", + "vaultProfileHint": "Les paramètres proviennent du profil partagé ; vous vous connecterez à Vault via OIDC lors de la connexion. Aucun secret n'est stocké.", "vaultNewProfile": "Nouveau profil", "vaultManageProfiles": "Gérer les profils Vault", - "vaultAddrLabel": "Adresse du coffre-fort", + "vaultAddrLabel": "Adresse Vault", "vaultNamespaceLabel": "Espace de noms", - "vaultOidcMountLabel": "OIDC Auth Mount", - "vaultOidcRoleLabel": "Rôle de l'OIDC", - "vaultSshMountLabel": "Secrets SSH Monter", - "vaultSshRoleLabel": "Rôle de signataire SSH", - "vaultValidPrincipalsLabel": "Principes valides", + "vaultOidcMountLabel": "Point de montage d'authentification OIDC", + "vaultOidcRoleLabel": "Rôle OIDC", + "vaultSshMountLabel": "Point de montage des secrets SSH", + "vaultSshRoleLabel": "Rôle de signature SSH", + "vaultValidPrincipalsLabel": "Principaux valides", "vaultKeyTypeLabel": "Type de clé éphémère", "vaultSharedLabel": "Partager avec tous les utilisateurs", "vaultCreateProfile": "Créer un profil", "vaultProfileCreated": "Profil Vault créé", "vaultProfileSaved": "Profil Vault enregistré", "vaultProfileDeleted": "Profil Vault supprimé", - "vaultProfileSaveFailed": "Impossible d'enregistrer le profil Vault", + "vaultProfileSaveFailed": "Échec de l'enregistrement du profil Vault", "vaultProfileDeleteFailed": "Échec de la suppression du profil Vault", "vaultSaveProfile": "Enregistrer le profil", - "vaultProfileValidationError": "Le nom, l'adresse du coffre-fort et le rôle de signataire SSH sont requis.", - "vaultNoProfiles": "Aucun profil Vault pour le moment.", - "vaultSharedBadge": "commun", + "vaultProfileValidationError": "Le nom, l'adresse Vault et le rôle de signataire SSH sont requis", + "vaultNoProfiles": "Aucun profil Vault pour le moment", + "vaultSharedBadge": "partagé", "keyTypeLabel": "Type de clé", "keyTypeAuto": "Détection automatique", "keyPasteTab": "Coller", - "keyUploadTab": "Télécharger", - "keyFileLoaded": "Fichier clé chargé", - "keyUploadClick": "Cliquez pour téléverser un fichier .pem / .key / .ppk", - "clearKey": "Touche Effacer", + "keyUploadTab": "Importer", + "keyFileLoaded": "Fichier de clé chargé", + "keyUploadClick": "Cliquer pour importer .pem / .key / .ppk", + "clearKey": "Effacer la clé", "keySaved": "Clé SSH enregistrée", - "keyReplaceNotice": "Collez une nouvelle clé ci-dessous pour la remplacer.", - "keyPassphraseSaved": "Mot de passe enregistré, saisissez-le pour le modifier", - "replaceKey": "Clé de remplacement", - "docsLink": "Consulter les documents", + "keyReplaceNotice": "Coller une nouvelle clé ci-dessous pour la remplacer", + "keyPassphraseSaved": "Phrase secrète enregistrée, saisissez pour modifier", + "replaceKey": "Remplacer la clé", + "docsLink": "Voir la documentation", "opksshLabel": "OPKSSH", - "opksshDesc": "Connectez-vous à cet hôte en utilisant votre fournisseur d'identité plutôt qu'un mot de passe ou une clé. Nécessite la configuration d'OPKSSH sur le serveur.", - "warpgateLabel": "Porte de distorsion", - "warpgateDesc": "Cet hôte se connecte via un proxy SSH Warpgate. Termix gérera automatiquement le processus d'approbation via navigateur après l'authentification.", + "opksshDesc": "Connectez-vous à cet hôte en utilisant votre fournisseur d'identité au lieu d'un mot de passe ou d'une clé. Nécessite OPKSSH configuré sur le serveur.", + "warpgateLabel": "Warpgate Gateway", + "warpgateDesc": "Cet hôte se connecte via un proxy SSH Warpgate. Termix gérera automatiquement le flux d'approbation basé sur le navigateur après l'authentification.", "agentLabel": "Agent SSH", - "agentDesc": "Authentifiez-vous à l'aide d'un agent SSH exécuté sur le serveur Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Cet agent doit être installé sur la machine hébergeant Termix.", + "agentDesc": "Authentifiez-vous en utilisant un agent SSH exécuté sur le serveur Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agent doit être en cours d'exécution sur la machine où Termix est hébergé.", "agentSocketPathLabel": "Chemin du socket de l'agent", "agentSocketPathPlaceholder": "Laisser vide pour utiliser SSH_AUTH_SOCK", - "agentSocketPathHint": "Laissez ce champ vide pour que la variable d'environnement SSH_AUTH_SOCK soit détectée automatiquement, ou saisissez un chemin de socket personnalisé (par exemple /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "Sélectionnez le dispositif Tailscale", - "tailscaleDeviceSelectPlaceholder": "Sélectionnez un appareil...", - "tailscaleNoApiKey": "Aucune clé API Tailscale n'est configurée. Ajoutez-en une dans les paramètres d'administration pour activer la détection des appareils.", - "tailscaleDocsLink": "Consulter les documents", - "tailscaleLoadingDevices": "Chargement des périphériques...", - "tailscaleNoDevices": "Aucun périphérique trouvé dans votre réseau.", - "tailscaleDeviceAutoFill": "La sélection d'un appareil renseignera automatiquement l'adresse IP de l'hôte.", - "forceKeyboardInteractiveLabel": "Forcer le clavier interactif", - "forceKeyboardInteractiveShortDesc": "Exiger la saisie manuelle du mot de passe même si des clés sont présentes", - "allowLegacyAlgorithmsLabel": "Autoriser les algorithmes hérités", - "allowLegacyAlgorithmsDesc": "Activez les algorithmes SSH obsolètes (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) pour les connexions aux anciens périphériques qui ne peuvent pas être mis à niveau.", - "insecure": "Précaire", + "agentSocketPathHint": "Laisser vide pour détecter automatiquement à partir de la variable d'environnement SSH_AUTH_SOCK, ou saisir un chemin de socket personnalisé (ex. /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Sélectionner un appareil Tailscale", + "tailscaleDeviceSelectPlaceholder": "Sélectionner un appareil...", + "tailscaleNoApiKey": "Aucune clé API Tailscale configurée. Ajoutez-en une dans les paramètres d'administration pour activer la découverte d'appareils.", + "tailscaleDocsLink": "Voir la documentation", + "tailscaleLoadingDevices": "Chargement des appareils...", + "tailscaleNoDevices": "Aucun appareil trouvé dans votre tailnet.", + "tailscaleDeviceAutoFill": "La sélection d’un appareil remplira automatiquement l’adresse IP de l’hôte.", + "forceKeyboardInteractiveLabel": "Forcer l’interaction clavier", + "forceKeyboardInteractiveShortDesc": "Forcer la saisie manuelle du mot de passe même si des clés sont présentes", + "allowLegacyAlgorithmsLabel": "Autoriser les algorithmes obsolètes", + "allowLegacyAlgorithmsDesc": "Activer les algorithmes SSH obsolètes (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) pour les connexions à d’anciens appareils ne pouvant être mis à niveau.", + "insecure": "Non sécurisé", "terminalAppearance": "Apparence du terminal", "colorTheme": "Thème de couleurs", - "fontFamilyLabel": "Famille de polices", - "fontSizeLabel": "Taille de la police", - "cursorStyleLabel": "Style de curseur", + "fontFamilyLabel": "Police de caractères", + "fontSizeLabel": "Taille de police", + "cursorStyleLabel": "Style du curseur", "letterSpacingPx": "Espacement des lettres (px)", "lineHeightLabel": "Hauteur de ligne", - "bellStyleLabel": "Style de cloche", + "bellStyleLabel": "Style du bip", "backspaceModeLabel": "Mode de retour arrière", "cursorBlinking": "Clignotement du curseur", - "cursorBlinkingDesc": "Activer l'animation de clignotement du curseur du terminal", - "rightClickSelectsWordLabel": "Clic droit Sélectionner le mot", - "rightClickSelectsWordShortDesc": "Sélectionnez le mot sous le curseur en cliquant avec le bouton droit.", - "backgroundImageLabel": "URL de l'image de fond", - "backgroundImageDesc": "URL facultative pour une image de fond de terminal", - "backgroundImageOpacityLabel": "Opacité de l'image de fond", + "cursorBlinkingDesc": "Activer l’animation de clignotement pour le curseur du terminal", + "rightClickSelectsWordLabel": "Clic droit sélectionne le mot", + "rightClickSelectsWordShortDesc": "Sélectionne le mot sous le curseur au clic droit", + "backgroundImageLabel": "URL de l’image d’arrière-plan", + "backgroundImageDesc": "URL facultative pour une image d’arrière-plan du terminal", + "backgroundImageOpacityLabel": "Opacité de l’image d’arrière-plan", "customThemeColors": "Couleurs personnalisées", "customThemeBackground": "Arrière-plan", "customThemeForeground": "Premier plan", @@ -754,277 +799,294 @@ "customThemeCyan": "Cyan", "customThemeWhite": "Blanc", "customThemeBrightBlack": "Noir brillant", - "customThemeBrightRed": "Rouge vif", - "customThemeBrightGreen": "Vert vif", - "customThemeBrightYellow": "Jaune vif", - "customThemeBrightBlue": "Bleu vif", - "customThemeBrightMagenta": "Magenta vif", + "customThemeBrightRed": "Rouge brillant", + "customThemeBrightGreen": "Vert brillant", + "customThemeBrightYellow": "Jaune brillant", + "customThemeBrightBlue": "Bleu brillant", + "customThemeBrightMagenta": "Magenta brillant", "customThemeBrightCyan": "Cyan brillant", - "customThemeBrightWhite": "Blanc éclatant", - "customThemeResetTooltip": "Réinitialiser les paramètres par défaut", - "syntaxHighlightingLabel": "Mise en évidence syntaxique", - "syntaxHighlightingDesc": "Colorer la sortie du terminal (erreurs, chemins, adresses IP, horodatages)", - "syntaxHighlightingCategories": "Catégories principales", - "syntaxHighlightingCategoriesDesc": "Choisissez les types de contenu à coloriser", - "syntaxCategoryLogLevels": "Niveaux de logarithme", - "syntaxCategoryLogLevelsDesc": "erreur, avertissement, information, débogage, fatal", - "syntaxCategoryPaths": "Chemins d'accès aux fichiers", + "customThemeBrightWhite": "Blanc brillant", + "customThemeResetTooltip": "Réinitialiser aux valeurs par défaut", + "savedThemesLabel": "Thèmes enregistrés", + "saveAsGlobalTheme": "Enregistrer comme thème global", + "saveGlobalThemeNamePrompt": "Saisissez un nom pour ce thème", + "saveGlobalThemeSuccess": "Thème enregistré", + "saveGlobalThemeError": "Impossible d'enregistrer le thème", + "applyGlobalThemeTooltip": "Appliquer ce thème", + "deleteGlobalThemeTooltip": "Supprimer ce thème", + "noSavedThemes": "Aucun thème enregistré pour le moment", + "syntaxHighlightingLabel": "Coloration syntaxique", + "syntaxHighlightingDesc": "Coloriser la sortie du terminal (erreurs, chemins, adresses IP, horodatages)", + "syntaxHighlightingCategories": "Catégories de coloration", + "syntaxHighlightingCategoriesDesc": "Sélectionnez les types de contenu à coloriser", + "syntaxCategoryLogLevels": "Niveaux de log", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "Chemins de fichiers", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", - "syntaxCategoryTimestamps": "Horodatage", - "syntaxCategoryTimestampsDesc": "[12:34:56], 15 janvier 2024", + "syntaxCategoryTimestamps": "Horodatages", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "Adresses IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", - "syntaxCategoryUrls": "URL", - "syntaxCategoryUrlsDesc": "https://exemple.com", - "syntaxCategoryNumbers": "Numéros étiquetés", - "syntaxCategoryNumbersDesc": "port 8080, sortie 1, statut 404", - "behaviorAndAdvanced": "Comportement et compétences avancées", - "scrollbackBufferLabel": "Tampon de défilement arrière", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Nombres étiquetés", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Comportement et options avancées", + "scrollbackBufferLabel": "Mémoire de défilement", "scrollbackMaxLines": "Nombre maximal de lignes conservées dans l'historique", "sshAgentForwardingLabel": "Transfert d'agent SSH", - "sshAgentForwardingShortDesc": "Transmettez vos clés SSH locales à cet hôte.", + "sshAgentForwardingShortDesc": "Transmettez vos clés SSH locales à cet hôte", "useSSHTitleLabel": "Utiliser le titre de la fenêtre SSH", - "useSSHTitleDesc": "Mettez à jour le titre de l'onglet à partir du titre de la fenêtre du shell au lieu du nom d'hôte.", - "enableAutoMosh": "Activer le mosh automatique", - "enableAutoMoshDesc": "Privilégiez Mosh à SSH si disponible.", + "useSSHTitleDesc": "Mettre à jour le titre de l'onglet à partir du titre de la fenêtre du shell au lieu du nom d'hôte", + "enableAutoMosh": "Activer Auto-Mosh", + "enableAutoMoshDesc": "Préférer Mosh à SSH si disponible", "enableAutoTmux": "Activer Auto-Tmux", - "enableAutoTmuxDesc": "Lancer automatiquement une session tmux ou s'y connecter.", - "enableSessionLogging": "Journalisation des sessions", - "enableSessionLoggingDesc": "Enregistrez la sortie de la session terminale pour consultation ultérieure.", + "enableAutoTmuxDesc": "Lancer ou rejoindre automatiquement une session tmux", + "enableSessionLogging": "Journalisation de session", + "enableSessionLoggingDesc": "Enregistrer la sortie de la session terminal pour une consultation ultérieure", + "allowSessionSharing": "Autoriser le partage de session", + "allowSessionSharingDesc": "Autoriser le partage des sessions en direct sur cet hébergeur via un lien ou avec d'autres utilisateurs", "enableCommandHistory": "Historique des commandes", - "enableCommandHistoryDesc": "Enregistrez les commandes exécutées dans ce terminal pour l'historique et la saisie semi-automatique.", - "linkClickBehaviorLabel": "Comportement de clic sur les liens", - "linkClickBehaviorDesc": "Contrôle le comportement du navigateur lorsque vous cliquez sur un lien dans le terminal. Utilisez « Par défaut » pour conserver le paramètre de l'application.", - "linkClickBehaviorDefault": "Par défaut (suivre les paramètres de l'application)", + "enableCommandHistoryDesc": "Enregistrer les commandes exécutées dans ce terminal pour l'historique et l'autocomplétion", + "linkClickBehaviorLabel": "Comportement au clic sur un lien", + "linkClickBehaviorDesc": "Contrôle ce qui se passe lorsque vous cliquez sur un lien dans le terminal. Utilisez « Par défaut » pour suivre le paramètre de l'application.", + "linkClickBehaviorDefault": "Par défaut (suivre le paramètre de l'application)", "linkClickBehaviorConfirm": "Afficher une fenêtre contextuelle pour ouvrir ou copier", "linkClickBehaviorDirect": "Ouvrir directement", - "sudoPasswordAutoFillLabel": "Remplissage automatique du mot de passe Sudo", - "sudoPasswordAutoFillShortDesc": "Fournir automatiquement le mot de passe sudo lorsque demandé", - "sudoPasswordAutoFillDesc": "Stockez un mot de passe sudo pour que les métriques de l'hôte, les invites de terminal et autres puissent exécuter automatiquement des commandes privilégiées.", - "sudoPasswordLabel": "Mot de passe Sudo", - "environmentVariablesLabel": "Variables environnementales", + "sudoPasswordAutoFillLabel": "Remplissage automatique du mot de passe sudo", + "sudoPasswordAutoFillShortDesc": "Fournir automatiquement le mot de passe sudo lorsqu'il est demandé", + "sudoPasswordAutoFillDesc": "Enregistrer un mot de passe sudo pour que les métriques de l'hôte, les invites du terminal et autres puissent exécuter automatiquement des commandes privilégiées.", + "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": "Commandement Mosh", + "fastScrollSensitivityLabel": "Sensibilité du défilement rapide", + "moshCommandLabel": "Commande Mosh", "startupSnippetLabel": "Extrait de démarrage", - "keepaliveIntervalLabel": "Intervalle de maintien en vie (secondes)", - "maxKeepaliveMisses": "Max Keepalive rate", + "keepaliveIntervalLabel": "Intervalle de keepalive (secondes)", + "maxKeepaliveMisses": "Nombre maximal d'échecs de keepalive", "tunnelSettings": "Paramètres du tunnel", - "enableTunneling": "Activer le tunnelage", + "enableTunneling": "Activer le tunneling", "enableTunnelingDesc": "Activer la fonctionnalité de tunnel SSH pour cet hôte", - "serverTunnelsSection": "Tunnels de serveur", + "serverTunnelsSection": "Tunnels serveur", "addTunnelBtn": "Ajouter un tunnel", "noTunnelsConfigured": "Aucun tunnel configuré.", "tunnelLabel": "Tunnel {{number}}", "tunnelType": "Type de tunnel", - "tunnelModeLocalDesc": "Redirigez un port local vers un port du serveur distant (ou d'un hôte accessible depuis celui-ci).", - "tunnelModeRemoteDesc": "Redirigez un port du serveur distant vers un port local de votre machine.", - "tunnelModeDynamicDesc": "Créez un proxy SOCKS5 sur un port local pour la redirection dynamique des ports.", + "tunnelModeLocalDesc": "Redirige un port local vers un port sur le serveur distant (ou un hôte accessible depuis celui-ci).", + "tunnelModeRemoteDesc": "Redirige un port sur le serveur distant vers un port local sur votre machine.", + "tunnelModeDynamicDesc": "Crée un proxy SOCKS5 sur un port local pour la redirection de port dynamique.", "sameHost": "Cet hôte (tunnel direct)", - "endpointHost": "Hôte de point de terminaison", + "endpointHost": "Hôte de destination", "endpointHostPlaceholder": "Même hôte, hôte SSH ou hôte/IP accessible", - "endpointPort": "Port de point de terminaison", - "bindHost": "Lier l'hôte", + "endpointPort": "Port de destination", + "bindHost": "Hôte de liaison", "sourcePort": "Port source", - "maxRetries": "Nombre maximal de tentatives", - "retryIntervalS": "Intervalle de nouvelle tentative (s)", + "maxRetries": "Tentatives max", + "retryIntervalS": "Intervalle de tentative (s)", "autoStartLabel": "Démarrage automatique", - "autoStartDesc": "Connexion automatique de ce tunnel lorsque l'hôte est chargé", - "tunnelConnecting": "Tunnel reliant...", + "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 Docker", "enableDockerMonitor": "Activer Docker", - "enableDockerMonitorDesc": "Surveillez et gérez les conteneurs sur cet hôte via Docker.", - "containerRuntime": "Exécution du conteneur", - "containerRuntimeDesc": "Choisissez l'interface de ligne de commande (CLI) utilisée pour la gestion des conteneurs sur cet hôte.", + "enableDockerMonitorDesc": "Surveiller et gérer les conteneurs sur cet hôte via Docker", + "containerRuntime": "Moteur d'exécution de conteneurs", + "containerRuntimeDesc": "Choisissez le CLI utilisé pour la gestion des conteneurs sur cet hôte", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "Activer le moniteur Tmux", - "enableTmuxMonitorDesc": "Afficher cet hôte dans le moniteur Tmux et ajouter ses actions tmux à la barre latérale", + "enableTmuxMonitor": "Activer le Moniteur Tmux", + "enableTmuxMonitorDesc": "Afficher cet hôte dans le Moniteur Tmux et ajouter ses actions tmux à la barre latérale", "tabProxmox": "Proxmox", "proxmoxIntegration": "Intégration Proxmox", "enableProxmox": "Activer Proxmox", - "enableProxmoxDesc": "Marquez cet hôte comme nœud Proxmox. Cela permet la découverte des invités et leur importation directe depuis cet hôte.", + "enableProxmoxDesc": "Marquer cet hôte comme un nœud Proxmox. Permet la découverte et l'importation des invités directement depuis cet hôte.", "proxmoxDefaultAuthType": "Type d'authentification par défaut", - "proxmoxDefaultAuthTypeDesc": "Méthode d'authentification appliquée aux hôtes invités importés. Choisissez le type d'authentification qui correspond à votre mode de connexion avec vos invités.", + "proxmoxDefaultAuthTypeDesc": "Méthode d'authentification appliquée aux hôtes invités importés. Choisissez le type d'authentification correspondant à votre méthode de connexion aux invités.", "authTypePassword": "Mot de passe", "authTypeKey": "Clé SSH", - "authTypeCredential": "Attestation", + "authTypeCredential": "Identifiants", "authTypeOpkssh": "OPKSSH", "authTypeNone": "Aucun", - "proxmoxDefaultCredential": "Identifiants par défaut", - "proxmoxDefaultCredentialDesc": "Identifiants utilisés pour les hôtes invités importés lorsque le type d'authentification est défini sur Identifiants.", - "proxmoxWindowsDetection": "Détection Windows/RDP", - "proxmoxWindowsDetectionDesc": "Modèles de noms séparés par des virgules qui déclenchent une connexion RDP au lieu de SSH (sans tenir compte de la casse)", + "proxmoxDefaultCredential": "Identifiant par défaut", + "proxmoxDefaultCredentialDesc": "Identifiant utilisé pour les hôtes invités importés lorsque le type d'authentification est défini sur Identifiant.", + "proxmoxWindowsDetection": "Détection Windows / RDP", + "proxmoxWindowsDetectionDesc": "Liste de motifs de noms, séparés par des virgules, qui déclenchent RDP au lieu de SSH (insensible à la casse).", "proxmoxDockerDetection": "Détection Docker", - "proxmoxDockerDetectionDesc": "Modèles de noms séparés par des virgules permettant à Docker d'identifier les invités", - "proxmoxPreferredRanges": "Plages d'adresses IP préférées", - "proxmoxPreferredRangesDesc": "Préfixes séparés par des virgules, classés par ordre de priorité pour la sélection d'adresses IP lorsqu'un invité possède plusieurs interfaces.", + "proxmoxDockerDetectionDesc": "Liste de motifs de noms, séparés par des virgules, qui activent Docker pour les invités correspondants.", + "proxmoxPreferredRanges": "Plages IP préférées", + "proxmoxPreferredRangesDesc": "Préfixes séparés par des virgules, par ordre de priorité, pour la sélection d'adresse IP lorsqu'un invité possède plusieurs interfaces.", "proxmoxAutoSync": "Synchronisation automatique des invités", - "proxmoxAutoSyncDesc": "Découvrez périodiquement ce nœud Proxmox et créez ou mettez à jour les hôtes invités importés pendant que votre session est déverrouillée.", + "proxmoxAutoSyncDesc": "Découvre périodiquement ce nœud Proxmox et crée ou met à jour les hôtes invités importés tant que votre session est déverrouillée.", "proxmoxSyncInterval": "Intervalle de synchronisation (minutes)", "proxmoxSyncIntervalDesc": "Minimum 5 minutes. Le planificateur ignore les sessions utilisateur verrouillées.", "proxmoxMarkMissing": "Marquer les invités manquants", - "proxmoxMarkMissingDesc": "Ajoutez une étiquette proxmox-missing lorsqu'un invité précédemment importé disparaît, au lieu de le supprimer.", + "proxmoxMarkMissingDesc": "Ajoute une étiquette proxmox-missing lorsqu'un invité précédemment importé disparaît, au lieu de le supprimer.", "proxmoxLastSync": "Dernière synchronisation", "proxmoxLastSyncNever": "Pas encore synchronisé", - "proxmoxLastSyncNoResult": "Aucun résultat détaillé", - "proxmoxLastSyncSummary": "{{created}} créé, {{updated}} mis à jour, {{markedMissing}} manquant, {{skipped}} ignoré", + "proxmoxLastSyncNoResult": "Aucun détail de résultat", + "proxmoxLastSyncSummary": "{{created}} créé(s), {{updated}} mis à jour, {{markedMissing}} manquant(s), {{skipped}} ignoré(s)", "proxmoxLastSyncStatus": { "success": "Succès", - "error": "Échoué", + "error": "Échec", "pending": "En attente" }, - "proxmoxDiscoverAction": "Découvrir et importer des invités Proxmox", + "proxmoxDiscoverAction": "Découvrir et importer les invités Proxmox", "proxmoxImportTitle": "Importer depuis Proxmox", - "proxmoxSelectHost": "Sélectionnez un hôte Proxmox…", + "proxmoxSelectHost": "Sélectionner un hôte Proxmox…", "proxmoxDiscover": "Découvrir", - "proxmoxDiscovering": "Découverte de…", - "proxmoxDiscoverGuests": "Découvrez les invités", + "proxmoxDiscovering": "Découverte en cours…", + "proxmoxDiscoverGuests": "Découvrir les invités", "proxmoxGuestsSelected_one": "{{count}} invité — {{selected}} sélectionné", "proxmoxGuestsSelected_other": "{{count}} invités — {{selected}} sélectionnés", "proxmoxSelectAll": "Tout sélectionner", - "proxmoxDeselectAll": "Désélectionnez tout", + "proxmoxDeselectAll": "Tout désélectionner", "proxmoxNoGuests": "Aucun invité trouvé sur ce nœud Proxmox.", - "proxmoxImportButton_one": "Importer l'hôte {{count}}", - "proxmoxImportButton_other": "Importer les hôtes {{count}}", - "proxmoxResultImported": "{{count}} importé", + "proxmoxImportButton_one": "Importer {{count}} hôte", + "proxmoxImportButton_other": "Importer {{count}} hôtes", + "proxmoxResultImported": "{{count}} importés", "proxmoxResultUpdated": "{{count}} mis à jour", - "proxmoxResultFailed": "{{count}} a échoué", - "proxmoxImportComplete": "Importation Proxmox terminée : {{summary}}", - "proxmoxDiscoveryFailed": "La découverte a échoué", - "proxmoxImportFailed": "L'importation a échoué", + "proxmoxResultFailed": "{{count}} échoués", + "proxmoxResultSkippedNoIp": "{{count}} ignoré (aucune adresse IP trouvée)", + "proxmoxImportComplete": "Importation Proxmox terminée : {{summary}}", + "proxmoxDiscoveryFailed": "Échec de la découverte", + "proxmoxImportFailed": "Échec de l'importation", "enableFileManagerMonitor": "Activer le gestionnaire de fichiers", - "enableFileManagerMonitorDesc": "Parcourez et gérez les fichiers sur cet hôte via SFTP", - "scpLegacyLabel": "Mode SCP Legacy", - "scpLegacyDesc": "Utilisez le transfert de fichiers hérité pour les serveurs qui ne prennent pas en charge le sous-système SFTP (par exemple, les serveurs SSH intégrés ou minimaux).", + "enableFileManagerMonitorDesc": "Parcourir et gérer les fichiers de cet hôte via SFTP", + "scpLegacyLabel": "Mode SCP hérité", + "scpLegacyDesc": "Utiliser le transfert de fichiers hérité pour les serveurs qui ne prennent pas en charge le sous-système SFTP (par exemple, serveurs SSH embarqués ou minimalistes).", "defaultPathLabel": "Chemin par défaut", - "fileManagerPathHint": "Le répertoire à ouvrir au lancement du gestionnaire de fichiers pour cet hôte.", - "statusChecksLabel": "Vérifications de statut", + "fileManagerPathHint": "Le répertoire à ouvrir lors du lancement du gestionnaire de fichiers pour cet hôte.", + "statusChecksLabel": "Vérifications d'état", "enableStatusChecks": "Activer les vérifications d'état", - "enableStatusChecksDesc": "Effectuez régulièrement une vérification de disponibilité auprès de cet hôte.", + "enableStatusChecksDesc": "Envoyer périodiquement un ping à cet hôte pour vérifier sa disponibilité", "useGlobalInterval": "Utiliser l'intervalle global", - "useGlobalIntervalDesc": "Remplacer par l'intervalle de vérification d'état global du serveur", + "useGlobalIntervalDesc": "Remplacer par l'intervalle de vérification d'état défini à l'échelle du serveur", "checkIntervalS": "Intervalle de vérification (s)", "checkIntervalDesc": "Secondes entre chaque ping de connectivité", - "metricsCollectionLabel": "Collecte de mesures", - "enableMetricsLabel": "Activer les indicateurs", - "enableMetricsDesc": "Collectez les données relatives au processeur, à la RAM, au disque et autres indicateurs de cet hôte.", + "metricsCollectionLabel": "Collecte de métriques", + "enableMetricsLabel": "Activer les métriques", + "enableMetricsDesc": "Collecter les métriques CPU, RAM, disque et autres de cet hôte", "useGlobalMetrics": "Utiliser l'intervalle global", - "useGlobalMetricsDesc": "Remplacer par l'intervalle des métriques à l'échelle du serveur", - "metricsIntervalS": "Intervalle des métriques (s)", + "useGlobalMetricsDesc": "Remplacer par l'intervalle de métriques défini à l'échelle du serveur", + "metricsIntervalS": "Intervalle de métriques (s)", "metricsIntervalDesc2": "Secondes entre les instantanés de métriques", "visibleWidgets": "Widgets visibles", - "widgetsMovedToHostMetrics": "Les cartes peuvent désormais être ajoutées, organisées et redimensionnées directement dans l'onglet « Statistiques de l'hôte ». Ouvrez les statistiques de cet hôte et utilisez l'option « Personnaliser » pour choisir les cartes à afficher.", - "cpuUsageLabel": "Utilisation du processeur", - "cpuUsageDesc": "Pourcentage d'utilisation du processeur, charges moyennes, graphique sparkline", + "widgetsMovedToHostMetrics": "Les cartes sont désormais ajoutées, organisées et redimensionnées directement dans l'onglet Métriques de l'hôte. Ouvrez les métriques de cet hôte et utilisez Personnaliser pour choisir les cartes à afficher.", + "cpuUsageLabel": "Utilisation du CPU", + "cpuUsageDesc": "Pourcentage CPU, moyennes de charge, graphique sparkline", "memoryLabel": "Utilisation de la mémoire", "memoryDesc": "Utilisation de la RAM, swap, 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": "Disponibilité", - "uptimeDesc": "Durée de fonctionnement du système et durée de démarrage", + "uptimeLabel": "Durée de fonctionnement", + "uptimeDesc": "Temps de fonctionnement et heure de démarrage", "systemInfoLabel": "Informations système", - "systemInfoDesc": "Système d'exploitation, noyau, nom d'hôte, architecture", + "systemInfoDesc": "OS, noyau, nom d'hôte, architecture", "recentLoginsLabel": "Connexions récentes", "recentLoginsDesc": "Événements de connexion réussis et échoués", - "topProcessesLabel": "Principaux processus", + "topProcessesLabel": "Top processus", "topProcessesDesc": "PID, CPU%, MEM%, commande", - "listeningPortsLabel": "Ports d'écoute", - "listeningPortsDesc": "Ouvrir les ports avec processus et état", + "listeningPortsLabel": "Ports en écoute", + "listeningPortsDesc": "Ports ouverts avec processus et état", "firewallLabel": "Pare-feu", - "firewallDesc": "État du pare-feu, d'AppArmor et de SELinux", + "firewallDesc": "État du pare-feu, AppArmor, SELinux", "quickActionsLabel": "Actions rapides", - "quickActionsToolbar": "Les actions rapides apparaissent sous forme de boutons dans la barre d'outils Host Metrics pour une exécution de commande en un clic.", + "quickActionsToolbar": "Les actions rapides apparaissent sous forme de boutons dans la barre d'outils Métriques de l'hôte pour une exécution en un clic.", "noQuickActions": "Aucune action rapide pour le moment.", "buttonLabel": "Étiquette du bouton", - "selectSnippetPlaceholder": "Sélectionnez un extrait...", + "selectSnippetPlaceholder": "Sélectionner un snippet...", "addActionBtn": "Ajouter une action", "hostSharedSuccessfully": "Hôte partagé avec succès", - "failedToShareHost": "Impossible de partager l'hôte", + "failedToShareHost": "Échec du partage de l'hôte", "accessRevoked": "Accès révoqué", "failedToRevokeAccess": "Échec de la révocation de l'accès", "cancelBtn": "Annuler", - "savingBtn": "Économie...", + "savingBtn": "Enregistrement...", "addHostBtn": "Ajouter un hôte", - "hostUpdated": "L'hôte a mis à jour", - "hostCreated": "L'hôte a créé", - "failedToSave": "Échec de la sauvegarde de l'hôte", - "credentialUpdated": "Identifiants mis à jour", + "hostUpdated": "Hôte mis à jour", + "hostCreated": "Hôte créé", + "failedToSave": "Échec de l'enregistrement de l'hôte", + "credentialUpdated": "Identifiant mis à jour", "credentialCreated": "Identifiant créé", - "failedToSaveCredential": "Impossible d'enregistrer les identifiants", - "credentialNameRequired": "Veuillez saisir un nom pour le justificatif d'identité", - "credentialAuthRequired": "Ajoutez un mot de passe, une clé SSH ou les deux.", - "backToHosts": "Retour à la liste des hôtes", + "failedToSaveCredential": "Échec de l'enregistrement de l'identifiant", + "credentialNameRequired": "Veuillez saisir un nom pour l'identifiant", + "credentialAuthRequired": "Ajoutez un mot de passe, une clé SSH, ou les deux", + "createCredentialFromHostBtn": "Créer un identifiant", + "createCredentialFromHostTitle": "Créer des informations d'identification à partir de l'hôte", + "createCredentialFromHostDesc": "Créez une entrée d'identification réutilisable et partageable, préremplie avec le nom d'utilisateur, le mot de passe et/ou la clé SSH actuels de cet hôte.", + "backToHosts": "Retour aux hôtes", "backToCredentials": "Retour aux identifiants", - "pinned": "Épinglé", + "pinned": "Épinglés", "noHostsFound": "Aucun hôte trouvé", "tryDifferentTerm": "Essayez un autre terme", "addFirstHost": "Ajoutez votre premier hôte pour commencer", - "noCredentialsFound": "Aucune authentification trouvée", + "noCredentialsFound": "Aucun identifiant trouvé", "addCredentialBtn": "Ajouter un identifiant", - "updateCredentialBtn": "Mise à jour des identifiants", - "features": "Caractéristiques", + "updateCredentialBtn": "Mettre à jour l'identifiant", + "features": "Fonctionnalités", "noFolder": "(Aucun dossier)", "deleteSelected": "Supprimer", - "exitSelection": "Sortie de la sélection", - "importSkip": "Importer (ignorer les éléments existants)", - "importOverwrite": "Importer (écraser)", - "collapseBtn": "Effondrement", - "importExportBtn": "Importation / Exportation", + "exitSelection": "Quitter la sélection", + "importSkip": "Importer (ignorer existants)", + "importOverwrite": "Importer (remplacer)", + "collapseBtn": "Réduire", + "importExportBtn": "Importer / Exporter", "hostStatusesRefreshed": "Statuts des hôtes actualisés", - "failedToRefreshHosts": "Échec de l'actualisation des hôtes", - "movedHostTo": "Déplacé {{host}} vers \"{{folder}}\"", - "failedToMoveHost": "Échec du déplacement de l'hôte", - "folderRenamedTo": "Dossier renommé en \"{{name}}\"", - "deletedFolder": "Dossier supprimé \"{{name}}\"", - "failedToDeleteFolder": "Échec de la suppression du dossier", - "deleteAllInFolder": "Supprimer tous les hôtes dans \"{{name}}\" ? Cette opération est irréversible.", + "failedToRefreshHosts": "Impossible d'actualiser les hôtes", + "movedHostTo": "{{host}} déplacé vers « {{folder}} »", + "failedToMoveHost": "Impossible de déplacer l'hôte", + "folderRenamedTo": "Dossier renommé en « {{name}} »", + "deletedFolder": "Dossier « {{name}} » supprimé", + "failedToDeleteFolder": "Impossible de supprimer le dossier", + "deleteAllInFolder": "Supprimer tous les hôtes dans « {{name}} » ? Cette action est irréversible.", "folderPickerPlaceholder": "Aucun dossier", - "folderPickerSearch": "Rechercher ou créer (utiliser / pour les sous-dossiers)...", + "folderPickerSearch": "Rechercher ou créer (utilisez / pour les sous-dossiers)...", "folderPickerNone": "Aucun dossier", - "folderPickerCreate": "Créer \"{{path}}\"", + "folderPickerCreate": "Créer « {{path}} »", "folderPickerEmpty": "Aucun dossier correspondant", "newFolder": "Nouveau dossier", "createFolderTitle": "Créer un dossier", "editFolderTitle": "Modifier le dossier", - "folderDialogDescription": "Choisissez un nom, une couleur et une icône. Utilisez / pour imbriquer les dossiers.", + "folderDialogDescription": "Choisissez un nom, une couleur et une icône. Utilisez / pour imbriquer des dossiers.", "folderNameLabel": "Nom du dossier", - "folderNamePlaceholder": "ex Production/Web", + "folderNamePlaceholder": "Exemple : Production/Web", "folderNestingHint": "Utilisez / pour séparer les niveaux et créer des dossiers imbriqués.", "folderColor": "Couleur", "folderIcon": "Icône", + "folderCredential": "Attestation", + "folderCredentialNone": "Aucun identifiant attribué", + "folderCredentialHint": "Les hôtes de ce dossier qui utilisent l'authentification « Informations d'identification enregistrées » sans avoir sélectionné leurs propres informations d'identification hériteront de celles-ci.", "folderPreview": "Aperçu", "folderNameFallback": "Dossier sans titre", "createFolderButton": "Créer un dossier", "saveFolderButton": "Enregistrer le dossier", "cancel": "Annuler", - "iconSearchPlaceholder": "Icônes de recherche...", + "iconSearchPlaceholder": "Rechercher des icônes...", "openAllSessions": "Ouvrir toutes les sessions", "editFolder": "Modifier le dossier", "deleteFolder": "Supprimer le dossier", "folderSaved": "Dossier enregistré", - "failedToSaveFolder": "Échec de l'enregistrement du dossier", - "folderDeleted": "Dossier supprimé \"{{name}}\"", - "deleteFolderConfirm": "Supprimer «{{name}}» et son ou ses hôtes {{count}}  ? Cette opération est irréversible.", + "failedToSaveFolder": "Impossible d'enregistrer le dossier", + "folderDeleted": "Dossier \"{{name}}\" supprimé", + "deleteFolderConfirm": "Supprimer \"{{name}}\" et ses {{count}} hôte(s) ? Cette action est irréversible.", "failedToMoveHosts": "Impossible de déplacer les hôtes", "expandAll": "Développer tous les dossiers", "collapseAll": "Réduire tous les dossiers", "moreActions": "Plus", - "groupBy": "Grouper par", + "groupBy": "Regrouper par", "GroupByFolder": "Dossier", - "GroupByTag": "Étiqueter", + "GroupByTag": "Tag", "GroupByStatus": "Statut", "GroupByProtocol": "Protocole", "GroupByAuth": "Type d'authentification", - "groupUngrouped": "Non groupé", - "deletedHost": "Supprimé {{name}}", + "groupUngrouped": "Sans groupe", + "deletedHost": "{{name}} supprimé", "copiedToClipboard": "Copié dans le presse-papiers", "terminalUrlCopied": "URL du terminal copiée", "fileManagerUrlCopied": "URL du gestionnaire de fichiers copiée", @@ -1035,14 +1097,14 @@ "rdpUrlCopied": "URL RDP copiée", "vncUrlCopied": "URL VNC copiée", "telnetUrlCopied": "URL Telnet copiée", - "remoteDesktopUrlCopied": "URL du bureau à distance copiée", + "remoteDesktopUrlCopied": "URL du Bureau à distance copiée", "expandActions": "Développer les actions", - "collapseActions": "Actions d'effondrement", + "collapseActions": "Réduire les actions", "wakeOnLanAction": "Wake on LAN", "wakeOnLanSuccess": "Paquet magique envoyé à {{name}}", "wakeOnLanError": "Échec de l'envoi du paquet magique", - "cloneHostAction": "Hôte clone", - "copyAddress": "Copie de l'adresse", + "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", @@ -1053,15 +1115,15 @@ "copyRdpUrlAction": "Copier l'URL RDP", "copyVncUrlAction": "Copier l'URL VNC", "copyTelnetUrlAction": "Copier l'URL Telnet", - "copyRemoteDesktopUrlAction": "Copier l'URL du bureau à distance", - "deleteCredentialConfirm": "Supprimer les informations d'identification \"{{name}}\"?", - "deletedCredential": "Supprimé {{name}}", + "copyRemoteDesktopUrlAction": "Copier l'URL du Bureau à distance", + "deleteCredentialConfirm": "Supprimer l'identifiant « {{name}} » ?", + "deletedCredential": "{{name}} supprimé", "deploySSHKeyTitle": "Déployer la clé SSH", - "deployingBtn": "Déploiement...", + "deployingBtn": "Déploiement en cours...", "deployBtn": "Déployer", "failedToDeployKey": "Échec du déploiement de la clé", - "deleteHostsConfirm": "Supprimer {{count}} hôte{{plural}} ? Cette opération est irréversible.", - "movedToRoot": "Déplacé vers la racine", + "deleteHostsConfirm": "Supprimer {{count}} hôte{{plural}} ? Cette action est irréversible.", + "movedToRoot": "Déplacé à la racine", "enableTerminalFeature": "Activer le terminal", "disableTerminalFeature": "Désactiver le terminal", "enableFilesFeature": "Activer les fichiers", @@ -1074,75 +1136,75 @@ "disableProxmoxFeature": "Désactiver Proxmox", "addTagsPlaceholder": "Ajouter des étiquettes...", "authDetails": "Détails d'authentification", - "credType": "Taper", - "generateKeyPairDesc": "Générez une nouvelle paire de clés ; les clés privée et publique seront renseignées automatiquement.", - "generatingKey": "Générateur...", + "credType": "Type", + "generateKeyPairDesc": "Générer une nouvelle paire de clés, les clés privée et publique seront remplies automatiquement.", + "generatingKey": "Génération en cours...", "generateLabel": "Générer {{label}}", "uploadFileBtn": "Téléverser un fichier", - "keyPassphraseOptional": "Phrase secrète (facultative)", + "keyPassphraseOptional": "Phrase de passe de la clé (facultatif)", "sshPublicKeyOptional": "Clé publique SSH (facultatif)", "publicKeyGenerated": "Clé publique générée", - "failedToGeneratePublicKey": "Impossible de générer la clé publique", + "failedToGeneratePublicKey": "Échec de la dérivation de la clé publique", "publicKeyCopied": "Clé publique copiée", - "keyPairGenerated": "{{label}} paire de clés générée", + "keyPairGenerated": "Paire de clés {{label}} générée", "failedToGenerateKeyPair": "Échec de la génération de la paire de clés", - "searchHostsPlaceholder": "Rechercher des hôtes, des adresses, des tags…", - "searchCredentialsPlaceholder": "Identifiants de recherche…", - "refreshBtn": "Rafraîchir", + "searchHostsPlaceholder": "Rechercher des hôtes, adresses, étiquettes…", + "searchCredentialsPlaceholder": "Rechercher des identifiants…", + "refreshBtn": "Actualiser", "addTag": "Ajouter des étiquettes...", "deleteConfirmBtn": "Supprimer", "tunnelRequirementsText": "Le serveur SSH doit avoir GatewayPorts yes, AllowTcpForwarding yes et PermitRootLogin yes définis dans /etc/ssh/sshd_config.", - "deleteHostConfirm": "Supprimer \"{{name}}\"?", - "enableAtLeastOneProtocol": "Activez au moins un des protocoles ci-dessus pour configurer les paramètres d'authentification et de connexion.", - "keyPassphrase": "Mot de passe clé", + "deleteHostConfirm": "Supprimer \"{{name}}\" ?", + "enableAtLeastOneProtocol": "Activez au moins un protocole ci-dessus pour configurer les paramètres d'authentification et de connexion.", + "keyPassphrase": "Phrase de passe de la clé", "connectBtn": "Connecter", "disconnectBtn": "Déconnecter", "basicInformation": "Informations de base", "authDetailsSection": "Détails d'authentification", - "credTypeLabel": "Taper", + "credTypeLabel": "Type", "hostsTab": "Hôtes", - "credentialsTab": "Informations d'identification", + "credentialsTab": "Identifiants", "selectMultiple": "Sélectionner plusieurs", "selectHosts": "Sélectionner les hôtes", "connectionLabel": "Connexion", "authenticationLabel": "Authentification", "generateKeyPairTitle": "Générer une paire de clés", - "generateKeyPairDescription": "Générez une nouvelle paire de clés ; les clés privée et publique seront renseignées automatiquement.", - "generateFromPrivateKey": "Générer à partir d'une clé privée", - "refreshBtn2": "Rafraîchir", - "exitSelectionTitle": "Sortie de la sélection", - "exportAll": "Exporter tout", - "exportForSharing": "Exporter pour partager", + "generateKeyPairDescription": "Générer une nouvelle paire de clés : les clés privée et publique seront automatiquement renseignées.", + "generateFromPrivateKey": "Générer à partir de la clé privée", + "refreshBtn2": "Actualiser", + "exitSelectionTitle": "Quitter la sélection", + "exportAll": "Tout exporter", + "exportForSharing": "Exporter pour partage", "addHostBtn2": "Ajouter un hôte", "addCredentialBtn2": "Ajouter un identifiant", - "checkingHostStatuses": "Vérification de l'état des hôtes...", - "pinnedSection": "Épinglé", + "checkingHostStatuses": "Vérification des statuts des hôtes...", + "pinnedSection": "Épinglés", "hostsExported": "Hôtes exportés avec succès", "hostsShareExported": "Hôtes partageables exportés avec succès", "exportFailed": "Échec de l'exportation des hôtes", - "sampleDownloaded": "Fichier exemple téléchargé", - "failedToDeleteCredential2": "Échec de la suppression des identifiants", + "sampleDownloaded": "Fichier d'exemple téléchargé", + "failedToDeleteCredential2": "Échec de la suppression de l'identifiant", "noFolderOption": "(Aucun dossier)", - "nSelected": "{{count}} sélectionné", - "featuresMenu": "Caractéristiques", - "moveMenu": "Se déplacer", + "nSelected": "{{count}} sélectionné(s)", + "featuresMenu": "Fonctionnalités", + "moveMenu": "Déplacer", "connectSelected": "Connecter", "cancelSelection": "Annuler", - "deployDialogDesc": "Déployez {{name}} sur les clés autorisées d'un hôte.", + "deployDialogDesc": "Déployer {{name}} dans les authorized_keys d'un hôte.", "targetHostLabel": "Hôte cible", - "selectHostOption": "Sélectionnez un hôte...", + "selectHostOption": "Sélectionner un hôte...", "keyDeployedSuccess": "Clé déployée avec succès", "failedToDeployKey2": "Échec du déploiement de la clé", - "deletedCount": "Hôtes supprimés {{count}}", - "failedToDeleteCount": "Échec de la suppression des hôtes {{count}}", - "duplicatedHost": "Dupliqué \"{{name}}\"", + "deletedCount": "{{count}} hôte(s) supprimé(s)", + "failedToDeleteCount": "Échec de la suppression de {{count}} hôte(s)", + "duplicatedHost": "\"{{name}}\" dupliqué", "failedToDuplicateHost": "Échec de la duplication de l'hôte", - "updatedCount": "Hôtes mis à jour {{count}}", - "friendlyNameLabel": "Nom amical", + "updatedCount": "{{count}} hôtes mis à jour", + "friendlyNameLabel": "Nom convivial", "descriptionLabel": "Description", "loadingHost": "Chargement de l'hôte...", "loadingHosts": "Chargement des hôtes...", - "loadingCredentials": "Chargement des informations d'identification...", + "loadingCredentials": "Chargement des identifiants...", "noHostsYet": "Aucun hôte pour le moment", "noHostsMatchSearch": "Aucun hôte ne correspond à votre recherche", "hostNotFound": "Hôte introuvable", @@ -1151,21 +1213,21 @@ "sortDefault": "Ordre par défaut", "sortNameAsc": "Nom (A → Z)", "sortNameDesc": "Nom (Z → A)", - "sortIpAsc": "Adresse IP (Asc)", - "sortIpDesc": "Adresse IP (Description)", + "sortIpAsc": "Adresse IP (croissant)", + "sortIpDesc": "Adresse IP (décroissant)", "sortOnlineFirst": "En ligne d'abord", - "sortOfflineFirst": "D'abord hors ligne", - "sortPinnedFirst": "Épinglé en premier", + "sortOfflineFirst": "Hors ligne d'abord", + "sortPinnedFirst": "Épinglés d'abord", "filterHosts": "Filtrer les hôtes", - "filterClearAll": "Filtres transparents", - "filterStatusGroup": "Statut", + "filterClearAll": "Effacer les filtres", + "filterStatusGroup": "État", "filterOnline": "En ligne", "filterOffline": "Hors ligne", - "filterPinned": "Épinglé", - "filterAuthGroup": "Type d'autorisation", + "filterPinned": "Épinglés", + "filterAuthGroup": "Type d'authentification", "filterAuthPassword": "Mot de passe", "filterAuthKey": "Clé SSH", - "filterAuthCredential": "Attestation", + "filterAuthCredential": "Identifiant", "filterAuthNone": "Aucun", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocole", @@ -1173,54 +1235,59 @@ "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Caractéristiques", + "filterFeaturesGroup": "Fonctionnalités", "filterFeatureTerminal": "Terminal", "filterFeatureFileManager": "Gestionnaire de fichiers", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Étiquettes", - "shareHost": "Hôte partagé", - "shareHostTitle": "Partager : {{name}}", + "shareHost": "Partager", + "shareHostTitle": "Partager : {{name}}", + "shareFolder": "Dossier partagé", + "shareFolderTitle": "Dossier partagé : {{name}}", + "folderSharedSuccessfully": "Hôte(s) partagé(s) {{count}} dans le dossier", + "failedToShareFolder": "Échec du partage du dossier", "sharing": { - "loadError": "Impossible de charger les données de partage. Veuillez réessayer.", + "loadError": "Échec du chargement des données de partage. Veuillez réessayer.", "shareWithSection": "Partager avec", "usersTab": "Utilisateurs", "rolesTab": "Rôles", - "searchPlaceholder": "Rechercher des utilisateurs ou des rôles...", + "searchPlaceholder": "Rechercher des utilisateurs ou des rôles…", "noMatches": "Aucun résultat trouvé", - "permissionLevelLabel": "Niveau d'autorisation", + "permissionLevelLabel": "Niveau de permission", "levels": { "connect": { - "label": "Connecter", - "description": "Seules les sessions ouvertes (terminal, bureau à distance, gestionnaire de fichiers, tunnels et Docker) sont autorisées. Aucun accès à la configuration de l'hôte n'est autorisé." + "label": "Connexion", + "description": "Ouverture de sessions uniquement : terminal, bureau à distance, gestionnaire de fichiers, tunnels et Docker. Aucun accès à la configuration de l'hôte." }, "view": { - "label": "Voir", - "description": "Connectez-vous et consultez la configuration de l'hôte. Les secrets ne sont jamais divulgués." + "label": "Consultation", + "description": "Connexion, plus consultation de la configuration de l’hôte. Les secrets ne sont jamais affichés." }, "edit": { - "label": "Modifier", - "description": "Consultez et modifiez l'hôte. Les secrets peuvent être remplacés mais jamais lus ; les identifiants restent réservés à leur propriétaire." + "label": "Modification", + "description": "Consultation, plus modification de l'hôte. Les secrets peuvent être remplacés mais jamais lus ; les attributions d’identifiants restent réservées au propriétaire." }, "manage": { - "label": "Gérer", - "description": "Modifiez, partagez l'hôte avec d'autres, changez les niveaux d'autorisation et révoquer l'accès." + "label": "Gestion", + "description": "Modification, plus partager l'hôte avec d'autres utilisateurs, modifier les niveaux de permission et révoquer l'accès." } }, - "expiryLabel": "Expiration de l'accès", + "expiryLabel": "Expiration de l’accès", "expiry": { "never": "Jamais", "oneHour": "1 heure", "oneDay": "24 heures", "sevenDays": "7 jours", "thirtyDays": "30 jours", - "custom": "Coutume" + "custom": "Personnalisé" }, - "customHoursPlaceholder": "Heures restantes avant l'expiration de l'accès", + "customHoursPlaceholder": "Heures jusqu'à l'expiration de l'accès", "shareButton": "Partager", "shareWithCount": "Partager ({{count}})", "currentAccess": "Accès actuel", - "noAccessEntries": "Cet hôte n'a pas encore été partagé.", + "noAccessEntries": "Cet hôte n'a pas encore été partagé", + "folderShareSummary": "Hôte(s) partagé(s) {{shared}} de {{total}} dans ce dossier", "grantedBy": "Accordé par", "expires": "Expire", "expired": "Expiré", @@ -1228,30 +1295,32 @@ "revoke": "Révoquer", "accessUpdated": "Accès mis à jour", "accessUpdateFailed": "Échec de la mise à jour de l'accès", - "sharedBadge": "Commun", - "sharedBadgeTooltip": "Partagé par {{owner}} ({{level}} accès)", - "viewOnlyBanner": "Cet hôte est partagé avec vous par {{owner}} qui dispose d'un accès en lecture seule. La configuration est en lecture seule.", - "sharedEditBanner": "Cet hôte est partagé avec vous par {{owner}} qui dispose d'un accès en modification. Les modifications s'appliquent à l'hôte réel ; les références d'authentification ne peuvent être modifiées que par le propriétaire.", - "ownerOnlyControl": "Seul le propriétaire de l'hôte peut modifier cela." + "sharedBadge": "Partagé", + "sharedBadgeTooltip": "Partagé par {{owner}} (accès {{level}})", + "viewOnlyBanner": "Cet hôte vous est partagé par {{owner}} avec un accès en lecture seule. La configuration est en lecture seule.", + "sharedEditBanner": "Cet hôte vous est partagé par {{owner}} avec un accès en modification. Les modifications s'appliquent à l'hôte réel ; les références d'authentification ne peuvent être modifiées que par le propriétaire.", + "ownerOnlyControl": "Modifiable uniquement par le propriétaire de l’hôte" }, "guac": { "connection": "Connexion", "authentication": "Authentification", - "storedCredential": "Identifiants stockés", - "noCredential": "Aucune accréditation (accréditations directes ci-dessous)", + "storedCredential": "Identifiant enregistré", + "noCredential": "Aucun identifiant (saisie directe ci-dessous)", "authMethod": "Méthode d'authentification", "authTypeDirect": "Direct", - "authTypeCredential": "Attestation", - "selectCredential": "Sélectionnez une qualification...", + "authTypeCredential": "Identifiant", + "authTypeNone": "Aucun", + "authTypeNoneDesc": "Aucune information d'identification n'est stockée. Un nom d'utilisateur et un mot de passe vous seront demandés à chaque connexion ; ils ne sont pas enregistrés.", + "selectCredential": "Sélectionner un identifiant...", "connectionSettings": "Paramètres de connexion", "displaySettings": "Paramètres d'affichage", "audioSettings": "Paramètres audio", "rdpPerformance": "Performances RDP", - "deviceRedirection": "Redirection de l'appareil", + "deviceRedirection": "Redirection de périphériques", "session": "Session", - "gateway": "Porte", - "remoteApp": "Application distante", - "clipboard": "presse-papiers", + "gateway": "Passerelle", + "remoteApp": "RemoteApp", + "clipboard": "Presse-papiers", "sessionRecording": "Enregistrement de session", "wakeOnLan": "Wake-on-LAN", "vncSettings": "Paramètres VNC", @@ -1259,6 +1328,7 @@ "rdpPort": "Port RDP", "username": "Nom d'utilisateur", "password": "Mot de passe", + "passwordSaved": "Mot de passe enregistré, saisissez-le pour le modifier", "domain": "Domaine", "securityMode": "Mode de sécurité", "colorDepth": "Profondeur de couleur", @@ -1268,20 +1338,20 @@ "resizeMethod": "Méthode de redimensionnement", "clientName": "Nom du client", "initialProgram": "Programme initial", - "serverLayout": "Disposition du serveur", + "serverLayout": "Disposition du clavier", "timezone": "Fuseau horaire", - "loadBalanceInfo": "Informations sur l'équilibrage de charge / Cookie", - "loadBalanceInfoDesc": "Cookie du courtier de connexion RD pour l'équilibrage de charge de la batterie de serveurs RDS (par exemple tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "Infos d'équilibrage de charge / Cookie", + "loadBalanceInfoDesc": "Cookie du RD Connection Broker pour l'équilibrage de charge d'une ferme RDS (ex. tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "Proxy guacd", "guacdHostname": "Hôte guacd", - "guacdHostnamePlaceholder": "Valeur par défaut globale", - "guacdPort": "Port de guacd", - "guacdProxyDesc": "Remplacez l'instance guacd globale pour cette connexion. Laissez ce champ vide pour utiliser l'instance guacd configurée globalement.", + "guacdHostnamePlaceholder": "Valeur globale par défaut", + "guacdPort": "Port guacd", + "guacdProxyDesc": "Remplacer l'instance guacd globale pour cette connexion. Laisser vide pour utiliser le guacd configuré globalement.", "gatewayHostname": "Nom d'hôte de la passerelle", - "gatewayPort": "Port de passerelle", + "gatewayPort": "Port de la passerelle", "gatewayUsername": "Nom d'utilisateur de la passerelle", "gatewayPassword": "Mot de passe de la passerelle", - "gatewayDomain": "Domaine de passerelle", + "gatewayDomain": "Domaine de la passerelle", "remoteAppProgram": "Programme RemoteApp", "workingDirectory": "Répertoire de travail", "arguments": "Arguments", @@ -1291,94 +1361,94 @@ "macAddress": "Adresse MAC", "broadcastAddress": "Adresse de diffusion", "udpPort": "Port UDP", - "waitTimeS": "Temps d'attente (s)", + "waitTimeS": "Délai d'attente (s)", "driveName": "Nom du lecteur", - "drivePath": "Chemin d'accès", + "drivePath": "Chemin du lecteur", "ignoreCertificate": "Ignorer le certificat", - "ignoreCertificateDesc": "Autoriser les connexions aux hôtes disposant de certificats auto-signés", - "forceLossless": "Force sans perte", - "forceLosslessDesc": "Encodage d'image sans perte forcé (qualité supérieure, bande passante plus élevée)", - "disableAudio": "Désactiver le son", - "disableAudioDesc": "Couper le son de la session distante", + "ignoreCertificateDesc": "Autoriser les connexions aux hôtes avec des certificats auto-signés", + "forceLossless": "Forcer sans perte", + "forceLosslessDesc": "Forcer l'encodage d'image sans perte (meilleure qualité, plus de bande passante)", + "disableAudio": "Désactiver l'audio", + "disableAudioDesc": "Couper tout le son de la session distante", "enableAudioInput": "Activer l'entrée audio (microphone)", "enableAudioInputDesc": "Transférer le microphone local vers la session distante", - "wallpaper": "Papier peint", - "wallpaperDesc": "Afficher le fond d'écran (la désactivation améliore les performances)", - "theming": "Thème", + "wallpaper": "Fond d'écran", + "wallpaperDesc": "Afficher le fond d'écran du bureau (désactiver cette option améliore les performances)", + "theming": "Thèmes", "themingDesc": "Activer les thèmes et styles visuels", "fontSmoothing": "Lissage des polices", "fontSmoothingDesc": "Activer le rendu des polices ClearType", - "fullWindowDrag": "Glisser en plein écran", - "fullWindowDragDesc": "Afficher le contenu de la fenêtre pendant le déplacement", - "desktopComposition": "Composition de bureau", - "desktopCompositionDesc": "Activer les effets de verre Aero", - "menuAnimations": "Animations du menu", - "menuAnimationsDesc": "Activer les animations de fondu et de glissement du menu", - "disableBitmapCaching": "Désactiver la mise en cache des bitmaps", - "disableBitmapCachingDesc": "Désactiver le cache bitmap (cela peut aider à résoudre les problèmes d'affichage)", - "disableOffscreenCaching": "Désactiver la mise en cache hors écran", + "fullWindowDrag": "Afficher le contenu de la fenêtre lors du déplacement", + "fullWindowDragDesc": "Afficher le contenu de la fenêtre lors du déplacement", + "desktopComposition": "Composition du bureau", + "desktopCompositionDesc": "Activer les effets Aero Glass", + "menuAnimations": "Animations des menus", + "menuAnimationsDesc": "Activer les animations de fondu et de glissement des menus", + "disableBitmapCaching": "Désactiver le cache bitmap", + "disableBitmapCachingDesc": "Désactiver le cache bitmap (peut aider en cas de problèmes graphiques)", + "disableOffscreenCaching": "Désactiver le cache hors écran", "disableOffscreenCachingDesc": "Désactiver le cache hors écran", - "disableGlyphCaching": "Désactiver la mise en cache des glyphes", + "disableGlyphCaching": "Désactiver le cache des glyphes", "disableGlyphCachingDesc": "Désactiver le cache des glyphes", - "enableGfx": "Activer les graphismes", + "enableGfx": "Activer GFX", "enableGfxDesc": "Utiliser le pipeline graphique RemoteFX", "enablePrinting": "Activer l'impression", "enablePrintingDesc": "Rediriger les imprimantes locales vers la session distante", "enableDriveRedirection": "Activer la redirection de lecteur", - "enableDriveRedirectionDesc": "Associer un dossier local à un lecteur dans la session distante", - "createDrivePath": "Créer un chemin d'accès", - "createDrivePathDesc": "Créer automatiquement le dossier s'il n'existe pas.", + "enableDriveRedirectionDesc": "Monter un dossier local en tant que lecteur dans la session distante", + "createDrivePath": "Créer le chemin du lecteur", + "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 de fichiers depuis la session distante", - "disableUpload": "Désactiver le téléchargement", - "disableUploadDesc": "Empêcher le téléchargement de fichiers vers la session distante", + "disableDownloadDesc": "Empêcher le téléchargement des fichiers depuis la session distante", + "disableUpload": "Désactiver le téléversement", + "disableUploadDesc": "Empêcher le téléversement de fichiers vers la session distante", "enableTouch": "Activer le tactile", - "enableTouchDesc": "Activer le transfert des entrées tactiles", - "consoleSession": "Session de console", - "consoleSessionDesc": "Connectez-vous à la console (session 0) au lieu d'une nouvelle session.", - "sendWolPacket": "Envoyer le paquet WOL", - "sendWolPacketDesc": "Envoyez un paquet magique pour réveiller cet hôte avant la connexion.", + "enableTouchDesc": "Activer la transmission des entrées tactiles", + "consoleSession": "Session console", + "consoleSessionDesc": "Se connecter à la console (session 0) au lieu d'une nouvelle session", + "sendWolPacket": "Envoyer un paquet WOL", + "sendWolPacketDesc": "Envoyer un paquet magique pour réveiller cet hôte avant la connexion", "disableCopy": "Désactiver la copie", "disableCopyDesc": "Empêcher la copie de texte depuis la session distante", "disablePaste": "Désactiver le collage", - "disablePasteDesc": "Empêcher de coller du texte dans la session distante", - "createPathIfMissing": "Créer un chemin si manquant", - "createPathIfMissingDesc": "Création automatique du répertoire d'enregistrement", + "disablePasteDesc": "Empêcher le collage de texte dans la session distante", + "createPathIfMissing": "Créer le chemin si absent", + "createPathIfMissingDesc": "Créer automatiquement le répertoire d'enregistrement", "excludeOutput": "Exclure la sortie", - "excludeOutputDesc": "Ne pas enregistrer la sortie écran (métadonnées uniquement)", + "excludeOutputDesc": "Ne pas enregistrer l'affichage écran (métadonnées uniquement)", "excludeMouse": "Exclure la souris", - "excludeMouseDesc": "N’enregistrez pas les mouvements de la souris.", - "includeKeystrokes": "Inclure les frappes au clavier", - "includeKeystrokesDesc": "Enregistrer les frappes clavier brutes en plus de l'affichage à l'écran", + "excludeMouseDesc": "Ne pas enregistrer les mouvements de la souris", + "includeKeystrokes": "Inclure les frappes", + "includeKeystrokesDesc": "Enregistrer les frappes brutes en plus de l'affichage écran", "vncPort": "Port VNC", "vncPassword": "Mot de passe VNC", "vncUsernameOptional": "Nom d'utilisateur (facultatif)", "vncLeaveBlank": "Laisser vide si non requis", "cursorMode": "Mode curseur", - "swapRedBlue": "Inverser rouge/bleu", - "swapRedBlueDesc": "Inverser les canaux de couleur rouge et bleu (corrige certains problèmes de couleur)", + "swapRedBlue": "Échanger rouge/bleu", + "swapRedBlueDesc": "Échanger les canaux de couleur rouge et bleu (corrige certains problèmes de couleur)", "readOnly": "Lecture seule", - "readOnlyDesc": "Visualisez l'écran distant sans envoyer de saisie.", + "readOnlyDesc": "Afficher l'écran distant sans envoyer d'entrée", "telnetPort": "Port Telnet", "terminalType": "Type de terminal", "fontName": "Nom de la police", "fontSize": "Taille de la police", "colorScheme": "Schéma de couleurs", - "backspaceKey": "Touche Retour arrière", - "saveHostFirst": "Sauvegardez d'abord l'hôte.", - "sharingOptionsAfterSave": "Les options de partage sont disponibles une fois l'hôte enregistré.", - "permissionLevel": "Niveau d'autorisation", - "typeHeader": "Taper", + "backspaceKey": "Touche Backspace", + "saveHostFirst": "Enregistrez d'abord l'hôte.", + "sharingOptionsAfterSave": "Les options de partage sont disponibles après l'enregistrement de l'hôte.", + "permissionLevel": "Niveau de permission", + "typeHeader": "Type", "targetHeader": "Cible", - "permissionHeader": "Autorisation", + "permissionHeader": "Permission", "cancelBtn": "Annuler", - "savingBtn": "Économie...", + "savingBtn": "Enregistrement...", "updateHostBtn": "Mettre à jour l'hôte", "addHostBtn": "Ajouter un hôte" } }, "commandPalette": { - "searchPlaceholder": "Rechercher des hôtes, des commandes ou des paramètres...", + "searchPlaceholder": "Rechercher des hôtes, commandes ou paramètres...", "quickActions": "Actions rapides", "hostManager": "Gestionnaire d'hôtes", "hostManagerDesc": "Gérer, ajouter ou modifier des hôtes", @@ -1387,118 +1457,176 @@ "adminSettings": "Paramètres d'administration", "adminSettingsDesc": "Configurer les préférences système et les utilisateurs", "userProfile": "Profil utilisateur", - "userProfileDesc": "Gérez votre compte et vos préférences", + "userProfileDesc": "Gérer votre compte et vos préférences", "addCredential": "Ajouter un identifiant", - "addCredentialDesc": "Stockez les clés ou mots de passe SSH", + "addCredentialDesc": "Stocker des clés SSH ou des mots de passe", "tmuxMonitor": "Moniteur Tmux", - "tmuxMonitorDesc": "Surveillez les sessions tmux sur vos hôtes.", + "tmuxMonitorDesc": "Surveiller les sessions tmux sur vos hôtes", "recentActivity": "Activité récente", - "serversAndHosts": "Serveurs et hébergeurs", + "serversAndHosts": "Serveurs et hôtes", "noHostsFound": "Aucun hôte trouvé correspondant à \"{{search}}\"", - "links": "Links", + "links": "Liens", "navigate": "Naviguer", "select": "Sélectionner", "toggleWith": "Basculer avec" }, "splitScreen": { - "paneEmpty": "Panneau {{index}} - vide", - "noTabAssigned": "Aucun onglet assigné", + "paneEmpty": "Volet {{index}} - vide", + "noTabAssigned": "Aucun onglet attribué", "focusedPane": "Volet actif" }, "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}}", + "connectedFor": "Connecté depuis {{duration}}", "connected": "Connecté", "disconnected": "Déconnecté", "closeTab": "Fermer l'onglet", - "closeConnection": "Connexion étroite", + "closeConnection": "Fermer la connexion", "forgetTab": "Oublier", - "removeBackground": "Retirer", - "reconnect": "Reconnect", + "removeBackground": "Supprimer", + "reconnect": "Reconnecter", "reopenTab": "Rouvrir", - "sectionOpen": "Ouvrir", + "sectionOpen": "Ouvertes", "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", + "backgroundDesc": "Les sessions persistent pendant 30 minutes après la déconnexion et peuvent être reconnectées.", + "persisted": "Persisté en arrière-plan", "expiresIn": "Expire dans {{duration}}", "search": "Rechercher des connexions...", - "noSearchResults": "Aucun résultat ne correspond à votre recherche", - "rename": "Renommer la session" + "noSearchResults": "Aucune connexion ne correspond à votre recherche", + "rename": "Renommer la session", + "sectionSharedWithMe": "Partagé avec moi", + "sharedBy": "Partagé par {{username}}", + "join": "Rejoindre", + "sharedSessionLabel": "{{hostName}} (partagé)" + }, + "sessionSharing": { + "guestView": { + "loading": "Connexion à la session partagée...", + "linkInvalid": "Ce lien de partage est invalide, a expiré ou a été révoqué.", + "rateLimited": "Trop de tentatives, veuillez réessayer dans quelques instants.", + "sessionEnded": "Cette session est terminée.", + "readOnlyBadge": "Affichage uniquement" + }, + "modalTitle": "Session de partage", + "shareButton": "Partager", + "notReadyToShare": "La session n'est pas encore prête à être partagée.", + "modeTab": { + "link": "Lien", + "user": "Utilisateur" + }, + "linkModeDescription": "Toute personne disposant de ce lien peut s'inscrire, aucun compte n'est requis.", + "userModeDescription": "Partagez cette session avec un utilisateur spécifique qui y a déjà accès. S'il n'y a pas encore accès, partagez-lui d'abord l'hôte ou utilisez un lien. Une fois partagée, la session apparaîtra dans son onglet Connexions.", + "permissionLevel": { + "label": "Niveau d'autorisation", + "readOnly": "Lecture seule", + "readOnlyDescription": "Je peux suivre la session en direct, mais je ne peux ni écrire ni interagir.", + "readWrite": "Lecture-écriture", + "readWriteDescription": "Peut saisir du texte et interagir avec la session comme le propriétaire." + }, + "expiryLabel": "Expiration du lien", + "createLinkButton": "Créer un lien", + "createShareButton": "Partager avec l'utilisateur", + "searchUsersPlaceholder": "Rechercher des utilisateurs...", + "noUsersFound": "Aucun utilisateur trouvé", + "linkCreated": "Lien de partage créé", + "linkCopied": "Lien copié dans le presse-papiers", + "copyLink": "Copier le lien", + "shareCreated": "Session partagée. Elle apparaîtra dans leur onglet Connexions.", + "shareFailed": "Échec de la création du partage", + "userLacksHostAccess": "Cet utilisateur n'a pas encore accès à cet hôte. Veuillez d'abord lui partager l'hôte ou utiliser un lien.", + "activeShares": "Actions actives", + "noActiveShares": "Aucune action active pour cette session", + "revoke": "Révoquer", + "revokeConfirmTitle": "Révoquer cette action ?", + "revokeConfirmDescription": "Toute personne utilisant ce partage perdra immédiatement l'accès.", + "revoked": "Action révoquée", + "revokeFailed": "Échec de la révocation de l'action", + "joinCount": "{{count}} rejoindre", + "joinCount_other": "{{count}} rejoint", + "expiresAt": "Expire {{date}}", + "linkShareBadge": "Lien", + "userShareBadge": "Utilisateur : {{username}}", + "loadSharesFailed": "Échec du chargement des actions actives" }, "guacamole": { "connecting": "Connexion à la session {{type}}...", "connectionError": "Erreur de connexion", "connectionFailed": "Échec de la connexion", - "failedToConnect": "Impossible d'obtenir le jeton de connexion", + "failedToConnect": "Échec de l'obtention du jeton de connexion", "hostNotFound": "Hôte introuvable", "noHostSelected": "Aucun hôte sélectionné", - "reconnect": "Reconnect", + "reconnect": "Reconnecter", "retry": "Réessayer", - "guacdUnavailable": "Le service de bureau à distance (guacd) est indisponible. Veuillez vérifier que guacd est en cours d'exécution, accessible et correctement configuré dans les paramètres d'administration.", - "ctrlAltDel": "Ctrl+Alt+Suppr", + "guacdUnavailable": "Le service de bureau à distance (guacd) n'est pas disponible. Veuillez vous assurer que guacd est en cours d'exécution, accessible et correctement configuré dans les paramètres administrateur.", + "credentialPromptTitle": "Saisir les informations d'identification RDP", + "credentialPromptDescription": "Cet hôte est configuré pour demander des identifiants lors de la connexion. Ces identifiants sont utilisés uniquement pour cette session et ne sont pas enregistrés.", + "connect": "Connecter", + "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 (Verrouiller l'écran)", + "winKey": "Touche Windows", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Changement", - "win": "Gagner", - "stickyActive": "{{key}} (verrouillé - cliquez pour déverrouiller)", + "shift": "Maj", + "win": "Win", + "stickyActive": "{{key}} (verrouillé - cliquer pour libérer)", "stickyInactive": "{{key}} (cliquer pour verrouiller)", - "esc": "S'échapper", - "tab": "Languette", - "home": "Maison", + "esc": "Échap", + "tab": "Tab", + "home": "Début", "end": "Fin", - "pageUp": "Page précédente", - "pageDown": "Page suivante", - "arrowUp": "Flèche vers le haut", - "arrowDown": "Flèche vers le bas", + "pageUp": "Page préc.", + "pageDown": "Page suiv.", + "arrowUp": "Flèche haut", + "arrowDown": "Flèche bas", "arrowLeft": "Flèche gauche", "arrowRight": "Flèche droite", "fnToggle": "Touches de fonction", - "reconnect": "Session de reconnexion", + "reconnect": "Reconnecter la session", "collapse": "Réduire la barre d'outils", - "expand": "Barre d'outils Développer", - "dragHandle": "Faites glisser pour repositionner" + "expand": "Développer la barre d'outils", + "dragHandle": "Glisser pour repositionner", + "switchToTrackpad": "Passez en mode pavé tactile (glissez pour déplacer le curseur, appuyez pour cliquer)", + "switchToTouch": "Passez en mode tactile (appuyez directement à l'endroit où vous souhaitez cliquer)." } }, "terminal": { "connect": "Se connecter à l'hôte", - "clear": "Clair", + "clear": "Effacer", "paste": "Coller", - "reconnect": "Reconnect", + "reconnect": "Reconnecter", "connectionLost": "Connexion perdue", "connected": "Connecté", - "clipboardWriteFailed": "Impossible de copier le contenu dans le presse-papiers. Assurez-vous que la page est servie via HTTPS ou en local.", - "clipboardReadFailed": "Impossible de lire le contenu du presse-papiers. Veuillez vérifier que les autorisations d'accès au presse-papiers sont correctement accordées.", - "clipboardHttpWarning": "Le collage nécessite HTTPS. Utilisez Ctrl+Maj+V ou servez Termix via HTTPS.", - "passwordPromptFillTitle": "Saisissez le mot de passe enregistré dans ce champ ?", - "unknownError": "Une erreur inconnue s'est produite.", + "clipboardWriteFailed": "Échec de la copie dans le presse-papiers. Assurez-vous que la page est servie en HTTPS ou localhost.", + "clipboardReadFailed": "Échec de la lecture du presse-papiers. Assurez-vous que les autorisations du presse-papiers sont accordées.", + "clipboardHttpWarning": "Le collage nécessite HTTPS. Utilisez Ctrl+Maj+V ou servez Termix en HTTPS.", + "passwordPromptFillTitle": "Remplir ce champ avec le mot de passe enregistré ?", + "unknownError": "Une erreur inconnue s'est produite", "websocketError": "Erreur de connexion WebSocket", - "connecting": "De liaison...", + "connecting": "Connexion en cours...", "noHostSelected": "Aucun hôte sélectionné", - "reconnecting": "Reconnexion en cours... ({{attempt}}/{{max}})", - "reconnected": "Connexion rétablie avec succès", - "tmuxSessionCreated": "Session tmux créée : {{name}}", - "tmuxSessionAttached": "Session tmux attachée : {{name}}", - "tmuxUnavailable": "tmux n'étant pas installé sur l'hôte distant, le shell standard sera utilisé.", + "reconnecting": "Reconnexion... ({{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, retour au shell standard", "tmuxSessionPickerTitle": "Sessions tmux", - "tmuxSessionPickerDesc": "Des sessions tmux sont déjà en cours sur cet hôte. Sélectionnez-en une pour vous reconnecter ou créez-en une nouvelle.", - "tmuxWindows": "Windows", - "tmuxWindowCount": "{{count}} fenêtre", - "tmuxAttached": "Clients rattachés", - "tmuxAttachedCount": "{{count}} attaché", + "tmuxSessionPickerDesc": "Des sessions tmux existantes ont été trouvées sur cet hôte. Sélectionnez-en une pour vous y rattacher ou créez une nouvelle session.", + "tmuxWindows": "Fenêtres", + "tmuxWindowCount": "{{count}} fenêtre(s)", + "tmuxAttached": "Clients attachés", + "tmuxAttachedCount": "{{count}} attaché(s)", "tmuxLastActivity": "Dernière activité", - "tmuxTimeJustNow": "tout à l' heure", - "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": "Ajustez la sélection et appuyez sur Entrée pour copier dans le presse-papiers.", - "tmuxDetach": "Se déconnecter de la session tmux", - "tmuxDetached": "Déconnecté de la session tmux", + "tmuxTimeJustNow": "à l'instant", + "tmuxTimeMinutes": "il y a {{count}} min", + "tmuxTimeHours": "il y a {{count}} h", + "tmuxTimeDays": "il y a {{count}} j", + "tmuxCreateNew": "Créer une nouvelle session", + "tmuxCopyHint": "Ajustez la sélection et appuyez sur Entrée pour copier dans le presse-papiers", + "tmuxDetach": "Se détacher de la session tmux", + "tmuxDetached": "Détaché de la session tmux", "maxReconnectAttemptsReached": "Nombre maximal de tentatives de reconnexion atteint", "closeTab": "Fermer", "connectionTimeout": "Délai de connexion dépassé", @@ -1506,438 +1634,443 @@ "terminalWithPath": "Terminal - {{host}}:{{path}}", "runTitle": "Exécution de {{command}} - {{host}}", "totpRequired": "Authentification à deux facteurs requise", - "totpCodeLabel": "Le code de vérification", + "totpCodeLabel": "Code de vérification", "totpVerify": "Vérifier", + "mfaPromptRequired": "Authentification requise", + "mfaPushRequired": "Authentification push requise", + "mfaMenuPlaceholder": "Saisissez votre réponse", + "mfaWaitingApproval": "En attente d'approbation sur votre appareil...", + "mfaSendRequest": "Envoyer une requête", "warpgateAuthRequired": "Authentification 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": "Veuillez vous authentifier dans votre navigateur pour continuer. Cette session restera valide pendant 24 heures.", - "opksshOpenBrowser": "Ouvrez votre navigateur pour vous authentifier.", - "opksshWaitingForAuth": "En attente d'authentification dans le navigateur...", - "opksshAuthenticating": "Authentification en cours...", + "opksshAuthDescription": "Terminez l'authentification dans votre navigateur pour continuer. Cette session restera valide pendant 24 heures.", + "opksshOpenBrowser": "Ouvrir le navigateur pour s'authentifier", + "opksshWaitingForAuth": "En attente de l'authentification dans le navigateur...", + "opksshAuthenticating": "Traitement de l'authentification...", "opksshTimeout": "L'authentification a expiré. Veuillez réessayer.", - "opksshAuthFailed": "L'authentification a échoué. Veuillez vérifier vos identifiants et réessayer.", - "opksshSignInWith": "Connectez-vous avec {{provider}}", - "vaultAuthTitle": "Connexion au coffre-fort requise", - "vaultAuthDescription": "Une fenêtre s'est ouverte pour vous connecter à HashiCorp Vault. Veuillez vous connecter ; la connexion se poursuivra automatiquement.", - "vaultAuthFailed": "L'authentification Vault a échoué. Veuillez réessayer.", + "opksshAuthFailed": "Échec de l'authentification. Veuillez vérifier vos identifiants et réessayer.", + "opksshSignInWith": "Se connecter avec {{provider}}", + "vaultAuthTitle": "Connexion à Vault requise", + "vaultAuthDescription": "Une fenêtre s'est ouverte pour vous connecter à HashiCorp Vault. Terminez la connexion dans cette fenêtre ; cette connexion continuera automatiquement.", + "vaultAuthFailed": "Échec de l'authentification Vault. Veuillez réessayer.", "vaultReopen": "Rouvrir la fenêtre de connexion", - "sudoPasswordPopupTitle": "Saisir le mot de passe ?", + "sudoPasswordPopupTitle": "Insérer le mot de passe ?", "linkDialogTitle": "Ouvrir le lien", "linkDialogOpen": "Ouvrir", - "linkDialogCopy": "Copie", - "websocketAbnormalClose": "La connexion a été interrompue de manière inattendue. Cela peut être dû à un problème de configuration du proxy inverse ou du protocole SSL. Veuillez consulter les journaux du serveur.", + "linkDialogCopy": "Copier", + "websocketAbnormalClose": "Connexion fermée de manière inattendue. Cela peut être dû à un problème de proxy inverse ou de configuration SSL. Veuillez vérifier les journaux du serveur.", "connectionLogTitle": "Journal de connexion", "connectionLogCopy": "Copier les journaux dans le presse-papiers", - "connectionLogEmpty": "Aucun journal de connexion pour le moment", + "connectionLogEmpty": "Aucun journal de connexion pour l'instant", "connectionLogWaiting": "En attente des journaux de connexion...", "connectionLogCopied": "Journaux de connexion copiés dans le presse-papiers", - "connectionLogCopyFailed": "Impossible de copier les journaux dans le presse-papiers", - "connectionRejected": "Connexion refusée par le serveur. Veuillez vérifier votre authentification et votre configuration réseau.", - "hostKeyRejected": "Vérification de la clé hôte SSH refusée. Connexion annulée.", - "sessionTakenOver": "La session a été ouverte dans un autre onglet. Reconnexion en cours…", + "connectionLogCopyFailed": "Échec de la copie des journaux dans le presse-papiers", + "connectionRejected": "Connexion rejetée par le serveur. Veuillez vérifier votre authentification et votre configuration réseau.", + "hostKeyRejected": "Vérification de la clé de l'hôte SSH rejetée. Connexion annulée.", + "sessionTakenOver": "La session a été ouverte dans un autre onglet. Reconnexion en cours...", "split": { - "splitTab": "Onglet fractionné", - "addToSplit": "Ajouter au fractionnement", + "splitTab": "Diviser l'onglet", + "addToSplit": "Ajouter à la division", "removeFromSplit": "Retirer de la division" } }, "fileManager": { "noHostSelected": "Aucun hôte sélectionné", "initializingEditor": "Initialisation de l'éditeur...", - "file": "Déposer", + "file": "Fichier", "folder": "Dossier", "uploadFile": "Téléverser un fichier", "downloadFile": "Télécharger", - "extractArchive": "Extraction de l'archive", + "extractArchive": "Extraire l'archive", "extractingArchive": "Extraction de {{name}}...", "archiveExtractedSuccessfully": "{{name}} extrait avec succès", - "extractFailed": "L'extraction a échoué.", - "compressFile": "Fichier compressé", + "extractFailed": "Échec de l'extraction", + "compressFile": "Compresser le fichier", "compressFiles": "Compresser les fichiers", - "compressFilesDesc": "Compresser les éléments {{count}} dans une archive", + "compressFilesDesc": "Compresser {{count}} éléments en une archive", "archiveName": "Nom de l'archive", - "enterArchiveName": "Saisissez le nom de l'archive...", + "enterArchiveName": "Entrez le nom de l'archive...", "compressionFormat": "Format de compression", "selectedFiles": "Fichiers sélectionnés", - "andMoreFiles": "et {{count}} plus...", - "compress": "Compresse", - "compressingFiles": "Compression des éléments {{count}} en {{name}}...", + "andMoreFiles": "et {{count}} de plus...", + "compress": "Compresser", + "compressingFiles": "Compression de {{count}} éléments en {{name}}...", "filesCompressedSuccessfully": "{{name}} créé avec succès", "compressFailed": "Échec de la compression", "edit": "Modifier", "preview": "Aperçu", "previous": "Précédent", "next": "Suivant", - "pageXOfY": "Page {{current}} de {{total}}", - "zoomOut": "Dézoomer", - "zoomIn": "Zoomer", + "pageXOfY": "Page {{current}} sur {{total}}", + "zoomOut": "Zoom arrière", + "zoomIn": "Zoom avant", "newFile": "Nouveau fichier", "newFolder": "Nouveau dossier", - "rename": "Rebaptiser", - "uploading": "Chargement en cours...", - "uploadingFile": "Téléchargement en cours {{name}}...", - "fileName": "Nom de fichier", + "rename": "Renommer", + "uploading": "Téléversement...", + "uploadingFile": "Téléversement de {{name}}...", + "fileName": "Nom du fichier", "folderName": "Nom du dossier", - "fileUploadedSuccessfully": "Le fichier «{{name}}» a été téléchargé avec succès.", - "failedToUploadFile": "Échec du chargement du fichier", - "fileDownloadedSuccessfully": "Le fichier «{{name}}» a été téléchargé avec succès", + "fileUploadedSuccessfully": "Fichier \"{{name}}\" téléversé avec succès", + "failedToUploadFile": "Échec du téléversement du fichier", + "fileDownloadedSuccessfully": "Fichier \"{{name}}\" téléchargé avec succès", "failedToDownloadFile": "Échec du téléchargement du fichier", - "fileCreatedSuccessfully": "Fichier «{{name}}» créé avec succès", - "folderCreatedSuccessfully": "Dossier «{{name}}» créé avec succès", + "fileCreatedSuccessfully": "Fichier \"{{name}}\" créé avec succès", + "folderCreatedSuccessfully": "Dossier \"{{name}}\" créé avec succès", "failedToCreateItem": "Échec de la création de l'élément", - "operationFailed": "L'opération {{operation}} a échoué pour {{name}}: {{error}}", - "failedToResolveSymlink": "Impossible de résoudre le lien symbolique", + "operationFailed": "L'opération {{operation}} a échoué pour {{name}} : {{error}}", + "failedToResolveSymlink": "Échec de la résolution du lien symbolique", "itemsDeletedSuccessfully": "{{count}} éléments supprimés avec succès", "failedToDeleteItems": "Échec de la suppression des éléments", "sudoPasswordRequired": "Mot de passe administrateur requis", - "enterSudoPassword": "Saisissez votre mot de passe sudo pour continuer cette opération.", + "enterSudoPassword": "Entrez le mot de passe sudo pour continuer cette opération", "sudoPassword": "Mot de passe sudo", - "sudoOperationFailed": "L'opération Sudo a échoué", + "sudoOperationFailed": "L'opération sudo a échoué", "sudoAuthFailed": "L'authentification sudo a échoué", - "dragFilesToUpload": "Déposez les fichiers ici pour les télécharger.", - "emptyFolder": "Ce dossier est vide.", + "dragFilesToUpload": "Déposez les fichiers ici pour les téléverser", + "emptyFolder": "Ce dossier est vide", "searchFiles": "Rechercher des fichiers...", - "upload": "Télécharger", + "upload": "Téléverser", "selectHostToStart": "Sélectionnez un hôte pour démarrer la gestion des fichiers", - "sshRequiredForFileManager": "Le gestionnaire de fichiers requiert une connexion SSH. Cette machine ne dispose pas de SSH activé.", + "sshRequiredForFileManager": "Le gestionnaire de fichiers nécessite SSH. Cet hôte n'a pas SSH activé.", "failedToConnect": "Échec de la connexion SSH", "failedToLoadDirectory": "Échec du chargement du répertoire", "noSSHConnection": "Aucune connexion SSH disponible", - "copy": "Copie", + "copy": "Copier", "cut": "Couper", "paste": "Coller", "copyPath": "Copier le chemin", "copyPaths": "Copier les chemins", "delete": "Supprimer", "properties": "Propriétés", - "refresh": "Rafraîchir", - "downloadFiles": "Télécharger les fichiers {{count}} dans le navigateur", - "copyFiles": "Copier les éléments {{count}}", - "cutFiles": "Couper les éléments {{count}}", - "deleteFiles": "Supprimer les éléments {{count}}", + "refresh": "Actualiser", + "downloadFiles": "Télécharger {{count}} fichiers dans le navigateur", + "copyFiles": "Copier {{count}} éléments", + "cutFiles": "Couper {{count}} éléments", + "deleteFiles": "Supprimer {{count}} éléments", "filesCopiedToClipboard": "{{count}} éléments copiés dans le presse-papiers", "filesCutToClipboard": "{{count}} éléments coupés dans le presse-papiers", "pathCopiedToClipboard": "Chemin copié dans le presse-papiers", "pathsCopiedToClipboard": "{{count}} chemins copiés dans le presse-papiers", - "failedToCopyPath": "Impossible de copier le chemin dans le presse-papiers", + "failedToCopyPath": "Échec de la copie du chemin dans le presse-papiers", "copyFolderLink": "Copier le lien vers le dossier", "copyCurrentFolderLink": "Copier le lien vers le dossier actuel", "folderLinkCopied": "Lien du dossier copié dans le presse-papiers", - "failedToCopyFolderLink": "Impossible de copier le lien du dossier", - "movedItems": "Éléments déplacés {{count}}", - "failedToDeleteItem": "Échec de la suppression de l'élément", + "failedToCopyFolderLink": "Échec de la copie du lien du dossier", + "movedItems": "{{count}} élément(s) 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", + "failedToRenameItem": "Échec du renommage de l’élément", "download": "Télécharger", - "openExternalEditor": "Ouvrir en externe", - "chooseExternalEditor": "Choisir un éditeur externe", + "openExternalEditor": "Ouvrir avec l’éditeur externe", + "chooseExternalEditor": "Choisir l’éditeur externe", "externalEditorSelected": "Éditeur externe sélectionné", - "externalEditorDesktopOnly": "L'éditeur externe est uniquement disponible dans l'application de bureau.", - "externalEditorOpened": "Ouvert dans un éditeur externe. L'enregistrement dans cet éditeur enverra les modifications au serveur.", - "failedToOpenExternalEditor": "Impossible d'ouvrir l'éditeur externe", - "failedToSelectExternalEditor": "Impossible de sélectionner un éditeur externe", - "permissions": "Autorisations", + "externalEditorDesktopOnly": "L’éditeur externe est uniquement disponible dans l’application de bureau", + "externalEditorOpened": "Ouvert dans l’éditeur externe. Les modifications enregistrées seront renvoyées vers le serveur.", + "failedToOpenExternalEditor": "Échec de l’ouverture de l’éditeur externe", + "failedToSelectExternalEditor": "Échec de la sélection de l’éditeur externe", + "permissions": "Permissions", "size": "Taille", "modified": "Modifié", "path": "Chemin", - "confirmDelete": "Êtes-vous sûr de vouloir supprimer {{name}}?", - "permissionDenied": "Autorisation refusée", + "confirmDelete": "Êtes-vous sûr de vouloir supprimer {{name}} ?", + "permissionDenied": "Permission refusée", "serverError": "Erreur serveur", "fileSavedSuccessfully": "Fichier enregistré avec succès", - "failedToSaveFile": "Impossible d'enregistrer le fichier", - "confirmDeleteSingleItem": "Êtes-vous sûr de vouloir supprimer définitivement \"{{name}} \" ?", - "confirmDeleteMultipleItems": "Êtes-vous sûr de vouloir supprimer définitivement {{count}} éléments ?", - "confirmDeleteMultipleItemsWithFolders": "Êtes-vous sûr de vouloir supprimer définitivement {{count}} éléments ? 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 est irréversible. Les éléments seront définitivement supprimés du serveur.", - "recent": "Récent", - "pinned": "Épinglé", + "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 {{count}} éléments ?", + "confirmDeleteMultipleItemsWithFolders": "Êtes-vous sûr de vouloir supprimer définitivement {{count}} éléments ? 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 est irréversible. Le ou les éléments seront définitivement supprimés du serveur.", + "recent": "Récents", + "pinned": "Épinglés", "folderShortcuts": "Raccourcis de dossiers", - "failedToReconnectSSH": "Échec de la reconnexion à la session SSH", + "failedToReconnectSSH": "Échec de la reconnexion de la session SSH", "openTerminalHere": "Ouvrir le terminal ici", - "run": "Courir", + "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": "Ne peut exécuter que des fichiers exécutables", - "directories": "Annuaires", - "removedFromRecentFiles": "Suppression de «{{name}}» des fichiers récents", - "removeFailed": "Supprimer l'échec", - "unpinnedSuccessfully": "Désépinglé «{{name}}» avec succès", - "unpinFailed": "Échec du déverrouillage", - "removedShortcut": "Raccourci supprimé \"{{name}}\"", + "onlyRunExecutableFiles": "Seuls les fichiers exécutables peuvent être exécutés", + "directories": "Dossiers", + "removedFromRecentFiles": "« {{name}} » retiré des fichiers récents", + "removeFailed": "Échec de la suppression", + "unpinnedSuccessfully": "« {{name}} » désépinglé avec succès", + "unpinFailed": "Échec du désépinglage", + "removedShortcut": "Raccourci « {{name}} » supprimé", "removeShortcutFailed": "Échec de la suppression du raccourci", - "clearedAllRecentFiles": "Tous les fichiers récents ont été supprimés.", - "clearFailed": "Échec de la clarification", - "removeFromRecentFiles": "Supprimer des fichiers récents", + "clearedAllRecentFiles": "Tous les fichiers récents ont été effacés", + "clearFailed": "Échec de l'effacement", + "removeFromRecentFiles": "Retirer des fichiers récents", "clearAllRecentFiles": "Effacer tous les fichiers récents", - "unpinFile": "Dépiner le fichier", + "unpinFile": "Désépingler le fichier", "removeShortcut": "Supprimer le raccourci", - "pinFile": "Fichier d'épingles", + "pinFile": "Épingler le fichier", "addToShortcuts": "Ajouter aux raccourcis", "pasteFailed": "Échec du collage", - "noUndoableActions": "Aucune action irréversible", - "undoCopySuccess": "Opération de copie annulée : Suppression de {{count}} fichiers copiés", - "undoCopyFailedDelete": "L'annulation a échoué : impossible de supprimer les fichiers copiés.", - "undoCopyFailedNoInfo": "L'annulation a échoué : impossible de trouver les informations du fichier copié.", - "undoMoveSuccess": "Opération de déplacement annulée : les fichiers {{count}} ont été replacés à leur emplacement d'origine.", - "undoMoveFailedMove": "L'annulation a échoué : impossible de récupérer les fichiers.", - "undoMoveFailedNoInfo": "L'annulation a échoué : impossible de trouver les informations du fichier déplacé.", - "undoDeleteNotSupported": "L'opération de suppression est irréversible : les fichiers ont été définitivement supprimés du serveur.", - "undoTypeNotSupported": "Type d'opération d'annulation non pris en charge", - "undoOperationFailed": "L'opération d'annulation a échoué", + "noUndoableActions": "Aucune action annulable", + "undoCopySuccess": "Annulation de la copie : {{count}} fichier(s) copié(s) supprimé(s)", + "undoCopyFailedDelete": "Échec de l'annulation : impossible de supprimer les fichiers copiés", + "undoCopyFailedNoInfo": "Échec de l'annulation : informations sur les fichiers copiés introuvables", + "undoMoveSuccess": "Annulation du déplacement : {{count}} fichier(s) remis à leur emplacement d’origine", + "undoMoveFailedMove": "Échec de l'annulation : impossible de remettre les fichiers en place", + "undoMoveFailedNoInfo": "Échec de l'annulation : informations sur les fichiers déplacés introuvables", + "undoDeleteNotSupported": "La suppression ne peut pas être annulée : les fichiers ont été définitivement supprimés du serveur", + "undoTypeNotSupported": "Type d’annulation non pris en charge", + "undoOperationFailed": "Échec de l’annulation", "unknownError": "Erreur inconnue", "confirm": "Confirmer", - "find": "Trouver...", + "find": "Rechercher...", "replace": "Remplacer", "downloadInstead": "Télécharger à la place", "keyboardShortcuts": "Raccourcis clavier", - "searchAndReplace": "Recherche et remplacement", + "searchAndReplace": "Rechercher et remplacer", "editing": "Édition", - "search": "Recherche", - "findNext": "Trouver le suivant", - "findPrevious": "Trouver les précédents", - "save": "Sauvegarder", + "search": "Rechercher", + "findNext": "Rechercher le suivant", + "findPrevious": "Rechercher le précédent", + "save": "Enregistrer", "selectAll": "Tout sélectionner", - "undo": "Défaire", - "redo": "Refaire", - "moveLineUp": "Déplacez la file d'attente", + "undo": "Annuler", + "redo": "Rétablir", + "moveLineUp": "Déplacer la ligne vers le haut", "moveLineDown": "Déplacer la ligne vers le bas", - "toggleComment": "Afficher/masquer les commentaires", - "autoComplete": "Saisie semi-automatique", - "imageLoadError": "Impossible de charger l'image", - "startTyping": "Commencez à taper...", + "toggleComment": "Commenter/Décommenter", + "autoComplete": "Complétion automatique", + "imageLoadError": "Échec du chargement de l’image", + "startTyping": "Commencez à taper…", "unknownSize": "Taille inconnue", - "fileIsEmpty": "Le fichier est vide.", - "largeFileWarning": "Avertissement concernant les fichiers volumineux", - "largeFileWarningDesc": "Ce fichier a une taille de {{size}} , ce qui peut entraîner des problèmes de performance lors de son ouverture en tant que texte.", - "fileNotFoundAndRemoved": "Le fichier «{{name}}» est introuvable et a été supprimé des fichiers récents/épinglés.", - "failedToLoadFile": "Échec du chargement du fichier : {{error}}", - "serverErrorOccurred": "Une erreur serveur s'est produite. Veuillez réessayer plus tard.", - "autoSaveFailed": "Échec de la sauvegarde automatique", + "fileIsEmpty": "Le fichier est vide", + "largeFileWarning": "Avertissement – Fichier volumineux", + "largeFileWarningDesc": "Ce fichier a une taille de {{size}}, ce qui peut entraîner des problèmes de performances lorsqu’il est ouvert en tant que texte.", + "fileNotFoundAndRemoved": "Le fichier « {{name}} » est introuvable et a été retiré des fichiers récents/épinglés.", + "failedToLoadFile": "Échec du chargement du fichier : {{error}}", + "serverErrorOccurred": "Une erreur serveur s’est produite. Veuillez réessayer plus tard.", + "autoSaveFailed": "Échec de l’enregistrement automatique", "fileAutoSaved": "Fichier enregistré automatiquement", - "moveFileFailed": "Échec du déplacement {{name}}", - "moveOperationFailed": "L'opération de déplacement a échoué.", - "canOnlyCompareFiles": "Impossible de comparer plus de deux fichiers", - "comparingFiles": "Comparaison des fichiers : {{file1}} et {{file2}}", - "dragFailed": "L'opération de glisser-déposer a échoué", - "filePinnedSuccessfully": "Fichier «{{name}}» épinglé avec succès", - "pinFileFailed": "Échec de l'épinglage du fichier", - "fileUnpinnedSuccessfully": "Le fichier «{{name}}» a été désépinglé avec succès", - "unpinFileFailed": "Échec du désépinglage du fichier", - "shortcutAddedSuccessfully": "Raccourci de dossier «{{name}}» ajouté avec succès", + "moveFileFailed": "Échec du déplacement de {{name}}", + "moveOperationFailed": "L’opération de déplacement a échoué", + "canOnlyCompareFiles": "Il est uniquement possible de comparer deux fichiers", + "comparingFiles": "Comparaison des fichiers : {{file1}} et {{file2}}", + "dragFailed": "L’opération de glisser-déposer a échoué", + "filePinnedSuccessfully": "Le fichier « {{name}} » a été épinglé avec succès", + "pinFileFailed": "Échec de l’épinglage du fichier", + "fileUnpinnedSuccessfully": "Le fichier « {{name}} » a été désépinglé avec succès", + "unpinFileFailed": "Impossible de désépingler le fichier", + "shortcutAddedSuccessfully": "Raccourci de dossier « {{name}} » ajouté avec succès", "addShortcutFailed": "Impossible d'ajouter le raccourci", - "operationCompletedSuccessfully": "{{operation}} {{count}} éléments réussis", + "operationCompletedSuccessfully": "{{operation}} {{count}} éléments avec succès", "operationCompleted": "{{operation}} {{count}} éléments", "downloadFileSuccess": "Fichier {{name}} téléchargé avec succès", "downloadFileFailed": "Échec du téléchargement", - "moveTo": "Déplacez-vous vers {{name}}", - "diffCompareWith": "Diff comparez avec {{name}}", - "dragOutsideToDownload": "Faites glisser en dehors de la fenêtre pour télécharger ({{count}} fichiers)", + "moveTo": "Déplacer vers {{name}}", + "diffCompareWith": "Diff avec {{name}}", + "dragOutsideToDownload": "Faites glisser hors de la fenêtre pour télécharger ({{count}} fichiers)", "newFolderDefault": "Nouveau dossier", - "newFileDefault": "NouveauFichier.txt", - "successfullyMovedItems": "Les objets {{count}} ont été déplacés vers {{target}} avec succès", - "move": "Se déplacer", - "searchInFile": "Rechercher dans un fichier (Ctrl+F)", + "newFileDefault": "Nouveau fichier.txt", + "successfullyMovedItems": "{{count}} éléments déplacés avec succès vers {{target}}", + "move": "Déplacer", + "searchInFile": "Rechercher dans le fichier (Ctrl+F)", "showKeyboardShortcuts": "Afficher les raccourcis clavier", - "decreaseFontSize": "Réduire la taille de la police", - "increaseFontSize": "Augmenter la taille de la police", - "startWritingMarkdown": "Commencez à rédiger votre contenu Markdown...", + "decreaseFontSize": "Diminuer la taille de police", + "increaseFontSize": "Augmenter la taille de police", + "startWritingMarkdown": "Commencez à rédiger votre contenu markdown...", "loadingFileComparison": "Chargement de la comparaison de fichiers...", "reload": "Recharger", "compare": "Comparer", - "sideBySide": "côte à côte", + "sideBySide": "Côte à côte", "inline": "En ligne", - "fileComparison": "Comparaison de fichiers : {{file1}} vs {{file2}}", - "fileTooLarge": "Fichier trop volumineux : {{error}}", + "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": "Échec du chargement du fichier : {{error}}", - "connecting": "De liaison...", - "connectedSuccessfully": "Connexion réussie", - "totpVerificationFailed": "La vérification TOTP a échoué", - "warpgateVerificationFailed": "L'authentification Warpgate a échoué", - "authenticationFailed": "L'authentification a échoué", - "incorrectPassphrase": "Mot de passe incorrect. Veuillez réessayer.", - "verificationCodePrompt": "Le code de vérification:", - "changePermissions": "Modifier les autorisations", - "currentPermissions": "Autorisations actuelles", + "loadFileFailed": "Impossible de charger le fichier : {{error}}", + "connecting": "Connexion en cours...", + "connectedSuccessfully": "Connecté avec succès", + "totpVerificationFailed": "Échec de la vérification TOTP", + "warpgateVerificationFailed": "Échec de l'authentification Warpgate", + "authenticationFailed": "Échec de l'authentification", + "incorrectPassphrase": "Phrase secrète incorrecte. Veuillez réessayer.", + "verificationCodePrompt": "Code de vérification :", + "changePermissions": "Modifier les permissions", + "currentPermissions": "Permissions actuelles", "owner": "Propriétaire", "group": "Groupe", "others": "Autres", - "read": "Lire", - "write": "Écrire", - "execute": "Exécuter", - "permissionsChangedSuccessfully": "Les autorisations ont été modifiées avec succès.", - "failedToChangePermissions": "Impossible de modifier les autorisations.", + "read": "Lecture", + "write": "Écriture", + "execute": "Exécution", + "permissionsChangedSuccessfully": "Permissions modifiées avec succès", + "failedToChangePermissions": "Échec de la modification des permissions", "name": "Nom", "sortByName": "Nom", "sortByDate": "Date de modification", "sortBySize": "Taille", - "ascending": "Ascendant", - "descending": "Descendant", + "ascending": "Croissant", + "descending": "Décroissant", "root": "Racine", "new": "Nouveau", "sortBy": "Trier par", - "items": "Articles", - "selected": "Choisi", + "items": "Éléments", + "selected": "Sélectionné", "editor": "Éditeur", "octal": "Octal", "storage": "Stockage", "disk": "Disque", "used": "Utilisé", - "of": "de", - "toggleSidebar": "Afficher/masquer la barre latérale", + "of": "sur", + "toggleSidebar": "Afficher/Masquer 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..." }, "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", + "copyToHost": "Copier vers l’hôte…", + "moveToHost": "Déplacer vers l’hôte…", + "copyItemsToHost": "Copier {{count}} éléments vers l’hôte…", + "moveItemsToHost": "Déplacer {{count}} éléments vers l’hôte…", + "noHostsConnected": "Aucun autre hôte de gestionnaire de fichiers disponible.", + "noHostsConnectedHint": "Ajoutez un autre hôte SSH avec le gestionnaire de fichiers activé dans le gestionnaire d’hôtes.", + "selectDestinationHost": "Sélectionner 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}}…", + "browseDestination": "Parcourir ou saisir un chemin", + "confirmCopy": "Copier", + "confirmMove": "Déplacer", + "transferring": "Transfert en cours…", + "compressing": "Compression en cours…", + "extracting": "Extraction en cours…", + "transferringItems": "Transfert de {{current}} sur {{total}} éléments…", "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…", + "itemsSummary": "{{count}} éléments", + "destMustBeDirectory": "La destination doit être un dossier pour les transferts de plusieurs éléments", + "selectThisFolder": "Sélectionner 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": "Remonter", + "copyFolderToHost": "Copier le dossier vers l’hôte…", + "moveFolderToHost": "Déplacer le dossier vers l’hôte…", "hostReady": "Prêt", - "hostConnecting": "Connexion…", + "hostConnecting": "Connexion en cours…", "hostDisconnected": "Non connecté", - "hostAuthRequired": "Authentification requise — ouvrez d’abord le Gestionnaire de fichiers sur cet hôte", + "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}}", + "metricsTitle": "Durées de transfert", + "metricsPrepare": "Préparation de la destination : {{duration}}", + "metricsCompress": "Compression sur la source : {{duration}}", "metricsHopSourceRead": "Source → serveur : {{throughput}}", - "metricsHopDestSftpWrite": "Serveur → destination (SFTP) : {{throughput}}", - "metricsHopDestLocalWrite": "Serveur → destination (locale) : {{throughput}}", + "metricsHopDestSftpWrite": "Serveur → dest (SFTP) : {{throughput}}", + "metricsHopDestLocalWrite": "Serveur → dest (local) : {{throughput}}", "metricsTransfer": "De bout en bout : {{throughput}} ({{duration}})", - "metricsExtract": "Extraire à destination : {{duration}}", - "metricsSourceDelete": "Supprimer de la source : {{duration}}", + "metricsExtract": "Extraction sur la destination : {{duration}}", + "metricsSourceDelete": "Suppression depuis la source : {{duration}}", "metricsTotal": "Total : {{duration}}", - "progressCompressing": "Compression sur l'hôte source…", + "progressCompressing": "Compression sur l’hôte source…", "progressExtracting": "Extraction sur la destination…", - "progressTransferring": "Transfert de données…", + "progressTransferring": "Transfert des données…", "progressReconnecting": "Reconnexion…", - "parallelSegmentsLabel": "voies de transfert parallèles", + "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}})…", + "parallelSegmentsHint": "Les fichiers volumineux sont divisés en segments de 256 Mo. Les voies multiples utilisent des connexions distinctes (comme lancer plusieurs transferts) pour un débit total plus élevé.", + "progressTotalSpeed": "{{speed}} au total ({{lanes}} voies)", + "progressTransferringItems": "Transfert des fichiers ({{current}} sur {{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.", + "jumpHostLimitation": "Les deux hôtes doivent être accessibles depuis le serveur Termix. Le routage direct d’hôte à hôte n’est pas pris en charge.", "cancel": "Annuler", "methodLabel": "Méthode de transfert", "methodAuto": "Auto", - "methodTar": "archive Tar", + "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.", + "methodAutoHint": "Choisit l’archive tar ou le SFTP par fichier selon le nombre de fichiers, leur taille et leur compressibilité. Les fichiers uniques utilisent toujours le SFTP en continu (streaming).", + "methodTarHint": "Compresse sur la source, transfère une archive, extrait sur la destination. Nécessite tar sur les deux hôtes Unix.", + "methodItemSftpHint": "Transfère chaque fichier individuellement via SFTP. Fonctionne sur tous les hôtes, y compris Windows.", + "methodPreviewLoading": "Calcul de la méthode de transfert…", + "methodPreviewError": "Impossible de prévisualiser la méthode de transfert. Le serveur choisira tout de même une méthode au lancement.", + "methodPreviewWillUseTar": "Utilisera : Archive tar", + "methodPreviewWillUseItemSftp": "Utilisera : SFTP par fichier", + "methodPreviewScanSummary": "{{fileCount}} fichiers, {{totalSize}} au total (analysés sur l’hôte source).", + "methodItemSftpLimitation": "Chaque fichier utilise le même flux SFTP qu’une copie de fichier unique, l’un après l’autre. La progression est combinée pour tous les fichiers, donc la barre avance lentement lors de gros fichiers.", "methodReason": { - "user_item_sftp": "Vous avez choisi le protocole SFTP par fichier.", + "user_item_sftp": "Vous avez choisi 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.", + "tar_unavailable": "Tar n'est pas disponible sur l'un ou les deux hôtes — SFTP par fichier sera utilisé à la place.", + "windows_host": "Un hôte Windows est impliqué — tar n'est pas utilisé.", + "auto_multi_large": "Auto : plusieurs fichiers dont un fichier volumineux ({{largestSize}}) avec des données compressibles — tar regroupe 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_many_incompressible": "Auto : données principalement incompressibles — SFTP par fichier.", + "auto_many_files": "Auto : nombreux fichiers ({{fileCount}}) — tar réduit la surcharge par fichier.", "auto_default": "Auto : SFTP par fichier pour cet ensemble." }, "progressCancel": "Annuler", - "progressCancelling": "Annulation…", + "progressCancelling": "Annulation en cours…", "progressStalled": "Bloqué", - "resumedHint": "Reconnexion à un transfert actif démarré dans une autre fenêtre.", + "resumedHint": "Reconnecté à 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é", + "transferCancelledCopyHint": "Des fichiers partiels peuvent subsister sur la destination.", + "transferCancelledMoveHint": "Des fichiers partiels peuvent subsister sur la destination. Les fichiers sources n'ont pas été supprimés.", + "cleanupDestFiles": "Nettoyer la destination", + "cleanupDestFilesSuccess": "Fichiers partiels supprimés de la destination", + "cleanupDestFilesPartial": "Certains fichiers partiels n'ont pas pu être supprimés", + "cleanupDestFilesNothing": "Rien à nettoyer sur la destination", + "cleanupDestFilesError": "Échec du nettoyage", "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." + "transferFailedRetryHint": "Des données partielles ont été conservées sur la destination. La nouvelle tentative reprendra lorsque la connexion sera rétablie." }, "tunnels": { - "noSshTunnels": "Pas de tunnels SSH", - "createFirstTunnelMessage": "Vous n'avez pas encore créé de tunnels SSH. Configurez les connexions de tunnel dans le Gestionnaire d'hôtes pour commencer.", + "noSshTunnels": "Aucun tunnel SSH", + "createFirstTunnelMessage": "Vous n'avez encore créé aucun tunnel SSH. Configurez des connexions de tunnel dans le Gestionnaire d'hôtes pour commencer.", "connected": "Connecté", "disconnected": "Déconnecté", - "connecting": "De liaison...", + "connecting": "Connexion en cours...", "error": "Erreur", - "canceling": "Annulation...", + "canceling": "Annulation en cours...", "connect": "Connecter", "disconnect": "Déconnecter", "cancel": "Annuler", "port": "Port", "localPort": "Port local", "remotePort": "Port distant", - "currentHostPort": "Port hôte actuel", - "endpointPort": "Port de point de terminaison", + "currentHostPort": "Port de l'hôte actuel", + "endpointPort": "Port du point de terminaison", "bindIp": "IP locale", "endpointSshConfig": "Configuration SSH du point de terminaison", "endpointSshHost": "Hôte SSH du point de terminaison", - "endpointSshHostPlaceholder": "Sélectionnez un hôte configuré", + "endpointSshHostPlaceholder": "Sélectionner un hôte configuré", "endpointSshHostRequired": "Sélectionnez un hôte SSH de point de terminaison pour chaque tunnel client.", - "attempt": "Tentative {{current}} de {{max}}", + "attempt": "Tentative {{current}} sur {{max}}", "nextRetryIn": "Prochaine tentative dans {{seconds}} secondes", - "clientTunnels": "Tunnels clients", + "clientTunnels": "Tunnels client", "clientTunnel": "Tunnel client", "addClientTunnel": "Ajouter un tunnel client", - "noClientTunnels": "Aucun tunnel client n'est configuré sur ce poste de travail.", + "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": "Utilisez les boutons Démarrer et Arrêter de cette ligne. Termix ne l'ouvrira pas automatiquement.", - "clientRemoteServerNote": "Le transfert de connexion à distance peut nécessiter les autorisations AllowTcpForwarding et GatewayPorts sur le serveur SSH du point de terminaison. Le port distant se ferme lorsque ce poste de travail se déconnecte.", - "clientTunnelStarted": "Le tunnel client a démarré", - "clientTunnelStopped": "Le tunnel client a été arrêté.", - "tunnelTestSucceeded": "Le test du tunnel a été conclu", - "tunnelTestFailed": "Le test du tunnel a échoué", - "localSaved": "Tunnels clients sauvegardés", - "localSaveError": "Impossible d'enregistrer les tunnels clients locaux.", - "invalidBindIp": "L'adresse IP locale doit être une adresse IPv4 valide.", - "invalidLocalTargetIp": "L'adresse IP cible locale doit être une adresse IPv4 valide.", + "clientAutoStartDesc": "Démarre à l'ouverture de ce client de bureau et reste connecté.", + "clientManualStartDesc": "Utilisez Démarrer et Arrêter depuis cette ligne. Termix ne l'ouvrira pas automatiquement.", + "clientRemoteServerNote": "La redirection distante peut nécessiter AllowTcpForwarding et GatewayPorts sur le serveur SSH du point de terminaison. 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": "Échec du test du tunnel", + "localSaved": "Tunnels client enregistrés", + "localSaveError": "Échec de l'enregistrement des tunnels client locaux", + "invalidBindIp": "L'IP locale doit être une adresse IPv4 valide.", + "invalidLocalTargetIp": "L'IP cible locale 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 cible local doit être compris entre 1 et 65535.", @@ -1945,107 +2078,107 @@ "duplicateAutoStartBind": "Un seul tunnel client à démarrage automatique peut utiliser {{bind}}.", "manualControlError": "Échec de la mise à jour de l'état du tunnel.", "active": "Actif", - "start": "Commencer", - "stop": "Arrêt", - "test": "Test", + "start": "Démarrer", + "stop": "Arrêter", + "test": "Tester", "type": "Type de tunnel", "typeLocal": "Local (-L)", - "typeRemote": "Télécommande (-R)", + "typeRemote": "Distant (-R)", "typeDynamic": "Dynamique (-D)", - "typeServerLocalDesc": "Hôte actuel vers point de terminaison.", - "typeServerRemoteDesc": "Point de terminaison vers l'hôte actuel.", - "typeClientLocalDesc": "Ordinateur local vers point de terminaison.", - "typeClientRemoteDesc": "Point de terminaison renvoyé à l'ordinateur local.", - "typeClientDynamicDesc": "SOCKS sur l'ordinateur local.", - "typeDynamicDesc": "Transférer le trafic SOCKS5 CONNECT via SSH", + "typeServerLocalDesc": "De l’hôte actuel vers le point de terminaison.", + "typeServerRemoteDesc": "Du point de terminaison vers l’hôte actuel.", + "typeClientLocalDesc": "De l’ordinateur local vers le point de terminaison.", + "typeClientRemoteDesc": "Du point de terminaison vers l’ordinateur local.", + "typeClientDynamicDesc": "SOCKS sur l’ordinateur local.", + "typeDynamicDesc": "Transférer le trafic SOCKS5 CONNECT via 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}}.", + "forwardDescriptionServerDynamic": "SOCKS sur l’hôte actuel {{sourcePort}}.", "forwardDescriptionClientLocal": "Local {{sourcePort}} → distant {{endpointPort}}.", "forwardDescriptionClientRemote": "Distant {{sourcePort}} → local {{endpointPort}}.", "forwardDescriptionClientDynamic": "SOCKS sur le port local {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → CHAUSSETTES via {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", - "autoNameClientDynamic": "CHAUSSETTES {{localPort}} via {{endpoint}}", - "route": "Itinéraire:", - "lastStarted": "Dernière mise en service", - "lastTested": "Dernière vérification", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route :", + "lastStarted": "Dernier démarrage", + "lastTested": "Dernier test", "lastError": "Dernière erreur", - "maxRetries": "Nombre maximal de tentatives", - "maxRetriesDescription": "Nombre maximal de tentatives de nouvelle tentative.", - "retryInterval": "Intervalle de nouvelle tentative (secondes)", - "retryIntervalDescription": "Délai d'attente entre les tentatives de nouvelle tentative.", - "local": "Locale", - "remote": "Télécommande", + "maxRetries": "Tentatives maximum", + "maxRetriesDescription": "Nombre maximal de tentatives.", + "retryInterval": "Intervalle de réessai (secondes)", + "retryIntervalDescription": "Temps d’attente entre les tentatives.", + "local": "Local", + "remote": "Distant", "destination": "Destination", "host": "Hôte", "mode": "Mode", "noHostSelected": "Aucun hôte sélectionné", - "working": "Fonctionnement..." + "working": "En cours…" }, "cardGrid": { - "dragToMove": "Faites glisser pour déplacer", - "dragToResize": "Faites glisser pour redimensionner", + "dragToMove": "Glisser pour déplacer", + "dragToResize": "Glisser pour redimensionner", "changeWidth": "Modifier la largeur", "removeCard": "Supprimer {{label}}", "addCard": "Ajouter", "columns": "Colonnes", - "empty": "Aucune carte. Utilisez l'option « Ajouter » ci-dessous pour insérer des cartes." + "empty": "Aucune carte. Utilisez Ajouter ci-dessous pour placer des cartes." }, "hostMetrics": { - "cpu": "processeur", + "cpu": "CPU", "memory": "Mémoire", "disk": "Disque", "network": "Réseau", - "uptime": "Disponibilité", + "uptime": "Uptime", "processes": "Processus", "available": "Disponible", - "free": "Gratuit", - "connecting": "De liaison...", - "connectionFailed": "Impossible de se connecter au serveur", - "naCpus": "Processeur(s) non applicable(s)", - "cpuCores_one": "{{count}} Noyau", - "cpuCores_other": "{{count}} Noyaux", - "cpuUsage": "Utilisation du processeur", - "memoryUsage": "Utilisation de la mémoire", - "diskUsage": "Utilisation du disque", + "free": "Libre", + "connecting": "Connexion en cours…", + "connectionFailed": "Échec de la connexion au serveur", + "naCpus": "CPU(s) N/A", + "cpuCores_one": "{{count}} cœur", + "cpuCores_other": "{{count}} cœurs", + "cpuUsage": "Utilisation CPU", + "memoryUsage": "Utilisation mémoire", + "diskUsage": "Utilisation disque", "temperature": "Température", - "highestTemperature": "température la plus élevée", + "highestTemperature": "Température maximale", "failedToFetchHostConfig": "Échec de la récupération de la configuration de l'hôte", "serverOffline": "Serveur hors ligne", "cannotFetchMetrics": "Impossible de récupérer les métriques du serveur hors ligne", - "totpFailed": "La vérification TOTP a échoué", - "noneAuthNotSupported": "Host Metrics ne prend pas en charge le type d'authentification « aucun ».", + "totpFailed": "Échec de la vérification TOTP", + "noneAuthNotSupported": "Les métriques de l'hôte ne prennent pas en charge le type d'authentification 'none'.", "noHostSelected": "Aucun hôte sélectionné", - "load": "Charger", + "load": "Charge", "systemInfo": "Informations système", "hostname": "Nom d'hôte", - "operatingSystem": "Système opérateur", + "operatingSystem": "Système d'exploitation", "kernel": "Noyau", "seconds": "secondes", "networkInterfaces": "Interfaces réseau", "noInterfacesFound": "Aucune interface réseau trouvée", "noProcessesFound": "Aucun processus trouvé", "processesTotal": "total", - "processesRunning": "en cours d'exécution", + "processesRunning": "en cours", "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é", + "quickActionFailed": "Échec de {{name}}", "quickActionError": "Échec de l'exécution de {{name}}", "ports": { - "title": "Ports d'écoute", + "title": "Ports en écoute", "protocol": "Protocole", "port": "Port", "address": "Adresse", "process": "Processus", - "search": "Rechercher les ports...", + "search": "Rechercher des ports...", "allProtocols": "Tous", - "noData": "Aucune donnée sur les ports d'écoute" + "noData": "Aucune donnée de ports en écoute" }, "firewall": { "title": "Pare-feu", @@ -2058,120 +2191,120 @@ "protocol": "Proto", "port": "Port", "source": "Source", - "anywhere": "N'importe où", + "anywhere": "Partout", "chains": "chaînes" }, "loadAvg": "Charge moyenne", - "swap": "Échanger", + "swap": "Swap", "architecture": "Architecture", - "refresh": "Rafraîchir", + "refresh": "Actualiser", "retry": "Réessayer", - "customize": "Personnaliser la mise en page", + "customize": "Personnaliser la disposition", "reset": "Réinitialiser", "tabLive": "En direct", - "editModeInstructions": "Faites glisser les cartes pour les réorganiser, faites glisser leur bord inférieur pour les redimensionner et utilisez le bouton de largeur pour modifier leur étendue. Ajoutez ou supprimez des cartes ci-dessous.", + "editModeInstructions": "Faites glisser les cartes pour les réorganiser, faites glisser le bord inférieur pour les redimensionner, et utilisez le bouton de largeur pour modifier l'étendue d'une carte. Ajoutez ou retirez des cartes ci-dessous.", "managers": { "services": "Services", - "processInspector": "Inspecteur des procédés", - "logViewer": "Visionneuse de journaux", + "processInspector": "Inspecteur de processus", + "logViewer": "Visualiseur de logs", "cron": "Tâches Cron", - "packages": "Colis", + "packages": "Paquets", "ssl": "Certificats SSL", "firewall": "Pare-feu", - "users": "Utilisateurs et autorisations", - "healthCheck": "bilans de santé", - "diskBreakdown": "Panne de disque", - "systemdTimers": "Minuteurs", - "topMemory": "En tête par mémoire", + "users": "Utilisateurs et permissions", + "healthCheck": "Contrôles de santé", + "diskBreakdown": "Ventilation du disque", + "systemdTimers": "Timers", + "topMemory": "Top mémoire", "noData": "Aucune donnée", - "sudoHint": "Définissez un mot de passe sudo pour cet hôte dans l'éditeur d'hôtes afin d'activer les actions privilégiées.", - "filter": "Filtre...", - "start": "Commencer", - "stop": "Arrêt", - "restart": "Redémarrage", + "sudoHint": "Définissez un mot de passe sudo pour cet hôte dans l'éditeur d'hôte pour activer les actions privilégiées.", + "filter": "Filtrer...", + "start": "Démarrer", + "stop": "Arrêter", + "restart": "Redémarrer", "actionDone": "{{name}} mis à jour", - "actionFailed": "L'action a échoué", + "actionFailed": "Action échouée", "signalSent": "Signal envoyé au PID {{pid}}", - "killHint": "Clic : terminer (SIGTERM). Clic droit : forcer l’arrêt (SIGKILL).", - "working": "Fonctionnement...", - "update": "Mise à jour", - "upgradeAll": "Mettre à niveau tout", + "killHint": "Clic : terminer (SIGTERM). Clic droit : forcer l'arrêt (SIGKILL).", + "working": "En cours...", + "update": "Mettre à jour", + "upgradeAll": "Tout mettre à niveau", "allUpToDate": "Tout est à jour", - "save": "Sauvegarder", + "save": "Enregistrer", "command": "Commande", "enabled": "Activé", "cronSaved": "Crontab mis à jour", "clients": "Clients", - "dryRun": "essai à sec", + "dryRun": "Simulation", "renew": "Renouveler", - "noAcmeClient": "Aucun client ACME (certbot ou acme.sh) n'a été trouvé sur cet hôte.", - "addInputRule": "Ajouter une règle d'entrée", - "firewallWarning": "Les modifications ne sont effectives que temporairement, jusqu'à leur enregistrement. Veillez à ne pas vous bloquer l'accès.", + "noAcmeClient": "Aucun client ACME (certbot ou acme.sh) trouvé sur cet hôte.", + "addInputRule": "Ajouter une règle INPUT", + "firewallWarning": "Les modifications sont appliquées uniquement en mémoire jusqu'à leur persistance. Veillez à ne pas vous bloquer l'accès.", "ruleApplied": "Règle appliquée", - "invalidPort": "Veuillez saisir un port valide (1-65535)", + "invalidPort": "Saisissez un port valide (1-65535)", "newUsername": "Nouveau nom d'utilisateur", "addUser": "Ajouter", "deleteUser": "Supprimer l'utilisateur", - "noHealthChecks": "Aucun contrôle d'intégrité n'est encore configuré.", + "noHealthChecks": "Aucune vérification d'état configurée pour le moment.", "follow": "Suivre", - "noLogData": "Aucun journal de sortie.", - "tree": "Arbre", - "enableDisable": "Activer/désactiver au démarrage", + "noLogData": "Aucun journal disponible.", + "tree": "Arborescence", + "enableDisable": "Activer / désactiver au démarrage", "grantSudo": "Accorder sudo", - "revokeSudo": "Révoquer les droits d'administrateur", - "sslIssueCert": "Délivrer un certificat", + "revokeSudo": "Révoquer sudo", + "sslIssueCert": "Émettre le certificat", "sslExpired": "Expiré", - "sslInDays": "dans {{days}}d", + "sslInDays": "dans {{days}} j", "sslNeedDomain": "Saisissez au moins un domaine", - "sslIssued": "Certificat délivré", - "sslDomainsPlaceholder": "exemple.com, www.exemple.com", + "sslIssued": "Certificat émis", + "sslDomainsPlaceholder": "example.com, www.example.com", "sslHttpStandalone": "HTTP (autonome)", - "sslHttpWebroot": "HTTP (racine web)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "fournisseur DNS (par exemple Cloudflare)", - "sslIssueHint": "Les identifiants du fournisseur DNS doivent déjà être configurés sur l'hôte.", + "sslDnsProvider": "Fournisseur DNS (ex. cloudflare)", + "sslIssueHint": "Les identifiants du fournisseur DNS doivent déjà être configurés sur l’hôte.", "sslRevoke": "Révoquer le certificat", "sslRevoked": "Certificat révoqué", - "sslRevokeConfirm": "Révoquer et supprimer le certificat «{{name}}» ? Cette opération est irréversible.", - "healthRun": "Courir", + "sslRevokeConfirm": "Révoquer et supprimer le certificat « {{name}} » ? Cette action est irréversible.", + "healthRun": "Exécuter", "healthName": "Nom", "healthTarget": "Hôte / adresse", - "healthAddCheck": "Ajouter un chèque", - "healthSaved": "bilans de santé sauvegardés", - "healthMissingFields": "Chaque chèque doit avoir un nom et une cible.", - "logFile": "Déposer", + "healthAddCheck": "Ajouter une vérification", + "healthSaved": "Vérifications de santé enregistrées", + "healthMissingFields": "Chaque vérification nécessite un nom et une cible", + "logFile": "Fichier", "logUnit": "Unité", - "logCustomPath": "Chemin personnalisé sous /var/log (facultatif)", - "logGrep": "Lignes de filtre...", - "firewallPersist": "Règles persistantes", - "firewallPersisted": "Les règles du pare-feu ont persisté.", + "logCustomPath": "Chemin personnalisé sous /var/log (optionnel)", + "logGrep": "Filtrer les lignes...", + "firewallPersist": "Persister les règles", + "firewallPersisted": "Règles du pare-feu rendues persistantes", "wireguard": "WireGuard", - "tailscale": "Écailles de queue", - "wgNotInstalled": "WireGuard n'est pas installé sur cet hôte.", + "tailscale": "Tailscale", + "wgNotInstalled": "WireGuard n’est pas installé sur cet hôte", "wgNoInterfaces": "Aucune interface WireGuard configurée", - "wgInterfaceUp": "Faire monter", - "wgInterfaceDown": "Faire baisser", - "wgBringingUp": "Afficher {{name}}...", - "wgBringingDown": "Faire tomber {{name}}...", - "wgInterfaceUpDone": "{{name}} est disponible", - "wgInterfaceDownDone": "{{name}} est hors service", + "wgInterfaceUp": "Activer", + "wgInterfaceDown": "Désactiver", + "wgBringingUp": "Activation de {{name}}...", + "wgBringingDown": "Désactivation de {{name}}...", + "wgInterfaceUpDone": "{{name}} est activé", + "wgInterfaceDownDone": "{{name}} est désactivé", "wgListenPort": "Port", "wgPublicKey": "Clé publique", "wgEndpoint": "Point de terminaison", - "wgAllowedIPs": "Adresses IP autorisées", + "wgAllowedIPs": "IP autorisées", "wgLastHandshake": "Dernière poignée de main", "wgHandshakeNever": "Jamais", "wgTransfer": "Transfert", - "tsNotInstalled": "Tailscale n'est pas installé sur cet hôte.", - "tsRunning": "En cours d'exécution", + "tsNotInstalled": "Tailscale n’est pas installé sur cet hôte", + "tsRunning": "Actif", "tsStopped": "Arrêté", "tsEnable": "Connecter", "tsDisable": "Déconnecter", "tsEnabling": "Connexion à Tailscale...", "tsDisabling": "Déconnexion de Tailscale...", - "tsEnabled": "Écailles de queue connectées", - "tsDisabled": "Échelle de queue déconnectée", - "tsIPs": "Adresses IP Tailscale", + "tsEnabled": "Tailscale connecté", + "tsDisabled": "Tailscale déconnecté", + "tsIPs": "IPs Tailscale", "tsPeers": "Pairs", "tsOnline": "En ligne", "tsOffline": "Hors ligne", @@ -2181,74 +2314,74 @@ } }, "auth": { - "tagline": "Gestion SSH et de bureau à distance auto-hébergée", - "loginTitle": "Content de te revoir", + "tagline": "Gestion auto-hébergée de SSH et de bureau à distance", + "loginTitle": "Bienvenue à nouveau", "registerTitle": "Créer un compte", - "forgotPassword": "Mot de passe oublié ?", - "rememberMe": "Mémoriser l'appareil pendant 30 jours (TOTP inclus)", - "noAccount": "Vous n'avez pas de compte ?", - "hasAccount": "Vous avez déjà un compte ?", + "forgotPassword": "Mot de passe oublié ?", + "rememberMe": "Se souvenir de cet appareil pendant 30 jours (inclut TOTP)", + "noAccount": "Pas de compte ?", + "hasAccount": "Déjà un compte ?", "twoFactorAuth": "Authentification à deux facteurs", - "enterCode": "Saisissez le code de vérification", - "backupCode": "Ou utilisez un code de sauvegarde", - "verifyCode": "Code de vérification", + "enterCode": "Saisir le code", + "backupCode": "Ou utiliser un code de secours", + "verifyCode": "Vérifier le code", "redirectingToApp": "Redirection vers l'application...", "sshAuthenticationRequired": "Authentification SSH requise", - "sshNoKeyboardInteractive": "Authentification interactive par clavier indisponible", + "sshNoKeyboardInteractive": "Authentification interactive au clavier indisponible", "sshAuthenticationFailed": "Échec de l'authentification", - "sshAuthenticationTimeout": "Délai d'authentification dépassé", - "sshNoKeyboardInteractiveDescription": "Le serveur ne prend pas en charge l'authentification interactive par clavier. Veuillez saisir votre mot de passe ou votre clé SSH.", - "sshAuthFailedDescription": "Les informations d'identification fournies sont incorrectes. Veuillez réessayer avec des informations d'identification valides.", + "sshAuthenticationTimeout": "Expiration de l'authentification", + "sshNoKeyboardInteractiveDescription": "Le serveur ne prend pas en charge l'authentification interactive au clavier. 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": "Saisissez le mot de passe pour cette connexion SSH.", "sshKeyPasswordDescription": "Si votre clé SSH est chiffrée, saisissez la phrase secrète ici.", - "passphraseRequired": "Mot de passe requis", + "passphraseRequired": "Phrase secrète requise", "passphraseRequiredDescription": "La clé SSH est chiffrée. Veuillez saisir la phrase secrète pour la déverrouiller.", - "back": "Dos", + "back": "Retour", "firstUser": "Premier utilisateur", - "firstUserMessage": "Vous êtes le premier utilisateur et recevrez les droits d'administrateur. Vous pouvez consulter les paramètres d'administration dans le menu déroulant Utilisateur de la barre latérale. Si vous pensez qu'il s'agit d'une erreur, consultez les journaux Docker ou créez un ticket sur GitHub.", + "firstUserMessage": "Vous êtes le premier utilisateur et serez défini comme administrateur. Vous pouvez consulter les paramètres administrateur dans le menu déroulant utilisateur de la barre latérale. Si vous pensez qu'il s'agit d'une erreur, vérifiez les journaux Docker ou créez un ticket GitHub.", "external": "Externe", - "loginWithExternal": "Connexion via un fournisseur externe", + "loginWithExternal": "Se connecter avec un fournisseur externe", "loginWithExternalDesc": "Connectez-vous à l'aide de votre fournisseur d'identité externe configuré.", - "externalNotSupportedInElectron": "L'authentification externe n'est pas encore prise en charge par l'application Electron. Veuillez utiliser la version web pour vous connecter via OIDC.", - "loginWithProvider": "Connectez-vous avec {{name}}", - "orContinueWith": "ou continuez avec", + "externalNotSupportedInElectron": "L'authentification externe n'est pas encore prise en charge dans l'application Electron. Veuillez utiliser la version web pour la connexion OIDC.", + "loginWithProvider": "Se connecter avec {{name}}", + "orContinueWith": "ou continuer avec", "ldapUsername": "Nom d'utilisateur LDAP", "ldapPassword": "Mot de passe LDAP", "ldapSignIn": "Se connecter", "ldapLoginFailed": "Échec de la connexion LDAP", "resetPasswordButton": "Réinitialiser le mot de passe", "sendResetCode": "Envoyer le code de réinitialisation", - "resetCodeDesc": "Saisissez votre nom d'utilisateur pour recevoir un code de réinitialisation de mot de passe. Ce code sera consigné dans les journaux du conteneur Docker.", + "resetCodeDesc": "Saisissez votre nom d'utilisateur pour recevoir un code de réinitialisation du mot de passe. Le code sera enregistré dans les journaux du conteneur Docker.", "resetCode": "Code de réinitialisation", - "verifyCodeButton": "Code de vérification", - "enterResetCode": "Saisissez le code à 6 chiffres figurant dans les journaux du conteneur Docker pour l'utilisateur :", + "verifyCodeButton": "Vérifier le code", + "enterResetCode": "Saisissez le code à 6 chiffres figurant dans les journaux du conteneur Docker pour l'utilisateur :", "newPassword": "Nouveau mot de passe", - "confirmNewPassword": "Confirmez le mot de passe", - "enterNewPassword": "Saisissez votre nouveau mot de passe pour l'utilisateur :", + "confirmNewPassword": "Confirmer le mot de passe", + "enterNewPassword": "Saisissez votre nouveau mot de passe pour l'utilisateur :", "signUp": "S'inscrire", "desktopApp": "Application de bureau", - "loggingInToDesktopApp": "Se connecter à l'application de bureau", + "loggingInToDesktopApp": "Connexion à l'application de bureau", "loadingServer": "Chargement du serveur...", - "dataLossWarning": "Réinitialiser votre mot de passe de cette manière effacera toutes vos connexions SSH enregistrées, vos identifiants et autres données chiffrées. Cette action est irréversible. N'utilisez cette méthode que si vous avez oublié votre mot de passe et que vous n'êtes pas connecté.", + "dataLossWarning": "Réinitialiser votre mot de passe de cette manière supprimera tous vos hôtes SSH enregistrés, vos identifiants et autres données chiffrées. Cette action est irréversible. N'utilisez cette méthode que 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.", - "passwordLoginDisabledDesc": "La connexion par mot de passe est désactivée. Veuillez utiliser une clé d'accès ou un fournisseur d'authentification externe.", - "signInWithPasskey": "Connectez-vous avec votre clé d'accès", - "passkeyLoginFailed": "Échec de la connexion par mot de passe", + "passwordLoginDisabledDesc": "La connexion par mot de passe est désactivée. Utilisez une clé d'accès ou un fournisseur d'authentification externe.", + "signInWithPasskey": "Se connecter avec une clé d'accès", + "passkeyLoginFailed": "Échec de la connexion par clé d'accès", "attemptsRemaining": "{{count}} tentatives restantes", - "confirmResetDataWipe": "Ce compte n'a pas été utilisé depuis la mise à jour du chiffrement ; ses données ne peuvent donc pas être récupérées sans l'ancien mot de passe. La réinitialisation supprimera définitivement ses hôtes, identifiants et extraits de code. Continuer ?" + "confirmResetDataWipe": "Ce compte ne s'est pas connecté depuis la mise à niveau du chiffrement, donc ses données stockées ne peuvent pas être récupérées sans l'ancien mot de passe. La réinitialisation supprimera définitivement ses hôtes, identifiants et extraits. Continuer ?" }, "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 être établie. Veuillez vérifier que l'empreinte digitale correspond à celle attendue.", + "firstConnectionDescription": "L'authenticité de cet hôte ne peut pas être établie. Vérifiez que l'empreinte correspond à ce que vous attendez.", "keyChangedDescription": "La clé d'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", + "newFingerprint": "Nouvelle empreinte", + "fingerprint": "Empreinte", "verifyInstructions": "Si vous faites confiance à cet hôte, cliquez sur Accepter pour continuer et enregistrer cette empreinte pour les connexions futures.", "securityWarning": "Avertissement de sécurité", "acceptAndContinue": "Accepter et continuer", @@ -2257,33 +2390,34 @@ "errors": { "databaseConnection": "Impossible de se connecter à la base de données", "unknownError": "Erreur inconnue", - "loginFailed": "La connexion a échoué", - "failedPasswordReset": "Échec de la réinitialisation du mot de passe", + "loginFailed": "Échec de la connexion", + "failedPasswordReset": "Échec du lancement de la réinitialisation du mot de passe", "failedVerifyCode": "Échec de la vérification du code de réinitialisation", - "failedCompleteReset": "Échec de la réinitialisation du mot de passe", + "failedCompleteReset": "Échec de la finalisation de la réinitialisation du mot de passe", "invalidTotpCode": "Code TOTP invalide", - "failedOidcLogin": "Échec de la connexion OIDC", + "failedOidcLogin": "Échec du démarrage de la connexion OIDC", "silentSigninOidcUnavailable": "Une connexion silencieuse a été demandée, mais la connexion OIDC n'est pas disponible.", - "failedUserInfo": "Impossible de récupérer les informations utilisateur après la connexion.", - "oidcAuthFailed": "L'authentification OIDC a échoué", - "invalidAuthUrl": "URL d'autorisation invalide reçue du serveur", + "failedUserInfo": "Échec de la récupération des informations 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": "La connexion par nom d'utilisateur/mot de passe est actuellement désactivée.", + "minLength": "La longueur minimale est de {{min}}", + "passwordMismatch": "Les mots de passe ne correspondent pas", + "passwordLoginDisabled": "La connexion par nom d'utilisateur/mot de passe est actuellement désactivée", "sessionExpired": "Session expirée - veuillez vous reconnecter", - "totpRateLimited": "Limitation du nombre de tentatives : Trop de tentatives de vérification TOTP. Veuillez réessayer plus tard.", - "totpRateLimitedWithTime": "Limitation du nombre de tentatives : Trop de tentatives de vérification TOTP. Veuillez patienter {{time}} secondes avant de réessayer.", - "resetCodeRateLimited": "Limite de tentatives : Trop de tentatives de vérification. Veuillez réessayer plus tard.", - "resetCodeRateLimitedWithTime": "Limite de tentatives : Trop de tentatives de vérification. Veuillez patienter {{time}} secondes avant de réessayer.", + "totpRateLimited": "Limitation de débit : trop de tentatives de vérification TOTP. Veuillez réessayer plus tard.", + "totpRateLimitedWithTime": "Limitation de débit : trop de tentatives de vérification TOTP. Veuillez attendre {{time}} secondes avant de réessayer.", + "resetCodeRateLimited": "Limitation de débit : trop de tentatives de vérification. Veuillez réessayer plus tard.", + "resetCodeRateLimitedWithTime": "Limitation de débit : trop de tentatives de vérification. Veuillez patienter {{time}} secondes avant de réessayer.", "authTokenSaveFailed": "Échec de l'enregistrement du jeton d'authentification", - "failedToLoadServer": "Impossible de charger le serveur" + "failedToLoadServer": "Échec du chargement du serveur", + "remoteServerRequired": "Serveur distant requis. Connectez un serveur distant dans les paramètres pour utiliser ce type de connexion." }, "messages": { "registrationDisabled": "L'inscription de nouveaux comptes est actuellement désactivée par un administrateur. Veuillez vous connecter ou contacter un administrateur.", "userNotAllowed": "Votre compte n'est pas autorisé à s'inscrire. Veuillez contacter un administrateur.", "databaseConnectionFailed": "Échec de la connexion au serveur de base de données", - "resetCodeSent": "Code de réinitialisation envoyé aux journaux Docker", + "resetCodeSent": "Code de réinitialisation envoyé dans les journaux Docker", "codeVerified": "Code vérifié avec succès", "passwordResetSuccess": "Réinitialisation du mot de passe réussie", "loginSuccess": "Connexion réussie", @@ -2291,32 +2425,32 @@ }, "profile": { "c2sTunnelConfigDesc": "Tunnels de bureau locaux 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 en tant que préréglage de serveur nommé, ou chargez un préréglage dans ce client.", - "c2sTunnelPresetsUnavailable": "Les préréglages de tunnel client sont uniquement disponibles dans le client de bureau.", - "c2sPresetName": "Nom prédéfini", - "c2sPresetNamePlaceholder": "Nom prédéfini du client", + "c2sTunnelPresets": "Préréglages de tunnel client", + "c2sTunnelPresetsDesc": "Enregistrez la liste de tunnels locaux de ce client de bureau en tant que préréglage serveur nommé, ou chargez un préréglage dans ce client.", + "c2sTunnelPresetsUnavailable": "Les préréglages de tunnel client sont disponibles uniquement 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 d'un préréglage remplace la liste des tunnels clients locaux de ce client de bureau.", - "c2sPresetSaved": "Préréglage du tunnel client enregistré", - "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": "Échec du chargement des préréglages du tunnel client" + "c2sCurrentLocalConfig": "{{count}} tunnel(s) client local configuré(s) sur ce bureau.", + "c2sPresetSyncNote": "Les préréglages sont des instantanés explicites ; en charger un remplace la liste de tunnels client locaux de ce client de bureau.", + "c2sPresetSaved": "Préréglage de tunnel client enregistré", + "c2sPresetLoaded": "Préréglage de tunnel client chargé localement", + "c2sPresetRenamed": "Préréglage de tunnel client renommé", + "c2sPresetDeleted": "Préréglage de tunnel client supprimé", + "c2sPresetLoadError": "Échec du chargement des préréglages de tunnel client" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "Langue", - "keyPassword": "mot de passe clé", + "keyPassword": "mot de passe de la clé", "pastePrivateKey": "Collez votre clé privée ici...", - "localListenerHost": "127.0.0.1 (écoute locale)", + "localListenerHost": "127.0.0.1 (écouter en local)", "localTargetHost": "127.0.0.1 (cible sur cet ordinateur)", - "socksListenerHost": "127.0.0.1 (Écouteur SOCKS)", + "socksListenerHost": "127.0.0.1 (écouteur SOCKS)", "enterPassword": "Saisissez votre mot de passe", "defaultPort": "22", "defaultEndpointPort": "224" @@ -2325,22 +2459,22 @@ "title": "Tableau de bord", "loading": "Chargement du tableau de bord...", "github": "GitHub", - "support": "Soutien", - "discord": "Discorde", - "docs": "Documents", + "support": "Support", + "discord": "Discord", + "docs": "Documentation", "donate": "Faire un don", - "serverOverview": "Présentation du serveur", + "serverOverview": "Vue d'ensemble du serveur", "version": "Version", "upToDate": "À jour", "updateAvailable": "Mise à jour disponible", "beta": "Bêta", "uptime": "Disponibilité", "database": "Base de données", - "healthy": "En bonne santé", + "healthy": "Opérationnel", "error": "Erreur", - "totalHosts": "Nombre total d'hôtes", - "totalTunnels": "Tunnels totaux", - "totalCredentials": "Informations complètes", + "totalHosts": "Total hôtes", + "totalTunnels": "Total tunnels", + "totalCredentials": "Total identifiants", "recentActivity": "Activité récente", "reset": "Réinitialiser", "loadingRecentActivity": "Chargement de l'activité récente...", @@ -2348,44 +2482,44 @@ "quickActions": "Actions rapides", "addHost": "Ajouter un hôte", "addCredential": "Ajouter un identifiant", - "adminSettings": "Paramètres d'administration", + "adminSettings": "Paramètres administrateur", "userProfile": "Profil utilisateur", - "serverStats": "Statistiques du serveur", - "loadingServerStats": "Chargement des statistiques du serveur...", + "serverStats": "Statistiques serveur", + "loadingServerStats": "Chargement des statistiques serveur...", "noServerData": "Aucune donnée serveur disponible", - "cpu": "processeur", - "ram": "BÉLIER", + "cpu": "CPU", + "ram": "RAM", "customizeLayout": "Personnaliser le tableau de bord", "dashboardSettings": "Paramètres du tableau de bord", "enableDisableCards": "Activer/Désactiver les cartes", - "resetLayout": "Réinitialiser par défaut", - "serverOverviewCard": "Présentation du serveur", + "resetLayout": "Rétablir la disposition par défaut", + "serverOverviewCard": "Vue d'ensemble du serveur", "recentActivityCard": "Activité récente", - "networkGraphCard": "Graphe de réseau", - "networkGraph": "Graphe de réseau", + "networkGraphCard": "Graphique réseau", + "networkGraph": "Graphique réseau", "quickActionsCard": "Actions rapides", "serverStatsCard": "Statistiques du serveur", "panelMain": "Principal", - "panelSide": "Côté", - "justNow": "tout à l' heure", + "panelSide": "Latéral", + "justNow": "à l'instant", "serviceLinks": "Liens de service", "homepagePreview": "Aperçu de la page d'accueil" }, "donation": { "title": "Vous appréciez Termix ?", - "body": "Termix est un logiciel libre et gratuit, développé et maintenu par une équipe de deux personnes sur leur temps libre. S'il remplace un outil commercial que vous auriez autrement acheté, un don contribue à couvrir les frais d'hébergement et à soutenir son développement. Pour l'instant, les dons sont uniquement acceptés en cryptomonnaie.", - "milestones": "Les dons permettent également de financer le temps consacré à la recherche et à l'apprentissage des compétences nécessaires au développement de fonctionnalités telles que la prise en charge de SAML et de Kubernetes. Suivez l'avancement du projet sur la page de don.", + "body": "Termix est gratuit et open source, conçu et maintenu par une équipe de deux personnes sur notre temps libre. S'il remplace un outil commercial pour lequel vous auriez autrement payé, un don aide à couvrir les coûts d'hébergement et à poursuivre le développement. Pour le moment, les dons sont uniquement en cryptomonnaie.", + "milestones": "Les dons aident également à financer le temps nécessaire à la recherche et à l'apprentissage pour développer des fonctionnalités telles que la prise en charge de SAML et Kubernetes. Consultez la progression sur la page des dons.", "cta": "Faire un don", "dismiss": "Peut-être plus tard" }, "dashboardTab": { - "stable": "ÉCURIE", + "stable": "STABLE", "hostsOnline": "Hôtes en ligne", "activeTunnels": "Tunnels actifs", "registerNewServer": "Enregistrer un nouveau serveur", - "storeSshKeysOrPasswords": "Stockez les clés ou mots de passe SSH", + "storeSshKeysOrPasswords": "Stocker des clés SSH ou des mots de passe", "manageUsersAndRoles": "Gérer les utilisateurs et les rôles", - "manageYourAccount": "Gérez votre compte", + "manageYourAccount": "Gérer votre compte", "hostStatus": "État de l'hôte", "noHostsConfigured": "Aucun hôte configuré", "online": "EN LIGNE", @@ -2393,35 +2527,35 @@ "checking": "VÉRIFICATION", "onlineLower": "En ligne", "nodes": "{{count}} nœuds", - "add": "Ajouter:", + "add": "Ajouter :", "commandPalette": "Palette de commandes", - "done": "Fait", - "editModeInstructions": "Faites glisser les cartes pour les réorganiser. Faites glisser le séparateur de colonnes pour redimensionner les colonnes. Faites glisser le bord inférieur d'une carte pour ajuster sa hauteur. Utilisez la corbeille pour supprimer.", + "done": "Terminé", + "editModeInstructions": "Faites glisser les cartes pour les réorganiser · Faites glisser le séparateur de colonne pour redimensionner les colonnes · Faites glisser le bord inférieur d’une carte pour redimensionner sa hauteur · Corbeille pour supprimer", "empty": "Vide", - "clear": "Clair", + "clear": "Effacer", "serviceLinksTitle": "Liens de service", - "serviceLinksEmpty": "Aucun lien de service pour le moment", - "serviceLinksAddLabel": "Étiquette", + "serviceLinksEmpty": "Aucun lien de service pour l’instant", + "serviceLinksAddLabel": "Libellé", "serviceLinksAddUrl": "URL", "serviceLinksAdd": "Ajouter", "serviceLinksLabelPlaceholder": "Mon service", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "Veuillez saisir une adresse web valide.", - "serviceLinksAddFailed": "Échec de l'ajout du lien de service", + "serviceLinksInvalidUrl": "Saisissez une adresse Web valide", + "serviceLinksAddFailed": "Échec de l’ajout du lien de service", "disk": "Disque", "viewServerDetails": "Afficher les détails du serveur" }, "sessionLogs": { "title": "Journaux de session", - "noLogs": "Aucun journal de session pour le moment", - "noLogsDesc": "Activez la journalisation des sessions sur un hôte pour démarrer l'enregistrement.", + "noLogs": "Aucun journal de session pour l’instant", + "noLogsDesc": "Activez la journalisation des sessions sur un hôte pour démarrer l’enregistrement", "duration": "Durée", - "viewLog": "Consulter le journal", + "viewLog": "Afficher le journal", "downloadLog": "Télécharger", "deleteLog": "Supprimer", - "confirmDelete": "Supprimer ce journal de session ?", + "confirmDelete": "Supprimer ce journal de session ?", "confirmDeleteDesc": "Cette action est irréversible.", - "copyContent": "Copie", + "copyContent": "Copier", "copied": "Copié !", "loadError": "Échec du chargement des journaux de session", "deleteError": "Échec de la suppression du journal de session", @@ -2431,35 +2565,35 @@ "addHost": "Ajouter un hôte", "addGroup": "Ajouter un groupe", "addLink": "Ajouter un lien", - "zoomIn": "Zoomer", - "zoomOut": "Dézoomer", - "resetView": "Réinitialiser l'affichage", + "zoomIn": "Zoom avant", + "zoomOut": "Zoom arrière", + "resetView": "Réinitialiser la vue", "selectHost": "Sélectionner l'hôte", - "chooseHost": "Choisissez un hôte...", + "chooseHost": "Choisir un hôte...", "parentGroup": "Groupe parent", "noGroup": "Aucun groupe", "groupName": "Nom du groupe", "color": "Couleur", "source": "Source", "target": "Cible", - "moveToGroup": "Passer au groupe", + "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 l'hôte ici", + "addHostHere": "Ajouter un hôte ici", "editGroup": "Modifier le groupe", "delete": "Supprimer", "add": "Ajouter", "create": "Créer", - "move": "Se déplacer", + "move": "Déplacer", "connect": "Connecter", "createGroup": "Créer un groupe", "selectSourcePlaceholder": "Sélectionner la source...", "selectTargetPlaceholder": "Sélectionner la cible...", - "invalidFile": "Fichier invalide", + "invalidFile": "Fichier non valide", "hostAlreadyExists": "L'hôte est déjà dans la topologie", - "connectionExists": "La connexion existe déjà.", + "connectionExists": "La connexion existe déjà", "unknown": "Inconnu", "name": "Nom", "ip": "IP", @@ -2467,7 +2601,7 @@ "failedToAddNode": "Échec de l'ajout du nœud", "sourceDifferentFromTarget": "La source et la cible doivent être différentes", "exportJSON": "Exporter JSON", - "importJSON": "Importer du JSON", + "importJSON": "Importer JSON", "terminal": "Terminal", "fileManager": "Gestionnaire de fichiers", "tunnel": "Tunnel", @@ -2477,77 +2611,77 @@ "noNodes": "Aucun nœud pour l'instant" }, "docker": { - "notEnabled": "Docker n'est pas activé pour cet hôte.", + "notEnabled": "Docker n'est pas activé pour cet hôte", "validating": "Validation de Docker...", - "connecting": "De liaison...", + "connecting": "Connexion...", "error": "Erreur", "version": "Docker {{version}}", "connectionFailed": "Échec de la connexion à Docker", "containerStarted": "Conteneur {{name}} démarré", "failedToStartContainer": "Échec du démarrage du conteneur {{name}}", - "containerStopped": "Le conteneur {{name}} s'est arrêté", + "containerStopped": "Conteneur {{name}} arrêté", "failedToStopContainer": "Échec de l'arrêt du conteneur {{name}}", - "containerRestarted": "Le conteneur {{name}} a redémarré", + "containerRestarted": "Conteneur {{name}} redémarré", "failedToRestartContainer": "Échec du redémarrage du conteneur {{name}}", - "containerPaused": "Conteneur {{name}} en pause", - "containerUnpaused": "Conteneur {{name}} non mis en pause", - "failedToTogglePauseContainer": "Impossible de basculer l'état de pause pour le conteneur {{name}}", + "containerPaused": "Conteneur {{name}} mis en pause", + "containerUnpaused": "Conteneur {{name}} repris", + "failedToTogglePauseContainer": "Échec du basculement de l'état de pause du conteneur {{name}}", "containerRemoved": "Conteneur {{name}} supprimé", "failedToRemoveContainer": "Échec de la suppression du conteneur {{name}}", "image": "Image", "ports": "Ports", "noPorts": "Aucun port", - "start": "Commencer", - "confirmRemoveContainer": "Êtes-vous sûr de vouloir supprimer le conteneur '{{name}}' ? Cette action est irréversible.", - "runningContainerWarning": "Avertissement : ce conteneur est actuellement en cours d’exécution. Sa suppression entraînera d’abord son arrêt.", + "start": "Démarrer", + "confirmRemoveContainer": "Êtes-vous sûr de vouloir supprimer le conteneur '{{name}}' ? Cette action est irréversible.", + "runningContainerWarning": "Attention : Ce conteneur est en cours d'exécution. Sa suppression l'arrêtera d'abord.", "loadingContainers": "Chargement des conteneurs...", "manager": "Gestionnaire Docker", "autoRefresh": "Actualisation automatique", - "timestamps": "Horodatage", + "timestamps": "Horodatages", "lines": "Lignes", "filterLogs": "Filtrer les journaux...", - "refresh": "Rafraîchir", + "refresh": "Actualiser", "download": "Télécharger", - "clear": "Clair", + "clear": "Effacer", "logsDownloaded": "Journaux téléchargés avec succès", - "last50": "Les 50 derniers", - "last100": "Les 100 derniers", - "last500": "Les 500 derniers", - "last1000": "Les 1000 derniers", + "last50": "50 derniers", + "last100": "100 derniers", + "last500": "500 derniers", + "last1000": "1 000 derniers", "allLogs": "Tous les journaux", "noLogsMatching": "Aucun journal correspondant à \"{{query}}\"", "noLogsAvailable": "Aucun journal disponible", "noContainersFound": "Aucun conteneur trouvé", - "noContainersFoundHint": "Aucun conteneur Docker n'est disponible sur cet hôte.", + "noContainersFoundHint": "Aucun conteneur Docker n’est disponible sur cet hôte", "searchPlaceholder": "Rechercher des conteneurs...", "allStatuses": "Tous les statuts", - "stateRunning": "En cours d'exécution", + "stateRunning": "En cours d’exécution", "statePaused": "En pause", - "stateExited": "Sortie", - "stateRestarting": "Redémarrage", - "noContainersMatchFilters": "Aucun contenant ne correspond à vos filtres", - "noContainersMatchFiltersHint": "Essayez de modifier vos critères de recherche ou de filtrage.", - "failedToFetchStats": "Impossible de récupérer les statistiques du conteneur", - "containerNotRunning": "Le conteneur ne s'exécute pas.", + "stateExited": "Arrêté", + "stateRestarting": "Redémarrage en cours", + "noContainersMatchFilters": "Aucun conteneur ne correspond à vos filtres", + "noContainersMatchFiltersHint": "Essayez de modifier vos critères de recherche ou de filtrage", + "failedToFetchStats": "Échec de la récupération des statistiques du conteneur", + "containerNotRunning": "Conteneur arrêté", "startContainerToViewStats": "Démarrez le conteneur pour afficher les statistiques", "loadingStats": "Chargement des statistiques...", - "errorLoadingStats": "Statistiques de chargement d'erreur", + "errorLoadingStats": "Erreur lors du chargement des statistiques", "noStatsAvailable": "Aucune statistique disponible", - "cpuUsage": "Utilisation du processeur", + "cpuUsage": "Utilisation CPU", "current": "Actuel", - "memoryUsage": "Utilisation de la mémoire", + "memoryUsage": "Utilisation mémoire", "networkIo": "E/S réseau", - "input": "Saisir", - "output": "Sortir", - "blockIo": "E/S de bloc", - "read": "Lire", - "write": "Écrire", - "pids": "PID", + "input": "Entrée", + "output": "Sortie", + "blockIo": "E/S de blocs", + "read": "Lecture", + "write": "Écriture", + "pids": "PIDs", "containerInformation": "Informations sur le conteneur", "name": "Nom", - "id": "IDENTIFIANT", + "id": "ID", "state": "État", - "containerMustBeRunning": "Le conteneur doit être en cours d'exécution pour accéder à la console.", + "containerMustBeRunning": "Le conteneur doit être en cours d'exécution pour accéder à la console", "verificationCodePrompt": "Saisissez le code de vérification", "totpVerificationFailed": "La vérification TOTP a échoué. Veuillez réessayer.", "warpgateVerificationFailed": "L'authentification Warpgate a échoué. Veuillez réessayer.", @@ -2557,491 +2691,508 @@ "errorMessage": "Erreur : {{message}}", "failedToConnect": "Échec de la connexion au conteneur", "console": "Console", - "selectShell": "Sélectionnez coquille", - "bash": "Frapper", + "selectShell": "Sélectionner le shell", + "bash": "Bash", "sh": "sh", - "ash": "cendre", + "ash": "ash", "connect": "Connecter", "disconnect": "Déconnecter", "notConnected": "Non connecté", - "clickToConnect": "Cliquez sur « Se connecter » pour démarrer une session shell.", - "connectingTo": "Connexion à {{containerName}}...", + "clickToConnect": "Cliquez sur Connecter pour démarrer une session shell", + "connectingTo": "Connexion à {{containerName}}…", "containerNotFound": "Conteneur introuvable", "backToList": "Retour à la liste", - "logs": "bûches", - "stats": "Statistiques", + "logs": "Journaux", + "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Démarrez le conteneur pour accéder à la console." + "startContainerToAccess": "Démarrez le conteneur pour accéder à la console" }, "admin": { "sectionGeneral": "Général", "sectionOidc": "OIDC", "sectionSso": "Fournisseurs SSO", "ssoAddProvider": "Ajouter un fournisseur", - "ssoDocsLink": "Consulter les documents", - "ssoProviderDocsLink": "Consulter les documents", + "ssoDocsLink": "Documentation", + "ssoProviderDocsLink": "Documentation", "ssoNoProviders": "Aucun fournisseur SSO configuré.", - "ssoProviderName": "Nom d'affichage", + "ssoProviderName": "Nom d’affichage", "ssoProviderType": "Type de fournisseur", "ssoDeleteProvider": "Supprimer le fournisseur", - "ssoDeleteConfirm": "Supprimer ce fournisseur ? Les utilisateurs associés ne pourront plus se connecter.", + "ssoDeleteConfirm": "Supprimer ce fournisseur ? Les utilisateurs associés ne pourront plus se connecter.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", "ssoEnabled": "Activé", "ssoDisabled": "Désactivé", - "ssoSaveProvider": "Fournisseur de sauvegarde", - "ssoTestConnection": "Test de connexion", + "ssoSaveProvider": "Enregistrer le fournisseur", + "ssoTestConnection": "Tester la connexion", "ssoEditProvider": "Modifier le fournisseur", "ldapHost": "Hôte LDAP", "ldapPort": "Port", - "ldapUseTls": "Utilisez TLS (LDAPS)", - "ldapBindDn": "Lier DN", - "ldapBindPassword": "Associer le mot de passe", + "ldapUseTls": "Utiliser TLS (LDAPS)", + "ldapBindDn": "DN de liaison", + "ldapBindPassword": "Mot de passe de liaison", "ldapUserSearchBase": "Base de recherche utilisateur", "ldapUserSearchFilter": "Filtre de recherche utilisateur", - "ldapUsernameAttr": "Attribut Nom d'utilisateur", - "ldapDisplayNameAttr": "Attribut Nom d'affichage", + "ldapUsernameAttr": "Attribut de nom d’utilisateur", + "ldapDisplayNameAttr": "Attribut de nom d’affichage", "ldapGroupSearchBase": "Base de recherche de groupe", - "ldapAdminGroup": "Groupe d'administration", + "ldapAdminGroup": "Groupe admin", "ldapAllowedUsers": "Utilisateurs autorisés", "sectionUsers": "Utilisateurs", - "sectionSessions": "Séances", + "sectionSessions": "Sessions", "sectionRoles": "Rôles", "sectionDatabase": "Base de données", "sectionApiKeys": "Clés API", "sectionAuditLog": "Journal d'audit", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Générez et renouvelez automatiquement un certificat SSL de confiance auprès de Let's Encrypt. Nécessite un domaine public et un accès au port 80 ou au DNS.", - "sslDocsLink": "Consultez la documentation SSL", + "sslDescription": "Émettre et renouveler automatiquement un certificat SSL de confiance via Let's Encrypt. Requiert un domaine public et un accès au port 80 ou au DNS.", + "sslDocsLink": "Voir la documentation SSL", "sslDomain": "Domaine", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "Le nom de domaine public du certificat.", + "sslDomainDesc": "Le nom de domaine public pour le certificat.", "sslEmail": "E-mail", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Adresse électronique de contact pour les notifications et le compte Let's Encrypt.", - "sslChallengeType": "Type de défi", - "sslChallengeTypeDesc": "Comment prouver la propriété d'un domaine à Let's Encrypt.", - "sslChallengeHttp": "HTTP (racine web) - nécessite le port 80 accessible depuis Internet", - "sslChallengeDns": "DNS (Cloudflare) - nécessite un jeton d'API Cloudflare", - "sslCloudflareToken": "Jeton d'API Cloudflare", + "sslEmailDesc": "Adresse e-mail de contact pour les notifications et le compte Let's Encrypt.", + "sslChallengeType": "Type de challenge", + "sslChallengeTypeDesc": "Méthode pour prouver la propriété du domaine à Let's Encrypt.", + "sslChallengeHttp": "HTTP (webroot) - nécessite le port 80 accessible depuis Internet", + "sslChallengeDns": "DNS (Cloudflare) - nécessite un jeton API Cloudflare", + "sslCloudflareToken": "Jeton API Cloudflare", "sslCloudflareTokenPlaceholder": "Saisissez le jeton...", - "sslCloudflareTokenDesc": "Jeton limité à votre domaine, avec l'autorisation Zone:DNS:Edit.", + "sslCloudflareTokenDesc": "Jeton limité avec l'autorisation Zone:DNS:Edit pour votre domaine.", "sslCertStatus": "Statut du certificat", "sslCertStatusNone": "Aucun certificat", "sslCertStatusValid": "Valide", - "sslCertStatusExpiring": "Expire bientôt", + "sslCertStatusExpiring": "Expiration prochaine", "sslCertStatusExpired": "Expiré", - "sslCertExpiresAt": "Expire {{date}}", - "sslLastIssued": "Dernière émission {{date}}", - "sslRequestCert": "Délivrer / renouveler un certificat", - "sslRequestCertLoading": "Demande de certificat...", + "sslCertExpiresAt": "Expire le {{date}}", + "sslLastIssued": "Dernière émission le {{date}}", + "sslRequestCert": "Émettre / Renouveler le certificat", + "sslRequestCertLoading": "Demande de certificat en cours...", "sslRequestCertSuccess": "Certificat émis et installé avec succès", - "sslRequestCertFailed": "La demande de certificat a échoué", + "sslRequestCertFailed": "Échec de la demande de certificat", "sslSave": "Enregistrer les paramètres", "sslSaved": "Paramètres SSL enregistrés", - "sslSaveFailed": "Impossible d'enregistrer les paramètres SSL", - "sslRequiresDomain": "Le domaine et l'adresse e-mail sont requis.", + "sslSaveFailed": "Échec de l'enregistrement des paramètres SSL", + "sslRequiresDomain": "Le domaine et l'e-mail sont requis", "sslInfoNote": "Après avoir émis un certificat, activez SSL dans vos variables d'environnement (ENABLE_SSL=true) et redémarrez Termix.", - "auditLogTotal": "{{total}} Nombre total d'entrées", + "sslManualOption": "Manuel (télécharger le certificat)", + "sslManualCert": "Certificat (PEM)", + "sslManualCertPlaceholder": "-----DÉBUT DU CERTIFICAT-----", + "sslManualKey": "Clé privée (PEM)", + "sslManualKeyPlaceholder": "-----DÉBUT DE LA CLÉ PRIVÉE-----", + "sslManualDesc": "Collez votre certificat et votre clé privée existants, y compris la chaîne complète si votre autorité de certification l'exige.", + "sslManualUpload": "Télécharger et installer le certificat", + "sslManualUploadLoading": "Téléchargement du certificat...", + "sslManualUploadSuccess": "Certificat téléchargé et installé avec succès", + "sslManualUploadFailed": "Échec du chargement du certificat", + "sslManualRequiresFields": "Un certificat et une clé privée sont nécessaires.", + "auditLogTotal": "{{total}} entrées au total", "auditLogEmpty": "Aucune entrée de journal d'audit trouvée", "auditLogSuccess": "Succès", - "auditLogFailed": "Échoué", - "auditLogClearFilters": "Filtres transparents", - "auditLogPage": "Page {{page}} de {{totalPages}} ({{total}} au total)", + "auditLogFailed": "Échec", + "auditLogClearFilters": "Effacer les filtres", + "auditLogPage": "Page {{page}} sur {{totalPages}} ({{total}} au total)", "auditLogIp": "IP", "auditLogResourceId": "ID de ressource", "auditLogFilterUser": "Utilisateur", "auditLogFilterAction": "Action", "auditLogFilterResourceType": "Type de ressource", "auditLogFilterStatus": "Statut", - "auditLogFilterFrom": "Depuis", + "auditLogFilterFrom": "De", "auditLogFilterTo": "À", "auditLogFilterAll": "Tous", - "allowRegistration": "Autoriser l'inscription des utilisateurs", - "allowRegistrationDesc": "Permettez aux nouveaux utilisateurs de s'inscrire eux-mêmes avec un nom d'utilisateur et un mot de passe.", + "allowRegistration": "Autoriser l'enregistrement des utilisateurs", + "allowRegistrationDesc": "Permettre aux nouveaux utilisateurs de s'enregistrer avec un nom d'utilisateur et un mot de passe", "allowPasswordLogin": "Autoriser la connexion par mot de passe", - "allowPasswordLoginDesc": "Identifiant/mot de passe de connexion", + "allowPasswordLoginDesc": "Connexion par nom d'utilisateur/mot de passe", "oidcAutoProvision": "Provisionnement automatique OIDC", - "oidcAutoProvisionDesc": "Création automatique de comptes pour les utilisateurs OIDC/SSO lors de la première connexion (indépendamment de l'activation/désactivation de l'enregistrement)", - "oidcSilentLoginDefault": "Connexion OIDC silencieuse par défaut", - "oidcSilentLoginDefaultDesc": "Redirection automatique vers la page de connexion OIDC à chaque visite, en ignorant complètement le formulaire de connexion.", + "oidcAutoProvisionDesc": "Créer automatiquement des comptes pour les utilisateurs OIDC/SSO lors de la première connexion (indépendant de l'option d'enregistrement)", + "oidcSilentLoginDefault": "Connexion silencieuse OIDC par défaut", + "oidcSilentLoginDefaultDesc": "Rediriger automatiquement vers la connexion OIDC à chaque visite, sans afficher le formulaire de connexion", "allowPasswordReset": "Autoriser la réinitialisation du mot de passe", - "allowPasswordResetDesc": "Réinitialiser le code via les journaux Docker", + "allowPasswordResetDesc": "Code de réinitialisation via les journaux Docker", "commandHistoryEnabled": "Historique des commandes", - "commandHistoryEnabledDesc": "Autoriser l'enregistrement de l'historique des commandes. Lorsqu'il est désactivé, l'historique n'est pas enregistré, quels que soient les paramètres par hôte.", - "updateCommandHistoryFailed": "Échec de la mise à jour des paramètres de l'historique des commandes", - "sessionTimeout": "Expiration de la session", + "commandHistoryEnabledDesc": "Autoriser l'enregistrement de l'historique des commandes. Lorsque désactivé, l'historique n'est pas sauvegardé, indépendamment des paramètres par hôte.", + "updateCommandHistoryFailed": "Échec de la mise à jour du paramètre d'historique des commandes", + "analyticsEnabled": "Statistiques d'utilisation anonymes partagées", + "analyticsEnabledDesc": "Envoie quotidiennement un décompte anonyme des utilisateurs, des hôtes et de l'utilisation des fonctionnalités afin d'améliorer Termix. Aucune donnée personnelle ni information de connexion n'est jamais incluse.", + "updateAnalyticsFailed": "Échec de la mise à jour des paramètres d'analyse", + "sessionSharingGloballyEnabled": "Autoriser le partage de session", + "sessionSharingGloballyEnabledDesc": "Autorise le partage des sessions de terminal en direct, RDP, VNC et Telnet à l'échelle de l'instance. Remplace toute option de partage par hôte lorsqu'elle est désactivée.", + "updateSessionSharingFailed": "Échec de la mise à jour des paramètres de partage de session", + "sessionTimeout": "Expiration de session", "hours": "heures", - "sessionTimeoutRange": "Durée minimale : 1 h · Durée maximale : 720 h", - "monitoringDefaults": "Surveillance des valeurs par défaut", + "sessionTimeoutRange": "Min 1 h · Max 720 h", + "monitoringDefaults": "Paramètres de surveillance par défaut", "statusCheck": "Vérification de l'état", - "metrics": "Métrique", - "sec": "seconde", + "metrics": "Métriques", + "sec": "s", "logLevel": "Niveau de journalisation", - "enableGuacamole": "Activer le guacamole", + "enableGuacamole": "Activer Guacamole", "enableGuacamoleDesc": "Bureau à distance RDP/VNC", - "enableGuacamoleDocsLink": "Consulter les documents", + "enableGuacamoleDocsLink": "Voir la documentation", "guacdUrl": "URL guacd", "tailscaleApiKey": "Clé API Tailscale", - "tailscaleApiKeyDescription": "Utilisée pour la détection des appareils dans l'éditeur d'hôtes. Générez une clé sur tailscale.com/admin/settings/keys.", - "tailscaleApiKeyDocsLink": "Consulter les documents", - "oidcDescription": "Configurer OpenID Connect pour l'authentification unique (SSO). Les champs marqués d'un astérisque (*) sont obligatoires.", - "oidcDocsLink": "Consulter les documents", + "tailscaleApiKeyDescription": "Utilisée pour la découverte de périphériques dans l'éditeur d'hôtes. Générez une clé sur tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "Voir la documentation", + "oidcDescription": "Configurer OpenID Connect pour l'authentification unique. Les champs marqués * sont obligatoires.", + "oidcDocsLink": "Voir la documentation", "oidcClientId": "ID client", - "oidcClientSecret": "Secret du client", + "oidcClientSecret": "Secret client", "oidcAuthUrl": "URL d'autorisation", "oidcIssuerUrl": "URL de l'émetteur", "oidcTokenUrl": "URL du jeton", - "oidcUserIdentifier": "Chemin d'identification de l'utilisateur", - "oidcDisplayName": "Nom d'affichage Chemin", - "oidcScopes": "Lunettes de visée", - "oidcUserinfoUrl": "URL de remplacement des informations utilisateur", + "oidcUserIdentifier": "Chemin de l'identifiant utilisateur", + "oidcDisplayName": "Chemin du nom affiché", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "URL Userinfo personnalisée", "oidcAllowedUsers": "Utilisateurs autorisés", - "oidcAllowedUsersDesc": "Une adresse e-mail par ligne. Laisser vide pour autoriser toutes les adresses.", - "oidcAdminGroup": "Groupe d'administration", - "oidcAdminGroupDesc": "Les utilisateurs de ce groupe disposent des droits d'administrateur. Laissez ce champ vide pour désactiver la synchronisation du groupe.", - "oidcGroupClaim": "Réclamation collective", - "oidcGroupClaimDesc": "Facultatif. Chemin d'accès à la revendication contenant les groupes de l'utilisateur. Par défaut : groupes, rôles, puis groupe. À utiliser pour les fournisseurs disposant d'une revendication personnalisée (par exemple, Zitadel).", + "oidcAllowedUsersDesc": "Une adresse e-mail par ligne. Laissez vide pour autoriser tout le monde.", + "oidcAdminGroup": "Groupe administrateur", + "oidcAdminGroupDesc": "Les utilisateurs de ce groupe ont les droits administrateur. Laissez vide pour désactiver la synchronisation de groupe.", + "oidcGroupClaim": "Revendication de groupe", + "oidcGroupClaimDesc": "Facultatif. Chemin de la revendication contenant les groupes de l'utilisateur. Par défaut : groups, roles, puis group. Utilisez ce champ pour les fournisseurs avec une revendication personnalisée (ex. Zitadel).", "oidcCaCert": "Certificat CA personnalisé", - "oidcCaCertDesc": "Facultatif. Certificat d'autorité de certification (CA) au format PEM pour les fournisseurs OIDC utilisant une CA privée ou auto-signée. Laisser vide pour utiliser le magasin de certificats de confiance du système.", - "removeOidc": "Retirer", + "oidcCaCertDesc": "Facultatif. Certificat CA au format PEM pour les fournisseurs OIDC utilisant une CA privée ou auto-signée. Laissez vide pour utiliser le magasin de confiance du système.", + "removeOidc": "Supprimer", "usersCount": "{{count}} utilisateurs", "createUser": "Créer", "newRole": "Nouveau rôle", "roleName": "Nom", - "roleDisplayName": "Nom d'affichage", + "roleDisplayName": "Nom affiché", "roleDescription": "Description", "rolesCount": "{{count}} rôles", "createRole": "Créer", "creating": "Création...", - "exportDatabase": "Base de données d'exportation", - "exportDatabaseDesc": "Téléchargez une sauvegarde de tous les hôtes, identifiants et paramètres", + "exportDatabase": "Exporter la base de données", + "exportDatabaseDesc": "Télécharger une sauvegarde de tous les hôtes, identifiants et paramètres", "export": "Exporter", - "exporting": "Exportation...", - "importDatabase": "Base de données d'importation", + "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", + "changeFile": "Modifier", "import": "Importer", "importing": "Importation en cours...", - "apiKeysCount": "{{count}} touches", - "apiKeysDocsLink": "Consulter les documents", + "apiKeysCount": "{{count}} clés", + "apiKeysDocsLink": "Voir la documentation", "newApiKey": "Nouvelle clé API", - "apiKeyCreatedWarning": "Clé créée - copiez-la maintenant, elle ne sera plus affichée.", + "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": "Aucune date d'expiration", - "revokedBadge": "RÉVOQUÉ", - "authTypeDual": "Double authentification", + "apiKeySelectUser": "Sélectionner un utilisateur...", + "apiKeyExpiresAt": "Date d'expiration", + "createKey": "Créer la clé", + "apiKeyNoExpiry": "Aucune expiration", + "revokedBadge": "RÉVOQUÉE", + "authTypeDual": "Authentification double", "authTypeOidc": "OIDC", "authTypeLocal": "Locale", "adminStatusAdministrator": "Administrateur", - "adminStatusRegularUser": "Utilisateur régulier", + "adminStatusRegularUser": "Utilisateur standard", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "COUTUME", - "youBadge": "TOI", - "sessionsActive": "{{count}} actif", - "sessionActive": "Actif : {{time}}", - "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tous", + "customBadge": "PERSONNALISÉ", + "youBadge": "VOUS", + "sessionsActive": "{{count}} actives", + "sessionActive": "Active : {{time}}", + "sessionExpires": "Exp : {{time}}", + "revokeAll": "Toutes", "revokeAllSessionsSuccess": "Toutes les sessions de l'utilisateur ont été révoquées.", - "revokeAllSessionsFailed": "Échec de la révocation des sessions", - "revokeSessionFailed": "Échec de la révocation de la session", + "revokeAllSessionsFailed": "Échec de la révocation des sessions.", + "revokeSessionFailed": "Échec de la révocation de la session.", "addRole": "Ajouter un rôle", - "noCustomRoles": "Aucun rôle personnalisé défini", - "removeRoleFailed": "Échec de la suppression du rôle", - "assignRoleFailed": "Échec de l'attribution du rôle", - "deleteRoleFailed": "Échec de la suppression du rôle", + "noCustomRoles": "Aucun rôle personnalisé défini.", + "removeRoleFailed": "Échec de la suppression du rôle.", + "assignRoleFailed": "Échec de l'attribution du rôle.", + "deleteRoleFailed": "Échec de la suppression du rôle.", "userAdminAccess": "Administrateur", - "userAdminAccessDesc": "Accès complet à tous les paramètres d'administration", + "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", + "revokeAllUserSessionsDesc": "Forcer la reconnexion sur tous les appareils.", "revoke": "Révoquer", "deleteUserWarning": "La suppression de cet utilisateur est définitive.", "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": "Échec de la révocation de la clé", - "copiedToClipboard": "Copié dans le presse-papiers", - "done": "Fait", + "deleting": "Suppression…", + "deleteUserFailed": "Échec de la suppression de l'utilisateur.", + "deleteUserSuccess": "Utilisateur « {{username}} » supprimé.", + "deleteRoleSuccess": "Rôle « {{name}} » supprimé.", + "revokeKeySuccess": "Clé « {{name}} » révoquée.", + "revokeKeyFailed": "Échec de la révocation de la clé.", + "copiedToClipboard": "Copié dans le presse-papiers.", + "done": "Terminé", "createUserTitle": "Créer un utilisateur", - "createUserDesc": "Créez un nouveau compte local.", + "createUserDesc": "Créer un nouveau compte local.", "createUserUsername": "Nom d'utilisateur", "createUserPassword": "Mot de passe", - "createUserPasswordHint": "Minimum 6 caractères.", - "createUserEnterUsername": "Saisissez votre nom d'utilisateur", + "createUserPasswordHint": "6 caractères minimum.", + "createUserEnterUsername": "Saisissez le nom d'utilisateur", "createUserEnterPassword": "Saisissez le mot de passe", - "createUserSubmit": "Créer un utilisateur", + "createUserSubmit": "Créer", "editUserTitle": "Gérer l'utilisateur : {{username}}", - "editUserDesc": "Modifier les rôles, le statut d'administrateur, les sessions et les paramètres du compte.", + "editUserDesc": "Modifier les rôles, le statut administrateur, les sessions et les paramètres du compte.", "editUserUsername": "Nom d'utilisateur", - "editUserAuthType": "Type d'autorisation", - "editUserAdminStatus": "Statut administratif", - "editUserUserId": "ID de l'utilisateur", - "linkAccountTitle": "Lier les comptes", - "linkAccountDesc": "Fusionnez le compte OIDC {{username}} avec un compte local existant.", - "linkAccountDescLocal": "Associez un compte local {{username}} à un compte OIDC existant.", - "linkAccountWarningTitle": "Cela permettra de :", + "editUserAuthType": "Type d'authentification", + "editUserAdminStatus": "Statut administrateur", + "editUserUserId": "ID utilisateur", + "linkAccountTitle": "Lier des comptes", + "linkAccountDesc": "Fusionner le compte OIDC {{username}} avec un compte local existant.", + "linkAccountDescLocal": "Lier un compte local {{username}} avec un compte existant uniquement OIDC.", + "linkAccountWarningTitle": "Cela aura pour effet :", "linkAccountEffect1": "Supprimer le compte OIDC uniquement", - "linkAccountEffect2": "Ajouter l'identifiant OIDC au compte cible", - "linkAccountEffect3": "Autoriser la connexion par OIDC et par mot de passe", + "linkAccountEffect2": "Ajouter la connexion OIDC au compte cible", + "linkAccountEffect3": "Autoriser la connexion OIDC et par mot de passe", "linkAccountTargetUsername": "Nom d'utilisateur du compte local", - "linkAccountTargetPlaceholder": "Saisissez le nom d'utilisateur du compte local à lier à", + "linkAccountTargetPlaceholder": "Saisissez le nom d'utilisateur du compte local à lier", "linkAccountOidcUsername": "Nom d'utilisateur du compte OIDC", - "linkAccountOidcPlaceholder": "Saisissez le nom d'utilisateur du compte OIDC uniquement à fusionner.", + "linkAccountOidcPlaceholder": "Saisissez le nom d'utilisateur du compte OIDC uniquement à fusionner", "linkAccountOidcNotFound": "Aucun compte OIDC uniquement trouvé avec ce nom d'utilisateur", "linkAccounts": "Lier les comptes", "linkAccountSuccess": "Comptes liés avec succès", "linkAccountFailed": "Échec de la liaison des comptes", - "linkAccountInProgress": "Enchaînement...", + "linkAccountInProgress": "Liaison en cours…", "unlinkAccountTitle": "Dissocier OIDC", - "unlinkAccountDesc": "Supprimez l'authentification OIDC de {{username}}. Ils ne pourront se connecter qu'avec leur mot de passe.", - "unlinkAccountWarning": "Cette action supprimera l'accès OIDC à ce compte. L'utilisateur doit définir un mot de passe pour continuer à se connecter.", + "unlinkAccountDesc": "Supprimer l'authentification OIDC de {{username}}. La connexion ne sera possible qu'avec le mot de passe.", + "unlinkAccountWarning": "Cela supprimera la connexion OIDC de ce compte. L'utilisateur doit avoir défini un mot de passe pour continuer à se connecter.", "unlinkAccount": "Dissocier OIDC", - "unlinkAccountInProgress": "Déconnexion...", - "unlinkAccountSuccess": "OIDC dissocié avec succès", - "unlinkAccountFailed": "Échec de la dissociation d'OIDC", - "saving": "Économie...", - "updateRegistrationFailed": "Échec de la mise à jour des paramètres d'enregistrement", - "updatePasswordLoginFailed": "Échec de la mise à jour des paramètres de connexion par mot de passe", - "cannotDisablePasswordLoginWithTotp": "Impossible de désactiver la connexion par mot de passe lorsque l'authentification à deux facteurs est activée pour un ou plusieurs utilisateurs. Désactivez d'abord l'authentification à deux facteurs.", - "updateOidcAutoProvisionFailed": "Échec de la mise à jour des paramètres d'approvisionnement automatique OIDC", - "updateOidcSilentLoginDefaultFailed": "Échec de la mise à jour des paramètres de connexion OIDC silencieuse", - "updatePasswordResetFailed": "Échec de la mise à jour des paramètres de réinitialisation du mot de passe", - "sessionTimeoutRange2": "La durée de session doit être comprise entre 1 et 720 heures.", + "unlinkAccountInProgress": "Dissociation en cours…", + "unlinkAccountSuccess": "Dissociation OIDC réussie", + "unlinkAccountFailed": "Échec de la dissociation OIDC", + "saving": "Enregistrement…", + "updateRegistrationFailed": "Échec de la mise à jour du paramètre d'enregistrement", + "updatePasswordLoginFailed": "Échec de la mise à jour du paramètre de connexion par mot de passe", + "cannotDisablePasswordLoginWithTotp": "Impossible de désactiver la connexion par mot de passe tant que la 2FA est activée pour un ou plusieurs utilisateurs. Désactivez d'abord la 2FA.", + "updateOidcAutoProvisionFailed": "Échec de la mise à jour du paramètre d'approvisionnement automatique OIDC", + "updateOidcSilentLoginDefaultFailed": "Échec de la mise à jour du paramètre de connexion silencieuse OIDC", + "updatePasswordResetFailed": "Échec de la mise à jour du paramètre de réinitialisation du mot de passe", + "sessionTimeoutRange2": "Le délai d'expiration de session doit être compris entre 1 et 720 heures", "sessionTimeoutSaved": "Délai d'expiration de session enregistré", - "sessionTimeoutSaveFailed": "Échec de l'enregistrement de la session (délai d'expiration dépassé).", - "monitoringIntervalInvalid": "valeurs d'intervalle invalides", + "sessionTimeoutSaveFailed": "Échec de l'enregistrement du délai d'expiration de session", + "monitoringIntervalInvalid": "Valeurs d'intervalle invalides", "monitoringSaved": "Paramètres de surveillance enregistrés", - "monitoringSaveFailed": "Impossible d'enregistrer les paramètres de surveillance", - "metricsHistoryRetention": "Métriques Historique de conservation", + "monitoringSaveFailed": "Échec de l'enregistrement des paramètres de surveillance", + "metricsHistoryRetention": "Rétention de l'historique des métriques", "metricsHistoryRetentionRange": "1 à 90 jours", "days": "jours", - "guacamoleSaved": "Paramètres de Guacamole enregistrés", - "guacamoleSaveFailed": "Impossible d'enregistrer les paramètres de Guacamole", - "guacamoleUpdateFailed": "Échec de la mise à jour des paramètres Guacamole", - "tailscaleSettingsSaved": "Paramètres de Tailscale enregistrés", - "tailscaleSettingsSaveFailed": "Impossible d'enregistrer les paramètres de Tailscale", + "guacamoleSaved": "Paramètres Guacamole enregistrés", + "guacamoleSaveFailed": "Échec de l'enregistrement des paramètres Guacamole", + "guacamoleUpdateFailed": "Échec de la mise à jour du paramètre Guacamole", + "tailscaleSettingsSaved": "Paramètres Tailscale enregistrés", + "tailscaleSettingsSaveFailed": "Échec de l'enregistrement des paramètres Tailscale", "logLevelUpdateFailed": "Échec de la mise à jour du niveau de journalisation", "oidcSaved": "Configuration OIDC enregistrée", "oidcSaveFailed": "Échec de l'enregistrement de la configuration OIDC", "oidcRemoved": "Configuration OIDC supprimée", "oidcRemoveFailed": "Échec de la suppression de la configuration OIDC", - "createUserRequired": "Un nom d'utilisateur et un mot de passe sont requis.", - "createUserPasswordTooShort": "Le mot de passe doit comporter au moins 6 caractères.", - "createUserSuccess": "L'utilisateur «{{username}}» a créé", + "createUserRequired": "Nom d'utilisateur et mot de passe requis", + "createUserPasswordTooShort": "Le mot de passe doit comporter au moins 6 caractères", + "createUserSuccess": "Utilisateur « {{username}} » créé", "createUserFailed": "Échec de la création de l'utilisateur", "updateAdminStatusFailed": "Échec de la mise à jour du statut d'administrateur", - "allSessionsRevoked": "Toutes les sessions ont été révoquées.", + "allSessionsRevoked": "Toutes les sessions révoquées", "revokeSessionsFailed": "Échec de la révocation des sessions", "manageUserData": "Gérer les données utilisateur", "backToUsers": "Retour aux utilisateurs", "manageTabAccount": "Compte", "manageTabHosts": "Hôtes", - "manageTabCredentials": "Informations d'identification", + "manageTabCredentials": "Identifiants", "manageTabSnippets": "Extraits", - "manageTabSessions": "Séances", + "manageTabSessions": "Sessions", "manageTabDanger": "Danger", "manageEditorBack": "Retour aux données de {{username}}", - "dataLockedBadge": "FERMÉ", - "dataLockedNotice": "Les données de {{username}}restent verrouillées jusqu'à sa prochaine connexion. Ses hôtes, identifiants et extraits de code ne peuvent être consultés ni modifiés d'ici là.", + "dataLockedBadge": "VERROUILLÉ", + "dataLockedNotice": "Les données de {{username}} restent verrouillées jusqu'à leur prochaine connexion. Leurs hôtes, identifiants et extraits ne peuvent être consultés ni modifiés d'ici là.", "resetPasswordTitle": "Réinitialiser le mot de passe", "resetPasswordOidcOnly": "Cet utilisateur se connecte via un fournisseur externe et n'a pas de mot de passe.", "resetPasswordPlaceholder": "Nouveau mot de passe", "resetPasswordBtn": "Réinitialiser", - "resetPasswordWorking": "Réinitialisation...", - "resetPasswordSuccess": "Réinitialisation du mot de passe", - "resetPasswordSuccessWiped": "Réinitialisation du mot de passe. Les données chiffrées de l'utilisateur ont été effacées.", + "resetPasswordWorking": "Réinitialisation en cours...", + "resetPasswordSuccess": "Mot de passe réinitialisé", + "resetPasswordSuccessWiped": "Mot de passe réinitialisé. Les données chiffrées de l'utilisateur ont été effacées.", "resetPasswordFailed": "Échec de la réinitialisation du mot de passe", - "resetPasswordConfirmWipe": "L'utilisateur {{username}} ne s'est pas connecté depuis la mise à jour du chiffrement ; ses données sont donc irrécupérables. La réinitialisation effacera ses hôtes, ses identifiants et ses extraits de code. Continuer ?", + "resetPasswordConfirmWipe": "{{username}} ne s'est pas connecté depuis la mise à niveau du chiffrement, ses données ne peuvent donc pas être récupérées. La réinitialisation supprimera ses hôtes, identifiants et extraits. Continuer ?", "totpSectionTitle": "Authentification à deux facteurs", - "totpStatusEnabled": "Le protocole TOTP est activé pour cet utilisateur.", - "totpStatusDisabled": "Le protocole TOTP n'est pas activé pour cet utilisateur.", + "totpStatusEnabled": "TOTP est activé pour cet utilisateur", + "totpStatusDisabled": "TOTP n'est pas activé pour cet utilisateur", "disableTotp": "Désactiver", - "disableTotpConfirm": "Désactiver l'authentification à deux facteurs pour {{username}}? Ils pourront se connecter avec leur seul mot de passe.", + "disableTotpConfirm": "Désactiver l'authentification à deux facteurs pour {{username}} ? Il pourra se connecter uniquement avec son mot de passe.", "totpDisabledSuccess": "TOTP désactivé", - "totpDisableFailed": "Échec de la désactivation du TOTP", + "totpDisableFailed": "Échec de la désactivation de TOTP", "manageApiKeys": "Clés API", "apiKeyNamePlaceholder": "Nom de la clé", "apiKeyCopyNotice": "Copiez cette clé maintenant, elle ne sera plus affichée.", "noApiKeysForUser": "Aucune clé API", "apiKeyDeleteFailed": "Échec de la suppression de la clé API", - "exportUserData": "Exportation de données", - "exportUserDataDesc": "Téléchargez les données d'hôtes, d'identifiants et de gestionnaire de fichiers de cet utilisateur au format JSON. Les secrets sont déchiffrés.", + "exportUserData": "Export des données", + "exportUserDataDesc": "Télécharger les hôtes, identifiants et données du gestionnaire de fichiers de cet utilisateur au format JSON. Les secrets sont déchiffrés.", "exportUserDataSuccess": "Données utilisateur exportées", "exportUserDataFailed": "Échec de l'exportation des données utilisateur", "hostsCount": "{{count}} hôtes", "addHostForUser": "Ajouter un hôte", - "noHostsForUser": "Cet utilisateur n'a pas d'hôtes.", - "connectToHost": "Connecter", - "deleteHostConfirm": "Supprimer l'hôte \"{{name}}\" appartenant à {{username}}?", + "noHostsForUser": "Cet utilisateur n'a aucun hôte", + "connectToHost": "Se connecter", + "deleteHostConfirm": "Supprimer l'hôte \"{{name}}\" appartenant à {{username}} ?", "hostDeletedSuccess": "Hôte supprimé", "hostDeleteFailed": "Échec de la suppression de l'hôte", "credentialsCount": "{{count}} identifiants", "addCredentialForUser": "Ajouter un identifiant", - "noCredentialsForUser": "Cet utilisateur ne possède aucun identifiant", - "deleteCredentialConfirm": "Supprimer l'identifiant «{{name}}» appartenant à {{username}}?", - "credentialDeletedSuccess": "Identifiants supprimés", - "credentialDeleteFailed": "Échec de la suppression des identifiants", - "snippetsCount": "{{count}} extraits", - "addSnippetForUser": "Ajouter un extrait", - "noSnippetsForUser": "Cet utilisateur n'a aucun extrait.", - "snippetRequiredFields": "Le nom et le contenu de l'extrait sont obligatoires.", - "snippetNamePlaceholder": "Nom de l'extrait", + "noCredentialsForUser": "Cet utilisateur n'a pas d'identifiants", + "deleteCredentialConfirm": "Supprimer l'identifiant \"{{name}}\" appartenant à {{username}} ?", + "credentialDeletedSuccess": "Identifiant supprimé", + "credentialDeleteFailed": "Échec de la suppression de l'identifiant", + "snippetsCount": "{{count}} snippets", + "addSnippetForUser": "Ajouter un snippet", + "noSnippetsForUser": "Cet utilisateur n'a aucun snippet", + "snippetRequiredFields": "Le nom et le contenu du snippet sont requis", + "snippetNamePlaceholder": "Nom du snippet", "snippetContentPlaceholder": "Contenu de la commande", - "snippetFolderPlaceholder": "Dossier (facultatif)", - "snippetSaved": "Extrait enregistré", - "snippetSaveFailed": "Impossible d'enregistrer l'extrait.", - "deleteSnippetConfirm": "Supprimer l'extrait \"{{name}}\" appartenant à {{username}}?", - "snippetDeletedSuccess": "Extrait supprimé", - "snippetDeleteFailed": "Échec de la suppression de l'extrait", + "snippetFolderPlaceholder": "Dossier (optionnel)", + "snippetSaved": "Snippet enregistré", + "snippetSaveFailed": "Échec de l'enregistrement du snippet", + "deleteSnippetConfirm": "Supprimer le snippet \"{{name}}\" appartenant à {{username}} ?", + "snippetDeletedSuccess": "Snippet supprimé", + "snippetDeleteFailed": "Échec de la suppression du snippet", "noSessionsForUser": "Aucune session active", - "deleteUserDangerDesc": "Supprimer définitivement {{username}} et toutes ses données (hôtes, identifiants, extraits, historique). Cette action est irréversible.", - "deleteUserConfirm": "Supprimer définitivement {{username}} et toutes ses données ?", - "deleteUserAdminBlocked": "Supprimez le statut d'administrateur avant de supprimer cet utilisateur.", - "createRoleRequired": "Le nom et le nom d'affichage sont obligatoires.", - "createRoleSuccess": "Rôle «{{name}}» créé", + "deleteUserDangerDesc": "Supprimer définitivement {{username}} et toutes ses données (hôtes, identifiants, snippets, historique). Cette action est irréversible.", + "deleteUserConfirm": "Supprimer définitivement {{username}} et toutes ses données ?", + "deleteUserAdminBlocked": "Retirez le statut d'administrateur avant de supprimer cet utilisateur.", + "createRoleRequired": "Le nom et le nom d'affichage sont requis", + "createRoleSuccess": "Rôle \"{{name}}\" créé", "createRoleFailed": "Échec de la création du rôle", - "apiKeyNameRequired": "Le nom de la clé est requis.", - "apiKeyUserRequired": "Un identifiant utilisateur est requis.", - "apiKeyCreatedSuccess": "Clé API «{{name}}» créée", + "apiKeyNameRequired": "Le nom de la clé est requis", + "apiKeyUserRequired": "L'identifiant utilisateur est requis", + "apiKeyCreatedSuccess": "Clé API \"{{name}}\" créée", "apiKeyCreateFailed": "Échec de la création de la clé API", "exportSuccess": "Base de données exportée avec succès", - "exportFailed": "L'exportation de la base de données a échoué.", + "exportFailed": "Échec de l'exportation de la base de données", "importSelectFile": "Veuillez d'abord sélectionner un fichier", - "importCompleted": "Importation terminée : {{total}} éléments importés, {{skipped}} ignorés", - "importFailed": "Échec de l'importation : {{error}}", - "importError": "L'importation de la base de données a échoué.", + "importCompleted": "Importation terminée : {{total}} éléments importés, {{skipped}} ignorés", + "importFailed": "Échec de l'importation : {{error}}", + "importError": "Échec de l'importation de la base de données", "sectionHostDefaults": "Paramètres par défaut de l'hôte", - "hostDefaultsDesc": "Ces paramètres sont appliqués automatiquement lors de la création d'un nouvel hôte. Chaque hôte peut toutefois les modifier.", + "hostDefaultsDesc": "Paramètres appliqués automatiquement lors de la création d'un nouvel hôte. Les hôtes individuels peuvent toujours les remplacer.", "hostDefaultsSocks5": "Proxy SOCKS5", "hostDefaultsUseSocks5": "Activer le proxy SOCKS5", - "hostDefaultsUseSocks5Desc": "Pré-remplissage du proxy SOCKS5 sur tous les nouveaux hôtes", - "hostDefaultsSocks5Host": "Hôte/Port du proxy", - "hostDefaultsSocks5Username": "Nom d'utilisateur du proxy", + "hostDefaultsUseSocks5Desc": "Pré-remplir le proxy SOCKS5 sur tous les nouveaux hôtes", + "hostDefaultsSocks5Host": "Hôte / Port du proxy", + "hostDefaultsSocks5Username": "Nom d’utilisateur du proxy", "hostDefaultsSocks5Password": "Mot de passe du proxy", - "hostDefaultsMetrics": "Métriques de l'hôte", - "hostDefaultsMetricsEnabled": "Activer les indicateurs", - "hostDefaultsMetricsEnabledDesc": "Collecter par défaut les statistiques du processeur, de la mémoire et autres sur les nouveaux hôtes", - "hostDefaultsStatusCheckEnabled": "Activer la vérification de l'état", - "hostDefaultsStatusCheckEnabledDesc": "Interroger par défaut le statut en ligne/hors ligne des nouveaux hôtes", + "hostDefaultsMetrics": "Métriques de l’hôte", + "hostDefaultsMetricsEnabled": "Activer les métriques", + "hostDefaultsMetricsEnabledDesc": "Collecter par défaut les statistiques CPU, mémoire, etc. des nouveaux hôtes", + "hostDefaultsStatusCheckEnabled": "Activer la vérification de l’état", + "hostDefaultsStatusCheckEnabledDesc": "Sonder par défaut l’état en ligne/hors ligne des nouveaux hôtes", "hostDefaultsTerminal": "Terminal", "hostDefaultsSessionLogging": "Journalisation des sessions", - "hostDefaultsSessionLoggingDesc": "Enregistrement par défaut des sessions de terminal sur les nouveaux hôtes", + "hostDefaultsSessionLoggingDesc": "Enregistrer par défaut les sessions de terminal sur les nouveaux hôtes", "hostDefaultsCommandHistory": "Historique des commandes", - "hostDefaultsCommandHistoryDesc": "Suivi de l'historique des commandes sur les nouveaux hôtes par défaut", - "hostDefaultsSaved": "Paramètres par défaut de l'hôte enregistrés", - "hostDefaultsSaveFailed": "Impossible d'enregistrer les paramètres par défaut de l'hôte.", + "hostDefaultsCommandHistoryDesc": "Suivre par défaut l’historique des commandes sur les nouveaux hôtes", + "hostDefaultsSaved": "Paramètres par défaut des hôtes sauvegardés", + "hostDefaultsSaveFailed": "Échec de la sauvegarde des paramètres par défaut des hôtes", "rolePermissions": { "count": "{{count}} permissions", - "editAction": "Modifier les autorisations", - "loadError": "Échec du chargement du catalogue des autorisations", - "saved": "Les autorisations de rôle ont été enregistrées", - "saveError": "Impossible d'enregistrer les autorisations de rôle.", - "save": "Sauvegarder", - "saving": "Économie..." + "editAction": "Modifier les permissions", + "loadError": "Impossible de charger le catalogue des permissions", + "saved": "Permissions du rôle sauvegardées", + "saveError": "Impossible de sauvegarder les permissions du rôle", + "save": "Enregistrer", + "saving": "Enregistrement..." } }, "newUi": { "sidebar": { "quickConnect": { "hostLabel": "Hôte", - "hostPlaceholder": "192.168.1.1 ou example.com", + "hostPlaceholder": "192.168.1.1 ou exemple.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nom d'utilisateur", - "usernamePlaceholder": "nom d'utilisateur", + "usernameLabel": "Nom d’utilisateur", + "usernamePlaceholder": "nom d’utilisateur", "authLabel": "Authentification", "passwordLabel": "Mot de passe", "passwordPlaceholder": "mot de passe", "privateKeyLabel": "Clé privée", - "privateKeyPlaceholder": "Collez la clé privée...", - "credentialLabel": "Attestation", - "credentialPlaceholder": "Sélectionnez un identifiant enregistré", + "privateKeyPlaceholder": "Coller la clé privée...", + "credentialLabel": "Identifiant", + "credentialPlaceholder": "Sélectionner un identifiant enregistré", "connectToTerminal": "Se connecter au terminal", "connectToFiles": "Se connecter aux fichiers" }, "history": { "noTerminalSelected": "Aucun terminal sélectionné", - "noTerminalSelectedHint": "Ouvrez un onglet de terminal SSH pour consulter son historique de commandes.", - "searchPlaceholder": "Historique de recherche...", - "clearAll": "Effacer tout", + "noTerminalSelectedHint": "Ouvrez un onglet de terminal SSH pour voir son historique de 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 du terminal hôte." + "trackingDisabled": "Le suivi de l'historique est désactivé", + "trackingDisabledHint": "Activez-le dans les paramètres du terminal de l'hôte." }, "sshTools": { - "keyRecordingTitle": "Enregistrement clé", - "recordToTerminals": "Enregistrement sur les terminaux", - "selectAll": "Tous", + "keyRecordingTitle": "Enregistrement des touches", + "recordToTerminals": "Enregistrer vers les terminaux", + "selectAll": "Tout", "selectNone": "Aucun", "noTerminalTabsOpen": "Aucun onglet de terminal ouvert", - "selectTerminalsAbove": "Sélectionnez les terminaux ci-dessus", - "broadcastInputPlaceholder": "Saisissez votre frappe ici...", - "fillPassword": "Saisissez le mot de passe", - "fillPasswordSuccess": "Mot de passe saisi dans le(s) terminal(s) {{count}}", - "fillPasswordMissing": "Aucun mot de passe enregistré pour le(s) terminal(s) sélectionné(s) {{count}}", + "selectTerminalsAbove": "Sélectionner les terminaux ci-dessus", + "broadcastInputPlaceholder": "Tapez ici pour diffuser les frappes...", + "fillPassword": "Remplir le mot de passe", + "fillPasswordSuccess": "Mot de passe rempli dans {{count}} terminal(s)", + "fillPasswordMissing": "Aucun mot de passe enregistré pour {{count}} terminal(s) sélectionné(s)", "stopRecording": "Arrêter l'enregistrement", "startRecording": "Démarrer l'enregistrement", "settingsTitle": "Paramètres", - "enableRightClickCopyPaste": "Activer le copier-coller par clic droit" + "enableRightClickCopyPaste": "Activer le copier/coller par clic droit" }, "splitScreen": { - "layoutTitle": "Mise en page", - "selectLayoutAbove": "Sélectionnez une mise en page ci-dessus", - "selectLayoutHint": "Choisissez le nombre de panneaux à afficher", - "panesTitle": "Vitres", - "openTabsTitle": "Ouvrir les onglets", - "dragTabsHint": "Faites glisser les onglets dans les volets ci-dessus ou utilisez l'attribution rapide.", - "dropHere": "Déposez ici", + "layoutTitle": "Disposition", + "selectLayoutAbove": "Sélectionnez une disposition ci-dessus", + "selectLayoutHint": "Choisissez le nombre de volets à afficher", + "panesTitle": "Volets", + "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": "Écran partagé clair", - "quickAssign": "Affectation rapide", + "clearSplitScreen": "Supprimer le fractionnement", + "quickAssign": "Attribution rapide", "alreadyAssigned": "Volet {{index}}", - "splitTab": "Onglet fractionné", + "splitTab": "Fractionner l'onglet", "addToSplit": "Ajouter au fractionnement", - "removeFromSplit": "Retirer de la division", - "assignToPane": "Affecter au volet", + "removeFromSplit": "Retirer du fractionnement", + "assignToPane": "Attribuer au volet", "hotkeysTitle": "Raccourcis clavier", - "hotkeysSplitRight": "Basculer la division en 2 voies", - "hotkeysSplitBelow": "Basculer la division en 3 voies", - "hotkeysNavigatePane": "Volets de navigation", + "hotkeysSplitRight": "Basculer en 2 volets", + "hotkeysSplitBelow": "Basculer en 3 volets", + "hotkeysNavigatePane": "Naviguer entre les volets", "hotkeysNextTab": "Onglet suivant", "hotkeysPrevTab": "Onglet précédent" }, "snippets": { "title": "Extraits", "createSnippetTitle": "Créer un extrait", - "createSnippetDescription": "Créez un nouveau fragment de commande pour une exécution rapide.", + "createSnippetDescription": "Créer un nouvel extrait de commande pour une exécution rapide", "nameLabel": "Nom", - "namePlaceholder": "Par exemple, redémarrer Nginx", + "namePlaceholder": "par ex., Redémarrer Nginx", "descriptionLabel": "Description", "descriptionPlaceholder": "Description facultative", "optional": "Facultatif", "folderLabel": "Dossier", "noFolder": "Aucun dossier (Non classé)", "commandLabel": "Commande", - "commandPlaceholder": "Par exemple, sudo systemctl restart nginx", + "commandPlaceholder": "par ex., sudo systemctl restart nginx", "cancel": "Annuler", - "createSnippetButton": "Créer un extrait", + "createSnippetButton": "Créer l'extrait", "createFolderTitle": "Créer un dossier", - "createFolderDescription": "Organisez vos extraits de code dans des dossiers.", + "createFolderDescription": "Organisez vos extraits dans des dossiers", "folderNameLabel": "Nom du dossier", - "folderNamePlaceholder": "Par exemple, les commandes système, les scripts Docker", + "folderNamePlaceholder": "par ex., Commandes système, Scripts Docker", "folderColorLabel": "Couleur du dossier", - "folderIconLabel": "Icône de dossier", + "folderIconLabel": "Icône du dossier", "previewLabel": "Aperçu", "folderNameFallback": "Nom du dossier", "createFolderButton": "Créer un dossier", @@ -3049,163 +3200,208 @@ "selectAll": "Tous", "selectNone": "Aucun", "noTerminalTabsOpen": "Aucun onglet de terminal ouvert", - "searchPlaceholder": "Extraits de recherche...", - "newSnippet": "Nouvel extrait", + "searchPlaceholder": "Rechercher des snippets...", + "newSnippet": "Nouveau snippet", "newFolder": "Nouveau dossier", - "run": "Courir", - "noSnippetsInFolder": "Aucun extrait dans ce dossier", + "run": "Exécuter", + "noSnippetsInFolder": "Aucun snippet dans ce dossier", "uncategorized": "Non classé", - "editSnippetTitle": "Modifier l'extrait", - "editSnippetDescription": "Mettez à jour cet extrait de commande", + "editSnippetTitle": "Modifier le snippet", + "editSnippetDescription": "Mettre à jour ce snippet de commande", "saveSnippetButton": "Enregistrer les modifications", - "createSuccess": "Extrait créé avec succès", - "createFailed": "Échec de la création de l'extrait de code", - "updateSuccess": "Extrait mis à jour avec succès", - "updateFailed": "Échec de la mise à jour de l'extrait", - "deleteFailed": "Échec de la suppression de l'extrait", + "createSuccess": "Snippet créé avec succès", + "createFailed": "Échec de la création du snippet", + "updateSuccess": "Snippet mis à jour avec succès", + "updateFailed": "Échec de la mise à jour du snippet", + "deleteFailed": "Échec de la suppression du snippet", "folderCreateSuccess": "Dossier créé avec succès", - "folderCreateFailed": "Impossible de créer le dossier", + "folderCreateFailed": "Échec de la création du dossier", "editFolderTitle": "Modifier le dossier", - "editFolderDescription": "Renommez ce dossier ou modifiez son apparence.", + "editFolderDescription": "Renommer ou modifier l'apparence de ce dossier", "saveFolderButton": "Enregistrer les modifications", "editFolder": "Modifier le dossier", "deleteFolder": "Supprimer le dossier", - "folderDeleteSuccess": "Dossier «{{name}}» supprimé", + "folderDeleteSuccess": "Dossier « {{name}} » 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écuté «{{name}}» dans {{count}} terminal(s)", - "copySuccess": "Copie de «{{name}}» dans le presse-papiers", - "shareTitle": "Extrait de partage", + "confirmRunMessage": "Exécuter « {{name}} » ?", + "confirmRunButton": "Exécuter", + "runSuccess": "Exécuté « {{name}} » dans {{count}} terminal(s)", + "copySuccess": "Copié « {{name}} » dans le presse-papiers", + "shareTitle": "Partager le snippet", "shareUser": "Utilisateur", "shareRole": "Rôle", - "selectUser": "Sélectionnez un utilisateur...", - "selectRole": "Choisissez un rôle...", - "shareSuccess": "Extrait partagé avec succès", - "shareFailed": "Échec du partage de l'extrait", + "selectUser": "Sélectionner un utilisateur...", + "selectRole": "Sélectionner un rôle...", + "shareSuccess": "Snippet partagé avec succès", + "shareFailed": "Échec du partage du snippet", "revokeSuccess": "Accès révoqué", - "revokeFailed": "Échec de la révocation de l'accès", + "revokeFailed": "Échec de la révocation de l’accès", "currentAccess": "Accès actuel", - "shareLoadError": "Échec du chargement des données partagées", + "shareLoadError": "Échec du chargement des données de partage", "loading": "Chargement...", "close": "Fermer", - "reorderFailed": "Impossible d'enregistrer l'ordre des extraits", - "importExport": "Importation / Exportation", + "reorderFailed": "Échec de l’enregistrement de l’ordre des snippets", + "importExport": "Importer / Exporter", "exportBtn": "Exporter JSON", - "importBtn": "Importer du JSON", - "exportSuccess": "Extraits exportés avec succès", - "exportFailed": "Échec de l'exportation des extraits", - "importTitle": "Importer des extraits", - "importDescription": "Importer des extraits et des dossiers à partir d'un fichier JSON exporté par Termix", + "importBtn": "Importer JSON", + "exportSuccess": "Snippets exportés avec succès", + "exportFailed": "Échec de l’exportation des snippets", + "importTitle": "Importer des snippets", + "importDescription": "Importer des snippets et des dossiers depuis un fichier JSON exporté par Termix", "importDropOrClick": "Déposez un fichier JSON ici ou cliquez pour parcourir", "importSelectedFile": "Sélectionné : {{name}}", - "importOverwrite": "Écrasez les extraits existants portant le même nom et appartenant au même dossier.", + "importOverwrite": "Écraser les snippets existants ayant le même nom et le même dossier", "importStartBtn": "Importer", - "importSuccess": "Importation terminée : {{snippets}} extrait(s) ajouté(s), {{updated}} mis à jour, {{skipped}} ignoré, {{folders}} dossier(s) ajouté(s)", - "importFailed": "Échec de l'importation des extraits de code", - "importInvalidFile": "Fichier invalide : un objet JSON contenant des tableaux d’extraits ou de dossiers était attendu.", + "importSuccess": "Importation terminée : {{snippets}} snippet(s) ajouté(s), {{updated}} mis à jour, {{skipped}} ignoré(s), {{folders}} dossier(s) ajouté(s)", + "importFailed": "Échec de l’importation des snippets", + "importInvalidFile": "Fichier non valide : un objet JSON avec des tableaux « snippets » ou « folders » est attendu", "targetHostsLabel": "Hôtes cibles", - "targetHostsHint": "Attribuez des hôtes pour exécuter directement ce fragment de code sans avoir besoin d'ouvrir un terminal.", + "targetHostsHint": "Attribuez des hôtes pour exécuter ce snippet directement sans terminal ouvert.", "noHostsAvailable": "Aucun hôte configuré", - "clearTargetHosts": "Effacer tout", - "hasTargetHosts": "A des hôtes cibles", - "runOnTargets": "Exécution sur les cibles", - "directRunSuccess": "Exécution de «{{name}}» sur {{count}} hôte(s)", - "directRunPartialFail": "\"{{name}}\" a échoué sur un ou plusieurs hôtes", - "executionResultTitle": "Résultats de l'exécution : {{name}}", - "executionResultDescription": "Résultat de l'exécution de l'extrait de code sur chaque hôte cible.", + "clearTargetHosts": "Tout effacer", + "hasTargetHosts": "Avec hôtes cibles", + "runOnTargets": "Exécuter sur les cibles", + "directRunSuccess": "« {{name}} » exécuté sur {{count}} hôte(s)", + "directRunPartialFail": "« {{name}} » a échoué sur un ou plusieurs hôtes", + "executionResultTitle": "Résultats d'exécution : {{name}}", + "executionResultDescription": "Sortie de l'exécution du snippet sur chaque hôte cible.", "executionSuccess": "Succès", - "executionFailed": "Échoué" + "executionFailed": "Échec" + }, + "keybindings": { + "title": "Raccourcis clavier", + "description": "Personnalisez les raccourcis de copie, de collage et de contrôle du terminal, ou associez des touches pour envoyer du texte ou exécuter un extrait de code.", + "defaultsHeading": "Raccourcis intégrés", + "customHeading": "Raccourcis personnalisés", + "addBinding": "Ajouter un raccourci", + "addBindingTitle": "Ajouter un raccourci", + "editBindingTitle": "Modifier le raccourci", + "loading": "Chargement...", + "noCustomBindings": "Aucun raccourci personnalisé pour le moment", + "defaultBadge": "Défaut", + "customizedBadge": "Personnalisé", + "customize": "Personnaliser", + "resetToDefault": "Réinitialiser par défaut", + "resetAllToDefaults": "Réinitialiser tout aux valeurs par défaut", + "close": "Fermer", + "cancel": "Annuler", + "saveBinding": "Enregistrer le raccourci", + "saveError": "Impossible d'enregistrer les raccourcis clavier.", + "pressKeysToRecord": "Combinaison de clés", + "pressKeysPlaceholder": "Cliquez et appuyez sur les touches", + "recording": "Appuyez sur les touches...", + "comboRequiredError": "Appuyez d'abord sur une combinaison de touches", + "textRequiredError": "Saisissez le texte à envoyer", + "controlCodeRequiredError": "Saisissez une seule lettre pour le code de contrôle", + "snippetRequiredError": "Sélectionnez un extrait", + "conflictWarning": "Cette combinaison est déjà liée à {{combo}}. Sauvegarder déclenchera les deux.", + "actionLabel": "Action", + "actionCopy": "Copier la sélection", + "actionPaste": "Coller", + "actionSendControlCode": "Envoyer le signal Ctrl+lettre", + "actionSendText": "Envoyer un SMS", + "actionRunSnippet": "Exécuter l'extrait de code existant", + "controlCodeLabel": "Lettre", + "textLabel": "Texte à envoyer", + "appendEnterLabel": "Appuyez sur Entrée après l'envoi", + "snippetLabel": "Fragment", + "selectSnippetPlaceholder": "Sélectionnez un extrait", + "orphanedSnippetWarning": "extrait introuvable", + "clipboardPermissionNote": "Il se peut que certains navigateurs demandent l'autorisation d'accéder au presse-papiers lors de la première utilisation." }, "userProfile": { - "donateTitle": "Maintenez Termix en vie", - "donateDescription": "Termix est gratuit, auto-hébergé et développé par une poignée de personnes sur leur temps libre. Si ce logiciel vous a permis d'économiser du temps ou de l'argent, un don en cryptomonnaie nous aidera à poursuivre son développement.", - "donateMilestones": "Vos dons nous permettent de financer le temps consacré à la recherche et à l'apprentissage des compétences nécessaires au développement de la prise en charge de SAML, Kubernetes et des agents. Suivez l'avancement du projet et faites un don.", - "donateButton": "Faire un don en cryptomonnaie", + "donateTitle": "Soutenir Termix", + "donateDescription": "Termix est gratuit, auto-hébergé et conçu par seulement quelques personnes pendant leur temps libre. Si cela vous a fait gagner du temps ou de l'argent, un don en crypto aide à le maintenir.", + "donateMilestones": "Les dons aident à financer le temps de recherche et d'apprentissage nécessaire pour développer le support SAML, Kubernetes et Agent. Voir les progrès et faire un don.", + "donateButton": "Faire un don en crypto", "storageModeLocal": "Navigateur", "storageModeCloud": "Base de données", - "storageModeDescription": "Le navigateur enregistre les paramètres uniquement dans ce navigateur. La base de données est synchronisée avec le serveur et accessible depuis n'importe quel appareil.", - "resetToDefaults": "Réinitialiser les paramètres par défaut", - "resetToDefaultsSuccess": "Les paramètres sont réinitialisés aux valeurs par défaut.", + "storageModeDescription": "Le navigateur stocke les paramètres uniquement dans ce navigateur. La base de données synchronise avec le serveur et charge sur n'importe quel appareil.", + "resetToDefaults": "Réinitialiser aux valeurs par défaut", + "resetToDefaultsSuccess": "Paramètres réinitialisés aux valeurs par défaut.", "storageModeSwitch": "Stockage des préférences", "sectionAccount": "Compte", "sectionAppearance": "Apparence", "sectionSecurity": "Sécurité", "sectionApiKeys": "Clés API", + "sectionData": "Données", "sectionC2sTunnels": "Tunnels C2S", "usernameLabel": "Nom d'utilisateur", "roleLabel": "Rôle", "roleAdministrator": "Administrateur", "authMethodLabel": "Méthode d'authentification", - "authMethodLocal": "Locale", - "twoFaLabel": "L'authentification à deux facteurs (2FA)", - "twoFaOn": "Sur", - "twoFaOff": "Désactivé", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "Activée", + "twoFaOff": "Désactivée", "versionLabel": "Version", "betaProgramTitle": "Programme bêta", - "betaProgramDescription": "Testez les nouvelles fonctionnalités en avant-première grâce à la balise Docker bêta (mise à jour hebdomadaire). Instable, ne convient pas à la production.", - "betaProgramFeedback": "Vous avez trouvé un bug ? Signalez-le ici.", - "betaProgramLearnMore": "Apprendre encore plus", + "betaProgramDescription": "Essayez les fonctionnalités à venir en avance avec le tag Docker :beta à rolling hebdomadaire. Instable, pas pour la production.", + "betaProgramFeedback": "Un bug trouvé ? Signalez-le ici.", + "betaProgramLearnMore": "En savoir plus", "deleteAccount": "Supprimer le compte", "deleteAccountDescription": "Supprimer définitivement votre compte", - "changeServerDescription": "Passer à un autre serveur backend Termix", + "changeServerDescription": "Basculer vers un autre serveur backend Termix", "deleteButton": "Supprimer", - "deleteAccountPermanent": "Cette action est irréversible.", + "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 définitivement supprimés.", "confirmPasswordDeletePlaceholder": "Saisissez votre mot de passe pour confirmer", "languageLabel": "Langue", "themeLabel": "Thème", "fontSizeLabel": "Taille de la police", - "accentColorLabel": "Couleur d'accent", + "accentColorLabel": "Couleur d’accentuation", "settingsTerminal": "Terminal", - "commandAutocomplete": "Saisie semi-automatique des commandes", - "commandAutocompleteDesc": "Afficher la saisie semi-automatique pendant la saisie", - "terminalLinkBehavior": "Lien vers le terminal Cliquez", - "terminalLinkBehaviorDesc": "Comportement par défaut lors du clic sur des liens dans le terminal", - "historyTracking": "Suivi de l'historique", + "commandAutocomplete": "Autocomplétion des commandes", + "commandAutocompleteDesc": "Afficher l’autocomplétion pendant la saisie", + "keyboardShortcuts": "Raccourcis clavier", + "keyboardShortcutsDescription": "Personnalisez les raccourcis de copie, de collage et de commandes du terminal", + "manageShortcuts": "Gérer", + "terminalLinkBehavior": "Clic lien terminal", + "terminalLinkBehaviorDesc": "Comportement par défaut lors du clic sur un lien dans le terminal", + "historyTracking": "Suivi de l’historique", "historyTrackingDesc": "Suivre les commandes 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": "Demandez la permission avant de fermer les onglets du terminal", - "settingsSidebar": "barre latérale", - "showHostTags": "Afficher les balises 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.", + "reopenTabsOnLoginDesc": "Restaurer vos onglets ouverts lors de la connexion ou du rafraîchissement de la page, même depuis un autre appareil", + "confirmTabClose": "Confirmer la fermeture des onglets", + "confirmTabCloseDesc": "Demander avant de fermer les onglets du terminal", + "settingsSidebar": "Barre latérale", + "showHostTags": "Afficher les étiquettes d’hôte", + "showHostTagsDesc": "Afficher les étiquettes dans la liste des hôtes", + "hostTrayOnClick": "Cliquer pour développer les actions de l’hôte", + "hostTrayOnClickDesc": "Toujours afficher les boutons de connexion ; cliquer pour développer les options de gestion au lieu de survoler", "compactHostView": "Vue hôte compacte", - "compactHostViewDesc": "Réduisez chaque hôte à une seule ligne affichant uniquement son nom et son adresse.", - "statusColors": "Couleurs du véritable statut", - "statusColorsDesc": "Utilisez le vert/rouge pour indiquer le statut en ligne/hors ligne au lieu de la couleur d'accentuation.", - "pinAppRail": "Rail d'application Pin", - "pinAppRailDesc": "Conserver le panneau latéral gauche toujours déployé au lieu de le déployer au survol.", - "expandAppRailOnHover": "Développez le rail d'application au survol.", - "expandAppRailOnHoverDesc": "Autoriser l'expansion du panneau latéral gauche lorsque le pointeur le survole.", + "compactHostViewDesc": "Réduire chaque hôte à une seule ligne affichant uniquement son nom et son adresse", + "statusColors": "Couleurs d’état réelles", + "statusColorsDesc": "Utiliser vert/rouge pour l’état en ligne/hors ligne au lieu de la couleur d’accentuation", + "pinAppRail": "Épingler la barre d’applications", + "pinAppRailDesc": "Garder la barre d’applications de la barre latérale gauche toujours développée au lieu de se développer au survol", + "expandAppRailOnHover": "Développer la barre d’applications au survol", + "expandAppRailOnHoverDesc": "Permettre à la barre d’applications de la barre latérale gauche de se développer lorsque le pointeur la survole", "settingsNavigation": "Navigation", - "navigationTabsDesc": "Choisissez les onglets qui apparaissent dans le rail de l'application", - "settingsSnippets": "Extraits", + "navigationTabsDesc": "Choisir les onglets affichés dans la barre latérale de l'application", + "settingsSnippets": "Snippets", "foldersCollapsed": "Dossiers réduits", "foldersCollapsedDesc": "Réduire les dossiers par défaut", "confirmExecution": "Confirmer l'exécution", - "confirmExecutionDesc": "Veuillez confirmer avant d'exécuter les extraits de code.", + "confirmExecutionDesc": "Confirmer avant d'exécuter des snippets", "settingsUpdates": "Mises à jour", - "disableUpdateChecks": "Désactiver les vérifications de mise à jour", - "disableUpdateChecksDesc": "Arrêtez de vérifier les mises à jour", + "disableUpdateChecks": "Désactiver la vérification des mises à jour", + "disableUpdateChecksDesc": "Ne plus vérifier les mises à jour", "totpAuthenticator": "Authentificateur TOTP", - "totpEnabled": "L'authentification à deux facteurs est activée", - "totpDisabled": "Renforcer la sécurité de connexion", + "totpEnabled": "2FA activée", + "totpDisabled": "Ajouter une sécurité de connexion supplémentaire", "disable": "Désactiver", "enable": "Activer", "setupTotp": "Configurer TOTP", "qrCode": "Code QR", - "totpInstructions": "Scannez le code QR ou saisissez le code secret dans votre application d'authentification, puis saisissez le code à 6 chiffres.", + "totpInstructions": "Scannez le code QR ou saisissez le secret dans votre application d'authentification, puis entrez le code à 6 chiffres", "totpCodePlaceholder": "000000", "verify": "Vérifier", - "changePassword": "Changer le mot de passe", + "changePassword": "Modifier le mot de passe", "currentPasswordLabel": "Mot de passe actuel", "currentPasswordPlaceholder": "Mot de passe actuel", "newPasswordLabel": "Nouveau mot de passe", @@ -3214,259 +3410,276 @@ "confirmPasswordPlaceholder": "Confirmer le nouveau mot de passe", "updatePassword": "Mettre à jour le mot de passe", "createApiKeyTitle": "Créer une clé API", - "createApiKeyDescription": "Générez une nouvelle clé API pour l'accès programmatique.", + "createApiKeyDescription": "Générer une nouvelle clé API pour un accès programmatique.", "apiKeyNameLabel": "Nom", - "apiKeyNamePlaceholder": "par exemple, pipeline CI", + "apiKeyNamePlaceholder": "par ex. Pipeline CI", "expiryDateLabel": "Date d'expiration", - "optional": "facultatif", + "optional": "optionnel", "cancel": "Annuler", - "createKey": "Créer une clé", - "apiKeyCount": "{{count}} touches", + "createKey": "Créer la clé", + "apiKeyCount": "{{count}} clés", "newKey": "Nouvelle clé", - "noApiKeys": "Pas encore de clés API.", - "apiKeyActive": "Actif", - "apiKeyUsageHint": "Incluez votre clé dans le", + "noApiKeys": "Aucune clé API pour le moment.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Incluez votre clé dans l’", "apiKeyUsageHintHeader": "en-tête.", - "apiKeyPermissionsHint": "Les clés héritent des permissions de l'utilisateur qui les a créées.", + "apiKeyPermissionsHint": "Les clés héritent des autorisations de l’utilisateur qui les a créées.", + "exportData": "Exporter mes données", + "exportDataDesc": "Téléchargez une sauvegarde de vos hôtes, identifiants et paramètres pour les transférer sur un autre appareil.", + "export": "Exporter", + "exporting": "Exportation...", + "importData": "Importer mes données", + "importDataDesc": "Restaurez vos hôtes, identifiants et paramètres à partir d'un fichier de sauvegarde .sqlite", + "importDataSelected": "Sélectionné : {{name}}", + "selectFile": "Sélectionner un fichier", + "changeFile": "Changement", + "import": "Importer", + "importing": "Importation en cours...", + "exportSuccess": "Données exportées avec succès", + "exportFailed": "L'exportation des données a échoué.", + "importSelectFile": "Veuillez d'abord sélectionner un fichier", + "importCompleted": "Importation terminée : {{total}} éléments importés, {{skipped}} ignorés", + "importFailed": "Échec de l'importation : {{error}}", "roleUser": "Utilisateur", - "authMethodDual": "Double authentification", + "authMethodDual": "Authentification double", "authMethodOidc": "OIDC", "totpSetupFailed": "Échec du démarrage de la configuration TOTP", "totpEnter6Digits": "Saisissez un code à 6 chiffres", - "totpEnabledSuccess": "L'authentification à deux facteurs a été activée.", - "totpInvalidCode": "Code invalide, veuillez réessayer.", + "totpEnabledSuccess": "Authentification à deux facteurs activée", + "totpInvalidCode": "Code invalide, veuillez réessayer", "totpDisableInputRequired": "Saisissez votre code TOTP ou votre mot de passe", - "totpDisabledSuccess": "L'authentification à deux facteurs est désactivée.", - "totpDisableFailed": "Impossible de désactiver l'authentification à deux facteurs.", - "totpDisableTitle": "Désactiver l'authentification à deux facteurs", - "totpDisablePlaceholder": "Saisissez le code TOTP ou le mot de passe", - "totpDisableConfirm": "Désactiver l'authentification à deux facteurs", - "totpContinueVerify": "Continuer la vérification", - "totpVerifyTitle": "Code de vérification", + "totpDisabledSuccess": "Authentification à deux facteurs désactivée", + "totpDisableFailed": "Échec de la désactivation de la 2FA", + "totpDisableTitle": "Désactiver l’A2F", + "totpDisablePlaceholder": "Code TOTP ou mot de passe", + "totpDisableConfirm": "Désactiver l’A2F", + "totpContinueVerify": "Poursuivre la vérification", + "totpVerifyTitle": "Vérifier le code", "totpBackupTitle": "Codes de sauvegarde", "totpDownloadBackup": "Télécharger les codes de sauvegarde", - "passkeys": "Clés d'accès", - "passkeysDesc": "Utilisez vos identifiants WebAuthn/FIDO2 pour vous connecter sans mot de passe.", - "passkeyName": "Nom du mot de passe", + "passkeys": "Clés d’accès", + "passkeysDesc": "Utilisez des identifiants WebAuthn/FIDO2 pour vous connecter sans mot de passe", + "passkeyName": "Nom de la clé d’accès", "passkeyUvPreferred": "Préféré", "passkeyUvRequired": "Requis", - "passkeyUvDiscouraged": "Découragé", - "addPasskey": "Ajouter un code d'accès", - "noPasskeys": "Aucun mot de passe enregistré.", - "passkeyAdded": "Clé d'accès ajoutée", - "passkeyAddFailed": "Impossible d'ajouter le code d'accès", - "passkeyDeleted": "Clé d'accès supprimée", - "passkeyDeleteFailed": "Échec de la suppression du code d'accès", - "done": "Fait", + "passkeyUvDiscouraged": "Déconseillé", + "addPasskey": "Ajouter une clé d’accès", + "noPasskeys": "Aucune clé d’accès enregistrée.", + "passkeyAdded": "Clé d’accès ajoutée", + "passkeyAddFailed": "Échec de l’ajout de la clé d’accès", + "passkeyDeleted": "Clé d’accès supprimée", + "passkeyDeleteFailed": "Échec de la suppression de la clé d'accès", + "done": "Terminé", "secretCopied": "Secret copié dans le presse-papiers", - "apiKeyNameRequired": "Le nom de la clé est requis.", - "apiKeyCreated": "Clé API «{{name}}» créée", + "apiKeyNameRequired": "Le nom de la clé est requis", + "apiKeyCreated": "Clé API \"{{name}}\" créée", "apiKeyCreateFailed": "Échec de la création de la clé API", "apiKeyUser": "Utilisateur", - "apiKeyExpires": "Expire", - "apiKeyRevoked": "Clé API «{{name}}» révoquée", + "apiKeyExpires": "Expiration", + "apiKeyRevoked": "Clé API \"{{name}}\" révoquée", "apiKeyRevokeFailed": "Échec de la révocation de 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.", + "passwordFieldsRequired": "Les mots de passe actuel et nouveau sont requis", + "passwordMismatch": "Les mots de passe ne correspondent pas", + "passwordTooShort": "Le mot de passe doit contenir 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": "Un mot de passe est requis pour supprimer votre compte.", - "deleteFailed": "Impossible de supprimer le compte", - "deleting": "Suppression en cours...", + "deletePasswordRequired": "Le mot de passe est requis pour supprimer votre compte", + "deleteFailed": "Échec de la suppression du compte", + "deleting": "Suppression...", "colorPickerTooltip": "Ouvrir le sélecteur de couleurs", "themeSystem": "Système", - "themeLight": "Lumière", + "themeLight": "Clair", "themeDark": "Sombre", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarisé", - "themeTokyoNight": "Nuit de Tokyo", - "themeOneDark": "Un sombre", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { - "title": "Moniteur Tmux", - "failedToLoadHosts": "Impossible de charger les hôtes", + "title": "Moniteur tmux", + "failedToLoadHosts": "Échec du chargement des hôtes", "failedToLoad": "Échec du chargement des sessions tmux", "tmuxUnavailable": "tmux n'est pas installé sur cet hôte", "noSessions": "Aucune session tmux sur cet hôte", "noHostSelected": "Aucun hôte sélectionné", - "attached": "Ci-joint", + "attached": "Attaché", "detached": "Détaché", "attach": "Attacher", - "editTags": "Modifier les balises", - "tagsHint": "Étiquettes séparées par des virgules (ex. YOLO, labo, formation)", - "tagsSaved": "Étiquettes enregistrées", - "tagsSaveFailed": "Impossible d'enregistrer les balises", - "searchPlaceholder": "Résultats de recherche sur plusieurs sessions...", - "searchResults": "{{count}} correspond", - "searchFailed": "La recherche a échoué.", - "selectPaneHint": "Sélectionnez un volet pour prévisualiser son résultat", + "editTags": "Modifier les tags", + "tagsHint": "Tags séparés par des virgules (ex. : YOLO, lab, training)", + "tagsSaved": "Tags enregistrés", + "tagsSaveFailed": "Échec de l'enregistrement des tags", + "searchPlaceholder": "Rechercher dans les sorties des sessions...", + "searchResults": "{{count}} correspondances", + "searchFailed": "Échec de la recherche", + "selectPaneHint": "Sélectionnez un volet pour prévisualiser sa sortie", "closePreview": "Fermer l'aperçu", "newSession": "Nouvelle session", - "newSessionHint": "Nom de la session (lettres, chiffres, _ @ % + = -)", - "newSessionPlaceholder": "ma session", + "newSessionHint": "Nom de session (lettres, chiffres, _ @ % + = -)", + "newSessionPlaceholder": "my-session", "create": "Créer", - "sessionCreated": "Session \"{{name}}\" créée", + "sessionCreated": "Session « {{name}} » créée", "sessionCreateFailed": "Échec de la création de la session", - "splitRight": "Séparer à droite", - "splitDown": "Diviser", - "splitFailed": "Impossible de séparer la vitre", + "splitRight": "Diviser à droite", + "splitDown": "Diviser en bas", + "splitFailed": "Échec de la division du volet", "newWindow": "Nouvelle fenêtre", - "windowCreateFailed": "Impossible de créer la fenêtre", + "windowCreateFailed": "Échec de la création de la fenêtre", "attachSessionTooltip": "Attacher à {{session}}", - "refresh": "Rafraîchir", + "refresh": "Actualiser", "refreshFailed": "Échec de l'actualisation des sessions", "collapseAll": "Réduire tout", "expandAll": "Développer tout", "moreActions": "Plus d'actions", "sessionStats": "Statistiques de session", - "renameSessionTitle": "Renommer la session \"{{name}}\"", - "rename": "Rebaptiser", - "sessionRenamed": "Session renommée en \"{{name}}\"", - "sessionRenameFailed": "Impossible de renommer la session", - "editTagsTitle": "Modifier les balises pour \"{{name}}\"", - "killPane": "Panneau de sécurité", - "resizeTree": "Faites glisser pour redimensionner — double-cliquez pour réinitialiser", + "renameSessionTitle": "Renommer la session « {{name}} »", + "rename": "Renommer", + "sessionRenamed": "Session renommée en « {{name}} »", + "sessionRenameFailed": "Échec du renommage de la session", + "editTagsTitle": "Modifier les tags pour « {{name}} »", + "killPane": "Fermer le volet", + "resizeTree": "Faire glisser pour redimensionner — double-cliquer pour réinitialiser", "reattach": "Rattacher (corrige un affichage brouillé)", - "killWindow": "fenêtre de mise à mort", - "killWindowTitle": "Fenêtre de suppression {{index}} de \"{{session}}\"?", - "killWindowBody": "Toutes les fenêtres et tous les processus de cette fenêtre seront fermés. La fermeture de la dernière fenêtre met fin à la session.", + "killWindow": "Fermer la fenêtre", + "killWindowTitle": "Fermer la fenêtre {{index}} de « {{session}} » ?", + "killWindowBody": "Tous les volets et processus de cette fenêtre seront terminés. La fermeture de la dernière fenêtre met fin à la session.", "windowKillFailed": "Échec de la fermeture de la fenêtre", "statusActivity": "Activité", - "statusWindows": "Windows", - "statusPanes": "vitres", + "statusWindows": "Fenêtres", + "statusPanes": "Volets", "statusTags": "Étiquettes", - "killPaneTitle": "Volet de suppression {{id}}?", - "killPaneBody": "Le processus exécuté dans ce volet sera arrêté. Fermer le dernier volet entraîne la fermeture de sa fenêtre.", - "paneKillFailed": "Échec de l'extinction de la vitre", - "killSessionTitle": "Session de suppression \"{{name}}\"?", - "killSessionBody": "Toutes les fenêtres et tous les processus en cours dans cette session seront fermés. Cette action est irréversible.", - "kill": "Tuer", - "sessionKilled": "Session \"{{name}}\" tuée", + "killPaneTitle": "Fermer le volet {{id}} ?", + "killPaneBody": "Le processus s’exécutant dans ce volet sera terminé. La fermeture du dernier volet ferme sa fenêtre.", + "paneKillFailed": "Échec de la fermeture du volet", + "killSessionTitle": "Fermer la session « {{name}} » ?", + "killSessionBody": "Toutes les fenêtres et processus en cours dans cette session seront terminés. Cette action est irréversible.", + "kill": "Fermer", + "sessionKilled": "Session « {{name}} » fermée", "sessionKillFailed": "Échec de la fermeture de la session", - "hostUnreachable": "Impossible de se connecter à l'hôte. Vérifiez qu'il est en ligne et accessible.", - "noServer": "Aucun serveur tmux n'est en cours d'exécution sur cet hôte.", - "searchTruncated": "Résultats partiels — la recherche couvre les {{lines}} dernières lignes de chaque volet et au maximum {{panes}} volets.", + "hostUnreachable": "Impossible de se connecter à l’hôte. Vérifiez qu’il est en ligne et accessible.", + "noServer": "Aucun serveur tmux n’est exécuté sur cet hôte.", + "searchTruncated": "Résultats partiels — la recherche couvre les {{lines}} dernières lignes de chaque volet et jusqu’à {{panes}} volets.", "closeSearchResults": "Fermer les résultats de recherche", "retry": "Réessayer", "noHosts": "Aucun hôte SSH disponible", - "noHostsHint": "Activez l'option Moniteur Tmux sur un hôte SSH (Gestionnaire d'hôtes → onglet Terminal) pour surveiller ses sessions tmux.", - "tmuxInstallHint": "Installez-le sur l'hôte avec :", - "attachTooltip": "Ouvrez un terminal pour {{host}}", - "attachTooltipPane": "Ouvrez un terminal pour {{host}} — session tmux {{session}}", - "timeJustNow": "tout à l' heure", - "timeMinutes": "{{count}}il y a m", - "timeHours": "{{count}}il y a h", - "timeDays": "{{count}}il y a j jours" + "noHostsHint": "Activez l’option Tmux Monitor sur un hôte SSH (Gestionnaire d’hôtes → onglet Terminal) pour surveiller ses sessions tmux.", + "tmuxInstallHint": "Installez-le sur l’hôte avec :", + "attachTooltip": "Ouvrir un terminal sur {{host}}", + "attachTooltipPane": "Ouvrir un terminal sur {{host}} — session tmux {{session}}", + "timeJustNow": "à l’instant", + "timeMinutes": "il y a {{count}} min", + "timeHours": "il y a {{count}} h", + "timeDays": "il y a {{count}} j" }, "mobileKeyboard": { - "shift": "Changement", + "shift": "Maj", "ctrl": "Ctrl", "esc": "Échap", - "tab": "Languette", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "Flèche vers le haut", - "arrowDown": "Flèche vers le bas", + "arrowUp": "Flèche haut", + "arrowDown": "Flèche bas", "arrowLeft": "Flèche gauche", "arrowRight": "Flèche droite", - "home": "Maison", + "home": "Début", "end": "Fin", - "pageUp": "Page précédente", - "pageDown": "PageDn", - "delete": "Del", - "editQuickKeys": "Modifier les raccourcis clavier", - "quickKeysTitle": "Raccourcis clavier", - "quickKeysDesc": "Appuyez sur × pour supprimer. Prend en charge jusqu'à 8 caractères.", - "quickKeyPlaceholder": "par exemple sudo ", + "pageUp": "PgUp", + "pageDown": "PgDn", + "delete": "Suppr", + "paste": "Coller", + "editQuickKeys": "Modifier les touches rapides", + "quickKeysTitle": "Touches rapides", + "quickKeysDesc": "Appuyez sur × pour supprimer. Prend en charge jusqu’à 8 caractères.", + "quickKeyPlaceholder": "ex. sudo ", "addQuickKey": "Ajouter", - "removeQuickKey": "Retirer", - "resetDefaults": "Réinitialiser les paramètres par défaut", - "done": "Fait" + "removeQuickKey": "Supprimer", + "resetDefaults": "Rétablir les paramètres par défaut", + "done": "Terminé" }, "serial": { - "title": "En série", + "title": "Série", "portLabel": "Port", - "portPlaceholder": "/dev/ttyUSB0 ou COM3", + "portPlaceholder": "/dev/ttyUSB0 or COM3", "baudRateLabel": "Débit en bauds", "dataBitsLabel": "Données", - "stopBitsLabel": "Arrêt", + "stopBitsLabel": "Stop", "parityLabel": "Parité", - "parityNone": "Aucun", - "parityEven": "Même", - "parityOdd": "Impair", - "connect": "Connexion au port série", + "parityNone": "Aucune", + "parityEven": "Paire", + "parityOdd": "Impaire", + "connect": "Connecter", "disconnect": "Déconnecter", - "refreshPorts": "Ports de rafraîchissement", - "connected": "Connecté à {{path}} à {{baud}} baud", + "refreshPorts": "Actualiser les ports", + "connected": "Connecté à {{path}} à {{baud}} bauds", "disconnected": "Port série déconnecté", - "connectionError": "Impossible d'ouvrir le port série", + "connectionError": "Échec de l'ouverture du port série", "wsError": "Erreur WebSocket", "errorNoServerUrl": "Aucune URL de serveur configurée", - "notSupportedTitle": "Numéro de série non pris en charge", - "notSupported": "Les connexions série nécessitent un navigateur prenant en charge l'API Web Serial (Chrome, Edge ou Firefox 151+), ou l'application de bureau Termix.", - "hideHint": "Vous pouvez masquer l'onglet « Numéro de série » dans Profil utilisateur > Apparence > Barre latérale > Navigation.", - "browserPickerHint": "Cliquez sur « Connecter » et votre navigateur ouvrira une fenêtre de sélection de port pour choisir l’appareil." + "notSupportedTitle": "Port série non pris en charge", + "notSupported": "Les connexions série nécessitent un navigateur prenant en charge l'API Web Serial (Chrome, Edge, ou Firefox 151+), ou l'application de bureau Termix.", + "hideHint": "Vous pouvez masquer l'onglet Série sous Profil utilisateur > Apparence > Barre latérale > Navigation.", + "browserPickerHint": "Cliquez sur Connexion et votre navigateur ouvrira un sélecteur de port pour choisir le périphérique." }, "metricsHistory": { - "title": "Historique des indicateurs", - "historySuffix": "Histoire", - "cpuMemoryDisk": "Processeur / Mémoire / Disque", + "title": "Historique des métriques", + "historySuffix": "Historique", + "cpuMemoryDisk": "CPU / Mémoire / Disque", "network": "Réseau", "download": "RX", "upload": "TX", - "noData": "Aucune donnée historique disponible pour cette période.", - "custom": "Coutume", + "noData": "Aucune donnée d'historique disponible pour cette plage de temps.", + "custom": "Personnalisé", "to": "à", "apply": "Appliquer", - "viewHistory": "Consulter l'historique" + "viewHistory": "Voir l'historique" }, "alerts": { "tabFirings": "Alertes", "tabRules": "Règles", - "tabChannels": "Chaînes", - "noFirings": "Aucune alerte non prise en compte", + "tabChannels": "Canaux", + "noFirings": "Aucune alerte non acquittée", "noRules": "Aucune règle d'alerte configurée", "noChannels": "Aucun canal de notification configuré", "rulesDesc": "Les règles d'alerte déclenchent des notifications", "channelsDesc": "Où les notifications d'alerte sont envoyées", - "acknowledge": "Reconnaître", - "ackAll": "Tout", - "allAcknowledged": "Toutes les alertes ont été prises en compte.", - "ackFailed": "L'alerte n'a pas été prise en compte.", - "ackAllFailed": "N'a pas accusé réception de toutes les alertes", - "showAcknowledged": "Afficher tout", - "hideAcknowledged": "Masquer Acquis", - "addChannel": "Ajouter une chaîne", - "editChannel": "Modifier la chaîne", + "acknowledge": "Acquitter", + "ackAll": "Tout acquitter", + "allAcknowledged": "Toutes les alertes acquittées", + "ackFailed": "Échec de l'acquittement de l'alerte", + "ackAllFailed": "Échec de l'acquittement de toutes les alertes", + "showAcknowledged": "Tout afficher", + "hideAcknowledged": "Masquer les acquittées", + "addChannel": "Ajouter un canal", + "editChannel": "Modifier le canal", "channelName": "Nom", - "channelType": "Taper", - "channelNameRequired": "Le nom est requis.", + "channelType": "Type", + "channelNameRequired": "Le nom est obligatoire", "webhookUrl": "URL", - "webhookUrlRequired": "L'URL du webhook est requise.", - "webhookDesc": "Envoyez une charge utile JSON à cette URL à chaque déclenchement d'alerte.", + "webhookUrlRequired": "L'URL du webhook est obligatoire", + "webhookDesc": "Envoie une charge utile JSON en POST à cette URL à chaque déclenchement d'alerte", "ntfyServer": "URL du serveur", "ntfyTopic": "Sujet", "ntfyTopicRequired": "Le sujet est obligatoire", "ntfyToken": "Jeton d'accès (facultatif)", - "channelSaveFailed": "Impossible d'enregistrer la chaîne", - "test": "Test", + "channelSaveFailed": "Échec de l'enregistrement du canal", + "test": "Tester", "testSent": "Notification de test envoyée", - "testFailed": "La notification de test a échoué", + "testFailed": "Échec de la notification de test", "addRule": "Ajouter une règle d'alerte", "editRule": "Modifier la règle d'alerte", "ruleName": "Nom de la règle", - "ruleNameRequired": "Le nom est requis.", - "triggerType": "Déclenchement", + "ruleNameRequired": "Le nom est obligatoire", + "triggerType": "Déclencheur", "thresholdValue": "Seuil (%)", - "durationSeconds": "Durée (secondes, 0 = tir immédiat)", - "cooldownMinutes": "Temps de récupération (minutes)", + "durationSeconds": "Durée (secondes, 0 = déclenchement immédiat)", + "cooldownMinutes": "Temporisation (minutes)", "channels": "Canaux de notification", - "noChannelsHint": "Ajoutez d'abord les chaînes dans l'onglet Chaînes.", - "ruleSaveFailed": "Échec de la sauvegarde de la règle" + "noChannelsHint": "Ajoutez d'abord des canaux dans l'onglet Canaux", + "ruleSaveFailed": "Échec de l'enregistrement de la règle" } } diff --git a/src/ui/locales/translated/he_IL.json b/src/ui/locales/translated/he_IL.json index 0db4e466..24bfc958 100644 --- a/src/ui/locales/translated/he_IL.json +++ b/src/ui/locales/translated/he_IL.json @@ -389,6 +389,45 @@ "noSavedServers": "אין שרתים שמורים", "removeServer": "לְהַסִיר" }, + "migrationNotice": { + "title": "שולחן העבודה של Termix פועל כעת באופן עצמאי", + "body1": "אפליקציה זו הייתה מתחברת ישירות לשרת Termix שלך. היא עובדה מחדש כדי לפעול באופן עצמאי לחלוטין, תוך אחסון מארחים ופרטי גישה מקומיים כך שהיא תמשיך לעבוד במצב לא מקוון. סנכרון דו-כיווני עם שרת Termix המתארח באופן עצמאי הוא כעת אופציונלי.", + "body2": "המארחים והאישורים הקיימים שלך עדיין נמצאים ב- {{url}}. הפעל את הסנכרון מרחוק והתחבר מחדש לשרת זה כדי להחזיר אותם ולשמור על שניהם מסונכרנים מעתה והלאה.", + "dismiss": "לא עכשיו", + "setUpSync": "הגדר סנכרון מרחוק" + }, + "remoteSync": { + "title": "סנכרון מרחוק", + "description": "ניתן לחבר אפליקציית שולחן עבודה זו לשרת Termix המתארח באופן עצמאי כדי לסנכרן את המארחים, האישורים והקטעים שלך בין מכשירים. האפליקציה תמיד פועלת באופן מלא במצב לא מקוון, בין אם אתה מתחבר ובין אם לא.", + "notConnected": "לא מחובר", + "connected": "מְחוּבָּר", + "connectedTo": "מחובר אל {{url}}", + "lastSynced": "סנכרון אחרון {{time}}", + "neverSynced": "מעולם לא סנכרן", + "syncError": "שגיאת סינכרון: {{message}}", + "needsReauth": "פג תוקף הכניסה", + "connectButton": "התחברות לשרת", + "disconnectButton": "לְנַתֵק", + "syncNowButton": "סנכרן עכשיו", + "syncing": "מסנכרן...", + "serverUrl": "כתובת URL של השרת", + "enterServerUrl": "אנא הזן כתובת URL של שרת", + "mustIncludeProtocol": "כתובת השרת חייבת להתחיל ב-http:// או https://", + "connectionTestFailed": "לא ניתן היה להגיע לשרת Termix בכתובת URL זו", + "allowInvalidCertificate": "אפשר אישור לא חוקי", + "allowInvalidCertificateDesc": "השתמש רק עבור שרתים מהימנים המתארחים בעצמם עם אישורי חתימה עצמית או אישורי כתובת IP.", + "savedServers": "שרתים שמורים", + "removeServer": "לְהַסִיר", + "continueButton": "לְהַמשִׁיך", + "cancelButton": "לְבַטֵל", + "signInTitle": "היכנס אל {{url}}", + "originTitle": "מקור החיבור", + "originDescription": "בחר כברירת מחדל מהיכן חיבורי SSH מגיעים. ניתן לעקוף זאת לפי מארח.", + "originLocal": "מכשיר זה (רשת מקומית)", + "originRemote": "שרת מרוחק", + "bannerReconnect": "התחבר מחדש", + "bannerMessage": "סנכרון מרחוק דורש אימות מחדש" + }, "versionCheck": { "error": "שגיאת בדיקת גרסה", "checkFailed": "נכשל בבדיקת עדכונים", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "עקיפת שם משתמש של פרטי כניסה", "overrideCredentialUsernameDesc": "השתמש בשם המשתמש שצוין למעלה במקום שם המשתמש של פרטי האישור", "oidcUsernameHint": "השתמש ב- $oidc.preferred_username כדי להחליף את שם המשתמש שלך ב-OIDC.", + "tailscaleUsernameHint": "זה חייב להיות משתמש יוניקס שזהות Tailscale שלך מוענקת לו ב-SSH ACL של tailnet, לאו דווקא root.", "jumpHostChain": "שרשרת מארח קפיצה", "portKnocking": "פורט נוקינג", "addKnock": "הוסף יציאה", @@ -650,6 +690,11 @@ "delayAfterMs": "עיכוב לאחר (מילישניות)", "useSocks5Proxy": "השתמש בפרוקסי SOCKS5", "useSocks5ProxyDesc": "ניתוב חיבור דרך שרת פרוקסי", + "connectionOrigin": "מקור החיבור", + "connectionOriginDesc": "מאיפה מגיע חיבור ה-SSH של מארח זה. עוקף את הגדרת ברירת המחדל הגלובלית של אפליקציית שולחן העבודה.", + "connectionOriginDefault": "השתמש בברירת מחדל", + "connectionOriginLocal": "מכשיר זה (רשת מקומית)", + "connectionOriginRemote": "שרת מרוחק", "proxyHost": "מארח פרוקסי", "proxyPort": "יציאת פרוקסי", "proxyUsername": "שם משתמש פרוקסי", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "ציאן בהיר", "customThemeBrightWhite": "לבן בהיר", "customThemeResetTooltip": "איפוס להגדרות ברירת מחדל", + "savedThemesLabel": "ערכות נושא שמורות", + "saveAsGlobalTheme": "שמור כערכת נושא גלובלית", + "saveGlobalThemeNamePrompt": "הזן שם עבור ערכת נושא זו", + "saveGlobalThemeSuccess": "ערכת הנושא נשמרה", + "saveGlobalThemeError": "שמירת ערכת הנושא נכשלה", + "applyGlobalThemeTooltip": "החל ערכת נושא זו", + "deleteGlobalThemeTooltip": "מחק את ערכת הנושא הזו", + "noSavedThemes": "עדיין אין ערכות נושא שנשמרו", "syntaxHighlightingLabel": "הדגשת תחביר", "syntaxHighlightingDesc": "צבע את פלט הטרמינל (שגיאות, נתיבים, כתובות IP, חותמות זמן)", "syntaxHighlightingCategories": "הדגש קטגוריות", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "הפעל או צורף אוטומטית לסשן tmux", "enableSessionLogging": "רישום מושבים", "enableSessionLoggingDesc": "הקלט את פלט הפעלת הטרמינל לצורך סקירה מאוחרת יותר", + "allowSessionSharing": "אפשר שיתוף סשנים", + "allowSessionSharingDesc": "אפשר שיתוף של מפגשים חיים במארח זה באמצעות קישור או עם משתמשים אחרים", "enableCommandHistory": "היסטוריית פקודות", "enableCommandHistoryDesc": "פקודות הקלטה פועלות בטרמינל זה לצורך היסטוריה והשלמה אוטומטית", "linkClickBehaviorLabel": "התנהגות לחיצה על קישור", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} מיובא", "proxmoxResultUpdated": "{{count}} updated", "proxmoxResultFailed": "{{count}} נכשל", + "proxmoxResultSkippedNoIp": "{{count}} דילג (לא נמצאה כתובת IP)", "proxmoxImportComplete": "ייבוא Proxmox הושלם: {{summary}}", "proxmoxDiscoveryFailed": "הגילוי נכשל", "proxmoxImportFailed": "הייבוא נכשל", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "שמירת האישורים נכשלה", "credentialNameRequired": "אנא הזן שם עבור האישור", "credentialAuthRequired": "הוסף סיסמה, מפתח SSH או שניהם", + "createCredentialFromHostBtn": "צור אישור", + "createCredentialFromHostTitle": "צור אישור מהמארח", + "createCredentialFromHostDesc": "צור ערך אישורים רב פעמי וניתן לשיתוף, הממולא מראש בשם המשתמש, הסיסמה ו/או מפתח ה-SSH הנוכחיים של מארח זה.", "backToHosts": "חזרה למארחים", "backToCredentials": "חזרה לאישורים", "pinned": "מוצמד", @@ -1000,6 +1059,9 @@ "folderNestingHint": "השתמשו ב-/ כדי להפריד רמות וליצור תיקיות מקוננות.", "folderColor": "צֶבַע", "folderIcon": "אייקון", + "folderCredential": "תְעוּדָה", + "folderCredentialNone": "לא הוקצו אישורים", + "folderCredentialHint": "מארחים בתיקייה זו המשתמשים באימות \"אישורים מאוחסנים\" מבלי שבחרו אישור משלהם, יירשו את האישור הזה.", "folderPreview": "תצוגה מקדימה", "folderNameFallback": "תיקייה ללא שם", "createFolderButton": "צור תיקייה", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "תגיות", "shareHost": "שתף מארח", "shareHostTitle": "שתף: {{name}}", + "shareFolder": "שיתוף תיקייה", + "shareFolderTitle": "שיתוף תיקייה: {{name}}", + "folderSharedSuccessfully": "מארחים משותפים {{count}} בתיקייה", + "failedToShareFolder": "שיתוף התיקייה נכשל", "sharing": { "loadError": "טעינת נתוני השיתוף נכשלה. אנא נסה שוב.", "shareWithSection": "שתף עם", @@ -1221,6 +1287,7 @@ "shareWithCount": "שתף ({{count}})", "currentAccess": "גישה נוכחית", "noAccessEntries": "מארח זה עדיין לא שותף", + "folderShareSummary": "מארחים משותפים {{shared}} של {{total}} בתיקייה זו", "grantedBy": "הוענק על ידי", "expires": "פג תוקף", "expired": "פג תוקף", @@ -1242,6 +1309,8 @@ "authMethod": "שיטת אימות", "authTypeDirect": "יָשִׁיר", "authTypeCredential": "תְעוּדָה", + "authTypeNone": "אַף לֹא אֶחָד", + "authTypeNoneDesc": "לא נשמרים פרטי גישה. תתבקש להזין שם משתמש וסיסמה בכל פעם שתתחבר; הם לא נשמרים.", "selectCredential": "בחר אישור...", "connectionSettings": "הגדרות חיבור", "displaySettings": "הגדרות תצוגה", @@ -1259,6 +1328,7 @@ "rdpPort": "יציאת RDP", "username": "שם משתמש", "password": "סִיסמָה", + "passwordSaved": "הסיסמה נשמרה, הקלד כדי לשנות", "domain": "תְחוּם", "securityMode": "מצב אבטחה", "colorDepth": "עומק צבע", @@ -1424,7 +1494,60 @@ "expiresIn": "פג תוקף בעוד {{duration}}", "search": "חיפוש חיבורים...", "noSearchResults": "אין קשרים התואמים את החיפוש שלך", - "rename": "שינוי שם של סשן" + "rename": "שינוי שם של סשן", + "sectionSharedWithMe": "שיתף איתי", + "sharedBy": "שותף על ידי {{username}}", + "join": "לְהִצְטַרֵף", + "sharedSessionLabel": "{{hostName}} (שותף)" + }, + "sessionSharing": { + "guestView": { + "loading": "מתחבר לסשן משותף...", + "linkInvalid": "קישור השיתוף הזה אינו תקין, פג תוקף או בוטל", + "rateLimited": "יותר מדי ניסיונות, אנא נסה שוב בקרוב", + "sessionEnded": "מושב זה הסתיים", + "readOnlyBadge": "צפייה בלבד" + }, + "modalTitle": "שיתוף סשן", + "shareButton": "לַחֲלוֹק", + "notReadyToShare": "הסשן עדיין לא מוכן לשיתוף", + "modeTab": { + "link": "לְקַשֵׁר", + "user": "מִשׁתַמֵשׁ" + }, + "linkModeDescription": "כל מי שיש לו קישור זה יכול להצטרף, אין צורך בחשבון.", + "userModeDescription": "שתף עם משתמש ספציפי שכבר יש לו גישה למארח זה. אם עדיין אין לו גישה, שתף איתו תחילה את המארח או השתמש בקישור במקום זאת. לאחר השיתוף, הסשן מופיע בכרטיסייה \"חיבורים\" שלו.", + "permissionLevel": { + "label": "רמת הרשאה", + "readOnly": "לקריאה בלבד", + "readOnlyDescription": "יכול לצפות בפגישה בשידור חי אך לא להקליד או לתקשר.", + "readWrite": "קריאה-כתיבה", + "readWriteDescription": "יכול להקליד ולקיים אינטראקציה עם הסשן בדיוק כמו הבעלים." + }, + "expiryLabel": "תוקף הקישור", + "createLinkButton": "צור קישור", + "createShareButton": "שתף עם המשתמש", + "searchUsersPlaceholder": "חיפוש משתמשים...", + "noUsersFound": "לא נמצאו משתמשים", + "linkCreated": "קישור השיתוף נוצר", + "linkCopied": "קישור שהועתק ללוח", + "copyLink": "העתקת קישור", + "shareCreated": "הסשן שותף. זה יופיע בלשונית החיבורים שלהם.", + "shareFailed": "יצירת השיתוף נכשלה", + "userLacksHostAccess": "למשתמש הזה עדיין אין גישה למארח הזה. שתף את המארח איתו תחילה, או השתמש בקישור במקום זאת.", + "activeShares": "מניות פעילות", + "noActiveShares": "אין שיתופים פעילים עבור סשן זה", + "revoke": "לְבַטֵל", + "revokeConfirmTitle": "לבטל את השיתוף הזה?", + "revokeConfirmDescription": "כל מי שישתמש בשיתוף הזה יאבד גישה באופן מיידי.", + "revoked": "השיתוף בוטל", + "revokeFailed": "ביטול השיתוף נכשל", + "joinCount": "{{count}} join", + "joinCount_other": "{{count}} מצטרף", + "expiresAt": "פג תוקף {{date}}", + "linkShareBadge": "לְקַשֵׁר", + "userShareBadge": "משתמש: {{username}}", + "loadSharesFailed": "נכשלה טעינת השיתופים הפעילים" }, "guacamole": { "connecting": "מתחבר לסשן {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "התחבר מחדש", "retry": "נסה שוב", "guacdUnavailable": "שירות שולחן עבודה מרוחק (guacd) אינו זמין. אנא ודא ש-guacd פועל, נגיש ומוגדר כראוי בהגדרות המנהל.", + "credentialPromptTitle": "הזן אישורי RDP", + "credentialPromptDescription": "מארח זה מוגדר לבקש אישורים בעת התחברות. הם משמשים עבור הפעלה זו בלבד ואינם נשמרים.", + "connect": "לְחַבֵּר", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "חיבור מחדש של הפעלת המושב", "collapse": "כיווץ סרגל הכלים", "expand": "הרחב את סרגל הכלים", - "dragHandle": "גרור כדי למקם מחדש" + "dragHandle": "גרור כדי למקם מחדש", + "switchToTrackpad": "מעבר למצב משטח עקיבה (גרור כדי להזיז את הסמן, הקש כדי ללחוץ)", + "switchToTouch": "מעבר למצב מגע (הקש ישירות במקום שברצונך ללחוץ)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "נדרש אימות דו-שלבי", "totpCodeLabel": "קוד אימות", "totpVerify": "לְאַמֵת", + "mfaPromptRequired": "נדרש אימות", + "mfaPushRequired": "נדרש אימות דחיפה", + "mfaMenuPlaceholder": "הזן את תגובתך", + "mfaWaitingApproval": "ממתין לאישור במכשיר שלך...", + "mfaSendRequest": "שלח בקשה", "warpgateAuthRequired": "נדרש אימות Warpgate", "warpgateSecurityKey": "מפתח אבטחה", "warpgateAuthUrl": "כתובת URL לאימות", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "קצב מוגבל: יותר מדי ניסיונות אימות. אנא נסה שוב מאוחר יותר.", "resetCodeRateLimitedWithTime": "קצב מוגבל: יותר מדי ניסיונות אימות. אנא המתן {{time}} שניות לפני שתנסה שוב.", "authTokenSaveFailed": "נכשלה שמירת אסימון האימות", - "failedToLoadServer": "טעינת השרת נכשלה" + "failedToLoadServer": "טעינת השרת נכשלה", + "remoteServerRequired": "נדרש שרת מרוחק. יש להתחבר לשרת מרוחק בהגדרות כדי להשתמש בסוג חיבור זה." }, "messages": { "registrationDisabled": "רישום חשבון חדש מושבת כעת על ידי מנהל. אנא התחבר או צור קשר עם מנהל.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "שמירת הגדרות SSL נכשלה", "sslRequiresDomain": "נדרשים דומיין וכתובת אימייל", "sslInfoNote": "לאחר הנפקת האישור, הפעל את SSL במשתני הסביבה שלך (ENABLE_SSL=true) והפעל מחדש את Termix.", + "sslManualOption": "מדריך (העלאת תעודה)", + "sslManualCert": "תעודה (PEM)", + "sslManualCertPlaceholder": "-----התחלת תעודה-----", + "sslManualKey": "מפתח פרטי (PEM)", + "sslManualKeyPlaceholder": "-----התחל מפתח פרטי-----", + "sslManualDesc": "הדבק את האישור והמפתח הפרטי הקיימים שלך, כולל שרשרת מלאה אם נדרשת על ידי רשות האישורים שלך.", + "sslManualUpload": "העלאה והתקנה של אישור", + "sslManualUploadLoading": "מעלה תעודה...", + "sslManualUploadSuccess": "התעודה הועלתה והותקנה בהצלחה", + "sslManualUploadFailed": "העלאת האישור נכשלה", + "sslManualRequiresFields": "נדרשים תעודה ומפתח פרטי", "auditLogTotal": "{{total}} סך כל הרשומות", "auditLogEmpty": "לא נמצאו ערכי יומן ביקורת", "auditLogSuccess": "הַצלָחָה", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "היסטוריית פקודות", "commandHistoryEnabledDesc": "אפשר רישום היסטוריית פקודות. כאשר האפשרות מושבתת, ההיסטוריה לא נשמרת ללא קשר להגדרות של כל מחשב מארח.", "updateCommandHistoryFailed": "נכשל עדכון הגדרת היסטוריית הפקודות", + "analyticsEnabled": "שתף סטטיסטיקות שימוש אנונימיות", + "analyticsEnabledDesc": "שולח ספירה יומית אנונימית של משתמשים, מארחים ושימוש בתכונות כדי לסייע בשיפור Termix. לא כלולים נתונים אישיים או פרטי חיבור.", + "updateAnalyticsFailed": "נכשל עדכון הגדרת הניתוח", + "sessionSharingGloballyEnabled": "אפשר שיתוף סשנים", + "sessionSharingGloballyEnabledDesc": "מאפשר שיתוף של הפעלות מסוף חי, RDP, VNC ו-Telnet בכל המופעים. מבטל כל אפשרות שיתוף לפי מארח כאשר היא מושבתת.", + "updateSessionSharingFailed": "נכשל עדכון הגדרת שיתוף הסשן", "sessionTimeout": "פסק זמן של סשן", "hours": "שעות", "sessionTimeoutRange": "מינימום שעה · מקסימום 720 שעות", @@ -3119,6 +3270,47 @@ "executionSuccess": "הַצלָחָה", "executionFailed": "נִכשָׁל" }, + "keybindings": { + "title": "קיצורי מקלדת", + "description": "התאם אישית קיצורי דרך להעתקה, הדבקה ובקרה של הטרמינל, או חבר מקשים לשליחת טקסט או להפעלת קטע קוד.", + "defaultsHeading": "קיצורי דרך מובנים", + "customHeading": "קיצורי דרך מותאמים אישית", + "addBinding": "הוסף קיצור דרך", + "addBindingTitle": "הוסף קיצור דרך", + "editBindingTitle": "עריכת קיצור דרך", + "loading": "טְעִינָה...", + "noCustomBindings": "עדיין אין קיצורי דרך מותאמים אישית", + "defaultBadge": "בְּרִירַת מֶחדָל", + "customizedBadge": "מותאם אישית", + "customize": "התאמה אישית", + "resetToDefault": "איפוס לברירת מחדל", + "resetAllToDefaults": "איפוס הכל להגדרות ברירת המחדל", + "close": "לִסְגוֹר", + "cancel": "לְבַטֵל", + "saveBinding": "שמירת קיצור דרך", + "saveError": "שמירת קיצורי מקלדת נכשלה", + "pressKeysToRecord": "צירוף מקשים", + "pressKeysPlaceholder": "לחיצה ולחיצה על המקשים", + "recording": "לחץ על מקשים...", + "comboRequiredError": "לחץ תחילה על צירוף מקשים", + "textRequiredError": "הזן את הטקסט לשליחה", + "controlCodeRequiredError": "הזן אות אחת עבור קוד הבקרה", + "snippetRequiredError": "בחר קטע", + "conflictWarning": "צירוף זה כבר קשור ל- {{combo}}. שמירה תגרום לשניהם לפעול.", + "actionLabel": "פְּעוּלָה", + "actionCopy": "העתקת הבחירה", + "actionPaste": "לְהַדבִּיק", + "actionSendControlCode": "שלח אות Ctrl+אות", + "actionSendText": "שלח טקסט", + "actionRunSnippet": "הפעלת קטע קוד קיים", + "controlCodeLabel": "מִכְתָב", + "textLabel": "טקסט לשליחה", + "appendEnterLabel": "לחץ על Enter לאחר השליחה", + "snippetLabel": "קטע", + "selectSnippetPlaceholder": "בחר קטע", + "orphanedSnippetWarning": "קטע לא נמצא", + "clipboardPermissionNote": "ייתכן שתבקש הרשאה ללוח בעת השימוש הראשון בדפדפנים מסוימים." + }, "userProfile": { "donateTitle": "לשמור על טרמיקס בחיים", "donateDescription": "Termix הוא שירות חינמי, מתארח בעצמו, ונבנה על ידי מספר אנשים בלבד בזמנם הפנוי. אם זה חסך לכם זמן או כסף, תרומת קריפטו תעזור לשמור עליו.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "הוֹפָעָה", "sectionSecurity": "בִּטָחוֹן", "sectionApiKeys": "מפתחות API", + "sectionData": "נְתוּנִים", "sectionC2sTunnels": "מנהרות C2S", "usernameLabel": "שם משתמש", "roleLabel": "תַפְקִיד", @@ -3162,6 +3355,9 @@ "settingsTerminal": "מָסוֹף", "commandAutocomplete": "השלמה אוטומטית של הפקודה", "commandAutocompleteDesc": "הצג השלמה אוטומטית בעת הקלדה", + "keyboardShortcuts": "קיצורי מקלדת", + "keyboardShortcutsDescription": "התאמה אישית של קיצורי דרך להעתקה, הדבקה ופקודות במסוף", + "manageShortcuts": "לְנַהֵל", "terminalLinkBehavior": "לחיצה על קישור הטרמינל", "terminalLinkBehaviorDesc": "אופן הפעולה המוגדר כברירת מחדל בעת לחיצה על קישורים בטרמינל", "historyTracking": "מעקב אחר היסטוריה", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "כלול את המפתח שלך ב-", "apiKeyUsageHintHeader": "כּוֹתֶרֶת.", "apiKeyPermissionsHint": "מפתחות יורשים את ההרשאות של המשתמש היוצר.", + "exportData": "ייצוא הנתונים שלי", + "exportDataDesc": "הורד גיבוי של המארחים, האישורים וההגדרות שלך כדי להעביר למכשיר אחר", + "export": "יְצוּא", + "exporting": "ייצוא...", + "importData": "ייבא את הנתונים שלי", + "importDataDesc": "שחזר את המארחים, האישורים וההגדרות שלך מקובץ גיבוי .sqlite", + "importDataSelected": "נבחר: {{name}}", + "selectFile": "בחר קובץ", + "changeFile": "לְשַׁנוֹת", + "import": "יְבוּא", + "importing": "מייבא...", + "exportSuccess": "הנתונים יוצאו בהצלחה", + "exportFailed": "ייצוא הנתונים נכשל", + "importSelectFile": "אנא בחר קובץ תחילה", + "importCompleted": "ייבוא הושלם: {{total}} פריטים יובאו, {{skipped}} דילג", + "importFailed": "ייבוא נכשל: {{error}}", "roleUser": "מִשׁתַמֵשׁ", "authMethodDual": "אימות כפול", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "עמוד למעלה", "pageDown": "PgDn", "delete": "דל", + "paste": "לְהַדבִּיק", "editQuickKeys": "עריכת מקשי מהירים", "quickKeysTitle": "מקשי מהירים", "quickKeysDesc": "הקש על × כדי להסיר. תומך בעד 8 תווים.", diff --git a/src/ui/locales/translated/hi_IN.json b/src/ui/locales/translated/hi_IN.json index 4d88b186..af81e18e 100644 --- a/src/ui/locales/translated/hi_IN.json +++ b/src/ui/locales/translated/hi_IN.json @@ -1,70 +1,70 @@ { "termixId": { - "title": "टर्मिक्स आईडी", - "loadFailed": "टर्मिक्स आईडी लोड करने में विफल", - "claimTitle": "अपना टर्मिक्स आईडी प्राप्त करें", - "claimIntro": "एक विशिष्ट हैंडल चुनें। आपकी SSH सार्वजनिक कुंजियाँ एक सार्वजनिक URL पर प्रकाशित की जाएंगी जिसे आप किसी भी सर्वर की authorized_keys फ़ाइल में जोड़ सकते हैं।", - "handleLabel": "सँभालना", - "handlePlaceholder": "एलिस", - "checking": "जाँच रहा है…", + "title": "Termix ID", + "loadFailed": "Termix ID लोड करने में विफल", + "claimTitle": "अपना Termix ID क्लेम करें", + "claimIntro": "एक अनूठा हैंडल चुनें। आपकी SSH सार्वजनिक कुंजियाँ एक सार्वजनिक URL पर प्रकाशित की जाएंगी जिसे आप किसी भी सर्वर की authorized_keys फ़ाइल में जोड़ सकते हैं।", + "handleLabel": "हैंडल", + "handlePlaceholder": "alice", + "checking": "जाँच हो रही है…", "available": "उपलब्ध", - "taken": "पहले से ही लिया जा चुका है", - "invalidHandle": "केवल छोटे अक्षर, संख्याएँ, - और _", + "taken": "पहले से उपयोग में", + "invalidHandle": "केवल छोटे अक्षर, अंक, - और _", "descriptionLabel": "विवरण (वैकल्पिक)", - "descriptionPlaceholder": "लैपटॉप और फ़ोन की कुंजी (कार्य के लिए)", - "create": "टर्मिक्स आईडी बनाएं", - "created": "टर्मिक्स आईडी बनाई गई", - "createFailed": "टर्मिक्स आईडी बनाने में विफल", - "deleteConfirm": "क्या आप अपनी टर्मिक्स आईडी और सभी प्रकाशित कुंजियाँ हटाना चाहते हैं? आपके द्वारा प्रोविज़न किए गए सर्वर कुंजियों को तब तक सुरक्षित रखेंगे जब तक आप उन्हें मैन्युअल रूप से नहीं हटा देते।", - "deleted": "टर्मिक्स आईडी हटा दी गई", - "deleteFailed": "टर्मिक्स आईडी को हटाने में विफल रहा", - "copyFailed": "कॉपी विफल", - "resolverUrlLabel": "सार्वजनिक रिजॉल्वर यूआरएल", - "provisionLabel": "एक सर्वर उपलब्ध कराएं", - "publishTitle": "एक सार्वजनिक कुंजी प्रकाशित करें", - "generate": "उत्पन्न", - "generateTooltip": "Ed25519 कुंजी युग्म जनरेट करें — यह सार्वजनिक कुंजी प्रकाशित करता है और निजी कुंजी को आपके डिवाइस पर डाउनलोड करता है।", - "saveToVault": "क्रेडेंशियल्स में सहेजें", + "descriptionPlaceholder": "कार्य लैपटॉप और फ़ोन कुंजियाँ", + "create": "Termix ID बनाएँ", + "created": "Termix ID बन गई", + "createFailed": "Termix ID बनाने में विफल", + "deleteConfirm": "अपनी Termix ID और सभी प्रकाशित कुंजियाँ हटाएँ? जो सर्वर आपने प्रोविज़न किए हैं वे कुंजियाँ तब तक रखेंगे जब तक आप उन्हें मैन्युअल रूप से नहीं हटाते।", + "deleted": "Termix ID हटाई गई", + "deleteFailed": "Termix ID हटाने में विफल", + "copyFailed": "कॉपी करने में विफल", + "resolverUrlLabel": "सार्वजनिक रिज़ॉल्वर URL", + "provisionLabel": "सर्वर प्रोविज़न करें", + "publishTitle": "सार्वजनिक कुंजी प्रकाशित करें", + "generate": "जनरेट करें", + "generateTooltip": "Ed25519 कुंजी युग्म जनरेट करता है — सार्वजनिक कुंजी प्रकाशित करता है और निजी कुंजी आपके डिवाइस पर डाउनलोड करता है", + "saveToVault": "क्रेडेंशियल में सहेजें", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "लेबल (वैकल्पिक)", - "add": "जोड़ना", + "add": "जोड़ें", "importFromCredential": "या संग्रहीत क्रेडेंशियल से आयात करें", "keyPublished": "कुंजी प्रकाशित", "addKeyFailed": "कुंजी जोड़ने में विफल", - "generatedSaved": "कुंजी युग्म तैयार हो गया है और आपके क्रेडेंशियल वॉल्ट में सहेज लिया गया है। निजी कुंजी भी डाउनलोड हो गई है।", - "generatedOnly": "कुंजी युग्म उत्पन्न हो गया — निजी कुंजी डाउनलोड हो गई (केवल एक बार दिखाई गई)।", - "generateFailed": "कुंजी उत्पन्न करने में विफल", - "imported": "क्रेडेंशियल से आयातित कुंजी", + "generatedSaved": "कुंजी युग्म जनरेट हुआ और आपके क्रेडेंशियल वॉल्ट में सहेजा गया। निजी कुंजी भी डाउनलोड की गई।", + "generatedOnly": "कुंजी युग्म जनरेट हुआ — निजी कुंजी डाउनलोड की गई (केवल एक बार दिखाई गई)।", + "generateFailed": "कुंजी जनरेट करने में विफल", + "imported": "क्रेडेंशियल से कुंजी आयात की गई", "importFailed": "कुंजी आयात करने में विफल", - "noKeys": "अभी तक कोई सार्वजनिक कुंजी प्रकाशित नहीं की गई है।", + "noKeys": "अभी तक कोई सार्वजनिक कुंजी प्रकाशित नहीं हुई है।", "keysTitle": "प्रकाशित कुंजियाँ", "published": "प्रकाशित", - "hidden": "छिपा हुआ", - "keyRemoved": "चाबी निकाल ली गई", - "removeKeyFailed": "कुंजी निकालने में विफल", - "updateKeyFailed": "कुंजी को अपडेट करने में विफल", + "hidden": "छिपी हुई", + "keyRemoved": "कुंजी हटाई गई", + "removeKeyFailed": "कुंजी हटाने में विफल", + "updateKeyFailed": "कुंजी अपडेट करने में विफल", "fromVault": "क्रेडेंशियल वॉल्ट से", - "linkedToTermixId": "टर्मिक्स आईडी के माध्यम से प्रकाशित", + "linkedToTermixId": "Termix ID के माध्यम से प्रकाशित", "selectCredential": "एक क्रेडेंशियल चुनें...", - "import": "आयात", - "caTitle": "प्रमाणपत्र, प्राधिकारी", - "caIntro": "किसी सर्वर पर इस CA पर भरोसा करें और यह आपके द्वारा हस्ताक्षरित किसी भी प्रमाणपत्र को स्वीकार कर लेगा। सभी प्रमाणपत्रों को एक साथ रद्द करने के लिए रोटेट करें; प्रमाणपत्र स्वयं भी समाप्त हो जाते हैं।", - "caEnable": "सीए को सक्षम करें", - "caEnabled": "सीए सक्षम", - "caCreateFailed": "सीए को सक्षम करने में विफल", - "caPublicKeyLabel": "सीए सार्वजनिक कुंजी", + "import": "आयात करें", + "caTitle": "प्रमाणपत्र प्राधिकरण", + "caIntro": "सर्वर पर इस CA पर भरोसा करें और यह आपके द्वारा हस्ताक्षरित किसी भी प्रमाणपत्र को स्वीकार करता है। एक ही बार में सब कुछ निरस्त करने के लिए CA को रोटेट करें; प्रमाणपत्र अपने आप भी समाप्त हो जाते हैं।", + "caEnable": "CA सक्षम करें", + "caEnabled": "CA सक्षम", + "caCreateFailed": "CA सक्षम करने में विफल", + "caPublicKeyLabel": "CA सार्वजनिक कुंजी", "caTrustLabel": "सर्वर पर भरोसा करें (रूट के रूप में चलाएँ)", - "caRotate": "घुमाएँ", - "caRotateConfirm": "क्या सीए को स्थानांतरित करना है? इसके द्वारा हस्ताक्षरित प्रत्येक प्रमाणपत्र अमान्य हो जाएगा और उसे पुनः जारी करना होगा।", - "caRotated": "सीए का तबादला हो गया — पिछले प्रमाणपत्र रद्द कर दिए गए", - "caRotateFailed": "सीए को घुमाने में विफल", - "caDelete": "CA को हटाएँ", - "caDeleteConfirm": "क्या सर्टिफिकेट अथॉरिटी को हटाना है?", - "caDeleted": "सीए हटा दिया गया", - "caDeleteFailed": "सीए को हटाने में विफल", - "caValidityLabel": "प्रमाणपत्र की वैधता (दिनों में)", + "caRotate": "रोटेट करें", + "caRotateConfirm": "CA को रोटेट करें? इसके द्वारा हस्ताक्षरित प्रत्येक प्रमाणपत्र स्वीकार करना बंद कर दिया जाएगा और उन्हें फिर से जारी करना होगा।", + "caRotated": "CA रोटेट किया गया — पिछले प्रमाणपत्र निरस्त", + "caRotateFailed": "CA रोटेट करने में विफल", + "caDelete": "CA हटाएँ", + "caDeleteConfirm": "प्रमाणपत्र प्राधिकरण हटाएँ?", + "caDeleted": "CA हटाया गया", + "caDeleteFailed": "CA हटाने में विफल", + "caValidityLabel": "प्रमाणपत्र वैधता (दिनों में)", "issueCert": "प्रमाणपत्र", - "issueCertTooltip": "इस कुंजी के लिए अपने CA द्वारा हस्ताक्षरित एक SSH प्रमाणपत्र जारी करें।", + "issueCertTooltip": "इस कुंजी के लिए एक SSH प्रमाणपत्र जारी करें, आपके CA द्वारा हस्ताक्षरित", "certIssued": "प्रमाणपत्र जारी और डाउनलोड किया गया", "certIssueFailed": "प्रमाणपत्र जारी करने में विफल" }, @@ -72,268 +72,268 @@ "folders": "फ़ोल्डर", "folder": "फ़ोल्डर", "password": "पासवर्ड", - "key": "चाबी", - "sshPrivateKey": "एसएसएच निजी कुंजी", + "key": "कुंजी", + "sshPrivateKey": "SSH निजी कुंजी", "upload": "अपलोड करें", "keyPassword": "कुंजी पासवर्ड", - "sshKey": "एसएसएच कुंजी", + "sshKey": "SSH कुंजी", "uploadPrivateKeyFile": "निजी कुंजी फ़ाइल अपलोड करें", - "searchCredentials": "खोज क्रेडेंशियल...", + "searchCredentials": "क्रेडेंशियल खोजें...", "addCredential": "क्रेडेंशियल जोड़ें", - "caCertificate": "सीए प्रमाणपत्र (-cert.pub)", - "caCertificateDescription": "वैकल्पिक: सीए द्वारा हस्ताक्षरित प्रमाणपत्र फ़ाइल अपलोड करें या पेस्ट करें (उदाहरण के लिए id_ed25519-cert.pub)। यह तब आवश्यक है जब आपका एसएसएच सर्वर प्रमाणपत्र-आधारित प्रमाणीकरण का उपयोग करता हो।", + "caCertificate": "CA प्रमाणपत्र (-cert.pub)", + "caCertificateDescription": "वैकल्पिक: CA-हस्ताक्षरित प्रमाणपत्र फ़ाइल अपलोड करें या पेस्ट करें (उदा. id_ed25519-cert.pub). आवश्यक है जब आपका SSH सर्वर प्रमाणपत्र-आधारित प्राधिकरण का उपयोग करता है।", "uploadCertFile": "-cert.pub फ़ाइल अपलोड करें", - "clearCert": "स्पष्ट", + "clearCert": "साफ़ करें", "certLoaded": "प्रमाणपत्र लोड हो गया", - "certPublicKeyLabel": "सीए प्रमाणपत्र", - "certTypeLabel": "सर्टिफिकेट टाइप", + "certPublicKeyLabel": "CA प्रमाणपत्र", + "certTypeLabel": "प्रमाणपत्र प्रकार", "pasteOrUploadCert": "-cert.pub प्रमाणपत्र पेस्ट करें या अपलोड करें...", - "hasCaCert": "सीए प्रमाणपत्र प्राप्त है", - "noCaCert": "सीए प्रमाणपत्र की आवश्यकता नहीं है", - "noPublicKeyAvailable": "कोई सार्वजनिक कुंजी उपलब्ध नहीं है। कृपया पहले क्रेडेंशियल एडिटर खोलें।", - "deployCommandCopied": "डिप्लॉयमेंट कमांड कॉपी हो गई", + "hasCaCert": "CA प्रमाणपत्र है", + "noCaCert": "कोई CA प्रमाणपत्र नहीं", + "noPublicKeyAvailable": "कोई सार्वजनिक कुंजी उपलब्ध नहीं है। पहले क्रेडेंशियल संपादक खोलें।", + "deployCommandCopied": "डिप्लॉय कमांड कॉपी किया गया", "sortCredentials": "क्रेडेंशियल क्रमबद्ध करें", - "sortDefault": "डिफ़ॉल्ट ऑर्डर", - "sortNameAsc": "नाम (अ से जेड तक)", + "sortDefault": "डिफ़ॉल्ट क्रम", + "sortNameAsc": "नाम (A → Z)", "sortNameDesc": "नाम (Z → A)", "sortUsernameAsc": "उपयोगकर्ता नाम (A → Z)", "sortUsernameDesc": "उपयोगकर्ता नाम (Z → A)", - "filterCredentials": "फ़िल्टर क्रेडेंशियल", + "filterCredentials": "क्रेडेंशियल फ़िल्टर करें", "filterClearAll": "फ़िल्टर साफ़ करें", "filterTypeGroup": "प्रकार", "filterTypePassword": "पासवर्ड", - "filterTypeKey": "एसएसएच कुंजी", + "filterTypeKey": "SSH कुंजी", "filterTagsGroup": "टैग" }, "homepage": { - "title": "मुखपृष्ठ", + "title": "होमपेज", "addWidget": "विजेट जोड़ें", "editWidget": "विजेट संपादित करें", - "deleteWidget": "मिटाना", + "deleteWidget": "डिलीट करें", "widgetTypes": "विजेट प्रकार", "serviceLink": "सेवा लिंक", "folder": "फ़ोल्डर", "clock": "घड़ी", "notes": "नोट्स", - "hostStatus": "मेज़बान स्थिति", + "hostStatus": "होस्ट स्थिति", "bookmarkList": "बुकमार्क", "zoomIn": "ज़ूम इन", "zoomOut": "ज़ूम आउट", - "resetView": "फिर से देख्ना", - "lockLayout": "लॉक लेआउट", + "resetView": "दृश्य रीसेट करें", + "lockLayout": "लेआउट लॉक करें", "unlockLayout": "लेआउट अनलॉक करें", - "noWidgets": "अपना पहला विजेट जोड़ने के लिए राइट-क्लिक करें या + पर क्लिक करें।", + "noWidgets": "अपना पहला विजेट जोड़ने के लिए राइट-क्लिक करें या + पर क्लिक करें", "openFullView": "पूर्ण दृश्य खोलें", - "previewTitle": "होमपेज का पूर्वावलोकन", - "cancel": "रद्द करना", - "save": "बचाना", + "previewTitle": "होमपेज पूर्वावलोकन", + "cancel": "रद्द करें", + "save": "सहेजें", "title_label": "शीर्षक", - "widgetTitlePlaceholder": "विजेट का शीर्षक (वैकल्पिक)", - "url": "यूआरएल", - "imageUrl": "कस्टम छवि यूआरएल", - "imageUrlHint": "साइट के फ़ेविकॉन का स्वचालित रूप से उपयोग करने के लिए इसे खाली छोड़ दें।", - "showImage": "छवि दिखाएँ", + "widgetTitlePlaceholder": "विजेट शीर्षक (वैकल्पिक)", + "url": "URL", + "imageUrl": "कस्टम इमेज URL", + "imageUrlHint": "साइट फ़ेविकॉन का स्वचालित रूप से उपयोग करने के लिए खाली छोड़ें", + "showImage": "इमेज दिखाएँ", "description": "विवरण", "color": "रंग", "icon": "आइकन", "expanded": "डिफ़ॉल्ट रूप से विस्तारित", "timezone": "समय क्षेत्र", "showSeconds": "सेकंड दिखाएँ", - "format12h": "12 घंटे", - "format24h": "24 घंटे", + "format12h": "12-घंटे", + "format24h": "24-घंटे", "content": "सामग्री", - "backgroundColor": "पृष्ठभूमि का रंग", - "host": "मेज़बान", - "showMetrics": "मैट्रिक्स दिखाएँ", + "backgroundColor": "पृष्ठभूमि रंग", + "host": "होस्ट", + "showMetrics": "मेट्रिक्स दिखाएं", "links": "लिंक", "addLink": "लिंक जोड़ें", "linkLabel": "लेबल", - "linkUrl": "यूआरएल", - "removeLink": "निकालना", + "linkUrl": "URL", + "removeLink": "हटाएं", "categoryLinks": "लिंक", "categoryInfo": "जानकारी", - "categorySystem": "प्रणाली", - "widgetServiceLinkDesc": "एक क्लिक करने योग्य टाइल जो सेवा URL से लिंक करती है", - "widgetFolderDesc": "संबंधित विजेट्स को समूहित करने के लिए एक कंटेनर", + "categorySystem": "सिस्टम", + "widgetServiceLinkDesc": "एक क्लिक करने योग्य टाइल जो सेवा URL से जुड़ता है", + "widgetFolderDesc": "संबंधित विजेट को समूहित करने का कंटेनर", "widgetClockDesc": "कॉन्फ़िगर करने योग्य समय क्षेत्र वाली लाइव घड़ी", - "widgetNotesDesc": "एक मार्कडाउन नोट्स विजेट", + "widgetNotesDesc": "मार्कडाउन नोट्स विजेट", "widgetHostStatusDesc": "SSH होस्ट के लिए लाइव CPU, मेमोरी और डिस्क दिखाता है", "widgetBookmarkListDesc": "त्वरित लिंक की सूची", - "copyLink": "लिंक की प्रतिलिपि करें", + "copyLink": "लिंक कॉपी करें", "linkCopied": "लिंक कॉपी हो गया!", - "location": "जगह", + "location": "स्थान", "temperatureUnit": "तापमान इकाई", - "showForecast": "3 दिन का पूर्वानुमान दिखाएँ", - "scrolling": "स्क्रॉल करने की अनुमति दें", - "feedUrl": "फ़ीड यूआरएल", + "showForecast": "3-दिन का पूर्वानुमान दिखाएं", + "scrolling": "स्क्रॉलिंग की अनुमति दें", + "feedUrl": "फ़ीड URL", "maxItems": "अधिकतम आइटम", - "showDescription": "विवरण दिखाएँ", + "showDescription": "विवरण दिखाएं", "widgetWeatherName": "मौसम", - "widgetWeatherDesc": "किसी भी स्थान के लिए लाइव मौसम की जानकारी", + "widgetWeatherDesc": "किसी भी स्थान का लाइव मौसम", "widgetIframeName": "iFrame एम्बेड", - "widgetIframeDesc": "किसी भी URL को iframe में एम्बेड करें", - "widgetRssName": "आरएसएस फीड", - "widgetRssDesc": "RSS या Atom फ़ीड से आइटम प्रदर्शित करें", - "dragToFolder": "विजेट्स को यहाँ खींचें या जोड़ने के लिए + का उपयोग करें", - "showDisk": "डिस्क उपयोग दिखाएँ", - "addToFolder": "विजेट को फ़ोल्डर में जोड़ें", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "metricsNotAvailable": "मेट्रिक्स उपलब्ध नहीं हैं", - "selectHost": "एक होस्ट का चयन करें...", + "widgetIframeDesc": "किसी भी URL को आइफ्रेम में एम्बेड करें", + "widgetRssName": "RSS फ़ीड", + "widgetRssDesc": "RSS या एटम फ़ीड से आइटम प्रदर्शित करें", + "dragToFolder": "विजेट यहाँ खींचें या + का उपयोग करके जोड़ें", + "showDisk": "डिस्क उपयोग दिखाएं", + "addToFolder": "फ़ोल्डर में विजेट जोड़ें", + "noHostSelected": "कोई होस्ट चयनित नहीं", + "metricsNotAvailable": "मेट्रिक्स उपलब्ध नहीं", + "selectHost": "होस्ट चुनें...", "displayedMetrics": "प्रदर्शित मेट्रिक्स", "metricCpu": "CPU", - "metricMemory": "याद", + "metricMemory": "मेमोरी", "metricDisk": "डिस्क", "metricUptime": "अपटाइम", - "metricSystem": "प्रणाली", + "metricSystem": "सिस्टम", "metricOs": "ओएस", - "metricKernel": "गुठली", - "metricHostname": "होस्ट का नाम", + "metricKernel": "कर्नेल", + "metricHostname": "होस्टनेम", "metricNetwork": "नेटवर्क", - "metricProcesses": "प्रक्रियाओं", + "metricProcesses": "प्रोसेस", "metricProcessesTotal": "कुल", - "metricProcessesRunning": "दौड़ना", + "metricProcessesRunning": "चालू", "categoryMonitoring": "निगरानी", "loading": "लोड हो रहा है...", - "noData": "कोई डेटा मौजूद नहीं", - "allClear": "सब साफ", - "acknowledgeAlert": "स्वीकार करना", - "noPingUrls": "कोई यूआरएल कॉन्फ़िगर नहीं किया गया है", + "noData": "कोई डेटा उपलब्ध नहीं", + "allClear": "सब ठीक है", + "acknowledgeAlert": "स्वीकार करें", + "noPingUrls": "कोई URL कॉन्फ़िगर नहीं किया गया", "pingLabel": "लेबल", - "addPingUrl": "यूआरएल जोड़ें", - "showLatency": "विलंबता दिखाएँ", - "refreshInterval": "ताजा अंतराल", + "addPingUrl": "URL जोड़ें", + "showLatency": "लेटेंसी दिखाएं", + "refreshInterval": "रीफ़्रेश अंतराल", "seconds": "सेकंड", - "filterActivityTypes": "फ़िल्टर प्रकार", - "filterTypesHint": "सभी दिखाने के लिए इसे खाली छोड़ दें", - "showTimestamp": "टाइमस्टैम्प दिखाएँ", + "filterActivityTypes": "प्रकार फ़िल्टर करें", + "filterTypesHint": "सभी दिखाने के लिए खाली छोड़ें", + "showTimestamp": "टाइमस्टैम्प दिखाएं", "uptimeUnavailable": "अपटाइम अनुपलब्ध", "uptimeLabel": "अपटाइम", "overviewVersion": "संस्करण", - "overviewUpdate": "अप टू डेट", - "overviewUpdateAvailable": "उपलब्ध अद्यतन", + "overviewUpdate": "अद्यतित", + "overviewUpdateAvailable": "अपडेट उपलब्ध है", "overviewDatabase": "डेटाबेस", "overviewUptime": "अपटाइम", - "noHosts": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है", - "hostGridHosts": "मेजबान", - "hostGridAllHint": "सभी होस्ट दिखाने के लिए इसे खाली छोड़ दें", + "noHosts": "कोई होस्ट कॉन्फ़िगर नहीं किए गए", + "hostGridHosts": "होस्ट", + "hostGridAllHint": "सभी होस्ट दिखाने के लिए खाली छोड़ें", "columns": "कॉलम", - "showIp": "आईपी पता दिखाएँ", - "connectionType": "रिश्ते का प्रकार", + "showIp": "IP पता दिखाएं", + "connectionType": "कनेक्शन प्रकार", "layout": "लेआउट", - "showStatus": "स्थिति दिखाएँ", - "showHostName": "शो के होस्ट का नाम", - "noDockerActivity": "कोई डॉकर गतिविधि नहीं", + "showStatus": "स्थिति दिखाएं", + "showHostName": "होस्ट का नाम दिखाएं", + "noDockerActivity": "कोई Docker गतिविधि नहीं", "noActivity": "कोई गतिविधि नहीं", - "showAcknowledged": "स्वीकृति दिखाएँ", - "showCurrentValue": "वर्तमान मान दिखाएँ", + "showAcknowledged": "स्वीकृत दिखाएं", + "showCurrentValue": "वर्तमान मान दिखाएं", "chartMetric": "मीट्रिक", - "metricRange": "श्रेणी", - "widgetMetricsChartName": "मैट्रिक्स चार्ट", - "widgetMetricsChartDesc": "किसी होस्ट के लिए ऐतिहासिक CPU, मेमोरी, डिस्क या नेटवर्क चार्ट", + "metricRange": "सीमा", + "widgetMetricsChartName": "मीट्रिक चार्ट", + "widgetMetricsChartDesc": "होस्ट के लिए ऐतिहासिक CPU, मेमोरी, डिस्क या नेटवर्क चार्ट", "widgetHostGridName": "होस्ट ग्रिड", - "widgetHostGridDesc": "एसएसएच होस्ट की स्थिति का ग्रिड दृश्य", - "widgetAlertFeedName": "अलर्ट फ़ीड", - "widgetAlertFeedDesc": "स्वीकृति समर्थन के साथ लाइव अलर्ट फायरिंग", + "widgetHostGridDesc": "SSH होस्ट की स्थितियों का ग्रिड दृश्य", + "widgetAlertFeedName": "अलर्ट फीड", + "widgetAlertFeedDesc": "स्वीकृति सहित लाइव अलर्ट फायरिंग", "widgetPingStatusName": "पिंग स्थिति", - "widgetPingStatusDesc": "एक या अधिक यूआरएल के लिए HTTP पिंग स्थिति", + "widgetPingStatusDesc": "एक या अधिक URLs के लिए HTTP पिंग स्थिति", "widgetRecentActivityName": "हाल की गतिविधि", - "widgetRecentActivityDesc": "टर्मिक्स की हालिया गतिविधि का स्क्रॉल करने योग्य फ़ीड", - "widgetTermixUptimeName": "टर्मिक्स अपटाइम", - "widgetTermixUptimeDesc": "टर्मिक्स सर्वर के लिए लाइव अपटाइम काउंटर", - "widgetSystemOverviewName": "तंत्र अवलोकन", - "widgetSystemOverviewDesc": "टर्मिक्स संस्करण, डेटाबेस की स्थिति और अपटाइम की संक्षिप्त जानकारी", - "widgetSshQuickConnectName": "एसएसएच क्विक कनेक्ट", - "widgetSshQuickConnectDesc": "SSH सेशन खोलने के लिए एक-क्लिक बटन", - "widgetDockerActivityName": "डॉकर गतिविधि", - "widgetDockerActivityDesc": "सभी होस्टों पर हाल ही में हुई डॉकर कंटेनर घटनाएं", + "widgetRecentActivityDesc": "Termix की हाल की गतिविधियों का स्क्रॉल करने योग्य फीड", + "widgetTermixUptimeName": "Termix अपटाइम", + "widgetTermixUptimeDesc": "Termix सर्वर के लिए लाइव अपटाइम काउंटर", + "widgetSystemOverviewName": "सिस्टम अवलोकन", + "widgetSystemOverviewDesc": "Termix संस्करण, डेटाबेस स्वास्थ्य और अपटाइम एक नज़र में", + "widgetSshQuickConnectName": "SSH त्वरित कनेक्ट", + "widgetSshQuickConnectDesc": "SSH सत्र खोलने के लिए एक-क्लिक बटन", + "widgetDockerActivityName": "Docker गतिविधि", + "widgetDockerActivityDesc": "सभी होस्ट पर हाल की Docker कंटेनर घटनाएं", "widgetCalendarName": "कैलेंडर", - "widgetCalendarDesc": "आज को हाइलाइट करते हुए एक मासिक कैलेंडर", - "widgetCountdownName": "उलटी गिनती", - "widgetCountdownDesc": "किसी निर्धारित तिथि तक उलटी गिनती का टाइमर", + "widgetCalendarDesc": "आज की तारीख हाइलाइट के साथ मासिक कैलेंडर", + "widgetCountdownName": "काउंटडाउन", + "widgetCountdownDesc": "लक्ष्य तिथि के लिए काउंटडाउन टाइमर", "widgetSearchBarName": "खोज पट्टी", "widgetSearchBarDesc": "त्वरित वेब खोज विजेट", "widgetTextBannerName": "टेक्स्ट बैनर", - "widgetTextBannerDesc": "कैनवास के लिए एक बोल्ड लेबल या सेक्शन हेडर", + "widgetTextBannerDesc": "कैनवास के लिए एक बोल्ड लेबल या अनुभाग शीर्षक", "widgetImageWidgetName": "छवि", - "widgetImageWidgetDesc": "किसी URL से छवि प्रदर्शित करें", + "widgetImageWidgetDesc": "URL से एक छवि प्रदर्शित करें", "widgetMarkdownNotesName": "मार्कडाउन नोट्स", - "widgetMarkdownNotesDesc": "इनलाइन मार्कडाउन रेंडरिंग के साथ रिच नोट्स", - "widgetCustomApiName": "कस्टम एपीआई", - "widgetCustomApiDesc": "किसी भी JSON API से डेटा प्राप्त करें और प्रदर्शित करें।", + "widgetMarkdownNotesDesc": "इनलाइन मार्कडाउन रेंडरिंग के साथ समृद्ध नोट्स", + "widgetCustomApiName": "कस्टम API", + "widgetCustomApiDesc": "किसी भी JSON API से डेटा प्राप्त करें और प्रदर्शित करें", "widgetServiceGridName": "सेवा ग्रिड", - "widgetServiceGridDesc": "सेवा टाइल लिंक का एक विन्यास योग्य ग्रिड", + "widgetServiceGridDesc": "सेवा टाइल लिंक का एक कॉन्फ़िगर करने योग्य ग्रिड", "widgetDashboardLinksName": "डैशबोर्ड लिंक", "widgetDashboardLinksDesc": "डैशबोर्ड से अपने कॉन्फ़िगर किए गए सेवा लिंक प्रदर्शित करें", "widgetSearchLinksName": "खोज शॉर्टकट", "widgetSearchLinksDesc": "इनलाइन इनपुट के साथ त्वरित खोज शॉर्टकट बटन", "widgetLinkTreeName": "लिंक ट्री", "widgetLinkTreeDesc": "शीर्षकों के साथ लिंक के समूहीकृत अनुभाग", - "calMon": "एमओ", - "calTue": "तू", - "calWed": "हम", - "calThu": "वां", - "calFri": "फादर", - "calSat": "एसए", - "calSun": "र", - "startOnMonday": "सप्ताह की शुरुआत सोमवार से करें", - "countdownNoDate": "कोई लक्ष्य तिथि निर्धारित नहीं की गई है", - "countdownPast": "कार्यक्रम समाप्त हो चुका है", + "calMon": "सोम", + "calTue": "मंगल", + "calWed": "बुध", + "calThu": "गुरु", + "calFri": "शुक्र", + "calSat": "शनि", + "calSun": "रवि", + "startOnMonday": "सप्ताह सोमवार से शुरू करें", + "countdownNoDate": "कोई लक्ष्य तिथि निर्धारित नहीं", + "countdownPast": "ईवेंट बीत चुका है", "countdownDays": "दिन", "countdownHours": "घंटे", - "countdownMinutes": "मिन", + "countdownMinutes": "मिनट", "countdownSeconds": "सेकंड", "countdownLabel": "लेबल", - "countdownLabelPlaceholder": "उदाहरण के लिए, लॉन्च दिवस", - "countdownShowDays": "शो के दिन", - "countdownShowHours": "शो के खुलने का समय", - "targetDate": "नियोजित तारीख", + "countdownLabelPlaceholder": "उदा. लॉन्च दिवस", + "countdownShowDays": "दिन दिखाएं", + "countdownShowHours": "घंटे दिखाएं", + "targetDate": "लक्ष्य तिथि", "searchEngine": "खोज इंजन", - "customSearchUrl": "कस्टम खोज यूआरएल", - "searchPlaceholder": "खोज...", - "searchGo": "जाना", + "customSearchUrl": "कस्टम खोज URL", + "searchPlaceholder": "खोजें...", + "searchGo": "जाएं", "searchPlaceholderLabel": "प्लेसहोल्डर टेक्स्ट", - "searchPlaceholderHint": "खोज इनपुट के अंदर प्रदर्शित पाठ", - "openInNewTab": "वेब टेब में खोलें", - "searchQueryPlaceholder": "प्रश्न दर्ज करें...", - "noSearchShortcuts": "कोई शॉर्टकट कॉन्फ़िगर नहीं किया गया है", - "addSearchShortcut": "छोटा रास्ता जोडें", + "searchPlaceholderHint": "खोज इनपुट के अंदर दिखाया जाने वाला पाठ", + "openInNewTab": "नए टैब में खोलें", + "searchQueryPlaceholder": "क्वेरी दर्ज करें...", + "noSearchShortcuts": "कोई शॉर्टकट कॉन्फ़िगर नहीं है", + "addSearchShortcut": "शॉर्टकट जोड़ें", "fontSize": "फ़ॉन्ट आकार", - "textAlign": "टेक्स्ट अलाइन", - "fontWeight": "फ़ॉन्ट वज़न", - "clearColor": "स्पष्ट रंग", - "imageFit": "छवि फिट", - "imageLinkUrl": "लिंक यूआरएल", - "noImage": "कोई छवि यूआरएल सेट नहीं किया गया", + "textAlign": "पाठ संरेखण", + "fontWeight": "फ़ॉन्ट मोटाई", + "clearColor": "रंग साफ़ करें", + "imageFit": "छवि फ़िट", + "imageLinkUrl": "लिंक URL", + "noImage": "कोई छवि URL सेट नहीं है", "altText": "Alt टेक्स्ट", - "altTextPlaceholder": "छवि का वर्णन करें", + "altTextPlaceholder": "छवि का विवरण दें", "renderMarkdown": "मार्कडाउन रेंडर करें", - "displayMode": "डिस्प्ले मोड", - "displayField": "फ़ील्ड प्रदर्शित करें", + "displayMode": "प्रदर्शन मोड", + "displayField": "प्रदर्शन फ़ील्ड", "jsonPath": "JSON पथ", "customApiLabel": "लेबल", - "customApiLabelPlaceholder": "उदाहरण के लिए तापमान", + "customApiLabelPlaceholder": "उदा. तापमान", "customApiUnit": "इकाई", - "customApiNoUrl": "कोई एपीआई यूआरएल कॉन्फ़िगर नहीं किया गया है", - "customApiError": "डेटा प्राप्त करने में विफल रहा", + "customApiNoUrl": "कोई API URL कॉन्फ़िगर नहीं है", + "customApiError": "फ़ेच करने में विफल", "customApiNotArray": "प्रतिक्रिया एक ऐरे नहीं है", "addService": "सेवा जोड़ें", - "showLabels": "लेबल दिखाएँ", + "showLabels": "लेबल दिखाएं", "iconSize": "आइकन का आकार", - "noDashboardLinks": "कोई डैशबोर्ड लिंक कॉन्फ़िगर नहीं किया गया है", + "noDashboardLinks": "कोई डैशबोर्ड लिंक कॉन्फ़िगर नहीं किया गया", "noLimit": "कोई सीमा नहीं", "sectionHeading": "अनुभाग शीर्षक", "addSection": "अनुभाग जोड़ें", - "compactMode": "कॉम्पैक्ट मोड", - "showDetailed": "विस्तृत जानकारी दिखाएँ", + "compactMode": "संक्षिप्त मोड", + "showDetailed": "विस्तृत दिखाएं", "selectHosts": "होस्ट चुनें", - "allHosts": "सभी मेजबान", + "allHosts": "सभी होस्ट", "listLayout": "सूची", "gridLayout": "ग्रिड", "terminal": "टर्मिनल", "files": "फ़ाइलें", - "docker": "डाक में काम करनेवाला मज़दूर", - "range15m": "15 मिनटों", + "docker": "Docker", + "range15m": "15 मिनट", "range1h": "1 घंटा", "range6h": "6 घंटे", "range24h": "24 घंटे", @@ -342,409 +342,454 @@ "severityFilter": "गंभीरता फ़िल्टर", "filterAll": "सभी", "accentColor": "एक्सेंट रंग", - "widgetSshTerminalName": "एसएसएच टर्मिनल", - "widgetSshTerminalDesc": "एक कॉन्फ़िगर किए गए होस्ट से जुड़ा एक इनलाइन SSH टर्मिनल", - "sshTerminalNoHost": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है", - "sshTerminalConnect": "जोड़ना", - "sshTerminalAutoConnect": "लोड होने पर स्वतः कनेक्ट हो जाता है", + "widgetSshTerminalName": "SSH टर्मिनल", + "widgetSshTerminalDesc": "कॉन्फ़िगर किए गए होस्ट से जुड़ा एक इनलाइन SSH टर्मिनल", + "sshTerminalNoHost": "कोई होस्ट कॉन्फ़िगर नहीं किया गया", + "sshTerminalConnect": "कनेक्ट करें", + "sshTerminalAutoConnect": "लोड पर स्वतः कनेक्ट", "widgetQuickConnectName": "त्वरित कनेक्ट", - "widgetQuickConnectDesc": "आपके सभी होस्ट पर किसी भी कनेक्शन प्रकार के लिए एक-क्लिक लॉन्च बटन", + "widgetQuickConnectDesc": "आपके होस्ट पर किसी भी कनेक्शन प्रकार के लिए एक-क्लिक लॉन्च बटन", "connectionTypes": "कनेक्शन प्रकार", "connType_terminal": "टर्मिनल", - "connType_files": "फ़ाइल मैनेजर", - "connType_docker": "डाक में काम करनेवाला मज़दूर", - "connType_tunnel": "सुरंग", + "connType_files": "फ़ाइल प्रबंधक", + "connType_docker": "Docker", + "connType_tunnel": "टनल", "connType_host-metrics": "होस्ट मेट्रिक्स", - "connType_rdp": "आरडीपी", - "connType_vnc": "वीएनसी", - "connType_telnet": "टेलनेट", - "widgetFileManagerName": "फ़ाइल मैनेजर", + "connType_rdp": "RDP", + "connType_vnc": "VNC", + "connType_telnet": "Telnet", + "widgetFileManagerName": "फ़ाइल प्रबंधक", "widgetFileManagerDesc": "कॉन्फ़िगर किए गए होस्ट के लिए एम्बेडेड SFTP फ़ाइल प्रबंधक", - "widgetDockerName": "डॉकर मैनेजर", - "widgetDockerDesc": "कॉन्फ़िगर किए गए होस्ट के लिए एम्बेडेड डॉकर कंटेनर मैनेजर", - "widgetTunnelName": "सुरंग प्रबंधक", - "widgetTunnelDesc": "कॉन्फ़िगर किए गए होस्ट के लिए एम्बेडेड SSH टनल मैनेजर", - "widgetNoHostSelected": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है" + "widgetDockerName": "Docker प्रबंधक", + "widgetDockerDesc": "कॉन्फ़िगर किए गए होस्ट के लिए एम्बेडेड Docker कंटेनर प्रबंधक", + "widgetTunnelName": "टनल प्रबंधक", + "widgetTunnelDesc": "कॉन्फ़िगर किए गए होस्ट के लिए एम्बेडेड SSH टनल प्रबंधक", + "widgetNoHostSelected": "कोई होस्ट कॉन्फ़िगर नहीं किया गया" }, "serverConfig": { "title": "सर्वर कॉन्फ़िगरेशन", - "description": "अपने बैकएंड सेवाओं से कनेक्ट करने के लिए टर्मिक्स सर्वर यूआरएल को कॉन्फ़िगर करें।", - "serverUrl": "सर्वर यूआरएल", - "enterServerUrl": "कृपया सर्वर URL दर्ज करें", + "description": "अपनी बैकएंड सेवाओं से कनेक्ट करने के लिए Termix सर्वर URL कॉन्फ़िगर करें", + "serverUrl": "सर्वर URL", + "enterServerUrl": "कृपया एक सर्वर URL दर्ज करें", "saveFailed": "कॉन्फ़िगरेशन सहेजने में विफल", "saveError": "कॉन्फ़िगरेशन सहेजने में त्रुटि", - "saving": "सहेजा जा रहा है...", + "saving": "सहेज रहा है...", "saveConfig": "कॉन्फ़िगरेशन सहेजें", - "helpText": "वह URL दर्ज करें जहां आपका टर्मिक्स सर्वर चल रहा है (उदाहरण के लिए, http://localhost:30001 या https://your-server.com)", + "helpText": "URL दर्ज करें जहाँ आपका Termix सर्वर चल रहा है (उदा., http://localhost:30001 या https://your-server.com)", "changeServer": "सर्वर बदलें", - "mustIncludeProtocol": "सर्वर URL की शुरुआत http:// या https:// से होनी चाहिए।", + "mustIncludeProtocol": "सर्वर URL http:// या https:// से शुरू होना चाहिए", "allowInvalidCertificate": "अमान्य प्रमाणपत्र की अनुमति दें", - "allowInvalidCertificateDesc": "इसका उपयोग केवल विश्वसनीय सेल्फ-होस्टेड सर्वरों के लिए करें जिनमें सेल्फ-साइन किए गए या आईपी-एड्रेस प्रमाणपत्र हों।", + "allowInvalidCertificateDesc": "केवल स्व-हस्ताक्षरित या IP-पता प्रमाणपत्र वाले विश्वसनीय स्व-होस्टेड सर्वर के लिए उपयोग करें।", "useEmbedded": "स्थानीय सर्वर का उपयोग करें", - "embeddedDesc": "Termix को बिल्ट-इन लोकल सर्वर के साथ चलाएं (रिमोट सर्वर की आवश्यकता नहीं है)", + "embeddedDesc": "Termix को अंतर्निहित स्थानीय सर्वर के साथ चलाएँ (कोई दूरस्थ सर्वर आवश्यक नहीं)", "embeddedConnecting": "स्थानीय सर्वर से कनेक्ट हो रहा है...", - "embeddedNotReady": "स्थानीय सर्वर अभी तैयार नहीं है। कृपया थोड़ी देर प्रतीक्षा करें और पुनः प्रयास करें।", + "embeddedNotReady": "स्थानीय सर्वर अभी तक तैयार नहीं है। कृपया कुछ क्षण प्रतीक्षा करें और पुनः प्रयास करें।", "localServer": "स्थानीय सर्वर", "savedServers": "सहेजे गए सर्वर", "noSavedServers": "कोई सहेजे गए सर्वर नहीं", - "removeServer": "निकालना" + "removeServer": "हटाएँ" + }, + "migrationNotice": { + "title": "टर्मिक्स डेस्कटॉप अब स्टैंडअलोन मोड में चलता है।", + "body1": "यह ऐप पहले सीधे आपके टर्मिक्स सर्वर से कनेक्ट होता था। अब इसे पूरी तरह से स्वतंत्र रूप से चलने के लिए संशोधित किया गया है, जिसमें होस्ट और क्रेडेंशियल स्थानीय रूप से संग्रहीत होते हैं ताकि यह ऑफ़लाइन भी काम करता रहे। स्व-होस्टेड टर्मिक्स सर्वर के साथ दो-तरफ़ा सिंक अब वैकल्पिक है।", + "body2": "आपके मौजूदा होस्ट और क्रेडेंशियल अभी भी {{url}}पर हैं। रिमोट सिंक चालू करें और उन्हें वापस लाने और भविष्य में दोनों को सिंक में रखने के लिए उस सर्वर से पुनः कनेक्ट करें।", + "dismiss": "अभी नहीं", + "setUpSync": "रिमोट सिंक सेट अप करें" + }, + "remoteSync": { + "title": "रिमोट सिंक", + "description": "आप चाहें तो इस डेस्कटॉप ऐप को अपने होस्ट, क्रेडेंशियल और स्निपेट्स को सभी डिवाइसों में सिंक करने के लिए सेल्फ-होस्टेड टर्मिक्स सर्वर से कनेक्ट कर सकते हैं। ऐप हमेशा पूरी तरह से ऑफलाइन काम करता है, चाहे आप कनेक्ट करें या न करें।", + "notConnected": "जुड़े नहीं हैं", + "connected": "जुड़े हुए", + "connectedTo": "{{url}} से जुड़ा हुआ", + "lastSynced": "अंतिम बार सिंक्रनाइज़ किया गया {{time}}", + "neverSynced": "कभी सिंक्रनाइज़ नहीं हुआ", + "syncError": "सिंक त्रुटि: {{message}}", + "needsReauth": "लॉग-इन की समय सीमा समाप्त हो गई है", + "connectButton": "सर्वर से कनेक्ट करें", + "disconnectButton": "डिस्कनेक्ट", + "syncNowButton": "अभी सिंक करें", + "syncing": "सिंक्रनाइज़ हो रहा है...", + "serverUrl": "सर्वर यूआरएल", + "enterServerUrl": "कृपया सर्वर URL दर्ज करें", + "mustIncludeProtocol": "सर्वर URL की शुरुआत http:// या https:// से होनी चाहिए।", + "connectionTestFailed": "उस यूआरएल पर टर्मिक्स सर्वर तक नहीं पहुंचा जा सका।", + "allowInvalidCertificate": "अमान्य प्रमाणपत्र की अनुमति दें", + "allowInvalidCertificateDesc": "इसका उपयोग केवल विश्वसनीय सेल्फ-होस्टेड सर्वरों के लिए करें जिनमें सेल्फ-साइन किए गए या आईपी-एड्रेस प्रमाणपत्र हों।", + "savedServers": "सहेजे गए सर्वर", + "removeServer": "निकालना", + "continueButton": "जारी रखना", + "cancelButton": "रद्द करना", + "signInTitle": "{{url}} में साइन इन करें", + "originTitle": "कनेक्शन का स्रोत", + "originDescription": "डिफ़ॉल्ट रूप से SSH कनेक्शन कहाँ से शुरू होंगे, यह चुनें। इसे प्रत्येक होस्ट के लिए बदला जा सकता है।", + "originLocal": "यह डिवाइस (स्थानीय नेटवर्क)", + "originRemote": "रिमोट सर्वर", + "bannerReconnect": "रिकनेक्ट", + "bannerMessage": "रिमोट सिंक के लिए पुनः प्रमाणीकरण आवश्यक है।" }, "versionCheck": { "error": "संस्करण जाँच त्रुटि", "checkFailed": "अपडेट की जाँच करने में विफल", - "upToDate": "ऐप अपडेटेड है", + "upToDate": "ऐप अद्यतित है", "currentVersion": "आप संस्करण {{version}} चला रहे हैं", - "updateAvailable": "उपलब्ध अद्यतन", - "newVersionAvailable": "एक नया संस्करण उपलब्ध है! आप {{current}}चला रहे हैं, लेकिन {{latest}} भी उपलब्ध है।", + "updateAvailable": "अपडेट उपलब्ध है", + "newVersionAvailable": "एक नया संस्करण उपलब्ध है! आप {{current}} चला रहे हैं, लेकिन {{latest}} उपलब्ध है।", "betaVersion": "बीटा संस्करण", - "betaVersionDesc": "आप {{current}}चला रहे हैं, जो नवीनतम स्थिर रिलीज़ {{latest}} से नया है।", + "betaVersionDesc": "आप {{current}} चला रहे हैं, जो नवीनतम स्थिर रिलीज़ {{latest}} से नया है।", "releasedOn": "{{date}} को जारी किया गया", "downloadUpdate": "अपडेट डाउनलोड करें", - "checking": "अपडेट के लिए जांच कर रहा है...", - "checkUpdates": "अद्यतन के लिए जाँच", - "checkingUpdates": "अपडेट के लिए जांच कर रहा है...", - "updateRequired": "अद्यतन आवश्यक है" + "checking": "अपडेट की जाँच हो रही है...", + "checkUpdates": "अपडेट की जाँच करें", + "checkingUpdates": "अपडेट की जाँच हो रही है...", + "updateRequired": "अपडेट आवश्यक है" }, "common": { - "close": "बंद करना", - "minimize": "छोटा करना", + "close": "बंद करें", + "minimize": "न्यूनतम करें", "online": "ऑनलाइन", - "offline": "ऑफलाइन", + "offline": "ऑफ़लाइन", "unknown": "अज्ञात", - "continue": "जारी रखना", + "continue": "जारी रखें", "maintenance": "रखरखाव", - "degraded": "अपमानित", - "error": "गलती", + "degraded": "डिग्रेडेड", + "error": "त्रुटि", "warning": "चेतावनी", - "unsavedChanges": "असहेजित परिवर्तन", - "dismiss": "नकार देना", + "unsavedChanges": "सहेजे न गए परिवर्तन", + "dismiss": "खारिज करें", "loading": "लोड हो रहा है...", "optional": "वैकल्पिक", - "connect": "जोड़ना", + "connect": "कनेक्ट करें", "copied": "कॉपी किया गया", "connecting": "कनेक्ट हो रहा है...", - "updateAvailable": "उपलब्ध अद्यतन", - "appName": "टर्मिक्स", - "openInNewTab": "वेब टेब में खोलें", + "updateAvailable": "अपडेट उपलब्ध है", + "appName": "Termix", + "openInNewTab": "नए टैब में खोलें", "noReleases": "कोई रिलीज़ नहीं", "updatesAndReleases": "अपडेट और रिलीज़", "newVersionAvailable": "एक नया संस्करण ({{version}}) उपलब्ध है।", - "failedToFetchUpdateInfo": "अद्यतन जानकारी प्राप्त करने में विफल", + "failedToFetchUpdateInfo": "अपडेट जानकारी प्राप्त करने में विफल", "preRelease": "पूर्व-रिलीज़", "noReleasesFound": "कोई रिलीज़ नहीं मिली।", - "cancel": "रद्द करना", + "cancel": "रद्द करें", "username": "उपयोगकर्ता नाम", - "login": "लॉग इन करें", + "login": "लॉग इन", "logout": "लॉग आउट", - "register": "पंजीकरण करवाना", + "register": "पंजीकरण करें", "password": "पासवर्ड", - "confirmPassword": "पासवर्ड की पुष्टि कीजिये", - "back": "पीछे", - "save": "बचाना", + "confirmPassword": "पासवर्ड की पुष्टि करें", + "back": "वापस", + "save": "सहेजें", "saving": "सहेजा जा रहा है...", - "delete": "मिटाना", + "delete": "हटाएं", "rename": "नाम बदलें", - "edit": "संपादन करना", - "add": "जोड़ना", - "confirm": "पुष्टि करना", + "edit": "संपादित करें", + "add": "जोड़ें", + "confirm": "पुष्टि करें", "no": "नहीं", "or": "या", "next": "अगला", - "previous": "पहले का", - "refresh": "ताज़ा करना", + "previous": "पिछला", + "refresh": "ताज़ा करें", "language": "भाषा", - "checking": "जाँच चल रही है...", - "checkingDatabase": "डेटाबेस कनेक्शन की जाँच की जा रही है...", - "checkingAuthentication": "प्रमाणीकरण की जाँच की जा रही है...", - "backendReconnected": "सर्वर कनेक्शन बहाल हो गया", - "connectionDegraded": "सर्वर कनेक्शन टूट गया, पुनः प्राप्त किया जा रहा है…", + "checking": "जांच हो रही है...", + "checkingDatabase": "डेटाबेस कनेक्शन की जांच हो रही है...", + "checkingAuthentication": "प्रमाणीकरण की जांच हो रही है...", + "backendReconnected": "सर्वर कनेक्शन बहाल किया गया", + "connectionDegraded": "सर्वर कनेक्शन टूट गया, पुनर्प्राप्त हो रहा है…", "reload": "पुनः लोड करें", - "remove": "निकालना", + "remove": "हटाएं", "create": "बनाएं", - "update": "अद्यतन", - "copy": "प्रतिलिपि", + "update": "अपडेट करें", + "copy": "कॉपी करें", "copyFailed": "क्लिपबोर्ड पर कॉपी करने में विफल", - "maximize": "अधिकतम", - "restore": "पुनर्स्थापित करना", + "maximize": "अधिकतम करें", + "restore": "पुनर्स्थापित करें", "of": "का", "saved": "सहेजा गया", - "deleted": "हटाए गए", + "deleted": "हटाया गया", "deleteFailed": "हटाने में विफल", "saveFailed": "सहेजने में विफल", "required": "आवश्यक" }, "nav": { - "home": "घर", + "home": "होम", "terminal": "टर्मिनल", - "docker": "डाक में काम करनेवाला मज़दूर", - "tunnels": "सुरंगों", - "fileManager": "फ़ाइल मैनेजर", + "docker": "Docker", + "tunnels": "टनल", + "fileManager": "फ़ाइल प्रबंधक", "serverStats": "होस्ट मेट्रिक्स", "hostMetrics": "होस्ट मेट्रिक्स", - "admin": "व्यवस्थापक", - "termixId": "पहचान", - "userProfile": "उपयोगकर्ता रूपरेखा", + "admin": "एडमिन", + "termixId": "आईडी", + "userProfile": "उपयोगकर्ता प्रोफ़ाइल", "splitScreen": "स्प्लिट स्क्रीन", - "confirmClose": "क्या आप इस सक्रिय सत्र को बंद करना चाहते हैं?", - "close": "बंद करना", - "cancel": "रद्द करना", - "sshManager": "एसएसएच प्रबंधक", - "cannotSplitTab": "इस टैब को विभाजित नहीं किया जा सकता", + "confirmClose": "इस सक्रिय सत्र को बंद करें?", + "close": "बंद करें", + "cancel": "रद्द करें", + "sshManager": "SSH प्रबंधक", + "cannotSplitTab": "इस टैब को विभाजित नहीं कर सकते", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "पासवर्ड कॉपी करें", "copySudoPassword": "सूडो पासवर्ड कॉपी करें", - "passwordCopied": "पासवर्ड क्लिपबोर्ड पर कॉपी हो गया है", - "noPasswordAvailable": "कोई पासवर्ड उपलब्ध नहीं है", + "passwordCopied": "पासवर्ड क्लिपबोर्ड पर कॉपी किया गया", + "noPasswordAvailable": "कोई पासवर्ड उपलब्ध नहीं", "failedToCopyPassword": "पासवर्ड कॉपी करने में विफल", - "refreshTab": "कनेक्शन रीफ़्रेश करें", + "refreshTab": "कनेक्शन ताज़ा करें", "renameTab": "टैब का नाम बदलें", "openFileManager": "फ़ाइल प्रबंधक खोलें", "dashboard": "डैशबोर्ड", - "networkGraph": "नेटवर्क ग्राफ", + "networkGraph": "नेटवर्क ग्राफ़", "tmuxMonitor": "Tmux मॉनिटर", - "homepage": "मुखपृष्ठ", + "homepage": "होमपेज", "quickConnect": "त्वरित कनेक्ट", - "sshTools": "एसएसएच उपकरण", + "sshTools": "SSH टूल्स", "history": "इतिहास", - "sessionLogs": "सत्र लॉग", - "hosts": "मेजबान", + "sessionLogs": "सत्र लॉग्स", + "hosts": "होस्ट्स", "snippets": "स्निपेट्स", - "hostManager": "मेजबान प्रबंधक", - "credentials": "साख", - "connections": "कनेक्शन", - "alerts": "अलर्ट", - "serial": "धारावाहिक", + "hostManager": "होस्ट प्रबंधक", + "credentials": "क्रेडेंशियल्स", + "connections": "कनेक्शन्स", + "alerts": "अलर्ट्स", + "serial": "सीरियल", "roleAdministrator": "प्रशासक", "roleUser": "उपयोगकर्ता" }, "hosts": { - "hosts": "मेजबान", - "noHosts": "कोई एसएसएच होस्ट नहीं", - "retry": "पुन: प्रयास करें", - "refresh": "ताज़ा करना", + "hosts": "होस्ट्स", + "noHosts": "कोई SSH होस्ट नहीं", + "retry": "पुनः प्रयास", + "refresh": "रिफ़्रेश", "optional": "वैकल्पिक", "downloadSample": "नमूना डाउनलोड करें", - "failedToDeleteHost": "{{name}} को हटाने में विफल", - "importSkipExisting": "आयात करें (मौजूदा को छोड़ दें)", - "importSSHConfig": "SSH कॉन्फ़िगरेशन से आयात करें", + "failedToDeleteHost": "{{name}} हटाने में विफल", + "importSkipExisting": "आयात करें (मौजूदा छोड़ें)", + "importSSHConfig": "SSH कॉन्फ़िग से आयात करें", "connectionDetails": "कनेक्शन विवरण", - "ssh": "एसएसएच", + "ssh": "SSH", "telnet": "टेलनेट", - "remoteDesktop": "दूरवर्ती डेस्कटॉप", - "port": "पत्तन", + "remoteDesktop": "दूरस्थ डेस्कटॉप", + "port": "पोर्ट", "username": "उपयोगकर्ता नाम", "folder": "फ़ोल्डर", - "tags": "टैग", - "pin": "नत्थी करना", + "tags": "टैग्स", + "pin": "पिन करें", "addHost": "होस्ट जोड़ें", "editHost": "होस्ट संपादित करें", - "cloneHost": "क्लोन होस्ट", + "cloneHost": "होस्ट क्लोन करें", "enableTerminal": "टर्मिनल सक्षम करें", - "enableTunnel": "सुरंग सक्षम करें", - "enableFileManager": "फ़ाइल प्रबंधक को सक्षम करें", - "enableDocker": "डॉकर को सक्षम करें", + "enableTunnel": "टनल सक्षम करें", + "enableFileManager": "फ़ाइल प्रबंधक सक्षम करें", + "enableDocker": "Docker सक्षम करें", "defaultPath": "डिफ़ॉल्ट पथ", - "connection": "संबंध", - "upload": "अपलोड करें", + "connection": "कनेक्शन", + "upload": "अपलोड", "authentication": "प्रमाणीकरण", "password": "पासवर्ड", - "key": "चाबी", + "key": "कुंजी", "credential": "क्रेडेंशियल", "none": "कोई नहीं", - "sshPrivateKey": "एसएसएच निजी कुंजी", + "sshPrivateKey": "SSH निजी कुंजी", "keyType": "कुंजी प्रकार", "uploadFile": "फ़ाइल अपलोड करें", "tabGeneral": "सामान्य", - "tabSsh": "एसएसएच", + "tabSsh": "SSH", "tabTerminal": "टर्मिनल", - "tabRdp": "आरडीपी", - "tabVnc": "वीएनसी", - "tabTunnels": "सुरंगों", - "tabDocker": "डाक में काम करनेवाला मज़दूर", + "tabRdp": "RDP", + "tabVnc": "VNC", + "tabTunnels": "टनलें", + "tabDocker": "Docker", "tabFiles": "फ़ाइलें", "tabStats": "होस्ट मेट्रिक्स", "tabHostMetrics": "होस्ट मेट्रिक्स", - "tabTelnet": "टेलनेट", - "tabSharing": "शेयरिंग", + "tabTelnet": "Telnet", + "tabSharing": "साझाकरण", "tabAuthentication": "प्रमाणीकरण", "terminal": "टर्मिनल", - "tunnel": "सुरंग", - "fileManager": "फ़ाइल मैनेजर", + "tunnel": "टनल", + "fileManager": "फ़ाइल प्रबंधक", "serverStats": "होस्ट मेट्रिक्स", "status": "स्थिति", - "folderRenamed": "फ़ोल्डर \"{{oldName}}\" का नाम बदलकर \"{{newName}}\" सफलतापूर्वक कर दिया गया है", + "folderRenamed": "फ़ोल्डर \"{{oldName}}\" का नाम बदलकर \"{{newName}}\" सफलतापूर्वक किया गया", "failedToRenameFolder": "फ़ोल्डर का नाम बदलने में विफल", - "movedToFolder": "{{count}} होस्ट को \"{{folder}} \" पर स्थानांतरित किया गया", + "movedToFolder": "{{count}} होस्ट(s) को \"{{folder}}\" में स्थानांतरित किया गया", "editHostTooltip": "होस्ट संपादित करें", "statusChecks": "स्थिति जांच", - "metricsCollection": "मैट्रिक्स संग्रह", - "metricsInterval": "मैट्रिक्स संग्रह अंतराल", - "metricsIntervalDesc": "सर्वर सांख्यिकी कितनी बार एकत्र करें (5 सेकंड - 1 घंटा)", + "metricsCollection": "मेट्रिक्स संग्रहण", + "metricsInterval": "मेट्रिक्स संग्रहण अंतराल", + "metricsIntervalDesc": "सर्वर आँकड़े कितनी बार एकत्र करें (5s - 1h)", "behavior": "व्यवहार", "themePreview": "थीम पूर्वावलोकन", - "theme": "विषय", - "fontFamily": "फुहारा परिवार", + "theme": "थीम", + "fontFamily": "फ़ॉन्ट परिवार", "fontSize": "फ़ॉन्ट आकार", - "letterSpacing": "पत्र अंतराल", - "lineHeight": "ऊंची लाईन", + "letterSpacing": "अक्षर अंतराल", + "lineHeight": "पंक्ति ऊंचाई", "cursorStyle": "कर्सर शैली", "cursorBlink": "कर्सर ब्लिंक", - "scrollbackBuffer": "स्क्रॉलबैक बफर", - "bellStyle": "घंटी शैली", - "rightClickSelectsWord": "दायाँ क्लिक करने पर वर्ड का चयन होता है", - "fastScrollModifier": "फास्ट स्क्रॉल मॉडिफायर", - "fastScrollSensitivity": "तेज़ स्क्रॉल संवेदनशीलता", - "sshAgentForwarding": "एसएसएच एजेंट फ़ॉरवर्डिंग", + "scrollbackBuffer": "स्क्रॉलबैक बफ़र", + "bellStyle": "बेल शैली", + "rightClickSelectsWord": "राइट क्लिक शब्द चयन", + "fastScrollModifier": "फास्ट स्क्रॉल मॉडिफ़ायर", + "fastScrollSensitivity": "फास्ट स्क्रॉल संवेदनशीलता", + "sshAgentForwarding": "SSH एजेंट अग्रेषण", "backspaceMode": "बैकस्पेस मोड", "startupSnippet": "स्टार्टअप स्निपेट", - "selectSnippet": "स्निपेट का चयन करें", - "forceKeyboardInteractive": "फ़ोर्स कीबोर्ड-इंटरैक्टिव", - "overrideCredentialUsername": "क्रेडेंशियल उपयोगकर्ता नाम को ओवरराइड करें", - "overrideCredentialUsernameDesc": "क्रेडेंशियल के उपयोगकर्ता नाम के बजाय ऊपर निर्दिष्ट उपयोगकर्ता नाम का उपयोग करें।", - "oidcUsernameHint": "अपने OIDC लॉगिन नाम को बदलने के लिए $oidc.preferred_username का उपयोग करें।", - "jumpHostChain": "जंप होस्ट चेन", + "selectSnippet": "स्निपेट चुनें", + "forceKeyboardInteractive": "कीबोर्ड-इंटरैक्टिव बाध्य करें", + "overrideCredentialUsername": "क्रेडेंशियल यूज़रनेम ओवरराइड करें", + "overrideCredentialUsernameDesc": "क्रेडेंशियल के यूज़रनेम के बजाय ऊपर दिए गए यूज़रनेम का उपयोग करें", + "oidcUsernameHint": "अपने OIDC लॉगिन नाम को प्रतिस्थापित करने के लिए $oidc.preferred_username का उपयोग करें।", + "tailscaleUsernameHint": "यह एक यूनिक्स उपयोगकर्ता होना चाहिए जिसे टेलनेट के एसएसएच एसीएल में आपकी टेलस्केल पहचान प्रदान की गई हो, जरूरी नहीं कि यह रूट हो।", + "jumpHostChain": "जंप होस्ट श्रृंखला", "portKnocking": "पोर्ट नॉकिंग", "addKnock": "पोर्ट जोड़ें", "addProxyNode": "नोड जोड़ें", "proxyNode": "प्रॉक्सी नोड", "proxyType": "प्रॉक्सी प्रकार", - "quickActions": "त्वरित कार्रवाइयां", - "sudoPasswordAutoFill": "सूडो पासवर्ड ऑटो-फिल", - "sudoPassword": "सूडो पासवर्ड", - "keepaliveInterval": "कीपअलाइव अंतराल (मिलीसेकंड)", + "quickActions": "त्वरित कार्रवाइयाँ", + "sudoPasswordAutoFill": "sudo पासवर्ड ऑटो-फिल", + "sudoPassword": "sudo पासवर्ड", + "keepaliveInterval": "Keepalive अंतराल (ms)", "moshCommand": "MOSH कमांड", - "environmentVariables": "पर्यावरण चर", - "addVariable": "चर जोड़ें", - "docker": "डाक में काम करनेवाला मज़दूर", - "copyTerminalUrl": "टर्मिनल यूआरएल कॉपी करें", - "copyFileManagerUrl": "फ़ाइल मैनेजर यूआरएल कॉपी करें", - "copyRemoteDesktopUrl": "रिमोट डेस्कटॉप यूआरएल कॉपी करें", + "environmentVariables": "एनवायरनमेंट वेरिएबल", + "addVariable": "वेरिएबल जोड़ें", + "docker": "Docker", + "copyTerminalUrl": "टर्मिनल URL कॉपी करें", + "copyFileManagerUrl": "फ़ाइल प्रबंधक URL कॉपी करें", + "copyRemoteDesktopUrl": "रिमोट डेस्कटॉप URL कॉपी करें", "failedToConnect": "कंसोल से कनेक्ट करने में विफल", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "start": "शुरू", + "connect": "कनेक्ट करें", + "disconnect": "डिस्कनेक्ट करें", + "start": "शुरू करें", "enableStatusCheck": "स्थिति जांच सक्षम करें", - "enableMetrics": "मैट्रिक्स सक्षम करें", - "bulkUpdateFailed": "बल्क अपडेट विफल रहा", - "selectAll": "सबका चयन करें", - "deselectAll": "सबको अचयनित करो", + "enableMetrics": "मेट्रिक्स सक्षम करें", + "bulkUpdateFailed": "सामूहिक अद्यतन विफल", + "selectAll": "सभी चुनें", + "deselectAll": "सभी अचयनित करें", "protocols": "प्रोटोकॉल", - "secureShell": "सुरक्षित खोल", + "secureShell": "सुरक्षित शेल", "virtualNetwork": "वर्चुअल नेटवर्क", "unencryptedShell": "अनएन्क्रिप्टेड शेल", - "addressIp": "पता / आईपी", - "friendlyName": "मित्रवत नाम", - "macAddress": "मैक पता", - "wolBroadcastAddress": "WoL प्रसारण पता", - "wolBroadcastAddressDesc": "डॉकर/राउटेड नेटवर्क के लिए वैकल्पिक निर्देशित प्रसारण (उदाहरण के लिए 192.168.1.255)। 255.255.255.255 का उपयोग करने के लिए इसे खाली छोड़ दें।", + "addressIp": "पता / IP", + "friendlyName": "अनुकूल नाम", + "macAddress": "MAC पता", + "wolBroadcastAddress": "WoL ब्रॉडकास्ट पता", + "wolBroadcastAddressDesc": "Docker/रूटेड नेटवर्क के लिए वैकल्पिक निर्देशित ब्रॉडकास्ट (उदा. 192.168.1.255)। 255.255.255.255 का उपयोग करने के लिए खाली छोड़ें।", "folderAndAdvanced": "फ़ोल्डर और उन्नत", "privateNotes": "निजी नोट्स", "privateNotesPlaceholder": "इस सर्वर के बारे में विवरण...", - "pinToTop": "शीर्ष पर पिन करें", + "pinToTop": "सबसे ऊपर पिन करें", "pinToTopDesc": "इस होस्ट को हमेशा सूची में सबसे ऊपर दिखाएं", "portKnockingSequence": "पोर्ट नॉकिंग अनुक्रम", - "addKnockBtn": "खटखटाहट जोड़ें", - "noPortKnocking": "पोर्ट नॉकिंग कॉन्फ़िगर नहीं किया गया है।", + "addKnockBtn": "नॉक जोड़ें", + "noPortKnocking": "कोई पोर्ट नॉकिंग कॉन्फ़िगर नहीं किया गया।", "knockPort": "नॉक पोर्ट", - "protocol": "शिष्टाचार", - "delayAfterMs": "विलंब (मिलीसेकंड में)", + "protocol": "प्रोटोकॉल", + "delayAfterMs": "के बाद विलंब (ms)", "useSocks5Proxy": "SOCKS5 प्रॉक्सी का उपयोग करें", - "useSocks5ProxyDesc": "प्रॉक्सी सर्वर के माध्यम से रूट कनेक्शन", - "proxyHost": "छद्म मेजबान", + "useSocks5ProxyDesc": "कनेक्शन को प्रॉक्सी सर्वर के माध्यम से रूट करें", + "connectionOrigin": "कनेक्शन का स्रोत", + "connectionOriginDesc": "यह बताता है कि इस होस्ट का SSH कनेक्शन कहाँ से शुरू होता है। यह डेस्कटॉप ऐप के वैश्विक डिफ़ॉल्ट को ओवरराइड करता है।", + "connectionOriginDefault": "डिफ़ॉल्ट उपयोग करें", + "connectionOriginLocal": "यह डिवाइस (स्थानीय नेटवर्क)", + "connectionOriginRemote": "रिमोट सर्वर", + "proxyHost": "प्रॉक्सी होस्ट", "proxyPort": "प्रॉक्सी पोर्ट", "proxyUsername": "प्रॉक्सी उपयोगकर्ता नाम", "proxyPassword": "प्रॉक्सी पासवर्ड", "proxySingleMode": "एकल प्रॉक्सी", - "proxyChainMode": "प्रॉक्सी श्रृंखला", + "proxyChainMode": "प्रॉक्सी चेन", "you": "आप", - "jumpHostChainLabel": "जंप होस्ट चेन", + "jumpHostChainLabel": "जंप होस्ट श्रृंखला", "addJumpBtn": "जंप जोड़ें", - "noJumpHosts": "कोई जंप होस्ट कॉन्फ़िगर नहीं किया गया है।", + "noJumpHosts": "कोई जंप होस्ट कॉन्फ़िगर नहीं किया गया।", "selectAServer": "एक सर्वर चुनें...", - "sshPort": "एसएसएच पोर्ट", + "sshPort": "SSH पोर्ट", "authMethod": "प्रमाणीकरण विधि", "storedCredential": "संग्रहीत क्रेडेंशियल", "selectACredential": "एक क्रेडेंशियल चुनें...", - "vaultProfile": "वॉल्ट हस्ताक्षरकर्ता प्रोफ़ाइल", - "selectAVaultProfile": "वॉल्ट प्रोफ़ाइल चुनें...", - "vaultProfileHint": "सेटिंग्स साझा प्रोफ़ाइल से आती हैं; कनेक्ट करते समय आप OIDC के माध्यम से वॉल्ट में साइन इन करेंगे। कोई भी गुप्त जानकारी संग्रहीत नहीं की जाती है।", + "vaultProfile": "Vault साइनर प्रोफ़ाइल", + "selectAVaultProfile": "एक Vault प्रोफ़ाइल चुनें...", + "vaultProfileHint": "सेटिंग साझा प्रोफ़ाइल से आती हैं; कनेक्ट करते समय आप OIDC के माध्यम से Vault में साइन इन करेंगे। कोई सीक्रेट संग्रहीत नहीं किए जाते।", "vaultNewProfile": "नई प्रोफ़ाइल", - "vaultManageProfiles": "वॉल्ट प्रोफाइल प्रबंधित करें", - "vaultAddrLabel": "वॉल्ट पता", - "vaultNamespaceLabel": "नाम स्थान", - "vaultOidcMountLabel": "ओआईडीसी प्राधिकरण माउंट", - "vaultOidcRoleLabel": "ओआईडीसी की भूमिका", - "vaultSshMountLabel": "एसएसएच सीक्रेट्स माउंट", - "vaultSshRoleLabel": "एसएसएच हस्ताक्षरकर्ता भूमिका", - "vaultValidPrincipalsLabel": "वैध सिद्धांत", - "vaultKeyTypeLabel": "क्षणिक कुंजी प्रकार", + "vaultManageProfiles": "Vault प्रोफ़ाइल प्रबंधित करें", + "vaultAddrLabel": "Vault पता", + "vaultNamespaceLabel": "नेमस्पेस", + "vaultOidcMountLabel": "OIDC ऑथ माउंट", + "vaultOidcRoleLabel": "OIDC रोल", + "vaultSshMountLabel": "SSH सीक्रेट माउंट", + "vaultSshRoleLabel": "SSH साइनर रोल", + "vaultValidPrincipalsLabel": "मान्य प्रिंसिपल", + "vaultKeyTypeLabel": "एफेमरल कुंजी प्रकार", "vaultSharedLabel": "सभी उपयोगकर्ताओं के साथ साझा करें", "vaultCreateProfile": "प्रोफ़ाइल बनाएँ", - "vaultProfileCreated": "वॉल्ट प्रोफ़ाइल बनाई गई", - "vaultProfileSaved": "वॉल्ट प्रोफ़ाइल सहेजी गई", - "vaultProfileDeleted": "वॉल्ट प्रोफ़ाइल हटा दी गई", - "vaultProfileSaveFailed": "वॉल्ट प्रोफ़ाइल सहेजने में विफल", - "vaultProfileDeleteFailed": "वॉल्ट प्रोफ़ाइल को हटाने में विफलता", - "vaultSaveProfile": "प्रोफ़ाइल बचा", - "vaultProfileValidationError": "नाम, वॉल्ट पता और एसएसएच हस्ताक्षरकर्ता की भूमिका आवश्यक हैं।", - "vaultNoProfiles": "अभी तक कोई वॉल्ट प्रोफाइल उपलब्ध नहीं है।", + "vaultProfileCreated": "Vault प्रोफ़ाइल बनाई गई", + "vaultProfileSaved": "Vault प्रोफ़ाइल सहेजी गई", + "vaultProfileDeleted": "Vault प्रोफ़ाइल हटाई गई", + "vaultProfileSaveFailed": "Vault प्रोफ़ाइल सहेजने में विफल", + "vaultProfileDeleteFailed": "Vault प्रोफ़ाइल हटाने में विफल", + "vaultSaveProfile": "प्रोफ़ाइल सहेजें", + "vaultProfileValidationError": "नाम, Vault पता और SSH हस्ताक्षरकर्ता भूमिका आवश्यक हैं", + "vaultNoProfiles": "अभी तक कोई Vault प्रोफ़ाइल नहीं है।", "vaultSharedBadge": "साझा", "keyTypeLabel": "कुंजी प्रकार", - "keyTypeAuto": "ऑटो का पता लगाने", - "keyPasteTab": "पेस्ट करें", + "keyTypeAuto": "स्वतः पहचानें", + "keyPasteTab": "चिपकाएँ", "keyUploadTab": "अपलोड करें", "keyFileLoaded": "कुंजी फ़ाइल लोड हो गई", - "keyUploadClick": ".pem / .key / .ppk फ़ाइलें अपलोड करने के लिए क्लिक करें", - "clearKey": "साफ़ कुंजी", - "keySaved": "SSH कुंजी सहेजी गई", - "keyReplaceNotice": "इसे बदलने के लिए नीचे एक नई कुंजी पेस्ट करें", - "keyPassphraseSaved": "पासफ़्रेज़ सहेज लिया गया है, बदलने के लिए टाइप करें", - "replaceKey": "चाबी बदलें", + "keyUploadClick": ".pem / .key / .ppk अपलोड करने के लिए क्लिक करें", + "clearKey": "कुंजी हटाएँ", + "keySaved": "SSH कुंजी सहेज ली गई", + "keyReplaceNotice": "बदलने के लिए नीचे नई कुंजी चिपकाएँ", + "keyPassphraseSaved": "पासफ़्रेज़ सहेजा गया, बदलने के लिए टाइप करें", + "replaceKey": "कुंजी बदलें", "docsLink": "दस्तावेज़ देखें", - "opksshLabel": "ओपीकेएसएच", - "opksshDesc": "पासवर्ड या कुंजी के बजाय अपने पहचान प्रदाता का उपयोग करके इस होस्ट में साइन इन करें। सर्वर पर OPKSSH सेटअप होना आवश्यक है।", - "warpgateLabel": "वार्पगेट गेटवे", - "warpgateDesc": "यह होस्ट वॉर्पगेट एसएसएच प्रॉक्सी के माध्यम से कनेक्ट होता है। प्रमाणीकरण के बाद टर्मिक्स ब्राउज़र-आधारित अनुमोदन प्रक्रिया को स्वचालित रूप से संभालेगा।", - "agentLabel": "एसएसएच एजेंट", - "agentDesc": "Termix सर्वर पर चल रहे SSH एजेंट (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) का उपयोग करके प्रमाणीकरण करें। एजेंट उसी मशीन पर चलना चाहिए जहाँ Termix होस्ट किया गया है।", + "opksshLabel": "OPKSSH", + "opksshDesc": "इस होस्ट में पासवर्ड या कुंजी के बजाय अपने पहचान प्रदाता का उपयोग करके साइन इन करें। सर्वर पर OPKSSH सेटअप आवश्यक है।", + "warpgateLabel": "Warpgate गेटवे", + "warpgateDesc": "यह होस्ट Warpgate SSH प्रॉक्सी के माध्यम से कनेक्ट होता है। Termix प्रमाणीकरण के बाद ब्राउज़र-आधारित अनुमोदन प्रवाह को स्वचालित रूप से संभालेगा।", + "agentLabel": "SSH एजेंट", + "agentDesc": "Termix सर्वर पर चल रहे SSH एजेंट (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) का उपयोग करके प्रमाणित करें। एजेंट उस मशीन पर चल रहा होना चाहिए जहां Termix होस्ट किया गया है।", "agentSocketPathLabel": "एजेंट सॉकेट पथ", - "agentSocketPathPlaceholder": "SSH_AUTH_SOCK का उपयोग करने के लिए इसे खाली छोड़ दें", - "agentSocketPathHint": "SSH_AUTH_SOCK पर्यावरण चर से स्वतः पता लगाने के लिए इसे खाली छोड़ दें, या एक कस्टम सॉकेट पथ दर्ज करें (उदाहरण के लिए /run/user/1000/gnupg/S.gpg-agent.ssh)।", - "tailscaleDeviceSelect": "टेलस्केल डिवाइस का चयन करें", + "agentSocketPathPlaceholder": "SSH_AUTH_SOCK का उपयोग करने के लिए खाली छोड़ें", + "agentSocketPathHint": "SSH_AUTH_SOCK पर्यावरण चर से स्वतः पता लगाने के लिए खाली छोड़ें, या एक कस्टम सॉकेट पथ दर्ज करें (जैसे /run/user/1000/gnupg/S.gpg-agent.ssh)।", + "tailscaleDeviceSelect": "Tailscale डिवाइस चुनें", "tailscaleDeviceSelectPlaceholder": "एक डिवाइस चुनें...", - "tailscaleNoApiKey": "कोई टेलस्केल एपीआई कुंजी कॉन्फ़िगर नहीं की गई है। डिवाइस डिस्कवरी को सक्षम करने के लिए एडमिन सेटिंग्स में एक कुंजी जोड़ें।", + "tailscaleNoApiKey": "कोई Tailscale API कुंजी कॉन्फ़िगर नहीं की गई है। डिवाइस खोज सक्षम करने के लिए व्यवस्थापक सेटिंग में एक जोड़ें।", "tailscaleDocsLink": "दस्तावेज़ देखें", - "tailscaleLoadingDevices": "डिवाइस लोड हो रहे हैं...", + "tailscaleLoadingDevices": "उपकरण लोड हो रहे हैं...", "tailscaleNoDevices": "आपके टेलनेट में कोई उपकरण नहीं मिला।", - "tailscaleDeviceAutoFill": "किसी डिवाइस का चयन करने पर होस्ट का आईपी पता स्वतः भर जाएगा।", - "forceKeyboardInteractiveLabel": "फोर्स कीबोर्ड इंटरेक्टिव", - "forceKeyboardInteractiveShortDesc": "कुंजी मौजूद होने पर भी पासवर्ड को मैन्युअल रूप से दर्ज करने के लिए बाध्य करें", - "allowLegacyAlgorithmsLabel": "पुराने एल्गोरिदम की अनुमति दें", - "allowLegacyAlgorithmsDesc": "उन पुराने उपकरणों से कनेक्शन के लिए अप्रचलित SSH एल्गोरिदम (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) को सक्षम करें जिन्हें अपग्रेड नहीं किया जा सकता है।", + "tailscaleDeviceAutoFill": "डिवाइस चुनने पर होस्ट IP पता स्वतः भर जाएगा।", + "forceKeyboardInteractiveLabel": "कीबोर्ड इंटरैक्टिव बाध्य करें", + "forceKeyboardInteractiveShortDesc": "कुंजियाँ मौजूद होने पर भी मैन्युअल पासवर्ड प्रविष्टि बाध्य करें", + "allowLegacyAlgorithmsLabel": "पुराने एल्गोरिदम अनुमति दें", + "allowLegacyAlgorithmsDesc": "अपग्रेड न हो सकने वाले पुराने उपकरणों से कनेक्शन के लिए पदावनत SSH एल्गोरिदम (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) सक्षम करें।", "insecure": "असुरक्षित", - "terminalAppearance": "टर्मिनल उपस्थिति", + "terminalAppearance": "टर्मिनल स्वरूप", "colorTheme": "रंग थीम", - "fontFamilyLabel": "फुहारा परिवार", + "fontFamilyLabel": "फ़ॉन्ट परिवार", "fontSizeLabel": "फ़ॉन्ट आकार", "cursorStyleLabel": "कर्सर शैली", - "letterSpacingPx": "अक्षरों के बीच की दूरी (px)", - "lineHeightLabel": "ऊंची लाईन", + "letterSpacingPx": "अक्षर अंतराल (px)", + "lineHeightLabel": "पंक्ति ऊंचाई", "bellStyleLabel": "घंटी शैली", "backspaceModeLabel": "बैकस्पेस मोड", - "cursorBlinking": "कर्सर का झपकना", - "cursorBlinkingDesc": "टर्मिनल कर्सर के लिए ब्लिंकिंग एनीमेशन सक्षम करें", - "rightClickSelectsWordLabel": "दाएँ क्लिक करने पर Word का चयन होता है", - "rightClickSelectsWordShortDesc": "कर्सर के नीचे वाले शब्द को दाएँ क्लिक करके चुनें", - "backgroundImageLabel": "पृष्ठभूमि छवि यूआरएल", - "backgroundImageDesc": "टर्मिनल की पृष्ठभूमि छवि के लिए वैकल्पिक URL", + "cursorBlinking": "कर्सर झपकना", + "cursorBlinkingDesc": "टर्मिनल कर्सर के लिए झपकने वाला एनीमेशन सक्षम करें", + "rightClickSelectsWordLabel": "राइट-क्लिक शब्द चुनता है", + "rightClickSelectsWordShortDesc": "राइट-क्लिक पर कर्सर के नीचे का शब्द चुनें", + "backgroundImageLabel": "पृष्ठभूमि छवि URL", + "backgroundImageDesc": "टर्मिनल पृष्ठभूमि छवि के लिए वैकल्पिक URL", "backgroundImageOpacityLabel": "पृष्ठभूमि छवि अपारदर्शिता", - "customThemeColors": "अनुकूलित रंग", + "customThemeColors": "कस्टम रंग", "customThemeBackground": "पृष्ठभूमि", "customThemeForeground": "अग्रभूमि", "customThemeCursor": "कर्सर", "customThemeCursorAccent": "कर्सर एक्सेंट", "customThemeSelection": "चयन", - "customThemeAnsiColors": "एएनएसआई रंग", + "customThemeAnsiColors": "ANSI रंग", "customThemeBlack": "काला", "customThemeRed": "लाल", "customThemeGreen": "हरा", @@ -752,393 +797,410 @@ "customThemeBlue": "नीला", "customThemeMagenta": "मैजेंटा", "customThemeCyan": "सियान", - "customThemeWhite": "सफ़ेद", - "customThemeBrightBlack": "गहरा काला", - "customThemeBrightRed": "कचरू लाल", - "customThemeBrightGreen": "चमकीला हरा", - "customThemeBrightYellow": "चमकीला पीला", - "customThemeBrightBlue": "तेज़ नीला", - "customThemeBrightMagenta": "चमकीला मैजेंटा", - "customThemeBrightCyan": "चमकीला सियान", - "customThemeBrightWhite": "चमकीला सफेद", - "customThemeResetTooltip": "डिफ़ॉल्ट पर पुनः सेट करें", - "syntaxHighlightingLabel": "वाक्य - विन्यास पर प्रकाश डालना", - "syntaxHighlightingDesc": "टर्मिनल आउटपुट को रंगीन करें (त्रुटियाँ, पथ, आईपी पते, टाइमस्टैम्प)", - "syntaxHighlightingCategories": "मुख्य श्रेणियाँ", - "syntaxHighlightingCategoriesDesc": "रंग भरने के लिए सामग्री के प्रकारों का चयन करें", + "customThemeWhite": "सफेद", + "customThemeBrightBlack": "चमकदार काला", + "customThemeBrightRed": "चमकदार लाल", + "customThemeBrightGreen": "चमकदार हरा", + "customThemeBrightYellow": "चमकदार पीला", + "customThemeBrightBlue": "चमकदार नीला", + "customThemeBrightMagenta": "चमकदार मैजेंटा", + "customThemeBrightCyan": "चमकदार सियान", + "customThemeBrightWhite": "चमकदार सफेद", + "customThemeResetTooltip": "डिफ़ॉल्ट पर रीसेट करें", + "savedThemesLabel": "सहेजे गए विषय", + "saveAsGlobalTheme": "वैश्विक थीम के रूप में सहेजें", + "saveGlobalThemeNamePrompt": "इस थीम के लिए एक नाम दर्ज करें", + "saveGlobalThemeSuccess": "थीम सहेजी गई", + "saveGlobalThemeError": "थीम सहेजने में विफल", + "applyGlobalThemeTooltip": "इस थीम को लागू करें", + "deleteGlobalThemeTooltip": "इस थीम को हटाएँ", + "noSavedThemes": "अभी तक कोई थीम सेव नहीं की गई है", + "syntaxHighlightingLabel": "सिंटैक्स हाइलाइटिंग", + "syntaxHighlightingDesc": "टर्मिनल आउटपुट को रंगीन करें (त्रुटियाँ, पथ, IP, समय-चिह्न)", + "syntaxHighlightingCategories": "हाइलाइट श्रेणियाँ", + "syntaxHighlightingCategoriesDesc": "रंगीन करने के लिए सामग्री के प्रकार चुनें", "syntaxCategoryLogLevels": "लॉग स्तर", - "syntaxCategoryLogLevelsDesc": "त्रुटि, चेतावनी, जानकारी, डीबग, घातक", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "फ़ाइल पथ", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", - "syntaxCategoryTimestamps": "मुहर", + "syntaxCategoryTimestamps": "समय-चिह्न", "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", - "syntaxCategoryIpAddresses": "आईपी पते", + "syntaxCategoryIpAddresses": "IP पते", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", - "syntaxCategoryUrls": "यूआरएल", + "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "लेबल किए गए नंबर", - "syntaxCategoryNumbersDesc": "पोर्ट 8080, निकास 1, स्थिति 404", + "syntaxCategoryNumbers": "लेबल की गई संख्याएँ", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", "behaviorAndAdvanced": "व्यवहार और उन्नत", - "scrollbackBufferLabel": "स्क्रॉलबैक बफर", - "scrollbackMaxLines": "इतिहास में रखी जाने वाली पंक्तियों की अधिकतम संख्या", - "sshAgentForwardingLabel": "एसएसएच एजेंट फ़ॉरवर्डिंग", - "sshAgentForwardingShortDesc": "अपनी स्थानीय SSH कुंजी इस होस्ट को भेजें", + "scrollbackBufferLabel": "स्क्रॉलबैक बफ़र", + "scrollbackMaxLines": "इतिहास में रखी जाने वाली लाइनों की अधिकतम संख्या", + "sshAgentForwardingLabel": "SSH एजेंट फ़ॉरवर्डिंग", + "sshAgentForwardingShortDesc": "अपनी स्थानीय SSH कुंजियाँ इस होस्ट को भेजें", "useSSHTitleLabel": "SSH विंडो शीर्षक का उपयोग करें", - "useSSHTitleDesc": "टैब का शीर्षक होस्ट नाम के बजाय शेल की विंडो के शीर्षक से अपडेट करें।", - "enableAutoMosh": "ऑटो-मोश को सक्षम करें", - "enableAutoMoshDesc": "यदि उपलब्ध हो तो SSH के बजाय Mosh को प्राथमिकता दें।", - "enableAutoTmux": "ऑटो-टीमक्स सक्षम करें", - "enableAutoTmuxDesc": "tmux सेशन को स्वचालित रूप से लॉन्च करें या उससे जुड़ें", + "useSSHTitleDesc": "टैब शीर्षक को होस्ट नाम के बजाय शेल की विंडो शीर्षक से अपडेट करें", + "enableAutoMosh": "ऑटो-मोश सक्षम करें", + "enableAutoMoshDesc": "यदि उपलब्ध हो तो SSH के बजाय Mosh को प्राथमिकता दें", + "enableAutoTmux": "ऑटो-tmux सक्षम करें", + "enableAutoTmuxDesc": "tmux सत्र स्वचालित रूप से लॉन्च या संलग्न करें", "enableSessionLogging": "सत्र लॉगिंग", - "enableSessionLoggingDesc": "बाद में समीक्षा के लिए टर्मिनल सत्र के आउटपुट को रिकॉर्ड करें", - "enableCommandHistory": "कमान का इतिहास", - "enableCommandHistoryDesc": "इतिहास और स्वतः पूर्णता के लिए इस टर्मिनल में चलाए गए कमांडों को रिकॉर्ड करें", + "enableSessionLoggingDesc": "बाद में समीक्षा के लिए टर्मिनल सत्र का आउटपुट रिकॉर्ड करें", + "allowSessionSharing": "सेशन शेयरिंग की अनुमति दें", + "allowSessionSharingDesc": "इस होस्ट पर होने वाले लाइव सेशन को लिंक के माध्यम से या अन्य उपयोगकर्ताओं के साथ साझा किया जा सकता है।", + "enableCommandHistory": "कमांड इतिहास", + "enableCommandHistoryDesc": "इतिहास और स्वतः पूर्ण के लिए इस टर्मिनल में चलाए गए कमांड रिकॉर्ड करें", "linkClickBehaviorLabel": "लिंक क्लिक व्यवहार", - "linkClickBehaviorDesc": "टर्मिनल में लिंक पर क्लिक करने पर क्या होता है, इसे नियंत्रित करता है। ऐप-स्तरीय सेटिंग का पालन करने के लिए 'डिफ़ॉल्ट' का उपयोग करें।", - "linkClickBehaviorDefault": "डिफ़ॉल्ट (ऐप सेटिंग का पालन करें)", - "linkClickBehaviorConfirm": "खोलने या कॉपी करने के लिए पॉपअप दिखाएँ", + "linkClickBehaviorDesc": "टर्मिनल में लिंक क्लिक करने पर क्या होता है यह नियंत्रित करता है। ऐप-स्तरीय सेटिंग का अनुसरण करने के लिए 'डिफ़ॉल्ट' का उपयोग करें।", + "linkClickBehaviorDefault": "डिफ़ॉल्ट (ऐप सेटिंग का अनुसरण करें)", + "linkClickBehaviorConfirm": "खोलने या कॉपी करने के लिए पॉपअप दिखाएं", "linkClickBehaviorDirect": "सीधे खोलें", - "sudoPasswordAutoFillLabel": "सूडो पासवर्ड ऑटो-फिल", - "sudoPasswordAutoFillShortDesc": "पूछे जाने पर स्वचालित रूप से sudo पासवर्ड प्रदान करें", - "sudoPasswordAutoFillDesc": "एक सूडो पासवर्ड सुरक्षित रखें ताकि होस्ट मेट्रिक्स, टर्मिनल प्रॉम्प्ट और अन्य विशेषाधिकार प्राप्त कमांड को स्वचालित रूप से चला सकें।", - "sudoPasswordLabel": "सूडो पासवर्ड", + "sudoPasswordAutoFillLabel": "sudo पासवर्ड स्वतः-भरण", + "sudoPasswordAutoFillShortDesc": "संकेत मिलने पर स्वचालित रूप से sudo पासवर्ड प्रदान करें", + "sudoPasswordAutoFillDesc": "sudo पासवर्ड संग्रहीत करें ताकि होस्ट मेट्रिक्स, टर्मिनल संकेत और अन्य स्वचालित रूप से विशेषाधिकार प्राप्त कमांड चला सकें।", + "sudoPasswordLabel": "sudo पासवर्ड", "environmentVariablesLabel": "पर्यावरण चर", "addVariableBtn": "चर जोड़ें", - "noEnvVars": "कोई पर्यावरण चर कॉन्फ़िगर नहीं किया गया है।", - "fastScrollModifierLabel": "फास्ट स्क्रॉल मॉडिफायर", + "noEnvVars": "कोई पर्यावरण चर कॉन्फ़िगर नहीं किया गया।", + "fastScrollModifierLabel": "तेज़ स्क्रॉल संशोधक", "fastScrollSensitivityLabel": "तेज़ स्क्रॉल संवेदनशीलता", - "moshCommandLabel": "मोश कमांड", + "moshCommandLabel": "Mosh कमांड", "startupSnippetLabel": "स्टार्टअप स्निपेट", "keepaliveIntervalLabel": "कीपअलाइव अंतराल (सेकंड)", - "maxKeepaliveMisses": "मैक्स कीपअलाइव चूक", + "maxKeepaliveMisses": "अधिकतम कीपअलाइव मिस", "tunnelSettings": "टनल सेटिंग्स", "enableTunneling": "टनलिंग सक्षम करें", "enableTunnelingDesc": "इस होस्ट के लिए SSH टनल कार्यक्षमता सक्षम करें", "serverTunnelsSection": "सर्वर टनल", - "addTunnelBtn": "सुरंग जोड़ें", - "noTunnelsConfigured": "कोई सुरंगें कॉन्फ़िगर नहीं की गई हैं।", - "tunnelLabel": "सुरंग {{number}}", - "tunnelType": "सुरंग प्रकार", - "tunnelModeLocalDesc": "स्थानीय पोर्ट को रिमोट सर्वर (या उससे पहुंच योग्य होस्ट) पर स्थित पोर्ट पर फॉरवर्ड करें।", - "tunnelModeRemoteDesc": "रिमोट सर्वर पर मौजूद पोर्ट को अपनी मशीन के लोकल पोर्ट पर फॉरवर्ड करें।", - "tunnelModeDynamicDesc": "डायनामिक पोर्ट फॉरवर्डिंग के लिए लोकल पोर्ट पर SOCKS5 प्रॉक्सी बनाएं।", - "sameHost": "यह होस्ट (प्रत्यक्ष सुरंग)", + "addTunnelBtn": "टनल जोड़ें", + "noTunnelsConfigured": "कोई टनल कॉन्फ़िगर नहीं की गई।", + "tunnelLabel": "टनल {{number}}", + "tunnelType": "टनल प्रकार", + "tunnelModeLocalDesc": "स्थानीय पोर्ट को दूरस्थ सर्वर (या उससे पहुंच योग्य किसी होस्ट) के पोर्ट पर अग्रेषित करें।", + "tunnelModeRemoteDesc": "दूरस्थ सर्वर पर एक पोर्ट को अपनी मशीन पर स्थानीय पोर्ट पर वापस अग्रेषित करें।", + "tunnelModeDynamicDesc": "गतिशील पोर्ट अग्रेषण के लिए स्थानीय पोर्ट पर SOCKS5 प्रॉक्सी बनाएं।", + "sameHost": "यह होस्ट (डायरेक्ट टनल)", "endpointHost": "एंडपॉइंट होस्ट", - "endpointHostPlaceholder": "वही होस्ट, एसएसएच होस्ट, या पहुंच योग्य होस्ट/आईपी", + "endpointHostPlaceholder": "समान होस्ट, SSH होस्ट, या पहुंच योग्य होस्ट/IP", "endpointPort": "एंडपॉइंट पोर्ट", - "bindHost": "होस्ट को बांधें", + "bindHost": "बाइंड होस्ट", "sourcePort": "स्रोत पोर्ट", - "maxRetries": "अधिकतम पुनः प्रयास", - "retryIntervalS": "पुनः प्रयास अंतराल (सेकंड)", - "autoStartLabel": "ऑटो स्टार्ट", - "autoStartDesc": "होस्ट लोड होने पर यह टनल स्वचालित रूप से कनेक्ट हो जाएगी", - "tunnelConnecting": "सुरंग जोड़ने वाली...", - "tunnelDisconnected": "सुरंग का संपर्क टूट गया", - "failedToConnectTunnel": "जोडने में विफल", + "maxRetries": "अधिकतम पुनर्प्रयास", + "retryIntervalS": "पुनर्प्रयास अंतराल (s)", + "autoStartLabel": "स्वतः आरंभ", + "autoStartDesc": "होस्ट लोड होने पर यह टनल स्वचालित रूप से कनेक्ट करें", + "tunnelConnecting": "टनल कनेक्ट हो रही है...", + "tunnelDisconnected": "टनल डिस्कनेक्ट हो गई", + "failedToConnectTunnel": "कनेक्ट करने में विफल", "failedToDisconnectTunnel": "डिस्कनेक्ट करने में विफल", - "dockerIntegration": "डॉकर एकीकरण", - "enableDockerMonitor": "डॉकर को सक्षम करें", - "enableDockerMonitorDesc": "डॉकर के माध्यम से इस होस्ट पर कंटेनरों की निगरानी और प्रबंधन करें।", + "dockerIntegration": "Docker एकीकरण", + "enableDockerMonitor": "Docker सक्षम करें", + "enableDockerMonitorDesc": "इस होस्ट पर Docker के माध्यम से कंटेनरों की निगरानी और प्रबंधन करें", "containerRuntime": "कंटेनर रनटाइम", - "containerRuntimeDesc": "इस होस्ट पर कंटेनर प्रबंधन के लिए उपयोग की जाने वाली CLI चुनें", - "containerRuntimeDocker": "डाक में काम करनेवाला मज़दूर", - "containerRuntimePodman": "पॉडमैन", - "enableTmuxMonitor": "Tmux मॉनिटर को सक्षम करें", - "enableTmuxMonitorDesc": "इस होस्ट को Tmux मॉनिटर में दिखाएं और इसके Tmux एक्शन को साइडबार में जोड़ें।", - "tabProxmox": "प्रॉक्समॉक्स", - "proxmoxIntegration": "प्रॉक्समॉक्स एकीकरण", - "enableProxmox": "प्रोक्समॉक्स को सक्षम करें", - "enableProxmoxDesc": "इस होस्ट को Proxmox नोड के रूप में चिह्नित करें। इससे इस होस्ट से सीधे गेस्ट डिस्कवर और इंपोर्ट सक्षम हो जाता है।", + "containerRuntimeDesc": "इस होस्ट पर कंटेनर प्रबंधन के लिए उपयोग किए जाने वाले CLI को चुनें", + "containerRuntimeDocker": "Docker", + "containerRuntimePodman": "Podman", + "enableTmuxMonitor": "Tmux मॉनिटर सक्षम करें", + "enableTmuxMonitorDesc": "इस होस्ट को Tmux मॉनिटर में दिखाएँ और इसकी tmux क्रियाओं को साइडबार में जोड़ें", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox एकीकरण", + "enableProxmox": "Proxmox सक्षम करें", + "enableProxmoxDesc": "इस होस्ट को Proxmox नोड के रूप में चिह्नित करें। इस होस्ट से सीधे अतिथि खोज और आयात सक्षम करता है।", "proxmoxDefaultAuthType": "डिफ़ॉल्ट प्रमाणीकरण प्रकार", - "proxmoxDefaultAuthTypeDesc": "आयातित अतिथि होस्टों पर लागू प्रमाणीकरण विधि। वह प्रमाणीकरण प्रकार चुनें जो आपके अतिथियों से जुड़ने के तरीके से मेल खाता हो।", + "proxmoxDefaultAuthTypeDesc": "आयातित अतिथि होस्ट पर लागू प्रमाणीकरण विधि। वह प्रमाणीकरण प्रकार चुनें जो आपके अतिथियों से कनेक्ट करने के तरीके से मेल खाता हो।", "authTypePassword": "पासवर्ड", - "authTypeKey": "एसएसएच कुंजी", + "authTypeKey": "SSH कुंजी", "authTypeCredential": "क्रेडेंशियल", - "authTypeOpkssh": "ओपीकेएसएच", + "authTypeOpkssh": "OPKSSH", "authTypeNone": "कोई नहीं", "proxmoxDefaultCredential": "डिफ़ॉल्ट क्रेडेंशियल", - "proxmoxDefaultCredentialDesc": "जब प्रमाणीकरण प्रकार को क्रेडेंशियल पर सेट किया जाता है, तो आयातित अतिथि होस्ट के लिए उपयोग किया जाने वाला क्रेडेंशियल।", - "proxmoxWindowsDetection": "विंडोज / आरडीपी पहचान", - "proxmoxWindowsDetectionDesc": "अल्पविराम से अलग किए गए नाम पैटर्न जो SSH के बजाय RDP को सक्रिय करते हैं (केस-असंवेदनशील)", - "proxmoxDockerDetection": "डॉकर का पता लगाना", - "proxmoxDockerDetectionDesc": "कॉमा-सेपरेटेड नाम पैटर्न जो डॉकर को गेस्ट्स से मिलान करने में सक्षम बनाते हैं", - "proxmoxPreferredRanges": "पसंदीदा आईपी रेंज", - "proxmoxPreferredRangesDesc": "अतिथि कंप्यूटर में एकाधिक इंटरफेस होने पर आईपी चयन के लिए अल्पविराम से अलग किए गए उपसर्गों को प्राथमिकता क्रम में रखा जाता है।", - "proxmoxAutoSync": "अतिथियों के साथ स्वतः सिंक", - "proxmoxAutoSyncDesc": "समय-समय पर इस प्रॉक्समॉक्स नोड का पता लगाएं और जब आपका सत्र अनलॉक हो, तब आयातित अतिथि होस्ट बनाएं या अपडेट करें।", - "proxmoxSyncInterval": "सिंक्रोनाइज़ेशन अंतराल (मिनटों में)", - "proxmoxSyncIntervalDesc": "कम से कम 5 मिनट। शेड्यूलर लॉक किए गए उपयोगकर्ता सत्रों को छोड़ देता है।", - "proxmoxMarkMissing": "अनुपस्थित मेहमानों को चिह्नित करें", - "proxmoxMarkMissingDesc": "पहले से आयात किए गए गेस्ट के गायब हो जाने पर, उसे हटाने के बजाय, एक proxmox-missing टैग जोड़ें।", + "proxmoxDefaultCredentialDesc": "जब प्रमाणीकरण प्रकार 'क्रेडेंशियल' पर सेट होता है तब आयातित अतिथि होस्ट के लिए उपयोग किया जाने वाला क्रेडेंशियल।", + "proxmoxWindowsDetection": "Windows / RDP पहचान", + "proxmoxWindowsDetectionDesc": "अल्पविराम से अलग नाम पैटर्न जो SSH के बजाय RDP ट्रिगर करते हैं (अक्षर-आकार असंवेदी)", + "proxmoxDockerDetection": "Docker पहचान", + "proxmoxDockerDetectionDesc": "अल्पविराम से अलग नाम पैटर्न जो मिलान वाले अतिथियों के लिए Docker सक्षम करते हैं", + "proxmoxPreferredRanges": "पसंदीदा IP श्रेणियाँ", + "proxmoxPreferredRangesDesc": "अतिथि के पास कई इंटरफ़ेस होने पर IP चयन के लिए प्राथमिकता क्रम में अल्पविराम से अलग उपसर्ग", + "proxmoxAutoSync": "अतिथि स्वतः सिंक", + "proxmoxAutoSyncDesc": "जब आपका सत्र अनलॉक हो तो समय-समय पर इस Proxmox नोड की खोज करें और आयातित अतिथि होस्ट बनाएँ या अपडेट करें।", + "proxmoxSyncInterval": "सिंक अंतराल (मिनट)", + "proxmoxSyncIntervalDesc": "न्यूनतम 5 मिनट। शेड्यूलर लॉक उपयोगकर्ता सत्रों को छोड़ देता है।", + "proxmoxMarkMissing": "गायब अतिथियों को चिह्नित करें", + "proxmoxMarkMissingDesc": "जब पहले से आयातित कोई अतिथि गायब हो जाए तो उसे हटाने के बजाय एक 'proxmox-missing' टैग जोड़ें।", "proxmoxLastSync": "अंतिम सिंक", - "proxmoxLastSyncNever": "अभी तक सिंक नहीं हुआ है", - "proxmoxLastSyncNoResult": "कोई परिणाम विवरण उपलब्ध नहीं है", - "proxmoxLastSyncSummary": "{{created}} बनाया गया, {{updated}} अपडेट किया गया, {{markedMissing}} अनुपस्थित, {{skipped}} छोड़ा गया", + "proxmoxLastSyncNever": "अभी तक सिंक नहीं हुआ", + "proxmoxLastSyncNoResult": "कोई परिणाम विवरण नहीं", + "proxmoxLastSyncSummary": "{{created}} बनाए गए, {{updated}} अपडेट किए गए, {{markedMissing}} गायब, {{skipped}} छोड़े गए", "proxmoxLastSyncStatus": { "success": "सफलता", - "error": "असफल", + "error": "विफल", "pending": "लंबित" }, - "proxmoxDiscoverAction": "Proxmox गेस्ट्स को खोजें और इंपोर्ट करें", + "proxmoxDiscoverAction": "Proxmox अतिथियों की खोज करें और आयात करें", "proxmoxImportTitle": "Proxmox से आयात करें", "proxmoxSelectHost": "एक Proxmox होस्ट चुनें…", - "proxmoxDiscover": "खोज करना", - "proxmoxDiscovering": "… की खोज करना", - "proxmoxDiscoverGuests": "मेहमानों को खोजें", + "proxmoxDiscover": "खोजें", + "proxmoxDiscovering": "खोज रहा है…", + "proxmoxDiscoverGuests": "अतिथियों की खोज करें", "proxmoxGuestsSelected_one": "{{count}} अतिथि — {{selected}} चयनित", "proxmoxGuestsSelected_other": "{{count}} अतिथि — {{selected}} चयनित", - "proxmoxSelectAll": "सबका चयन करें", - "proxmoxDeselectAll": "सबको अचयनित करो", - "proxmoxNoGuests": "इस प्रॉक्समॉक्स नोड पर कोई अतिथि नहीं मिला।", - "proxmoxImportButton_one": "Import {{count}} host", - "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxSelectAll": "सभी चुनें", + "proxmoxDeselectAll": "सभी अचयनित करें", + "proxmoxNoGuests": "इस Proxmox नोड पर कोई अतिथि नहीं मिला।", + "proxmoxImportButton_one": "{{count}} होस्ट आयात करें", + "proxmoxImportButton_other": "{{count}} होस्ट आयात करें", "proxmoxResultImported": "{{count}} आयातित", - "proxmoxResultUpdated": "{{count}} अपडेट किया गया", - "proxmoxResultFailed": "{{count}} असफल", + "proxmoxResultUpdated": "{{count}} अद्यतित", + "proxmoxResultFailed": "{{count}} विफल", + "proxmoxResultSkippedNoIp": "{{count}} को छोड़ दिया गया (कोई आईपी नहीं मिला)", "proxmoxImportComplete": "Proxmox आयात पूर्ण: {{summary}}", - "proxmoxDiscoveryFailed": "खोज विफल रही", + "proxmoxDiscoveryFailed": "डिस्कवरी विफल", "proxmoxImportFailed": "आयात विफल", - "enableFileManagerMonitor": "फ़ाइल प्रबंधक को सक्षम करें", - "enableFileManagerMonitorDesc": "SFTP के माध्यम से इस होस्ट पर फ़ाइलों को ब्राउज़ करें और प्रबंधित करें।", - "scpLegacyLabel": "एससीपी लेगेसी मोड", - "scpLegacyDesc": "उन सर्वरों के लिए पारंपरिक फ़ाइल स्थानांतरण विधि का उपयोग करें जो SFTP सबसिस्टम का समर्थन नहीं करते हैं (उदाहरण के लिए एम्बेडेड या न्यूनतम SSH सर्वर)।", + "enableFileManagerMonitor": "फ़ाइल प्रबंधक सक्षम करें", + "enableFileManagerMonitorDesc": "इस होस्ट पर SFTP के ज़रिए फ़ाइलें ब्राउज़ और प्रबंधित करें", + "scpLegacyLabel": "SCP लीगेसी मोड", + "scpLegacyDesc": "उन सर्वरों के लिए लीगेसी फ़ाइल स्थानांतरण का उपयोग करें जो SFTP सबसिस्टम का समर्थन नहीं करते (जैसे एम्बेडेड या न्यूनतम SSH सर्वर)।", "defaultPathLabel": "डिफ़ॉल्ट पथ", - "fileManagerPathHint": "इस होस्ट के लिए फ़ाइल मैनेजर लॉन्च होने पर खुलने वाली निर्देशिका।", - "statusChecksLabel": "स्थिति जांच", - "enableStatusChecks": "स्थिति जांच सक्षम करें", - "enableStatusChecksDesc": "उपलब्धता की पुष्टि करने के लिए समय-समय पर इस होस्ट को पिंग करें।", + "fileManagerPathHint": "जब इस होस्ट के लिए फ़ाइल प्रबंधक लॉन्च होता है तो खोलने के लिए निर्देशिका।", + "statusChecksLabel": "स्थिति जाँच", + "enableStatusChecks": "स्थिति जाँच सक्षम करें", + "enableStatusChecksDesc": "उपलब्धता सत्यापित करने के लिए समय-समय पर इस होस्ट को पिंग करें", "useGlobalInterval": "वैश्विक अंतराल का उपयोग करें", - "useGlobalIntervalDesc": "सर्वर-व्यापी स्थिति जांच अंतराल के साथ ओवरराइड करें", - "checkIntervalS": "जाँच अंतराल", - "checkIntervalDesc": "प्रत्येक कनेक्टिविटी पिंग के बीच सेकंड का अंतर", - "metricsCollectionLabel": "मैट्रिक्स संग्रह", - "enableMetricsLabel": "मैट्रिक्स सक्षम करें", + "useGlobalIntervalDesc": "सर्वर-व्यापी स्थिति जाँच अंतराल से ओवरराइड करें", + "checkIntervalS": "जाँच अंतराल (सेकंड)", + "checkIntervalDesc": "प्रत्येक कनेक्टिविटी पिंग के बीच सेकंड", + "metricsCollectionLabel": "मेट्रिक्स संग्रह", + "enableMetricsLabel": "मेट्रिक्स सक्षम करें", "enableMetricsDesc": "इस होस्ट से CPU, RAM, डिस्क और अन्य मेट्रिक्स एकत्र करें", "useGlobalMetrics": "वैश्विक अंतराल का उपयोग करें", - "useGlobalMetricsDesc": "सर्वर-व्यापी मेट्रिक्स अंतराल के साथ ओवरराइड करें", - "metricsIntervalS": "मैट्रिक्स अंतराल (सेकंड)", - "metricsIntervalDesc2": "मीट्रिक स्नैपशॉट के बीच सेकंड", - "visibleWidgets": "दृश्यमान विजेट", - "widgetsMovedToHostMetrics": "अब होस्ट मेट्रिक्स टैब में ही कार्ड जोड़े, व्यवस्थित किए और उनका आकार बदला जा सकता है। इस होस्ट के लिए होस्ट मेट्रिक्स खोलें और कस्टमाइज़ का उपयोग करके चुनें कि कौन से कार्ड दिखाए जाएं।", - "cpuUsageLabel": "सीपीयू उपयोग", - "cpuUsageDesc": "सीपीयू प्रतिशत, लोड औसत, स्पार्कलाइन ग्राफ", - "memoryLabel": "स्मृति प्रयोग", - "memoryDesc": "रैम उपयोग, स्वैप, कैश किया गया", + "useGlobalMetricsDesc": "सर्वर-व्यापी मेट्रिक्स अंतराल से ओवरराइड करें", + "metricsIntervalS": "मेट्रिक्स अंतराल (सेकंड)", + "metricsIntervalDesc2": "मेट्रिक स्नैपशॉट के बीच सेकंड", + "visibleWidgets": "दृश्यमान विजेट्स", + "widgetsMovedToHostMetrics": "कार्ड अब सीधे होस्ट मेट्रिक्स टैब में जोड़े, व्यवस्थित और आकार बदले जा सकते हैं। इस होस्ट के लिए होस्ट मेट्रिक्स खोलें और यह चुनने के लिए कस्टमाइज़ का उपयोग करें कि कौन से कार्ड दिखाए जाएँ।", + "cpuUsageLabel": "CPU उपयोग", + "cpuUsageDesc": "CPU प्रतिशत, लोड औसत, स्पार्कलाइन ग्राफ़", + "memoryLabel": "मेमोरी उपयोग", + "memoryDesc": "RAM उपयोग, स्वैप, कैश्ड", "storageLabel": "डिस्क उपयोग", "storageDesc": "प्रति माउंट पॉइंट डिस्क उपयोग", - "networkLabel": "नेटवर्क इंटरफेस", - "networkDesc": "इंटरफ़ेस सूची और बैंडविड्थ", + "networkLabel": "नेटवर्क इंटरफेसेस", + "networkDesc": "इंटरफेस सूची और बैंडविड्थ", "uptimeLabel": "अपटाइम", "uptimeDesc": "सिस्टम अपटाइम और बूट समय", - "systemInfoLabel": "व्यवस्था की सूचना", - "systemInfoDesc": "ऑपरेटिंग सिस्टम, कर्नेल, होस्टनाम, आर्किटेक्चर", - "recentLoginsLabel": "हाल की लॉगिन", - "recentLoginsDesc": "सफल और असफल लॉगिन इवेंट", - "topProcessesLabel": "शीर्ष प्रक्रियाएँ", - "topProcessesDesc": "पीआईडी, सीपीयू%, एमईएम%, कमांड", - "listeningPortsLabel": "श्रवण पोर्ट", - "listeningPortsDesc": "प्रक्रिया और स्थिति के साथ पोर्ट खोलें", + "systemInfoLabel": "सिस्टम जानकारी", + "systemInfoDesc": "OS, कर्नेल, होस्टनाम, आर्किटेक्चर", + "recentLoginsLabel": "हाल के लॉगिन", + "recentLoginsDesc": "सफल और असफल लॉगिन घटनाएं", + "topProcessesLabel": "शीर्ष प्रक्रियाएं", + "topProcessesDesc": "PID, CPU%, MEM%, कमांड", + "listeningPortsLabel": "सुनने वाले पोर्ट", + "listeningPortsDesc": "प्रक्रिया और स्थिति के साथ खुले पोर्ट", "firewallLabel": "फ़ायरवॉल", - "firewallDesc": "फ़ायरवॉल, ऐपआर्मर, SELinux स्थिति", - "quickActionsLabel": "त्वरित कार्रवाइयां", - "quickActionsToolbar": "त्वरित क्रियाएं होस्ट मेट्रिक्स टूलबार में बटन के रूप में दिखाई देती हैं, जिन्हें एक क्लिक में निष्पादित किया जा सकता है।", - "noQuickActions": "अभी तक कोई त्वरित कार्रवाई नहीं की गई है।", + "firewallDesc": "फ़ायरवॉल, AppArmor, SELinux स्थिति", + "quickActionsLabel": "त्वरित कार्रवाइयाँ", + "quickActionsToolbar": "त्वरित कार्रवाइयाँ एक-क्लिक कमांड निष्पादन के लिए होस्ट मेट्रिक्स टूलबार में बटन के रूप में दिखाई देती हैं।", + "noQuickActions": "अभी तक कोई त्वरित कार्रवाई नहीं।", "buttonLabel": "बटन लेबल", - "selectSnippetPlaceholder": "अंश का चयन करें...", + "selectSnippetPlaceholder": "स्निपेट चुनें...", "addActionBtn": "कार्रवाई जोड़ें", - "hostSharedSuccessfully": "होस्ट ने सफलतापूर्वक साझा किया", + "hostSharedSuccessfully": "होस्ट सफलतापूर्वक साझा किया गया", "failedToShareHost": "होस्ट साझा करने में विफल", - "accessRevoked": "पहुँच रद्द कर दी गई", + "accessRevoked": "पहुँच रद्द की गई", "failedToRevokeAccess": "पहुँच रद्द करने में विफल", - "cancelBtn": "रद्द करना", - "savingBtn": "सहेजा जा रहा है...", + "cancelBtn": "रद्द करें", + "savingBtn": "सहेज रहा है...", "addHostBtn": "होस्ट जोड़ें", "hostUpdated": "होस्ट अपडेट किया गया", - "hostCreated": "होस्ट ने बनाया", - "failedToSave": "होस्ट को सहेजने में विफल", + "hostCreated": "होस्ट बनाया गया", + "failedToSave": "होस्ट सहेजने में विफल", "credentialUpdated": "क्रेडेंशियल अपडेट किया गया", "credentialCreated": "क्रेडेंशियल बनाया गया", "failedToSaveCredential": "क्रेडेंशियल सहेजने में विफल", "credentialNameRequired": "कृपया क्रेडेंशियल के लिए एक नाम दर्ज करें", - "credentialAuthRequired": "पासवर्ड, एसएसएच कुंजी, या दोनों जोड़ें", - "backToHosts": "मेज़बानों पर वापस जाएँ", - "backToCredentials": "क्रेडेंशियल्स पर वापस जाएँ", - "pinned": "पिन की गई", + "credentialAuthRequired": "पासवर्ड, SSH कुंजी, या दोनों जोड़ें", + "createCredentialFromHostBtn": "क्रेडेंशियल बनाएं", + "createCredentialFromHostTitle": "होस्ट से क्रेडेंशियल बनाएं", + "createCredentialFromHostDesc": "इस होस्ट के वर्तमान उपयोगकर्ता नाम, पासवर्ड और/या एसएसएच कुंजी से पहले से भरी हुई, पुनः उपयोग योग्य और साझा करने योग्य क्रेडेंशियल प्रविष्टि बनाएं।", + "backToHosts": "होस्ट पर वापस जाएं", + "backToCredentials": "क्रेडेंशियल पर वापस जाएं", + "pinned": "पिन किए गए", "noHostsFound": "कोई होस्ट नहीं मिला", - "tryDifferentTerm": "कोई दूसरा शब्द आजमाएँ", - "addFirstHost": "शुरुआत करने के लिए अपना पहला होस्ट जोड़ें", + "tryDifferentTerm": "कोई दूसरा शब्द आज़माएं", + "addFirstHost": "शुरू करने के लिए अपना पहला होस्ट जोड़ें", "noCredentialsFound": "कोई क्रेडेंशियल नहीं मिला", "addCredentialBtn": "क्रेडेंशियल जोड़ें", "updateCredentialBtn": "क्रेडेंशियल अपडेट करें", - "features": "विशेषताएँ", + "features": "विशेषताएं", "noFolder": "(कोई फ़ोल्डर नहीं)", - "deleteSelected": "मिटाना", - "exitSelection": "निकास चयन", - "importSkip": "आयात करें (मौजूदा को छोड़ दें)", - "importOverwrite": "आयात (ओवरराइट)", - "collapseBtn": "गिर जाना", - "importExportBtn": "आयात / निर्यात", - "hostStatusesRefreshed": "होस्ट की स्थिति रीफ्रेश हो गई है", - "failedToRefreshHosts": "होस्ट को रीफ़्रेश करने में विफल", - "movedHostTo": "{{host}} को \"{{folder}} \" में स्थानांतरित किया गया", + "deleteSelected": "हटाएं", + "exitSelection": "चयन से बाहर निकलें", + "importSkip": "इम्पोर्ट करें (मौजूदा को छोड़ें)", + "importOverwrite": "इम्पोर्ट करें (अधिलेखित करें)", + "collapseBtn": "संकुचित करें", + "importExportBtn": "इम्पोर्ट / एक्सपोर्ट", + "hostStatusesRefreshed": "होस्ट स्थितियां ताज़ा हो गईं", + "failedToRefreshHosts": "होस्ट को ताज़ा करने में विफल", + "movedHostTo": "{{host}} को \"{{folder}}\" में स्थानांतरित किया गया", "failedToMoveHost": "होस्ट को स्थानांतरित करने में विफल", - "folderRenamedTo": "फ़ोल्डर का नाम बदलकर \"{{name}} \" कर दिया गया है", - "deletedFolder": "फ़ोल्डर \"{{name}} \" हटा दिया गया", - "failedToDeleteFolder": "फ़ोल्डर को हटाने में विफल", - "deleteAllInFolder": "\"{{name}}\" में सभी होस्ट हटाएँ? इसे पूर्ववत नहीं किया जा सकता है।", + "folderRenamedTo": "फ़ोल्डर का नाम बदलकर \"{{name}}\" कर दिया गया", + "deletedFolder": "फ़ोल्डर \"{{name}}\" हटा दिया गया", + "failedToDeleteFolder": "फ़ोल्डर हटाने में विफल", + "deleteAllInFolder": "क्या आप \"{{name}}\" के सभी होस्ट हटाना चाहते हैं? इसे पूर्ववत नहीं किया जा सकता.", "folderPickerPlaceholder": "कोई फ़ोल्डर नहीं", - "folderPickerSearch": "खोजें या बनाएं (उपफ़ोल्डरों के लिए / का उपयोग करें)...", + "folderPickerSearch": "खोजें या बनाएं (सबफ़ोल्डर के लिए / का उपयोग करें)...", "folderPickerNone": "कोई फ़ोल्डर नहीं", - "folderPickerCreate": "\"{{path}} \" बनाएँ", - "folderPickerEmpty": "कोई मेल खाने वाले फ़ोल्डर नहीं मिले", + "folderPickerCreate": "\"{{path}}\" बनाएं", + "folderPickerEmpty": "कोई मेल खाता फ़ोल्डर नहीं", "newFolder": "नया फ़ोल्डर", - "createFolderTitle": "फ़ोल्डर बनाएँ", + "createFolderTitle": "फ़ोल्डर बनाएं", "editFolderTitle": "फ़ोल्डर संपादित करें", - "folderDialogDescription": "नाम, रंग और आइकन चुनें। फ़ोल्डरों को एक-दूसरे के अंदर रखने के लिए / का उपयोग करें।", + "folderDialogDescription": "एक नाम, रंग और आइकन चुनें। फ़ोल्डर नेस्ट करने के लिए / का उपयोग करें।", "folderNameLabel": "फ़ोल्डर का नाम", - "folderNamePlaceholder": "उदाहरण के लिए उत्पादन/वेब", + "folderNamePlaceholder": "उदा. Production/Web", "folderNestingHint": "स्तरों को अलग करने और नेस्टेड फ़ोल्डर बनाने के लिए / का उपयोग करें।", "folderColor": "रंग", "folderIcon": "आइकन", - "folderPreview": "पूर्व दर्शन", - "folderNameFallback": "शीर्षकहीन फ़ोल्डर", + "folderCredential": "क्रेडेंशियल", + "folderCredentialNone": "कोई पहचान पत्र आवंटित नहीं किया गया", + "folderCredentialHint": "इस फोल्डर में मौजूद वे होस्ट जो अपने स्वयं के क्रेडेंशियल का चयन किए बिना \"स्टोर्ड क्रेडेंशियल\" प्रमाणीकरण का उपयोग करते हैं, वे इस क्रेडेंशियल को इनहेरिट करेंगे।", + "folderPreview": "पूर्वावलोकन", + "folderNameFallback": "शीर्षक रहित फ़ोल्डर", "createFolderButton": "फ़ोल्डर बनाएँ", "saveFolderButton": "फ़ोल्डर सहेजें", - "cancel": "रद्द करना", - "iconSearchPlaceholder": "खोज आइकन...", + "cancel": "रद्द करें", + "iconSearchPlaceholder": "आइकन खोजें...", "openAllSessions": "सभी सत्र खोलें", "editFolder": "फ़ोल्डर संपादित करें", - "deleteFolder": "फ़ोल्डर हटाएं", + "deleteFolder": "फ़ोल्डर हटाएँ", "folderSaved": "फ़ोल्डर सहेजा गया", "failedToSaveFolder": "फ़ोल्डर सहेजने में विफल", - "folderDeleted": "फ़ोल्डर \"{{name}} \" हटा दिया गया", - "deleteFolderConfirm": "क्या \"{{name}}\" और इसके {{count}} होस्ट को हटाना संभव है? इसे पूर्ववत नहीं किया जा सकता।", - "failedToMoveHosts": "होस्ट को स्थानांतरित करने में विफल", - "expandAll": "सभी फ़ोल्डर खोलें", - "collapseAll": "सभी फ़ोल्डरों को बंद करें", + "folderDeleted": "फ़ोल्डर \"{{name}}\" हटा दिया गया", + "deleteFolderConfirm": "\"{{name}}\" और इसके {{count}} होस्ट हटाएँ? इसे पूर्ववत नहीं किया जा सकता।", + "failedToMoveHosts": "होस्ट स्थानांतरित करने में विफल", + "expandAll": "सभी फ़ोल्डर विस्तृत करें", + "collapseAll": "सभी फ़ोल्डर संकुचित करें", "moreActions": "अधिक", - "groupBy": "द्वारा समूह बनाएं", + "groupBy": "इसके अनुसार समूहित करें", "GroupByFolder": "फ़ोल्डर", "GroupByTag": "टैग", "GroupByStatus": "स्थिति", - "GroupByProtocol": "शिष्टाचार", - "GroupByAuth": "प्राधिकरण प्रकार", - "groupUngrouped": "असमूहीकृत", - "deletedHost": "हटा दिया गया {{name}}", - "copiedToClipboard": "क्लिपबोर्ड पर कॉपी हो गया", - "terminalUrlCopied": "टर्मिनल यूआरएल कॉपी हो गया", - "fileManagerUrlCopied": "फ़ाइल मैनेजर यूआरएल कॉपी हो गया", - "tunnelUrlCopied": "टनल यूआरएल कॉपी किया गया", - "dockerUrlCopied": "डॉकर यूआरएल कॉपी किया गया", - "hostMetricsUrlCopied": "होस्ट मेट्रिक्स यूआरएल कॉपी किया गया", - "tmuxMonitorUrlCopied": "Tmux मॉनिटर URL कॉपी हो गया", - "rdpUrlCopied": "आरडीपी यूआरएल कॉपी हो गया", - "vncUrlCopied": "VNC URL कॉपी हो गया", - "telnetUrlCopied": "टेलनेट यूआरएल कॉपी हो गया", - "remoteDesktopUrlCopied": "रिमोट डेस्कटॉप यूआरएल कॉपी हो गया", - "expandActions": "कार्यों का विस्तार करें", - "collapseActions": "कार्यों को संक्षिप्त करें", - "wakeOnLanAction": "लैन पर जागो", - "wakeOnLanSuccess": "मैजिक पैकेट {{name}} को भेजा गया", + "GroupByProtocol": "प्रोटोकॉल", + "GroupByAuth": "प्रमाणीकरण प्रकार", + "groupUngrouped": "असमूहित", + "deletedHost": "{{name}} हटा दिया गया", + "copiedToClipboard": "क्लिपबोर्ड पर कॉपी किया गया", + "terminalUrlCopied": "टर्मिनल URL कॉपी किया गया", + "fileManagerUrlCopied": "फ़ाइल प्रबंधक URL कॉपी किया गया", + "tunnelUrlCopied": "टनल URL कॉपी किया गया", + "dockerUrlCopied": "Docker URL कॉपी किया गया", + "hostMetricsUrlCopied": "होस्ट मेट्रिक्स URL कॉपी किया गया", + "tmuxMonitorUrlCopied": "Tmux मॉनिटर URL कॉपी किया गया", + "rdpUrlCopied": "RDP URL कॉपी किया गया", + "vncUrlCopied": "VNC URL कॉपी किया गया", + "telnetUrlCopied": "Telnet URL कॉपी किया गया", + "remoteDesktopUrlCopied": "रिमोट डेस्कटॉप URL कॉपी किया गया", + "expandActions": "कार्रवाइयाँ विस्तृत करें", + "collapseActions": "कार्रवाइयाँ संक्षिप्त करें", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "{{name}} को मैजिक पैकेट भेजा गया", "wakeOnLanError": "मैजिक पैकेट भेजने में विफल", - "cloneHostAction": "क्लोन होस्ट", + "cloneHostAction": "होस्ट क्लोन करें", "copyAddress": "पता कॉपी करें", - "copyLink": "लिंक की प्रतिलिपि करें", - "copyTerminalUrlAction": "टर्मिनल यूआरएल कॉपी करें", - "copyFileManagerUrlAction": "फ़ाइल मैनेजर यूआरएल कॉपी करें", - "copyTunnelUrlAction": "टनल यूआरएल कॉपी करें", - "copyDockerUrlAction": "डॉकर यूआरएल कॉपी करें", - "copyHostMetricsUrlAction": "होस्ट मेट्रिक्स यूआरएल कॉपी करें", + "copyLink": "लिंक कॉपी करें", + "copyTerminalUrlAction": "टर्मिनल URL कॉपी करें", + "copyFileManagerUrlAction": "फ़ाइल प्रबंधक URL कॉपी करें", + "copyTunnelUrlAction": "टनल URL कॉपी करें", + "copyDockerUrlAction": "Docker URL कॉपी करें", + "copyHostMetricsUrlAction": "होस्ट मेट्रिक्स URL कॉपी करें", "copyTmuxMonitorUrlAction": "Tmux मॉनिटर URL कॉपी करें", - "copyRdpUrlAction": "आरडीपी यूआरएल कॉपी करें", + "copyRdpUrlAction": "RDP URL कॉपी करें", "copyVncUrlAction": "VNC URL कॉपी करें", - "copyTelnetUrlAction": "टेलनेट यूआरएल कॉपी करें", - "copyRemoteDesktopUrlAction": "रिमोट डेस्कटॉप यूआरएल कॉपी करें", - "deleteCredentialConfirm": "क्रेडेंशियल \"{{name}} \" हटाएं?", - "deletedCredential": "हटा दिया गया {{name}}", - "deploySSHKeyTitle": "एसएसएच कुंजी तैनात करें", - "deployingBtn": "तैनाती जारी है...", - "deployBtn": "तैनात करना", + "copyTelnetUrlAction": "Telnet URL कॉपी करें", + "copyRemoteDesktopUrlAction": "रिमोट डेस्कटॉप URL कॉपी करें", + "deleteCredentialConfirm": "क्रेडेंशियल \"{{name}}\" हटाएँ?", + "deletedCredential": "{{name}} हटा दिया गया", + "deploySSHKeyTitle": "SSH कुंजी तैनात करें", + "deployingBtn": "तैनात कर रहे हैं...", + "deployBtn": "तैनात करें", "failedToDeployKey": "कुंजी तैनात करने में विफल", - "deleteHostsConfirm": "{{count}} होस्ट{{plural}}को हटाएँ? इसे पूर्ववत नहीं किया जा सकता।", - "movedToRoot": "रूट में स्थानांतरित किया गया", + "deleteHostsConfirm": "{{count}} होस्ट{{plural}} हटाएँ? यह पूर्ववत नहीं किया जा सकता।", + "movedToRoot": "रूट में ले जाया गया", "enableTerminalFeature": "टर्मिनल सक्षम करें", - "disableTerminalFeature": "टर्मिनल को अक्षम करें", + "disableTerminalFeature": "टर्मिनल अक्षम करें", "enableFilesFeature": "फ़ाइलें सक्षम करें", "disableFilesFeature": "फ़ाइलें अक्षम करें", - "enableTunnelsFeature": "सुरंगों को सक्षम करें", - "disableTunnelsFeature": "सुरंगों को निष्क्रिय करें", - "enableDockerFeature": "डॉकर को सक्षम करें", - "disableDockerFeature": "डॉकर को अक्षम करें", - "enableProxmoxFeature": "प्रोक्समॉक्स को सक्षम करें", - "disableProxmoxFeature": "Proxmox को अक्षम करें", - "addTagsPlaceholder": "टैगों को जोड़ें...", + "enableTunnelsFeature": "टनल सक्षम करें", + "disableTunnelsFeature": "टनल अक्षम करें", + "enableDockerFeature": "Docker सक्षम करें", + "disableDockerFeature": "Docker अक्षम करें", + "enableProxmoxFeature": "Proxmox सक्षम करें", + "disableProxmoxFeature": "Proxmox अक्षम करें", + "addTagsPlaceholder": "टैग जोड़ें…", "authDetails": "प्रमाणीकरण विवरण", "credType": "प्रकार", - "generateKeyPairDesc": "एक नया कुंजी युग्म उत्पन्न करें, निजी और सार्वजनिक दोनों कुंजियाँ स्वचालित रूप से भर दी जाएंगी।", - "generatingKey": "जनरेट हो रहा है...", - "generateLabel": "जनरेट {{label}}", + "generateKeyPairDesc": "एक नई कुंजी जोड़ी उत्पन्न करें, निजी और सार्वजनिक दोनों कुंजियाँ स्वचालित रूप से भर दी जाएँगी।", + "generatingKey": "उत्पन्न कर रहा है…", + "generateLabel": "{{label}} उत्पन्न करें", "uploadFileBtn": "फ़ाइल अपलोड करें", "keyPassphraseOptional": "कुंजी पासफ़्रेज़ (वैकल्पिक)", - "sshPublicKeyOptional": "एसएसएच सार्वजनिक कुंजी (वैकल्पिक)", - "publicKeyGenerated": "जनित सार्वजनिक कुंजी", - "failedToGeneratePublicKey": "सार्वजनिक कुंजी प्राप्त करने में विफल", - "publicKeyCopied": "सार्वजनिक कुंजी कॉपी कर ली गई", - "keyPairGenerated": "{{label}} कुंजी युग्म उत्पन्न हुआ", - "failedToGenerateKeyPair": "कुंजी युग्म उत्पन्न करने में विफल", + "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": "प्रमाणीकरण और कनेक्शन सेटिंग्स को कॉन्फ़िगर करने के लिए ऊपर दिए गए प्रोटोकॉल में से कम से कम एक को सक्षम करें।", + "searchCredentialsPlaceholder": "क्रेडेंशियल खोजें…", + "refreshBtn": "ताज़ा करें", + "addTag": "टैग जोड़ें…", + "deleteConfirmBtn": "हटाएँ", + "tunnelRequirementsText": "SSH सर्वर में /etc/ssh/sshd_config में GatewayPorts yes, AllowTcpForwarding yes, और PermitRootLogin yes सेट होना चाहिए।", + "deleteHostConfirm": "क्या \"{{name}}\" हटाएँ?", + "enableAtLeastOneProtocol": "प्रमाणीकरण और कनेक्शन सेटिंग्स कॉन्फ़िगर करने के लिए ऊपर कम से कम एक प्रोटोकॉल सक्षम करें।", "keyPassphrase": "कुंजी पासफ़्रेज़", - "connectBtn": "जोड़ना", - "disconnectBtn": "डिस्कनेक्ट", + "connectBtn": "कनेक्ट करें", + "disconnectBtn": "डिस्कनेक्ट करें", "basicInformation": "मूल जानकारी", "authDetailsSection": "प्रमाणीकरण विवरण", "credTypeLabel": "प्रकार", - "hostsTab": "मेजबान", - "credentialsTab": "साख", - "selectMultiple": "एकाधिक का चयन करें", + "hostsTab": "होस्ट", + "credentialsTab": "क्रेडेंशियल्स", + "selectMultiple": "एकाधिक चुनें", "selectHosts": "होस्ट चुनें", - "connectionLabel": "संबंध", + "connectionLabel": "कनेक्शन", "authenticationLabel": "प्रमाणीकरण", - "generateKeyPairTitle": "कुंजी युग्म उत्पन्न करें", - "generateKeyPairDescription": "एक नया कुंजी युग्म उत्पन्न करें, निजी और सार्वजनिक दोनों कुंजियाँ स्वचालित रूप से भर दी जाएंगी।", - "generateFromPrivateKey": "निजी कुंजी से उत्पन्न करें", - "refreshBtn2": "ताज़ा करना", - "exitSelectionTitle": "निकास चयन", + "generateKeyPairTitle": "की-पेयर जनरेट करें", + "generateKeyPairDescription": "एक नई की-पेयर जनरेट करें, प्राइवेट और पब्लिक की दोनों स्वतः भर जाएँगी।", + "generateFromPrivateKey": "प्राइवेट की से जनरेट करें", + "refreshBtn2": "रीफ़्रेश", + "exitSelectionTitle": "चयन से बाहर निकलें", "exportAll": "सभी निर्यात करें", "exportForSharing": "साझा करने के लिए निर्यात करें", "addHostBtn2": "होस्ट जोड़ें", "addCredentialBtn2": "क्रेडेंशियल जोड़ें", - "checkingHostStatuses": "होस्ट की स्थिति की जाँच की जा रही है...", - "pinnedSection": "पिन की गई", - "hostsExported": "होस्ट सफलतापूर्वक निर्यात हो गए", + "checkingHostStatuses": "होस्ट की स्थितियाँ जाँच रहे हैं...", + "pinnedSection": "पिन किए गए", + "hostsExported": "होस्ट सफलतापूर्वक निर्यात किए गए", "hostsShareExported": "साझा करने योग्य होस्ट सफलतापूर्वक निर्यात किए गए", "exportFailed": "होस्ट निर्यात करने में विफल", - "sampleDownloaded": "नमूना फ़ाइल डाउनलोड हो गई", + "sampleDownloaded": "नमूना फ़ाइल डाउनलोड की गई", "failedToDeleteCredential2": "क्रेडेंशियल हटाने में विफल", "noFolderOption": "(कोई फ़ोल्डर नहीं)", "nSelected": "{{count}} चयनित", "featuresMenu": "विशेषताएँ", - "moveMenu": "कदम", - "connectSelected": "जोड़ना", - "cancelSelection": "रद्द करना", - "deployDialogDesc": "होस्ट के authorized_keys पर {{name}} तैनात करें।", - "targetHostLabel": "लक्ष्य मेजबान", - "selectHostOption": "एक होस्ट का चयन करें...", - "keyDeployedSuccess": "कुंजी सफलतापूर्वक तैनात की गई", - "failedToDeployKey2": "कुंजी तैनात करने में विफल", - "deletedCount": "हटाए गए {{count}} होस्ट", - "failedToDeleteCount": "{{count}} होस्ट को हटाने में विफल", - "duplicatedHost": "डुप्लिकेट \"{{name}}\"", - "failedToDuplicateHost": "होस्ट को डुप्लिकेट करने में विफल", - "updatedCount": "अपडेट किए गए {{count}} होस्ट", - "friendlyNameLabel": "मित्रवत नाम", + "moveMenu": "स्थानांतरित करें", + "connectSelected": "कनेक्ट करें", + "cancelSelection": "रद्द करें", + "deployDialogDesc": "{{name}} को होस्ट की authorized_keys में डिप्लॉय करें।", + "targetHostLabel": "लक्ष्य होस्ट", + "selectHostOption": "एक होस्ट चुनें...", + "keyDeployedSuccess": "की सफलतापूर्वक डिप्लॉय की गई", + "failedToDeployKey2": "की डिप्लॉय करने में विफल", + "deletedCount": "{{count}} होस्ट हटाए गए", + "failedToDeleteCount": "{{count}} होस्ट हटाने में विफल", + "duplicatedHost": "\"{{name}}\" की प्रतिलिपि बनाई गई", + "failedToDuplicateHost": "होस्ट डुप्लिकेट नहीं हो सका", + "updatedCount": "{{count}} होस्ट अपडेट किए गए", + "friendlyNameLabel": "अनुकूल नाम", "descriptionLabel": "विवरण", "loadingHost": "होस्ट लोड हो रहा है...", "loadingHosts": "होस्ट लोड हो रहे हैं...", @@ -1147,64 +1209,68 @@ "noHostsMatchSearch": "आपकी खोज से मेल खाने वाले कोई होस्ट नहीं हैं", "hostNotFound": "होस्ट नहीं मिला", "searchHosts": "होस्ट खोजें...", - "sortHosts": "होस्ट को क्रमबद्ध करें", - "sortDefault": "डिफ़ॉल्ट ऑर्डर", - "sortNameAsc": "नाम (अ से जेड तक)", + "sortHosts": "होस्ट क्रमबद्ध करें", + "sortDefault": "डिफ़ॉल्ट क्रम", + "sortNameAsc": "नाम (A → Z)", "sortNameDesc": "नाम (Z → A)", - "sortIpAsc": "आईपी पता (आरोही)", - "sortIpDesc": "आईपी पता (विवरण)", - "sortOnlineFirst": "ऑनलाइन फर्स्ट", - "sortOfflineFirst": "ऑफ़लाइन प्रथम", - "sortPinnedFirst": "पहले पिन किया गया", - "filterHosts": "होस्ट को फ़िल्टर करें", - "filterClearAll": "फ़िल्टर साफ़ करें", + "sortIpAsc": "IP पता (आरोही)", + "sortIpDesc": "IP पता (अवरोही)", + "sortOnlineFirst": "पहले ऑनलाइन", + "sortOfflineFirst": "पहले ऑफ़लाइन", + "sortPinnedFirst": "पहले पिन किए गए", + "filterHosts": "होस्ट फ़िल्टर करें", + "filterClearAll": "सभी फ़िल्टर साफ़ करें", "filterStatusGroup": "स्थिति", "filterOnline": "ऑनलाइन", - "filterOffline": "ऑफलाइन", - "filterPinned": "पिन की गई", - "filterAuthGroup": "प्राधिकरण प्रकार", + "filterOffline": "ऑफ़लाइन", + "filterPinned": "पिन किए गए", + "filterAuthGroup": "प्रमाणीकरण प्रकार", "filterAuthPassword": "पासवर्ड", - "filterAuthKey": "एसएसएच कुंजी", + "filterAuthKey": "SSH कुंजी", "filterAuthCredential": "क्रेडेंशियल", "filterAuthNone": "कोई नहीं", - "filterAuthOpkssh": "ओपीकेएसएच", - "filterProtocolGroup": "शिष्टाचार", - "filterProtocolSsh": "एसएसएच", - "filterProtocolRdp": "आरडीपी", - "filterProtocolVnc": "वीएनसी", - "filterProtocolTelnet": "टेलनेट", - "filterFeaturesGroup": "विशेषताएँ", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "प्रोटोकॉल", + "filterProtocolSsh": "SSH", + "filterProtocolRdp": "RDP", + "filterProtocolVnc": "VNC", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "विशेषताएं", "filterFeatureTerminal": "टर्मिनल", - "filterFeatureFileManager": "फ़ाइल मैनेजर", - "filterFeatureTunnel": "सुरंग", - "filterFeatureDocker": "डाक में काम करनेवाला मज़दूर", - "filterTagsGroup": "टैग", + "filterFeatureFileManager": "फ़ाइल प्रबंधक", + "filterFeatureTunnel": "टनल", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "टैग्स", "shareHost": "होस्ट साझा करें", - "shareHostTitle": "शेयर करें: {{name}}", + "shareHostTitle": "साझा करें: {{name}}", + "shareFolder": "फ़ोल्डर साझा करें", + "shareFolderTitle": "फ़ोल्डर साझा करें: {{name}}", + "folderSharedSuccessfully": "फ़ोल्डर में साझा {{count}} होस्ट", + "failedToShareFolder": "फ़ोल्डर साझा करने में विफल", "sharing": { - "loadError": "शेयरिंग डेटा लोड करने में विफल। कृपया पुनः प्रयास करें।", - "shareWithSection": "के साथ शेयर करें", - "usersTab": "उपयोगकर्ताओं", + "loadError": "साझाकरण डेटा लोड करने में विफल। कृपया पुनः प्रयास करें।", + "shareWithSection": "के साथ साझा करें", + "usersTab": "उपयोगकर्ता", "rolesTab": "भूमिकाएँ", - "searchPlaceholder": "उपयोगकर्ताओं या भूमिकाओं को खोजें...", - "noMatches": "कोई मेल नहीं मिले", + "searchPlaceholder": "उपयोगकर्ता या भूमिकाएँ खोजें...", + "noMatches": "कोई मिलान नहीं मिला", "permissionLevelLabel": "अनुमति स्तर", "levels": { "connect": { - "label": "जोड़ना", - "description": "केवल टर्मिनल, रिमोट डेस्कटॉप, फ़ाइल मैनेजर, टनल और डॉकर के लिए ही ओपन सेशन उपलब्ध हैं। होस्ट कॉन्फ़िगरेशन तक कोई पहुंच नहीं है।" + "label": "कनेक्ट", + "description": "केवल सत्र खोलें: टर्मिनल, रिमोट डेस्कटॉप, फ़ाइल प्रबंधक, टनल और Docker। होस्ट कॉन्फ़िगरेशन तक पहुँच नहीं।" }, "view": { - "label": "देखना", - "description": "कनेक्ट करें और होस्ट कॉन्फ़िगरेशन देखें। गुप्त जानकारी कभी प्रदर्शित नहीं की जाती है।" + "label": "देखें", + "description": "कनेक्ट करें, साथ ही होस्ट कॉन्फ़िगरेशन देखें। सीक्रेट कभी नहीं दिखाए जाते।" }, "edit": { - "label": "संपादन करना", - "description": "होस्ट को देखें और संशोधित करें। गुप्त जानकारी को बदला जा सकता है लेकिन पढ़ा नहीं जा सकता; क्रेडेंशियल असाइनमेंट केवल मालिक के पास ही रहते हैं।" + "label": "संपादित करें", + "description": "देखें, और होस्ट को संशोधित करें। सीक्रेट बदले जा सकते हैं लेकिन कभी पढ़े नहीं जाते; क्रेडेंशियल असाइनमेंट केवल स्वामी के लिए रहते हैं।" }, "manage": { - "label": "प्रबंधित करना", - "description": "होस्ट को संपादित करें, साथ ही दूसरों के साथ साझा करें, अनुमति स्तर बदलें और पहुंच रद्द करें।" + "label": "प्रबंधित करें", + "description": "संपादित करें, और दूसरों के साथ होस्ट साझा करें, अनुमति स्तर बदलें और पहुँच रद्द करें।" } }, "expiryLabel": "पहुँच समाप्ति", @@ -1214,150 +1280,154 @@ "oneDay": "24 घंटे", "sevenDays": "7 दिन", "thirtyDays": "30 दिन", - "custom": "रिवाज़" + "custom": "कस्टम" }, - "customHoursPlaceholder": "पहुँच समाप्त होने में लगने वाले घंटे", - "shareButton": "शेयर करना", - "shareWithCount": "शेयर ({{count}})", - "currentAccess": "वर्तमान पहुंच", - "noAccessEntries": "इस होस्ट को अभी तक साझा नहीं किया गया है", - "grantedBy": "अनुमती देना", - "expires": "समय-सीमा समाप्त", - "expired": "खत्म हो चुका", + "customHoursPlaceholder": "पहुँच समाप्त होने तक के घंटे", + "shareButton": "साझा करें", + "shareWithCount": "साझा करें ({{count}})", + "currentAccess": "वर्तमान पहुँच", + "noAccessEntries": "यह होस्ट अभी तक साझा नहीं किया गया है", + "folderShareSummary": "इस फ़ोल्डर में {{total}} होस्ट का साझा {{shared}} है", + "grantedBy": "प्रदानकर्ता", + "expires": "समाप्ति", + "expired": "समाप्त", "never": "कभी नहीं", - "revoke": "रद्द करना", - "accessUpdated": "अपडेट एक्सेस करें", - "accessUpdateFailed": "एक्सेस अपडेट करने में विफल", + "revoke": "निरस्त करें", + "accessUpdated": "पहुँच अद्यतित", + "accessUpdateFailed": "पहुँच अद्यतन विफल", "sharedBadge": "साझा", - "sharedBadgeTooltip": "{{owner}} द्वारा साझा किया गया ({{level}} पहुंच)", - "viewOnlyBanner": "यह होस्ट {{owner}} द्वारा आपके साथ साझा किया गया है और इसे देखने की अनुमति है। कॉन्फ़िगरेशन केवल पढ़ने योग्य है।", - "sharedEditBanner": "यह होस्ट {{owner}} द्वारा संपादन अनुमति के साथ आपके साथ साझा किया गया है। परिवर्तन वास्तविक होस्ट पर लागू होते हैं; प्रमाणीकरण संदर्भों को केवल स्वामी द्वारा ही बदला जा सकता है।", - "ownerOnlyControl": "इसे केवल होस्ट का मालिक ही बदल सकता है।" + "sharedBadgeTooltip": "{{owner}} द्वारा साझा ({{level}} पहुँच)", + "viewOnlyBanner": "यह होस्ट आपके साथ {{owner}} द्वारा दृश्य पहुँच के साथ साझा किया गया है। कॉन्फ़िगरेशन केवल-पठनीय है।", + "sharedEditBanner": "यह होस्ट आपके साथ {{owner}} द्वारा संपादन पहुँच के साथ साझा किया गया है। परिवर्तन वास्तविक होस्ट पर लागू होते हैं; प्रमाणीकरण संदर्भ केवल स्वामी द्वारा बदले जा सकते हैं।", + "ownerOnlyControl": "केवल होस्ट स्वामी ही इसे बदल सकता है" }, "guac": { - "connection": "संबंध", + "connection": "कनेक्शन", "authentication": "प्रमाणीकरण", "storedCredential": "संग्रहीत क्रेडेंशियल", - "noCredential": "कोई प्रमाण पत्र आवश्यक नहीं (प्रत्यक्ष प्रमाण पत्र नीचे दिए गए हैं)", + "noCredential": "कोई क्रेडेंशियल नहीं (नीचे सीधे क्रेडेंशियल)", "authMethod": "प्रमाणीकरण विधि", - "authTypeDirect": "प्रत्यक्ष", + "authTypeDirect": "सीधा", "authTypeCredential": "क्रेडेंशियल", + "authTypeNone": "कोई नहीं", + "authTypeNoneDesc": "कोई भी क्रेडेंशियल स्टोर नहीं किए जाते हैं। हर बार कनेक्ट करते समय आपसे उपयोगकर्ता नाम और पासवर्ड मांगा जाएगा; वे सहेजे नहीं जाते हैं।", "selectCredential": "एक क्रेडेंशियल चुनें...", "connectionSettings": "कनेक्शन सेटिंग्स", "displaySettings": "प्रदर्शन सेटिंग्स", - "audioSettings": "श्रव्य विन्यास", - "rdpPerformance": "आरडीपी प्रदर्शन", + "audioSettings": "ऑडियो सेटिंग्स", + "rdpPerformance": "RDP प्रदर्शन", "deviceRedirection": "डिवाइस पुनर्निर्देशन", "session": "सत्र", - "gateway": "द्वार", - "remoteApp": "रिमोटऐप", + "gateway": "गेटवे", + "remoteApp": "RemoteApp", "clipboard": "क्लिपबोर्ड", "sessionRecording": "सत्र रिकॉर्डिंग", - "wakeOnLan": "लैन पर जागो", - "vncSettings": "वीएनसी सेटिंग्स", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC सेटिंग्स", "terminalSettings": "टर्मिनल सेटिंग्स", - "rdpPort": "आरडीपी पोर्ट", + "rdpPort": "RDP पोर्ट", "username": "उपयोगकर्ता नाम", "password": "पासवर्ड", - "domain": "कार्यक्षेत्र", + "passwordSaved": "पासवर्ड सहेज लिया गया है, बदलने के लिए टाइप करें", + "domain": "डोमेन", "securityMode": "सुरक्षा मोड", - "colorDepth": "रंग की गहराई", + "colorDepth": "रंग गहराई", "width": "चौड़ाई", "height": "ऊंचाई", - "dpi": "डीपीआई", - "resizeMethod": "आकार बदलने की विधि", - "clientName": "ग्राहक नाम", - "initialProgram": "प्रारंभिक कार्यक्रम", + "dpi": "DPI", + "resizeMethod": "रीसाइज़ विधि", + "clientName": "क्लाइंट नाम", + "initialProgram": "आरंभिक प्रोग्राम", "serverLayout": "सर्वर लेआउट", "timezone": "समय क्षेत्र", "loadBalanceInfo": "लोड बैलेंस जानकारी / कुकी", - "loadBalanceInfoDesc": "RDS फ़ार्म लोड बैलेंसिंग के लिए RD कनेक्शन ब्रोकर कुकी (उदाहरण के लिए tsv://MS टर्मिनल सर्विसेज प्लगइन.1.CollectionName)", - "guacdProxy": "गुआकडी प्रॉक्सी", - "guacdHostname": "गुआकड होस्ट", + "loadBalanceInfoDesc": "RDS फ़ार्म लोड संतुलन के लिए RD कनेक्शन ब्रोकर कुकी (उदा. tsv://MS Terminal Services Plugin.1.CollectionName)", + "guacdProxy": "guacd प्रॉक्सी", + "guacdHostname": "guacd होस्ट", "guacdHostnamePlaceholder": "वैश्विक डिफ़ॉल्ट", - "guacdPort": "गुआकड पोर्ट", - "guacdProxyDesc": "इस कनेक्शन के लिए वैश्विक guacd इंस्टेंस को ओवरराइड करें। वैश्विक स्तर पर कॉन्फ़िगर किए गए guacd का उपयोग करने के लिए इसे खाली छोड़ दें।", + "guacdPort": "guacd पोर्ट", + "guacdProxyDesc": "इस कनेक्शन के लिए वैश्विक guacd इंस्टेंस को अधिलेखित करें। वैश्विक रूप से कॉन्फ़िगर किए गए guacd का उपयोग करने के लिए खाली छोड़ें।", "gatewayHostname": "गेटवे होस्टनाम", "gatewayPort": "गेटवे पोर्ट", "gatewayUsername": "गेटवे उपयोगकर्ता नाम", "gatewayPassword": "गेटवे पासवर्ड", "gatewayDomain": "गेटवे डोमेन", - "remoteAppProgram": "रिमोटऐप प्रोग्राम", - "workingDirectory": "कार्यकारी डाइरेक्टरी", - "arguments": "बहस", - "normalizeLineEndings": "लाइन के अंत को सामान्य करें", + "remoteAppProgram": "RemoteApp प्रोग्राम", + "workingDirectory": "कार्यशील निर्देशिका", + "arguments": "तर्क", + "normalizeLineEndings": "लाइन अंत सामान्यीकृत करें", "recordingPath": "रिकॉर्डिंग पथ", "recordingName": "रिकॉर्डिंग नाम", - "macAddress": "मैक पता", - "broadcastAddress": "ब्रॉडकास्ट पता", - "udpPort": "यूडीपी पोर्ट", - "waitTimeS": "प्रतीक्षा समय (सेकंड में)", + "macAddress": "MAC पता", + "broadcastAddress": "प्रसारण पता", + "udpPort": "UDP पोर्ट", + "waitTimeS": "प्रतीक्षा समय (s)", "driveName": "ड्राइव का नाम", "drivePath": "ड्राइव पथ", - "ignoreCertificate": "प्रमाणपत्र को अनदेखा करें", - "ignoreCertificateDesc": "सेल्फ-साइन किए गए प्रमाणपत्रों वाले होस्ट से कनेक्शन की अनुमति दें", - "forceLossless": "बल हानि रहित", - "forceLosslessDesc": "दोषरहित छवि एन्कोडिंग को लागू करें (उच्च गुणवत्ता, अधिक बैंडविड्थ)", + "ignoreCertificate": "प्रमाणपत्र अनदेखा करें", + "ignoreCertificateDesc": "स्व-हस्ताक्षरित प्रमाणपत्र वाले होस्ट से कनेक्शन की अनुमति दें", + "forceLossless": "दोषरहित बाध्य करें", + "forceLosslessDesc": "दोषरहित छवि एन्कोडिंग बाध्य करें (उच्च गुणवत्ता, अधिक बैंडविड्थ)", "disableAudio": "ऑडियो अक्षम करें", - "disableAudioDesc": "रिमोट सेशन से सभी ऑडियो म्यूट करें", - "enableAudioInput": "ऑडियो इनपुट (माइक्रोफ़ोन) चालू करें", - "enableAudioInputDesc": "स्थानीय माइक्रोफ़ोन को रिमोट सेशन पर फ़ॉरवर्ड करें", + "disableAudioDesc": "दूरस्थ सत्र से सभी ऑडियो म्यूट करें", + "enableAudioInput": "ऑडियो इनपुट सक्षम करें (माइक्रोफ़ोन)", + "enableAudioInputDesc": "स्थानीय माइक्रोफ़ोन को दूरस्थ सत्र में अग्रेषित करें", "wallpaper": "वॉलपेपर", - "wallpaperDesc": "डेस्कटॉप वॉलपेपर दिखाएं (इसे बंद करने से प्रदर्शन बेहतर होता है)", - "theming": "थीम", - "themingDesc": "दृश्य थीम और शैलियों को सक्षम करें", - "fontSmoothing": "आकर्षक फ़ॉन्ट", - "fontSmoothingDesc": "क्लियरटाइप फ़ॉन्ट रेंडरिंग सक्षम करें", - "fullWindowDrag": "पूरी विंडो को ड्रैग करें", - "fullWindowDragDesc": "खींचते समय विंडो की सामग्री दिखाएँ", - "desktopComposition": "डेस्कटॉप कंपोजीशन", - "desktopCompositionDesc": "एयरो ग्लास प्रभाव सक्षम करें", - "menuAnimations": "मेनू एनिमेशन", - "menuAnimationsDesc": "मेनू फेड और स्लाइड एनिमेशन सक्षम करें", - "disableBitmapCaching": "बिटमैप कैशिंग को अक्षम करें", - "disableBitmapCachingDesc": "बिटमैप कैश बंद करें (इससे तकनीकी गड़बड़ियों को ठीक करने में मदद मिल सकती है)", - "disableOffscreenCaching": "ऑफस्क्रीन कैशिंग को अक्षम करें", + "wallpaperDesc": "डेस्कटॉप वॉलपेपर दिखाएं (अक्षम करने से प्रदर्शन बेहतर होता है)", + "theming": "थीमिंग", + "themingDesc": "दृश्य थीम और शैलियाँ सक्षम करें", + "fontSmoothing": "फ़ॉन्ट स्मूथिंग", + "fontSmoothingDesc": "ClearType फ़ॉन्ट रेंडरिंग सक्षम करें", + "fullWindowDrag": "पूर्ण विंडो ड्रैग", + "fullWindowDragDesc": "ड्रैग करते समय विंडो सामग्री दिखाएं", + "desktopComposition": "डेस्कटॉप कंपोज़िशन", + "desktopCompositionDesc": "Aero ग्लास प्रभाव सक्षम करें", + "menuAnimations": "मेन्यू एनिमेशन", + "menuAnimationsDesc": "मेन्यू फेड और स्लाइड एनिमेशन सक्षम करें", + "disableBitmapCaching": "बिटमैप कैशिंग अक्षम करें", + "disableBitmapCachingDesc": "बिटमैप कैश बंद करें (गड़बड़ियों में मदद कर सकता है)", + "disableOffscreenCaching": "ऑफस्क्रीन कैशिंग अक्षम करें", "disableOffscreenCachingDesc": "ऑफस्क्रीन कैश बंद करें", - "disableGlyphCaching": "ग्लिफ़ कैशिंग को अक्षम करें", + "disableGlyphCaching": "ग्लिफ़ कैशिंग अक्षम करें", "disableGlyphCachingDesc": "ग्लिफ़ कैश बंद करें", - "enableGfx": "जीएफएक्स सक्षम करें", - "enableGfxDesc": "RemoteFX ग्राफिक्स पाइपलाइन का उपयोग करें", - "enablePrinting": "मुद्रण सक्षम करें", - "enablePrintingDesc": "स्थानीय प्रिंटरों को रिमोट सेशन पर रीडायरेक्ट करें", - "enableDriveRedirection": "ड्राइव रीडायरेक्शन सक्षम करें", - "enableDriveRedirectionDesc": "रिमोट सेशन में स्थानीय फ़ोल्डर को ड्राइव के रूप में मैप करें", + "enableGfx": "GFX सक्षम करें", + "enableGfxDesc": "RemoteFX ग्राफ़िक्स पाइपलाइन का उपयोग करें", + "enablePrinting": "प्रिंटिंग सक्षम करें", + "enablePrintingDesc": "स्थानीय प्रिंटर को दूरस्थ सत्र में पुनर्निर्देशित करें", + "enableDriveRedirection": "ड्राइव पुनर्निर्देशन सक्षम करें", + "enableDriveRedirectionDesc": "स्थानीय फ़ोल्डर को दूरस्थ सत्र में ड्राइव के रूप में मैप करें", "createDrivePath": "ड्राइव पथ बनाएँ", - "createDrivePathDesc": "यदि फ़ोल्डर मौजूद नहीं है तो उसे स्वचालित रूप से बना दें", + "createDrivePathDesc": "यदि फ़ोल्डर मौजूद न हो तो स्वचालित रूप से बनाएँ", "disableDownload": "डाउनलोड अक्षम करें", - "disableDownloadDesc": "रिमोट सेशन से फ़ाइलें डाउनलोड करने से रोकें", + "disableDownloadDesc": "रिमोट सत्र से फ़ाइलें डाउनलोड करना रोकें", "disableUpload": "अपलोड अक्षम करें", - "disableUploadDesc": "रिमोट सेशन में फ़ाइलें अपलोड करने से रोकें", + "disableUploadDesc": "रिमोट सत्र पर फ़ाइलें अपलोड करना रोकें", "enableTouch": "टच सक्षम करें", - "enableTouchDesc": "टच इनपुट फ़ॉरवर्डिंग सक्षम करें", + "enableTouchDesc": "टच इनपुट अग्रेषण सक्षम करें", "consoleSession": "कंसोल सत्र", "consoleSessionDesc": "नए सत्र के बजाय कंसोल (सत्र 0) से कनेक्ट करें", "sendWolPacket": "WOL पैकेट भेजें", - "sendWolPacketDesc": "कनेक्ट करने से पहले इस होस्ट को जगाने के लिए एक मैजिक पैकेट भेजें।", + "sendWolPacketDesc": "कनेक्ट करने से पहले इस होस्ट को जगाने के लिए मैजिक पैकेट भेजें", "disableCopy": "कॉपी अक्षम करें", - "disableCopyDesc": "रिमोट सेशन से टेक्स्ट कॉपी करने से रोकें", + "disableCopyDesc": "रिमोट सत्र से टेक्स्ट कॉपी करना रोकें", "disablePaste": "पेस्ट अक्षम करें", - "disablePasteDesc": "रिमोट सेशन में टेक्स्ट पेस्ट करने से रोकें", - "createPathIfMissing": "यदि पथ मौजूद नहीं है तो उसे बनाएं", - "createPathIfMissingDesc": "रिकॉर्डिंग डायरेक्टरी स्वचालित रूप से बनाएं", - "excludeOutput": "आउटपुट को बाहर रखें", - "excludeOutputDesc": "स्क्रीन आउटपुट रिकॉर्ड न करें (केवल मेटाडेटा रिकॉर्ड करें)", - "excludeMouse": "माउस को बाहर रखें", - "excludeMouseDesc": "माउस की गतिविधियों को रिकॉर्ड न करें", + "disablePasteDesc": "रिमोट सत्र में टेक्स्ट पेस्ट करना रोकें", + "createPathIfMissing": "यदि पथ मौजूद न हो तो बनाएँ", + "createPathIfMissingDesc": "रिकॉर्डिंग निर्देशिका स्वचालित रूप से बनाएँ", + "excludeOutput": "आउटपुट बहिष्कृत करें", + "excludeOutputDesc": "स्क्रीन आउटपुट रिकॉर्ड न करें (केवल मेटाडेटा)", + "excludeMouse": "माउस बहिष्कृत करें", + "excludeMouseDesc": "माउस गतिविधियाँ रिकॉर्ड न करें", "includeKeystrokes": "कीस्ट्रोक्स शामिल करें", - "includeKeystrokesDesc": "स्क्रीन आउटपुट के अलावा मूल कीस्ट्रोक्स को भी रिकॉर्ड करें", - "vncPort": "वीएनसी पोर्ट", - "vncPassword": "वीएनसी पासवर्ड", + "includeKeystrokesDesc": "स्क्रीन आउटपुट के अतिरिक्त कच्चे कीस्ट्रोक्स रिकॉर्ड करें", + "vncPort": "VNC पोर्ट", + "vncPassword": "VNC पासवर्ड", "vncUsernameOptional": "उपयोगकर्ता नाम (वैकल्पिक)", - "vncLeaveBlank": "यदि आवश्यक न हो तो खाली छोड़ दें", + "vncLeaveBlank": "यदि आवश्यक न हो तो खाली छोड़ें", "cursorMode": "कर्सर मोड", - "swapRedBlue": "लाल/नीले रंग की अदला-बदली करें", - "swapRedBlueDesc": "लाल और नीले रंग के चैनलों को आपस में बदलें (इससे कुछ रंग संबंधी समस्याएं ठीक हो जाती हैं)", - "readOnly": "केवल पढ़ने के लिए", + "swapRedBlue": "लाल/नीला अदल-बदल करें", + "swapRedBlueDesc": "लाल और नीले रंग चैनलों को अदल-बदल करें (कुछ रंग समस्याओं को ठीक करता है)", + "readOnly": "केवल-पठनीय", "readOnlyDesc": "बिना कोई इनपुट भेजे रिमोट स्क्रीन देखें", "telnetPort": "टेलनेट पोर्ट", "terminalType": "टर्मिनल प्रकार", @@ -1365,222 +1435,285 @@ "fontSize": "फ़ॉन्ट आकार", "colorScheme": "रंग योजना", "backspaceKey": "बैकस्पेस कुंजी", - "saveHostFirst": "पहले होस्ट को सेव करें।", - "sharingOptionsAfterSave": "होस्ट को सेव करने के बाद शेयरिंग के विकल्प उपलब्ध हो जाते हैं।", + "saveHostFirst": "पहले होस्ट सहेजें।", + "sharingOptionsAfterSave": "साझाकरण विकल्प होस्ट सहेजे जाने के बाद उपलब्ध हैं।", "permissionLevel": "अनुमति स्तर", "typeHeader": "प्रकार", "targetHeader": "लक्ष्य", "permissionHeader": "अनुमति", - "cancelBtn": "रद्द करना", - "savingBtn": "सहेजा जा रहा है...", - "updateHostBtn": "होस्ट को अपडेट करें", + "cancelBtn": "रद्द करें", + "savingBtn": "सहेज रहा है...", + "updateHostBtn": "होस्ट अपडेट करें", "addHostBtn": "होस्ट जोड़ें" } }, "commandPalette": { - "searchPlaceholder": "होस्ट, कमांड या सेटिंग्स खोजें...", - "quickActions": "त्वरित कार्रवाइयां", - "hostManager": "मेजबान प्रबंधक", - "hostManagerDesc": "होस्ट को प्रबंधित करें, जोड़ें या संपादित करें", + "searchPlaceholder": "होस्ट, कमांड या सेटिंग खोजें...", + "quickActions": "त्वरित कार्य", + "hostManager": "होस्ट प्रबंधक", + "hostManagerDesc": "होस्ट प्रबंधित करें, जोड़ें या संपादित करें", "addNewHost": "नया होस्ट जोड़ें", - "addNewHostDesc": "नए होस्ट को पंजीकृत करें", - "adminSettings": "व्यवस्थापक सेटिंग्स", - "adminSettingsDesc": "सिस्टम प्राथमिकताएं और उपयोगकर्ता कॉन्फ़िगर करें", - "userProfile": "उपयोगकर्ता रूपरेखा", - "userProfileDesc": "अपना खाता और प्राथमिकताएं प्रबंधित करें", + "addNewHostDesc": "नया होस्ट पंजीकृत करें", + "adminSettings": "एडमिन सेटिंग्स", + "adminSettingsDesc": "सिस्टम वरीयताएँ और उपयोगकर्ता कॉन्फ़िगर करें", + "userProfile": "उपयोगकर्ता प्रोफ़ाइल", + "userProfileDesc": "अपना खाता और वरीयताएँ प्रबंधित करें", "addCredential": "क्रेडेंशियल जोड़ें", "addCredentialDesc": "SSH कुंजी या पासवर्ड संग्रहीत करें", "tmuxMonitor": "Tmux मॉनिटर", - "tmuxMonitorDesc": "अपने सभी होस्ट पर tmux सेशन की निगरानी करें", + "tmuxMonitorDesc": "अपने होस्ट में tmux सत्रों की निगरानी करें", "recentActivity": "हाल की गतिविधि", "serversAndHosts": "सर्वर और होस्ट", - "noHostsFound": "\"{{search}} \" से मेल खाने वाले कोई होस्ट नहीं मिले", + "noHostsFound": "\"{{search}}\" से मेल खाने वाला कोई होस्ट नहीं मिला", "links": "लिंक", - "navigate": "नेविगेट", - "select": "चुनना", - "toggleWith": "टॉगल करें" + "navigate": "नेविगेट करें", + "select": "चुनें", + "toggleWith": "इसके साथ टॉगल करें" }, "splitScreen": { - "paneEmpty": "फलक {{index}} - खाली", - "noTabAssigned": "कोई टैब आवंटित नहीं किया गया", - "focusedPane": "सक्रिय फलक" + "paneEmpty": "पैन {{index}} - खाली", + "noTabAssigned": "कोई टैब असाइन नहीं", + "focusedPane": "सक्रिय पैन" }, "connections": { "noConnections": "कोई कनेक्शन नहीं", - "noConnectionsDesc": "यहां कनेक्शन देखने के लिए टर्मिनल, फ़ाइल मैनेजर या रिमोट डेस्कटॉप खोलें।", - "connectedFor": "{{duration}} के लिए कनेक्टेड", - "connected": "जुड़े हुए", - "disconnected": "डिस्कनेक्ट किया गया", + "noConnectionsDesc": "कनेक्शन देखने के लिए टर्मिनल, फ़ाइल मैनेजर या रिमोट डेस्कटॉप खोलें", + "connectedFor": "{{duration}} से कनेक्टेड", + "connected": "कनेक्टेड", + "disconnected": "डिस्कनेक्टेड", "closeTab": "टैब बंद करें", - "closeConnection": "घनिष्ठ संबंध", - "forgetTab": "भूल जाओ", - "removeBackground": "निकालना", - "reconnect": "रिकनेक्ट", - "reopenTab": "फिर से खोलना", - "sectionOpen": "खुला", - "sectionBackground": "पृष्ठभूमि", - "backgroundDesc": "कनेक्शन टूटने के बाद भी सेशन 30 मिनट तक जारी रहता है और दोबारा कनेक्ट किया जा सकता है।", - "persisted": "पृष्ठभूमि में बना रहा", - "expiresIn": "समाप्ति तिथि {{duration}}", + "closeConnection": "कनेक्शन बंद करें", + "forgetTab": "हटाएं", + "removeBackground": "हटाएं", + "reconnect": "रीकनेक्ट", + "reopenTab": "पुनः खोलें", + "sectionOpen": "खुले हुए", + "sectionBackground": "बैकग्राउंड", + "backgroundDesc": "डिस्कनेक्ट के बाद सत्र 30 मिनट तक बने रहते हैं और पुनः कनेक्ट किए जा सकते हैं।", + "persisted": "बैकग्राउंड में बना हुआ", + "expiresIn": "{{duration}} में समाप्त होगा", "search": "कनेक्शन खोजें...", - "noSearchResults": "आपकी खोज से कोई संबंध नहीं मिला", - "rename": "सत्र का नाम बदलें" + "noSearchResults": "आपकी खोज से मेल खाने वाला कोई कनेक्शन नहीं", + "rename": "सत्र का नाम बदलें", + "sectionSharedWithMe": "मेरे साथ साझा किया गया", + "sharedBy": "{{username}} द्वारा साझा किया गया", + "join": "जोड़ना", + "sharedSessionLabel": "{{hostName}} (साझा)" + }, + "sessionSharing": { + "guestView": { + "loading": "साझा सत्र से जुड़ रहा है...", + "linkInvalid": "यह शेयर लिंक अमान्य है, इसकी समय सीमा समाप्त हो चुकी है, या इसे रद्द कर दिया गया है।", + "rateLimited": "बहुत अधिक प्रयास किए गए, कृपया कुछ देर बाद पुनः प्रयास करें।", + "sessionEnded": "यह सत्र समाप्त हो गया है।", + "readOnlyBadge": "केवल देखें" + }, + "modalTitle": "सत्र साझा करें", + "shareButton": "शेयर करना", + "notReadyToShare": "सेशन अभी शेयर करने के लिए तैयार नहीं है", + "modeTab": { + "link": "जोड़ना", + "user": "उपयोगकर्ता" + }, + "linkModeDescription": "इस लिंक के जरिए कोई भी जुड़ सकता है, इसके लिए किसी खाते की आवश्यकता नहीं है।", + "userModeDescription": "इसे उस विशिष्ट उपयोगकर्ता के साथ साझा करें जिसके पास पहले से ही इस होस्ट का एक्सेस है। यदि उनके पास अभी एक्सेस नहीं है, तो पहले उनके साथ होस्ट साझा करें या इसके बजाय एक लिंक का उपयोग करें। साझा करने के बाद, सेशन उनके कनेक्शन टैब में दिखाई देगा।", + "permissionLevel": { + "label": "अनुमति स्तर", + "readOnly": "केवल पढ़ने के लिए", + "readOnlyDescription": "आप सेशन को लाइव देख सकते हैं लेकिन टाइप या इंटरैक्ट नहीं कर सकते।", + "readWrite": "पढ़ना-लिखना", + "readWriteDescription": "मालिक की तरह ही सेशन में टाइप कर सकता है और इंटरैक्ट कर सकता है।" + }, + "expiryLabel": "लिंक की समय सीमा समाप्त", + "createLinkButton": "लिंक बनाएं", + "createShareButton": "उपयोगकर्ता के साथ साझा करें", + "searchUsersPlaceholder": "उपयोगकर्ताओं को खोजें...", + "noUsersFound": "कोई उपयोगकर्ता नहीं मिला", + "linkCreated": "शेयर लिंक बनाया गया", + "linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हो गया है", + "copyLink": "लिंक की प्रतिलिपि करें", + "shareCreated": "सेशन साझा कर दिया गया है। यह उनके कनेक्शन टैब में दिखाई देगा।", + "shareFailed": "शेयर बनाने में विफल", + "userLacksHostAccess": "उस उपयोगकर्ता के पास अभी इस होस्ट तक पहुंच नहीं है। कृपया पहले उनके साथ होस्ट साझा करें, या इसके बजाय एक लिंक का उपयोग करें।", + "activeShares": "सक्रिय शेयर", + "noActiveShares": "इस सत्र के लिए कोई सक्रिय शेयर नहीं हैं", + "revoke": "रद्द करना", + "revokeConfirmTitle": "क्या इस शेयर को रद्द कर दें?", + "revokeConfirmDescription": "इस शेयर का उपयोग करने वाले किसी भी व्यक्ति की पहुंच तुरंत समाप्त हो जाएगी।", + "revoked": "शेयर रद्द कर दिया गया", + "revokeFailed": "शेयर रद्द करने में विफल", + "joinCount": "{{count}} जुड़ें", + "joinCount_other": "{{count}} जुड़ता है", + "expiresAt": "समाप्ति तिथि {{date}}", + "linkShareBadge": "जोड़ना", + "userShareBadge": "उपयोगकर्ता: {{username}}", + "loadSharesFailed": "सक्रिय शेयर लोड करने में विफल" }, "guacamole": { "connecting": "{{type}} सत्र से कनेक्ट हो रहा है...", - "connectionError": "संपर्क त्रुटि", + "connectionError": "कनेक्शन त्रुटि", "connectionFailed": "कनेक्शन विफल", "failedToConnect": "कनेक्शन टोकन प्राप्त करने में विफल", "hostNotFound": "होस्ट नहीं मिला", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "reconnect": "रिकनेक्ट", - "retry": "पुन: प्रयास करें", - "guacdUnavailable": "रिमोट डेस्कटॉप सेवा (guacd) उपलब्ध नहीं है। कृपया सुनिश्चित करें कि guacd चल रहा है, सुलभ है और प्रशासनिक सेटिंग्स में ठीक से कॉन्फ़िगर किया गया है।", + "noHostSelected": "कोई होस्ट चयनित नहीं", + "reconnect": "रीकनेक्ट", + "retry": "पुनः प्रयास करें", + "guacdUnavailable": "रिमोट डेस्कटॉप सेवा (guacd) उपलब्ध नहीं है। कृपया सुनिश्चित करें कि guacd चल रहा है और पहुंच योग्य है और एडमिन सेटिंग्स में ठीक से कॉन्फ़िगर किया गया है।", + "credentialPromptTitle": "आरडीपी क्रेडेंशियल दर्ज करें", + "credentialPromptDescription": "इस होस्ट पर कनेक्ट होने पर क्रेडेंशियल मांगे जाएंगे। ये क्रेडेंशियल केवल इसी सेशन के लिए उपयोग किए जाएंगे और सेव नहीं किए जाएंगे।", + "connect": "जोड़ना", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", "winL": "Win+L (लॉक स्क्रीन)", - "winKey": "विंडोज़ कुंजी", - "ctrl": "कंट्रोल", + "winKey": "विंडोज की", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "बदलाव", - "win": "जीतना", - "stickyActive": "{{key}} (लॉक हो गया - छोड़ने के लिए क्लिक करें)", - "stickyInactive": "{{key}} (लॉक करने के लिए क्लिक करें)", - "esc": "पलायन", - "tab": "टैब", - "home": "घर", - "end": "अंत", - "pageUp": "पेज अप", - "pageDown": "पेज नीचे", - "arrowUp": "ऊपर की ओर तीर", - "arrowDown": "नीचे की ओर तीर", - "arrowLeft": "बाएँ तीर", - "arrowRight": "दाएँ तीर", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (लैच्ड - रिलीज़ करने के लिए क्लिक करें)", + "stickyInactive": "{{key}} (लैच करने के लिए क्लिक करें)", + "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": "फ़ंक्शन कुंजियाँ", - "reconnect": "पुनः संपर्क सत्र", - "collapse": "टूलबार को छोटा करें", - "expand": "टूलबार का विस्तार करें", - "dragHandle": "स्थान बदलने के लिए खींचें" + "reconnect": "सत्र पुनः कनेक्ट करें", + "collapse": "टूलबार समेटें", + "expand": "टूलबार विस्तारित करें", + "dragHandle": "स्थान बदलने के लिए खींचें", + "switchToTrackpad": "ट्रैकपैड मोड पर स्विच करें (कर्सर को स्थानांतरित करने के लिए ड्रैग करें, क्लिक करने के लिए टैप करें)", + "switchToTouch": "टच मोड पर स्विच करें (जहां क्लिक करना चाहते हैं वहां सीधे टैप करें)" } }, "terminal": { "connect": "होस्ट से कनेक्ट करें", - "clear": "स्पष्ट", - "paste": "पेस्ट करें", - "reconnect": "रिकनेक्ट", - "connectionLost": "कनेक्शन टूट गया", - "connected": "जुड़े हुए", - "clipboardWriteFailed": "क्लिपबोर्ड पर कॉपी करने में विफल। सुनिश्चित करें कि पृष्ठ HTTPS या लोकलहोस्ट पर चल रहा है।", - "clipboardReadFailed": "क्लिपबोर्ड से पढ़ने में विफलता। सुनिश्चित करें कि क्लिपबोर्ड की अनुमतियाँ दी गई हैं।", - "clipboardHttpWarning": "पेस्ट करने के लिए HTTPS आवश्यक है। Ctrl+Shift+V का उपयोग करें या HTTPS पर Termix को सर्व करें।", - "passwordPromptFillTitle": "इस प्रॉम्प्ट में सहेजा गया पासवर्ड भरें?", - "unknownError": "अज्ञात त्रुटि उत्पन्न हुई", - "websocketError": "वेबसॉकेट कनेक्शन त्रुटि", + "clear": "साफ़ करें", + "paste": "चिपकाएँ", + "reconnect": "पुनः कनेक्ट करें", + "connectionLost": "कनेक्शन खो गया", + "connected": "कनेक्टेड", + "clipboardWriteFailed": "क्लिपबोर्ड पर कॉपी करने में विफल। सुनिश्चित करें कि पेज HTTPS या लोकलहोस्ट पर सर्व किया गया है।", + "clipboardReadFailed": "क्लिपबोर्ड से पढ़ने में विफल। सुनिश्चित करें कि क्लिपबोर्ड अनुमतियाँ दी गई हैं।", + "clipboardHttpWarning": "चिपकाने के लिए HTTPS आवश्यक है। Ctrl+Shift+V का उपयोग करें या Termix को HTTPS पर सर्व करें।", + "passwordPromptFillTitle": "क्या सहेजे गए पासवर्ड को इस प्रांप्ट में भरें?", + "unknownError": "अज्ञात त्रुटि हुई", + "websocketError": "WebSocket कनेक्शन त्रुटि", "connecting": "कनेक्ट हो रहा है...", - "noHostSelected": "कोई होस्ट चयनित नहीं है", + "noHostSelected": "कोई होस्ट चयनित नहीं", "reconnecting": "पुनः कनेक्ट हो रहा है... ({{attempt}}/{{max}})", - "reconnected": "सफलतापूर्वक पुनः कनेक्ट हो गया", + "reconnected": "पुनः कनेक्ट हुआ", "tmuxSessionCreated": "tmux सत्र बनाया गया: {{name}}", - "tmuxSessionAttached": "tmux सत्र संलग्न: {{name}}", - "tmuxUnavailable": "रिमोट होस्ट पर tmux इंस्टॉल नहीं है, इसलिए स्टैंडर्ड शेल का उपयोग किया जा रहा है।", + "tmuxSessionAttached": "tmux सत्र अटैच किया गया: {{name}}", + "tmuxUnavailable": "tmux रिमोट होस्ट पर स्थापित नहीं है, मानक शेल पर वापस लौट रहा है", "tmuxSessionPickerTitle": "tmux सत्र", - "tmuxSessionPickerDesc": "इस होस्ट पर पहले से मौजूद tmux सेशन पाए गए हैं। पुनः जुड़ने के लिए किसी एक का चयन करें या नया सेशन बनाएं।", - "tmuxWindows": "विंडोज़", + "tmuxSessionPickerDesc": "इस होस्ट पर मौजूदा tmux सत्र पाए गए। पुनः अटैच करने के लिए एक का चयन करें या नया सत्र बनाएं।", + "tmuxWindows": "विंडो", "tmuxWindowCount": "{{count}} विंडो", - "tmuxAttached": "संलग्न ग्राहक", - "tmuxAttachedCount": "{{count}} संलग्न", + "tmuxAttached": "अटैच किए गए क्लाइंट", + "tmuxAttachedCount": "{{count}} अटैच किए गए", "tmuxLastActivity": "अंतिम गतिविधि", - "tmuxTimeJustNow": "बस अब", - "tmuxTimeMinutes": "{{count}}मीटर पहले", - "tmuxTimeHours": "{{count}}घंटे पहले", - "tmuxTimeDays": "{{count}}दिन पहले", - "tmuxCreateNew": "नया सत्र शुरू करें", - "tmuxCopyHint": "चयन को समायोजित करें और क्लिपबोर्ड पर कॉपी करने के लिए एंटर दबाएं।", - "tmuxDetach": "tmux सत्र से अलग हो जाएं", - "tmuxDetached": "tmux सत्र से अलग हो गया", - "maxReconnectAttemptsReached": "अधिकतम पुनः कनेक्शन प्रयासों की सीमा पूरी हो गई है।", - "closeTab": "बंद करना", - "connectionTimeout": "रिश्तों का समय बाहर", + "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}}", + "runTitle": "{{command}} चल रहा है - {{host}}", "totpRequired": "दो-कारक प्रमाणीकरण आवश्यक है", "totpCodeLabel": "सत्यापन कोड", "totpVerify": "सत्यापित करें", - "warpgateAuthRequired": "वॉरपगेट प्रमाणीकरण आवश्यक है", + "mfaPromptRequired": "प्रमाणित करना", + "mfaPushRequired": "पुश प्रमाणीकरण आवश्यक है", + "mfaMenuPlaceholder": "अपना जवाब दर्ज करें", + "mfaWaitingApproval": "आपके डिवाइस की स्वीकृति का इंतजार है...", + "mfaSendRequest": "अनुरोध भेजा", + "warpgateAuthRequired": "Warpgate प्रमाणीकरण आवश्यक है", "warpgateSecurityKey": "सुरक्षा कुंजी", - "warpgateAuthUrl": "प्रमाणीकरण यूआरएल", + "warpgateAuthUrl": "प्रमाणीकरण URL", "warpgateOpenBrowser": "ब्राउज़र में खोलें", "warpgateContinue": "मैंने प्रमाणीकरण पूरा कर लिया है", "opksshAuthRequired": "OPKSSH प्रमाणीकरण आवश्यक है", - "opksshAuthDescription": "आगे बढ़ने के लिए अपने ब्राउज़र में प्रमाणीकरण पूरा करें। यह सत्र 24 घंटे तक वैध रहेगा।", + "opksshAuthDescription": "जारी रखने के लिए अपने ब्राउज़र में प्रमाणीकरण पूरा करें। यह सत्र 24 घंटे के लिए मान्य रहेगा।", "opksshOpenBrowser": "प्रमाणीकरण के लिए ब्राउज़र खोलें", "opksshWaitingForAuth": "ब्राउज़र में प्रमाणीकरण की प्रतीक्षा हो रही है...", - "opksshAuthenticating": "प्रमाणीकरण प्रक्रिया जारी है...", + "opksshAuthenticating": "प्रमाणीकरण प्रक्रिया में है...", "opksshTimeout": "प्रमाणीकरण का समय समाप्त हो गया। कृपया पुनः प्रयास करें।", - "opksshAuthFailed": "प्रमाणीकरण विफल रहा। कृपया अपनी पहचान सत्यापित करें और पुनः प्रयास करें।", + "opksshAuthFailed": "प्रमाणीकरण विफल। कृपया अपने क्रेडेंशियल जाँचें और पुनः प्रयास करें।", "opksshSignInWith": "{{provider}} से साइन इन करें", - "vaultAuthTitle": "वॉल्ट में साइन-इन करना आवश्यक है", - "vaultAuthDescription": "HashiCorp Vault में साइन इन करने के लिए एक विंडो खुल गई है। कृपया वहां साइन इन करें; यह कनेक्शन स्वचालित रूप से जारी रहेगा।", - "vaultAuthFailed": "वॉल्ट प्रमाणीकरण विफल रहा। कृपया पुनः प्रयास करें।", - "vaultReopen": "साइन-इन विंडो दोबारा खोलें", + "vaultAuthTitle": "Vault साइन-इन आवश्यक है", + "vaultAuthDescription": "HashiCorp Vault में साइन इन करने के लिए एक विंडो खुल गई है। वहाँ साइन-इन पूरा करें; यह कनेक्शन स्वचालित रूप से जारी रहेगा।", + "vaultAuthFailed": "Vault प्रमाणीकरण विफल। कृपया पुनः प्रयास करें।", + "vaultReopen": "साइन-इन विंडो फिर से खोलें", "sudoPasswordPopupTitle": "पासवर्ड डालें?", - "linkDialogTitle": "खुला लिंक", - "linkDialogOpen": "खुला", - "linkDialogCopy": "प्रतिलिपि", - "websocketAbnormalClose": "कनेक्शन अप्रत्याशित रूप से बंद हो गया। यह रिवर्स प्रॉक्सी या एसएसएल कॉन्फ़िगरेशन समस्या के कारण हो सकता है। कृपया सर्वर लॉग की जाँच करें।", + "linkDialogTitle": "लिंक खोलें", + "linkDialogOpen": "खोलें", + "linkDialogCopy": "कॉपी करें", + "websocketAbnormalClose": "कनेक्शन अप्रत्याशित रूप से बंद हो गया। यह रिवर्स प्रॉक्सी या SSL कॉन्फ़िगरेशन समस्या के कारण हो सकता है। कृपया सर्वर लॉग जाँचें।", "connectionLogTitle": "कनेक्शन लॉग", - "connectionLogCopy": "लॉग को क्लिपबोर्ड पर कॉपी करें", - "connectionLogEmpty": "अभी तक कोई कनेक्शन लॉग उपलब्ध नहीं हैं।", - "connectionLogWaiting": "कनेक्शन लॉग की प्रतीक्षा की जा रही है...", - "connectionLogCopied": "कनेक्शन लॉग क्लिपबोर्ड पर कॉपी हो गए हैं", - "connectionLogCopyFailed": "लॉग फ़ाइलों को क्लिपबोर्ड पर कॉपी करने में विफल रहा।", - "connectionRejected": "सर्वर द्वारा कनेक्शन अस्वीकृत कर दिया गया है। कृपया अपनी प्रमाणीकरण और नेटवर्क कॉन्फ़िगरेशन की जाँच करें।", - "hostKeyRejected": "SSH होस्ट कुंजी सत्यापन अस्वीकृत। कनेक्शन रद्द।", - "sessionTakenOver": "सेशन दूसरे टैब में खुल गया था। पुनः कनेक्ट हो रहा है...", + "connectionLogCopy": "क्लिपबोर्ड पर लॉग कॉपी करें", + "connectionLogEmpty": "अभी तक कोई कनेक्शन लॉग नहीं", + "connectionLogWaiting": "कनेक्शन लॉग की प्रतीक्षा हो रही है...", + "connectionLogCopied": "कनेक्शन लॉग क्लिपबोर्ड पर कॉपी कर लिए गए हैं", + "connectionLogCopyFailed": "क्लिपबोर्ड पर लॉग कॉपी करने में विफल", + "connectionRejected": "सर्वर द्वारा कनेक्शन अस्वीकृत। कृपया अपनी प्रमाणीकरण और नेटवर्क कॉन्फ़िगरेशन जाँचें।", + "hostKeyRejected": "SSH होस्ट कुंजी सत्यापन अस्वीकृत। कनेक्शन रद्द किया गया।", + "sessionTakenOver": "सत्र किसी अन्य टैब में खोला गया था। पुनः कनेक्ट हो रहा है...", "split": { - "splitTab": "स्प्लिट टैब", - "addToSplit": "स्प्लिट में जोड़ें", - "removeFromSplit": "स्प्लिट से निकालें" + "splitTab": "टैब विभाजित करें", + "addToSplit": "विभाजन में जोड़ें", + "removeFromSplit": "विभाजन से हटाएँ" } }, "fileManager": { - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "initializingEditor": "एडिटर को आरंभ किया जा रहा है...", + "noHostSelected": "कोई होस्ट चयनित नहीं", + "initializingEditor": "संपादक आरंभ हो रहा है...", "file": "फ़ाइल", "folder": "फ़ोल्डर", "uploadFile": "फ़ाइल अपलोड करें", - "downloadFile": "डाउनलोड करना", + "downloadFile": "डाउनलोड करें", "extractArchive": "संग्रह निकालें", - "extractingArchive": "{{name}} को निकालना ...", + "extractingArchive": "{{name}} निकाला जा रहा है...", "archiveExtractedSuccessfully": "{{name}} सफलतापूर्वक निकाला गया", - "extractFailed": "निष्कर्षण विफल रहा", - "compressFile": "फ़ाइल को संपीड़ित करें", - "compressFiles": "फ़ाइलों को संपीड़ित करें", - "compressFilesDesc": "{{count}} आइटमों को एक संग्रह में संपीड़ित करें", - "archiveName": "संग्रह नाम", - "enterArchiveName": "आर्काइव का नाम दर्ज करें...", + "extractFailed": "निकालने में विफल", + "compressFile": "फ़ाइल संपीड़ित करें", + "compressFiles": "फ़ाइलें संपीड़ित करें", + "compressFilesDesc": "{{count}} आइटम को एक संग्रह में संपीड़ित करें", + "archiveName": "संग्रह का नाम", + "enterArchiveName": "संग्रह का नाम दर्ज करें...", "compressionFormat": "संपीड़न प्रारूप", "selectedFiles": "चयनित फ़ाइलें", - "andMoreFiles": "और {{count}} अधिक...", - "compress": "संकुचित करें", - "compressingFiles": "{{count}} आइटमों को {{name}} में संपीड़ित करना...", - "filesCompressedSuccessfully": "{{name}} सफलतापूर्वक निर्मित हुआ", - "compressFailed": "संपीड़न विफल रहा", - "edit": "संपादन करना", - "preview": "पूर्व दर्शन", - "previous": "पहले का", + "andMoreFiles": "और {{count}}...", + "compress": "संपीड़ित करें", + "compressingFiles": "{{count}} आइटम को {{name}} में संपीड़ित किया जा रहा है...", + "filesCompressedSuccessfully": "{{name}} सफलतापूर्वक बनाया गया", + "compressFailed": "संपीड़न विफल", + "edit": "संपादित करें", + "preview": "पूर्वावलोकन", + "previous": "पिछला", "next": "अगला", - "pageXOfY": "पृष्ठ {{current}} का {{total}}", + "pageXOfY": "पृष्ठ {{current}} / {{total}}", "zoomOut": "ज़ूम आउट", "zoomIn": "ज़ूम इन", "newFile": "नई फ़ाइल", "newFolder": "नया फ़ोल्डर", "rename": "नाम बदलें", "uploading": "अपलोड हो रहा है...", - "uploadingFile": "अपलोड हो रहा है {{name}}...", - "fileName": "फ़ाइल नाम", + "uploadingFile": "{{name}} अपलोड हो रहा है...", + "fileName": "फ़ाइल का नाम", "folderName": "फ़ोल्डर का नाम", "fileUploadedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक अपलोड हो गई", "failedToUploadFile": "फ़ाइल अपलोड करने में विफल", @@ -1589,723 +1722,724 @@ "fileCreatedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक बनाई गई", "folderCreatedSuccessfully": "फ़ोल्डर \"{{name}}\" सफलतापूर्वक बनाया गया", "failedToCreateItem": "आइटम बनाने में विफल", - "operationFailed": "{{operation}} ऑपरेशन {{name}}के लिए विफल रहा : {{error}}", - "failedToResolveSymlink": "सिम्लिंक को हल करने में विफल", - "itemsDeletedSuccessfully": "{{count}} आइटम सफलतापूर्वक हटा दिए गए", + "operationFailed": "{{name}} के लिए {{operation}} कार्रवाई विफल: {{error}}", + "failedToResolveSymlink": "symlink को हल करने में विफल", + "itemsDeletedSuccessfully": "{{count}} आइटम सफलतापूर्वक हटाए गए", "failedToDeleteItems": "आइटम हटाने में विफल", - "sudoPasswordRequired": "व्यवस्थापक पासवर्ड आवश्यक है", - "enterSudoPassword": "इस प्रक्रिया को जारी रखने के लिए sudo पासवर्ड दर्ज करें।", - "sudoPassword": "सूडो पासवर्ड", - "sudoOperationFailed": "Sudo ऑपरेशन विफल रहा", - "sudoAuthFailed": "सूडो प्रमाणीकरण विफल रहा", - "dragFilesToUpload": "फ़ाइलें अपलोड करने के लिए उन्हें यहाँ ड्रॉप करें", + "sudoPasswordRequired": "प्रशासक पासवर्ड आवश्यक है", + "enterSudoPassword": "इस कार्रवाई को जारी रखने के लिए sudo पासवर्ड दर्ज करें", + "sudoPassword": "sudo पासवर्ड", + "sudoOperationFailed": "sudo संचालन विफल", + "sudoAuthFailed": "sudo प्रमाणीकरण विफल", + "dragFilesToUpload": "अपलोड करने के लिए फ़ाइलें यहाँ छोड़ें", "emptyFolder": "यह फ़ोल्डर खाली है", "searchFiles": "फ़ाइलें खोजें...", "upload": "अपलोड करें", "selectHostToStart": "फ़ाइल प्रबंधन शुरू करने के लिए एक होस्ट चुनें", - "sshRequiredForFileManager": "फ़ाइल मैनेजर को SSH की आवश्यकता होती है। इस होस्ट पर SSH सक्षम नहीं है।", - "failedToConnect": "एसएसएच से कनेक्ट करने में विफल", + "sshRequiredForFileManager": "फ़ाइल प्रबंधक को SSH की आवश्यकता है। इस होस्ट में SSH सक्षम नहीं है।", + "failedToConnect": "SSH से कनेक्ट करने में विफल", "failedToLoadDirectory": "निर्देशिका लोड करने में विफल", "noSSHConnection": "कोई SSH कनेक्शन उपलब्ध नहीं है", - "copy": "प्रतिलिपि", - "cut": "काटना", - "paste": "पेस्ट करें", + "copy": "कॉपी", + "cut": "काटें", + "paste": "चिपकाएँ", "copyPath": "पथ कॉपी करें", - "copyPaths": "पथों की प्रतिलिपि बनाएँ", - "delete": "मिटाना", + "copyPaths": "पथ कॉपी करें", + "delete": "हटाएँ", "properties": "गुण", - "refresh": "ताज़ा करना", - "downloadFiles": "ब्राउज़र में {{count}} फ़ाइलें डाउनलोड करें", - "copyFiles": "आइटम कॉपी करें {{count}}", + "refresh": "ताज़ा करें", + "downloadFiles": "{{count}} फ़ाइलें ब्राउज़र पर डाउनलोड करें", + "copyFiles": "{{count}} आइटम कॉपी करें", "cutFiles": "{{count}} आइटम काटें", - "deleteFiles": "{{count}} आइटम हटाएं", + "deleteFiles": "{{count}} आइटम हटाएँ", "filesCopiedToClipboard": "{{count}} आइटम क्लिपबोर्ड पर कॉपी किए गए", - "filesCutToClipboard": "{{count}} आइटम क्लिपबोर्ड पर सेव हो गए", - "pathCopiedToClipboard": "पथ क्लिपबोर्ड पर कॉपी हो गया", + "filesCutToClipboard": "{{count}} आइटम क्लिपबोर्ड पर काटे गए", + "pathCopiedToClipboard": "पथ क्लिपबोर्ड पर कॉपी किया गया", "pathsCopiedToClipboard": "{{count}} पथ क्लिपबोर्ड पर कॉपी किए गए", "failedToCopyPath": "क्लिपबोर्ड पर पथ कॉपी करने में विफल", "copyFolderLink": "फ़ोल्डर का लिंक कॉपी करें", "copyCurrentFolderLink": "वर्तमान फ़ोल्डर का लिंक कॉपी करें", - "folderLinkCopied": "फ़ोल्डर लिंक क्लिपबोर्ड पर कॉपी हो गया है", + "folderLinkCopied": "फ़ोल्डर लिंक क्लिपबोर्ड पर कॉपी किया गया", "failedToCopyFolderLink": "फ़ोल्डर लिंक कॉपी करने में विफल", "movedItems": "{{count}} आइटम स्थानांतरित किए गए", "failedToDeleteItem": "आइटम हटाने में विफल", "itemRenamedSuccessfully": "{{type}} का नाम सफलतापूर्वक बदल दिया गया", "failedToRenameItem": "आइटम का नाम बदलने में विफल", - "download": "डाउनलोड करना", - "openExternalEditor": "बाहरी रूप से खोलें", + "download": "डाउनलोड", + "openExternalEditor": "बाह्य संपादक खोलें", "chooseExternalEditor": "बाह्य संपादक चुनें", - "externalEditorSelected": "बाह्य संपादक का चयन किया गया", - "externalEditorDesktopOnly": "बाह्य संपादक केवल डेस्कटॉप ऐप में उपलब्ध है।", - "externalEditorOpened": "इसे बाहरी संपादक में खोला गया है। इसे वहां सहेजने से परिवर्तन सर्वर पर अपलोड हो जाएंगे।", + "externalEditorSelected": "बाह्य संपादक चयनित", + "externalEditorDesktopOnly": "बाह्य संपादक केवल डेस्कटॉप ऐप में उपलब्ध है", + "externalEditorOpened": "बाह्य संपादक में खोला गया। वहाँ सहेजने पर परिवर्तन सर्वर पर वापस अपलोड हो जाएँगे।", "failedToOpenExternalEditor": "बाह्य संपादक खोलने में विफल", - "failedToSelectExternalEditor": "बाह्य संपादक का चयन करने में विफल", - "permissions": "अनुमतियां", + "failedToSelectExternalEditor": "बाह्य संपादक चुनने में विफल", + "permissions": "अनुमतियाँ", "size": "आकार", "modified": "संशोधित", "path": "पथ", - "confirmDelete": "क्या आप वाकई {{name}} को हटाना चाहते हैं?", - "permissionDenied": "अनुमति नहीं मिली", + "confirmDelete": "क्या आप वाकई {{name}} हटाना चाहते हैं?", + "permissionDenied": "अनुमति अस्वीकृत", "serverError": "सर्वर त्रुटि", - "fileSavedSuccessfully": "फ़ाइल सफलतापूर्वक सहेज ली गई", + "fileSavedSuccessfully": "फ़ाइल सफलतापूर्वक सहेजी गई", "failedToSaveFile": "फ़ाइल सहेजने में विफल", - "confirmDeleteSingleItem": "क्या आप वाकई \"{{name}} \" को स्थायी रूप से हटाना चाहते हैं?", - "confirmDeleteMultipleItems": "क्या आप वाकई {{count}} आइटम को स्थायी रूप से हटाना चाहते हैं?", - "confirmDeleteMultipleItemsWithFolders": "क्या आप वाकई {{count}} आइटम को स्थायी रूप से हटाना चाहते हैं? इसमें फ़ोल्डर और उनकी सामग्री शामिल है।", - "confirmDeleteFolder": "क्या आप वाकई फोल्डर \"{{name}}\" और उसकी सभी सामग्री को स्थायी रूप से हटाना चाहते हैं?", - "permanentDeleteWarning": "यह कार्रवाई पूर्ववत नहीं की जा सकती। आइटम सर्वर से स्थायी रूप से हटा दिए जाएंगे।", - "recent": "हाल ही का", - "pinned": "पिन की गई", + "confirmDeleteSingleItem": "क्या आप वाकई \"{{name}}\" को स्थायी रूप से हटाना चाहते हैं?", + "confirmDeleteMultipleItems": "क्या आप वाकई {{count}} आइटम स्थायी रूप से हटाना चाहते हैं?", + "confirmDeleteMultipleItemsWithFolders": "क्या आप वाकई {{count}} आइटम स्थायी रूप से हटाना चाहते हैं? इसमें फ़ोल्डर और उनकी सामग्री शामिल है।", + "confirmDeleteFolder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को स्थायी रूप से हटाना चाहते हैं?", + "permanentDeleteWarning": "यह क्रिया पूर्ववत नहीं की जा सकती। आइटम सर्वर से स्थायी रूप से हटा दिए जाएँगे।", + "recent": "हाल ही", + "pinned": "पिन किए गए", "folderShortcuts": "फ़ोल्डर शॉर्टकट", - "failedToReconnectSSH": "एसएसएच सत्र को पुनः कनेक्ट करने में विफल।", - "openTerminalHere": "टर्मिनल यहाँ खोलें", - "run": "दौड़ना", + "failedToReconnectSSH": "SSH सत्र पुनः कनेक्ट करने में विफल", + "openTerminalHere": "यहाँ टर्मिनल खोलें", + "run": "चलाएँ", "openTerminalInFolder": "इस फ़ोल्डर में टर्मिनल खोलें", "openTerminalInFileLocation": "फ़ाइल स्थान पर टर्मिनल खोलें", - "runningFile": "दौड़ना - {{file}}", - "onlyRunExecutableFiles": "केवल निष्पादन योग्य फ़ाइलें ही चला सकते हैं", - "directories": "निर्देशिका", - "removedFromRecentFiles": "हाल की फ़ाइलों से \"{{name}}\" हटा दिया गया है", - "removeFailed": "हटाने में विफलता", - "unpinnedSuccessfully": "\"{{name}}\" को सफलतापूर्वक अनपिन कर दिया गया", + "runningFile": "चल रहा है - {{file}}", + "onlyRunExecutableFiles": "केवल एक्सीक्यूटेबल फ़ाइलें ही चलाई जा सकती हैं", + "directories": "निर्देशिकाएँ", + "removedFromRecentFiles": "हाल की फ़ाइलों से \"{{name}}\" हटा दिया गया", + "removeFailed": "हटाना विफल", + "unpinnedSuccessfully": "\"{{name}}\" सफलतापूर्वक अनपिन किया गया", "unpinFailed": "अनपिन विफल", - "removedShortcut": "शॉर्टकट \"{{name}} \" हटा दिया गया", - "removeShortcutFailed": "शॉर्टकट हटाने में विफलता", - "clearedAllRecentFiles": "सभी हालिया फ़ाइलें हटा दी गईं", - "clearFailed": "क्लियर विफल", + "removedShortcut": "शॉर्टकट \"{{name}}\" हटा दिया गया", + "removeShortcutFailed": "शॉर्टकट हटाना विफल", + "clearedAllRecentFiles": "सभी हाल की फ़ाइलें साफ़ कर दी गईं", + "clearFailed": "साफ़ करना विफल", "removeFromRecentFiles": "हाल की फ़ाइलों से हटाएँ", - "clearAllRecentFiles": "सभी हालिया फ़ाइलें साफ़ करें", - "unpinFile": "फ़ाइल को अनपिन करें", + "clearAllRecentFiles": "सभी हाल की फ़ाइलें साफ़ करें", + "unpinFile": "फ़ाइल अनपिन करें", "removeShortcut": "शॉर्टकट हटाएँ", - "pinFile": "पिन फ़ाइल", + "pinFile": "फ़ाइल पिन करें", "addToShortcuts": "शॉर्टकट में जोड़ें", - "pasteFailed": "पेस्ट विफल", - "noUndoableActions": "कोई भी कार्य पूर्ववत नहीं किया जा सकता", - "undoCopySuccess": "कॉपी करने की प्रक्रिया पूर्ववत की गई: कॉपी की गई {{count}} फ़ाइलें हटाई गईं", - "undoCopyFailedDelete": "पूर्ववत करने का प्रयास विफल: प्रतिलिपि की गई कोई भी फ़ाइल हटाई नहीं जा सकी", - "undoCopyFailedNoInfo": "पूर्ववत करने में विफल: प्रतिलिपि की गई फ़ाइल की जानकारी नहीं मिल सकी", - "undoMoveSuccess": "स्थानांतरण प्रक्रिया को पूर्ववत किया गया: {{count}} फ़ाइलों को वापस मूल स्थान पर स्थानांतरित कर दिया गया", - "undoMoveFailedMove": "पूर्ववत करने में विफल: कोई भी फ़ाइल वापस नहीं ले जा सका", - "undoMoveFailedNoInfo": "पूर्ववत करने में विफल: स्थानांतरित फ़ाइल की जानकारी नहीं मिल सकी", - "undoDeleteNotSupported": "डिलीट ऑपरेशन को पूर्ववत नहीं किया जा सकता: फ़ाइलें सर्वर से स्थायी रूप से हटा दी गई हैं।", - "undoTypeNotSupported": "असमर्थित पूर्ववत करें ऑपरेशन प्रकार", - "undoOperationFailed": "अनडू ऑपरेशन विफल रहा", + "pasteFailed": "चिपकाना विफल", + "noUndoableActions": "कोई पूर्ववत करने योग्य कार्रवाई नहीं", + "undoCopySuccess": "कॉपी कार्रवाई पूर्ववत की गई: {{count}} कॉपी की गई फ़ाइलें हटा दी गईं", + "undoCopyFailedDelete": "पूर्ववत विफल: कोई भी कॉपी की गई फ़ाइल नहीं हटा सका", + "undoCopyFailedNoInfo": "पूर्ववत विफल: कॉपी की गई फ़ाइल की जानकारी नहीं मिली", + "undoMoveSuccess": "स्थानांतरण कार्रवाई पूर्ववत की गई: {{count}} फ़ाइलें वापस मूल स्थान पर ले जाई गईं", + "undoMoveFailedMove": "पूर्ववत विफल: कोई भी फ़ाइल वापस नहीं ले जा सका", + "undoMoveFailedNoInfo": "पूर्ववत विफल: स्थानांतरित फ़ाइल की जानकारी नहीं मिली", + "undoDeleteNotSupported": "हटाने की कार्रवाई पूर्ववत नहीं की जा सकती: फ़ाइलें सर्वर से स्थायी रूप से हटा दी गई हैं", + "undoTypeNotSupported": "असमर्थित पूर्ववत कार्रवाई प्रकार", + "undoOperationFailed": "पूर्ववत कार्रवाई विफल", "unknownError": "अज्ञात त्रुटि", - "confirm": "पुष्टि करना", - "find": "खोजो...", - "replace": "प्रतिस्थापित करें", + "confirm": "पुष्टि करें", + "find": "खोजें...", + "replace": "बदलें", "downloadInstead": "इसके बजाय डाउनलोड करें", - "keyboardShortcuts": "कुंजीपटल अल्प मार्ग", + "keyboardShortcuts": "कीबोर्ड शॉर्टकट्स", "searchAndReplace": "खोजें और बदलें", "editing": "संपादन", - "search": "खोज", - "findNext": "दूसरा खोजो", + "search": "खोजें", + "findNext": "अगला खोजें", "findPrevious": "पिछला खोजें", - "save": "बचाना", - "selectAll": "सबका चयन करें", - "undo": "पूर्ववत", - "redo": "फिर से करना", - "moveLineUp": "लाइन अप को आगे बढ़ाएं", - "moveLineDown": "लाइन को नीचे ले जाएं", - "toggleComment": "टिप्पणी को टॉगल करें", - "autoComplete": "स्वतः पूर्ण", + "save": "सहेजें", + "selectAll": "सभी चुनें", + "undo": "पूर्ववत करें", + "redo": "फिर से करें", + "moveLineUp": "पंक्ति ऊपर ले जाएँ", + "moveLineDown": "पंक्ति नीचे ले जाएँ", + "toggleComment": "टिप्पणी टॉगल करें", + "autoComplete": "स्वतः पूर्ण करें", "imageLoadError": "छवि लोड करने में विफल", "startTyping": "टाइप करना शुरू करें...", "unknownSize": "अज्ञात आकार", "fileIsEmpty": "फ़ाइल खाली है", - "largeFileWarning": "बड़ी फ़ाइल संबंधी चेतावनी", - "largeFileWarningDesc": "इस फ़ाइल का आकार {{size}} है, जिससे टेक्स्ट के रूप में खोलने पर प्रदर्शन संबंधी समस्याएँ उत्पन्न हो सकती हैं।", - "fileNotFoundAndRemoved": "फ़ाइल \"{{name}}\" नहीं मिली और इसे हाल ही में/पिन की गई फ़ाइलों से हटा दिया गया है", + "largeFileWarning": "बड़ी फ़ाइल की चेतावनी", + "largeFileWarningDesc": "यह फ़ाइल {{size}} आकार की है, जिसे टेक्स्ट के रूप में खोलने पर प्रदर्शन समस्याएँ हो सकती हैं।", + "fileNotFoundAndRemoved": "फ़ाइल \"{{name}}\" नहीं मिली और हाल की/पिन की गई फ़ाइलों से हटा दी गई है", "failedToLoadFile": "फ़ाइल लोड करने में विफल: {{error}}", - "serverErrorOccurred": "सर्वर में त्रुटि आ गई है। कृपया बाद में पुनः प्रयास करें।", - "autoSaveFailed": "ऑटो-सेव विफल रहा", + "serverErrorOccurred": "सर्वर त्रुटि हुई। कृपया बाद में पुनः प्रयास करें।", + "autoSaveFailed": "स्वतः सहेजना विफल", "fileAutoSaved": "फ़ाइल स्वतः सहेजी गई", "moveFileFailed": "{{name}} को स्थानांतरित करने में विफल", - "moveOperationFailed": "स्थानांतरण प्रक्रिया विफल रही", - "canOnlyCompareFiles": "केवल दो फाइलों की तुलना की जा सकती है", + "moveOperationFailed": "स्थानांतरण क्रिया विफल", + "canOnlyCompareFiles": "केवल दो फ़ाइलों की तुलना की जा सकती है", "comparingFiles": "फ़ाइलों की तुलना: {{file1}} और {{file2}}", - "dragFailed": "ड्रैग ऑपरेशन विफल रहा", - "filePinnedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक पिन कर दी गई", - "pinFileFailed": "फ़ाइल को पिन करने में विफल", - "fileUnpinnedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक अनपिन कर दी गई", - "unpinFileFailed": "फ़ाइल को अनपिन करने में विफल", - "shortcutAddedSuccessfully": "फ़ोल्डर शॉर्टकट \"{{name}}\" सफलतापूर्वक जोड़ दिया गया", + "dragFailed": "खींचने की क्रिया विफल", + "filePinnedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक पिन की गई", + "pinFileFailed": "फ़ाइल पिन करने में विफल", + "fileUnpinnedSuccessfully": "फ़ाइल \"{{name}}\" अनपिन की गई सफलतापूर्वक", + "unpinFileFailed": "फ़ाइल अनपिन करने में विफल", + "shortcutAddedSuccessfully": "फ़ोल्डर शॉर्टकट \"{{name}}\" सफलतापूर्वक जोड़ा गया", "addShortcutFailed": "शॉर्टकट जोड़ने में विफल", "operationCompletedSuccessfully": "{{operation}} {{count}} आइटम सफलतापूर्वक", "operationCompleted": "{{operation}} {{count}} आइटम", - "downloadFileSuccess": "फ़ाइल {{name}} सफलतापूर्वक डाउनलोड हो गई", + "downloadFileSuccess": "फ़ाइल {{name}} सफलतापूर्वक डाउनलोड हुई", "downloadFileFailed": "डाउनलोड विफल", - "moveTo": "{{name}} पर जाएँ", - "diffCompareWith": "{{name}} के साथ अंतर की तुलना करें", - "dragOutsideToDownload": "फ़ाइलें डाउनलोड करने के लिए विंडो के बाहर खींचें ({{count}} फ़ाइलें)", + "moveTo": "{{name}} में ले जाएँ", + "diffCompareWith": "{{name}} के साथ डिफ तुलना करें", + "dragOutsideToDownload": "डाउनलोड करने के लिए विंडो से बाहर खींचें ({{count}} फ़ाइलें)", "newFolderDefault": "नया फ़ोल्डर", - "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} आइटम सफलतापूर्वक {{target}} में स्थानांतरित कर दिए गए हैं।", - "move": "कदम", + "newFileDefault": "नई फ़ाइल.txt", + "successfullyMovedItems": "{{count}} आइटम सफलतापूर्वक {{target}} में ले जाए गए", + "move": "स्थानांतरित करें", "searchInFile": "फ़ाइल में खोजें (Ctrl+F)", "showKeyboardShortcuts": "कीबोर्ड शॉर्टकट दिखाएँ", - "decreaseFontSize": "फ़ॉन्ट का आकार घटाएँ", - "increaseFontSize": "फ़ॉन्ट का आकार बढ़ाएँ", - "startWritingMarkdown": "अपना मार्कडाउन कंटेंट लिखना शुरू करें...", + "decreaseFontSize": "फ़ॉन्ट आकार घटाएँ", + "increaseFontSize": "फ़ॉन्ट आकार बढ़ाएँ", + "startWritingMarkdown": "अपनी Markdown सामग्री लिखना शुरू करें...", "loadingFileComparison": "फ़ाइल तुलना लोड हो रही है...", "reload": "पुनः लोड करें", - "compare": "तुलना करना", - "sideBySide": "अगल बगल", - "inline": "इन - लाइन", + "compare": "तुलना करें", + "sideBySide": "साथ-साथ", + "inline": "इनलाइन", "fileComparison": "फ़ाइल तुलना: {{file1}} बनाम {{file2}}", "fileTooLarge": "फ़ाइल बहुत बड़ी है: {{error}}", - "sshConnectionFailed": "SSH कनेक्शन विफल हो गया। कृपया {{name}} ({{ip}}:{{port}} ) से अपना कनेक्शन जांचें।", + "sshConnectionFailed": "SSH कनेक्शन विफल। कृपया {{name}} ({{ip}}:{{port}}) से अपना कनेक्शन जाँचें।", "loadFileFailed": "फ़ाइल लोड करने में विफल: {{error}}", "connecting": "कनेक्ट हो रहा है...", - "connectedSuccessfully": "सफलतापूर्वक कनेक्ट हो गया", - "totpVerificationFailed": "टीओटीपी सत्यापन विफल रहा", - "warpgateVerificationFailed": "वॉरपगेट प्रमाणीकरण विफल रहा", - "authenticationFailed": "प्रमाणीकरण विफल होना", + "connectedSuccessfully": "सफलतापूर्वक कनेक्ट हुआ", + "totpVerificationFailed": "TOTP सत्यापन विफल", + "warpgateVerificationFailed": "Warpgate सत्यापन विफल", + "authenticationFailed": "प्रमाणीकरण विफल", "incorrectPassphrase": "गलत पासफ़्रेज़। कृपया पुनः प्रयास करें।", "verificationCodePrompt": "सत्यापन कोड:", "changePermissions": "अनुमतियाँ बदलें", "currentPermissions": "वर्तमान अनुमतियाँ", - "owner": "मालिक", + "owner": "स्वामी", "group": "समूह", "others": "अन्य", - "read": "पढ़ना", - "write": "लिखना", - "execute": "निष्पादित करना", + "read": "पठन", + "write": "लेखन", + "execute": "निष्पादन", "permissionsChangedSuccessfully": "अनुमतियाँ सफलतापूर्वक बदल दी गईं", "failedToChangePermissions": "अनुमतियाँ बदलने में विफल", "name": "नाम", "sortByName": "नाम", - "sortByDate": "डेटा संशोधित", + "sortByDate": "संशोधन तिथि", "sortBySize": "आकार", "ascending": "आरोही", "descending": "अवरोही", - "root": "जड़", + "root": "रूट", "new": "नया", - "sortBy": "इसके अनुसार क्रमबद्ध करें", - "items": "सामान", + "sortBy": "क्रमबद्ध करें", + "items": "आइटम", "selected": "चयनित", "editor": "संपादक", - "octal": "अष्टभुजाकार", + "octal": "ऑक्टल", "storage": "भंडारण", "disk": "डिस्क", - "used": "इस्तेमाल किया गया", - "of": "का", + "used": "प्रयुक्त", + "of": "में से", "toggleSidebar": "साइडबार टॉगल करें", - "cannotLoadPdf": "पीडीएफ लोड नहीं हो पा रही है", - "pdfLoadError": "इस पीडीएफ फाइल को लोड करने में त्रुटि हुई।", - "loadingPdf": "पीडीएफ लोड हो रही है...", - "loadingPage": "लोडिंग पृष्ठ..." + "cannotLoadPdf": "PDF लोड नहीं कर सकता", + "pdfLoadError": "इस PDF फ़ाइल को लोड करने में त्रुटि हुई।", + "loadingPdf": "PDF लोड हो रहा है...", + "loadingPage": "पृष्ठ लोड हो रहा है..." }, "transfer": { - "copyToHost": "होस्ट को कॉपी करें…", - "moveToHost": "होस्ट पर जाएँ…", - "copyItemsToHost": "आइटमों को होस्ट… पर कॉपी करें {{count}}", - "moveItemsToHost": "{{count}} आइटम को होस्ट… पर ले जाएं", - "noHostsConnected": "कोई अन्य फाइल-मैनेजर होस्ट उपलब्ध नहीं है।", - "noHostsConnectedHint": "होस्ट मैनेजर में फाइल मैनेजर सक्षम करके एक और एसएसएच होस्ट जोड़ें।", - "selectDestinationHost": "गंतव्य होस्ट का चयन करें", + "copyToHost": "होस्ट पर कॉपी करें…", + "moveToHost": "होस्ट पर ले जाएँ…", + "copyItemsToHost": "{{count}} आइटम होस्ट पर कॉपी करें…", + "moveItemsToHost": "{{count}} आइटम होस्ट पर स्थानांतरित करें…", + "noHostsConnected": "कोई अन्य फ़ाइल प्रबंधक होस्ट उपलब्ध नहीं है।", + "noHostsConnectedHint": "होस्ट प्रबंधक में फ़ाइल प्रबंधक सक्षम किए हुए एक और SSH होस्ट जोड़ें।", + "selectDestinationHost": "गंतव्य होस्ट चुनें", "destinationPath": "गंतव्य पथ", "recentDestinations": "हाल के गंतव्य", - "collapseRecentDestinations": "हाल के गंतव्यों को संक्षिप्त करें", - "expandRecentDestinations": "हाल के गंतव्यों का विस्तार करें", - "browseFolders": "गंतव्य फ़ोल्डरों को ब्राउज़ करें", - "browseDestination": "ब्राउज़ करें या पथ दर्ज करें", - "confirmCopy": "प्रतिलिपि", - "confirmMove": "कदम", - "transferring": "स्थानांतरण…", - "compressing": "संपीड़ित करना…", - "extracting": "… को निकालना", - "transferringItems": "Transferring {{current}} of {{total}} items…", - "transferSuccess": "स्थानांतरण पूरा हुआ", + "collapseRecentDestinations": "हाल के गंतव्य संकुचित करें", + "expandRecentDestinations": "हाल के गंतव्य विस्तारित करें", + "browseFolders": "गंतव्य फ़ोल्डर ब्राउज़ करें", + "browseDestination": "पथ ब्राउज़ करें या दर्ज करें", + "confirmCopy": "कॉपी करें", + "confirmMove": "स्थानांतरित करें", + "transferring": "स्थानांतरित हो रहा है…", + "compressing": "संपीड़ित किया जा रहा है…", + "extracting": "निकाला जा रहा है…", + "transferringItems": "{{total}} में से {{current}} आइटम स्थानांतरित हो रहे हैं…", + "transferSuccess": "स्थानांतरण पूर्ण", "transferError": "स्थानांतरण विफल", - "transferPartial": "स्थानांतरण {{count}} त्रुटियों के साथ पूरा हुआ", - "transferPartialHint": "स्थानांतरण नहीं हो सका: {{paths}}", + "transferPartial": "स्थानांतरण {{count}} त्रुटियों के साथ पूर्ण हुआ", + "transferPartialHint": "स्थानांतरित नहीं किया जा सका: {{paths}}", "itemsSummary": "{{count}} आइटम", - "destMustBeDirectory": "एक से अधिक वस्तुओं के स्थानांतरण के लिए गंतव्य एक निर्देशिका होनी चाहिए।", - "selectThisFolder": "इस फ़ोल्डर का चयन करें", - "browsePathWillBeCreated": "यह फ़ोल्डर अभी मौजूद नहीं है। स्थानांतरण शुरू होने पर यह बन जाएगा।", + "destMustBeDirectory": "कई आइटम स्थानांतरण के लिए गंतव्य एक निर्देशिका होना चाहिए", + "selectThisFolder": "यह फ़ोल्डर चुनें", + "browsePathWillBeCreated": "यह फ़ोल्डर अभी मौजूद नहीं है। स्थानांतरण शुरू होने पर इसे बनाया जाएगा।", "browsePathError": "गंतव्य होस्ट पर यह पथ नहीं खोला जा सका।", - "goUp": "ऊपर जाना", - "copyFolderToHost": "फ़ोल्डर को होस्ट पर कॉपी करें…", - "moveFolderToHost": "फ़ोल्डर को होस्ट… पर ले जाएं", + "goUp": "ऊपर जाएं", + "copyFolderToHost": "फ़ोल्डर होस्ट पर कॉपी करें…", + "moveFolderToHost": "फ़ोल्डर होस्ट पर स्थानांतरित करें…", "hostReady": "तैयार", - "hostConnecting": "कनेक्टिंग…", - "hostDisconnected": "जुड़े नहीं हैं", - "hostAuthRequired": "प्रमाणीकरण आवश्यक है — पहले इस होस्ट पर फ़ाइल मैनेजर खोलें", + "hostConnecting": "कनेक्ट हो रहा है…", + "hostDisconnected": "कनेक्ट नहीं है", + "hostAuthRequired": "प्रमाणीकरण आवश्यक है — पहले इस होस्ट पर फ़ाइल प्रबंधक खोलें", "hostConnectionFailed": "कनेक्शन विफल", "metricsTitle": "स्थानांतरण समय", - "metricsPrepare": "गंतव्य तैयार करें: {{duration}}", - "metricsCompress": "स्रोत पर संपीड़ित करें: {{duration}}", + "metricsPrepare": "गंतव्य तैयारी: {{duration}}", + "metricsCompress": "स्रोत पर संपीड़न: {{duration}}", "metricsHopSourceRead": "स्रोत → सर्वर: {{throughput}}", "metricsHopDestSftpWrite": "सर्वर → गंतव्य (SFTP): {{throughput}}", "metricsHopDestLocalWrite": "सर्वर → गंतव्य (स्थानीय): {{throughput}}", - "metricsTransfer": "अंत से अंत तक: {{throughput}} ({{duration}})", + "metricsTransfer": "एंड-टू-एंड: {{throughput}} ({{duration}})", "metricsExtract": "गंतव्य पर निष्कर्षण: {{duration}}", - "metricsSourceDelete": "स्रोत से हटाएँ: {{duration}}", + "metricsSourceDelete": "स्रोत से हटाना: {{duration}}", "metricsTotal": "कुल: {{duration}}", - "progressCompressing": "स्रोत होस्ट पर संपीड़न…", - "progressExtracting": "गंतव्य… पर निष्कर्षण", - "progressTransferring": "डेटा स्थानांतरित करना…", + "progressCompressing": "स्रोत होस्ट पर संपीड़ित कर रहा है…", + "progressExtracting": "गंतव्य पर निष्कर्षण कर रहा है…", + "progressTransferring": "डेटा स्थानांतरित कर रहा है…", "progressReconnecting": "पुनः कनेक्ट हो रहा है…", "parallelSegmentsLabel": "समानांतर स्थानांतरण लेन", "parallelSegmentsOption": "{{count}} लेन", - "parallelSegmentsHint": "बड़ी फाइलों को 256 एमबी के टुकड़ों में विभाजित किया जाता है। उच्च कुल थ्रूपुट के लिए कई लेन अलग-अलग कनेक्शनों का उपयोग करते हैं (जैसे कई स्थानांतरण शुरू करना)।", - "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", - "progressTransferringItems": "फ़ाइलें स्थानांतरित करना ({{current}} का {{total}})…", + "parallelSegmentsHint": "बड़ी फ़ाइलें 256 MB टुकड़ों में विभाजित होती हैं। एकाधिक लेन उच्च कुल थ्रूपुट के लिए अलग-अलग कनेक्शन का उपयोग करती हैं (जैसे कई स्थानांतरण प्रारंभ करना)।", + "progressTotalSpeed": "{{speed}} कुल ({{lanes}} लेन)", + "progressTransferringItems": "फ़ाइलें स्थानांतरित कर रहा है ({{current}} / {{total}})…", "progressBytes": "{{transferred}} / {{total}}", "progressItems": "{{current}} / {{total}} फ़ाइलें", - "sourceNotDeletedPartial": "मूल फाइलें सुरक्षित रखी गई हैं (आंशिक स्थानांतरण)", - "jumpHostLimitation": "टर्मिक्स सर्वर से दोनों होस्ट तक पहुंच होनी चाहिए। होस्ट-टू-होस्ट डायरेक्ट रूटिंग समर्थित नहीं है।", - "cancel": "रद्द करना", + "sourceNotDeletedPartial": "स्रोत फ़ाइलें रखी गईं (आंशिक स्थानांतरण)", + "jumpHostLimitation": "दोनों होस्ट Termix सर्वर से पहुँच योग्य होने चाहिए। सीधा होस्ट-से-होस्ट रूटिंग समर्थित नहीं है।", + "cancel": "रद्द करें", "methodLabel": "स्थानांतरण विधि", - "methodAuto": "ऑटो", - "methodTar": "टार संग्रह", + "methodAuto": "स्वचालित", + "methodTar": "Tar संग्रह", "methodItemSftp": "प्रति-फ़ाइल SFTP", - "methodAutoHint": "यह फ़ाइल संख्या, आकार और संपीड़नीयता के आधार पर tar या प्रति-फ़ाइल SFTP का चयन करता है। एकल फ़ाइलें हमेशा स्ट्रीमिंग SFTP का उपयोग करती हैं।", - "methodTarHint": "स्रोत पर संपीड़न करें, एक संग्रह स्थानांतरित करें, गंतव्य पर निष्कर्षण करें। दोनों यूनिक्स होस्ट पर टार फ़ाइल का होना आवश्यक है।", - "methodItemSftpHint": "प्रत्येक फ़ाइल को SFTP के माध्यम से अलग-अलग स्थानांतरित करें। यह विंडोज़ सहित सभी होस्ट पर काम करता है।", - "methodPreviewLoading": "स्थानांतरण विधि की गणना…", - "methodPreviewError": "स्थानांतरण विधि का पूर्वावलोकन नहीं किया जा सका। सर्वर प्रारंभ होने पर विधि का चयन करेगा।", - "methodPreviewWillUseTar": "उपयोग करेगा: टार संग्रह", - "methodPreviewWillUseItemSftp": "उपयोग किया जाएगा: प्रति-फ़ाइल SFTP", - "methodPreviewScanSummary": "{{fileCount}} फाइलें, {{totalSize}} कुल (स्रोत होस्ट पर स्कैन किया गया)।", - "methodItemSftpLimitation": "प्रत्येक फ़ाइल एक के बाद एक, सिंगल-फ़ाइल कॉपी के रूप में एक ही SFTP स्ट्रीम का उपयोग करती है। सभी फ़ाइलों की प्रगति को एक साथ जोड़ा जाता है, इसलिए बड़ी फ़ाइलों के दौरान बार धीरे-धीरे चलता है।", + "methodAutoHint": "फ़ाइलों की संख्या, आकार और संपीड़न क्षमता के आधार पर tar या प्रति-फ़ाइल SFTP चुनता है। एकल फ़ाइलें हमेशा स्ट्रीमिंग SFTP का उपयोग करती हैं।", + "methodTarHint": "स्रोत पर संपीड़ित करें, एक संग्रह स्थानांतरित करें, गंतव्य पर निष्कर्षण करें। दोनों यूनिक्स होस्ट पर tar आवश्यक है।", + "methodItemSftpHint": "प्रत्येक फ़ाइल को SFTP पर अलग-अलग स्थानांतरित करें। विंडोज़ सहित सभी होस्ट पर काम करता है।", + "methodPreviewLoading": "स्थानांतरण विधि की गणना कर रहा है…", + "methodPreviewError": "स्थानांतरण विधि का पूर्वावलोकन नहीं किया जा सका। प्रारंभ करने पर सर्वर फिर भी एक विधि चुनेगा।", + "methodPreviewWillUseTar": "उपयोग करेगा: Tar संग्रह", + "methodPreviewWillUseItemSftp": "उपयोग करेगा: प्रति-फ़ाइल SFTP", + "methodPreviewScanSummary": "{{fileCount}} फ़ाइलें, कुल {{totalSize}} (स्रोत होस्ट पर स्कैन किया गया)।", + "methodItemSftpLimitation": "प्रत्येक फ़ाइल एक-एक करके एकल-फ़ाइल प्रतिलिपि के रूप में समान SFTP स्ट्रीम का उपयोग करती है। सभी फ़ाइलों में प्रगति संयुक्त होती है, इसलिए बड़ी फ़ाइलों के दौरान बार धीरे-धीरे चलता है।", "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}}) — टार प्रति-फाइल ओवरहेड को कम करता है।", + "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।" }, - "progressCancel": "रद्द करना", - "progressCancelling": "रद्द करना…", - "progressStalled": "ठप", - "resumedHint": "किसी अन्य विंडो में शुरू किए गए सक्रिय स्थानांतरण से पुनः कनेक्ट हो गया।", - "transferCancelled": "स्थानांतरण रद्द", - "transferCancelledCopyHint": "गंतव्य स्थान पर कुछ फाइलें अधूरी रह सकती हैं।", - "transferCancelledMoveHint": "गंतव्य स्थान पर कुछ फाइलें अधूरी रह सकती हैं। स्रोत फाइलें हटाई नहीं गई हैं।", - "cleanupDestFiles": "सफाई गंतव्य", - "cleanupDestFilesSuccess": "गंतव्य स्थान से आंशिक फ़ाइलें हटा दी गईं", - "cleanupDestFilesPartial": "कुछ आंशिक फ़ाइलें हटाई नहीं जा सकीं", - "cleanupDestFilesNothing": "गंतव्य पर सफाई करने के लिए कुछ भी नहीं है।", - "cleanupDestFilesError": "सफाई विफल रही", - "retryTransfer": "पुन: प्रयास करें", - "retryTransferError": "पुनः प्रयास विफल रहा", - "transferFailedRetryHint": "गंतव्य स्थान पर आंशिक डेटा सुरक्षित रखा गया है। कनेक्शन बहाल होने पर पुनः प्रयास शुरू होगा।" + "progressCancel": "रद्द करें", + "progressCancelling": "रद्द कर रहा है…", + "progressStalled": "रुका हुआ", + "resumedHint": "किसी अन्य विंडो में शुरू हुए सक्रिय स्थानांतरण से पुनः जुड़ा।", + "transferCancelled": "स्थानांतरण रद्द किया गया।", + "transferCancelledCopyHint": "आंशिक फ़ाइलें गंतव्य पर रह सकती हैं।", + "transferCancelledMoveHint": "आंशिक फ़ाइलें गंतव्य पर रह सकती हैं। स्रोत फ़ाइलें नहीं हटाई गईं।", + "cleanupDestFiles": "गंतव्य साफ करें", + "cleanupDestFilesSuccess": "गंतव्य से आंशिक फ़ाइलें हटा दी गईं।", + "cleanupDestFilesPartial": "कुछ आंशिक फ़ाइलें नहीं हटाई जा सकीं।", + "cleanupDestFilesNothing": "गंतव्य पर साफ करने के लिए कुछ नहीं।", + "cleanupDestFilesError": "सफाई विफल रही।", + "retryTransfer": "पुनः प्रयास करें", + "retryTransferError": "पुनः प्रयास विफल रहा।", + "transferFailedRetryHint": "आंशिक डेटा गंतव्य पर रखा गया था। कनेक्शन वापस आने पर पुनः प्रयास फिर से शुरू होगा।" }, "tunnels": { - "noSshTunnels": "कोई एसएसएच टनल नहीं", - "createFirstTunnelMessage": "आपने अभी तक कोई SSH टनल नहीं बनाई है। शुरुआत करने के लिए होस्ट मैनेजर में टनल कनेक्शन कॉन्फ़िगर करें।", - "connected": "जुड़े हुए", - "disconnected": "डिस्कनेक्ट किया गया", + "noSshTunnels": "कोई SSH टनल नहीं", + "createFirstTunnelMessage": "आपने अभी तक कोई SSH टनल नहीं बनाई है। शुरू करने के लिए होस्ट प्रबंधक में टनल कनेक्शन कॉन्फ़िगर करें।", + "connected": "कनेक्टेड", + "disconnected": "डिस्कनेक्टेड", "connecting": "कनेक्ट हो रहा है...", - "error": "गलती", - "canceling": "रद्द किया जा रहा है...", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "cancel": "रद्द करना", - "port": "पत्तन", - "localPort": "स्थानीय बंदरगाह", - "remotePort": "रिमोट पोर्ट", + "error": "त्रुटि", + "canceling": "रद्द कर रहा है...", + "connect": "कनेक्ट करें", + "disconnect": "डिस्कनेक्ट करें", + "cancel": "रद्द करें", + "port": "पोर्ट", + "localPort": "स्थानीय पोर्ट", + "remotePort": "दूरस्थ पोर्ट", "currentHostPort": "वर्तमान होस्ट पोर्ट", "endpointPort": "एंडपॉइंट पोर्ट", "bindIp": "स्थानीय आईपी", - "endpointSshConfig": "एंडपॉइंट एसएसएच कॉन्फ़िगरेशन", - "endpointSshHost": "एंडपॉइंट एसएसएच होस्ट", - "endpointSshHostPlaceholder": "एक कॉन्फ़िगर किए गए होस्ट का चयन करें", - "endpointSshHostRequired": "प्रत्येक क्लाइंट टनल के लिए एक एंडपॉइंट एसएसएच होस्ट का चयन करें।", - "attempt": "प्रयास {{current}} का {{max}}", - "nextRetryIn": "अगला प्रयास {{seconds}} सेकंड में होगा", - "clientTunnels": "क्लाइंट टनल", + "endpointSshConfig": "एंडपॉइंट SSH कॉन्फ़िगरेशन", + "endpointSshHost": "एंडपॉइंट SSH होस्ट", + "endpointSshHostPlaceholder": "कॉन्फ़िगर किया गया होस्ट चुनें", + "endpointSshHostRequired": "प्रत्येक क्लाइंट टनल के लिए एक एंडपॉइंट SSH होस्ट चुनें।", + "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 के बीच होना चाहिए।", + "noClientTunnels": "इस डेस्कटॉप पर कोई क्लाइंट टनल कॉन्फ़िगर नहीं है।", + "tunnelName": "टनल का नाम", + "remoteHost": "दूरस्थ होस्ट", + "autoStart": "स्वतः आरंभ", + "clientAutoStartDesc": "यह डेस्कटॉप क्लाइंट खुलने पर आरंभ होता है और जुड़ा रहता है।", + "clientManualStartDesc": "इस पंक्ति से आरंभ और रोकें का उपयोग करें। Termix इसे स्वचालित रूप से नहीं खोलेगा।", + "clientRemoteServerNote": "दूरस्थ अग्रेषण के लिए एंडपॉइंट SSH सर्वर पर AllowTcpForwarding और GatewayPorts की आवश्यकता हो सकती है। यह डेस्कटॉप डिस्कनेक्ट होने पर दूरस्थ पोर्ट बंद हो जाता है।", + "clientTunnelStarted": "क्लाइंट टनल आरंभ हो गई", + "clientTunnelStopped": "क्लाइंट टनल रुक गई", + "tunnelTestSucceeded": "टनल परीक्षण सफल", + "tunnelTestFailed": "टनल परीक्षण विफल", + "localSaved": "क्लाइंट टनलें सहेजी गईं", + "localSaveError": "स्थानीय क्लाइंट टनल सहेजने में विफल", + "invalidBindIp": "स्थानीय आईपी एक मान्य IPv4 पता होना चाहिए।", + "invalidLocalTargetIp": "स्थानीय लक्ष्य आईपी एक मान्य IPv4 पता होना चाहिए।", + "invalidLocalPort": "स्थानीय पोर्ट 1 और 65535 के बीच होना चाहिए।", + "invalidRemotePort": "दूरस्थ पोर्ट 1 और 65535 के बीच होना चाहिए।", "invalidLocalTargetPort": "स्थानीय लक्ष्य पोर्ट 1 और 65535 के बीच होना चाहिए।", "invalidEndpointPort": "एंडपॉइंट पोर्ट 1 और 65535 के बीच होना चाहिए।", - "duplicateAutoStartBind": "केवल एक ऑटो-स्टार्ट क्लाइंट टनल {{bind}} का उपयोग कर सकता है।", - "manualControlError": "टनल की स्थिति अपडेट करने में विफल।", + "duplicateAutoStartBind": "केवल एक स्वतः-आरंभ क्लाइंट टनल {{bind}} का उपयोग कर सकती है।", + "manualControlError": "टनल स्थिति अपडेट करने में विफल।", "active": "सक्रिय", - "start": "शुरू", - "stop": "रुकना", - "test": "परीक्षा", - "type": "सुरंग प्रकार", - "typeLocal": "स्थानीय (-एल)", - "typeRemote": "रिमोट (-R)", - "typeDynamic": "गतिशील (-डी)", + "start": "शुरू करें", + "stop": "बंद करें", + "test": "परीक्षण करें", + "type": "टनल प्रकार", + "typeLocal": "स्थानीय (-L)", + "typeRemote": "दूरस्थ (-R)", + "typeDynamic": "डायनामिक (-D)", "typeServerLocalDesc": "वर्तमान होस्ट से एंडपॉइंट तक।", - "typeServerRemoteDesc": "एंडपॉइंट वापस वर्तमान होस्ट पर।", + "typeServerRemoteDesc": "एंडपॉइंट से वर्तमान होस्ट तक वापस।", "typeClientLocalDesc": "स्थानीय कंप्यूटर से एंडपॉइंट तक।", - "typeClientRemoteDesc": "एंडपॉइंट वापस स्थानीय कंप्यूटर पर।", + "typeClientRemoteDesc": "एंडपॉइंट से स्थानीय कंप्यूटर तक वापस।", "typeClientDynamicDesc": "स्थानीय कंप्यूटर पर SOCKS।", - "typeDynamicDesc": "SSH के माध्यम से SOCKS5 CONNECT ट्रैफ़िक को आगे भेजें", - "forwardDescriptionServerLocal": "वर्तमान होस्ट {{sourcePort}} → एंडपॉइंट {{endpointPort}}.", - "forwardDescriptionServerRemote": "एंडपॉइंट {{endpointPort}} → वर्तमान होस्ट {{sourcePort}}.", - "forwardDescriptionServerDynamic": "वर्तमान होस्ट पर मोज़े {{sourcePort}}.", - "forwardDescriptionClientLocal": "स्थानीय {{sourcePort}} → दूरस्थ {{endpointPort}}.", - "forwardDescriptionClientRemote": "रिमोट {{sourcePort}} → लोकल {{endpointPort}}.", - "forwardDescriptionClientDynamic": "स्थानीय पोर्ट {{sourcePort}} पर SOCKS .", + "typeDynamicDesc": "SSH के माध्यम से SOCKS5 CONNECT ट्रैफ़िक अग्रेषित करें", + "forwardDescriptionServerLocal": "वर्तमान होस्ट {{sourcePort}} → एंडपॉइंट {{endpointPort}}।", + "forwardDescriptionServerRemote": "एंडपॉइंट {{endpointPort}} → वर्तमान होस्ट {{sourcePort}}।", + "forwardDescriptionServerDynamic": "वर्तमान होस्ट {{sourcePort}} पर SOCKS।", + "forwardDescriptionClientLocal": "स्थानीय {{sourcePort}} → दूरस्थ {{endpointPort}}।", + "forwardDescriptionClientRemote": "दूरस्थ {{sourcePort}} → स्थानीय {{endpointPort}}।", + "forwardDescriptionClientDynamic": "स्थानीय पोर्ट {{sourcePort}} पर SOCKS।", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → मोज़े वाया {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", "autoNameClientLocal": "स्थानीय {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → स्थानीय {{localPort}}", - "autoNameClientDynamic": "मोज़े {{localPort}} के माध्यम से {{endpoint}}", - "route": "मार्ग:", - "lastStarted": "आखिरी बार शुरू हुआ", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "रूट:", + "lastStarted": "अंतिम बार शुरू हुआ", "lastTested": "अंतिम बार परीक्षण किया गया", "lastError": "अंतिम त्रुटि", - "maxRetries": "अधिकतम पुनः प्रयास", - "maxRetriesDescription": "पुनः प्रयास करने की अधिकतम संख्या।", - "retryInterval": "पुनः प्रयास अंतराल (सेकंड)", - "retryIntervalDescription": "पुनः प्रयास करने के बीच प्रतीक्षा करने का समय।", + "maxRetries": "अधिकतम पुनर्प्रयास", + "maxRetriesDescription": "पुनर्प्रयास प्रयासों की अधिकतम संख्या।", + "retryInterval": "पुनर्प्रयास अंतराल (सेकंड)", + "retryIntervalDescription": "पुनर्प्रयास प्रयासों के बीच प्रतीक्षा समय।", "local": "स्थानीय", - "remote": "दूर", - "destination": "गंतव्य", - "host": "मेज़बान", - "mode": "तरीका", - "noHostSelected": "कोई होस्ट चयनित नहीं है", + "remote": "रिमोट", + "destination": "डेस्टिनेशन", + "host": "होस्ट", + "mode": "मोड", + "noHostSelected": "कोई होस्ट चयनित नहीं", "working": "कार्यरत..." }, "cardGrid": { - "dragToMove": "हिलाने के लिए खींचें", - "dragToResize": "आकार बदलने के लिए खींचें", + "dragToMove": "खिसकाने के लिए खींचें", + "dragToResize": "रीसाइज़ करने के लिए खींचें", "changeWidth": "चौड़ाई बदलें", "removeCard": "{{label}} हटाएँ", - "addCard": "जोड़ना", + "addCard": "जोड़ें", "columns": "कॉलम", - "empty": "कोई कार्ड उपलब्ध नहीं हैं। कार्ड रखने के लिए नीचे दिए गए 'जोड़ें' बटन का उपयोग करें।" + "empty": "कोई कार्ड नहीं। कार्ड रखने के लिए नीचे 'जोड़ें' का उपयोग करें।" }, "hostMetrics": { "cpu": "CPU", - "memory": "याद", + "memory": "मेमोरी", "disk": "डिस्क", "network": "नेटवर्क", "uptime": "अपटाइम", - "processes": "प्रक्रियाओं", + "processes": "प्रोसेसेस", "available": "उपलब्ध", - "free": "मुक्त", + "free": "फ्री", "connecting": "कनेक्ट हो रहा है...", "connectionFailed": "सर्वर से कनेक्ट करने में विफल", - "naCpus": "लागू नहीं सीपीयू(ओं)", + "naCpus": "N/A CPU(s)", "cpuCores_one": "{{count}} कोर", "cpuCores_other": "{{count}} कोर", - "cpuUsage": "सीपीयू उपयोग", - "memoryUsage": "स्मृति प्रयोग", + "cpuUsage": "CPU उपयोग", + "memoryUsage": "मेमोरी उपयोग", "diskUsage": "डिस्क उपयोग", "temperature": "तापमान", "highestTemperature": "उच्चतम तापमान", "failedToFetchHostConfig": "होस्ट कॉन्फ़िगरेशन प्राप्त करने में विफल", "serverOffline": "सर्वर ऑफ़लाइन", - "cannotFetchMetrics": "ऑफ़लाइन सर्वर से मेट्रिक्स प्राप्त नहीं किए जा सकते", - "totpFailed": "टीओटीपी सत्यापन विफल रहा", - "noneAuthNotSupported": "होस्ट मेट्रिक्स 'none' प्रमाणीकरण प्रकार का समर्थन नहीं करता है।", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "load": "भार", - "systemInfo": "व्यवस्था जानकारी", - "hostname": "होस्ट का नाम", + "cannotFetchMetrics": "ऑफ़लाइन सर्वर से मीट्रिक्स प्राप्त नहीं कर सकते", + "totpFailed": "TOTP सत्यापन विफल", + "noneAuthNotSupported": "होस्ट मेट्रिक्स 'none' प्रमाणीकरण प्रकार का समर्थन नहीं करता।", + "noHostSelected": "कोई होस्ट चयनित नहीं", + "load": "लोड", + "systemInfo": "सिस्टम जानकारी", + "hostname": "होस्टनाम", "operatingSystem": "ऑपरेटिंग सिस्टम", - "kernel": "गुठली", + "kernel": "कर्नेल", "seconds": "सेकंड", - "networkInterfaces": "नेटवर्क इंटरफेस", + "networkInterfaces": "नेटवर्क इंटरफ़ेस", "noInterfacesFound": "कोई नेटवर्क इंटरफ़ेस नहीं मिला", - "noProcessesFound": "कोई प्रक्रिया नहीं मिली", + "noProcessesFound": "कोई प्रोसेस नहीं मिला", "processesTotal": "कुल", - "processesRunning": "दौड़ना", - "loginStats": "एसएसएच लॉगिन सांख्यिकी", - "noRecentLoginData": "हाल ही में लॉगिन डेटा उपलब्ध नहीं है", - "executingQuickAction": "{{name}} निष्पादित किया जा रहा है...", - "quickActionSuccess": "{{name}} सफलतापूर्वक पूरा हुआ", - "quickActionFailed": "{{name}} असफल", - "quickActionError": "{{name}} को निष्पादित करने में विफल", + "processesRunning": "चल रहे", + "loginStats": "SSH लॉगिन आँकड़े", + "noRecentLoginData": "कोई हालिया लॉगिन डेटा नहीं", + "executingQuickAction": "{{name}} निष्पादित हो रहा है...", + "quickActionSuccess": "{{name}} सफलतापूर्वक पूर्ण हुआ", + "quickActionFailed": "{{name}} विफल रहा", + "quickActionError": "{{name}} निष्पादित करने में विफल", "ports": { - "title": "श्रवण पोर्ट", - "protocol": "शिष्टाचार", - "port": "पत्तन", + "title": "लिसनिंग पोर्ट", + "protocol": "प्रोटोकॉल", + "port": "पोर्ट", "address": "पता", - "process": "प्रक्रिया", + "process": "प्रोसेस", "search": "पोर्ट खोजें...", "allProtocols": "सभी", - "noData": "कोई श्रवण पोर्ट डेटा नहीं" + "noData": "कोई लिसनिंग पोर्ट डेटा नहीं" }, "firewall": { "title": "फ़ायरवॉल", "inactive": "निष्क्रिय", "policy": "नीति", "rules": "नियम", - "noRules": "इस श्रृंखला में कोई नियम नहीं हैं", - "noData": "फ़ायरवॉल से संबंधित कोई डेटा उपलब्ध नहीं है", + "noRules": "इस चेन में कोई नियम नहीं", + "noData": "कोई फ़ायरवॉल डेटा उपलब्ध नहीं", "action": "कार्रवाई", - "protocol": "आद्य", - "port": "पत्तन", + "protocol": "प्रोटो", + "port": "पोर्ट", "source": "स्रोत", "anywhere": "कहीं भी", - "chains": "चेन" + "chains": "चेन्स" }, "loadAvg": "लोड औसत", - "swap": "बदलना", - "architecture": "वास्तुकला", - "refresh": "ताज़ा करना", - "retry": "पुन: प्रयास करें", - "customize": "लेआउट को अनुकूलित करें", - "reset": "रीसेट करें", - "tabLive": "रहना", - "editModeInstructions": "कार्डों को पुनर्व्यवस्थित करने के लिए उन्हें खींचें, आकार बदलने के लिए नीचे के किनारे को खींचें और कार्ड की चौड़ाई बदलने के लिए चौड़ाई बटन का उपयोग करें। नीचे कार्ड जोड़ें या हटाएं।", + "swap": "स्वैप", + "architecture": "आर्किटेक्चर", + "refresh": "रीफ़्रेश", + "retry": "पुनः प्रयास करें", + "customize": "लेआउट अनुकूलित करें", + "reset": "रीसेट", + "tabLive": "लाइव", + "editModeInstructions": "कार्ड को पुनर्व्यवस्थित करने के लिए खींचें, आकार बदलने के लिए निचले किनारे को खींचें, और कार्ड की चौड़ाई बदलने के लिए चौड़ाई बटन का उपयोग करें। नीचे कार्ड जोड़ें या हटाएं।", "managers": { - "services": "सेवाएं", - "processInspector": "प्रक्रिया निरीक्षक", - "logViewer": "लॉग देखने वाला", + "services": "सेवाएँ", + "processInspector": "प्रोसेस इंस्पेक्टर", + "logViewer": "लॉग व्यूअर", "cron": "क्रॉन जॉब्स", - "packages": "संकुल", - "ssl": "एसएसएल प्रमाणपत्र", + "packages": "पैकेजेस", + "ssl": "SSL प्रमाणपत्र", "firewall": "फ़ायरवॉल", "users": "उपयोगकर्ता और अनुमतियाँ", "healthCheck": "स्वास्थ्य जांच", "diskBreakdown": "डिस्क ब्रेकडाउन", "systemdTimers": "टाइमर", - "topMemory": "मेमोरी द्वारा शीर्ष", + "topMemory": "मेमोरी के अनुसार शीर्ष", "noData": "कोई डेटा नहीं", - "sudoHint": "विशेषाधिकार प्राप्त कार्यों को सक्षम करने के लिए होस्ट एडिटर में इस होस्ट के लिए एक सूडो पासवर्ड सेट करें।", + "sudoHint": "विशेषाधिकार क्रियाओं को सक्षम करने के लिए होस्ट संपादक में इस होस्ट के लिए एक sudo पासवर्ड सेट करें।", "filter": "फ़िल्टर...", - "start": "शुरू", - "stop": "रुकना", - "restart": "पुनः आरंभ करें", - "actionDone": "{{name}} अपडेट किया गया", - "actionFailed": "क्रिया: विफल रही है", + "start": "प्रारंभ", + "stop": "रोकें", + "restart": "पुनरारंभ", + "actionDone": "{{name}} अद्यतित", + "actionFailed": "कार्रवाई विफल", "signalSent": "PID {{pid}} को सिग्नल भेजा गया", - "killHint": "क्लिक करें: समाप्त करें (SIGTERM)। दाएँ क्लिक करें: जबरन बंद करें (SIGKILL)।", - "working": "कार्यरत...", - "update": "अद्यतन", - "upgradeAll": "सभी अपग्रेड करो", - "allUpToDate": "सब कुछ अद्यतन है।", - "save": "बचाना", - "command": "आज्ञा", - "enabled": "सक्रिय", - "cronSaved": "क्रोनटैब अपडेट किया गया", - "clients": "ग्राहकों", - "dryRun": "पूर्वाभ्यास", - "renew": "नवीकरण", + "killHint": "क्लिक: समाप्त करें (SIGTERM)। राइट-क्लिक: बलपूर्वक समाप्त करें (SIGKILL)।", + "working": "काम कर रहा है...", + "update": "अपडेट करें", + "upgradeAll": "सभी अपग्रेड करें", + "allUpToDate": "सब कुछ अप टू डेट है", + "save": "सहेजें", + "command": "कमांड", + "enabled": "सक्षम", + "cronSaved": "क्रॉनटैब अपडेट किया गया", + "clients": "क्लाइंट", + "dryRun": "ड्राई रन", + "renew": "रीन्यू करें", "noAcmeClient": "इस होस्ट पर कोई ACME क्लाइंट (certbot या acme.sh) नहीं मिला।", - "addInputRule": "इनपुट नियम जोड़ें", - "firewallWarning": "परिवर्तन तब तक केवल रनटाइम पर ही लागू होते हैं जब तक कि उन्हें स्थायी रूप से सहेजा न जाए। सावधान रहें, कहीं ऐसा न हो कि आपका लॉगिन लॉक हो जाए।", - "ruleApplied": "नियम लागू", + "addInputRule": "INPUT नियम जोड़ें", + "firewallWarning": "परिवर्तन केवल रनटाइम के लिए हैं जब तक स्थायी न हों। स्वयं को लॉक आउट करने से सावधान रहें।", + "ruleApplied": "नियम लागू किया गया", "invalidPort": "एक वैध पोर्ट दर्ज करें (1-65535)", "newUsername": "नया उपयोगकर्ता नाम", - "addUser": "जोड़ना", + "addUser": "जोड़ें", "deleteUser": "उपयोगकर्ता हटाएं", - "noHealthChecks": "अभी तक कोई स्वास्थ्य जांच कॉन्फ़िगर नहीं की गई है।", - "follow": "अनुसरण करना", + "noHealthChecks": "अभी तक कोई हेल्थ चेक कॉन्फ़िगर नहीं किया गया है।", + "follow": "अनुसरण करें", "noLogData": "कोई लॉग आउटपुट नहीं।", - "tree": "पेड़", - "enableDisable": "बूट के समय सक्षम/अक्षम करें", - "grantSudo": "ग्रांट सूडो", - "revokeSudo": "sudo को रद्द करें", + "tree": "ट्री", + "enableDisable": "बूट पर सक्षम / अक्षम करें", + "grantSudo": "sudo दें", + "revokeSudo": "sudo वापस लें", "sslIssueCert": "प्रमाणपत्र जारी करें", - "sslExpired": "खत्म हो चुका", - "sslInDays": "in {{days}}d", + "sslExpired": "समाप्त", + "sslInDays": "{{days}} दिनों में", "sslNeedDomain": "कम से कम एक डोमेन दर्ज करें", "sslIssued": "प्रमाणपत्र जारी किया गया", "sslDomainsPlaceholder": "example.com, www.example.com", "sslHttpStandalone": "HTTP (स्टैंडअलोन)", - "sslHttpWebroot": "HTTP (वेब रूट)", - "sslDns": "डीएनएस", - "sslDnsProvider": "DNS प्रदाता (जैसे क्लाउडफ्लेयर)", - "sslIssueHint": "होस्ट पर DNS प्रदाता क्रेडेंशियल पहले से ही कॉन्फ़िगर किए जाने चाहिए।", - "sslRevoke": "प्रमाणपत्र रद्द करें", - "sslRevoked": "प्रमाणपत्र रद्द कर दिया गया", - "sslRevokeConfirm": "क्या प्रमाणपत्र \"{{name}}\" को रद्द करके हटाया जा सकता है? इसे पूर्ववत नहीं किया जा सकता।", - "healthRun": "दौड़ना", + "sslHttpWebroot": "HTTP (वेबरूट)", + "sslDns": "DNS", + "sslDnsProvider": "DNS प्रदाता (उदा. cloudflare)", + "sslIssueHint": "DNS प्रदाता क्रेडेंशियल पहले से होस्ट पर कॉन्फ़िगर किए गए होने चाहिए।", + "sslRevoke": "प्रमाणपत्र निरस्त करें", + "sslRevoked": "प्रमाणपत्र निरस्त किया गया", + "sslRevokeConfirm": "प्रमाणपत्र \"{{name}}\" निरस्त करें और हटाएं? यह पूर्ववत नहीं किया जा सकता।", + "healthRun": "चलाएं", "healthName": "नाम", - "healthTarget": "मेजबान का पता", - "healthAddCheck": "जाँच जोड़ें", - "healthSaved": "स्वास्थ्य जांच में बचत हुई", - "healthMissingFields": "प्रत्येक चेक में नाम और लक्ष्य होना आवश्यक है।", + "healthTarget": "होस्ट / पता", + "healthAddCheck": "जांच जोड़ें", + "healthSaved": "स्वास्थ्य जांच सहेजी गईं", + "healthMissingFields": "प्रत्येक जांच के लिए एक नाम और लक्ष्य आवश्यक है", "logFile": "फ़ाइल", - "logUnit": "इकाई", + "logUnit": "यूनिट", "logCustomPath": "/var/log के अंतर्गत कस्टम पथ (वैकल्पिक)", - "logGrep": "फ़िल्टर लाइनें...", - "firewallPersist": "नियमों को बनाए रखें", - "firewallPersisted": "फ़ायरवॉल नियम बने रहे", - "wireguard": "वायरगार्ड", - "tailscale": "टेलस्केल", - "wgNotInstalled": "इस होस्ट पर WireGuard स्थापित नहीं है।", - "wgNoInterfaces": "कोई वायरगार्ड इंटरफेस कॉन्फ़िगर नहीं किया गया है", - "wgInterfaceUp": "ऊपर लाना", - "wgInterfaceDown": "उतार देना", - "wgBringingUp": "{{name}} को ऊपर लाना...", - "wgBringingDown": "{{name}} को नीचे लाना...", + "logGrep": "लाइनें फ़िल्टर करें...", + "firewallPersist": "नियम स्थायी करें", + "firewallPersisted": "फ़ायरवॉल नियम स्थायी किए गए", + "wireguard": "WireGuard", + "tailscale": "Tailscale", + "wgNotInstalled": "WireGuard इस होस्ट पर स्थापित नहीं है", + "wgNoInterfaces": "कोई WireGuard इंटरफ़ेस कॉन्फ़िगर्ड नहीं हैं", + "wgInterfaceUp": "चालू करें", + "wgInterfaceDown": "बंद करें", + "wgBringingUp": "{{name}} चालू किया जा रहा है...", + "wgBringingDown": "{{name}} बंद किया जा रहा है...", "wgInterfaceUpDone": "{{name}} चालू है", - "wgInterfaceDownDone": "{{name}} डाउन है", - "wgListenPort": "पत्तन", + "wgInterfaceDownDone": "{{name}} बंद है", + "wgListenPort": "पोर्ट", "wgPublicKey": "सार्वजनिक कुंजी", - "wgEndpoint": "endpoint", - "wgAllowedIPs": "अनुमत आईपी", - "wgLastHandshake": "आखिरी हाथ मिलाना", + "wgEndpoint": "एंडपॉइंट", + "wgAllowedIPs": "अनुमत IPs", + "wgLastHandshake": "अंतिम हैंडशेक", "wgHandshakeNever": "कभी नहीं", - "wgTransfer": "स्थानांतरण", - "tsNotInstalled": "इस होस्ट पर टेलस्केल स्थापित नहीं है।", - "tsRunning": "दौड़ना", - "tsStopped": "रुका हुआ", - "tsEnable": "जोड़ना", - "tsDisable": "डिस्कनेक्ट", - "tsEnabling": "टेलस्केल से कनेक्ट हो रहा है...", - "tsDisabling": "टेलस्केल से कनेक्शन टूट रहा है...", - "tsEnabled": "टेलस्केल जुड़ा हुआ है", - "tsDisabled": "टेलस्केल अलग हो गया", - "tsIPs": "टेलस्केल आईपी", - "tsPeers": "समकक्ष लोग", + "wgTransfer": "ट्रांसफर", + "tsNotInstalled": "Tailscale इस होस्ट पर स्थापित नहीं है", + "tsRunning": "चालू", + "tsStopped": "बंद", + "tsEnable": "कनेक्ट करें", + "tsDisable": "डिस्कनेक्ट करें", + "tsEnabling": "Tailscale से कनेक्ट हो रहा है...", + "tsDisabling": "Tailscale से डिस्कनेक्ट हो रहा है...", + "tsEnabled": "Tailscale कनेक्टेड", + "tsDisabled": "Tailscale डिस्कनेक्टेड", + "tsIPs": "Tailscale IPs", + "tsPeers": "पीयर्स", "tsOnline": "ऑनलाइन", "tsOffline": "ऑफलाइन", - "tsExitNode": "निकास नोड", - "tsExitNodeActive": "निकास नोड सक्रिय", - "tsHostname": "होस्ट का नाम" + "tsExitNode": "एक्जिट नोड", + "tsExitNodeActive": "एक्जिट नोड सक्रिय", + "tsHostname": "होस्टनाम" } }, "auth": { - "tagline": "सेल्फ-होस्टेड एसएसएच और रिमोट डेस्कटॉप प्रबंधन", - "loginTitle": "वापसी पर स्वागत है", - "registerTitle": "खाता बनाएं", + "tagline": "स्व-होस्टेड SSH एवं रिमोट डेस्कटॉप प्रबंधन", + "loginTitle": "पुनः स्वागत है", + "registerTitle": "खाता बनाएँ", "forgotPassword": "पासवर्ड भूल गए?", - "rememberMe": "डिवाइस को 30 दिनों तक याद रखें (इसमें TOTP शामिल है)", - "noAccount": "क्या आपके पास खाता नहीं है?", - "hasAccount": "क्या आपके पास पहले से एक खाता मौजूद है?", - "twoFactorAuth": "दो-कारक प्रमाणीकरण", + "rememberMe": "डिवाइस को 30 दिनों के लिए याद रखें (TOTP सहित)", + "noAccount": "खाता नहीं है?", + "hasAccount": "पहले से खाता है?", + "twoFactorAuth": "द्वि-कारक प्रमाणीकरण", "enterCode": "सत्यापन कोड दर्ज करें", "backupCode": "या बैकअप कोड का उपयोग करें", "verifyCode": "कोड सत्यापित करें", "redirectingToApp": "ऐप पर रीडायरेक्ट किया जा रहा है...", - "sshAuthenticationRequired": "SSH प्रमाणीकरण आवश्यक है", - "sshNoKeyboardInteractive": "कीबोर्ड-इंटरैक्टिव प्रमाणीकरण अनुपलब्ध है", - "sshAuthenticationFailed": "प्रमाणीकरण विफल होना", - "sshAuthenticationTimeout": "प्रमाणीकरण समय सीमा समाप्त", - "sshNoKeyboardInteractiveDescription": "सर्वर कीबोर्ड-आधारित प्रमाणीकरण का समर्थन नहीं करता है। कृपया अपना पासवर्ड या SSH कुंजी प्रदान करें।", - "sshAuthFailedDescription": "आपके द्वारा दी गई जानकारी गलत थी। कृपया सही जानकारी के साथ पुनः प्रयास करें।", - "sshTimeoutDescription": "प्रमाणीकरण का प्रयास समय के साथ समाप्त हो गया। कृपया पुनः प्रयास करें।", + "sshAuthenticationRequired": "SSH प्रमाणीकरण आवश्यक", + "sshNoKeyboardInteractive": "कीबोर्ड इंटरैक्टिव प्रमाणीकरण अनुपलब्ध", + "sshAuthenticationFailed": "प्रमाणीकरण विफल", + "sshAuthenticationTimeout": "प्रमाणीकरण समय समाप्त", + "sshNoKeyboardInteractiveDescription": "सर्वर कीबोर्ड इंटरैक्टिव प्रमाणीकरण का समर्थन नहीं करता। कृपया अपना पासवर्ड या SSH कुंजी प्रदान करें।", + "sshAuthFailedDescription": "प्रदान किए गए क्रेडेंशियल गलत थे। कृपया मान्य क्रेडेंशियल के साथ पुनः प्रयास करें।", + "sshTimeoutDescription": "प्रमाणीकरण प्रयास का समय समाप्त हो गया। कृपया पुनः प्रयास करें।", "sshProvideCredentialsDescription": "इस सर्वर से कनेक्ट करने के लिए कृपया अपने SSH क्रेडेंशियल प्रदान करें।", - "sshPasswordDescription": "इस एसएसएच कनेक्शन के लिए पासवर्ड दर्ज करें।", + "sshPasswordDescription": "इस SSH कनेक्शन के लिए पासवर्ड दर्ज करें।", "sshKeyPasswordDescription": "यदि आपकी SSH कुंजी एन्क्रिप्टेड है, तो यहां पासफ़्रेज़ दर्ज करें।", - "passphraseRequired": "पासफ़्रेज़ आवश्यक है", - "passphraseRequiredDescription": "एसएसएच कुंजी एन्क्रिप्टेड है। इसे अनलॉक करने के लिए कृपया पासफ़्रेज़ दर्ज करें।", - "back": "पीछे", + "passphraseRequired": "पासफ़्रेज़ आवश्यक", + "passphraseRequiredDescription": "SSH कुंजी एन्क्रिप्टेड है। कृपया इसे अनलॉक करने के लिए पासफ़्रेज़ दर्ज करें।", + "back": "वापस", "firstUser": "पहला उपयोगकर्ता", - "firstUserMessage": "आप पहले उपयोगकर्ता हैं और आपको व्यवस्थापक बनाया जाएगा। आप साइडबार में उपयोगकर्ता ड्रॉपडाउन में व्यवस्थापक सेटिंग्स देख सकते हैं। यदि आपको लगता है कि यह कोई गलती है, तो डॉकर लॉग्स की जाँच करें या GitHub पर एक समस्या दर्ज करें।", + "firstUserMessage": "आप पहले उपयोगकर्ता हैं और आपको व्यवस्थापक बनाया जाएगा। आप साइडबार उपयोगकर्ता ड्रॉपडाउन में व्यवस्थापक सेटिंग देख सकते हैं। यदि आपको लगता है कि यह एक गलती है, तो Docker लॉग जांचें, या एक GitHub इश्यू बनाएं।", "external": "बाहरी", - "loginWithExternal": "बाह्य प्रदाता के साथ लॉगिन करें", - "loginWithExternalDesc": "अपने कॉन्फ़िगर किए गए बाहरी पहचान प्रदाता का उपयोग करके लॉग इन करें", - "externalNotSupportedInElectron": "इलेक्ट्रॉन ऐप में अभी तक बाहरी प्रमाणीकरण समर्थित नहीं है। कृपया OIDC लॉगिन के लिए वेब संस्करण का उपयोग करें।", + "loginWithExternal": "बाहरी प्रदाता से लॉगिन करें", + "loginWithExternalDesc": "अपने कॉन्फ़िगर किए गए बाहरी पहचान प्रदाता का उपयोग करके लॉगिन करें", + "externalNotSupportedInElectron": "Electron ऐप में अभी तक बाहरी प्रमाणीकरण समर्थित नहीं है। OIDC लॉगिन के लिए कृपया वेब संस्करण का उपयोग करें।", "loginWithProvider": "{{name}} से लॉगिन करें", - "orContinueWith": "या जारी रखें", - "ldapUsername": "एलडीएपी उपयोगकर्ता नाम", - "ldapPassword": "एलडीएपी पासवर्ड", - "ldapSignIn": "दाखिल करना", + "orContinueWith": "या इसके साथ जारी रखें", + "ldapUsername": "LDAP उपयोगकर्ता नाम", + "ldapPassword": "LDAP पासवर्ड", + "ldapSignIn": "साइन इन करें", "ldapLoginFailed": "LDAP लॉगिन विफल", - "resetPasswordButton": "पासवर्ड रीसेट", + "resetPasswordButton": "पासवर्ड रीसेट करें", "sendResetCode": "रीसेट कोड भेजें", - "resetCodeDesc": "पासवर्ड रीसेट कोड प्राप्त करने के लिए अपना उपयोगकर्ता नाम दर्ज करें। यह कोड डॉकर कंटेनर लॉग में दर्ज हो जाएगा।", - "resetCode": "कोड फिर ठीक करें", + "resetCodeDesc": "पासवर्ड रीसेट कोड प्राप्त करने के लिए अपना उपयोगकर्ता नाम दर्ज करें। कोड Docker कंटेनर लॉग में लॉग किया जाएगा।", + "resetCode": "रीसेट कोड", "verifyCodeButton": "कोड सत्यापित करें", - "enterResetCode": "उपयोगकर्ता के लिए डॉकर कंटेनर लॉग से 6-अंकीय कोड दर्ज करें:", + "enterResetCode": "उपयोगकर्ता के लिए Docker कंटेनर लॉग से 6-अंकीय कोड दर्ज करें:", "newPassword": "नया पासवर्ड", - "confirmNewPassword": "पासवर्ड की पुष्टि कीजिये", + "confirmNewPassword": "पासवर्ड की पुष्टि करें", "enterNewPassword": "उपयोगकर्ता के लिए अपना नया पासवर्ड दर्ज करें:", "signUp": "साइन अप करें", "desktopApp": "डेस्कटॉप ऐप", - "loggingInToDesktopApp": "डेस्कटॉप ऐप में लॉग इन करना", + "loggingInToDesktopApp": "डेस्कटॉप ऐप में लॉग इन किया जा रहा है", "loadingServer": "सर्वर लोड हो रहा है...", - "dataLossWarning": "इस तरह से पासवर्ड रीसेट करने पर आपके सभी सेव किए गए SSH होस्ट, क्रेडेंशियल और अन्य एन्क्रिप्टेड डेटा डिलीट हो जाएंगे। इस कार्रवाई को वापस नहीं लिया जा सकता। इसका उपयोग केवल तभी करें जब आप अपना पासवर्ड भूल गए हों और लॉग इन न हों।", + "dataLossWarning": "इस तरह से पासवर्ड रीसेट करने पर आपके सभी सहेजे गए SSH होस्ट, क्रेडेंशियल और अन्य एन्क्रिप्टेड डेटा हटा दिए जाएंगे। यह कार्रवाई पूर्ववत नहीं की जा सकती। इसका उपयोग केवल तभी करें जब आप अपना पासवर्ड भूल गए हों और लॉग इन नहीं हैं।", "authenticationDisabled": "प्रमाणीकरण अक्षम", - "authenticationDisabledDesc": "सभी प्रमाणीकरण विधियाँ वर्तमान में निष्क्रिय हैं। कृपया अपने व्यवस्थापक से संपर्क करें।", - "passwordLoginDisabledDesc": "पासवर्ड से लॉगिन करना अक्षम है। कृपया पासकी या किसी बाहरी प्रमाणीकरण प्रदाता का उपयोग करें।", - "signInWithPasskey": "पासकी से लॉग इन करें", + "authenticationDisabledDesc": "सभी प्रमाणीकरण विधियां वर्तमान में अक्षम हैं। कृपया अपने व्यवस्थापक से संपर्क करें।", + "passwordLoginDisabledDesc": "पासवर्ड लॉगिन अक्षम है। पासकी या बाहरी प्रमाणीकरण प्रदाता का उपयोग करें।", + "signInWithPasskey": "पासकी से साइन इन करें", "passkeyLoginFailed": "पासकी लॉगिन विफल", - "attemptsRemaining": "{{count}} attempts remaining", - "confirmResetDataWipe": "इस खाते ने एन्क्रिप्शन अपग्रेड के बाद से लॉग इन नहीं किया है, इसलिए पुराने पासवर्ड के बिना इसका संग्रहित डेटा पुनर्प्राप्त नहीं किया जा सकता है। रीसेट करने से इसके होस्ट, क्रेडेंशियल और स्निपेट स्थायी रूप से हटा दिए जाएंगे। जारी रखें?" + "attemptsRemaining": "{{count}} प्रयास शेष", + "confirmResetDataWipe": "एन्क्रिप्शन अपग्रेड के बाद से इस खाते ने लॉगिन नहीं किया है, इसलिए पुराने पासवर्ड के बिना इसका संग्रहीत डेटा पुनर्प्राप्त नहीं किया जा सकता। रीसेट करने से इसके होस्ट, क्रेडेंशियल और स्निपेट स्थायी रूप से हटा दिए जाएंगे। जारी रखें?" }, "hostKey": { "verifyNewHost": "SSH होस्ट कुंजी सत्यापित करें", - "keyChangedWarning": "SSH होस्ट कुंजी बदल गई", - "firstConnectionTitle": "पहली बार इस होस्ट से कनेक्ट हो रहा है", - "firstConnectionDescription": "इस होस्ट की प्रामाणिकता स्थापित नहीं की जा सकती। कृपया सुनिश्चित करें कि फिंगरप्रिंट आपकी अपेक्षा के अनुरूप है।", - "keyChangedDescription": "आपके पिछले कनेक्शन के बाद से इस सर्वर की होस्ट कुंजी बदल गई है। यह किसी सुरक्षा समस्या का संकेत हो सकता है।", + "keyChangedWarning": "SSH होस्ट कुंजी बदल गई है", + "firstConnectionTitle": "इस होस्ट से पहली बार कनेक्ट हो रहे हैं", + "firstConnectionDescription": "इस होस्ट की प्रामाणिकता स्थापित नहीं की जा सकती। सत्यापित करें कि फिंगरप्रिंट आपकी अपेक्षा से मेल खाता है।", + "keyChangedDescription": "पिछले कनेक्शन के बाद से इस सर्वर की होस्ट कुंजी बदल गई है। यह सुरक्षा समस्या का संकेत हो सकता है।", "previousKey": "पिछली कुंजी", "newFingerprint": "नया फिंगरप्रिंट", - "fingerprint": "अंगुली की छाप", - "verifyInstructions": "यदि आप इस होस्ट पर भरोसा करते हैं, तो जारी रखने के लिए स्वीकार करें पर क्लिक करें और भविष्य के कनेक्शनों के लिए इस फिंगरप्रिंट को सहेजें।", + "fingerprint": "फिंगरप्रिंट", + "verifyInstructions": "यदि आप इस होस्ट पर भरोसा करते हैं, तो जारी रखने और भविष्य के कनेक्शनों के लिए इस फिंगरप्रिंट को सहेजने के लिए 'स्वीकार करें' पर क्लिक करें।", "securityWarning": "सुरक्षा चेतावनी", "acceptAndContinue": "स्वीकार करें और जारी रखें", "acceptNewKey": "नई कुंजी स्वीकार करें और जारी रखें" }, "errors": { - "databaseConnection": "डेटाबेस के कनेक्ट नहीं कर सके", + "databaseConnection": "डेटाबेस से कनेक्ट नहीं हो सका", "unknownError": "अज्ञात त्रुटि", "loginFailed": "लॉगिन विफल", - "failedPasswordReset": "पासवर्ड रीसेट शुरू करने में विफल", - "failedVerifyCode": "रीसेट कोड को सत्यापित करने में विफल", - "failedCompleteReset": "पासवर्ड रीसेट करने में विफलता", - "invalidTotpCode": "अमान्य टीओटीपी कोड", - "failedOidcLogin": "OIDC लॉगिन शुरू करने में विफल", + "failedPasswordReset": "पासवर्ड रीसेट आरंभ करने में विफल", + "failedVerifyCode": "रीसेट कोड सत्यापित करने में विफल", + "failedCompleteReset": "पासवर्ड रीसेट पूरा करने में विफल", + "invalidTotpCode": "अमान्य TOTP कोड", + "failedOidcLogin": "OIDC लॉगिन प्रारंभ करने में विफल", "silentSigninOidcUnavailable": "साइलेंट साइन-इन का अनुरोध किया गया था, लेकिन OIDC लॉगिन उपलब्ध नहीं है।", - "failedUserInfo": "लॉगिन के बाद उपयोगकर्ता की जानकारी प्राप्त करने में विफल रहा", - "oidcAuthFailed": "OIDC प्रमाणीकरण विफल रहा", - "invalidAuthUrl": "बैकएंड से अमान्य प्राधिकरण यूआरएल प्राप्त हुआ।", + "failedUserInfo": "लॉगिन के बाद उपयोगकर्ता जानकारी प्राप्त करने में विफल", + "oidcAuthFailed": "OIDC प्रमाणीकरण विफल", + "invalidAuthUrl": "बैकएंड से अमान्य प्राधिकरण URL प्राप्त हुआ", "requiredField": "यह फ़ील्ड आवश्यक है", "minLength": "न्यूनतम लंबाई {{min}} है", - "passwordMismatch": "सांकेतिक शब्द मेल नहीं खाते", + "passwordMismatch": "पासवर्ड मेल नहीं खाते", "passwordLoginDisabled": "उपयोगकर्ता नाम/पासवर्ड लॉगिन वर्तमान में अक्षम है", - "sessionExpired": "सत्र समाप्त हो गया है - कृपया दोबारा लॉग इन करें", - "totpRateLimited": "दर सीमित: TOTP सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया बाद में पुनः प्रयास करें।", - "totpRateLimitedWithTime": "दर सीमित: TOTP सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया पुनः प्रयास करने से पहले {{time}} सेकंड प्रतीक्षा करें।", - "resetCodeRateLimited": "दर सीमित: सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया बाद में पुनः प्रयास करें।", - "resetCodeRateLimitedWithTime": "दर सीमित: सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया पुनः प्रयास करने से पहले {{time}} सेकंड प्रतीक्षा करें।", + "sessionExpired": "सत्र समाप्त - कृपया पुनः लॉगिन करें", + "totpRateLimited": "दर सीमित: बहुत अधिक TOTP सत्यापन प्रयास। कृपया बाद में पुनः प्रयास करें।", + "totpRateLimitedWithTime": "दर सीमित: बहुत अधिक TOTP सत्यापन प्रयास। पुनः प्रयास करने से पहले कृपया {{time}} सेकंड प्रतीक्षा करें।", + "resetCodeRateLimited": "दर सीमित: बहुत अधिक सत्यापन प्रयास। कृपया बाद में पुनः प्रयास करें।", + "resetCodeRateLimitedWithTime": "दर सीमित: बहुत अधिक सत्यापन प्रयास। कृपया पुनः प्रयास करने से पहले {{time}} सेकंड प्रतीक्षा करें।", "authTokenSaveFailed": "प्रमाणीकरण टोकन सहेजने में विफल", - "failedToLoadServer": "सर्वर लोड करने में विफल" + "failedToLoadServer": "सर्वर लोड करने में विफल", + "remoteServerRequired": "रिमोट सर्वर आवश्यक है। इस कनेक्शन प्रकार का उपयोग करने के लिए सेटिंग्स में जाकर रिमोट सर्वर कनेक्ट करें।" }, "messages": { - "registrationDisabled": "प्रशासक द्वारा नए खाते का पंजीकरण फिलहाल बंद कर दिया गया है। कृपया लॉग इन करें या प्रशासक से संपर्क करें।", - "userNotAllowed": "आपके खाते को पंजीकरण करने की अनुमति नहीं है। कृपया व्यवस्थापक से संपर्क करें।", - "databaseConnectionFailed": "डेटाबेस सर्वर से कनेक्ट करने में विफल।", - "resetCodeSent": "रीसेट कोड डॉकर लॉग्स में भेजा गया", - "codeVerified": "कोड सफलतापूर्वक सत्यापित हो गया", - "passwordResetSuccess": "पासवर्ड सफलतापूर्वक रीसेट हो गया", - "loginSuccess": "लॉग इन सफल", - "registrationSuccess": "सफल पंजीकरण" + "registrationDisabled": "नया खाता पंजीकरण वर्तमान में व्यवस्थापक द्वारा अक्षम है। कृपया लॉग इन करें या व्यवस्थापक से संपर्क करें।", + "userNotAllowed": "आपका खाता पंजीकरण करने के लिए अधिकृत नहीं है। कृपया व्यवस्थापक से संपर्क करें।", + "databaseConnectionFailed": "डेटाबेस सर्वर से कनेक्ट करने में विफल", + "resetCodeSent": "रीसेट कोड Docker लॉग में भेजा गया", + "codeVerified": "कोड सफलतापूर्वक सत्यापित हुआ", + "passwordResetSuccess": "पासवर्ड सफलतापूर्वक रीसेट हुआ", + "loginSuccess": "लॉगिन सफल", + "registrationSuccess": "पंजीकरण सफल" }, "profile": { - "c2sTunnelConfigDesc": "कॉन्फ़िगर किए गए SSH होस्ट को लक्षित करने वाले स्थानीय डेस्कटॉप टनल।", + "c2sTunnelConfigDesc": "कॉन्फ़िगर किए गए SSH होस्ट को लक्षित करने वाली स्थानीय डेस्कटॉप टनलें।", "c2sTunnelPresets": "क्लाइंट टनल प्रीसेट", - "c2sTunnelPresetsDesc": "इस डेस्कटॉप क्लाइंट की स्थानीय टनल सूची को एक नामित सर्वर प्रीसेट के रूप में सहेजें, या इस क्लाइंट में एक प्रीसेट लोड करें।", - "c2sTunnelPresetsUnavailable": "क्लाइंट टनल प्रीसेट केवल डेस्कटॉप क्लाइंट में ही उपलब्ध हैं।", + "c2sTunnelPresetsDesc": "इस डेस्कटॉप क्लाइंट की स्थानीय टनल सूची को एक नामित सर्वर प्रीसेट के रूप में सहेजें, या किसी प्रीसेट को वापस इस क्लाइंट में लोड करें।", + "c2sTunnelPresetsUnavailable": "क्लाइंट टनल प्रीसेट केवल डेस्कटॉप क्लाइंट में उपलब्ध हैं।", "c2sPresetName": "प्रीसेट नाम", "c2sPresetNamePlaceholder": "क्लाइंट प्रीसेट नाम", - "c2sPresetToLoad": "लोड करने के लिए पूर्व निर्धारित", - "c2sNoPresetSelected": "कोई पूर्व निर्धारित सेटिंग चयनित नहीं है", + "c2sPresetToLoad": "लोड करने के लिए प्रीसेट", + "c2sNoPresetSelected": "कोई प्रीसेट चयनित नहीं", "c2sNoPresets": "कोई प्रीसेट सहेजा नहीं गया", - "c2sLoadPreset": "भार", - "c2sCurrentLocalConfig": "{{count}} इस डेस्कटॉप पर कॉन्फ़िगर किए गए स्थानीय क्लाइंट टनल।", - "c2sPresetSyncNote": "प्रीसेट स्पष्ट स्नैपशॉट होते हैं; इनमें से किसी एक को लोड करने से इस डेस्कटॉप क्लाइंट की स्थानीय क्लाइंट टनल सूची बदल जाती है।", + "c2sLoadPreset": "लोड करें", + "c2sCurrentLocalConfig": "इस डेस्कटॉप पर {{count}} स्थानीय क्लाइंट टनल(s) कॉन्फ़िगर की गई हैं।", + "c2sPresetSyncNote": "प्रीसेट स्पष्ट स्नैपशॉट हैं; एक लोड करने पर यह डेस्कटॉप क्लाइंट की स्थानीय क्लाइंट टनल सूची को बदल देता है।", "c2sPresetSaved": "क्लाइंट टनल प्रीसेट सहेजा गया", - "c2sPresetLoaded": "क्लाइंट टनल प्रीसेट स्थानीय रूप से लोड किया गया", - "c2sPresetRenamed": "क्लाइंट टनल प्रीसेट का नाम बदल दिया गया", - "c2sPresetDeleted": "क्लाइंट टनल प्रीसेट हटा दिया गया", + "c2sPresetLoaded": "क्लाइंट टनल प्रीसेट स्थानीय रूप से लोड हुआ", + "c2sPresetRenamed": "क्लाइंट टनल प्रीसेट का नाम बदला गया", + "c2sPresetDeleted": "क्लाइंट टनल प्रीसेट हटाया गया", "c2sPresetLoadError": "क्लाइंट टनल प्रीसेट लोड करने में विफल" }, "placeholders": { @@ -2313,11 +2447,11 @@ "retryInterval": "10", "language": "भाषा", "keyPassword": "कुंजी पासवर्ड", - "pastePrivateKey": "अपनी निजी कुंजी यहाँ पेस्ट करें...", - "localListenerHost": "127.0.0.1 (स्थानीय स्तर पर सुनें)", + "pastePrivateKey": "अपनी निजी कुंजी यहाँ चिपकाएँ...", + "localListenerHost": "127.0.0.1 (स्थानीय रूप से सुनें)", "localTargetHost": "127.0.0.1 (इस कंप्यूटर पर लक्ष्य)", - "socksListenerHost": "127.0.0.1 (सॉक्स श्रोता)", - "enterPassword": "अपना कूटशब्द भरें", + "socksListenerHost": "127.0.0.1 (SOCKS श्रोता)", + "enterPassword": "अपना पासवर्ड दर्ज करें", "defaultPort": "22", "defaultEndpointPort": "224" }, @@ -2326,106 +2460,106 @@ "loading": "डैशबोर्ड लोड हो रहा है...", "github": "GitHub", "support": "सहायता", - "discord": "कलह", - "docs": "डॉक्स", + "discord": "Discord", + "docs": "दस्तावेज़", "donate": "दान करें", "serverOverview": "सर्वर अवलोकन", "version": "संस्करण", - "upToDate": "अप टू डेट", - "updateAvailable": "उपलब्ध अद्यतन", + "upToDate": "अद्यतित", + "updateAvailable": "अपडेट उपलब्ध है", "beta": "बीटा", "uptime": "अपटाइम", "database": "डेटाबेस", "healthy": "स्वस्थ", - "error": "गलती", - "totalHosts": "कुल मेजबान", - "totalTunnels": "कुल सुरंगें", - "totalCredentials": "कुल प्रमाण पत्र", + "error": "त्रुटि", + "totalHosts": "कुल होस्ट", + "totalTunnels": "कुल टनल", + "totalCredentials": "कुल क्रेडेंशियल", "recentActivity": "हाल की गतिविधि", - "reset": "रीसेट करें", + "reset": "रीसेट", "loadingRecentActivity": "हाल की गतिविधि लोड हो रही है...", - "noRecentActivity": "कोई हालिया गतिविधि नहीं", - "quickActions": "त्वरित कार्रवाइयां", + "noRecentActivity": "कोई हाल की गतिविधि नहीं", + "quickActions": "त्वरित कार्रवाइयाँ", "addHost": "होस्ट जोड़ें", "addCredential": "क्रेडेंशियल जोड़ें", "adminSettings": "व्यवस्थापक सेटिंग्स", - "userProfile": "उपयोगकर्ता रूपरेखा", + "userProfile": "उपयोगकर्ता प्रोफ़ाइल", "serverStats": "सर्वर आँकड़े", - "loadingServerStats": "सर्वर के आंकड़े लोड हो रहे हैं...", + "loadingServerStats": "सर्वर आँकड़े लोड हो रहे हैं...", "noServerData": "कोई सर्वर डेटा उपलब्ध नहीं है", "cpu": "CPU", - "ram": "टक्कर मारना", - "customizeLayout": "डैशबोर्ड को अनुकूलित करें", + "ram": "RAM", + "customizeLayout": "डैशबोर्ड अनुकूलित करें", "dashboardSettings": "डैशबोर्ड सेटिंग्स", "enableDisableCards": "कार्ड सक्षम/अक्षम करें", - "resetLayout": "वितथ पर ले जाएं", + "resetLayout": "डिफ़ॉल्ट पर रीसेट करें", "serverOverviewCard": "सर्वर अवलोकन", "recentActivityCard": "हाल की गतिविधि", - "networkGraphCard": "नेटवर्क ग्राफ", - "networkGraph": "नेटवर्क ग्राफ", - "quickActionsCard": "त्वरित कार्रवाइयां", + "networkGraphCard": "नेटवर्क ग्राफ़", + "networkGraph": "नेटवर्क ग्राफ़", + "quickActionsCard": "त्वरित कार्रवाइयाँ", "serverStatsCard": "सर्वर आँकड़े", "panelMain": "मुख्य", - "panelSide": "ओर", - "justNow": "बस अब", + "panelSide": "साइड", + "justNow": "अभी-अभी", "serviceLinks": "सेवा लिंक", - "homepagePreview": "होमपेज का पूर्वावलोकन" + "homepagePreview": "मुखपृष्ठ पूर्वावलोकन" }, "donation": { - "title": "क्या आप टर्मिक्स का आनंद ले रहे हैं?", - "body": "टर्मिक्स एक मुफ़्त और ओपन सोर्स सॉफ़्टवेयर है, जिसे दो लोगों की टीम ने अपने खाली समय में बनाया और रखरखाव किया है। अगर इसने आपके किसी ऐसे व्यावसायिक टूल की जगह ले ली है जिसके लिए आप भुगतान कर रहे होते, तो आपका दान होस्टिंग लागत को कवर करने और विकास को जारी रखने में मदद करता है। फिलहाल, दान केवल क्रिप्टोकरेंसी में ही स्वीकार किए जाते हैं।", - "milestones": "दान से SAML और Kubernetes जैसी सुविधाओं को विकसित करने के लिए आवश्यक शोध और सीखने में लगने वाले समय को भी वित्त पोषित करने में मदद मिलती है। प्रगति देखने के लिए दान पृष्ठ पर जाएँ।", + "title": "टर्मिक्स का आनंद ले रहे हैं?", + "body": "टर्मिक्स मुफ़्त और ओपन सोर्स है, जिसे दो लोगों की टीम ने अपने खाली समय में बनाया और बनाए रखा है। अगर इसने किसी ऐसे व्यावसायिक टूल की जगह ली है जिसके लिए आपको भुगतान करना पड़ता, तो दान होस्टिंग लागत को कवर करने और विकास जारी रखने में मदद करता है। फ़िलहाल, दान केवल क्रिप्टो में स्वीकार किए जाते हैं।", + "milestones": "दान से SAML और Kubernetes समर्थन जैसी सुविधाओं के निर्माण के लिए आवश्यक शोध और सीखने के समय का वित्तपोषण भी होता है। प्रगति दान पृष्ठ पर देखें।", "cta": "दान करें", "dismiss": "शायद बाद में" }, "dashboardTab": { "stable": "स्थिर", - "hostsOnline": "ऑनलाइन होस्ट", - "activeTunnels": "सक्रिय सुरंगें", + "hostsOnline": "होस्ट ऑनलाइन", + "activeTunnels": "सक्रिय टनल", "registerNewServer": "नया सर्वर पंजीकृत करें", - "storeSshKeysOrPasswords": "SSH कुंजी या पासवर्ड संग्रहीत करें", - "manageUsersAndRoles": "उपयोगकर्ताओं और भूमिकाओं का प्रबंधन करें", + "storeSshKeysOrPasswords": "SSH कुंजियाँ या पासवर्ड संग्रहीत करें", + "manageUsersAndRoles": "उपयोगकर्ता और भूमिकाएँ प्रबंधित करें", "manageYourAccount": "अपना खाता प्रबंधित करें", - "hostStatus": "मेज़बान स्थिति", - "noHostsConfigured": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है", + "hostStatus": "होस्ट स्थिति", + "noHostsConfigured": "कोई होस्ट कॉन्फ़िगर नहीं है", "online": "ऑनलाइन", - "offline": "ऑफलाइन", - "checking": "चेकिंग", + "offline": "ऑफ़लाइन", + "checking": "जाँच रहा है", "onlineLower": "ऑनलाइन", "nodes": "{{count}} नोड्स", - "add": "जोड़ना:", + "add": "जोड़ें:", "commandPalette": "कमांड पैलेट", - "done": "हो गया", - "editModeInstructions": "कार्डों को खींचकर उनका क्रम बदलें · कॉलम विभाजक को खींचकर कॉलम का आकार बदलें · कार्ड के निचले किनारे को खींचकर उसकी ऊंचाई बदलें · हटाने के लिए ट्रैश में डालें", + "done": "पूर्ण", + "editModeInstructions": "कार्ड्स को पुन: क्रमित करने के लिए खींचें · कॉलम का आकार बदलने के लिए कॉलम विभाजक को खींचें · कार्ड की ऊंचाई बदलने के लिए उसके निचले किनारे को खींचें · हटाने के लिए ट्रैश में डालें", "empty": "खाली", - "clear": "स्पष्ट", - "serviceLinksTitle": "सेवा लिंक", - "serviceLinksEmpty": "अभी तक कोई सेवा लिंक उपलब्ध नहीं हैं", + "clear": "साफ़ करें", + "serviceLinksTitle": "सर्विस लिंक्स", + "serviceLinksEmpty": "अभी तक कोई सर्विस लिंक नहीं", "serviceLinksAddLabel": "लेबल", - "serviceLinksAddUrl": "यूआरएल", - "serviceLinksAdd": "जोड़ना", - "serviceLinksLabelPlaceholder": "मेरी सेवा", + "serviceLinksAddUrl": "URL", + "serviceLinksAdd": "जोड़ें", + "serviceLinksLabelPlaceholder": "मेरी सर्विस", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "एक वैध वेब पता दर्ज करें", - "serviceLinksAddFailed": "सेवा लिंक जोड़ने में विफल", + "serviceLinksInvalidUrl": "एक मान्य वेब पता दर्ज करें", + "serviceLinksAddFailed": "सर्विस लिंक जोड़ने में विफल", "disk": "डिस्क", "viewServerDetails": "सर्वर विवरण देखें" }, "sessionLogs": { - "title": "सत्र लॉग", - "noLogs": "अभी तक कोई सेशन लॉग उपलब्ध नहीं हैं।", - "noLogsDesc": "रिकॉर्डिंग शुरू करने के लिए होस्ट पर सेशन लॉगिंग सक्षम करें।", + "title": "सेशन लॉग्स", + "noLogs": "अभी तक कोई सेशन लॉग नहीं", + "noLogsDesc": "रिकॉर्डिंग शुरू करने के लिए किसी होस्ट पर सेशन लॉगिंग सक्षम करें", "duration": "अवधि", - "viewLog": "देखें प्रवेश", - "downloadLog": "डाउनलोड करना", - "deleteLog": "मिटाना", - "confirmDelete": "क्या इस सेशन लॉग को डिलीट करना है?", - "confirmDeleteDesc": "इस एक्शन को वापस नहीं किया जा सकता।", - "copyContent": "प्रतिलिपि", + "viewLog": "लॉग देखें", + "downloadLog": "डाउनलोड करें", + "deleteLog": "हटाएं", + "confirmDelete": "क्या इस सेशन लॉग को हटाएं?", + "confirmDeleteDesc": "यह क्रिया वापस नहीं ली जा सकती।", + "copyContent": "कॉपी करें", "copied": "कॉपी हो गया!", "loadError": "सेशन लॉग लोड करने में विफल", - "deleteError": "सेशन लॉग को हटाने में विफल", - "filterByHost": "होस्ट के आधार पर फ़िल्टर करें..." + "deleteError": "सेशन लॉग हटाने में विफल", + "filterByHost": "होस्ट द्वारा फ़िल्टर करें..." }, "networkGraph": { "addHost": "होस्ट जोड़ें", @@ -2433,591 +2567,608 @@ "addLink": "लिंक जोड़ें", "zoomIn": "ज़ूम इन", "zoomOut": "ज़ूम आउट", - "resetView": "फिर से देख्ना", - "selectHost": "होस्ट का चयन करें", - "chooseHost": "एक मेज़बान चुनें...", - "parentGroup": "अभिभावक समूह", + "resetView": "रीसेट करें", + "selectHost": "होस्ट चुनें", + "chooseHost": "कोई होस्ट चुनें...", + "parentGroup": "मूल समूह", "noGroup": "कोई समूह नहीं", - "groupName": "समूह नाम", + "groupName": "समूह का नाम", "color": "रंग", "source": "स्रोत", "target": "लक्ष्य", - "moveToGroup": "समूह में ले जाएं", - "selectGroup": "समूह का चयन करें...", + "moveToGroup": "समूह में ले जाएँ", + "selectGroup": "समूह चुनें...", "addConnection": "कनेक्शन जोड़ें", - "hostDetails": "मेज़बान का विवरण", - "removeFromGroup": "समूह से हटाएँ", - "addHostHere": "यहां होस्ट जोड़ें", + "hostDetails": "होस्ट विवरण", + "removeFromGroup": "समूह से निकालें", + "addHostHere": "यहाँ होस्ट जोड़ें", "editGroup": "समूह संपादित करें", - "delete": "मिटाना", - "add": "जोड़ना", - "create": "बनाएं", - "move": "कदम", - "connect": "जोड़ना", - "createGroup": "समूह बनाना", - "selectSourcePlaceholder": "स्रोत का चयन करें...", - "selectTargetPlaceholder": "लक्ष्य का चयन करें...", - "invalidFile": "अमान्य लेख्यपत्र", - "hostAlreadyExists": "होस्ट पहले से ही टोपोलॉजी में मौजूद है", + "delete": "हटाएँ", + "add": "जोड़ें", + "create": "बनाएँ", + "move": "ले जाएँ", + "connect": "कनेक्ट करें", + "createGroup": "समूह बनाएँ", + "selectSourcePlaceholder": "स्रोत चुनें...", + "selectTargetPlaceholder": "लक्ष्य चुनें...", + "invalidFile": "अमान्य फ़ाइल", + "hostAlreadyExists": "होस्ट पहले से टोपोलॉजी में मौजूद है", "connectionExists": "कनेक्शन पहले से मौजूद है", "unknown": "अज्ञात", "name": "नाम", - "ip": "आई पी", + "ip": "IP", "status": "स्थिति", "failedToAddNode": "नोड जोड़ने में विफल", - "sourceDifferentFromTarget": "स्रोत और लक्ष्य अलग-अलग होने चाहिए", + "sourceDifferentFromTarget": "स्रोत और लक्ष्य भिन्न होने चाहिए", "exportJSON": "JSON निर्यात करें", "importJSON": "JSON आयात करें", "terminal": "टर्मिनल", - "fileManager": "फ़ाइल मैनेजर", - "tunnel": "सुरंग", - "docker": "डाक में काम करनेवाला मज़दूर", + "fileManager": "फ़ाइल प्रबंधक", + "tunnel": "टनल", + "docker": "Docker", "serverStats": "होस्ट मेट्रिक्स", "hostMetrics": "होस्ट मेट्रिक्स", "noNodes": "अभी तक कोई नोड नहीं" }, "docker": { - "notEnabled": "इस होस्ट के लिए डॉकर सक्षम नहीं है।", - "validating": "डॉकर का सत्यापन किया जा रहा है...", + "notEnabled": "इस होस्ट के लिए Docker सक्षम नहीं है", + "validating": "Docker सत्यापित कर रहा है...", "connecting": "कनेक्ट हो रहा है...", - "error": "गलती", - "version": "डॉकर {{version}}", - "connectionFailed": "डॉकर से कनेक्ट करने में विफल", - "containerStarted": "कंटेनर {{name}} शुरू हो गया", - "failedToStartContainer": "कंटेनर {{name}} को प्रारंभ करने में विफल", - "containerStopped": "कंटेनर {{name}} रुक गया", - "failedToStopContainer": "कंटेनर {{name}} को रोकने में विफल", - "containerRestarted": "कंटेनर {{name}} पुनः आरंभ हुआ", - "failedToRestartContainer": "कंटेनर {{name}} को पुनः आरंभ करने में विफल रहा", - "containerPaused": "कंटेनर {{name}} रुका हुआ है", - "containerUnpaused": "कंटेनर {{name}} अनपॉज़ किया गया", - "failedToTogglePauseContainer": "कंटेनर {{name}} के लिए पॉज़ स्थिति को टॉगल करने में विफल।", + "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": "चेतावनी: यह कंटेनर वर्तमान में चल रहा है। इसे हटाने से पहले यह कंटेनर बंद हो जाएगा।", + "failedToRemoveContainer": "कंटेनर {{name}} हटाने में विफल", + "image": "इमेज", + "ports": "पोर्ट", + "noPorts": "कोई पोर्ट नहीं", + "start": "प्रारंभ करें", + "confirmRemoveContainer": "क्या आप वाकई कंटेनर '{{name}}' हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।", + "runningContainerWarning": "चेतावनी: यह कंटेनर वर्तमान में चल रहा है। हटाने पर पहले इसे रोका जाएगा।", "loadingContainers": "कंटेनर लोड हो रहे हैं...", - "manager": "डॉकर मैनेजर", - "autoRefresh": "ऑटो रिफ्रेश", - "timestamps": "मुहर", - "lines": "पंक्तियां", + "manager": "Docker प्रबंधक", + "autoRefresh": "स्वतः ताज़ा करें", + "timestamps": "टाइमस्टैम्प्स", + "lines": "पंक्तियाँ", "filterLogs": "लॉग फ़िल्टर करें...", - "refresh": "ताज़ा करना", - "download": "डाउनलोड करना", - "clear": "स्पष्ट", + "refresh": "ताज़ा करें", + "download": "डाउनलोड करें", + "clear": "साफ़ करें", "logsDownloaded": "लॉग सफलतापूर्वक डाउनलोड हो गए", - "last50": "पिछले 50", - "last100": "पिछले 100", + "last50": "अंतिम 50", + "last100": "अंतिम 100", "last500": "अंतिम 500", - "last1000": "पिछले 1000", + "last1000": "अंतिम 1000", "allLogs": "सभी लॉग", - "noLogsMatching": "\"{{query}} \" से मेल खाने वाली कोई लॉग नहीं मिली", - "noLogsAvailable": "कोई लॉग उपलब्ध नहीं हैं", - "noContainersFound": "कोई कंटेनर नहीं मिला", - "noContainersFoundHint": "इस होस्ट पर कोई डॉकर कंटेनर उपलब्ध नहीं हैं।", + "noLogsMatching": "\"{{query}}\" से मेल खाने वाले कोई लॉग नहीं हैं", + "noLogsAvailable": "कोई लॉग उपलब्ध नहीं", + "noContainersFound": "कोई कंटेनर नहीं मिले", + "noContainersFoundHint": "इस होस्ट पर कोई Docker कंटेनर उपलब्ध नहीं हैं", "searchPlaceholder": "कंटेनर खोजें...", - "allStatuses": "सभी स्थितियाँ", - "stateRunning": "दौड़ना", + "allStatuses": "सभी स्टेटस", + "stateRunning": "चालू", "statePaused": "रुका हुआ", - "stateExited": "बाहर निकल गया", - "stateRestarting": "पुन: प्रारंभ हो", - "noContainersMatchFilters": "आपके फ़िल्टर से कोई कंटेनर मेल नहीं खाता।", - "noContainersMatchFiltersHint": "अपनी खोज या फ़िल्टर के मानदंडों को समायोजित करने का प्रयास करें", - "failedToFetchStats": "कंटेनर सांख्यिकी प्राप्त करने में विफल", - "containerNotRunning": "कंटेनर नहीं चल रहा है", - "startContainerToViewStats": "आंकड़े देखने के लिए कंटेनर शुरू करें", - "loadingStats": "आंकड़े लोड हो रहे हैं...", - "errorLoadingStats": "सांख्यिकी लोड करने में त्रुटि", - "noStatsAvailable": "कोई आंकड़े उपलब्ध नहीं हैं", - "cpuUsage": "सीपीयू उपयोग", - "current": "मौजूदा", - "memoryUsage": "स्मृति प्रयोग", + "stateExited": "बंद", + "stateRestarting": "रीस्टार्ट हो रहा है", + "noContainersMatchFilters": "आपके फ़िल्टर से मेल खाने वाले कोई कंटेनर नहीं हैं", + "noContainersMatchFiltersHint": "अपनी खोज या फ़िल्टर मापदंड बदलकर देखें", + "failedToFetchStats": "कंटेनर के आँकड़े प्राप्त नहीं हो सके", + "containerNotRunning": "कंटेनर चालू नहीं है", + "startContainerToViewStats": "आँकड़े देखने के लिए कंटेनर चालू करें", + "loadingStats": "आँकड़े लोड हो रहे हैं...", + "errorLoadingStats": "आँकड़े लोड करने में त्रुटि", + "noStatsAvailable": "कोई आँकड़े उपलब्ध नहीं", + "cpuUsage": "CPU उपयोग", + "current": "वर्तमान", + "memoryUsage": "मेमोरी उपयोग", "networkIo": "नेटवर्क I/O", "input": "इनपुट", - "output": "उत्पादन", + "output": "आउटपुट", "blockIo": "ब्लॉक I/O", - "read": "पढ़ना", - "write": "लिखना", - "pids": "पीआईडी", + "read": "रीड", + "write": "राइट", + "pids": "PIDs", "containerInformation": "कंटेनर जानकारी", "name": "नाम", - "id": "पहचान", - "state": "राज्य", - "containerMustBeRunning": "कंसोल तक पहुँचने के लिए कंटेनर का चालू होना आवश्यक है।", + "id": "आईडी", + "state": "स्थिति", + "containerMustBeRunning": "कंसोल तक पहुंचने के लिए कंटेनर चालू होना चाहिए", "verificationCodePrompt": "सत्यापन कोड दर्ज करें", - "totpVerificationFailed": "टीओटीपी सत्यापन विफल रहा। कृपया पुनः प्रयास करें।", - "warpgateVerificationFailed": "वॉरपगेट प्रमाणीकरण विफल रहा। कृपया पुनः प्रयास करें।", - "connectedTo": "{{containerName}} से जुड़ा हुआ", - "disconnected": "डिस्कनेक्ट किया गया", + "totpVerificationFailed": "TOTP सत्यापन विफल। कृपया पुनः प्रयास करें।", + "warpgateVerificationFailed": "Warpgate प्रमाणीकरण विफल। कृपया पुनः प्रयास करें।", + "connectedTo": "{{containerName}} से जुड़ा", + "disconnected": "डिस्कनेक्टेड", "consoleError": "कंसोल त्रुटि", "errorMessage": "त्रुटि: {{message}}", - "failedToConnect": "कंटेनर से कनेक्ट करने में विफल", - "console": "सांत्वना देना", - "selectShell": "शेल का चयन करें", - "bash": "दे घुमा के", - "sh": "श", - "ash": "राख", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "notConnected": "जुड़े नहीं हैं", - "clickToConnect": "शेल सेशन शुरू करने के लिए कनेक्ट पर क्लिक करें", + "failedToConnect": "कंटेनर से जुड़ने में विफल", + "console": "कंसोल", + "selectShell": "शेल चुनें", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "कनेक्ट करें", + "disconnect": "डिस्कनेक्ट करें", + "notConnected": "कनेक्टेड नहीं", + "clickToConnect": "शेल सत्र शुरू करने के लिए कनेक्ट पर क्लिक करें", "connectingTo": "{{containerName}} से कनेक्ट हो रहा है...", "containerNotFound": "कंटेनर नहीं मिला", - "backToList": "सूची पर वापस जाएं", - "logs": "लॉग्स", - "stats": "आँकड़े", - "consoleTab": "सांत्वना देना", - "startContainerToAccess": "कंसोल तक पहुँचने के लिए कंटेनर को प्रारंभ करें" + "backToList": "सूची में वापस जाएं", + "logs": "लॉग", + "stats": "आंकड़े", + "consoleTab": "कंसोल", + "startContainerToAccess": "कंसोल तक पहुंचने के लिए कंटेनर चालू करें" }, "admin": { "sectionGeneral": "सामान्य", - "sectionOidc": "ओआईडीसी", - "sectionSso": "एसएसओ प्रदाता", + "sectionOidc": "OIDC", + "sectionSso": "SSO प्रदाता", "ssoAddProvider": "प्रदाता जोड़ें", "ssoDocsLink": "दस्तावेज़ देखें", "ssoProviderDocsLink": "दस्तावेज़ देखें", - "ssoNoProviders": "कोई एसएसओ प्रदाता कॉन्फ़िगर नहीं किया गया है।", - "ssoProviderName": "प्रदर्शित होने वाला नाम", + "ssoNoProviders": "कोई SSO प्रदाता कॉन्फ़िगर नहीं है।", + "ssoProviderName": "प्रदर्शन नाम", "ssoProviderType": "प्रदाता प्रकार", - "ssoDeleteProvider": "प्रदाता हटाएं", - "ssoDeleteConfirm": "इस प्रदाता को हटा दें? इससे जुड़े उपयोगकर्ता लॉग इन नहीं कर पाएंगे।", - "ssoTypeOidc": "ओआईडीसी", - "ssoTypeLdap": "एलडीएपी", + "ssoDeleteProvider": "प्रदाता हटाएँ", + "ssoDeleteConfirm": "इस प्रदाता को हटाएँ? इससे जुड़े उपयोगकर्ता लॉगिन नहीं कर पाएंगे।", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", - "ssoTypeGoogle": "गूगल", - "ssoEnabled": "सक्रिय", + "ssoTypeGoogle": "Google", + "ssoEnabled": "सक्षम", "ssoDisabled": "अक्षम", - "ssoSaveProvider": "प्रदाता को बचाएँ", - "ssoTestConnection": "कनेक्शन का परीक्षण करें", + "ssoSaveProvider": "प्रदाता सहेजें", + "ssoTestConnection": "कनेक्शन परीक्षण", "ssoEditProvider": "प्रदाता संपादित करें", - "ldapHost": "एलडीएपी होस्ट", - "ldapPort": "पत्तन", + "ldapHost": "LDAP होस्ट", + "ldapPort": "पोर्ट", "ldapUseTls": "TLS (LDAPS) का उपयोग करें", - "ldapBindDn": "डीएन को बांधें", - "ldapBindPassword": "पासवर्ड बाइंड करें", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind पासवर्ड", "ldapUserSearchBase": "उपयोगकर्ता खोज आधार", "ldapUserSearchFilter": "उपयोगकर्ता खोज फ़िल्टर", "ldapUsernameAttr": "उपयोगकर्ता नाम विशेषता", - "ldapDisplayNameAttr": "डिस्प्ले नाम विशेषता", + "ldapDisplayNameAttr": "प्रदर्शन नाम विशेषता", "ldapGroupSearchBase": "समूह खोज आधार", - "ldapAdminGroup": "व्यवस्थापक समूह", + "ldapAdminGroup": "एडमिन समूह", "ldapAllowedUsers": "अनुमत उपयोगकर्ता", - "sectionUsers": "उपयोगकर्ताओं", - "sectionSessions": "सत्रों", + "sectionUsers": "उपयोगकर्ता", + "sectionSessions": "सत्र", "sectionRoles": "भूमिकाएँ", "sectionDatabase": "डेटाबेस", - "sectionApiKeys": "एपीआई कुंजी", + "sectionApiKeys": "API कुंजियाँ", "sectionAuditLog": "ऑडिट लॉग", - "sectionSsl": "एसएसएल / लेट्स एनक्रिप्ट", - "sslDescription": "Let's Encrypt से स्वचालित रूप से एक विश्वसनीय SSL प्रमाणपत्र जारी करें और नवीनीकृत करें। इसके लिए सार्वजनिक डोमेन और पोर्ट 80 या DNS एक्सेस की आवश्यकता होती है।", - "sslDocsLink": "एसएसएल दस्तावेज़ देखें", - "sslDomain": "कार्यक्षेत्र", + "sectionSsl": "SSL / Let's Encrypt", + "sslDescription": "Let's Encrypt से एक विश्वसनीय SSL प्रमाणपत्र स्वचालित रूप से जारी करें और नवीनीकृत करें। इसके लिए सार्वजनिक डोमेन और पोर्ट 80 या DNS पहुँच आवश्यक है।", + "sslDocsLink": "SSL दस्तावेज़ देखें", + "sslDomain": "डोमेन", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "प्रमाणपत्र का सार्वजनिक डोमेन नाम।", + "sslDomainDesc": "प्रमाणपत्र के लिए सार्वजनिक डोमेन नाम।", "sslEmail": "ईमेल", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encrypt की सूचनाओं और खाते के लिए संपर्क ईमेल पता।", - "sslChallengeType": "चुनौती का प्रकार", - "sslChallengeTypeDesc": "Let's Encrypt को डोमेन स्वामित्व कैसे साबित करें।", - "sslChallengeHttp": "HTTP (वेब रूट) - इसके लिए इंटरनेट से पोर्ट 80 तक पहुंच आवश्यक है।", - "sslChallengeDns": "DNS (Cloudflare) - इसके लिए Cloudflare API टोकन आवश्यक है।", - "sslCloudflareToken": "क्लाउडफ्लेयर एपीआई टोकन", + "sslEmailDesc": "Let's Encrypt सूचनाओं और खाते के लिए संपर्क ईमेल।", + "sslChallengeType": "चुनौती प्रकार", + "sslChallengeTypeDesc": "Let's Encrypt को डोमेन स्वामित्व साबित करने का तरीका।", + "sslChallengeHttp": "HTTP (webroot) - इंटरनेट से पोर्ट 80 सुलभ होना आवश्यक है", + "sslChallengeDns": "DNS (Cloudflare) - Cloudflare API टोकन आवश्यक है", + "sslCloudflareToken": "Cloudflare API टोकन", "sslCloudflareTokenPlaceholder": "टोकन दर्ज करें...", "sslCloudflareTokenDesc": "आपके डोमेन के लिए Zone:DNS:Edit अनुमति वाला स्कोप्ड टोकन।", - "sslCertStatus": "प्रमाणपत्र की स्थिति", + "sslCertStatus": "प्रमाणपत्र स्थिति", "sslCertStatusNone": "कोई प्रमाणपत्र नहीं", - "sslCertStatusValid": "वैध", - "sslCertStatusExpiring": "जल्द ही समाप्त होने वाला है", - "sslCertStatusExpired": "खत्म हो चुका", - "sslCertExpiresAt": "समाप्ति तिथि {{date}}", - "sslLastIssued": "अंतिम बार जारी किया गया {{date}}", - "sslRequestCert": "प्रमाणपत्र जारी करना/नवीनीकरण करना", - "sslRequestCertLoading": "प्रमाणपत्र का अनुरोध किया जा रहा है...", - "sslRequestCertSuccess": "प्रमाणपत्र जारी और सफलतापूर्वक स्थापित हो गया।", - "sslRequestCertFailed": "प्रमाणपत्र अनुरोध विफल रहा", - "sslSave": "सेटिंग्स सेव करें", - "sslSaved": "एसएसएल सेटिंग्स सहेजी गईं", - "sslSaveFailed": "एसएसएल सेटिंग्स सहेजने में विफल", + "sslCertStatusValid": "मान्य", + "sslCertStatusExpiring": "जल्द समाप्त हो रहा है", + "sslCertStatusExpired": "समाप्त", + "sslCertExpiresAt": "{{date}} को समाप्त होगा", + "sslLastIssued": "अंतिम जारी: {{date}}", + "sslRequestCert": "प्रमाणपत्र जारी करें / नवीनीकरण करें", + "sslRequestCertLoading": "प्रमाणपत्र अनुरोध किया जा रहा है...", + "sslRequestCertSuccess": "प्रमाणपत्र सफलतापूर्वक जारी और स्थापित कर दिया गया", + "sslRequestCertFailed": "प्रमाणपत्र अनुरोध विफल", + "sslSave": "सेटिंग सहेजें", + "sslSaved": "SSL सेटिंग सहेजी गई", + "sslSaveFailed": "SSL सेटिंग सहेजने में विफल", "sslRequiresDomain": "डोमेन और ईमेल आवश्यक हैं", - "sslInfoNote": "सर्टिफिकेट जारी करने के बाद, अपने एनवायरनमेंट वेरिएबल्स में SSL को सक्षम करें (ENABLE_SSL=true) और Termix को रीस्टार्ट करें।", - "auditLogTotal": "{{total}} कुल प्रविष्टियाँ", - "auditLogEmpty": "कोई ऑडिट लॉग प्रविष्टियाँ नहीं मिलीं", - "auditLogSuccess": "सफलता", + "sslInfoNote": "प्रमाणपत्र जारी करने के बाद, अपने एनवायरनमेंट वेरिएबल्स में SSL सक्षम करें (ENABLE_SSL=true) और Termix को पुनरारंभ करें।", + "sslManualOption": "मैन्युअल (प्रमाणपत्र अपलोड करें)", + "sslManualCert": "प्रमाणपत्र (पीईएम)", + "sslManualCertPlaceholder": "-----प्रमाणपत्र प्रारंभ-----", + "sslManualKey": "निजी कुंजी (पीईएम)", + "sslManualKeyPlaceholder": "-----निजी कुंजी प्रारंभ-----", + "sslManualDesc": "अपने मौजूदा प्रमाणपत्र और निजी कुंजी को पेस्ट करें, यदि आपके सीए द्वारा आवश्यक हो तो पूरी श्रृंखला भी शामिल करें।", + "sslManualUpload": "प्रमाणपत्र अपलोड और इंस्टॉल करें", + "sslManualUploadLoading": "प्रमाणपत्र अपलोड किया जा रहा है...", + "sslManualUploadSuccess": "प्रमाणपत्र सफलतापूर्वक अपलोड और इंस्टॉल हो गया।", + "sslManualUploadFailed": "प्रमाणपत्र अपलोड विफल रहा", + "sslManualRequiresFields": "प्रमाणपत्र और निजी कुंजी आवश्यक हैं", + "auditLogTotal": "कुल {{total}} प्रविष्टियाँ", + "auditLogEmpty": "ऑडिट लॉग में कोई प्रविष्टि नहीं मिली", + "auditLogSuccess": "सफल", "auditLogFailed": "असफल", "auditLogClearFilters": "फ़िल्टर साफ़ करें", - "auditLogPage": "पृष्ठ {{page}} का {{totalPages}} (कुल{{total}})", - "auditLogIp": "आई पी", + "auditLogPage": "पृष्ठ {{page}} / {{totalPages}} (कुल {{total}})", + "auditLogIp": "IP", "auditLogResourceId": "संसाधन आईडी", "auditLogFilterUser": "उपयोगकर्ता", - "auditLogFilterAction": "कार्रवाई", + "auditLogFilterAction": "क्रिया", "auditLogFilterResourceType": "संसाधन प्रकार", "auditLogFilterStatus": "स्थिति", "auditLogFilterFrom": "से", - "auditLogFilterTo": "को", + "auditLogFilterTo": "तक", "auditLogFilterAll": "सभी", "allowRegistration": "उपयोगकर्ता पंजीकरण की अनुमति दें", - "allowRegistrationDesc": "नए उपयोगकर्ताओं को उपयोगकर्ता नाम और पासवर्ड के साथ स्वयं पंजीकरण करने दें।", + "allowRegistrationDesc": "नए उपयोगकर्ताओं को उपयोगकर्ता नाम और पासवर्ड के साथ स्वयं पंजीकरण करने दें", "allowPasswordLogin": "पासवर्ड लॉगिन की अनुमति दें", "allowPasswordLoginDesc": "उपयोगकर्ता नाम/पासवर्ड लॉगिन", - "oidcAutoProvision": "ओआईडीसी ऑटो-प्रोविजन", - "oidcAutoProvisionDesc": "OIDC/SSO उपयोगकर्ताओं के लिए पहली बार लॉगिन करने पर स्वचालित रूप से खाते बनाएं (पंजीकरण टॉगल से स्वतंत्र)", + "oidcAutoProvision": "OIDC स्वतः-प्रावधान", + "oidcAutoProvisionDesc": "OIDC/SSO उपयोगकर्ताओं के लिए पहली लॉगिन पर स्वचालित रूप से खाते बनाएं (पंजीकरण टॉगल से स्वतंत्र)", "oidcSilentLoginDefault": "डिफ़ॉल्ट रूप से साइलेंट OIDC लॉगिन", - "oidcSilentLoginDefaultDesc": "हर बार विज़िट करने पर लॉगिन फ़ॉर्म को पूरी तरह से छोड़कर, स्वचालित रूप से OIDC लॉगिन पर रीडायरेक्ट करें।", + "oidcSilentLoginDefaultDesc": "हर विज़िट पर स्वचालित रूप से OIDC लॉगिन पर रीडायरेक्ट करें, लॉगिन फ़ॉर्म को पूरी तरह छोड़ दें", "allowPasswordReset": "पासवर्ड रीसेट की अनुमति दें", - "allowPasswordResetDesc": "डॉकर लॉग के माध्यम से कोड रीसेट करें", - "commandHistoryEnabled": "कमान का इतिहास", + "allowPasswordResetDesc": "Docker लॉग के माध्यम से रीसेट कोड", + "commandHistoryEnabled": "कमांड इतिहास", "commandHistoryEnabledDesc": "कमांड इतिहास रिकॉर्डिंग की अनुमति दें। अक्षम होने पर, प्रति-होस्ट सेटिंग्स की परवाह किए बिना इतिहास सहेजा नहीं जाता है।", - "updateCommandHistoryFailed": "कमांड इतिहास सेटिंग को अपडेट करने में विफल।", - "sessionTimeout": "सेशन खत्म", + "updateCommandHistoryFailed": "कमांड इतिहास सेटिंग अपडेट करने में विफल", + "analyticsEnabled": "अनाम उपयोग के आँकड़े साझा करें", + "analyticsEnabledDesc": "यह Termix को बेहतर बनाने में मदद करने के लिए उपयोगकर्ताओं, होस्टों और फ़ीचर उपयोग की दैनिक गुमनाम गणना भेजता है। इसमें कभी भी कोई व्यक्तिगत डेटा या कनेक्शन विवरण शामिल नहीं किया जाता है।", + "updateAnalyticsFailed": "एनालिटिक्स सेटिंग को अपडेट करने में विफल", + "sessionSharingGloballyEnabled": "सेशन शेयरिंग की अनुमति दें", + "sessionSharingGloballyEnabledDesc": "लाइव टर्मिनल, RDP, VNC और Telnet सेशन को पूरे इंस्टेंस में साझा करने की अनुमति दें। अक्षम होने पर यह प्रति-होस्ट साझाकरण टॉगल को ओवरराइड कर देता है।", + "updateSessionSharingFailed": "सेशन शेयरिंग सेटिंग को अपडेट करने में विफलता", + "sessionTimeout": "सत्र टाइमआउट", "hours": "घंटे", "sessionTimeoutRange": "न्यूनतम 1 घंटा · अधिकतम 720 घंटे", - "monitoringDefaults": "मॉनिटरिंग डिफ़ॉल्ट", - "statusCheck": "स्थिति जांच", + "monitoringDefaults": "निगरानी डिफ़ॉल्ट", + "statusCheck": "स्थिति जाँच", "metrics": "मेट्रिक्स", "sec": "सेकंड", "logLevel": "लॉग स्तर", - "enableGuacamole": "गुआकामोल को सक्षम करें", - "enableGuacamoleDesc": "आरडीपी/वीएनसी रिमोट डेस्कटॉप", + "enableGuacamole": "Guacamole सक्षम करें", + "enableGuacamoleDesc": "RDP/VNC रिमोट डेस्कटॉप", "enableGuacamoleDocsLink": "दस्तावेज़ देखें", - "guacdUrl": "गुआकडी यूआरएल", - "tailscaleApiKey": "टेलस्केल एपीआई कुंजी", - "tailscaleApiKeyDescription": "होस्ट एडिटर में डिवाइस की पहचान के लिए उपयोग किया जाता है। tailscale.com/admin/settings/keys पर जाकर कुंजी जनरेट करें।", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API कुंजी", + "tailscaleApiKeyDescription": "होस्ट संपादक में उपकरण खोज के लिए उपयोग किया जाता है। tailscale.com/admin/settings/keys पर एक कुंजी बनाएं।", "tailscaleApiKeyDocsLink": "दस्तावेज़ देखें", - "oidcDescription": "SSO के लिए OpenID Connect को कॉन्फ़िगर करें। जिन फ़ील्ड्स पर * का निशान लगा है, उन्हें भरना अनिवार्य है।", + "oidcDescription": "SSO के लिए OpenID Connect कॉन्फ़िगर करें। * से चिह्नित फ़ील्ड आवश्यक हैं।", "oidcDocsLink": "दस्तावेज़ देखें", "oidcClientId": "क्लाइंट आईडी", - "oidcClientSecret": "ग्राहक रहस्य", - "oidcAuthUrl": "प्राधिकरण यूआरएल", - "oidcIssuerUrl": "जारीकर्ता यूआरएल", - "oidcTokenUrl": "टोकन यूआरएल", + "oidcClientSecret": "क्लाइंट सीक्रेट", + "oidcAuthUrl": "प्राधिकरण URL", + "oidcIssuerUrl": "जारीकर्ता URL", + "oidcTokenUrl": "टोकन URL", "oidcUserIdentifier": "उपयोगकर्ता पहचानकर्ता पथ", - "oidcDisplayName": "नाम पथ प्रदर्शित करें", - "oidcScopes": "कार्यक्षेत्र", - "oidcUserinfoUrl": "उपयोगकर्ता जानकारी यूआरएल को ओवरराइड करें", + "oidcDisplayName": "प्रदर्शन नाम पथ", + "oidcScopes": "स्कोप", + "oidcUserinfoUrl": "Userinfo URL ओवरराइड करें", "oidcAllowedUsers": "अनुमत उपयोगकर्ता", - "oidcAllowedUsersDesc": "एक पंक्ति में एक ही ईमेल लिखें। सभी ईमेल भेजने के लिए इसे खाली छोड़ दें।", + "oidcAllowedUsersDesc": "प्रति पंक्ति एक ईमेल। सभी को अनुमति देने के लिए खाली छोड़ें।", "oidcAdminGroup": "व्यवस्थापक समूह", - "oidcAdminGroupDesc": "इस समूह के उपयोगकर्ताओं को व्यवस्थापक अधिकार प्राप्त हैं। समूह सिंक को अक्षम करने के लिए इसे खाली छोड़ दें।", + "oidcAdminGroupDesc": "इस समूह के उपयोगकर्ताओं को व्यवस्थापक अधिकार दिए जाते हैं। समूह सिंक अक्षम करने के लिए खाली छोड़ें।", "oidcGroupClaim": "समूह दावा", - "oidcGroupClaimDesc": "यह विकल्प वैकल्पिक है। यह क्लेम पाथ उपयोगकर्ता के समूहों को दर्शाता है। डिफ़ॉल्ट रूप से, यह समूहों, भूमिकाओं और फिर समूह पर आधारित होता है। इसका उपयोग कस्टम क्लेम वाले प्रदाताओं (जैसे ज़िटाडेल) के लिए करें।", - "oidcCaCert": "कस्टम सीए प्रमाणपत्र", - "oidcCaCertDesc": "यह विकल्प वैकल्पिक है। निजी या स्व-हस्ताक्षरित सीए का उपयोग करने वाले ओआईडीसी प्रदाताओं के लिए पीईएम-एन्कोडेड सीए प्रमाणपत्र। सिस्टम ट्रस्ट स्टोर का उपयोग करने के लिए इसे खाली छोड़ दें।", - "removeOidc": "निकालना", + "oidcGroupClaimDesc": "वैकल्पिक। वह दावा पथ जिसमें उपयोगकर्ता के समूह शामिल हैं। डिफ़ॉल्ट रूप से groups, roles, फिर group होता है। कस्टम दावे वाले प्रदाताओं के लिए इसका उपयोग करें (जैसे Zitadel)।", + "oidcCaCert": "कस्टम CA प्रमाणपत्र", + "oidcCaCertDesc": "वैकल्पिक। निजी या स्व-हस्ताक्षरित CA का उपयोग करने वाले OIDC प्रदाताओं के लिए PEM-एन्कोडेड CA प्रमाणपत्र। सिस्टम ट्रस्ट स्टोर का उपयोग करने के लिए खाली छोड़ें।", + "removeOidc": "हटाएं", "usersCount": "{{count}} उपयोगकर्ता", "createUser": "बनाएं", - "newRole": "नयी भूमिका", + "newRole": "नई भूमिका", "roleName": "नाम", - "roleDisplayName": "प्रदर्शित होने वाला नाम", + "roleDisplayName": "प्रदर्शन नाम", "roleDescription": "विवरण", - "rolesCount": "{{count}} भूमिकाएँ", + "rolesCount": "{{count}} भूमिकाएं", "createRole": "बनाएं", - "creating": "सृजन...", + "creating": "बन रहा है...", "exportDatabase": "डेटाबेस निर्यात करें", - "exportDatabaseDesc": "सभी होस्ट, क्रेडेंशियल और सेटिंग्स का बैकअप डाउनलोड करें", - "export": "निर्यात", + "exportDatabaseDesc": "सभी होस्टों, क्रेडेंशियलों और सेटिंग्स का बैकअप डाउनलोड करें", + "export": "निर्यात करें", "exporting": "निर्यात हो रहा है...", "importDatabase": "डेटाबेस आयात करें", "importDatabaseDesc": ".sqlite बैकअप फ़ाइल से पुनर्स्थापित करें", "importDatabaseSelected": "चयनित: {{name}}", - "selectFile": "फ़ाइल का चयन करें", - "changeFile": "परिवर्तन", - "import": "आयात", + "selectFile": "फ़ाइल चुनें", + "changeFile": "बदलें", + "import": "आयात करें", "importing": "आयात हो रहा है...", "apiKeysCount": "{{count}} कुंजियाँ", "apiKeysDocsLink": "दस्तावेज़ देखें", - "newApiKey": "नई एपीआई कुंजी", - "apiKeyCreatedWarning": "कुंजी बन गई है - इसे अभी कॉपी कर लें, यह दोबारा नहीं दिखाई देगी।", + "newApiKey": "नई API कुंजी", + "apiKeyCreatedWarning": "कुंजी बन गई है - कृपया इसे अभी कॉपी करें, बाद में यह नहीं दिखाई देगी।", "apiKeyName": "नाम", "apiKeyUser": "उपयोगकर्ता", - "apiKeySelectUser": "किसी उपयोगकर्ता का चयन करें...", + "apiKeySelectUser": "एक उपयोगकर्ता चुनें...", "apiKeyExpiresAt": "समाप्ति तिथि", "createKey": "कुंजी बनाएँ", "apiKeyNoExpiry": "कोई समाप्ति नहीं", - "revokedBadge": "निरस्त किया गया", - "authTypeDual": "दोहरी प्राधिकरण", - "authTypeOidc": "ओआईडीसी", + "revokedBadge": "रद्द", + "authTypeDual": "दोहरा प्रमाणीकरण", + "authTypeOidc": "OIDC", "authTypeLocal": "स्थानीय", "adminStatusAdministrator": "प्रशासक", - "adminStatusRegularUser": "नियमित उपयोगकर्ता", - "adminBadge": "व्यवस्थापक", - "systemBadge": "एसवाईएस", - "customBadge": "रिवाज़", + "adminStatusRegularUser": "सामान्य उपयोगकर्ता", + "adminBadge": "एडमिन", + "systemBadge": "सिस्टम", + "customBadge": "कस्टम", "youBadge": "आप", "sessionsActive": "{{count}} सक्रिय", "sessionActive": "सक्रिय: {{time}}", - "sessionExpires": "एक्सप: {{time}}", + "sessionExpires": "समाप्ति: {{time}}", "revokeAll": "सभी", - "revokeAllSessionsSuccess": "उपयोगकर्ता के लिए सभी सत्र रद्द कर दिए गए हैं।", - "revokeAllSessionsFailed": "सत्र रद्द करने में विफल", - "revokeSessionFailed": "सत्र रद्द करने में विफल", + "revokeAllSessionsSuccess": "उपयोगकर्ता के सभी सत्र निरस्त कर दिए गए", + "revokeAllSessionsFailed": "सत्र निरस्त करने में विफल", + "revokeSessionFailed": "सत्र निरस्त करने में विफल", "addRole": "भूमिका जोड़ें", - "noCustomRoles": "कोई कस्टम भूमिकाएँ परिभाषित नहीं हैं", + "noCustomRoles": "कोई कस्टम भूमिका परिभाषित नहीं", "removeRoleFailed": "भूमिका हटाने में विफल", - "assignRoleFailed": "भूमिका निर्धारित करने में विफल", + "assignRoleFailed": "भूमिका निर्दिष्ट करने में विफल", "deleteRoleFailed": "भूमिका हटाने में विफल", "userAdminAccess": "प्रशासक", - "userAdminAccessDesc": "सभी प्रशासनिक सेटिंग्स तक पूर्ण पहुंच", - "userRoles": "भूमिकाएँ", - "revokeAllUserSessions": "सभी सत्र रद्द करें", - "revokeAllUserSessionsDesc": "सभी डिवाइसों पर पुनः लॉगिन करने के लिए बाध्य करें", - "revoke": "रद्द करना", + "userAdminAccessDesc": "सभी प्रशासन सेटिंग्स तक पूर्ण पहुंच", + "userRoles": "भूमिकाएं", + "revokeAllUserSessions": "सभी सत्र निरस्त करें", + "revokeAllUserSessionsDesc": "सभी डिवाइसों पर पुनः लॉगिन के लिए बाध्य करें", + "revoke": "निरस्त करें", "deleteUserWarning": "इस उपयोगकर्ता को हटाना स्थायी है।", - "deleteUser": "{{username}} को हटाएँ", - "deleting": "हटा रहा हूँ...", - "deleteUserFailed": "उपयोगकर्ता को हटाने में विफल", + "deleteUser": "{{username}} हटाएं", + "deleting": "हटा रहा है...", + "deleteUserFailed": "उपयोगकर्ता हटाने में विफल", "deleteUserSuccess": "उपयोगकर्ता \"{{username}}\" हटा दिया गया", "deleteRoleSuccess": "भूमिका \"{{name}}\" हटा दी गई", "revokeKeySuccess": "कुंजी \"{{name}}\" निरस्त कर दी गई", - "revokeKeyFailed": "कुंजी रद्द करने में विफल", - "copiedToClipboard": "क्लिपबोर्ड पर कॉपी हो गया", - "done": "हो गया", - "createUserTitle": "उपयोगकर्ता बनाएँ", + "revokeKeyFailed": "कुंजी निरस्त करने में विफल", + "copiedToClipboard": "क्लिपबोर्ड पर कॉपी किया गया", + "done": "समाप्त", + "createUserTitle": "उपयोगकर्ता बनाएं", "createUserDesc": "एक नया स्थानीय खाता बनाएं।", "createUserUsername": "उपयोगकर्ता नाम", "createUserPassword": "पासवर्ड", - "createUserPasswordHint": "कम से कम 6 अक्षर।", + "createUserPasswordHint": "न्यूनतम 6 अक्षर।", "createUserEnterUsername": "उपयोगकर्ता नाम दर्ज करें", - "createUserEnterPassword": "पास वर्ड दर्ज करें", - "createUserSubmit": "उपयोगकर्ता बनाएँ", + "createUserEnterPassword": "पासवर्ड दर्ज करें", + "createUserSubmit": "उपयोगकर्ता बनाएं", "editUserTitle": "उपयोगकर्ता प्रबंधित करें: {{username}}", - "editUserDesc": "भूमिकाओं, व्यवस्थापक स्थिति, सत्रों और खाता सेटिंग्स को संपादित करें।", + "editUserDesc": "भूमिकाएं, प्रशासक स्थिति, सत्र और खाता सेटिंग्स संपादित करें।", "editUserUsername": "उपयोगकर्ता नाम", - "editUserAuthType": "प्राधिकरण प्रकार", + "editUserAuthType": "प्रमाणीकरण प्रकार", "editUserAdminStatus": "व्यवस्थापक स्थिति", - "editUserUserId": "उपयोगकर्ता पहचान", + "editUserUserId": "उपयोगकर्ता आईडी", "linkAccountTitle": "खाते लिंक करें", - "linkAccountDesc": "OIDC खाते {{username}} को मौजूदा स्थानीय खाते के साथ मर्ज करें।", - "linkAccountDescLocal": "एक स्थानीय खाते {{username}} को मौजूदा OIDC-केवल खाते से लिंक करें।", - "linkAccountWarningTitle": "यह करेगा:", - "linkAccountEffect1": "केवल OIDC खाते को हटाएँ", + "linkAccountDesc": "OIDC खाते {{username}} को मौजूदा स्थानीय खाते में विलय करें।", + "linkAccountDescLocal": "स्थानीय खाते {{username}} को मौजूदा केवल-OIDC खाते से लिंक करें।", + "linkAccountWarningTitle": "यह होगा:", + "linkAccountEffect1": "केवल-OIDC खाता हटाएं", "linkAccountEffect2": "लक्ष्य खाते में OIDC लॉगिन जोड़ें", - "linkAccountEffect3": "OIDC और पासवर्ड लॉगिन दोनों की अनुमति दें", - "linkAccountTargetUsername": "स्थानीय खाता उपयोगकर्ता नाम", + "linkAccountEffect3": "OIDC और पासवर्ड दोनों लॉगिन की अनुमति दें", + "linkAccountTargetUsername": "स्थानीय खाते का उपयोगकर्ता नाम", "linkAccountTargetPlaceholder": "लिंक करने के लिए स्थानीय खाते का उपयोगकर्ता नाम दर्ज करें", - "linkAccountOidcUsername": "OIDC खाता उपयोगकर्ता नाम", - "linkAccountOidcPlaceholder": "विलय करने के लिए केवल OIDC खाते का उपयोगकर्ता नाम दर्ज करें", - "linkAccountOidcNotFound": "उस उपयोगकर्ता नाम से कोई OIDC-विशिष्ट खाता नहीं मिला।", + "linkAccountOidcUsername": "OIDC खाते का उपयोगकर्ता नाम", + "linkAccountOidcPlaceholder": "विलय करने के लिए केवल-OIDC खाते का उपयोगकर्ता नाम दर्ज करें", + "linkAccountOidcNotFound": "उस उपयोगकर्ता नाम के साथ कोई केवल-OIDC खाता नहीं मिला", "linkAccounts": "खाते लिंक करें", - "linkAccountSuccess": "खाते सफलतापूर्वक लिंक हो गए", - "linkAccountFailed": "खातों को लिंक करने में विफल", + "linkAccountSuccess": "खाते सफलतापूर्वक लिंक किए गए", + "linkAccountFailed": "खाते लिंक करने में विफल", "linkAccountInProgress": "लिंक हो रहा है...", - "unlinkAccountTitle": "OIDC को अनलिंक करें", - "unlinkAccountDesc": "{{username}}से OIDC प्रमाणीकरण हटा दें। वे केवल अपने पासवर्ड से ही लॉग इन कर पाएंगे।", - "unlinkAccountWarning": "इससे इस खाते से OIDC लॉगिन हट जाएगा। लॉगिन जारी रखने के लिए उपयोगकर्ता को पासवर्ड सेट करना होगा।", - "unlinkAccount": "OIDC को अनलिंक करें", - "unlinkAccountInProgress": "लिंक हटाया जा रहा है...", - "unlinkAccountSuccess": "OIDC सफलतापूर्वक अनलिंक हो गया", - "unlinkAccountFailed": "OIDC को अनलिंक करने में विफल", + "unlinkAccountTitle": "OIDC अनलिंक करें", + "unlinkAccountDesc": "{{username}} से OIDC प्रमाणीकरण हटाएं। वे केवल अपने पासवर्ड से लॉग इन कर पाएंगे।", + "unlinkAccountWarning": "इससे इस खाते से OIDC लॉगिन हट जाएगा। लॉग इन जारी रखने के लिए उपयोगकर्ता का पासवर्ड सेट होना चाहिए।", + "unlinkAccount": "OIDC अनलिंक करें", + "unlinkAccountInProgress": "अनलिंक हो रहा है...", + "unlinkAccountSuccess": "OIDC सफलतापूर्वक अनलिंक किया गया", + "unlinkAccountFailed": "OIDC अनलिंक करने में विफल", "saving": "सहेजा जा रहा है...", - "updateRegistrationFailed": "पंजीकरण सेटिंग को अपडेट करने में विफल", + "updateRegistrationFailed": "पंजीकरण सेटिंग अपडेट करने में विफल", "updatePasswordLoginFailed": "पासवर्ड लॉगिन सेटिंग अपडेट करने में विफल", - "cannotDisablePasswordLoginWithTotp": "एक या अधिक उपयोगकर्ताओं के लिए 2FA सक्षम होने पर पासवर्ड लॉगिन अक्षम नहीं किया जा सकता है। कृपया पहले 2FA अक्षम करें।", - "updateOidcAutoProvisionFailed": "OIDC ऑटो-प्रोविजन सेटिंग को अपडेट करने में विफलता", - "updateOidcSilentLoginDefaultFailed": "साइलेंट OIDC लॉगिन सेटिंग को अपडेट करने में विफलता", - "updatePasswordResetFailed": "पासवर्ड रीसेट सेटिंग को अपडेट करने में विफलता", - "sessionTimeoutRange2": "सेशन टाइमआउट 1 से 720 घंटे के बीच होना चाहिए।", + "cannotDisablePasswordLoginWithTotp": "जब एक या अधिक उपयोगकर्ताओं के लिए 2FA सक्षम है, तब पासवर्ड लॉगिन अक्षम नहीं किया जा सकता। पहले 2FA अक्षम करें।", + "updateOidcAutoProvisionFailed": "OIDC ऑटो-प्रोविज़निंग सेटिंग अपडेट करने में विफल", + "updateOidcSilentLoginDefaultFailed": "साइलेंट OIDC लॉगिन सेटिंग अपडेट करने में विफल", + "updatePasswordResetFailed": "पासवर्ड रीसेट सेटिंग अपडेट करने में विफल", + "sessionTimeoutRange2": "सत्र समय-सीमा 1 से 720 घंटों के बीच होनी चाहिए", "sessionTimeoutSaved": "सेशन टाइमआउट सहेजा गया", - "sessionTimeoutSaveFailed": "सेशन टाइमआउट को सहेजने में विफल", + "sessionTimeoutSaveFailed": "सेशन टाइमआउट सहेजने में विफल", "monitoringIntervalInvalid": "अमान्य अंतराल मान", "monitoringSaved": "मॉनिटरिंग सेटिंग्स सहेजी गईं", "monitoringSaveFailed": "मॉनिटरिंग सेटिंग्स सहेजने में विफल", - "metricsHistoryRetention": "मैट्रिक्स इतिहास प्रतिधारण", + "metricsHistoryRetention": "मेट्रिक्स इतिहास प्रतिधारण", "metricsHistoryRetentionRange": "1 से 90 दिन", "days": "दिन", - "guacamoleSaved": "गुआकामोल की सेटिंग सहेज ली गई", - "guacamoleSaveFailed": "गुआकामोल की सेटिंग्स सहेजने में विफल।", - "guacamoleUpdateFailed": "गुआकामोल सेटिंग को अपडेट करने में विफलता", - "tailscaleSettingsSaved": "टेलस्केल सेटिंग्स सहेजी गईं", - "tailscaleSettingsSaveFailed": "टेलस्केल सेटिंग्स को सहेजने में विफल।", - "logLevelUpdateFailed": "लॉग स्तर को अपडेट करने में विफल", + "guacamoleSaved": "Guacamole सेटिंग्स सहेजी गईं", + "guacamoleSaveFailed": "Guacamole सेटिंग्स सहेजने में विफल", + "guacamoleUpdateFailed": "Guacamole सेटिंग अपडेट करने में विफल", + "tailscaleSettingsSaved": "Tailscale सेटिंग्स सहेजी गईं", + "tailscaleSettingsSaveFailed": "Tailscale सेटिंग्स सहेजने में विफल", + "logLevelUpdateFailed": "लॉग स्तर अपडेट करने में विफल", "oidcSaved": "OIDC कॉन्फ़िगरेशन सहेजा गया", - "oidcSaveFailed": "OIDC कॉन्फ़िगरेशन को सहेजने में विफल", - "oidcRemoved": "OIDC कॉन्फ़िगरेशन हटा दिया गया", - "oidcRemoveFailed": "OIDC कॉन्फ़िगरेशन को हटाने में विफल", + "oidcSaveFailed": "OIDC कॉन्फ़िग सहेजने में विफल", + "oidcRemoved": "OIDC कॉन्फ़िगरेशन हटाया गया", + "oidcRemoveFailed": "OIDC कॉन्फ़िग हटाने में विफल", "createUserRequired": "उपयोगकर्ता नाम और पासवर्ड आवश्यक हैं", - "createUserPasswordTooShort": "पासवर्ड कम से कम 6 अंकों का होना चाहिए", - "createUserSuccess": "उपयोगकर्ता \"{{username}}\" ने बनाया", + "createUserPasswordTooShort": "पासवर्ड कम से कम 6 वर्ण का होना चाहिए", + "createUserSuccess": "उपयोगकर्ता \"{{username}}\" बनाया गया", "createUserFailed": "उपयोगकर्ता बनाने में विफल", - "updateAdminStatusFailed": "व्यवस्थापक की स्थिति अपडेट करने में विफल", - "allSessionsRevoked": "सभी सत्र रद्द कर दिए गए", - "revokeSessionsFailed": "सत्र रद्द करने में विफल", + "updateAdminStatusFailed": "एडमिन स्थिति अपडेट करने में विफल", + "allSessionsRevoked": "सभी सेशन निरस्त कर दिए गए", + "revokeSessionsFailed": "सेशन निरस्त करने में विफल", "manageUserData": "उपयोगकर्ता डेटा प्रबंधित करें", - "backToUsers": "उपयोगकर्ताओं पर वापस जाएँ", + "backToUsers": "उपयोगकर्ताओं पर वापस जाएं", "manageTabAccount": "खाता", - "manageTabHosts": "मेजबान", - "manageTabCredentials": "साख", + "manageTabHosts": "होस्ट", + "manageTabCredentials": "क्रेडेंशियल्स", "manageTabSnippets": "स्निपेट्स", - "manageTabSessions": "सत्रों", + "manageTabSessions": "सेशन", "manageTabDanger": "खतरा", - "manageEditorBack": "{{username}}के डेटा पर वापस जाएँ", - "dataLockedBadge": "बंद", - "dataLockedNotice": "{{username}}का डेटा तब तक लॉक रहता है जब तक वे अगली बार लॉग इन नहीं करते। तब तक उनके होस्ट, क्रेडेंशियल और स्निपेट देखे या संपादित नहीं किए जा सकते।", - "resetPasswordTitle": "पासवर्ड रीसेट", - "resetPasswordOidcOnly": "यह उपयोगकर्ता किसी बाहरी प्रदाता के माध्यम से लॉग इन करता है और उसके पास कोई पासवर्ड नहीं है।", + "manageEditorBack": "{{username}} के डेटा पर वापस जाएं", + "dataLockedBadge": "लॉक्ड", + "dataLockedNotice": "{{username}} का डेटा तब तक लॉक रहता है जब तक वे अगली बार लॉग इन नहीं कर लेते। तब तक उनके होस्ट, क्रेडेंशियल और स्निपेट को न तो देखा जा सकता है और न ही संपादित किया जा सकता है।", + "resetPasswordTitle": "पासवर्ड रीसेट करें", + "resetPasswordOidcOnly": "यह उपयोगकर्ता किसी बाहरी प्रदाता के माध्यम से साइन इन करता है और उसका कोई पासवर्ड नहीं है।", "resetPasswordPlaceholder": "नया पासवर्ड", - "resetPasswordBtn": "रीसेट करें", + "resetPasswordBtn": "रीसेट", "resetPasswordWorking": "रीसेट हो रहा है...", - "resetPasswordSuccess": "पासवर्ड रीसेट", - "resetPasswordSuccessWiped": "पासवर्ड रीसेट कर दिया गया है। उपयोगकर्ता का एन्क्रिप्टेड डेटा मिटा दिया गया है।", + "resetPasswordSuccess": "पासवर्ड रीसेट हो गया", + "resetPasswordSuccessWiped": "पासवर्ड रीसेट हो गया। उपयोगकर्ता का एन्क्रिप्टेड डेटा मिटा दिया गया।", "resetPasswordFailed": "पासवर्ड रीसेट करने में विफल", - "resetPasswordConfirmWipe": "{{username}} ने एन्क्रिप्शन अपग्रेड के बाद से लॉग इन नहीं किया है, इसलिए उनका डेटा रिकवर नहीं किया जा सकता है। अभी रीसेट करने से उनके होस्ट, क्रेडेंशियल और स्निपेट डिलीट हो जाएंगे। जारी रखें?", + "resetPasswordConfirmWipe": "{{username}} ने एन्क्रिप्शन अपग्रेड के बाद से लॉग इन नहीं किया है, इसलिए उनका डेटा पुनर्प्राप्त नहीं किया जा सकता। अभी रीसेट करने से उनके होस्ट, क्रेडेंशियल और स्निपेट हटा दिए जाएँगे। जारी रखें?", "totpSectionTitle": "दो-कारक प्रमाणीकरण", - "totpStatusEnabled": "इस उपयोगकर्ता के लिए TOTP सक्षम है।", - "totpStatusDisabled": "इस उपयोगकर्ता के लिए TOTP सक्षम नहीं है।", - "disableTotp": "अक्षम करना", - "disableTotpConfirm": "{{username}}के लिए दो-कारक प्रमाणीकरण अक्षम करें? वे केवल अपने पासवर्ड से लॉग इन कर सकेंगे।", - "totpDisabledSuccess": "टीओटीपी अक्षम", - "totpDisableFailed": "TOTP को निष्क्रिय करने में विफल", - "manageApiKeys": "एपीआई कुंजी", - "apiKeyNamePlaceholder": "कुंजी नाम", - "apiKeyCopyNotice": "इस कुंजी को अभी कॉपी कर लें, यह दोबारा नहीं दिखाई देगी।", - "noApiKeysForUser": "कोई एपीआई कुंजी नहीं", - "apiKeyDeleteFailed": "एपीआई कुंजी को हटाने में विफल रहा", + "totpStatusEnabled": "इस उपयोगकर्ता के लिए TOTP सक्षम है", + "totpStatusDisabled": "इस उपयोगकर्ता के लिए TOTP सक्षम नहीं है", + "disableTotp": "अक्षम करें", + "disableTotpConfirm": "{{username}} के लिए दो-कारक प्रमाणीकरण अक्षम करें? वे केवल अपने पासवर्ड से लॉग इन कर सकेंगे।", + "totpDisabledSuccess": "TOTP अक्षम किया गया", + "totpDisableFailed": "TOTP अक्षम करने में विफल", + "manageApiKeys": "API कुंजियाँ", + "apiKeyNamePlaceholder": "कुंजी का नाम", + "apiKeyCopyNotice": "इस कुंजी को अभी कॉपी करें, यह दोबारा नहीं दिखाई जाएगी।", + "noApiKeysForUser": "कोई API कुंजियाँ नहीं", + "apiKeyDeleteFailed": "API कुंजी हटाने में विफल", "exportUserData": "डेटा निर्यात", - "exportUserDataDesc": "इस उपयोगकर्ता के होस्ट, क्रेडेंशियल और फ़ाइल मैनेजर डेटा को JSON फॉर्मेट में डाउनलोड करें। गुप्त जानकारी को डिक्रिप्ट कर दिया गया है।", + "exportUserDataDesc": "इस उपयोगकर्ता के होस्ट, क्रेडेंशियल और फ़ाइल प्रबंधक डेटा को JSON के रूप में डाउनलोड करें। सीक्रेट डिक्रिप्ट किए जाते हैं।", "exportUserDataSuccess": "उपयोगकर्ता डेटा निर्यात किया गया", "exportUserDataFailed": "उपयोगकर्ता डेटा निर्यात करने में विफल", - "hostsCount": "{{count}} मेजबान", + "hostsCount": "{{count}} होस्ट", "addHostForUser": "होस्ट जोड़ें", "noHostsForUser": "इस उपयोगकर्ता के पास कोई होस्ट नहीं है", - "connectToHost": "जोड़ना", - "deleteHostConfirm": "{{username}} से संबंधित होस्ट \"{{name}}\" को हटाएँ?", - "hostDeletedSuccess": "होस्ट हटा दिया गया", - "hostDeleteFailed": "होस्ट को हटाने में विफल", + "connectToHost": "कनेक्ट करें", + "deleteHostConfirm": "{{username}} से संबंधित होस्ट \"{{name}}\" हटाएँ?", + "hostDeletedSuccess": "होस्ट हटाया गया", + "hostDeleteFailed": "होस्ट हटाने में विफल", "credentialsCount": "{{count}} क्रेडेंशियल", "addCredentialForUser": "क्रेडेंशियल जोड़ें", "noCredentialsForUser": "इस उपयोगकर्ता के पास कोई क्रेडेंशियल नहीं है", - "deleteCredentialConfirm": "{{username}} से संबंधित क्रेडेंशियल \"{{name}}\" को हटाएँ?", + "deleteCredentialConfirm": "क्रेडेंशियल \"{{name}}\" हटाएं जो {{username}} का है?", "credentialDeletedSuccess": "क्रेडेंशियल हटा दिया गया", "credentialDeleteFailed": "क्रेडेंशियल हटाने में विफल", - "snippetsCount": "{{count}} स्निपेट्स", + "snippetsCount": "{{count}} स्निपेट", "addSnippetForUser": "स्निपेट जोड़ें", "noSnippetsForUser": "इस उपयोगकर्ता के पास कोई स्निपेट नहीं है", "snippetRequiredFields": "स्निपेट का नाम और सामग्री आवश्यक हैं", - "snippetNamePlaceholder": "स्निपेट नाम", + "snippetNamePlaceholder": "स्निपेट का नाम", "snippetContentPlaceholder": "कमांड सामग्री", "snippetFolderPlaceholder": "फ़ोल्डर (वैकल्पिक)", - "snippetSaved": "अंश सहेजा गया", + "snippetSaved": "स्निपेट सहेजा गया", "snippetSaveFailed": "स्निपेट सहेजने में विफल", - "deleteSnippetConfirm": "{{username}} से संबंधित अंश \"{{name}}\" को हटाएँ?", - "snippetDeletedSuccess": "अंश हटा दिया गया", - "snippetDeleteFailed": "स्निपेट को हटाने में विफल", + "deleteSnippetConfirm": "स्निपेट \"{{name}}\" हटाएं जो {{username}} का है?", + "snippetDeletedSuccess": "स्निपेट हटा दिया गया", + "snippetDeleteFailed": "स्निपेट हटाने में विफल", "noSessionsForUser": "कोई सक्रिय सत्र नहीं", - "deleteUserDangerDesc": "{{username}} और उनके सभी डेटा (होस्ट, क्रेडेंशियल, स्निपेट, इतिहास) को स्थायी रूप से हटा दें। इसे पूर्ववत नहीं किया जा सकता है।", - "deleteUserConfirm": "{{username}} और उनके सभी डेटा को स्थायी रूप से हटा दें?", - "deleteUserAdminBlocked": "इस उपयोगकर्ता को हटाने से पहले उसका व्यवस्थापक दर्जा हटा दें।", - "createRoleRequired": "नाम और डिस्प्ले नाम आवश्यक हैं", + "deleteUserDangerDesc": "{{username}} और उनके सभी डेटा (होस्ट, क्रेडेंशियल, स्निपेट, इतिहास) को स्थायी रूप से हटाएं। इसे पूर्ववत नहीं किया जा सकता।", + "deleteUserConfirm": "{{username}} और उनके सभी डेटा को स्थायी रूप से हटाएं?", + "deleteUserAdminBlocked": "इस उपयोगकर्ता को हटाने से पहले एडमिन स्थिति हटाएं।", + "createRoleRequired": "नाम और प्रदर्शन नाम आवश्यक हैं", "createRoleSuccess": "भूमिका \"{{name}}\" बनाई गई", "createRoleFailed": "भूमिका बनाने में विफल", "apiKeyNameRequired": "कुंजी का नाम आवश्यक है", "apiKeyUserRequired": "उपयोगकर्ता आईडी आवश्यक है", - "apiKeyCreatedSuccess": "एपीआई कुंजी \"{{name}}\" बनाई गई", - "apiKeyCreateFailed": "एपीआई कुंजी बनाने में विफल", - "exportSuccess": "डेटाबेस सफलतापूर्वक निर्यात हो गया", - "exportFailed": "डेटाबेस निर्यात विफल रहा", + "apiKeyCreatedSuccess": "API कुंजी \"{{name}}\" बनाई गई", + "apiKeyCreateFailed": "API कुंजी बनाने में विफल", + "exportSuccess": "डेटाबेस सफलतापूर्वक निर्यात किया गया", + "exportFailed": "डेटाबेस निर्यात विफल", "importSelectFile": "कृपया पहले एक फ़ाइल चुनें", - "importCompleted": "आयात पूर्ण हुआ: {{total}} आइटम आयात किए गए, {{skipped}} छोड़े गए", + "importCompleted": "आयात पूर्ण: {{total}} आइटम आयात किए गए, {{skipped}} छोड़े गए", "importFailed": "आयात विफल: {{error}}", - "importError": "डेटाबेस आयात विफल रहा", + "importError": "डेटाबेस आयात विफल", "sectionHostDefaults": "होस्ट डिफ़ॉल्ट", - "hostDefaultsDesc": "नया होस्ट बनाते समय सेटिंग्स स्वचालित रूप से लागू हो जाती हैं। व्यक्तिगत होस्ट इन्हें ओवरराइड कर सकते हैं।", + "hostDefaultsDesc": "नया होस्ट बनाते समय स्वचालित रूप से लागू होने वाली सेटिंग्स। व्यक्तिगत होस्ट अभी भी इन्हें ओवरराइड कर सकते हैं।", "hostDefaultsSocks5": "SOCKS5 प्रॉक्सी", - "hostDefaultsUseSocks5": "SOCKS5 प्रॉक्सी को सक्षम करें", - "hostDefaultsUseSocks5Desc": "सभी नए होस्ट पर SOCKS5 प्रॉक्सी को पहले से भरें", + "hostDefaultsUseSocks5": "SOCKS5 प्रॉक्सी सक्षम करें", + "hostDefaultsUseSocks5Desc": "सभी नए होस्ट पर SOCKS5 प्रॉक्सी पूर्व-भरें", "hostDefaultsSocks5Host": "प्रॉक्सी होस्ट / पोर्ट", "hostDefaultsSocks5Username": "प्रॉक्सी उपयोगकर्ता नाम", "hostDefaultsSocks5Password": "प्रॉक्सी पासवर्ड", "hostDefaultsMetrics": "होस्ट मेट्रिक्स", - "hostDefaultsMetricsEnabled": "मैट्रिक्स सक्षम करें", - "hostDefaultsMetricsEnabledDesc": "नए होस्ट पर CPU, मेमोरी और अन्य आँकड़े डिफ़ॉल्ट रूप से एकत्रित करें", - "hostDefaultsStatusCheckEnabled": "स्थिति जांच सक्षम करें", - "hostDefaultsStatusCheckEnabledDesc": "नए होस्ट पर डिफ़ॉल्ट रूप से ऑनलाइन/ऑफ़लाइन स्थिति की जाँच करें", + "hostDefaultsMetricsEnabled": "मेट्रिक्स सक्षम करें", + "hostDefaultsMetricsEnabledDesc": "डिफ़ॉल्ट रूप से नए होस्ट पर CPU, मेमोरी और अन्य आँकड़े एकत्र करें", + "hostDefaultsStatusCheckEnabled": "स्टेटस चेक सक्षम करें", + "hostDefaultsStatusCheckEnabledDesc": "डिफ़ॉल्ट रूप से नए होस्ट पर ऑनलाइन/ऑफ़लाइन स्थिति की जाँच करें", "hostDefaultsTerminal": "टर्मिनल", "hostDefaultsSessionLogging": "सत्र लॉगिंग", - "hostDefaultsSessionLoggingDesc": "नए होस्ट पर टर्मिनल सत्रों को डिफ़ॉल्ट रूप से रिकॉर्ड करें", - "hostDefaultsCommandHistory": "कमान का इतिहास", - "hostDefaultsCommandHistoryDesc": "नए होस्ट पर कमांड इतिहास को डिफ़ॉल्ट रूप से ट्रैक करें", + "hostDefaultsSessionLoggingDesc": "डिफ़ॉल्ट रूप से नए होस्ट पर टर्मिनल सत्र रिकॉर्ड करें", + "hostDefaultsCommandHistory": "कमांड इतिहास", + "hostDefaultsCommandHistoryDesc": "डिफ़ॉल्ट रूप से नए होस्ट पर कमांड इतिहास ट्रैक करें", "hostDefaultsSaved": "होस्ट डिफ़ॉल्ट सहेजे गए", "hostDefaultsSaveFailed": "होस्ट डिफ़ॉल्ट सहेजने में विफल", "rolePermissions": { "count": "{{count}} अनुमतियाँ", - "editAction": "संपादन अनुमतियाँ", - "loadError": "अनुमतियों की सूची लोड करने में विफल।", + "editAction": "अनुमतियाँ संपादित करें", + "loadError": "अनुमति कैटलॉग लोड करने में विफल", "saved": "भूमिका अनुमतियाँ सहेजी गईं", - "saveError": "भूमिका अनुमतियों को सहेजने में विफल", - "save": "बचाना", - "saving": "सहेजा जा रहा है..." + "saveError": "भूमिका अनुमतियाँ सहेजने में विफल", + "save": "सहेजें", + "saving": "सहेज रहा है..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "मेज़बान", - "hostPlaceholder": "192.168.1.1 या example.com", - "portLabel": "पत्तन", + "hostLabel": "होस्ट", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "पोर्ट", "portPlaceholder": "22", "usernameLabel": "उपयोगकर्ता नाम", - "usernamePlaceholder": "उपयोगकर्ता नाम", + "usernamePlaceholder": "username", "authLabel": "प्रमाणीकरण", "passwordLabel": "पासवर्ड", - "passwordPlaceholder": "पासवर्ड", - "privateKeyLabel": "निजी चाबी", - "privateKeyPlaceholder": "निजी कुंजी पेस्ट करें...", + "passwordPlaceholder": "password", + "privateKeyLabel": "निजी कुंजी", + "privateKeyPlaceholder": "निजी कुंजी चिपकाएं...", "credentialLabel": "क्रेडेंशियल", - "credentialPlaceholder": "सहेजे गए क्रेडेंशियल का चयन करें", + "credentialPlaceholder": "सहेजा गया क्रेडेंशियल चुनें", "connectToTerminal": "टर्मिनल से कनेक्ट करें", "connectToFiles": "फ़ाइलों से कनेक्ट करें" }, "history": { - "noTerminalSelected": "कोई टर्मिनल चयनित नहीं है", - "noTerminalSelectedHint": "कमांड हिस्ट्री देखने के लिए SSH टर्मिनल टैब खोलें।", - "searchPlaceholder": "खोज इतिहास...", - "clearAll": "सभी साफ करें", - "noHistoryEntries": "कोई इतिहास प्रविष्टियाँ नहीं हैं", + "noTerminalSelected": "कोई टर्मिनल चयनित नहीं", + "noTerminalSelectedHint": "कमांड इतिहास देखने के लिए SSH टर्मिनल टैब खोलें", + "searchPlaceholder": "इतिहास खोजें...", + "clearAll": "सभी साफ़ करें", + "noHistoryEntries": "कोई इतिहास प्रविष्टियाँ नहीं", "trackingDisabled": "इतिहास ट्रैकिंग अक्षम है", - "trackingDisabledHint": "इसे होस्ट के टर्मिनल सेटिंग्स में सक्षम करें।" + "trackingDisabledHint": "इसे होस्ट की टर्मिनल सेटिंग्स में सक्षम करें।" }, "sshTools": { - "keyRecordingTitle": "मुख्य रिकॉर्डिंग", - "recordToTerminals": "टर्मिनलों पर रिकॉर्ड करें", + "keyRecordingTitle": "की रिकॉर्डिंग", + "recordToTerminals": "टर्मिनलों में रिकॉर्ड करें", "selectAll": "सभी", "selectNone": "कोई नहीं", - "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं है", - "selectTerminalsAbove": "ऊपर दिए गए टर्मिनलों का चयन करें", - "broadcastInputPlaceholder": "यहां टाइप करके कीस्ट्रोक्स को प्रसारित करें...", + "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं", + "selectTerminalsAbove": "ऊपर के टर्मिनल चुनें", + "broadcastInputPlaceholder": "कीस्ट्रोक्स प्रसारित करने के लिए यहाँ टाइप करें...", "fillPassword": "पासवर्ड भरें", - "fillPasswordSuccess": "{{count}} टर्मिनल(ओं) में पासवर्ड भरा गया", - "fillPasswordMissing": "चयनित टर्मिनल(ओं) के लिए कोई पासवर्ड सहेजा नहीं गया है ( {{count}})।", - "stopRecording": "रिकॉर्डिंग बंद करें", + "fillPasswordSuccess": "{{count}} टर्मिनल(ों) में पासवर्ड भरा गया", + "fillPasswordMissing": "{{count}} चयनित टर्मिनल(ों) के लिए कोई सहेजा गया पासवर्ड नहीं", + "stopRecording": "रिकॉर्डिंग रोकें", "startRecording": "रिकॉर्डिंग शुरू करें", "settingsTitle": "सेटिंग्स", - "enableRightClickCopyPaste": "राइट-क्लिक कॉपी/पेस्ट को सक्षम करें" + "enableRightClickCopyPaste": "राइट-क्लिक कॉपी/पेस्ट सक्षम करें" }, "splitScreen": { "layoutTitle": "लेआउट", - "selectLayoutAbove": "ऊपर दिए गए लेआउट में से एक चुनें", - "selectLayoutHint": "प्रदर्शित होने वाले पैनलों की संख्या चुनें", - "panesTitle": "फलक", - "openTabsTitle": "टैब खोलें", - "dragTabsHint": "टैब को ऊपर दिए गए पैन में खींचें, या क्विक असाइनमेंट का उपयोग करें।", - "dropHere": "यहां छोड़ें", + "selectLayoutAbove": "ऊपर से एक लेआउट चुनें", + "selectLayoutHint": "कितने पैन दिखाने हैं चुनें", + "panesTitle": "पैन", + "openTabsTitle": "खुले टैब", + "dragTabsHint": "टैब को ऊपर के पैनों में खींचें, या त्वरित असाइन का उपयोग करें", + "dropHere": "यहाँ छोड़ें", "emptyPane": "खाली", "dashboard": "डैशबोर्ड", - "clearSplitScreen": "स्प्लिट स्क्रीन साफ़ करें", - "quickAssign": "त्वरित असाइनमेंट", - "alreadyAssigned": "फलक {{index}}", + "clearSplitScreen": "स्प्लिट स्क्रीन हटाएं", + "quickAssign": "त्वरित असाइन", + "alreadyAssigned": "पेन {{index}}", "splitTab": "स्प्लिट टैब", "addToSplit": "स्प्लिट में जोड़ें", - "removeFromSplit": "स्प्लिट से निकालें", - "assignToPane": "फलक को असाइन करें", - "hotkeysTitle": "कुंजीपटल अल्प मार्ग", - "hotkeysSplitRight": "दो-तरफ़ा विभाजन को टॉगल करें", - "hotkeysSplitBelow": "तीन-तरफ़ा विभाजन को टॉगल करें", - "hotkeysNavigatePane": "पैन नेविगेट करें", + "removeFromSplit": "स्प्लिट से हटाएं", + "assignToPane": "पेन को असाइन करें", + "hotkeysTitle": "कीबोर्ड शॉर्टकट", + "hotkeysSplitRight": "2-वे स्प्लिट टॉगल करें", + "hotkeysSplitBelow": "3-वे स्प्लिट टॉगल करें", + "hotkeysNavigatePane": "पेन नेविगेट करें", "hotkeysNextTab": "अगला टैब", "hotkeysPrevTab": "पिछला टैब" }, @@ -3026,183 +3177,228 @@ "createSnippetTitle": "स्निपेट बनाएं", "createSnippetDescription": "त्वरित निष्पादन के लिए एक नया कमांड स्निपेट बनाएं", "nameLabel": "नाम", - "namePlaceholder": "उदाहरण के लिए, Nginx को रीस्टार्ट करें", + "namePlaceholder": "जैसे, Nginx पुनरारंभ करें", "descriptionLabel": "विवरण", "descriptionPlaceholder": "वैकल्पिक विवरण", "optional": "वैकल्पिक", "folderLabel": "फ़ोल्डर", "noFolder": "कोई फ़ोल्डर नहीं (अवर्गीकृत)", - "commandLabel": "आज्ञा", - "commandPlaceholder": "उदाहरण के लिए, sudo systemctl restart nginx", - "cancel": "रद्द करना", + "commandLabel": "कमांड", + "commandPlaceholder": "जैसे, sudo systemctl restart nginx", + "cancel": "रद्द करें", "createSnippetButton": "स्निपेट बनाएं", - "createFolderTitle": "फ़ोल्डर बनाएँ", - "createFolderDescription": "अपने लेखों के अंशों को फ़ोल्डरों में व्यवस्थित करें।", + "createFolderTitle": "फ़ोल्डर बनाएं", + "createFolderDescription": "अपने स्निपेट्स को फ़ोल्डर में व्यवस्थित करें", "folderNameLabel": "फ़ोल्डर का नाम", - "folderNamePlaceholder": "उदाहरण के लिए, सिस्टम कमांड, डॉकर स्क्रिप्ट", + "folderNamePlaceholder": "जैसे, सिस्टम कमांड्स, Docker स्क्रिप्ट", "folderColorLabel": "फ़ोल्डर का रंग", "folderIconLabel": "फ़ोल्डर आइकन", - "previewLabel": "पूर्व दर्शन", + "previewLabel": "पूर्वावलोकन", "folderNameFallback": "फ़ोल्डर का नाम", - "createFolderButton": "फ़ोल्डर बनाएँ", + "createFolderButton": "फ़ोल्डर बनाएं", "targetTerminals": "लक्ष्य टर्मिनल", "selectAll": "सभी", "selectNone": "कोई नहीं", "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं है", - "searchPlaceholder": "खोज के अंश...", - "newSnippet": "नया अंश", + "searchPlaceholder": "स्निपेट खोजें...", + "newSnippet": "नया स्निपेट", "newFolder": "नया फ़ोल्डर", - "run": "दौड़ना", - "noSnippetsInFolder": "इस फ़ोल्डर में कोई स्निपेट नहीं हैं", + "run": "चलाएँ", + "noSnippetsInFolder": "इस फ़ोल्डर में कोई स्निपेट नहीं है", "uncategorized": "अवर्गीकृत", "editSnippetTitle": "स्निपेट संपादित करें", "editSnippetDescription": "इस कमांड स्निपेट को अपडेट करें", - "saveSnippetButton": "परिवर्तनों को सुरक्षित करें", + "saveSnippetButton": "परिवर्तन सहेजें", "createSuccess": "स्निपेट सफलतापूर्वक बनाया गया", "createFailed": "स्निपेट बनाने में विफल", - "updateSuccess": "स्निपेट सफलतापूर्वक अपडेट हो गया", - "updateFailed": "स्निपेट को अपडेट करने में विफल", - "deleteFailed": "स्निपेट को हटाने में विफल", + "updateSuccess": "स्निपेट सफलतापूर्वक अपडेट किया गया", + "updateFailed": "स्निपेट अपडेट करने में विफल", + "deleteFailed": "स्निपेट हटाने में विफल", "folderCreateSuccess": "फ़ोल्डर सफलतापूर्वक बनाया गया", "folderCreateFailed": "फ़ोल्डर बनाने में विफल", "editFolderTitle": "फ़ोल्डर संपादित करें", - "editFolderDescription": "इस फ़ोल्डर का नाम बदलें या इसका स्वरूप बदलें", - "saveFolderButton": "परिवर्तनों को सुरक्षित करें", + "editFolderDescription": "इस फ़ोल्डर का नाम बदलें या उसका स्वरूप बदलें", + "saveFolderButton": "परिवर्तन सहेजें", "editFolder": "फ़ोल्डर संपादित करें", "deleteFolder": "फ़ोल्डर हटाएं", - "folderDeleteSuccess": "फ़ोल्डर \"{{name}}\" हटा दिया गया", - "folderDeleteFailed": "फ़ोल्डर को हटाने में विफल", - "folderEditSuccess": "फ़ोल्डर सफलतापूर्वक अपडेट हो गया", - "folderEditFailed": "फ़ोल्डर को अपडेट करने में विफल", - "confirmRunMessage": "\"{{name}} \" चलाएँ?", - "confirmRunButton": "दौड़ना", - "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", - "copySuccess": "क्लिपबोर्ड पर \"{{name}}\" कॉपी किया गया", + "folderDeleteSuccess": "फ़ोल्डर \"{{name}}\" हटाया गया", + "folderDeleteFailed": "फ़ोल्डर हटाने में विफल", + "folderEditSuccess": "फ़ोल्डर सफलतापूर्वक अपडेट किया गया", + "folderEditFailed": "फ़ोल्डर अपडेट करने में विफल", + "confirmRunMessage": "\"{{name}}\" चलाएँ?", + "confirmRunButton": "चलाएँ", + "runSuccess": "\"{{name}}\" को {{count}} टर्मिनल में चलाया गया", + "copySuccess": "\"{{name}}\" को क्लिपबोर्ड पर कॉपी किया गया", "shareTitle": "स्निपेट साझा करें", "shareUser": "उपयोगकर्ता", "shareRole": "भूमिका", - "selectUser": "किसी उपयोगकर्ता का चयन करें...", + "selectUser": "एक उपयोगकर्ता चुनें...", "selectRole": "एक भूमिका चुनें...", - "shareSuccess": "अंश सफलतापूर्वक साझा किया गया", - "shareFailed": "अंश साझा करने में विफल", - "revokeSuccess": "पहुँच रद्द कर दी गई", + "shareSuccess": "स्निपेट सफलतापूर्वक साझा किया गया", + "shareFailed": "स्निपेट साझा करने में विफल", + "revokeSuccess": "पहुँच रद्द की गई", "revokeFailed": "पहुँच रद्द करने में विफल", - "currentAccess": "वर्तमान पहुंच", - "shareLoadError": "शेयर डेटा लोड करने में विफल", + "currentAccess": "वर्तमान पहुँच", + "shareLoadError": "साझा डेटा लोड करने में विफल", "loading": "लोड हो रहा है...", - "close": "बंद करना", - "reorderFailed": "स्निपेट क्रम को सहेजने में विफल", + "close": "बंद करें", + "reorderFailed": "स्निपेट क्रम सहेजने में विफल", "importExport": "आयात / निर्यात", "exportBtn": "JSON निर्यात करें", "importBtn": "JSON आयात करें", - "exportSuccess": "स्निपेट्स सफलतापूर्वक निर्यात किए गए", - "exportFailed": "स्निपेट्स निर्यात करने में विफल", - "importTitle": "आयात स्निपेट", - "importDescription": "टर्मिक्स द्वारा निर्यात की गई JSON फ़ाइल से स्निपेट और फ़ोल्डर आयात करें", - "importDropOrClick": "यहां एक JSON फ़ाइल डालें या ब्राउज़ करने के लिए क्लिक करें", + "exportSuccess": "स्निपेट सफलतापूर्वक निर्यात किए गए", + "exportFailed": "स्निपेट निर्यात करने में विफल", + "importTitle": "स्निपेट आयात करें", + "importDescription": "Termix द्वारा निर्यात की गई JSON फ़ाइल से स्निपेट और फ़ोल्डर आयात करें", + "importDropOrClick": "यहाँ JSON फ़ाइल ड्रॉप करें या ब्राउज़ करने के लिए क्लिक करें", "importSelectedFile": "चयनित: {{name}}", - "importOverwrite": "समान नाम और फ़ोल्डर वाले मौजूदा स्निपेट्स को ओवरराइट करें", - "importStartBtn": "आयात", + "importOverwrite": "समान नाम और फ़ोल्डर वाले मौजूदा स्निपेट को अधिलेखित करें", + "importStartBtn": "आयात करें", "importSuccess": "आयात पूर्ण: {{snippets}} स्निपेट जोड़े गए, {{updated}} अपडेट किए गए, {{skipped}} छोड़े गए, {{folders}} फ़ोल्डर जोड़े गए", - "importFailed": "स्निपेट्स आयात करने में विफल", - "importInvalidFile": "अमान्य फ़ाइल: स्निपेट्स या फ़ोल्डर सरणियों के साथ एक JSON ऑब्जेक्ट अपेक्षित था", - "targetHostsLabel": "लक्षित मेजबान", - "targetHostsHint": "इस कोड स्निपेट को बिना टर्मिनल खोले सीधे चलाने के लिए होस्ट असाइन करें।", - "noHostsAvailable": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है", - "clearTargetHosts": "सभी साफ करें", - "hasTargetHosts": "लक्ष्य होस्ट हैं", + "importFailed": "स्निपेट आयात करने में विफल", + "importInvalidFile": "अमान्य फ़ाइल: स्निपेट या फ़ोल्डर सरणियों के साथ एक JSON ऑब्जेक्ट अपेक्षित था", + "targetHostsLabel": "लक्ष्य होस्ट", + "targetHostsHint": "बिना खुले टर्मिनल के इस स्निपेट को सीधे चलाने के लिए होस्ट असाइन करें।", + "noHostsAvailable": "कोई होस्ट कॉन्फ़िगर नहीं है", + "clearTargetHosts": "सभी साफ़ करें", + "hasTargetHosts": "लक्ष्य होस्ट मौजूद हैं", "runOnTargets": "लक्ष्यों पर चलाएँ", - "directRunSuccess": "Ran \"{{name}}\" on {{count}} host(s)", + "directRunSuccess": "\"{{name}}\" को {{count}} होस्ट पर चलाया गया", "directRunPartialFail": "\"{{name}}\" एक या अधिक होस्ट पर विफल रहा", "executionResultTitle": "निष्पादन परिणाम: {{name}}", "executionResultDescription": "प्रत्येक लक्ष्य होस्ट पर स्निपेट चलाने से प्राप्त आउटपुट।", - "executionSuccess": "सफलता", + "executionSuccess": "सफल", "executionFailed": "असफल" }, + "keybindings": { + "title": "कुंजीपटल अल्प मार्ग", + "description": "टर्मिनल कॉपी, पेस्ट और कंट्रोल शॉर्टकट को कस्टमाइज़ करें, या टेक्स्ट भेजने या स्निपेट चलाने के लिए कुंजियों को बाइंड करें।", + "defaultsHeading": "अंतर्निर्मित शॉर्टकट", + "customHeading": "कस्टम शॉर्टकट", + "addBinding": "छोटा रास्ता जोडें", + "addBindingTitle": "छोटा रास्ता जोडें", + "editBindingTitle": "शॉर्टकट संपादित करें", + "loading": "लोड हो रहा है...", + "noCustomBindings": "अभी तक कोई कस्टम शॉर्टकट उपलब्ध नहीं हैं", + "defaultBadge": "गलती करना", + "customizedBadge": "स्वनिर्धारित", + "customize": "अनुकूलित करें", + "resetToDefault": "वितथ पर ले जाएं", + "resetAllToDefaults": "सभी सेटिंग्स को डिफ़ॉल्ट पर रीसेट करें", + "close": "बंद करना", + "cancel": "रद्द करना", + "saveBinding": "शॉर्टकट सहेजें", + "saveError": "कीबोर्ड शॉर्टकट सहेजने में विफल", + "pressKeysToRecord": "कुंजी संयोजन", + "pressKeysPlaceholder": "क्लिक करें और कुंजियाँ दबाएँ", + "recording": "बटन दबाएँ...", + "comboRequiredError": "पहले एक कुंजी संयोजन दबाएँ", + "textRequiredError": "भेजने के लिए टेक्स्ट दर्ज करें", + "controlCodeRequiredError": "नियंत्रण कोड के लिए एक अक्षर दर्ज करें", + "snippetRequiredError": "एक अंश का चयन करें", + "conflictWarning": "यह संयोजन पहले से ही {{combo}}से जुड़ा हुआ है। सहेजने से दोनों आग पकड़ लेंगे।", + "actionLabel": "कार्रवाई", + "actionCopy": "चयन की प्रतिलिपि बनाएँ", + "actionPaste": "पेस्ट करें", + "actionSendControlCode": "Ctrl+अक्षर सिग्नल भेजें", + "actionSendText": "संदेश भेजें", + "actionRunSnippet": "मौजूदा स्निपेट चलाएँ", + "controlCodeLabel": "पत्र", + "textLabel": "भेजने के लिए संदेश", + "appendEnterLabel": "भेजने के बाद एंटर दबाएँ", + "snippetLabel": "टुकड़ा", + "selectSnippetPlaceholder": "एक अंश का चयन करें", + "orphanedSnippetWarning": "अंश नहीं मिला", + "clipboardPermissionNote": "कुछ ब्राउज़रों में पहली बार उपयोग करने पर क्लिपबोर्ड की अनुमति मांगी जा सकती है।" + }, "userProfile": { - "donateTitle": "टर्मिक्स को जीवित रखें", - "donateDescription": "टर्मिक्स मुफ़्त है, सेल्फ-होस्टेड है और इसे कुछ ही लोगों ने अपने खाली समय में बनाया है। अगर इससे आपका समय या पैसा बचा है, तो क्रिप्टोकरेंसी में दान करने से इसे चालू रखने में मदद मिलेगी।", - "donateMilestones": "दान से SAML, Kubernetes और एजेंट सपोर्ट बनाने के लिए आवश्यक शोध और सीखने में लगने वाले समय को वित्त पोषित करने में मदद मिलती है। प्रगति देखें और दान करें।", - "donateButton": "क्रिप्टोकरेंसी दान करें", + "donateTitle": "Termix को जीवित रखें", + "donateDescription": "Termix मुफ़्त, स्व-होस्टेड है और केवल कुछ लोगों द्वारा अपने खाली समय में बनाया गया है। यदि इसने आपका समय या पैसा बचाया है, तो एक क्रिप्टो दान इसे चालू रखने में मदद करता है।", + "donateMilestones": "दान SAML, Kubernetes और एजेंट समर्थन बनाने के लिए आवश्यक शोध और सीखने के समय को निधि देने में मदद करते हैं। प्रगति देखें और दान करें।", + "donateButton": "क्रिप्टो दान करें", "storageModeLocal": "ब्राउज़र", "storageModeCloud": "डेटाबेस", - "storageModeDescription": "ब्राउज़र सेटिंग्स को केवल इसी ब्राउज़र में स्टोर करता है। डेटाबेस सर्वर से सिंक्रोनाइज़ होता है और किसी भी डिवाइस पर लोड हो जाता है।", - "resetToDefaults": "डिफ़ॉल्ट पर पुनः सेट करें", - "resetToDefaultsSuccess": "सेटिंग्स डिफ़ॉल्ट पर रीसेट हो गईं।", + "storageModeDescription": "ब्राउज़र सेटिंग्स को केवल इस ब्राउज़र में संग्रहीत करता है। डेटाबेस सर्वर से सिंक होता है और किसी भी डिवाइस पर लोड होता है।", + "resetToDefaults": "डिफ़ॉल्ट पर रीसेट करें", + "resetToDefaultsSuccess": "सेटिंग्स डिफ़ॉल्ट पर रीसेट कर दी गई हैं।", "storageModeSwitch": "वरीयता संग्रहण", "sectionAccount": "खाता", - "sectionAppearance": "उपस्थिति", + "sectionAppearance": "स्वरूप", "sectionSecurity": "सुरक्षा", - "sectionApiKeys": "एपीआई कुंजी", - "sectionC2sTunnels": "सी2एस सुरंगें", + "sectionApiKeys": "API कुंजियाँ", + "sectionData": "डेटा", + "sectionC2sTunnels": "C2S सुरंगें", "usernameLabel": "उपयोगकर्ता नाम", "roleLabel": "भूमिका", "roleAdministrator": "प्रशासक", "authMethodLabel": "प्रमाणीकरण विधि", "authMethodLocal": "स्थानीय", "twoFaLabel": "2FA", - "twoFaOn": "पर", + "twoFaOn": "चालू", "twoFaOff": "बंद", "versionLabel": "संस्करण", "betaProgramTitle": "बीटा प्रोग्राम", - "betaProgramDescription": "साप्ताहिक रूप से जारी होने वाले :beta Docker टैग के साथ आगामी सुविधाओं को पहले ही आजमाएं। यह अस्थिर है और उत्पादन के लिए उपयुक्त नहीं है।", - "betaProgramFeedback": "कोई बग मिला? इसकी रिपोर्ट यहां करें।", - "betaProgramLearnMore": "और अधिक जानें", - "deleteAccount": "खाता हटा दो", - "deleteAccountDescription": "अपना खाता स्थायी रूप से हटाएँ", - "changeServerDescription": "किसी दूसरे टर्मिक्स बैकएंड सर्वर पर स्विच करें", - "deleteButton": "मिटाना", - "deleteAccountPermanent": "यह कार्रवाई स्थायी है और इसे पूर्ववत नहीं किया जा सकता है।", - "deleteAccountWarning": "सभी सेशन, होस्ट, क्रेडेंशियल और सेटिंग्स स्थायी रूप से हटा दिए जाएंगे।", + "betaProgramDescription": "साप्ताहिक रोलिंग :beta Docker टैग के साथ आगामी सुविधाओं को जल्दी आज़माएँ। अस्थिर, उत्पादन के लिए नहीं।", + "betaProgramFeedback": "कोई बग मिला? यहाँ रिपोर्ट करें।", + "betaProgramLearnMore": "और जानें", + "deleteAccount": "खाता हटाएँ", + "deleteAccountDescription": "स्थायी रूप से अपना खाता हटाएँ", + "changeServerDescription": "किसी भिन्न Termix बैकएंड सर्वर पर स्विच करें", + "deleteButton": "हटाएं", + "deleteAccountPermanent": "यह कार्रवाई स्थायी है और इसे पूर्ववत नहीं किया जा सकता।", + "deleteAccountWarning": "सभी सत्र, होस्ट, क्रेडेंशियल और सेटिंग्स स्थायी रूप से हटा दिए जाएंगे।", "confirmPasswordDeletePlaceholder": "पुष्टि करने के लिए अपना पासवर्ड दर्ज करें", "languageLabel": "भाषा", - "themeLabel": "विषय", + "themeLabel": "थीम", "fontSizeLabel": "फ़ॉन्ट आकार", "accentColorLabel": "एक्सेंट रंग", "settingsTerminal": "टर्मिनल", - "commandAutocomplete": "कमांड ऑटो-कंप्लीट", - "commandAutocompleteDesc": "टाइप करते समय ऑटो-कंप्लीट दिखाएं", - "terminalLinkBehavior": "टर्मिनल लिंक पर क्लिक करें", - "terminalLinkBehaviorDesc": "टर्मिनल में लिंक पर क्लिक करने पर डिफ़ॉल्ट व्यवहार", + "commandAutocomplete": "कमांड ऑटोकम्प्लीट", + "commandAutocompleteDesc": "टाइप करते समय ऑटोकम्प्लीट दिखाएं", + "keyboardShortcuts": "कुंजीपटल अल्प मार्ग", + "keyboardShortcutsDescription": "टर्मिनल कॉपी, पेस्ट और कमांड शॉर्टकट को कस्टमाइज़ करें", + "manageShortcuts": "प्रबंधित करना", + "terminalLinkBehavior": "टर्मिनल लिंक क्लिक", + "terminalLinkBehaviorDesc": "टर्मिनल में लिंक क्लिक करने पर डिफ़ॉल्ट व्यवहार", "historyTracking": "इतिहास ट्रैकिंग", - "historyTrackingDesc": "टर्मिनल कमांड को ट्रैक करें", + "historyTrackingDesc": "टर्मिनल कमांड का ट्रैक रखें", "commandPalette": "कमांड पैलेट", "commandPaletteDesc": "कीबोर्ड शॉर्टकट सक्षम करें", - "reopenTabsOnLogin": "लॉगिन करने पर टैब दोबारा खोलें", - "reopenTabsOnLoginDesc": "लॉग इन करने या पेज को रिफ्रेश करने पर, यहां तक कि किसी अन्य डिवाइस से भी, आपके खुले टैब रीस्टोर हो जाएंगे।", + "reopenTabsOnLogin": "लॉगिन पर टैब फिर से खोलें", + "reopenTabsOnLoginDesc": "लॉग इन करने या पेज रिफ्रेश करने पर, दूसरे डिवाइस से भी, अपने खुले टैब पुनर्स्थापित करें", "confirmTabClose": "टैब बंद करने की पुष्टि करें", "confirmTabCloseDesc": "टर्मिनल टैब बंद करने से पहले पूछें", - "settingsSidebar": "साइड बार", - "showHostTags": "शो होस्ट टैग", + "settingsSidebar": "साइडबार", + "showHostTags": "होस्ट टैग दिखाएं", "showHostTagsDesc": "होस्ट सूची में टैग प्रदर्शित करें", - "hostTrayOnClick": "होस्ट की कार्रवाइयों का विस्तार करने के लिए क्लिक करें", - "hostTrayOnClickDesc": "कनेक्शन बटन हमेशा दिखाएं; प्रबंधन विकल्पों को विस्तारित करने के लिए होवर करने के बजाय क्लिक करें।", - "compactHostView": "कॉम्पैक्ट होस्ट व्यू", - "compactHostViewDesc": "प्रत्येक होस्ट को एक ही पंक्ति में संक्षिप्त करें, जिसमें केवल उसका नाम और पता दिखाई दे।", + "hostTrayOnClick": "होस्ट क्रियाएं विस्तृत करने के लिए क्लिक करें", + "hostTrayOnClickDesc": "कनेक्शन बटन हमेशा दिखाएं; मैनेजमेंट विकल्पों को होवर करने के बजाय क्लिक करके विस्तृत करें", + "compactHostView": "कॉम्पैक्ट होस्ट दृश्य", + "compactHostViewDesc": "प्रत्येक होस्ट को एक ही पंक्ति में संक्षिप्त करें जिसमें केवल उसका नाम और पता दिखे", "statusColors": "वास्तविक स्थिति रंग", - "statusColorsDesc": "ऑनलाइन/ऑफलाइन स्थिति दर्शाने के लिए एक्सेंट कलर के बजाय हरे/लाल रंग का उपयोग करें।", - "pinAppRail": "पिन ऐप रेल", - "pinAppRailDesc": "होवर करने पर विस्तार करने के बजाय, बाएँ साइडबार ऐप रेल को हमेशा विस्तारित रखें।", - "expandAppRailOnHover": "होवर करने पर ऐप रेल का विस्तार करें", - "expandAppRailOnHoverDesc": "जब पॉइंटर बाईं ओर के साइडबार ऐप रेल पर जाता है, तो उसे फैलने दें।", - "settingsNavigation": "मार्गदर्शन", - "navigationTabsDesc": "ऐप रेल में कौन से टैब दिखाई देंगे, यह चुनें", + "statusColorsDesc": "ऑनलाइन/ऑफ़लाइन स्थिति के लिए एक्सेंट रंग के बजाय हरा/लाल उपयोग करें", + "pinAppRail": "ऐप रेल पिन करें", + "pinAppRailDesc": "बाएं साइडबार ऐप रेल को होवर पर विस्तृत करने के बजाय हमेशा विस्तृत रखें", + "expandAppRailOnHover": "होवर पर ऐप रेल विस्तृत करें", + "expandAppRailOnHoverDesc": "जब पॉइंटर इसके ऊपर जाए तो बाएं साइडबार ऐप रेल को विस्तृत होने दें", + "settingsNavigation": "नेविगेशन", + "navigationTabsDesc": "चुनें कि ऐप रेल में कौन से टैब दिखाई दें", "settingsSnippets": "स्निपेट्स", - "foldersCollapsed": "फ़ोल्डर बंद कर दिए गए", - "foldersCollapsedDesc": "फ़ोल्डर डिफ़ॉल्ट रूप से बंद हो जाते हैं", + "foldersCollapsed": "फ़ोल्डर्स संकुचित", + "foldersCollapsedDesc": "डिफ़ॉल्ट रूप से फ़ोल्डर्स संकुचित करें", "confirmExecution": "निष्पादन की पुष्टि करें", - "confirmExecutionDesc": "स्निपेट चलाने से पहले पुष्टि करें", - "settingsUpdates": "अपडेट", + "confirmExecutionDesc": "स्निपेट्स चलाने से पहले पुष्टि करें", + "settingsUpdates": "अपडेट्स", "disableUpdateChecks": "अपडेट जांच अक्षम करें", - "disableUpdateChecksDesc": "अपडेट की जाँच करना बंद करें", - "totpAuthenticator": "टीओटीपी प्रमाणीकरणकर्ता", + "disableUpdateChecksDesc": "अपडेट की जांच करना बंद करें", + "totpAuthenticator": "TOTP प्रमाणक", "totpEnabled": "2FA सक्षम है", - "totpDisabled": "लॉगिन सुरक्षा को और बढ़ाएं", - "disable": "अक्षम करना", - "enable": "सक्षम", - "setupTotp": "टीओटीपी सेटअप करें", - "qrCode": "क्यू आर संहिता", - "totpInstructions": "अपने प्रमाणीकरण ऐप में क्यूआर कोड स्कैन करें या गुप्त जानकारी दर्ज करें, फिर 6 अंकों का कोड दर्ज करें।", + "totpDisabled": "अतिरिक्त लॉगिन सुरक्षा जोड़ें", + "disable": "अक्षम करें", + "enable": "सक्षम करें", + "setupTotp": "TOTP सेटअप करें", + "qrCode": "QR कोड", + "totpInstructions": "QR कोड स्कैन करें या अपने प्रमाणक ऐप में सीक्रेट दर्ज करें, फिर 6-अंकीय कोड दर्ज करें", "totpCodePlaceholder": "000000", "verify": "सत्यापित करें", "changePassword": "पासवर्ड बदलें", @@ -3213,41 +3409,57 @@ "confirmPasswordLabel": "नए पासवर्ड की पुष्टि करें", "confirmPasswordPlaceholder": "नए पासवर्ड की पुष्टि करें", "updatePassword": "पासवर्ड अपडेट करें", - "createApiKeyTitle": "एपीआई कुंजी बनाएं", - "createApiKeyDescription": "प्रोग्रामेटिक एक्सेस के लिए एक नई एपीआई कुंजी जनरेट करें।", + "createApiKeyTitle": "API कुंजी बनाएं", + "createApiKeyDescription": "प्रोग्रामेटिक एक्सेस के लिए एक नई API कुंजी जनरेट करें।", "apiKeyNameLabel": "नाम", - "apiKeyNamePlaceholder": "उदाहरण के लिए सीआई पाइपलाइन", + "apiKeyNamePlaceholder": "जैसे CI पाइपलाइन", "expiryDateLabel": "समाप्ति तिथि", "optional": "वैकल्पिक", - "cancel": "रद्द करना", - "createKey": "कुंजी बनाएँ", + "cancel": "रद्द करें", + "createKey": "कुंजी बनाएं", "apiKeyCount": "{{count}} कुंजियाँ", "newKey": "नई कुंजी", - "noApiKeys": "अभी तक कोई एपीआई कुंजी उपलब्ध नहीं है।", + "noApiKeys": "अभी तक कोई API कुंजी नहीं है।", "apiKeyActive": "सक्रिय", - "apiKeyUsageHint": "अपनी चाबी भी साथ में रखें।", - "apiKeyUsageHintHeader": "शीर्षक।", - "apiKeyPermissionsHint": "कुंजी बनाने वाले उपयोगकर्ता की अनुमतियों को विरासत में प्राप्त करती है।", + "apiKeyUsageHint": "अपनी कुंजी को", + "apiKeyUsageHintHeader": " हेडर में शामिल करें।", + "apiKeyPermissionsHint": "कुंजियाँ बनाने वाले उपयोगकर्ता की अनुमतियाँ प्राप्त करती हैं।", + "exportData": "मेरा डेटा निर्यात करें", + "exportDataDesc": "अपने होस्ट, क्रेडेंशियल और सेटिंग्स का बैकअप डाउनलोड करें और इसे किसी अन्य डिवाइस पर ट्रांसफर करें।", + "export": "निर्यात", + "exporting": "निर्यात हो रहा है...", + "importData": "मेरा डेटा आयात करें", + "importDataDesc": ".sqlite बैकअप फ़ाइल से अपने होस्ट, क्रेडेंशियल और सेटिंग्स को पुनर्स्थापित करें", + "importDataSelected": "चयनित: {{name}}", + "selectFile": "फ़ाइल का चयन करें", + "changeFile": "परिवर्तन", + "import": "आयात", + "importing": "आयात हो रहा है...", + "exportSuccess": "डेटा सफलतापूर्वक निर्यात हो गया", + "exportFailed": "डेटा निर्यात विफल रहा", + "importSelectFile": "कृपया पहले एक फ़ाइल चुनें", + "importCompleted": "आयात पूर्ण हुआ: {{total}} आइटम आयात किए गए, {{skipped}} छोड़े गए", + "importFailed": "आयात विफल: {{error}}", "roleUser": "उपयोगकर्ता", - "authMethodDual": "दोहरी प्राधिकरण", - "authMethodOidc": "ओआईडीसी", + "authMethodDual": "दोहरा प्रमाणीकरण", + "authMethodOidc": "OIDC", "totpSetupFailed": "TOTP सेटअप शुरू करने में विफल", - "totpEnter6Digits": "6 अंकों का कोड दर्ज करें", - "totpEnabledSuccess": "दो-कारक प्रमाणीकरण सक्षम", - "totpInvalidCode": "अवैध कोड, पुन: प्रयास करें", + "totpEnter6Digits": "6-अंकीय कोड दर्ज करें", + "totpEnabledSuccess": "दो-कारक प्रमाणीकरण सक्षम किया गया", + "totpInvalidCode": "अमान्य कोड, कृपया पुनः प्रयास करें", "totpDisableInputRequired": "अपना TOTP कोड या पासवर्ड दर्ज करें", - "totpDisabledSuccess": "दो-कारक प्रमाणीकरण अक्षम", - "totpDisableFailed": "2FA को अक्षम करने में विफल।", - "totpDisableTitle": "2FA को अक्षम करें", - "totpDisablePlaceholder": "टीओटीपी कोड या पासवर्ड दर्ज करें", - "totpDisableConfirm": "2FA को अक्षम करें", + "totpDisabledSuccess": "दो-कारक प्रमाणीकरण अक्षम किया गया", + "totpDisableFailed": "2FA अक्षम करने में विफल", + "totpDisableTitle": "2FA अक्षम करें", + "totpDisablePlaceholder": "TOTP कोड या पासवर्ड दर्ज करें", + "totpDisableConfirm": "2FA अक्षम करें", "totpContinueVerify": "सत्यापन जारी रखें", "totpVerifyTitle": "कोड सत्यापित करें", "totpBackupTitle": "बैकअप कोड", "totpDownloadBackup": "बैकअप कोड डाउनलोड करें", "passkeys": "पासकीज़", - "passkeysDesc": "पासवर्ड के बिना साइन इन करने के लिए WebAuthn/FIDO2 क्रेडेंशियल का उपयोग करें।", - "passkeyName": "पासकी का नाम", + "passkeysDesc": "बिना पासवर्ड साइन इन करने के लिए WebAuthn/FIDO2 क्रेडेंशियल का उपयोग करें", + "passkeyName": "पासकी नाम", "passkeyUvPreferred": "पसंदीदा", "passkeyUvRequired": "आवश्यक", "passkeyUvDiscouraged": "हतोत्साहित", @@ -3255,35 +3467,35 @@ "noPasskeys": "कोई पासकी पंजीकृत नहीं है।", "passkeyAdded": "पासकी जोड़ी गई", "passkeyAddFailed": "पासकी जोड़ने में विफल", - "passkeyDeleted": "पासकी हटा दी गई", + "passkeyDeleted": "पासकी हटाई गई", "passkeyDeleteFailed": "पासकी हटाने में विफल", - "done": "हो गया", - "secretCopied": "गुप्त कुंजी क्लिपबोर्ड पर कॉपी हो गई", - "apiKeyNameRequired": "कुंजी का नाम आवश्यक है", - "apiKeyCreated": "एपीआई कुंजी \"{{name}}\" बनाई गई", - "apiKeyCreateFailed": "एपीआई कुंजी बनाने में विफल", + "done": "पूर्ण", + "secretCopied": "सीक्रेट क्लिपबोर्ड पर कॉपी किया गया", + "apiKeyNameRequired": "कुंजी नाम आवश्यक है", + "apiKeyCreated": "API कुंजी \"{{name}}\" बनाई गई", + "apiKeyCreateFailed": "API कुंजी बनाने में विफल", "apiKeyUser": "उपयोगकर्ता", - "apiKeyExpires": "समय-सीमा समाप्त", - "apiKeyRevoked": "एपीआई कुंजी \"{{name}}\" निरस्त कर दी गई", - "apiKeyRevokeFailed": "एपीआई कुंजी रद्द करने में विफल", - "passwordFieldsRequired": "वर्तमान और नए पासवर्ड आवश्यक हैं", - "passwordMismatch": "सांकेतिक शब्द मेल नहीं खाते", - "passwordTooShort": "पासवर्ड कम से कम 6 अंकों का होना चाहिए", - "passwordUpdated": "पासवर्ड सफलतापूर्वक अद्यतन", + "apiKeyExpires": "समाप्ति", + "apiKeyRevoked": "API कुंजी \"{{name}}\" रद्द की गई", + "apiKeyRevokeFailed": "API कुंजी रद्द करने में विफल", + "passwordFieldsRequired": "वर्तमान और नया पासवर्ड आवश्यक हैं", + "passwordMismatch": "पासवर्ड मेल नहीं खाते", + "passwordTooShort": "पासवर्ड कम से कम 6 अक्षरों का होना चाहिए", + "passwordUpdated": "पासवर्ड सफलतापूर्वक अपडेट किया गया", "passwordUpdateFailed": "पासवर्ड अपडेट करने में विफल", - "deletePasswordRequired": "अपना खाता हटाने के लिए पासवर्ड आवश्यक है।", + "deletePasswordRequired": "खाता हटाने के लिए पासवर्ड आवश्यक है", "deleteFailed": "खाता हटाने में विफल", - "deleting": "हटा रहा हूँ...", - "colorPickerTooltip": "कलर पिकर खोलें", - "themeSystem": "प्रणाली", - "themeLight": "रोशनी", - "themeDark": "अँधेरा", - "themeDracula": "ड्रेकुला", - "themeCatppuccin": "कैटपुचिन", + "deleting": "हटाया जा रहा है...", + "colorPickerTooltip": "रंग चयनक खोलें", + "themeSystem": "सिस्टम", + "themeLight": "लाइट", + "themeDark": "डार्क", + "themeDracula": "ड्रैकुला", + "themeCatppuccin": "कैटपुचीन", "themeNord": "नॉर्ड", - "themeSolarized": "सौर ऊर्जा से युक्त", + "themeSolarized": "सोलाराइज़्ड", "themeTokyoNight": "टोक्यो नाइट", - "themeOneDark": "एक अंधेरा", + "themeOneDark": "वन डार्क", "themeGruvbox": "ग्रुवबॉक्स" } } @@ -3292,181 +3504,182 @@ "title": "Tmux मॉनिटर", "failedToLoadHosts": "होस्ट लोड करने में विफल", "failedToLoad": "tmux सत्र लोड करने में विफल", - "tmuxUnavailable": "इस होस्ट पर tmux स्थापित नहीं है।", + "tmuxUnavailable": "इस होस्ट पर tmux इंस्टॉल नहीं है", "noSessions": "इस होस्ट पर कोई tmux सत्र नहीं है", "noHostSelected": "कोई होस्ट चयनित नहीं है", - "attached": "जुड़ा हुआ", - "detached": "अलग", - "attach": "संलग्न करना", - "editTags": "जोड़ संपादित करें", - "tagsHint": "अल्पविराम से अलग किए गए टैग (जैसे YOLO, lab, training)", + "attached": "अटैच्ड", + "detached": "डिटैच्ड", + "attach": "अटैच करें", + "editTags": "टैग संपादित करें", + "tagsHint": "अल्पविराम से अलग टैग (जैसे YOLO, lab, training)", "tagsSaved": "टैग सहेजे गए", "tagsSaveFailed": "टैग सहेजने में विफल", - "searchPlaceholder": "विभिन्न सत्रों में खोज परिणाम...", + "searchPlaceholder": "सत्रों में आउटपुट खोजें...", "searchResults": "{{count}} मिलान", - "searchFailed": "खोज विफल रही", - "selectPaneHint": "आउटपुट का पूर्वावलोकन करने के लिए एक फलक का चयन करें", + "searchFailed": "खोज विफल", + "selectPaneHint": "इसके आउटपुट का पूर्वावलोकन करने के लिए एक पैन चुनें", "closePreview": "पूर्वावलोकन बंद करें", "newSession": "नया सत्र", "newSessionHint": "सत्र का नाम (अक्षर, अंक, _ @ % + = -)", - "newSessionPlaceholder": "मेरा सत्र", + "newSessionPlaceholder": "my-session", "create": "बनाएं", "sessionCreated": "सत्र \"{{name}}\" बनाया गया", "sessionCreateFailed": "सत्र बनाने में विफल", - "splitRight": "दाईं ओर विभाजित करें", - "splitDown": "विभाजित करें", - "splitFailed": "फलक को विभाजित करने में विफल", + "splitRight": "दाएं विभाजित करें", + "splitDown": "नीचे विभाजित करें", + "splitFailed": "पैन विभाजित करने में विफल", "newWindow": "नई विंडो", "windowCreateFailed": "विंडो बनाने में विफल", - "attachSessionTooltip": "{{session}} से संलग्न करें", - "refresh": "ताज़ा करना", - "refreshFailed": "सत्रों को रीफ़्रेश करने में विफल", - "collapseAll": "सभी को संकुचित करें", - "expandAll": "सभी का विस्तार", - "moreActions": "और कार्रवाइयां", - "sessionStats": "सत्र के आँकड़े", - "renameSessionTitle": "सत्र का नाम बदलें \"{{name}}\"", + "attachSessionTooltip": "{{session}} में अटैच करें", + "refresh": "ताज़ा करें", + "refreshFailed": "सत्र ताज़ा करने में विफल", + "collapseAll": "सभी संकुचित करें", + "expandAll": "सभी विस्तृत करें", + "moreActions": "अधिक कार्रवाइयां", + "sessionStats": "सत्र आँकड़े", + "renameSessionTitle": "सत्र \"{{name}}\" का नाम बदलें", "rename": "नाम बदलें", - "sessionRenamed": "सत्र का नाम बदलकर \"{{name}} \" कर दिया गया है", + "sessionRenamed": "सत्र का नाम बदलकर \"{{name}}\" कर दिया गया", "sessionRenameFailed": "सत्र का नाम बदलने में विफल", - "editTagsTitle": "\"{{name}} \" के लिए टैग संपादित करें", - "killPane": "किल पेन", + "editTagsTitle": "\"{{name}}\" के लिए टैग संपादित करें", + "killPane": "पेन नष्ट करें", "resizeTree": "आकार बदलने के लिए खींचें — रीसेट करने के लिए डबल-क्लिक करें", - "reattach": "पुनः संलग्न करें (अस्पष्ट दृश्य को ठीक करता है)", - "killWindow": "किल विंडो", - "killWindowTitle": "\"{{session}} \" की विंडो {{index}} को बंद करें?", - "killWindowBody": "इस विंडो में मौजूद सभी फलक और प्रक्रियाएं समाप्त हो जाएंगी। अंतिम विंडो को बंद करने से सत्र समाप्त हो जाएगा।", - "windowKillFailed": "विंडो को बंद करने में विफल", + "reattach": "पुनः अटैच करें (विकृत दृश्य को ठीक करता है)", + "killWindow": "विंडो नष्ट करें", + "killWindowTitle": "क्या \"{{session}}\" की विंडो {{index}} नष्ट करें?", + "killWindowBody": "इस विंडो के सभी पेन और प्रक्रियाएँ समाप्त हो जाएँगी। अंतिम विंडो नष्ट करने से सत्र समाप्त हो जाता है।", + "windowKillFailed": "विंडो नष्ट करने में विफल", "statusActivity": "गतिविधि", "statusWindows": "विंडोज़", - "statusPanes": "फलक", + "statusPanes": "पेन", "statusTags": "टैग", - "killPaneTitle": "किल पेन {{id}}?", - "killPaneBody": "इस फलक में चल रही प्रक्रिया समाप्त कर दी जाएगी। अंतिम फलक को बंद करने से उसकी विंडो भी बंद हो जाएगी।", - "paneKillFailed": "फलक को नष्ट करने में विफल", - "killSessionTitle": "सत्र को समाप्त करें \"{{name}}\"?", - "killSessionBody": "इस सत्र में चल रही सभी विंडो और प्रक्रियाएं समाप्त हो जाएंगी। इसे वापस नहीं लिया जा सकता।", - "kill": "मारना", - "sessionKilled": "सत्र \"{{name}}\" समाप्त हो गया", - "sessionKillFailed": "सेशन समाप्त करने में विफल", - "hostUnreachable": "होस्ट से कनेक्ट नहीं हो सका। कृपया जांच लें कि वह ऑनलाइन है और पहुंच योग्य है।", + "killPaneTitle": "क्या पेन {{id}} नष्ट करें?", + "killPaneBody": "इस पेन में चल रही प्रक्रिया समाप्त हो जाएगी। अंतिम पेन नष्ट करने पर इसकी विंडो बंद हो जाती है।", + "paneKillFailed": "पेन नष्ट करने में विफल", + "killSessionTitle": "क्या सत्र \"{{name}}\" नष्ट करें?", + "killSessionBody": "इस सत्र की सभी विंडोज़ और चल रही प्रक्रियाएँ समाप्त हो जाएँगी। इसे पूर्ववत नहीं किया जा सकता।", + "kill": "नष्ट करें", + "sessionKilled": "सत्र \"{{name}}\" नष्ट किया गया", + "sessionKillFailed": "सत्र नष्ट करने में विफल", + "hostUnreachable": "होस्ट से कनेक्ट नहीं हो सका। जाँचें कि यह ऑनलाइन और पहुँच योग्य है।", "noServer": "इस होस्ट पर कोई tmux सर्वर नहीं चल रहा है।", - "searchTruncated": "आंशिक परिणाम — खोज प्रत्येक फलक की अंतिम {{lines}} पंक्तियों और अधिकतम {{panes}} फलकों को कवर करती है।", - "closeSearchResults": "खोज परिणामों को बंद करें", - "retry": "पुन: प्रयास करें", + "searchTruncated": "आंशिक परिणाम — खोज प्रत्येक पेन की अंतिम {{lines}} पंक्तियों और अधिकतम {{panes}} पेनों में की गई है।", + "closeSearchResults": "खोज परिणाम बंद करें", + "retry": "पुनः प्रयास करें", "noHosts": "कोई SSH होस्ट उपलब्ध नहीं है", - "noHostsHint": "किसी SSH होस्ट पर Tmux Monitor विकल्प को सक्षम करें (होस्ट मैनेजर → टर्मिनल टैब) ताकि उसके tmux सत्रों की निगरानी की जा सके।", - "tmuxInstallHint": "इसे होस्ट पर निम्न प्रकार से इंस्टॉल करें:", - "attachTooltip": "{{host}} पर एक टर्मिनल खोलें", - "attachTooltipPane": "{{host}} — tmux सत्र {{session}} के लिए एक टर्मिनल खोलें", - "timeJustNow": "बस अब", - "timeMinutes": "{{count}}मीटर पहले", - "timeHours": "{{count}}घंटे पहले", - "timeDays": "{{count}}दिन पहले" + "noHostsHint": "किसी SSH होस्ट पर Tmux Monitor विकल्प सक्षम करें (होस्ट प्रबंधक → टर्मिनल टैब) ताकि उसके tmux सत्रों की निगरानी की जा सके।", + "tmuxInstallHint": "इसे होस्ट पर इंस्टॉल करें:", + "attachTooltip": "{{host}} पर टर्मिनल खोलें", + "attachTooltipPane": "{{host}} पर टर्मिनल खोलें — tmux सत्र {{session}}", + "timeJustNow": "अभी", + "timeMinutes": "{{count}}m पहले", + "timeHours": "{{count}}h पहले", + "timeDays": "{{count}}d पहले" }, "mobileKeyboard": { - "shift": "बदलाव", - "ctrl": "कंट्रोल", - "esc": "ईएससी", - "tab": "टैब", + "shift": "Shift", + "ctrl": "Ctrl", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "ऊपर की ओर तीर", - "arrowDown": "नीचे की ओर तीर", + "arrowUp": "ऊपर तीर", + "arrowDown": "नीचे तीर", "arrowLeft": "बाएँ तीर", "arrowRight": "दाएँ तीर", - "home": "घर", - "end": "अंत", - "pageUp": "पीजीअप", - "pageDown": "पीजीडीएन", - "delete": "डेल", - "editQuickKeys": "त्वरित कुंजियों को संपादित करें", - "quickKeysTitle": "त्वरित कुंजी", - "quickKeysDesc": "हटाने के लिए × पर टैप करें। अधिकतम 8 अक्षर तक समर्थित।", - "quickKeyPlaceholder": "उदाहरण के लिए, सूडो ", - "addQuickKey": "जोड़ना", - "removeQuickKey": "निकालना", - "resetDefaults": "डिफ़ॉल्ट पर पुनः सेट करें", - "done": "हो गया" + "home": "होम", + "end": "एंड", + "pageUp": "PgUp", + "pageDown": "PgDn", + "delete": "Del", + "paste": "पेस्ट करें", + "editQuickKeys": "त्वरित कुंजियाँ संपादित करें", + "quickKeysTitle": "त्वरित कुंजियाँ", + "quickKeysDesc": "हटाने के लिए × पर टैप करें। अधिकतम 8 वर्ण समर्थित।", + "quickKeyPlaceholder": "उदा. sudo ", + "addQuickKey": "जोड़ें", + "removeQuickKey": "हटाएँ", + "resetDefaults": "डिफ़ॉल्ट पर रीसेट करें", + "done": "पूर्ण" }, "serial": { - "title": "धारावाहिक", - "portLabel": "पत्तन", - "portPlaceholder": "/dev/ttyUSB0 या COM3", + "title": "सीरियल", + "portLabel": "पोर्ट", + "portPlaceholder": "/dev/ttyUSB0 or COM3", "baudRateLabel": "बॉड दर", "dataBitsLabel": "डेटा", - "stopBitsLabel": "रुकना", - "parityLabel": "समता", + "stopBitsLabel": "स्टॉप", + "parityLabel": "पैरिटी", "parityNone": "कोई नहीं", - "parityEven": "यहां तक की", + "parityEven": "सम", "parityOdd": "विषम", - "connect": "सीरियल से कनेक्ट करें", - "disconnect": "डिस्कनेक्ट", + "connect": "सीरियल कनेक्ट करें", + "disconnect": "डिस्कनेक्ट करें", "refreshPorts": "पोर्ट रीफ़्रेश करें", - "connected": "{{path}} से {{baud}} बॉड पर कनेक्ट किया गया", + "connected": "{{path}} से {{baud}} बॉड पर कनेक्टेड", "disconnected": "सीरियल पोर्ट डिस्कनेक्ट हो गया", "connectionError": "सीरियल पोर्ट खोलने में विफल", - "wsError": "वेबसॉकेट त्रुटि", - "errorNoServerUrl": "कोई सर्वर यूआरएल कॉन्फ़िगर नहीं किया गया है", - "notSupportedTitle": "सीरियल समर्थित नहीं है", - "notSupported": "सीरियल कनेक्शन के लिए वेब सीरियल एपीआई सपोर्ट वाला ब्राउज़र (क्रोम, एज या फ़ायरफ़ॉक्स 151+) या टर्मिक्स डेस्कटॉप ऐप आवश्यक है।", - "hideHint": "आप यूजर प्रोफाइल > अपीयरेंस > साइडबार > नेविगेशन के अंतर्गत सीरियल टैब को छिपा सकते हैं।", - "browserPickerHint": "कनेक्ट पर क्लिक करें और आपका ब्राउज़र डिवाइस चुनने के लिए एक पोर्ट पिकर खोलेगा।" + "wsError": "WebSocket त्रुटि", + "errorNoServerUrl": "कोई सर्वर URL कॉन्फ़िगर नहीं है", + "notSupportedTitle": "सीरियल समर्थित नहीं", + "notSupported": "सीरियल कनेक्शन के लिए Web Serial API समर्थन वाले ब्राउज़र (Chrome, Edge, या Firefox 151+) या Termix डेस्कटॉप ऐप की आवश्यकता है।", + "hideHint": "आप सीरियल टैब को User Profile > Appearance > Sidebar > Navigation में छिपा सकते हैं।", + "browserPickerHint": "Connect पर क्लिक करें और आपका ब्राउज़र डिवाइस चुनने के लिए एक पोर्ट पिकर खोलेगा।" }, "metricsHistory": { - "title": "मैट्रिक्स इतिहास", + "title": "मेट्रिक्स इतिहास", "historySuffix": "इतिहास", - "cpuMemoryDisk": "सीपीयू / मेमोरी / डिस्क", + "cpuMemoryDisk": "CPU / मेमोरी / डिस्क", "network": "नेटवर्क", - "download": "आरएक्स", - "upload": "टेक्सास", - "noData": "इस समयावधि के लिए कोई ऐतिहासिक डेटा उपलब्ध नहीं है।", - "custom": "रिवाज़", - "to": "को", - "apply": "आवेदन करना", + "download": "RX", + "upload": "TX", + "noData": "इस समय सीमा के लिए कोई इतिहास डेटा उपलब्ध नहीं है।", + "custom": "कस्टम", + "to": "तक", + "apply": "लागू करें", "viewHistory": "इतिहास देखें" }, "alerts": { "tabFirings": "अलर्ट", "tabRules": "नियम", "tabChannels": "चैनल", - "noFirings": "कोई अस्वीकृत अलर्ट नहीं", - "noRules": "कोई अलर्ट नियम कॉन्फ़िगर नहीं किए गए हैं", - "noChannels": "कोई अधिसूचना चैनल कॉन्फ़िगर नहीं किया गया है", - "rulesDesc": "अलर्ट नियम सूचनाओं को ट्रिगर करते हैं", - "channelsDesc": "जहां अलर्ट नोटिफिकेशन भेजे जाते हैं", - "acknowledge": "स्वीकार करना", - "ackAll": "सभी को स्वीकार करें", - "allAcknowledged": "सभी चेतावनियों की पुष्टि हो गई है", - "ackFailed": "अलर्ट को स्वीकार करने में विफल", - "ackAllFailed": "सभी अलर्ट स्वीकार करने में विफल", - "showAcknowledged": "सब दिखाएं", - "hideAcknowledged": "छिपा हुआ", + "noFirings": "कोई अपुष्ट अलर्ट नहीं", + "noRules": "कोई अलर्ट नियम कॉन्फ़िगर नहीं", + "noChannels": "कोई सूचना चैनल कॉन्फ़िगर नहीं", + "rulesDesc": "अलर्ट नियम सूचनाएं ट्रिगर करते हैं", + "channelsDesc": "जहां अलर्ट सूचनाएं भेजी जाती हैं", + "acknowledge": "पुष्टि करें", + "ackAll": "सभी की पुष्टि करें", + "allAcknowledged": "सभी अलर्ट की पुष्टि हो गई", + "ackFailed": "अलर्ट की पुष्टि करने में विफल", + "ackAllFailed": "सभी अलर्ट की पुष्टि करने में विफल", + "showAcknowledged": "सभी दिखाएं", + "hideAcknowledged": "पुष्टि किए गए छिपाएं", "addChannel": "चैनल जोड़ें", "editChannel": "चैनल संपादित करें", "channelName": "नाम", "channelType": "प्रकार", "channelNameRequired": "नाम आवश्यक है", - "webhookUrl": "यूआरएल", - "webhookUrlRequired": "वेबहुक यूआरएल आवश्यक है", - "webhookDesc": "प्रत्येक अलर्ट ट्रिगर होने पर इस URL पर JSON पेलोड पोस्ट करें", - "ntfyServer": "सर्वर यूआरएल", + "webhookUrl": "URL", + "webhookUrlRequired": "Webhook URL आवश्यक है", + "webhookDesc": "प्रत्येक अलर्ट सक्रियण पर इस URL पर JSON पेलोड POST करें", + "ntfyServer": "सर्वर URL", "ntfyTopic": "विषय", "ntfyTopicRequired": "विषय आवश्यक है", "ntfyToken": "एक्सेस टोकन (वैकल्पिक)", - "channelSaveFailed": "चैनल को सहेजने में विफल", - "test": "परीक्षा", - "testSent": "परीक्षण अधिसूचना भेजी गई", - "testFailed": "टेस्ट नोटिफिकेशन विफल रहा", + "channelSaveFailed": "चैनल सहेजने में विफल", + "test": "परीक्षण", + "testSent": "परीक्षण सूचना भेजी गई", + "testFailed": "परीक्षण सूचना विफल", "addRule": "अलर्ट नियम जोड़ें", "editRule": "अलर्ट नियम संपादित करें", - "ruleName": "नियम का नाम", + "ruleName": "नियम नाम", "ruleNameRequired": "नाम आवश्यक है", - "triggerType": "चालू कर देना", + "triggerType": "ट्रिगर", "thresholdValue": "सीमा (%)", - "durationSeconds": "अवधि (सेकंड, 0 = तुरंत फायर करें)", - "cooldownMinutes": "कूलडाउन (मिनटों में)", + "durationSeconds": "अवधि (सेकंड, 0 = तुरंत सक्रिय करें)", + "cooldownMinutes": "कूलडाउन (मिनट)", "channels": "अधिसूचना चैनल", - "noChannelsHint": "सबसे पहले चैनल टैब में चैनल जोड़ें।", + "noChannelsHint": "पहले चैनल टैब में चैनल जोड़ें", "ruleSaveFailed": "नियम सहेजने में विफल" } } diff --git a/src/ui/locales/translated/hu_HU.json b/src/ui/locales/translated/hu_HU.json index 29e358f1..a291427f 100644 --- a/src/ui/locales/translated/hu_HU.json +++ b/src/ui/locales/translated/hu_HU.json @@ -389,6 +389,45 @@ "noSavedServers": "Nincsenek mentett szerverek", "removeServer": "Eltávolítás" }, + "migrationNotice": { + "title": "A Termix Desktop mostantól önállóan is fut", + "body1": "Ez az alkalmazás korábban közvetlenül a Termix szerveredhez csatlakozott. Átdolgoztuk, hogy teljesen önállóan fusson, helyben tárolja a hosztokat és a hitelesítő adatokat, így offline is működik. A kétirányú szinkronizálás egy saját üzemeltetésű Termix szerverrel mostantól opcionális.", + "body2": "A meglévő hosztok és hitelesítő adatok továbbra is a {{url}}címen találhatók. Kapcsold be a távoli szinkronizálást, és csatlakozz újra ehhez a szerverhez, hogy visszahozd őket, és a jövőben is szinkronban maradjanak.", + "dismiss": "Most nem", + "setUpSync": "Távoli szinkronizálás beállítása" + }, + "remoteSync": { + "title": "Távoli szinkronizálás", + "description": "Opcionálisan csatlakoztathatod ezt az asztali alkalmazást egy saját üzemeltetésű Termix szerverhez, hogy szinkronizálhasd a hosztjaidat, hitelesítő adataidat és kódrészleteidet az eszközök között. Az alkalmazás mindig teljesen offline működik, függetlenül attól, hogy csatlakozol-e vagy sem.", + "notConnected": "Nincs csatlakoztatva", + "connected": "Csatlakoztatva", + "connectedTo": "Csatlakoztatva ehhez: {{url}}", + "lastSynced": "Utolsó szinkronizálás {{time}}", + "neverSynced": "Soha nem szinkronizálva", + "syncError": "Szinkronizálási hiba: {{message}}", + "needsReauth": "Bejelentkezés lejárt", + "connectButton": "Kapcsolódás a szerverhez", + "disconnectButton": "Leválasztás", + "syncNowButton": "Szinkronizálás most", + "syncing": "Szinkronizálás...", + "serverUrl": "Szerver URL-címe", + "enterServerUrl": "Kérjük, adjon meg egy szerver URL-címét", + "mustIncludeProtocol": "A szerver URL-címének http:// vagy https:// előtaggal kell kezdődnie.", + "connectionTestFailed": "Nem sikerült elérni a Termix szervert ezen az URL-címen.", + "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.", + "savedServers": "Mentett szerverek", + "removeServer": "Eltávolítás", + "continueButton": "Folytatás", + "cancelButton": "Mégsem", + "signInTitle": "Jelentkezzen be ide: {{url}}", + "originTitle": "Kapcsolat eredete", + "originDescription": "Alapértelmezés szerint válassza ki az SSH-kapcsolatok forrását. Ez gazdagépenként felülbírálható.", + "originLocal": "Ez az eszköz (helyi hálózat)", + "originRemote": "Távoli szerver", + "bannerReconnect": "Újracsatlakozás", + "bannerMessage": "A távoli szinkronizáláshoz újra kell hitelesíteni" + }, "versionCheck": { "error": "Verzióellenőrzési hiba", "checkFailed": "Nem sikerült ellenőrizni a frissítéseket", @@ -602,6 +641,7 @@ "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.", "oidcUsernameHint": "A $oidc.preferred_username paraméterrel helyettesítheti az OIDC bejelentkezési nevét.", + "tailscaleUsernameHint": "Ennek Unix felhasználónak kell lennie, akinek a Tailscale identitását a tailnet SSH ACL-jében adták meg, nem feltétlenül root felhasználónak.", "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", "addKnock": "Port hozzáadása", @@ -650,6 +690,11 @@ "delayAfterMs": "Késleltetés után (ms)", "useSocks5Proxy": "Használjon SOCKS5 proxyt", "useSocks5ProxyDesc": "Kapcsolat átirányítása proxy szerveren keresztül", + "connectionOrigin": "Kapcsolat eredete", + "connectionOriginDesc": "Honnan származik ennek a gazdagépnek az SSH-kapcsolata. Felülírja az asztali alkalmazás globális alapértelmezett beállítását.", + "connectionOriginDefault": "Alapértelmezett használata", + "connectionOriginLocal": "Ez az eszköz (helyi hálózat)", + "connectionOriginRemote": "Távoli szerver", "proxyHost": "Proxy állomás", "proxyPort": "Proxy port", "proxyUsername": "Proxy felhasználónév", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Élénk ciánkék", "customThemeBrightWhite": "Élénk fehér", "customThemeResetTooltip": "Alapértelmezett beállítások visszaállítása", + "savedThemesLabel": "Mentett témák", + "saveAsGlobalTheme": "Mentés globális témaként", + "saveGlobalThemeNamePrompt": "Adjon meg egy nevet ennek a témának", + "saveGlobalThemeSuccess": "Téma mentve", + "saveGlobalThemeError": "Nem sikerült menteni a témát", + "applyGlobalThemeTooltip": "Alkalmazd ezt a témát", + "deleteGlobalThemeTooltip": "Téma törlése", + "noSavedThemes": "Még nincsenek mentett témák", "syntaxHighlightingLabel": "Szintaxis kiemelése", "syntaxHighlightingDesc": "Terminál kimenetének színezése (hibák, elérési utak, IP-címek, időbélyegek)", "syntaxHighlightingCategories": "Kiemelt kategóriák", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Automatikus indítás vagy csatlakozás a tmux munkamenethez", "enableSessionLogging": "Munkamenet-naplózás", "enableSessionLoggingDesc": "Terminál munkamenet kimenetének rögzítése későbbi áttekintés céljából", + "allowSessionSharing": "Munkamenet-megosztás engedélyezése", + "allowSessionSharingDesc": "Engedélyezi az élő munkamenetek megosztását ezen a gazdagépen linken vagy más felhasználókkal", "enableCommandHistory": "Parancsnoki előzmények", "enableCommandHistoryDesc": "A terminálon futó parancsok rögzítése előzmények és automatikus kiegészítés céljából", "linkClickBehaviorLabel": "Linkkattintási viselkedés", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importálva", "proxmoxResultUpdated": "{{count}} frissítve", "proxmoxResultFailed": "{{count}} sikertelen", + "proxmoxResultSkippedNoIp": "{{count}} kihagyva (nem található IP cím)", "proxmoxImportComplete": "Proxmox importálás kész: {{summary}}", "proxmoxDiscoveryFailed": "A felfedezés sikertelen", "proxmoxImportFailed": "Importálás sikertelen", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Nem sikerült menteni a hitelesítő adatokat", "credentialNameRequired": "Kérjük, adjon meg egy nevet a hitelesítő adatnak", "credentialAuthRequired": "Jelszó, SSH-kulcs vagy mindkettő hozzáadása", + "createCredentialFromHostBtn": "Hitelesítő adat létrehozása", + "createCredentialFromHostTitle": "Hitelesítő adat létrehozása a gazdagépről", + "createCredentialFromHostDesc": "Hozzon létre egy újrafelhasználható, megosztható hitelesítőadat-bejegyzést, amely előre ki van töltve a gazdagép jelenlegi felhasználónevével, jelszavával és/vagy SSH-kulcsával.", "backToHosts": "Vissza a házigazdákhoz", "backToCredentials": "Vissza a hitelesítő adatokhoz", "pinned": "Rögzítve", @@ -1000,6 +1059,9 @@ "folderNestingHint": "A / jellel választhatja el a szinteket, és hozhat létre beágyazott mappákat.", "folderColor": "Szín", "folderIcon": "Ikon", + "folderCredential": "Hitelesítő adat", + "folderCredentialNone": "Nincs hozzárendelve hitelesítő adat", + "folderCredentialHint": "Az ebben a mappában található, kiválasztott hitelesítő adatok nélküli „Tárolt hitelesítő adatok” hitelesítést használó hosztok öröklik ezt a hitelesítő adatot.", "folderPreview": "Előnézet", "folderNameFallback": "Névtelen mappa", "createFolderButton": "Mappa létrehozása", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Címkék", "shareHost": "Megosztási házigazda", "shareHostTitle": "Megosztás: {{name}}", + "shareFolder": "Mappa megosztása", + "shareFolderTitle": "Megosztott mappa: {{name}}", + "folderSharedSuccessfully": "Megosztott {{count}} gazdagép(ek) a mappában", + "failedToShareFolder": "Nem sikerült megosztani a mappát", "sharing": { "loadError": "Nem sikerült betölteni a megosztási adatokat. Próbáld újra.", "shareWithSection": "Megosztás", @@ -1221,6 +1287,7 @@ "shareWithCount": "Megosztás ({{count}})", "currentAccess": "Jelenlegi hozzáférés", "noAccessEntries": "Ez a hoszt még nincs megosztva", + "folderShareSummary": "Megosztott {{shared}} / {{total}} gazdagép ebben a mappában", "grantedBy": "Megadta", "expires": "Lejár", "expired": "Lejárt", @@ -1242,6 +1309,8 @@ "authMethod": "Aut. módszer", "authTypeDirect": "Közvetlen", "authTypeCredential": "Hitelesítő adat", + "authTypeNone": "Egyik sem", + "authTypeNoneDesc": "Nincsenek tárolt hitelesítő adatok. Minden csatlakozáskor meg kell adnia a felhasználónevet és a jelszót; ezek nem kerülnek mentésre.", "selectCredential": "Válasszon hitelesítő adatot...", "connectionSettings": "Kapcsolati beállítások", "displaySettings": "Kijelzőbeállítások", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-port", "username": "Felhasználónév", "password": "Jelszó", + "passwordSaved": "Jelszó mentve, gépelje be a módosításhoz", "domain": "Domain", "securityMode": "Biztonsági mód", "colorDepth": "Színmélység", @@ -1424,7 +1494,60 @@ "expiresIn": "Lejár {{duration}} múlva", "search": "Kapcsolatok keresése...", "noSearchResults": "Nincsenek a keresésnek megfelelő kapcsolatok", - "rename": "Munkamenet átnevezése" + "rename": "Munkamenet átnevezése", + "sectionSharedWithMe": "Megosztva velem", + "sharedBy": "Megosztotta: {{username}}", + "join": "Csatlakozik", + "sharedSessionLabel": "{{hostName}} (megosztva)" + }, + "sessionSharing": { + "guestView": { + "loading": "Kapcsolódás a megosztott munkamenethez...", + "linkInvalid": "Ez a megosztási link érvénytelen, lejárt vagy visszavonták.", + "rateLimited": "Túl sok próbálkozás, kérjük, próbáld újra később", + "sessionEnded": "Ez a munkamenet véget ért", + "readOnlyBadge": "Csak megtekintés" + }, + "modalTitle": "Megosztási munkamenet", + "shareButton": "Részesedés", + "notReadyToShare": "A munkamenet még nem megosztható", + "modeTab": { + "link": "Link", + "user": "Felhasználó" + }, + "linkModeDescription": "Bárki csatlakozhat a link birtokában, nincs szükség regisztrációra.", + "userModeDescription": "Megosztás egy adott felhasználóval, aki már rendelkezik hozzáféréssel ehhez a gazdagéphez. Ha még nincs hozzáférésük, először oszd meg vele a gazdagépet, vagy használj hivatkozást. A megosztás után a munkamenet megjelenik a Kapcsolatok lapon.", + "permissionLevel": { + "label": "Jogosultsági szint", + "readOnly": "Csak olvasható", + "readOnlyDescription": "Élőben nézhetem az előadást, de nem gépelhetek és nem léphetek interakcióba.", + "readWrite": "Olvasható-írható", + "readWriteDescription": "Pont úgy tud gépelni és kommunikálni a munkamenettel, mint a tulajdonos." + }, + "expiryLabel": "Link lejárata", + "createLinkButton": "Link létrehozása", + "createShareButton": "Megosztás a felhasználóval", + "searchUsersPlaceholder": "Felhasználók keresése...", + "noUsersFound": "Nem található felhasználó", + "linkCreated": "Megosztási link létrehozva", + "linkCopied": "Link a vágólapra másolva", + "copyLink": "Link másolása", + "shareCreated": "Munkamenet megosztva. Megjelenik a Kapcsolatok fülön.", + "shareFailed": "Nem sikerült létrehozni a megosztást", + "userLacksHostAccess": "A felhasználónak még nincs hozzáférése ehhez a gazdagéphez. Először oszd meg vele a gazdagépet, vagy használj linket.", + "activeShares": "Aktív megosztások", + "noActiveShares": "Nincsenek aktív megosztások ebben a munkamenetben", + "revoke": "Visszavonás", + "revokeConfirmTitle": "Visszavonja ezt a megosztást?", + "revokeConfirmDescription": "Bárki, aki ezt a megosztást használja, azonnal elveszíti a hozzáférését.", + "revoked": "Megosztás visszavonva", + "revokeFailed": "Nem sikerült visszavonni a megosztást", + "joinCount": "{{count}} csatlakozás", + "joinCount_other": "{{count}} csatlakozások", + "expiresAt": "Lejár {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "Felhasználó: {{username}}", + "loadSharesFailed": "Nem sikerült betölteni az aktív megosztásokat" }, "guacamole": { "connecting": "Kapcsolódás ehhez: {{type}} munkamenet...", @@ -1436,6 +1559,9 @@ "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.", + "credentialPromptTitle": "RDP hitelesítő adatok megadása", + "credentialPromptDescription": "Ez a gazdagép úgy van beállítva, hogy csatlakozáskor hitelesítő adatokat kérjen. Ezeket csak ehhez a munkamenethez használjuk, és nem kerülnek mentésre.", + "connect": "Csatlakozás", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Munkamenet újracsatlakoztatása", "collapse": "Eszköztár összecsukása", "expand": "Eszköztár kibontása", - "dragHandle": "Húzással áthelyezhető" + "dragHandle": "Húzással áthelyezhető", + "switchToTrackpad": "Váltás trackpad módra (húzással mozgathatja a kurzort, koppintással kattinthat)", + "switchToTouch": "Váltás érintőképernyős módra (koppintson közvetlenül oda, ahová kattintani szeretne)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Kétfaktoros hitelesítés szükséges", "totpCodeLabel": "Ellenőrző kód", "totpVerify": "Ellenőrzés", + "mfaPromptRequired": "Hitelesítés szükséges", + "mfaPushRequired": "Push hitelesítés szükséges", + "mfaMenuPlaceholder": "Írd be a válaszod", + "mfaWaitingApproval": "Várakozás a jóváhagyásra az eszközödön...", + "mfaSendRequest": "Kérés küldése", "warpgateAuthRequired": "Warpgate hitelesítés szükséges", "warpgateSecurityKey": "Biztonsági kulcs", "warpgateAuthUrl": "Hitelesítési URL", @@ -2277,7 +2410,8 @@ "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" + "failedToLoadServer": "Nem sikerült betölteni a szervert", + "remoteServerRequired": "Távoli szerver szükséges. Csatlakoztasson egy távoli szervert a Beállításokban a kapcsolattípus használatához." }, "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.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Nem sikerült menteni az SSL-beállításokat", "sslRequiresDomain": "Domain és email cím megadása kötelező", "sslInfoNote": "Tanúsítvány kiállítása után engedélyezze az SSL-t a környezeti változókban (ENABLE_SSL=true), és indítsa újra a Termixet.", + "sslManualOption": "Kézikönyv (tanúsítvány feltöltése)", + "sslManualCert": "Tanúsítvány (PEM)", + "sslManualCertPlaceholder": "-----TANÚSÍTVÁNY KEZDETE-----", + "sslManualKey": "Privát kulcs (PEM)", + "sslManualKeyPlaceholder": "-----PRIVÁT KULCS KEZDETE-----", + "sslManualDesc": "Illeszd be a meglévő tanúsítványodat és privát kulcsodat, beleértve a teljes láncot is, ha a hitelesítésszolgáltatód megköveteli.", + "sslManualUpload": "Tanúsítvány feltöltése és telepítése", + "sslManualUploadLoading": "Tanúsítvány feltöltése...", + "sslManualUploadSuccess": "A tanúsítvány feltöltése és telepítése sikeresen megtörtént", + "sslManualUploadFailed": "A tanúsítvány feltöltése sikertelen", + "sslManualRequiresFields": "Tanúsítvány és privát kulcs szükséges", "auditLogTotal": "{{total}} összes bejegyzés", "auditLogEmpty": "Nem találhatók naplóbejegyzések", "auditLogSuccess": "Siker", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Parancsnoki előzmények", "commandHistoryEnabledDesc": "Parancselőzmények rögzítésének engedélyezése. Ha le van tiltva, az előzmények nem kerülnek mentésre, függetlenül a gazdagép beállításaitól.", "updateCommandHistoryFailed": "Nem sikerült frissíteni a parancselőzmények beállítását", + "analyticsEnabled": "Névtelen használati statisztikák megosztása", + "analyticsEnabledDesc": "Naponta névtelenül küldi a felhasználók, a hosztok és a funkciók használatának számát a Termix fejlesztése érdekében. Személyes adatokat vagy kapcsolati adatokat nem tartalmaz.", + "updateAnalyticsFailed": "Nem sikerült frissíteni az analitikai beállításokat", + "sessionSharingGloballyEnabled": "Munkamenet-megosztás engedélyezése", + "sessionSharingGloballyEnabledDesc": "Engedélyezi az élő terminál-, RDP-, VNC- és Telnet-munkamenetek példányonkénti megosztását. Letiltás esetén felülírja az összes gazdagépenkénti megosztási kapcsolót.", + "updateSessionSharingFailed": "Nem sikerült frissíteni a munkamenet-megosztási beállítást", "sessionTimeout": "Munkamenet időkorlátja", "hours": "órák", "sessionTimeoutRange": "Minimum 1 óra · Max. 720 óra", @@ -3119,6 +3270,47 @@ "executionSuccess": "Siker", "executionFailed": "Sikertelen" }, + "keybindings": { + "title": "Billentyűparancsok", + "description": "Testreszabhatja a terminál másolási, beillesztési és vezérlési billentyűparancsait, vagy billentyűket köthet szöveg küldéséhez vagy kódrészlet futtatásához.", + "defaultsHeading": "Beépített gyorsbillentyűk", + "customHeading": "Egyéni billentyűparancsok", + "addBinding": "Parancsikon hozzáadása", + "addBindingTitle": "Parancsikon hozzáadása", + "editBindingTitle": "Parancsikon szerkesztése", + "loading": "Terhelés...", + "noCustomBindings": "Még nincsenek egyéni billentyűparancsok", + "defaultBadge": "Alapértelmezett", + "customizedBadge": "Testreszabott", + "customize": "Testreszabás", + "resetToDefault": "Visszaállítás az alapértelmezettre", + "resetAllToDefaults": "Minden visszaállítása alapértelmezettre", + "close": "Közeli", + "cancel": "Mégsem", + "saveBinding": "Parancsikon mentése", + "saveError": "Nem sikerült menteni a billentyűparancsokat", + "pressKeysToRecord": "Billentyűkombináció", + "pressKeysPlaceholder": "Kattintson és nyomja meg a billentyűket", + "recording": "Nyomja meg a billentyűket...", + "comboRequiredError": "Először nyomjon meg egy billentyűkombinációt", + "textRequiredError": "Írd be a küldendő szöveget", + "controlCodeRequiredError": "Írjon be egyetlen betűt az ellenőrző kódhoz", + "snippetRequiredError": "Válasszon ki egy részletet", + "conflictWarning": "Ez a kombináció már a {{combo}}-hoz van kötve. Mentés esetén mindkettő aktiválódik.", + "actionLabel": "Akció", + "actionCopy": "Kijelölés másolása", + "actionPaste": "Paszta", + "actionSendControlCode": "Ctrl+betűjel küldése", + "actionSendText": "SMS küldése", + "actionRunSnippet": "Meglévő kódrészlet futtatása", + "controlCodeLabel": "Levél", + "textLabel": "Küldendő szöveg", + "appendEnterLabel": "Küldés után nyomja meg az Enter billentyűt", + "snippetLabel": "Töredék", + "selectSnippetPlaceholder": "Válasszon ki egy részletet", + "orphanedSnippetWarning": "részlet nem található", + "clipboardPermissionNote": "Egyes böngészőkben első használatkor vágólap-engedélyt kérhet." + }, "userProfile": { "donateTitle": "Tartsd életben a Termixet", "donateDescription": "A Termix ingyenes, saját tárhelyen üzemel, és csak néhány ember fejlesztette a szabadidejében. Ha időt vagy pénzt takarított meg vele, egy kriptovaluta-adomány segíthet a működésben.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Megjelenés", "sectionSecurity": "Biztonság", "sectionApiKeys": "API-kulcsok", + "sectionData": "Adat", "sectionC2sTunnels": "C2S alagutak", "usernameLabel": "Felhasználónév", "roleLabel": "Szerep", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminál", "commandAutocomplete": "Parancs automatikus kiegészítése", "commandAutocompleteDesc": "Automatikus kiegészítés megjelenítése gépelés közben", + "keyboardShortcuts": "Billentyűparancsok", + "keyboardShortcutsDescription": "A terminál másolási, beillesztési és parancsikonjainak testreszabása", + "manageShortcuts": "Kezelés", "terminalLinkBehavior": "Terminálkapcsolat kattintása", "terminalLinkBehaviorDesc": "Alapértelmezett viselkedés hivatkozásokra kattintva a terminálban", "historyTracking": "Előzménykövetés", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Tedd bele a kulcsodat", "apiKeyUsageHintHeader": "fejléc.", "apiKeyPermissionsHint": "A kulcsok öröklik a létrehozó felhasználó jogosultságait.", + "exportData": "Adatok exportálása", + "exportDataDesc": "Töltsd le a gazdagépeid, hitelesítő adataid és beállításaid biztonsági másolatát egy másik eszközre való átvitelhez.", + "export": "Export", + "exporting": "Exportálás...", + "importData": "Adatok importálása", + "importDataDesc": "Állítsa vissza a gazdagépeket, hitelesítő adatokat és beállításokat egy .sqlite biztonsági mentésfájlból", + "importDataSelected": "Kiválasztva: {{name}}", + "selectFile": "Fájl kiválasztása", + "changeFile": "Változás", + "import": "Importálás", + "importing": "Importálás...", + "exportSuccess": "Az adatok exportálása sikeresen megtörtént", + "exportFailed": "Adatexportálás 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}}", "roleUser": "Felhasználó", "authMethodDual": "Kettős hitelesítés", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Paszta", "editQuickKeys": "Gyorsbillentyűk szerkesztése", "quickKeysTitle": "Gyorsbillentyűk", "quickKeysDesc": "Koppintson a × jelre az eltávolításhoz. Legfeljebb 8 karaktert támogat.", diff --git a/src/ui/locales/translated/id_ID.json b/src/ui/locales/translated/id_ID.json index f48afa19..2857a377 100644 --- a/src/ui/locales/translated/id_ID.json +++ b/src/ui/locales/translated/id_ID.json @@ -389,6 +389,45 @@ "noSavedServers": "Tidak ada server yang tersimpan.", "removeServer": "Menghapus" }, + "migrationNotice": { + "title": "Termix Desktop sekarang berjalan secara mandiri.", + "body1": "Aplikasi ini sebelumnya terhubung langsung ke server Termix Anda. Aplikasi ini telah dirancang ulang agar dapat berjalan sepenuhnya secara mandiri, menyimpan host dan kredensial secara lokal sehingga tetap berfungsi saat offline. Sinkronisasi dua arah dengan server Termix yang dihosting sendiri sekarang bersifat opsional.", + "body2": "Host dan kredensial Anda yang ada masih berada di {{url}}. Aktifkan Sinkronisasi Jarak Jauh dan sambungkan kembali ke server tersebut untuk mengembalikannya dan menjaga agar keduanya tetap sinkron ke depannya.", + "dismiss": "Tidak sekarang", + "setUpSync": "Siapkan Sinkronisasi Jarak Jauh" + }, + "remoteSync": { + "title": "Sinkronisasi Jarak Jauh", + "description": "Secara opsional, hubungkan aplikasi desktop ini ke server Termix yang dihosting sendiri untuk menyinkronkan host, kredensial, dan cuplikan Anda di berbagai perangkat. Aplikasi ini selalu berfungsi sepenuhnya secara offline, baik Anda terhubung atau tidak.", + "notConnected": "Tidak terhubung", + "connected": "Terhubung", + "connectedTo": "Terhubung ke {{url}}", + "lastSynced": "Sinkronisasi terakhir {{time}}", + "neverSynced": "Tidak pernah disinkronkan", + "syncError": "Kesalahan sinkronisasi: {{message}}", + "needsReauth": "Login telah kedaluwarsa.", + "connectButton": "Terhubung ke Server", + "disconnectButton": "Memutuskan", + "syncNowButton": "Sinkronkan Sekarang", + "syncing": "Sinkronisasi...", + "serverUrl": "URL Server", + "enterServerUrl": "Silakan masukkan URL server", + "mustIncludeProtocol": "URL server harus diawali dengan http:// atau https://", + "connectionTestFailed": "Tidak dapat terhubung ke server Termix di URL tersebut.", + "allowInvalidCertificate": "Izinkan sertifikat tidak valid", + "allowInvalidCertificateDesc": "Gunakan hanya untuk server mandiri tepercaya dengan sertifikat yang ditandatangani sendiri atau sertifikat alamat IP.", + "savedServers": "Server Tersimpan", + "removeServer": "Menghapus", + "continueButton": "Melanjutkan", + "cancelButton": "Membatalkan", + "signInTitle": "Masuk ke {{url}}", + "originTitle": "Asal Koneksi", + "originDescription": "Pilih dari mana koneksi SSH berasal secara default. Pengaturan ini dapat diubah per host.", + "originLocal": "Perangkat ini (jaringan lokal)", + "originRemote": "Server jarak jauh", + "bannerReconnect": "Terhubung kembali", + "bannerMessage": "Sinkronisasi jarak jauh memerlukan autentikasi ulang." + }, "versionCheck": { "error": "Kesalahan Pemeriksaan Versi", "checkFailed": "Gagal memeriksa pembaruan.", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Ganti Nama Pengguna Kredensial", "overrideCredentialUsernameDesc": "Gunakan nama pengguna yang telah ditentukan di atas, bukan nama pengguna kredensial.", "oidcUsernameHint": "Gunakan $oidc.preferred_username untuk mengganti nama login OIDC Anda.", + "tailscaleUsernameHint": "Ini harus berupa pengguna Unix yang identitas Tailscale Anda diberikan dalam ACL SSH tailnet, tidak harus root.", "jumpHostChain": "Rantai Host Lompatan", "portKnocking": "Ketukan Pelabuhan", "addKnock": "Tambahkan Port", @@ -650,6 +690,11 @@ "delayAfterMs": "Penundaan Setelah (ms)", "useSocks5Proxy": "Gunakan Proxy SOCKS5", "useSocks5ProxyDesc": "Rute koneksi melalui server proxy.", + "connectionOrigin": "Asal Koneksi", + "connectionOriginDesc": "Lokasi asal koneksi SSH host ini. Menggantikan pengaturan default global aplikasi desktop.", + "connectionOriginDefault": "Gunakan pengaturan default", + "connectionOriginLocal": "Perangkat ini (jaringan lokal)", + "connectionOriginRemote": "Server jarak jauh", "proxyHost": "Host Proksi", "proxyPort": "Port Proksi", "proxyUsername": "Nama Pengguna Proksi", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Biru Muda Terang", "customThemeBrightWhite": "Putih Terang", "customThemeResetTooltip": "Kembalikan ke pengaturan default", + "savedThemesLabel": "Tema Tersimpan", + "saveAsGlobalTheme": "Simpan sebagai Tema Global", + "saveGlobalThemeNamePrompt": "Masukkan nama untuk tema ini", + "saveGlobalThemeSuccess": "Tema tersimpan", + "saveGlobalThemeError": "Gagal menyimpan tema", + "applyGlobalThemeTooltip": "Terapkan tema ini", + "deleteGlobalThemeTooltip": "Hapus tema ini", + "noSavedThemes": "Belum ada tema yang tersimpan.", "syntaxHighlightingLabel": "Penyorotan Sintaksis", "syntaxHighlightingDesc": "Berikan warna pada output terminal (kesalahan, jalur, IP, stempel waktu)", "syntaxHighlightingCategories": "Kategori Unggulan", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Secara otomatis meluncurkan atau terhubung ke sesi tmux.", "enableSessionLogging": "Pencatatan Sesi", "enableSessionLoggingDesc": "Rekam output sesi terminal untuk ditinjau nanti.", + "allowSessionSharing": "Izinkan Berbagi Sesi", + "allowSessionSharingDesc": "Izinkan sesi langsung di host ini dibagikan melalui tautan atau dengan pengguna lain.", "enableCommandHistory": "Riwayat Perintah", "enableCommandHistoryDesc": "Rekam perintah yang dijalankan di terminal ini untuk riwayat dan pelengkapan otomatis.", "linkClickBehaviorLabel": "Perilaku Klik Tautan", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} diimpor", "proxmoxResultUpdated": "{{count}} diperbarui", "proxmoxResultFailed": "{{count}} gagal", + "proxmoxResultSkippedNoIp": "{{count}} dilewati (tidak ditemukan IP)", "proxmoxImportComplete": "Impor Proxmox selesai: {{summary}}", "proxmoxDiscoveryFailed": "Penemuan gagal", "proxmoxImportFailed": "Impor gagal", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Gagal menyimpan kredensial", "credentialNameRequired": "Silakan masukkan nama untuk kredensial tersebut.", "credentialAuthRequired": "Tambahkan kata sandi, kunci SSH, atau keduanya.", + "createCredentialFromHostBtn": "Buat Kredensial", + "createCredentialFromHostTitle": "Buat Kredensial dari Host", + "createCredentialFromHostDesc": "Buat entri kredensial yang dapat digunakan kembali dan dibagikan, yang telah diisi sebelumnya dengan nama pengguna, kata sandi, dan/atau kunci SSH host ini saat ini.", "backToHosts": "Kembali ke Host", "backToCredentials": "Kembali ke Kredensial", "pinned": "Disematkan", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Gunakan / untuk memisahkan level dan membuat folder bertingkat.", "folderColor": "Warna", "folderIcon": "Ikon", + "folderCredential": "Mandat", + "folderCredentialNone": "Tidak ada kredensial yang diberikan.", + "folderCredentialHint": "Host dalam folder ini yang menggunakan otentikasi \"Kredensial tersimpan\" tanpa kredensial mereka sendiri yang dipilih akan mewarisi kredensial ini.", "folderPreview": "Pratinjau", "folderNameFallback": "Folder tanpa judul", "createFolderButton": "Buat folder", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tag", "shareHost": "Bagikan Host", "shareHostTitle": "Bagikan: {{name}}", + "shareFolder": "Folder Bersama", + "shareFolderTitle": "Folder berbagi: {{name}}", + "folderSharedSuccessfully": "Host bersama {{count}} di dalam folder", + "failedToShareFolder": "Gagal berbagi folder", "sharing": { "loadError": "Gagal memuat data berbagi. Silakan coba lagi.", "shareWithSection": "Bagikan dengan", @@ -1221,6 +1287,7 @@ "shareWithCount": "Bagikan ({{count}})", "currentAccess": "Akses saat ini", "noAccessEntries": "Host ini belum dibagikan", + "folderShareSummary": "Berbagi {{shared}} dari {{total}} host di folder ini", "grantedBy": "Diberikan oleh", "expires": "Berakhir", "expired": "Kedaluwarsa", @@ -1242,6 +1309,8 @@ "authMethod": "Metode Otentikasi", "authTypeDirect": "Langsung", "authTypeCredential": "Mandat", + "authTypeNone": "Tidak ada", + "authTypeNoneDesc": "Tidak ada kredensial yang disimpan. Anda akan diminta memasukkan nama pengguna dan kata sandi setiap kali Anda terhubung; informasi tersebut tidak disimpan.", "selectCredential": "Pilih kredensial...", "connectionSettings": "Pengaturan Koneksi", "displaySettings": "Pengaturan Tampilan", @@ -1259,6 +1328,7 @@ "rdpPort": "Port RDP", "username": "Nama belakang", "password": "Kata sandi", + "passwordSaved": "Kata sandi tersimpan, ketik untuk mengubahnya", "domain": "Domain", "securityMode": "Mode Keamanan", "colorDepth": "Kedalaman Warna", @@ -1424,7 +1494,60 @@ "expiresIn": "Berakhir dalam {{duration}}", "search": "Cari koneksi...", "noSearchResults": "Tidak ada koneksi yang sesuai dengan pencarian Anda.", - "rename": "Ganti nama sesi" + "rename": "Ganti nama sesi", + "sectionSharedWithMe": "Dibagikan kepada saya", + "sharedBy": "Dibagikan oleh {{username}}", + "join": "Bergabung", + "sharedSessionLabel": "{{hostName}} (dibagikan)" + }, + "sessionSharing": { + "guestView": { + "loading": "Menghubungkan ke sesi bersama...", + "linkInvalid": "Tautan berbagi ini tidak valid, telah kedaluwarsa, atau telah dicabut.", + "rateLimited": "Terlalu banyak percobaan, silakan coba lagi sebentar lagi.", + "sessionEnded": "Sesi ini telah berakhir.", + "readOnlyBadge": "Hanya tampilan" + }, + "modalTitle": "Sesi berbagi", + "shareButton": "Membagikan", + "notReadyToShare": "Sesi belum siap untuk dibagikan.", + "modeTab": { + "link": "Link", + "user": "Pengguna" + }, + "linkModeDescription": "Siapa pun yang memiliki tautan ini dapat bergabung, tidak perlu membuat akun.", + "userModeDescription": "Bagikan dengan pengguna tertentu yang sudah memiliki akses ke host ini. Jika mereka belum memiliki akses, bagikan host tersebut kepada mereka terlebih dahulu atau gunakan tautan. Setelah dibagikan, sesi akan muncul di tab Koneksi mereka.", + "permissionLevel": { + "label": "Tingkat izin", + "readOnly": "Hanya baca", + "readOnlyDescription": "Bisa menonton sesi secara langsung tetapi tidak bisa mengetik atau berinteraksi.", + "readWrite": "Baca-tulis", + "readWriteDescription": "Dapat mengetik dan berinteraksi dengan sesi seperti halnya pemilik." + }, + "expiryLabel": "Kedaluwarsa tautan", + "createLinkButton": "Buat tautan", + "createShareButton": "Bagikan dengan pengguna", + "searchUsersPlaceholder": "Cari pengguna...", + "noUsersFound": "Tidak ada pengguna yang ditemukan", + "linkCreated": "Tautan berbagi telah dibuat", + "linkCopied": "Tautan disalin ke papan klip", + "copyLink": "Salin tautan", + "shareCreated": "Sesi dibagikan. Sesi tersebut akan muncul di tab Koneksi mereka.", + "shareFailed": "Gagal membuat saham", + "userLacksHostAccess": "Pengguna tersebut belum memiliki akses ke host ini. Bagikan host tersebut kepada mereka terlebih dahulu, atau gunakan tautan sebagai gantinya.", + "activeShares": "Saham aktif", + "noActiveShares": "Tidak ada saham aktif untuk sesi ini.", + "revoke": "Menarik kembali", + "revokeConfirmTitle": "Batalkan pembagian ini?", + "revokeConfirmDescription": "Siapa pun yang menggunakan share ini akan langsung kehilangan akses.", + "revoked": "Saham dicabut", + "revokeFailed": "Gagal mencabut saham", + "joinCount": "{{count}} bergabung", + "joinCount_other": "{{count}} bergabung", + "expiresAt": "Berakhir {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "Pengguna: {{username}}", + "loadSharesFailed": "Gagal memuat saham aktif." }, "guacamole": { "connecting": "Menghubungkan ke sesi {{type}}...", @@ -1436,6 +1559,9 @@ "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.", + "credentialPromptTitle": "Masukkan Kredensial RDP", + "credentialPromptDescription": "Host ini diatur untuk meminta kredensial saat terhubung. Kredensial tersebut hanya digunakan untuk sesi ini dan tidak disimpan.", + "connect": "Menghubungkan", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Sesi Terhubung Kembali", "collapse": "Ciutkan bilah alat", "expand": "Perluas bilah alat", - "dragHandle": "Seret untuk mengubah posisi" + "dragHandle": "Seret untuk mengubah posisi", + "switchToTrackpad": "Beralih ke mode trackpad (seret untuk memindahkan kursor, ketuk untuk mengklik)", + "switchToTouch": "Beralih ke mode sentuh (ketuk langsung di tempat yang ingin Anda klik)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Diperlukan Otentikasi Dua Faktor", "totpCodeLabel": "Kode Verifikasi", "totpVerify": "Memeriksa", + "mfaPromptRequired": "Autentikasi Diperlukan", + "mfaPushRequired": "Otentikasi Push Diperlukan", + "mfaMenuPlaceholder": "Masukkan jawaban Anda", + "mfaWaitingApproval": "Menunggu persetujuan di perangkat Anda...", + "mfaSendRequest": "Kirim Permintaan", "warpgateAuthRequired": "Autentikasi Warpgate Diperlukan", "warpgateSecurityKey": "Kunci Keamanan", "warpgateAuthUrl": "URL Otentikasi", @@ -2277,7 +2410,8 @@ "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." + "failedToLoadServer": "Server gagal dimuat.", + "remoteServerRequired": "Diperlukan server jarak jauh. Hubungkan server jarak jauh di Pengaturan untuk menggunakan jenis koneksi ini." }, "messages": { "registrationDisabled": "Pendaftaran akun baru saat ini dinonaktifkan oleh administrator. Silakan masuk atau hubungi administrator.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Gagal menyimpan pengaturan SSL", "sslRequiresDomain": "Nama domain dan email wajib diisi.", "sslInfoNote": "Setelah menerbitkan sertifikat, aktifkan SSL di variabel lingkungan Anda (ENABLE_SSL=true) dan mulai ulang Termix.", + "sslManualOption": "Manual (unggah sertifikat)", + "sslManualCert": "Sertifikat (PEM)", + "sslManualCertPlaceholder": "-----MULAI SERTIFIKAT-----", + "sslManualKey": "Kunci Pribadi (PEM)", + "sslManualKeyPlaceholder": "-----MULAI KUNCI PRIVAT-----", + "sslManualDesc": "Tempelkan sertifikat dan kunci pribadi Anda yang sudah ada, termasuk rantai lengkap jika dipersyaratkan oleh CA Anda.", + "sslManualUpload": "Unggah & Instal Sertifikat", + "sslManualUploadLoading": "Mengunggah sertifikat...", + "sslManualUploadSuccess": "Sertifikat berhasil diunggah dan diinstal.", + "sslManualUploadFailed": "Unggahan sertifikat gagal.", + "sslManualRequiresFields": "Sertifikat dan kunci pribadi diperlukan.", "auditLogTotal": "{{total}} total entri", "auditLogEmpty": "Tidak ditemukan entri log audit.", "auditLogSuccess": "Kesuksesan", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Riwayat Perintah", "commandHistoryEnabledDesc": "Izinkan perekaman riwayat perintah. Jika dinonaktifkan, riwayat tidak akan disimpan terlepas dari pengaturan per host.", "updateCommandHistoryFailed": "Gagal memperbarui pengaturan riwayat perintah.", + "analyticsEnabled": "Bagikan Statistik Penggunaan Anonim", + "analyticsEnabledDesc": "Mengirimkan data harian anonim mengenai jumlah pengguna, host, dan penggunaan fitur untuk membantu meningkatkan Termix. Tidak ada data pribadi atau detail koneksi yang disertakan.", + "updateAnalyticsFailed": "Gagal memperbarui pengaturan analitik.", + "sessionSharingGloballyEnabled": "Izinkan Berbagi Sesi", + "sessionSharingGloballyEnabledDesc": "Izinkan sesi terminal langsung, RDP, VNC, dan Telnet untuk dibagikan di seluruh instance. Menggantikan setiap pengaturan berbagi per host saat dinonaktifkan.", + "updateSessionSharingFailed": "Gagal memperbarui pengaturan berbagi sesi.", "sessionTimeout": "Waktu sesi habis", "hours": "jam", "sessionTimeoutRange": "Minimal 1 jam · Maksimal 720 jam", @@ -3119,6 +3270,47 @@ "executionSuccess": "Kesuksesan", "executionFailed": "Gagal" }, + "keybindings": { + "title": "Pintasan Keyboard", + "description": "Sesuaikan pintasan salin, tempel, dan kontrol terminal, atau tetapkan tombol untuk mengirim teks atau menjalankan cuplikan kode.", + "defaultsHeading": "Pintasan bawaan", + "customHeading": "Pintasan Kustom", + "addBinding": "Tambahkan Pintasan", + "addBindingTitle": "Tambahkan Pintasan", + "editBindingTitle": "Edit Pintasan", + "loading": "Memuat...", + "noCustomBindings": "Belum ada pintasan khusus.", + "defaultBadge": "Bawaan", + "customizedBadge": "Disesuaikan", + "customize": "Sesuaikan", + "resetToDefault": "Atur ulang ke pengaturan default", + "resetAllToDefaults": "Atur ulang semuanya ke pengaturan default.", + "close": "Menutup", + "cancel": "Membatalkan", + "saveBinding": "Simpan Pintasan", + "saveError": "Gagal menyimpan pintasan keyboard", + "pressKeysToRecord": "Kombinasi tombol", + "pressKeysPlaceholder": "Klik dan tekan tombol", + "recording": "Tekan tombol...", + "comboRequiredError": "Tekan kombinasi tombol terlebih dahulu.", + "textRequiredError": "Masukkan teks yang ingin dikirim", + "controlCodeRequiredError": "Masukkan satu huruf untuk kode kontrol.", + "snippetRequiredError": "Pilih cuplikan", + "conflictWarning": "Kombinasi ini sudah terikat ke {{combo}}. Menyimpan akan membuat keduanya menyala.", + "actionLabel": "Tindakan", + "actionCopy": "Salin pilihan", + "actionPaste": "Pasta", + "actionSendControlCode": "Kirim sinyal Ctrl+huruf", + "actionSendText": "Kirim pesan teks", + "actionRunSnippet": "Jalankan cuplikan kode yang sudah ada.", + "controlCodeLabel": "Surat", + "textLabel": "Teks untuk dikirim", + "appendEnterLabel": "Tekan Enter setelah mengirim", + "snippetLabel": "Potongan", + "selectSnippetPlaceholder": "Pilih cuplikan", + "orphanedSnippetWarning": "cuplikan tidak ditemukan", + "clipboardPermissionNote": "Pada beberapa browser, mungkin akan meminta izin akses clipboard saat pertama kali digunakan." + }, "userProfile": { "donateTitle": "Jaga agar Termix tetap hidup.", "donateDescription": "Termix gratis, dihosting sendiri, dan dibangun hanya oleh beberapa orang di waktu luang mereka. Jika Termix telah menghemat waktu atau uang Anda, donasi kripto akan membantu kelangsungannya.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Penampilan", "sectionSecurity": "Keamanan", "sectionApiKeys": "Kunci API", + "sectionData": "Data", "sectionC2sTunnels": "Terowongan C2S", "usernameLabel": "Nama belakang", "roleLabel": "Peran", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Pelengkapan Otomatis Perintah", "commandAutocompleteDesc": "Tampilkan fitur pelengkapan otomatis saat mengetik.", + "keyboardShortcuts": "Pintasan Keyboard", + "keyboardShortcutsDescription": "Sesuaikan pintasan salin, tempel, dan perintah terminal.", + "manageShortcuts": "Mengelola", "terminalLinkBehavior": "Klik Tautan Terminal", "terminalLinkBehaviorDesc": "Perilaku default saat mengklik tautan di terminal.", "historyTracking": "Pelacakan Riwayat", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Sertakan kunci Anda di dalam", "apiKeyUsageHintHeader": "Judul.", "apiKeyPermissionsHint": "Kunci mewarisi izin dari pengguna yang membuatnya.", + "exportData": "Ekspor Data Saya", + "exportDataDesc": "Unduh cadangan data host, kredensial, dan pengaturan Anda untuk dipindahkan ke perangkat lain.", + "export": "Ekspor", + "exporting": "Mengekspor...", + "importData": "Impor Data Saya", + "importDataDesc": "Pulihkan host, kredensial, dan pengaturan Anda dari file cadangan .sqlite.", + "importDataSelected": "Dipilih: {{name}}", + "selectFile": "Pilih File", + "changeFile": "Mengubah", + "import": "Impor", + "importing": "Pengimporan...", + "exportSuccess": "Data berhasil diekspor.", + "exportFailed": "Ekspor data gagal", + "importSelectFile": "Silakan pilih file terlebih dahulu.", + "importCompleted": "Impor selesai: {{total}} item diimpor, {{skipped}} dilewati", + "importFailed": "Impor gagal: {{error}}", "roleUser": "Pengguna", "authMethodDual": "Otorisasi Ganda", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Pasta", "editQuickKeys": "Edit tombol pintasan", "quickKeysTitle": "Tombol Pintas", "quickKeysDesc": "Ketuk × untuk menghapus. Mendukung hingga 8 karakter.", diff --git a/src/ui/locales/translated/it_IT.json b/src/ui/locales/translated/it_IT.json index 63c8d40a..53aa0d14 100644 --- a/src/ui/locales/translated/it_IT.json +++ b/src/ui/locales/translated/it_IT.json @@ -1,72 +1,72 @@ { "termixId": { - "title": "ID Termix", - "loadFailed": "Impossibile caricare l'ID Termix", - "claimTitle": "Richiedi il tuo ID Termix", - "claimIntro": "Scegli un nome utente univoco. Le tue chiavi pubbliche SSH saranno pubblicate a un URL pubblico che potrai aggiungere al file authorized_keys di qualsiasi server.", - "handleLabel": "Maniglia", - "handlePlaceholder": "Alice", + "title": "Termix ID", + "loadFailed": "Caricamento Termix ID fallito", + "claimTitle": "Rivendica il tuo Termix ID", + "claimIntro": "Scegli un identificativo univoco. Le tue chiavi pubbliche SSH saranno pubblicate a un URL pubblico che puoi aggiungere al file authorized_keys di qualsiasi server.", + "handleLabel": "Identificativo", + "handlePlaceholder": "alice", "checking": "Controllo…", "available": "Disponibile", - "taken": "già preso", - "invalidHandle": "Lettere minuscole, numeri, - e _ soltanto", + "taken": "Già in uso", + "invalidHandle": "Solo lettere minuscole, numeri, - e _", "descriptionLabel": "Descrizione (facoltativa)", - "descriptionPlaceholder": "Chiavi del computer portatile e del telefono aziendali", - "create": "Crea ID Termix", - "created": "ID Termix creato", - "createFailed": "Impossibile creare l'ID Termix", - "deleteConfirm": "Vuoi eliminare il tuo ID Termix e tutte le chiavi pubblicate? I server che hai configurato manterranno le chiavi finché non le rimuoverai manualmente.", - "deleted": "ID Termix eliminato", - "deleteFailed": "Impossibile eliminare l'ID Termix", - "copyFailed": "Copia non riuscita", + "descriptionPlaceholder": "Chiavi per portatile da lavoro e telefono", + "create": "Crea Termix ID", + "created": "Termix ID creato", + "createFailed": "Creazione Termix ID fallita", + "deleteConfirm": "Eliminare il tuo Termix ID e tutte le chiavi pubblicate? I server configurati manterranno le chiavi finché non le rimuovi manualmente.", + "deleted": "Termix ID eliminato", + "deleteFailed": "Eliminazione Termix ID fallita", + "copyFailed": "Copia fallita", "resolverUrlLabel": "URL del resolver pubblico", - "provisionLabel": "Impostazione di un server", - "publishTitle": "Pubblicare una chiave pubblica", - "generate": "Generare", - "generateTooltip": "Genera una coppia di chiavi Ed25519: pubblica la chiave pubblica e scarica la chiave privata sul tuo dispositivo.", + "provisionLabel": "Provisiona un server", + "publishTitle": "Pubblica una chiave pubblica", + "generate": "Genera", + "generateTooltip": "Genera una coppia di chiavi Ed25519 — pubblica la chiave pubblica e scarica la chiave privata sul tuo dispositivo", "saveToVault": "Salva nelle credenziali", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Etichetta (facoltativa)", - "add": "Aggiungere", + "add": "Aggiungi", "importFromCredential": "Oppure importa da una credenziale memorizzata", - "keyPublished": "Pubblicazione chiave", - "addKeyFailed": "Impossibile aggiungere la chiave", - "generatedSaved": "Coppia di chiavi generata e salvata nel tuo archivio di credenziali. Chiave privata scaricata.", - "generatedOnly": "Coppia di chiavi generata — chiave privata scaricata (mostrata una sola volta).", - "generateFailed": "Impossibile generare la chiave", - "imported": "Chiave importata dalle credenziali", - "importFailed": "Impossibile importare la chiave", - "noKeys": "Non sono ancora state pubblicate chiavi pubbliche.", + "keyPublished": "Chiave pubblicata", + "addKeyFailed": "Aggiunta chiave fallita", + "generatedSaved": "Coppia di chiavi generata e salvata nell'archivio delle credenziali. Anche la chiave privata è stata scaricata.", + "generatedOnly": "Coppia di chiavi generata — chiave privata scaricata (visualizzata una sola volta).", + "generateFailed": "Generazione chiave fallita", + "imported": "Chiave importata dalla credenziale", + "importFailed": "Importazione chiave non riuscita", + "noKeys": "Nessuna chiave pubblica ancora pubblicata.", "keysTitle": "Chiavi pubblicate", - "published": "Pubblicato", - "hidden": "Nascosto", + "published": "Pubblicata", + "hidden": "Nascosta", "keyRemoved": "Chiave rimossa", - "removeKeyFailed": "Impossibile rimuovere la chiave", - "updateKeyFailed": "Impossibile aggiornare la chiave", + "removeKeyFailed": "Rimozione chiave non riuscita", + "updateKeyFailed": "Aggiornamento chiave non riuscito", "fromVault": "Dal vault delle credenziali", - "linkedToTermixId": "Pubblicato tramite ID Termix", + "linkedToTermixId": "Pubblicata tramite Termix ID", "selectCredential": "Seleziona una credenziale...", - "import": "Importare", + "import": "Importa", "caTitle": "Autorità di certificazione", - "caIntro": "Se consideri attendibile questa CA su un server, accetterà qualsiasi certificato tu firmi. La funzione \"Rotazione\" revoca tutti i certificati contemporaneamente; i certificati inoltre scadono automaticamente.", - "caEnable": "Capacità CA", - "caEnabled": "CA abilitato", - "caCreateFailed": "Impossibile abilitare CA", + "caIntro": "Rendi attendibile questa CA su un server e questo accetterà qualsiasi certificato da te firmato. Ruota per revocare tutto in una volta; i certificati scadono anche automaticamente.", + "caEnable": "Abilita CA", + "caEnabled": "CA abilitata", + "caCreateFailed": "Abilitazione CA non riuscita", "caPublicKeyLabel": "Chiave pubblica CA", - "caTrustLabel": "Affidati al server (esegui come root)", - "caRotate": "Ruotare", - "caRotateConfirm": "Ruotare l'autorità di certificazione (CA)? Tutti i certificati da essa firmati smetteranno di essere accettati e dovranno essere riemessi.", - "caRotated": "CA ruotata: i certificati precedenti sono stati revocati.", + "caTrustLabel": "Rendi attendibile sul server (esegui come root)", + "caRotate": "Ruota", + "caRotateConfirm": "Ruotare la CA? Tutti i certificati da essa firmati smetteranno di essere accettati e dovranno essere riemessi.", + "caRotated": "CA ruotata — certificati precedenti revocati", "caRotateFailed": "Rotazione CA non riuscita", - "caDelete": "Rimuovere CA", + "caDelete": "Rimuovi CA", "caDeleteConfirm": "Rimuovere l'autorità di certificazione?", - "caDeleted": "CA rimosso", - "caDeleteFailed": "Impossibile rimuovere CA", - "caValidityLabel": "Validità del certificato (giorni)", + "caDeleted": "CA rimossa", + "caDeleteFailed": "Rimozione CA non riuscita", + "caValidityLabel": "Validità certificato (giorni)", "issueCert": "Certificato", "issueCertTooltip": "Emetti un certificato SSH per questa chiave, firmato dalla tua CA", - "certIssued": "Certificato rilasciato e scaricato", - "certIssueFailed": "Impossibile emettere il certificato" + "certIssued": "Certificato emesso e scaricato", + "certIssueFailed": "Emissione certificato non riuscita" }, "credentials": { "folders": "Cartelle", @@ -74,263 +74,263 @@ "password": "Password", "key": "Chiave", "sshPrivateKey": "Chiave privata SSH", - "upload": "Caricamento", - "keyPassword": "Chiave Password", + "upload": "Carica", + "keyPassword": "Password della chiave", "sshKey": "Chiave SSH", - "uploadPrivateKeyFile": "Carica il file della chiave privata", - "searchCredentials": "Cerca le credenziali...", - "addCredential": "Aggiungi credenziali", + "uploadPrivateKeyFile": "Carica file chiave privata", + "searchCredentials": "Cerca credenziali...", + "addCredential": "Aggiungi credenziale", "caCertificate": "Certificato CA (-cert.pub)", - "caCertificateDescription": "Facoltativo: carica o incolla il file del certificato firmato dall'autorità di certificazione (ad esempio id_ed25519-cert.pub). Obbligatorio se il server SSH utilizza l'autorizzazione basata su certificati.", - "uploadCertFile": "Carica il file cert.pub", - "clearCert": "Chiaro", + "caCertificateDescription": "Opzionale: Carica o incolla il file del certificato firmato dalla CA (es. id_ed25519-cert.pub). Richiesto quando il server SSH utilizza l'autorizzazione basata su certificati.", + "uploadCertFile": "Carica file -cert.pub", + "clearCert": "Cancella", "certLoaded": "Certificato caricato", "certPublicKeyLabel": "Certificato CA", "certTypeLabel": "Tipo di certificato", "pasteOrUploadCert": "Incolla o carica un certificato -cert.pub...", - "hasCaCert": "Possiede la certificazione CA", + "hasCaCert": "Con certificato CA", "noCaCert": "Nessun certificato CA", - "noPublicKeyAvailable": "Nessuna chiave pubblica disponibile. Aprire prima l'editor delle credenziali.", + "noPublicKeyAvailable": "Nessuna chiave pubblica disponibile. Apri prima l'editor delle credenziali.", "deployCommandCopied": "Comando di distribuzione copiato", - "sortCredentials": "Ordina le credenziali", + "sortCredentials": "Ordina credenziali", "sortDefault": "Ordine predefinito", "sortNameAsc": "Nome (A → Z)", "sortNameDesc": "Nome (Z → A)", "sortUsernameAsc": "Nome utente (A → Z)", "sortUsernameDesc": "Nome utente (Z → A)", - "filterCredentials": "Filtro credenziali", - "filterClearAll": "Filtri trasparenti", + "filterCredentials": "Filtra credenziali", + "filterClearAll": "Cancella filtri", "filterTypeGroup": "Tipo", "filterTypePassword": "Password", "filterTypeKey": "Chiave SSH", - "filterTagsGroup": "Etichette" + "filterTagsGroup": "Tag" }, "homepage": { - "title": "pagine iniziali", + "title": "Pagina iniziale", "addWidget": "Aggiungi widget", "editWidget": "Modifica widget", - "deleteWidget": "Eliminare", + "deleteWidget": "Elimina", "widgetTypes": "Tipi di widget", - "serviceLink": "Collegamento al servizio", + "serviceLink": "Collegamento servizio", "folder": "Cartella", "clock": "Orologio", "notes": "Note", - "hostStatus": "Host Motel", + "hostStatus": "Stato host", "bookmarkList": "Segnalibri", "zoomIn": "Ingrandisci", - "zoomOut": "Riduci zoom", - "resetView": "Ripristina la vista", - "lockLayout": "Schema di chiusura", - "unlockLayout": "Sblocca il layout", - "noWidgets": "Fai clic con il pulsante destro del mouse o fai clic su + per aggiungere il tuo primo widget", - "openFullView": "Visualizza a schermo intero", - "previewTitle": "Anteprima della homepage", - "cancel": "Cancellare", + "zoomOut": "Rimpicciolisci", + "resetView": "Reimposta vista", + "lockLayout": "Blocca layout", + "unlockLayout": "Sblocca layout", + "noWidgets": "Fai clic con il tasto destro o clicca su + per aggiungere il tuo primo widget", + "openFullView": "Apri vista completa", + "previewTitle": "Anteprima homepage", + "cancel": "Annulla", "save": "Salva", "title_label": "Titolo", - "widgetTitlePlaceholder": "Titolo del widget (facoltativo)", + "widgetTitlePlaceholder": "Titolo widget (opzionale)", "url": "URL", - "imageUrl": "URL dell'immagine personalizzata", - "imageUrlHint": "Lascia vuoto per utilizzare automaticamente la favicon del sito.", + "imageUrl": "URL immagine personalizzata", + "imageUrlHint": "Lascia vuoto per utilizzare automaticamente la favicon del sito", "showImage": "Mostra immagine", "description": "Descrizione", "color": "Colore", "icon": "Icona", - "expanded": "Espande per impostazione predefinita", + "expanded": "Espanso per impostazione predefinita", "timezone": "Fuso orario", - "showSeconds": "Mostra i secondi", + "showSeconds": "Mostra secondi", "format12h": "12 ore", - "format24h": "24 ore su 24", + "format24h": "24 ore", "content": "Contenuto", - "backgroundColor": "Colore di sfondo", - "host": "Ospite", - "showMetrics": "Mostra le metriche", + "backgroundColor": "Colore sfondo", + "host": "Host", + "showMetrics": "Mostra Metriche", "links": "Collegamenti", - "addLink": "Aggiungi collegamento", + "addLink": "Aggiungi Collegamento", "linkLabel": "Etichetta", "linkUrl": "URL", - "removeLink": "Rimuovere", + "removeLink": "Rimuovi", "categoryLinks": "Collegamenti", - "categoryInfo": "Informazioni", + "categoryInfo": "Info", "categorySystem": "Sistema", - "widgetServiceLinkDesc": "Un riquadro cliccabile che rimanda all'URL di un servizio.", + "widgetServiceLinkDesc": "Un riquadro cliccabile che collega a un URL di servizio", "widgetFolderDesc": "Un contenitore per raggruppare widget correlati", "widgetClockDesc": "Un orologio in tempo reale con fuso orario configurabile", - "widgetNotesDesc": "Un widget per le note in formato Markdown.", - "widgetHostStatusDesc": "Mostra in tempo reale l'utilizzo di CPU, memoria e disco per un host SSH.", - "widgetBookmarkListDesc": "Elenco di collegamenti rapidi", - "copyLink": "Copia collegamento", - "linkCopied": "Link copiato!", - "location": "Posizione", + "widgetNotesDesc": "Un widget per note in formato markdown", + "widgetHostStatusDesc": "Mostra CPU, memoria e disco in tempo reale per un host SSH", + "widgetBookmarkListDesc": "Un elenco di collegamenti rapidi", + "copyLink": "Copia Collegamento", + "linkCopied": "Collegamento copiato!", + "location": "Località", "temperatureUnit": "Unità di temperatura", - "showForecast": "Visualizza le previsioni a 3 giorni", - "scrolling": "Consenti lo scorrimento", - "feedUrl": "Feed unit", - "maxItems": "Numero massimo di articoli", + "showForecast": "Mostra previsioni a 3 giorni", + "scrolling": "Consenti scorrimento", + "feedUrl": "URL del feed", + "maxItems": "Numero massimo di elementi", "showDescription": "Mostra descrizione", - "widgetWeatherName": "Tempo atmosferico", + "widgetWeatherName": "Meteo", "widgetWeatherDesc": "Meteo in tempo reale per qualsiasi località", "widgetIframeName": "Incorporamento iFrame", "widgetIframeDesc": "Incorpora qualsiasi URL in un iframe", "widgetRssName": "Feed RSS", - "widgetRssDesc": "Visualizza gli elementi da un feed RSS o Atom", - "dragToFolder": "Trascina i widget qui oppure usa + per aggiungerli", - "showDisk": "Mostra l'utilizzo del disco", - "addToFolder": "Aggiungi il widget alla cartella", + "widgetRssDesc": "Visualizza elementi da un feed RSS o Atom", + "dragToFolder": "Trascina i widget qui o usa + per aggiungere", + "showDisk": "Mostra utilizzo disco", + "addToFolder": "Aggiungi widget alla cartella", "noHostSelected": "Nessun host selezionato", - "metricsNotAvailable": "Dati non disponibili", + "metricsNotAvailable": "Metriche non disponibili", "selectHost": "Seleziona un host...", "displayedMetrics": "Metriche visualizzate", - "metricCpu": "processore", + "metricCpu": "CPU", "metricMemory": "Memoria", "metricDisk": "Disco", - "metricUptime": "Tempo di attività", + "metricUptime": "Uptime", "metricSystem": "Sistema", - "metricOs": "Sistema operativo", + "metricOs": "OS", "metricKernel": "Kernel", "metricHostname": "Nome host", "metricNetwork": "Rete", "metricProcesses": "Processi", "metricProcessesTotal": "totale", - "metricProcessesRunning": "corsa", + "metricProcessesRunning": "in esecuzione", "categoryMonitoring": "Monitoraggio", "loading": "Caricamento...", "noData": "Nessun dato disponibile", "allClear": "Tutto a posto", - "acknowledgeAlert": "Riconoscere", + "acknowledgeAlert": "Riconosci", "noPingUrls": "Nessun URL configurato", "pingLabel": "Etichetta", "addPingUrl": "Aggiungi URL", - "showLatency": "Mostra la latenza", + "showLatency": "Mostra latenza", "refreshInterval": "Intervallo di aggiornamento", "seconds": "secondi", - "filterActivityTypes": "Tipi di filtro", + "filterActivityTypes": "Filtra tipi", "filterTypesHint": "Lascia vuoto per mostrare tutto", - "showTimestamp": "Mostra il timestamp", - "uptimeUnavailable": "Tempo di attività non disponibile", - "uptimeLabel": "Tempo di attività", + "showTimestamp": "Mostra timestamp", + "uptimeUnavailable": "Uptime non disponibile", + "uptimeLabel": "Uptime", "overviewVersion": "Versione", "overviewUpdate": "Aggiornato", "overviewUpdateAvailable": "Aggiornamento disponibile", - "overviewDatabase": "Schermata", - "overviewUptime": "Tempo di attività", + "overviewDatabase": "Database", + "overviewUptime": "Uptime", "noHosts": "Nessun host configurato", - "hostGridHosts": "Ospiti", + "hostGridHosts": "Host", "hostGridAllHint": "Lascia vuoto per mostrare tutti gli host", "columns": "Colonne", - "showIp": "Mostra l'indirizzo IP", + "showIp": "Mostra indirizzo IP", "connectionType": "Tipo di connessione", - "layout": "Disposizione", + "layout": "Layout", "showStatus": "Mostra stato", - "showHostName": "Mostra il nome host", - "noDockerActivity": "Nessuna attività Docker.", + "showHostName": "Mostra nome host", + "noDockerActivity": "Nessuna attività Docker", "noActivity": "Nessuna attività", - "showAcknowledged": "Mostra riconosciuto", - "showCurrentValue": "Mostra il valore corrente", - "chartMetric": "metrico", - "metricRange": "Allineare", - "widgetMetricsChartName": "Grafico delle metriche", + "showAcknowledged": "Mostra riconosciuti", + "showCurrentValue": "Mostra valore corrente", + "chartMetric": "Metrica", + "metricRange": "Intervallo", + "widgetMetricsChartName": "Grafico metriche", "widgetMetricsChartDesc": "Grafico storico di CPU, memoria, disco o rete per un host", "widgetHostGridName": "Griglia host", - "widgetHostGridDesc": "Visualizzazione a griglia dello stato degli host SSH", - "widgetAlertFeedName": "Feed di avvisi", - "widgetAlertFeedDesc": "Avvisi di attivazione in tempo reale con supporto di conferma", - "widgetPingStatusName": "Stato del ping", + "widgetHostGridDesc": "Vista a griglia degli stati degli host SSH", + "widgetAlertFeedName": "Feed degli avvisi", + "widgetAlertFeedDesc": "Avvisi in tempo reale con supporto di riconoscimento", + "widgetPingStatusName": "Stato ping", "widgetPingStatusDesc": "Stato del ping HTTP per uno o più URL", - "widgetRecentActivityName": "Attività recenti", - "widgetRecentActivityDesc": "Feed scorrevole delle attività recenti di Termix", - "widgetTermixUptimeName": "Tempo di attività di Termix", - "widgetTermixUptimeDesc": "Contatore in tempo reale del tempo di attività del server Termix", + "widgetRecentActivityName": "Attività recente", + "widgetRecentActivityDesc": "Feed scorrevole dell'attività recente di Termix", + "widgetTermixUptimeName": "Uptime di Termix", + "widgetTermixUptimeDesc": "Contatore di uptime in tempo reale per il server Termix", "widgetSystemOverviewName": "Panoramica del sistema", - "widgetSystemOverviewDesc": "Versione di Termix, stato del database e tempo di attività a colpo d'occhio", + "widgetSystemOverviewDesc": "Versione Termix, stato del database e uptime a colpo d'occhio", "widgetSshQuickConnectName": "Connessione rapida SSH", - "widgetSshQuickConnectDesc": "Pulsanti con un solo clic per aprire sessioni SSH", + "widgetSshQuickConnectDesc": "Pulsanti per aprire sessioni SSH con un clic", "widgetDockerActivityName": "Attività Docker", "widgetDockerActivityDesc": "Eventi recenti dei container Docker su tutti gli host", "widgetCalendarName": "Calendario", - "widgetCalendarDesc": "Un calendario mensile con la data odierna evidenziata.", + "widgetCalendarDesc": "Un calendario mensile con oggi evidenziato", "widgetCountdownName": "Conto alla rovescia", - "widgetCountdownDesc": "Conto alla rovescia fino alla data obiettivo", + "widgetCountdownDesc": "Timer per il conto alla rovescia verso una data obiettivo", "widgetSearchBarName": "Barra di ricerca", "widgetSearchBarDesc": "Widget per la ricerca rapida sul web", "widgetTextBannerName": "Banner di testo", - "widgetTextBannerDesc": "Un'etichetta o un'intestazione di sezione in grassetto per la tela", + "widgetTextBannerDesc": "Un'etichetta in grassetto o un'intestazione di sezione per l'area di lavoro", "widgetImageWidgetName": "Immagine", - "widgetImageWidgetDesc": "Visualizzare un'immagine da un URL", - "widgetMarkdownNotesName": "Note di Markdown", - "widgetMarkdownNotesDesc": "Note dettagliate con rendering Markdown in linea", + "widgetImageWidgetDesc": "Visualizza un'immagine da un URL", + "widgetMarkdownNotesName": "Note in Markdown", + "widgetMarkdownNotesDesc": "Note ricche con rendering markdown inline", "widgetCustomApiName": "API personalizzata", - "widgetCustomApiDesc": "Recupera e visualizza i dati da qualsiasi API JSON", - "widgetServiceGridName": "Griglia di servizio", - "widgetServiceGridDesc": "Una griglia configurabile di collegamenti tra riquadri di servizio", - "widgetDashboardLinksName": "Collegamenti della dashboard", - "widgetDashboardLinksDesc": "Visualizza i collegamenti di servizio configurati dalla dashboard", + "widgetCustomApiDesc": "Recupera e visualizza dati da qualsiasi API JSON", + "widgetServiceGridName": "Griglia dei servizi", + "widgetServiceGridDesc": "Una griglia configurabile di collegamenti a schede di servizio", + "widgetDashboardLinksName": "Collegamenti dashboard", + "widgetDashboardLinksDesc": "Visualizza i tuoi collegamenti ai servizi configurati dalla dashboard", "widgetSearchLinksName": "Scorciatoie di ricerca", - "widgetSearchLinksDesc": "Pulsanti di ricerca rapida con input in linea", + "widgetSearchLinksDesc": "Pulsanti di ricerca rapida con input inline", "widgetLinkTreeName": "Albero dei collegamenti", - "widgetLinkTreeDesc": "Sezioni di link raggruppate con intestazioni", - "calMon": "Mo", - "calTue": "Tu", - "calWed": "Noi", - "calThu": "Il", - "calFri": "Fr", + "widgetLinkTreeDesc": "Sezioni raggruppate di collegamenti con intestazioni", + "calMon": "Lu", + "calTue": "Ma", + "calWed": "Me", + "calThu": "Gi", + "calFri": "Ve", "calSat": "Sa", - "calSun": "Su", - "startOnMonday": "Inizia la settimana di lunedì", - "countdownNoDate": "Nessuna data di scadenza impostata", - "countdownPast": "L'evento si è concluso", + "calSun": "Do", + "startOnMonday": "Inizia la settimana il lunedì", + "countdownNoDate": "Nessuna data obiettivo impostata", + "countdownPast": "Evento passato", "countdownDays": "giorni", "countdownHours": "ore", "countdownMinutes": "min", - "countdownSeconds": "sicurezza", + "countdownSeconds": "sec", "countdownLabel": "Etichetta", - "countdownLabelPlaceholder": "ad esempio il giorno del lancio", - "countdownShowDays": "Giorni di spettacolo", - "countdownShowHours": "Orari di apertura", - "targetDate": "Data prevista", + "countdownLabelPlaceholder": "es. Giorno di lancio", + "countdownShowDays": "Mostra giorni", + "countdownShowHours": "Mostra ore", + "targetDate": "Data obiettivo", "searchEngine": "Motore di ricerca", "customSearchUrl": "URL di ricerca personalizzato", - "searchPlaceholder": "Ricerca...", - "searchGo": "Andare", + "searchPlaceholder": "Cerca...", + "searchGo": "Vai", "searchPlaceholderLabel": "Testo segnaposto", - "searchPlaceholderHint": "Testo visualizzato all'interno del campo di ricerca", - "openInNewTab": "Apri in una nuova scheda", - "searchQueryPlaceholder": "Inserisci la query...", + "searchPlaceholderHint": "Testo mostrato nel campo di ricerca", + "openInNewTab": "Apri in nuova scheda", + "searchQueryPlaceholder": "Inserisci query...", "noSearchShortcuts": "Nessuna scorciatoia configurata", - "addSearchShortcut": "Aggiungi collegamento", - "fontSize": "Dimensione del carattere", - "textAlign": "Allineamento del testo", - "fontWeight": "Peso del carattere", - "clearColor": "Colore trasparente", - "imageFit": "Adattamento dell'immagine", - "imageLinkUrl": "URL del collegamento", - "noImage": "Nessun URL dell'immagine impostato", + "addSearchShortcut": "Aggiungi scorciatoia", + "fontSize": "Dimensione carattere", + "textAlign": "Allineamento testo", + "fontWeight": "Spessore carattere", + "clearColor": "Rimuovi colore", + "imageFit": "Adatta immagine", + "imageLinkUrl": "URL collegamento", + "noImage": "Nessun URL immagine impostato", "altText": "Testo alternativo", "altTextPlaceholder": "Descrivi l'immagine", - "renderMarkdown": "Renderizza Markdown", - "displayMode": "Modalità di visualizzazione", - "displayField": "Campo di visualizzazione", + "renderMarkdown": "Visualizza Markdown", + "displayMode": "Modalità visualizzazione", + "displayField": "Campo visualizzazione", "jsonPath": "Percorso JSON", "customApiLabel": "Etichetta", - "customApiLabelPlaceholder": "ad esempio Temperatura", + "customApiLabelPlaceholder": "es. Temperatura", "customApiUnit": "Unità", "customApiNoUrl": "Nessun URL API configurato", - "customApiError": "Impossibile recuperare", + "customApiError": "Recupero fallito", "customApiNotArray": "La risposta non è un array", "addService": "Aggiungi servizio", "showLabels": "Mostra etichette", - "iconSize": "Dimensione icona", - "noDashboardLinks": "Nessun collegamento alla dashboard configurato", + "iconSize": "Dimensioni icone", + "noDashboardLinks": "Nessun collegamento dashboard configurato", "noLimit": "Nessun limite", - "sectionHeading": "Intestazione della sezione", + "sectionHeading": "Intestazione sezione", "addSection": "Aggiungi sezione", "compactMode": "Modalità compatta", "showDetailed": "Mostra dettagli", - "selectHosts": "Seleziona gli host", + "selectHosts": "Seleziona host", "allHosts": "Tutti gli host", - "listLayout": "Lista", + "listLayout": "Elenco", "gridLayout": "Griglia", - "terminal": "terminale", + "terminal": "Terminale", "files": "File", "docker": "Docker", "range15m": "15 minuti", @@ -338,178 +338,217 @@ "range6h": "6 ore", "range24h": "24 ore", "metricNetRx": "Download di rete", - "metricNetTx": "Caricamento netto", - "severityFilter": "Filtro di gravità", - "filterAll": "Tutto", - "accentColor": "Colore d'accento", + "metricNetTx": "Upload di rete", + "severityFilter": "Filtro gravità", + "filterAll": "Tutti", + "accentColor": "Colore accento", "widgetSshTerminalName": "Terminale SSH", - "widgetSshTerminalDesc": "Un terminale SSH in linea connesso a un host configurato", + "widgetSshTerminalDesc": "Un terminale SSH inline connesso a un host configurato", "sshTerminalNoHost": "Nessun host configurato", - "sshTerminalConnect": "Collegare", - "sshTerminalAutoConnect": "Connessione automatica al carico", + "sshTerminalConnect": "Connetti", + "sshTerminalAutoConnect": "Connessione automatica al caricamento", "widgetQuickConnectName": "Connessione rapida", - "widgetQuickConnectDesc": "Pulsanti di avvio con un solo clic per qualsiasi tipo di connessione su tutti i tuoi host", + "widgetQuickConnectDesc": "Pulsanti di avvio con un clic per qualsiasi tipo di connessione ai tuoi host", "connectionTypes": "Tipi di connessione", - "connType_terminal": "terminale", - "connType_files": "Gestore file", + "connType_terminal": "Terminale", + "connType_files": "Gestione file", "connType_docker": "Docker", "connType_tunnel": "Tunnel", - "connType_host-metrics": "Metriche dell'host", + "connType_host-metrics": "Metriche host", "connType_rdp": "RDP", "connType_vnc": "VNC", - "connType_telnet": "Internet", + "connType_telnet": "Telnet", "widgetFileManagerName": "Gestore file", - "widgetFileManagerDesc": "Gestore di file SFTP integrato per un host configurato", + "widgetFileManagerDesc": "Gestore file SFTP integrato per un host configurato", "widgetDockerName": "Gestore Docker", "widgetDockerDesc": "Gestore di container Docker integrato per un host configurato", - "widgetTunnelName": "Responsabile del tunnel", + "widgetTunnelName": "Gestore Tunnel", "widgetTunnelDesc": "Gestore di tunnel SSH integrato per un host configurato", "widgetNoHostSelected": "Nessun host configurato" }, "serverConfig": { - "title": "Configurazione del server", - "description": "Configura l'URL del server Termix per connetterti ai tuoi servizi di backend.", + "title": "Configurazione del Server", + "description": "Configura l'URL del server Termix per connetterti ai tuoi servizi backend", "serverUrl": "URL del server", - "enterServerUrl": "Inserisci l'URL del server", - "saveFailed": "Impossibile salvare la configurazione", - "saveError": "Configurazione di salvataggio degli errori", - "saving": "Risparmio...", + "enterServerUrl": "Inserisci un URL del server", + "saveFailed": "Salvataggio della configurazione non riuscito", + "saveError": "Errore durante il salvataggio della configurazione", + "saving": "Salvataggio in corso...", "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)", + "helpText": "Inserisci l'URL del server Termix (es. http://localhost:30001 o https://your-server.com)", "changeServer": "Cambia server", "mustIncludeProtocol": "L'URL del server deve iniziare con http:// o https://", "allowInvalidCertificate": "Consenti certificato non valido", - "allowInvalidCertificateDesc": "Da utilizzare esclusivamente per server self-hosted affidabili con certificati autofirmati o certificati IP.", - "useEmbedded": "Utilizzare il server locale", - "embeddedDesc": "Esegui Termix con il server locale integrato (non è necessario un server remoto).", + "allowInvalidCertificateDesc": "Utilizzare solo per server self-hosted attendibili con certificati autofirmati o basati su indirizzo IP.", + "useEmbedded": "Usa server locale", + "embeddedDesc": "Esegui Termix con il server locale integrato (nessun server remoto richiesto)", "embeddedConnecting": "Connessione al server locale in corso...", - "embeddedNotReady": "Il server locale non è ancora pronto. Attendi qualche istante e riprova.", + "embeddedNotReady": "Il server locale non è ancora pronto. Attendere qualche istante e riprovare.", "localServer": "Server locale", "savedServers": "Server salvati", "noSavedServers": "Nessun server salvato", - "removeServer": "Rimuovere" + "removeServer": "Rimuovi" + }, + "migrationNotice": { + "title": "Termix Desktop ora funziona in modalità standalone.", + "body1": "Questa app in precedenza si connetteva direttamente al server Termix. È stata rielaborata per funzionare in modo completamente autonomo, memorizzando host e credenziali localmente in modo da continuare a funzionare offline. La sincronizzazione bidirezionale con un server Termix self-hosted è ora opzionale.", + "body2": "I tuoi host e le tue credenziali attuali sono ancora su {{url}}. Attiva la sincronizzazione remota e riconnettiti a quel server per ripristinarli e mantenerli sincronizzati in futuro.", + "dismiss": "Non adesso", + "setUpSync": "Configura la sincronizzazione remota" + }, + "remoteSync": { + "title": "Sincronizzazione remota", + "description": "Facoltativamente, è possibile collegare questa applicazione desktop a un server Termix autogestito per sincronizzare host, credenziali e snippet tra i dispositivi. L'applicazione funziona sempre completamente offline, indipendentemente dal fatto che sia connessa o meno.", + "notConnected": "Non connesso", + "connected": "Collegato", + "connectedTo": "Connesso a {{url}}", + "lastSynced": "Ultima sincronizzazione {{time}}", + "neverSynced": "Mai sincronizzato", + "syncError": "Errore di sincronizzazione: {{message}}", + "needsReauth": "Accesso scaduto", + "connectButton": "Connessione al server", + "disconnectButton": "Disconnetti", + "syncNowButton": "Sincronizza ora", + "syncing": "Sincronizzazione in corso...", + "serverUrl": "URL del server", + "enterServerUrl": "Inserisci l'URL del server", + "mustIncludeProtocol": "L'URL del server deve iniziare con http:// o https://", + "connectionTestFailed": "Impossibile raggiungere un server Termix a quell'URL", + "allowInvalidCertificate": "Consenti certificato non valido", + "allowInvalidCertificateDesc": "Da utilizzare esclusivamente per server self-hosted affidabili con certificati autofirmati o certificati IP.", + "savedServers": "Server salvati", + "removeServer": "Rimuovere", + "continueButton": "Continuare", + "cancelButton": "Cancellare", + "signInTitle": "Accedi a {{url}}", + "originTitle": "Origine della connessione", + "originDescription": "Scegli da dove originare le connessioni SSH per impostazione predefinita. Questa impostazione può essere modificata per ogni singolo host.", + "originLocal": "Questo dispositivo (rete locale)", + "originRemote": "Server remoto", + "bannerReconnect": "Ricollega", + "bannerMessage": "La sincronizzazione remota richiede una nuova autenticazione." }, "versionCheck": { - "error": "Errore di verifica", - "checkFailed": "Impossibile verificare la presenza di aggiornamenti", - "upToDate": "L'app è aggiornata", - "currentVersion": "Stai eseguendo la versione {{version}}", + "error": "Errore controllo versione", + "checkFailed": "Controllo aggiornamenti non riuscito", + "upToDate": "App aggiornata", + "currentVersion": "Stai utilizzando la versione {{version}}", "updateAvailable": "Aggiornamento disponibile", - "newVersionAvailable": "È disponibile una nuova versione! Stai eseguendo {{current}}, ma è disponibile {{latest}}.", + "newVersionAvailable": "È disponibile una nuova versione! Stai utilizzando {{current}}, ma è disponibile {{latest}}.", "betaVersion": "Versione beta", - "betaVersionDesc": "Stai eseguendo {{current}}, che è più recente dell'ultima versione stabile {{latest}}.", - "releasedOn": "Pubblicato il {{date}}", - "downloadUpdate": "Scarica l'aggiornamento", - "checking": "Verifica della presenza di aggiornamenti...", - "checkUpdates": "Verifica la presenza di aggiornamenti.", - "checkingUpdates": "Verifica della presenza di aggiornamenti...", - "updateRequired": "Aggiornamento necessario" + "betaVersionDesc": "Stai utilizzando {{current}}, che è più recente dell'ultima versione stabile {{latest}}.", + "releasedOn": "Rilasciato il {{date}}", + "downloadUpdate": "Scarica aggiornamento", + "checking": "Controllo aggiornamenti in corso...", + "checkUpdates": "Controlla aggiornamenti", + "checkingUpdates": "Controllo aggiornamenti in corso...", + "updateRequired": "Aggiornamento richiesto" }, "common": { - "close": "Vicino", - "minimize": "Minimizzare", + "close": "Chiudi", + "minimize": "Riduci a icona", "online": "Online", "offline": "Offline", "unknown": "Sconosciuto", - "continue": "Continuare", + "continue": "Continua", "maintenance": "Manutenzione", "degraded": "Degradato", "error": "Errore", - "warning": "Avvertimento", + "warning": "Attenzione", "unsavedChanges": "Modifiche non salvate", - "dismiss": "Congedare", - "loading": "Caricamento...", + "dismiss": "Ignora", + "loading": "Caricamento in corso...", "optional": "Opzionale", - "connect": "Collegare", + "connect": "Connetti", "copied": "Copiato", "connecting": "Connessione in corso...", "updateAvailable": "Aggiornamento disponibile", - "appName": "Terminix", - "openInNewTab": "Apri in una nuova scheda", - "noReleases": "Nessuna pubblicazione", - "updatesAndReleases": "Aggiornamenti e nuove versioni", - "newVersionAvailable": "È disponibile una nuova versione ({{version}}).", - "failedToFetchUpdateInfo": "Impossibile recuperare le informazioni di aggiornamento", - "preRelease": "Anteprima", - "noReleasesFound": "Nessuna pubblicazione trovata.", - "cancel": "Cancellare", + "appName": "Termix", + "openInNewTab": "Apri in nuova scheda", + "noReleases": "Nessuna versione", + "updatesAndReleases": "Aggiornamenti e versioni", + "newVersionAvailable": "Una nuova versione ({{version}}) è disponibile.", + "failedToFetchUpdateInfo": "Impossibile recuperare le informazioni sull'aggiornamento", + "preRelease": "Pre-rilascio", + "noReleasesFound": "Nessuna release trovata", + "cancel": "Annulla", "username": "Nome utente", - "login": "Login", - "logout": "Disconnettiti", - "register": "Registro", + "login": "Accedi", + "logout": "Esci", + "register": "Registrati", "password": "Password", "confirmPassword": "Conferma password", "back": "Indietro", "save": "Salva", - "saving": "Risparmio...", - "delete": "Eliminare", - "rename": "Rinominare", - "edit": "Modificare", - "add": "Aggiungere", - "confirm": "Confermare", - "no": "NO", + "saving": "Salvataggio...", + "delete": "Elimina", + "rename": "Rinomina", + "edit": "Modifica", + "add": "Aggiungi", + "confirm": "Conferma", + "no": "No", "or": "O", - "next": "Prossimo", + "next": "Avanti", "previous": "Precedente", - "refresh": "Rinfrescato", + "refresh": "Aggiorna", "language": "Lingua", - "checking": "Controllo in corso...", - "checkingDatabase": "Verifica della connessione al database...", - "checkingAuthentication": "Verifica dell'autenticazione in corso...", + "checking": "Verifica...", + "checkingDatabase": "Verifica connessione al database...", + "checkingAuthentication": "Verifica autenticazione...", "backendReconnected": "Connessione al server ripristinata", - "connectionDegraded": "Connessione al server persa, ripristino in corso…", - "reload": "Ricaricare", - "remove": "Rimuovere", - "create": "Creare", - "update": "Aggiornamento", + "connectionDegraded": "Connessione al server persa, recupero in corso…", + "reload": "Ricarica", + "remove": "Rimuovi", + "create": "Crea", + "update": "Aggiorna", "copy": "Copia", - "copyFailed": "Impossibile copiare negli appunti", - "maximize": "Sviluppa", - "restore": "Ripristinare", - "of": "Di", + "copyFailed": "Copia negli appunti non riuscita", + "maximize": "Massimizza", + "restore": "Ripristina", + "of": "di", "saved": "Salvato", "deleted": "Eliminato", - "deleteFailed": "Impossibile eliminare", + "deleteFailed": "Eliminazione non riuscita", "saveFailed": "Salvataggio non riuscito", - "required": "Necessario" + "required": "Obbligatorio" }, "nav": { - "home": "Casa", - "terminal": "terminale", + "home": "Home", + "terminal": "Terminale", "docker": "Docker", - "tunnels": "Gallerie", - "fileManager": "Gestore file", - "serverStats": "Metriche dell'host", - "hostMetrics": "Metriche dell'host", - "admin": "Amministratore", + "tunnels": "Tunnel", + "fileManager": "Gestione file", + "serverStats": "Metriche host", + "hostMetrics": "Metriche host", + "admin": "Admin", "termixId": "ID", "userProfile": "Profilo utente", - "splitScreen": "Schermo diviso", + "splitScreen": "Dividi schermo", "confirmClose": "Chiudere questa sessione attiva?", - "close": "Vicino", - "cancel": "Cancellare", + "close": "Chiudi", + "cancel": "Annulla", "sshManager": "Gestore SSH", "cannotSplitTab": "Impossibile dividere questa scheda", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "Copia password", - "copySudoPassword": "Copia la password di Sudo", + "copySudoPassword": "Copia password sudo", "passwordCopied": "Password copiata negli appunti", "noPasswordAvailable": "Nessuna password disponibile", - "failedToCopyPassword": "Impossibile copiare la password", - "refreshTab": "Connessione aggiornata", - "renameTab": "Rinomina la scheda", + "failedToCopyPassword": "Copia password non riuscita", + "refreshTab": "Aggiorna connessione", + "renameTab": "Rinomina scheda", "openFileManager": "Apri Gestione file", - "dashboard": "Pannello di controllo", + "dashboard": "Dashboard", "networkGraph": "Grafico di rete", - "tmuxMonitor": "Monitoraggio Tmux", - "homepage": "pagine iniziali", + "tmuxMonitor": "Monitor Tmux", + "homepage": "Homepage", "quickConnect": "Connessione rapida", "sshTools": "Strumenti SSH", - "history": "Storia", - "sessionLogs": "Registri delle sessioni", - "hosts": "Ospiti", + "history": "Cronologia", + "sessionLogs": "Log di sessione", + "hosts": "Host", "snippets": "Frammenti", - "hostManager": "Responsabile dell'hosting", + "hostManager": "Gestione host", "credentials": "Credenziali", "connections": "Connessioni", "alerts": "Avvisi", @@ -518,113 +557,114 @@ "roleUser": "Utente" }, "hosts": { - "hosts": "Ospiti", + "hosts": "Host", "noHosts": "Nessun host SSH", "retry": "Riprova", - "refresh": "Rinfrescato", + "refresh": "Aggiorna", "optional": "Opzionale", - "downloadSample": "Scarica un esempio", + "downloadSample": "Scarica esempio", "failedToDeleteHost": "Impossibile eliminare {{name}}", - "importSkipExisting": "Importa (salta i file esistenti)", - "importSSHConfig": "Importa dalla configurazione SSH", - "connectionDetails": "Dettagli di connessione", + "importSkipExisting": "Importa (salta esistenti)", + "importSSHConfig": "Importa da configurazione SSH", + "connectionDetails": "Dettagli connessione", "ssh": "SSH", - "telnet": "Internet", + "telnet": "Telnet", "remoteDesktop": "Desktop remoto", "port": "Porta", "username": "Nome utente", "folder": "Cartella", - "tags": "Etichette", - "pin": "Spillo", + "tags": "Tag", + "pin": "Fissa", "addHost": "Aggiungi host", "editHost": "Modifica host", - "cloneHost": "Clone Host", - "enableTerminal": "Terminale di supporto", + "cloneHost": "Clona host", + "enableTerminal": "Abilita Terminale", "enableTunnel": "Abilita Tunnel", - "enableFileManager": "Abilita Gestione file", + "enableFileManager": "Abilita File Manager", "enableDocker": "Abilita Docker", "defaultPath": "Percorso predefinito", "connection": "Connessione", - "upload": "Caricamento", + "upload": "Carica", "authentication": "Autenticazione", "password": "Password", "key": "Chiave", - "credential": "Credenziali", - "none": "Nessuno", + "credential": "Credenziale", + "none": "Nessuna", "sshPrivateKey": "Chiave privata SSH", "keyType": "Tipo di chiave", "uploadFile": "Carica file", "tabGeneral": "Generale", "tabSsh": "SSH", - "tabTerminal": "terminale", + "tabTerminal": "Terminale", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Gallerie", + "tabTunnels": "Tunnel", "tabDocker": "Docker", "tabFiles": "File", - "tabStats": "Metriche dell'host", - "tabHostMetrics": "Metriche dell'host", - "tabTelnet": "Internet", + "tabStats": "Metriche host", + "tabHostMetrics": "Metriche host", + "tabTelnet": "Telnet", "tabSharing": "Condivisione", "tabAuthentication": "Autenticazione", - "terminal": "terminale", + "terminal": "Terminale", "tunnel": "Tunnel", - "fileManager": "Gestore file", - "serverStats": "Metriche dell'host", + "fileManager": "File Manager", + "serverStats": "Metriche host", "status": "Stato", - "folderRenamed": "La cartella \"{{oldName}}\" è stata rinominata in \"{{newName}}\" con successo", + "folderRenamed": "Cartella \"{{oldName}}\" rinominata in \"{{newName}}\" con successo", "failedToRenameFolder": "Impossibile rinominare la cartella", - "movedToFolder": "Spostato l'host(i) {{count}} su \"{{folder}}\"", + "movedToFolder": "Spostato/i {{count}} host nella cartella \"{{folder}}\"", "editHostTooltip": "Modifica host", - "statusChecks": "Verifiche di stato", - "metricsCollection": "Raccolta di metriche", - "metricsInterval": "Intervallo di raccolta delle metriche", - "metricsIntervalDesc": "Con quale frequenza raccogliere le statistiche del server (5 secondi - 1 ora)", + "statusChecks": "Controlli di stato", + "metricsCollection": "Raccolta metriche", + "metricsInterval": "Intervallo raccolta metriche", + "metricsIntervalDesc": "Intervallo di raccolta delle metriche del server (5s - 1h)", "behavior": "Comportamento", - "themePreview": "Anteprima del tema", + "themePreview": "Anteprima tema", "theme": "Tema", "fontFamily": "Famiglia di caratteri", - "fontSize": "Dimensione del carattere", - "letterSpacing": "Spaziatura tra le lettere", - "lineHeight": "Altezza linea", - "cursorStyle": "Stile del cursore", - "cursorBlink": "Lampeggio del cursore", + "fontSize": "Dimensione carattere", + "letterSpacing": "Spaziatura caratteri", + "lineHeight": "Altezza riga", + "cursorStyle": "Stile cursore", + "cursorBlink": "Lampeggio cursore", "scrollbackBuffer": "Buffer di scorrimento", - "bellStyle": "Stile campana", - "rightClickSelectsWord": "Il clic destro seleziona Parola", - "fastScrollModifier": "Modificatore di scorrimento veloce", - "fastScrollSensitivity": "Sensibilità di scorrimento rapida", - "sshAgentForwarding": "Inoltro dell'agente SSH", + "bellStyle": "Stile campanello", + "rightClickSelectsWord": "Clic destro seleziona la parola", + "fastScrollModifier": "Modificatore scorrimento veloce", + "fastScrollSensitivity": "Sensibilità scorrimento veloce", + "sshAgentForwarding": "Inoltro agente SSH", "backspaceMode": "Modalità Backspace", "startupSnippet": "Snippet di avvio", - "selectSnippet": "Seleziona il frammento", - "forceKeyboardInteractive": "Forza tastiera interattiva", - "overrideCredentialUsername": "Sovrascrivi nome utente delle credenziali", - "overrideCredentialUsernameDesc": "Utilizzare il nome utente specificato sopra al posto del nome utente delle credenziali.", - "oidcUsernameHint": "Utilizza $oidc.preferred_username per sostituire il tuo nome di accesso OIDC.", - "jumpHostChain": "Catena host di salto", - "portKnocking": "Bussare al porto", + "selectSnippet": "Seleziona snippet", + "forceKeyboardInteractive": "Forza Keyboard-Interactive", + "overrideCredentialUsername": "Sovrascrivi nome utente della credenziale", + "overrideCredentialUsernameDesc": "Utilizza il nome utente specificato sopra invece di quello della credenziale", + "oidcUsernameHint": "Usa $oidc.preferred_username per sostituire il tuo nome di login OIDC.", + "tailscaleUsernameHint": "Deve trattarsi di un utente Unix a cui è concessa l'identità Tailscale nell'ACL SSH di Tailnet, non necessariamente root.", + "jumpHostChain": "Catena Jump Host", + "portKnocking": "Port Knocking", "addKnock": "Aggiungi porta", "addProxyNode": "Aggiungi nodo", "proxyNode": "Nodo proxy", - "proxyType": "Tipo di proxy", + "proxyType": "Tipo proxy", "quickActions": "Azioni rapide", - "sudoPasswordAutoFill": "Sudo Password Auto-Fill", - "sudoPassword": "Password Sudo", - "keepaliveInterval": "Intervallo di keep-alive (ms)", + "sudoPasswordAutoFill": "Compilazione automatica password sudo", + "sudoPassword": "Password sudo", + "keepaliveInterval": "Intervallo keepalive (ms)", "moshCommand": "Comando MOSH", - "environmentVariables": "Variabili ambientali", + "environmentVariables": "Variabili d'ambiente", "addVariable": "Aggiungi variabile", "docker": "Docker", - "copyTerminalUrl": "Copia URL del terminale", - "copyFileManagerUrl": "Copia URL di Gestione file", - "copyRemoteDesktopUrl": "Copia l'URL di Desktop remoto", - "failedToConnect": "Impossibile connettersi alla console", - "connect": "Collegare", + "copyTerminalUrl": "Copia URL terminale", + "copyFileManagerUrl": "Copia URL gestore file", + "copyRemoteDesktopUrl": "Copia URL desktop remoto", + "failedToConnect": "Connessione alla console non riuscita", + "connect": "Connetti", "disconnect": "Disconnetti", - "start": "Inizio", - "enableStatusCheck": "Abilitare il controllo dello stato", - "enableMetrics": "Abilita le metriche", + "start": "Avvia", + "enableStatusCheck": "Abilita controllo stato", + "enableMetrics": "Abilita metriche", "bulkUpdateFailed": "Aggiornamento in blocco non riuscito", "selectAll": "Seleziona tutto", "deselectAll": "Deseleziona tutto", @@ -632,117 +672,122 @@ "secureShell": "Secure Shell", "virtualNetwork": "Rete virtuale", "unencryptedShell": "Shell non crittografata", - "addressIp": "Indirizzo IP", - "friendlyName": "Nome amichevole", + "addressIp": "Indirizzo / IP", + "friendlyName": "Nome descrittivo", "macAddress": "Indirizzo MAC", - "wolBroadcastAddress": "Discorso trasmesso da WoL", - "wolBroadcastAddressDesc": "Indirizzo di trasmissione direzionale opzionale per Docker/reti instradate (ad esempio 192.168.1.255). Lasciare vuoto per utilizzare 255.255.255.255.", - "folderAndAdvanced": "Cartella e Avanzate", + "wolBroadcastAddress": "Indirizzo broadcast WoL", + "wolBroadcastAddressDesc": "Broadcast diretto opzionale per reti Docker/instradate (es. 192.168.1.255). Lasciare vuoto per usare 255.255.255.255.", + "folderAndAdvanced": "Cartella & Avanzate", "privateNotes": "Note private", "privateNotesPlaceholder": "Dettagli su questo server...", - "pinToTop": "Aggiungi alla parte superiore", + "pinToTop": "Fissa in cima", "pinToTopDesc": "Mostra sempre questo host in cima all'elenco", - "portKnockingSequence": "Sequenza di bussare al porto", - "addKnockBtn": "Aggiungi bussare", - "noPortKnocking": "Nessuna configurazione per il port knocking.", - "knockPort": "Porto di Knock", + "portKnockingSequence": "Sequenza port knocking", + "addKnockBtn": "Aggiungi knock", + "noPortKnocking": "Nessun port knocking configurato.", + "knockPort": "Porta Knock", "protocol": "Protocollo", - "delayAfterMs": "Ritardo dopo (ms)", - "useSocks5Proxy": "Utilizza il proxy SOCKS5", - "useSocks5ProxyDesc": "Instradare la connessione tramite un server proxy.", + "delayAfterMs": "Ritardo (ms)", + "useSocks5Proxy": "Usa proxy SOCKS5", + "useSocks5ProxyDesc": "Instrada la connessione attraverso un server proxy", + "connectionOrigin": "Origine della connessione", + "connectionOriginDesc": "Indirizzo di origine della connessione SSH di questo host. Sovrascrive l'impostazione predefinita globale dell'applicazione desktop.", + "connectionOriginDefault": "Usa le impostazioni predefinite", + "connectionOriginLocal": "Questo dispositivo (rete locale)", + "connectionOriginRemote": "Server remoto", "proxyHost": "Host proxy", "proxyPort": "Porta proxy", "proxyUsername": "Nome utente proxy", "proxyPassword": "Password proxy", "proxySingleMode": "Proxy singolo", - "proxyChainMode": "Catena proxy", - "you": "Voi", - "jumpHostChainLabel": "Catena host di salto", - "addJumpBtn": "Aggiungi salto", - "noJumpHosts": "Nessun host di salto configurato.", + "proxyChainMode": "Catena di proxy", + "you": "Tu", + "jumpHostChainLabel": "Catena di jump host", + "addJumpBtn": "Aggiungi jump", + "noJumpHosts": "Nessun jump host configurato.", "selectAServer": "Seleziona un server...", "sshPort": "Porta SSH", "authMethod": "Metodo di autenticazione", - "storedCredential": "Credenziali memorizzate", + "storedCredential": "Credenziale memorizzata", "selectACredential": "Seleziona una credenziale...", - "vaultProfile": "Profilo del firmatario del caveau", + "vaultProfile": "Profilo firmatario Vault", "selectAVaultProfile": "Seleziona un profilo Vault...", - "vaultProfileHint": "Le impostazioni provengono dal profilo condiviso; al momento della connessione, accederai a Vault tramite OIDC. Nessun segreto viene memorizzato.", + "vaultProfileHint": "Le impostazioni provengono dal profilo condiviso; effettuerai l'accesso a Vault tramite OIDC quando ti connetti. Nessun segreto è memorizzato.", "vaultNewProfile": "Nuovo profilo", - "vaultManageProfiles": "Gestisci i profili di Vault", - "vaultAddrLabel": "Indirizzo del caveau", + "vaultManageProfiles": "Gestisci profili Vault", + "vaultAddrLabel": "Indirizzo Vault", "vaultNamespaceLabel": "Namespace", - "vaultOidcMountLabel": "Supporto di autorizzazione OIDC", - "vaultOidcRoleLabel": "Ruolo dell'OIDC", - "vaultSshMountLabel": "Segreti SSH Mount", - "vaultSshRoleLabel": "Ruolo di firmatario SSH", - "vaultValidPrincipalsLabel": "Principali validi", + "vaultOidcMountLabel": "Mount di autenticazione OIDC", + "vaultOidcRoleLabel": "Ruolo OIDC", + "vaultSshMountLabel": "Mount dei segreti SSH", + "vaultSshRoleLabel": "Ruolo firmatario SSH", + "vaultValidPrincipalsLabel": "Principal validi", "vaultKeyTypeLabel": "Tipo di chiave effimera", "vaultSharedLabel": "Condividi con tutti gli utenti", "vaultCreateProfile": "Crea profilo", - "vaultProfileCreated": "Profilo del vault creato", - "vaultProfileSaved": "Profilo del vault salvato", - "vaultProfileDeleted": "Profilo del vault eliminato", - "vaultProfileSaveFailed": "Impossibile salvare il profilo di Vault", - "vaultProfileDeleteFailed": "Impossibile eliminare il profilo di Vault", + "vaultProfileCreated": "Profilo Vault creato", + "vaultProfileSaved": "Profilo Vault salvato", + "vaultProfileDeleted": "Profilo Vault eliminato", + "vaultProfileSaveFailed": "Salvataggio profilo Vault non riuscito", + "vaultProfileDeleteFailed": "Eliminazione profilo Vault non riuscita", "vaultSaveProfile": "Salva profilo", - "vaultProfileValidationError": "Sono richiesti nome, indirizzo del vault e ruolo di firmatario SSH.", - "vaultNoProfiles": "Nessun profilo Vault ancora disponibile.", + "vaultProfileValidationError": "Nome, indirizzo Vault e ruolo di firma SSH sono obbligatori", + "vaultNoProfiles": "Nessun profilo Vault ancora.", "vaultSharedBadge": "condiviso", - "keyTypeLabel": "Tipo di chiave", + "keyTypeLabel": "Tipo chiave", "keyTypeAuto": "Rilevamento automatico", - "keyPasteTab": "Impasto", - "keyUploadTab": "Caricamento", + "keyPasteTab": "Incolla", + "keyUploadTab": "Carica", "keyFileLoaded": "File chiave caricato", - "keyUploadClick": "Fai clic per caricare .pem / .key / .ppk", - "clearKey": "Chiave di cancellazione", + "keyUploadClick": "Clicca per caricare .pem / .key / .ppk", + "clearKey": "Cancella chiave", "keySaved": "Chiave SSH salvata", - "keyReplaceNotice": "Incolla una nuova chiave qui sotto per sostituirla", - "keyPassphraseSaved": "Frase d'identità salvata, digita per modificarla", - "replaceKey": "Chiave sostituita", - "docsLink": "Visualizza i documenti", + "keyReplaceNotice": "incolla una nuova chiave qui sotto per sostituirla", + "keyPassphraseSaved": "Passphrase salvata, digita per modificare", + "replaceKey": "Sostituisci chiave", + "docsLink": "Visualizza documentazione", "opksshLabel": "OPKSSH", - "opksshDesc": "Accedi a questo host utilizzando il tuo provider di identità anziché una password o una chiave. Richiede la configurazione di OPKSSH sul server.", - "warpgateLabel": "Portale di teletrasporto", - "warpgateDesc": "Questo host si connette tramite un proxy SSH Warpgate. Termix gestirà automaticamente il flusso di approvazione basato sul browser dopo l'autenticazione.", + "opksshDesc": "Accedi a questo host usando il tuo provider di identità invece di una password o chiave. Richiede OPKSSH configurato sul server.", + "warpgateLabel": "Warpgate Gateway", + "warpgateDesc": "Questo host si connette tramite un proxy SSH Warpgate. Termix gestirà automaticamente il flusso di approvazione basato su browser dopo l'autenticazione.", "agentLabel": "Agente SSH", - "agentDesc": "Autenticarsi utilizzando un agente SSH in esecuzione sul server Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agente deve essere in esecuzione sulla macchina che ospita Termix.", + "agentDesc": "Autenticati usando un agente SSH in esecuzione sul server Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agente deve essere in esecuzione sulla macchina dove è ospitato Termix.", "agentSocketPathLabel": "Percorso socket agente", - "agentSocketPathPlaceholder": "Lascia vuoto per utilizzare SSH_AUTH_SOCK", - "agentSocketPathHint": "Lascia vuoto per rilevare automaticamente il socket tramite la variabile d'ambiente SSH_AUTH_SOCK, oppure inserisci un percorso socket personalizzato (ad esempio /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "Seleziona il dispositivo Tailscale", + "agentSocketPathPlaceholder": "Lascia vuoto per usare SSH_AUTH_SOCK", + "agentSocketPathHint": "Lascia vuoto per il rilevamento automatico dalla variabile d'ambiente SSH_AUTH_SOCK o inserisci un percorso socket personalizzato (es. /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Seleziona dispositivo Tailscale", "tailscaleDeviceSelectPlaceholder": "Seleziona un dispositivo...", - "tailscaleNoApiKey": "Nessuna chiave API di Tailscale configurata. Aggiungine una nelle Impostazioni di amministrazione per abilitare il rilevamento dei dispositivi.", - "tailscaleDocsLink": "Visualizza i documenti", - "tailscaleLoadingDevices": "Caricamento dispositivi in corso...", - "tailscaleNoDevices": "Nessun dispositivo trovato nella tua rete di tracciamento.", - "tailscaleDeviceAutoFill": "Selezionando un dispositivo, l'indirizzo IP dell'host verrà compilato automaticamente.", - "forceKeyboardInteractiveLabel": "Forza l'interazione con la tastiera", - "forceKeyboardInteractiveShortDesc": "Forza l'inserimento manuale della password anche se sono presenti le chiavi", + "tailscaleNoApiKey": "Nessuna chiave API Tailscale configurata. Aggiungine una in Impostazioni amministratore per abilitare il rilevamento dispositivi.", + "tailscaleDocsLink": "Visualizza documentazione", + "tailscaleLoadingDevices": "Caricamento dispositivi...", + "tailscaleNoDevices": "Nessun dispositivo trovato nel tuo tailnet.", + "tailscaleDeviceAutoFill": "Selezionando un dispositivo l'indirizzo IP dell'host verrà compilato automaticamente.", + "forceKeyboardInteractiveLabel": "Forza Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Forza l'inserimento manuale della password anche se sono presenti chiavi", "allowLegacyAlgorithmsLabel": "Consenti algoritmi legacy", - "allowLegacyAlgorithmsDesc": "Abilita gli algoritmi SSH obsoleti (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) per le connessioni a dispositivi obsoleti che non possono essere aggiornati.", - "insecure": "Insicuro", + "allowLegacyAlgorithmsDesc": "Abilita algoritmi SSH deprecati (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) per connessioni a dispositivi obsoleti che non possono essere aggiornati.", + "insecure": "Non sicuro", "terminalAppearance": "Aspetto del terminale", - "colorTheme": "Tema di colori", + "colorTheme": "Tema colore", "fontFamilyLabel": "Famiglia di caratteri", - "fontSizeLabel": "Dimensione del carattere", - "cursorStyleLabel": "Stile del cursore", - "letterSpacingPx": "Spaziatura tra le lettere (px)", - "lineHeightLabel": "Altezza linea", - "bellStyleLabel": "Stile campana", - "backspaceModeLabel": "Modalità Backspace", - "cursorBlinking": "Lampeggio del cursore", + "fontSizeLabel": "Dimensione carattere", + "cursorStyleLabel": "Stile cursore", + "letterSpacingPx": "Spaziatura caratteri (px)", + "lineHeightLabel": "Interlinea", + "bellStyleLabel": "Stile segnale acustico", + "backspaceModeLabel": "Modalità backspace", + "cursorBlinking": "Lampeggiamento cursore", "cursorBlinkingDesc": "Abilita l'animazione lampeggiante per il cursore del terminale", - "rightClickSelectsWordLabel": "Fare clic con il pulsante destro del mouse e selezionare Word", - "rightClickSelectsWordShortDesc": "Seleziona la parola sotto il cursore facendo clic con il pulsante destro del mouse", - "backgroundImageLabel": "URL dell'immagine di sfondo", - "backgroundImageDesc": "URL facoltativo per l'immagine di sfondo del terminale", - "backgroundImageOpacityLabel": "Opacità dell'immagine di sfondo", + "rightClickSelectsWordLabel": "Clic destro seleziona la parola", + "rightClickSelectsWordShortDesc": "Seleziona la parola sotto il cursore con clic destro", + "backgroundImageLabel": "URL immagine di sfondo", + "backgroundImageDesc": "URL opzionale per un'immagine di sfondo del terminale", + "backgroundImageOpacityLabel": "Opacità immagine di sfondo", "customThemeColors": "Colori personalizzati", "customThemeBackground": "Sfondo", "customThemeForeground": "Primo piano", "customThemeCursor": "Cursore", - "customThemeCursorAccent": "Accento del cursore", + "customThemeCursorAccent": "Accento cursore", "customThemeSelection": "Selezione", "customThemeAnsiColors": "Colori ANSI", "customThemeBlack": "Nero", @@ -754,435 +799,456 @@ "customThemeCyan": "Ciano", "customThemeWhite": "Bianco", "customThemeBrightBlack": "Nero brillante", - "customThemeBrightRed": "Rosso acceso", + "customThemeBrightRed": "Rosso brillante", "customThemeBrightGreen": "Verde brillante", "customThemeBrightYellow": "Giallo brillante", "customThemeBrightBlue": "Blu brillante", "customThemeBrightMagenta": "Magenta brillante", "customThemeBrightCyan": "Ciano brillante", "customThemeBrightWhite": "Bianco brillante", - "customThemeResetTooltip": "Ripristina le impostazioni predefinite", - "syntaxHighlightingLabel": "Evidenziazione della sintassi", - "syntaxHighlightingDesc": "Colora l'output del terminale (errori, percorsi, indirizzi IP, timestamp)", - "syntaxHighlightingCategories": "Categorie in evidenza", + "customThemeResetTooltip": "Ripristina predefiniti", + "savedThemesLabel": "Temi salvati", + "saveAsGlobalTheme": "Salva come tema globale", + "saveGlobalThemeNamePrompt": "Inserisci un nome per questo tema", + "saveGlobalThemeSuccess": "Tema salvato", + "saveGlobalThemeError": "Impossibile salvare il tema", + "applyGlobalThemeTooltip": "Applica questo tema", + "deleteGlobalThemeTooltip": "Elimina questo tema", + "noSavedThemes": "Nessun tema salvato al momento", + "syntaxHighlightingLabel": "Evidenziazione sintassi", + "syntaxHighlightingDesc": "Colora l'output del terminale (errori, percorsi, IP, timestamp)", + "syntaxHighlightingCategories": "Categorie di evidenziazione", "syntaxHighlightingCategoriesDesc": "Scegli quali tipi di contenuto colorare", - "syntaxCategoryLogLevels": "Livelli di registro", - "syntaxCategoryLogLevelsDesc": "errore, avviso, informazioni, debug, fatale", - "syntaxCategoryPaths": "Percorsi dei file", + "syntaxCategoryLogLevels": "Livelli di log", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "Percorsi file", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", - "syntaxCategoryTimestamps": "Indicazioni temporali", - "syntaxCategoryTimestampsDesc": "[12:34:56], 15/01/2024", + "syntaxCategoryTimestamps": "Timestamp", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "Indirizzi IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "Numeri etichettati", - "syntaxCategoryNumbersDesc": "porta 8080, uscita 1, stato 404", - "behaviorAndAdvanced": "Comportamento e Avanzato", + "syntaxCategoryNumbers": "Numeri con etichetta", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Comportamento e avanzate", "scrollbackBufferLabel": "Buffer di scorrimento", "scrollbackMaxLines": "Numero massimo di righe conservate nella cronologia", - "sshAgentForwardingLabel": "Inoltro dell'agente SSH", - "sshAgentForwardingShortDesc": "Passa le tue chiavi SSH locali a questo host", - "useSSHTitleLabel": "Utilizzare il titolo della finestra SSH", - "useSSHTitleDesc": "Aggiorna il titolo della scheda utilizzando il titolo della finestra della shell anziché il nome host.", - "enableAutoMosh": "Attiva Auto-Mosh", - "enableAutoMoshDesc": "Se disponibile, preferisco Mosh a SSH.", + "sshAgentForwardingLabel": "Inoltro agente SSH", + "sshAgentForwardingShortDesc": "Inoltra le tue chiavi SSH locali a questo host", + "useSSHTitleLabel": "Usa il titolo della finestra SSH", + "useSSHTitleDesc": "Aggiorna il titolo della scheda dal titolo della finestra del terminale invece del nome host", + "enableAutoMosh": "Abilita Auto-Mosh", + "enableAutoMoshDesc": "Preferisci Mosh rispetto a SSH se disponibile", "enableAutoTmux": "Abilita Auto-Tmux", - "enableAutoTmuxDesc": "Avviare o collegarsi automaticamente alla sessione tmux", - "enableSessionLogging": "Registrazione della sessione", - "enableSessionLoggingDesc": "Registra l'output della sessione del terminale per una successiva analisi.", - "enableCommandHistory": "Storia del comando", + "enableAutoTmuxDesc": "Avvia o collega automaticamente a una sessione tmux", + "enableSessionLogging": "Registrazione sessione", + "enableSessionLoggingDesc": "Registra l'output della sessione del terminale per la revisione successiva", + "allowSessionSharing": "Consenti la condivisione della sessione", + "allowSessionSharingDesc": "Consenti la condivisione delle sessioni live su questo host tramite link o con altri utenti", + "enableCommandHistory": "Cronologia comandi", "enableCommandHistoryDesc": "Registra i comandi eseguiti in questo terminale per la cronologia e il completamento automatico", - "linkClickBehaviorLabel": "Comportamento al clic sul collegamento", - "linkClickBehaviorDesc": "Controlla cosa succede quando fai clic su un collegamento nel terminale. Usa 'Predefinito' per seguire l'impostazione a livello di applicazione.", - "linkClickBehaviorDefault": "Predefinito (segui le impostazioni dell'app)", - "linkClickBehaviorConfirm": "Mostra il popup per aprire o copiare", + "linkClickBehaviorLabel": "Comportamento al clic del link", + "linkClickBehaviorDesc": "Controlla cosa succede quando fai clic su un link nel terminale. Usa 'Predefinito' per seguire l'impostazione a livello di app.", + "linkClickBehaviorDefault": "Predefinito (segui impostazione app)", + "linkClickBehaviorConfirm": "Mostra popup per aprire o copiare", "linkClickBehaviorDirect": "Apri direttamente", - "sudoPasswordAutoFillLabel": "Compilazione automatica della password di Sudo", - "sudoPasswordAutoFillShortDesc": "Fornisci automaticamente la password di sudo quando richiesto", - "sudoPasswordAutoFillDesc": "Memorizza una password sudo in modo che le metriche dell'host, i prompt del terminale e altri elementi possano eseguire automaticamente comandi con privilegi elevati.", - "sudoPasswordLabel": "Password Sudo", - "environmentVariablesLabel": "Variabili ambientali", + "sudoPasswordAutoFillLabel": "Compilazione automatica password sudo", + "sudoPasswordAutoFillShortDesc": "Fornisci automaticamente la password sudo quando richiesta", + "sudoPasswordAutoFillDesc": "Memorizza una password sudo in modo che le metriche dell'host, i prompt del terminale e altro possano eseguire automaticamente comandi privilegiati.", + "sudoPasswordLabel": "Password sudo", + "environmentVariablesLabel": "Variabili d'ambiente", "addVariableBtn": "Aggiungi variabile", "noEnvVars": "Nessuna variabile d'ambiente configurata.", - "fastScrollModifierLabel": "Modificatore di scorrimento veloce", - "fastScrollSensitivityLabel": "Sensibilità di scorrimento rapida", + "fastScrollModifierLabel": "Modificatore scorrimento veloce", + "fastScrollSensitivityLabel": "Sensibilità scorrimento veloce", "moshCommandLabel": "Comando Mosh", - "startupSnippetLabel": "Snippet di avvio", - "keepaliveIntervalLabel": "Intervallo di keep-alive (secondi)", - "maxKeepaliveMisses": "Max Keepalive sbaglia", - "tunnelSettings": "Impostazioni del tunnel", - "enableTunneling": "Abilita il tunneling", - "enableTunnelingDesc": "Abilita la funzionalità del tunnel SSH per questo host", + "startupSnippetLabel": "Frammento di avvio", + "keepaliveIntervalLabel": "Intervallo keepalive (secondi)", + "maxKeepaliveMisses": "Numero massimo di mancate keepalive", + "tunnelSettings": "Impostazioni tunnel", + "enableTunneling": "Abilita tunneling", + "enableTunnelingDesc": "Abilita la funzionalità di tunnel SSH per questo host", "serverTunnelsSection": "Tunnel server", "addTunnelBtn": "Aggiungi tunnel", "noTunnelsConfigured": "Nessun tunnel configurato.", "tunnelLabel": "Tunnel {{number}}", "tunnelType": "Tipo di tunnel", - "tunnelModeLocalDesc": "Inoltra una porta locale a una porta sul server remoto (o su un host raggiungibile da esso).", - "tunnelModeRemoteDesc": "Inoltra una porta sul server remoto a una porta locale sulla tua macchina.", - "tunnelModeDynamicDesc": "Crea un proxy SOCKS5 su una porta locale per l'inoltro dinamico delle porte.", + "tunnelModeLocalDesc": "Inoltra una porta locale a una porta sul server remoto (o a un host raggiungibile da esso).", + "tunnelModeRemoteDesc": "Inoltra una porta sul server remoto verso una porta locale sulla tua macchina.", + "tunnelModeDynamicDesc": "Crea un proxy SOCKS5 su una porta locale per il port forwarding dinamico.", "sameHost": "Questo host (tunnel diretto)", - "endpointHost": "Host dell'endpoint", + "endpointHost": "Host di destinazione", "endpointHostPlaceholder": "Stesso host, host SSH o host/IP raggiungibile", - "endpointPort": "Porta dell'endpoint", - "bindHost": "Bind Host", + "endpointPort": "Porta di destinazione", + "bindHost": "Host di bind", "sourcePort": "Porta sorgente", - "maxRetries": "Numero massimo di tentativi", - "retryIntervalS": "Intervallo di riprova (s)", + "maxRetries": "Tentativi massimi", + "retryIntervalS": "Intervallo tentativi (s)", "autoStartLabel": "Avvio automatico", - "autoStartDesc": "Connetti automaticamente questo tunnel all'avvio dell'host.", - "tunnelConnecting": "Tunnel di collegamento...", + "autoStartDesc": "Connetti automaticamente questo tunnel all'avvio dell'host", + "tunnelConnecting": "Connessione tunnel in corso...", "tunnelDisconnected": "Tunnel disconnesso", - "failedToConnectTunnel": "Impossibile connettersi", - "failedToDisconnectTunnel": "Impossibile disconnettersi", + "failedToConnectTunnel": "Connessione fallita", + "failedToDisconnectTunnel": "Disconnessione fallita", "dockerIntegration": "Integrazione Docker", "enableDockerMonitor": "Abilita Docker", - "enableDockerMonitorDesc": "Monitora e gestisci i container su questo host tramite Docker.", - "containerRuntime": "Runtime del contenitore", - "containerRuntimeDesc": "Scegli l'interfaccia a riga di comando (CLI) utilizzata per la gestione dei container su questo host.", + "enableDockerMonitorDesc": "Monitora e gestisci i container su questo host tramite Docker", + "containerRuntime": "Runtime container", + "containerRuntimeDesc": "Scegli la CLI utilizzata per la gestione dei container su questo host", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "Abilita Tmux Monitor", - "enableTmuxMonitorDesc": "Mostra questo host nel Tmux Monitor e aggiungi le sue azioni tmux alla barra laterale", + "enableTmuxMonitor": "Abilita Monitor Tmux", + "enableTmuxMonitorDesc": "Mostra questo host nel Monitor Tmux e aggiungi le sue azioni tmux alla barra laterale", "tabProxmox": "Proxmox", - "proxmoxIntegration": "Integrazione con Proxmox", + "proxmoxIntegration": "Integrazione Proxmox", "enableProxmox": "Abilita Proxmox", - "enableProxmoxDesc": "Contrassegna questo host come nodo Proxmox. Consente il rilevamento e l'importazione diretta delle macchine virtuali da questo host.", + "enableProxmoxDesc": "Contrassegna questo host come nodo Proxmox. Abilita il discovery e l'importazione di guest direttamente da questo host.", "proxmoxDefaultAuthType": "Tipo di autenticazione predefinito", - "proxmoxDefaultAuthTypeDesc": "Metodo di autenticazione applicato agli host guest importati. Scegli il tipo di autenticazione che corrisponde al modo in cui ti connetti ai tuoi ospiti.", + "proxmoxDefaultAuthTypeDesc": "Metodo di autenticazione applicato agli host guest importati. Scegli il tipo di autenticazione corrispondente a come ti connetti ai tuoi guest.", "authTypePassword": "Password", "authTypeKey": "Chiave SSH", - "authTypeCredential": "Credenziali", + "authTypeCredential": "Credenziale", "authTypeOpkssh": "OPKSSH", "authTypeNone": "Nessuno", - "proxmoxDefaultCredential": "Credenziali predefinite", - "proxmoxDefaultCredentialDesc": "Credenziali utilizzate per gli host guest importati quando il tipo di autenticazione è impostato su Credenziali.", - "proxmoxWindowsDetection": "Rilevamento di Windows/RDP", - "proxmoxWindowsDetectionDesc": "Modelli di nomi separati da virgole che attivano RDP anziché SSH (senza distinzione tra maiuscole e minuscole)", + "proxmoxDefaultCredential": "Credenziale predefinita", + "proxmoxDefaultCredentialDesc": "Credenziale utilizzata per gli host guest importati quando il tipo di autenticazione è impostato su Credenziale.", + "proxmoxWindowsDetection": "Rilevamento Windows / RDP", + "proxmoxWindowsDetectionDesc": "Elenco di pattern di nomi separati da virgole che attivano RDP invece di SSH (case‑insensitive)", "proxmoxDockerDetection": "Rilevamento Docker", - "proxmoxDockerDetectionDesc": "Modelli di nomi separati da virgole che consentono a Docker di abbinare gli ospiti", + "proxmoxDockerDetectionDesc": "Elenco di pattern di nomi separati da virgole che abilitano Docker per i guest corrispondenti", "proxmoxPreferredRanges": "Intervalli IP preferiti", - "proxmoxPreferredRangesDesc": "Prefissi separati da virgole in ordine di priorità per la selezione dell'indirizzo IP quando una macchina virtuale dispone di più interfacce.", - "proxmoxAutoSync": "Sincronizzazione automatica degli ospiti", - "proxmoxAutoSyncDesc": "Rileva periodicamente questo nodo Proxmox e crea o aggiorna gli host guest importati mentre la tua sessione è sbloccata.", + "proxmoxPreferredRangesDesc": "Prefissi separati da virgole in ordine di priorità per la selezione dell'IP quando un guest ha più interfacce", + "proxmoxAutoSync": "Sincronizzazione automatica dei guest", + "proxmoxAutoSyncDesc": "Scopri periodicamente questo nodo Proxmox e crea o aggiorna gli host guest importati mentre la sessione è sbloccata.", "proxmoxSyncInterval": "Intervallo di sincronizzazione (minuti)", - "proxmoxSyncIntervalDesc": "Minimo 5 minuti. Lo scheduler salta le sessioni utente bloccate.", - "proxmoxMarkMissing": "Mark Ospiti mancanti", - "proxmoxMarkMissingDesc": "Aggiungi il tag proxmox-missing quando una macchina virtuale precedentemente importata scompare, invece di eliminarla.", + "proxmoxSyncIntervalDesc": "Minimo 5 minuti. La pianificazione salta le sessioni utente bloccate.", + "proxmoxMarkMissing": "Segna i guest mancanti", + "proxmoxMarkMissingDesc": "Aggiungi un tag proxmox-missing quando un guest precedentemente importato scompare invece di eliminarlo.", "proxmoxLastSync": "Ultima sincronizzazione", "proxmoxLastSyncNever": "Non ancora sincronizzato", - "proxmoxLastSyncNoResult": "Nessun risultato disponibile", - "proxmoxLastSyncSummary": "{{created}} creato, {{updated}} aggiornato, {{markedMissing}} mancante, {{skipped}} saltato", + "proxmoxLastSyncNoResult": "Nessun dettaglio del risultato", + "proxmoxLastSyncSummary": "{{created}} creati, {{updated}} aggiornati, {{markedMissing}} mancanti, {{skipped}} saltati", "proxmoxLastSyncStatus": { - "success": "Successo", - "error": "Fallito", - "pending": "In attesa di" + "success": "Riuscita", + "error": "Fallita", + "pending": "In attesa" }, "proxmoxDiscoverAction": "Scopri e importa guest Proxmox", "proxmoxImportTitle": "Importa da Proxmox", "proxmoxSelectHost": "Seleziona un host Proxmox…", - "proxmoxDiscover": "Scoprire", - "proxmoxDiscovering": "Scoprire…", - "proxmoxDiscoverGuests": "Scopri gli ospiti", - "proxmoxGuestsSelected_one": "{{count}} ospite — {{selected}} selezionato", - "proxmoxGuestsSelected_other": "{{count}} ospiti — {{selected}} selezionati", + "proxmoxDiscover": "Scopri", + "proxmoxDiscovering": "Scoperta in corso…", + "proxmoxDiscoverGuests": "Scopri guest", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selezionato", + "proxmoxGuestsSelected_other": "{{count}} guest — {{selected}} selezionati", "proxmoxSelectAll": "Seleziona tutto", "proxmoxDeselectAll": "Deseleziona tutto", - "proxmoxNoGuests": "Nessun ospite trovato su questo nodo Proxmox.", + "proxmoxNoGuests": "Nessun guest trovato su questo nodo Proxmox.", "proxmoxImportButton_one": "Importa {{count}} host", "proxmoxImportButton_other": "Importa {{count}} host", - "proxmoxResultImported": "{{count}} importato", - "proxmoxResultUpdated": "{{count}} aggiornato", - "proxmoxResultFailed": "{{count}} fallito", + "proxmoxResultImported": "{{count}} importati", + "proxmoxResultUpdated": "{{count}} aggiornati", + "proxmoxResultFailed": "{{count}} falliti", + "proxmoxResultSkippedNoIp": "{{count}} saltato (nessun IP trovato)", "proxmoxImportComplete": "Importazione Proxmox completata: {{summary}}", - "proxmoxDiscoveryFailed": "Scoperta fallita", - "proxmoxImportFailed": "Lavoro fallito", - "enableFileManagerMonitor": "Abilita Gestione file", - "enableFileManagerMonitorDesc": "Esplora e gestisci i file su questo host tramite SFTP", - "scpLegacyLabel": "Modalità Legacy di SCP", - "scpLegacyDesc": "Utilizzare il trasferimento file legacy per i server che non supportano il sottosistema SFTP (ad esempio, server SSH integrati o minimali).", + "proxmoxDiscoveryFailed": "Rilevamento non riuscito", + "proxmoxImportFailed": "Importazione non riuscita", + "enableFileManagerMonitor": "Abilita File Manager", + "enableFileManagerMonitorDesc": "Sfoglia e gestisci i file su questo host tramite SFTP", + "scpLegacyLabel": "Modalità SCP legacy", + "scpLegacyDesc": "Usa il trasferimento file legacy per server che non supportano il sottosistema SFTP (ad es. server SSH embedded o minimali).", "defaultPathLabel": "Percorso predefinito", "fileManagerPathHint": "La directory da aprire all'avvio del file manager per questo host.", - "statusChecksLabel": "Verifiche di stato", - "enableStatusChecks": "Abilita i controlli di stato", - "enableStatusChecksDesc": "Effettua periodicamente un ping a questo host per verificarne la disponibilità.", - "useGlobalInterval": "Utilizzare l'intervallo globale", - "useGlobalIntervalDesc": "Sovrascrivi con l'intervallo di controllo dello stato a livello di server", + "statusChecksLabel": "Controlli di stato", + "enableStatusChecks": "Abilita controlli di stato", + "enableStatusChecksDesc": "Esegui periodicamente il ping di questo host per verificarne la disponibilità", + "useGlobalInterval": "Usa intervallo globale", + "useGlobalIntervalDesc": "Sovrascrivi con l'intervallo di controllo dello stato del server", "checkIntervalS": "Intervallo di controllo (s)", - "checkIntervalDesc": "Secondi tra ogni ping di connettività", - "metricsCollectionLabel": "Raccolta di metriche", - "enableMetricsLabel": "Abilita le metriche", - "enableMetricsDesc": "Raccogli i dati relativi a CPU, RAM, disco e altre metriche da questo host", - "useGlobalMetrics": "Utilizzare l'intervallo globale", - "useGlobalMetricsDesc": "Sovrascrivi con l'intervallo delle metriche a livello di server", - "metricsIntervalS": "Intervallo delle metriche (s)", - "metricsIntervalDesc2": "Secondi tra le istantanee metriche", + "checkIntervalDesc": "Secondi tra un ping di connettività e l'altro", + "metricsCollectionLabel": "Raccolta metriche", + "enableMetricsLabel": "Abilita metriche", + "enableMetricsDesc": "Raccogli CPU, RAM, disco e altre metriche da questo host", + "useGlobalMetrics": "Usa intervallo globale", + "useGlobalMetricsDesc": "Sovrascrivi con l'intervallo delle metriche del server", + "metricsIntervalS": "Intervallo metriche (s)", + "metricsIntervalDesc2": "Secondi tra uno snapshot delle metriche e l'altro", "visibleWidgets": "Widget visibili", - "widgetsMovedToHostMetrics": "Ora è possibile aggiungere, disporre e ridimensionare le schede direttamente nella scheda Metriche host. Apri le Metriche host per questo host e utilizza Personalizza per scegliere quali schede visualizzare.", - "cpuUsageLabel": "Utilizzo della CPU", - "cpuUsageDesc": "Percentuale CPU, carico medio, grafico sparkline", - "memoryLabel": "Utilizzo della memoria", - "memoryDesc": "Utilizzo della RAM, swap, cache", - "storageLabel": "Utilizzo del disco", - "storageDesc": "Utilizzo del disco per punto di montaggio", - "networkLabel": "Interfacce di rete", - "networkDesc": "Elenco delle interfacce e larghezza di banda", - "uptimeLabel": "Tempo di attività", - "uptimeDesc": "tempo di attività del sistema e tempo di avvio", - "systemInfoLabel": "Informazioni di sistema", - "systemInfoDesc": "Sistema operativo, kernel, nome host, architettura", - "recentLoginsLabel": "Accessi recenti", - "recentLoginsDesc": "Eventi di accesso riusciti e non riusciti", - "topProcessesLabel": "Processi principali", + "widgetsMovedToHostMetrics": "Le schede ora vengono aggiunte, disposte e ridimensionate direttamente nella scheda Metriche host. Apri Metriche host per questo host e usa Personalizza per scegliere quali schede mostrare.", + "cpuUsageLabel": "Utilizzo CPU", + "cpuUsageDesc": "Percentuale CPU, medie di carico, grafico sparkline", + "memoryLabel": "Utilizzo memoria", + "memoryDesc": "Utilizzo RAM, swap, cache", + "storageLabel": "Utilizzo disco", + "storageDesc": "Utilizzo del disco per punto di mount", + "networkLabel": "Interfacce di Rete", + "networkDesc": "Elenco interfacce e larghezza di banda", + "uptimeLabel": "Uptime", + "uptimeDesc": "Uptime di sistema e ora di avvio", + "systemInfoLabel": "Informazioni di Sistema", + "systemInfoDesc": "SO, kernel, hostname, architettura", + "recentLoginsLabel": "Accessi Recenti", + "recentLoginsDesc": "Eventi di accesso riusciti e falliti", + "topProcessesLabel": "Processi più attivi", "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Porte di ascolto", + "listeningPortsLabel": "Porte in Ascolto", "listeningPortsDesc": "Porte aperte con processo e stato", "firewallLabel": "Firewall", - "firewallDesc": "Stato di Firewall, AppArmor e SELinux", - "quickActionsLabel": "Azioni rapide", - "quickActionsToolbar": "Le azioni rapide vengono visualizzate come pulsanti nella barra degli strumenti di Host Metrics per l'esecuzione dei comandi con un solo clic.", - "noQuickActions": "Ancora nessuna azione immediata.", - "buttonLabel": "Etichetta del pulsante", - "selectSnippetPlaceholder": "Seleziona il frammento...", - "addActionBtn": "Aggiungi azione", + "firewallDesc": "Stato firewall, AppArmor, SELinux", + "quickActionsLabel": "Azioni Rapide", + "quickActionsToolbar": "Le azioni rapide appaiono come pulsanti nella barra delle metriche host per l'esecuzione di comandi con un clic.", + "noQuickActions": "Nessuna azione rapida ancora.", + "buttonLabel": "Etichetta pulsante", + "selectSnippetPlaceholder": "Seleziona snippet...", + "addActionBtn": "Aggiungi Azione", "hostSharedSuccessfully": "Host condiviso con successo", - "failedToShareHost": "Impossibile condividere l'host", + "failedToShareHost": "Condivisione host fallita", "accessRevoked": "Accesso revocato", - "failedToRevokeAccess": "Impossibile revocare l'accesso", - "cancelBtn": "Cancellare", - "savingBtn": "Risparmio...", - "addHostBtn": "Aggiungi host", + "failedToRevokeAccess": "Revoca accesso fallita", + "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", - "credentialNameRequired": "Inserisci un nome per le credenziali", + "failedToSave": "Salvataggio host fallito", + "credentialUpdated": "Credenziale aggiornata", + "credentialCreated": "Credenziale creata", + "failedToSaveCredential": "Salvataggio credenziale fallito", + "credentialNameRequired": "Inserisci un nome per la credenziale", "credentialAuthRequired": "Aggiungi una password, una chiave SSH o entrambe", + "createCredentialFromHostBtn": "Crea credenziali", + "createCredentialFromHostTitle": "Creazione delle credenziali dall'host", + "createCredentialFromHostDesc": "Crea una voce di credenziali riutilizzabile e condivisibile precompilata con il nome utente, la password e/o la chiave SSH correnti di questo host.", "backToHosts": "Torna agli host", "backToCredentials": "Torna alle credenziali", - "pinned": "Fissato", + "pinned": "Fissati", "noHostsFound": "Nessun host trovato", - "tryDifferentTerm": "Prova un termine diverso", + "tryDifferentTerm": "Prova con un termine diverso", "addFirstHost": "Aggiungi il tuo primo host per iniziare", - "noCredentialsFound": "Credenziali non trovate", - "addCredentialBtn": "Aggiungi credenziali", - "updateCredentialBtn": "Aggiorna le credenziali", - "features": "Caratteristiche", + "noCredentialsFound": "Nessuna credenziale trovata", + "addCredentialBtn": "Aggiungi credenziale", + "updateCredentialBtn": "Aggiorna credenziale", + "features": "Funzionalità", "noFolder": "(Nessuna cartella)", - "deleteSelected": "Eliminare", - "exitSelection": "Selezione di uscita", - "importSkip": "Importa (salta i file esistenti)", + "deleteSelected": "Elimina", + "exitSelection": "Esci dalla selezione", + "importSkip": "Importa (salta esistenti)", "importOverwrite": "Importa (sovrascrivi)", - "collapseBtn": "Crollo", - "importExportBtn": "Importazione/Esportazione", - "hostStatusesRefreshed": "Stato degli host aggiornato", + "collapseBtn": "Comprimi", + "importExportBtn": "Importa / Esporta", + "hostStatusesRefreshed": "Stati degli host aggiornati", "failedToRefreshHosts": "Impossibile aggiornare gli host", - "movedHostTo": "Spostato {{host}} in \"{{folder}}\"", + "movedHostTo": "{{host}} spostato in \"{{folder}}\"", "failedToMoveHost": "Impossibile spostare l'host", "folderRenamedTo": "Cartella rinominata in \"{{name}}\"", - "deletedFolder": "Cartella eliminata \"{{name}}\"", + "deletedFolder": "Cartella \"{{name}}\" eliminata", "failedToDeleteFolder": "Impossibile eliminare la cartella", - "deleteAllInFolder": "Eliminare tutti gli host in \"{{name}}\"? Questa operazione non può essere annullata.", + "deleteAllInFolder": "Eliminare tutti gli host in \"{{name}}\"? Questa azione non può essere annullata.", "folderPickerPlaceholder": "Nessuna cartella", - "folderPickerSearch": "Cerca o crea (usa / per le sottocartelle)...", + "folderPickerSearch": "Cerca o crea (usa / per sottocartelle)...", "folderPickerNone": "Nessuna cartella", "folderPickerCreate": "Crea \"{{path}}\"", "folderPickerEmpty": "Nessuna cartella corrispondente", "newFolder": "Nuova cartella", "createFolderTitle": "Crea cartella", "editFolderTitle": "Modifica cartella", - "folderDialogDescription": "Scegli un nome, un colore e un'icona. Usa la barra / per annidare le cartelle.", + "folderDialogDescription": "Scegli un nome, un colore e un'icona. Usa / per annidare le cartelle.", "folderNameLabel": "Nome della cartella", - "folderNamePlaceholder": "ad esempio Produzione/Web", - "folderNestingHint": "Utilizzare la barra / per separare i livelli e creare cartelle annidate.", + "folderNamePlaceholder": "es. Produzione/Web", + "folderNestingHint": "Usa / per separare i livelli e creare cartelle nidificate.", "folderColor": "Colore", "folderIcon": "Icona", + "folderCredential": "Credenziali", + "folderCredentialNone": "Nessuna credenziale assegnata", + "folderCredentialHint": "Gli host in questa cartella che utilizzano l'autenticazione \"Credenziali memorizzate\" senza aver selezionato le proprie credenziali erediteranno questa impostazione.", "folderPreview": "Anteprima", - "folderNameFallback": "Cartella senza titolo", + "folderNameFallback": "Cartella senza nome", "createFolderButton": "Crea cartella", "saveFolderButton": "Salva cartella", - "cancel": "Cancellare", - "iconSearchPlaceholder": "Icone di ricerca...", + "cancel": "Annulla", + "iconSearchPlaceholder": "Cerca icone...", "openAllSessions": "Apri tutte le sessioni", "editFolder": "Modifica cartella", - "deleteFolder": "Elimina la cartella", + "deleteFolder": "Elimina cartella", "folderSaved": "Cartella salvata", - "failedToSaveFolder": "Impossibile salvare la cartella", - "folderDeleted": "Cartella eliminata \"{{name}}\"", - "deleteFolderConfirm": "Eliminare \"{{name}}\" e il/i suo/i host {{count}} ? Questa operazione non può essere annullata.", - "failedToMoveHosts": "Impossibile spostare gli host", + "failedToSaveFolder": "Salvataggio cartella non riuscito", + "folderDeleted": "Cartella \"{{name}}\" eliminata", + "deleteFolderConfirm": "Eliminare \"{{name}}\" e i suoi {{count}} host? Operazione irreversibile.", + "failedToMoveHosts": "Spostamento host non riuscito", "expandAll": "Espandi tutte le cartelle", "collapseAll": "Comprimi tutte le cartelle", - "moreActions": "Di più", + "moreActions": "Altro", "groupBy": "Raggruppa per", "GroupByFolder": "Cartella", - "GroupByTag": "Etichetta", + "GroupByTag": "Tag", "GroupByStatus": "Stato", "GroupByProtocol": "Protocollo", - "GroupByAuth": "Tipo di autorizzazione", - "groupUngrouped": "Non raggruppato", - "deletedHost": "Eliminato {{name}}", + "GroupByAuth": "Tipo autenticazione", + "groupUngrouped": "Non raggruppati", + "deletedHost": "{{name}} eliminato", "copiedToClipboard": "Copiato negli appunti", - "terminalUrlCopied": "URL del terminale copiato", - "fileManagerUrlCopied": "URL del gestore file copiato", - "tunnelUrlCopied": "URL del tunnel copiato", - "dockerUrlCopied": "URL di Docker copiato", - "hostMetricsUrlCopied": "URL delle metriche dell'host copiato", - "tmuxMonitorUrlCopied": "URL di Tmux Monitor copiato", + "terminalUrlCopied": "URL terminale copiato", + "fileManagerUrlCopied": "URL file manager copiato", + "tunnelUrlCopied": "URL tunnel copiato", + "dockerUrlCopied": "URL Docker copiato", + "hostMetricsUrlCopied": "URL metriche host copiato", + "tmuxMonitorUrlCopied": "URL Tmux Monitor copiato", "rdpUrlCopied": "URL RDP copiato", "vncUrlCopied": "URL VNC copiato", "telnetUrlCopied": "URL Telnet copiato", - "remoteDesktopUrlCopied": "URL del desktop remoto copiato", - "expandActions": "Espandi le azioni", - "collapseActions": "Comprimi le azioni", + "remoteDesktopUrlCopied": "URL Desktop remoto copiato", + "expandActions": "Espandi azioni", + "collapseActions": "Comprimi azioni", "wakeOnLanAction": "Wake on LAN", "wakeOnLanSuccess": "Pacchetto magico inviato a {{name}}", - "wakeOnLanError": "Invio del pacchetto magico non riuscito", - "cloneHostAction": "Clone Host", + "wakeOnLanError": "Invio pacchetto magico fallito", + "cloneHostAction": "Clona host", "copyAddress": "Copia indirizzo", - "copyLink": "Copia collegamento", - "copyTerminalUrlAction": "Copia URL del terminale", - "copyFileManagerUrlAction": "Copia URL di Gestione file", - "copyTunnelUrlAction": "Copia URL del tunnel", - "copyDockerUrlAction": "Copia l'URL di Docker", - "copyHostMetricsUrlAction": "Copia URL delle metriche dell'host", - "copyTmuxMonitorUrlAction": "Copia l'URL di Tmux Monitor", - "copyRdpUrlAction": "Copia l'URL RDP", - "copyVncUrlAction": "Copia l'URL VNC", + "copyLink": "Copia link", + "copyTerminalUrlAction": "Copia URL Terminal", + "copyFileManagerUrlAction": "Copia URL File Manager", + "copyTunnelUrlAction": "Copia URL Tunnel", + "copyDockerUrlAction": "Copia URL Docker", + "copyHostMetricsUrlAction": "Copia URL metriche host", + "copyTmuxMonitorUrlAction": "Copia URL Tmux Monitor", + "copyRdpUrlAction": "Copia URL RDP", + "copyVncUrlAction": "Copia URL VNC", "copyTelnetUrlAction": "Copia URL Telnet", - "copyRemoteDesktopUrlAction": "Copia l'URL di Desktop remoto", - "deleteCredentialConfirm": "Eliminare le credenziali \"{{name}}\"?", - "deletedCredential": "Eliminato {{name}}", - "deploySSHKeyTitle": "Distribuzione della chiave SSH", - "deployingBtn": "Distribuzione in corso...", - "deployBtn": "Distribuisci", - "failedToDeployKey": "Impossibile distribuire la chiave", - "deleteHostsConfirm": "Eliminare {{count}} host{{plural}}? Questa operazione non può essere annullata.", - "movedToRoot": "Spostato nella directory principale", - "enableTerminalFeature": "Terminale di supporto", - "disableTerminalFeature": "Disabilita il terminale", - "enableFilesFeature": "Abilita file", - "disableFilesFeature": "Disabilita i file", - "enableTunnelsFeature": "Abilita i tunnel", - "disableTunnelsFeature": "Disabilitare i tunnel", + "copyRemoteDesktopUrlAction": "Copia URL Desktop remoto", + "deleteCredentialConfirm": "Eliminare credenziale \"{{name}}\"?", + "deletedCredential": "Eliminata credenziale {{name}}", + "deploySSHKeyTitle": "Deploy chiave SSH", + "deployingBtn": "Deploy in corso...", + "deployBtn": "Deploy", + "failedToDeployKey": "Deploy della chiave fallito", + "deleteHostsConfirm": "Eliminare {{count}} host{{plural}}? Operazione irreversibile.", + "movedToRoot": "Spostato nella radice", + "enableTerminalFeature": "Abilita Terminal", + "disableTerminalFeature": "Disabilita Terminal", + "enableFilesFeature": "Abilita File", + "disableFilesFeature": "Disabilita File", + "enableTunnelsFeature": "Abilita tunnel", + "disableTunnelsFeature": "Disabilita tunnel", "enableDockerFeature": "Abilita Docker", "disableDockerFeature": "Disabilita Docker", "enableProxmoxFeature": "Abilita Proxmox", - "disableProxmoxFeature": "Disabilitare Proxmox", + "disableProxmoxFeature": "Disabilita Proxmox", "addTagsPlaceholder": "Aggiungi tag...", "authDetails": "Dettagli di autenticazione", "credType": "Tipo", - "generateKeyPairDesc": "Genera una nuova coppia di chiavi; sia la chiave privata che quella pubblica verranno compilate automaticamente.", - "generatingKey": "Generazione in corso...", + "generateKeyPairDesc": "Genera una nuova coppia di chiavi; le chiavi privata e pubblica verranno inserite automaticamente.", + "generatingKey": "Generazione...", "generateLabel": "Genera {{label}}", - "uploadFileBtn": "Carica il file", - "keyPassphraseOptional": "Frase di accesso chiave (facoltativa)", - "sshPublicKeyOptional": "Chiave pubblica SSH (facoltativa)", + "uploadFileBtn": "Carica file", + "keyPassphraseOptional": "Passphrase della chiave (Opzionale)", + "sshPublicKeyOptional": "Chiave pubblica SSH (Opzionale)", "publicKeyGenerated": "Chiave pubblica generata", - "failedToGeneratePublicKey": "Impossibile ricavare la chiave pubblica", + "failedToGeneratePublicKey": "Impossibile derivare la chiave pubblica", "publicKeyCopied": "Chiave pubblica copiata", - "keyPairGenerated": "{{label}} coppia di chiavi generata", - "failedToGenerateKeyPair": "Impossibile generare la coppia di chiavi", + "keyPairGenerated": "Coppia di chiavi {{label}} generata", + "failedToGenerateKeyPair": "Generazione della coppia di chiavi non riuscita", "searchHostsPlaceholder": "Cerca host, indirizzi, tag…", - "searchCredentialsPlaceholder": "Credenziali di ricerca…", - "refreshBtn": "Rinfrescato", + "searchCredentialsPlaceholder": "Cerca credenziali…", + "refreshBtn": "Aggiorna", "addTag": "Aggiungi tag...", - "deleteConfirmBtn": "Eliminare", - "tunnelRequirementsText": "Il server SSH deve avere GatewayPorts impostato su yes, AllowTcpForwarding su yes e PermitRootLogin impostato su yes nel file /etc/ssh/sshd_config.", + "deleteConfirmBtn": "Elimina", + "tunnelRequirementsText": "Il server SSH deve avere GatewayPorts yes, AllowTcpForwarding yes e PermitRootLogin yes impostati in /etc/ssh/sshd_config.", "deleteHostConfirm": "Eliminare \"{{name}}\"?", - "enableAtLeastOneProtocol": "Abilita almeno uno dei protocolli sopra elencati per configurare le impostazioni di autenticazione e connessione.", - "keyPassphrase": "Frase di accesso chiave", - "connectBtn": "Collegare", + "enableAtLeastOneProtocol": "Abilita almeno un protocollo tra quelli sopra per configurare le impostazioni di autenticazione e connessione.", + "keyPassphrase": "Passphrase della chiave", + "connectBtn": "Connetti", "disconnectBtn": "Disconnetti", "basicInformation": "Informazioni di base", "authDetailsSection": "Dettagli di autenticazione", "credTypeLabel": "Tipo", - "hostsTab": "Ospiti", + "hostsTab": "Host", "credentialsTab": "Credenziali", - "selectMultiple": "Seleziona più", - "selectHosts": "Seleziona gli host", + "selectMultiple": "Selezione multipla", + "selectHosts": "Seleziona host", "connectionLabel": "Connessione", "authenticationLabel": "Autenticazione", "generateKeyPairTitle": "Genera coppia di chiavi", - "generateKeyPairDescription": "Genera una nuova coppia di chiavi; sia la chiave privata che quella pubblica verranno compilate automaticamente.", - "generateFromPrivateKey": "Genera dalla chiave privata", - "refreshBtn2": "Rinfrescato", - "exitSelectionTitle": "Selezione di uscita", + "generateKeyPairDescription": "Genera una nuova coppia di chiavi, le chiavi privata e pubblica verranno inserite automaticamente.", + "generateFromPrivateKey": "Genera da chiave privata", + "refreshBtn2": "Aggiorna", + "exitSelectionTitle": "Esci dalla selezione", "exportAll": "Esporta tutto", - "exportForSharing": "Esporta per la condivisione", + "exportForSharing": "Esporta per condivisione", "addHostBtn2": "Aggiungi host", - "addCredentialBtn2": "Aggiungi credenziali", - "checkingHostStatuses": "Verifica dello stato degli host in corso...", - "pinnedSection": "Fissato", + "addCredentialBtn2": "Aggiungi credenziale", + "checkingHostStatuses": "Verifica degli stati degli host in corso...", + "pinnedSection": "Fissati", "hostsExported": "Host esportati con successo", - "hostsShareExported": "Host condivisibili esportati correttamente", - "exportFailed": "Impossibile esportare gli host", + "hostsShareExported": "Host condivisibili esportati con successo", + "exportFailed": "Esportazione host non riuscita", "sampleDownloaded": "File di esempio scaricato", - "failedToDeleteCredential2": "Impossibile eliminare le credenziali", + "failedToDeleteCredential2": "Eliminazione credenziale non riuscita", "noFolderOption": "(Nessuna cartella)", - "nSelected": "{{count}} selezionato", - "featuresMenu": "Caratteristiche", - "moveMenu": "Mossa", - "connectSelected": "Collegare", - "cancelSelection": "Cancellare", - "deployDialogDesc": "Distribuisci {{name}} nelle authorized_keys di un host.", + "nSelected": "{{count}} selezionati", + "featuresMenu": "Funzionalità", + "moveMenu": "Sposta", + "connectSelected": "Connetti", + "cancelSelection": "Annulla", + "deployDialogDesc": "Distribuisci {{name}} nel file authorized_keys di un host.", "targetHostLabel": "Host di destinazione", "selectHostOption": "Seleziona un host...", "keyDeployedSuccess": "Chiave distribuita con successo", - "failedToDeployKey2": "Impossibile distribuire la chiave", - "deletedCount": "Host eliminati {{count}}", - "failedToDeleteCount": "Impossibile eliminare gli host {{count}}", - "duplicatedHost": "Duplicato \"{{name}}\"", + "failedToDeployKey2": "Distribuzione chiave non riuscita", + "deletedCount": "{{count}} host eliminati", + "failedToDeleteCount": "Eliminazione di {{count}} host non riuscita", + "duplicatedHost": "\"{{name}}\" duplicato", "failedToDuplicateHost": "Impossibile duplicare l'host", - "updatedCount": "Host aggiornati {{count}}", - "friendlyNameLabel": "Nome amichevole", + "updatedCount": "Aggiornati {{count}} host", + "friendlyNameLabel": "Nome descrittivo", "descriptionLabel": "Descrizione", - "loadingHost": "Caricamento host in corso...", - "loadingHosts": "Caricamento host in corso...", - "loadingCredentials": "Caricamento delle credenziali in corso...", + "loadingHost": "Caricamento host...", + "loadingHosts": "Caricamento host...", + "loadingCredentials": "Caricamento credenziali...", "noHostsYet": "Nessun host ancora", - "noHostsMatchSearch": "Nessun host corrisponde alla tua ricerca", + "noHostsMatchSearch": "Nessun host corrisponde alla ricerca", "hostNotFound": "Host non trovato", "searchHosts": "Cerca host...", - "sortHosts": "Ordina gli host", + "sortHosts": "Ordina host", "sortDefault": "Ordine predefinito", "sortNameAsc": "Nome (A → Z)", "sortNameDesc": "Nome (Z → A)", - "sortIpAsc": "Indirizzo IP (ascendente)", - "sortIpDesc": "Indirizzo IP (Descrizione)", - "sortOnlineFirst": "Online First", - "sortOfflineFirst": "Prima offline", - "sortPinnedFirst": "Fissato per primo", - "filterHosts": "Host di filtro", - "filterClearAll": "Filtri trasparenti", + "sortIpAsc": "Indirizzo IP (crescente)", + "sortIpDesc": "Indirizzo IP (decrescente)", + "sortOnlineFirst": "Online per primi", + "sortOfflineFirst": "Offline per primi", + "sortPinnedFirst": "Fissati per primi", + "filterHosts": "Filtra host", + "filterClearAll": "Cancella filtri", "filterStatusGroup": "Stato", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fissato", - "filterAuthGroup": "Tipo di autorizzazione", + "filterPinned": "Fissati", + "filterAuthGroup": "Tipo di autenticazione", "filterAuthPassword": "Password", "filterAuthKey": "Chiave SSH", - "filterAuthCredential": "Credenziali", - "filterAuthNone": "Nessuno", + "filterAuthCredential": "Credenziale", + "filterAuthNone": "Nessuna", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocollo", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Internet", - "filterFeaturesGroup": "Caratteristiche", - "filterFeatureTerminal": "terminale", - "filterFeatureFileManager": "Gestore file", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Funzionalità", + "filterFeatureTerminal": "Terminale", + "filterFeatureFileManager": "Gestione file", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etichette", - "shareHost": "Condividi l'host", + "filterTagsGroup": "Tag", + "shareHost": "Condividi host", "shareHostTitle": "Condividi: {{name}}", + "shareFolder": "Condividi la cartella", + "shareFolderTitle": "Condividi la cartella: {{name}}", + "folderSharedSuccessfully": "Host(i) condivisi {{count}} nella cartella", + "failedToShareFolder": "Impossibile condividere la cartella", "sharing": { - "loadError": "Impossibile caricare i dati di condivisione. Riprova.", + "loadError": "Caricamento dati di condivisione non riuscito. Riprova.", "shareWithSection": "Condividi con", "usersTab": "Utenti", "rolesTab": "Ruoli", @@ -1191,188 +1257,192 @@ "permissionLevelLabel": "Livello di autorizzazione", "levels": { "connect": { - "label": "Collegare", - "description": "Sono consentite solo le seguenti sessioni: terminale, desktop remoto, file manager, tunnel e Docker. Nessun accesso alla configurazione dell'host." + "label": "Connetti", + "description": "Apri solo sessioni: terminale, desktop remoto, gestione file, tunnel e Docker. Nessun accesso alla configurazione dell'host." }, "view": { - "label": "Visualizzazione", - "description": "Connettiti e visualizza la configurazione dell'host. I segreti non vengono mai mostrati." + "label": "Visualizza", + "description": "Connetti, più visualizza la configurazione dell'host. I segreti non vengono mai mostrati." }, "edit": { - "label": "Modificare", - "description": "Visualizza e modifica l'host. I segreti possono essere sostituiti ma non letti; le credenziali assegnate rimangono riservate al proprietario." + "label": "Modifica", + "description": "Visualizza, più modifica l'host. I segreti possono essere sostituiti ma mai letti; le assegnazioni delle credenziali restano solo del proprietario." }, "manage": { - "label": "Maneggio", - "description": "Modifica e condividi l'host con altri, cambia i livelli di autorizzazione e revoca l'accesso." + "label": "Gestisci", + "description": "Modifica, più condividi l'host con altri, modifica i livelli di autorizzazione e revoca l'accesso." } }, - "expiryLabel": "Scadenza dell'accesso", + "expiryLabel": "Scadenza accesso", "expiry": { "never": "Mai", "oneHour": "1 ora", "oneDay": "24 ore", "sevenDays": "7 giorni", "thirtyDays": "30 giorni", - "custom": "Costume" + "custom": "Personalizzato" }, - "customHoursPlaceholder": "Ore rimanenti alla scadenza dell'accesso", - "shareButton": "Condividere", + "customHoursPlaceholder": "Ore fino alla scadenza dell'accesso", + "shareButton": "Condividi", "shareWithCount": "Condividi ({{count}})", - "currentAccess": "Accesso attuale", + "currentAccess": "Accesso corrente", "noAccessEntries": "Questo host non è stato ancora condiviso", + "folderShareSummary": "Condiviso {{shared}} di {{total}} host(i) in questa cartella", "grantedBy": "Concesso da", "expires": "Scade", "expired": "Scaduto", "never": "Mai", - "revoke": "Revocare", + "revoke": "Revoca", "accessUpdated": "Accesso aggiornato", - "accessUpdateFailed": "Impossibile aggiornare l'accesso", + "accessUpdateFailed": "Aggiornamento dell'accesso non riuscito", "sharedBadge": "Condiviso", - "sharedBadgeTooltip": "Condiviso da {{owner}} ({{level}} accesso)", - "viewOnlyBanner": "Questo host è condiviso con te da {{owner}} con accesso in sola visualizzazione. La configurazione è di sola lettura.", - "sharedEditBanner": "Questo host è condiviso con te da {{owner}} con accesso in modifica. Le modifiche si applicano all'host reale; i riferimenti di autenticazione possono essere modificati solo dal proprietario.", - "ownerOnlyControl": "Solo il proprietario dell'host può modificare questo" + "sharedBadgeTooltip": "Condiviso da {{owner}} (accesso {{level}})", + "viewOnlyBanner": "Questo host è condiviso con te da {{owner}} con accesso di sola lettura. La configurazione è di sola lettura.", + "sharedEditBanner": "Questo host è condiviso con te da {{owner}} con accesso di modifica. Le modifiche si applicano all'host reale; i riferimenti di autenticazione possono essere modificati solo dal proprietario.", + "ownerOnlyControl": "Solo il proprietario dell'host può modificare questa impostazione" }, "guac": { "connection": "Connessione", "authentication": "Autenticazione", - "storedCredential": "Credenziali memorizzate", + "storedCredential": "Credenziale memorizzata", "noCredential": "Nessuna credenziale (credenziali dirette di seguito)", "authMethod": "Metodo di autenticazione", - "authTypeDirect": "Diretto", - "authTypeCredential": "Credenziali", + "authTypeDirect": "Diretta", + "authTypeCredential": "Credenziale", + "authTypeNone": "Nessuno", + "authTypeNoneDesc": "Non vengono memorizzate credenziali. Ti verrà richiesto di inserire nome utente e password ogni volta che effettuerai la connessione; questi dati non verranno salvati.", "selectCredential": "Seleziona una credenziale...", "connectionSettings": "Impostazioni di connessione", - "displaySettings": "Impostazioni di visualizzazione", + "displaySettings": "Impostazioni schermo", "audioSettings": "Impostazioni audio", "rdpPerformance": "Prestazioni RDP", - "deviceRedirection": "Reindirizzamento del dispositivo", + "deviceRedirection": "Reindirizzamento dispositivi", "session": "Sessione", "gateway": "Gateway", "remoteApp": "RemoteApp", "clipboard": "Appunti", - "sessionRecording": "Registrazione della sessione", + "sessionRecording": "Registrazione sessione", "wakeOnLan": "Wake-on-LAN", "vncSettings": "Impostazioni VNC", - "terminalSettings": "Impostazioni del terminale", + "terminalSettings": "Impostazioni terminale", "rdpPort": "Porta RDP", "username": "Nome utente", "password": "Password", + "passwordSaved": "Password salvata, digita per modificarla", "domain": "Dominio", "securityMode": "Modalità di sicurezza", - "colorDepth": "Doppio colore", + "colorDepth": "Profondità colore", "width": "Larghezza", "height": "Altezza", "dpi": "DPI", "resizeMethod": "Metodo di ridimensionamento", - "clientName": "Nome del cliente", + "clientName": "Nome client", "initialProgram": "Programma iniziale", "serverLayout": "Layout del server", "timezone": "Fuso orario", - "loadBalanceInfo": "Informazioni sul bilanciamento del carico / Cookie", - "loadBalanceInfoDesc": "Cookie di RD Connection Broker per il bilanciamento del carico della farm RDS (ad esempio tsv://MS Terminal Services Plugin.1.CollectionName)", - "guacdProxy": "guacd Proxy", - "guacdHostname": "guacd Host", - "guacdHostnamePlaceholder": "Impostazione predefinita globale", - "guacdPort": "Porto di Guacd", - "guacdProxyDesc": "Sovrascrivi l'istanza globale di guacd per questa connessione. Lascia vuoto per utilizzare l'istanza di guacd configurata a livello globale.", - "gatewayHostname": "Nome host del gateway", - "gatewayPort": "Porta gateway", - "gatewayUsername": "Nome utente del gateway", - "gatewayPassword": "Password del gateway", - "gatewayDomain": "Dominio Gateway", + "loadBalanceInfo": "Info bilanciamento carico / Cookie", + "loadBalanceInfoDesc": "Cookie del RD Connection Broker per il bilanciamento del carico della farm RDS (es. tsv://MS Terminal Services Plugin.1.CollectionName)", + "guacdProxy": "Proxy guacd", + "guacdHostname": "Host guacd", + "guacdHostnamePlaceholder": "Default globale", + "guacdPort": "Porta guacd", + "guacdProxyDesc": "Sostituisci l'istanza guacd globale per questa connessione. Lascia vuoto per utilizzare il guacd configurato globalmente.", + "gatewayHostname": "Hostname del gateway", + "gatewayPort": "Porta del gateway", + "gatewayUsername": "Nome utente gateway", + "gatewayPassword": "Password gateway", + "gatewayDomain": "Dominio gateway", "remoteAppProgram": "Programma RemoteApp", "workingDirectory": "Directory di lavoro", - "arguments": "Argomentazioni", - "normalizeLineEndings": "Normalizza le terminazioni di riga", - "recordingPath": "Percorso di registrazione", - "recordingName": "Nome della registrazione", + "arguments": "Argomenti", + "normalizeLineEndings": "Normalizza terminazioni di riga", + "recordingPath": "Percorso registrazione", + "recordingName": "Nome registrazione", "macAddress": "Indirizzo MAC", - "broadcastAddress": "Discorso di trasmissione", + "broadcastAddress": "Indirizzo di broadcast", "udpPort": "Porta UDP", "waitTimeS": "Tempo di attesa (s)", "driveName": "Nome unità", - "drivePath": "Percorso di guida", - "ignoreCertificate": "Ignora il certificato", - "ignoreCertificateDesc": "Consenti le connessioni agli host con certificati autofirmati", - "forceLossless": "Forza senza perdite", - "forceLosslessDesc": "Forza la codifica delle immagini senza perdita di dati (qualità superiore, maggiore larghezza di banda)", - "disableAudio": "Disattiva l'audio", + "drivePath": "Percorso unità", + "ignoreCertificate": "Ignora certificato", + "ignoreCertificateDesc": "Consente connessioni a host con certificati autofirmati", + "forceLossless": "Forza lossless", + "forceLosslessDesc": "Forza la codifica lossless delle immagini (qualità più alta, maggiore utilizzo di banda)", + "disableAudio": "Disabilita audio", "disableAudioDesc": "Disattiva tutto l'audio dalla sessione remota", - "enableAudioInput": "Abilita l'ingresso audio (microfono)", + "enableAudioInput": "Abilita ingresso audio (microfono)", "enableAudioInputDesc": "Inoltra il microfono locale alla sessione remota", - "wallpaper": "Carta da parati", - "wallpaperDesc": "Mostra lo sfondo del desktop (disabilitandolo si migliorano le prestazioni)", - "theming": "Tematizzazione", + "wallpaper": "Sfondo", + "wallpaperDesc": "Mostra lo sfondo del desktop (disabilitarlo migliora le prestazioni)", + "theming": "Temi", "themingDesc": "Abilita temi e stili visivi", - "fontSmoothing": "Anti-aliasing dei caratteri", + "fontSmoothing": "Smussamento caratteri", "fontSmoothingDesc": "Abilita il rendering dei caratteri ClearType", - "fullWindowDrag": "Trascinamento a finestra intera", + "fullWindowDrag": "Trascinamento finestra intera", "fullWindowDragDesc": "Mostra il contenuto della finestra durante il trascinamento", "desktopComposition": "Composizione desktop", - "desktopCompositionDesc": "Effetti vetro Aero Abilita", - "menuAnimations": "Animazioni del menu", - "menuAnimationsDesc": "Abilita le animazioni di dissolvenza e scorrimento del menu", - "disableBitmapCaching": "Disabilita la memorizzazione nella cache delle immagini bitmap", - "disableBitmapCachingDesc": "Disattiva la cache delle immagini bitmap (potrebbe risolvere alcuni problemi).", - "disableOffscreenCaching": "Disabilita la memorizzazione nella cache offscreen", - "disableOffscreenCachingDesc": "Disattiva la cache offscreen", - "disableGlyphCaching": "Disabilita la memorizzazione nella cache dei glifi", + "desktopCompositionDesc": "Abilita gli effetti Aero Glass", + "menuAnimations": "Animazioni menu", + "menuAnimationsDesc": "Abilita animazioni di dissolvenza e scorrimento dei menu", + "disableBitmapCaching": "Disabilita cache bitmap", + "disableBitmapCachingDesc": "Disattiva la cache bitmap (può essere utile per risolvere anomalie)", + "disableOffscreenCaching": "Disabilita cache fuori schermo", + "disableOffscreenCachingDesc": "Disattiva la cache fuori schermo", + "disableGlyphCaching": "Disabilita cache glifi", "disableGlyphCachingDesc": "Disattiva la cache dei glifi", "enableGfx": "Abilita GFX", - "enableGfxDesc": "Utilizzare la pipeline grafica RemoteFX", - "enablePrinting": "Stampa aperta", - "enablePrintingDesc": "Reindirizzare le stampanti locali alla sessione remota", - "enableDriveRedirection": "Abilita il reindirizzamento delle unità", + "enableGfxDesc": "Usa la 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 unità", - "createDrivePathDesc": "Crea automaticamente la cartella se non esiste.", - "disableDownload": "Disabilita il download", - "disableDownloadDesc": "Impedire il download di file dalla sessione remota", - "disableUpload": "Disabilita il caricamento", - "disableUploadDesc": "Impedire il caricamento di file nella sessione remota", - "enableTouch": "Abilita il tocco", - "enableTouchDesc": "Abilita l'inoltro dell'input touch", + "createDrivePath": "Crea percorso drive", + "createDrivePathDesc": "Crea automaticamente la cartella se non esiste", + "disableDownload": "Disabilita download", + "disableDownloadDesc": "Impedisce il download di file dalla sessione remota", + "disableUpload": "Disabilita upload", + "disableUploadDesc": "Impedisce il caricamento di file sulla sessione remota", + "enableTouch": "Abilita input tattile", + "enableTouchDesc": "Abilita l'inoltro dell'input tattile", "consoleSession": "Sessione console", - "consoleSessionDesc": "Connettiti alla console (sessione 0) invece di una nuova sessione", + "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 connetterti", - "disableCopy": "Disabilita la copia", - "disableCopyDesc": "Impedire la copia del testo dalla sessione remota", - "disablePaste": "Disabilita Incolla", - "disablePasteDesc": "Impedire l'incollamento di testo nella sessione remota", - "createPathIfMissing": "Creare il percorso se mancante", + "sendWolPacketDesc": "Invia un pacchetto magico per riattivare questo host prima della connessione", + "disableCopy": "Disabilita copia", + "disableCopyDesc": "Impedisce la copia di testo dalla sessione remota", + "disablePaste": "Disabilita incolla", + "disablePasteDesc": "Impedisce di incollare testo nella sessione remota", + "createPathIfMissing": "Crea percorso se mancante", "createPathIfMissingDesc": "Crea automaticamente la directory di registrazione", "excludeOutput": "Escludi output", - "excludeOutputDesc": "Non registrare l'output dello schermo (solo metadati).", - "excludeMouse": "Escludi topo", + "excludeOutputDesc": "Non registrare l'output dello schermo (solo metadati)", + "excludeMouse": "Escludi mouse", "excludeMouseDesc": "Non registrare i movimenti del mouse", - "includeKeystrokes": "Includi sequenze di tasti", - "includeKeystrokesDesc": "Registra le sequenze di tasti grezze oltre all'output a schermo.", + "includeKeystrokes": "Includi tasti premuti", + "includeKeystrokesDesc": "Registra i tasti premuti non elaborati in aggiunta all'output dello schermo", "vncPort": "Porta VNC", "vncPassword": "Password VNC", - "vncUsernameOptional": "Nome utente (facoltativo)", - "vncLeaveBlank": "Lasciare vuoto se non necessario", + "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)", + "swapRedBlueDesc": "Scambia i canali di colore rosso e blu (corregge alcuni problemi di colore)", "readOnly": "Sola lettura", "readOnlyDesc": "Visualizza lo schermo remoto senza inviare alcun input", "telnetPort": "Porta Telnet", - "terminalType": "Tipo di terminale", - "fontName": "Nome del carattere", - "fontSize": "Dimensione del carattere", - "colorScheme": "Combinazione di colori", + "terminalType": "Tipo terminale", + "fontName": "Nome font", + "fontSize": "Dimensione font", + "colorScheme": "Schema colori", "backspaceKey": "Tasto Backspace", - "saveHostFirst": "Salva prima l'host.", - "sharingOptionsAfterSave": "Le opzioni di condivisione saranno disponibili dopo aver salvato l'host.", + "saveHostFirst": "Salva prima l'host", + "sharingOptionsAfterSave": "Le opzioni di condivisione sono disponibili dopo aver salvato l'host", "permissionLevel": "Livello di autorizzazione", "typeHeader": "Tipo", - "targetHeader": "Bersaglio", + "targetHeader": "Destinazione", "permissionHeader": "Autorizzazione", - "cancelBtn": "Cancellare", - "savingBtn": "Risparmio...", + "cancelBtn": "Annulla", + "savingBtn": "Salvando...", "updateHostBtn": "Aggiorna host", "addHostBtn": "Aggiungi host" } @@ -1380,242 +1450,305 @@ "commandPalette": { "searchPlaceholder": "Cerca host, comandi o impostazioni...", "quickActions": "Azioni rapide", - "hostManager": "Responsabile dell'hosting", + "hostManager": "Gestione host", "hostManagerDesc": "Gestisci, aggiungi o modifica gli host", "addNewHost": "Aggiungi nuovo host", "addNewHostDesc": "Registra un nuovo host", - "adminSettings": "Impostazioni di amministrazione", + "adminSettings": "Impostazioni amministratore", "adminSettingsDesc": "Configura le preferenze di sistema e gli utenti", "userProfile": "Profilo utente", - "userProfileDesc": "Gestisci il tuo account e le tue preferenze", - "addCredential": "Aggiungi credenziali", - "addCredentialDesc": "Memorizza le chiavi SSH o le password", - "tmuxMonitor": "Monitoraggio Tmux", + "userProfileDesc": "Gestisci il tuo account e le preferenze", + "addCredential": "Aggiungi credenziale", + "addCredentialDesc": "Archivia chiavi SSH o password", + "tmuxMonitor": "Monitor Tmux", "tmuxMonitorDesc": "Monitora le sessioni tmux sui tuoi host", "recentActivity": "Attività recenti", "serversAndHosts": "Server e host", "noHostsFound": "Nessun host trovato corrispondente a \"{{search}}\"", "links": "Collegamenti", - "navigate": "Navigare", - "select": "Selezionare", + "navigate": "Naviga", + "select": "Seleziona", "toggleWith": "Attiva/disattiva con" }, "splitScreen": { "paneEmpty": "Pannello {{index}} - vuoto", "noTabAssigned": "Nessuna scheda assegnata", - "focusedPane": "Riquadro attivo" + "focusedPane": "Pannello attivo" }, "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", + "noConnectionsDesc": "Apri un terminale, un file manager o un desktop remoto per visualizzare le connessioni qui", + "connectedFor": "Connesso da {{duration}}", + "connected": "Connesso", "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", + "closeTab": "Chiudi scheda", + "closeConnection": "Chiudi connessione", + "forgetTab": "Dimentica", + "removeBackground": "Rimuovi", + "reconnect": "Riconnetti", + "reopenTab": "Riapri", + "sectionOpen": "Aperte", + "sectionBackground": "In background", + "backgroundDesc": "Le sessioni persistono per 30 minuti dopo la disconnessione e possono essere riconnesse.", + "persisted": "Mantenuta in background", "expiresIn": "Scade tra {{duration}}", "search": "Cerca connessioni...", - "noSearchResults": "Nessun risultato corrispondente alla ricerca", - "rename": "Rinominare sessione" + "noSearchResults": "Nessuna connessione corrisponde alla ricerca", + "rename": "Rinomina sessione", + "sectionSharedWithMe": "Condiviso con me", + "sharedBy": "Condiviso da {{username}}", + "join": "Giuntura", + "sharedSessionLabel": "{{hostName}} (condiviso)" + }, + "sessionSharing": { + "guestView": { + "loading": "Connessione alla sessione condivisa in corso...", + "linkInvalid": "Questo link di condivisione non è valido, è scaduto o è stato revocato.", + "rateLimited": "Troppi tentativi, riprova tra poco.", + "sessionEnded": "Questa sessione è terminata", + "readOnlyBadge": "Solo visualizzazione" + }, + "modalTitle": "Sessione di condivisione", + "shareButton": "Condividere", + "notReadyToShare": "La sessione non è ancora pronta per la condivisione.", + "modeTab": { + "link": "Collegamento", + "user": "Utente" + }, + "linkModeDescription": "Chiunque abbia questo link può partecipare, non è necessario creare un account.", + "userModeDescription": "Condividi la sessione con un utente specifico che ha già accesso a questo host. Se non ha ancora accesso, condividi prima l'host con lui oppure usa un link. Una volta condivisa, la sessione apparirà nella sua scheda Connessioni.", + "permissionLevel": { + "label": "Livello di autorizzazione", + "readOnly": "Sola lettura", + "readOnlyDescription": "È possibile seguire la sessione in diretta, ma non è possibile digitare o interagire.", + "readWrite": "lettura-scrittura", + "readWriteDescription": "Può digitare e interagire con la sessione proprio come il proprietario." + }, + "expiryLabel": "Scadenza del collegamento", + "createLinkButton": "Crea collegamento", + "createShareButton": "Condividi con l'utente", + "searchUsersPlaceholder": "Cerca utenti...", + "noUsersFound": "Nessun utente trovato", + "linkCreated": "Link di condivisione creato", + "linkCopied": "Link copiato negli appunti", + "copyLink": "Copia il link", + "shareCreated": "Sessione condivisa. Sarà visibile nella scheda Connessioni.", + "shareFailed": "Impossibile creare la condivisione", + "userLacksHostAccess": "Quell'utente non ha ancora accesso a questo host. Condividi prima l'host con lui, oppure usa un link.", + "activeShares": "Condivisioni attive", + "noActiveShares": "Nessuna azione attiva in questa sessione.", + "revoke": "Revocare", + "revokeConfirmTitle": "Revocare questa condivisione?", + "revokeConfirmDescription": "Chiunque utilizzi questa condivisione perderà immediatamente l'accesso.", + "revoked": "Azione revocata.", + "revokeFailed": "Impossibile revocare la quota", + "joinCount": "{{count}} unisciti", + "joinCount_other": "{{count}} si unisce", + "expiresAt": "Scade il {{date}}", + "linkShareBadge": "Collegamento", + "userShareBadge": "Utente: {{username}}", + "loadSharesFailed": "Impossibile caricare le azioni attive" }, "guacamole": { "connecting": "Connessione alla sessione {{type}} in corso...", - "connectionError": "Errore di combinazione", + "connectionError": "Errore di connessione", "connectionFailed": "Connessione fallita", "failedToConnect": "Impossibile ottenere il token di connessione", "hostNotFound": "Host non trovato", "noHostSelected": "Nessun host selezionato", - "reconnect": "Ricollega", + "reconnect": "Riconnetti", "retry": "Riprova", - "guacdUnavailable": "Il servizio desktop remoto (guacd) non è disponibile. Assicurarsi che guacd sia in esecuzione, accessibile e configurato correttamente nelle impostazioni di amministrazione.", - "ctrlAltDel": "Ctrl+Alt+Canc", + "guacdUnavailable": "Il servizio desktop remoto (guacd) non è disponibile. Assicurati che guacd sia in esecuzione, accessibile e configurato correttamente nelle impostazioni di amministrazione.", + "credentialPromptTitle": "Inserisci le credenziali RDP", + "credentialPromptDescription": "Questo host è configurato per richiedere le credenziali al momento della connessione. Tali credenziali vengono utilizzate solo per questa sessione e non vengono salvate.", + "connect": "Collegare", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl+Alt+Canc", - "winL": "Win+L (Schermata di blocco)", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Blocca schermo)", "winKey": "Tasto Windows", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Spostare", - "win": "Vincita", + "shift": "Shift", + "win": "Win", "stickyActive": "{{key}} (bloccato - clicca per rilasciare)", - "stickyInactive": "{{key}} (clicca per agganciare)", - "esc": "Fuga", + "stickyInactive": "{{key}} (clicca per bloccare)", + "esc": "Esc", "tab": "Tab", - "home": "Casa", - "end": "FINE", - "pageUp": "Pagina su", - "pageDown": "Pagina giù", - "arrowUp": "Freccia in su", - "arrowDown": "Freccia in basso", - "arrowLeft": "Freccia a sinistra", - "arrowRight": "Freccia a destra", + "home": "Home", + "end": "Fine", + "pageUp": "Pag Su", + "pageDown": "Pag Giù", + "arrowUp": "Freccia su", + "arrowDown": "Freccia giù", + "arrowLeft": "Freccia sinistra", + "arrowRight": "Freccia destra", "fnToggle": "Tasti funzione", "reconnect": "Riconnetti sessione", - "collapse": "Riduci la barra degli strumenti", - "expand": "Espandi la barra degli strumenti", - "dragHandle": "Trascina per riposizionare" + "collapse": "Comprimi barra degli strumenti", + "expand": "Espandi barra degli strumenti", + "dragHandle": "Trascina per riposizionare", + "switchToTrackpad": "Passa alla modalità trackpad (trascina per spostare il cursore, tocca per fare clic)", + "switchToTouch": "Passa alla modalità touch (tocca direttamente il punto in cui desideri fare clic)" } }, "terminal": { - "connect": "Connettiti all'host", - "clear": "Chiaro", - "paste": "Impasto", - "reconnect": "Ricollega", + "connect": "Connetti all'host", + "clear": "Cancella", + "paste": "Incolla", + "reconnect": "Riconnetti", "connectionLost": "Connessione persa", - "connected": "Collegato", - "clipboardWriteFailed": "Impossibile copiare negli appunti. Assicurati che la pagina venga servita tramite HTTPS o localhost.", - "clipboardReadFailed": "Impossibile leggere dagli appunti. Assicurarsi che le autorizzazioni per gli appunti siano concesse.", - "clipboardHttpWarning": "L'incolla richiede HTTPS. Utilizza Ctrl+Shift+V oppure servi Termix tramite HTTPS.", - "passwordPromptFillTitle": "Inserire la password salvata in questo campo?", + "connected": "Connesso", + "clipboardWriteFailed": "Copia negli appunti non riuscita. Assicurati che la pagina sia servita via HTTPS o localhost.", + "clipboardReadFailed": "Lettura dagli appunti non riuscita. Assicurati che i permessi degli appunti siano stati concessi.", + "clipboardHttpWarning": "Incolla richiede HTTPS. Utilizza Ctrl+Shift+V o accedi a Termix tramite HTTPS.", + "passwordPromptFillTitle": "Inserire la password salvata in questa richiesta?", "unknownError": "Si è verificato un errore sconosciuto", "websocketError": "Errore di connessione WebSocket", "connecting": "Connessione in corso...", "noHostSelected": "Nessun host selezionato", - "reconnecting": "Riconnessione in corso... ({{attempt}}/{{max}})", - "reconnected": "Riconnessione avvenuta con successo.", + "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, si ripiega sulla shell standard", + "tmuxUnavailable": "tmux non è installato sull'host remoto, si utilizza la shell standard", "tmuxSessionPickerTitle": "Sessioni tmux", - "tmuxSessionPickerDesc": "Sono state trovate sessioni tmux esistenti su questo host. Selezionane una per riconnetterti o creane una nuova.", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "Sono state trovate sessioni tmux esistenti su questo host. Selezionane una per ricollegarti o crea una nuova sessione.", + "tmuxWindows": "Finestre", "tmuxWindowCount": "{{count}} finestra", - "tmuxAttached": "Clienti collegati", - "tmuxAttachedCount": "{{count}} allegato", + "tmuxAttached": "Client collegati", + "tmuxAttachedCount": "{{count}} collegato", "tmuxLastActivity": "Ultima attività", - "tmuxTimeJustNow": "proprio adesso", + "tmuxTimeJustNow": "Adesso", "tmuxTimeMinutes": "{{count}}m fa", - "tmuxTimeHours": "{{count}}ore fa", - "tmuxTimeDays": "{{count}}giorni fa", - "tmuxCreateNew": "Avvia una nuova sessione", + "tmuxTimeHours": "{{count}}h fa", + "tmuxTimeDays": "{{count}}d fa", + "tmuxCreateNew": "Avvia nuova sessione", "tmuxCopyHint": "Regola la selezione e premi Invio per copiare negli appunti", - "tmuxDetach": "Disconnettiti dalla sessione tmux", - "tmuxDetached": "Disconnessa dalla sessione tmux", - "maxReconnectAttemptsReached": "Numero massimo di tentativi di riconnessione raggiunto", - "closeTab": "Vicino", - "connectionTimeout": "Timeout della connessione", + "tmuxDetach": "Scollegati dalla sessione tmux", + "tmuxDetached": "Scollegato dalla sessione tmux", + "maxReconnectAttemptsReached": "Raggiunto il numero massimo di tentativi di riconnessione", + "closeTab": "Chiudi", + "connectionTimeout": "Timeout di connessione", "terminalTitle": "Terminale - {{host}}", "terminalWithPath": "Terminale - {{host}}:{{path}}", "runTitle": "Esecuzione {{command}} - {{host}}", "totpRequired": "Autenticazione a due fattori richiesta", "totpCodeLabel": "Codice di verifica", - "totpVerify": "Verificare", + "totpVerify": "Verifica", + "mfaPromptRequired": "Autenticazione richiesta", + "mfaPushRequired": "Autenticazione push richiesta", + "mfaMenuPlaceholder": "Inserisci la tua risposta", + "mfaWaitingApproval": "In attesa di approvazione sul tuo dispositivo...", + "mfaSendRequest": "Invia richiesta", "warpgateAuthRequired": "Autenticazione Warpgate richiesta", "warpgateSecurityKey": "Chiave di sicurezza", "warpgateAuthUrl": "URL di autenticazione", "warpgateOpenBrowser": "Apri nel browser", "warpgateContinue": "Ho completato l'autenticazione", "opksshAuthRequired": "Autenticazione OPKSSH richiesta", - "opksshAuthDescription": "Completa l'autenticazione nel tuo browser per continuare. Questa sessione rimarrà valida per 24 ore.", + "opksshAuthDescription": "Completa l'autenticazione nel browser per continuare. Questa sessione rimarrà valida per 24 ore.", "opksshOpenBrowser": "Apri il browser per autenticarti", - "opksshWaitingForAuth": "In attesa di autenticazione nel browser...", + "opksshWaitingForAuth": "In attesa dell'autenticazione nel browser...", "opksshAuthenticating": "Autenticazione in corso...", "opksshTimeout": "Autenticazione scaduta. Riprova.", - "opksshAuthFailed": "Autenticazione non riuscita. Verifica le tue credenziali e riprova.", + "opksshAuthFailed": "Autenticazione fallita. Controlla le credenziali e riprova.", "opksshSignInWith": "Accedi con {{provider}}", - "vaultAuthTitle": "È necessario accedere al vault.", - "vaultAuthDescription": "Si è aperta una finestra per accedere a HashiCorp Vault. Completa l'accesso; la connessione proseguirà automaticamente.", - "vaultAuthFailed": "Autenticazione del vault non riuscita. Riprova.", + "vaultAuthTitle": "Accesso a Vault richiesto", + "vaultAuthDescription": "Una finestra si è aperta per accedere a HashiCorp Vault. Completa l'accesso lì; la connessione continuerà automaticamente.", + "vaultAuthFailed": "Autenticazione Vault fallita. Riprova.", "vaultReopen": "Riapri la finestra di accesso", - "sudoPasswordPopupTitle": "Inserire la password?", - "linkDialogTitle": "Apri il collegamento", - "linkDialogOpen": "Aprire", + "sudoPasswordPopupTitle": "Inserisci password?", + "linkDialogTitle": "Apri collegamento", + "linkDialogOpen": "Apri", "linkDialogCopy": "Copia", - "websocketAbnormalClose": "La connessione si è chiusa inaspettatamente. Ciò potrebbe essere dovuto a un problema di configurazione del proxy inverso o SSL. Si prega di controllare i log del server.", - "connectionLogTitle": "Registro di connessione", + "websocketAbnormalClose": "La connessione si è chiusa in modo imprevisto. Potrebbe trattarsi di un problema di reverse proxy o configurazione SSL. Controlla i log del server.", + "connectionLogTitle": "Log di connessione", "connectionLogCopy": "Copia i log negli appunti", - "connectionLogEmpty": "Nessun registro di connessione ancora disponibile", + "connectionLogEmpty": "Nessun log di connessione al momento", "connectionLogWaiting": "In attesa dei log di connessione...", - "connectionLogCopied": "I registri di connessione sono stati copiati negli appunti", - "connectionLogCopyFailed": "Impossibile copiare i log negli appunti", - "connectionRejected": "Connessione rifiutata dal server. Si prega di verificare l'autenticazione e la configurazione di rete.", + "connectionLogCopied": "Log di connessione copiati negli appunti", + "connectionLogCopyFailed": "Copia dei log negli appunti fallita", + "connectionRejected": "Connessione rifiutata dal server. Controlla l'autenticazione e la configurazione di rete.", "hostKeyRejected": "Verifica della chiave host SSH rifiutata. Connessione annullata.", - "sessionTakenOver": "La sessione è stata aperta in un'altra scheda. Riconnessione in corso...", + "sessionTakenOver": "La sessione è stata aperta in un'altra scheda. Riconnessione...", "split": { - "splitTab": "Scheda divisa", - "addToSplit": "Aggiungi a Divide", - "removeFromSplit": "Rimuovi da Split" + "splitTab": "Dividi scheda", + "addToSplit": "Aggiungi alla divisione", + "removeFromSplit": "Rimuovi dalla divisione" } }, "fileManager": { "noHostSelected": "Nessun host selezionato", - "initializingEditor": "Inizializzazione dell'editor in corso...", + "initializingEditor": "Inizializzazione editor...", "file": "File", "folder": "Cartella", "uploadFile": "Carica file", - "downloadFile": "Scaricamento", - "extractArchive": "Archivio di estrazione", + "downloadFile": "Scarica", + "extractArchive": "Estrai archivio", "extractingArchive": "Estrazione di {{name}}...", "archiveExtractedSuccessfully": "{{name}} estratto con successo", "extractFailed": "Estrazione non riuscita", - "compressFile": "Comprimi il file", - "compressFiles": "Comprimi i file", + "compressFile": "Comprimi file", + "compressFiles": "Comprimi file", "compressFilesDesc": "Comprimi {{count}} elementi in un archivio", - "archiveName": "Nome dell'archivio", - "enterArchiveName": "Inserisci il nome dell'archivio...", - "compressionFormat": "Formato di compressione", + "archiveName": "Nome archivio", + "enterArchiveName": "Inserisci nome archivio...", + "compressionFormat": "Formato compressione", "selectedFiles": "File selezionati", - "andMoreFiles": "e {{count}} altro...", - "compress": "Comprimere", - "compressingFiles": "Compressione degli {{count}} elementi in {{name}}...", + "andMoreFiles": "e altri {{count}}...", + "compress": "Comprimi", + "compressingFiles": "Comprimendo {{count}} elementi in {{name}}...", "filesCompressedSuccessfully": "{{name}} creato con successo", - "compressFailed": "Compressione fallita", - "edit": "Modificare", + "compressFailed": "Compressione non riuscita", + "edit": "Modifica", "preview": "Anteprima", "previous": "Precedente", - "next": "Prossimo", + "next": "Successivo", "pageXOfY": "Pagina {{current}} di {{total}}", "zoomOut": "Riduci zoom", - "zoomIn": "Ingrandisci", + "zoomIn": "Aumenta zoom", "newFile": "Nuovo file", "newFolder": "Nuova cartella", - "rename": "Rinominare", + "rename": "Rinomina", "uploading": "Caricamento in corso...", - "uploadingFile": "Caricamento in corso {{name}}...", - "fileName": "Nome del file", - "folderName": "Nome della cartella", - "fileUploadedSuccessfully": "File \"{{name}}\" caricato correttamente", - "failedToUploadFile": "Impossibile caricare il file", - "fileDownloadedSuccessfully": "File \"{{name}}\" scaricato correttamente", - "failedToDownloadFile": "Impossibile scaricare il file", + "uploadingFile": "Caricamento di {{name}}...", + "fileName": "Nome file", + "folderName": "Nome cartella", + "fileUploadedSuccessfully": "File \"{{name}}\" caricato con successo", + "failedToUploadFile": "Caricamento file non riuscito", + "fileDownloadedSuccessfully": "File \"{{name}}\" scaricato con successo", + "failedToDownloadFile": "Scaricamento file non riuscito", "fileCreatedSuccessfully": "File \"{{name}}\" creato con successo", "folderCreatedSuccessfully": "Cartella \"{{name}}\" creata con successo", - "failedToCreateItem": "Impossibile creare l'elemento", - "operationFailed": "{{operation}} operazione non riuscita per {{name}}: {{error}}", - "failedToResolveSymlink": "Impossibile risolvere il collegamento simbolico", + "failedToCreateItem": "Creazione elemento non riuscita", + "operationFailed": "Operazione {{operation}} non riuscita per {{name}}: {{error}}", + "failedToResolveSymlink": "Risoluzione del collegamento simbolico non riuscita", "itemsDeletedSuccessfully": "{{count}} elementi eliminati con successo", - "failedToDeleteItems": "Impossibile eliminare gli elementi", - "sudoPasswordRequired": "Password di amministratore richiesta", - "enterSudoPassword": "Inserisci la password di sudo per continuare l'operazione", - "sudoPassword": "Password Sudo", - "sudoOperationFailed": "L'operazione Sudo non è riuscita.", - "sudoAuthFailed": "Autenticazione Sudo non riuscita", + "failedToDeleteItems": "Eliminazione elementi non riuscita", + "sudoPasswordRequired": "Password amministratore richiesta", + "enterSudoPassword": "Inserisci la password sudo per continuare l'operazione", + "sudoPassword": "Password sudo", + "sudoOperationFailed": "Operazione sudo non riuscita", + "sudoAuthFailed": "Autenticazione sudo non riuscita", "dragFilesToUpload": "Trascina qui i file per caricarli", "emptyFolder": "Questa cartella è vuota", "searchFiles": "Cerca file...", - "upload": "Caricamento", - "selectHostToStart": "Seleziona un host per avviare la gestione dei file", - "sshRequiredForFileManager": "Il gestore file richiede SSH. Su questo host SSH non è abilitato.", - "failedToConnect": "Impossibile connettersi a SSH", - "failedToLoadDirectory": "Impossibile caricare la directory", + "upload": "Carica", + "selectHostToStart": "Seleziona un host per avviare la gestione file", + "sshRequiredForFileManager": "Il gestore file richiede SSH. Questo host non ha SSH abilitato.", + "failedToConnect": "Connessione a SSH non riuscita", + "failedToLoadDirectory": "Caricamento directory non riuscito", "noSSHConnection": "Nessuna connessione SSH disponibile", "copy": "Copia", - "cut": "Taglio", - "paste": "Impasto", + "cut": "Taglia", + "paste": "Incolla", "copyPath": "Copia percorso", "copyPaths": "Copia percorsi", - "delete": "Eliminare", + "delete": "Elimina", "properties": "Proprietà", - "refresh": "Rinfrescato", - "downloadFiles": "Scarica i file {{count}} nel browser", + "refresh": "Aggiorna", + "downloadFiles": "Scarica {{count}} file nel browser", "copyFiles": "Copia {{count}} elementi", "cutFiles": "Taglia {{count}} elementi", "deleteFiles": "Elimina {{count}} elementi", @@ -1624,7 +1757,7 @@ "pathCopiedToClipboard": "Percorso copiato negli appunti", "pathsCopiedToClipboard": "{{count}} percorsi copiati negli appunti", "failedToCopyPath": "Impossibile copiare il percorso negli appunti", - "copyFolderLink": "Copia il collegamento nella cartella", + "copyFolderLink": "Copia collegamento alla cartella", "copyCurrentFolderLink": "Copia collegamento alla cartella corrente", "folderLinkCopied": "Collegamento alla cartella copiato negli appunti", "failedToCopyFolderLink": "Impossibile copiare il collegamento alla cartella", @@ -1632,216 +1765,216 @@ "failedToDeleteItem": "Impossibile eliminare l'elemento", "itemRenamedSuccessfully": "{{type}} rinominato con successo", "failedToRenameItem": "Impossibile rinominare l'elemento", - "download": "Scaricamento", + "download": "Scarica", "openExternalEditor": "Apri esternamente", - "chooseExternalEditor": "Scegli un editor esterno", - "externalEditorSelected": "redazione editoriale", - "externalEditorDesktopOnly": "L'editor esterno è disponibile solo nell'applicazione desktop.", - "externalEditorOpened": "Aperto con un editor esterno. Salvando in questa posizione, le modifiche verranno caricate sul server.", + "chooseExternalEditor": "Scegli editor esterno", + "externalEditorSelected": "Editor esterno selezionato", + "externalEditorDesktopOnly": "L'editor esterno è disponibile solo nell'app desktop", + "externalEditorOpened": "Aperto nell'editor esterno. Salvando lì, le modifiche verranno caricate nuovamente sul server.", "failedToOpenExternalEditor": "Impossibile aprire l'editor esterno", "failedToSelectExternalEditor": "Impossibile selezionare l'editor esterno", - "permissions": "Autorizzazioni", - "size": "Misurare", + "permissions": "Permessi", + "size": "Dimensione", "modified": "Modificato", - "path": "Sentiero", + "path": "Percorso", "confirmDelete": "Sei sicuro di voler eliminare {{name}}?", - "permissionDenied": "Autorizzazione negata", + "permissionDenied": "Permesso negato", "serverError": "Errore del server", - "fileSavedSuccessfully": "File salvato correttamente", + "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.", + "confirmDeleteMultipleItems": "Sei sicuro di voler eliminare definitivamente {{count}} elementi?", + "confirmDeleteMultipleItemsWithFolders": "Sei sicuro di voler eliminare definitivamente {{count}} elementi? Sono incluse cartelle e il loro contenuto.", "confirmDeleteFolder": "Sei sicuro di voler eliminare definitivamente la cartella \"{{name}}\" e tutto il suo contenuto?", - "permanentDeleteWarning": "Questa azione non può essere annullata. L'elemento/gli elementi verranno eliminati definitivamente dal server.", - "recent": "Recente", - "pinned": "Fissato", + "permanentDeleteWarning": "Questa azione non può essere annullata. Gli elementi verranno eliminati definitivamente dal server.", + "recent": "Recenti", + "pinned": "Fissati", "folderShortcuts": "Collegamenti alle cartelle", - "failedToReconnectSSH": "Impossibile riconnettere la sessione SSH", - "openTerminalHere": "Apri il terminale qui", - "run": "Correre", - "openTerminalInFolder": "Apri Terminale in questa cartella", - "openTerminalInFileLocation": "Apri il terminale nella posizione del file", - "runningFile": "In corsa - {{file}}", + "failedToReconnectSSH": "Impossibile riconnettersi alla sessione SSH", + "openTerminalHere": "Apri terminale qui", + "run": "Esegui", + "openTerminalInFolder": "Apri terminale in questa cartella", + "openTerminalInFileLocation": "Apri terminale nella posizione del file", + "runningFile": "In esecuzione - {{file}}", "onlyRunExecutableFiles": "È possibile eseguire solo file eseguibili", "directories": "Directory", "removedFromRecentFiles": "Rimosso \"{{name}}\" dai file recenti", - "removeFailed": "Rimuovere l'errore", - "unpinnedSuccessfully": "Rimosso \"{{name}}\" con successo", - "unpinFailed": "Sblocco fallito", - "removedShortcut": "Rimossa la scorciatoia \"{{name}}\"", - "removeShortcutFailed": "Rimozione del collegamento non riuscita", - "clearedAllRecentFiles": "Tutti i file recenti sono stati eliminati.", - "clearFailed": "Cancellazione fallita", + "removeFailed": "Rimozione non riuscita", + "unpinnedSuccessfully": "Rimosso \"{{name}}\" dai preferiti con successo", + "unpinFailed": "Rimozione dai preferiti non riuscita", + "removedShortcut": "Rimosso collegamento \"{{name}}\"", + "removeShortcutFailed": "Rimozione collegamento non riuscita", + "clearedAllRecentFiles": "Cancellati tutti i file recenti", + "clearFailed": "Cancellazione non riuscita", "removeFromRecentFiles": "Rimuovi dai file recenti", "clearAllRecentFiles": "Cancella tutti i file recenti", - "unpinFile": "Sblocca il file", + "unpinFile": "Rimuovi dai preferiti", "removeShortcut": "Rimuovi collegamento", - "pinFile": "File di spilli", - "addToShortcuts": "Aggiungi ai collegamenti rapidi", + "pinFile": "Aggiungi ai preferiti", + "addToShortcuts": "Aggiungi ai collegamenti", "pasteFailed": "Incolla non riuscito", - "noUndoableActions": "Nessuna azione irreversibile", - "undoCopySuccess": "Operazione di copia annullata: eliminati i file copiati {{count}}", + "noUndoableActions": "Nessuna azione annullabile", + "undoCopySuccess": "Operazione di copia annullata: eliminati {{count}} file copiati", "undoCopyFailedDelete": "Annullamento non riuscito: impossibile eliminare i file copiati", - "undoCopyFailedNoInfo": "Annullamento non riuscito: impossibile trovare le informazioni del file copiato", - "undoMoveSuccess": "Operazione di spostamento annullata: i file {{count}} sono stati spostati nuovamente nella posizione originale.", - "undoMoveFailedMove": "Annullamento non riuscito: impossibile ripristinare i file", - "undoMoveFailedNoInfo": "Annullamento non riuscito: impossibile trovare le informazioni sul file spostato.", - "undoDeleteNotSupported": "L'operazione di eliminazione non può essere annullata: i file sono stati eliminati definitivamente dal server.", + "undoCopyFailedNoInfo": "Annullamento non riuscito: impossibile trovare informazioni sui file copiati", + "undoMoveSuccess": "Operazione di spostamento annullata: riportati {{count}} file nella posizione originale", + "undoMoveFailedMove": "Annullamento non riuscito: impossibile riportare i file indietro", + "undoMoveFailedNoInfo": "Annullamento non riuscito: impossibile trovare informazioni sui file spostati", + "undoDeleteNotSupported": "L'operazione di eliminazione non può essere annullata: i file sono stati eliminati permanentemente dal server", "undoTypeNotSupported": "Tipo di operazione di annullamento non supportato", - "undoOperationFailed": "Annullamento operazione non riuscito", + "undoOperationFailed": "Operazione di annullamento non riuscita", "unknownError": "Errore sconosciuto", - "confirm": "Confermare", - "find": "Trovare...", - "replace": "Sostituire", + "confirm": "Conferma", + "find": "Trova...", + "replace": "Sostituisci", "downloadInstead": "Scarica invece", "keyboardShortcuts": "Scorciatoie da tastiera", "searchAndReplace": "Cerca e sostituisci", "editing": "Modifica", - "search": "Ricerca", + "search": "Cerca", "findNext": "Trova successivo", - "findPrevious": "Prenota Precedente", + "findPrevious": "Trova precedente", "save": "Salva", "selectAll": "Seleziona tutto", - "undo": "Disfare", - "redo": "Rifare", - "moveLineUp": "Spostare la fila", - "moveLineDown": "Sposta la riga verso il basso", - "toggleComment": "Attiva/disattiva commento", + "undo": "Annulla", + "redo": "Ripristina", + "moveLineUp": "Sposta riga su", + "moveLineDown": "Sposta riga giù", + "toggleComment": "Commenta/Decommenta", "autoComplete": "Completamento automatico", - "imageLoadError": "Impossibile caricare l'immagine", + "imageLoadError": "Caricamento immagine non riuscito", "startTyping": "Inizia a digitare...", - "unknownSize": "Dimensioni sconosciute", + "unknownSize": "Dimensione sconosciuta", "fileIsEmpty": "Il file è vuoto", "largeFileWarning": "Avviso file di grandi dimensioni", - "largeFileWarningDesc": "Questo file ha una dimensione di {{size}} , il che potrebbe causare problemi di prestazioni quando viene aperto come testo.", - "fileNotFoundAndRemoved": "Il file \"{{name}}\" non è stato trovato ed è stato rimosso dai file recenti/aggiunti.", - "failedToLoadFile": "Impossibile caricare il file: {{error}}", + "largeFileWarningDesc": "Questo file ha una dimensione di {{size}}, che potrebbe causare problemi di prestazioni se aperto come testo.", + "fileNotFoundAndRemoved": "File \"{{name}}\" non trovato ed è stato rimosso dai file recenti/fissati", + "failedToLoadFile": "Caricamento del file non riuscito: {{error}}", "serverErrorOccurred": "Si è verificato un errore del server. Riprova più tardi.", "autoSaveFailed": "Salvataggio automatico non riuscito", "fileAutoSaved": "File salvato automaticamente", - "moveFileFailed": "Impossibile spostare {{name}}", + "moveFileFailed": "Spostamento di {{name}} non riuscito", "moveOperationFailed": "Operazione di spostamento non riuscita", "canOnlyCompareFiles": "È possibile confrontare solo due file", "comparingFiles": "Confronto dei file: {{file1}} e {{file2}}", "dragFailed": "Operazione di trascinamento non riuscita", - "filePinnedSuccessfully": "File \"{{name}}\" bloccato con successo", - "pinFileFailed": "Impossibile bloccare il file", - "fileUnpinnedSuccessfully": "File \"{{name}}\" sbloccato con successo", - "unpinFileFailed": "Impossibile sbloccare il file", - "shortcutAddedSuccessfully": "Collegamento alla cartella \"{{name}}\" aggiunto correttamente", + "filePinnedSuccessfully": "File \"{{name}}\" fissato con successo", + "pinFileFailed": "Fissaggio del file non riuscito", + "fileUnpinnedSuccessfully": "File \"{{name}}\" rimosso dai fissati con successo", + "unpinFileFailed": "Impossibile rimuovere il file fissato", + "shortcutAddedSuccessfully": "Collegamento alla cartella \"{{name}}\" aggiunto con successo", "addShortcutFailed": "Impossibile aggiungere il collegamento", "operationCompletedSuccessfully": "{{operation}} {{count}} elementi con successo", "operationCompleted": "{{operation}} {{count}} elementi", "downloadFileSuccess": "File {{name}} scaricato con successo", "downloadFileFailed": "Download non riuscito", - "moveTo": "Spostati su {{name}}", - "diffCompareWith": "Differenza rispetto a {{name}}", + "moveTo": "Sposta in {{name}}", + "diffCompareWith": "Diff con {{name}}", "dragOutsideToDownload": "Trascina fuori dalla finestra per scaricare ({{count}} file)", - "newFolderDefault": "Nuova cartella", + "newFolderDefault": "NuovaCartella", "newFileDefault": "NuovoFile.txt", - "successfullyMovedItems": "Spostamento completato con successo di {{count}} elementi in {{target}}", - "move": "Mossa", + "successfullyMovedItems": "{{count}} elementi spostati con successo in {{target}}", + "move": "Sposta", "searchInFile": "Cerca nel file (Ctrl+F)", - "showKeyboardShortcuts": "Mostra le scorciatoie da tastiera", - "decreaseFontSize": "Riduci la dimensione del carattere", - "increaseFontSize": "Aumenta la dimensione del carattere", - "startWritingMarkdown": "Inizia a scrivere i tuoi contenuti in Markdown...", - "loadingFileComparison": "Caricamento del confronto file in corso...", - "reload": "Ricaricare", - "compare": "Confrontare", + "showKeyboardShortcuts": "Mostra scorciatoie da tastiera", + "decreaseFontSize": "Riduci dimensione carattere", + "increaseFontSize": "Aumenta dimensione carattere", + "startWritingMarkdown": "Inizia a scrivere il tuo contenuto markdown...", + "loadingFileComparison": "Caricamento confronto file in corso...", + "reload": "Ricarica", + "compare": "Confronta", "sideBySide": "Fianco a fianco", "inline": "In linea", "fileComparison": "Confronto file: {{file1}} vs {{file2}}", "fileTooLarge": "File troppo grande: {{error}}", - "sshConnectionFailed": "Connessione SSH non riuscita. Verifica la tua connessione a {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Impossibile caricare il file: {{error}}", + "sshConnectionFailed": "Connessione SSH non riuscita. Controlla la connessione a {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Caricamento del file non riuscito: {{error}}", "connecting": "Connessione in corso...", - "connectedSuccessfully": "Connessione stabilita con successo.", + "connectedSuccessfully": "Connesso con successo", "totpVerificationFailed": "Verifica TOTP non riuscita", - "warpgateVerificationFailed": "Autenticazione del portale di teletrasporto non riuscita.", + "warpgateVerificationFailed": "Autenticazione Warpgate non riuscita", "authenticationFailed": "Autenticazione non riuscita", - "incorrectPassphrase": "Password errata. Riprova.", + "incorrectPassphrase": "Frase di accesso non corretta. Riprova.", "verificationCodePrompt": "Codice di verifica:", - "changePermissions": "Modifica autorizzazioni", - "currentPermissions": "Autorizzazioni attuali", + "changePermissions": "Modifica permessi", + "currentPermissions": "Permessi attuali", "owner": "Proprietario", "group": "Gruppo", "others": "Altri", - "read": "Leggere", - "write": "Scrivere", - "execute": "Eseguire", - "permissionsChangedSuccessfully": "Autorizzazioni modificate con successo", - "failedToChangePermissions": "Impossibile modificare le autorizzazioni", + "read": "Lettura", + "write": "Scrittura", + "execute": "Esecuzione", + "permissionsChangedSuccessfully": "Permessi modificati con successo", + "failedToChangePermissions": "Modifica dei permessi non riuscita", "name": "Nome", "sortByName": "Nome", "sortByDate": "Data di modifica", - "sortBySize": "Misurare", - "ascending": "Ascendente", - "descending": "Discendente", + "sortBySize": "Dimensione", + "ascending": "Crescente", + "descending": "Decrescente", "root": "Radice", "new": "Nuovo", "sortBy": "Ordina per", "items": "Elementi", - "selected": "Selezionato", - "editor": "Redattore", + "selected": "Selezionati", + "editor": "Editor", "octal": "Ottale", - "storage": "Magazzinaggio", + "storage": "Storage", "disk": "Disco", "used": "Usato", - "of": "Di", - "toggleSidebar": "Attiva/disattiva la barra laterale", + "of": "di", + "toggleSidebar": "Mostra/Nascondi barra laterale", "cannotLoadPdf": "Impossibile caricare il PDF", - "pdfLoadError": "Si è verificato un errore durante il caricamento di questo file PDF.", - "loadingPdf": "Caricamento del PDF in corso...", + "pdfLoadError": "Si è verificato un errore durante il caricamento del file PDF.", + "loadingPdf": "Caricamento PDF in corso...", "loadingPage": "Caricamento pagina in corso..." }, "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", + "copyToHost": "Copia su host…", + "moveToHost": "Sposta su host…", + "copyItemsToHost": "Copia {{count}} elementi su host…", + "moveItemsToHost": "Sposta {{count}} elementi sull'host…", + "noHostsConnected": "Nessun altro host con File Manager disponibile.", + "noHostsConnectedHint": "Aggiungi un altro host SSH con File Manager abilitato nella Gestione Host.", + "selectDestinationHost": "Seleziona 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", + "collapseRecentDestinations": "Comprimi destinazioni recenti", + "expandRecentDestinations": "Espandi destinazioni recenti", + "browseFolders": "Sfoglia cartelle di destinazione", + "browseDestination": "Sfoglia o inserisci percorso", "confirmCopy": "Copia", - "confirmMove": "Mossa", - "transferring": "Trasferimento…", - "compressing": "Compressione…", - "extracting": "Estrazione di…", - "transferringItems": "Trasferimento di {{current}} di {{total}} elementi…", + "confirmMove": "Sposta", + "transferring": "Trasferimento in corso…", + "compressing": "Compressione in corso…", + "extracting": "Estrazione in corso…", + "transferringItems": "Trasferimento {{current}} su {{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", + "destMustBeDirectory": "La destinazione deve essere una directory per il trasferimento 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…", + "copyFolderToHost": "Copia cartella sull'host…", + "moveFolderToHost": "Sposta 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", + "hostAuthRequired": "Autenticazione richiesta — aprire prima File Manager su questo host", + "hostConnectionFailed": "Connessione non riuscita", "metricsTitle": "Tempi di trasferimento", - "metricsPrepare": "Prepara la destinazione: {{duration}}", - "metricsCompress": "Comprimi sulla sorgente: {{duration}}", + "metricsPrepare": "Prepara destinazione: {{duration}}", + "metricsCompress": "Compressione sul 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}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (locale): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Estrazione sulla destinazione: {{duration}}", + "metricsSourceDelete": "Rimozione dal sorgente: {{duration}}", "metricsTotal": "Totale: {{duration}}", "progressCompressing": "Compressione sull'host sorgente…", "progressExtracting": "Estrazione sulla destinazione…", @@ -1849,172 +1982,172 @@ "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.", + "parallelSegmentsHint": "I file di grandi dimensioni vengono suddivisi in blocchi da 256 MB. Più corsie utilizzano connessioni separate (come avviare più trasferimenti) per una maggiore velocità complessiva.", "progressTotalSpeed": "{{speed}} totale ({{lanes}} corsie)", - "progressTransferringItems": "Trasferimento dei file ({{current}} di {{total}})…", + "progressTransferringItems": "Trasferimento file ({{current}} di {{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", + "sourceNotDeletedPartial": "File sorgente mantenuti (trasferimento parziale)", + "jumpHostLimitation": "Entrambi gli host devono essere raggiungibili dal server Termix. Il routing diretto host-to-host non è supportato.", + "cancel": "Annulla", "methodLabel": "Metodo di trasferimento", "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.", + "methodAutoHint": "Sceglie tar o SFTP per file in base al numero, dimensione e comprimibilità dei file. I file singoli usano sempre SFTP in streaming.", + "methodTarHint": "Comprime sul sorgente, trasferisce un archivio, estrae sulla destinazione. Richiede tar su entrambi gli host Unix.", + "methodItemSftpHint": "Trasferisce ogni file singolarmente tramite SFTP. Funziona su tutti gli host, 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.", + "methodPreviewError": "Impossibile visualizzare l'anteprima del metodo di trasferimento. Il server sceglierà comunque un metodo all'avvio.", + "methodPreviewWillUseTar": "Userà: Archivio Tar", + "methodPreviewWillUseItemSftp": "Userà: SFTP per file", + "methodPreviewScanSummary": "{{fileCount}} file, {{totalSize}} in totale (scansionati sull'host sorgente).", + "methodItemSftpLimitation": "Ogni file utilizza lo stesso flusso SFTP come una copia di file singolo, uno dopo l'altro. L'avanzamento è combinato su tutti i file, quindi la barra si muove lentamente durante i file di grandi dimensioni.", "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": "Il 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": "Hai scelto SFTP per-file.", + "user_tar": "Hai scelto archivio tar.", + "tar_unavailable": "Tar non è disponibile su uno o entrambi gli host — verrà utilizzato SFTP per-file.", + "windows_host": "È coinvolto un host Windows — tar non viene utilizzato.", + "auto_multi_large": "Auto: più file, incluso un file grande ({{largestSize}}) con dati comprimibili — tar li raggruppa in un unico trasferimento.", + "auto_single_large_in_archive": "Auto: un file grande ({{largestSize}}) in questo set — SFTP per-file.", + "auto_many_incompressible": "Auto: dati per lo più incomprimibili — SFTP per-file.", + "auto_many_files": "Auto: molti file ({{fileCount}}) — tar riduce il sovraccarico per-file.", + "auto_default": "Auto: SFTP per-file per questo set." }, - "progressCancel": "Cancellare", - "progressCancelling": "Annullamento…", - "progressStalled": "In stallo", - "resumedHint": "Riconnessione stabilita a un trasferimento attivo avviato in un'altra finestra.", + "progressCancel": "Annulla", + "progressCancelling": "Annullamento in corso…", + "progressStalled": "Bloccato", + "resumedHint": "Riconnesso 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", + "transferCancelledCopyHint": "Potrebbero rimanere file parziali sulla destinazione.", + "transferCancelledMoveHint": "Potrebbero rimanere file parziali sulla destinazione. I file sorgente non sono stati rimossi.", + "cleanupDestFiles": "Pulisci destinazione", + "cleanupDestFilesSuccess": "File parziali rimossi dalla destinazione", "cleanupDestFilesPartial": "Alcuni file parziali non sono stati rimossi", - "cleanupDestFilesNothing": "Non c'è nulla da pulire a destinazione.", + "cleanupDestFilesNothing": "Niente da pulire sulla 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." + "retryTransferError": "Riprova fallita", + "transferFailedRetryHint": "I dati parziali sono stati conservati sulla destinazione. Il nuovo tentativo riprenderà quando la connessione sarà ripristinata." }, "tunnels": { "noSshTunnels": "Nessun tunnel SSH", - "createFirstTunnelMessage": "Non hai ancora creato alcun tunnel SSH. Configura le connessioni tunnel in Host Manager per iniziare.", - "connected": "Collegato", + "createFirstTunnelMessage": "Non hai ancora creato nessun tunnel SSH. Configura le connessioni tunnel nel Gestore host per iniziare.", + "connected": "Connesso", "disconnected": "Disconnesso", "connecting": "Connessione in corso...", "error": "Errore", "canceling": "Annullamento in corso...", - "connect": "Collegare", + "connect": "Connetti", "disconnect": "Disconnetti", - "cancel": "Cancellare", + "cancel": "Annulla", "port": "Porta", - "localPort": "Porto locale", + "localPort": "Porta locale", "remotePort": "Porta remota", "currentHostPort": "Porta host corrente", - "endpointPort": "Porta dell'endpoint", + "endpointPort": "Porta endpoint", "bindIp": "IP locale", - "endpointSshConfig": "Configurazione SSH dell'endpoint", - "endpointSshHost": "Host SSH dell'endpoint", + "endpointSshConfig": "Configurazione SSH endpoint", + "endpointSshHost": "Host SSH endpoint", "endpointSshHostPlaceholder": "Seleziona un host configurato", - "endpointSshHostRequired": "Selezionare un host SSH di endpoint per ogni tunnel client.", + "endpointSshHostRequired": "Seleziona un host SSH endpoint per ogni tunnel client.", "attempt": "Tentativo {{current}} di {{max}}", "nextRetryIn": "Prossimo tentativo tra {{seconds}} secondi", "clientTunnels": "Tunnel client", "clientTunnel": "Tunnel client", "addClientTunnel": "Aggiungi tunnel client", - "noClientTunnels": "Su questo desktop non è configurato alcun tunnel client.", - "tunnelName": "Nome del tunnel", + "noClientTunnels": "Nessun tunnel client configurato su questo desktop.", + "tunnelName": "Nome tunnel", "remoteHost": "Host remoto", "autoStart": "Avvio automatico", "clientAutoStartDesc": "Si avvia all'apertura del client desktop e rimane connesso.", - "clientManualStartDesc": "Utilizzare i pulsanti Avvia e Arresta da questa riga. Termix non la aprirà automaticamente.", - "clientRemoteServerNote": "L'inoltro remoto potrebbe richiedere le autorizzazioni AllowTcpForwarding e GatewayPorts sul server SSH dell'endpoint. La porta remota viene chiusa quando questo desktop si disconnette.", + "clientManualStartDesc": "Usa Avvia e Arresta da questa riga. Termix non lo avvierà automaticamente.", + "clientRemoteServerNote": "L'inoltro remoto potrebbe richiedere AllowTcpForwarding e GatewayPorts sul server SSH endpoint. La porta remota viene chiusa quando questo desktop si disconnette.", "clientTunnelStarted": "Tunnel client avviato", - "clientTunnelStopped": "Tunnel client interrotto", - "tunnelTestSucceeded": "Il test in galleria è riuscito", - "tunnelTestFailed": "Il test in galleria è fallito.", + "clientTunnelStopped": "Tunnel client arrestato", + "tunnelTestSucceeded": "Test del tunnel riuscito", + "tunnelTestFailed": "Test del tunnel fallito", "localSaved": "Tunnel client salvati", - "localSaveError": "Impossibile salvare i tunnel client locali.", - "invalidBindIp": "L'indirizzo IP locale deve essere un indirizzo IPv4 valido.", - "invalidLocalTargetIp": "L'indirizzo IP di destinazione locale deve essere un indirizzo IPv4 valido.", + "localSaveError": "Salvataggio dei tunnel client locali non riuscito", + "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 di destinazione locale deve essere compresa tra 1 e 65535.", - "invalidEndpointPort": "La porta dell'endpoint deve essere compresa tra 1 e 65535.", - "duplicateAutoStartBind": "Solo un tunnel client ad avvio automatico può utilizzare {{bind}}.", - "manualControlError": "Impossibile aggiornare lo stato del tunnel.", + "invalidEndpointPort": "La porta endpoint deve essere compresa tra 1 e 65535.", + "duplicateAutoStartBind": "Solo un tunnel client con avvio automatico può utilizzare {{bind}}.", + "manualControlError": "Aggiornamento dello stato del tunnel non riuscito.", "active": "Attivo", - "start": "Inizio", - "stop": "Fermare", - "test": "Test", + "start": "Avvia", + "stop": "Ferma", + "test": "Verifica", "type": "Tipo di tunnel", "typeLocal": "Locale (-L)", "typeRemote": "Remoto (-R)", "typeDynamic": "Dinamico (-D)", - "typeServerLocalDesc": "Host corrente verso l'endpoint.", - "typeServerRemoteDesc": "Endpoint di ritorno all'host corrente.", - "typeClientLocalDesc": "Dal computer locale al punto finale.", - "typeClientRemoteDesc": "Punto di ritorno al computer locale.", + "typeServerLocalDesc": "Dall'host attuale all'endpoint.", + "typeServerRemoteDesc": "Dall'endpoint all'host attuale.", + "typeClientLocalDesc": "Dal computer locale all'endpoint.", + "typeClientRemoteDesc": "Dall'endpoint al computer locale.", "typeClientDynamicDesc": "SOCKS sul computer locale.", - "typeDynamicDesc": "Inoltrare il traffico SOCKS5 CONNECT tramite SSH", - "forwardDescriptionServerLocal": "Host corrente {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host corrente {{sourcePort}}.", - "forwardDescriptionServerDynamic": "CALZINI sull'host corrente {{sourcePort}}.", + "typeDynamicDesc": "Inoltra traffico SOCKS5 CONNECT via SSH", + "forwardDescriptionServerLocal": "Host attuale {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host attuale {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS su host attuale {{sourcePort}}.", "forwardDescriptionClientLocal": "Locale {{sourcePort}} → remoto {{endpointPort}}.", "forwardDescriptionClientRemote": "Remoto {{sourcePort}} → locale {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS sulla porta locale {{sourcePort}}.", + "forwardDescriptionClientDynamic": "SOCKS su porta locale {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → CALZINI tramite {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", "autoNameClientLocal": "Locale {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → locale {{localPort}}", - "autoNameClientDynamic": "CALZINI {{localPort}} via {{endpoint}}", - "route": "Itinerario:", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Percorso:", "lastStarted": "Ultimo avvio", "lastTested": "Ultimo test", "lastError": "Ultimo errore", - "maxRetries": "Numero massimo di tentativi", - "maxRetriesDescription": "Numero massimo di tentativi di ripetizione.", - "retryInterval": "Intervallo di riprova (secondi)", - "retryIntervalDescription": "Tempo di attesa tra un tentativo e l'altro.", + "maxRetries": "Tentativi massimi", + "maxRetriesDescription": "Numero massimo di tentativi.", + "retryInterval": "Intervallo tra tentativi (secondi)", + "retryIntervalDescription": "Tempo di attesa tra i tentativi.", "local": "Locale", "remote": "Remoto", "destination": "Destinazione", - "host": "Ospite", + "host": "Host", "mode": "Modalità", "noHostSelected": "Nessun host selezionato", - "working": "Lavorando..." + "working": "In corso..." }, "cardGrid": { - "dragToMove": "Trascinare per spostare", + "dragToMove": "Trascina per spostare", "dragToResize": "Trascina per ridimensionare", - "changeWidth": "Cambia larghezza", + "changeWidth": "Modifica larghezza", "removeCard": "Rimuovi {{label}}", - "addCard": "Aggiungere", + "addCard": "Aggiungi", "columns": "Colonne", - "empty": "Niente carte. Usa la funzione \"Aggiungi\" qui sotto per inserire le carte." + "empty": "Nessuna scheda. Usa Aggiungi sotto per inserire le schede." }, "hostMetrics": { - "cpu": "processore", + "cpu": "CPU", "memory": "Memoria", "disk": "Disco", "network": "Rete", "uptime": "Tempo di attività", "processes": "Processi", "available": "Disponibile", - "free": "Gratuito", + "free": "Libero", "connecting": "Connessione in corso...", - "connectionFailed": "Impossibile connettersi al server", - "naCpus": "CPU non disponibili", - "cpuCores_one": "{{count}} Nucleo", - "cpuCores_other": "{{count}} Nuclei", - "cpuUsage": "Utilizzo della CPU", - "memoryUsage": "Utilizzo della memoria", - "diskUsage": "Utilizzo del disco", + "connectionFailed": "Connessione al server non riuscita", + "naCpus": "CPU N/D", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Core", + "cpuUsage": "Utilizzo CPU", + "memoryUsage": "Utilizzo memoria", + "diskUsage": "Utilizzo disco", "temperature": "Temperatura", "highestTemperature": "Temperatura massima", - "failedToFetchHostConfig": "Impossibile recuperare la configurazione dell'host", + "failedToFetchHostConfig": "Recupero configurazione host non riuscito", "serverOffline": "Server offline", "cannotFetchMetrics": "Impossibile recuperare le metriche dal server offline", "totpFailed": "Verifica TOTP non riuscita", @@ -2030,283 +2163,284 @@ "noInterfacesFound": "Nessuna interfaccia di rete trovata", "noProcessesFound": "Nessun processo trovato", "processesTotal": "totale", - "processesRunning": "corsa", + "processesRunning": "in esecuzione", "loginStats": "Statistiche di accesso SSH", "noRecentLoginData": "Nessun dato di accesso recente", "executingQuickAction": "Esecuzione di {{name}}...", "quickActionSuccess": "{{name}} completato con successo", - "quickActionFailed": "{{name}} fallito", + "quickActionFailed": "{{name}} non riuscito", "quickActionError": "Impossibile eseguire {{name}}", "ports": { - "title": "Porte di ascolto", + "title": "Porte in ascolto", "protocol": "Protocollo", "port": "Porta", "address": "Indirizzo", "process": "Processo", - "search": "Cerca le porte...", - "allProtocols": "Tutto", - "noData": "Nessun dato sulle porte di ascolto" + "search": "Cerca porte...", + "allProtocols": "Tutti", + "noData": "Nessun dato sulle porte in ascolto" }, "firewall": { "title": "Firewall", "inactive": "Inattivo", "policy": "Politica", "rules": "regole", - "noRules": "In questa catena non ci sono regole.", - "noData": "Nessun dato firewall disponibile", + "noRules": "Nessuna regola in questa catena", + "noData": "Nessun dato sul firewall disponibile", "action": "Azione", "protocol": "Proto", "port": "Porta", - "source": "Fonte", + "source": "Sorgente", "anywhere": "Ovunque", - "chains": "catene" + "chains": "Catene" }, "loadAvg": "Carico medio", - "swap": "Scambio", + "swap": "Swap", "architecture": "Architettura", - "refresh": "Rinfrescato", + "refresh": "Aggiorna", "retry": "Riprova", - "customize": "Personalizza il layout", - "reset": "Reset", - "tabLive": "Vivere", - "editModeInstructions": "Trascina le schede per riorganizzarle, trascina il bordo inferiore per ridimensionarle e usa il pulsante \"Larghezza\" per modificare l'estensione di una scheda. Aggiungi o rimuovi schede qui sotto.", + "customize": "Personalizza layout", + "reset": "Reimposta", + "tabLive": "Live", + "editModeInstructions": "Trascina le schede per riordinarle, trascina il bordo inferiore per ridimensionarle e usa il pulsante della larghezza per modificare l'estensione della scheda. Aggiungi o rimuovi schede qui sotto.", "managers": { - "services": "Fibre", - "processInspector": "Ispettore di processo", - "logViewer": "Visualizzatore del registro", - "cron": "Lavori Cron", + "services": "Servizi", + "processInspector": "Ispettore dei processi", + "logViewer": "Visualizzatore di log", + "cron": "Cron", "packages": "Pacchetti", "ssl": "Certificati SSL", "firewall": "Firewall", - "users": "Utenti e autorizzazioni", - "healthCheck": "Controlli sanitari", + "users": "Utenti e permessi", + "healthCheck": "Controlli di integrità", "diskBreakdown": "Analisi del disco", "systemdTimers": "Timer", - "topMemory": "Top by Memory", + "topMemory": "Principali per memoria", "noData": "Nessun dato", - "sudoHint": "Nell'editor degli host, imposta una password sudo per questo host per abilitare le azioni privilegiate.", - "filter": "Filtro...", - "start": "Inizio", - "stop": "Fermare", - "restart": "Ricomincia", + "sudoHint": "Imposta una password sudo per questo host nell'editor host per abilitare azioni privilegiate.", + "filter": "Filtra...", + "start": "Avvia", + "stop": "Ferma", + "restart": "Riavvia", "actionDone": "{{name}} aggiornato", - "actionFailed": "L'azione è fallita", + "actionFailed": "Azione non riuscita", "signalSent": "Segnale inviato al PID {{pid}}", - "killHint": "Clic: termina (SIGTERM). Clic destro: termina forzatamente (SIGKILL).", - "working": "Lavorando...", - "update": "Aggiornamento", + "killHint": "Clic: termina (SIGTERM). Clic destro: terminazione forzata (SIGKILL).", + "working": "In corso...", + "update": "Aggiorna", "upgradeAll": "Aggiorna tutto", - "allUpToDate": "Tutto è aggiornato", + "allUpToDate": "Tutto aggiornato", "save": "Salva", "command": "Comando", "enabled": "Abilitato", "cronSaved": "Crontab aggiornato", - "clients": "Clienti", - "dryRun": "Prova a secco", - "renew": "Rinnovare", + "clients": "Client", + "dryRun": "Prova", + "renew": "Rinnova", "noAcmeClient": "Nessun client ACME (certbot o acme.sh) trovato su questo host.", "addInputRule": "Aggiungi regola INPUT", - "firewallWarning": "Le modifiche sono visibili solo in fase di esecuzione finché non vengono salvate. Fai attenzione a non bloccare il tuo account.", + "firewallWarning": "Le modifiche sono solo runtime finché non vengono rese permanenti. Fai attenzione a non rimanere bloccato fuori.", "ruleApplied": "Regola applicata", "invalidPort": "Inserisci una porta valida (1-65535)", "newUsername": "Nuovo nome utente", - "addUser": "Aggiungere", + "addUser": "Aggiungi", "deleteUser": "Elimina utente", - "noHealthChecks": "Nessun controllo di integrità configurato al momento.", - "follow": "Seguire", + "noHealthChecks": "Nessun health check configurato al momento.", + "follow": "Segui", "noLogData": "Nessun output di log.", "tree": "Albero", - "enableDisable": "Abilita/disabilita all'avvio", + "enableDisable": "Abilita / disabilita all'avvio", "grantSudo": "Concedi sudo", "revokeSudo": "Revoca sudo", - "sslIssueCert": "Rilascio del certificato", + "sslIssueCert": "Emetti certificato", "sslExpired": "Scaduto", "sslInDays": "in {{days}}d", "sslNeedDomain": "Inserisci almeno un dominio", - "sslIssued": "Certificato rilasciato", + "sslIssued": "Certificato emesso", "sslDomainsPlaceholder": "example.com, www.example.com", - "sslHttpStandalone": "HTTP (autonomo)", + "sslHttpStandalone": "HTTP (standalone)", "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "Provider DNS (ad esempio Cloudflare)", + "sslDnsProvider": "Provider DNS (es. cloudflare)", "sslIssueHint": "Le credenziali del provider DNS devono essere già configurate sull'host.", - "sslRevoke": "Revoca del certificato", + "sslRevoke": "Revoca certificato", "sslRevoked": "Certificato revocato", - "sslRevokeConfirm": "Revoca e rimuovi il certificato \"{{name}}\"? Questa operazione non può essere annullata.", - "healthRun": "Correre", + "sslRevokeConfirm": "Revocare e rimuovere il certificato \"{{name}}\"? Questa azione non può essere annullata.", + "healthRun": "Esegui", "healthName": "Nome", "healthTarget": "Host / indirizzo", "healthAddCheck": "Aggiungi controllo", - "healthSaved": "Controlli sanitari salvati", - "healthMissingFields": "Ogni controllo necessita di un nome e di un obiettivo", + "healthSaved": "Controlli di integrità salvati", + "healthMissingFields": "Ogni controllo richiede un nome e un obiettivo", "logFile": "File", "logUnit": "Unità", - "logCustomPath": "Percorso personalizzato nella directory /var/log (facoltativo)", - "logGrep": "Linee di filtraggio...", - "firewallPersist": "Regole persistenti", - "firewallPersisted": "Le regole del firewall sono rimaste invariate", + "logCustomPath": "Percorso personalizzato in /var/log (opzionale)", + "logGrep": "Filtra righe...", + "firewallPersist": "Rendi permanenti le regole", + "firewallPersisted": "Regole del firewall rese permanenti", "wireguard": "WireGuard", - "tailscale": "Squama della coda", + "tailscale": "Tailscale", "wgNotInstalled": "WireGuard non è installato su questo host", "wgNoInterfaces": "Nessuna interfaccia WireGuard configurata", - "wgInterfaceUp": "Menzionare", - "wgInterfaceDown": "Abbatti", - "wgBringingUp": "Portando {{name}} su...", - "wgBringingDown": "Abbattere {{name}}...", + "wgInterfaceUp": "Attiva", + "wgInterfaceDown": "Disattiva", + "wgBringingUp": "Attivazione di {{name}} in corso...", + "wgBringingDown": "Disattivazione di {{name}} in corso...", "wgInterfaceUpDone": "{{name}} è attivo", - "wgInterfaceDownDone": "{{name}} non funziona", + "wgInterfaceDownDone": "{{name}} è disattivo", "wgListenPort": "Porta", "wgPublicKey": "Chiave pubblica", - "wgEndpoint": "Punto finale", - "wgAllowedIPs": "Indirizzi IP consentiti", - "wgLastHandshake": "Ultima stretta di mano", + "wgEndpoint": "Endpoint", + "wgAllowedIPs": "IP consentiti", + "wgLastHandshake": "Ultimo handshake", "wgHandshakeNever": "Mai", - "wgTransfer": "Trasferire", + "wgTransfer": "Trasferimento", "tsNotInstalled": "Tailscale non è installato su questo host", - "tsRunning": "Corsa", - "tsStopped": "Si è fermato", - "tsEnable": "Collegare", + "tsRunning": "In esecuzione", + "tsStopped": "Arrestato", + "tsEnable": "Connetti", "tsDisable": "Disconnetti", - "tsEnabling": "Collegamento a Tailscale...", + "tsEnabling": "Connessione a Tailscale in corso...", "tsDisabling": "Disconnessione da Tailscale in corso...", "tsEnabled": "Tailscale connesso", - "tsDisabled": "Bilancia di coda scollegata", + "tsDisabled": "Tailscale disconnesso", "tsIPs": "IP Tailscale", - "tsPeers": "Colleghi", + "tsPeers": "Peer", "tsOnline": "Online", "tsOffline": "Offline", "tsExitNode": "Nodo di uscita", - "tsExitNodeActive": "Uscita dal nodo attivo", + "tsExitNodeActive": "Nodo di uscita attivo", "tsHostname": "Nome host" } }, "auth": { "tagline": "Gestione SSH e desktop remoto self-hosted", "loginTitle": "Bentornato", - "registerTitle": "Creare un account", - "forgotPassword": "Ha dimenticato la password?", - "rememberMe": "Ricorda il dispositivo per 30 giorni (include il codice TOTP)", + "registerTitle": "Crea account", + "forgotPassword": "Password dimenticata?", + "rememberMe": "Ricorda dispositivo per 30 giorni (include TOTP)", "noAccount": "Non hai un account?", "hasAccount": "Hai già un account?", "twoFactorAuth": "Autenticazione a due fattori", - "enterCode": "Inserisci il codice di verifica", - "backupCode": "Oppure usa il codice di backup", - "verifyCode": "Verifica il codice", - "redirectingToApp": "Reindirizzamento all'app...", + "enterCode": "Inserisci codice di verifica", + "backupCode": "Oppure usa codice di backup", + "verifyCode": "Verifica codice", + "redirectingToApp": "Reindirizzamento all'app in corso...", "sshAuthenticationRequired": "Autenticazione SSH richiesta", - "sshNoKeyboardInteractive": "Autenticazione interattiva tramite tastiera non disponibile", + "sshNoKeyboardInteractive": "Autenticazione keyboard-interactive non disponibile", "sshAuthenticationFailed": "Autenticazione non riuscita", - "sshAuthenticationTimeout": "Timeout di autenticazione", - "sshNoKeyboardInteractiveDescription": "Il server non supporta l'autenticazione interattiva tramite tastiera. Si prega di fornire la password o la chiave SSH.", - "sshAuthFailedDescription": "Le credenziali fornite non sono corrette. Si prega di riprovare con credenziali valide.", + "sshAuthenticationTimeout": "Timeout autenticazione", + "sshNoKeyboardInteractiveDescription": "Il server non supporta l'autenticazione keyboard-interactive. Fornisci 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": "Si prega di fornire le proprie credenziali SSH per connettersi a questo server.", + "sshProvideCredentialsDescription": "Fornisci le credenziali SSH per connetterti a questo server.", "sshPasswordDescription": "Inserisci la password per questa connessione SSH.", "sshKeyPasswordDescription": "Se la tua chiave SSH è crittografata, inserisci qui la passphrase.", - "passphraseRequired": "È richiesta una passphrase.", + "passphraseRequired": "Passphrase richiesta", "passphraseRequiredDescription": "La chiave SSH è crittografata. Inserisci la passphrase per sbloccarla.", "back": "Indietro", "firstUser": "Primo utente", - "firstUserMessage": "Sei il primo utente e ti verrà assegnato il ruolo di amministratore. Puoi visualizzare le impostazioni di amministrazione nel menu a tendina utente nella barra laterale. Se ritieni che si tratti di un errore, controlla i log di Docker o segnala un problema su GitHub.", + "firstUserMessage": "Sei il primo utente e verrai designato amministratore. Puoi visualizzare le impostazioni di amministrazione nel menu a tendina dell'utente nella barra laterale. Se pensi che sia un errore, controlla i log di Docker o crea un issue su GitHub.", "external": "Esterno", - "loginWithExternal": "Accedi con un provider esterno", - "loginWithExternalDesc": "Accedi utilizzando il 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.", + "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. Utilizza la versione web per l'accesso OIDC.", "loginWithProvider": "Accedi con {{name}}", - "orContinueWith": "oppure continuare con", + "orContinueWith": "oppure continua con", "ldapUsername": "Nome utente LDAP", "ldapPassword": "Password LDAP", - "ldapSignIn": "Registrazione", + "ldapSignIn": "Accedi", "ldapLoginFailed": "Accesso LDAP non riuscito", "resetPasswordButton": "Reimposta password", - "sendResetCode": "Invia il codice di ripristino", - "resetCodeDesc": "Inserisci il tuo nome utente per ricevere un codice di reimpostazione della password. Il codice verrà registrato nei log del container Docker.", - "resetCode": "Codice di ripristino", - "verifyCodeButton": "Verifica il codice", + "sendResetCode": "Invia codice di reset", + "resetCodeDesc": "Inserisci il tuo nome utente per ricevere un codice di reset della password. Il codice verrà registrato nei log del container Docker.", + "resetCode": "Codice di reset", + "verifyCodeButton": "Verifica codice", "enterResetCode": "Inserisci il codice a 6 cifre dai log del container Docker per l'utente:", "newPassword": "Nuova password", "confirmNewPassword": "Conferma password", - "enterNewPassword": "Inserisci la tua nuova password per l'utente:", - "signUp": "Iscrizione", - "desktopApp": "Applicazione desktop", - "loggingInToDesktopApp": "Accesso all'applicazione desktop", - "loadingServer": "Caricamento del server in corso...", - "dataLossWarning": "Reimpostando la password in questo modo, verranno eliminati tutti gli host SSH salvati, le credenziali e gli altri dati crittografati. Questa operazione è irreversibile. Utilizzare questa procedura solo se si è dimenticata la password e non si è effettuato l'accesso.", + "enterNewPassword": "Inserisci la nuova password per l'utente:", + "signUp": "Registrati", + "desktopApp": "App desktop", + "loggingInToDesktopApp": "Accesso all'app desktop in corso", + "loadingServer": "Caricamento server in corso...", + "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. Utilizza 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. Si prega di contattare l'amministratore.", - "passwordLoginDisabledDesc": "L'accesso tramite password è disabilitato. Utilizzare una chiave di accesso o un provider di autenticazione esterno.", - "signInWithPasskey": "Accedi con la password", - "passkeyLoginFailed": "Accesso con password non riuscito", + "authenticationDisabledDesc": "Tutti i metodi di autenticazione sono attualmente disabilitati. Contatta l'amministratore.", + "passwordLoginDisabledDesc": "L'accesso con password è disabilitato. Utilizza una passkey o un provider di autenticazione esterno.", + "signInWithPasskey": "Accedi con passkey", + "passkeyLoginFailed": "Accesso con passkey non riuscito", "attemptsRemaining": "{{count}} tentativi rimanenti", - "confirmResetDataWipe": "Questo account non ha effettuato l'accesso dall'aggiornamento della crittografia, pertanto i dati memorizzati non possono essere recuperati senza la vecchia password. Il ripristino eliminerà definitivamente host, credenziali e snippet. Continuare?" + "confirmResetDataWipe": "Questo account non ha effettuato l'accesso dopo l'aggiornamento della crittografia, pertanto i dati archiviati non possono essere recuperati senza la vecchia password. Il ripristino eliminerà definitivamente host, credenziali e snippet. Continuare?" }, "hostKey": { - "verifyNewHost": "Verifica la chiave host SSH", + "verifyNewHost": "Verifica chiave host SSH", "keyChangedWarning": "Chiave host SSH modificata", "firstConnectionTitle": "Prima connessione a questo host", - "firstConnectionDescription": "Non è possibile stabilire l'autenticità di questo host. Verifica che l'impronta digitale corrisponda a quella prevista.", - "keyChangedDescription": "La chiave host per questo server è cambiata dall'ultima connessione. Ciò potrebbe indicare un problema di sicurezza.", + "firstConnectionDescription": "L'autenticità di questo host non può essere stabilita. Verifica che l'impronta corrisponda a quanto ti aspetti.", + "keyChangedDescription": "La chiave host di questo server è cambiata dall'ultima connessione. Ciò potrebbe indicare un problema di sicurezza.", "previousKey": "Chiave precedente", - "newFingerprint": "Nuova impronta digitale", - "fingerprint": "Impronta digitale", - "verifyInstructions": "Se ti fidi di questo host, fai clic su Accetta per continuare e salvare questa impronta digitale per le connessioni future.", + "newFingerprint": "Nuova impronta", + "fingerprint": "Impronta", + "verifyInstructions": "Se ti fidi di questo host, fai clic su Accetta per continuare e salvare questa impronta per le connessioni future.", "securityWarning": "Avviso di sicurezza", "acceptAndContinue": "Accetta e continua", - "acceptNewKey": "Accetta la nuova chiave e continua" + "acceptNewKey": "Accetta nuova chiave e continua" }, "errors": { "databaseConnection": "Impossibile connettersi al database", "unknownError": "Errore sconosciuto", "loginFailed": "Accesso non riuscito", "failedPasswordReset": "Impossibile avviare il ripristino della password", - "failedVerifyCode": "Impossibile verificare il codice di ripristino", - "failedCompleteReset": "Impossibile completare il ripristino della password", + "failedVerifyCode": "Verifica del codice di ripristino non riuscita", + "failedCompleteReset": "Completamento del ripristino della password non riuscito", "invalidTotpCode": "Codice TOTP non valido", - "failedOidcLogin": "Impossibile avviare il login OIDC", - "silentSigninOidcUnavailable": "È stata richiesta la modalità di accesso silenzioso, ma l'accesso tramite OIDC non è disponibile.", - "failedUserInfo": "Impossibile ottenere le informazioni utente dopo il login.", + "failedOidcLogin": "Impossibile avviare l'accesso OIDC", + "silentSigninOidcUnavailable": "È stato richiesto l'accesso silenzioso, ma l'accesso OIDC non è disponibile.", + "failedUserInfo": "Impossibile ottenere le informazioni utente dopo l'accesso", "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": "L'accesso tramite nome utente e password è attualmente disabilitato.", - "sessionExpired": "Sessione scaduta - effettua nuovamente l'accesso.", - "totpRateLimited": "Numero di tentativi limitato: troppi tentativi di verifica TOTP. Riprova più tardi.", - "totpRateLimitedWithTime": "Tentativi limitati: troppi tentativi di verifica TOTP. Attendi {{time}} secondi prima di riprovare.", - "resetCodeRateLimited": "Numero di tentativi limitato: troppi tentativi di verifica. Riprova più tardi.", - "resetCodeRateLimitedWithTime": "Numero di tentativi limitato: troppi tentativi di verifica. Attendi {{time}} secondi prima di riprovare.", - "authTokenSaveFailed": "Impossibile salvare il token di autenticazione", - "failedToLoadServer": "Impossibile caricare il server" + "passwordLoginDisabled": "L'accesso con nome utente/password è attualmente disabilitato", + "sessionExpired": "Sessione scaduta - effettua di nuovo l'accesso", + "totpRateLimited": "Limite di tentativi superato: troppi tentativi di verifica TOTP. Riprova più tardi.", + "totpRateLimitedWithTime": "Limite di tentativi superato: troppi tentativi di verifica TOTP. Attendi {{time}} secondi prima di riprovare.", + "resetCodeRateLimited": "Limite di tentativi superato: troppi tentativi di verifica. Riprova più tardi.", + "resetCodeRateLimitedWithTime": "Limite di tentativi raggiunto: troppi tentativi di verifica. Attendere {{time}} secondi prima di riprovare.", + "authTokenSaveFailed": "Salvataggio del token di autenticazione non riuscito", + "failedToLoadServer": "Caricamento del server non riuscito", + "remoteServerRequired": "È necessario un server remoto. Per utilizzare questo tipo di connessione, collega un server remoto nelle Impostazioni." }, "messages": { - "registrationDisabled": "La registrazione di nuovi account è momentaneamente disabilitata da un amministratore. Effettua l'accesso o contatta un amministratore.", - "userNotAllowed": "Il tuo account non è autorizzato alla registrazione. Contatta un amministratore.", - "databaseConnectionFailed": "Impossibile connettersi al server del database.", - "resetCodeSent": "Codice di reset inviato ai log di Docker", + "registrationDisabled": "La registrazione di nuovi 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": "Codice di reset inviato ai log Docker", "codeVerified": "Codice verificato con successo", "passwordResetSuccess": "Password reimpostata con successo", - "loginSuccess": "Accesso effettuato con successo", - "registrationSuccess": "Registrazione avvenuta con successo" + "loginSuccess": "Login effettuato con successo", + "registrationSuccess": "Registrazione completata con successo" }, "profile": { - "c2sTunnelConfigDesc": "Tunnel desktop locali diretti a host SSH configurati.", - "c2sTunnelPresets": "Preimpostazioni del tunnel client", - "c2sTunnelPresetsDesc": "Salva l'elenco dei tunnel locali di questo client desktop come preimpostazione del server denominata oppure carica una preimpostazione in questo client.", - "c2sTunnelPresetsUnavailable": "Le impostazioni predefinite del tunnel client sono disponibili solo nel client desktop.", - "c2sPresetName": "Nome predefinito", - "c2sPresetNamePlaceholder": "Nome preimpostato del client", - "c2sPresetToLoad": "Preimpostato per caricare", - "c2sNoPresetSelected": "Nessun preset selezionato", - "c2sNoPresets": "Nessun preset salvato", - "c2sLoadPreset": "Carico", - "c2sCurrentLocalConfig": "{{count}} tunnel client locali configurati su questo desktop.", - "c2sPresetSyncNote": "I preset sono snapshot espliciti; caricandone uno, si sostituisce l'elenco dei tunnel client locali di questo client desktop.", - "c2sPresetSaved": "Impostazioni predefinite del tunnel client salvate", - "c2sPresetLoaded": "Preimpostazione del tunnel client caricata localmente", - "c2sPresetRenamed": "Rinominato il preset del tunnel client", - "c2sPresetDeleted": "Preimpostazione del tunnel client eliminata", - "c2sPresetLoadError": "Impossibile caricare le preimpostazioni del tunnel client" + "c2sTunnelConfigDesc": "Tunnel desktop locale verso host SSH configurati.", + "c2sTunnelPresets": "Preimpostazioni tunnel client", + "c2sTunnelPresetsDesc": "Salva l'elenco dei tunnel locali di questo client desktop come preimpostazione server con nome, oppure carica una preimpostazione in questo client.", + "c2sTunnelPresetsUnavailable": "Le preimpostazioni tunnel client sono disponibili solo nel client desktop.", + "c2sPresetName": "Nome preimpostazione", + "c2sPresetNamePlaceholder": "Nome preimpostazione client", + "c2sPresetToLoad": "Preimpostazione da caricare", + "c2sNoPresetSelected": "Nessuna preimpostazione selezionata", + "c2sNoPresets": "Nessuna preimpostazione salvata", + "c2sLoadPreset": "Carica", + "c2sCurrentLocalConfig": "{{count}} tunnel client locale configurato/i su questo desktop.", + "c2sPresetSyncNote": "Le preimpostazioni sono snapshot espliciti; caricandone una si sostituisce l'elenco dei tunnel client locali di questo desktop.", + "c2sPresetSaved": "Preimpostazione tunnel client salvata", + "c2sPresetLoaded": "Preimpostazione tunnel client caricata localmente", + "c2sPresetRenamed": "Preimpostazione tunnel client rinominata", + "c2sPresetDeleted": "Preimpostazione tunnel client eliminata", + "c2sPresetLoadError": "Caricamento delle preimpostazioni tunnel client non riuscito" }, "placeholders": { "maxRetries": "3", @@ -2314,68 +2448,68 @@ "language": "Lingua", "keyPassword": "password chiave", "pastePrivateKey": "Incolla qui la tua chiave privata...", - "localListenerHost": "127.0.0.1 (ascolta in locale)", + "localListenerHost": "127.0.0.1 (ascolta localmente)", "localTargetHost": "127.0.0.1 (destinazione su questo computer)", "socksListenerHost": "127.0.0.1 (listener SOCKS)", - "enterPassword": "Inserisci la tua password", + "enterPassword": "Inserisci la password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Pannello di controllo", - "loading": "Caricamento della dashboard...", + "title": "Dashboard", + "loading": "Caricamento dashboard...", "github": "GitHub", "support": "Supporto", - "discord": "Discordia", - "docs": "Documenti", - "donate": "Donare", - "serverOverview": "Panoramica del server", + "discord": "Discord", + "docs": "Documentazione", + "donate": "Dona", + "serverOverview": "Panoramica server", "version": "Versione", "upToDate": "Aggiornato", "updateAvailable": "Aggiornamento disponibile", "beta": "Beta", - "uptime": "Tempo di attività", - "database": "Schermata", - "healthy": "Salutare", + "uptime": "Uptime", + "database": "Database", + "healthy": "In salute", "error": "Errore", - "totalHosts": "Numero totale di host", - "totalTunnels": "Total Tunnels", + "totalHosts": "Host totali", + "totalTunnels": "Tunnel totali", "totalCredentials": "Credenziali totali", - "recentActivity": "Attività recenti", - "reset": "Reset", - "loadingRecentActivity": "Caricamento delle attività recenti...", + "recentActivity": "Attività recente", + "reset": "Reimposta", + "loadingRecentActivity": "Caricamento attività recente...", "noRecentActivity": "Nessuna attività recente", "quickActions": "Azioni rapide", "addHost": "Aggiungi host", - "addCredential": "Aggiungi credenziali", - "adminSettings": "Impostazioni di amministrazione", + "addCredential": "Aggiungi credenziale", + "adminSettings": "Impostazioni amministratore", "userProfile": "Profilo utente", - "serverStats": "Statistiche del server", - "loadingServerStats": "Caricamento delle statistiche del server...", + "serverStats": "Statistiche server", + "loadingServerStats": "Caricamento statistiche server...", "noServerData": "Nessun dato del server disponibile", - "cpu": "processore", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizza la dashboard", - "dashboardSettings": "Impostazioni del pannello di controllo", - "enableDisableCards": "Abilita/Disabilita le schede", - "resetLayout": "Ripristina le impostazioni predefinite", - "serverOverviewCard": "Panoramica del server", - "recentActivityCard": "Attività recenti", + "customizeLayout": "Personalizza Dashboard", + "dashboardSettings": "Impostazioni Dashboard", + "enableDisableCards": "Abilita/Disabilita schede", + "resetLayout": "Ripristina predefinito", + "serverOverviewCard": "Riepilogo server", + "recentActivityCard": "Attività recente", "networkGraphCard": "Grafico di rete", "networkGraph": "Grafico di rete", "quickActionsCard": "Azioni rapide", - "serverStatsCard": "Statistiche del server", + "serverStatsCard": "Statistiche server", "panelMain": "Principale", - "panelSide": "Lato", - "justNow": "proprio adesso", - "serviceLinks": "Collegamenti ai servizi", - "homepagePreview": "Anteprima della homepage" + "panelSide": "Laterale", + "justNow": "adesso", + "serviceLinks": "Link ai servizi", + "homepagePreview": "Anteprima home page" }, "donation": { "title": "Ti piace Termix?", - "body": "Termix è gratuito e open source, sviluppato e gestito da un team di due persone nel nostro tempo libero. Se ha sostituito uno strumento commerciale che altrimenti avresti pagato, una donazione contribuisce a coprire i costi di hosting e a sostenere lo sviluppo. Al momento, le donazioni sono accettate solo in criptovalute.", - "milestones": "Le donazioni contribuiscono anche a finanziare il tempo dedicato alla ricerca e all'apprendimento di ciò che è necessario per sviluppare funzionalità come il supporto per SAML e Kubernetes. È possibile seguire gli aggiornamenti sulla pagina delle donazioni.", - "cta": "Donare", + "body": "Termix è gratuito e open source, sviluppato e mantenuto da un team di due persone nel tempo libero. Se ha sostituito uno strumento commerciale per il quale altrimenti pagheresti, una donazione aiuta a coprire i costi di hosting e a sostenere lo sviluppo. Al momento, le donazioni sono solo in criptovaluta.", + "milestones": "Le donazioni aiutano anche a finanziare il tempo necessario per ricercare e apprendere ciò che serve per sviluppare funzionalità come il supporto a SAML e Kubernetes. Vedi i progressi nella pagina delle donazioni.", + "cta": "Dona", "dismiss": "Forse più tardi" }, "dashboardTab": { @@ -2383,47 +2517,47 @@ "hostsOnline": "Host online", "activeTunnels": "Tunnel attivi", "registerNewServer": "Registra un nuovo server", - "storeSshKeysOrPasswords": "Memorizza le chiavi SSH o le password", + "storeSshKeysOrPasswords": "Memorizza chiavi SSH o password", "manageUsersAndRoles": "Gestisci utenti e ruoli", "manageYourAccount": "Gestisci il tuo account", - "hostStatus": "Host Motel", + "hostStatus": "Stato host", "noHostsConfigured": "Nessun host configurato", "online": "ONLINE", "offline": "OFFLINE", - "checking": "CONTROLLO", + "checking": "IN VERIFICA", "onlineLower": "Online", "nodes": "{{count}} nodi", - "add": "Aggiungere:", - "commandPalette": "Tavolozza dei comandi", + "add": "Aggiungi:", + "commandPalette": "Palette comandi", "done": "Fatto", "editModeInstructions": "Trascina le schede per riordinarle · Trascina il divisore di colonna per ridimensionare le colonne · Trascina il bordo inferiore di una scheda per ridimensionarne l'altezza · Cestino per rimuovere", "empty": "Vuoto", - "clear": "Chiaro", + "clear": "Pulisci", "serviceLinksTitle": "Collegamenti ai servizi", - "serviceLinksEmpty": "Nessun collegamento di servizio ancora disponibile", + "serviceLinksEmpty": "Nessun collegamento ai servizi", "serviceLinksAddLabel": "Etichetta", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "Aggiungere", + "serviceLinksAdd": "Aggiungi", "serviceLinksLabelPlaceholder": "Il mio servizio", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", "serviceLinksInvalidUrl": "Inserisci un indirizzo web valido", "serviceLinksAddFailed": "Impossibile aggiungere il collegamento al servizio", "disk": "Disco", - "viewServerDetails": "Visualizza i dettagli del server" + "viewServerDetails": "Visualizza dettagli server" }, "sessionLogs": { - "title": "Registri delle sessioni", - "noLogs": "Nessun registro di sessione ancora disponibile", - "noLogsDesc": "Abilita la registrazione delle sessioni su un host per avviare la registrazione", + "title": "Registri di sessione", + "noLogs": "Ancora nessun registro di sessione", + "noLogsDesc": "Abilita la registrazione delle sessioni su un host per iniziare a registrare", "duration": "Durata", "viewLog": "Visualizza registro", - "downloadLog": "Scaricamento", - "deleteLog": "Eliminare", + "downloadLog": "Scarica", + "deleteLog": "Elimina", "confirmDelete": "Eliminare questo registro di sessione?", "confirmDeleteDesc": "Questa azione non può essere annullata.", "copyContent": "Copia", "copied": "Copiato!", - "loadError": "Impossibile caricare i log della sessione", + "loadError": "Impossibile caricare i registri di sessione", "deleteError": "Impossibile eliminare il registro di sessione", "filterByHost": "Filtra per host..." }, @@ -2432,347 +2566,364 @@ "addGroup": "Aggiungi gruppo", "addLink": "Aggiungi collegamento", "zoomIn": "Ingrandisci", - "zoomOut": "Riduci zoom", - "resetView": "Ripristina la vista", - "selectHost": "Seleziona l'host", + "zoomOut": "Riduci", + "resetView": "Reimposta vista", + "selectHost": "Seleziona host", "chooseHost": "Scegli un host...", - "parentGroup": "Gruppo Genitori", + "parentGroup": "Gruppo padre", "noGroup": "Nessun gruppo", - "groupName": "Nome del gruppo", + "groupName": "Nome gruppo", "color": "Colore", - "source": "Fonte", - "target": "Bersaglio", - "moveToGroup": "Passa al gruppo", - "selectGroup": "Seleziona il gruppo...", + "source": "Origine", + "target": "Destinazione", + "moveToGroup": "Sposta nel gruppo", + "selectGroup": "Seleziona gruppo...", "addConnection": "Aggiungi connessione", - "hostDetails": "Dettagli dell'host", + "hostDetails": "Dettagli host", "removeFromGroup": "Rimuovi dal gruppo", - "addHostHere": "Aggiungi l'host qui", + "addHostHere": "Aggiungi host qui", "editGroup": "Modifica gruppo", - "delete": "Eliminare", - "add": "Aggiungere", - "create": "Creare", - "move": "Mossa", - "connect": "Collegare", + "delete": "Elimina", + "add": "Aggiungi", + "create": "Crea", + "move": "Sposta", + "connect": "Connetti", "createGroup": "Crea gruppo", - "selectSourcePlaceholder": "Seleziona la sorgente...", - "selectTargetPlaceholder": "Seleziona il bersaglio...", + "selectSourcePlaceholder": "Seleziona origine...", + "selectTargetPlaceholder": "Seleziona destinazione...", "invalidFile": "File non valido", - "hostAlreadyExists": "L'host è già nella topologia", + "hostAlreadyExists": "L'host è già presente nella topologia", "connectionExists": "La connessione esiste già", "unknown": "Sconosciuto", "name": "Nome", "ip": "IP", "status": "Stato", "failedToAddNode": "Impossibile aggiungere il nodo", - "sourceDifferentFromTarget": "La sorgente e la destinazione devono essere diverse", + "sourceDifferentFromTarget": "Origine e destinazione devono essere diverse", "exportJSON": "Esporta JSON", "importJSON": "Importa JSON", - "terminal": "terminale", + "terminal": "Terminale", "fileManager": "Gestore file", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Metriche dell'host", - "hostMetrics": "Metriche dell'host", - "noNodes": "Nessun nodo ancora" + "serverStats": "Metriche host", + "hostMetrics": "Metriche host", + "noNodes": "Nessun nodo al momento" }, "docker": { "notEnabled": "Docker non è abilitato per questo host", - "validating": "Convalida di Docker in corso...", + "validating": "Convalida Docker in corso...", "connecting": "Connessione in corso...", "error": "Errore", "version": "Docker {{version}}", - "connectionFailed": "Impossibile connettersi a Docker", - "containerStarted": "Contenitore {{name}} avviato", - "failedToStartContainer": "Impossibile avviare il contenitore {{name}}", - "containerStopped": "Contenitore {{name}} arrestato", - "failedToStopContainer": "Impossibile arrestare il contenitore {{name}}", - "containerRestarted": "Contenitore {{name}} riavviato", + "connectionFailed": "Connessione a Docker non riuscita", + "containerStarted": "Container {{name}} avviato", + "failedToStartContainer": "Impossibile avviare il container {{name}}", + "containerStopped": "Container {{name}} arrestato", + "failedToStopContainer": "Impossibile arrestare il container {{name}}", + "containerRestarted": "Container {{name}} riavviato", "failedToRestartContainer": "Impossibile riavviare il container {{name}}", - "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": "Impossibile rimuovere il contenitore {{name}}", + "containerPaused": "Container {{name}} in pausa", + "containerUnpaused": "Container {{name}} riattivato", + "failedToTogglePauseContainer": "Impossibile cambiare lo stato di pausa del container {{name}}", + "containerRemoved": "Container {{name}} rimosso", + "failedToRemoveContainer": "Impossibile rimuovere il container {{name}}", "image": "Immagine", - "ports": "Porti", + "ports": "Porte", "noPorts": "Nessuna porta", - "start": "Inizio", - "confirmRemoveContainer": "Sei sicuro di voler rimuovere il contenitore '{{name}}'? Questa azione non può essere annullata.", - "runningContainerWarning": "Attenzione: questo contenitore è attualmente in esecuzione. La sua rimozione comporterà prima l'arresto del contenitore stesso.", - "loadingContainers": "Caricamento dei container in corso...", + "start": "Avvia", + "confirmRemoveContainer": "Sei sicuro di voler rimuovere il container '{{name}}'? Questa azione non può essere annullata.", + "runningContainerWarning": "Attenzione: questo container è attualmente in esecuzione. La rimozione lo arresterà prima.", + "loadingContainers": "Caricamento container in corso...", "manager": "Gestore Docker", "autoRefresh": "Aggiornamento automatico", - "timestamps": "Indicazioni temporali", - "lines": "Linee", - "filterLogs": "Filtra i log...", - "refresh": "Rinfrescato", - "download": "Scaricamento", - "clear": "Chiaro", - "logsDownloaded": "Log scaricati correttamente", - "last50": "Ultimi 50", - "last100": "Ultimi 100", - "last500": "Ultimi 500", - "last1000": "Ultimi 1000", - "allLogs": "Tutti i registri", - "noLogsMatching": "Nessun registro corrispondente a \"{{query}}\"", - "noLogsAvailable": "Nessun registro disponibile", + "timestamps": "Timestamp", + "lines": "Righe", + "filterLogs": "Filtra log...", + "refresh": "Aggiorna", + "download": "Scarica", + "clear": "Pulisci", + "logsDownloaded": "Log scaricati con successo", + "last50": "Ultime 50", + "last100": "Ultime 100", + "last500": "Ultime 500", + "last1000": "Ultime 1000", + "allLogs": "Tutti i log", + "noLogsMatching": "Nessun log corrispondente a \"{{query}}\"", + "noLogsAvailable": "Nessun log disponibile", "noContainersFound": "Nessun contenitore trovato", - "noContainersFoundHint": "Su questo host non sono disponibili container Docker.", - "searchPlaceholder": "Cerca i container...", + "noContainersFoundHint": "Nessun contenitore Docker disponibile su questo host", + "searchPlaceholder": "Cerca contenitori...", "allStatuses": "Tutti gli stati", - "stateRunning": "Corsa", + "stateRunning": "In esecuzione", "statePaused": "In pausa", - "stateExited": "Uscito", - "stateRestarting": "Riavvio", - "noContainersMatchFilters": "Nessun contenitore corrisponde ai tuoi filtri", - "noContainersMatchFiltersHint": "Prova a modificare i criteri di ricerca o di filtro.", - "failedToFetchStats": "Impossibile recuperare le statistiche del container.", + "stateExited": "Terminato", + "stateRestarting": "In riavvio", + "noContainersMatchFilters": "Nessun contenitore corrisponde ai filtri", + "noContainersMatchFiltersHint": "Prova a modificare i criteri di ricerca o di filtro", + "failedToFetchStats": "Impossibile recuperare le statistiche del contenitore", "containerNotRunning": "Contenitore non in esecuzione", - "startContainerToViewStats": "Avvia il container per visualizzare le statistiche", - "loadingStats": "Caricamento delle statistiche in corso...", - "errorLoadingStats": "Statistiche di caricamento degli errori", + "startContainerToViewStats": "Avvia il contenitore per visualizzare le statistiche", + "loadingStats": "Caricamento statistiche...", + "errorLoadingStats": "Errore nel caricamento delle statistiche", "noStatsAvailable": "Nessuna statistica disponibile", - "cpuUsage": "Utilizzo della CPU", - "current": "Attuale", - "memoryUsage": "Utilizzo della memoria", + "cpuUsage": "Utilizzo CPU", + "current": "Corrente", + "memoryUsage": "Utilizzo memoria", "networkIo": "I/O di rete", - "input": "Ingresso", - "output": "Produzione", - "blockIo": "Blocco I/O", - "read": "Leggere", - "write": "Scrivere", - "pids": "PID", - "containerInformation": "Informazioni sul contenitore", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Lettura", + "write": "Scrittura", + "pids": "PIDs", + "containerInformation": "Informazioni sul container", "name": "Nome", "id": "ID", "state": "Stato", - "containerMustBeRunning": "Il container deve essere in esecuzione per accedere alla console.", + "containerMustBeRunning": "Il container deve essere in esecuzione per accedere alla console", "verificationCodePrompt": "Inserisci il codice di verifica", - "totpVerificationFailed": "Verifica TOTP non riuscita. Riprova.", - "warpgateVerificationFailed": "Autenticazione Warpgate non riuscita. Riprova.", + "totpVerificationFailed": "Verifica TOTP fallita. Riprova.", + "warpgateVerificationFailed": "Autenticazione Warpgate fallita. Riprova.", "connectedTo": "Connesso a {{containerName}}", "disconnected": "Disconnesso", - "consoleError": "Errore della console", + "consoleError": "Errore console", "errorMessage": "Errore: {{message}}", - "failedToConnect": "Impossibile connettersi al container", + "failedToConnect": "Connessione al container fallita", "console": "Console", - "selectShell": "Seleziona la conchiglia", + "selectShell": "Seleziona shell", "bash": "Bash", "sh": "sh", - "ash": "cenere", - "connect": "Collegare", + "ash": "ash", + "connect": "Connetti", "disconnect": "Disconnetti", "notConnected": "Non connesso", - "clickToConnect": "Fai clic su Connetti per avviare una sessione shell", - "connectingTo": "Connessione a {{containerName}}...", - "containerNotFound": "Contenitore non trovato", + "clickToConnect": "Clicca Connetti per avviare una sessione shell", + "connectingTo": "Connessione a {{containerName}} in corso...", + "containerNotFound": "Container non trovato", "backToList": "Torna all'elenco", - "logs": "Registri", + "logs": "Log", "stats": "Statistiche", "consoleTab": "Console", "startContainerToAccess": "Avvia il container per accedere alla console" }, "admin": { "sectionGeneral": "Generale", - "sectionOidc": "ODDC", - "sectionSso": "Fornitori di SSO", - "ssoAddProvider": "Aggiungi fornitore", - "ssoDocsLink": "Visualizza i documenti", - "ssoProviderDocsLink": "Visualizza i documenti", + "sectionOidc": "OIDC", + "sectionSso": "Provider SSO", + "ssoAddProvider": "Aggiungi provider", + "ssoDocsLink": "Visualizza documentazione", + "ssoProviderDocsLink": "Visualizza documentazione", "ssoNoProviders": "Nessun provider SSO configurato.", - "ssoProviderName": "Nome da visualizzare", - "ssoProviderType": "Tipo di fornitore", - "ssoDeleteProvider": "Elimina il provider", + "ssoProviderName": "Nome visualizzato", + "ssoProviderType": "Tipo provider", + "ssoDeleteProvider": "Elimina provider", "ssoDeleteConfirm": "Eliminare questo provider? Gli utenti associati non potranno più accedere.", - "ssoTypeOidc": "ODDC", + "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", "ssoEnabled": "Abilitato", - "ssoDisabled": "Disabile", - "ssoSaveProvider": "Fornitore di salvataggi", - "ssoTestConnection": "Connessione di prova", - "ssoEditProvider": "Modifica fornitore", + "ssoDisabled": "Disabilitato", + "ssoSaveProvider": "Salva provider", + "ssoTestConnection": "Test connessione", + "ssoEditProvider": "Modifica provider", "ldapHost": "Host LDAP", "ldapPort": "Porta", - "ldapUseTls": "Utilizzare TLS (LDAPS)", + "ldapUseTls": "Usa TLS (LDAPS)", "ldapBindDn": "Bind DN", - "ldapBindPassword": "Password di collegamento", + "ldapBindPassword": "Password di bind", "ldapUserSearchBase": "Base di ricerca utenti", - "ldapUserSearchFilter": "Filtro di ricerca utente", - "ldapUsernameAttr": "Attributo NomeUtente", - "ldapDisplayNameAttr": "Attributo Nome visualizzato", - "ldapGroupSearchBase": "Base di ricerca del gruppo", - "ldapAdminGroup": "Gruppo Amministrativo", + "ldapUserSearchFilter": "Filtro di ricerca utenti", + "ldapUsernameAttr": "Attributo nome utente", + "ldapDisplayNameAttr": "Attributo nome visualizzato", + "ldapGroupSearchBase": "Base di ricerca gruppi", + "ldapAdminGroup": "Gruppo amministratori", "ldapAllowedUsers": "Utenti autorizzati", "sectionUsers": "Utenti", "sectionSessions": "Sessioni", "sectionRoles": "Ruoli", - "sectionDatabase": "Schermata", + "sectionDatabase": "Database", "sectionApiKeys": "Chiavi API", "sectionAuditLog": "Registro di controllo", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Emetti e rinnova automaticamente un certificato SSL attendibile da Let's Encrypt. Richiede un dominio pubblico e l'accesso alla porta 80 o al DNS.", - "sslDocsLink": "Visualizza la documentazione SSL", + "sslDescription": "Emettere e rinnovare automaticamente un certificato SSL attendibile da Let's Encrypt. Richiede un dominio pubblico e accesso alla porta 80 o al DNS.", + "sslDocsLink": "Visualizza documentazione SSL", "sslDomain": "Dominio", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "Il nome di dominio pubblico del certificato.", - "sslEmail": "E-mail", + "sslDomainDesc": "Il nome di dominio pubblico per il certificato.", + "sslEmail": "Email", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Indirizzo email per le notifiche e l'account di Let's Encrypt.", - "sslChallengeType": "Tipo di sfida", + "sslEmailDesc": "Email di contatto per notifiche e account Let's Encrypt.", + "sslChallengeType": "Tipo di challenge", "sslChallengeTypeDesc": "Come dimostrare la proprietà del dominio a Let's Encrypt.", - "sslChallengeHttp": "HTTP (webroot) - richiede che la porta 80 sia accessibile da Internet", - "sslChallengeDns": "DNS (Cloudflare) - richiede un token API di Cloudflare", - "sslCloudflareToken": "Token API di Cloudflare", - "sslCloudflareTokenPlaceholder": "Inserisci il token...", - "sslCloudflareTokenDesc": "Token con ambito limitato e autorizzazione Zone:DNS:Edit per il tuo dominio.", + "sslChallengeHttp": "HTTP (webroot) - richiede la porta 80 accessibile da internet", + "sslChallengeDns": "DNS (Cloudflare) - richiede un token API Cloudflare", + "sslCloudflareToken": "Token API Cloudflare", + "sslCloudflareTokenPlaceholder": "Inserisci token...", + "sslCloudflareTokenDesc": "Token con autorizzazione Zone:DNS:Edit per il tuo dominio.", "sslCertStatus": "Stato del certificato", "sslCertStatusNone": "Nessun certificato", "sslCertStatusValid": "Valido", - "sslCertStatusExpiring": "Scade a breve", + "sslCertStatusExpiring": "In scadenza", "sslCertStatusExpired": "Scaduto", "sslCertExpiresAt": "Scade il {{date}}", - "sslLastIssued": "Ultimo numero emesso {{date}}", - "sslRequestCert": "Emissione/Rinnovo del certificato", - "sslRequestCertLoading": "Richiesta del certificato...", - "sslRequestCertSuccess": "Certificato rilasciato e installato con successo.", - "sslRequestCertFailed": "Richiesta di certificato non riuscita", + "sslLastIssued": "Ultimo rilascio il {{date}}", + "sslRequestCert": "Emetti / Rinnova certificato", + "sslRequestCertLoading": "Richiesta certificato in corso...", + "sslRequestCertSuccess": "Certificato emesso e installato con successo", + "sslRequestCertFailed": "Richiesta certificato fallita", "sslSave": "Salva impostazioni", "sslSaved": "Impostazioni SSL salvate", - "sslSaveFailed": "Impossibile salvare le impostazioni SSL", - "sslRequiresDomain": "Sono richiesti dominio e indirizzo email.", + "sslSaveFailed": "Salvataggio impostazioni SSL fallito", + "sslRequiresDomain": "Dominio ed email sono obbligatori", "sslInfoNote": "Dopo aver emesso un certificato, abilita SSL nelle variabili d'ambiente (ENABLE_SSL=true) e riavvia Termix.", + "sslManualOption": "Manuale (caricamento del certificato)", + "sslManualCert": "Certificato (PEM)", + "sslManualCertPlaceholder": "-----INIZIO CERTIFICATO-----", + "sslManualKey": "Chiave privata (PEM)", + "sslManualKeyPlaceholder": "-----INIZIO CHIAVE PRIVATA-----", + "sslManualDesc": "Incolla il tuo certificato e la chiave privata esistenti, inclusa la catena completa se richiesta dalla tua CA.", + "sslManualUpload": "Carica e installa il certificato", + "sslManualUploadLoading": "Caricamento del certificato in corso...", + "sslManualUploadSuccess": "Certificato caricato e installato correttamente.", + "sslManualUploadFailed": "Caricamento del certificato non riuscito", + "sslManualRequiresFields": "Sono necessari il certificato e la chiave privata.", "auditLogTotal": "{{total}} voci totali", - "auditLogEmpty": "Nessuna voce trovata nel registro di controllo", + "auditLogEmpty": "Nessuna voce trovata nel registro di audit", "auditLogSuccess": "Successo", "auditLogFailed": "Fallito", - "auditLogClearFilters": "Filtri trasparenti", - "auditLogPage": "Pagina {{page}} di {{totalPages}} ({{total}} in totale)", + "auditLogClearFilters": "Cancella filtri", + "auditLogPage": "Pagina {{page}} di {{totalPages}} ({{total}} totali)", "auditLogIp": "IP", "auditLogResourceId": "ID risorsa", "auditLogFilterUser": "Utente", "auditLogFilterAction": "Azione", - "auditLogFilterResourceType": "Tipo di risorsa", + "auditLogFilterResourceType": "Tipo risorsa", "auditLogFilterStatus": "Stato", "auditLogFilterFrom": "Da", "auditLogFilterTo": "A", - "auditLogFilterAll": "Tutto", - "allowRegistration": "Consenti la registrazione dell'utente", - "allowRegistrationDesc": "Consenti ai nuovi utenti di registrarsi autonomamente con nome utente e password.", - "allowPasswordLogin": "Consenti l'accesso tramite password", - "allowPasswordLoginDesc": "Accesso tramite nome utente/password", - "oidcAutoProvision": "Auto-provisioning OIDC", - "oidcAutoProvisionDesc": "Creazione automatica degli account per gli utenti OIDC/SSO al primo accesso (indipendentemente dall'attivazione/disattivazione della registrazione).", - "oidcSilentLoginDefault": "Accesso OIDC silenzioso per impostazione predefinita", - "oidcSilentLoginDefaultDesc": "Reindirizzamento automatico alla pagina di accesso OIDC ad ogni visita, saltando completamente il modulo di accesso.", - "allowPasswordReset": "Consenti il ripristino della password", - "allowPasswordResetDesc": "Reimposta il codice tramite i log di Docker", - "commandHistoryEnabled": "Storia del comando", - "commandHistoryEnabledDesc": "Consente la registrazione della cronologia dei comandi. Quando è disabilitata, la cronologia non viene salvata indipendentemente dalle impostazioni per singolo host.", - "updateCommandHistoryFailed": "Impossibile aggiornare le impostazioni della cronologia dei comandi.", - "sessionTimeout": "Timeout della sessione", + "auditLogFilterAll": "Tutti", + "allowRegistration": "Consenti registrazione utenti", + "allowRegistrationDesc": "Consenti ai nuovi utenti di registrarsi autonomamente con nome utente e password", + "allowPasswordLogin": "Consenti accesso con password", + "allowPasswordLoginDesc": "Accesso con nome utente/password", + "oidcAutoProvision": "Provisioning automatico OIDC", + "oidcAutoProvisionDesc": "Crea automaticamente account per utenti OIDC/SSO al primo accesso (indipendentemente dall'opzione di registrazione)", + "oidcSilentLoginDefault": "Accesso OIDC silenzioso predefinito", + "oidcSilentLoginDefaultDesc": "Reindirizza automaticamente all'accesso OIDC ad ogni visita, saltando completamente il modulo di accesso", + "allowPasswordReset": "Consenti reset password", + "allowPasswordResetDesc": "Codice di reset tramite log Docker", + "commandHistoryEnabled": "Cronologia comandi", + "commandHistoryEnabledDesc": "Consenti la registrazione della cronologia comandi. Se disabilitato, la cronologia non viene salvata indipendentemente dalle impostazioni per host.", + "updateCommandHistoryFailed": "Aggiornamento dell'impostazione della cronologia comandi non riuscito", + "analyticsEnabled": "Condividi le statistiche di utilizzo anonime", + "analyticsEnabledDesc": "Invia un conteggio giornaliero anonimo di utenti, host e utilizzo delle funzionalità per contribuire al miglioramento di Termix. Non vengono mai inclusi dati personali o dettagli di connessione.", + "updateAnalyticsFailed": "Impossibile aggiornare le impostazioni di analisi", + "sessionSharingGloballyEnabled": "Consenti la condivisione della sessione", + "sessionSharingGloballyEnabledDesc": "Consente la condivisione di sessioni live di terminale, RDP, VNC e Telnet a livello di istanza. Quando disabilitata, questa opzione sovrascrive tutte le impostazioni di condivisione per singolo host.", + "updateSessionSharingFailed": "Impossibile aggiornare le impostazioni di condivisione della sessione", + "sessionTimeout": "Timeout sessione", "hours": "ore", - "sessionTimeoutRange": "Minimo 1 ora · Massimo 720 ore", - "monitoringDefaults": "Monitoraggio delle impostazioni predefinite", - "statusCheck": "Controllo dello stato", - "metrics": "Metrica", - "sec": "sicurezza", - "logLevel": "Livello di registro", - "enableGuacamole": "Abilita il guacamole", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Impostazioni predefinite monitoraggio", + "statusCheck": "Verifica stato", + "metrics": "Metriche", + "sec": "sec", + "logLevel": "Livello di log", + "enableGuacamole": "Abilita Guacamole", "enableGuacamoleDesc": "Desktop remoto RDP/VNC", - "enableGuacamoleDocsLink": "Visualizza i documenti", - "guacdUrl": "URL di guacd", - "tailscaleApiKey": "Chiave API di Tailscale", - "tailscaleApiKeyDescription": "Utilizzato per il rilevamento dei dispositivi nell'editor host. Genera una chiave su tailscale.com/admin/settings/keys.", - "tailscaleApiKeyDocsLink": "Visualizza i documenti", - "oidcDescription": "Configura OpenID Connect per l'SSO. I campi contrassegnati da * sono obbligatori.", - "oidcDocsLink": "Visualizza i documenti", - "oidcClientId": "ID cliente", - "oidcClientSecret": "Segreto del cliente", - "oidcAuthUrl": "Autorizzazione DB", + "enableGuacamoleDocsLink": "Visualizza documentazione", + "guacdUrl": "URL guacd", + "tailscaleApiKey": "Chiave API Tailscale", + "tailscaleApiKeyDescription": "Utilizzata per il rilevamento dei dispositivi nell'editor host. Genera una chiave su tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "Visualizza documentazione", + "oidcDescription": "Configura OpenID Connect per il SSO. I campi contrassegnati con * sono obbligatori.", + "oidcDocsLink": "Visualizza documentazione", + "oidcClientId": "ID client", + "oidcClientSecret": "Segreto client", + "oidcAuthUrl": "URL di autorizzazione", "oidcIssuerUrl": "URL dell'emittente", - "oidcTokenUrl": "Token DB", - "oidcUserIdentifier": "Percorso identificativo utente", - "oidcDisplayName": "Percorso del nome visualizzato", - "oidcScopes": "Cannocchiali", - "oidcUserinfoUrl": "Sovrascrivi l'URL delle informazioni utente", - "oidcAllowedUsers": "Utenti autorizzati", - "oidcAllowedUsersDesc": "Un indirizzo email per riga. Lasciare vuoto per consentire l'accesso a tutti gli indirizzi.", - "oidcAdminGroup": "Gruppo Amministrativo", - "oidcAdminGroupDesc": "Agli utenti di questo gruppo vengono concessi i privilegi di amministratore. Lasciare vuoto per disabilitare la sincronizzazione del gruppo.", - "oidcGroupClaim": "Richiesta di risarcimento collettiva", - "oidcGroupClaimDesc": "Opzionale. Il percorso di rivendicazione che contiene i gruppi dell'utente. Il valore predefinito è gruppi, ruoli, quindi gruppo. Utilizzare questo parametro per i provider con una rivendicazione personalizzata (ad esempio Zitadel).", + "oidcTokenUrl": "URL token", + "oidcUserIdentifier": "Percorso identificatore utente", + "oidcDisplayName": "Percorso nome visualizzato", + "oidcScopes": "Ambiti", + "oidcUserinfoUrl": "Sovrascrivi URL Userinfo", + "oidcAllowedUsers": "Utenti consentiti", + "oidcAllowedUsersDesc": "Un'email per riga. Lascia vuoto per consentire a tutti.", + "oidcAdminGroup": "Gruppo amministratori", + "oidcAdminGroupDesc": "Gli utenti di questo gruppo ottengono i diritti di amministratore. Lascia vuoto per disabilitare la sincronizzazione dei gruppi.", + "oidcGroupClaim": "Attributo gruppo", + "oidcGroupClaimDesc": "Opzionale. Il percorso dell'attributo contenente i gruppi dell'utente. Il valore predefinito è groups, roles, quindi group. Da utilizzare per provider con un attributo personalizzato (es. Zitadel).", "oidcCaCert": "Certificato CA personalizzato", - "oidcCaCertDesc": "Opzionale. Certificato CA codificato in formato PEM per i provider OIDC che utilizzano una CA privata o autofirmata. Lasciare vuoto per utilizzare l'archivio di certificati attendibili del sistema.", - "removeOidc": "Rimuovere", + "oidcCaCertDesc": "Opzionale. Certificato CA in formato PEM per provider OIDC che utilizzano una CA privata o autofirmata. Lascia vuoto per usare l'archivio dei certificati attendibili di sistema.", + "removeOidc": "Rimuovi", "usersCount": "{{count}} utenti", - "createUser": "Creare", + "createUser": "Crea", "newRole": "Nuovo ruolo", "roleName": "Nome", - "roleDisplayName": "Nome da visualizzare", + "roleDisplayName": "Nome visualizzato", "roleDescription": "Descrizione", "rolesCount": "{{count}} ruoli", - "createRole": "Creare", + "createRole": "Crea", "creating": "Creazione...", - "exportDatabase": "Database di esportazione", - "exportDatabaseDesc": "Scarica un backup di tutti gli host, le credenziali e le impostazioni", - "export": "Esportare", + "exportDatabase": "Esporta database", + "exportDatabaseDesc": "Scarica un backup di tutti gli host, credenziali e impostazioni", + "export": "Esporta", "exporting": "Esportazione...", "importDatabase": "Importa database", - "importDatabaseDesc": "Ripristino da un file di backup .sqlite", + "importDatabaseDesc": "Ripristina da un file di backup .sqlite", "importDatabaseSelected": "Selezionato: {{name}}", - "selectFile": "Seleziona il file", - "changeFile": "Modifica", - "import": "Importare", + "selectFile": "Seleziona file", + "changeFile": "Cambia", + "import": "Importa", "importing": "Importazione...", "apiKeysCount": "{{count}} chiavi", - "apiKeysDocsLink": "Visualizza i documenti", + "apiKeysDocsLink": "Vedi documenti", "newApiKey": "Nuova chiave API", - "apiKeyCreatedWarning": "Chiave creata: copiala ora, non verrà più visualizzata.", + "apiKeyCreatedWarning": "Chiave creata - copiala ora, non verrà più mostrata.", "apiKeyName": "Nome", "apiKeyUser": "Utente", "apiKeySelectUser": "Seleziona un utente...", - "apiKeyExpiresAt": "Scade alle", + "apiKeyExpiresAt": "Scade il", "createKey": "Crea chiave", - "apiKeyNoExpiry": "Nessuna scadenza", + "apiKeyNoExpiry": "Senza scadenza", "revokedBadge": "REVOCATO", "authTypeDual": "Autenticazione doppia", - "authTypeOidc": "ODDC", + "authTypeOidc": "OIDC", "authTypeLocal": "Locale", "adminStatusAdministrator": "Amministratore", "adminStatusRegularUser": "Utente regolare", - "adminBadge": "AMMINISTRATORE", - "systemBadge": "SISTEMA", - "customBadge": "COSTUME", - "youBadge": "VOI", - "sessionsActive": "{{count}} attivo", + "adminBadge": "AMMIN", + "systemBadge": "SIST", + "customBadge": "PERSONALIZZATO", + "youBadge": "TU", + "sessionsActive": "{{count}} attivi", "sessionActive": "Attivo: {{time}}", - "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tutto", - "revokeAllSessionsSuccess": "Tutte le sessioni per l'utente revocate.", - "revokeAllSessionsFailed": "Impossibile revocare le sessioni", - "revokeSessionFailed": "Impossibile revocare la sessione", + "sessionExpires": "Scad: {{time}}", + "revokeAll": "Tutte", + "revokeAllSessionsSuccess": "Tutte le sessioni dell'utente revocate", + "revokeAllSessionsFailed": "Revoca sessioni non riuscita", + "revokeSessionFailed": "Revoca sessione non riuscita", "addRole": "Aggiungi ruolo", "noCustomRoles": "Nessun ruolo personalizzato definito", - "removeRoleFailed": "Impossibile rimuovere il ruolo", - "assignRoleFailed": "Impossibile assegnare il ruolo", - "deleteRoleFailed": "Impossibile eliminare il ruolo", + "removeRoleFailed": "Rimozione ruolo non riuscita", + "assignRoleFailed": "Assegnazione ruolo non riuscita", + "deleteRoleFailed": "Eliminazione ruolo non riuscita", "userAdminAccess": "Amministratore", - "userAdminAccessDesc": "Accesso completo a tutte le impostazioni di amministrazione.", + "userAdminAccessDesc": "Accesso completo a tutte le impostazioni di amministrazione", "userRoles": "Ruoli", "revokeAllUserSessions": "Revoca tutte le sessioni", - "revokeAllUserSessionsDesc": "Effettua nuovamente l'accesso forzato su tutti i dispositivi", - "revoke": "Revocare", - "deleteUserWarning": "L'eliminazione di questo utente è definitiva.", + "revokeAllUserSessionsDesc": "Forza il riaccesso su tutti i dispositivi", + "revoke": "Revoca", + "deleteUserWarning": "L'eliminazione di questo utente è permanente.", "deleteUser": "Elimina {{username}}", "deleting": "Eliminazione in corso...", - "deleteUserFailed": "Impossibile eliminare l'utente", + "deleteUserFailed": "Eliminazione utente non riuscita", "deleteUserSuccess": "Utente \"{{username}}\" eliminato", "deleteRoleSuccess": "Ruolo \"{{name}}\" eliminato", "revokeKeySuccess": "Chiave \"{{name}}\" revocata", - "revokeKeyFailed": "Impossibile revocare la chiave", + "revokeKeyFailed": "Revoca chiave non riuscita", "copiedToClipboard": "Copiato negli appunti", "done": "Fatto", "createUserTitle": "Crea utente", @@ -2780,650 +2931,712 @@ "createUserUsername": "Nome utente", "createUserPassword": "Password", "createUserPasswordHint": "Minimo 6 caratteri.", - "createUserEnterUsername": "Inserisci il nome utente", - "createUserEnterPassword": "Inserisci la password", + "createUserEnterUsername": "Inserisci nome utente", + "createUserEnterPassword": "Inserisci password", "createUserSubmit": "Crea utente", "editUserTitle": "Gestisci utente: {{username}}", "editUserDesc": "Modifica ruoli, stato di amministratore, sessioni e impostazioni dell'account.", "editUserUsername": "Nome utente", - "editUserAuthType": "Tipo di autorizzazione", + "editUserAuthType": "Tipo di autenticazione", "editUserAdminStatus": "Stato amministratore", "editUserUserId": "ID utente", - "linkAccountTitle": "Collega gli account", + "linkAccountTitle": "Collega account", "linkAccountDesc": "Unisci l'account OIDC {{username}} con un account locale esistente.", - "linkAccountDescLocal": "Collega un account locale {{username}} a un account esistente che supporta solo OIDC.", - "linkAccountWarningTitle": "Questo permetterà di:", - "linkAccountEffect1": "Elimina l'account solo OIDC", - "linkAccountEffect2": "Aggiungere le credenziali di accesso OIDC all'account di destinazione.", - "linkAccountEffect3": "Consenti l'accesso sia tramite OIDC che tramite password.", - "linkAccountTargetUsername": "Nome utente dell'account locale", - "linkAccountTargetPlaceholder": "Inserisci il nome utente dell'account locale per collegarti", - "linkAccountOidcUsername": "Nome utente dell'account OIDC", + "linkAccountDescLocal": "Collega un account locale {{username}} con un account solo OIDC esistente.", + "linkAccountWarningTitle": "Questo comporterà:", + "linkAccountEffect1": "Eliminare l'account solo OIDC", + "linkAccountEffect2": "Aggiungere l'accesso OIDC all'account di destinazione", + "linkAccountEffect3": "Consentire sia l'accesso OIDC che quello con password", + "linkAccountTargetUsername": "Nome utente account locale", + "linkAccountTargetPlaceholder": "Inserisci il nome utente dell'account locale da collegare", + "linkAccountOidcUsername": "Nome utente account OIDC", "linkAccountOidcPlaceholder": "Inserisci il nome utente dell'account solo OIDC da unire", - "linkAccountOidcNotFound": "Nessun account OIDC-only trovato con quel nome utente", - "linkAccounts": "Collega gli account", + "linkAccountOidcNotFound": "Nessun account solo OIDC trovato con quel nome utente", + "linkAccounts": "Collega account", "linkAccountSuccess": "Account collegati con successo", - "linkAccountFailed": "Impossibile collegare gli account", - "linkAccountInProgress": "Collegamento...", + "linkAccountFailed": "Collegamento account non riuscito", + "linkAccountInProgress": "Collegamento in corso...", "unlinkAccountTitle": "Scollega OIDC", - "unlinkAccountDesc": "Rimuovi l'autenticazione OIDC da {{username}}. Potranno accedere solo con la loro password.", - "unlinkAccountWarning": "Questa operazione rimuoverà l'accesso OIDC da questo account. L'utente deve aver impostato una password per poter continuare ad accedere.", + "unlinkAccountDesc": "Rimuovi l'autenticazione OIDC da {{username}}. Potrà accedere solo con la password.", + "unlinkAccountWarning": "Questo rimuoverà l'accesso OIDC da questo account. L'utente deve avere una password impostata per continuare ad accedere.", "unlinkAccount": "Scollega OIDC", "unlinkAccountInProgress": "Scollegamento in corso...", - "unlinkAccountSuccess": "OIDC disconnesso con successo", - "unlinkAccountFailed": "Impossibile scollegare OIDC", - "saving": "Risparmio...", - "updateRegistrationFailed": "Impossibile aggiornare le impostazioni di registrazione", - "updatePasswordLoginFailed": "Impossibile aggiornare le impostazioni di accesso con la password.", - "cannotDisablePasswordLoginWithTotp": "Non è possibile disabilitare l'accesso tramite password se l'autenticazione a due fattori (2FA) è attiva per uno o più utenti. Disabilitare prima l'autenticazione a due fattori.", - "updateOidcAutoProvisionFailed": "Impossibile aggiornare l'impostazione di auto-provisioning OIDC.", - "updateOidcSilentLoginDefaultFailed": "Impossibile aggiornare le impostazioni di accesso OIDC silenzioso.", - "updatePasswordResetFailed": "Impossibile aggiornare le impostazioni di reimpostazione della password.", - "sessionTimeoutRange2": "Il timeout della sessione deve essere compreso tra 1 e 720 ore.", - "sessionTimeoutSaved": "Timeout della sessione salvato", - "sessionTimeoutSaveFailed": "Impossibile salvare la sessione (timeout)", + "unlinkAccountSuccess": "OIDC scollegato con successo", + "unlinkAccountFailed": "Scollegamento OIDC non riuscito", + "saving": "Salvataggio in corso...", + "updateRegistrationFailed": "Aggiornamento impostazione registrazione non riuscito", + "updatePasswordLoginFailed": "Aggiornamento impostazione accesso con password non riuscito", + "cannotDisablePasswordLoginWithTotp": "Impossibile disabilitare l'accesso con password mentre la 2FA è abilitata per uno o più utenti. Disabilita prima la 2FA.", + "updateOidcAutoProvisionFailed": "Aggiornamento impostazione provisioning automatico OIDC non riuscito", + "updateOidcSilentLoginDefaultFailed": "Aggiornamento impostazione accesso OIDC silenzioso non riuscito", + "updatePasswordResetFailed": "Aggiornamento impostazione reimpostazione password non riuscito", + "sessionTimeoutRange2": "Il timeout della sessione deve essere compreso tra 1 e 720 ore", + "sessionTimeoutSaved": "Timeout di sessione salvato", + "sessionTimeoutSaveFailed": "Salvataggio timeout di sessione non riuscito", "monitoringIntervalInvalid": "Valori di intervallo non validi", "monitoringSaved": "Impostazioni di monitoraggio salvate", - "monitoringSaveFailed": "Impossibile salvare le impostazioni di monitoraggio.", - "metricsHistoryRetention": "Conservazione della cronologia delle metriche", + "monitoringSaveFailed": "Salvataggio impostazioni di monitoraggio non riuscito", + "metricsHistoryRetention": "Conservazione cronologia metriche", "metricsHistoryRetentionRange": "Da 1 a 90 giorni", "days": "giorni", - "guacamoleSaved": "Impostazioni del guacamole salvate", - "guacamoleSaveFailed": "Impossibile salvare le impostazioni del guacamole", - "guacamoleUpdateFailed": "Impossibile aggiornare le impostazioni del guacamole", - "tailscaleSettingsSaved": "Impostazioni della scala di coda salvate", - "tailscaleSettingsSaveFailed": "Impossibile salvare le impostazioni di Tailscale.", - "logLevelUpdateFailed": "Impossibile aggiornare il livello di registro", + "guacamoleSaved": "Impostazioni di Guacamole salvate", + "guacamoleSaveFailed": "Salvataggio impostazioni di Guacamole non riuscito", + "guacamoleUpdateFailed": "Aggiornamento impostazione Guacamole non riuscito", + "tailscaleSettingsSaved": "Impostazioni di Tailscale salvate", + "tailscaleSettingsSaveFailed": "Salvataggio impostazioni di Tailscale non riuscito", + "logLevelUpdateFailed": "Aggiornamento livello di log non riuscito", "oidcSaved": "Configurazione OIDC salvata", - "oidcSaveFailed": "Impossibile salvare la configurazione OIDC", + "oidcSaveFailed": "Salvataggio configurazione OIDC non riuscito", "oidcRemoved": "Configurazione OIDC rimossa", - "oidcRemoveFailed": "Impossibile rimuovere la configurazione OIDC", - "createUserRequired": "Sono richiesti nome utente e password.", - "createUserPasswordTooShort": "La password deve essere lunga almeno 6 caratteri.", + "oidcRemoveFailed": "Rimozione configurazione OIDC non riuscita", + "createUserRequired": "Nome utente e password sono obbligatori", + "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 sono state revocate.", - "revokeSessionsFailed": "Impossibile revocare le sessioni", - "manageUserData": "Gestire i dati utente", + "createUserFailed": "Creazione utente non riuscita", + "updateAdminStatusFailed": "Aggiornamento stato amministratore non riuscito", + "allSessionsRevoked": "Tutte le sessioni revocate", + "revokeSessionsFailed": "Revoca sessioni non riuscita", + "manageUserData": "Gestisci dati utente", "backToUsers": "Torna agli utenti", "manageTabAccount": "Account", - "manageTabHosts": "Ospiti", + "manageTabHosts": "Host", "manageTabCredentials": "Credenziali", - "manageTabSnippets": "Frammenti", + "manageTabSnippets": "Snippet", "manageTabSessions": "Sessioni", "manageTabDanger": "Pericolo", "manageEditorBack": "Torna ai dati di {{username}}", - "dataLockedBadge": "CHIUSO", - "dataLockedNotice": "I dati di {{username}}rimangono bloccati fino al loro prossimo accesso. I loro host, credenziali e snippet non possono essere visualizzati o modificati fino ad allora.", + "dataLockedBadge": "BLOCCATO", + "dataLockedNotice": "I dati di {{username}} rimangono bloccati fino al prossimo accesso. I relativi host, credenziali e frammenti non possono essere visualizzati o modificati fino ad allora.", "resetPasswordTitle": "Reimposta password", - "resetPasswordOidcOnly": "Questo utente effettua l'accesso tramite un provider esterno e non ha una password.", + "resetPasswordOidcOnly": "Questo utente accede tramite un provider esterno e non ha una password.", "resetPasswordPlaceholder": "Nuova password", - "resetPasswordBtn": "Reset", - "resetPasswordWorking": "Ripristino in corso...", - "resetPasswordSuccess": "reimposta la password", - "resetPasswordSuccessWiped": "Reimpostazione della password. I dati crittografati dell'utente sono stati cancellati.", - "resetPasswordFailed": "Impossibile reimpostare la password", - "resetPasswordConfirmWipe": "{{username}} non ha effettuato l'accesso dall'aggiornamento della crittografia, pertanto i suoi dati non possono essere recuperati. Il ripristino ora eliminerà host, credenziali e snippet. Continuare?", + "resetPasswordBtn": "Reimposta", + "resetPasswordWorking": "Reimpostazione in corso...", + "resetPasswordSuccess": "Password reimpostata", + "resetPasswordSuccessWiped": "Password reimpostata. I dati crittografati dell'utente sono stati cancellati.", + "resetPasswordFailed": "Reimpostazione password non riuscita", + "resetPasswordConfirmWipe": "{{username}} non ha effettuato l'accesso dall'aggiornamento della crittografia, pertanto i suoi dati non possono essere recuperati. Con la reimpostazione verranno eliminati i suoi host, credenziali e frammenti. Continuare?", "totpSectionTitle": "Autenticazione a due fattori", "totpStatusEnabled": "TOTP è abilitato per questo utente", "totpStatusDisabled": "TOTP non è abilitato per questo utente", "disableTotp": "Disabilita", - "disableTotpConfirm": "Disabilitare l'autenticazione a due fattori per {{username}}? Potranno accedere solo con la loro password.", + "disableTotpConfirm": "Disabilitare l’autenticazione a due fattori per {{username}}? L’utente potrà accedere solo con la password.", "totpDisabledSuccess": "TOTP disabilitato", - "totpDisableFailed": "Impossibile disattivare TOTP", + "totpDisableFailed": "Disabilitazione TOTP non riuscita", "manageApiKeys": "Chiavi API", "apiKeyNamePlaceholder": "Nome chiave", - "apiKeyCopyNotice": "Copia subito questa chiave, non verrà più mostrata.", + "apiKeyCopyNotice": "Copia questa chiave ora, non verrà più mostrata.", "noApiKeysForUser": "Nessuna chiave API", - "apiKeyDeleteFailed": "Impossibile eliminare la chiave API", - "exportUserData": "Esportazione dei dati", - "exportUserDataDesc": "Scarica i dati relativi a host, credenziali e file manager di questo utente in formato JSON. I dati sensibili vengono decrittografati.", + "apiKeyDeleteFailed": "Eliminazione chiave API non riuscita", + "exportUserData": "Esportazione dati", + "exportUserDataDesc": "Scarica gli host, le credenziali e i dati del gestore file di questo utente in formato JSON. I segreti vengono decrittografati.", "exportUserDataSuccess": "Dati utente esportati", - "exportUserDataFailed": "Impossibile esportare i dati utente", + "exportUserDataFailed": "Esportazione dati utente non riuscita", "hostsCount": "{{count}} host", "addHostForUser": "Aggiungi host", "noHostsForUser": "Questo utente non ha host", - "connectToHost": "Collegare", - "deleteHostConfirm": "Eliminare l'host \"{{name}}\" appartenente a {{username}}?", + "connectToHost": "Connetti", + "deleteHostConfirm": "Eliminare l’host \"{{name}}\" appartenente a {{username}}?", "hostDeletedSuccess": "Host eliminato", - "hostDeleteFailed": "Impossibile eliminare l'host", + "hostDeleteFailed": "Eliminazione host non riuscita", "credentialsCount": "{{count}} credenziali", - "addCredentialForUser": "Aggiungi credenziali", + "addCredentialForUser": "Aggiungi credenziale", "noCredentialsForUser": "Questo utente non ha credenziali", - "deleteCredentialConfirm": "Eliminare le credenziali \"{{name}}\" appartenenti a {{username}}?", - "credentialDeletedSuccess": "Credenziali eliminate", - "credentialDeleteFailed": "Impossibile eliminare le credenziali", - "snippetsCount": "{{count}} frammenti", + "deleteCredentialConfirm": "Eliminare la credenziale \"{{name}}\" appartenente a {{username}}?", + "credentialDeletedSuccess": "Credenziale eliminata", + "credentialDeleteFailed": "Impossibile eliminare la credenziale", + "snippetsCount": "{{count}} snippet", "addSnippetForUser": "Aggiungi snippet", "noSnippetsForUser": "Questo utente non ha snippet", - "snippetRequiredFields": "Il nome e il contenuto dello snippet sono obbligatori.", - "snippetNamePlaceholder": "Nome del frammento", - "snippetContentPlaceholder": "Contenuto del comando", - "snippetFolderPlaceholder": "Cartella (facoltativa)", - "snippetSaved": "Frammento salvato", - "snippetSaveFailed": "Impossibile salvare il frammento", - "deleteSnippetConfirm": "Eliminare il frammento \"{{name}}\" appartenente a {{username}}?", - "snippetDeletedSuccess": "Frammento eliminato", - "snippetDeleteFailed": "Impossibile eliminare il frammento", + "snippetRequiredFields": "Nome e contenuto dello snippet sono obbligatori", + "snippetNamePlaceholder": "Nome snippet", + "snippetContentPlaceholder": "Contenuto comando", + "snippetFolderPlaceholder": "Cartella (opzionale)", + "snippetSaved": "Snippet salvato", + "snippetSaveFailed": "Impossibile salvare lo snippet", + "deleteSnippetConfirm": "Eliminare lo snippet \"{{name}}\" appartenente a {{username}}?", + "snippetDeletedSuccess": "Snippet eliminato", + "snippetDeleteFailed": "Impossibile eliminare lo snippet", "noSessionsForUser": "Nessuna sessione attiva", - "deleteUserDangerDesc": "Elimina definitivamente {{username}} e tutti i relativi dati (host, credenziali, snippet, cronologia). Questa operazione non può essere annullata.", - "deleteUserConfirm": "Eliminare definitivamente {{username}} e tutti i suoi dati?", - "deleteUserAdminBlocked": "Rimuovere i privilegi di amministratore prima di eliminare questo utente.", - "createRoleRequired": "Nome e nome visualizzato sono obbligatori.", + "deleteUserDangerDesc": "Eliminare definitivamente {{username}} e tutti i relativi dati (host, credenziali, snippet, cronologia). Questa azione non può essere annullata.", + "deleteUserConfirm": "Eliminare definitivamente {{username}} e tutti i relativi dati?", + "deleteUserAdminBlocked": "Rimuovere lo stato di amministratore prima di eliminare questo utente.", + "createRoleRequired": "Nome e nome visualizzato sono obbligatori", "createRoleSuccess": "Ruolo \"{{name}}\" creato", "createRoleFailed": "Impossibile creare il ruolo", - "apiKeyNameRequired": "È richiesto il nome della chiave", - "apiKeyUserRequired": "È richiesto l'ID utente", + "apiKeyNameRequired": "Il nome della chiave è obbligatorio", + "apiKeyUserRequired": "L'ID utente è obbligatorio", "apiKeyCreatedSuccess": "Chiave API \"{{name}}\" creata", "apiKeyCreateFailed": "Impossibile creare la chiave API", - "exportSuccess": "Esportazione del database completata con successo.", + "exportSuccess": "Database esportato con successo", "exportFailed": "Esportazione del database non riuscita", - "importSelectFile": "Seleziona prima un file", - "importCompleted": "Importazione completata: {{total}} elementi importati, {{skipped}} elementi saltati", + "importSelectFile": "Selezionare prima un file", + "importCompleted": "Importazione completata: {{total}} elementi importati, {{skipped}} ignorati", "importFailed": "Importazione non riuscita: {{error}}", "importError": "Importazione del database non riuscita", - "sectionHostDefaults": "Impostazioni predefinite dell'host", - "hostDefaultsDesc": "Le impostazioni vengono applicate automaticamente alla creazione di un nuovo host. I singoli host possono comunque sovrascriverle.", + "sectionHostDefaults": "Predefiniti host", + "hostDefaultsDesc": "Impostazioni applicate automaticamente alla creazione di un nuovo host. I singoli host possono comunque sovrascriverle.", "hostDefaultsSocks5": "Proxy SOCKS5", - "hostDefaultsUseSocks5": "Abilita il proxy SOCKS5", - "hostDefaultsUseSocks5Desc": "Precompilare il proxy SOCKS5 su tutti i nuovi host", - "hostDefaultsSocks5Host": "Host/Porta proxy", + "hostDefaultsUseSocks5": "Abilita proxy SOCKS5", + "hostDefaultsUseSocks5Desc": "Precompila proxy SOCKS5 su tutti i nuovi host", + "hostDefaultsSocks5Host": "Host / Porta proxy", "hostDefaultsSocks5Username": "Nome utente proxy", "hostDefaultsSocks5Password": "Password proxy", - "hostDefaultsMetrics": "Metriche dell'host", - "hostDefaultsMetricsEnabled": "Abilita le metriche", - "hostDefaultsMetricsEnabledDesc": "Per impostazione predefinita, vengono raccolte le statistiche relative a CPU, memoria e altri parametri sui nuovi host.", - "hostDefaultsStatusCheckEnabled": "Abilitare il controllo dello stato", - "hostDefaultsStatusCheckEnabledDesc": "Verifica lo stato online/offline dei nuovi host per impostazione predefinita", - "hostDefaultsTerminal": "terminale", - "hostDefaultsSessionLogging": "Registrazione della sessione", - "hostDefaultsSessionLoggingDesc": "Registra le sessioni del terminale sui nuovi host per impostazione predefinita", - "hostDefaultsCommandHistory": "Storia del comando", - "hostDefaultsCommandHistoryDesc": "Per impostazione predefinita, viene monitorata la cronologia dei comandi sui nuovi host.", - "hostDefaultsSaved": "Hostdefaults salvato", - "hostDefaultsSaveFailed": "Impossibile salvare le impostazioni predefinite dell'host", + "hostDefaultsMetrics": "Metriche host", + "hostDefaultsMetricsEnabled": "Abilita metriche", + "hostDefaultsMetricsEnabledDesc": "Raccogli metriche CPU, memoria e altre sui nuovi host per impostazione predefinita", + "hostDefaultsStatusCheckEnabled": "Abilita controllo stato", + "hostDefaultsStatusCheckEnabledDesc": "Verifica lo stato online/offline sui nuovi host per impostazione predefinita", + "hostDefaultsTerminal": "Terminale", + "hostDefaultsSessionLogging": "Registrazione sessione", + "hostDefaultsSessionLoggingDesc": "Registra sessioni terminale sui nuovi host per impostazione predefinita", + "hostDefaultsCommandHistory": "Cronologia comandi", + "hostDefaultsCommandHistoryDesc": "Registra cronologia comandi sui nuovi host per impostazione predefinita", + "hostDefaultsSaved": "Impostazioni predefinite host salvate", + "hostDefaultsSaveFailed": "Salvataggio impostazioni predefinite host non riuscito", "rolePermissions": { "count": "{{count}} permessi", - "editAction": "Modifica autorizzazioni", - "loadError": "Impossibile caricare il catalogo delle autorizzazioni.", - "saved": "Permessi di ruolo salvati", - "saveError": "Impossibile salvare le autorizzazioni del ruolo.", + "editAction": "Modifica permessi", + "loadError": "Caricamento catalogo permessi non riuscito", + "saved": "Permessi ruolo salvati", + "saveError": "Salvataggio permessi ruolo non riuscito", "save": "Salva", - "saving": "Risparmio..." + "saving": "Salvataggio in corso..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Ospite", + "hostLabel": "Host", "hostPlaceholder": "192.168.1.1 o example.com", "portLabel": "Porta", "portPlaceholder": "22", "usernameLabel": "Nome utente", - "usernamePlaceholder": "nome utente", + "usernamePlaceholder": "username", "authLabel": "Autenticazione", "passwordLabel": "Password", "passwordPlaceholder": "password", "privateKeyLabel": "Chiave privata", "privateKeyPlaceholder": "Incolla la chiave privata...", - "credentialLabel": "Credenziali", + "credentialLabel": "Credenziale", "credentialPlaceholder": "Seleziona una credenziale salvata", - "connectToTerminal": "Connettersi al terminale", + "connectToTerminal": "Connetti al terminale", "connectToFiles": "Connetti ai file" }, "history": { "noTerminalSelected": "Nessun terminale selezionato", - "noTerminalSelectedHint": "Apri una scheda del terminale SSH per visualizzare la cronologia dei comandi.", - "searchPlaceholder": "Cronologia delle ricerche...", + "noTerminalSelectedHint": "Apri una scheda di terminale SSH per visualizzare la cronologia dei comandi", + "searchPlaceholder": "Cerca nella cronologia...", "clearAll": "Cancella tutto", "noHistoryEntries": "Nessuna voce nella cronologia", - "trackingDisabled": "La cronologia delle attività è disabilitata.", + "trackingDisabled": "Tracciamento cronologia disabilitato", "trackingDisabledHint": "Abilitalo nelle impostazioni del terminale dell'host." }, "sshTools": { - "keyRecordingTitle": "Registrazione chiave", - "recordToTerminals": "Registrare ai terminali", - "selectAll": "Tutto", + "keyRecordingTitle": "Registrazione tasti", + "recordToTerminals": "Registra sui terminali", + "selectAll": "Tutti", "selectNone": "Nessuno", - "noTerminalTabsOpen": "Nessuna scheda del terminale aperta", - "selectTerminalsAbove": "Selezionare i terminali sopra", - "broadcastInputPlaceholder": "Digita qui per trasmettere la sequenza di tasti premuti...", - "fillPassword": "Inserisci la password", - "fillPasswordSuccess": "Password inserita nel/nei terminale/i {{count}}", - "fillPasswordMissing": "Nessuna password salvata per il/i terminale/i selezionato/i {{count}}", - "stopRecording": "Interrompi la registrazione", - "startRecording": "Avvia la registrazione", + "noTerminalTabsOpen": "Nessuna scheda di terminale aperta", + "selectTerminalsAbove": "Seleziona i terminali sopra", + "broadcastInputPlaceholder": "Digita qui per trasmettere le pressioni dei tasti...", + "fillPassword": "Inserisci password", + "fillPasswordSuccess": "Password inserita in {{count}} terminale/i", + "fillPasswordMissing": "Nessuna password salvata per {{count}} terminale/i selezionato/i", + "stopRecording": "Interrompi registrazione", + "startRecording": "Avvia registrazione", "settingsTitle": "Impostazioni", - "enableRightClickCopyPaste": "Abilita copia/incolla tramite clic destro" + "enableRightClickCopyPaste": "Abilita copia/incolla con clic destro" }, "splitScreen": { - "layoutTitle": "Disposizione", - "selectLayoutAbove": "Seleziona un layout qui sopra", + "layoutTitle": "Layout", + "selectLayoutAbove": "Seleziona un layout sopra", "selectLayoutHint": "Scegli quanti riquadri visualizzare", - "panesTitle": "Pannelli", + "panesTitle": "Riquadri", "openTabsTitle": "Schede aperte", - "dragTabsHint": "Trascina le schede nei riquadri sopra oppure usa Assegnazione rapida", - "dropHere": "Lascia qui", + "dragTabsHint": "Trascina le schede nei riquadri sopra, oppure usa Assegnazione rapida", + "dropHere": "Rilascia qui", "emptyPane": "Vuoto", - "dashboard": "Pannello di controllo", - "clearSplitScreen": "Schermo diviso trasparente", + "dashboard": "Dashboard", + "clearSplitScreen": "Cancella divisione schermo", "quickAssign": "Assegnazione rapida", - "alreadyAssigned": "Pane {{index}}", - "splitTab": "Scheda divisa", - "addToSplit": "Aggiungi a Divide", - "removeFromSplit": "Rimuovi da Split", - "assignToPane": "Assegna al riquadro", + "alreadyAssigned": "Pannello {{index}}", + "splitTab": "Dividi scheda", + "addToSplit": "Aggiungi alla divisione", + "removeFromSplit": "Rimuovi dalla divisione", + "assignToPane": "Assegna al pannello", "hotkeysTitle": "Scorciatoie da tastiera", - "hotkeysSplitRight": "Attiva/disattiva la divisione a due vie", - "hotkeysSplitBelow": "Attiva/disattiva la divisione in 3 parti", - "hotkeysNavigatePane": "Pannelli di navigazione", + "hotkeysSplitRight": "Attiva/disattiva divisione in 2 pannelli", + "hotkeysSplitBelow": "Attiva/disattiva divisione in 3 pannelli", + "hotkeysNavigatePane": "Naviga tra i pannelli", "hotkeysNextTab": "Scheda successiva", - "hotkeysPrevTab": "scheda precedente" + "hotkeysPrevTab": "Scheda precedente" }, "snippets": { - "title": "Frammenti", + "title": "Snippet", "createSnippetTitle": "Crea snippet", - "createSnippetDescription": "Crea un nuovo frammento di comando per un'esecuzione rapida", + "createSnippetDescription": "Crea un nuovo snippet di comando per un'esecuzione rapida", "nameLabel": "Nome", - "namePlaceholder": "Ad esempio, riavviare Nginx", + "namePlaceholder": "es. Riavvia Nginx", "descriptionLabel": "Descrizione", - "descriptionPlaceholder": "Descrizione facoltativa", + "descriptionPlaceholder": "Descrizione opzionale", "optional": "Opzionale", "folderLabel": "Cartella", - "noFolder": "Nessuna cartella (Non categorizzato)", + "noFolder": "Nessuna cartella (Senza categoria)", "commandLabel": "Comando", - "commandPlaceholder": "ad esempio, sudo systemctl restart nginx", - "cancel": "Cancellare", + "commandPlaceholder": "es. sudo systemctl restart nginx", + "cancel": "Annulla", "createSnippetButton": "Crea snippet", "createFolderTitle": "Crea cartella", - "createFolderDescription": "Organizza i tuoi frammenti in cartelle", - "folderNameLabel": "Nome della cartella", - "folderNamePlaceholder": "Ad esempio, comandi di sistema, script Docker.", - "folderColorLabel": "Colore della miscela", - "folderIconLabel": "Icona della cartella", + "createFolderDescription": "Organizza i tuoi snippet in cartelle", + "folderNameLabel": "Nome cartella", + "folderNamePlaceholder": "es. Comandi di sistema, Script Docker", + "folderColorLabel": "Colore cartella", + "folderIconLabel": "Icona cartella", "previewLabel": "Anteprima", - "folderNameFallback": "Nome della cartella", + "folderNameFallback": "Nome cartella", "createFolderButton": "Crea cartella", "targetTerminals": "Terminali di destinazione", - "selectAll": "Tutto", + "selectAll": "Tutti", "selectNone": "Nessuno", - "noTerminalTabsOpen": "Nessuna scheda del terminale aperta", - "searchPlaceholder": "Frammenti di ricerca...", - "newSnippet": "Nuovo frammento", + "noTerminalTabsOpen": "Nessuna scheda terminale aperta", + "searchPlaceholder": "Cerca snippet...", + "newSnippet": "Nuovo snippet", "newFolder": "Nuova cartella", - "run": "Correre", - "noSnippetsInFolder": "Nessun frammento in questa cartella", - "uncategorized": "Non categorizzato", - "editSnippetTitle": "Modifica frammento", - "editSnippetDescription": "Aggiorna questo frammento di comando", - "saveSnippetButton": "Salva le modifiche", + "run": "Esegui", + "noSnippetsInFolder": "Nessuno snippet in questa cartella", + "uncategorized": "Senza categoria", + "editSnippetTitle": "Modifica snippet", + "editSnippetDescription": "Aggiorna questo snippet di comando", + "saveSnippetButton": "Salva modifiche", "createSuccess": "Snippet creato con successo", "createFailed": "Impossibile creare lo snippet", "updateSuccess": "Snippet aggiornato con successo", "updateFailed": "Impossibile aggiornare lo snippet", - "deleteFailed": "Impossibile eliminare il frammento", + "deleteFailed": "Impossibile eliminare lo snippet", "folderCreateSuccess": "Cartella creata con successo", "folderCreateFailed": "Impossibile creare la cartella", "editFolderTitle": "Modifica cartella", - "editFolderDescription": "Rinominare o modificare l'aspetto di questa cartella", - "saveFolderButton": "Salva le modifiche", + "editFolderDescription": "Rinomina o modifica l'aspetto di questa cartella", + "saveFolderButton": "Salva modifiche", "editFolder": "Modifica cartella", - "deleteFolder": "Elimina la cartella", + "deleteFolder": "Elimina cartella", "folderDeleteSuccess": "Cartella \"{{name}}\" eliminata", "folderDeleteFailed": "Impossibile eliminare la cartella", - "folderEditSuccess": "Cartella aggiornata correttamente", + "folderEditSuccess": "Cartella aggiornata con successo", "folderEditFailed": "Impossibile aggiornare la cartella", "confirmRunMessage": "Eseguire \"{{name}}\"?", - "confirmRunButton": "Correre", - "runSuccess": "Eseguito \"{{name}}\" nel/nei terminale/i {{count}}", + "confirmRunButton": "Esegui", + "runSuccess": "Eseguito \"{{name}}\" su {{count}} terminali", "copySuccess": "Copiato \"{{name}}\" negli appunti", - "shareTitle": "Condividi frammento", + "shareTitle": "Condividi snippet", "shareUser": "Utente", "shareRole": "Ruolo", "selectUser": "Seleziona un utente...", "selectRole": "Seleziona un ruolo...", - "shareSuccess": "Frammento condiviso con successo", - "shareFailed": "Impossibile condividere lo snippet", + "shareSuccess": "Snippet condiviso con successo", + "shareFailed": "Condivisione snippet non riuscita", "revokeSuccess": "Accesso revocato", - "revokeFailed": "Impossibile revocare l'accesso", - "currentAccess": "Accesso attuale", - "shareLoadError": "Impossibile caricare i dati condivisi", + "revokeFailed": "Revoca accesso non riuscita", + "currentAccess": "Accesso corrente", + "shareLoadError": "Caricamento dati di condivisione non riuscito", "loading": "Caricamento...", - "close": "Vicino", - "reorderFailed": "Impossibile salvare l'ordine degli snippet", - "importExport": "Importazione/Esportazione", + "close": "Chiudi", + "reorderFailed": "Salvataggio ordine snippet non riuscito", + "importExport": "Importa / Esporta", "exportBtn": "Esporta JSON", "importBtn": "Importa JSON", - "exportSuccess": "Frammenti esportati con successo", - "exportFailed": "Impossibile esportare gli snippet", - "importTitle": "Importa frammenti", - "importDescription": "Importa frammenti e cartelle da un file JSON esportato da Termix", - "importDropOrClick": "Trascina qui un file JSON oppure fai clic per sfogliare", + "exportSuccess": "Snippet esportati con successo", + "exportFailed": "Esportazione snippet non riuscita", + "importTitle": "Importa snippet", + "importDescription": "Importa snippet e cartelle da un file JSON esportato da Termix", + "importDropOrClick": "Trascina qui un file JSON o clicca per cercare", "importSelectedFile": "Selezionato: {{name}}", - "importOverwrite": "Sovrascrivi i frammenti esistenti con lo stesso nome e nella stessa cartella", - "importStartBtn": "Importare", - "importSuccess": "Importazione completata: {{snippets}} snippet(s) aggiunti, {{updated}} aggiornati, {{skipped}} saltati, {{folders}} cartelle(s) aggiunte.", - "importFailed": "Impossibile importare gli snippet", - "importInvalidFile": "File non valido: previsto un oggetto JSON con array di frammenti o cartelle", - "targetHostsLabel": "Target Hosts", - "targetHostsHint": "Assegna gli host per eseguire questo frammento direttamente senza un terminale aperto.", + "importOverwrite": "Sovrascrivi snippet esistenti con stesso nome e cartella", + "importStartBtn": "Importa", + "importSuccess": "Importazione completata: {{snippets}} snippet aggiunti, {{updated}} aggiornati, {{skipped}} saltati, {{folders}} cartelle aggiunte", + "importFailed": "Importazione snippet non riuscita", + "importInvalidFile": "File non valido: previsto un oggetto JSON con array di snippet o cartelle", + "targetHostsLabel": "Host di destinazione", + "targetHostsHint": "Assegna host per eseguire questo snippet direttamente senza un terminale aperto.", "noHostsAvailable": "Nessun host configurato", "clearTargetHosts": "Cancella tutto", "hasTargetHosts": "Ha host di destinazione", - "runOnTargets": "Esecuzione sui bersagli", - "directRunSuccess": "Eseguito \"{{name}}\" su {{count}} host(i)", + "runOnTargets": "Esegui sugli host", + "directRunSuccess": "Eseguito \"{{name}}\" su {{count}} host", "directRunPartialFail": "\"{{name}}\" non riuscito su uno o più host", - "executionResultTitle": "Risultati dell'esecuzione: {{name}}", - "executionResultDescription": "Output derivante dall'esecuzione del frammento di codice su ciascun host di destinazione.", + "executionResultTitle": "Risultati esecuzione: {{name}}", + "executionResultDescription": "Output dell'esecuzione dello snippet su ciascun host di destinazione.", "executionSuccess": "Successo", "executionFailed": "Fallito" }, + "keybindings": { + "title": "Scorciatoie da tastiera", + "description": "Personalizza le scorciatoie da tastiera per copiare, incollare e controllare il terminale, oppure associa dei tasti per inviare testo o eseguire un frammento di codice.", + "defaultsHeading": "Scorciatoie integrate", + "customHeading": "Collegamenti personalizzati", + "addBinding": "Aggiungi collegamento", + "addBindingTitle": "Aggiungi collegamento", + "editBindingTitle": "Modifica collegamento", + "loading": "Caricamento...", + "noCustomBindings": "Nessuna scorciatoia personalizzata ancora", + "defaultBadge": "Predefinito", + "customizedBadge": "Personalizzato", + "customize": "Personalizzare", + "resetToDefault": "Ripristina le impostazioni predefinite", + "resetAllToDefaults": "Ripristina tutte le impostazioni predefinite", + "close": "Vicino", + "cancel": "Cancellare", + "saveBinding": "Salva collegamento", + "saveError": "Impossibile salvare le scorciatoie da tastiera", + "pressKeysToRecord": "Combinazione di tasti", + "pressKeysPlaceholder": "Fare clic e premere i tasti", + "recording": "Premere i tasti...", + "comboRequiredError": "Premere prima una combinazione di tasti", + "textRequiredError": "Inserisci il testo da inviare", + "controlCodeRequiredError": "Inserisci una sola lettera come codice di controllo", + "snippetRequiredError": "Seleziona un frammento", + "conflictWarning": "Questa combinazione è già associata a {{combo}}. Il salvataggio farà scattare entrambi.", + "actionLabel": "Azione", + "actionCopy": "Copia selezione", + "actionPaste": "Impasto", + "actionSendControlCode": "Invia il segnale Ctrl+lettera", + "actionSendText": "Invia testo", + "actionRunSnippet": "Esegui il frammento di codice esistente", + "controlCodeLabel": "Lettera", + "textLabel": "Testo da inviare", + "appendEnterLabel": "Premi Invio dopo aver inviato", + "snippetLabel": "Frammento", + "selectSnippetPlaceholder": "Seleziona un frammento", + "orphanedSnippetWarning": "frammento non trovato", + "clipboardPermissionNote": "In alcuni browser potrebbe essere richiesta l'autorizzazione per l'apposizione degli appunti al primo utilizzo." + }, "userProfile": { - "donateTitle": "Mantieni in vita Termix", - "donateDescription": "Termix è gratuito, autogestito e sviluppato da poche persone nel loro tempo libero. Se ti ha fatto risparmiare tempo o denaro, una donazione in criptovalute contribuisce a mantenerlo attivo.", - "donateMilestones": "Le donazioni contribuiscono a finanziare il tempo dedicato alla ricerca e all'apprendimento di ciò che è necessario per sviluppare il supporto per SAML, Kubernetes e Agent. Segui i progressi e dona.", + "donateTitle": "Mantieni Termix vivo", + "donateDescription": "Termix è gratuito, self-hosted e sviluppato da poche persone nel tempo libero. Se ti ha fatto risparmiare tempo o denaro, una donazione in criptovalute aiuta a mantenerlo attivo.", + "donateMilestones": "Le donazioni finanziano il tempo per ricercare e apprendere ciò che serve per implementare il supporto a SAML, Kubernetes e Agent. Vedi i progressi e dona.", "donateButton": "Dona criptovalute", - "storageModeLocal": "Sito web", - "storageModeCloud": "Schermata", - "storageModeDescription": "Il browser memorizza le impostazioni solo in questo browser. Il database si sincronizza con il server e viene caricato su qualsiasi dispositivo.", - "resetToDefaults": "Ripristina le impostazioni predefinite", - "resetToDefaultsSuccess": "Le impostazioni sono state ripristinate ai valori predefiniti.", - "storageModeSwitch": "Preference Storage", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser salva le impostazioni solo in questo browser. Database le sincronizza con il server e le carica su qualsiasi dispositivo.", + "resetToDefaults": "Ripristina predefiniti", + "resetToDefaultsSuccess": "Impostazioni ripristinate ai valori predefiniti.", + "storageModeSwitch": "Memorizzazione preferenze", "sectionAccount": "Account", "sectionAppearance": "Aspetto", "sectionSecurity": "Sicurezza", "sectionApiKeys": "Chiavi API", - "sectionC2sTunnels": "Gallerie C2S", + "sectionData": "Dati", + "sectionC2sTunnels": "Tunnel C2S", "usernameLabel": "Nome utente", "roleLabel": "Ruolo", "roleAdministrator": "Amministratore", "authMethodLabel": "Metodo di autenticazione", "authMethodLocal": "Locale", - "twoFaLabel": "Autenticazione a due fattori (2FA)", - "twoFaOn": "SU", - "twoFaOff": "Spento", + "twoFaLabel": "2FA", + "twoFaOn": "Attivo", + "twoFaOff": "Disattivo", "versionLabel": "Versione", "betaProgramTitle": "Programma Beta", - "betaProgramDescription": "Prova in anteprima le nuove funzionalità con il tag Docker :beta, disponibile settimanalmente. Versione instabile, non adatta all'ambiente di produzione.", + "betaProgramDescription": "Prova in anteprima le prossime funzionalità con il tag Docker :beta a rilascio continuo. Instabile, non per ambienti di produzione.", "betaProgramFeedback": "Hai trovato un bug? Segnalalo qui.", - "betaProgramLearnMore": "Saperne di più", + "betaProgramLearnMore": "Scopri di più", "deleteAccount": "Elimina account", "deleteAccountDescription": "Elimina definitivamente il tuo account", - "changeServerDescription": "Passa a un server backend Termix diverso", - "deleteButton": "Eliminare", + "changeServerDescription": "Passa a un altro server backend Termix", + "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", + "deleteAccountWarning": "Tutte le sessioni, gli host, le credenziali e le impostazioni verranno eliminate definitivamente.", + "confirmPasswordDeletePlaceholder": "Inserisci la password per confermare", "languageLabel": "Lingua", "themeLabel": "Tema", - "fontSizeLabel": "Dimensione del carattere", - "accentColorLabel": "Colore d'accento", - "settingsTerminal": "terminale", - "commandAutocomplete": "Completamento automatico dei comandi", - "commandAutocompleteDesc": "Mostra il completamento automatico durante la digitazione", - "terminalLinkBehavior": "Collegamento terminale Fare clic", - "terminalLinkBehaviorDesc": "Comportamento predefinito quando si fa clic sui collegamenti nel terminale", - "historyTracking": "Tracciamento della cronologia", + "fontSizeLabel": "Dimensione carattere", + "accentColorLabel": "Colore accento", + "settingsTerminal": "Terminale", + "commandAutocomplete": "Completamento comandi", + "commandAutocompleteDesc": "Mostra completamento durante la digitazione", + "keyboardShortcuts": "Scorciatoie da tastiera", + "keyboardShortcutsDescription": "Personalizza le scorciatoie da tastiera per copiare, incollare e i comandi del terminale.", + "manageShortcuts": "Maneggio", + "terminalLinkBehavior": "Clic su link nel terminale", + "terminalLinkBehaviorDesc": "Comportamento predefinito quando si fa clic su un collegamento nel terminale", + "historyTracking": "Tracciamento cronologia", "historyTrackingDesc": "Traccia i comandi del terminale", - "commandPalette": "Tavolozza dei comandi", - "commandPaletteDesc": "Apri la 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 la chiusura della scheda", - "confirmTabCloseDesc": "Chiedi il permesso prima di chiudere le schede del terminale.", + "commandPalette": "Palette comandi", + "commandPaletteDesc": "Abilita scorciatoia da tastiera", + "reopenTabsOnLogin": "Riapri 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", "settingsSidebar": "Barra laterale", - "showHostTags": "Tag del presentatore dello show", - "showHostTagsDesc": "Visualizza i tag nell'elenco 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", - "compactHostView": "Visualizzazione host compatta", - "compactHostViewDesc": "Riduci ciascun host a una singola riga, mostrando solo il suo nome e indirizzo.", + "showHostTags": "Mostra etichette host", + "showHostTagsDesc": "Visualizza le etichette nell'elenco host", + "hostTrayOnClick": "Clic per espandere azioni host", + "hostTrayOnClickDesc": "Mostra sempre i pulsanti di connessione; clicca per espandere le opzioni di gestione invece del passaggio del mouse", + "compactHostView": "Vista host compatta", + "compactHostViewDesc": "Riduci ogni host a una singola riga mostrando solo nome e indirizzo", "statusColors": "Colori di stato reali", - "statusColorsDesc": "Utilizzare il verde/rosso per lo stato online/offline al posto del colore di accento", - "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Mantieni la barra laterale sinistra sempre espansa, anziché espanderla al passaggio del mouse.", - "expandAppRailOnHover": "Espandi App Rail al passaggio del mouse", - "expandAppRailOnHoverDesc": "Consenti alla barra laterale sinistra di espandersi quando il puntatore si sposta su di essa", + "statusColorsDesc": "Usa verde/rosso per lo stato online/offline invece del colore accento", + "pinAppRail": "Fissa barra app", + "pinAppRailDesc": "Mantieni la barra app laterale sinistra sempre espansa, invece di espanderla al passaggio del mouse", + "expandAppRailOnHover": "Espandi barra app al passaggio del mouse", + "expandAppRailOnHoverDesc": "Consenti alla barra app laterale sinistra di espandersi quando il puntatore vi passa sopra", "settingsNavigation": "Navigazione", - "navigationTabsDesc": "Scegli quali schede visualizzare nella barra delle applicazioni", - "settingsSnippets": "Frammenti", - "foldersCollapsed": "Cartelle compresse", - "foldersCollapsedDesc": "Comprimi le cartelle per impostazione predefinita", + "navigationTabsDesc": "Scegli quali schede visualizzare nella barra laterale", + "settingsSnippets": "Snippet", + "foldersCollapsed": "Cartelle chiuse", + "foldersCollapsedDesc": "Chiudi le cartelle per impostazione predefinita", "confirmExecution": "Conferma esecuzione", "confirmExecutionDesc": "Conferma prima di eseguire gli snippet", "settingsUpdates": "Aggiornamenti", - "disableUpdateChecks": "Disabilita i controlli degli aggiornamenti", - "disableUpdateChecksDesc": "Smetti di controllare gli aggiornamenti", + "disableUpdateChecks": "Disabilita controllo aggiornamenti", + "disableUpdateChecksDesc": "Non verificare la presenza di aggiornamenti", "totpAuthenticator": "Autenticatore TOTP", - "totpEnabled": "L'autenticazione a due fattori è abilitata", - "totpDisabled": "Aggiungi ulteriori misure di sicurezza per l'accesso.", + "totpEnabled": "2FA è attivata", + "totpDisabled": "Aggiungi sicurezza aggiuntiva all'accesso", "disable": "Disabilita", - "enable": "Abilitare", + "enable": "Abilita", "setupTotp": "Configura TOTP", "qrCode": "Codice QR", - "totpInstructions": "Scansiona il codice QR o inserisci il codice segreto nella tua app di autenticazione, quindi inserisci il codice a 6 cifre", + "totpInstructions": "Scansiona il codice QR o inserisci il segreto nella tua app di autenticazione, quindi inserisci il codice a 6 cifre", "totpCodePlaceholder": "000000", - "verify": "Verificare", - "changePassword": "Cambiare la password", - "currentPasswordLabel": "password attuale", + "verify": "Verifica", + "changePassword": "Modifica password", + "currentPasswordLabel": "Password attuale", "currentPasswordPlaceholder": "Password attuale", "newPasswordLabel": "Nuova password", "newPasswordPlaceholder": "Nuova password", - "confirmPasswordLabel": "Conferma la nuova password", - "confirmPasswordPlaceholder": "Conferma la nuova password", + "confirmPasswordLabel": "Conferma nuova password", + "confirmPasswordPlaceholder": "Conferma nuova password", "updatePassword": "Aggiorna password", "createApiKeyTitle": "Crea chiave API", "createApiKeyDescription": "Genera una nuova chiave API per l'accesso programmatico.", "apiKeyNameLabel": "Nome", - "apiKeyNamePlaceholder": "ad esempio Pipeline CI", + "apiKeyNamePlaceholder": "es. Pipeline CI", "expiryDateLabel": "Data di scadenza", "optional": "opzionale", - "cancel": "Cancellare", + "cancel": "Annulla", "createKey": "Crea chiave", "apiKeyCount": "{{count}} chiavi", "newKey": "Nuova chiave", - "noApiKeys": "Non sono ancora disponibili chiavi API.", - "apiKeyActive": "Attivo", - "apiKeyUsageHint": "Includi la tua chiave nel", + "noApiKeys": "Nessuna chiave API ancora.", + "apiKeyActive": "Attiva", + "apiKeyUsageHint": "Includi la tua chiave nell'", "apiKeyUsageHintHeader": "intestazione.", - "apiKeyPermissionsHint": "Le chiavi ereditano le autorizzazioni dell'utente che le ha create.", + "apiKeyPermissionsHint": "Le chiavi ereditano i permessi dell'utente che le ha create.", + "exportData": "Esporta i miei dati", + "exportDataDesc": "Scarica una copia di backup dei tuoi host, credenziali e impostazioni per trasferirla su un altro dispositivo.", + "export": "Esportare", + "exporting": "Esportazione...", + "importData": "Importa i miei dati", + "importDataDesc": "Ripristina host, credenziali e impostazioni da un file di backup .sqlite", + "importDataSelected": "Selezionato: {{name}}", + "selectFile": "Seleziona il file", + "changeFile": "Modifica", + "import": "Importare", + "importing": "Importazione...", + "exportSuccess": "Esportazione dei dati completata con successo.", + "exportFailed": "L'esportazione dei dati non è riuscita.", + "importSelectFile": "Seleziona prima un file", + "importCompleted": "Importazione completata: {{total}} elementi importati, {{skipped}} elementi saltati", + "importFailed": "Importazione non riuscita: {{error}}", "roleUser": "Utente", - "authMethodDual": "Autenticazione doppia", - "authMethodOidc": "ODDC", + "authMethodDual": "Doppia autenticazione", + "authMethodOidc": "OIDC", "totpSetupFailed": "Impossibile avviare la configurazione TOTP", - "totpEnter6Digits": "Inserisci un codice di 6 cifre", + "totpEnter6Digits": "Inserisci un codice a 6 cifre", "totpEnabledSuccess": "Autenticazione a due fattori abilitata", - "totpInvalidCode": "Codice non valido, riprova.", + "totpInvalidCode": "Codice non valido, riprova", "totpDisableInputRequired": "Inserisci il tuo codice TOTP o la password", "totpDisabledSuccess": "Autenticazione a due fattori disabilitata", - "totpDisableFailed": "Impossibile disattivare l'autenticazione a due fattori (2FA).", - "totpDisableTitle": "Disabilita l'autenticazione a due fattori", - "totpDisablePlaceholder": "Inserisci il codice TOTP o la password", - "totpDisableConfirm": "Disabilita l'autenticazione a due fattori", - "totpContinueVerify": "Continua a verificare", - "totpVerifyTitle": "Verifica il codice", + "totpDisableFailed": "Impossibile disabilitare la 2FA", + "totpDisableTitle": "Disabilita 2FA", + "totpDisablePlaceholder": "Inserisci codice TOTP o password", + "totpDisableConfirm": "Disabilita 2FA", + "totpContinueVerify": "Procedi alla verifica", + "totpVerifyTitle": "Verifica codice", "totpBackupTitle": "Codici di backup", "totpDownloadBackup": "Scarica i codici di backup", - "passkeys": "Password", - "passkeysDesc": "Utilizza le credenziali WebAuthn/FIDO2 per accedere senza password.", - "passkeyName": "Nome della password", + "passkeys": "Passkeys", + "passkeysDesc": "Utilizza credenziali WebAuthn/FIDO2 per accedere senza password", + "passkeyName": "Nome passkey", "passkeyUvPreferred": "Preferita", - "passkeyUvRequired": "Necessario", - "passkeyUvDiscouraged": "Scoraggiato", - "addPasskey": "Aggiungi password", - "noPasskeys": "Nessuna password registrata.", - "passkeyAdded": "Parkey ha aggiunto", - "passkeyAddFailed": "Impossibile aggiungere la password", - "passkeyDeleted": "Password eliminata", - "passkeyDeleteFailed": "Impossibile eliminare la password", + "passkeyUvRequired": "Obbligatoria", + "passkeyUvDiscouraged": "Sconsigliata", + "addPasskey": "Aggiungi passkey", + "noPasskeys": "Nessuna passkey registrata.", + "passkeyAdded": "Passkey aggiunta", + "passkeyAddFailed": "Impossibile aggiungere la passkey", + "passkeyDeleted": "Passkey eliminata", + "passkeyDeleteFailed": "Impossibile eliminare la passkey", "done": "Fatto", "secretCopied": "Segreto copiato negli appunti", - "apiKeyNameRequired": "È richiesto il nome della chiave", + "apiKeyNameRequired": "Il nome della chiave è obbligatorio", "apiKeyCreated": "Chiave API \"{{name}}\" creata", "apiKeyCreateFailed": "Impossibile creare la chiave API", "apiKeyUser": "Utente", - "apiKeyExpires": "Scade", + "apiKeyExpires": "Scadenza", "apiKeyRevoked": "Chiave API \"{{name}}\" revocata", "apiKeyRevokeFailed": "Impossibile revocare la chiave API", - "passwordFieldsRequired": "Sono richieste password attuali e nuove.", + "passwordFieldsRequired": "Password attuale e nuova sono obbligatorie", "passwordMismatch": "Le password non corrispondono", - "passwordTooShort": "La password deve essere lunga almeno 6 caratteri.", - "passwordUpdated": "Password aggiornata correttamente", + "passwordTooShort": "La password deve contenere almeno 6 caratteri", + "passwordUpdated": "Password aggiornata con successo", "passwordUpdateFailed": "Impossibile aggiornare la password", - "deletePasswordRequired": "Per eliminare il tuo account è necessaria la password.", + "deletePasswordRequired": "La password è necessaria per eliminare l'account", "deleteFailed": "Impossibile eliminare l'account", "deleting": "Eliminazione in corso...", - "colorPickerTooltip": "Apri il selettore colori", + "colorPickerTooltip": "Apri selettore colore", "themeSystem": "Sistema", - "themeLight": "Leggero", - "themeDark": "Buio", + "themeLight": "Chiaro", + "themeDark": "Scuro", "themeDracula": "Dracula", - "themeCatppuccin": "Catppuccino", + "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizzato", - "themeTokyoNight": "Notte a Tokyo", - "themeOneDark": "Una Oscura", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { - "title": "Monitoraggio Tmux", + "title": "Monitor Tmux", "failedToLoadHosts": "Impossibile caricare gli host", "failedToLoad": "Impossibile caricare le sessioni tmux", "tmuxUnavailable": "tmux non è installato su questo host", "noSessions": "Nessuna sessione tmux su questo host", "noHostSelected": "Nessun host selezionato", - "attached": "Allegato", - "detached": "Distaccato", - "attach": "Allegare", + "attached": "Connesso", + "detached": "Scollegato", + "attach": "Connetti", "editTags": "Modifica tag", "tagsHint": "Tag separati da virgole (es. YOLO, lab, training)", "tagsSaved": "Tag salvati", - "tagsSaveFailed": "Impossibile salvare i tag", - "searchPlaceholder": "Risultati della ricerca tra le sessioni...", + "tagsSaveFailed": "Salvataggio dei tag non riuscito", + "searchPlaceholder": "Cerca output tra le sessioni...", "searchResults": "{{count}} corrispondenze", "searchFailed": "Ricerca non riuscita", - "selectPaneHint": "Seleziona un riquadro per visualizzarne l'anteprima", + "selectPaneHint": "Seleziona un pannello per visualizzare l'anteprima dell'output", "closePreview": "Chiudi anteprima", "newSession": "Nuova sessione", - "newSessionHint": "Nome della sessione (lettere, cifre, _ @ % + = -)", - "newSessionPlaceholder": "la mia sessione", - "create": "Creare", + "newSessionHint": "Nome sessione (lettere, cifre, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Crea", "sessionCreated": "Sessione \"{{name}}\" creata", - "sessionCreateFailed": "Impossibile creare la sessione", - "splitRight": "chiamata divisa", - "splitDown": "Diviso", - "splitFailed": "Impossibile dividere il riquadro", + "sessionCreateFailed": "Creazione della sessione non riuscita", + "splitRight": "Dividi a destra", + "splitDown": "Dividi in basso", + "splitFailed": "Divisione del pannello non riuscita", "newWindow": "Nuova finestra", - "windowCreateFailed": "Impossibile creare la finestra", - "attachSessionTooltip": "Allega a {{session}}", - "refresh": "Rinfrescato", - "refreshFailed": "Impossibile aggiornare le sessioni", + "windowCreateFailed": "Creazione della finestra non riuscita", + "attachSessionTooltip": "Connetti a {{session}}", + "refresh": "Aggiorna", + "refreshFailed": "Aggiornamento delle sessioni non riuscito", "collapseAll": "Comprimi tutto", "expandAll": "Espandi tutto", "moreActions": "Altre azioni", - "sessionStats": "Statistiche della sessione", - "renameSessionTitle": "Rinomina la sessione \"{{name}}\"", - "rename": "Rinominare", + "sessionStats": "Statistiche sessione", + "renameSessionTitle": "Rinomina sessione \"{{name}}\"", + "rename": "Rinomina", "sessionRenamed": "Sessione rinominata in \"{{name}}\"", - "sessionRenameFailed": "Impossibile rinominare la sessione", - "editTagsTitle": "Modifica i tag per \"{{name}}\"", - "killPane": "Pannello di controllo", - "resizeTree": "Trascina per ridimensionare — fai doppio clic per ripristinare", - "reattach": "Ricollega (corregge una visualizzazione distorta)", - "killWindow": "Finestra di chiusura", - "killWindowTitle": "Finestra di eliminazione {{index}} di \"{{session}}\"?", - "killWindowBody": "Ogni riquadro e processo in questa finestra verrà terminato. La chiusura dell'ultima finestra termina la sessione.", - "windowKillFailed": "Impossibile chiudere la finestra", + "sessionRenameFailed": "Rinomina della sessione non riuscita", + "editTagsTitle": "Modifica tag per \"{{name}}\"", + "killPane": "Termina riquadro", + "resizeTree": "Trascina per ridimensionare — doppio clic per ripristinare", + "reattach": "Ricollega (risolve una visualizzazione alterata)", + "killWindow": "Termina finestra", + "killWindowTitle": "Terminare la finestra {{index}} di \"{{session}}\"?", + "killWindowBody": "Tutti i riquadri e i processi in questa finestra verranno terminati. Terminare l'ultima finestra chiude la sessione.", + "windowKillFailed": "Terminazione della finestra non riuscita", "statusActivity": "Attività", - "statusWindows": "Windows", - "statusPanes": "pannelli", - "statusTags": "Etichette", - "killPaneTitle": "Chiudi pannello {{id}}?", - "killPaneBody": "Il processo in esecuzione in questo riquadro verrà terminato. La chiusura dell'ultimo riquadro ne chiude la finestra.", - "paneKillFailed": "Impossibile terminare il pannello", - "killSessionTitle": "Termina la sessione \"{{name}}\"?", - "killSessionBody": "Tutte le finestre e i processi in esecuzione in questa sessione verranno terminati. Questa operazione è irreversibile.", - "kill": "Uccisione", + "statusWindows": "Finestre", + "statusPanes": "riquadri", + "statusTags": "Tag", + "killPaneTitle": "Terminare il riquadro {{id}}?", + "killPaneBody": "Il processo in esecuzione in questo riquadro verrà terminato. Terminare l'ultimo riquadro chiude la sua finestra.", + "paneKillFailed": "Terminazione del riquadro non riuscita", + "killSessionTitle": "Terminare la sessione \"{{name}}\"?", + "killSessionBody": "Tutte le finestre e i processi in esecuzione in questa sessione verranno terminati. Questa operazione non può essere annullata.", + "kill": "Termina", "sessionKilled": "Sessione \"{{name}}\" terminata", - "sessionKillFailed": "Impossibile terminare la sessione", - "hostUnreachable": "Impossibile connettersi all'host. Verificare che sia online e raggiungibile.", - "noServer": "Su questo host non è in esecuzione alcun server tmux.", - "searchTruncated": "Risultati parziali: la ricerca copre le ultime {{lines}} righe di ciascun riquadro e al massimo {{panes}} riquadri.", - "closeSearchResults": "Chiudi i risultati della ricerca", + "sessionKillFailed": "Terminazione della sessione non riuscita", + "hostUnreachable": "Impossibile connettersi all'host. Verifica che sia online e raggiungibile.", + "noServer": "Nessun server tmux in esecuzione su questo host.", + "searchTruncated": "Risultati parziali — la ricerca copre le ultime {{lines}} righe di ogni riquadro e al massimo {{panes}} riquadri.", + "closeSearchResults": "Chiudi risultati ricerca", "retry": "Riprova", "noHosts": "Nessun host SSH disponibile", - "noHostsHint": "Abilita l'opzione Tmux Monitor su un host SSH (Gestione host → scheda Terminale) per monitorare le sue sessioni tmux.", + "noHostsHint": "Abilita l'opzione Monitor Tmux su un host SSH (Gestione host → scheda Terminale) per monitorare le sue sessioni tmux.", "tmuxInstallHint": "Installalo sull'host con:", "attachTooltip": "Apri un terminale su {{host}}", "attachTooltipPane": "Apri un terminale su {{host}} — sessione tmux {{session}}", - "timeJustNow": "proprio adesso", + "timeJustNow": "adesso", "timeMinutes": "{{count}}m fa", - "timeHours": "{{count}}ore fa", - "timeDays": "{{count}}giorni fa" + "timeHours": "{{count}}h fa", + "timeDays": "{{count}}d fa" }, "mobileKeyboard": { - "shift": "Spostare", + "shift": "Maiusc", "ctrl": "Ctrl", "esc": "Esc", "tab": "Tab", "backTab": "⇥", - "arrowUp": "Freccia in su", - "arrowDown": "Freccia in basso", - "arrowLeft": "Freccia a sinistra", - "arrowRight": "Freccia a destra", - "home": "Casa", - "end": "FINE", - "pageUp": "PgUp", - "pageDown": "PgDn", - "delete": "Del", + "arrowUp": "Freccia Su", + "arrowDown": "Freccia Giù", + "arrowLeft": "Freccia Sinistra", + "arrowRight": "Freccia Destra", + "home": "Home", + "end": "Fine", + "pageUp": "Pag Su", + "pageDown": "Pag Giù", + "delete": "Canc", + "paste": "Impasto", "editQuickKeys": "Modifica tasti rapidi", - "quickKeysTitle": "Tasti rapidi", + "quickKeysTitle": "Tasti Rapidi", "quickKeysDesc": "Tocca × per rimuovere. Supporta fino a 8 caratteri.", - "quickKeyPlaceholder": "ad esempio sudo ", - "addQuickKey": "Aggiungere", - "removeQuickKey": "Rimuovere", - "resetDefaults": "Ripristina le impostazioni predefinite", - "done": "Fatto" + "quickKeyPlaceholder": "ad es. sudo ", + "addQuickKey": "Aggiungi", + "removeQuickKey": "Rimuovi", + "resetDefaults": "Ripristina impostazioni predefinite", + "done": "Fine" }, "serial": { "title": "Seriale", "portLabel": "Porta", - "portPlaceholder": "/dev/ttyUSB0 o COM3", - "baudRateLabel": "Velocità di trasmissione", + "portPlaceholder": "/dev/ttyUSB0 or COM3", + "baudRateLabel": "Baud rate", "dataBitsLabel": "Dati", - "stopBitsLabel": "Fermare", + "stopBitsLabel": "Stop", "parityLabel": "Parità", - "parityNone": "Nessuno", - "parityEven": "Anche", - "parityOdd": "Strano", - "connect": "Connettersi alla porta seriale", + "parityNone": "Nessuna", + "parityEven": "Pari", + "parityOdd": "Dispari", + "connect": "Connetti alla Seriale", "disconnect": "Disconnetti", - "refreshPorts": "Porti rinnovati", + "refreshPorts": "Aggiorna porte", "connected": "Connesso a {{path}} a {{baud}} baud", "disconnected": "Porta seriale disconnessa", "connectionError": "Impossibile aprire la porta seriale", "wsError": "Errore WebSocket", "errorNoServerUrl": "Nessun URL del server configurato", "notSupportedTitle": "Seriale non supportato", - "notSupported": "Le connessioni seriali richiedono un browser con supporto per l'API Web Serial (Chrome, Edge o Firefox 151+) oppure l'applicazione desktop Termix.", - "hideHint": "È possibile nascondere la scheda Seriale in Profilo utente > Aspetto > Barra laterale > Navigazione.", - "browserPickerHint": "Fai clic su Connetti e il tuo browser aprirà una finestra di selezione della porta per scegliere il dispositivo." + "notSupported": "Le connessioni seriali richiedono un browser con supporto Web Serial API (Chrome, Edge o Firefox 151+), oppure l'app desktop Termix.", + "hideHint": "Puoi nascondere la scheda Seriale in Profilo Utente > Aspetto > Barra laterale > Navigazione.", + "browserPickerHint": "Fai clic su Connetti e il browser aprirà un selettore di porte per scegliere il dispositivo." }, "metricsHistory": { - "title": "Cronologia delle metriche", - "historySuffix": "Storia", + "title": "Cronologia metriche", + "historySuffix": "Cronologia", "cpuMemoryDisk": "CPU / Memoria / Disco", "network": "Rete", "download": "RX", "upload": "TX", "noData": "Nessun dato storico disponibile per questo intervallo di tempo.", - "custom": "Costume", - "to": "A", - "apply": "Fare domanda a", - "viewHistory": "Visualizza la cronologia" + "custom": "Personalizzato", + "to": "a", + "apply": "Applica", + "viewHistory": "Visualizza cronologia" }, "alerts": { "tabFirings": "Avvisi", @@ -3433,40 +3646,40 @@ "noRules": "Nessuna regola di avviso configurata", "noChannels": "Nessun canale di notifica configurato", "rulesDesc": "Le regole di avviso attivano le notifiche", - "channelsDesc": "Dove vengono inviate le notifiche di allerta", - "acknowledge": "Riconoscere", - "ackAll": "Riprendi tutto", - "allAcknowledged": "Tutti gli avvisi sono stati confermati.", - "ackFailed": "Avviso non confermato", - "ackAllFailed": "Non è stato possibile confermare tutti gli avvisi.", - "showAcknowledged": "Mostra tutto", - "hideAcknowledged": "Nascondi Acked", + "channelsDesc": "Dove vengono inviate le notifiche di avviso", + "acknowledge": "Conferma", + "ackAll": "Conferma tutto", + "allAcknowledged": "Tutti gli avvisi confermati", + "ackFailed": "Conferma dell'avviso non riuscita", + "ackAllFailed": "Conferma di tutti gli avvisi non riuscita", + "showAcknowledged": "Mostra tutti", + "hideAcknowledged": "Nascondi confermati", "addChannel": "Aggiungi canale", "editChannel": "Modifica canale", "channelName": "Nome", "channelType": "Tipo", - "channelNameRequired": "Il nome è obbligatorio", + "channelNameRequired": "Nome obbligatorio", "webhookUrl": "URL", - "webhookUrlRequired": "È richiesto l'URL del webhook.", - "webhookDesc": "Invia il payload JSON a questo URL per ogni avviso che si attiva", - "ntfyServer": "URL del server", + "webhookUrlRequired": "URL webhook obbligatorio", + "webhookDesc": "Invia un payload JSON via POST a questo URL quando scatta un avviso", + "ntfyServer": "URL server", "ntfyTopic": "Argomento", - "ntfyTopicRequired": "È richiesto l'argomento", + "ntfyTopicRequired": "Argomento obbligatorio", "ntfyToken": "Token di accesso (facoltativo)", - "channelSaveFailed": "Impossibile salvare il canale", + "channelSaveFailed": "Salvataggio del canale non riuscito", "test": "Test", "testSent": "Notifica di test inviata", - "testFailed": "Notifica di prova non riuscita", + "testFailed": "Notifica di test fallita", "addRule": "Aggiungi regola di avviso", "editRule": "Modifica regola di avviso", - "ruleName": "Nome della regola", - "ruleNameRequired": "Il nome è obbligatorio", - "triggerType": "Grilletto", + "ruleName": "Nome regola", + "ruleNameRequired": "Nome obbligatorio", + "triggerType": "Trigger", "thresholdValue": "Soglia (%)", - "durationSeconds": "Durata (secondi, 0 = spara immediatamente)", - "cooldownMinutes": "Tempo di raffreddamento (minuti)", - "channels": "canali di notifica", + "durationSeconds": "Durata (secondi, 0 = attiva immediatamente)", + "cooldownMinutes": "Cooldown (minuti)", + "channels": "Canali di notifica", "noChannelsHint": "Aggiungi prima i canali nella scheda Canali", - "ruleSaveFailed": "Impossibile salvare la regola" + "ruleSaveFailed": "Salvataggio della regola non riuscito" } } diff --git a/src/ui/locales/translated/ja_JP.json b/src/ui/locales/translated/ja_JP.json index 0c13324e..7309f87c 100644 --- a/src/ui/locales/translated/ja_JP.json +++ b/src/ui/locales/translated/ja_JP.json @@ -1,186 +1,186 @@ { "termixId": { - "title": "ターミックスID", - "loadFailed": "Termix IDの読み込みに失敗しました", - "claimTitle": "Termix IDを取得してください", - "claimIntro": "固有のハンドルを選択してください。SSH公開鍵は公開URLで公開され、任意のサーバーのauthorized_keysファイルに追加できます。", + "title": "Termix ID", + "loadFailed": "Termix IDのロードに失敗しました", + "claimTitle": "Termix IDを取得", + "claimIntro": "一意のハンドルを選択します。SSH公開鍵が公開URLで公開され、任意のサーバーのauthorized_keysファイルに追加できます。", "handleLabel": "ハンドル", - "handlePlaceholder": "アリス", - "checking": "… を確認中", + "handlePlaceholder": "alice", + "checking": "確認中…", "available": "利用可能", - "taken": "すでに取られた", - "invalidHandle": "小文字、数字、-、_のみ", + "taken": "既に使用されています", + "invalidHandle": "英小文字、数字、ハイフン(-)、アンダースコア(_)のみ使用できます", "descriptionLabel": "説明(任意)", - "descriptionPlaceholder": "仕事用ノートパソコンと携帯電話の鍵", - "create": "Termix IDを作成する", + "descriptionPlaceholder": "仕事用ラップトップとスマートフォンのキー", + "create": "Termix IDを作成", "created": "Termix IDが作成されました", - "createFailed": "Termix ID の作成に失敗しました", - "deleteConfirm": "Termix IDと公開済みのすべてのキーを削除しますか?プロビジョニングしたサーバーは、手動で削除するまでキーを保持します。", + "createFailed": "Termix IDの作成に失敗しました", + "deleteConfirm": "Termix IDと公開されたすべてのキーを削除しますか?プロビジョニングしたサーバーでは、手動で削除するまでキーが保持されます。", "deleted": "Termix IDが削除されました", "deleteFailed": "Termix IDの削除に失敗しました", "copyFailed": "コピーに失敗しました", - "resolverUrlLabel": "パブリックリゾルバURL", - "provisionLabel": "サーバーをプロビジョニングする", - "publishTitle": "公開鍵を公開する", - "generate": "生成する", - "generateTooltip": "Ed25519キーペアを生成します — 公開鍵を公開し、秘密鍵をデバイスにダウンロードします", - "saveToVault": "認証情報を保存する", + "resolverUrlLabel": "公開リゾルバーURL", + "provisionLabel": "サーバーをプロビジョニング", + "publishTitle": "公開鍵を公開", + "generate": "生成", + "generateTooltip": "Ed25519キーペアを生成 — 公開鍵を公開し、秘密鍵をデバイスにダウンロードします", + "saveToVault": "認証情報に保存", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "ラベル(任意)", "add": "追加", - "importFromCredential": "または、保存済みの認証情報からインポートする", - "keyPublished": "主要な公開", + "importFromCredential": "または保存済みの認証情報からインポート", + "keyPublished": "キーが公開されました", "addKeyFailed": "キーの追加に失敗しました", - "generatedSaved": "鍵ペアが生成され、認証情報保管庫に保存されました。秘密鍵もダウンロードされました。", - "generatedOnly": "鍵ペアが生成されました — 秘密鍵がダウンロードされました(一度だけ表示されます)。", + "generatedSaved": "キーペアが生成され、認証情報ボールトに保存されました。秘密鍵もダウンロードされました。", + "generatedOnly": "キーペアが生成されました — 秘密鍵がダウンロードされました(一度だけ表示されます)。", "generateFailed": "キーの生成に失敗しました", - "imported": "認証情報からインポートされたキー", - "importFailed": "キーのインポートに失敗しました", - "noKeys": "公開鍵はまだ公開されていません。", - "keysTitle": "公開された鍵", + "imported": "キーが認証情報からインポートされました", + "importFailed": "鍵のインポートに失敗しました", + "noKeys": "まだ公開鍵は公開されていません。", + "keysTitle": "公開鍵", "published": "公開済み", - "hidden": "隠れた", - "keyRemoved": "鍵が取り外されました", - "removeKeyFailed": "キーの削除に失敗しました", - "updateKeyFailed": "キーの更新に失敗しました", - "fromVault": "認証情報保管庫から", - "linkedToTermixId": "Termix ID経由で公開", - "selectCredential": "資格情報を選択してください...", - "import": "輸入", + "hidden": "非表示", + "keyRemoved": "鍵が削除されました", + "removeKeyFailed": "鍵の削除に失敗しました", + "updateKeyFailed": "鍵の更新に失敗しました", + "fromVault": "認証情報ボールトから", + "linkedToTermixId": "Termix ID 経由で公開", + "selectCredential": "認証情報を選択...", + "import": "インポート", "caTitle": "認証局", - "caIntro": "サーバー上でこの認証局を信頼すれば、署名した証明書はすべて受け入れられます。ローテーションを実行すると、すべての証明書が一度に失効します。証明書は自動的に期限切れになる場合もあります。", - "caEnable": "CAを有効にする", - "caEnabled": "CAが有効", - "caCreateFailed": "CAの有効化に失敗しました", - "caPublicKeyLabel": "CA公開鍵", - "caTrustLabel": "サーバーを信頼する(root権限で実行)", - "caRotate": "回転", - "caRotateConfirm": "認証局をローテーションすると、その認証局が署名したすべての証明書は無効になり、再発行が必要になります。", - "caRotated": "CAがローテーションされました — 以前の証明書は失効しました", - "caRotateFailed": "CAの回転に失敗しました", - "caDelete": "CAを削除する", + "caIntro": "このCAをサーバーで信頼すると、署名した証明書が受け入れられます。ローテーションで一度にすべてを失効させることができ、証明書自体も有効期限が切れます。", + "caEnable": "CA を有効化", + "caEnabled": "CA 有効", + "caCreateFailed": "CA の有効化に失敗しました", + "caPublicKeyLabel": "CA 公開鍵", + "caTrustLabel": "サーバーで信頼する(root で実行)", + "caRotate": "ローテーション", + "caRotateConfirm": "CA をローテーションしますか?署名されたすべての証明書の受け入れが停止され、再発行が必要になります。", + "caRotated": "CA がローテーションされました — 以前の証明書は失効しました", + "caRotateFailed": "CA のローテーションに失敗しました", + "caDelete": "CA を削除", "caDeleteConfirm": "認証局を削除しますか?", - "caDeleted": "CAが削除されました", - "caDeleteFailed": "CAの削除に失敗しました", - "caValidityLabel": "証明書の有効期間(日数)", + "caDeleted": "CA が削除されました", + "caDeleteFailed": "CA の削除に失敗しました", + "caValidityLabel": "証明書の有効期間(日)", "issueCert": "証明書", - "issueCertTooltip": "このキーに対して、認証局(CA)が署名したSSH証明書を発行してください。", - "certIssued": "証明書が発行され、ダウンロードされました。", + "issueCertTooltip": "この鍵に対して、CA が署名した SSH 証明書を発行します", + "certIssued": "証明書が発行され、ダウンロードされました", "certIssueFailed": "証明書の発行に失敗しました" }, "credentials": { "folders": "フォルダー", - "folder": "フォルダ", + "folder": "フォルダー", "password": "パスワード", - "key": "鍵", - "sshPrivateKey": "SSH秘密鍵", + "key": "キー", + "sshPrivateKey": "SSH 秘密鍵", "upload": "アップロード", - "keyPassword": "キーパスワード", - "sshKey": "SSHキー", - "uploadPrivateKeyFile": "秘密鍵ファイルをアップロードしてください", - "searchCredentials": "認証情報を検索中...", + "keyPassword": "鍵のパスワード", + "sshKey": "SSH 鍵", + "uploadPrivateKeyFile": "秘密鍵ファイルをアップロード", + "searchCredentials": "認証情報を検索...", "addCredential": "認証情報を追加", - "caCertificate": "CA証明書(-cert.pub)", - "caCertificateDescription": "オプション:認証局(CA)が署名した証明書ファイル(例:id_ed25519-cert.pub)をアップロードまたは貼り付けてください。SSHサーバーが証明書ベースの認証を使用している場合は必須です。", - "uploadCertFile": "cert.pub ファイルをアップロードします。", + "caCertificate": "CA 証明書 (-cert.pub)", + "caCertificateDescription": "任意: CA署名済み証明書ファイルをアップロードまたは貼り付けてください (例: id_ed25519-cert.pub)。SSHサーバーが証明書ベースの認証を使用する場合に必要です。", + "uploadCertFile": "-cert.pub ファイルをアップロード", "clearCert": "クリア", "certLoaded": "証明書が読み込まれました", - "certPublicKeyLabel": "CA資格", + "certPublicKeyLabel": "CA 証明書", "certTypeLabel": "証明書の種類", - "pasteOrUploadCert": "-cert.pub 証明書を貼り付けるかアップロードしてください...", - "hasCaCert": "CA資格を保有しています。", - "noCaCert": "CA証明書なし", - "noPublicKeyAvailable": "公開鍵がありません。まず認証情報エディタを開いてください。", + "pasteOrUploadCert": "-cert.pub 証明書を貼り付けまたはアップロード...", + "hasCaCert": "CA 証明書あり", + "noCaCert": "CA 証明書なし", + "noPublicKeyAvailable": "公開鍵がありません。最初に認証情報エディタを開いてください。", "deployCommandCopied": "デプロイコマンドがコピーされました", - "sortCredentials": "ソート資格情報", - "sortDefault": "デフォルトオーダー", - "sortNameAsc": "名前(A~Z)", - "sortNameDesc": "名前(Z → A)", - "sortUsernameAsc": "ユーザー名(A~Z)", - "sortUsernameDesc": "ユーザー名(Z → A)", - "filterCredentials": "フィルター認証情報", + "sortCredentials": "認証情報の並べ替え", + "sortDefault": "デフォルトの並び順", + "sortNameAsc": "名前 (A → Z)", + "sortNameDesc": "名前 (Z → A)", + "sortUsernameAsc": "ユーザー名 (A → Z)", + "sortUsernameDesc": "ユーザー名 (Z → A)", + "filterCredentials": "認証情報の絞り込み", "filterClearAll": "フィルターをクリア", "filterTypeGroup": "タイプ", "filterTypePassword": "パスワード", - "filterTypeKey": "SSHキー", + "filterTypeKey": "SSH 鍵", "filterTagsGroup": "タグ" }, "homepage": { "title": "ホームページ", "addWidget": "ウィジェットを追加", "editWidget": "ウィジェットを編集", - "deleteWidget": "消去", - "widgetTypes": "ウィジェットの種類", + "deleteWidget": "削除", + "widgetTypes": "ウィジェットタイプ", "serviceLink": "サービスリンク", - "folder": "フォルダ", - "clock": "クロック", - "notes": "注記", + "folder": "フォルダー", + "clock": "時計", + "notes": "メモ", "hostStatus": "ホストステータス", "bookmarkList": "ブックマーク", - "zoomIn": "ズームイン", - "zoomOut": "ズームアウト", - "resetView": "リセットビュー", - "lockLayout": "ロックレイアウト", + "zoomIn": "拡大", + "zoomOut": "縮小", + "resetView": "表示をリセット", + "lockLayout": "レイアウトをロック", "unlockLayout": "レイアウトのロックを解除", - "noWidgets": "右クリックまたは「+」をクリックして最初のウィジェットを追加します", - "openFullView": "全画面表示", - "previewTitle": "ホームページのプレビュー", + "noWidgets": "右クリックまたは「+」をクリックして最初のウィジェットを追加してください", + "openFullView": "全画面表示を開く", + "previewTitle": "ホームページプレビュー", "cancel": "キャンセル", "save": "保存", "title_label": "タイトル", "widgetTitlePlaceholder": "ウィジェットのタイトル(任意)", "url": "URL", "imageUrl": "カスタム画像URL", - "imageUrlHint": "サイトのファビコンを自動的に使用する場合は、空欄のままにしてください。", + "imageUrlHint": "空白のままにすると、サイトのファビコンが自動的に使用されます", "showImage": "画像を表示", "description": "説明", "color": "色", "icon": "アイコン", - "expanded": "デフォルトで展開済み", + "expanded": "デフォルトで展開", "timezone": "タイムゾーン", - "showSeconds": "ショーセカンド", - "format12h": "12時間", - "format24h": "24時間", - "content": "コンテンツ", + "showSeconds": "秒を表示", + "format12h": "12時間表示", + "format24h": "24時間表示", + "content": "内容", "backgroundColor": "背景色", "host": "ホスト", - "showMetrics": "指標を表示する", + "showMetrics": "メトリクス表示", "links": "リンク", "addLink": "リンクを追加", "linkLabel": "ラベル", "linkUrl": "URL", - "removeLink": "取り除く", + "removeLink": "削除", "categoryLinks": "リンク", "categoryInfo": "情報", "categorySystem": "システム", - "widgetServiceLinkDesc": "サービスURLにリンクするクリック可能なタイル", - "widgetFolderDesc": "関連するウィジェットをグループ化するためのコンテナ", - "widgetClockDesc": "タイムゾーンを設定可能なライブクロック", - "widgetNotesDesc": "マークダウンノートウィジェット", - "widgetHostStatusDesc": "SSHホストのCPU、メモリ、ディスクの稼働状況をリアルタイムで表示します。", - "widgetBookmarkListDesc": "クイックリンク一覧", + "widgetServiceLinkDesc": "サービスURLへリンクするクリック可能なタイル", + "widgetFolderDesc": "関連するウィジェットをグループ化するコンテナ", + "widgetClockDesc": "設定可能なタイムゾーンを備えた現在時刻の時計", + "widgetNotesDesc": "Markdownメモウィジェット", + "widgetHostStatusDesc": "SSHホストのCPU、メモリ、ディスク使用状況をライブ表示", + "widgetBookmarkListDesc": "クイックリンクのリスト", "copyLink": "リンクをコピー", - "linkCopied": "リンクをコピーしました!", - "location": "位置", + "linkCopied": "リンクがコピーされました!", + "location": "場所", "temperatureUnit": "温度単位", - "showForecast": "3日間の天気予報を表示", - "scrolling": "スクロールを許可する", + "showForecast": "3日間の予報を表示", + "scrolling": "スクロール許可", "feedUrl": "フィードURL", "maxItems": "最大アイテム数", "showDescription": "説明を表示", "widgetWeatherName": "天気", - "widgetWeatherDesc": "あらゆる場所のリアルタイム天気予報", + "widgetWeatherDesc": "任意の場所の現在の天気", "widgetIframeName": "iFrame埋め込み", - "widgetIframeDesc": "iframe内に任意のURLを埋め込む", + "widgetIframeDesc": "任意のURLをiFrameで埋め込み", "widgetRssName": "RSSフィード", - "widgetRssDesc": "RSSまたはAtomフィードからアイテムを表示する", - "dragToFolder": "ウィジェットをここにドラッグするか、+ を使用して追加してください", - "showDisk": "ディスク使用量を表示する", - "addToFolder": "ウィジェットをフォルダに追加する", + "widgetRssDesc": "RSSまたはAtomフィードのアイテムを表示", + "dragToFolder": "ウィジェットをここにドラッグ、または+で追加", + "showDisk": "ディスク使用量を表示", + "addToFolder": "ウィジェットをフォルダに追加", "noHostSelected": "ホストが選択されていません", - "metricsNotAvailable": "指標は利用できません", - "selectHost": "ホストを選択してください...", - "displayedMetrics": "表示される指標", + "metricsNotAvailable": "メトリクスは利用できません", + "selectHost": "ホストを選択...", + "displayedMetrics": "表示メトリクス", "metricCpu": "CPU", "metricMemory": "メモリ", "metricDisk": "ディスク", @@ -192,131 +192,131 @@ "metricNetwork": "ネットワーク", "metricProcesses": "プロセス", "metricProcessesTotal": "合計", - "metricProcessesRunning": "ランニング", - "categoryMonitoring": "監視", - "loading": "読み込み中…", - "noData": "データがありません", - "allClear": "すべて異常なし", - "acknowledgeAlert": "認める", - "noPingUrls": "URLは設定されていません", + "metricProcessesRunning": "実行中", + "categoryMonitoring": "モニタリング", + "loading": "読み込み中...", + "noData": "利用可能なデータはありません", + "allClear": "すべて正常", + "acknowledgeAlert": "確認", + "noPingUrls": "URLが設定されていません", "pingLabel": "ラベル", "addPingUrl": "URLを追加", "showLatency": "レイテンシを表示", "refreshInterval": "更新間隔", "seconds": "秒", - "filterActivityTypes": "フィルターの種類", - "filterTypesHint": "すべて表示するには空欄のままにしてください", + "filterActivityTypes": "アクティビティタイプフィルター", + "filterTypesHint": "すべてを表示するには空のままにします", "showTimestamp": "タイムスタンプを表示", - "uptimeUnavailable": "稼働時間は不明です", + "uptimeUnavailable": "稼働時間は利用できません", "uptimeLabel": "稼働時間", "overviewVersion": "バージョン", - "overviewUpdate": "最新の", - "overviewUpdateAvailable": "アップデートが利用可能です", + "overviewUpdate": "最新", + "overviewUpdateAvailable": "アップデートあり", "overviewDatabase": "データベース", "overviewUptime": "稼働時間", - "noHosts": "ホストは設定されていません", - "hostGridHosts": "主催者", - "hostGridAllHint": "すべてのホストを表示するには空欄のままにしてください。", - "columns": "コラム", - "showIp": "IPアドレスを表示する", + "noHosts": "ホストが設定されていません", + "hostGridHosts": "ホスト", + "hostGridAllHint": "空のままにするとすべてのホストを表示します", + "columns": "列", + "showIp": "IPアドレスを表示", "connectionType": "接続タイプ", "layout": "レイアウト", "showStatus": "ステータスを表示", "showHostName": "ホスト名を表示", - "noDockerActivity": "Dockerアクティビティなし", - "noActivity": "活動なし", - "showAcknowledged": "承認済みを表示", + "noDockerActivity": "Dockerアクティビティはありません", + "noActivity": "アクティビティはありません", + "showAcknowledged": "確認済みを表示", "showCurrentValue": "現在の値を表示", "chartMetric": "メトリック", "metricRange": "範囲", - "widgetMetricsChartName": "指標チャート", - "widgetMetricsChartDesc": "ホストの過去のCPU、メモリ、ディスク、またはネットワークのチャート", + "widgetMetricsChartName": "メトリックチャート", + "widgetMetricsChartDesc": "ホストのCPU、メモリ、ディスク、またはネットワークの履歴チャート", "widgetHostGridName": "ホストグリッド", - "widgetHostGridDesc": "SSHホストの状態をグリッド表示", + "widgetHostGridDesc": "SSHホストステータスのグリッドビュー", "widgetAlertFeedName": "アラートフィード", - "widgetAlertFeedDesc": "承認サポート付きのライブアラート発砲", - "widgetPingStatusName": "ピングステータス", - "widgetPingStatusDesc": "1つ以上のURLに対するHTTPピングステータス", - "widgetRecentActivityName": "最近の活動", - "widgetRecentActivityDesc": "Termixの最近の活動状況を表示するスクロール可能なフィード", - "widgetTermixUptimeName": "Termixの稼働状況", - "widgetTermixUptimeDesc": "Termixサーバーの稼働時間カウンター", + "widgetAlertFeedDesc": "確認機能付きのライブアラート通知", + "widgetPingStatusName": "Pingステータス", + "widgetPingStatusDesc": "1つ以上のURLのHTTP pingステータス", + "widgetRecentActivityName": "最近のアクティビティ", + "widgetRecentActivityDesc": "スクロール可能なTermixの最近のアクティビティフィード", + "widgetTermixUptimeName": "Termixアップタイム", + "widgetTermixUptimeDesc": "Termixサーバーのライブアップタイムカウンター", "widgetSystemOverviewName": "システム概要", - "widgetSystemOverviewDesc": "Termixのバージョン、データベースの状態、稼働状況の概要", - "widgetSshQuickConnectName": "SSHクイックコネクト", - "widgetSshQuickConnectDesc": "SSHセッションを開くためのワンクリックボタン", + "widgetSystemOverviewDesc": "Termixバージョン、データベースの状態、アップタイムを一目で", + "widgetSshQuickConnectName": "SSHクイック接続", + "widgetSshQuickConnectDesc": "SSHセッションを開くワンクリックボタン", "widgetDockerActivityName": "Dockerアクティビティ", - "widgetDockerActivityDesc": "すべてのホストにおける最近のDockerコンテナイベント", + "widgetDockerActivityDesc": "すべてのホストにわたる最近のDockerコンテナイベント", "widgetCalendarName": "カレンダー", - "widgetCalendarDesc": "今日がハイライトされた月間カレンダー", + "widgetCalendarDesc": "今日が強調表示された月間カレンダー", "widgetCountdownName": "カウントダウン", "widgetCountdownDesc": "目標日までのカウントダウンタイマー", "widgetSearchBarName": "検索バー", "widgetSearchBarDesc": "クイックウェブ検索ウィジェット", "widgetTextBannerName": "テキストバナー", - "widgetTextBannerDesc": "キャンバスの太字ラベルまたはセクションヘッダー", + "widgetTextBannerDesc": "キャンバス上の見出しや強調ラベル", "widgetImageWidgetName": "画像", - "widgetImageWidgetDesc": "URLから画像を表示する", - "widgetMarkdownNotesName": "マークダウンノート", - "widgetMarkdownNotesDesc": "インラインマークダウンレンダリングによるリッチノート", + "widgetImageWidgetDesc": "URLから画像を表示", + "widgetMarkdownNotesName": "Markdownノート", + "widgetMarkdownNotesDesc": "Markdownをインラインで表示できるリッチなノート", "widgetCustomApiName": "カスタムAPI", - "widgetCustomApiDesc": "任意のJSON APIからデータを取得して表示する", + "widgetCustomApiDesc": "任意のJSON APIからデータを取得して表示", "widgetServiceGridName": "サービスグリッド", - "widgetServiceGridDesc": "構成可能なサービスタイルリンクのグリッド", + "widgetServiceGridDesc": "設定可能なサービスリンクのタイルグリッド", "widgetDashboardLinksName": "ダッシュボードリンク", - "widgetDashboardLinksDesc": "ダッシュボードから設定済みのサービスリンクを表示します", + "widgetDashboardLinksDesc": "ダッシュボードで設定したサービスリンクを表示", "widgetSearchLinksName": "検索ショートカット", - "widgetSearchLinksDesc": "インライン入力付きのクイック検索ショートカットボタン", + "widgetSearchLinksDesc": "インライン入力対応のクイック検索ショートカット", "widgetLinkTreeName": "リンクツリー", - "widgetLinkTreeDesc": "見出し付きのリンクのグループ化されたセクション", - "calMon": "モ", - "calTue": "トゥ", - "calWed": "私たちは", - "calThu": "ト", + "widgetLinkTreeDesc": "見出し付きのリンクグループ", + "calMon": "月", + "calTue": "火", + "calWed": "水", + "calThu": "木", "calFri": "金", - "calSat": "サ", - "calSun": "スー", - "startOnMonday": "月曜日から週が始まります", - "countdownNoDate": "目標期日は設定されていません", + "calSat": "土", + "calSun": "日", + "startOnMonday": "週の始まりを月曜に", + "countdownNoDate": "目標日が設定されていません", "countdownPast": "イベントは終了しました", "countdownDays": "日", "countdownHours": "時間", - "countdownMinutes": "ミニ", + "countdownMinutes": "分", "countdownSeconds": "秒", "countdownLabel": "ラベル", - "countdownLabelPlaceholder": "例:発売日", - "countdownShowDays": "ショー開催日", - "countdownShowHours": "ショーの営業時間", + "countdownLabelPlaceholder": "例: リリース日", + "countdownShowDays": "日数を表示", + "countdownShowHours": "時間を表示", "targetDate": "目標日", "searchEngine": "検索エンジン", "customSearchUrl": "カスタム検索URL", "searchPlaceholder": "検索...", - "searchGo": "行く", + "searchGo": "検索", "searchPlaceholderLabel": "プレースホルダーテキスト", "searchPlaceholderHint": "検索入力欄に表示されるテキスト", "openInNewTab": "新しいタブで開く", - "searchQueryPlaceholder": "クエリを入力してください...", - "noSearchShortcuts": "ショートカットは設定されていません", + "searchQueryPlaceholder": "クエリを入力...", + "noSearchShortcuts": "設定済みのショートカットはありません", "addSearchShortcut": "ショートカットを追加", "fontSize": "フォントサイズ", - "textAlign": "テキストの配置", + "textAlign": "テキスト配置", "fontWeight": "フォントの太さ", - "clearColor": "透明", + "clearColor": "色をクリア", "imageFit": "画像フィット", "imageLinkUrl": "リンクURL", "noImage": "画像URLが設定されていません", "altText": "代替テキスト", - "altTextPlaceholder": "画像の説明", - "renderMarkdown": "マークダウンをレンダリングする", + "altTextPlaceholder": "画像の説明を入力", + "renderMarkdown": "Markdownをレンダリング", "displayMode": "表示モード", "displayField": "表示フィールド", "jsonPath": "JSONパス", "customApiLabel": "ラベル", - "customApiLabelPlaceholder": "例:温度", - "customApiUnit": "ユニット", + "customApiLabelPlaceholder": "例: 温度", + "customApiUnit": "単位", "customApiNoUrl": "API URLが設定されていません", "customApiError": "取得に失敗しました", - "customApiNotArray": "レスポンスは配列ではありません", + "customApiNotArray": "レスポンスが配列ではありません", "addService": "サービスを追加", "showLabels": "ラベルを表示", "iconSize": "アイコンサイズ", @@ -332,200 +332,239 @@ "gridLayout": "グリッド", "terminal": "ターミナル", "files": "ファイル", - "docker": "ドッカー", + "docker": "Docker", "range15m": "15分", "range1h": "1時間", "range6h": "6時間", "range24h": "24時間", - "metricNetRx": "ネットダウンロード", - "metricNetTx": "ネットアップロード", - "severityFilter": "重大度フィルター", - "filterAll": "全て", + "metricNetRx": "ネットワーク受信", + "metricNetTx": "ネットワーク送信", + "severityFilter": "重要度フィルター", + "filterAll": "すべて", "accentColor": "アクセントカラー", "widgetSshTerminalName": "SSHターミナル", - "widgetSshTerminalDesc": "設定済みのホストに接続されたインラインSSH端末", + "widgetSshTerminalDesc": "設定済みホストに接続するインラインSSHターミナル", "sshTerminalNoHost": "ホストが設定されていません", - "sshTerminalConnect": "接続する", - "sshTerminalAutoConnect": "負荷時に自動接続", - "widgetQuickConnectName": "クイックコネクト", - "widgetQuickConnectDesc": "ホスト全体で、あらゆる接続タイプに対応するワンクリック起動ボタン", + "sshTerminalConnect": "接続", + "sshTerminalAutoConnect": "読み込み時に自動接続", + "widgetQuickConnectName": "クイック接続", + "widgetQuickConnectDesc": "ホスト上のあらゆる接続タイプをワンクリックで起動するボタン", "connectionTypes": "接続タイプ", "connType_terminal": "ターミナル", "connType_files": "ファイルマネージャー", - "connType_docker": "ドッカー", + "connType_docker": "Docker", "connType_tunnel": "トンネル", "connType_host-metrics": "ホストメトリクス", "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", "widgetFileManagerName": "ファイルマネージャー", - "widgetFileManagerDesc": "設定済みホスト用の組み込みSFTPファイルマネージャ", - "widgetDockerName": "Docker Manager", - "widgetDockerDesc": "設定済みホスト用の組み込みDockerコンテナマネージャ", - "widgetTunnelName": "トンネル管理者", - "widgetTunnelDesc": "設定済みホスト用の組み込みSSHトンネルマネージャ", + "widgetFileManagerDesc": "設定済みホスト用の組み込みSFTPファイルマネージャー", + "widgetDockerName": "Dockerマネージャー", + "widgetDockerDesc": "設定済みホスト用の組み込みDockerコンテナマネージャー", + "widgetTunnelName": "トンネルマネージャー", + "widgetTunnelDesc": "設定済みホスト用の組み込みSSHトンネルマネージャー", "widgetNoHostSelected": "ホストが設定されていません" }, "serverConfig": { - "title": "サーバー構成", - "description": "バックエンドサービスに接続するためのTermixサーバーURLを設定します。", + "title": "サーバー設定", + "description": "TermixサーバーのURLを設定してバックエンドサービスに接続します", "serverUrl": "サーバーURL", - "enterServerUrl": "サーバーの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を内蔵のローカルサーバーで実行します(リモートサーバーは不要です)。", + "helpText": "Termixサーバーが実行されているURLを入力してください (例: http://localhost:30001 または https://your-server.com)", + "changeServer": "サーバー変更", + "mustIncludeProtocol": "サーバーURLはhttp://またはhttps://で始まる必要があります", + "allowInvalidCertificate": "無効な証明書を許可", + "allowInvalidCertificateDesc": "自己署名証明書やIPアドレス証明書を使った信頼できる自己ホストサーバーにのみ使用してください。", + "useEmbedded": "ローカルサーバーを使用", + "embeddedDesc": "組み込みのローカルサーバーでTermixを実行します(リモートサーバー不要)", "embeddedConnecting": "ローカルサーバーに接続中...", - "embeddedNotReady": "ローカルサーバーの準備がまだ整っていません。しばらくお待ちいただいてから、もう一度お試しください。", + "embeddedNotReady": "ローカルサーバーの準備ができていません。しばらく待ってからやり直してください。", "localServer": "ローカルサーバー", "savedServers": "保存済みサーバー", "noSavedServers": "保存されたサーバーはありません", - "removeServer": "取り除く" + "removeServer": "削除" + }, + "migrationNotice": { + "title": "Termix Desktopがスタンドアロンで動作するようになりました", + "body1": "このアプリは以前はTermixサーバーに直接接続していましたが、完全に独立して動作するように改良され、ホスト名と認証情報をローカルに保存することでオフラインでも動作し続けます。セルフホスト型のTermixサーバーとの双方向同期はオプションになりました。", + "body2": "既存のホストと認証情報はまだ {{url}}にあります。リモート同期を有効にして、そのサーバーに再接続すると、それらが復元され、今後両方のサーバーが同期された状態になります。", + "dismiss": "今じゃない", + "setUpSync": "リモート同期の設定" + }, + "remoteSync": { + "title": "リモート同期", + "description": "オプションとして、このデスクトップアプリをセルフホスト型のTermixサーバーに接続することで、ホスト、認証情報、およびスニペットをデバイス間で同期できます。アプリは接続の有無に関わらず、常に完全にオフラインで動作します。", + "notConnected": "接続されていません", + "connected": "接続済み", + "connectedTo": "{{url}}に接続しました", + "lastSynced": "最終同期日時 {{time}}", + "neverSynced": "同期されたことはありません", + "syncError": "同期エラー: {{message}}", + "needsReauth": "ログイン期限が切れました", + "connectButton": "サーバーに接続", + "disconnectButton": "切断", + "syncNowButton": "今すぐ同期", + "syncing": "同期中...", + "serverUrl": "サーバーURL", + "enterServerUrl": "サーバーのURLを入力してください", + "mustIncludeProtocol": "サーバーURLはhttp://またはhttps://で始まる必要があります。", + "connectionTestFailed": "そのURLのTermixサーバーに接続できませんでした", + "allowInvalidCertificate": "無効な証明書を許可する", + "allowInvalidCertificateDesc": "自己署名証明書またはIPアドレス証明書を使用する、信頼できる自己ホスト型サーバーでのみ使用してください。", + "savedServers": "保存済みサーバー", + "removeServer": "取り除く", + "continueButton": "続く", + "cancelButton": "キャンセル", + "signInTitle": "{{url}}にサインインしてください。", + "originTitle": "接続元", + "originDescription": "SSH接続のデフォルトの発信元を選択します。この設定はホストごとに変更できます。", + "originLocal": "このデバイス(ローカルネットワーク)", + "originRemote": "リモートサーバー", + "bannerReconnect": "再接続", + "bannerMessage": "リモート同期には再認証が必要です" }, "versionCheck": { - "error": "バージョンチェックエラー", - "checkFailed": "アップデートの確認に失敗しました", + "error": "バージョン確認エラー", + "checkFailed": "更新の確認に失敗しました", "upToDate": "アプリは最新です", - "currentVersion": "現在、バージョン {{version}} を実行中です。", - "updateAvailable": "アップデートが利用可能です", - "newVersionAvailable": "新しいバージョンが利用可能です!現在 {{current}}を実行していますが、 {{latest}} が利用可能です。", + "currentVersion": "バージョン {{version}} を実行中です", + "updateAvailable": "アップデートがあります", + "newVersionAvailable": "新しいバージョンが利用可能です。現在 {{current}} を使用中ですが、{{latest}} が利用可能です。", "betaVersion": "ベータ版", - "betaVersionDesc": "あなたは {{current}}を実行していますが、これは最新の安定版リリース {{latest}}よりも新しいものです。", - "releasedOn": "{{date}}にリリースされました", + "betaVersionDesc": "{{current}} を実行中です。これは最新安定版 {{latest}} よりも新しいバージョンです。", + "releasedOn": "リリース日: {{date}}", "downloadUpdate": "アップデートをダウンロード", "checking": "アップデートを確認中...", - "checkUpdates": "アップデートを確認する", + "checkUpdates": "アップデートを確認", "checkingUpdates": "アップデートを確認中...", "updateRequired": "アップデートが必要です" }, "common": { - "close": "近い", - "minimize": "最小化する", + "close": "閉じる", + "minimize": "最小化", "online": "オンライン", "offline": "オフライン", - "unknown": "未知", - "continue": "続く", + "unknown": "不明", + "continue": "続行", "maintenance": "メンテナンス", - "degraded": "劣化", + "degraded": "低下", "error": "エラー", "warning": "警告", - "unsavedChanges": "保存されていない変更", - "dismiss": "却下する", - "loading": "読み込み中…", - "optional": "オプション", - "connect": "接続する", + "unsavedChanges": "保存されていない変更があります", + "dismiss": "閉じる", + "loading": "読み込み中...", + "optional": "任意", + "connect": "接続", "copied": "コピーしました", "connecting": "接続中...", - "updateAvailable": "アップデートが利用可能です", - "appName": "ターミックス", + "updateAvailable": "アップデートがあります", + "appName": "Termix", "openInNewTab": "新しいタブで開く", - "noReleases": "リリースなし", + "noReleases": "リリースはありません", "updatesAndReleases": "アップデートとリリース", - "newVersionAvailable": "新しいバージョン({{version}})が利用可能です。", - "failedToFetchUpdateInfo": "更新情報の取得に失敗しました", - "preRelease": "発売前", - "noReleasesFound": "リリース情報は見つかりませんでした。", + "newVersionAvailable": "新しいバージョン ({{version}}) が利用可能です。", + "failedToFetchUpdateInfo": "アップデート情報の取得に失敗しました", + "preRelease": "プレリリース", + "noReleasesFound": "リリースが見つかりません。", "cancel": "キャンセル", "username": "ユーザー名", "login": "ログイン", "logout": "ログアウト", - "register": "登録する", + "register": "登録", "password": "パスワード", - "confirmPassword": "パスワードを認証する", + "confirmPassword": "パスワードの確認", "back": "戻る", "save": "保存", "saving": "保存中...", - "delete": "消去", - "rename": "名前を変更する", + "delete": "削除", + "rename": "名前の変更", "edit": "編集", "add": "追加", - "confirm": "確認する", + "confirm": "確認", "no": "いいえ", "or": "または", - "next": "次", - "previous": "前の", + "next": "次へ", + "previous": "前へ", "refresh": "更新", "language": "言語", - "checking": "チェック中...", - "checkingDatabase": "データベース接続を確認しています...", + "checking": "確認中...", + "checkingDatabase": "データベース接続を確認中...", "checkingAuthentication": "認証を確認中...", "backendReconnected": "サーバー接続が復旧しました", - "connectionDegraded": "サーバー接続が失われました。復旧中です。…", - "reload": "リロード", - "remove": "取り除く", - "create": "作成する", - "update": "アップデート", + "connectionDegraded": "サーバー接続が切断されました。再接続中…", + "reload": "再読み込み", + "remove": "削除", + "create": "作成", + "update": "更新", "copy": "コピー", "copyFailed": "クリップボードへのコピーに失敗しました", - "maximize": "最大化する", - "restore": "復元する", + "maximize": "最大化", + "restore": "復元", "of": "の", - "saved": "保存済み", - "deleted": "削除済み", + "saved": "保存しました", + "deleted": "削除しました", "deleteFailed": "削除に失敗しました", "saveFailed": "保存に失敗しました", "required": "必須" }, "nav": { - "home": "家", + "home": "ホーム", "terminal": "ターミナル", - "docker": "ドッカー", + "docker": "Docker", "tunnels": "トンネル", "fileManager": "ファイルマネージャー", "serverStats": "ホストメトリクス", "hostMetrics": "ホストメトリクス", - "admin": "管理者", + "admin": "管理", "termixId": "ID", - "userProfile": "ユーザープロフィール", + "userProfile": "ユーザープロファイル", "splitScreen": "画面分割", - "confirmClose": "このアクティブなセッションを閉じますか?", - "close": "近い", + "confirmClose": "このアクティブセッションを閉じますか?", + "close": "閉じる", "cancel": "キャンセル", "sshManager": "SSHマネージャー", "cannotSplitTab": "このタブは分割できません", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "パスワードをコピー", - "copySudoPassword": "Sudoパスワードをコピー", - "passwordCopied": "パスワードがクリップボードにコピーされました", - "noPasswordAvailable": "パスワードがありません", + "copySudoPassword": "sudoパスワードをコピー", + "passwordCopied": "パスワードをクリップボードにコピーしました", + "noPasswordAvailable": "利用可能なパスワードがありません", "failedToCopyPassword": "パスワードのコピーに失敗しました", - "refreshTab": "接続を更新", - "renameTab": "タブの名前を変更する", + "refreshTab": "接続をリフレッシュ", + "renameTab": "タブ名を変更", "openFileManager": "ファイルマネージャーを開く", "dashboard": "ダッシュボード", "networkGraph": "ネットワークグラフ", - "tmuxMonitor": "Tmuxモニター", + "tmuxMonitor": "tmuxモニター", "homepage": "ホームページ", - "quickConnect": "クイックコネクト", + "quickConnect": "クイック接続", "sshTools": "SSHツール", - "history": "歴史", + "history": "履歴", "sessionLogs": "セッションログ", - "hosts": "主催者", - "snippets": "断片", + "hosts": "ホスト", + "snippets": "スニペット", "hostManager": "ホストマネージャー", - "credentials": "資格", - "connections": "つながり", + "credentials": "認証情報", + "connections": "接続", "alerts": "アラート", "serial": "シリアル", "roleAdministrator": "管理者", "roleUser": "ユーザー" }, "hosts": { - "hosts": "主催者", - "noHosts": "SSHホストなし", - "retry": "リトライ", + "hosts": "ホスト", + "noHosts": "SSHホストがありません", + "retry": "再試行", "refresh": "更新", - "optional": "オプション", + "optional": "任意", "downloadSample": "サンプルをダウンロード", - "failedToDeleteHost": "{{name}} の削除に失敗しました", - "importSkipExisting": "インポート(既存データはスキップ)", + "failedToDeleteHost": "{{name}}の削除に失敗しました", + "importSkipExisting": "インポート(既存をスキップ)", "importSSHConfig": "SSH設定からインポート", "connectionDetails": "接続の詳細", "ssh": "SSH", @@ -533,34 +572,34 @@ "remoteDesktop": "リモートデスクトップ", "port": "ポート", "username": "ユーザー名", - "folder": "フォルダ", + "folder": "フォルダー", "tags": "タグ", - "pin": "ピン", + "pin": "ピン留め", "addHost": "ホストを追加", "editHost": "ホストを編集", - "cloneHost": "クローンホスト", + "cloneHost": "ホストを複製", "enableTerminal": "ターミナルを有効にする", "enableTunnel": "トンネルを有効にする", "enableFileManager": "ファイルマネージャーを有効にする", "enableDocker": "Dockerを有効にする", "defaultPath": "デフォルトパス", - "connection": "繋がり", + "connection": "接続", "upload": "アップロード", "authentication": "認証", "password": "パスワード", "key": "鍵", - "credential": "資格証明書", + "credential": "認証情報", "none": "なし", "sshPrivateKey": "SSH秘密鍵", - "keyType": "キータイプ", - "uploadFile": "ファイルのアップロード", - "tabGeneral": "一般的な", + "keyType": "鍵の種類", + "uploadFile": "ファイルをアップロード", + "tabGeneral": "一般", "tabSsh": "SSH", "tabTerminal": "ターミナル", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "トンネル", - "tabDocker": "ドッカー", + "tabDocker": "Docker", "tabFiles": "ファイル", "tabStats": "ホストメトリクス", "tabHostMetrics": "ホストメトリクス", @@ -571,16 +610,16 @@ "tunnel": "トンネル", "fileManager": "ファイルマネージャー", "serverStats": "ホストメトリクス", - "status": "状態", - "folderRenamed": "フォルダ \"{{oldName}}\" は \"{{newName}}\" に正常に名前変更されました。", - "failedToRenameFolder": "フォルダ名の変更に失敗しました", - "movedToFolder": "{{count}} ホストを \"{{folder}} \" に移動しました", + "status": "ステータス", + "folderRenamed": "フォルダ「{{oldName}}」を「{{newName}}」に正常にリネームしました", + "failedToRenameFolder": "フォルダの名前変更に失敗しました", + "movedToFolder": "{{count}} 個のホストを \"{{folder}}\" に移動しました", "editHostTooltip": "ホストを編集", "statusChecks": "ステータスチェック", - "metricsCollection": "指標収集", + "metricsCollection": "メトリクス収集", "metricsInterval": "メトリクス収集間隔", - "metricsIntervalDesc": "サーバー統計情報を収集する頻度(5秒~1時間)", - "behavior": "行動", + "metricsIntervalDesc": "サーバー統計を収集する頻度 (5秒〜1時間)", + "behavior": "動作", "themePreview": "テーマプレビュー", "theme": "テーマ", "fontFamily": "フォントファミリー", @@ -591,38 +630,39 @@ "cursorBlink": "カーソル点滅", "scrollbackBuffer": "スクロールバックバッファ", "bellStyle": "ベルスタイル", - "rightClickSelectsWord": "右クリックで単語を選択", - "fastScrollModifier": "高速スクロール修飾子", + "rightClickSelectsWord": "右クリックで単語選択", + "fastScrollModifier": "高速スクロール修飾キー", "fastScrollSensitivity": "高速スクロール感度", "sshAgentForwarding": "SSHエージェント転送", "backspaceMode": "バックスペースモード", - "startupSnippet": "スタートアップスニペット", + "startupSnippet": "起動時スニペット", "selectSnippet": "スニペットを選択", - "forceKeyboardInteractive": "キーボード操作を強制する", - "overrideCredentialUsername": "認証情報の上書きユーザー名", - "overrideCredentialUsernameDesc": "認証情報のユーザー名の代わりに、上記で指定したユーザー名を使用してください。", - "oidcUsernameHint": "OIDC ログイン名を置き換えるには、 $oidc.preferred_username を使用してください。", + "forceKeyboardInteractive": "Keyboard-Interactive を強制", + "overrideCredentialUsername": "認証情報のユーザー名を上書き", + "overrideCredentialUsernameDesc": "認証情報のユーザー名の代わりに上記で指定したユーザー名を使用します", + "oidcUsernameHint": "$oidc.preferred_username を使用してOIDCログイン名を置き換えます。", + "tailscaleUsernameHint": "これは、tailnetのSSH ACLでTailscaleのIDが付与されているUnixユーザーである必要があり、必ずしもrootである必要はありません。", "jumpHostChain": "ジャンプホストチェーン", "portKnocking": "ポートノッキング", "addKnock": "ポートを追加", "addProxyNode": "ノードを追加", "proxyNode": "プロキシノード", - "proxyType": "プロキシの種類", + "proxyType": "プロキシタイプ", "quickActions": "クイックアクション", - "sudoPasswordAutoFill": "Sudoパスワードの自動入力", - "sudoPassword": "sudo パスワード", - "keepaliveInterval": "キープアライブ間隔(ミリ秒)", - "moshCommand": "MOSHコマンド", + "sudoPasswordAutoFill": "Sudo パスワードの自動入力", + "sudoPassword": "Sudo パスワード", + "keepaliveInterval": "キープアライブ間隔 (ms)", + "moshCommand": "Mosh コマンド", "environmentVariables": "環境変数", - "addVariable": "変数を追加する", - "docker": "ドッカー", - "copyTerminalUrl": "ターミナルのURLをコピー", - "copyFileManagerUrl": "ファイルマネージャーのURLをコピーする", - "copyRemoteDesktopUrl": "リモートデスクトップのURLをコピーする", - "failedToConnect": "コンソールへの接続に失敗しました", - "connect": "接続する", + "addVariable": "変数を追加", + "docker": "Docker", + "copyTerminalUrl": "ターミナル URL をコピー", + "copyFileManagerUrl": "ファイルマネージャー URL をコピー", + "copyRemoteDesktopUrl": "リモートデスクトップ URL をコピー", + "failedToConnect": "コンソールに接続できませんでした", + "connect": "接続", "disconnect": "切断", - "start": "始める", + "start": "開始", "enableStatusCheck": "ステータスチェックを有効にする", "enableMetrics": "メトリクスを有効にする", "bulkUpdateFailed": "一括更新に失敗しました", @@ -632,111 +672,116 @@ "secureShell": "セキュアシェル", "virtualNetwork": "仮想ネットワーク", "unencryptedShell": "暗号化されていないシェル", - "addressIp": "アドレス/IPアドレス", - "friendlyName": "フレンドリー名", - "macAddress": "MACアドレス", - "wolBroadcastAddress": "WoL放送アドレス", - "wolBroadcastAddressDesc": "Docker/ルーティングされたネットワーク(例:192.168.1.255)へのオプションの直接ブロードキャスト。空欄のままにすると255.255.255.255が使用されます。", + "addressIp": "アドレス / IP", + "friendlyName": "表示名", + "macAddress": "MAC アドレス", + "wolBroadcastAddress": "WoL ブロードキャストアドレス", + "wolBroadcastAddressDesc": "Docker/ルーテッドネットワーク用のオプションの直接ブロードキャスト (例: 192.168.1.255)。空のままにすると 255.255.255.255 を使用します。", "folderAndAdvanced": "フォルダーと詳細設定", - "privateNotes": "プライベートノート", - "privateNotesPlaceholder": "このサーバーに関する詳細情報...", - "pinToTop": "トップにピン留め", + "privateNotes": "非公開メモ", + "privateNotesPlaceholder": "このサーバーの詳細...", + "pinToTop": "トップに固定", "pinToTopDesc": "このホストを常にリストの一番上に表示する", "portKnockingSequence": "ポートノッキングシーケンス", "addKnockBtn": "ノックを追加", - "noPortKnocking": "ポートノッキングは設定されていません。", + "noPortKnocking": "ポートノッキングは設定されていません", "knockPort": "ノックポート", "protocol": "プロトコル", - "delayAfterMs": "遅延時間(ミリ秒)", - "useSocks5Proxy": "SOCKS5プロキシを使用する", - "useSocks5ProxyDesc": "プロキシサーバー経由で接続をルーティングする", + "delayAfterMs": "遅延 (ms)", + "useSocks5Proxy": "SOCKS5 プロキシを使用する", + "useSocks5ProxyDesc": "プロキシサーバーを経由して接続します", + "connectionOrigin": "接続元", + "connectionOriginDesc": "このホストのSSH接続の発信元。デスクトップアプリのグローバルデフォルト設定を上書きします。", + "connectionOriginDefault": "デフォルトを使用する", + "connectionOriginLocal": "このデバイス(ローカルネットワーク)", + "connectionOriginRemote": "リモートサーバー", "proxyHost": "プロキシホスト", "proxyPort": "プロキシポート", "proxyUsername": "プロキシユーザー名", "proxyPassword": "プロキシパスワード", - "proxySingleMode": "シングルプロキシ", + "proxySingleMode": "単一プロキシ", "proxyChainMode": "プロキシチェーン", - "you": "あなた", + "you": "自分", "jumpHostChainLabel": "ジャンプホストチェーン", "addJumpBtn": "ジャンプを追加", - "noJumpHosts": "ジャンプホストは設定されていません。", - "selectAServer": "サーバーを選択してください...", - "sshPort": "SSHポート", - "authMethod": "認証方法", - "storedCredential": "保存された認証情報", - "selectACredential": "資格情報を選択してください...", - "vaultProfile": "金庫署名者プロフィール", - "selectAVaultProfile": "Vaultプロファイルを選択してください...", - "vaultProfileHint": "設定は共有プロファイルから取得されます。接続時にはOIDC経由でVaultにサインインします。機密情報は一切保存されません。", - "vaultNewProfile": "新しいプロフィール", - "vaultManageProfiles": "Vaultプロファイルの管理", - "vaultAddrLabel": "金庫の住所", + "noJumpHosts": "ジャンプホストは設定されていません", + "selectAServer": "サーバーを選択...", + "sshPort": "SSH ポート", + "authMethod": "認証方式", + "storedCredential": "保存済み認証情報", + "selectACredential": "認証情報を選択...", + "vaultProfile": "Vault 署名者プロファイル", + "selectAVaultProfile": "Vault プロファイルを選択...", + "vaultProfileHint": "設定は共有プロファイルから取得され、接続時に OIDC で Vault にサインインします。シークレットは保存されません。", + "vaultNewProfile": "新規プロファイル", + "vaultManageProfiles": "Vault プロファイルの管理", + "vaultAddrLabel": "Vault アドレス", "vaultNamespaceLabel": "名前空間", - "vaultOidcMountLabel": "OIDC認証マウント", - "vaultOidcRoleLabel": "OIDCの役割", - "vaultSshMountLabel": "SSHシークレットマウント", - "vaultSshRoleLabel": "SSH署名者の役割", - "vaultValidPrincipalsLabel": "有効な原則", - "vaultKeyTypeLabel": "一時的なキータイプ", - "vaultSharedLabel": "すべてのユーザーと共有する", - "vaultCreateProfile": "プロフィールを作成する", + "vaultOidcMountLabel": "OIDC 認証マウント", + "vaultOidcRoleLabel": "OIDC ロール", + "vaultSshMountLabel": "SSH シークレットマウント", + "vaultSshRoleLabel": "SSH 署名者ロール", + "vaultValidPrincipalsLabel": "有効なプリンシパル", + "vaultKeyTypeLabel": "エフェメラルキータイプ", + "vaultSharedLabel": "すべてのユーザーと共有", + "vaultCreateProfile": "プロファイルを作成", "vaultProfileCreated": "Vaultプロファイルが作成されました", "vaultProfileSaved": "Vaultプロファイルが保存されました", - "vaultProfileDeleted": "Vault プロファイルが削除されました", + "vaultProfileDeleted": "Vaultプロファイルが削除されました", "vaultProfileSaveFailed": "Vaultプロファイルの保存に失敗しました", "vaultProfileDeleteFailed": "Vaultプロファイルの削除に失敗しました", - "vaultSaveProfile": "プロフィールを保存", - "vaultProfileValidationError": "名前、Vaultアドレス、SSH署名者ロールが必要です", - "vaultNoProfiles": "Vaultプロファイルはまだありません。", + "vaultSaveProfile": "プロファイルを保存", + "vaultProfileValidationError": "名前、Vaultアドレス、SSH署名ロールは必須です", + "vaultNoProfiles": "まだVaultプロファイルがありません。", "vaultSharedBadge": "共有", - "keyTypeLabel": "キータイプ", + "keyTypeLabel": "鍵の種類", "keyTypeAuto": "自動検出", - "keyPasteTab": "ペースト", + "keyPasteTab": "貼り付け", "keyUploadTab": "アップロード", - "keyFileLoaded": "キーファイルが読み込まれました", - "keyUploadClick": ".pem / .key / .ppk ファイルをアップロードするにはここをクリックしてください", - "clearKey": "クリアキー", - "keySaved": "SSHキーが保存されました", - "keyReplaceNotice": "以下に新しいキーを貼り付けて置き換えてください", - "keyPassphraseSaved": "パスフレーズが保存されました。変更するには入力してください。", - "replaceKey": "キーを交換する", + "keyFileLoaded": "鍵ファイルが読み込まれました", + "keyUploadClick": "クリックして .pem / .key / .ppk をアップロード", + "clearKey": "鍵をクリア", + "keySaved": "SSH鍵が保存されました", + "keyReplaceNotice": "置き換えるには、下に新しい鍵を貼り付けてください", + "keyPassphraseSaved": "パスフレーズが保存されました。変更するには入力してください", + "replaceKey": "鍵を置き換え", "docsLink": "ドキュメントを表示", "opksshLabel": "OPKSSH", - "opksshDesc": "パスワードやキーの代わりに、IDプロバイダを使用してこのホストにサインインしてください。サーバーにOPKSSHが設定されている必要があります。", - "warpgateLabel": "ワープゲートゲートウェイ", - "warpgateDesc": "このホストはWarpgate SSHプロキシ経由で接続します。Termixは認証後、ブラウザベースの承認フローを自動的に処理します。", + "opksshDesc": "パスワードや鍵の代わりに、IDプロバイダーを使用してこのホストにサインインします。サーバーにOPKSSHのセットアップが必要です。", + "warpgateLabel": "Warpgateゲートウェイ", + "warpgateDesc": "このホストはWarpgate SSHプロキシ経由で接続します。認証後、Termixがブラウザベースの承認フローを自動的に処理します。", "agentLabel": "SSHエージェント", - "agentDesc": "Termixサーバー上で動作するSSHエージェント(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agentなど)を使用して認証を行います。エージェントは、Termixがホストされているマシン上で動作している必要があります。", + "agentDesc": "Termixサーバー上で動作しているSSHエージェント(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)を使用して認証します。エージェントはTermixがホストされているマシン上で実行されている必要があります。", "agentSocketPathLabel": "エージェントソケットパス", - "agentSocketPathPlaceholder": "SSH_AUTH_SOCKを使用する場合は空欄のままにしてください。", - "agentSocketPathHint": "SSH_AUTH_SOCK環境変数から自動検出する場合は空欄のままにするか、カスタムソケットパス(例:/run/user/1000/gnupg/S.gpg-agent.ssh)を入力してください。", - "tailscaleDeviceSelect": "テールスケールデバイスを選択してください", - "tailscaleDeviceSelectPlaceholder": "デバイスを選択してください...", - "tailscaleNoApiKey": "Tailscale APIキーが設定されていません。デバイス検出を有効にするには、管理者設定でAPIキーを追加してください。", + "agentSocketPathPlaceholder": "SSH_AUTH_SOCKを使用するには空のままにしてください", + "agentSocketPathHint": "SSH_AUTH_SOCK環境変数から自動検出するには空のままにするか、カスタムソケットパスを入力してください(例: /run/user/1000/gnupg/S.gpg-agent.ssh)。", + "tailscaleDeviceSelect": "Tailscaleデバイスを選択", + "tailscaleDeviceSelectPlaceholder": "デバイスを選択...", + "tailscaleNoApiKey": "Tailscale APIキーが設定されていません。デバイス検出を有効にするには、管理者設定で追加してください。", "tailscaleDocsLink": "ドキュメントを表示", - "tailscaleLoadingDevices": "デバイスを読み込んでいます...", - "tailscaleNoDevices": "テールネット上にデバイスは見つかりませんでした。", - "tailscaleDeviceAutoFill": "デバイスを選択すると、ホストのIPアドレスが自動的に入力されます。", - "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", - "forceKeyboardInteractiveShortDesc": "キーが存在する場合でも、手動でのパスワード入力を強制する", - "allowLegacyAlgorithmsLabel": "レガシーアルゴリズムを許可する", - "allowLegacyAlgorithmsDesc": "アップグレードできない古いデバイスへの接続のために、非推奨の SSH アルゴリズム (ssh-dss、ssh-rsa、diffie-hellman-group1-sha1、hmac-md5、3des-cbc) を有効にします。", - "insecure": "不安", - "terminalAppearance": "終末期の外観", + "tailscaleLoadingDevices": "デバイスを読み込み中...", + "tailscaleNoDevices": "テールネットにデバイスが見つかりません。", + "tailscaleDeviceAutoFill": "デバイスを選択するとホストIPアドレスが自動入力されます。", + "forceKeyboardInteractiveLabel": "キーボードインタラクティブを強制", + "forceKeyboardInteractiveShortDesc": "キーが存在する場合でも手動パスワード入力を強制します", + "allowLegacyAlgorithmsLabel": "レガシーアルゴリズムを許可", + "allowLegacyAlgorithmsDesc": "アップグレードできない古いデバイスへの接続用に、非推奨のSSHアルゴリズム (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) を有効にします。", + "insecure": "安全でない", + "terminalAppearance": "ターミナルの外観", "colorTheme": "カラーテーマ", "fontFamilyLabel": "フォントファミリー", "fontSizeLabel": "フォントサイズ", "cursorStyleLabel": "カーソルスタイル", - "letterSpacingPx": "文字間隔(ピクセル)", + "letterSpacingPx": "文字間隔 (px)", "lineHeightLabel": "行の高さ", "bellStyleLabel": "ベルスタイル", "backspaceModeLabel": "バックスペースモード", - "cursorBlinking": "カーソルが点滅する", - "cursorBlinkingDesc": "端末カーソルの点滅アニメーションを有効にする", - "rightClickSelectsWordLabel": "右クリックで単語を選択", - "rightClickSelectsWordShortDesc": "右クリックしてカーソル下の単語を選択します", - "backgroundImageLabel": "背景画像のURL", - "backgroundImageDesc": "端末の背景画像のURL(オプション)", + "cursorBlinking": "カーソル点滅", + "cursorBlinkingDesc": "ターミナルカーソルの点滅アニメーションを有効にします", + "rightClickSelectsWordLabel": "右クリックで単語選択", + "rightClickSelectsWordShortDesc": "右クリック時にカーソル下の単語を選択します", + "backgroundImageLabel": "背景画像 URL", + "backgroundImageDesc": "ターミナルの背景画像のオプションURL", "backgroundImageOpacityLabel": "背景画像の不透明度", "customThemeColors": "カスタムカラー", "customThemeBackground": "背景", @@ -748,208 +793,219 @@ "customThemeBlack": "黒", "customThemeRed": "赤", "customThemeGreen": "緑", - "customThemeYellow": "黄色", - "customThemeBlue": "青", + "customThemeYellow": "黄", + "customThemeBlue": "ブルー", "customThemeMagenta": "マゼンタ", "customThemeCyan": "シアン", - "customThemeWhite": "白", - "customThemeBrightBlack": "明るい黒", - "customThemeBrightRed": "鮮やかな赤", - "customThemeBrightGreen": "鮮やかな緑色", - "customThemeBrightYellow": "鮮やかな黄色", - "customThemeBrightBlue": "鮮やかな青", - "customThemeBrightMagenta": "鮮やかなマゼンタ", - "customThemeBrightCyan": "明るいシアン", - "customThemeBrightWhite": "明るい白色", - "customThemeResetTooltip": "初期設定に戻す", - "syntaxHighlightingLabel": "構文ハイライト", - "syntaxHighlightingDesc": "ターミナル出力(エラー、パス、IPアドレス、タイムスタンプ)を色分けする", - "syntaxHighlightingCategories": "注目のカテゴリ", - "syntaxHighlightingCategoriesDesc": "色付けするコンテンツの種類を選択してください", + "customThemeWhite": "ホワイト", + "customThemeBrightBlack": "ブライトブラック", + "customThemeBrightRed": "ブライトレッド", + "customThemeBrightGreen": "ブライトグリーン", + "customThemeBrightYellow": "ブライトイエロー", + "customThemeBrightBlue": "ブライトブルー", + "customThemeBrightMagenta": "ブライトマゼンタ", + "customThemeBrightCyan": "ブライトシアン", + "customThemeBrightWhite": "ブライトホワイト", + "customThemeResetTooltip": "デフォルトにリセット", + "savedThemesLabel": "保存済みテーマ", + "saveAsGlobalTheme": "グローバルテーマとして保存", + "saveGlobalThemeNamePrompt": "このテーマの名前を入力してください", + "saveGlobalThemeSuccess": "テーマが保存されました", + "saveGlobalThemeError": "テーマの保存に失敗しました", + "applyGlobalThemeTooltip": "このテーマを適用する", + "deleteGlobalThemeTooltip": "このテーマを削除", + "noSavedThemes": "まだテーマは保存されていません", + "syntaxHighlightingLabel": "シンタックスハイライト", + "syntaxHighlightingDesc": "ターミナル出力に色を付ける(エラー、パス、IP、タイムスタンプ)", + "syntaxHighlightingCategories": "ハイライトカテゴリ", + "syntaxHighlightingCategoriesDesc": "色を付けるコンテンツの種類を選択", "syntaxCategoryLogLevels": "ログレベル", - "syntaxCategoryLogLevelsDesc": "エラー、警告、情報、デバッグ、致命的", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "ファイルパス", - "syntaxCategoryPathsDesc": "/usr/share/doc、~/file.txt", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "タイムスタンプ", "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "IPアドレス", - "syntaxCategoryIpAddressesDesc": "192.168.1.1、10.0.0.1:8080", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "ラベル付き番号", - "syntaxCategoryNumbersDesc": "ポート8080、出口1、ステータス404", - "behaviorAndAdvanced": "行動と高度な", - "scrollbackBufferLabel": "スクロールバックバッファ", - "scrollbackMaxLines": "履歴に保持される最大行数", + "syntaxCategoryNumbers": "ラベル付き数値", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "動作と詳細設定", + "scrollbackBufferLabel": "スクロールバッファ", + "scrollbackMaxLines": "履歴に保持する最大行数", "sshAgentForwardingLabel": "SSHエージェント転送", - "sshAgentForwardingShortDesc": "ローカルのSSHキーをこのホストに渡してください", - "useSSHTitleLabel": "SSHウィンドウのタイトルを使用する", - "useSSHTitleDesc": "タブのタイトルをホスト名ではなく、シェルのウィンドウタイトルから更新する", - "enableAutoMosh": "オートモッシュを有効にする", - "enableAutoMoshDesc": "可能であれば、SSHよりもMoshを優先する。", - "enableAutoTmux": "Auto-Tmuxを有効にする", - "enableAutoTmuxDesc": "tmuxセッションを自動的に起動または接続する", + "sshAgentForwardingShortDesc": "ローカルのSSHキーをこのホストに渡す", + "useSSHTitleLabel": "SSHウィンドウタイトルを使用する", + "useSSHTitleDesc": "タブのタイトルをホスト名ではなくシェルのウィンドウタイトルから更新する", + "enableAutoMosh": "Auto-Mosh を有効にする", + "enableAutoMoshDesc": "利用可能な場合、SSH よりも Mosh を優先します", + "enableAutoTmux": "Auto-Tmux を有効にする", + "enableAutoTmuxDesc": "自動的に tmux セッションを起動またはアタッチします", "enableSessionLogging": "セッションログ", - "enableSessionLoggingDesc": "後で確認するために、端末セッションの出力を記録しておく。", + "enableSessionLoggingDesc": "後で確認できるようにターミナルセッションの出力を記録します", + "allowSessionSharing": "セッション共有を許可する", + "allowSessionSharingDesc": "このホスト上のライブセッションをリンクまたは他のユーザーと共有できるようにします。", "enableCommandHistory": "コマンド履歴", - "enableCommandHistoryDesc": "このターミナルで実行されたコマンドを記録して、履歴とオートコンプリートに利用します。", - "linkClickBehaviorLabel": "リンククリック時の挙動", - "linkClickBehaviorDesc": "ターミナルでリンクをクリックしたときの動作を制御します。「デフォルト」を選択すると、アプリレベルの設定が適用されます。", - "linkClickBehaviorDefault": "デフォルト(アプリの設定に従う)", - "linkClickBehaviorConfirm": "ポップアップを表示して開くかコピーする", + "enableCommandHistoryDesc": "このターミナルで実行されたコマンドを履歴とオートコンプリート用に記録します", + "linkClickBehaviorLabel": "リンククリック時の動作", + "linkClickBehaviorDesc": "ターミナル内のリンクをクリックしたときの動作を制御します。「デフォルト」を選択することで、アプリ全体の設定に従います。", + "linkClickBehaviorDefault": "デフォルト(アプリ設定に従う)", + "linkClickBehaviorConfirm": "開くまたはコピーするポップアップを表示", "linkClickBehaviorDirect": "直接開く", - "sudoPasswordAutoFillLabel": "Sudoパスワードの自動入力", - "sudoPasswordAutoFillShortDesc": "プロンプトが表示されたら自動的にsudoパスワードを入力する", - "sudoPasswordAutoFillDesc": "ホストメトリクス、ターミナルプロンプトなどが特権コマンドを自動的に実行できるように、sudoパスワードを保存してください。", + "sudoPasswordAutoFillLabel": "sudo パスワードの自動入力", + "sudoPasswordAutoFillShortDesc": "プロンプトが表示されたときに自動的にsudo パスワードを入力します", + "sudoPasswordAutoFillDesc": "sudo パスワードを保存して、ホストメトリクス、ターミナルプロンプトなどが自動的に特権コマンドを実行できるようにします", "sudoPasswordLabel": "sudo パスワード", "environmentVariablesLabel": "環境変数", - "addVariableBtn": "変数を追加する", - "noEnvVars": "環境変数は設定されていません。", - "fastScrollModifierLabel": "高速スクロール修飾子", - "fastScrollSensitivityLabel": "高速スクロール感度", - "moshCommandLabel": "モッシュコマンド", - "startupSnippetLabel": "スタートアップスニペット", + "addVariableBtn": "変数を追加", + "noEnvVars": "環境変数が設定されていません", + "fastScrollModifierLabel": "高速スクロールの修飾キー", + "fastScrollSensitivityLabel": "高速スクロールの感度", + "moshCommandLabel": "Mosh コマンド", + "startupSnippetLabel": "起動時スニペット", "keepaliveIntervalLabel": "キープアライブ間隔(秒)", - "maxKeepaliveMisses": "マックス・キープアライブがミス", + "maxKeepaliveMisses": "最大キープアライブミス回数", "tunnelSettings": "トンネル設定", "enableTunneling": "トンネリングを有効にする", - "enableTunnelingDesc": "このホストのSSHトンネル機能を有効にする", + "enableTunnelingDesc": "このホストのSSHトンネル機能を有効にします", "serverTunnelsSection": "サーバートンネル", "addTunnelBtn": "トンネルを追加", - "noTunnelsConfigured": "トンネルは設定されていません。", + "noTunnelsConfigured": "トンネルが設定されていません", "tunnelLabel": "トンネル {{number}}", "tunnelType": "トンネルの種類", "tunnelModeLocalDesc": "ローカルポートをリモートサーバー(またはそこから到達可能なホスト)のポートに転送します。", - "tunnelModeRemoteDesc": "リモートサーバー上のポートを、自分のマシン上のローカルポートに転送します。", - "tunnelModeDynamicDesc": "動的ポートフォワーディングのために、ローカルポート上にSOCKS5プロキシを作成します。", - "sameHost": "このホスト(ダイレクトトンネル)", + "tunnelModeRemoteDesc": "リモートサーバーのポートをローカルマシンのポートに転送します。", + "tunnelModeDynamicDesc": "動的ポート転送用にローカルポートにSOCKS5プロキシを作成します。", + "sameHost": "このホスト(直接トンネル)", "endpointHost": "エンドポイントホスト", - "endpointHostPlaceholder": "同一ホスト、SSHホスト、または到達可能なホスト/IP", + "endpointHostPlaceholder": "同じホスト、SSHホスト、または到達可能なホスト/IP", "endpointPort": "エンドポイントポート", - "bindHost": "ホストをバインドする", + "bindHost": "バインドホスト", "sourcePort": "ソースポート", "maxRetries": "最大再試行回数", "retryIntervalS": "再試行間隔(秒)", "autoStartLabel": "自動起動", - "autoStartDesc": "ホストがロードされたときにこのトンネルを自動的に接続します", - "tunnelConnecting": "接続するトンネル...", - "tunnelDisconnected": "トンネルが切断されました", + "autoStartDesc": "ホスト読み込み時にこのトンネルを自動接続します。", + "tunnelConnecting": "トンネル接続中...", + "tunnelDisconnected": "トンネル切断", "failedToConnectTunnel": "接続に失敗しました", "failedToDisconnectTunnel": "切断に失敗しました", "dockerIntegration": "Docker統合", - "enableDockerMonitor": "Dockerを有効にする", - "enableDockerMonitorDesc": "このホスト上のコンテナをDocker経由で監視および管理する", + "enableDockerMonitor": "Docker有効化", + "enableDockerMonitorDesc": "Docker経由でこのホスト上のコンテナを監視・管理します。", "containerRuntime": "コンテナランタイム", - "containerRuntimeDesc": "このホストでコンテナ管理に使用するCLIを選択してください。", - "containerRuntimeDocker": "ドッカー", - "containerRuntimePodman": "ポッドマン", + "containerRuntimeDesc": "このホストのコンテナ管理に使用するCLIを選択します。", + "containerRuntimeDocker": "Docker", + "containerRuntimePodman": "Podman", "enableTmuxMonitor": "Tmuxモニターを有効にする", - "enableTmuxMonitorDesc": "このホストをTmuxモニターに表示し、そのtmuxアクションをサイドバーに追加する", - "tabProxmox": "プロックスモックス", - "proxmoxIntegration": "Proxmoxとの統合", + "enableTmuxMonitorDesc": "Tmuxモニターにこのホストを表示し、tmuxアクションをサイドバーに追加します。", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox統合", "enableProxmox": "Proxmoxを有効にする", - "enableProxmoxDesc": "このホストをProxmoxノードとしてマークします。これにより、このホストから直接ゲストOSの検出とインポートが可能になります。", - "proxmoxDefaultAuthType": "デフォルトの認証タイプ", - "proxmoxDefaultAuthTypeDesc": "インポートされたゲストホストに適用される認証方法。ゲストとの接続方法に合った認証タイプを選択してください。", + "enableProxmoxDesc": "このホストをProxmoxノードとしてマークします。ゲストの検出とインポートをこのホストから直接行えるようにします。", + "proxmoxDefaultAuthType": "デフォルト認証タイプ", + "proxmoxDefaultAuthTypeDesc": "インポートされたゲストホストに適用される認証方式です。ゲストへの接続方法に合わせて認証タイプを選択してください。", "authTypePassword": "パスワード", - "authTypeKey": "SSHキー", - "authTypeCredential": "資格証明書", + "authTypeKey": "SSH鍵", + "authTypeCredential": "クレデンシャル", "authTypeOpkssh": "OPKSSH", "authTypeNone": "なし", - "proxmoxDefaultCredential": "デフォルトの認証情報", - "proxmoxDefaultCredentialDesc": "認証タイプが「認証情報」に設定されている場合、インポートされたゲストホストに使用される認証情報。", + "proxmoxDefaultCredential": "デフォルト認証情報", + "proxmoxDefaultCredentialDesc": "認証タイプが「Credential」に設定されている場合に、インポートされたゲストホストに使用される認証情報。", "proxmoxWindowsDetection": "Windows / RDP 検出", - "proxmoxWindowsDetectionDesc": "RDPをSSHではなく起動するカンマ区切りの名前パターン(大文字小文字を区別しない)", - "proxmoxDockerDetection": "Docker検出", - "proxmoxDockerDetectionDesc": "ゲストのマッチングを可能にする、カンマ区切りの名前パターン", - "proxmoxPreferredRanges": "推奨IPアドレス範囲", - "proxmoxPreferredRangesDesc": "ゲストが複数のインターフェースを持つ場合のIP選択の優先順位を示すカンマ区切りのプレフィックス", + "proxmoxWindowsDetectionDesc": "SSH の代わりに RDP をトリガーする、カンマ区切りの名前パターン(大文字と小文字を区別しない)", + "proxmoxDockerDetection": "Docker 検出", + "proxmoxDockerDetectionDesc": "一致するゲストに対して Docker を有効にする、カンマ区切りの名前パターン", + "proxmoxPreferredRanges": "優先 IP 範囲", + "proxmoxPreferredRangesDesc": "ゲストが複数のインターフェースを持つ場合の IP 選択の優先順位で指定する、カンマ区切りのプレフィックス", "proxmoxAutoSync": "ゲストの自動同期", - "proxmoxAutoSyncDesc": "セッションがロック解除されている間に、定期的にこの Proxmox ノードを検出し、インポート済みのゲストホストを作成または更新します。", + "proxmoxAutoSyncDesc": "セッションがロックされていないときに、この Proxmox ノードを定期的に検出し、インポートされたゲストホストを作成または更新します。", "proxmoxSyncInterval": "同期間隔(分)", - "proxmoxSyncIntervalDesc": "最低5分。スケジューラはロックされたユーザーセッションをスキップします。", - "proxmoxMarkMissing": "行方不明のゲストをマークする", - "proxmoxMarkMissingDesc": "以前インポートしたゲストが消えた場合、削除する代わりにproxmox-missingタグを追加する。", + "proxmoxSyncIntervalDesc": "最小 5 分。スケジューラーはロックされたユーザーセッションをスキップします。", + "proxmoxMarkMissing": "欠落ゲストをマーク", + "proxmoxMarkMissingDesc": "以前にインポートされたゲストが消えたときに、削除する代わりに proxmox-missing タグを追加します。", "proxmoxLastSync": "最終同期", - "proxmoxLastSyncNever": "まだ同期されていません", - "proxmoxLastSyncNoResult": "結果の詳細はありません", - "proxmoxLastSyncSummary": "{{created}} 作成、 {{updated}} 更新、 {{markedMissing}} 欠落、 {{skipped}} スキップ", + "proxmoxLastSyncNever": "未同期", + "proxmoxLastSyncNoResult": "結果の詳細なし", + "proxmoxLastSyncSummary": "{{created}} 作成、{{updated}} 更新、{{markedMissing}} 欠落、{{skipped}} スキップ", "proxmoxLastSyncStatus": { "success": "成功", - "error": "失敗した", + "error": "失敗", "pending": "保留中" }, - "proxmoxDiscoverAction": "Proxmoxゲストの検出とインポート", - "proxmoxImportTitle": "Proxmoxからインポート", - "proxmoxSelectHost": "Proxmoxホストを選択してください…", - "proxmoxDiscover": "発見する", - "proxmoxDiscovering": "… を発見", - "proxmoxDiscoverGuests": "ゲストを探す", - "proxmoxGuestsSelected_one": "{{count}} ゲスト — {{selected}} 選択済み", - "proxmoxGuestsSelected_other": "{{count}} ゲスト — {{selected}} 選択済み", + "proxmoxDiscoverAction": "Proxmox ゲストの検出とインポート", + "proxmoxImportTitle": "Proxmox からインポート", + "proxmoxSelectHost": "Proxmox ホストを選択…", + "proxmoxDiscover": "検出", + "proxmoxDiscovering": "検出中…", + "proxmoxDiscoverGuests": "ゲストを検出", + "proxmoxGuestsSelected_one": "{{count}} ゲスト — {{selected}} 選択中", + "proxmoxGuestsSelected_other": "{{count}} ゲスト — {{selected}} 選択中", "proxmoxSelectAll": "すべて選択", "proxmoxDeselectAll": "すべて選択解除", - "proxmoxNoGuests": "このProxmoxノードにはゲストが見つかりませんでした。", - "proxmoxImportButton_one": "{{count}} ホストをインポートします。", - "proxmoxImportButton_other": "{{count}} ホストをインポートします", + "proxmoxNoGuests": "この Proxmox ノードにゲストが見つかりません。", + "proxmoxImportButton_one": "{{count}} ホストをインポート", + "proxmoxImportButton_other": "{{count}} 台のホストをインポート", "proxmoxResultImported": "{{count}} インポート済み", "proxmoxResultUpdated": "{{count}} 更新済み", "proxmoxResultFailed": "{{count}} 失敗", - "proxmoxImportComplete": "Proxmoxへのインポートが完了しました: {{summary}}", - "proxmoxDiscoveryFailed": "発見に失敗した", + "proxmoxResultSkippedNoIp": "{{count}} スキップしました(IPアドレスが見つかりませんでした)", + "proxmoxImportComplete": "Proxmox インポート完了: {{summary}}", + "proxmoxDiscoveryFailed": "検出に失敗しました", "proxmoxImportFailed": "インポートに失敗しました", "enableFileManagerMonitor": "ファイルマネージャーを有効にする", - "enableFileManagerMonitorDesc": "SFTP経由でこのホスト上のファイルを閲覧および管理します。", - "scpLegacyLabel": "SCPレガシーモード", - "scpLegacyDesc": "SFTPサブシステムをサポートしていないサーバー(組み込み型SSHサーバーや最小限のSSHサーバーなど)には、従来のファイル転送方式を使用してください。", + "enableFileManagerMonitorDesc": "このホストのファイルを SFTP で閲覧・管理します", + "scpLegacyLabel": "SCP レガシーモード", + "scpLegacyDesc": "SFTP サブシステムをサポートしないサーバー(組み込みや最小構成の SSH サーバーなど)に対してレガシーファイル転送を使用します", "defaultPathLabel": "デフォルトパス", - "fileManagerPathHint": "このホストのファイルマネージャ起動時に開くディレクトリ。", + "fileManagerPathHint": "このホストのファイルマネージャー起動時に開くディレクトリ", "statusChecksLabel": "ステータスチェック", "enableStatusChecks": "ステータスチェックを有効にする", - "enableStatusChecksDesc": "定期的にこのホストにpingを実行して、可用性を確認してください。", - "useGlobalInterval": "グローバルインターバルを使用する", + "enableStatusChecksDesc": "定期的にこのホストに ping を送信して可用性を確認します", + "useGlobalInterval": "グローバル間隔を使用する", "useGlobalIntervalDesc": "サーバー全体のステータスチェック間隔で上書きします", - "checkIntervalS": "チェック間隔(秒)", - "checkIntervalDesc": "接続ping間の秒数", - "metricsCollectionLabel": "指標収集", + "checkIntervalS": "チェック間隔 (秒)", + "checkIntervalDesc": "接続確認 ping の間隔 (秒)", + "metricsCollectionLabel": "メトリクス収集", "enableMetricsLabel": "メトリクスを有効にする", - "enableMetricsDesc": "このホストからCPU、RAM、ディスク、その他のメトリックを収集します。", - "useGlobalMetrics": "グローバルインターバルを使用する", - "useGlobalMetricsDesc": "サーバー全体のメトリクス間隔で上書きします", - "metricsIntervalS": "指標間隔(秒)", - "metricsIntervalDesc2": "メトリックのスナップショット間の秒数", - "visibleWidgets": "表示可能なウィジェット", - "widgetsMovedToHostMetrics": "カードの追加、配置、サイズ変更は、「ホストメトリクス」タブで直接行えるようになりました。このホストの「ホストメトリクス」を開き、「カスタマイズ」を使用して表示するカードを選択してください。", - "cpuUsageLabel": "CPU使用率", - "cpuUsageDesc": "CPU使用率、負荷平均、スパークライングラフ", - "memoryLabel": "メモリ使用量", - "memoryDesc": "RAM使用量、スワップ、キャッシュ", - "storageLabel": "ディスク使用量", - "storageDesc": "マウントポイントごとのディスク使用量", + "enableMetricsDesc": "このホストから CPU、RAM、ディスクなどのメトリクスを収集します", + "useGlobalMetrics": "グローバル間隔を使用する", + "useGlobalMetricsDesc": "サーバー全体のメトリクス収集間隔で上書きします", + "metricsIntervalS": "メトリクス間隔 (秒)", + "metricsIntervalDesc2": "メトリクススナップショットの間隔 (秒)", + "visibleWidgets": "表示するウィジェット", + "widgetsMovedToHostMetrics": "カードは、ホストメトリクスタブで直接追加、配置、サイズ変更できるようになりました。このホストのホストメトリクスを開き、カスタマイズを使用して表示するカードを選択してください。", + "cpuUsageLabel": "CPU 使用率", + "cpuUsageDesc": "CPU 使用率、ロードアベレージ、スパークライングラフ", + "memoryLabel": "メモリ使用率", + "memoryDesc": "RAM 使用率、スワップ、キャッシュ", + "storageLabel": "ディスク使用率", + "storageDesc": "マウントポイントごとのディスク使用率", "networkLabel": "ネットワークインターフェース", "networkDesc": "インターフェース一覧と帯域幅", - "uptimeLabel": "稼働時間", - "uptimeDesc": "システム稼働時間と起動時間", + "uptimeLabel": "アップタイム", + "uptimeDesc": "システム稼働時間と起動時刻", "systemInfoLabel": "システム情報", "systemInfoDesc": "OS、カーネル、ホスト名、アーキテクチャ", - "recentLoginsLabel": "最近ログインしたユーザー", - "recentLoginsDesc": "ログイン成功およびログイン失敗イベント", - "topProcessesLabel": "主要プロセス", - "topProcessesDesc": "PID、CPU%、MEM%、コマンド", + "recentLoginsLabel": "最近のログイン", + "recentLoginsDesc": "成功/失敗ログインイベント", + "topProcessesLabel": "トッププロセス", + "topProcessesDesc": "PID、CPU使用率、メモリ使用率、コマンド", "listeningPortsLabel": "リスニングポート", - "listeningPortsDesc": "プロセスと状態を持つポートを開く", + "listeningPortsDesc": "プロセスと状態を含むオープンポート", "firewallLabel": "ファイアウォール", - "firewallDesc": "ファイアウォール、AppArmor、SELinux の状態", + "firewallDesc": "ファイアウォール、AppArmor、SELinuxの状態", "quickActionsLabel": "クイックアクション", - "quickActionsToolbar": "ホストメトリクスのツールバーには、ワンクリックでコマンドを実行できるボタンとしてクイックアクションが表示されます。", - "noQuickActions": "現時点では、迅速な対応は不要です。", + "quickActionsToolbar": "クイックアクションはホストメトリクスツールバーにボタンとして表示され、ワンクリックでコマンドを実行できます。", + "noQuickActions": "クイックアクションはまだありません", "buttonLabel": "ボタンラベル", - "selectSnippetPlaceholder": "抜粋を選択してください...", + "selectSnippetPlaceholder": "スニペットを選択...", "addActionBtn": "アクションを追加", - "hostSharedSuccessfully": "ホスト共有が正常に完了しました", + "hostSharedSuccessfully": "ホストの共有に成功しました", "failedToShareHost": "ホストの共有に失敗しました", - "accessRevoked": "アクセス権が取り消されました", - "failedToRevokeAccess": "アクセス権の取り消しに失敗しました", + "accessRevoked": "アクセスが取り消されました", + "failedToRevokeAccess": "アクセスの取り消しに失敗しました", "cancelBtn": "キャンセル", "savingBtn": "保存中...", "addHostBtn": "ホストを追加", @@ -960,204 +1016,210 @@ "credentialCreated": "認証情報が作成されました", "failedToSaveCredential": "認証情報の保存に失敗しました", "credentialNameRequired": "認証情報の名前を入力してください", - "credentialAuthRequired": "パスワード、SSHキー、またはその両方を追加してください。", - "backToHosts": "ホスト一覧に戻る", + "credentialAuthRequired": "パスワード、SSHキー、またはその両方を追加してください", + "createCredentialFromHostBtn": "認証情報を作成する", + "createCredentialFromHostTitle": "ホストから認証情報を作成する", + "createCredentialFromHostDesc": "このホストの現在のユーザー名、パスワード、および/またはSSHキーがあらかじめ入力された、再利用可能で共有可能な認証情報エントリを作成します。", + "backToHosts": "ホストに戻る", "backToCredentials": "認証情報に戻る", "pinned": "ピン留め", - "noHostsFound": "ホストが見つかりませんでした", - "tryDifferentTerm": "別の用語を試してください", - "addFirstHost": "ホストを追加して始めましょう", + "noHostsFound": "ホストが見つかりません", + "tryDifferentTerm": "別の語句で試してください", + "addFirstHost": "最初のホストを追加して開始しましょう", "noCredentialsFound": "認証情報が見つかりません", "addCredentialBtn": "認証情報を追加", - "updateCredentialBtn": "認証情報の更新", - "features": "特徴", - "noFolder": "(フォルダなし)", - "deleteSelected": "消去", - "exitSelection": "選択を終了する", - "importSkip": "インポート(既存データをスキップ)", - "importOverwrite": "インポート(上書き)", - "collapseBtn": "崩壊", - "importExportBtn": "輸入/輸出", + "updateCredentialBtn": "認証情報を更新", + "features": "機能", + "noFolder": "(フォルダなし)", + "deleteSelected": "削除", + "exitSelection": "選択解除", + "importSkip": "インポート (既存をスキップ)", + "importOverwrite": "インポート (上書き)", + "collapseBtn": "折りたたむ", + "importExportBtn": "インポート / エクスポート", "hostStatusesRefreshed": "ホストの状態が更新されました", "failedToRefreshHosts": "ホストの更新に失敗しました", - "movedHostTo": "{{host}} を \"{{folder}} \" に移動しました。", + "movedHostTo": "{{host}} を \"{{folder}}\" に移動しました", "failedToMoveHost": "ホストの移動に失敗しました", - "folderRenamedTo": "フォルダ名が「{{name}}」に変更されました", - "deletedFolder": "削除されたフォルダ \"{{name}}\"", + "folderRenamedTo": "フォルダ名を \"{{name}}\" に変更しました", + "deletedFolder": "フォルダ \"{{name}}\" を削除しました", "failedToDeleteFolder": "フォルダの削除に失敗しました", - "deleteAllInFolder": "「{{name}}」内のすべてのホストを削除しますか?これは元に戻せません。", + "deleteAllInFolder": "\"{{name}}\" 内の全ホストを削除しますか? この操作は元に戻せません。", "folderPickerPlaceholder": "フォルダなし", - "folderPickerSearch": "検索または作成(サブフォルダには / を使用)...", + "folderPickerSearch": "検索または作成 (サブフォルダには / を使用)...", "folderPickerNone": "フォルダなし", - "folderPickerCreate": "「{{path}}」を作成します。", - "folderPickerEmpty": "一致するフォルダはありません", - "newFolder": "新しいフォルダー", - "createFolderTitle": "フォルダーを作成する", - "editFolderTitle": "フォルダを編集", - "folderDialogDescription": "名前、色、アイコンを選択してください。スラッシュ(/)を使ってフォルダを階層化できます。", + "folderPickerCreate": "\"{{path}}\" を作成", + "folderPickerEmpty": "一致するフォルダがありません", + "newFolder": "新規フォルダ", + "createFolderTitle": "フォルダの作成", + "editFolderTitle": "フォルダの編集", + "folderDialogDescription": "名前、色、アイコンを選択してください。フォルダを入れ子にするには / を使用します。", "folderNameLabel": "フォルダ名", - "folderNamePlaceholder": "例:制作/ウェブ", - "folderNestingHint": "階層を区切ってネストされたフォルダを作成するには、/ を使用します。", + "folderNamePlaceholder": "例: Production/Web", + "folderNestingHint": "/ で区切ってネストフォルダーを作成します", "folderColor": "色", "folderIcon": "アイコン", + "folderCredential": "資格証明書", + "folderCredentialNone": "認証情報が割り当てられていません", + "folderCredentialHint": "このフォルダ内のホストで、独自の認証情報を選択せずに「保存済み認証情報」認証を使用する場合は、この認証情報が継承されます。", "folderPreview": "プレビュー", - "folderNameFallback": "無題のフォルダ", - "createFolderButton": "フォルダーを作成する", - "saveFolderButton": "保存フォルダ", + "folderNameFallback": "無題のフォルダー", + "createFolderButton": "フォルダーを作成", + "saveFolderButton": "フォルダーを保存", "cancel": "キャンセル", "iconSearchPlaceholder": "アイコンを検索...", "openAllSessions": "すべてのセッションを開く", - "editFolder": "フォルダを編集", - "deleteFolder": "フォルダを削除する", - "folderSaved": "フォルダが保存されました", - "failedToSaveFolder": "フォルダの保存に失敗しました", - "folderDeleted": "削除されたフォルダ \"{{name}}\"", - "deleteFolderConfirm": "\"{{name}}\" とその {{count}} ホストを削除しますか?この操作は元に戻せません。", + "editFolder": "フォルダーを編集", + "deleteFolder": "フォルダーを削除", + "folderSaved": "フォルダーを保存しました", + "failedToSaveFolder": "フォルダーの保存に失敗しました", + "folderDeleted": "フォルダー \"{{name}}\" を削除しました", + "deleteFolderConfirm": "フォルダー \"{{name}}\" とその {{count}} 件のホストを削除しますか? この操作は元に戻せません。", "failedToMoveHosts": "ホストの移動に失敗しました", - "expandAll": "すべてのフォルダーを展開する", + "expandAll": "すべてのフォルダーを展開", "collapseAll": "すべてのフォルダーを折りたたむ", - "moreActions": "もっと", + "moreActions": "その他", "groupBy": "グループ化", - "GroupByFolder": "フォルダ", + "GroupByFolder": "フォルダー", "GroupByTag": "タグ", - "GroupByStatus": "状態", + "GroupByStatus": "ステータス", "GroupByProtocol": "プロトコル", "GroupByAuth": "認証タイプ", - "groupUngrouped": "グループ分けなし", - "deletedHost": "削除済み {{name}}", - "copiedToClipboard": "クリップボードにコピーされました", - "terminalUrlCopied": "ターミナルのURLがコピーされました", - "fileManagerUrlCopied": "ファイルマネージャーのURLがコピーされました", + "groupUngrouped": "グループなし", + "deletedHost": "{{name}} を削除しました", + "copiedToClipboard": "クリップボードにコピーしました", + "terminalUrlCopied": "ターミナルURLをコピーしました", + "fileManagerUrlCopied": "ファイルマネージャーURLをコピーしました", "tunnelUrlCopied": "トンネルURLをコピーしました", - "dockerUrlCopied": "Docker URLがコピーされました", - "hostMetricsUrlCopied": "ホストメトリクスのURLがコピーされました", - "tmuxMonitorUrlCopied": "TmuxモニターのURLがコピーされました", - "rdpUrlCopied": "RDP URLがコピーされました", - "vncUrlCopied": "VNC URLをコピーしました", - "telnetUrlCopied": "Telnet URLをコピーしました", - "remoteDesktopUrlCopied": "リモートデスクトップのURLがコピーされました", - "expandActions": "アクションを展開する", - "collapseActions": "折りたたみアクション", + "dockerUrlCopied": "Docker URLをコピーしました", + "hostMetricsUrlCopied": "ホストメトリクスURLをコピーしました", + "tmuxMonitorUrlCopied": "Tmux Monitor URL をコピーしました", + "rdpUrlCopied": "RDP URL をコピーしました", + "vncUrlCopied": "VNC URL をコピーしました", + "telnetUrlCopied": "Telnet URL をコピーしました", + "remoteDesktopUrlCopied": "リモートデスクトップ URL をコピーしました", + "expandActions": "アクションを展開", + "collapseActions": "アクションを折りたたむ", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "{{name}}宛にマジックパケットが送信されました。", + "wakeOnLanSuccess": "{{name}} にマジックパケットを送信しました", "wakeOnLanError": "マジックパケットの送信に失敗しました", - "cloneHostAction": "クローンホスト", - "copyAddress": "住所をコピーする", + "cloneHostAction": "ホストを複製", + "copyAddress": "アドレスをコピー", "copyLink": "リンクをコピー", - "copyTerminalUrlAction": "ターミナルのURLをコピー", - "copyFileManagerUrlAction": "ファイルマネージャーのURLをコピーする", - "copyTunnelUrlAction": "トンネルURLをコピー", - "copyDockerUrlAction": "Docker URLをコピー", - "copyHostMetricsUrlAction": "ホストメトリクスのURLをコピー", - "copyTmuxMonitorUrlAction": "Tmux MonitorのURLをコピーする", - "copyRdpUrlAction": "RDP URLをコピー", - "copyVncUrlAction": "VNC URLをコピー", - "copyTelnetUrlAction": "Telnet URLをコピー", - "copyRemoteDesktopUrlAction": "リモートデスクトップのURLをコピーする", - "deleteCredentialConfirm": "認証情報「{{name}}」を削除しますか?", - "deletedCredential": "削除済み {{name}}", - "deploySSHKeyTitle": "SSHキーをデプロイする", - "deployingBtn": "展開中...", - "deployBtn": "展開する", + "copyTerminalUrlAction": "ターミナル URL をコピー", + "copyFileManagerUrlAction": "ファイルマネージャー URL をコピー", + "copyTunnelUrlAction": "トンネル URL をコピー", + "copyDockerUrlAction": "Docker URL をコピー", + "copyHostMetricsUrlAction": "ホストメトリクス URL をコピー", + "copyTmuxMonitorUrlAction": "Tmux Monitor URL をコピー", + "copyRdpUrlAction": "RDP URL をコピー", + "copyVncUrlAction": "VNC URL をコピー", + "copyTelnetUrlAction": "Telnet URL をコピー", + "copyRemoteDesktopUrlAction": "リモートデスクトップ URL をコピー", + "deleteCredentialConfirm": "認証情報 \"{{name}}\" を削除しますか?", + "deletedCredential": "{{name}} を削除しました", + "deploySSHKeyTitle": "SSH キーをデプロイ", + "deployingBtn": "デプロイ中...", + "deployBtn": "デプロイ", "failedToDeployKey": "キーのデプロイに失敗しました", - "deleteHostsConfirm": "{{count}} ホスト{{plural}}を削除しますか?これは元に戻せません。", + "deleteHostsConfirm": "{{count}} 台のホスト{{plural}}を削除しますか?元に戻せません。", "movedToRoot": "ルートに移動しました", - "enableTerminalFeature": "ターミナルを有効にする", - "disableTerminalFeature": "ターミナルを無効にする", - "enableFilesFeature": "ファイルを有効にする", - "disableFilesFeature": "ファイルの無効化", - "enableTunnelsFeature": "トンネルを有効にする", - "disableTunnelsFeature": "トンネルを無効にする", - "enableDockerFeature": "Dockerを有効にする", - "disableDockerFeature": "Dockerを無効にする", - "enableProxmoxFeature": "Proxmoxを有効にする", - "disableProxmoxFeature": "Proxmoxを無効にする", + "enableTerminalFeature": "ターミナルを有効化", + "disableTerminalFeature": "ターミナルを無効化", + "enableFilesFeature": "ファイルを有効化", + "disableFilesFeature": "ファイルを無効化", + "enableTunnelsFeature": "トンネルを有効化", + "disableTunnelsFeature": "トンネルを無効化", + "enableDockerFeature": "Dockerを有効化", + "disableDockerFeature": "Dockerを無効化", + "enableProxmoxFeature": "Proxmoxを有効化", + "disableProxmoxFeature": "Proxmoxを無効化", "addTagsPlaceholder": "タグを追加...", "authDetails": "認証の詳細", "credType": "タイプ", - "generateKeyPairDesc": "新しい鍵ペアを生成します。秘密鍵と公開鍵は自動的に入力されます。", + "generateKeyPairDesc": "新しい鍵ペアを生成します。秘密鍵と公開鍵の両方が自動的に入力されます。", "generatingKey": "生成中...", - "generateLabel": "{{label}} を生成します。", + "generateLabel": "{{label}}を生成", "uploadFileBtn": "ファイルをアップロード", - "keyPassphraseOptional": "キーパスフレーズ(任意)", - "sshPublicKeyOptional": "SSH公開鍵(オプション)", + "keyPassphraseOptional": "鍵パスフレーズ (オプション)", + "sshPublicKeyOptional": "SSH公開鍵 (オプション)", "publicKeyGenerated": "公開鍵が生成されました", "failedToGeneratePublicKey": "公開鍵の導出に失敗しました", - "publicKeyCopied": "公開鍵をコピーしました", - "keyPairGenerated": "{{label}} キーペアが生成されました", - "failedToGenerateKeyPair": "キーペアの生成に失敗しました", + "publicKeyCopied": "公開鍵がコピーされました", + "keyPairGenerated": "{{label}}鍵ペアが生成されました", + "failedToGenerateKeyPair": "鍵ペアの生成に失敗しました", "searchHostsPlaceholder": "ホスト、アドレス、タグを検索…", - "searchCredentialsPlaceholder": "検索資格情報…", + "searchCredentialsPlaceholder": "認証情報を検索…", "refreshBtn": "更新", "addTag": "タグを追加...", - "deleteConfirmBtn": "消去", - "tunnelRequirementsText": "SSHサーバーでは、/etc/ssh/sshd_configにGatewayPorts yes、AllowTcpForwarding yes、およびPermitRootLogin yesが設定されている必要があります。", - "deleteHostConfirm": "「{{name}}」を削除しますか?", - "enableAtLeastOneProtocol": "認証および接続設定を構成するには、上記のプロトコルのうち少なくとも1つを有効にしてください。", - "keyPassphrase": "キーパスフレーズ", - "connectBtn": "接続する", + "deleteConfirmBtn": "削除", + "tunnelRequirementsText": "SSHサーバーの/etc/ssh/sshd_configに、GatewayPorts yes、AllowTcpForwarding yes、PermitRootLogin yesが設定されている必要があります。", + "deleteHostConfirm": "\"{{name}}\"を削除しますか?", + "enableAtLeastOneProtocol": "認証と接続の設定を行うには、上記のプロトコルを少なくとも1つ有効にしてください。", + "keyPassphrase": "鍵パスフレーズ", + "connectBtn": "接続", "disconnectBtn": "切断", "basicInformation": "基本情報", "authDetailsSection": "認証の詳細", "credTypeLabel": "タイプ", - "hostsTab": "主催者", - "credentialsTab": "資格", + "hostsTab": "ホスト", + "credentialsTab": "認証情報", "selectMultiple": "複数選択", "selectHosts": "ホストを選択", - "connectionLabel": "繋がり", + "connectionLabel": "接続", "authenticationLabel": "認証", - "generateKeyPairTitle": "キーペアを生成する", - "generateKeyPairDescription": "新しい鍵ペアを生成します。秘密鍵と公開鍵は自動的に入力されます。", + "generateKeyPairTitle": "キーペアを生成", + "generateKeyPairDescription": "新しいキーペアを生成します。秘密鍵と公開鍵が自動的に入力されます。", "generateFromPrivateKey": "秘密鍵から生成", "refreshBtn2": "更新", - "exitSelectionTitle": "選択を終了する", + "exitSelectionTitle": "選択を終了", "exportAll": "すべてエクスポート", "exportForSharing": "共有用にエクスポート", "addHostBtn2": "ホストを追加", "addCredentialBtn2": "認証情報を追加", "checkingHostStatuses": "ホストの状態を確認しています...", "pinnedSection": "ピン留め", - "hostsExported": "ホストのエクスポートが正常に完了しました", - "hostsShareExported": "共有可能なホストが正常にエクスポートされました", + "hostsExported": "ホストを正常にエクスポートしました", + "hostsShareExported": "共有用ホストを正常にエクスポートしました", "exportFailed": "ホストのエクスポートに失敗しました", - "sampleDownloaded": "サンプルファイルがダウンロードされました", + "sampleDownloaded": "サンプルファイルをダウンロードしました", "failedToDeleteCredential2": "認証情報の削除に失敗しました", - "noFolderOption": "(フォルダなし)", - "nSelected": "{{count}} 選択済み", - "featuresMenu": "特徴", - "moveMenu": "動く", - "connectSelected": "接続する", + "noFolderOption": "(フォルダーなし)", + "nSelected": "{{count}} 件選択中", + "featuresMenu": "機能", + "moveMenu": "移動", + "connectSelected": "接続", "cancelSelection": "キャンセル", - "deployDialogDesc": "ホストの authorized_keys に {{name}} をデプロイします。", - "targetHostLabel": "ターゲットホスト", + "deployDialogDesc": "{{name}} をホストの authorized_keys にデプロイします。", + "targetHostLabel": "対象ホスト", "selectHostOption": "ホストを選択してください...", - "keyDeployedSuccess": "キーの展開に成功しました", - "failedToDeployKey2": "キーのデプロイに失敗しました", - "deletedCount": "削除された {{count}} ホスト", - "failedToDeleteCount": "{{count}} ホストの削除に失敗しました", - "duplicatedHost": "重複した \"{{name}}\"", + "keyDeployedSuccess": "鍵が正常にデプロイされました", + "failedToDeployKey2": "鍵のデプロイに失敗しました", + "deletedCount": "{{count}} 件のホストを削除しました", + "failedToDeleteCount": "{{count}} 件のホストの削除に失敗しました", + "duplicatedHost": "\"{{name}}\" を複製しました", "failedToDuplicateHost": "ホストの複製に失敗しました", - "updatedCount": "更新された {{count}} ホスト", - "friendlyNameLabel": "フレンドリー名", + "updatedCount": "{{count}} ホストを更新しました", + "friendlyNameLabel": "表示名", "descriptionLabel": "説明", - "loadingHost": "ホストを読み込んでいます...", - "loadingHosts": "ホストを読み込んでいます...", - "loadingCredentials": "認証情報を読み込んでいます...", - "noHostsYet": "ホストはまだいません", - "noHostsMatchSearch": "検索条件に一致するホストは見つかりませんでした。", + "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": "フィルターをクリア", + "sortHosts": "ホストの並べ替え", + "sortDefault": "デフォルトの順序", + "sortNameAsc": "名前 (A → Z)", + "sortNameDesc": "名前 (Z → A)", + "sortIpAsc": "IPアドレス (昇順)", + "sortIpDesc": "IPアドレス (降順)", + "sortOnlineFirst": "オンラインを先頭に", + "sortOfflineFirst": "オフラインを先頭に", + "sortPinnedFirst": "ピン留めを先頭に", + "filterHosts": "ホストの絞り込み", + "filterClearAll": "すべてのフィルターをクリア", "filterStatusGroup": "状態", "filterOnline": "オンライン", "filterOffline": "オフライン", @@ -1165,7 +1227,7 @@ "filterAuthGroup": "認証タイプ", "filterAuthPassword": "パスワード", "filterAuthKey": "SSHキー", - "filterAuthCredential": "資格証明書", + "filterAuthCredential": "資格情報", "filterAuthNone": "なし", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "プロトコル", @@ -1173,110 +1235,118 @@ "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特徴", + "filterFeaturesGroup": "機能", "filterFeatureTerminal": "ターミナル", "filterFeatureFileManager": "ファイルマネージャー", "filterFeatureTunnel": "トンネル", - "filterFeatureDocker": "ドッカー", + "filterFeatureDocker": "Docker", "filterTagsGroup": "タグ", - "shareHost": "共有ホスト", + "shareHost": "ホストを共有", "shareHostTitle": "共有: {{name}}", + "shareFolder": "フォルダを共有する", + "shareFolderTitle": "共有フォルダ: {{name}}", + "folderSharedSuccessfully": "フォルダー内の共有ホスト {{count}} 個", + "failedToShareFolder": "フォルダの共有に失敗しました", "sharing": { "loadError": "共有データの読み込みに失敗しました。もう一度お試しください。", - "shareWithSection": "シェアする", + "shareWithSection": "共有相手", "usersTab": "ユーザー", - "rolesTab": "役割", - "searchPlaceholder": "ユーザーまたは役割を検索...", - "noMatches": "一致する項目が見つかりませんでした", + "rolesTab": "ロール", + "searchPlaceholder": "ユーザーまたはロールを検索...", + "noMatches": "一致する結果がありません", "permissionLevelLabel": "権限レベル", "levels": { "connect": { - "label": "接続する", - "description": "開いているセッションのみ:ターミナル、リモートデスクトップ、ファイルマネージャ、トンネル、およびDocker。ホスト構成へのアクセスはできません。" + "label": "接続", + "description": "セッションのみ開く: ターミナル、リモートデスクトップ、ファイルマネージャー、トンネル、Docker。ホスト設定へのアクセスはありません。" }, "view": { - "label": "ビュー", - "description": "接続してホストの設定を確認できます。機密情報は一切表示されません。" + "label": "表示", + "description": "接続に加え、ホスト設定を表示できます。シークレットは表示されません。" }, "edit": { "label": "編集", - "description": "ホストの表示と変更が可能です。シークレットは置き換えることはできますが、読み取ることはできません。認証情報の割り当ては所有者のみが行えます。" + "description": "表示に加え、ホストを編集できます。シークレットは置き換え可能ですが読み取りはできません。認証情報の割り当ては所有者のみが行えます。" }, "manage": { "label": "管理", - "description": "編集、ホストの共有、権限レベルの変更、アクセス権の取り消しが可能です。" + "description": "編集に加え、ホストを他者と共有し、権限レベルを変更、アクセスを取り消すことができます。" } }, - "expiryLabel": "アクセス期限切れ", + "expiryLabel": "アクセス期限", "expiry": { - "never": "一度もない", + "never": "無期限", "oneHour": "1時間", "oneDay": "24時間", "sevenDays": "7日間", "thirtyDays": "30日間", "custom": "カスタム" }, - "customHoursPlaceholder": "アクセス期限まであと数時間", + "customHoursPlaceholder": "アクセス期限までの時間数", "shareButton": "共有", - "shareWithCount": "シェア({{count}})", - "currentAccess": "現在のアクセス状況", + "shareWithCount": "共有 ({{count}})", + "currentAccess": "現在のアクセス", "noAccessEntries": "このホストはまだ共有されていません", - "grantedBy": "許可された", + "folderShareSummary": "Shared {{shared}} of {{total}} host(s) in this folder", + "grantedBy": "付与者", "expires": "有効期限", "expired": "期限切れ", - "never": "一度もない", - "revoke": "取り消す", - "accessUpdated": "アクセス情報が更新されました", - "accessUpdateFailed": "アクセス情報の更新に失敗しました", - "sharedBadge": "共有", - "sharedBadgeTooltip": "{{owner}} ({{level}} アクセス) によって共有されています", - "viewOnlyBanner": "このホストは、 {{owner}} によって閲覧権限で共有されています。設定は読み取り専用です。", - "sharedEditBanner": "このホストは、編集権限を持つ {{owner}} によって共有されています。変更は実際のホストに適用されます。認証情報は所有者のみが変更できます。", - "ownerOnlyControl": "ホストの所有者のみがこれを変更できます" + "never": "無期限", + "revoke": "取り消し", + "accessUpdated": "アクセスが更新されました", + "accessUpdateFailed": "アクセスの更新に失敗しました", + "sharedBadge": "共有済み", + "sharedBadgeTooltip": "{{owner}} により共有 ({{level}}アクセス)", + "viewOnlyBanner": "このホストは{{owner}}によりビューアクセスで共有されています。設定は読み取り専用です。", + "sharedEditBanner": "このホストは{{owner}}により編集アクセスで共有されています。変更は実際のホストに適用されます。認証情報は所有者のみ変更できます。", + "ownerOnlyControl": "ホスト所有者のみ変更できます" }, "guac": { - "connection": "繋がり", + "connection": "接続", "authentication": "認証", "storedCredential": "保存された認証情報", - "noCredential": "資格証明書なし(資格証明書は下記参照)", + "noCredential": "認証情報なし (直接入力は以下)", "authMethod": "認証方法", "authTypeDirect": "直接", - "authTypeCredential": "資格証明書", - "selectCredential": "資格情報を選択してください...", + "authTypeCredential": "認証情報", + "authTypeNone": "なし", + "authTypeNoneDesc": "認証情報は保存されません。接続するたびにユーザー名とパスワードの入力を求められますが、それらは保存されません。", + "selectCredential": "認証情報を選択...", "connectionSettings": "接続設定", "displaySettings": "表示設定", "audioSettings": "オーディオ設定", - "rdpPerformance": "RDPパフォーマンス", + "rdpPerformance": "RDP パフォーマンス", "deviceRedirection": "デバイスリダイレクト", "session": "セッション", "gateway": "ゲートウェイ", "remoteApp": "RemoteApp", "clipboard": "クリップボード", - "sessionRecording": "セッション録画", + "sessionRecording": "セッション記録", "wakeOnLan": "Wake-on-LAN", "vncSettings": "VNC設定", - "terminalSettings": "端末設定", + "terminalSettings": "ターミナル設定", "rdpPort": "RDPポート", "username": "ユーザー名", "password": "パスワード", + "passwordSaved": "パスワードが保存されました。変更するには入力してください。", "domain": "ドメイン", "securityMode": "セキュリティモード", - "colorDepth": "色の深度", + "colorDepth": "色深度", "width": "幅", - "height": "身長", + "height": "高さ", "dpi": "DPI", - "resizeMethod": "サイズ変更方法", + "resizeMethod": "リサイズ方法", "clientName": "クライアント名", "initialProgram": "初期プログラム", "serverLayout": "サーバーレイアウト", "timezone": "タイムゾーン", - "loadBalanceInfo": "ロードバランス情報 / クッキー", - "loadBalanceInfoDesc": "RDSファームの負荷分散のためのRD接続ブローカーのCookie(例:tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "負荷分散情報 / Cookie", + "loadBalanceInfoDesc": "RDSファームの負荷分散用RD接続ブローカーCookie (例: tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacdプロキシ", - "guacdHostname": "guacd ホスト", - "guacdHostnamePlaceholder": "グローバルデフォルト", - "guacdPort": "グアッド港", - "guacdProxyDesc": "この接続で使用するグローバルなguacdインスタンスを上書きします。グローバルに設定されたguacdを使用する場合は、空欄のままにしてください。", + "guacdHostname": "guacdホスト", + "guacdHostnamePlaceholder": "全体のデフォルト", + "guacdPort": "guacdポート", + "guacdProxyDesc": "この接続に対してグローバル guacd インスタンスを上書きします。空白にするとグローバルに設定された guacd を使用します。", "gatewayHostname": "ゲートウェイホスト名", "gatewayPort": "ゲートウェイポート", "gatewayUsername": "ゲートウェイユーザー名", @@ -1284,716 +1354,779 @@ "gatewayDomain": "ゲートウェイドメイン", "remoteAppProgram": "RemoteAppプログラム", "workingDirectory": "作業ディレクトリ", - "arguments": "議論", - "normalizeLineEndings": "改行コードを正規化する", - "recordingPath": "記録パス", - "recordingName": "録音名", + "arguments": "引数", + "normalizeLineEndings": "改行コードの正規化", + "recordingPath": "録画パス", + "recordingName": "録画名", "macAddress": "MACアドレス", - "broadcastAddress": "放送演説", + "broadcastAddress": "ブロードキャストアドレス", "udpPort": "UDPポート", - "waitTimeS": "待ち時間(秒)", + "waitTimeS": "待機時間(秒)", "driveName": "ドライブ名", "drivePath": "ドライブパス", - "ignoreCertificate": "証明書を無視する", + "ignoreCertificate": "証明書を無視", "ignoreCertificateDesc": "自己署名証明書を持つホストへの接続を許可する", - "forceLossless": "力損失なし", - "forceLosslessDesc": "強制的にロスレス画像エンコードを実行する(高画質、広帯域幅)", - "disableAudio": "音声を無効にする", - "disableAudioDesc": "リモートセッションのすべての音声をミュートします", - "enableAudioInput": "音声入力(マイク)を有効にする", + "forceLossless": "ロスレスを強制", + "forceLosslessDesc": "ロスレス画像エンコーディングを強制する(高品質だが帯域幅を多く消費)", + "disableAudio": "オーディオを無効化", + "disableAudioDesc": "リモートセッションの音声をすべてミュートする", + "enableAudioInput": "音声入力(マイク)を有効化", "enableAudioInputDesc": "ローカルマイクをリモートセッションに転送する", "wallpaper": "壁紙", - "wallpaperDesc": "デスクトップの壁紙を表示する(無効にするとパフォーマンスが向上します)", - "theming": "テーマ設定", + "wallpaperDesc": "デスクトップ壁紙を表示する(無効にするとパフォーマンスが向上)", + "theming": "テーマ", "themingDesc": "ビジュアルテーマとスタイルを有効にする", "fontSmoothing": "フォントスムージング", "fontSmoothingDesc": "ClearTypeフォントレンダリングを有効にする", - "fullWindowDrag": "ウィンドウ全体をドラッグ", - "fullWindowDragDesc": "ドラッグ中にウィンドウの内容を表示する", - "desktopComposition": "デスクトップ構成", - "desktopCompositionDesc": "エアロガラス効果を有効にする", + "fullWindowDrag": "フルウィンドウドラッグ", + "fullWindowDragDesc": "ウィンドウドラッグ中に内容を表示する", + "desktopComposition": "デスクトップコンポジション", + "desktopCompositionDesc": "Aero Glass効果を有効にする", "menuAnimations": "メニューアニメーション", - "menuAnimationsDesc": "メニューのフェードイン・フェードアウトとスライドアニメーションを有効にする", - "disableBitmapCaching": "ビットマップキャッシュを無効にする", - "disableBitmapCachingDesc": "ビットマップキャッシュをオフにする(不具合の解消に役立つ場合があります)", - "disableOffscreenCaching": "オフスクリーンキャッシュを無効にする", + "menuAnimationsDesc": "メニューのフェードおよびスライドアニメーションを有効にする", + "disableBitmapCaching": "ビットマップキャッシュを無効化", + "disableBitmapCachingDesc": "ビットマップキャッシュをオフにする(不具合の解決に役立つ場合がある)", + "disableOffscreenCaching": "オフスクリーンキャッシュを無効化", "disableOffscreenCachingDesc": "オフスクリーンキャッシュをオフにする", - "disableGlyphCaching": "グリフキャッシュを無効にする", + "disableGlyphCaching": "グリフキャッシュを無効化", "disableGlyphCachingDesc": "グリフキャッシュをオフにする", - "enableGfx": "GFXを有効にする", + "enableGfx": "GFXを有効化", "enableGfxDesc": "RemoteFXグラフィックスパイプラインを使用する", - "enablePrinting": "印刷を有効にする", + "enablePrinting": "印刷を有効化", "enablePrintingDesc": "ローカルプリンターをリモートセッションにリダイレクトする", - "enableDriveRedirection": "ドライブリダイレクトを有効にする", - "enableDriveRedirectionDesc": "ローカルフォルダをリモートセッションのドライブとしてマッピングする", - "createDrivePath": "ドライブパスを作成する", - "createDrivePathDesc": "フォルダが存在しない場合は自動的に作成する", + "enableDriveRedirection": "ドライブリダイレクションを有効化", + "enableDriveRedirectionDesc": "リモートセッションでローカルフォルダーをドライブとしてマッピングする", + "createDrivePath": "ドライブパスを作成", + "createDrivePathDesc": "フォルダが存在しない場合に自動的に作成します", "disableDownload": "ダウンロードを無効にする", - "disableDownloadDesc": "リモートセッションからのファイルのダウンロードを防止する", + "disableDownloadDesc": "リモートセッションからファイルをダウンロードできないようにします", "disableUpload": "アップロードを無効にする", - "disableUploadDesc": "リモートセッションへのファイルのアップロードを防止する", + "disableUploadDesc": "リモートセッションにファイルをアップロードできないようにします", "enableTouch": "タッチを有効にする", - "enableTouchDesc": "タッチ入力転送を有効にする", + "enableTouchDesc": "タッチ入力転送を有効にします", "consoleSession": "コンソールセッション", - "consoleSessionDesc": "新しいセッションではなく、コンソール(セッション0)に接続します。", - "sendWolPacket": "WOLパケットを送信する", - "sendWolPacketDesc": "接続する前に、このホストを起動するためのマジックパケットを送信します。", + "consoleSessionDesc": "新しいセッションではなくコンソール(セッション0)に接続します", + "sendWolPacket": "WOLパケットを送信", + "sendWolPacketDesc": "接続前にこのホストを起動するためのマジックパケットを送信します", "disableCopy": "コピーを無効にする", - "disableCopyDesc": "リモートセッションからのテキストのコピーを防止する", + "disableCopyDesc": "リモートセッションからのテキストコピーを禁止します", "disablePaste": "貼り付けを無効にする", - "disablePasteDesc": "リモートセッションへのテキスト貼り付けを防止する", - "createPathIfMissing": "パスが存在しない場合は作成します", - "createPathIfMissingDesc": "録音ディレクトリを自動的に作成する", - "excludeOutput": "出力除外", - "excludeOutputDesc": "画面出力は記録しないでください(メタデータのみ記録してください)", - "excludeMouse": "マウスを除外する", - "excludeMouseDesc": "マウスの動きを記録しないでください", - "includeKeystrokes": "キー入力を含める", - "includeKeystrokesDesc": "画面出力に加えて、生のキーストロークも記録する", + "disablePasteDesc": "リモートセッションへのテキスト貼り付けを禁止します", + "createPathIfMissing": "パスが存在しない場合に作成", + "createPathIfMissingDesc": "レコーディングディレクトリが存在しない場合に自動的に作成します", + "excludeOutput": "出力を除外", + "excludeOutputDesc": "画面出力を記録しません(メタデータのみ)", + "excludeMouse": "マウスを除外", + "excludeMouseDesc": "マウスの動きを記録しません", + "includeKeystrokes": "キーストロークを含める", + "includeKeystrokesDesc": "画面出力に加えて生のキーストロークを記録します", "vncPort": "VNCポート", "vncPassword": "VNCパスワード", - "vncUsernameOptional": "ユーザー名(任意)", - "vncLeaveBlank": "不要な場合は空欄のままにしてください。", + "vncUsernameOptional": "ユーザー名(オプション)", + "vncLeaveBlank": "必要ない場合は空白のままにしてください", "cursorMode": "カーソルモード", - "swapRedBlue": "赤と青を入れ替える", - "swapRedBlueDesc": "赤と青のカラーチャンネルを入れ替える(一部の色に関する問題を修正します)", + "swapRedBlue": "赤/青を入れ替え", + "swapRedBlueDesc": "赤と青の色チャネルを入れ替えます(色の問題を修正)", "readOnly": "読み取り専用", - "readOnlyDesc": "入力を送信せずにリモート画面を表示する", + "readOnlyDesc": "入力を送信せずにリモート画面を表示します", "telnetPort": "Telnetポート", "terminalType": "端末タイプ", "fontName": "フォント名", "fontSize": "フォントサイズ", - "colorScheme": "配色", + "colorScheme": "カラースキーム", "backspaceKey": "バックスペースキー", - "saveHostFirst": "まずホストをセーブしてください。", - "sharingOptionsAfterSave": "ホストを保存すると、共有オプションが利用可能になります。", + "saveHostFirst": "最初にホストを保存してください。", + "sharingOptionsAfterSave": "共有オプションはホストの保存後に利用可能です。", "permissionLevel": "権限レベル", "typeHeader": "タイプ", "targetHeader": "ターゲット", - "permissionHeader": "許可", + "permissionHeader": "権限", "cancelBtn": "キャンセル", "savingBtn": "保存中...", - "updateHostBtn": "ホストの更新", + "updateHostBtn": "ホストを更新", "addHostBtn": "ホストを追加" } }, "commandPalette": { - "searchPlaceholder": "ホスト、コマンド、または設定を検索します...", - "quickActions": "クイックアクション", + "searchPlaceholder": "ホスト、コマンド、設定を検索...", + "quickActions": "クイック操作", "hostManager": "ホストマネージャー", "hostManagerDesc": "ホストの管理、追加、編集", - "addNewHost": "新しいホストを追加", - "addNewHostDesc": "新しいホストを登録する", - "adminSettings": "管理者設定", - "adminSettingsDesc": "システム環境設定とユーザーを設定する", - "userProfile": "ユーザープロフィール", - "userProfileDesc": "アカウントと設定を管理する", + "addNewHost": "新規ホストを追加", + "addNewHostDesc": "新しいホストを登録", + "adminSettings": "管理設定", + "adminSettingsDesc": "システム設定とユーザーを構成", + "userProfile": "ユーザープロファイル", + "userProfileDesc": "アカウントと設定を管理", "addCredential": "認証情報を追加", - "addCredentialDesc": "SSHキーまたはパスワードを保存する", - "tmuxMonitor": "Tmuxモニター", - "tmuxMonitorDesc": "ホスト間でtmuxセッションを監視する", - "recentActivity": "最近の活動", - "serversAndHosts": "サーバーとホスト", - "noHostsFound": "「{{search}}」に一致するホストは見つかりませんでした", + "addCredentialDesc": "SSH鍵やパスワードを保存", + "tmuxMonitor": "Tmux モニター", + "tmuxMonitorDesc": "ホスト間の tmux セッションを監視", + "recentActivity": "最近のアクティビティ", + "serversAndHosts": "サーバー & ホスト", + "noHostsFound": "\"{{search}}\" に一致するホストは見つかりませんでした", "links": "リンク", - "navigate": "ナビゲート", + "navigate": "移動", "select": "選択", - "toggleWith": "切り替える" + "toggleWith": "切り替え" }, "splitScreen": { "paneEmpty": "ペイン {{index}} - 空", - "noTabAssigned": "タブは割り当てられていません", + "noTabAssigned": "タブが割り当てられていません", "focusedPane": "アクティブペイン" }, "connections": { - "noConnections": "接続なし", - "noConnectionsDesc": "ターミナル、ファイルマネージャ、またはリモートデスクトップを開いて、ここに接続状況を確認してください。", - "connectedFor": "{{duration}} まで接続しました", + "noConnections": "接続がありません", + "noConnectionsDesc": "ここに接続を表示するには、ターミナル、ファイルマネージャー、またはリモートデスクトップを開いてください", + "connectedFor": "接続時間 {{duration}}", "connected": "接続済み", - "disconnected": "接続が切断されました", + "disconnected": "切断済み", "closeTab": "タブを閉じる", - "closeConnection": "密接なつながり", + "closeConnection": "接続を閉じる", "forgetTab": "忘れる", - "removeBackground": "取り除く", + "removeBackground": "削除", "reconnect": "再接続", "reopenTab": "再開", - "sectionOpen": "開ける", - "sectionBackground": "背景", - "backgroundDesc": "セッションは切断後30分間維持され、再接続が可能です。", - "persisted": "バックグラウンドで継続", - "expiresIn": "有効期限は {{duration}}です", + "sectionOpen": "開く", + "sectionBackground": "バックグラウンド", + "backgroundDesc": "切断後もセッションは30分間バックグラウンドで保持され、再接続できます。", + "persisted": "バックグラウンドで保持中", + "expiresIn": "{{duration}} 後に期限切れ", "search": "接続を検索...", "noSearchResults": "検索条件に一致する接続はありません", - "rename": "セッション名の変更" + "rename": "セッション名を変更", + "sectionSharedWithMe": "私と共有しました", + "sharedBy": "{{username}} によって共有されました", + "join": "参加する", + "sharedSessionLabel": "{{hostName}} (共有)" + }, + "sessionSharing": { + "guestView": { + "loading": "共有セッションに接続しています...", + "linkInvalid": "この共有リンクは無効、期限切れ、または取り消されています。", + "rateLimited": "試行回数が多すぎます。しばらくしてからもう一度お試しください。", + "sessionEnded": "このセッションは終了しました", + "readOnlyBadge": "閲覧のみ" + }, + "modalTitle": "セッションを共有する", + "shareButton": "共有", + "notReadyToShare": "セッションはまだ共有できません", + "modeTab": { + "link": "リンク", + "user": "ユーザー" + }, + "linkModeDescription": "このリンクを知っている人なら誰でも参加できます。アカウントは必要ありません。", + "userModeDescription": "このホストへのアクセス権を既に持っている特定のユーザーと共有します。まだアクセス権がない場合は、まずホストを共有するか、代わりにリンクを使用してください。共有が完了すると、セッションはユーザーの「接続」タブに表示されます。", + "permissionLevel": { + "label": "権限レベル", + "readOnly": "読み取り専用", + "readOnlyDescription": "セッションをライブで視聴することはできますが、文字入力やインタラクションはできません。", + "readWrite": "読み書き", + "readWriteDescription": "所有者と同様に、セッション内で文字を入力したり操作したりできます。" + }, + "expiryLabel": "リンクの有効期限", + "createLinkButton": "リンクを作成する", + "createShareButton": "ユーザーと共有する", + "searchUsersPlaceholder": "ユーザーを検索...", + "noUsersFound": "ユーザーが見つかりませんでした", + "linkCreated": "共有リンクが作成されました", + "linkCopied": "リンクがクリップボードにコピーされました", + "copyLink": "リンクをコピー", + "shareCreated": "セッションが共有されました。相手の「接続」タブに表示されます。", + "shareFailed": "共有の作成に失敗しました", + "userLacksHostAccess": "そのユーザーはまだこのホストへのアクセス権を持っていません。まずホストを共有するか、代わりにリンクを使用してください。", + "activeShares": "アクティブなシェア", + "noActiveShares": "今セッションで有効な株式はありません", + "revoke": "取り消す", + "revokeConfirmTitle": "この共有を取り消しますか?", + "revokeConfirmDescription": "この共有ファイルを使用している人は、即座にアクセス権を失います。", + "revoked": "シェアが取り消されました", + "revokeFailed": "シェアの取り消しに失敗しました", + "joinCount": "{{count}} 参加する", + "joinCount_other": "{{count}} が参加します", + "expiresAt": "有効期限 {{date}}", + "linkShareBadge": "リンク", + "userShareBadge": "ユーザー: {{username}}", + "loadSharesFailed": "アクティブな共有の読み込みに失敗しました" }, "guacamole": { - "connecting": "{{type}} セッションに接続しています...", + "connecting": "{{type}} セッションに接続中...", "connectionError": "接続エラー", - "connectionFailed": "接続に失敗しました", + "connectionFailed": "接続失敗", "failedToConnect": "接続トークンの取得に失敗しました", "hostNotFound": "ホストが見つかりません", "noHostSelected": "ホストが選択されていません", "reconnect": "再接続", - "retry": "リトライ", - "guacdUnavailable": "リモートデスクトップサービス(guacd)が利用できません。guacdが実行されており、アクセス可能で、管理者設定で正しく構成されていることを確認してください。", + "retry": "再試行", + "guacdUnavailable": "リモートデスクトップサービス (guacd) が利用できません。guacd が実行中でアクセス可能であり、管理者設定で正しく構成されていることを確認してください。", + "credentialPromptTitle": "RDP認証情報を入力してください", + "credentialPromptDescription": "このホストは、接続時に認証情報の入力を求めるように設定されています。認証情報は今回のセッションのみで使用され、保存されません。", + "connect": "接続する", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L(ロック画面)", - "winKey": "Windowsキー", + "winL": "Win+L (画面ロック)", + "winKey": "Windows キー", "ctrl": "Ctrl", - "alt": "代替", - "shift": "シフト", - "win": "勝つ", - "stickyActive": "{{key}} (ラッチされています - クリックして解除)", - "stickyInactive": "{{key}} (クリックしてロック)", - "esc": "逃げる", - "tab": "タブ", - "home": "家", - "end": "終わり", - "pageUp": "ページアップ", - "pageDown": "ページダウン", - "arrowUp": "上向き矢印", - "arrowDown": "下向き矢印", - "arrowLeft": "左矢印", - "arrowRight": "右矢印", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}}(ラッチ中 - クリックで解除)", + "stickyInactive": "{{key}}(クリックでラッチ)", + "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": "ファンクションキー", - "reconnect": "セッションを再接続する", + "reconnect": "セッション再接続", "collapse": "ツールバーを折りたたむ", - "expand": "ツールバーを展開する", - "dragHandle": "ドラッグして位置を変更します" + "expand": "ツールバーを展開", + "dragHandle": "ドラッグで位置変更", + "switchToTrackpad": "トラックパッドモードに切り替える(ドラッグでカーソルを移動、タップでクリック)", + "switchToTouch": "タッチモードに切り替える(クリックしたい場所を直接タップする)" } }, "terminal": { "connect": "ホストに接続", "clear": "クリア", - "paste": "ペースト", + "paste": "貼り付け", "reconnect": "再接続", - "connectionLost": "接続が切断されました", - "connected": "接続済み", - "clipboardWriteFailed": "クリップボードへのコピーに失敗しました。ページがHTTPSまたはlocalhost経由で配信されていることを確認してください。", - "clipboardReadFailed": "クリップボードからの読み取りに失敗しました。クリップボードへのアクセス権限が付与されていることを確認してください。", - "clipboardHttpWarning": "貼り付けにはHTTPSが必要です。Ctrl+Shift+Vを使用するか、TermixをHTTPS経由で提供してください。", - "passwordPromptFillTitle": "保存済みのパスワードをこのプロンプトに入力しますか?", + "connectionLost": "接続切断", + "connected": "接続中", + "clipboardWriteFailed": "クリップボードへのコピーに失敗しました。ページがHTTPSまたはlocalhostで提供されていることを確認してください。", + "clipboardReadFailed": "クリップボードからの読み取りに失敗しました。クリップボードのアクセス許可が付与されていることを確認してください。", + "clipboardHttpWarning": "貼り付けにはHTTPSが必要です。Ctrl+Shift+Vを使用するか、TermixをHTTPSで提供してください。", + "passwordPromptFillTitle": "保存されたパスワードをこのプロンプトに入力しますか?", "unknownError": "不明なエラーが発生しました", "websocketError": "WebSocket接続エラー", "connecting": "接続中...", "noHostSelected": "ホストが選択されていません", "reconnecting": "再接続中... ({{attempt}}/{{max}})", - "reconnected": "再接続に成功しました", + "reconnected": "再接続完了", "tmuxSessionCreated": "tmuxセッションが作成されました: {{name}}", - "tmuxSessionAttached": "tmuxセッションが接続されました: {{name}}", - "tmuxUnavailable": "リモートホストにtmuxがインストールされていないため、標準シェルにフォールバックします。", + "tmuxSessionAttached": "tmuxセッションに接続しました: {{name}}", + "tmuxUnavailable": "リモートホストにtmuxがインストールされていません。標準シェルにフォールバックします。", "tmuxSessionPickerTitle": "tmuxセッション", - "tmuxSessionPickerDesc": "このホスト上で既存のtmuxセッションが見つかりました。いずれかを選択して再接続するか、新しいセッションを作成してください。", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "このホストに既存のtmuxセッションがあります。再アタッチするか、新しいセッションを作成してください。", + "tmuxWindows": "ウィンドウ", "tmuxWindowCount": "{{count}} ウィンドウ", - "tmuxAttached": "添付クライアント", - "tmuxAttachedCount": "{{count}} 添付済み", - "tmuxLastActivity": "最後のアクティビティ", - "tmuxTimeJustNow": "ちょうど今", + "tmuxAttached": "接続クライアント", + "tmuxAttachedCount": "{{count}} 接続", + "tmuxLastActivity": "最終アクティビティ", + "tmuxTimeJustNow": "たった今", "tmuxTimeMinutes": "{{count}}分前", "tmuxTimeHours": "{{count}}時間前", "tmuxTimeDays": "{{count}}日前", - "tmuxCreateNew": "新しいセッションを開始する", - "tmuxCopyHint": "選択範囲を調整してEnterキーを押すと、クリップボードにコピーされます。", - "tmuxDetach": "tmuxセッションから切り離す", - "tmuxDetached": "tmuxセッションから切り離されました", - "maxReconnectAttemptsReached": "再接続試行回数の上限に達しました", - "closeTab": "近い", + "tmuxCreateNew": "新規セッションを開始", + "tmuxCopyHint": "選択範囲を調整してEnterでクリップボードにコピー", + "tmuxDetach": "tmuxセッションからデタッチ", + "tmuxDetached": "tmuxセッションからデタッチしました", + "maxReconnectAttemptsReached": "再接続の最大試行回数に達しました", + "closeTab": "閉じる", "connectionTimeout": "接続タイムアウト", "terminalTitle": "ターミナル - {{host}}", - "terminalWithPath": "端末 - {{host}}:{{path}}", - "runTitle": "{{command}} - {{host}} を実行中", - "totpRequired": "二段階認証が必要です", - "totpCodeLabel": "検証コード", - "totpVerify": "確認する", - "warpgateAuthRequired": "ワープゲート認証が必要です", + "terminalWithPath": "ターミナル - {{host}}:{{path}}", + "runTitle": "{{command}} 実行中 - {{host}}", + "totpRequired": "二要素認証が必要です", + "totpCodeLabel": "確認コード", + "totpVerify": "確認", + "mfaPromptRequired": "認証が必要です", + "mfaPushRequired": "プッシュ認証が必要です", + "mfaMenuPlaceholder": "回答を入力してください", + "mfaWaitingApproval": "お使いのデバイスでの承認をお待ちしています...", + "mfaSendRequest": "リクエストを送信", + "warpgateAuthRequired": "Warpgate認証が必要です", "warpgateSecurityKey": "セキュリティキー", "warpgateAuthUrl": "認証URL", "warpgateOpenBrowser": "ブラウザで開く", "warpgateContinue": "認証が完了しました", "opksshAuthRequired": "OPKSSH認証が必要です", - "opksshAuthDescription": "続行するには、ブラウザで認証を完了してください。このセッションは24時間有効です。", - "opksshOpenBrowser": "認証のためにブラウザを開く", - "opksshWaitingForAuth": "ブラウザでの認証を待っています...", - "opksshAuthenticating": "認証処理中...", + "opksshAuthDescription": "ブラウザで認証を完了して続行してください。このセッションは24時間有効です。", + "opksshOpenBrowser": "ブラウザを開いて認証", + "opksshWaitingForAuth": "ブラウザでの認証を待機中...", + "opksshAuthenticating": "認証を処理中...", "opksshTimeout": "認証がタイムアウトしました。もう一度お試しください。", - "opksshAuthFailed": "認証に失敗しました。認証情報を確認して、もう一度お試しください。", - "opksshSignInWith": "{{provider}}でサインイン", - "vaultAuthTitle": "Vaultへのサインインが必要です", - "vaultAuthDescription": "HashiCorp Vaultへのサインイン画面が開きました。そちらでサインインを完了してください。接続は自動的に継続されます。", - "vaultAuthFailed": "Vaultの認証に失敗しました。もう一度お試しください。", - "vaultReopen": "サインインウィンドウを再度開く", - "sudoPasswordPopupTitle": "パスワードを入力してください。", + "opksshAuthFailed": "認証に失敗しました。資格情報を確認して再試行してください。", + "opksshSignInWith": "{{provider}} でサインイン", + "vaultAuthTitle": "Vault サインインが必要です", + "vaultAuthDescription": "HashiCorp Vaultへのサインイン用のウィンドウが開きました。そこでサインインを完了すると、この接続は自動的に続行されます。", + "vaultAuthFailed": "Vault 認証に失敗しました。もう一度お試しください。", + "vaultReopen": "サインインウィンドウを再表示", + "sudoPasswordPopupTitle": "パスワードを入力しますか?", "linkDialogTitle": "リンクを開く", - "linkDialogOpen": "開ける", + "linkDialogOpen": "開く", "linkDialogCopy": "コピー", - "websocketAbnormalClose": "接続が予期せず切断されました。これはリバースプロキシまたはSSL設定の問題が原因である可能性があります。サーバーログを確認してください。", + "websocketAbnormalClose": "接続が予期せず切断されました。リバースプロキシまたはSSLの設定に問題がある可能性があります。サーバーログを確認してください。", "connectionLogTitle": "接続ログ", - "connectionLogCopy": "ログをクリップボードにコピーする", - "connectionLogEmpty": "接続ログはまだありません", - "connectionLogWaiting": "接続ログを待っています...", + "connectionLogCopy": "ログをクリップボードにコピー", + "connectionLogEmpty": "まだ接続ログはありません", + "connectionLogWaiting": "接続ログを待機中...", "connectionLogCopied": "接続ログがクリップボードにコピーされました", - "connectionLogCopyFailed": "ログをクリップボードにコピーできませんでした", - "connectionRejected": "サーバーによって接続が拒否されました。認証情報とネットワーク設定を確認してください。", + "connectionLogCopyFailed": "ログのクリップボードへのコピーに失敗しました", + "connectionRejected": "サーバーによって接続が拒否されました。認証設定とネットワーク構成を確認してください。", "hostKeyRejected": "SSHホストキーの検証が拒否されました。接続がキャンセルされました。", - "sessionTakenOver": "セッションは別のタブで開かれました。再接続しています...", + "sessionTakenOver": "セッションが別のタブで開かれました。再接続中...", "split": { - "splitTab": "分割タブ", + "splitTab": "タブを分割", "addToSplit": "分割に追加", "removeFromSplit": "分割から削除" } }, "fileManager": { "noHostSelected": "ホストが選択されていません", - "initializingEditor": "エディタを初期化しています...", + "initializingEditor": "エディターを初期化中...", "file": "ファイル", - "folder": "フォルダ", - "uploadFile": "ファイルのアップロード", + "folder": "フォルダー", + "uploadFile": "ファイルをアップロード", "downloadFile": "ダウンロード", - "extractArchive": "アーカイブを展開する", - "extractingArchive": "{{name}} を抽出中...", - "archiveExtractedSuccessfully": "{{name}} 抽出に成功しました", - "extractFailed": "抽出に失敗しました", - "compressFile": "圧縮ファイル", - "compressFiles": "ファイルを圧縮する", - "compressFilesDesc": "{{count}} 個のアイテムをアーカイブに圧縮します", + "extractArchive": "アーカイブを展開", + "extractingArchive": "{{name}} を展開中...", + "archiveExtractedSuccessfully": "{{name}} の展開が完了しました", + "extractFailed": "展開に失敗しました", + "compressFile": "ファイルを圧縮", + "compressFiles": "ファイルを圧縮", + "compressFilesDesc": "{{count}} 個の項目をアーカイブに圧縮", "archiveName": "アーカイブ名", - "enterArchiveName": "アーカイブ名を入力してください...", + "enterArchiveName": "アーカイブ名を入力...", "compressionFormat": "圧縮形式", - "selectedFiles": "選択されたファイル", - "andMoreFiles": "そして {{count}} もっと...", + "selectedFiles": "選択したファイル", + "andMoreFiles": "他 {{count}} 件...", "compress": "圧縮", - "compressingFiles": "{{count}} 個のアイテムを {{name}}に圧縮しています...", + "compressingFiles": "{{count}} 個の項目を {{name}} に圧縮中...", "filesCompressedSuccessfully": "{{name}} が正常に作成されました", "compressFailed": "圧縮に失敗しました", "edit": "編集", "preview": "プレビュー", - "previous": "前の", - "next": "次", - "pageXOfY": "ページ {{current}} / {{total}}", - "zoomOut": "ズームアウト", - "zoomIn": "ズームイン", - "newFile": "新しいファイル", - "newFolder": "新しいフォルダー", - "rename": "名前を変更する", + "previous": "前へ", + "next": "次へ", + "pageXOfY": "{{current}} / {{total}} ページ", + "zoomOut": "縮小", + "zoomIn": "拡大", + "newFile": "新規ファイル", + "newFolder": "新規フォルダ", + "rename": "名前を変更", "uploading": "アップロード中...", "uploadingFile": "{{name}} をアップロード中...", "fileName": "ファイル名", "folderName": "フォルダ名", - "fileUploadedSuccessfully": "ファイル \"{{name}}\" のアップロードに成功しました", + "fileUploadedSuccessfully": "ファイル \"{{name}}\" のアップロードが完了しました", "failedToUploadFile": "ファイルのアップロードに失敗しました", - "fileDownloadedSuccessfully": "ファイル \"{{name}}\" が正常にダウンロードされました。", + "fileDownloadedSuccessfully": "ファイル \"{{name}}\" のダウンロードが完了しました", "failedToDownloadFile": "ファイルのダウンロードに失敗しました", "fileCreatedSuccessfully": "ファイル \"{{name}}\" が正常に作成されました", "folderCreatedSuccessfully": "フォルダ \"{{name}}\" が正常に作成されました", "failedToCreateItem": "アイテムの作成に失敗しました", - "operationFailed": "{{operation}} の操作が {{name}}に対して失敗しました: {{error}}", + "operationFailed": "{{name}} の {{operation}} 操作が失敗しました: {{error}}", "failedToResolveSymlink": "シンボリックリンクの解決に失敗しました", - "itemsDeletedSuccessfully": "{{count}} アイテムが正常に削除されました", + "itemsDeletedSuccessfully": "{{count}} 個のアイテムが正常に削除されました", "failedToDeleteItems": "アイテムの削除に失敗しました", "sudoPasswordRequired": "管理者パスワードが必要です", - "enterSudoPassword": "この操作を続行するには、sudo パスワードを入力してください。", - "sudoPassword": "sudo パスワード", - "sudoOperationFailed": "sudo操作が失敗しました", - "sudoAuthFailed": "sudo認証に失敗しました", - "dragFilesToUpload": "ファイルをここにドロップしてアップロードしてください", - "emptyFolder": "このフォルダは空です", - "searchFiles": "ファイルを検索中...", + "enterSudoPassword": "操作を続行するには sudo パスワードを入力してください", + "sudoPassword": "Sudo パスワード", + "sudoOperationFailed": "Sudo 操作に失敗しました", + "sudoAuthFailed": "Sudo 認証に失敗しました", + "dragFilesToUpload": "ここにファイルをドロップしてアップロード", + "emptyFolder": "このフォルダーは空です", + "searchFiles": "ファイルを検索...", "upload": "アップロード", "selectHostToStart": "ファイル管理を開始するホストを選択してください", - "sshRequiredForFileManager": "ファイルマネージャーにはSSHが必要です。このホストではSSHが有効になっていません。", - "failedToConnect": "SSHへの接続に失敗しました", + "sshRequiredForFileManager": "ファイルマネージャーには SSH が必要です。このホストでは SSH が有効になっていません。", + "failedToConnect": "SSH への接続に失敗しました", "failedToLoadDirectory": "ディレクトリの読み込みに失敗しました", - "noSSHConnection": "SSH接続が利用できません", + "noSSHConnection": "利用可能な SSH 接続がありません", "copy": "コピー", - "cut": "カット", - "paste": "ペースト", + "cut": "切り取り", + "paste": "貼り付け", "copyPath": "パスをコピー", - "copyPaths": "パスをコピーする", - "delete": "消去", - "properties": "物件", + "copyPaths": "パスをコピー", + "delete": "削除", + "properties": "プロパティ", "refresh": "更新", - "downloadFiles": "{{count}} ファイルをブラウザにダウンロード", - "copyFiles": "{{count}} 個のアイテムをコピーします。", - "cutFiles": "{{count}} 個のアイテムを切り取ります。", - "deleteFiles": "{{count}} 個のアイテムを削除します。", + "downloadFiles": "{{count}} 個のファイルをブラウザにダウンロード", + "copyFiles": "{{count}} 個のアイテムをコピー", + "cutFiles": "{{count}} 個のアイテムを切り取り", + "deleteFiles": "{{count}} 個のアイテムを削除", "filesCopiedToClipboard": "{{count}} 個のアイテムがクリップボードにコピーされました", - "filesCutToClipboard": "{{count}} アイテムがクリップボードに切り取られました", + "filesCutToClipboard": "{{count}} 個のアイテムがクリップボードに切り取られました", "pathCopiedToClipboard": "パスがクリップボードにコピーされました", - "pathsCopiedToClipboard": "{{count}} パスがクリップボードにコピーされました", + "pathsCopiedToClipboard": "{{count}} 個のパスがクリップボードにコピーされました", "failedToCopyPath": "パスをクリップボードにコピーできませんでした", "copyFolderLink": "フォルダへのリンクをコピー", - "copyCurrentFolderLink": "現在のフォルダーへのリンクをコピー", - "folderLinkCopied": "フォルダリンクがクリップボードにコピーされました", - "failedToCopyFolderLink": "フォルダリンクのコピーに失敗しました", + "copyCurrentFolderLink": "現在のフォルダへのリンクをコピー", + "folderLinkCopied": "フォルダリンクをクリップボードにコピーしました", + "failedToCopyFolderLink": "フォルダリンクをコピーできませんでした", "movedItems": "{{count}} 個のアイテムを移動しました", - "failedToDeleteItem": "アイテムの削除に失敗しました", - "itemRenamedSuccessfully": "{{type}} は正常に名前変更されました", - "failedToRenameItem": "アイテムの名前変更に失敗しました", + "failedToDeleteItem": "アイテムを削除できませんでした", + "itemRenamedSuccessfully": "{{type}} の名前を変更しました", + "failedToRenameItem": "アイテムの名前を変更できませんでした", "download": "ダウンロード", - "openExternalEditor": "外部に開く", - "chooseExternalEditor": "外部エディターを選択", - "externalEditorSelected": "外部エディターが選択されました", - "externalEditorDesktopOnly": "外部エディターはデスクトップアプリでのみ利用可能です", - "externalEditorOpened": "外部エディタで開きました。そこで保存すると、変更内容がサーバーにアップロードされます。", - "failedToOpenExternalEditor": "外部エディタを開くのに失敗しました", - "failedToSelectExternalEditor": "外部エディターの選択に失敗しました", - "permissions": "権限", + "openExternalEditor": "外部エディタで開く", + "chooseExternalEditor": "外部エディタを選択", + "externalEditorSelected": "外部エディタを選択しました", + "externalEditorDesktopOnly": "外部エディタはデスクトップアプリでのみ利用可能です", + "externalEditorOpened": "外部エディタで開きました。保存すると変更がサーバーにアップロードされます。", + "failedToOpenExternalEditor": "外部エディタを開けませんでした", + "failedToSelectExternalEditor": "外部エディタを選択できませんでした", + "permissions": "アクセス権限", "size": "サイズ", - "modified": "修正済み", + "modified": "更新日時", "path": "パス", - "confirmDelete": "{{name}} を削除してもよろしいですか?", - "permissionDenied": "許可が拒否されました", + "confirmDelete": "本当に {{name}} を削除しますか?", + "permissionDenied": "アクセスが拒否されました", "serverError": "サーバーエラー", - "fileSavedSuccessfully": "ファイルが正常に保存されました", - "failedToSaveFile": "ファイルの保存に失敗しました", - "confirmDeleteSingleItem": "「{{name}}」を完全に削除してもよろしいですか?", + "fileSavedSuccessfully": "ファイルを保存しました", + "failedToSaveFile": "ファイルを保存できませんでした", + "confirmDeleteSingleItem": "\"{{name}}\" を完全に削除してもよろしいですか?", "confirmDeleteMultipleItems": "{{count}} 個のアイテムを完全に削除してもよろしいですか?", - "confirmDeleteMultipleItemsWithFolders": "{{count}} 個のアイテムを完全に削除してもよろしいですか?これにはフォルダとその内容も含まれます。", - "confirmDeleteFolder": "フォルダ「{{name}}」とその中のすべてのコンテンツを完全に削除してもよろしいですか?", - "permanentDeleteWarning": "この操作は元に戻せません。該当アイテムはサーバーから完全に削除されます。", - "recent": "最近の", + "confirmDeleteMultipleItemsWithFolders": "{{count}} 個のアイテムを完全に削除してもよろしいですか?フォルダとその内容も含まれます。", + "confirmDeleteFolder": "フォルダ \"{{name}}\" とその内容をすべて完全に削除してもよろしいですか?", + "permanentDeleteWarning": "この操作は元に戻せません。アイテムはサーバーから完全に削除されます。", + "recent": "最近使用した項目", "pinned": "ピン留め", - "folderShortcuts": "フォルダーショートカット", - "failedToReconnectSSH": "SSHセッションへの再接続に失敗しました", + "folderShortcuts": "フォルダショートカット", + "failedToReconnectSSH": "SSH セッションに再接続できませんでした", "openTerminalHere": "ここでターミナルを開く", - "run": "走る", + "run": "実行", "openTerminalInFolder": "このフォルダでターミナルを開く", - "openTerminalInFileLocation": "ファイルの場所にあるターミナルを開く", + "openTerminalInFileLocation": "ファイルの場所でターミナルを開く", "runningFile": "実行中 - {{file}}", "onlyRunExecutableFiles": "実行可能ファイルのみ実行できます", "directories": "ディレクトリ", - "removedFromRecentFiles": "最近使用したファイルから「{{name}}」を削除しました", + "removedFromRecentFiles": "最近使用したファイルから \"{{name}}\" を削除しました", "removeFailed": "削除に失敗しました", - "unpinnedSuccessfully": "「{{name}}」のピン留めが解除されました。", + "unpinnedSuccessfully": "\"{{name}}\" のピン留めを解除しました", "unpinFailed": "ピン留め解除に失敗しました", - "removedShortcut": "ショートカット「{{name}}」を削除しました", + "removedShortcut": "ショートカット \"{{name}}\" を削除しました", "removeShortcutFailed": "ショートカットの削除に失敗しました", - "clearedAllRecentFiles": "最近使用したファイルをすべて削除しました", - "clearFailed": "クリアに失敗しました", + "clearedAllRecentFiles": "最近使用したファイルをすべて消去しました", + "clearFailed": "消去に失敗しました", "removeFromRecentFiles": "最近使用したファイルから削除", - "clearAllRecentFiles": "最近使用したファイルをすべてクリアする", - "unpinFile": "ファイルのピン留めを解除する", + "clearAllRecentFiles": "最近使用したファイルをすべて消去", + "unpinFile": "ファイルのピン留めを解除", "removeShortcut": "ショートカットを削除", - "pinFile": "ピンファイル", + "pinFile": "ファイルをピン留め", "addToShortcuts": "ショートカットに追加", "pasteFailed": "貼り付けに失敗しました", - "noUndoableActions": "取り消し可能な操作はありません", - "undoCopySuccess": "コピー操作を取り消しました: {{count}} 個のコピーされたファイルが削除されました", - "undoCopyFailedDelete": "元に戻す操作に失敗しました: コピーしたファイルを削除できませんでした", - "undoCopyFailedNoInfo": "元に戻す操作に失敗しました: コピーされたファイルの情報が見つかりませんでした", - "undoMoveSuccess": "移動操作を取り消しました: {{count}} 個のファイルを元の場所に戻しました", - "undoMoveFailedMove": "元に戻す操作に失敗しました: ファイルを元に戻すことができませんでした", - "undoMoveFailedNoInfo": "元に戻す操作に失敗しました: 移動したファイルの情報が見つかりませんでした", - "undoDeleteNotSupported": "削除操作は取り消せません:ファイルはサーバーから完全に削除されました", - "undoTypeNotSupported": "サポートされていない取り消し操作タイプ", - "undoOperationFailed": "元に戻す操作が失敗しました", + "noUndoableActions": "元に戻せる操作はありません", + "undoCopySuccess": "コピー操作を元に戻しました: {{count}} 個のコピーファイルを削除しました", + "undoCopyFailedDelete": "元に戻せませんでした: コピーファイルを削除できませんでした", + "undoCopyFailedNoInfo": "元に戻せませんでした: コピーファイル情報が見つかりませんでした", + "undoMoveSuccess": "移動操作を元に戻しました: {{count}} 個のファイルを元の場所に戻しました", + "undoMoveFailedMove": "元に戻せませんでした: ファイルを元の場所に戻せませんでした", + "undoMoveFailedNoInfo": "元に戻せませんでした: 移動ファイル情報が見つかりませんでした", + "undoDeleteNotSupported": "削除操作は元に戻せません: ファイルはサーバーから完全に削除されました", + "undoTypeNotSupported": "元に戻せない操作タイプです", + "undoOperationFailed": "元に戻す操作に失敗しました", "unknownError": "不明なエラー", - "confirm": "確認する", - "find": "探す...", - "replace": "交換する", - "downloadInstead": "ダウンロードする", + "confirm": "確認", + "find": "検索...", + "replace": "置換", + "downloadInstead": "代わりにダウンロード", "keyboardShortcuts": "キーボードショートカット", "searchAndReplace": "検索と置換", "editing": "編集", "search": "検索", - "findNext": "次の検索", - "findPrevious": "前の項目を探す", + "findNext": "次を検索", + "findPrevious": "前を検索", "save": "保存", "selectAll": "すべて選択", "undo": "元に戻す", "redo": "やり直し", - "moveLineUp": "ラインナップを移動", - "moveLineDown": "ラインを下へ移動", - "toggleComment": "コメントの切り替え", - "autoComplete": "オートコンプリート", + "moveLineUp": "行を上に移動", + "moveLineDown": "行を下に移動", + "toggleComment": "コメント行の切り替え", + "autoComplete": "自動補完", "imageLoadError": "画像の読み込みに失敗しました", - "startTyping": "入力を開始してください...", + "startTyping": "入力を開始...", "unknownSize": "サイズ不明", "fileIsEmpty": "ファイルは空です", - "largeFileWarning": "大容量ファイルに関する警告", - "largeFileWarningDesc": "このファイルのサイズは {{size}} であるため、テキストとして開くとパフォーマンスの問題が発生する可能性があります。", - "fileNotFoundAndRemoved": "ファイル \"{{name}}\" が見つからなかったため、最近使用したファイル/ピン留めされたファイルから削除されました", + "largeFileWarning": "大きなファイルの警告", + "largeFileWarningDesc": "このファイルのサイズは{{size}}で、テキストとして開くとパフォーマンスの問題が発生する可能性があります。", + "fileNotFoundAndRemoved": "ファイル \"{{name}}\" が見つからないため、最近使ったファイル/ピン留めから削除されました。", "failedToLoadFile": "ファイルの読み込みに失敗しました: {{error}}", - "serverErrorOccurred": "サーバーエラーが発生しました。しばらくしてからもう一度お試しください。", + "serverErrorOccurred": "サーバーエラーが発生しました。後でもう一度お試しください。", "autoSaveFailed": "自動保存に失敗しました", - "fileAutoSaved": "ファイルは自動保存されました", - "moveFileFailed": "移動に失敗しました {{name}}", - "moveOperationFailed": "移動操作が失敗しました", - "canOnlyCompareFiles": "2つのファイルしか比較できません", - "comparingFiles": "ファイルの比較: {{file1}} と {{file2}}", - "dragFailed": "ドラッグ操作が失敗しました", - "filePinnedSuccessfully": "ファイル \"{{name}}\" が正常に固定されました", + "fileAutoSaved": "ファイルを自動保存しました", + "moveFileFailed": "{{name}} の移動に失敗しました", + "moveOperationFailed": "移動操作に失敗しました", + "canOnlyCompareFiles": "2つのファイルのみ比較できます", + "comparingFiles": "ファイルを比較中: {{file1}} と {{file2}}", + "dragFailed": "ドラッグ操作に失敗しました", + "filePinnedSuccessfully": "ファイル \"{{name}}\" をピン留めしました", "pinFileFailed": "ファイルのピン留めに失敗しました", - "fileUnpinnedSuccessfully": "ファイル \"{{name}}\" のピン留めが正常に解除されました。", + "fileUnpinnedSuccessfully": "ファイル \"{{name}}\" のピン留めを解除しました", "unpinFileFailed": "ファイルのピン留め解除に失敗しました", - "shortcutAddedSuccessfully": "フォルダショートカット \"{{name}}\" が正常に追加されました", + "shortcutAddedSuccessfully": "フォルダショートカット \"{{name}}\" を追加しました", "addShortcutFailed": "ショートカットの追加に失敗しました", - "operationCompletedSuccessfully": "{{operation}} {{count}} アイテムが正常に動作しました", - "operationCompleted": "{{operation}} {{count}} アイテム", - "downloadFileSuccess": "ファイル {{name}} が正常にダウンロードされました", + "operationCompletedSuccessfully": "{{count}} アイテムを正常に{{operation}}しました", + "operationCompleted": "{{count}} アイテムを{{operation}}しました", + "downloadFileSuccess": "ファイル {{name}} のダウンロードが完了しました", "downloadFileFailed": "ダウンロードに失敗しました", "moveTo": "{{name}} に移動", - "diffCompareWith": "{{name}} との差分比較", - "dragOutsideToDownload": "ダウンロードするには、ウィンドウの外にドラッグしてください({{count}} ファイル)。", - "newFolderDefault": "新しいフォルダー", - "newFileDefault": "新しいファイル.txt", - "successfullyMovedItems": "{{count}} 個のアイテムを {{target}}に正常に移動しました。", - "move": "動く", - "searchInFile": "ファイル内検索(Ctrl+F)", - "showKeyboardShortcuts": "キーボードショートカットを表示する", - "decreaseFontSize": "フォントサイズを小さくする", - "increaseFontSize": "フォントサイズを大きくする", - "startWritingMarkdown": "マークダウンコンテンツの作成を開始しましょう...", - "loadingFileComparison": "ファイル比較を読み込んでいます...", - "reload": "リロード", - "compare": "比較する", - "sideBySide": "並べて", - "inline": "列をなして", - "fileComparison": "ファイル比較: {{file1}} vs {{file2}}", - "fileTooLarge": "ファイルサイズが大きすぎます: {{error}}", - "sshConnectionFailed": "SSH接続に失敗しました。 {{name}} ({{ip}}:{{port}} )への接続を確認してください。", + "diffCompareWith": "{{name}} と差分比較", + "dragOutsideToDownload": "ウィンドウ外にドラッグしてダウンロード ({{count}} ファイル)", + "newFolderDefault": "新規フォルダ", + "newFileDefault": "新規ファイル.txt", + "successfullyMovedItems": "{{count}} アイテムを {{target}} に移動しました", + "move": "移動", + "searchInFile": "ファイル内検索 (Ctrl+F)", + "showKeyboardShortcuts": "キーボードショートカットを表示", + "decreaseFontSize": "フォントサイズを縮小", + "increaseFontSize": "フォントサイズを拡大", + "startWritingMarkdown": "マークダウンコンテンツの作成を開始...", + "loadingFileComparison": "ファイル比較を読み込み中...", + "reload": "再読み込み", + "compare": "比較", + "sideBySide": "横並び表示", + "inline": "インライン表示", + "fileComparison": "ファイル比較: {{file1}} 対 {{file2}}", + "fileTooLarge": "ファイルが大きすぎます: {{error}}", + "sshConnectionFailed": "SSH接続に失敗しました。 {{name}} ({{ip}}:{{port}}) への接続を確認してください", "loadFileFailed": "ファイルの読み込みに失敗しました: {{error}}", "connecting": "接続中...", - "connectedSuccessfully": "接続に成功しました", - "totpVerificationFailed": "TOTP認証に失敗しました", - "warpgateVerificationFailed": "ワープゲート認証に失敗しました", + "connectedSuccessfully": "接続しました", + "totpVerificationFailed": "TOTP検証に失敗しました", + "warpgateVerificationFailed": "Warpgate認証に失敗しました", "authenticationFailed": "認証に失敗しました", "incorrectPassphrase": "パスフレーズが間違っています。もう一度お試しください。", - "verificationCodePrompt": "検証コード:", - "changePermissions": "権限を変更する", - "currentPermissions": "現在の権限", + "verificationCodePrompt": "認証コード:", + "changePermissions": "パーミッション変更", + "currentPermissions": "現在のパーミッション", "owner": "所有者", "group": "グループ", "others": "その他", - "read": "読む", - "write": "書く", - "execute": "実行する", - "permissionsChangedSuccessfully": "権限の変更に成功しました", - "failedToChangePermissions": "権限の変更に失敗しました", + "read": "読み取り", + "write": "書き込み", + "execute": "実行", + "permissionsChangedSuccessfully": "パーミッションが正常に変更されました", + "failedToChangePermissions": "パーミッションの変更に失敗しました", "name": "名前", "sortByName": "名前", - "sortByDate": "更新日", + "sortByDate": "更新日時", "sortBySize": "サイズ", - "ascending": "上昇", - "descending": "下降", - "root": "根", - "new": "新しい", + "ascending": "昇順", + "descending": "降順", + "root": "ルート", + "new": "新規", "sortBy": "並べ替え", - "items": "商品", - "selected": "選ばれた", - "editor": "エディタ", + "items": "アイテム", + "selected": "選択中", + "editor": "エディター", "octal": "8進数", "storage": "ストレージ", "disk": "ディスク", - "used": "使用済み", + "used": "使用量", "of": "の", - "toggleSidebar": "サイドバーの表示/非表示を切り替える", - "cannotLoadPdf": "PDFファイルを読み込めません", - "pdfLoadError": "PDFファイルの読み込み中にエラーが発生しました。", - "loadingPdf": "PDFを読み込んでいます...", - "loadingPage": "ページを読み込んでいます..." + "toggleSidebar": "サイドバー切り替え", + "cannotLoadPdf": "PDFを読み込めません", + "pdfLoadError": "このPDFファイルの読み込み中にエラーが発生しました。", + "loadingPdf": "PDFを読み込み中...", + "loadingPage": "ページを読み込み中..." }, "transfer": { - "copyToHost": "ホストにコピー…", - "moveToHost": "ホストに移動…", - "copyItemsToHost": "{{count}} 個のアイテムをホスト…にコピーします。", - "moveItemsToHost": "{{count}} 個のアイテムをホスト…に移動します。", - "noHostsConnected": "他に利用可能なファイルマネージャーホストはありません。", - "noHostsConnectedHint": "ホストマネージャで、ファイルマネージャが有効になっている別のSSHホストを追加します。", - "selectDestinationHost": "宛先ホストを選択してください", - "destinationPath": "目的地への経路", - "recentDestinations": "最近の旅行先", - "collapseRecentDestinations": "最近訪れた場所を折りたたむ", - "expandRecentDestinations": "最近の目的地を拡大", - "browseFolders": "保存先フォルダを参照する", - "browseDestination": "パスを参照または入力してください", + "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": "転送完了", + "confirmMove": "移動", + "transferring": "転送中…", + "compressing": "圧縮中…", + "extracting": "展開中…", + "transferringItems": "{{total}} 個中 {{current}} 個のアイテムを転送中…", + "transferSuccess": "転送が完了しました", "transferError": "転送に失敗しました", - "transferPartial": "転送は {{count}} 個のエラーで完了しました。", + "transferPartial": "{{count}} 件のエラーで転送が完了しました", "transferPartialHint": "転送できませんでした: {{paths}}", "itemsSummary": "{{count}} アイテム", - "destMustBeDirectory": "宛先は複数アイテム転送用のディレクトリである必要があります。", - "selectThisFolder": "このフォルダーを選択してください", - "browsePathWillBeCreated": "このフォルダはまだ存在しません。転送が開始されると作成されます。", - "browsePathError": "宛先ホストでこのパスを開くことができませんでした。", + "destMustBeDirectory": "複数アイテムの転送では転送先はディレクトリである必要があります", + "selectThisFolder": "このフォルダを選択", + "browsePathWillBeCreated": "このフォルダはまだ存在しません。転送開始時に作成されます。", + "browsePathError": "転送先ホストでこのパスを開けませんでした。", "goUp": "上へ", - "copyFolderToHost": "フォルダをホストにコピーします…", + "copyFolderToHost": "フォルダをホストにコピー…", "moveFolderToHost": "フォルダをホストに移動…", - "hostReady": "準備ができて", + "hostReady": "準備完了", "hostConnecting": "接続中…", - "hostDisconnected": "接続されていません", + "hostDisconnected": "未接続", "hostAuthRequired": "認証が必要です — まずこのホストでファイルマネージャを開いてください", "hostConnectionFailed": "接続に失敗しました", - "metricsTitle": "転送時間", - "metricsPrepare": "宛先を準備します: {{duration}}", - "metricsCompress": "ソースで圧縮: {{duration}}", - "metricsHopSourceRead": "ソース → サーバー: {{throughput}}", - "metricsHopDestSftpWrite": "サーバー → 宛先 (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "サーバー → 宛先 (ローカル): {{throughput}}", + "metricsTitle": "転送タイミング", + "metricsPrepare": "転送先の準備: {{duration}}", + "metricsCompress": "送信元での圧縮: {{duration}}", + "metricsHopSourceRead": "送信元 → サーバー: {{throughput}}", + "metricsHopDestSftpWrite": "サーバー → 送信先 (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "サーバー → 送信先 (ローカル): {{throughput}}", "metricsTransfer": "エンドツーエンド: {{throughput}} ({{duration}})", - "metricsExtract": "抽出先: {{duration}}", - "metricsSourceDelete": "ソースから削除: {{duration}}", + "metricsExtract": "送信先で展開: {{duration}}", + "metricsSourceDelete": "送信元から削除: {{duration}}", "metricsTotal": "合計: {{duration}}", - "progressCompressing": "ソースホスト上で圧縮中…", - "progressExtracting": "抽出先… で抽出中", + "progressCompressing": "ソースホストで圧縮中…", + "progressExtracting": "送信先で展開中…", "progressTransferring": "データ転送中…", "progressReconnecting": "再接続中…", - "parallelSegmentsLabel": "並行する乗り換えレーン", - "parallelSegmentsOption": "{{count}} 車線", - "parallelSegmentsHint": "大きなファイルは256MBずつに分割されます。複数のレーンはそれぞれ別の接続(複数の転送を開始するようなもの)を使用することで、全体の処理能力を高めています。", - "progressTotalSpeed": "{{speed}} 合計 ({{lanes}} 車線)", - "progressTransferringItems": "ファイル転送中({{current}} / {{total}})…", + "parallelSegmentsLabel": "並列転送レーン", + "parallelSegmentsOption": "{{count}} レーン", + "parallelSegmentsHint": "大きなファイルは 256 MB のチャンクに分割されます。複数レーンでは個別の接続を使用し (複数の転送を開始するように)、合計スループットが向上します。", + "progressTotalSpeed": "合計 {{speed}} ({{lanes}} レーン)", + "progressTransferringItems": "ファイル転送中 ({{current}} / {{total}})…", "progressBytes": "{{transferred}} / {{total}}", "progressItems": "{{current}} / {{total}} ファイル", - "sourceNotDeletedPartial": "ソースファイルは保持されます(部分転送)", - "jumpHostLimitation": "両方のホストはTermixサーバーからアクセス可能である必要があります。ホスト間の直接ルーティングはサポートされていません。", + "sourceNotDeletedPartial": "ソースファイルを保持 (部分転送)", + "jumpHostLimitation": "両方のホストが Termix サーバーから到達可能である必要があります。ホスト間直接ルーティングはサポートされていません。", "cancel": "キャンセル", - "methodLabel": "転送方法", + "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ストリームを順番に使用します。進行状況はすべてのファイルで合算されるため、大きなファイルの場合はプログレスバーの動きが遅くなります。", + "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 ストリームを順次使用します。進捗は全ファイルで統合されるため、大きなファイルではバーの動きが遅くなります。", "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": "ファイルごとの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です。" }, "progressCancel": "キャンセル", - "progressCancelling": "… をキャンセルしています。", - "progressStalled": "停滞", + "progressCancelling": "キャンセル中…", + "progressStalled": "停滞中", "resumedHint": "別のウィンドウで開始されたアクティブな転送に再接続しました。", "transferCancelled": "転送がキャンセルされました", - "transferCancelledCopyHint": "宛先に一部のファイルが残る場合があります。", - "transferCancelledMoveHint": "宛先に一部ファイルが残る場合があります。元のファイルは削除されませんでした。", - "cleanupDestFiles": "清掃場所", + "transferCancelledCopyHint": "部分的なファイルが宛先に残る可能性があります。", + "transferCancelledMoveHint": "部分的なファイルが宛先に残る可能性があります。ソースファイルは削除されていません。", + "cleanupDestFiles": "宛先をクリーンアップ", "cleanupDestFilesSuccess": "宛先から部分的なファイルを削除しました", - "cleanupDestFilesPartial": "一部ファイルが削除できませんでした", - "cleanupDestFilesNothing": "目的地では何も片付ける必要はありません", + "cleanupDestFilesPartial": "一部の部分的なファイルを削除できませんでした", + "cleanupDestFilesNothing": "宛先にクリーンアップするものはありません", "cleanupDestFilesError": "クリーンアップに失敗しました", - "retryTransfer": "リトライ", + "retryTransfer": "再試行", "retryTransferError": "再試行に失敗しました", - "transferFailedRetryHint": "宛先に一部のデータが保存されました。接続が回復次第、再試行を再開します。" + "transferFailedRetryHint": "部分的なデータが宛先に保持されました。接続が復旧したら再試行が再開されます。" }, "tunnels": { - "noSshTunnels": "SSHトンネルなし", - "createFirstTunnelMessage": "SSHトンネルがまだ作成されていません。まずはホストマネージャーでトンネル接続を設定してください。", + "noSshTunnels": "SSHトンネルがありません", + "createFirstTunnelMessage": "まだSSHトンネルが作成されていません。開始するにはホストマネージャーでトンネル接続を設定してください。", "connected": "接続済み", - "disconnected": "接続が切断されました", + "disconnected": "切断済み", "connecting": "接続中...", "error": "エラー", "canceling": "キャンセル中...", - "connect": "接続する", + "connect": "接続", "disconnect": "切断", "cancel": "キャンセル", "port": "ポート", - "localPort": "地元の港", + "localPort": "ローカルポート", "remotePort": "リモートポート", "currentHostPort": "現在のホストポート", "endpointPort": "エンドポイントポート", "bindIp": "ローカルIP", "endpointSshConfig": "エンドポイントSSH設定", "endpointSshHost": "エンドポイントSSHホスト", - "endpointSshHostPlaceholder": "設定済みのホストを選択してください", - "endpointSshHostRequired": "クライアントトンネルごとに、エンドポイントとなるSSHホストを選択してください。", - "attempt": "試行回数 {{current}} / {{max}}", - "nextRetryIn": "{{seconds}} 秒後に再度試行します。", + "endpointSshHostPlaceholder": "設定済みのホストを選択", + "endpointSshHostRequired": "各クライアントトンネルにエンドポイントSSHホストを選択してください。", + "attempt": "試行 {{current}} / {{max}}", + "nextRetryIn": "{{seconds}} 秒後に再試行", "clientTunnels": "クライアントトンネル", "clientTunnel": "クライアントトンネル", - "addClientTunnel": "クライアントトンネルを追加する", + "addClientTunnel": "クライアントトンネルを追加", "noClientTunnels": "このデスクトップにはクライアントトンネルが設定されていません。", "tunnelName": "トンネル名", "remoteHost": "リモートホスト", - "autoStart": "自動スタート", - "clientAutoStartDesc": "このデスクトップクライアントが起動すると開始され、接続状態が維持されます。", - "clientManualStartDesc": "この行から「開始」と「停止」を使用してください。Termixは自動的にこの行を開きません。", - "clientRemoteServerNote": "リモート転送には、エンドポイントのSSHサーバーでAllowTcpForwardingとGatewayPortsの設定が必要になる場合があります。このデスクトップが切断されると、リモートポートは閉じられます。", - "clientTunnelStarted": "クライアントトンネルが開始されました", + "autoStart": "自動起動", + "clientAutoStartDesc": "デスクトップクライアントが起動すると自動的に接続し、接続を維持します。", + "clientManualStartDesc": "この行の開始/停止を使用します。Termixは自動的に起動しません。", + "clientRemoteServerNote": "リモート転送にはエンドポイントSSHサーバーでAllowTcpForwardingとGatewayPortsが必要な場合があります。このデスクトップが切断されるとリモートポートは閉じます。", + "clientTunnelStarted": "クライアントトンネルが起動しました", "clientTunnelStopped": "クライアントトンネルが停止しました", - "tunnelTestSucceeded": "トンネル試験成功", + "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": "トンネルの状態更新に失敗しました。", + "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": "始める", + "start": "開始", "stop": "停止", "test": "テスト", - "type": "トンネルの種類", + "type": "トンネルタイプ", "typeLocal": "ローカル (-L)", - "typeRemote": "リモコン(-R)", - "typeDynamic": "ダイナミック(-D)", + "typeRemote": "リモート (-R)", + "typeDynamic": "ダイナミック (-D)", "typeServerLocalDesc": "現在のホストからエンドポイントへ。", - "typeServerRemoteDesc": "現在のホストへのエンドポイント。", - "typeClientLocalDesc": "ローカルコンピュータからエンドポイントへ。", - "typeClientRemoteDesc": "エンドポイントはローカルコンピュータに戻ります。", - "typeClientDynamicDesc": "ローカルコンピュータ上のSOCKS。", - "typeDynamicDesc": "SOCKS5 CONNECTトラフィックをSSH経由で転送する", + "typeServerRemoteDesc": "エンドポイントから現在のホストへ。", + "typeClientLocalDesc": "ローカルコンピューターからエンドポイントへ。", + "typeClientRemoteDesc": "エンドポイントからローカルコンピューターへ。", + "typeClientDynamicDesc": "ローカルコンピューター上のSOCKS。", + "typeDynamicDesc": "SSH経由でSOCKS5 CONNECTトラフィックを転送", "forwardDescriptionServerLocal": "現在のホスト {{sourcePort}} → エンドポイント {{endpointPort}}。", "forwardDescriptionServerRemote": "エンドポイント {{endpointPort}} → 現在のホスト {{sourcePort}}。", - "forwardDescriptionServerDynamic": "現在のホスト {{sourcePort}} の SOCKS 。", + "forwardDescriptionServerDynamic": "現在のホスト上のSOCKS {{sourcePort}}。", "forwardDescriptionClientLocal": "ローカル {{sourcePort}} → リモート {{endpointPort}}。", "forwardDescriptionClientRemote": "リモート {{sourcePort}} → ローカル {{endpointPort}}。", - "forwardDescriptionClientDynamic": "ローカルポート {{sourcePort}} 上の SOCKS 。", + "forwardDescriptionClientDynamic": "ローカルポート {{sourcePort}} 上のSOCKS。", "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}} via {{endpoint}}", - "route": "ルート:", - "lastStarted": "最終開始日時", - "lastTested": "最終テスト済み", - "lastError": "最後のエラー", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "ルート:", + "lastStarted": "最終起動日時", + "lastTested": "最終テスト日時", + "lastError": "最終エラー", "maxRetries": "最大再試行回数", - "maxRetriesDescription": "再試行の最大回数。", - "retryInterval": "再試行間隔(秒)", - "retryIntervalDescription": "再試行の間隔を空ける時間。", - "local": "地元", + "maxRetriesDescription": "再試行の最大試行回数。", + "retryInterval": "再試行間隔 (秒)", + "retryIntervalDescription": "再試行の間隔。", + "local": "ローカル", "remote": "リモート", - "destination": "行き先", + "destination": "宛先", "host": "ホスト", "mode": "モード", "noHostSelected": "ホストが選択されていません", - "working": "働く..." + "working": "処理中..." }, "cardGrid": { "dragToMove": "ドラッグして移動", - "dragToResize": "ドラッグしてサイズを変更", - "changeWidth": "幅を変更する", - "removeCard": "{{label}} を削除", + "dragToResize": "ドラッグしてサイズ変更", + "changeWidth": "幅を変更", + "removeCard": "{{label}}を削除", "addCard": "追加", - "columns": "コラム", - "empty": "カードはありません。カードを配置するには、下の「追加」を使用してください。" + "columns": "列", + "empty": "カードがありません。下の「追加」を使ってカードを配置してください。" }, "hostMetrics": { "cpu": "CPU", @@ -2003,256 +2136,256 @@ "uptime": "稼働時間", "processes": "プロセス", "available": "利用可能", - "free": "無料", + "free": "空き", "connecting": "接続中...", "connectionFailed": "サーバーへの接続に失敗しました", - "naCpus": "該当なし CPU", + "naCpus": "N/A CPU", "cpuCores_one": "{{count}} コア", "cpuCores_other": "{{count}} コア", "cpuUsage": "CPU使用率", - "memoryUsage": "メモリ使用量", - "diskUsage": "ディスク使用量", + "memoryUsage": "メモリ使用率", + "diskUsage": "ディスク使用率", "temperature": "温度", - "highestTemperature": "最高気温", - "failedToFetchHostConfig": "ホスト構成の取得に失敗しました", + "highestTemperature": "最高温度", + "failedToFetchHostConfig": "ホスト設定の取得に失敗しました", "serverOffline": "サーバーオフライン", - "cannotFetchMetrics": "オフラインサーバーからメトリックを取得できません", - "totpFailed": "TOTP認証に失敗しました", - "noneAuthNotSupported": "Host Metricsは「none」認証タイプをサポートしていません。", + "cannotFetchMetrics": "オフラインのサーバーからメトリクスを取得できません。", + "totpFailed": "TOTP検証に失敗しました", + "noneAuthNotSupported": "ホストメトリクスは「none」認証タイプをサポートしていません。", "noHostSelected": "ホストが選択されていません", "load": "負荷", "systemInfo": "システム情報", "hostname": "ホスト名", - "operatingSystem": "オペレーティング·システム", + "operatingSystem": "オペレーティングシステム", "kernel": "カーネル", "seconds": "秒", "networkInterfaces": "ネットワークインターフェース", "noInterfacesFound": "ネットワークインターフェースが見つかりません", - "noProcessesFound": "プロセスが見つかりませんでした", + "noProcessesFound": "プロセスが見つかりません", "processesTotal": "合計", - "processesRunning": "ランニング", + "processesRunning": "実行中", "loginStats": "SSHログイン統計", - "noRecentLoginData": "最近のログインデータはありません", + "noRecentLoginData": "最近のログインデータがありません", "executingQuickAction": "{{name}} を実行中...", - "quickActionSuccess": "{{name}} 正常に完了しました", - "quickActionFailed": "{{name}} 失敗", + "quickActionSuccess": "{{name}} が正常に完了しました", + "quickActionFailed": "{{name}} が失敗しました", "quickActionError": "{{name}} の実行に失敗しました", "ports": { "title": "リスニングポート", "protocol": "プロトコル", "port": "ポート", - "address": "住所", + "address": "アドレス", "process": "プロセス", - "search": "ポートを検索中...", - "allProtocols": "全て", - "noData": "リスニングポートデータなし" + "search": "ポートを検索...", + "allProtocols": "すべて", + "noData": "リスニングポートのデータがありません" }, "firewall": { "title": "ファイアウォール", "inactive": "非アクティブ", "policy": "ポリシー", "rules": "ルール", - "noRules": "このチェーンにはルールはありません", - "noData": "ファイアウォールデータは利用できません", + "noRules": "このチェーンにルールはありません", + "noData": "利用可能なファイアウォールデータがありません", "action": "アクション", - "protocol": "プロト", + "protocol": "プロトコル", "port": "ポート", "source": "ソース", - "anywhere": "どこでも", + "anywhere": "任意", "chains": "チェーン" }, "loadAvg": "ロードアベレージ", "swap": "スワップ", - "architecture": "建築", + "architecture": "アーキテクチャ", "refresh": "更新", - "retry": "リトライ", - "customize": "レイアウトをカスタマイズする", + "retry": "再試行", + "customize": "レイアウトをカスタマイズ", "reset": "リセット", "tabLive": "ライブ", - "editModeInstructions": "カードをドラッグして並べ替え、下端をドラッグしてサイズを変更し、幅ボタンを使用してカードの幅を変更できます。カードの追加または削除は下から行えます。", + "editModeInstructions": "カードをドラッグして並べ替え、下辺をドラッグしてサイズを変更し、幅ボタンでカードのスパンを変更します。下のボタンでカードを追加または削除してください。", "managers": { "services": "サービス", - "processInspector": "プロセス検査官", + "processInspector": "プロセスインスペクタ", "logViewer": "ログビューア", "cron": "Cronジョブ", "packages": "パッケージ", "ssl": "SSL証明書", "firewall": "ファイアウォール", "users": "ユーザーと権限", - "healthCheck": "健康診断", - "diskBreakdown": "ディスクの内訳", + "healthCheck": "ヘルスチェック", + "diskBreakdown": "ディスク内訳", "systemdTimers": "タイマー", - "topMemory": "トップ by Memory", + "topMemory": "メモリ使用量トップ", "noData": "データなし", - "sudoHint": "特権操作を有効にするには、ホストエディタでこのホストにsudoパスワードを設定してください。", + "sudoHint": "特権アクションを有効にするには、ホストエディタでこのホストのsudoパスワードを設定してください。", "filter": "フィルター...", - "start": "始める", + "start": "起動", "stop": "停止", "restart": "再起動", - "actionDone": "{{name}} 更新済み", - "actionFailed": "アクションが失敗しました", - "signalSent": "PID {{pid}}に信号が送信されました。", - "killHint": "クリック:終了(SIGTERM)。右クリック:強制終了(SIGKILL)。", - "working": "働く...", - "update": "アップデート", + "actionDone": "{{name}} が更新されました", + "actionFailed": "アクションに失敗しました", + "signalSent": "PID {{pid}} にシグナルが送信されました", + "killHint": "クリック: 終了 (SIGTERM)。右クリック: 強制終了 (SIGKILL)。", + "working": "処理中...", + "update": "更新", "upgradeAll": "すべてアップグレード", - "allUpToDate": "すべて最新の状態です", + "allUpToDate": "すべて最新です", "save": "保存", - "command": "指示", + "command": "コマンド", "enabled": "有効", - "cronSaved": "Crontabが更新されました", + "cronSaved": "crontab を更新しました", "clients": "クライアント", "dryRun": "ドライラン", "renew": "更新", - "noAcmeClient": "このホスト上にACMEクライアント(certbotまたはacme.sh)が見つかりませんでした。", - "addInputRule": "INPUTルールを追加する", - "firewallWarning": "変更は永続化されるまで実行時のみ有効です。誤ってアクセスできなくなることがないようご注意ください。", - "ruleApplied": "ルールが適用されました", - "invalidPort": "有効なポート番号(1~65535)を入力してください。", + "noAcmeClient": "このホストに ACME クライアント (certbot または acme.sh) が見つかりません。", + "addInputRule": "INPUT ルールを追加", + "firewallWarning": "変更は永続化されるまで実行中のみ有効です。自分をロックアウトしないよう注意してください。", + "ruleApplied": "ルールを適用しました", + "invalidPort": "有効なポート番号 (1-65535) を入力してください", "newUsername": "新しいユーザー名", "addUser": "追加", "deleteUser": "ユーザーを削除", "noHealthChecks": "ヘルスチェックはまだ設定されていません。", - "follow": "フォローする", - "noLogData": "ログ出力なし。", - "tree": "木", - "enableDisable": "起動時に有効/無効にする", - "grantSudo": "グラント・スド", - "revokeSudo": "sudo を取り消す", - "sslIssueCert": "証明書の発行", + "follow": "フォロー", + "noLogData": "ログ出力がありません。", + "tree": "ツリー", + "enableDisable": "起動時に有効/無効", + "grantSudo": "sudo 権限を付与", + "revokeSudo": "sudo 権限を剥奪", + "sslIssueCert": "証明書を発行", "sslExpired": "期限切れ", - "sslInDays": "{{days}}d で", + "sslInDays": "あと {{days}} 日", "sslNeedDomain": "少なくとも1つのドメインを入力してください", "sslIssued": "証明書が発行されました", - "sslDomainsPlaceholder": "example.com、www.example.com", - "sslHttpStandalone": "HTTP(スタンドアロン)", - "sslHttpWebroot": "HTTP(ウェブルート)", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (スタンドアロン)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNSプロバイダー(例:Cloudflare)", - "sslIssueHint": "ホストには、DNSプロバイダーの認証情報が既に設定されている必要があります。", - "sslRevoke": "証明書の失効", - "sslRevoked": "証明書が取り消されました", - "sslRevokeConfirm": "証明書「{{name}}」を失効させて削除しますか?これは元に戻せません。", - "healthRun": "走る", + "sslDnsProvider": "DNSプロバイダ(例: cloudflare)", + "sslIssueHint": "DNSプロバイダの認証情報はホストに設定済みである必要があります。", + "sslRevoke": "証明書を失効", + "sslRevoked": "証明書が失効しました", + "sslRevokeConfirm": "証明書 \"{{name}}\"を失効して削除しますか?この操作は取り消せません。", + "healthRun": "実行", "healthName": "名前", - "healthTarget": "ホスト/アドレス", + "healthTarget": "ホスト / アドレス", "healthAddCheck": "チェックを追加", - "healthSaved": "健康チェックが保存されました", - "healthMissingFields": "各チェックには名前と対象が必要です", + "healthSaved": "ヘルスチェックが保存されました", + "healthMissingFields": "各チェックには名前とターゲットが必要です", "logFile": "ファイル", "logUnit": "ユニット", - "logCustomPath": "/var/log 配下のカスタムパス(オプション)", - "logGrep": "フィルターライン...", - "firewallPersist": "ルールを貫く", - "firewallPersisted": "ファイアウォールルールが継続", - "wireguard": "ワイヤーガード", - "tailscale": "テールスケール", - "wgNotInstalled": "このホストにはWireGuardがインストールされていません", - "wgNoInterfaces": "WireGuardインターフェースは設定されていません", - "wgInterfaceUp": "育てる", - "wgInterfaceDown": "倒す", - "wgBringingUp": "{{name}} を呼び出しています...", - "wgBringingDown": "{{name}} をダウンさせています...", - "wgInterfaceUpDone": "{{name}} がアップしました", - "wgInterfaceDownDone": "{{name}} がダウンしています", + "logCustomPath": "/var/log 以下のカスタムパス (任意)", + "logGrep": "行をフィルタ...", + "firewallPersist": "ルールを永続化", + "firewallPersisted": "ファイアウォールルールが永続化されました", + "wireguard": "WireGuard", + "tailscale": "Tailscale", + "wgNotInstalled": "このホストにWireGuardがインストールされていません", + "wgNoInterfaces": "WireGuardインターフェースが設定されていません", + "wgInterfaceUp": "起動", + "wgInterfaceDown": "停止", + "wgBringingUp": "{{name}}を起動中...", + "wgBringingDown": "{{name}}を停止中...", + "wgInterfaceUpDone": "{{name}}が起動しました", + "wgInterfaceDownDone": "{{name}}が停止しました", "wgListenPort": "ポート", "wgPublicKey": "公開鍵", - "wgEndpoint": "終点", - "wgAllowedIPs": "許可されたIPアドレス", - "wgLastHandshake": "最後の握手", - "wgHandshakeNever": "一度もない", - "wgTransfer": "移行", - "tsNotInstalled": "このホストにはTailscaleがインストールされていません", - "tsRunning": "ランニング", - "tsStopped": "停止しました", - "tsEnable": "接続する", + "wgEndpoint": "エンドポイント", + "wgAllowedIPs": "許可されたIP", + "wgLastHandshake": "最終ハンドシェイク", + "wgHandshakeNever": "なし", + "wgTransfer": "転送量", + "tsNotInstalled": "このホストにTailscaleがインストールされていません", + "tsRunning": "実行中", + "tsStopped": "停止中", + "tsEnable": "接続", "tsDisable": "切断", "tsEnabling": "Tailscaleに接続中...", - "tsDisabling": "Tailscaleとの接続を切断しています...", - "tsEnabled": "テールスケール接続済み", - "tsDisabled": "テールスケールが外れています", - "tsIPs": "テールスケールIP", - "tsPeers": "仲間", + "tsDisabling": "Tailscaleから切断中...", + "tsEnabled": "Tailscale接続済み", + "tsDisabled": "Tailscale切断済み", + "tsIPs": "Tailscale IP", + "tsPeers": "ピア", "tsOnline": "オンライン", "tsOffline": "オフライン", - "tsExitNode": "出口ノード", - "tsExitNodeActive": "出口ノードがアクティブです", + "tsExitNode": "Exitノード", + "tsExitNodeActive": "Exitノード有効", "tsHostname": "ホスト名" } }, "auth": { - "tagline": "セルフホスト型のSSHおよびリモートデスクトップ管理", - "loginTitle": "おかえり", - "registerTitle": "アカウントを作成する", + "tagline": "セルフホストのSSH & リモートデスクトップ管理", + "loginTitle": "おかえりなさい", + "registerTitle": "アカウント作成", "forgotPassword": "パスワードをお忘れですか?", - "rememberMe": "デバイスを30日間記憶する(TOTPを含む)", - "noAccount": "アカウントをお持ちではありませんか?", - "hasAccount": "既にアカウントをお持ちですか?", - "twoFactorAuth": "二段階認証", - "enterCode": "確認コードを入力してください", - "backupCode": "またはバックアップコードを使用する", - "verifyCode": "確認コード", - "redirectingToApp": "アプリにリダイレクトしています...", + "rememberMe": "30日間このデバイスを記憶(TOTPを含む)", + "noAccount": "アカウントをお持ちでないですか?", + "hasAccount": "すでにアカウントをお持ちですか?", + "twoFactorAuth": "二要素認証", + "enterCode": "認証コードを入力", + "backupCode": "またはバックアップコードを使用", + "verifyCode": "認証コードを確認", + "redirectingToApp": "アプリにリダイレクト中...", "sshAuthenticationRequired": "SSH認証が必要です", - "sshNoKeyboardInteractive": "キーボードによる認証は利用できません", - "sshAuthenticationFailed": "認証失敗", + "sshNoKeyboardInteractive": "キーボードインタラクティブ認証は利用できません", + "sshAuthenticationFailed": "認証に失敗しました", "sshAuthenticationTimeout": "認証タイムアウト", - "sshNoKeyboardInteractiveDescription": "このサーバーはキーボードによる認証をサポートしていません。パスワードまたはSSHキーを入力してください。", - "sshAuthFailedDescription": "ご入力いただいた認証情報が間違っています。正しい認証情報で再度お試しください。", + "sshNoKeyboardInteractiveDescription": "サーバーはキーボードインタラクティブ認証をサポートしていません。パスワードまたはSSH鍵を入力してください。", + "sshAuthFailedDescription": "入力された認証情報が正しくありません。有効な認証情報でもう一度お試しください。", "sshTimeoutDescription": "認証試行がタイムアウトしました。もう一度お試しください。", - "sshProvideCredentialsDescription": "このサーバーに接続するには、SSH認証情報を入力してください。", + "sshProvideCredentialsDescription": "このサーバーに接続するためにSSH認証情報を入力してください。", "sshPasswordDescription": "このSSH接続のパスワードを入力してください。", - "sshKeyPasswordDescription": "SSHキーが暗号化されている場合は、ここにパスフレーズを入力してください。", + "sshKeyPasswordDescription": "SSHキーが暗号化されている場合、パスフレーズを入力してください。", "passphraseRequired": "パスフレーズが必要です", - "passphraseRequiredDescription": "SSHキーは暗号化されています。ロックを解除するにはパスフレーズを入力してください。", + "passphraseRequiredDescription": "SSHキーは暗号化されています。パスフレーズを入力してロックを解除してください。", "back": "戻る", "firstUser": "最初のユーザー", - "firstUserMessage": "あなたは最初のユーザーであり、管理者権限が付与されます。管理者設定は、サイドバーのユーザードロップダウンメニューから確認できます。もしこれが誤りだと思われる場合は、Dockerのログを確認するか、GitHubで問題を報告してください。", - "external": "外部の", + "firstUserMessage": "あなたは最初のユーザーで、管理者になります。管理者設定はサイドバーのユーザードロップダウンから確認できます。これが誤りだと思われる場合は、Dockerログを確認するか、GitHub issueを作成してください。", + "external": "外部", "loginWithExternal": "外部プロバイダーでログイン", - "loginWithExternalDesc": "設定済みの外部IDプロバイダーを使用してログインしてください。", + "loginWithExternalDesc": "設定された外部IDプロバイダーを使用してログイン", "externalNotSupportedInElectron": "Electronアプリではまだ外部認証はサポートされていません。OIDCログインにはWeb版をご利用ください。", - "loginWithProvider": "{{name}}でログインしてください。", - "orContinueWith": "または、", + "loginWithProvider": "{{name}}でログイン", + "orContinueWith": "または次で続行", "ldapUsername": "LDAPユーザー名", "ldapPassword": "LDAPパスワード", "ldapSignIn": "サインイン", "ldapLoginFailed": "LDAPログインに失敗しました", "resetPasswordButton": "パスワードをリセット", "sendResetCode": "リセットコードを送信", - "resetCodeDesc": "ユーザー名を入力してパスワードリセットコードを受け取ってください。コードはDockerコンテナのログに記録されます。", + "resetCodeDesc": "ユーザー名を入力して、パスワードリセットコードを受け取ります。コードはDockerコンテナログに記録されます。", "resetCode": "リセットコード", - "verifyCodeButton": "確認コード", - "enterResetCode": "ユーザーのDockerコンテナログから6桁のコードを入力してください。", + "verifyCodeButton": "コードを確認", + "enterResetCode": "Dockerコンテナログから、ユーザーの6桁のコードを入力してください:", "newPassword": "新しいパスワード", - "confirmNewPassword": "パスワードを認証する", - "enterNewPassword": "ユーザー用の新しいパスワードを入力してください:", + "confirmNewPassword": "パスワードを確認", + "enterNewPassword": "ユーザーの新しいパスワードを入力してください:", "signUp": "サインアップ", "desktopApp": "デスクトップアプリ", - "loggingInToDesktopApp": "デスクトップアプリにログインする", - "loadingServer": "サーバーを読み込んでいます...", - "dataLossWarning": "この方法でパスワードをリセットすると、保存されているすべてのSSHホスト、認証情報、およびその他の暗号化されたデータが削除されます。この操作は元に戻せません。パスワードを忘れてログインしていない場合にのみ使用してください。", - "authenticationDisabled": "認証無効", - "authenticationDisabledDesc": "現在、すべての認証方法が無効になっています。システム管理者にお問い合わせください。", - "passwordLoginDisabledDesc": "パスワードによるログインは無効になっています。パスキーまたは外部認証プロバイダーをご利用ください。", + "loggingInToDesktopApp": "デスクトップアプリにログインしています", + "loadingServer": "サーバーを読み込み中...", + "dataLossWarning": "この方法でパスワードをリセットすると、保存されているSSHホスト、認証情報、その他の暗号化データがすべて削除されます。この操作は元に戻せません。パスワードを忘れ、ログインしていない場合にのみ使用してください。", + "authenticationDisabled": "認証が無効です", + "authenticationDisabledDesc": "現在、すべての認証方法が無効になっています。管理者に連絡してください。", + "passwordLoginDisabledDesc": "パスワードログインは無効です。パスキーまたは外部認証プロバイダーを使用してください。", "signInWithPasskey": "パスキーでサインイン", "passkeyLoginFailed": "パスキーログインに失敗しました", - "attemptsRemaining": "{{count}} 残り試行回数", - "confirmResetDataWipe": "このアカウントは暗号化のアップグレード以降ログインしていないため、古いパスワードがないと保存されたデータを復元できません。リセットすると、ホスト、認証情報、スニペットが完全に削除されます。続行しますか?" + "attemptsRemaining": "残り{{count}}回", + "confirmResetDataWipe": "このアカウントは暗号化アップグレード以降ログインしていないため、旧パスワードがないと保存データを復旧できません。リセットすると、ホスト、認証情報、スニペットが完全に削除されます。続行しますか?" }, "hostKey": { - "verifyNewHost": "SSHホストキーを確認する", + "verifyNewHost": "SSHホストキーを確認", "keyChangedWarning": "SSHホストキーが変更されました", - "firstConnectionTitle": "このホストへの接続は初めてです", - "firstConnectionDescription": "このホストの信頼性を確認できません。フィンガープリントが想定どおりであることを確認してください。", - "keyChangedDescription": "このサーバーのホストキーは、前回の接続以降に変更されています。これはセキュリティ上の問題を示している可能性があります。", - "previousKey": "前のキー", - "newFingerprint": "新しい指紋", - "fingerprint": "指紋", - "verifyInstructions": "このホストを信頼できる場合は、「承認」をクリックして続行し、今後の接続のためにこのフィンガープリントを保存してください。", + "firstConnectionTitle": "このホストへの初回接続", + "firstConnectionDescription": "このホストの真正性を確認できません。フィンガープリントが想定したものと一致するか確認してください。", + "keyChangedDescription": "このサーバーのホストキーが前回接続時から変更されています。これはセキュリティ上の問題の可能性があります。", + "previousKey": "以前のキー", + "newFingerprint": "新しいフィンガープリント", + "fingerprint": "フィンガープリント", + "verifyInstructions": "このホストを信頼する場合は、[承諾] をクリックして続行し、このフィンガープリントを今後の接続用に保存してください。", "securityWarning": "セキュリティ警告", - "acceptAndContinue": "同意して続行", - "acceptNewKey": "新しいキーを受け入れて続行する" + "acceptAndContinue": "承諾して続行", + "acceptNewKey": "新しいキーを承諾して続行" }, "errors": { "databaseConnection": "データベースに接続できませんでした", @@ -2260,52 +2393,53 @@ "loginFailed": "ログインに失敗しました", "failedPasswordReset": "パスワードリセットの開始に失敗しました", "failedVerifyCode": "リセットコードの検証に失敗しました", - "failedCompleteReset": "パスワードのリセットが完了しませんでした", - "invalidTotpCode": "無効なTOTPコード", + "failedCompleteReset": "パスワードリセットの完了に失敗しました", + "invalidTotpCode": "無効なTOTPコードです", "failedOidcLogin": "OIDCログインの開始に失敗しました", - "silentSigninOidcUnavailable": "サイレントサインインが要求されましたが、OIDCログインは利用できません。", - "failedUserInfo": "ログイン後にユーザー情報を取得できませんでした", + "silentSigninOidcUnavailable": "サイレントサインインが要求されましたが、OIDCログインが利用できません。", + "failedUserInfo": "ログイン後のユーザー情報の取得に失敗しました", "oidcAuthFailed": "OIDC認証に失敗しました", "invalidAuthUrl": "バックエンドから無効な認証URLを受信しました", "requiredField": "この項目は必須です", - "minLength": "最小長さは {{min}}です。", + "minLength": "最小{{min}}文字です", "passwordMismatch": "パスワードが一致しません", - "passwordLoginDisabled": "ユーザー名/パスワードによるログインは現在無効になっています。", + "passwordLoginDisabled": "ユーザー名/パスワードログインは現在無効になっています", "sessionExpired": "セッションの有効期限が切れました。再度ログインしてください。", - "totpRateLimited": "レート制限:TOTP認証の試行回数が多すぎます。しばらくしてからもう一度お試しください。", - "totpRateLimitedWithTime": "レート制限:TOTP認証の試行回数が多すぎます。 {{time}} 秒待ってから再度お試しください。", - "resetCodeRateLimited": "レート制限:認証試行回数が多すぎます。しばらくしてからもう一度お試しください。", - "resetCodeRateLimitedWithTime": "レート制限:認証試行回数が多すぎます。 {{time}} 秒待ってから再度お試しください。", + "totpRateLimited": "レート制限: TOTP検証の試行が多すぎます。しばらくしてから再試行してください。", + "totpRateLimitedWithTime": "レート制限: TOTP検証の試行が多すぎます。{{time}}秒待ってから再試行してください。", + "resetCodeRateLimited": "レート制限: 検証の試行が多すぎます。後でもう一度お試しください。", + "resetCodeRateLimitedWithTime": "レート制限: 認証試行が多すぎます。{{time}}秒待ってから再試行してください。", "authTokenSaveFailed": "認証トークンの保存に失敗しました", - "failedToLoadServer": "サーバーの読み込みに失敗しました" + "failedToLoadServer": "サーバーの読み込みに失敗しました", + "remoteServerRequired": "リモートサーバーが必要です。この接続タイプを使用するには、設定でリモートサーバーを接続してください。" }, "messages": { - "registrationDisabled": "現在、管理者により新規アカウント登録は無効になっています。ログインするか、管理者にお問い合わせください。", - "userNotAllowed": "お客様のアカウントには登録権限がありません。管理者にお問い合わせください。", + "registrationDisabled": "現在、管理者が新規アカウント登録を無効にしています。ログインするか、管理者にお問い合わせください。", + "userNotAllowed": "アカウントは登録を許可されていません。管理者にお問い合わせください。", "databaseConnectionFailed": "データベースサーバーへの接続に失敗しました", - "resetCodeSent": "リセットコードがDockerログに送信されます", + "resetCodeSent": "リセットコードをDockerログに出力しました", "codeVerified": "コードの検証に成功しました", "passwordResetSuccess": "パスワードのリセットに成功しました", "loginSuccess": "ログインに成功しました", - "registrationSuccess": "登録が完了しました" + "registrationSuccess": "登録に成功しました" }, "profile": { - "c2sTunnelConfigDesc": "設定済みのSSHホストを対象とするローカルデスクトップトンネル。", + "c2sTunnelConfigDesc": "設定されたSSHホストを対象とするローカルデスクトップトンネル。", "c2sTunnelPresets": "クライアントトンネルプリセット", - "c2sTunnelPresetsDesc": "このデスクトップクライアントのローカルトンネルリストを名前付きサーバープリセットとして保存するか、プリセットをこのクライアントにロードします。", - "c2sTunnelPresetsUnavailable": "クライアントトンネルのプリセットは、デスクトップクライアントでのみ利用可能です。", + "c2sTunnelPresetsDesc": "このデスクトップクライアントのローカルトンネルリストを名前付きのサーバープリセットとして保存するか、プリセットをこのクライアントに読み込みます。", + "c2sTunnelPresetsUnavailable": "クライアントトンネルプリセットはデスクトップクライアントでのみ利用可能です。", "c2sPresetName": "プリセット名", "c2sPresetNamePlaceholder": "クライアントプリセット名", - "c2sPresetToLoad": "ロード用にプリセット", + "c2sPresetToLoad": "読み込むプリセット", "c2sNoPresetSelected": "プリセットが選択されていません", - "c2sNoPresets": "プリセットは保存されていません", - "c2sLoadPreset": "負荷", - "c2sCurrentLocalConfig": "{{count}} このデスクトップに設定されているローカルクライアントトンネル。", - "c2sPresetSyncNote": "プリセットは明示的なスナップショットであり、プリセットを読み込むと、このデスクトップクライアントのローカルクライアントトンネルリストが置き換えられます。", - "c2sPresetSaved": "クライアントトンネルのプリセットが保存されました", - "c2sPresetLoaded": "クライアントトンネルプリセットがローカルにロードされました", - "c2sPresetRenamed": "クライアントトンネルプリセットの名前が変更されました", - "c2sPresetDeleted": "クライアントトンネルプリセットが削除されました", + "c2sNoPresets": "保存されたプリセットはありません", + "c2sLoadPreset": "読み込み", + "c2sCurrentLocalConfig": "このデスクトップに{{count}}個のローカルクライアントトンネルが設定されています。", + "c2sPresetSyncNote": "プリセットは明示的なスナップショットです。読み込むと、このデスクトップクライアントのローカルクライアントトンネルリストが置き換えられます。", + "c2sPresetSaved": "クライアントトンネルプリセットを保存しました", + "c2sPresetLoaded": "クライアントトンネルプリセットをローカルに読み込みました", + "c2sPresetRenamed": "クライアントトンネルプリセットの名前を変更しました", + "c2sPresetDeleted": "クライアントトンネルプリセットを削除しました", "c2sPresetLoadError": "クライアントトンネルプリセットの読み込みに失敗しました" }, "placeholders": { @@ -2313,100 +2447,100 @@ "retryInterval": "10", "language": "言語", "keyPassword": "キーパスワード", - "pastePrivateKey": "ここに秘密鍵を貼り付けてください...", - "localListenerHost": "127.0.0.1(ローカルで聴く)", - "localTargetHost": "127.0.0.1(このコンピュータ上のターゲット)", + "pastePrivateKey": "秘密鍵をここに貼り付け...", + "localListenerHost": "127.0.0.1(ローカルでリッスン)", + "localTargetHost": "127.0.0.1(このコンピューターのターゲット)", "socksListenerHost": "127.0.0.1 (SOCKSリスナー)", - "enterPassword": "パスワードを入力してください", + "enterPassword": "パスワードを入力", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "ダッシュボード", - "loading": "ダッシュボードを読み込んでいます...", + "loading": "ダッシュボードを読み込み中...", "github": "GitHub", "support": "サポート", - "discord": "不和", + "discord": "Discord", "docs": "ドキュメント", - "donate": "寄付する", + "donate": "寄付", "serverOverview": "サーバー概要", "version": "バージョン", - "upToDate": "最新の", - "updateAvailable": "アップデートが利用可能です", + "upToDate": "最新", + "updateAvailable": "更新あり", "beta": "ベータ", - "uptime": "稼働時間", + "uptime": "アップタイム", "database": "データベース", - "healthy": "健康", + "healthy": "正常", "error": "エラー", - "totalHosts": "ホスト総数", - "totalTunnels": "総トンネル数", - "totalCredentials": "資格情報合計", - "recentActivity": "最近の活動", + "totalHosts": "合計ホスト数", + "totalTunnels": "合計トンネル数", + "totalCredentials": "合計認証情報数", + "recentActivity": "最近のアクティビティ", "reset": "リセット", - "loadingRecentActivity": "最近のアクティビティを読み込んでいます...", - "noRecentActivity": "最近アクティビティはありません", + "loadingRecentActivity": "最近のアクティビティを読み込み中...", + "noRecentActivity": "最近のアクティビティはありません", "quickActions": "クイックアクション", "addHost": "ホストを追加", "addCredential": "認証情報を追加", "adminSettings": "管理者設定", - "userProfile": "ユーザープロフィール", + "userProfile": "ユーザープロファイル", "serverStats": "サーバー統計", - "loadingServerStats": "サーバー統計情報を読み込んでいます...", + "loadingServerStats": "サーバー統計を読み込み中...", "noServerData": "サーバーデータがありません", "cpu": "CPU", - "ram": "ラム", - "customizeLayout": "ダッシュボードをカスタマイズする", + "ram": "RAM", + "customizeLayout": "ダッシュボードのカスタマイズ", "dashboardSettings": "ダッシュボード設定", - "enableDisableCards": "カードの有効化/無効化", - "resetLayout": "初期設定に戻す", + "enableDisableCards": "カードの表示/非表示", + "resetLayout": "デフォルトにリセット", "serverOverviewCard": "サーバー概要", - "recentActivityCard": "最近の活動", + "recentActivityCard": "最近のアクティビティ", "networkGraphCard": "ネットワークグラフ", "networkGraph": "ネットワークグラフ", "quickActionsCard": "クイックアクション", "serverStatsCard": "サーバー統計", - "panelMain": "主要", - "panelSide": "側", - "justNow": "ちょうど今", + "panelMain": "メイン", + "panelSide": "サイド", + "justNow": "たった今", "serviceLinks": "サービスリンク", - "homepagePreview": "ホームページのプレビュー" + "homepagePreview": "ホームページプレビュー" }, "donation": { - "title": "Termixを楽しんでいますか?", - "body": "Termixは無料のオープンソースソフトウェアで、2人チームが空き時間を使って開発・保守しています。もしTermixが、本来有料で利用していた商用ツールの代替として使えるのであれば、ご寄付をいただけるとホスティング費用や開発継続に役立ちます。現時点では、寄付は仮想通貨のみ受け付けています。", - "milestones": "寄付金は、SAMLやKubernetesサポートといった機能の開発に必要な調査や学習に費やす時間にも役立てられます。寄付ページで進捗状況をご確認ください。", + "title": "Termixをお楽しみいただけていますか?", + "body": "Termixは無料のオープンソースソフトウェアで、2人のチームが余暇を使って開発・保守しています。もし商用ツールを置き換えてすでにお使いいただいているなら、寄付によりホスティング費用のカバーと開発の継続が可能になります。現在のところ、寄付は暗号通貨のみでの受付となっております。", + "milestones": "寄付は、SAMLやKubernetesサポートなどの機能構築に必要な調査や学習の時間を確保するためにも役立ちます。進捗状況は寄付ページでご確認ください。", "cta": "寄付する", "dismiss": "後で" }, "dashboardTab": { - "stable": "安定した", - "hostsOnline": "ホストがオンラインです", - "activeTunnels": "現役トンネル", - "registerNewServer": "新しいサーバーを登録する", - "storeSshKeysOrPasswords": "SSHキーまたはパスワードを保存する", - "manageUsersAndRoles": "ユーザーと役割を管理する", - "manageYourAccount": "アカウントを管理する", + "stable": "安定版", + "hostsOnline": "オンラインホスト", + "activeTunnels": "アクティブトンネル", + "registerNewServer": "新しいサーバーを登録", + "storeSshKeysOrPasswords": "SSHキーまたはパスワードを保存", + "manageUsersAndRoles": "ユーザーとロールを管理", + "manageYourAccount": "アカウントを管理", "hostStatus": "ホストステータス", - "noHostsConfigured": "ホストは設定されていません", + "noHostsConfigured": "設定済みホストなし", "online": "オンライン", "offline": "オフライン", "checking": "チェック中", "onlineLower": "オンライン", "nodes": "{{count}} ノード", - "add": "追加:", + "add": "追加:", "commandPalette": "コマンドパレット", - "done": "終わり", - "editModeInstructions": "カードをドラッグして並べ替える · 列の区切り線をドラッグして列のサイズを変更する · カードの下端をドラッグして高さを変更する · ゴミ箱に移動して削除する", - "empty": "空の", + "done": "完了", + "editModeInstructions": "カードをドラッグして並べ替え · 列の仕切りをドラッグして列幅を調整 · カードの下端をドラッグして高さを調整 · ゴミ箱にドラッグして削除", + "empty": "空", "clear": "クリア", "serviceLinksTitle": "サービスリンク", "serviceLinksEmpty": "サービスリンクはまだありません", "serviceLinksAddLabel": "ラベル", "serviceLinksAddUrl": "URL", "serviceLinksAdd": "追加", - "serviceLinksLabelPlaceholder": "私のサービス", + "serviceLinksLabelPlaceholder": "マイサービス", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "有効なウェブアドレスを入力してください", + "serviceLinksInvalidUrl": "有効なWebアドレスを入力してください", "serviceLinksAddFailed": "サービスリンクの追加に失敗しました", "disk": "ディスク", "viewServerDetails": "サーバーの詳細を表示" @@ -2414,281 +2548,298 @@ "sessionLogs": { "title": "セッションログ", "noLogs": "セッションログはまだありません", - "noLogsDesc": "ホストでセッションログを有効にして記録を開始します", - "duration": "間隔", + "noLogsDesc": "ホストでセッションログを有効にして記録を開始してください", + "duration": "所要時間", "viewLog": "ログを表示", "downloadLog": "ダウンロード", - "deleteLog": "消去", + "deleteLog": "削除", "confirmDelete": "このセッションログを削除しますか?", - "confirmDeleteDesc": "この操作は取り消すことができません。", + "confirmDeleteDesc": "この操作は元に戻せません。", "copyContent": "コピー", "copied": "コピーしました!", "loadError": "セッションログの読み込みに失敗しました", "deleteError": "セッションログの削除に失敗しました", - "filterByHost": "ホストで絞り込む..." + "filterByHost": "ホストでフィルター..." }, "networkGraph": { "addHost": "ホストを追加", "addGroup": "グループを追加", "addLink": "リンクを追加", - "zoomIn": "ズームイン", - "zoomOut": "ズームアウト", - "resetView": "リセットビュー", + "zoomIn": "拡大", + "zoomOut": "縮小", + "resetView": "表示をリセット", "selectHost": "ホストを選択", - "chooseHost": "ホストを選択してください...", - "parentGroup": "保護者グループ", + "chooseHost": "ホストを選択...", + "parentGroup": "親グループ", "noGroup": "グループなし", "groupName": "グループ名", "color": "色", "source": "ソース", "target": "ターゲット", "moveToGroup": "グループに移動", - "selectGroup": "グループを選択してください...", - "addConnection": "接続を追加する", - "hostDetails": "ホストの詳細", + "selectGroup": "グループを選択...", + "addConnection": "接続を追加", + "hostDetails": "ホスト詳細", "removeFromGroup": "グループから削除", - "addHostHere": "ホストを追加", + "addHostHere": "ここにホストを追加", "editGroup": "グループを編集", - "delete": "消去", + "delete": "削除", "add": "追加", - "create": "作成する", - "move": "動く", - "connect": "接続する", - "createGroup": "グループを作成する", + "create": "作成", + "move": "移動", + "connect": "接続", + "createGroup": "グループを作成", "selectSourcePlaceholder": "ソースを選択...", - "selectTargetPlaceholder": "対象を選択...", + "selectTargetPlaceholder": "ターゲットを選択...", "invalidFile": "無効なファイル", - "hostAlreadyExists": "ホストは既にトポロジーに含まれています", - "connectionExists": "接続は既に存在します", - "unknown": "未知", + "hostAlreadyExists": "ホストはトポロジーに既に存在します", + "connectionExists": "接続は既に存在しています", + "unknown": "不明", "name": "名前", "ip": "IP", - "status": "状態", + "status": "ステータス", "failedToAddNode": "ノードの追加に失敗しました", - "sourceDifferentFromTarget": "ソースとターゲットは異なる必要がある", - "exportJSON": "JSONをエクスポート", - "importJSON": "JSONをインポート", + "sourceDifferentFromTarget": "ソースとターゲットは異なる必要があります", + "exportJSON": "JSON エクスポート", + "importJSON": "JSON インポート", "terminal": "ターミナル", "fileManager": "ファイルマネージャー", "tunnel": "トンネル", - "docker": "ドッカー", + "docker": "Docker", "serverStats": "ホストメトリクス", "hostMetrics": "ホストメトリクス", "noNodes": "ノードはまだありません" }, "docker": { "notEnabled": "このホストではDockerが有効になっていません", - "validating": "Dockerを検証しています...", + "validating": "Dockerを検証中...", "connecting": "接続中...", "error": "エラー", "version": "Docker {{version}}", "connectionFailed": "Dockerへの接続に失敗しました", - "containerStarted": "コンテナ {{name}} が開始されました", - "failedToStartContainer": "コンテナの起動に失敗しました {{name}}", + "containerStarted": "コンテナ {{name}} が起動しました", + "failedToStartContainer": "コンテナ {{name}} の起動に失敗しました", "containerStopped": "コンテナ {{name}} が停止しました", - "failedToStopContainer": "コンテナの停止に失敗しました {{name}}", - "containerRestarted": "コンテナ {{name}} が再起動されました", - "failedToRestartContainer": "コンテナの再起動に失敗しました {{name}}", - "containerPaused": "コンテナ {{name}} 一時停止", - "containerUnpaused": "コンテナ {{name}} 一時停止解除", + "failedToStopContainer": "コンテナ {{name}} の停止に失敗しました", + "containerRestarted": "コンテナ {{name}} が再起動しました", + "failedToRestartContainer": "コンテナ {{name}} の再起動に失敗しました", + "containerPaused": "コンテナ {{name}} が一時停止しました", + "containerUnpaused": "コンテナ {{name}} の一時停止が解除されました", "failedToTogglePauseContainer": "コンテナ {{name}} の一時停止状態の切り替えに失敗しました", "containerRemoved": "コンテナ {{name}} が削除されました", - "failedToRemoveContainer": "コンテナの削除に失敗しました {{name}}", - "image": "画像", - "ports": "港", + "failedToRemoveContainer": "コンテナ {{name}} の削除に失敗しました", + "image": "イメージ", + "ports": "ポート", "noPorts": "ポートなし", - "start": "始める", - "confirmRemoveContainer": "コンテナ「{{name}}」を削除してもよろしいですか?この操作は元に戻せません。", - "runningContainerWarning": "警告:このコンテナは現在実行中です。削除すると、まずコンテナが停止します。", - "loadingContainers": "コンテナを積み込んでいます...", - "manager": "Docker Manager", + "start": "起動", + "confirmRemoveContainer": "コンテナ '{{name}}' を削除してもよろしいですか? この操作は元に戻せません。", + "runningContainerWarning": "警告: このコンテナは現在実行中です。削除すると、まずコンテナが停止されます。", + "loadingContainers": "コンテナを読み込み中...", + "manager": "Docker マネージャー", "autoRefresh": "自動更新", "timestamps": "タイムスタンプ", - "lines": "線", - "filterLogs": "ログをフィルタリング...", - "refresh": "更新", + "lines": "行数", + "filterLogs": "ログをフィルター...", + "refresh": "再読み込み", "download": "ダウンロード", "clear": "クリア", "logsDownloaded": "ログのダウンロードに成功しました", - "last50": "過去50件", - "last100": "過去100件", - "last500": "最後の500件", - "last1000": "過去1000件", + "last50": "直近50件", + "last100": "直近100件", + "last500": "直近500件", + "last1000": "直近1000件", "allLogs": "すべてのログ", - "noLogsMatching": "「{{query}}」に一致するログはありません", - "noLogsAvailable": "ログはありません", - "noContainersFound": "コンテナが見つかりませんでした", - "noContainersFoundHint": "このホストにはDockerコンテナがありません", + "noLogsMatching": "検索条件 \"{{query}}\" に一致するログはありません", + "noLogsAvailable": "利用可能なログはありません", + "noContainersFound": "コンテナが見つかりません", + "noContainersFoundHint": "このホストには利用可能なDockerコンテナがありません", "searchPlaceholder": "コンテナを検索...", - "allStatuses": "すべてのステータス", - "stateRunning": "ランニング", - "statePaused": "一時停止", - "stateExited": "退出しました", + "allStatuses": "すべての状態", + "stateRunning": "実行中", + "statePaused": "一時停止中", + "stateExited": "終了済み", "stateRestarting": "再起動中", - "noContainersMatchFilters": "フィルターに一致するコンテナはありません", - "noContainersMatchFiltersHint": "検索条件またはフィルター条件を調整してみてください", - "failedToFetchStats": "コンテナ統計情報の取得に失敗しました", + "noContainersMatchFilters": "条件に一致するコンテナはありません", + "noContainersMatchFiltersHint": "検索条件またはフィルター条件を調整してください", + "failedToFetchStats": "コンテナの統計情報の取得に失敗しました", "containerNotRunning": "コンテナが実行されていません", "startContainerToViewStats": "統計情報を表示するにはコンテナを起動してください", - "loadingStats": "統計情報を読み込んでいます...", - "errorLoadingStats": "エラー読み込み統計", - "noStatsAvailable": "統計情報はありません", + "loadingStats": "統計情報を読み込み中...", + "errorLoadingStats": "統計情報の読み込みエラー", + "noStatsAvailable": "利用可能な統計情報はありません", "cpuUsage": "CPU使用率", "current": "現在", "memoryUsage": "メモリ使用量", "networkIo": "ネットワークI/O", - "input": "入力", + "input": "受信", "output": "出力", - "blockIo": "ブロックI/O", - "read": "読む", - "write": "書く", - "pids": "PID", + "blockIo": "ブロック I/O", + "read": "読み取り", + "write": "書き込み", + "pids": "PIDs", "containerInformation": "コンテナ情報", "name": "名前", "id": "ID", - "state": "州", - "containerMustBeRunning": "コンソールにアクセスするには、コンテナが実行されている必要があります。", - "verificationCodePrompt": "確認コードを入力してください", + "state": "状態", + "containerMustBeRunning": "コンソールにアクセスするにはコンテナが実行中である必要があります", + "verificationCodePrompt": "認証コードを入力してください", "totpVerificationFailed": "TOTP認証に失敗しました。もう一度お試しください。", - "warpgateVerificationFailed": "ワープゲート認証に失敗しました。もう一度お試しください。", + "warpgateVerificationFailed": "Warpgate認証に失敗しました。もう一度お試しください。", "connectedTo": "{{containerName}}に接続しました", - "disconnected": "接続が切断されました", + "disconnected": "切断されました", "consoleError": "コンソールエラー", "errorMessage": "エラー: {{message}}", - "failedToConnect": "コンテナへの接続に失敗しました", + "failedToConnect": "コンテナに接続できませんでした", "console": "コンソール", - "selectShell": "シェルを選択してください", - "bash": "バッシュ", - "sh": "し", - "ash": "灰", - "connect": "接続する", + "selectShell": "シェルを選択", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "接続", "disconnect": "切断", - "notConnected": "接続されていません", - "clickToConnect": "「接続」をクリックしてシェルセッションを開始します。", - "connectingTo": "{{containerName}} に接続中...", + "notConnected": "未接続", + "clickToConnect": "接続ボタンをクリックしてシェルセッションを開始", + "connectingTo": "{{containerName}}に接続中...", "containerNotFound": "コンテナが見つかりません", - "backToList": "リストに戻る", + "backToList": "一覧に戻る", "logs": "ログ", "stats": "統計", "consoleTab": "コンソール", - "startContainerToAccess": "コンソールにアクセスするにはコンテナを起動してください" + "startContainerToAccess": "コンテナを起動してコンソールにアクセスしてください" }, "admin": { - "sectionGeneral": "一般的な", + "sectionGeneral": "全般", "sectionOidc": "OIDC", "sectionSso": "SSOプロバイダー", - "ssoAddProvider": "プロバイダーを追加", + "ssoAddProvider": "追加", "ssoDocsLink": "ドキュメントを表示", "ssoProviderDocsLink": "ドキュメントを表示", "ssoNoProviders": "SSOプロバイダーが設定されていません。", "ssoProviderName": "表示名", - "ssoProviderType": "プロバイダーの種類", - "ssoDeleteProvider": "プロバイダーを削除", - "ssoDeleteConfirm": "このプロバイダーを削除しますか?関連ユーザーはログインできなくなります。", + "ssoProviderType": "プロバイダータイプ", + "ssoDeleteProvider": "削除", + "ssoDeleteConfirm": "このプロバイダーを削除しますか?関連付けられているユーザーはログインできなくなります。", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", - "ssoTypeGoogle": "グーグル", + "ssoTypeGoogle": "Google", "ssoEnabled": "有効", "ssoDisabled": "無効", - "ssoSaveProvider": "プロバイダーを保存", + "ssoSaveProvider": "保存", "ssoTestConnection": "接続テスト", - "ssoEditProvider": "プロバイダーを編集", + "ssoEditProvider": "編集", "ldapHost": "LDAPホスト", "ldapPort": "ポート", - "ldapUseTls": "TLS(LDAPS)を使用する", + "ldapUseTls": "TLS (LDAPS) を使用", "ldapBindDn": "バインドDN", - "ldapBindPassword": "パスワードをバインドする", + "ldapBindPassword": "バインドパスワード", "ldapUserSearchBase": "ユーザー検索ベース", "ldapUserSearchFilter": "ユーザー検索フィルター", "ldapUsernameAttr": "ユーザー名属性", "ldapDisplayNameAttr": "表示名属性", "ldapGroupSearchBase": "グループ検索ベース", "ldapAdminGroup": "管理者グループ", - "ldapAllowedUsers": "許可されたユーザー", + "ldapAllowedUsers": "許可ユーザー", "sectionUsers": "ユーザー", "sectionSessions": "セッション", - "sectionRoles": "役割", + "sectionRoles": "ロール", "sectionDatabase": "データベース", "sectionApiKeys": "APIキー", "sectionAuditLog": "監査ログ", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Let's Encryptから信頼できるSSL証明書を自動的に発行および更新します。パブリックドメインとポート80またはDNSアクセスが必要です。", - "sslDocsLink": "SSLドキュメントを見る", + "sslDescription": "Let's Encryptから信頼できるSSL証明書を自動的に発行・更新します。公開ドメインとポート80、またはDNSアクセスが必要です。", + "sslDocsLink": "SSLドキュメントを表示", "sslDomain": "ドメイン", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "証明書のパブリックドメイン名。", - "sslEmail": "メール", + "sslDomainDesc": "証明書の公開ドメイン名。", + "sslEmail": "メールアドレス", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encryptの通知やアカウントに関するお問い合わせは、こちらのメールアドレスまでお願いいたします。", - "sslChallengeType": "チャレンジの種類", + "sslEmailDesc": "Let's Encryptの通知およびアカウント用の連絡先メールアドレス。", + "sslChallengeType": "チャレンジタイプ", "sslChallengeTypeDesc": "Let's Encryptにドメイン所有権を証明する方法。", - "sslChallengeHttp": "HTTP(ウェブルート) - インターネットからアクセス可能なポート80が必要です", - "sslChallengeDns": "DNS(Cloudflare) - Cloudflare APIトークンが必要です", + "sslChallengeHttp": "HTTP(webroot)- インターネットからポート80にアクセスできる必要があります", + "sslChallengeDns": "DNS(Cloudflare)- Cloudflare APIトークンが必要です", "sslCloudflareToken": "Cloudflare APIトークン", - "sslCloudflareTokenPlaceholder": "トークンを入力してください...", - "sslCloudflareTokenDesc": "ドメインに対する Zone:DNS:Edit 権限を持つスコープ付きトークン。", - "sslCertStatus": "証明書のステータス", + "sslCloudflareTokenPlaceholder": "トークンを入力...", + "sslCloudflareTokenDesc": "ドメインに対するZone:DNS:Edit権限を持つスコープ付きトークン。", + "sslCertStatus": "証明書ステータス", "sslCertStatusNone": "証明書なし", "sslCertStatusValid": "有効", - "sslCertStatusExpiring": "まもなく期限切れ", + "sslCertStatusExpiring": "間もなく期限切れ", "sslCertStatusExpired": "期限切れ", "sslCertExpiresAt": "有効期限 {{date}}", - "sslLastIssued": "最終発行 {{date}}", - "sslRequestCert": "証明書の発行/更新", - "sslRequestCertLoading": "証明書を要求しています...", - "sslRequestCertSuccess": "証明書の発行とインストールが正常に完了しました。", - "sslRequestCertFailed": "証明書要求が失敗しました", + "sslLastIssued": "最終発行日 {{date}}", + "sslRequestCert": "証明書を発行/更新", + "sslRequestCertLoading": "証明書をリクエスト中...", + "sslRequestCertSuccess": "証明書が正常に発行され、インストールされました", + "sslRequestCertFailed": "証明書リクエストに失敗しました", "sslSave": "設定を保存", "sslSaved": "SSL設定が保存されました", "sslSaveFailed": "SSL設定の保存に失敗しました", - "sslRequiresDomain": "ドメインとメールアドレスは必須です", - "sslInfoNote": "証明書を発行した後、環境変数でSSLを有効にし(ENABLE_SSL=true)、Termixを再起動してください。", - "auditLogTotal": "{{total}} 合計エントリ数", - "auditLogEmpty": "監査ログエントリは見つかりませんでした", + "sslRequiresDomain": "ドメインとメールアドレスが必要です", + "sslInfoNote": "証明書の発行後、環境変数(ENABLE_SSL=true)でSSLを有効にし、Termixを再起動してください。", + "sslManualOption": "手動(証明書のアップロード)", + "sslManualCert": "証明書(PEM)", + "sslManualCertPlaceholder": "-----証明書開始-----", + "sslManualKey": "秘密鍵(PEM形式)", + "sslManualKeyPlaceholder": "-----秘密鍵開始-----", + "sslManualDesc": "既存の証明書と秘密鍵を貼り付けてください。認証局(CA)が必要とする場合は、証明書チェーン全体も添付してください。", + "sslManualUpload": "証明書のアップロードとインストール", + "sslManualUploadLoading": "証明書をアップロードしています...", + "sslManualUploadSuccess": "証明書のアップロードとインストールが正常に完了しました。", + "sslManualUploadFailed": "証明書のアップロードに失敗しました", + "sslManualRequiresFields": "証明書と秘密鍵が必要です", + "auditLogTotal": "全{{total}}件", + "auditLogEmpty": "監査ログエントリが見つかりません", "auditLogSuccess": "成功", - "auditLogFailed": "失敗した", + "auditLogFailed": "失敗", "auditLogClearFilters": "フィルターをクリア", - "auditLogPage": "ページ {{page}} / {{totalPages}} (合計{{total}})", + "auditLogPage": "{{page}} / {{totalPages}} ページ (全 {{total}} 件)", "auditLogIp": "IP", "auditLogResourceId": "リソースID", "auditLogFilterUser": "ユーザー", "auditLogFilterAction": "アクション", - "auditLogFilterResourceType": "リソースタイプ", - "auditLogFilterStatus": "状態", - "auditLogFilterFrom": "から", - "auditLogFilterTo": "に", - "auditLogFilterAll": "全て", - "allowRegistration": "ユーザー登録を許可する", - "allowRegistrationDesc": "新規ユーザーがユーザー名とパスワードで自己登録できるようにする", - "allowPasswordLogin": "パスワードログインを許可する", - "allowPasswordLoginDesc": "ユーザー名/パスワードでログイン", + "auditLogFilterResourceType": "リソース種別", + "auditLogFilterStatus": "ステータス", + "auditLogFilterFrom": "開始", + "auditLogFilterTo": "終了", + "auditLogFilterAll": "すべて", + "allowRegistration": "ユーザー登録を許可", + "allowRegistrationDesc": "新規ユーザーがユーザー名とパスワードで自己登録できます", + "allowPasswordLogin": "パスワードログインを許可", + "allowPasswordLoginDesc": "ユーザー名/パスワードログイン", "oidcAutoProvision": "OIDC自動プロビジョニング", - "oidcAutoProvisionDesc": "OIDC/SSOユーザーのアカウントを初回ログイン時に自動作成する(登録設定とは無関係)", + "oidcAutoProvisionDesc": "初回ログイン時にOIDC/SSOユーザーのアカウントを自動作成します(登録のトグルとは独立)", "oidcSilentLoginDefault": "デフォルトでサイレントOIDCログイン", - "oidcSilentLoginDefaultDesc": "訪問のたびに自動的にOIDCログインページにリダイレクトし、ログインフォームを完全にスキップする", - "allowPasswordReset": "パスワードのリセットを許可する", - "allowPasswordResetDesc": "Dockerログ経由でコードをリセット", + "oidcSilentLoginDefaultDesc": "アクセス時に自動でOIDCログインにリダイレクトし、ログインフォームを完全にスキップします", + "allowPasswordReset": "パスワードリセットを許可", + "allowPasswordResetDesc": "リセットコードをDockerログから取得", "commandHistoryEnabled": "コマンド履歴", - "commandHistoryEnabledDesc": "コマンド履歴の記録を許可します。無効にすると、ホストごとの設定に関係なく履歴は保存されません。", + "commandHistoryEnabledDesc": "コマンド履歴の記録を許可します。無効にすると、ホストごとの設定に関わらず履歴は保存されません。", "updateCommandHistoryFailed": "コマンド履歴設定の更新に失敗しました", + "analyticsEnabled": "匿名利用統計を共有する", + "analyticsEnabledDesc": "Termixの改善に役立てるため、ユーザー数、ホスト数、機能使用状況に関する匿名データを毎日送信します。個人データや接続情報は一切含まれません。", + "updateAnalyticsFailed": "分析設定の更新に失敗しました", + "sessionSharingGloballyEnabled": "セッション共有を許可する", + "sessionSharingGloballyEnabledDesc": "ライブターミナル、RDP、VNC、およびTelnetセッションをインスタンス全体で共有できるようにします。無効にすると、ホストごとの共有設定がすべて上書きされます。", + "updateSessionSharingFailed": "セッション共有設定の更新に失敗しました", "sessionTimeout": "セッションタイムアウト", - "hours": "営業時間", - "sessionTimeoutRange": "最短1時間・最長720時間", - "monitoringDefaults": "デフォルトの監視", + "hours": "時間", + "sessionTimeoutRange": "最小1時間・最大720時間", + "monitoringDefaults": "監視のデフォルト", "statusCheck": "ステータスチェック", - "metrics": "指標", + "metrics": "メトリクス", "sec": "秒", "logLevel": "ログレベル", - "enableGuacamole": "グアカモレを有効にする", + "enableGuacamole": "Guacamoleを有効にする", "enableGuacamoleDesc": "RDP/VNCリモートデスクトップ", - "enableGuacamoleDocsLink": "ドキュメントを表示", + "enableGuacamoleDocsLink": "ドキュメントを見る", "guacdUrl": "guacd URL", "tailscaleApiKey": "Tailscale APIキー", - "tailscaleApiKeyDescription": "ホストエディタでのデバイス検出に使用されます。tailscale.com/admin/settings/keys でキーを生成してください。", - "tailscaleApiKeyDocsLink": "ドキュメントを表示", - "oidcDescription": "OpenID Connectを使用してSSOを設定します。*印の付いた項目は必須です。", - "oidcDocsLink": "ドキュメントを表示", + "tailscaleApiKeyDescription": "ホストエディタでのデバイス検出に使用します。tailscale.com/admin/settings/keysでキーを生成してください。", + "tailscaleApiKeyDocsLink": "ドキュメントを見る", + "oidcDescription": "SSO用のOpenID Connectを設定します。*マークのフィールドは必須です。", + "oidcDocsLink": "ドキュメントを見る", "oidcClientId": "クライアントID", "oidcClientSecret": "クライアントシークレット", "oidcAuthUrl": "認証URL", @@ -2697,258 +2848,258 @@ "oidcUserIdentifier": "ユーザー識別子パス", "oidcDisplayName": "表示名パス", "oidcScopes": "スコープ", - "oidcUserinfoUrl": "ユーザー情報URLを上書きする", - "oidcAllowedUsers": "許可されたユーザー", - "oidcAllowedUsersDesc": "1行に1つのメールアドレスのみを入力してください。すべてを許可する場合は空欄のままにしてください。", + "oidcUserinfoUrl": "Userinfo URLの上書き", + "oidcAllowedUsers": "許可ユーザー", + "oidcAllowedUsersDesc": "1行に1つのメールアドレス。空の場合は全員許可。", "oidcAdminGroup": "管理者グループ", - "oidcAdminGroupDesc": "このグループのユーザーには管理者権限が付与されます。グループ同期を無効にするには、空欄のままにしてください。", + "oidcAdminGroupDesc": "このグループのユーザーは管理者権限が付与されます。空にするとグループ同期は無効です。", "oidcGroupClaim": "グループクレーム", - "oidcGroupClaimDesc": "オプション。ユーザーのグループを含むクレームパス。デフォルトでは、グループ、ロール、グループの順になります。カスタムクレームを持つプロバイダー(例:Zitadel)に使用します。", + "oidcGroupClaimDesc": "オプション。ユーザーのグループを含むクレームパス。デフォルトは groups, roles, group の順。カスタムクレームを使うプロバイダー用 (例: Zitadel)。", "oidcCaCert": "カスタムCA証明書", - "oidcCaCertDesc": "オプション。プライベートCAまたは自己署名CAを使用するOIDCプロバイダー用のPEMエンコードされたCA証明書。システムトラストストアを使用する場合は空欄のままにしてください。", - "removeOidc": "取り除く", - "usersCount": "{{count}} ユーザー", - "createUser": "作成する", - "newRole": "新しい役割", + "oidcCaCertDesc": "オプション。プライベートまたは自己署名CAを使用するOIDCプロバイダー用のPEMエンコードCA証明書。空の場合はシステムの信頼ストアを使用。", + "removeOidc": "削除", + "usersCount": "{{count}}人のユーザー", + "createUser": "作成", + "newRole": "新しいロール", "roleName": "名前", "roleDisplayName": "表示名", "roleDescription": "説明", - "rolesCount": "{{count}} 役割", - "createRole": "作成する", - "creating": "作成...", - "exportDatabase": "データベースのエクスポート", - "exportDatabaseDesc": "すべてのホスト、認証情報、および設定のバックアップをダウンロードします。", - "export": "輸出", + "rolesCount": "{{count}}個のロール", + "createRole": "作成", + "creating": "作成中...", + "exportDatabase": "データベースをエクスポート", + "exportDatabaseDesc": "すべてのホスト、認証情報、設定のバックアップをダウンロードします。", + "export": "エクスポート", "exporting": "エクスポート中...", - "importDatabase": "データベースのインポート", - "importDatabaseDesc": ".sqliteバックアップファイルから復元する", + "importDatabase": "データベースをインポート", + "importDatabaseDesc": ".sqlite バックアップファイルから復元します。", "importDatabaseSelected": "選択済み: {{name}}", "selectFile": "ファイルを選択", - "changeFile": "変化", - "import": "輸入", + "changeFile": "変更", + "import": "インポート", "importing": "インポート中...", "apiKeysCount": "{{count}} キー", "apiKeysDocsLink": "ドキュメントを表示", - "newApiKey": "新しいAPIキー", - "apiKeyCreatedWarning": "キーが作成されました。今すぐコピーしてください。二度と表示されません。", + "newApiKey": "新規APIキー", + "apiKeyCreatedWarning": "キーが作成されました。すぐにコピーしてください。再度表示されません。", "apiKeyName": "名前", "apiKeyUser": "ユーザー", - "apiKeySelectUser": "ユーザーを選択してください...", + "apiKeySelectUser": "ユーザーを選択...", "apiKeyExpiresAt": "有効期限", - "createKey": "キーの作成", - "apiKeyNoExpiry": "有効期限なし", - "revokedBadge": "取り消し済み", - "authTypeDual": "二元認証", + "createKey": "キーを作成", + "apiKeyNoExpiry": "期限なし", + "revokedBadge": "失効", + "authTypeDual": "二要素認証", "authTypeOidc": "OIDC", - "authTypeLocal": "地元", + "authTypeLocal": "ローカル", "adminStatusAdministrator": "管理者", "adminStatusRegularUser": "一般ユーザー", "adminBadge": "管理者", "systemBadge": "システム", "customBadge": "カスタム", - "youBadge": "あなた", - "sessionsActive": "{{count}} アクティブ", + "youBadge": "自分", + "sessionsActive": "{{count}} 件アクティブ", "sessionActive": "アクティブ: {{time}}", - "sessionExpires": "経験値: {{time}}", - "revokeAll": "全て", - "revokeAllSessionsSuccess": "ユーザーのすべてのセッションが取り消されました", - "revokeAllSessionsFailed": "セッションの取り消しに失敗しました", - "revokeSessionFailed": "セッションの取り消しに失敗しました", - "addRole": "役割を追加する", - "noCustomRoles": "カスタムロールは定義されていません", - "removeRoleFailed": "役割の削除に失敗しました", - "assignRoleFailed": "役割の割り当てに失敗しました", + "sessionExpires": "有効期限: {{time}}", + "revokeAll": "すべて", + "revokeAllSessionsSuccess": "ユーザーの全セッションを無効化しました", + "revokeAllSessionsFailed": "セッションの無効化に失敗しました", + "revokeSessionFailed": "セッションの無効化に失敗しました", + "addRole": "ロール追加", + "noCustomRoles": "カスタムロールが定義されていません", + "removeRoleFailed": "ロールの削除に失敗しました", + "assignRoleFailed": "ロールの割り当てに失敗しました", "deleteRoleFailed": "ロールの削除に失敗しました", "userAdminAccess": "管理者", - "userAdminAccessDesc": "すべての管理者設定へのフルアクセス", - "userRoles": "役割", - "revokeAllUserSessions": "すべてのセッションを取り消す", - "revokeAllUserSessionsDesc": "すべてのデバイスで強制的に再ログインする", - "revoke": "取り消す", - "deleteUserWarning": "このユーザーを削除すると、データは永久に削除されます。", + "userAdminAccessDesc": "すべての管理設定への完全なアクセス権", + "userRoles": "ロール", + "revokeAllUserSessions": "すべてのセッションを無効化", + "revokeAllUserSessionsDesc": "すべてのデバイスで再ログインを強制します", + "revoke": "無効化", + "deleteUserWarning": "このユーザーを削除すると元に戻せません。", "deleteUser": "{{username}} を削除", "deleting": "削除中...", "deleteUserFailed": "ユーザーの削除に失敗しました", - "deleteUserSuccess": "ユーザー \"{{username}}\" が削除しました", - "deleteRoleSuccess": "役割 \"{{name}}\" が削除されました", - "revokeKeySuccess": "キー \"{{name}}\" は取り消されました", - "revokeKeyFailed": "キーの取り消しに失敗しました", - "copiedToClipboard": "クリップボードにコピーされました", - "done": "終わり", - "createUserTitle": "ユーザーを作成する", - "createUserDesc": "新しいローカルアカウントを作成してください。", + "deleteUserSuccess": "ユーザー \"{{username}}\" を削除しました", + "deleteRoleSuccess": "ロール \"{{name}}\" を削除しました", + "revokeKeySuccess": "キー \"{{name}}\" を無効化しました", + "revokeKeyFailed": "キーの無効化に失敗しました", + "copiedToClipboard": "クリップボードにコピーしました", + "done": "完了", + "createUserTitle": "ユーザー作成", + "createUserDesc": "新しいローカルアカウントを作成します。", "createUserUsername": "ユーザー名", "createUserPassword": "パスワード", - "createUserPasswordHint": "最低6文字。", - "createUserEnterUsername": "ユーザー名を入力してください", - "createUserEnterPassword": "パスワードを入力してください", - "createUserSubmit": "ユーザーを作成する", + "createUserPasswordHint": "6文字以上", + "createUserEnterUsername": "ユーザー名を入力", + "createUserEnterPassword": "パスワードを入力", + "createUserSubmit": "ユーザーを作成", "editUserTitle": "ユーザー管理: {{username}}", - "editUserDesc": "役割、管理者ステータス、セッション、アカウント設定を編集します。", + "editUserDesc": "ロール、管理者ステータス、セッション、アカウント設定を編集します。", "editUserUsername": "ユーザー名", "editUserAuthType": "認証タイプ", "editUserAdminStatus": "管理者ステータス", "editUserUserId": "ユーザーID", - "linkAccountTitle": "アカウントをリンクする", - "linkAccountDesc": "OIDC アカウント {{username}} を既存のローカル アカウントと統合します。", - "linkAccountDescLocal": "ローカルアカウント {{username}} を既存の OIDC 専用アカウントにリンクします。", - "linkAccountWarningTitle": "これにより、以下のことが可能になります。", - "linkAccountEffect1": "OIDC専用アカウントを削除します", - "linkAccountEffect2": "対象アカウントにOIDCログインを追加する", - "linkAccountEffect3": "OIDCとパスワードの両方のログインを許可する", + "linkAccountTitle": "アカウントのリンク", + "linkAccountDesc": "OIDCアカウント {{username}} を既存のローカルアカウントに統合します。", + "linkAccountDescLocal": "ローカルアカウント {{username}} を既存のOIDC専用アカウントにリンクします。", + "linkAccountWarningTitle": "以下の処理が実行されます:", + "linkAccountEffect1": "OIDC専用アカウントを削除", + "linkAccountEffect2": "対象アカウントにOIDCログインを追加", + "linkAccountEffect3": "OIDCとパスワードの両方でログイン可能にする", "linkAccountTargetUsername": "ローカルアカウントのユーザー名", - "linkAccountTargetPlaceholder": "リンクするローカルアカウントのユーザー名を入力してください", + "linkAccountTargetPlaceholder": "リンク先のローカルアカウントのユーザー名を入力", "linkAccountOidcUsername": "OIDCアカウントのユーザー名", - "linkAccountOidcPlaceholder": "統合するOIDC専用アカウントのユーザー名を入力してください", - "linkAccountOidcNotFound": "そのユーザー名のOIDC専用アカウントは見つかりませんでした。", - "linkAccounts": "アカウントをリンクする", + "linkAccountOidcPlaceholder": "統合するOIDC専用アカウントのユーザー名を入力", + "linkAccountOidcNotFound": "そのユーザー名のOIDC専用アカウントは見つかりませんでした", + "linkAccounts": "アカウントをリンク", "linkAccountSuccess": "アカウントのリンクに成功しました", "linkAccountFailed": "アカウントのリンクに失敗しました", "linkAccountInProgress": "リンク中...", - "unlinkAccountTitle": "OIDCのリンク解除", - "unlinkAccountDesc": "{{username}}から OIDC 認証を削除してください。これにより、ユーザーはパスワードでのみログインできるようになります。", - "unlinkAccountWarning": "これにより、このアカウントからOIDCログインが削除されます。ログインを続けるには、ユーザーはパスワードを設定する必要があります。", - "unlinkAccount": "OIDCのリンク解除", - "unlinkAccountInProgress": "リンクを解除しています...", - "unlinkAccountSuccess": "OIDCのリンク解除に成功しました。", - "unlinkAccountFailed": "OIDCのリンク解除に失敗しました", + "unlinkAccountTitle": "OIDCリンク解除", + "unlinkAccountDesc": "{{username}} からOIDC認証を削除します。パスワードでのみログイン可能になります。", + "unlinkAccountWarning": "このアカウントからOIDCログインが削除されます。引き続きログインするにはパスワードが設定されている必要があります。", + "unlinkAccount": "OIDCリンク解除", + "unlinkAccountInProgress": "リンク解除中...", + "unlinkAccountSuccess": "OIDCリンク解除に成功しました", + "unlinkAccountFailed": "OIDCリンク解除に失敗しました", "saving": "保存中...", "updateRegistrationFailed": "登録設定の更新に失敗しました", "updatePasswordLoginFailed": "パスワードログイン設定の更新に失敗しました", - "cannotDisablePasswordLoginWithTotp": "1人以上のユーザーに対して2段階認証が有効になっている場合、パスワードログインを無効にすることはできません。まず2段階認証を無効にしてください。", + "cannotDisablePasswordLoginWithTotp": "1人以上のユーザーで2FAが有効な間はパスワードログインを無効にできません。最初に2FAを無効にしてください。", "updateOidcAutoProvisionFailed": "OIDC自動プロビジョニング設定の更新に失敗しました", "updateOidcSilentLoginDefaultFailed": "サイレントOIDCログイン設定の更新に失敗しました", "updatePasswordResetFailed": "パスワードリセット設定の更新に失敗しました", - "sessionTimeoutRange2": "セッションタイムアウトは1時間から720時間の間で設定する必要があります。", - "sessionTimeoutSaved": "セッションタイムアウトが保存されました", - "sessionTimeoutSaveFailed": "セッションの保存に失敗しました(タイムアウト)", - "monitoringIntervalInvalid": "無効な区間値", - "monitoringSaved": "監視設定が保存されました", + "sessionTimeoutRange2": "セッションタイムアウトは1時間から720時間の間である必要があります", + "sessionTimeoutSaved": "セッションタイムアウトを保存しました", + "sessionTimeoutSaveFailed": "セッションタイムアウトの保存に失敗しました", + "monitoringIntervalInvalid": "無効な間隔値です", + "monitoringSaved": "監視設定を保存しました", "monitoringSaveFailed": "監視設定の保存に失敗しました", - "metricsHistoryRetention": "指標履歴の保持", - "metricsHistoryRetentionRange": "1~90日", + "metricsHistoryRetention": "メトリクス履歴保持期間", + "metricsHistoryRetentionRange": "1〜90日", "days": "日", - "guacamoleSaved": "グアカモレの設定が保存されました", - "guacamoleSaveFailed": "Guacamoleの設定を保存できませんでした", - "guacamoleUpdateFailed": "Guacamoleの設定の更新に失敗しました", - "tailscaleSettingsSaved": "テールスケールの設定が保存されました", - "tailscaleSettingsSaveFailed": "テールスケールの設定を保存できませんでした", + "guacamoleSaved": "Guacamole設定を保存しました", + "guacamoleSaveFailed": "Guacamole設定の保存に失敗しました", + "guacamoleUpdateFailed": "Guacamole設定の更新に失敗しました", + "tailscaleSettingsSaved": "Tailscale設定を保存しました", + "tailscaleSettingsSaveFailed": "Tailscale設定の保存に失敗しました", "logLevelUpdateFailed": "ログレベルの更新に失敗しました", - "oidcSaved": "OIDC設定が保存されました", + "oidcSaved": "OIDC設定を保存しました", "oidcSaveFailed": "OIDC設定の保存に失敗しました", - "oidcRemoved": "OIDC構成が削除されました", + "oidcRemoved": "OIDC設定を削除しました", "oidcRemoveFailed": "OIDC設定の削除に失敗しました", "createUserRequired": "ユーザー名とパスワードが必要です", - "createUserPasswordTooShort": "パスワードは6文字以上である必要があります", - "createUserSuccess": "ユーザー \"{{username}}\" が作成しました", + "createUserPasswordTooShort": "パスワードは6文字以上必要です", + "createUserSuccess": "ユーザー \"{{username}}\" を作成しました", "createUserFailed": "ユーザーの作成に失敗しました", - "updateAdminStatusFailed": "管理者ステータスの更新に失敗しました", - "allSessionsRevoked": "すべてのセッションが取り消されました", - "revokeSessionsFailed": "セッションの取り消しに失敗しました", + "updateAdminStatusFailed": "管理者権限の更新に失敗しました", + "allSessionsRevoked": "すべてのセッションを破棄しました", + "revokeSessionsFailed": "セッションの破棄に失敗しました", "manageUserData": "ユーザーデータの管理", "backToUsers": "ユーザー一覧に戻る", "manageTabAccount": "アカウント", - "manageTabHosts": "主催者", - "manageTabCredentials": "資格", - "manageTabSnippets": "断片", + "manageTabHosts": "ホスト", + "manageTabCredentials": "認証情報", + "manageTabSnippets": "スニペット", "manageTabSessions": "セッション", "manageTabDanger": "危険", "manageEditorBack": "{{username}}のデータに戻る", "dataLockedBadge": "ロック済み", - "dataLockedNotice": "{{username}}のデータは、次回ログインするまでロックされたままです。それまでは、ホスト、認証情報、スニペットを表示したり編集したりすることはできません。", + "dataLockedNotice": "{{username}} のデータは次回ログインまでロックされたままです。それまで、ホスト、認証情報、スニペットの閲覧や編集はできません。", "resetPasswordTitle": "パスワードをリセット", - "resetPasswordOidcOnly": "このユーザーは外部プロバイダー経由でログインしており、パスワードは設定されていません。", + "resetPasswordOidcOnly": "このユーザーは外部プロバイダー経由でサインインしており、パスワードはありません。", "resetPasswordPlaceholder": "新しいパスワード", "resetPasswordBtn": "リセット", "resetPasswordWorking": "リセット中...", - "resetPasswordSuccess": "パスワードリセット", - "resetPasswordSuccessWiped": "パスワードがリセットされました。ユーザーの暗号化されたデータは消去されました。", + "resetPasswordSuccess": "パスワードがリセットされました", + "resetPasswordSuccessWiped": "パスワードがリセットされました。ユーザーの暗号化データは消去されました。", "resetPasswordFailed": "パスワードのリセットに失敗しました", - "resetPasswordConfirmWipe": "{{username}} は暗号化アップグレード以降ログインしていないため、データは復元できません。今すぐリセットすると、ホスト、認証情報、スニペットが削除されます。続行しますか?", - "totpSectionTitle": "二段階認証", - "totpStatusEnabled": "このユーザーに対してTOTPが有効になっています", - "totpStatusDisabled": "このユーザーに対してTOTPは有効になっていません", - "disableTotp": "無効にする", - "disableTotpConfirm": "{{username}}の二段階認証を無効にしてください。パスワードだけでログインできるようになります。", - "totpDisabledSuccess": "TOTPが無効になっています", + "resetPasswordConfirmWipe": "{{username}} は暗号化アップグレード以降にログインしていないため、データを復旧できません。今リセットするとホスト、認証情報、スニペットが削除されます。続行しますか?", + "totpSectionTitle": "二要素認証", + "totpStatusEnabled": "このユーザーはTOTPが有効です", + "totpStatusDisabled": "このユーザーはTOTPが無効です", + "disableTotp": "無効化", + "disableTotpConfirm": "{{username}} の二要素認証を無効にしますか? パスワードのみでログインできるようになります。", + "totpDisabledSuccess": "TOTPが無効化されました", "totpDisableFailed": "TOTPの無効化に失敗しました", "manageApiKeys": "APIキー", "apiKeyNamePlaceholder": "キー名", - "apiKeyCopyNotice": "このキーを今すぐコピーしてください。二度と表示されません。", - "noApiKeysForUser": "APIキーなし", + "apiKeyCopyNotice": "このキーを今コピーしてください。再度表示されません。", + "noApiKeysForUser": "APIキーがありません", "apiKeyDeleteFailed": "APIキーの削除に失敗しました", "exportUserData": "データエクスポート", - "exportUserDataDesc": "このユーザーのホスト、認証情報、ファイルマネージャーのデータをJSON形式でダウンロードします。秘密情報は復号化されています。", - "exportUserDataSuccess": "ユーザーデータのエクスポート", + "exportUserDataDesc": "このユーザーのホスト、認証情報、ファイルマネージャーデータをJSONでダウンロードします。シークレットは復号化されます。", + "exportUserDataSuccess": "ユーザーデータがエクスポートされました", "exportUserDataFailed": "ユーザーデータのエクスポートに失敗しました", "hostsCount": "{{count}} ホスト", "addHostForUser": "ホストを追加", "noHostsForUser": "このユーザーにはホストがありません", - "connectToHost": "接続する", - "deleteHostConfirm": "Delete host \"{{name}}\" belonging to {{username}}?", + "connectToHost": "接続", + "deleteHostConfirm": "{{username}} のホスト \"{{name}}\" を削除しますか?", "hostDeletedSuccess": "ホストが削除されました", "hostDeleteFailed": "ホストの削除に失敗しました", "credentialsCount": "{{count}} 認証情報", "addCredentialForUser": "認証情報を追加", - "noCredentialsForUser": "このユーザーには認証情報がありません", - "deleteCredentialConfirm": "{{username}} に属する認証情報 \"{{name}}\" を削除しますか?", - "credentialDeletedSuccess": "認証情報が削除されました", - "credentialDeleteFailed": "認証情報の削除に失敗しました", - "snippetsCount": "{{count}} スニペット", + "noCredentialsForUser": "このユーザーには資格情報がありません", + "deleteCredentialConfirm": "{{username}} に属する資格情報 \"{{name}}\" を削除しますか?", + "credentialDeletedSuccess": "資格情報を削除しました", + "credentialDeleteFailed": "資格情報の削除に失敗しました", + "snippetsCount": "{{count}} 個のスニペット", "addSnippetForUser": "スニペットを追加", - "noSnippetsForUser": "このユーザーにはスニペットがありません", - "snippetRequiredFields": "スニペット名とコンテンツは必須です。", + "noSnippetsForUser": "このユーザーはスニペットを持っていません", + "snippetRequiredFields": "スニペット名と内容は必須です", "snippetNamePlaceholder": "スニペット名", "snippetContentPlaceholder": "コマンドの内容", - "snippetFolderPlaceholder": "フォルダ(オプション)", - "snippetSaved": "スニペットが保存されました", + "snippetFolderPlaceholder": "フォルダー (任意)", + "snippetSaved": "スニペットを保存しました", "snippetSaveFailed": "スニペットの保存に失敗しました", "deleteSnippetConfirm": "{{username}} に属するスニペット \"{{name}}\" を削除しますか?", - "snippetDeletedSuccess": "抜粋が削除されました", + "snippetDeletedSuccess": "スニペットを削除しました", "snippetDeleteFailed": "スニペットの削除に失敗しました", "noSessionsForUser": "アクティブなセッションはありません", - "deleteUserDangerDesc": "{{username}} とそのすべてのデータ(ホスト、認証情報、スニペット、履歴)を完全に削除します。この操作は元に戻せません。", + "deleteUserDangerDesc": "{{username}} とそのすべてのデータ (ホスト、資格情報、スニペット、履歴) を完全に削除します。この操作は元に戻せません。", "deleteUserConfirm": "{{username}} とそのすべてのデータを完全に削除しますか?", - "deleteUserAdminBlocked": "このユーザーを削除する前に、管理者権限を解除してください。", - "createRoleRequired": "名前と表示名は必須です。", + "deleteUserAdminBlocked": "このユーザーを削除する前に管理者権限を解除してください。", + "createRoleRequired": "名前と表示名は必須です", "createRoleSuccess": "ロール \"{{name}}\" が作成されました", "createRoleFailed": "ロールの作成に失敗しました", - "apiKeyNameRequired": "キー名は必須です。", - "apiKeyUserRequired": "ユーザーIDが必要です", - "apiKeyCreatedSuccess": "APIキー「{{name}}」が作成されました", - "apiKeyCreateFailed": "APIキーの作成に失敗しました", - "exportSuccess": "データベースのエクスポートに成功しました。", + "apiKeyNameRequired": "キー名は必須です", + "apiKeyUserRequired": "ユーザー ID は必須です", + "apiKeyCreatedSuccess": "API キー \"{{name}}\" が作成されました", + "apiKeyCreateFailed": "API キーの作成に失敗しました", + "exportSuccess": "データベースのエクスポートに成功しました", "exportFailed": "データベースのエクスポートに失敗しました", - "importSelectFile": "まずファイルを選択してください", - "importCompleted": "インポート完了: {{total}} 個のアイテムがインポートされ、 {{skipped}} 個がスキップされました。", + "importSelectFile": "最初にファイルを選択してください", + "importCompleted": "インポートが完了しました: {{total}} 件をインポートし、{{skipped}} 件をスキップしました", "importFailed": "インポートに失敗しました: {{error}}", "importError": "データベースのインポートに失敗しました", "sectionHostDefaults": "ホストのデフォルト設定", - "hostDefaultsDesc": "設定は新規ホスト作成時に自動的に適用されます。ただし、個々のホストはこれらの設定を上書きすることも可能です。", - "hostDefaultsSocks5": "SOCKS5プロキシ", - "hostDefaultsUseSocks5": "SOCKS5プロキシを有効にする", - "hostDefaultsUseSocks5Desc": "すべての新規ホストにSOCKS5プロキシを事前設定する", - "hostDefaultsSocks5Host": "プロキシホスト/ポート", + "hostDefaultsDesc": "新しいホストを作成するときに自動的に適用される設定です。個別のホストで上書き可能です。", + "hostDefaultsSocks5": "SOCKS5 プロキシ", + "hostDefaultsUseSocks5": "SOCKS5 プロキシを有効にする", + "hostDefaultsUseSocks5Desc": "すべての新規ホストで SOCKS5 プロキシを事前入力する", + "hostDefaultsSocks5Host": "プロキシホスト / ポート", "hostDefaultsSocks5Username": "プロキシユーザー名", "hostDefaultsSocks5Password": "プロキシパスワード", "hostDefaultsMetrics": "ホストメトリクス", "hostDefaultsMetricsEnabled": "メトリクスを有効にする", - "hostDefaultsMetricsEnabledDesc": "新規ホストのCPU、メモリ、その他の統計情報をデフォルトで収集する", + "hostDefaultsMetricsEnabledDesc": "デフォルトで新規ホストのCPU、メモリなどの統計情報を収集する", "hostDefaultsStatusCheckEnabled": "ステータスチェックを有効にする", - "hostDefaultsStatusCheckEnabledDesc": "新規ホストのオンライン/オフライン状態をデフォルトで確認する", + "hostDefaultsStatusCheckEnabledDesc": "デフォルトで新規ホストのオンライン/オフライン状態をポーリングする", "hostDefaultsTerminal": "ターミナル", - "hostDefaultsSessionLogging": "セッションログ", - "hostDefaultsSessionLoggingDesc": "デフォルトでは、新しいホスト上のターミナルセッションを記録する", + "hostDefaultsSessionLogging": "セッションロギング", + "hostDefaultsSessionLoggingDesc": "デフォルトで新規ホストのターミナルセッションを記録する", "hostDefaultsCommandHistory": "コマンド履歴", - "hostDefaultsCommandHistoryDesc": "デフォルトでは、新しいホストでコマンド履歴を追跡します。", - "hostDefaultsSaved": "ホストのデフォルト設定が保存されました", + "hostDefaultsCommandHistoryDesc": "デフォルトで新規ホストのコマンド履歴を追跡する", + "hostDefaultsSaved": "ホストのデフォルト設定を保存しました", "hostDefaultsSaveFailed": "ホストのデフォルト設定の保存に失敗しました", "rolePermissions": { - "count": "{{count}} 権限", - "editAction": "編集権限", + "count": "{{count}} 件の権限", + "editAction": "権限を編集", "loadError": "権限カタログの読み込みに失敗しました", - "saved": "ロール権限が保存されました", - "saveError": "ロール権限の保存に失敗しました", + "saved": "ロールの権限を保存しました", + "saveError": "ロールの権限の保存に失敗しました", "save": "保存", "saving": "保存中..." } @@ -2966,97 +3117,97 @@ "passwordLabel": "パスワード", "passwordPlaceholder": "パスワード", "privateKeyLabel": "秘密鍵", - "privateKeyPlaceholder": "秘密鍵を貼り付けてください...", - "credentialLabel": "資格証明書", - "credentialPlaceholder": "保存済みの認証情報を選択してください", + "privateKeyPlaceholder": "プライベートキーを貼り付け...", + "credentialLabel": "認証情報", + "credentialPlaceholder": "保存済み認証情報を選択", "connectToTerminal": "ターミナルに接続", - "connectToFiles": "ファイルに接続する" + "connectToFiles": "ファイルに接続" }, "history": { - "noTerminalSelected": "端末が選択されていません", - "noTerminalSelectedHint": "SSHターミナルタブを開いてコマンド履歴を表示する", - "searchPlaceholder": "検索履歴...", + "noTerminalSelected": "ターミナルが選択されていません", + "noTerminalSelectedHint": "SSH ターミナルタブを開いてコマンド履歴を表示", + "searchPlaceholder": "履歴を検索...", "clearAll": "すべてクリア", - "noHistoryEntries": "履歴エントリなし", - "trackingDisabled": "履歴追跡は無効になっています", - "trackingDisabledHint": "ホストの端末設定で有効にしてください。" + "noHistoryEntries": "履歴エントリがありません", + "trackingDisabled": "履歴の追跡が無効です", + "trackingDisabledHint": "ホストのターミナル設定で有効にしてください。" }, "sshTools": { - "keyRecordingTitle": "キーレコーディング", - "recordToTerminals": "端末への記録", - "selectAll": "全て", + "keyRecordingTitle": "キー記録", + "recordToTerminals": "ターミナルに記録", + "selectAll": "すべて", "selectNone": "なし", - "noTerminalTabsOpen": "ターミナルタブは開いていません", - "selectTerminalsAbove": "上記の端末を選択してください", - "broadcastInputPlaceholder": "ここに入力してキーストロークを送信します...", - "fillPassword": "パスワードを入力してください", - "fillPasswordSuccess": "{{count}} 端末にパスワードを入力しました", - "fillPasswordMissing": "選択された端末 {{count}} には保存されたパスワードがありません", - "stopRecording": "録画を停止", - "startRecording": "録画を開始", + "noTerminalTabsOpen": "ターミナルタブが開いていません", + "selectTerminalsAbove": "上のターミナルを選択", + "broadcastInputPlaceholder": "ここに入力してキーをブロードキャスト...", + "fillPassword": "パスワードを自動入力", + "fillPasswordSuccess": "{{count}} 個のターミナルにパスワードを入力しました", + "fillPasswordMissing": "選択した {{count}} 個のターミナルに保存されたパスワードがありません", + "stopRecording": "記録停止", + "startRecording": "記録開始", "settingsTitle": "設定", - "enableRightClickCopyPaste": "右クリックでのコピー/ペーストを有効にする" + "enableRightClickCopyPaste": "右クリックでのコピー/貼り付けを有効にする" }, "splitScreen": { "layoutTitle": "レイアウト", - "selectLayoutAbove": "上のレイアウトを選択してください", - "selectLayoutHint": "表示するペインの数を選択してください", - "panesTitle": "窓ガラス", + "selectLayoutAbove": "上のレイアウトを選択", + "selectLayoutHint": "表示するペインの数を選択", + "panesTitle": "ペイン", "openTabsTitle": "開いているタブ", - "dragTabsHint": "タブを上のペインにドラッグするか、クイック割り当てを使用してください。", + "dragTabsHint": "タブを上のペインにドラッグするか、クイック割り当てを使用します", "dropHere": "ここにドロップ", - "emptyPane": "空の", + "emptyPane": "空", "dashboard": "ダッシュボード", - "clearSplitScreen": "クリアスプリットスクリーン", + "clearSplitScreen": "分割画面をクリア", "quickAssign": "クイック割り当て", "alreadyAssigned": "ペイン {{index}}", - "splitTab": "分割タブ", + "splitTab": "タブを分割", "addToSplit": "分割に追加", "removeFromSplit": "分割から削除", - "assignToPane": "ペインに割り当てる", + "assignToPane": "ペインに割り当て", "hotkeysTitle": "キーボードショートカット", "hotkeysSplitRight": "2分割表示の切り替え", "hotkeysSplitBelow": "3分割表示の切り替え", - "hotkeysNavigatePane": "ペインのナビゲーション", + "hotkeysNavigatePane": "ペイン間の移動", "hotkeysNextTab": "次のタブ", "hotkeysPrevTab": "前のタブ" }, "snippets": { - "title": "断片", - "createSnippetTitle": "スニペットを作成する", - "createSnippetDescription": "素早く実行するための新しいコマンドスニペットを作成します", + "title": "スニペット", + "createSnippetTitle": "スニペットを作成", + "createSnippetDescription": "クイック実行用の新しいコマンドスニペットを作成します", "nameLabel": "名前", - "namePlaceholder": "例:Nginxを再起動する", + "namePlaceholder": "例: Nginxの再起動", "descriptionLabel": "説明", - "descriptionPlaceholder": "オプションの説明", - "optional": "オプション", + "descriptionPlaceholder": "任意の説明", + "optional": "任意", "folderLabel": "フォルダ", - "noFolder": "フォルダなし(未分類)", - "commandLabel": "指示", - "commandPlaceholder": "例:sudo systemctl restart nginx", + "noFolder": "フォルダなし (未分類)", + "commandLabel": "コマンド", + "commandPlaceholder": "例: sudo systemctl restart nginx", "cancel": "キャンセル", - "createSnippetButton": "スニペットを作成する", - "createFolderTitle": "フォルダを作成する", - "createFolderDescription": "スニペットをフォルダに整理する", + "createSnippetButton": "スニペットを作成", + "createFolderTitle": "フォルダを作成", + "createFolderDescription": "スニペットをフォルダに整理します", "folderNameLabel": "フォルダ名", - "folderNamePlaceholder": "例:システムコマンド、Dockerスクリプト", - "folderColorLabel": "フォルダーの色", + "folderNamePlaceholder": "例: システムコマンド, Dockerスクリプト", + "folderColorLabel": "フォルダの色", "folderIconLabel": "フォルダアイコン", "previewLabel": "プレビュー", "folderNameFallback": "フォルダ名", - "createFolderButton": "フォルダを作成する", + "createFolderButton": "フォルダを作成", "targetTerminals": "対象端末", - "selectAll": "全て", - "selectNone": "なし", - "noTerminalTabsOpen": "ターミナルタブは開いていません", - "searchPlaceholder": "検索スニペット...", - "newSnippet": "新しいスニペット", - "newFolder": "新しいフォルダ", - "run": "走る", - "noSnippetsInFolder": "このフォルダにはスニペットがありません", + "selectAll": "すべて選択", + "selectNone": "すべて解除", + "noTerminalTabsOpen": "開いている端末タブはありません", + "searchPlaceholder": "スニペットを検索...", + "newSnippet": "新規スニペット", + "newFolder": "新規フォルダ", + "run": "実行", + "noSnippetsInFolder": "このフォルダにスニペットはありません", "uncategorized": "未分類", - "editSnippetTitle": "スニペットを編集", - "editSnippetDescription": "このコマンドスニペットを更新してください", + "editSnippetTitle": "スニペットの編集", + "editSnippetDescription": "このコマンドスニペットを更新", "saveSnippetButton": "変更を保存", "createSuccess": "スニペットが正常に作成されました", "createFailed": "スニペットの作成に失敗しました", @@ -3065,328 +3216,390 @@ "deleteFailed": "スニペットの削除に失敗しました", "folderCreateSuccess": "フォルダが正常に作成されました", "folderCreateFailed": "フォルダの作成に失敗しました", - "editFolderTitle": "フォルダを編集", - "editFolderDescription": "このフォルダーの名前を変更したり、外観を変更したりする", + "editFolderTitle": "フォルダの編集", + "editFolderDescription": "フォルダの名前や外観を変更", "saveFolderButton": "変更を保存", "editFolder": "フォルダを編集", - "deleteFolder": "フォルダを削除する", - "folderDeleteSuccess": "フォルダ \"{{name}}\" が削除されました", + "deleteFolder": "フォルダを削除", + "folderDeleteSuccess": "フォルダ「{{name}}」を削除しました", "folderDeleteFailed": "フォルダの削除に失敗しました", - "folderEditSuccess": "フォルダの更新に成功しました", + "folderEditSuccess": "フォルダが正常に更新されました", "folderEditFailed": "フォルダの更新に失敗しました", "confirmRunMessage": "「{{name}}」を実行しますか?", - "confirmRunButton": "走る", - "runSuccess": "{{name}}\" を {{count}} 端末で実行しました", - "copySuccess": "\"{{name}}\" をクリップボードにコピーしました", + "confirmRunButton": "実行", + "runSuccess": "{{count}}個の端末で「{{name}}」を実行しました", + "copySuccess": "「{{name}}」をクリップボードにコピーしました", "shareTitle": "スニペットを共有", "shareUser": "ユーザー", - "shareRole": "役割", - "selectUser": "ユーザーを選択してください...", - "selectRole": "役割を選択してください...", - "shareSuccess": "スニペットの共有に成功しました", + "shareRole": "ロール", + "selectUser": "ユーザーを選択...", + "selectRole": "ロールを選択...", + "shareSuccess": "スニペットが正常に共有されました", "shareFailed": "スニペットの共有に失敗しました", - "revokeSuccess": "アクセス権が取り消されました", - "revokeFailed": "アクセス権の取り消しに失敗しました", - "currentAccess": "現在のアクセス状況", + "revokeSuccess": "アクセスが取り消されました", + "revokeFailed": "アクセスの取り消しに失敗しました", + "currentAccess": "現在のアクセス権", "shareLoadError": "共有データの読み込みに失敗しました", - "loading": "読み込み中…", - "close": "近い", - "reorderFailed": "スニペットの順序を保存できませんでした", - "importExport": "輸入/輸出", + "loading": "読み込み中...", + "close": "閉じる", + "reorderFailed": "スニペットの並び順の保存に失敗しました", + "importExport": "インポート / エクスポート", "exportBtn": "JSONをエクスポート", "importBtn": "JSONをインポート", - "exportSuccess": "スニペットのエクスポートに成功しました", + "exportSuccess": "スニペットが正常にエクスポートされました", "exportFailed": "スニペットのエクスポートに失敗しました", "importTitle": "スニペットのインポート", - "importDescription": "TermixによってエクスポートされたJSONファイルからスニペットとフォルダをインポートします。", - "importDropOrClick": "JSONファイルをここにドロップするか、クリックして参照してください。", - "importSelectedFile": "選択済み: {{name}}", - "importOverwrite": "同じ名前とフォルダの既存のスニペットを上書きします", - "importStartBtn": "輸入", - "importSuccess": "インポート完了: {{snippets}} 個のスニペットが追加され、 {{updated}} 個が更新され、 {{skipped}} 個がスキップされ、 {{folders}} 個のフォルダが追加されました", + "importDescription": "Termix がエクスポートした JSON ファイルからスニペットとフォルダーをインポートします", + "importDropOrClick": "JSON ファイルをここにドロップするか、クリックして参照", + "importSelectedFile": "選択中: {{name}}", + "importOverwrite": "同じ名前とフォルダーの既存スニペットを上書きする", + "importStartBtn": "インポート", + "importSuccess": "インポート完了: {{snippets}} 件のスニペット追加、{{updated}} 件更新、{{skipped}} 件スキップ、{{folders}} 件のフォルダー追加", "importFailed": "スニペットのインポートに失敗しました", - "importInvalidFile": "無効なファイルです。スニペットまたはフォルダ配列を含むJSONオブジェクトが必要です。", - "targetHostsLabel": "対象ホスト", - "targetHostsHint": "ホストに、ターミナルを開かずにこのコードスニペットを直接実行するよう割り当てます。", - "noHostsAvailable": "ホストは設定されていません", + "importInvalidFile": "無効なファイルです: snippets または folders 配列を含む JSON オブジェクトが必要です", + "targetHostsLabel": "ターゲットホスト", + "targetHostsHint": "このスニペットを端末を開かずに直接実行するホストを割り当てます。", + "noHostsAvailable": "設定されたホストがありません", "clearTargetHosts": "すべてクリア", - "hasTargetHosts": "ターゲットホストがあります", - "runOnTargets": "目標に向かって走る", - "directRunSuccess": "{{name}}を {{count}} ホストで実行しました", - "directRunPartialFail": "\"{{name}}\" が 1 つ以上のホストで失敗しました", + "hasTargetHosts": "ターゲットホストあり", + "runOnTargets": "ターゲットで実行", + "directRunSuccess": "\"{{name}}\" を {{count}} 台のホストで実行しました", + "directRunPartialFail": "\"{{name}}\" が1つ以上のホストで失敗しました", "executionResultTitle": "実行結果: {{name}}", - "executionResultDescription": "各ターゲットホストでコードスニペットを実行した際の出力。", + "executionResultDescription": "各ターゲットホストでスニペットを実行した結果の出力です。", "executionSuccess": "成功", - "executionFailed": "失敗した" + "executionFailed": "失敗" + }, + "keybindings": { + "title": "キーボードショートカット", + "description": "ターミナルのコピー、ペースト、コントロールのショートカットをカスタマイズしたり、テキストの送信やスニペットの実行にキーを割り当てたりできます。", + "defaultsHeading": "組み込みショートカット", + "customHeading": "カスタムショートカット", + "addBinding": "ショートカットを追加", + "addBindingTitle": "ショートカットを追加", + "editBindingTitle": "編集ショートカット", + "loading": "読み込み中…", + "noCustomBindings": "カスタムショートカットはまだありません", + "defaultBadge": "デフォルト", + "customizedBadge": "カスタマイズされた", + "customize": "カスタマイズ", + "resetToDefault": "初期設定に戻す", + "resetAllToDefaults": "すべてをデフォルト設定に戻す", + "close": "近い", + "cancel": "キャンセル", + "saveBinding": "ショートカットを保存", + "saveError": "キーボードショートカットの保存に失敗しました", + "pressKeysToRecord": "キーの組み合わせ", + "pressKeysPlaceholder": "クリックしてキーを押す", + "recording": "キーを押してください...", + "comboRequiredError": "まずキーの組み合わせを押してください", + "textRequiredError": "送信するテキストを入力してください", + "controlCodeRequiredError": "制御コードとして1文字を入力してください", + "snippetRequiredError": "抜粋を選択してください", + "conflictWarning": "この組み合わせは既に {{combo}}にバインドされています。保存すると両方とも発火します。", + "actionLabel": "アクション", + "actionCopy": "選択範囲をコピー", + "actionPaste": "ペースト", + "actionSendControlCode": "Ctrlキーと文字キーを同時に押して信号を送る", + "actionSendText": "テキストを送信", + "actionRunSnippet": "既存のスニペットを実行する", + "controlCodeLabel": "手紙", + "textLabel": "送信するテキスト", + "appendEnterLabel": "送信後にEnterキーを押してください。", + "snippetLabel": "抜粋", + "selectSnippetPlaceholder": "抜粋を選択してください", + "orphanedSnippetWarning": "スニペットが見つかりません", + "clipboardPermissionNote": "一部のブラウザでは、初回使用時にクリップボードのアクセス許可を求められる場合があります。" }, "userProfile": { - "donateTitle": "Termixを存続させよう", - "donateDescription": "Termixは無料で、セルフホスティング方式で、少数の開発者が余暇を利用して開発しています。もしTermixがあなたの時間や費用を節約できたなら、仮想通貨での寄付が開発継続の助けになります。", - "donateMilestones": "寄付金は、SAML、Kubernetes、およびエージェントのサポート構築に必要な調査や学習に費やす時間を支援するために活用されます。進捗状況を確認し、ぜひご寄付をお願いいたします。", - "donateButton": "仮想通貨を寄付する", + "donateTitle": "Termixを支援する", + "donateDescription": "Termixは無料で自己ホスト型、少数の開発者が余暇に開発しています。時間や費用の節約になったなら、仮想通貨による寄付が継続の助けになります。", + "donateMilestones": "寄付は、SAML、Kubernetes、Agentサポートの構築に必要な調査や学習の時間を賄うのに役立ちます。進捗を見て寄付してください。", + "donateButton": "仮想通貨で寄付", "storageModeLocal": "ブラウザ", "storageModeCloud": "データベース", - "storageModeDescription": "ブラウザは設定をこのブラウザのみに保存します。データベースはサーバーと同期され、あらゆるデバイスに読み込まれます。", - "resetToDefaults": "初期設定に戻す", - "resetToDefaultsSuccess": "設定が初期状態に戻ります。", - "storageModeSwitch": "設定の保存", + "storageModeDescription": "ブラウザは設定をこのブラウザにのみ保存します。データベースはサーバーと同期し、任意のデバイスで読み込めます。", + "resetToDefaults": "デフォルトにリセット", + "resetToDefaultsSuccess": "設定がデフォルトにリセットされました。", + "storageModeSwitch": "設定の保存先", "sectionAccount": "アカウント", "sectionAppearance": "外観", - "sectionSecurity": "安全", + "sectionSecurity": "セキュリティ", "sectionApiKeys": "APIキー", + "sectionData": "データ", "sectionC2sTunnels": "C2Sトンネル", "usernameLabel": "ユーザー名", "roleLabel": "役割", "roleAdministrator": "管理者", - "authMethodLabel": "認証方法", - "authMethodLocal": "地元", - "twoFaLabel": "2段階認証", - "twoFaOn": "の上", + "authMethodLabel": "認証方式", + "authMethodLocal": "ローカル", + "twoFaLabel": "2FA", + "twoFaOn": "オン", "twoFaOff": "オフ", "versionLabel": "バージョン", "betaProgramTitle": "ベータプログラム", - "betaProgramDescription": "毎週ローリングされる :beta Docker タグを使用して、今後の新機能をいち早くお試しください。不安定なため、本番環境での使用は推奨しません。", + "betaProgramDescription": "週次更新の:beta Dockerタグで先行機能をいち早くお試しください。不安定なため、本番環境では使用しないでください。", "betaProgramFeedback": "バグを見つけましたか?こちらから報告してください。", - "betaProgramLearnMore": "もっと詳しく知る", + "betaProgramLearnMore": "詳細を見る", "deleteAccount": "アカウントを削除", - "deleteAccountDescription": "アカウントを完全に削除する", + "deleteAccountDescription": "アカウントを完全に削除します", "changeServerDescription": "別のTermixバックエンドサーバーに切り替える", - "deleteButton": "消去", - "deleteAccountPermanent": "この操作は永続的なものであり、元に戻すことはできません。", - "deleteAccountWarning": "すべてのセッション、ホスト、認証情報、および設定は完全に削除されます。", - "confirmPasswordDeletePlaceholder": "確認のためパスワードを入力してください", + "deleteButton": "削除", + "deleteAccountPermanent": "この操作は永続的で元に戻せません。", + "deleteAccountWarning": "すべてのセッション、ホスト、資格情報、設定が完全に削除されます。", + "confirmPasswordDeletePlaceholder": "確認のためパスワードを入力", "languageLabel": "言語", "themeLabel": "テーマ", "fontSizeLabel": "フォントサイズ", "accentColorLabel": "アクセントカラー", "settingsTerminal": "ターミナル", - "commandAutocomplete": "コマンドのオートコンプリート", - "commandAutocompleteDesc": "入力中にオートコンプリートを表示する", - "terminalLinkBehavior": "ターミナルリンクをクリック", - "terminalLinkBehaviorDesc": "ターミナルでリンクをクリックしたときのデフォルトの動作", + "commandAutocomplete": "コマンド補完", + "commandAutocompleteDesc": "入力中に補完を表示", + "keyboardShortcuts": "キーボードショートカット", + "keyboardShortcutsDescription": "ターミナルのコピー、貼り付け、コマンドのショートカットをカスタマイズする", + "manageShortcuts": "管理", + "terminalLinkBehavior": "ターミナルリンククリック", + "terminalLinkBehaviorDesc": "ターミナル内のリンクをクリックしたときのデフォルト動作", "historyTracking": "履歴追跡", - "historyTrackingDesc": "端末コマンドを追跡する", + "historyTrackingDesc": "ターミナルコマンドの履歴を追跡", "commandPalette": "コマンドパレット", - "commandPaletteDesc": "キーボードショートカットを有効にする", - "reopenTabsOnLogin": "ログイン時にタブを再度開く", - "reopenTabsOnLoginDesc": "別のデバイスからログインしたりページを更新したりしても、開いているタブを復元します。", - "confirmTabClose": "確認タブを閉じる", - "confirmTabCloseDesc": "ターミナルタブを閉じる前に確認してください", + "commandPaletteDesc": "キーボードショートカットを有効化", + "reopenTabsOnLogin": "ログイン時にタブを再表示", + "reopenTabsOnLoginDesc": "ログイン時またはページ更新時に、別のデバイスからでも開いていたタブを復元します", + "confirmTabClose": "タブを閉じる前に確認", + "confirmTabCloseDesc": "ターミナルタブを閉じる前に確認を表示", "settingsSidebar": "サイドバー", "showHostTags": "ホストタグを表示", - "showHostTagsDesc": "ホストリストにタグを表示する", - "hostTrayOnClick": "クリックしてホストアクションを展開", - "hostTrayOnClickDesc": "接続ボタンを常に表示する。管理オプションを展開するには、マウスオーバーではなくクリックする。", - "compactHostView": "コンパクトホストビュー", - "compactHostViewDesc": "各ホストを名前とアドレスのみを表示する1行にまとめます。", - "statusColors": "リアルステータスカラー", - "statusColorsDesc": "アクセントカラーの代わりに、オンライン/オフラインの状態には緑/赤を使用してください。", - "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "左サイドバーのアプリレールを、マウスオーバー時ではなく常に展開した状態にする。", - "expandAppRailOnHover": "マウスオーバー時にアプリレールを展開", - "expandAppRailOnHoverDesc": "ポインターが左サイドバーアプリレールの上に移動したときに、左サイドバーアプリレールが展開するようにする。", + "showHostTagsDesc": "ホスト一覧でタグを表示", + "hostTrayOnClick": "クリックでホストアクションを展開", + "hostTrayOnClickDesc": "接続ボタンを常に表示し、ホバーではなくクリックで管理オプションを展開します", + "compactHostView": "コンパクトなホスト表示", + "compactHostViewDesc": "各ホストを名前とアドレスのみの1行に折りたたみます", + "statusColors": "実際のステータスカラー", + "statusColorsDesc": "オンライン/オフラインのステータスにアクセントカラーではなく緑/赤を使用します", + "pinAppRail": "アプリレールを固定", + "pinAppRailDesc": "左側のサイドバーアプリレールを常に展開状態に保ち、ホバーで展開しないようにします", + "expandAppRailOnHover": "ホバーでアプリレールを展開", + "expandAppRailOnHoverDesc": "ポインターが上に移動したときに左側のサイドバーアプリレールを展開できるようにします", "settingsNavigation": "ナビゲーション", - "navigationTabsDesc": "アプリレールに表示するタブを選択します", - "settingsSnippets": "断片", - "foldersCollapsed": "折りたたまれたフォルダ", + "navigationTabsDesc": "アプリレールに表示するタブを選択", + "settingsSnippets": "スニペット", + "foldersCollapsed": "フォルダを折りたたむ", "foldersCollapsedDesc": "デフォルトでフォルダを折りたたむ", "confirmExecution": "実行確認", - "confirmExecutionDesc": "スニペットを実行する前に確認してください", - "settingsUpdates": "最新情報", - "disableUpdateChecks": "アップデートチェックを無効にする", - "disableUpdateChecksDesc": "アップデートの確認を停止します", - "totpAuthenticator": "TOTP認証アプリ", - "totpEnabled": "2段階認証が有効になっています", - "totpDisabled": "ログインセキュリティを強化する", + "confirmExecutionDesc": "スニペット実行前に確認する", + "settingsUpdates": "アップデート", + "disableUpdateChecks": "アップデートチェックを無効化", + "disableUpdateChecksDesc": "アップデートの確認を停止する", + "totpAuthenticator": "TOTP認証", + "totpEnabled": "2FAが有効です", + "totpDisabled": "ログインのセキュリティを強化", "disable": "無効にする", "enable": "有効にする", - "setupTotp": "TOTPの設定", + "setupTotp": "TOTPを設定", "qrCode": "QRコード", - "totpInstructions": "認証アプリでQRコードをスキャンするか、秘密情報を入力し、6桁のコードを入力してください。", + "totpInstructions": "QRコードをスキャンするか、認証アプリにシークレットを入力し、6桁のコードを入力してください", "totpCodePlaceholder": "000000", - "verify": "確認する", - "changePassword": "パスワードを変更する", + "verify": "確認", + "changePassword": "パスワード変更", "currentPasswordLabel": "現在のパスワード", "currentPasswordPlaceholder": "現在のパスワード", "newPasswordLabel": "新しいパスワード", "newPasswordPlaceholder": "新しいパスワード", - "confirmPasswordLabel": "新しいパスワードを確認する", - "confirmPasswordPlaceholder": "新しいパスワードを確認してください", + "confirmPasswordLabel": "新しいパスワードの確認", + "confirmPasswordPlaceholder": "新しいパスワードの確認", "updatePassword": "パスワードを更新", - "createApiKeyTitle": "APIキーを作成する", - "createApiKeyDescription": "プログラムによるアクセス用に新しいAPIキーを生成します。", + "createApiKeyTitle": "APIキーを作成", + "createApiKeyDescription": "プログラムからのアクセス用に新しいAPIキーを生成します。", "apiKeyNameLabel": "名前", - "apiKeyNamePlaceholder": "例:CIパイプライン", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "有効期限", - "optional": "オプション", + "optional": "任意", "cancel": "キャンセル", - "createKey": "キーの作成", - "apiKeyCount": "{{count}} キー", - "newKey": "新しい鍵", - "noApiKeys": "まだAPIキーがありません。", - "apiKeyActive": "アクティブ", - "apiKeyUsageHint": "鍵を", - "apiKeyUsageHintHeader": "ヘッダ。", - "apiKeyPermissionsHint": "キーは作成ユーザーの権限を継承します。", + "createKey": "キーを作成", + "apiKeyCount": "{{count}}件のキー", + "newKey": "新規キー", + "noApiKeys": "APIキーがまだありません。", + "apiKeyActive": "有効", + "apiKeyUsageHint": "APIキーを", + "apiKeyUsageHintHeader": "ヘッダーに含めてください", + "apiKeyPermissionsHint": "キーは作成したユーザーの権限を引き継ぎます。", + "exportData": "データをエクスポートする", + "exportDataDesc": "ホスト、認証情報、設定のバックアップをダウンロードして、別のデバイスに転送してください。", + "export": "輸出", + "exporting": "エクスポート中...", + "importData": "データのインポート", + "importDataDesc": ".sqliteバックアップファイルからホスト、認証情報、設定を復元します。", + "importDataSelected": "選択済み: {{name}}", + "selectFile": "ファイルを選択", + "changeFile": "変化", + "import": "輸入", + "importing": "インポート中...", + "exportSuccess": "データのエクスポートに成功しました。", + "exportFailed": "データのエクスポートに失敗しました", + "importSelectFile": "まずファイルを選択してください", + "importCompleted": "インポート完了: {{total}} 個のアイテムがインポートされ、 {{skipped}} 個がスキップされました。", + "importFailed": "インポートに失敗しました: {{error}}", "roleUser": "ユーザー", - "authMethodDual": "二元認証", + "authMethodDual": "二要素認証", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP設定の開始に失敗しました", + "totpSetupFailed": "TOTPのセットアップを開始できませんでした", "totpEnter6Digits": "6桁のコードを入力してください", - "totpEnabledSuccess": "二段階認証が有効になっています", - "totpInvalidCode": "無効なコードです。もう一度お試しください。", + "totpEnabledSuccess": "二要素認証が有効になりました", + "totpInvalidCode": "コードが無効です。もう一度お試しください", "totpDisableInputRequired": "TOTPコードまたはパスワードを入力してください", - "totpDisabledSuccess": "二段階認証は無効になっています", - "totpDisableFailed": "2段階認証の無効化に失敗しました", - "totpDisableTitle": "2段階認証を無効にする", - "totpDisablePlaceholder": "TOTPコードまたはパスワードを入力してください", - "totpDisableConfirm": "2段階認証を無効にする", - "totpContinueVerify": "検証を続ける", - "totpVerifyTitle": "確認コード", + "totpDisabledSuccess": "二要素認証が無効になりました", + "totpDisableFailed": "2FAの無効化に失敗しました", + "totpDisableTitle": "2FAを無効にする", + "totpDisablePlaceholder": "TOTPコードまたはパスワードを入力", + "totpDisableConfirm": "2FAを無効にする", + "totpContinueVerify": "確認に進む", + "totpVerifyTitle": "コードの確認", "totpBackupTitle": "バックアップコード", "totpDownloadBackup": "バックアップコードをダウンロード", "passkeys": "パスキー", - "passkeysDesc": "パスワードなしでサインインするには、WebAuthn/FIDO2認証情報を使用してください。", + "passkeysDesc": "WebAuthn/FIDO2認証情報を使用してパスワードなしでサインインできます", "passkeyName": "パスキー名", - "passkeyUvPreferred": "推奨", + "passkeyUvPreferred": "優先", "passkeyUvRequired": "必須", - "passkeyUvDiscouraged": "落胆", + "passkeyUvDiscouraged": "非推奨", "addPasskey": "パスキーを追加", - "noPasskeys": "パスキーが登録されていません。", - "passkeyAdded": "Passkeyが追加されました", + "noPasskeys": "登録されたパスキーはありません。", + "passkeyAdded": "パスキーが追加されました", "passkeyAddFailed": "パスキーの追加に失敗しました", "passkeyDeleted": "パスキーが削除されました", "passkeyDeleteFailed": "パスキーの削除に失敗しました", - "done": "終わり", - "secretCopied": "秘密情報がクリップボードにコピーされました", - "apiKeyNameRequired": "キー名は必須です。", + "done": "完了", + "secretCopied": "シークレットをクリップボードにコピーしました", + "apiKeyNameRequired": "キー名は必須です", "apiKeyCreated": "APIキー「{{name}}」が作成されました", "apiKeyCreateFailed": "APIキーの作成に失敗しました", "apiKeyUser": "ユーザー", "apiKeyExpires": "有効期限", - "apiKeyRevoked": "APIキー「{{name}}」は取り消されました", + "apiKeyRevoked": "APIキー「{{name}}」は失効されました", "apiKeyRevokeFailed": "APIキーの失効に失敗しました", - "passwordFieldsRequired": "現在使用しているパスワードと新しいパスワードが必要です", + "passwordFieldsRequired": "現在のパスワードと新しいパスワードが必要です", "passwordMismatch": "パスワードが一致しません", - "passwordTooShort": "パスワードは6文字以上である必要があります", + "passwordTooShort": "パスワードは6文字以上でなければなりません", "passwordUpdated": "パスワードが正常に更新されました", "passwordUpdateFailed": "パスワードの更新に失敗しました", - "deletePasswordRequired": "アカウントを削除するにはパスワードが必要です", + "deletePasswordRequired": "アカウント削除にはパスワードが必要です", "deleteFailed": "アカウントの削除に失敗しました", "deleting": "削除中...", "colorPickerTooltip": "カラーピッカーを開く", "themeSystem": "システム", "themeLight": "ライト", - "themeDark": "暗い", - "themeDracula": "ドラキュラ", - "themeCatppuccin": "カプチーノ", - "themeNord": "ノルド", - "themeSolarized": "太陽光照射", - "themeTokyoNight": "東京の夜景", - "themeOneDark": "ワンダーク", - "themeGruvbox": "グルーヴボックス" + "themeDark": "ダーク", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { - "title": "Tmuxモニター", + "title": "Tmux モニター", "failedToLoadHosts": "ホストの読み込みに失敗しました", - "failedToLoad": "tmuxセッションの読み込みに失敗しました", - "tmuxUnavailable": "このホストにはtmuxがインストールされていません", - "noSessions": "このホストにはtmuxセッションがありません", + "failedToLoad": "Tmux セッションの読み込みに失敗しました", + "tmuxUnavailable": "このホストに tmux がインストールされていません", + "noSessions": "このホストに tmux セッションがありません", "noHostSelected": "ホストが選択されていません", - "attached": "添付", - "detached": "分離", - "attach": "添付", + "attached": "アタッチ済み", + "detached": "デタッチ済み", + "attach": "アタッチ", "editTags": "タグを編集", - "tagsHint": "カンマ区切りのタグ(例:YOLO、lab、training)", - "tagsSaved": "タグが保存されました", + "tagsHint": "カンマ区切りのタグ (例: YOLO, lab, training)", + "tagsSaved": "タグを保存しました", "tagsSaveFailed": "タグの保存に失敗しました", - "searchPlaceholder": "セッションをまたいだ検索結果...", - "searchResults": "{{count}} が一致します", + "searchPlaceholder": "セッション間で出力を検索...", + "searchResults": "{{count}}件の一致", "searchFailed": "検索に失敗しました", - "selectPaneHint": "出力プレビューを行うには、ペインを選択してください。", + "selectPaneHint": "出力をプレビューするには、ペインを選択してください", "closePreview": "プレビューを閉じる", - "newSession": "新しいセッション", - "newSessionHint": "セッション名(文字、数字、_ @ % + = -)", - "newSessionPlaceholder": "私のセッション", - "create": "作成する", - "sessionCreated": "セッション \"{{name}}\" が作成されました", + "newSession": "新規セッション", + "newSessionHint": "セッション名 (英数字, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "作成", + "sessionCreated": "セッション \"{{name}}\" を作成しました", "sessionCreateFailed": "セッションの作成に失敗しました", "splitRight": "右に分割", - "splitDown": "分割", + "splitDown": "下に分割", "splitFailed": "ペインの分割に失敗しました", - "newWindow": "新しいウィンドウ", + "newWindow": "新規ウィンドウ", "windowCreateFailed": "ウィンドウの作成に失敗しました", - "attachSessionTooltip": "{{session}}に添付", + "attachSessionTooltip": "{{session}}にアタッチ", "refresh": "更新", "refreshFailed": "セッションの更新に失敗しました", "collapseAll": "すべて折りたたむ", "expandAll": "すべて展開", "moreActions": "その他の操作", "sessionStats": "セッション統計", - "renameSessionTitle": "セッション名を「{{name}}」に変更します。", - "rename": "名前を変更する", - "sessionRenamed": "セッション名が「{{name}}」に変更されました", - "sessionRenameFailed": "セッション名の変更に失敗しました", - "editTagsTitle": "「{{name}} 」のタグを編集します", - "killPane": "キルパネル", - "resizeTree": "ドラッグしてサイズを変更 — ダブルクリックでリセット", - "reattach": "再接続(表示の乱れを修正)", - "killWindow": "キルウィンドウ", - "killWindowTitle": "{{index}} の \"{{session}} \" をキルしますか?", - "killWindowBody": "このウィンドウ内のすべてのペインとプロセスが終了します。最後のウィンドウを閉じるとセッションが終了します。", + "renameSessionTitle": "セッション \"{{name}}\" の名前変更", + "rename": "名前変更", + "sessionRenamed": "セッション名を \"{{name}}\" に変更しました", + "sessionRenameFailed": "セッションの名前変更に失敗しました", + "editTagsTitle": "\"{{name}}\" のタグを編集", + "killPane": "ペインを終了", + "resizeTree": "ドラッグでリサイズ — ダブルクリックでリセット", + "reattach": "再接続 (表示の乱れを修正)", + "killWindow": "ウィンドウを終了", + "killWindowTitle": "ウィンドウ {{index}} (\"{{session}}\") を終了しますか?", + "killWindowBody": "このウィンドウ内のすべてのペインとプロセスが終了されます。最後のウィンドウを終了するとセッションが終了します。", "windowKillFailed": "ウィンドウの終了に失敗しました", - "statusActivity": "活動", - "statusWindows": "Windows", - "statusPanes": "窓ガラス", + "statusActivity": "アクティビティ", + "statusWindows": "ウィンドウ", + "statusPanes": "ペイン", "statusTags": "タグ", - "killPaneTitle": "パネルをキル {{id}}?", - "killPaneBody": "このペインで実行中のプロセスは終了します。最後のペインを終了すると、そのウィンドウが閉じます。", + "killPaneTitle": "ペイン {{id}} を終了しますか?", + "killPaneBody": "このペインで実行中のプロセスが終了されます。最後のペインを終了するとウィンドウが閉じます。", "paneKillFailed": "ペインの終了に失敗しました", - "killSessionTitle": "Kill session \"{{name}}\"?", - "killSessionBody": "このセッション内のすべてのウィンドウと実行中のプロセスは終了します。この操作は元に戻せません。", - "kill": "殺す", - "sessionKilled": "セッション \"{{name}}\" が終了しました", + "killSessionTitle": "セッション \"{{name}}\" を終了しますか?", + "killSessionBody": "このセッション内のすべてのウィンドウと実行中のプロセスが終了されます。この操作は取り消せません。", + "kill": "終了", + "sessionKilled": "セッション \"{{name}}\" を終了しました", "sessionKillFailed": "セッションの終了に失敗しました", - "hostUnreachable": "ホストに接続できませんでした。ホストがオンラインでアクセス可能であることを確認してください。", - "noServer": "このホスト上ではtmuxサーバーは実行されていません。", - "searchTruncated": "部分的な結果 — 検索は各ペインの最後の {{lines}} 行と最大で {{panes}} ペインを対象とします。", + "hostUnreachable": "ホストに接続できませんでした。オンラインで到達可能か確認してください。", + "noServer": "このホストで tmux サーバーが実行されていません。", + "searchTruncated": "一部の結果 — 各ペインの直近 {{lines}} 行と最大 {{panes}} ペインを検索対象としています。", "closeSearchResults": "検索結果を閉じる", - "retry": "リトライ", - "noHosts": "SSHホストが利用できません", - "noHostsHint": "SSHホストでTmuxモニターオプションを有効にすると(ホストマネージャー → ターミナルタブ)、そのホストのtmuxセッションを監視できます。", - "tmuxInstallHint": "ホストにインストールするには、以下を使用してください。", - "attachTooltip": "{{host}} にターミナルを開きます。", - "attachTooltipPane": "{{host}} — tmux セッション {{session}} にターミナルを開きます", - "timeJustNow": "ちょうど今", + "retry": "再試行", + "noHosts": "利用可能な SSH ホストがありません", + "noHostsHint": "SSH ホストで Tmux モニターオプションを有効にすると(ホストマネージャー → 端末タブ)、tmux セッションを監視できます。", + "tmuxInstallHint": "ホストにインストールするには:", + "attachTooltip": "{{host}} にターミナルを開く", + "attachTooltipPane": "{{host}} にターミナルを開く — tmux セッション {{session}}", + "timeJustNow": "たった今", "timeMinutes": "{{count}}分前", "timeHours": "{{count}}時間前", "timeDays": "{{count}}日前" }, "mobileKeyboard": { - "shift": "シフト", + "shift": "Shift", "ctrl": "Ctrl", - "esc": "ESC", - "tab": "タブ", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "上向き矢印", - "arrowDown": "下向き矢印", - "arrowLeft": "左矢印", - "arrowRight": "右矢印", - "home": "家", - "end": "終わり", + "arrowUp": "↑", + "arrowDown": "↓", + "arrowLeft": "←", + "arrowRight": "→", + "home": "ホーム", + "end": "エンド", "pageUp": "PgUp", "pageDown": "PgDn", - "delete": "デル", - "editQuickKeys": "編集クイックキー", + "delete": "Del", + "paste": "ペースト", + "editQuickKeys": "クイックキー編集", "quickKeysTitle": "クイックキー", - "quickKeysDesc": "削除するには「×」をタップしてください。最大8文字まで対応しています。", - "quickKeyPlaceholder": "例:sudo ", + "quickKeysDesc": "× をタップして削除します。最大8文字まで対応。", + "quickKeyPlaceholder": "例: sudo ", "addQuickKey": "追加", - "removeQuickKey": "取り除く", - "resetDefaults": "初期設定に戻す", - "done": "終わり" + "removeQuickKey": "削除", + "resetDefaults": "デフォルトにリセット", + "done": "完了" }, "serial": { "title": "シリアル", @@ -3394,35 +3607,35 @@ "portPlaceholder": "/dev/ttyUSB0 または COM3", "baudRateLabel": "ボーレート", "dataBitsLabel": "データ", - "stopBitsLabel": "停止", + "stopBitsLabel": "ストップ", "parityLabel": "パリティ", "parityNone": "なし", - "parityEven": "平", + "parityEven": "偶数", "parityOdd": "奇数", - "connect": "シリアル接続", + "connect": "シリアルに接続", "disconnect": "切断", - "refreshPorts": "リフレッシュポート", + "refreshPorts": "ポートを更新", "connected": "{{path}} に {{baud}} ボーで接続しました", "disconnected": "シリアルポートが切断されました", - "connectionError": "シリアルポートを開くのに失敗しました", - "wsError": "WebSocketエラー", - "errorNoServerUrl": "サーバーURLが設定されていません", - "notSupportedTitle": "シリアル番号はサポートされていません", - "notSupported": "シリアル接続には、Web Serial APIをサポートするブラウザ(Chrome、Edge、またはFirefox 151以降)またはTermixデスクトップアプリが必要です。", - "hideHint": "「ユーザープロファイル」>「外観」>「サイドバー」>「ナビゲーション」でシリアルタブを非表示にできます。", - "browserPickerHint": "「接続」をクリックすると、ブラウザにポート選択画面が表示され、デバイスを選択できます。" + "connectionError": "シリアルポートを開けませんでした", + "wsError": "WebSocket エラー", + "errorNoServerUrl": "サーバー URL が設定されていません", + "notSupportedTitle": "シリアル非対応", + "notSupported": "シリアル接続には Web Serial API をサポートするブラウザ (Chrome, Edge, Firefox 151+) または Termix デスクトップアプリが必要です。", + "hideHint": "シリアルタブは「ユーザープロファイル > 外観 > サイドバー > ナビゲーション」で非表示にできます。", + "browserPickerHint": "接続をクリックすると、ブラウザがポートピッカーを開き、デバイスを選択できます。" }, "metricsHistory": { - "title": "指標履歴", - "historySuffix": "歴史", + "title": "メトリクス履歴", + "historySuffix": "履歴", "cpuMemoryDisk": "CPU / メモリ / ディスク", "network": "ネットワーク", - "download": "RX", - "upload": "テキサス州", - "noData": "この期間の履歴データは利用できません。", + "download": "受信", + "upload": "送信", + "noData": "この期間の履歴データはありません。", "custom": "カスタム", - "to": "に", - "apply": "適用する", + "to": "~", + "apply": "適用", "viewHistory": "履歴を表示" }, "alerts": { @@ -3430,43 +3643,43 @@ "tabRules": "ルール", "tabChannels": "チャンネル", "noFirings": "未確認のアラートはありません", - "noRules": "アラートルールは設定されていません", - "noChannels": "通知チャネルが設定されていません", - "rulesDesc": "アラートルールによって通知がトリガーされます", - "channelsDesc": "アラート通知が送信される場所", - "acknowledge": "認める", - "ackAll": "すべて承認", - "allAcknowledged": "すべてのアラートを確認しました", + "noRules": "アラートルールが設定されていません", + "noChannels": "通知チャンネルが設定されていません", + "rulesDesc": "アラートルールは通知をトリガーします", + "channelsDesc": "アラート通知の送信先です", + "acknowledge": "確認", + "ackAll": "すべて確認", + "allAcknowledged": "すべてのアラートが確認済みです", "ackFailed": "アラートの確認に失敗しました", - "ackAllFailed": "すべてのアラートを確認できませんでした", + "ackAllFailed": "すべてのアラートの確認に失敗しました", "showAcknowledged": "すべて表示", - "hideAcknowledged": "承認済みを隠す", + "hideAcknowledged": "確認済みを非表示", "addChannel": "チャンネルを追加", "editChannel": "チャンネルを編集", "channelName": "名前", "channelType": "タイプ", "channelNameRequired": "名前は必須です", "webhookUrl": "URL", - "webhookUrlRequired": "Webhook URLは必須です。", - "webhookDesc": "アラートが発生するたびに、このURLにJSONペイロードをPOSTします。", + "webhookUrlRequired": "Webhook URLは必須です", + "webhookDesc": "各アラートの発生時にこのURLにJSONペイロードをPOSTします", "ntfyServer": "サーバーURL", "ntfyTopic": "トピック", "ntfyTopicRequired": "トピックは必須です", "ntfyToken": "アクセストークン(任意)", "channelSaveFailed": "チャンネルの保存に失敗しました", "test": "テスト", - "testSent": "テスト通知が送信されました", - "testFailed": "テスト通知が失敗しました", - "addRule": "アラートルールを追加する", - "editRule": "アラートルールの編集", + "testSent": "テスト通知を送信しました", + "testFailed": "テスト通知に失敗しました", + "addRule": "アラートルールを追加", + "editRule": "アラートルールを編集", "ruleName": "ルール名", - "ruleNameRequired": "名前は必須です", + "ruleNameRequired": "ルール名は必須です", "triggerType": "トリガー", - "thresholdValue": "しきい値 (%)", - "durationSeconds": "持続時間(秒、0=即時発射)", - "cooldownMinutes": "クールダウン時間(分)", + "thresholdValue": "しきい値 (%)", + "durationSeconds": "継続時間(秒、0は即時発火)", + "cooldownMinutes": "クールダウン(分)", "channels": "通知チャンネル", - "noChannelsHint": "まず「チャンネル」タブでチャンネルを追加してください。", + "noChannelsHint": "先にチャンネルタブでチャンネルを追加してください", "ruleSaveFailed": "ルールの保存に失敗しました" } } diff --git a/src/ui/locales/translated/ko_KR.json b/src/ui/locales/translated/ko_KR.json index cbd43435..c86f3cf8 100644 --- a/src/ui/locales/translated/ko_KR.json +++ b/src/ui/locales/translated/ko_KR.json @@ -1,104 +1,104 @@ { "termixId": { - "title": "터믹스 ID", - "loadFailed": "Termix ID를 불러오는 데 실패했습니다.", - "claimTitle": "Termix ID를 신청하세요", - "claimIntro": "고유한 사용자 이름을 선택하세요. SSH 공개 키는 공개 URL에 게시되며, 이 URL을 모든 서버의 authorized_keys 파일에 추가할 수 있습니다.", + "title": "Termix ID", + "loadFailed": "Termix ID 로드 실패", + "claimTitle": "Termix ID 등록", + "claimIntro": "고유한 핸들을 선택하세요. SSH 공개 키는 공개 URL에 게시되어 어느 서버의 authorized_keys 파일에든 추가할 수 있습니다.", "handleLabel": "핸들", - "handlePlaceholder": "앨리스", - "checking": "… 확인 중", + "handlePlaceholder": "alice", + "checking": "확인 중…", "available": "사용 가능", - "taken": "이미 예약되었습니다", - "invalidHandle": "소문자, 숫자, 하이픈(-) 및 밑줄(_)만 사용 가능", + "taken": "이미 사용 중", + "invalidHandle": "소문자, 숫자, - 및 _만 사용", "descriptionLabel": "설명 (선택 사항)", "descriptionPlaceholder": "업무용 노트북 및 휴대폰 키", - "create": "Termix ID 생성", - "created": "Termix ID가 생성되었습니다.", - "createFailed": "Termix ID 생성에 실패했습니다.", - "deleteConfirm": "Termix ID와 게시된 모든 키를 삭제하시겠습니까? 프로비저닝한 서버는 사용자가 수동으로 키를 삭제할 때까지 키를 유지합니다.", - "deleted": "Termix ID가 삭제되었습니다.", - "deleteFailed": "Termix ID 삭제에 실패했습니다.", + "create": "Termix ID 만들기", + "created": "Termix ID 생성됨", + "createFailed": "Termix ID 생성 실패", + "deleteConfirm": "Termix ID와 게시된 모든 키를 삭제하시겠습니까? 프로비저닝된 서버는 수동으로 제거할 때까지 키를 유지합니다.", + "deleted": "Termix ID 삭제됨", + "deleteFailed": "Termix ID 삭제 실패", "copyFailed": "복사 실패", "resolverUrlLabel": "공개 리졸버 URL", - "provisionLabel": "서버를 프로비저닝하세요", - "publishTitle": "공개 키를 게시합니다", - "generate": "생성하다", - "generateTooltip": "Ed25519 키 쌍 생성 — 공개 키를 게시하고 개인 키를 기기에 다운로드합니다.", + "provisionLabel": "서버 프로비저닝", + "publishTitle": "공개 키 게시", + "generate": "생성", + "generateTooltip": "Ed25519 키 쌍 생성 — 공개 키를 게시하고 개인 키를 기기에 다운로드합니다", "saveToVault": "자격 증명에 저장", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", - "labelPlaceholder": "라벨 (선택 사항)", - "add": "추가하다", - "importFromCredential": "또는 저장된 자격 증명에서 가져옵니다.", - "keyPublished": "주요 게시됨", - "addKeyFailed": "키를 추가하는 데 실패했습니다.", - "generatedSaved": "키 쌍이 생성되어 자격 증명 저장소에 저장되었습니다. 개인 키도 다운로드되었습니다.", - "generatedOnly": "키 쌍이 생성되었습니다. 개인 키가 다운로드되었습니다(한 번만 표시됨).", - "generateFailed": "키 생성에 실패했습니다.", - "imported": "자격 증명에서 가져온 키", - "importFailed": "키를 가져오는 데 실패했습니다.", - "noKeys": "아직 공개 키가 게시되지 않았습니다.", - "keysTitle": "공개된 키", + "labelPlaceholder": "레이블 (선택 사항)", + "add": "추가", + "importFromCredential": "또는 저장된 자격 증명에서 가져오기", + "keyPublished": "키 게시됨", + "addKeyFailed": "키 추가 실패", + "generatedSaved": "키 쌍이 생성되어 자격 증명 보관함에 저장되었습니다. 개인 키도 다운로드되었습니다.", + "generatedOnly": "키 쌍 생성됨 — 개인 키가 다운로드되었습니다 (한 번만 표시됨).", + "generateFailed": "키 생성 실패", + "imported": "자격 증명에서 키를 가져왔습니다", + "importFailed": "키 가져오기 실패", + "noKeys": "아직 게시된 공개 키가 없습니다.", + "keysTitle": "게시된 키", "published": "게시됨", - "hidden": "숨겨진", - "keyRemoved": "키가 제거되었습니다", - "removeKeyFailed": "키를 제거하는 데 실패했습니다.", - "updateKeyFailed": "키 업데이트에 실패했습니다", - "fromVault": "자격 증명 저장소에서", + "hidden": "숨김", + "keyRemoved": "키 제거됨", + "removeKeyFailed": "키 제거 실패", + "updateKeyFailed": "키 업데이트 실패", + "fromVault": "자격 증명 보관소에서", "linkedToTermixId": "Termix ID를 통해 게시됨", - "selectCredential": "자격증명을 선택하세요...", - "import": "수입", - "caTitle": "인증기관", - "caIntro": "서버에서 이 CA를 신뢰하면 사용자가 서명하는 모든 인증서를 허용합니다. 인증서를 한 번에 모두 폐기하려면 인증서 순환 기능을 사용하세요. 인증서는 자동으로 만료됩니다.", + "selectCredential": "자격 증명 선택...", + "import": "가져오기", + "caTitle": "인증 기관", + "caIntro": "서버에서 이 CA를 신뢰하면 서명한 모든 인증서가 수락됩니다. 로테이션하면 모든 인증서가 즉시 폐기되며, 인증서는 자체적으로도 만료됩니다.", "caEnable": "CA 활성화", "caEnabled": "CA 활성화됨", - "caCreateFailed": "CA 활성화에 실패했습니다.", + "caCreateFailed": "CA 활성화 실패", "caPublicKeyLabel": "CA 공개 키", - "caTrustLabel": "서버에 대한 신뢰 설정(루트 권한으로 실행)", - "caRotate": "회전", - "caRotateConfirm": "인증기관(CA)을 교체하시겠습니까? 해당 CA가 서명한 모든 인증서는 더 이상 사용할 수 없게 되며 재발급받아야 합니다.", - "caRotated": "CA가 갱신되었습니다 - 이전 인증서가 취소되었습니다", - "caRotateFailed": "CA를 회전시키는 데 실패했습니다.", - "caDelete": "CA를 제거하세요", + "caTrustLabel": "서버에서 신뢰 (루트로 실행)", + "caRotate": "로테이션", + "caRotateConfirm": "CA를 로테이션하시겠습니까? 서명한 모든 인증서가 수락되지 않으며 다시 발급해야 합니다.", + "caRotated": "CA 로테이션됨 — 이전 인증서 폐기됨", + "caRotateFailed": "CA 로테이션 실패", + "caDelete": "CA 제거", "caDeleteConfirm": "인증 기관을 제거하시겠습니까?", - "caDeleted": "CA가 제거했습니다", - "caDeleteFailed": "CA 제거에 실패했습니다", - "caValidityLabel": "인증서 유효기간(일)", - "issueCert": "자격증", - "issueCertTooltip": "CA에서 서명한 이 키에 대한 SSH 인증서를 발급하십시오.", - "certIssued": "인증서가 발급 및 다운로드되었습니다.", - "certIssueFailed": "인증서 발급에 실패했습니다" + "caDeleted": "CA 제거됨", + "caDeleteFailed": "CA 제거 실패", + "caValidityLabel": "인증서 유효 기간(일)", + "issueCert": "인증서", + "issueCertTooltip": "이 키에 대해 CA가 서명한 SSH 인증서를 발급합니다.", + "certIssued": "인증서 발급 및 다운로드 완료", + "certIssueFailed": "인증서 발급 실패" }, "credentials": { "folders": "폴더", - "folder": "접는 사람", + "folder": "폴더", "password": "비밀번호", - "key": "열쇠", + "key": "키", "sshPrivateKey": "SSH 개인 키", "upload": "업로드", - "keyPassword": "키 비밀번호", + "keyPassword": "키 암호", "sshKey": "SSH 키", "uploadPrivateKeyFile": "개인 키 파일 업로드", - "searchCredentials": "자격 증명을 검색하세요...", + "searchCredentials": "자격 증명 검색...", "addCredential": "자격 증명 추가", - "caCertificate": "CA 인증서(-cert.pub)", - "caCertificateDescription": "선택 사항: CA에서 서명한 인증서 파일(예: id_ed25519-cert.pub)을 업로드하거나 붙여넣으세요. SSH 서버에서 인증서 기반 인증을 사용하는 경우 필수입니다.", + "caCertificate": "CA 인증서 (-cert.pub)", + "caCertificateDescription": "선택 사항: CA 서명 인증서 파일(예: id_ed25519-cert.pub)을 업로드하거나 붙여넣으세요. SSH 서버가 인증서 기반 인증을 사용할 때 필요합니다.", "uploadCertFile": "-cert.pub 파일 업로드", - "clearCert": "분명한", - "certLoaded": "인증서가 로드되었습니다", - "certPublicKeyLabel": "캘리포니아 주 인증서", + "clearCert": "지우기", + "certLoaded": "인증서 로드됨", + "certPublicKeyLabel": "CA 인증서", "certTypeLabel": "인증서 유형", "pasteOrUploadCert": "-cert.pub 인증서를 붙여넣거나 업로드하세요...", - "hasCaCert": "CA 자격증 보유", - "noCaCert": "공인회계사 자격증 없음", - "noPublicKeyAvailable": "공개 키를 사용할 수 없습니다. 먼저 자격 증명 편집기를 여십시오.", - "deployCommandCopied": "배포 명령이 복사되었습니다", + "hasCaCert": "CA 인증서 있음", + "noCaCert": "CA 인증서 없음", + "noPublicKeyAvailable": "공개 키를 사용할 수 없습니다. 먼저 자격 증명 편집기를 여세요.", + "deployCommandCopied": "배포 명령 복사됨", "sortCredentials": "자격 증명 정렬", - "sortDefault": "기본 주문", + "sortDefault": "기본 순서", "sortNameAsc": "이름 (A → Z)", "sortNameDesc": "이름 (Z → A)", "sortUsernameAsc": "사용자 이름 (A → Z)", "sortUsernameDesc": "사용자 이름 (Z → A)", - "filterCredentials": "필터 자격 증명", + "filterCredentials": "자격 증명 필터", "filterClearAll": "필터 지우기", "filterTypeGroup": "유형", "filterTypePassword": "비밀번호", @@ -112,371 +112,410 @@ "deleteWidget": "삭제", "widgetTypes": "위젯 유형", "serviceLink": "서비스 링크", - "folder": "접는 사람", + "folder": "폴더", "clock": "시계", "notes": "메모", "hostStatus": "호스트 상태", - "bookmarkList": "책갈피", + "bookmarkList": "즐겨찾기", "zoomIn": "확대", - "zoomOut": "축소하기", - "resetView": "보기 재설정", - "lockLayout": "잠금 레이아웃", + "zoomOut": "축소", + "resetView": "보기 초기화", + "lockLayout": "레이아웃 잠금", "unlockLayout": "레이아웃 잠금 해제", - "noWidgets": "마우스 오른쪽 버튼을 클릭하거나 '+' 버튼을 클릭하여 첫 번째 위젯을 추가하세요.", - "openFullView": "전체 화면으로 열기", + "noWidgets": "마우스 오른쪽 클릭 또는 +를 클릭하여 첫 위젯을 추가하세요.", + "openFullView": "전체 보기 열기", "previewTitle": "홈페이지 미리보기", "cancel": "취소", - "save": "구하다", + "save": "저장", "title_label": "제목", "widgetTitlePlaceholder": "위젯 제목 (선택 사항)", "url": "URL", "imageUrl": "사용자 지정 이미지 URL", - "imageUrlHint": "사이트 파비콘을 자동으로 사용하려면 비워 두세요.", + "imageUrlHint": "비워 두면 사이트 파비콘이 자동으로 사용됩니다.", "showImage": "이미지 표시", "description": "설명", "color": "색상", - "icon": "상", + "icon": "아이콘", "expanded": "기본적으로 확장됨", "timezone": "시간대", - "showSeconds": "초를 표시합니다", + "showSeconds": "초 표시", "format12h": "12시간", "format24h": "24시간", - "content": "콘텐츠", + "content": "내용", "backgroundColor": "배경색", - "host": "주인", - "showMetrics": "지표 표시", - "links": "모래밭", + "host": "호스트", + "showMetrics": "메트릭 표시", + "links": "링크", "addLink": "링크 추가", - "linkLabel": "상표", + "linkLabel": "레이블", "linkUrl": "URL", - "removeLink": "제거하다", - "categoryLinks": "모래밭", + "removeLink": "제거", + "categoryLinks": "링크", "categoryInfo": "정보", - "categorySystem": "체계", + "categorySystem": "시스템", "widgetServiceLinkDesc": "서비스 URL로 연결되는 클릭 가능한 타일", - "widgetFolderDesc": "관련 위젯들을 그룹화하는 컨테이너", + "widgetFolderDesc": "관련 위젯을 그룹화하는 컨테이너", "widgetClockDesc": "시간대 설정이 가능한 실시간 시계", "widgetNotesDesc": "마크다운 메모 위젯", - "widgetHostStatusDesc": "SSH 호스트의 실시간 CPU, 메모리 및 디스크 사용량을 표시합니다.", + "widgetHostStatusDesc": "SSH 호스트의 실시간 CPU, 메모리, 디스크 사용량 표시", "widgetBookmarkListDesc": "빠른 링크 목록", "copyLink": "링크 복사", - "linkCopied": "링크 복사 완료!", + "linkCopied": "링크가 복사되었습니다!", "location": "위치", "temperatureUnit": "온도 단위", - "showForecast": "3일 예보를 보여주세요", + "showForecast": "3일 예보 표시", "scrolling": "스크롤 허용", "feedUrl": "피드 URL", - "maxItems": "최대 품목 수", + "maxItems": "최대 항목 수", "showDescription": "설명 표시", "widgetWeatherName": "날씨", - "widgetWeatherDesc": "어느 위치든 실시간 날씨 정보 확인 가능", + "widgetWeatherDesc": "모든 지역의 실시간 날씨", "widgetIframeName": "iFrame 임베드", - "widgetIframeDesc": "iframe에 모든 URL을 삽입할 수 있습니다.", + "widgetIframeDesc": "URL을 iframe으로 임베드", "widgetRssName": "RSS 피드", - "widgetRssDesc": "RSS 또는 Atom 피드의 항목을 표시합니다.", - "dragToFolder": "위젯을 여기에 드래그하거나 + 버튼을 사용하여 추가하세요.", + "widgetRssDesc": "RSS 또는 Atom 피드의 항목 표시", + "dragToFolder": "위젯을 여기로 드래그하거나 +를 눌러 추가하세요.", "showDisk": "디스크 사용량 표시", - "addToFolder": "위젯을 폴더에 추가", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "metricsNotAvailable": "측정항목을 사용할 수 없습니다.", - "selectHost": "호스트를 선택하세요...", + "addToFolder": "폴더에 위젯 추가", + "noHostSelected": "선택된 호스트 없음", + "metricsNotAvailable": "메트릭을 사용할 수 없습니다", + "selectHost": "호스트 선택...", "displayedMetrics": "표시된 메트릭", "metricCpu": "CPU", "metricMemory": "메모리", "metricDisk": "디스크", - "metricUptime": "가동 시간", - "metricSystem": "체계", + "metricUptime": "업타임", + "metricSystem": "시스템", "metricOs": "OS", - "metricKernel": "핵심", - "metricHostname": "호스트 이름", - "metricNetwork": "회로망", + "metricKernel": "커널", + "metricHostname": "호스트명", + "metricNetwork": "네트워크", "metricProcesses": "프로세스", - "metricProcessesTotal": "총", - "metricProcessesRunning": "달리기", + "metricProcessesTotal": "전체", + "metricProcessesRunning": "실행 중", "categoryMonitoring": "모니터링", "loading": "로딩 중...", - "noData": "사용 가능한 데이터가 없습니다.", - "allClear": "공습 경보 신호", - "acknowledgeAlert": "인정하다", - "noPingUrls": "구성된 URL이 없습니다.", - "pingLabel": "상표", + "noData": "데이터 없음", + "allClear": "모두 정상", + "acknowledgeAlert": "확인", + "noPingUrls": "구성된 URL 없음", + "pingLabel": "레이블", "addPingUrl": "URL 추가", "showLatency": "지연 시간 표시", "refreshInterval": "새로 고침 간격", "seconds": "초", - "filterActivityTypes": "필터 유형", - "filterTypesHint": "모두 표시하려면 비워 두세요.", + "filterActivityTypes": "유형 필터", + "filterTypesHint": "모두 보려면 비워 두세요", "showTimestamp": "타임스탬프 표시", - "uptimeUnavailable": "가동 시간 정보 없음", - "uptimeLabel": "가동 시간", + "uptimeUnavailable": "업타임 없음", + "uptimeLabel": "업타임", "overviewVersion": "버전", - "overviewUpdate": "최신 정보", - "overviewUpdateAvailable": "업데이트 가능", - "overviewDatabase": "데이터 베이스", - "overviewUptime": "가동 시간", - "noHosts": "구성된 호스트가 없습니다.", + "overviewUpdate": "최신 상태", + "overviewUpdateAvailable": "업데이트 있음", + "overviewDatabase": "데이터베이스", + "overviewUptime": "업타임", + "noHosts": "호스트 구성 없음", "hostGridHosts": "호스트", - "hostGridAllHint": "모든 호스트를 표시하려면 비워 두세요.", - "columns": "컬럼", - "showIp": "IP 주소를 표시합니다", + "hostGridAllHint": "비워 두면 모든 호스트 표시", + "columns": "열", + "showIp": "IP 주소 표시", "connectionType": "연결 유형", - "layout": "공들여 나열한 것", + "layout": "레이아웃", "showStatus": "상태 표시", "showHostName": "호스트 이름 표시", "noDockerActivity": "Docker 활동 없음", "noActivity": "활동 없음", - "showAcknowledged": "표시됨", - "showCurrentValue": "현재 값을 표시합니다", - "chartMetric": "미터법", + "showAcknowledged": "확인됨 표시", + "showCurrentValue": "현재 값 표시", + "chartMetric": "메트릭", "metricRange": "범위", - "widgetMetricsChartName": "측정 지표 차트", - "widgetMetricsChartDesc": "호스트의 CPU, 메모리, 디스크 또는 네트워크 사용량 기록도", + "widgetMetricsChartName": "메트릭 차트", + "widgetMetricsChartDesc": "호스트의 CPU, 메모리, 디스크, 네트워크 기록 차트", "widgetHostGridName": "호스트 그리드", - "widgetHostGridDesc": "SSH 호스트 상태의 그리드 보기", + "widgetHostGridDesc": "SSH 호스트 상태 그리드 뷰", "widgetAlertFeedName": "알림 피드", - "widgetAlertFeedDesc": "실시간 경보 발사 및 지원 확인", - "widgetPingStatusName": "핑 상태", + "widgetAlertFeedDesc": "실시간 알림 및 확인 지원", + "widgetPingStatusName": "Ping 상태", "widgetPingStatusDesc": "하나 이상의 URL에 대한 HTTP 핑 상태", "widgetRecentActivityName": "최근 활동", - "widgetRecentActivityDesc": "최근 Termix 활동 내역을 스크롤하여 볼 수 있는 피드입니다.", + "widgetRecentActivityDesc": "Termix 최근 활동의 스크롤 가능한 피드", "widgetTermixUptimeName": "Termix 가동 시간", "widgetTermixUptimeDesc": "Termix 서버의 실시간 가동 시간 카운터", "widgetSystemOverviewName": "시스템 개요", - "widgetSystemOverviewDesc": "Termix 버전, 데이터베이스 상태 및 가동 시간을 한눈에 확인하세요", + "widgetSystemOverviewDesc": "한눈에 보는 Termix 버전, 데이터베이스 상태, 가동 시간", "widgetSshQuickConnectName": "SSH 빠른 연결", - "widgetSshQuickConnectDesc": "원클릭 버튼으로 SSH 세션을 열 수 있습니다.", + "widgetSshQuickConnectDesc": "SSH 세션을 열기 위한 원클릭 버튼", "widgetDockerActivityName": "Docker 활동", - "widgetDockerActivityDesc": "모든 호스트에서 발생한 최근 Docker 컨테이너 이벤트", + "widgetDockerActivityDesc": "모든 호스트의 최근 Docker 컨테이너 이벤트", "widgetCalendarName": "달력", - "widgetCalendarDesc": "오늘 날짜가 표시된 월간 달력", + "widgetCalendarDesc": "오늘을 강조 표시한 월간 달력", "widgetCountdownName": "카운트다운", - "widgetCountdownDesc": "목표 날짜까지 남은 시간을 알려주는 카운트다운 타이머", + "widgetCountdownDesc": "목표 날짜까지의 카운트다운 타이머", "widgetSearchBarName": "검색창", "widgetSearchBarDesc": "빠른 웹 검색 위젯", "widgetTextBannerName": "텍스트 배너", - "widgetTextBannerDesc": "캔버스에 표시할 굵은 레이블 또는 섹션 헤더", - "widgetImageWidgetName": "영상", - "widgetImageWidgetDesc": "URL에서 이미지를 표시합니다", + "widgetTextBannerDesc": "캔버스의 굵은 레이블 또는 섹션 헤더", + "widgetImageWidgetName": "이미지", + "widgetImageWidgetDesc": "URL에서 이미지 표시", "widgetMarkdownNotesName": "마크다운 노트", - "widgetMarkdownNotesDesc": "인라인 마크다운 렌더링을 지원하는 리치 노트", - "widgetCustomApiName": "사용자 지정 API", - "widgetCustomApiDesc": "JSON API에서 데이터를 가져와 표시합니다.", + "widgetMarkdownNotesDesc": "인라인 마크다운 렌더링이 포함된 리치 노트", + "widgetCustomApiName": "사용자 정의 API", + "widgetCustomApiDesc": "모든 JSON API에서 데이터를 가져와 표시", "widgetServiceGridName": "서비스 그리드", "widgetServiceGridDesc": "구성 가능한 서비스 타일 링크 그리드", "widgetDashboardLinksName": "대시보드 링크", - "widgetDashboardLinksDesc": "대시보드에서 구성된 서비스 링크를 표시합니다.", - "widgetSearchLinksName": "바로가기 검색", - "widgetSearchLinksDesc": "인라인 입력이 가능한 빠른 검색 바로가기 버튼", + "widgetDashboardLinksDesc": "대시보드에서 구성한 서비스 링크 표시", + "widgetSearchLinksName": "검색 바로가기", + "widgetSearchLinksDesc": "인라인 입력이 있는 빠른 검색 바로가기 버튼", "widgetLinkTreeName": "링크 트리", - "widgetLinkTreeDesc": "제목이 있는 링크 그룹", - "calMon": "모", - "calTue": "투", - "calWed": "우리", - "calThu": "그", - "calFri": "정말로", - "calSat": "사", - "calSun": "수", - "startOnMonday": "월요일에 한 주를 시작하세요", - "countdownNoDate": "목표 날짜는 정해지지 않았습니다.", - "countdownPast": "이벤트가 종료되었습니다", - "countdownDays": "날", + "widgetLinkTreeDesc": "제목이 있는 링크의 그룹화된 섹션", + "calMon": "월", + "calTue": "화", + "calWed": "수", + "calThu": "목", + "calFri": "금", + "calSat": "토", + "calSun": "일", + "startOnMonday": "주 시작을 월요일로", + "countdownNoDate": "목표 날짜가 설정되지 않음", + "countdownPast": "이벤트가 지났습니다", + "countdownDays": "일", "countdownHours": "시간", - "countdownMinutes": "최소", - "countdownSeconds": "비서", - "countdownLabel": "상표", + "countdownMinutes": "분", + "countdownSeconds": "초", + "countdownLabel": "레이블", "countdownLabelPlaceholder": "예: 출시일", - "countdownShowDays": "쇼 데이", - "countdownShowHours": "상영 시간", + "countdownShowDays": "일 표시", + "countdownShowHours": "시간 표시", "targetDate": "목표 날짜", "searchEngine": "검색 엔진", "customSearchUrl": "사용자 지정 검색 URL", - "searchPlaceholder": "찾다...", - "searchGo": "가다", - "searchPlaceholderLabel": "자리 표시자 텍스트", - "searchPlaceholderHint": "검색 입력란 안에 표시되는 텍스트", + "searchPlaceholder": "검색...", + "searchGo": "이동", + "searchPlaceholderLabel": "플레이스홀더 텍스트", + "searchPlaceholderHint": "검색 입력란에 표시되는 텍스트", "openInNewTab": "새 탭에서 열기", - "searchQueryPlaceholder": "검색어를 입력하세요...", - "noSearchShortcuts": "바로 가기가 설정되어 있지 않습니다.", + "searchQueryPlaceholder": "검색어 입력...", + "noSearchShortcuts": "바로가기가 구성되지 않음", "addSearchShortcut": "바로가기 추가", "fontSize": "글꼴 크기", "textAlign": "텍스트 정렬", "fontWeight": "글꼴 두께", - "clearColor": "투명색", + "clearColor": "색상 지우기", "imageFit": "이미지 맞춤", "imageLinkUrl": "링크 URL", - "noImage": "이미지 URL이 설정되지 않았습니다.", + "noImage": "이미지 URL이 설정되지 않음", "altText": "대체 텍스트", - "altTextPlaceholder": "이미지를 설명하세요", + "altTextPlaceholder": "이미지 설명 입력", "renderMarkdown": "마크다운 렌더링", "displayMode": "표시 모드", "displayField": "표시 필드", "jsonPath": "JSON 경로", - "customApiLabel": "상표", + "customApiLabel": "레이블", "customApiLabelPlaceholder": "예: 온도", "customApiUnit": "단위", - "customApiNoUrl": "API URL이 구성되지 않았습니다.", - "customApiError": "가져오기에 실패했습니다", - "customApiNotArray": "응답은 배열이 아닙니다.", + "customApiNoUrl": "API URL이 구성되지 않음", + "customApiError": "가져오기 실패", + "customApiNotArray": "응답이 배열이 아닙니다", "addService": "서비스 추가", "showLabels": "레이블 표시", "iconSize": "아이콘 크기", - "noDashboardLinks": "대시보드 링크가 구성되지 않았습니다.", + "noDashboardLinks": "대시보드 링크가 구성되지 않음", "noLimit": "제한 없음", "sectionHeading": "섹션 제목", "addSection": "섹션 추가", - "compactMode": "컴팩트 모드", - "showDetailed": "자세히 보기", - "selectHosts": "호스트를 선택하세요", + "compactMode": "간결 모드", + "showDetailed": "상세 보기", + "selectHosts": "호스트 선택", "allHosts": "모든 호스트", "listLayout": "목록", "gridLayout": "그리드", - "terminal": "단말기", + "terminal": "터미널", "files": "파일", - "docker": "도커", + "docker": "Docker", "range15m": "15분", "range1h": "1시간", "range6h": "6시간", "range24h": "24시간", - "metricNetRx": "넷 다운로드", - "metricNetTx": "네트워크 업로드", + "metricNetRx": "네트워크 수신", + "metricNetTx": "네트워크 송신", "severityFilter": "심각도 필터", - "filterAll": "모두", - "accentColor": "포인트 색상", + "filterAll": "전체", + "accentColor": "강조 색상", "widgetSshTerminalName": "SSH 터미널", "widgetSshTerminalDesc": "구성된 호스트에 연결된 인라인 SSH 터미널", - "sshTerminalNoHost": "호스트가 구성되지 않았습니다.", - "sshTerminalConnect": "연결하다", - "sshTerminalAutoConnect": "부하 시 자동 연결", - "widgetQuickConnectName": "빠른 연결", - "widgetQuickConnectDesc": "호스트 간 모든 연결 유형에 대한 원클릭 실행 버튼", + "sshTerminalNoHost": "호스트가 구성되지 않음", + "sshTerminalConnect": "연결", + "sshTerminalAutoConnect": "로드 시 자동 연결", + "widgetQuickConnectName": "빨리 연결", + "widgetQuickConnectDesc": "호스트 전반에 걸친 모든 연결 유형에 대한 원클릭 실행 버튼", "connectionTypes": "연결 유형", - "connType_terminal": "단말기", + "connType_terminal": "터미널", "connType_files": "파일 관리자", - "connType_docker": "도커", + "connType_docker": "Docker", "connType_tunnel": "터널", "connType_host-metrics": "호스트 메트릭", "connType_rdp": "RDP", "connType_vnc": "VNC", - "connType_telnet": "텔넷", + "connType_telnet": "Telnet", "widgetFileManagerName": "파일 관리자", - "widgetFileManagerDesc": "구성된 호스트용 내장 SFTP 파일 관리자", - "widgetDockerName": "도커 관리자", - "widgetDockerDesc": "구성된 호스트용 내장 Docker 컨테이너 관리자", + "widgetFileManagerDesc": "설정된 호스트를 위한 내장 SFTP 파일 관리자", + "widgetDockerName": "Docker 관리자", + "widgetDockerDesc": "설정된 호스트를 위한 내장 Docker 컨테이너 관리자", "widgetTunnelName": "터널 관리자", - "widgetTunnelDesc": "구성된 호스트용 내장 SSH 터널 관리자", - "widgetNoHostSelected": "호스트가 구성되지 않았습니다." + "widgetTunnelDesc": "설정된 호스트를 위한 내장 SSH 터널 관리자", + "widgetNoHostSelected": "구성된 호스트 없음" }, "serverConfig": { - "title": "서버 구성", - "description": "Termix 서버 URL을 구성하여 백엔드 서비스에 연결하십시오.", + "title": "서버 설정", + "description": "백엔드 서비스에 연결할 Termix 서버 URL을 구성합니다", "serverUrl": "서버 URL", - "enterServerUrl": "서버 URL을 입력해 주세요.", - "saveFailed": "설정을 저장하는 데 실패했습니다.", + "enterServerUrl": "서버 URL을 입력하세요", + "saveFailed": "설정 저장 실패", "saveError": "설정 저장 오류", - "saving": "절약...", + "saving": "저장 중...", "saveConfig": "설정 저장", - "helpText": "Termix 서버가 실행 중인 URL을 입력하십시오(예: http://localhost:30001 또는 https://your-server.com).", + "helpText": "Termix 서버가 실행 중인 URL을 입력하세요 (예: http://localhost:30001 또는 https://your-server.com)", "changeServer": "서버 변경", - "mustIncludeProtocol": "서버 URL은 http:// 또는 https://로 시작해야 합니다.", - "allowInvalidCertificate": "유효하지 않은 인증서를 허용합니다", - "allowInvalidCertificateDesc": "자체 서명 인증서 또는 IP 주소 인증서를 사용하는 신뢰할 수 있는 자체 호스팅 서버에서만 사용하십시오.", + "mustIncludeProtocol": "서버 URL은 http:// 또는 https://로 시작해야 합니다", + "allowInvalidCertificate": "유효하지 않은 인증서 허용", + "allowInvalidCertificateDesc": "자체 서명 또는 IP 주소 인증서를 사용하는 신뢰할 수 있는 자체 호스팅 서버에만 사용하세요.", "useEmbedded": "로컬 서버 사용", - "embeddedDesc": "Termix를 내장된 로컬 서버로 실행하세요(원격 서버는 필요하지 않습니다).", + "embeddedDesc": "내장 로컬 서버로 Termix 실행 (원격 서버 불필요)", "embeddedConnecting": "로컬 서버에 연결 중...", - "embeddedNotReady": "로컬 서버가 아직 준비되지 않았습니다. 잠시 기다렸다가 다시 시도해 주세요.", + "embeddedNotReady": "로컬 서버가 아직 준비되지 않았습니다. 잠시 후 다시 시도해 주세요.", "localServer": "로컬 서버", "savedServers": "저장된 서버", - "noSavedServers": "저장된 서버가 없습니다", - "removeServer": "제거하다" + "noSavedServers": "저장된 서버 없음", + "removeServer": "제거" + }, + "migrationNotice": { + "title": "Termix Desktop이 이제 독립 실행형으로 실행됩니다.", + "body1": "이 앱은 이전에는 Termix 서버에 직접 연결되었지만, 이제 완전히 독립적으로 실행되도록 재설계되었습니다. 호스트와 자격 증명을 로컬에 저장하여 오프라인 상태에서도 작동합니다. 자체 호스팅 Termix 서버와의 양방향 동기화는 이제 선택 사항입니다.", + "body2": "기존 호스트 및 자격 증명은 여전히 {{url}}에 있습니다. 원격 동기화를 켜고 해당 서버에 다시 연결하여 정보를 복원하고 향후 두 상태를 동기화 상태로 유지하세요.", + "dismiss": "지금은 안 돼", + "setUpSync": "원격 동기화 설정" + }, + "remoteSync": { + "title": "원격 동기화", + "description": "선택적으로 이 데스크톱 앱을 자체 호스팅 Termix 서버에 연결하여 호스트, 자격 증명 및 코드 조각을 여러 장치에서 동기화할 수 있습니다. 연결 여부와 관계없이 앱은 항상 완전히 오프라인에서 작동합니다.", + "notConnected": "연결되지 않음", + "connected": "연결됨", + "connectedTo": "{{url}}에 연결됨", + "lastSynced": "마지막 동기화 시간: {{time}}", + "neverSynced": "동기화되지 않음", + "syncError": "동기화 오류: {{message}}", + "needsReauth": "로그인 시간이 만료되었습니다", + "connectButton": "서버에 연결", + "disconnectButton": "연결을 끊으세요", + "syncNowButton": "지금 동기화하세요", + "syncing": "동기화 중...", + "serverUrl": "서버 URL", + "enterServerUrl": "서버 URL을 입력해 주세요.", + "mustIncludeProtocol": "서버 URL은 http:// 또는 https://로 시작해야 합니다.", + "connectionTestFailed": "해당 URL에서 Termix 서버에 연결할 수 없습니다.", + "allowInvalidCertificate": "유효하지 않은 인증서를 허용합니다", + "allowInvalidCertificateDesc": "자체 서명 인증서 또는 IP 주소 인증서를 사용하는 신뢰할 수 있는 자체 호스팅 서버에서만 사용하십시오.", + "savedServers": "저장된 서버", + "removeServer": "제거하다", + "continueButton": "계속하다", + "cancelButton": "취소", + "signInTitle": "{{url}}에 로그인하세요", + "originTitle": "연결 출처", + "originDescription": "SSH 연결의 기본 시작 위치를 선택합니다. 이 설정은 호스트별로 재정의할 수 있습니다.", + "originLocal": "이 장치(로컬 네트워크)", + "originRemote": "원격 서버", + "bannerReconnect": "다시 연결", + "bannerMessage": "원격 동기화에는 재인증이 필요합니다." }, "versionCheck": { "error": "버전 확인 오류", - "checkFailed": "업데이트 확인에 실패했습니다.", - "upToDate": "앱이 최신 버전입니다.", - "currentVersion": "현재 {{version}} 버전을 실행 중입니다.", + "checkFailed": "업데이트 확인 실패", + "upToDate": "앱이 최신 상태입니다", + "currentVersion": "현재 버전 {{version}}을(를) 사용 중입니다.", "updateAvailable": "업데이트 가능", - "newVersionAvailable": "새 버전이 나왔습니다! 현재 {{current}}를 실행 중이지만, {{latest}} 를 사용할 수 있습니다.", + "newVersionAvailable": "새 버전이 출시되었습니다! 현재 {{current}}을(를) 사용 중이며, {{latest}} 버전이 제공됩니다.", "betaVersion": "베타 버전", - "betaVersionDesc": "현재 실행 중인 버전은 {{current}}이며, 이는 최신 안정 버전인 {{latest}}보다 최신 버전입니다.", + "betaVersionDesc": "현재 {{current}}을(를) 사용 중이며, 이는 최신 안정 릴리스 {{latest}}보다 최신 버전입니다.", "releasedOn": "{{date}}에 출시됨", "downloadUpdate": "업데이트 다운로드", - "checking": "업데이트를 확인하는 중...", - "checkUpdates": "업데이트를 확인하세요", - "checkingUpdates": "업데이트를 확인하는 중...", + "checking": "업데이트 확인 중...", + "checkUpdates": "업데이트 확인", + "checkingUpdates": "업데이트 확인 중...", "updateRequired": "업데이트 필요" }, "common": { - "close": "닫다", + "close": "닫기", "minimize": "최소화", "online": "온라인", "offline": "오프라인", - "unknown": "알려지지 않은", - "continue": "계속하다", - "maintenance": "유지", - "degraded": "타락한", + "unknown": "알 수 없음", + "continue": "계속", + "maintenance": "유지 관리", + "degraded": "성능 저하", "error": "오류", "warning": "경고", "unsavedChanges": "저장되지 않은 변경 사항", - "dismiss": "해고하다", + "dismiss": "무시", "loading": "로딩 중...", - "optional": "선택 과목", - "connect": "연결하다", + "optional": "선택 사항", + "connect": "연결", "copied": "복사됨", "connecting": "연결 중...", "updateAvailable": "업데이트 가능", - "appName": "터믹스", + "appName": "Termix", "openInNewTab": "새 탭에서 열기", - "noReleases": "출시 없음", + "noReleases": "릴리스 없음", "updatesAndReleases": "업데이트 및 릴리스", - "newVersionAvailable": "새 버전({{version}})이 출시되었습니다.", - "failedToFetchUpdateInfo": "업데이트 정보를 가져오는 데 실패했습니다.", - "preRelease": "사전 공개", - "noReleasesFound": "새로운 릴리스를 찾을 수 없습니다.", + "newVersionAvailable": "새 버전 ({{version}})이 출시되었습니다.", + "failedToFetchUpdateInfo": "업데이트 정보를 가져오지 못했습니다.", + "preRelease": "프리릴리스", + "noReleasesFound": "릴리스를 찾을 수 없습니다.", "cancel": "취소", "username": "사용자 이름", "login": "로그인", "logout": "로그아웃", - "register": "등록하다", + "register": "등록", "password": "비밀번호", "confirmPassword": "비밀번호 확인", - "back": "뒤쪽에", - "save": "구하다", - "saving": "절약...", + "back": "뒤로", + "save": "저장", + "saving": "저장 중...", "delete": "삭제", - "rename": "이름 변경", - "edit": "편집하다", - "add": "추가하다", - "confirm": "확인하다", + "rename": "이름 바꾸기", + "edit": "편집", + "add": "추가", + "confirm": "확인", "no": "아니요", "or": "또는", "next": "다음", - "previous": "이전의", - "refresh": "새로 고치다", + "previous": "이전", + "refresh": "새로 고침", "language": "언어", "checking": "확인 중...", - "checkingDatabase": "데이터베이스 연결을 확인하는 중...", - "checkingAuthentication": "인증 상태를 확인하는 중...", - "backendReconnected": "서버 연결이 복구되었습니다.", - "connectionDegraded": "서버 연결이 끊어졌습니다. 복구 중입니다.…", - "reload": "재장전", - "remove": "제거하다", - "create": "만들다", + "checkingDatabase": "데이터베이스 연결 확인 중...", + "checkingAuthentication": "인증 확인 중...", + "backendReconnected": "서버 연결이 복원되었습니다.", + "connectionDegraded": "서버 연결이 끊어졌습니다. 복구 중…", + "reload": "다시 로드", + "remove": "제거", + "create": "생성", "update": "업데이트", "copy": "복사", - "copyFailed": "클립보드에 복사하는 데 실패했습니다.", + "copyFailed": "클립보드로 복사하지 못했습니다.", "maximize": "최대화", - "restore": "복원하다", - "of": "~의", + "restore": "복원", + "of": "중", "saved": "저장됨", "deleted": "삭제됨", - "deleteFailed": "삭제에 실패했습니다", - "saveFailed": "저장에 실패했습니다", - "required": "필수의" + "deleteFailed": "삭제 실패", + "saveFailed": "저장 실패", + "required": "필수" }, "nav": { - "home": "집", - "terminal": "단말기", - "docker": "도커", + "home": "홈", + "terminal": "터미널", + "docker": "Docker", "tunnels": "터널", "fileManager": "파일 관리자", "serverStats": "호스트 메트릭", @@ -484,61 +523,61 @@ "admin": "관리자", "termixId": "ID", "userProfile": "사용자 프로필", - "splitScreen": "분할 화면", - "confirmClose": "이 세션을 종료하시겠습니까?", - "close": "닫다", + "splitScreen": "화면 분할", + "confirmClose": "이 활성 세션을 닫으시겠습니까?", + "close": "닫기", "cancel": "취소", "sshManager": "SSH 관리자", - "cannotSplitTab": "이 탭을 분할할 수 없습니다.", + "cannotSplitTab": "이 탭을 분할할 수 없습니다", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "비밀번호 복사", - "copySudoPassword": "Sudo 암호 복사", - "passwordCopied": "비밀번호가 클립보드에 복사되었습니다.", - "noPasswordAvailable": "비밀번호를 사용할 수 없습니다.", + "copySudoPassword": "Sudo 비밀번호 복사", + "passwordCopied": "비밀번호가 클립보드에 복사되었습니다", + "noPasswordAvailable": "사용 가능한 비밀번호가 없습니다", "failedToCopyPassword": "비밀번호 복사에 실패했습니다", "refreshTab": "연결 새로 고침", "renameTab": "탭 이름 변경", "openFileManager": "파일 관리자 열기", - "dashboard": "계기반", + "dashboard": "대시보드", "networkGraph": "네트워크 그래프", "tmuxMonitor": "Tmux 모니터", "homepage": "홈페이지", "quickConnect": "빠른 연결", "sshTools": "SSH 도구", - "history": "역사", + "history": "기록", "sessionLogs": "세션 로그", "hosts": "호스트", - "snippets": "짧은 발췌", + "snippets": "스니펫", "hostManager": "호스트 관리자", - "credentials": "신임장", - "connections": "사이", + "credentials": "자격 증명", + "connections": "연결", "alerts": "알림", - "serial": "연속물", + "serial": "시리얼", "roleAdministrator": "관리자", "roleUser": "사용자" }, "hosts": { "hosts": "호스트", - "noHosts": "SSH 호스트 없음", - "retry": "다시 해 보다", - "refresh": "새로 고치다", - "optional": "선택 과목", + "noHosts": "SSH 호스트가 없습니다", + "retry": "재시도", + "refresh": "새로 고침", + "optional": "선택 사항", "downloadSample": "샘플 다운로드", - "failedToDeleteHost": "{{name}} 삭제에 실패했습니다.", + "failedToDeleteHost": "{{name}} 삭제 실패", "importSkipExisting": "가져오기 (기존 항목 건너뛰기)", - "importSSHConfig": "SSH 설정에서 가져오기", + "importSSHConfig": "SSH 구성에서 가져오기", "connectionDetails": "연결 세부 정보", "ssh": "SSH", "telnet": "텔넷", "remoteDesktop": "원격 데스크톱", "port": "포트", "username": "사용자 이름", - "folder": "접는 사람", + "folder": "폴더", "tags": "태그", - "pin": "핀", + "pin": "고정", "addHost": "호스트 추가", "editHost": "호스트 편집", - "cloneHost": "클론 호스트", + "cloneHost": "호스트 복제", "enableTerminal": "터미널 활성화", "enableTunnel": "터널 활성화", "enableFileManager": "파일 관리자 활성화", @@ -546,382 +585,399 @@ "defaultPath": "기본 경로", "connection": "연결", "upload": "업로드", - "authentication": "입증", + "authentication": "인증", "password": "비밀번호", - "key": "열쇠", - "credential": "신임장", + "key": "키", + "credential": "자격 증명", "none": "없음", "sshPrivateKey": "SSH 개인 키", "keyType": "키 유형", "uploadFile": "파일 업로드", - "tabGeneral": "일반적인", + "tabGeneral": "일반", "tabSsh": "SSH", - "tabTerminal": "단말기", + "tabTerminal": "터미널", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "터널", - "tabDocker": "도커", + "tabDocker": "Docker", "tabFiles": "파일", "tabStats": "호스트 메트릭", "tabHostMetrics": "호스트 메트릭", "tabTelnet": "텔넷", - "tabSharing": "공유하기", - "tabAuthentication": "입증", - "terminal": "단말기", + "tabSharing": "공유", + "tabAuthentication": "인증", + "terminal": "터미널", "tunnel": "터널", "fileManager": "파일 관리자", "serverStats": "호스트 메트릭", "status": "상태", - "folderRenamed": "폴더 \"{{oldName}}\"의 이름이 \"{{newName}}\"로 성공적으로 변경되었습니다.", - "failedToRenameFolder": "폴더 이름 변경에 실패했습니다", - "movedToFolder": "{{count}} 호스트를 \"{{folder}} \"로 이동했습니다.", + "folderRenamed": "폴더 \"{{oldName}}\"이(가) \"{{newName}}\"(으)로 성공적으로 이름이 변경되었습니다.", + "failedToRenameFolder": "폴더 이름 변경에 실패했습니다.", + "movedToFolder": "호스트 {{count}}개를 \"{{folder}}\" 폴더로 이동했습니다", "editHostTooltip": "호스트 편집", "statusChecks": "상태 확인", - "metricsCollection": "측정항목 수집", - "metricsInterval": "측정항목 수집 간격", - "metricsIntervalDesc": "서버 통계 수집 빈도 (5초 ~ 1시간)", - "behavior": "행동", + "metricsCollection": "메트릭 수집", + "metricsInterval": "메트릭 수집 간격", + "metricsIntervalDesc": "서버 통계 수집 빈도 (5초 - 1시간)", + "behavior": "동작", "themePreview": "테마 미리보기", - "theme": "주제", - "fontFamily": "글꼴 패밀리", + "theme": "테마", + "fontFamily": "글꼴", "fontSize": "글꼴 크기", - "letterSpacing": "글자 간격", - "lineHeight": "선 높이", + "letterSpacing": "자간", + "lineHeight": "줄 간격", "cursorStyle": "커서 스타일", "cursorBlink": "커서 깜빡임", "scrollbackBuffer": "스크롤백 버퍼", "bellStyle": "벨 스타일", - "rightClickSelectsWord": "마우스 오른쪽 버튼을 클릭하고 단어를 선택하세요.", - "fastScrollModifier": "빠른 스크롤 수정자", + "rightClickSelectsWord": "우클릭 단어 선택", + "fastScrollModifier": "빠른 스크롤 수정 키", "fastScrollSensitivity": "빠른 스크롤 감도", "sshAgentForwarding": "SSH 에이전트 포워딩", "backspaceMode": "백스페이스 모드", - "startupSnippet": "스타트업 스니펫", - "selectSnippet": "코드 조각을 선택하세요", - "forceKeyboardInteractive": "키보드 상호작용 강제 적용", + "startupSnippet": "시작 스니펫", + "selectSnippet": "스니펫 선택", + "forceKeyboardInteractive": "키보드 인터랙티브 강제", "overrideCredentialUsername": "자격 증명 사용자 이름 재정의", - "overrideCredentialUsernameDesc": "자격 증명의 사용자 이름 대신 위에 지정된 사용자 이름을 사용하십시오.", - "oidcUsernameHint": "$oidc.preferred_username을 사용하여 OIDC 로그인 이름을 대체하십시오.", + "overrideCredentialUsernameDesc": "자격 증명의 사용자 이름 대신 위에 지정된 사용자 이름을 사용합니다", + "oidcUsernameHint": "OIDC 로그인 이름으로 $oidc.preferred_username을 사용하세요", + "tailscaleUsernameHint": "이 값은 Tailscale ID가 Tailnet의 SSH ACL에 부여된 Unix 사용자여야 하며, 반드시 root일 필요는 없습니다.", "jumpHostChain": "점프 호스트 체인", "portKnocking": "포트 노킹", "addKnock": "포트 추가", "addProxyNode": "노드 추가", "proxyNode": "프록시 노드", "proxyType": "프록시 유형", - "quickActions": "빠른 조치", + "quickActions": "빠른 동작", "sudoPasswordAutoFill": "Sudo 비밀번호 자동 완성", "sudoPassword": "Sudo 비밀번호", - "keepaliveInterval": "유지 간격(밀리초)", - "moshCommand": "MOSH 명령", + "keepaliveInterval": "Keepalive 간격 (ms)", + "moshCommand": "MOSH 명령어", "environmentVariables": "환경 변수", "addVariable": "변수 추가", - "docker": "도커", + "docker": "Docker", "copyTerminalUrl": "터미널 URL 복사", "copyFileManagerUrl": "파일 관리자 URL 복사", "copyRemoteDesktopUrl": "원격 데스크톱 URL 복사", - "failedToConnect": "콘솔에 연결하지 못했습니다.", - "connect": "연결하다", - "disconnect": "연결을 끊으세요", + "failedToConnect": "콘솔 연결 실패", + "connect": "연결", + "disconnect": "연결 해제", "start": "시작", "enableStatusCheck": "상태 확인 활성화", "enableMetrics": "메트릭 활성화", "bulkUpdateFailed": "일괄 업데이트 실패", - "selectAll": "모두 선택", - "deselectAll": "모두 선택 해제", + "selectAll": "전체 선택", + "deselectAll": "전체 선택 해제", "protocols": "프로토콜", "secureShell": "보안 셸", "virtualNetwork": "가상 네트워크", "unencryptedShell": "암호화되지 않은 셸", - "addressIp": "주소/IP", - "friendlyName": "친근한 이름", + "addressIp": "주소 / IP", + "friendlyName": "친숙한 이름", "macAddress": "MAC 주소", - "wolBroadcastAddress": "WoL 방송 주소", - "wolBroadcastAddressDesc": "Docker/라우팅된 네트워크(예: 192.168.1.255)에 대한 선택적 지향 브로드캐스트입니다. 255.255.255.255를 사용하려면 비워 두십시오.", + "wolBroadcastAddress": "WoL 브로드캐스트 주소", + "wolBroadcastAddressDesc": "Docker/라우팅 네트워크를 위한 선택적 방향성 브로드캐스트입니다(예: 192.168.1.255). 비워두면 255.255.255.255가 사용됩니다.", "folderAndAdvanced": "폴더 및 고급", "privateNotes": "개인 메모", - "privateNotesPlaceholder": "이 서버에 대한 자세한 정보...", - "pinToTop": "맨 위에 고정", - "pinToTopDesc": "이 호스트를 항상 목록 맨 위에 표시하세요", + "privateNotesPlaceholder": "이 서버에 대한 세부 정보...", + "pinToTop": "상단에 고정", + "pinToTopDesc": "항상 이 호스트를 목록 상단에 표시", "portKnockingSequence": "포트 노킹 시퀀스", - "addKnockBtn": "노크를 추가하세요", + "addKnockBtn": "노크 추가", "noPortKnocking": "포트 노킹이 구성되지 않았습니다.", "knockPort": "노크 포트", - "protocol": "규약", - "delayAfterMs": "지연 시간(밀리초)", - "useSocks5Proxy": "SOCKS5 프록시를 사용하세요", - "useSocks5ProxyDesc": "프록시 서버를 통한 연결 라우팅", + "protocol": "프로토콜", + "delayAfterMs": "지연 시간(ms)", + "useSocks5Proxy": "SOCKS5 프록시 사용", + "useSocks5ProxyDesc": "프록시 서버를 통해 연결 라우팅", + "connectionOrigin": "연결 출처", + "connectionOriginDesc": "이 호스트의 SSH 연결이 시작된 위치입니다. 데스크톱 앱의 전역 기본값을 재정의합니다.", + "connectionOriginDefault": "기본값을 사용합니다", + "connectionOriginLocal": "이 장치(로컬 네트워크)", + "connectionOriginRemote": "원격 서버", "proxyHost": "프록시 호스트", "proxyPort": "프록시 포트", "proxyUsername": "프록시 사용자 이름", "proxyPassword": "프록시 비밀번호", - "proxySingleMode": "싱글 프록시", + "proxySingleMode": "단일 프록시", "proxyChainMode": "프록시 체인", - "you": "너", + "you": "내 컴퓨터", "jumpHostChainLabel": "점프 호스트 체인", "addJumpBtn": "점프 추가", - "noJumpHosts": "구성된 점프 호스트가 없습니다.", - "selectAServer": "서버를 선택하세요...", + "noJumpHosts": "점프 호스트가 구성되지 않았습니다.", + "selectAServer": "서버 선택...", "sshPort": "SSH 포트", - "authMethod": "인증 방식", + "authMethod": "인증 방법", "storedCredential": "저장된 자격 증명", - "selectACredential": "자격증명을 선택하세요...", - "vaultProfile": "Vault Signer 프로필", - "selectAVaultProfile": "Vault 프로필을 선택하세요...", - "vaultProfileHint": "설정은 공유 프로필에서 가져옵니다. 연결 시 OIDC를 통해 Vault에 로그인하게 됩니다. 비밀 정보는 저장되지 않습니다.", + "selectACredential": "자격 증명 선택...", + "vaultProfile": "Vault 서명자 프로필", + "selectAVaultProfile": "Vault 프로필 선택...", + "vaultProfileHint": "설정은 공유 프로필에서 가져옵니다. 연결 시 OIDC를 통해 Vault에 로그인하게 됩니다. 비밀은 저장되지 않습니다.", "vaultNewProfile": "새 프로필", "vaultManageProfiles": "Vault 프로필 관리", - "vaultAddrLabel": "금고 주소", + "vaultAddrLabel": "Vault 주소", "vaultNamespaceLabel": "네임스페이스", "vaultOidcMountLabel": "OIDC 인증 마운트", "vaultOidcRoleLabel": "OIDC 역할", - "vaultSshMountLabel": "SSH 비밀 마운트", + "vaultSshMountLabel": "SSH 시크릿 마운트", "vaultSshRoleLabel": "SSH 서명자 역할", - "vaultValidPrincipalsLabel": "유효한 원칙", + "vaultValidPrincipalsLabel": "유효한 프린시펄", "vaultKeyTypeLabel": "임시 키 유형", "vaultSharedLabel": "모든 사용자와 공유", "vaultCreateProfile": "프로필 생성", - "vaultProfileCreated": "Vault 프로필이 생성되었습니다.", - "vaultProfileSaved": "Vault 프로필이 저장되었습니다.", - "vaultProfileDeleted": "Vault 프로필이 삭제되었습니다.", - "vaultProfileSaveFailed": "Vault 프로필 저장에 실패했습니다.", - "vaultProfileDeleteFailed": "Vault 프로필 삭제에 실패했습니다.", + "vaultProfileCreated": "Vault 프로필 생성됨", + "vaultProfileSaved": "Vault 프로필 저장됨", + "vaultProfileDeleted": "Vault 프로필 삭제됨", + "vaultProfileSaveFailed": "Vault 프로필 저장 실패", + "vaultProfileDeleteFailed": "Vault 프로필 삭제 실패", "vaultSaveProfile": "프로필 저장", - "vaultProfileValidationError": "이름, Vault 주소 및 SSH 서명자 역할은 필수 입력 사항입니다.", + "vaultProfileValidationError": "이름, Vault 주소 및 SSH 서명자 역할이 필요합니다.", "vaultNoProfiles": "아직 Vault 프로필이 없습니다.", - "vaultSharedBadge": "공유됨", + "vaultSharedBadge": "공유", "keyTypeLabel": "키 유형", "keyTypeAuto": "자동 감지", - "keyPasteTab": "반죽", + "keyPasteTab": "붙여넣기", "keyUploadTab": "업로드", - "keyFileLoaded": "키 파일이 로드되었습니다.", - "keyUploadClick": ".pem / .key / .ppk 파일을 업로드하려면 클릭하세요.", - "clearKey": "지우기 키", - "keySaved": "SSH 키가 저장되었습니다", - "keyReplaceNotice": "아래에 새 키를 붙여넣어 기존 키를 대체하세요.", + "keyFileLoaded": "키 파일 로드됨", + "keyUploadClick": ".pem / .key / .ppk 파일을 업로드하려면 클릭", + "clearKey": "키 지우기", + "keySaved": "SSH 키 저장됨", + "keyReplaceNotice": "교체하려면 아래에 새 키를 붙여넣으세요.", "keyPassphraseSaved": "암호가 저장되었습니다. 변경하려면 입력하세요.", - "replaceKey": "키를 교체하세요", + "replaceKey": "키 교체", "docsLink": "문서 보기", - "opksshLabel": "오프크시", - "opksshDesc": "암호나 키 대신 ID 공급자를 사용하여 이 호스트에 로그인하십시오. 서버에 OPKSSH가 설정되어 있어야 합니다.", - "warpgateLabel": "워프게이트 게이트웨이", - "warpgateDesc": "이 호스트는 Warpgate SSH 프록시를 통해 연결됩니다. Termix는 인증 후 브라우저 기반 승인 절차를 자동으로 처리합니다.", + "opksshLabel": "OPKSSH", + "opksshDesc": "비밀번호나 키 대신 ID 공급자로 이 호스트에 로그인합니다. 서버에 OPKSSH가 설정되어 있어야 합니다.", + "warpgateLabel": "Warpgate 게이트웨이", + "warpgateDesc": "이 호스트는 Warpgate SSH 프록시를 통해 연결됩니다. Termix는 인증 후 브라우저 기반 승인 과정을 자동으로 처리합니다.", "agentLabel": "SSH 에이전트", - "agentDesc": "Termix 서버에서 실행 중인 SSH 에이전트(Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent)를 사용하여 인증하십시오. 에이전트는 Termix가 호스팅되는 시스템에서 실행 중이어야 합니다.", + "agentDesc": "Termix 서버에서 실행 중인 SSH 에이전트(Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent)를 사용하여 인증합니다. 에이전트는 Termix가 호스팅된 시스템에서 실행 중이어야 합니다.", "agentSocketPathLabel": "에이전트 소켓 경로", - "agentSocketPathPlaceholder": "SSH_AUTH_SOCK을 사용하려면 비워 두십시오.", - "agentSocketPathHint": "SSH_AUTH_SOCK 환경 변수에서 자동으로 감지하려면 비워 두거나, 사용자 지정 소켓 경로(예: /run/user/1000/gnupg/S.gpg-agent.ssh)를 입력하십시오.", - "tailscaleDeviceSelect": "테일스케일 장치를 선택하세요", - "tailscaleDeviceSelectPlaceholder": "기기를 선택하세요...", - "tailscaleNoApiKey": "Tailscale API 키가 구성되어 있지 않습니다. 기기 검색을 활성화하려면 관리자 설정에서 API 키를 추가하세요.", + "agentSocketPathPlaceholder": "SSH_AUTH_SOCK을 사용하려면 비워 두세요.", + "agentSocketPathHint": "비워 두면 SSH_AUTH_SOCK 환경 변수에서 자동 감지합니다. 또는 사용자 지정 소켓 경로를 입력하세요(예: /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Tailscale 기기 선택", + "tailscaleDeviceSelectPlaceholder": "기기 선택...", + "tailscaleNoApiKey": "Tailscale API 키가 구성되지 않았습니다. 기기 검색을 활성화하려면 관리자 설정에서 추가하세요.", "tailscaleDocsLink": "문서 보기", - "tailscaleLoadingDevices": "기기를 불러오는 중...", - "tailscaleNoDevices": "tailnet에서 장치를 찾을 수 없습니다.", - "tailscaleDeviceAutoFill": "기기를 선택하면 호스트 IP 주소가 자동으로 입력됩니다.", - "forceKeyboardInteractiveLabel": "키보드 강제 입력", - "forceKeyboardInteractiveShortDesc": "열쇠가 있더라도 수동으로 암호를 입력하도록 강제합니다.", - "allowLegacyAlgorithmsLabel": "기존 알고리즘 허용", - "allowLegacyAlgorithmsDesc": "업그레이드할 수 없는 구형 장치에 연결할 때 더 이상 사용되지 않는 SSH 알고리즘(ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc)을 활성화합니다.", - "insecure": "불안정한", - "terminalAppearance": "터미널 외관", + "tailscaleLoadingDevices": "장치를 불러오는 중...", + "tailscaleNoDevices": "테일넷에서 장치를 찾을 수 없습니다.", + "tailscaleDeviceAutoFill": "장치를 선택하면 호스트 IP 주소가 자동으로 채워집니다.", + "forceKeyboardInteractiveLabel": "키보드 인터랙티브 강제", + "forceKeyboardInteractiveShortDesc": "키가 있더라도 수동 비밀번호 입력을 강제합니다.", + "allowLegacyAlgorithmsLabel": "레거시 알고리즘 허용", + "allowLegacyAlgorithmsDesc": "업그레이드할 수 없는 오래된 장치에 연결하기 위해 더 이상 사용되지 않는 SSH 알고리즘(ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc)을 활성화합니다.", + "insecure": "비보안", + "terminalAppearance": "터미널 모양", "colorTheme": "색상 테마", "fontFamilyLabel": "글꼴 패밀리", "fontSizeLabel": "글꼴 크기", "cursorStyleLabel": "커서 스타일", - "letterSpacingPx": "자간(픽셀)", - "lineHeightLabel": "선 높이", + "letterSpacingPx": "글자 간격(px)", + "lineHeightLabel": "줄 높이", "bellStyleLabel": "벨 스타일", "backspaceModeLabel": "백스페이스 모드", - "cursorBlinking": "커서 깜빡임", - "cursorBlinkingDesc": "터미널 커서의 깜빡임 애니메이션을 활성화합니다.", - "rightClickSelectsWordLabel": "마우스 오른쪽 버튼을 클릭하고 단어를 선택합니다.", - "rightClickSelectsWordShortDesc": "마우스 오른쪽 버튼을 클릭하여 커서 아래 단어를 선택하세요.", + "cursorBlinking": "커서 깜박임", + "cursorBlinkingDesc": "터미널 커서의 깜박이는 애니메이션을 활성화합니다.", + "rightClickSelectsWordLabel": "오른쪽 클릭으로 단어 선택", + "rightClickSelectsWordShortDesc": "오른쪽 클릭 시 커서 아래의 단어를 선택합니다.", "backgroundImageLabel": "배경 이미지 URL", - "backgroundImageDesc": "터미널 배경 이미지에 사용할 URL(선택 사항)", + "backgroundImageDesc": "터미널 배경 이미지를 위한 선택적인 URL입니다.", "backgroundImageOpacityLabel": "배경 이미지 불투명도", - "customThemeColors": "맞춤 색상", + "customThemeColors": "사용자 정의 색상", "customThemeBackground": "배경", "customThemeForeground": "전경", "customThemeCursor": "커서", - "customThemeCursorAccent": "커서 강조", - "customThemeSelection": "선택", + "customThemeCursorAccent": "커서 강조색", + "customThemeSelection": "선택 영역", "customThemeAnsiColors": "ANSI 색상", - "customThemeBlack": "검은색", - "customThemeRed": "빨간색", - "customThemeGreen": "녹색", - "customThemeYellow": "노란색", + "customThemeBlack": "검정", + "customThemeRed": "빨강", + "customThemeGreen": "초록", + "customThemeYellow": "노랑", "customThemeBlue": "파란색", "customThemeMagenta": "마젠타", "customThemeCyan": "시안", - "customThemeWhite": "하얀색", + "customThemeWhite": "흰색", "customThemeBrightBlack": "밝은 검정색", "customThemeBrightRed": "밝은 빨간색", - "customThemeBrightGreen": "밝은 녹색", + "customThemeBrightGreen": "밝은 초록색", "customThemeBrightYellow": "밝은 노란색", "customThemeBrightBlue": "밝은 파란색", "customThemeBrightMagenta": "밝은 마젠타", - "customThemeBrightCyan": "밝은 청록색", + "customThemeBrightCyan": "밝은 시안", "customThemeBrightWhite": "밝은 흰색", - "customThemeResetTooltip": "기본 설정으로 재설정", - "syntaxHighlightingLabel": "구문 강조 표시", - "syntaxHighlightingDesc": "터미널 출력(오류, 경로, IP 주소, 타임스탬프)에 색상을 입힙니다.", - "syntaxHighlightingCategories": "주요 카테고리", - "syntaxHighlightingCategoriesDesc": "색칠할 콘텐츠 유형을 선택하세요", - "syntaxCategoryLogLevels": "로그 레벨", - "syntaxCategoryLogLevelsDesc": "오류, 경고, 정보, 디버그, 치명적", + "customThemeResetTooltip": "기본값으로 재설정", + "savedThemesLabel": "저장된 테마", + "saveAsGlobalTheme": "전역 테마로 저장", + "saveGlobalThemeNamePrompt": "이 테마의 이름을 입력하세요", + "saveGlobalThemeSuccess": "테마 저장됨", + "saveGlobalThemeError": "테마 저장에 실패했습니다", + "applyGlobalThemeTooltip": "이 테마를 적용하세요", + "deleteGlobalThemeTooltip": "이 테마를 삭제하세요", + "noSavedThemes": "아직 저장된 테마가 없습니다.", + "syntaxHighlightingLabel": "구문 강조", + "syntaxHighlightingDesc": "터미널 출력에 색상 지정 (오류, 경로, IP, 타임스탬프)", + "syntaxHighlightingCategories": "강조 항목", + "syntaxHighlightingCategoriesDesc": "색상을 지정할 콘텐츠 유형 선택", + "syntaxCategoryLogLevels": "로그 수준", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "파일 경로", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "타임스탬프", - "syntaxCategoryTimestampsDesc": "[12:34:56], 2024년 1월 15일", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "IP 주소", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "숫자로 표시된 부분", - "syntaxCategoryNumbersDesc": "포트 8080, 종료 코드 1, 상태 코드 404", - "behaviorAndAdvanced": "행동 및 고급", + "syntaxCategoryNumbers": "레이블이 지정된 숫자", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "동작 및 고급", "scrollbackBufferLabel": "스크롤백 버퍼", - "scrollbackMaxLines": "기록에 저장되는 최대 행 수", - "sshAgentForwardingLabel": "SSH 에이전트 포워딩", - "sshAgentForwardingShortDesc": "로컬 SSH 키를 이 호스트로 전달하세요.", + "scrollbackMaxLines": "기록에 유지할 최대 라인 수", + "sshAgentForwardingLabel": "SSH 에이전트 전달", + "sshAgentForwardingShortDesc": "로컬 SSH 키를 이 호스트로 전달", "useSSHTitleLabel": "SSH 창 제목 사용", - "useSSHTitleDesc": "호스트 이름 대신 셸 창 제목으로 탭 제목을 업데이트하세요.", - "enableAutoMosh": "자동 모싱 활성화", - "enableAutoMoshDesc": "가능하다면 SSH보다 Mosh를 선호합니다.", - "enableAutoTmux": "자동 Tmux 활성화", - "enableAutoTmuxDesc": "tmux 세션을 자동으로 시작하거나 연결합니다.", + "useSSHTitleDesc": "탭 제목을 호스트 이름 대신 셸 창 제목에서 업데이트합니다", + "enableAutoMosh": "Auto-Mosh 활성화", + "enableAutoMoshDesc": "사용 가능한 경우 SSH보다 Mosh를 우선합니다", + "enableAutoTmux": "Auto-Tmux 활성화", + "enableAutoTmuxDesc": "tmux 세션을 자동으로 시작하거나 연결합니다", "enableSessionLogging": "세션 로깅", - "enableSessionLoggingDesc": "추후 검토를 위해 터미널 세션 출력을 기록합니다.", - "enableCommandHistory": "명령 기록", - "enableCommandHistoryDesc": "이 터미널에서 실행된 명령어를 기록하여 기록 및 자동 완성 기능에 활용하세요.", + "enableSessionLoggingDesc": "나중에 검토할 수 있도록 터미널 세션 출력을 기록합니다", + "allowSessionSharing": "세션 공유 허용", + "allowSessionSharingDesc": "이 호스트에서 진행되는 라이브 세션을 링크 또는 다른 사용자와 공유하세요.", + "enableCommandHistory": "명령어 히스토리", + "enableCommandHistoryDesc": "히스토리 및 자동 완성을 위해 이 터미널에서 실행된 명령어를 기록합니다", "linkClickBehaviorLabel": "링크 클릭 동작", - "linkClickBehaviorDesc": "터미널에서 링크를 클릭했을 때 발생하는 동작을 제어합니다. 앱 수준 설정을 따르려면 '기본값'을 사용하세요.", - "linkClickBehaviorDefault": "기본 설정(앱 설정 따르기)", - "linkClickBehaviorConfirm": "팝업을 표시하여 열거나 복사하세요.", + "linkClickBehaviorDesc": "터미널에서 링크를 클릭할 때의 동작을 제어합니다. 앱 수준 설정을 따르려면 '기본값'을 사용하세요", + "linkClickBehaviorDefault": "기본값 (앱 설정 따르기)", + "linkClickBehaviorConfirm": "팝업을 표시하여 열거나 복사", "linkClickBehaviorDirect": "바로 열기", - "sudoPasswordAutoFillLabel": "Sudo 비밀번호 자동 완성", - "sudoPasswordAutoFillShortDesc": "sudo 암호를 입력하라는 메시지가 나타나면 자동으로 암호를 제공합니다.", - "sudoPasswordAutoFillDesc": "호스트 메트릭, 터미널 프롬프트 등이 권한 있는 명령을 자동으로 실행할 수 있도록 sudo 암호를 저장하세요.", + "sudoPasswordAutoFillLabel": "Sudo 비밀번호 자동 입력", + "sudoPasswordAutoFillShortDesc": "메시지가 표시될 때 sudo 비밀번호를 자동으로 제공합니다", + "sudoPasswordAutoFillDesc": "sudo 비밀번호를 저장하여 호스트 메트릭, 터미널 프롬프트 등이 자동으로 권한 있는 명령을 실행할 수 있도록 합니다", "sudoPasswordLabel": "Sudo 비밀번호", "environmentVariablesLabel": "환경 변수", "addVariableBtn": "변수 추가", - "noEnvVars": "환경 변수가 설정되지 않았습니다.", - "fastScrollModifierLabel": "빠른 스크롤 수정자", + "noEnvVars": "구성된 환경 변수가 없습니다", + "fastScrollModifierLabel": "빠른 스크롤 보조 키", "fastScrollSensitivityLabel": "빠른 스크롤 감도", - "moshCommandLabel": "모쉬 커맨드", - "startupSnippetLabel": "스타트업 스니펫", - "keepaliveIntervalLabel": "유지 간격(초)", - "maxKeepaliveMisses": "맥스 킵얼라이브 미스", + "moshCommandLabel": "Mosh 명령어", + "startupSnippetLabel": "시작 스니펫", + "keepaliveIntervalLabel": "연결 유지 간격(초)", + "maxKeepaliveMisses": "최대 연결 유지 미스", "tunnelSettings": "터널 설정", "enableTunneling": "터널링 활성화", - "enableTunnelingDesc": "이 호스트에 대해 SSH 터널 기능을 활성화합니다.", + "enableTunnelingDesc": "이 호스트에 대한 SSH 터널 기능을 활성화합니다", "serverTunnelsSection": "서버 터널", "addTunnelBtn": "터널 추가", - "noTunnelsConfigured": "구성된 터널이 없습니다.", + "noTunnelsConfigured": "구성된 터널이 없습니다", "tunnelLabel": "터널 {{number}}", - "tunnelType": "터널형", + "tunnelType": "터널 유형", "tunnelModeLocalDesc": "로컬 포트를 원격 서버(또는 해당 서버에서 접근 가능한 호스트)의 포트로 포워딩합니다.", - "tunnelModeRemoteDesc": "원격 서버의 포트를 사용자 컴퓨터의 로컬 포트로 포트 포워딩하십시오.", + "tunnelModeRemoteDesc": "원격 서버의 포트를 내 컴퓨터의 로컬 포트로 역방향 포워딩합니다.", "tunnelModeDynamicDesc": "동적 포트 포워딩을 위해 로컬 포트에 SOCKS5 프록시를 생성합니다.", - "sameHost": "이 호스트(직접 터널)", + "sameHost": "이 호스트 (직접 터널)", "endpointHost": "엔드포인트 호스트", - "endpointHostPlaceholder": "동일 호스트, SSH 호스트 또는 접속 가능한 호스트/IP", + "endpointHostPlaceholder": "동일 호스트, SSH 호스트 또는 접근 가능한 호스트/IP", "endpointPort": "엔드포인트 포트", - "bindHost": "호스트 바인딩", + "bindHost": "바인드 호스트", "sourcePort": "소스 포트", - "maxRetries": "최대 재시도 횟수", - "retryIntervalS": "재시도 간격(초)", + "maxRetries": "최대 재시도", + "retryIntervalS": "재시도 간격 (초)", "autoStartLabel": "자동 시작", "autoStartDesc": "호스트가 로드될 때 이 터널을 자동으로 연결합니다.", - "tunnelConnecting": "터널 연결...", - "tunnelDisconnected": "터널 연결이 끊어졌습니다", - "failedToConnectTunnel": "연결에 실패했습니다", - "failedToDisconnectTunnel": "연결 해제에 실패했습니다", + "tunnelConnecting": "터널 연결 중...", + "tunnelDisconnected": "터널 연결 끊김", + "failedToConnectTunnel": "연결 실패", + "failedToDisconnectTunnel": "연결 해제 실패", "dockerIntegration": "Docker 통합", "enableDockerMonitor": "Docker 활성화", - "enableDockerMonitorDesc": "Docker를 통해 이 호스트의 컨테이너를 모니터링하고 관리하세요.", + "enableDockerMonitorDesc": "Docker를 통해 이 호스트의 컨테이너를 모니터링하고 관리합니다.", "containerRuntime": "컨테이너 런타임", - "containerRuntimeDesc": "이 호스트에서 컨테이너 관리에 사용할 CLI를 선택하십시오.", - "containerRuntimeDocker": "도커", - "containerRuntimePodman": "포드맨", + "containerRuntimeDesc": "이 호스트에서 컨테이너 관리에 사용할 CLI를 선택합니다.", + "containerRuntimeDocker": "Docker", + "containerRuntimePodman": "Podman", "enableTmuxMonitor": "Tmux 모니터 활성화", - "enableTmuxMonitorDesc": "이 호스트를 Tmux 모니터에 표시하고 해당 호스트의 Tmux 액션을 사이드바에 추가합니다.", - "tabProxmox": "프록스목스", - "proxmoxIntegration": "프록스목스 통합", - "enableProxmox": "Proxmox를 활성화합니다.", - "enableProxmoxDesc": "이 호스트를 Proxmox 노드로 표시하세요. 이렇게 하면 게스트 검색 및 이 호스트에서 직접 가져오기가 가능해집니다.", + "enableTmuxMonitorDesc": "Tmux 모니터에 이 호스트를 표시하고 tmux 작업을 사이드바에 추가합니다.", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox 통합", + "enableProxmox": "Proxmox 활성화", + "enableProxmoxDesc": "이 호스트를 Proxmox 노드로 표시합니다. 이 호스트에서 직접 게스트를 검색하고 가져올 수 있도록 합니다.", "proxmoxDefaultAuthType": "기본 인증 유형", - "proxmoxDefaultAuthTypeDesc": "가져온 게스트 호스트에 적용되는 인증 방법입니다. 게스트에 연결하는 방식과 일치하는 인증 유형을 선택하십시오.", + "proxmoxDefaultAuthTypeDesc": "가져온 게스트 호스트에 적용되는 인증 방법입니다. 게스트에 연결하는 방식에 맞는 인증 유형을 선택하세요.", "authTypePassword": "비밀번호", "authTypeKey": "SSH 키", - "authTypeCredential": "신임장", - "authTypeOpkssh": "오프크시", + "authTypeCredential": "자격 증명", + "authTypeOpkssh": "OPKSSH", "authTypeNone": "없음", "proxmoxDefaultCredential": "기본 자격 증명", - "proxmoxDefaultCredentialDesc": "인증 유형이 '자격 증명'으로 설정된 경우 가져온 게스트 호스트에 사용되는 자격 증명입니다.", + "proxmoxDefaultCredentialDesc": "인증 유형이 자격 증명으로 설정된 경우 가져온 게스트 호스트에 사용되는 자격 증명입니다.", "proxmoxWindowsDetection": "Windows / RDP 감지", - "proxmoxWindowsDetectionDesc": "쉼표로 구분된 이름 패턴은 SSH 대신 RDP 연결을 실행합니다(대소문자 구분 없음).", + "proxmoxWindowsDetectionDesc": "SSH 대신 RDP를 트리거하는 쉼표로 구분된 이름 패턴(대소문자 구분 안 함)", "proxmoxDockerDetection": "Docker 감지", - "proxmoxDockerDetectionDesc": "Docker가 게스트를 일치시킬 수 있도록 쉼표로 구분된 이름 패턴입니다.", + "proxmoxDockerDetectionDesc": "일치하는 게스트에 대해 Docker를 활성화하는 쉼표로 구분된 이름 패턴", "proxmoxPreferredRanges": "선호하는 IP 범위", - "proxmoxPreferredRangesDesc": "게스트 운영체제에 여러 인터페이스가 있는 경우 IP 주소 선택 우선순위를 쉼표로 구분하여 입력합니다.", + "proxmoxPreferredRangesDesc": "게스트에 여러 인터페이스가 있을 때 IP 선택을 위한 우선 순위 순서의 쉼표로 구분된 접두사", "proxmoxAutoSync": "게스트 자동 동기화", - "proxmoxAutoSyncDesc": "주기적으로 이 Proxmox 노드를 검색하고 세션이 잠금 해제된 동안 가져온 게스트 호스트를 생성하거나 업데이트하세요.", + "proxmoxAutoSyncDesc": "세션이 잠금 해제되어 있을 때 주기적으로 이 Proxmox 노드를 검색하고 가져온 게스트 호스트를 생성하거나 업데이트합니다.", "proxmoxSyncInterval": "동기화 간격(분)", "proxmoxSyncIntervalDesc": "최소 5분. 스케줄러는 잠긴 사용자 세션을 건너뜁니다.", - "proxmoxMarkMissing": "누락된 손님을 표시하세요", - "proxmoxMarkMissingDesc": "이전에 불러온 게스트가 삭제되는 대신 사라질 경우 proxmox-missing 태그를 추가합니다.", + "proxmoxMarkMissing": "누락된 게스트 표시", + "proxmoxMarkMissingDesc": "이전에 가져온 게스트가 사라지면 삭제 대신 proxmox-missing 태그를 추가합니다.", "proxmoxLastSync": "마지막 동기화", - "proxmoxLastSyncNever": "아직 동기화되지 않았습니다", + "proxmoxLastSyncNever": "아직 동기화되지 않음", "proxmoxLastSyncNoResult": "결과 세부 정보 없음", - "proxmoxLastSyncSummary": "{{created}} 생성됨, {{updated}} 업데이트됨, {{markedMissing}} 누락됨, {{skipped}} 건너뛰어짐", + "proxmoxLastSyncSummary": "생성 {{created}}개, 업데이트 {{updated}}개, 누락 {{markedMissing}}개, 건너뜀 {{skipped}}개", "proxmoxLastSyncStatus": { "success": "성공", - "error": "실패한", + "error": "실패", "pending": "보류 중" }, - "proxmoxDiscoverAction": "Proxmox 게스트를 검색하고 가져오세요.", + "proxmoxDiscoverAction": "Proxmox 게스트 검색 및 가져오기", "proxmoxImportTitle": "Proxmox에서 가져오기", - "proxmoxSelectHost": "Proxmox 호스트를 선택하세요…", - "proxmoxDiscover": "발견하다", - "proxmoxDiscovering": "…를 발견하다", - "proxmoxDiscoverGuests": "손님을 만나보세요", - "proxmoxGuestsSelected_one": "{{count}} 게스트 — {{selected}} 선택됨", - "proxmoxGuestsSelected_other": "{{count}} 명의 손님 — {{selected}} 명이 선택됨", - "proxmoxSelectAll": "모두 선택하세요", - "proxmoxDeselectAll": "모두 선택 해제", + "proxmoxSelectHost": "Proxmox 호스트 선택…", + "proxmoxDiscover": "검색", + "proxmoxDiscovering": "검색 중…", + "proxmoxDiscoverGuests": "게스트 검색", + "proxmoxGuestsSelected_one": "게스트 {{count}}개 — {{selected}}개 선택됨", + "proxmoxGuestsSelected_other": "게스트 {{count}}개 — {{selected}}개 선택됨", + "proxmoxSelectAll": "전체 선택", + "proxmoxDeselectAll": "전체 선택 취소", "proxmoxNoGuests": "이 Proxmox 노드에서 게스트를 찾을 수 없습니다.", - "proxmoxImportButton_one": "{{count}} 호스트 가져오기", - "proxmoxImportButton_other": "{{count}} 호스트 가져오기", - "proxmoxResultImported": "{{count}} 가져옴", - "proxmoxResultUpdated": "{{count}} 업데이트됨", - "proxmoxResultFailed": "{{count}} 실패", + "proxmoxImportButton_one": "호스트 {{count}}개 가져오기", + "proxmoxImportButton_other": "{{count}}개 호스트 가져오기", + "proxmoxResultImported": "{{count}}개 가져옴", + "proxmoxResultUpdated": "{{count}}개 업데이트됨", + "proxmoxResultFailed": "{{count}}개 실패", + "proxmoxResultSkippedNoIp": "{{count}} 건너뛰었습니다(IP 주소를 찾을 수 없음)", "proxmoxImportComplete": "Proxmox 가져오기 완료: {{summary}}", - "proxmoxDiscoveryFailed": "발견 실패", + "proxmoxDiscoveryFailed": "검색 실패", "proxmoxImportFailed": "가져오기 실패", "enableFileManagerMonitor": "파일 관리자 활성화", - "enableFileManagerMonitorDesc": "SFTP를 통해 이 호스트의 파일을 탐색하고 관리하세요.", + "enableFileManagerMonitorDesc": "SFTP를 통해 이 호스트의 파일 탐색 및 관리", "scpLegacyLabel": "SCP 레거시 모드", - "scpLegacyDesc": "SFTP 하위 시스템을 지원하지 않는 서버(예: 내장형 또는 최소 기능 SSH 서버)에는 기존 파일 전송 방식을 사용하십시오.", + "scpLegacyDesc": "SFTP 하위 시스템을 지원하지 않는 서버(예: 임베디드 또는 최소 SSH 서버)에 레거시 파일 전송을 사용합니다.", "defaultPathLabel": "기본 경로", - "fileManagerPathHint": "이 호스트에서 파일 관리자가 실행될 때 열릴 디렉터리입니다.", + "fileManagerPathHint": "이 호스트의 파일 관리자 실행 시 열리는 디렉터리입니다.", "statusChecksLabel": "상태 확인", "enableStatusChecks": "상태 확인 활성화", - "enableStatusChecksDesc": "주기적으로 이 호스트에 ping을 보내 가용성을 확인하십시오.", - "useGlobalInterval": "글로벌 간격을 사용하세요", + "enableStatusChecksDesc": "주기적으로 이 호스트에 ping을 보내 가용성을 확인합니다.", + "useGlobalInterval": "전역 간격 사용", "useGlobalIntervalDesc": "서버 전체 상태 확인 간격으로 재정의합니다.", - "checkIntervalS": "점검 간격(초)", - "checkIntervalDesc": "각 연결 핑 사이의 간격(초)", - "metricsCollectionLabel": "측정항목 수집", + "checkIntervalS": "확인 간격(초)", + "checkIntervalDesc": "연결 핑 간격(초)", + "metricsCollectionLabel": "메트릭 수집", "enableMetricsLabel": "메트릭 활성화", "enableMetricsDesc": "이 호스트에서 CPU, RAM, 디스크 및 기타 메트릭을 수집합니다.", - "useGlobalMetrics": "글로벌 간격을 사용하세요", + "useGlobalMetrics": "전역 간격 사용", "useGlobalMetricsDesc": "서버 전체 메트릭 간격으로 재정의합니다.", - "metricsIntervalS": "측정 간격(초)", - "metricsIntervalDesc2": "측정 항목 스냅샷 간 간격(초)", - "visibleWidgets": "표시되는 위젯", - "widgetsMovedToHostMetrics": "이제 호스트 메트릭 탭에서 카드를 직접 추가, 정렬 및 크기 조정할 수 있습니다. 해당 호스트의 호스트 메트릭을 열고 사용자 지정을 사용하여 표시할 카드를 선택하세요.", + "metricsIntervalS": "메트릭 간격(초)", + "metricsIntervalDesc2": "메트릭 스냅샷 간격(초)", + "visibleWidgets": "표시 위젯", + "widgetsMovedToHostMetrics": "카드는 이제 호스트 메트릭 탭에서 직접 추가, 정렬 및 크기 조정됩니다. 이 호스트의 호스트 메트릭을 열고 사용자 정의를 사용하여 표시할 카드를 선택하세요.", "cpuUsageLabel": "CPU 사용량", - "cpuUsageDesc": "CPU 사용률, 평균 부하, 스파크라인 그래프", + "cpuUsageDesc": "CPU 백분율, 로드 평균, 스파크라인 그래프", "memoryLabel": "메모리 사용량", "memoryDesc": "RAM 사용량, 스왑, 캐시", "storageLabel": "디스크 사용량", @@ -933,115 +989,121 @@ "systemInfoLabel": "시스템 정보", "systemInfoDesc": "OS, 커널, 호스트 이름, 아키텍처", "recentLoginsLabel": "최근 로그인", - "recentLoginsDesc": "로그인 성공 및 실패 이벤트", - "topProcessesLabel": "주요 프로세스", - "topProcessesDesc": "PID, CPU%, 메모리%, 명령", - "listeningPortsLabel": "수신 포트", - "listeningPortsDesc": "프로세스 및 상태를 사용하여 포트를 엽니다.", + "recentLoginsDesc": "성공 및 실패한 로그인 이벤트", + "topProcessesLabel": "상위 프로세스", + "topProcessesDesc": "PID, CPU%, MEM%, 명령", + "listeningPortsLabel": "수신 대기 포트", + "listeningPortsDesc": "프로세스와 상태를 표시하는 열린 포트", "firewallLabel": "방화벽", "firewallDesc": "방화벽, AppArmor, SELinux 상태", - "quickActionsLabel": "빠른 조치", - "quickActionsToolbar": "빠른 실행 기능은 호스트 메트릭 도구 모음에 버튼으로 표시되어 한 번의 클릭으로 명령을 실행할 수 있습니다.", - "noQuickActions": "아직 빠른 조치는 없습니다.", - "buttonLabel": "버튼 라벨", - "selectSnippetPlaceholder": "코드 조각을 선택하세요...", + "quickActionsLabel": "빠른 작업", + "quickActionsToolbar": "빠른 작업은 한 번의 클릭으로 명령을 실행할 수 있도록 호스트 메트릭 도구 모음에 버튼으로 표시됩니다.", + "noQuickActions": "아직 빠른 작업이 없습니다.", + "buttonLabel": "버튼 레이블", + "selectSnippetPlaceholder": "스니펫 선택...", "addActionBtn": "작업 추가", - "hostSharedSuccessfully": "호스트가 성공적으로 공유했습니다.", - "failedToShareHost": "호스트 공유에 실패했습니다", - "accessRevoked": "접근 권한이 취소되었습니다", - "failedToRevokeAccess": "접근 권한 취소에 실패했습니다.", + "hostSharedSuccessfully": "호스트가 성공적으로 공유되었습니다.", + "failedToShareHost": "호스트 공유 실패", + "accessRevoked": "접근 권한이 취소되었습니다.", + "failedToRevokeAccess": "접근 권한 취소 실패", "cancelBtn": "취소", - "savingBtn": "절약...", + "savingBtn": "저장 중...", "addHostBtn": "호스트 추가", "hostUpdated": "호스트가 업데이트되었습니다.", - "hostCreated": "호스트가 생성했습니다", - "failedToSave": "호스트 저장에 실패했습니다", + "hostCreated": "호스트가 생성되었습니다.", + "failedToSave": "호스트 저장 실패", "credentialUpdated": "자격 증명이 업데이트되었습니다.", - "credentialCreated": "자격 증명이 생성되었습니다", - "failedToSaveCredential": "자격 증명을 저장하는 데 실패했습니다.", - "credentialNameRequired": "자격증에 사용할 이름을 입력해 주세요.", - "credentialAuthRequired": "암호, SSH 키 또는 둘 다를 추가하세요.", - "backToHosts": "호스트 목록으로 돌아가기", - "backToCredentials": "자격 증명 페이지로 돌아가기", + "credentialCreated": "자격 증명이 생성되었습니다.", + "failedToSaveCredential": "자격 증명 저장 실패", + "credentialNameRequired": "자격 증명 이름을 입력하세요.", + "credentialAuthRequired": "비밀번호, SSH 키 또는 둘 다 추가하세요", + "createCredentialFromHostBtn": "자격 증명 생성", + "createCredentialFromHostTitle": "호스트로부터 자격 증명 생성", + "createCredentialFromHostDesc": "이 호스트의 현재 사용자 이름, 암호 및/또는 SSH 키로 미리 채워진 재사용 및 공유 가능한 자격 증명 항목을 생성합니다.", + "backToHosts": "호스트로 돌아가기", + "backToCredentials": "자격증명으로 돌아가기", "pinned": "고정됨", - "noHostsFound": "호스트를 찾을 수 없습니다.", - "tryDifferentTerm": "다른 용어를 사용해 보세요.", - "addFirstHost": "시작하려면 첫 번째 호스트를 추가하세요.", - "noCredentialsFound": "자격 증명을 찾을 수 없습니다.", - "addCredentialBtn": "자격 증명 추가", - "updateCredentialBtn": "자격 증명 업데이트", - "features": "특징", + "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}}\"에 있는 모든 호스트를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "exitSelection": "선택 해제", + "importSkip": "가져오기 (기존 건너뛰기)", + "importOverwrite": "가져오기 (덮어쓰기)", + "collapseBtn": "접기", + "importExportBtn": "가져오기 / 내보내기", + "hostStatusesRefreshed": "호스트 상태 새로 고침 완료", + "failedToRefreshHosts": "호스트 새로 고침 실패", + "movedHostTo": "{{host}}을(를) \"{{folder}}\" 폴더로 이동했습니다", + "failedToMoveHost": "호스트 이동 실패", + "folderRenamedTo": "폴더 이름이 \"{{name}}\"(으)로 변경되었습니다", + "deletedFolder": "\"{{name}}\" 폴더가 삭제되었습니다", + "failedToDeleteFolder": "폴더 삭제 실패", + "deleteAllInFolder": "\"{{name}}\" 폴더의 모든 호스트를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", "folderPickerPlaceholder": "폴더 없음", - "folderPickerSearch": "검색 또는 생성(하위 폴더의 경우 / 사용)...", + "folderPickerSearch": "검색 또는 생성 (하위 폴더는 / 사용)...", "folderPickerNone": "폴더 없음", - "folderPickerCreate": "\"{{path}} \"을 생성하세요", + "folderPickerCreate": "\"{{path}}\" 생성", "folderPickerEmpty": "일치하는 폴더가 없습니다", "newFolder": "새 폴더", "createFolderTitle": "폴더 생성", - "editFolderTitle": "편집 폴더", + "editFolderTitle": "폴더 편집", "folderDialogDescription": "이름, 색상, 아이콘을 선택하세요. /를 사용하여 폴더를 중첩할 수 있습니다.", "folderNameLabel": "폴더 이름", - "folderNamePlaceholder": "예: 제작/웹", - "folderNestingHint": "/를 사용하여 레벨을 구분하고 중첩 폴더를 만들 수 있습니다.", + "folderNamePlaceholder": "예: Production/Web", + "folderNestingHint": "/를 사용해 레벨을 구분하고 중첩 폴더를 만드세요.", "folderColor": "색상", - "folderIcon": "상", - "folderPreview": "시사", + "folderIcon": "아이콘", + "folderCredential": "신임장", + "folderCredentialNone": "자격 증명이 부여되지 않았습니다.", + "folderCredentialHint": "이 폴더에 있는 호스트 중 자체 자격 증명을 선택하지 않고 \"저장된 자격 증명\" 인증을 사용하는 호스트는 이 자격 증명을 상속받습니다.", + "folderPreview": "미리보기", "folderNameFallback": "제목 없는 폴더", - "createFolderButton": "폴더 생성", - "saveFolderButton": "저장 폴더", + "createFolderButton": "폴더 만들기", + "saveFolderButton": "폴더 저장", "cancel": "취소", - "iconSearchPlaceholder": "검색 아이콘...", - "openAllSessions": "모든 세션을 엽니다", - "editFolder": "편집 폴더", + "iconSearchPlaceholder": "아이콘 검색...", + "openAllSessions": "모든 세션 열기", + "editFolder": "폴더 편집", "deleteFolder": "폴더 삭제", - "folderSaved": "폴더 저장됨", + "folderSaved": "폴더가 저장되었습니다", "failedToSaveFolder": "폴더 저장에 실패했습니다", - "folderDeleted": "삭제된 폴더 \"{{name}}\"", - "deleteFolderConfirm": "\"{{name}}\" 및 해당 {{count}} 호스트를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "folderDeleted": "\"{{name}}\" 폴더가 삭제되었습니다", + "deleteFolderConfirm": "\"{{name}}\" 폴더와 {{count}}개의 호스트를 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", "failedToMoveHosts": "호스트 이동에 실패했습니다", - "expandAll": "모든 폴더를 펼치세요", + "expandAll": "모든 폴더 펼치기", "collapseAll": "모든 폴더 접기", - "moreActions": "더", - "groupBy": "그룹화 기준", - "GroupByFolder": "접는 사람", - "GroupByTag": "꼬리표", + "moreActions": "더 보기", + "groupBy": "그룹화", + "GroupByFolder": "폴더", + "GroupByTag": "태그", "GroupByStatus": "상태", - "GroupByProtocol": "규약", + "GroupByProtocol": "프로토콜", "GroupByAuth": "인증 유형", - "groupUngrouped": "그룹화되지 않음", - "deletedHost": "삭제됨 {{name}}", + "groupUngrouped": "그룹 없음", + "deletedHost": "{{name}} 삭제됨", "copiedToClipboard": "클립보드에 복사됨", - "terminalUrlCopied": "터미널 URL이 복사되었습니다.", - "fileManagerUrlCopied": "파일 관리자 URL이 복사되었습니다.", - "tunnelUrlCopied": "터널 URL이 복사되었습니다", - "dockerUrlCopied": "Docker URL이 복사되었습니다.", - "hostMetricsUrlCopied": "호스트 메트릭 URL이 복사되었습니다", - "tmuxMonitorUrlCopied": "Tmux 모니터 URL이 복사되었습니다.", - "rdpUrlCopied": "RDP URL이 복사되었습니다.", - "vncUrlCopied": "VNC URL이 복사되었습니다.", - "telnetUrlCopied": "텔넷 URL이 복사되었습니다.", - "remoteDesktopUrlCopied": "원격 데스크톱 URL이 복사되었습니다.", - "expandActions": "확장 작업", - "collapseActions": "붕괴 조치", - "wakeOnLanAction": "LAN에서 웨이크업", - "wakeOnLanSuccess": "매직 패킷이 {{name}}로 전송되었습니다.", - "wakeOnLanError": "매직 패킷 전송에 실패했습니다.", - "cloneHostAction": "클론 호스트", + "terminalUrlCopied": "터미널 URL 복사됨", + "fileManagerUrlCopied": "파일 관리자 URL 복사됨", + "tunnelUrlCopied": "터널 URL 복사됨", + "dockerUrlCopied": "Docker URL 복사됨", + "hostMetricsUrlCopied": "호스트 메트릭 URL 복사됨", + "tmuxMonitorUrlCopied": "Tmux 모니터 URL 복사됨", + "rdpUrlCopied": "RDP URL 복사됨", + "vncUrlCopied": "VNC URL 복사됨", + "telnetUrlCopied": "Telnet URL 복사됨", + "remoteDesktopUrlCopied": "원격 데스크톱 URL 복사됨", + "expandActions": "작업 펼치기", + "collapseActions": "작업 접기", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "매직 패킷이 {{name}}에 전송되었습니다", + "wakeOnLanError": "매직 패킷 전송 실패", + "cloneHostAction": "호스트 복제", "copyAddress": "주소 복사", "copyLink": "링크 복사", "copyTerminalUrlAction": "터미널 URL 복사", @@ -1052,15 +1114,15 @@ "copyTmuxMonitorUrlAction": "Tmux 모니터 URL 복사", "copyRdpUrlAction": "RDP URL 복사", "copyVncUrlAction": "VNC URL 복사", - "copyTelnetUrlAction": "텔넷 URL 복사", + "copyTelnetUrlAction": "Telnet URL 복사", "copyRemoteDesktopUrlAction": "원격 데스크톱 URL 복사", - "deleteCredentialConfirm": "자격 증명 \"{{name}} \"을 삭제하시겠습니까?", - "deletedCredential": "삭제됨 {{name}}", + "deleteCredentialConfirm": "자격 증명 \"{{name}}\"을(를) 삭제하시겠습니까?", + "deletedCredential": "{{name}} 삭제됨", "deploySSHKeyTitle": "SSH 키 배포", "deployingBtn": "배포 중...", "deployBtn": "배포", - "failedToDeployKey": "키 배포에 실패했습니다", - "deleteHostsConfirm": "{{count}} 호스트{{plural}}를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "failedToDeployKey": "키 배포 실패", + "deleteHostsConfirm": "{{count}}개 호스트{{plural}}을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", "movedToRoot": "루트로 이동됨", "enableTerminalFeature": "터미널 활성화", "disableTerminalFeature": "터미널 비활성화", @@ -1070,94 +1132,94 @@ "disableTunnelsFeature": "터널 비활성화", "enableDockerFeature": "Docker 활성화", "disableDockerFeature": "Docker 비활성화", - "enableProxmoxFeature": "Proxmox를 활성화합니다.", - "disableProxmoxFeature": "Proxmox를 비활성화합니다.", + "enableProxmoxFeature": "Proxmox 활성화", + "disableProxmoxFeature": "Proxmox 비활성화", "addTagsPlaceholder": "태그 추가...", - "authDetails": "인증 정보", + "authDetails": "인증 세부 정보", "credType": "유형", - "generateKeyPairDesc": "새로운 키 쌍을 생성하면 개인 키와 공개 키가 자동으로 채워집니다.", + "generateKeyPairDesc": "새 키 쌍을 생성하면 개인 키와 공개 키가 자동으로 입력됩니다.", "generatingKey": "생성 중...", "generateLabel": "{{label}} 생성", "uploadFileBtn": "파일 업로드", "keyPassphraseOptional": "키 암호문 (선택 사항)", "sshPublicKeyOptional": "SSH 공개 키 (선택 사항)", - "publicKeyGenerated": "공개 키가 생성되었습니다", - "failedToGeneratePublicKey": "공개 키를 생성하는 데 실패했습니다.", - "publicKeyCopied": "공개 키가 복사되었습니다.", - "keyPairGenerated": "{{label}} 키 쌍이 생성되었습니다", - "failedToGenerateKeyPair": "키 쌍 생성에 실패했습니다.", - "searchHostsPlaceholder": "호스트, 주소, 태그를 검색하세요…", + "publicKeyGenerated": "공개 키 생성됨", + "failedToGeneratePublicKey": "공개 키 파생 실패", + "publicKeyCopied": "공개 키 복사됨", + "keyPairGenerated": "{{label}} 키 쌍 생성됨", + "failedToGenerateKeyPair": "키 쌍 생성 실패", + "searchHostsPlaceholder": "호스트, 주소, 태그 검색…", "searchCredentialsPlaceholder": "자격 증명 검색…", - "refreshBtn": "새로 고치다", + "refreshBtn": "새로 고침", "addTag": "태그 추가...", "deleteConfirmBtn": "삭제", - "tunnelRequirementsText": "SSH 서버는 /etc/ssh/sshd_config 파일에 GatewayPorts yes, AllowTcpForwarding yes, PermitRootLogin yes 설정이 되어 있어야 합니다.", - "deleteHostConfirm": "\"{{name}} \"을 삭제하시겠습니까?", - "enableAtLeastOneProtocol": "인증 및 연결 설정을 구성하려면 위의 프로토콜 중 하나 이상을 활성화하십시오.", + "tunnelRequirementsText": "SSH 서버의 /etc/ssh/sshd_config에 GatewayPorts yes, AllowTcpForwarding yes, PermitRootLogin yes가 설정되어 있어야 합니다.", + "deleteHostConfirm": "\"{{name}}\"을(를) 삭제하시겠습니까?", + "enableAtLeastOneProtocol": "위에서 하나 이상의 프로토콜을 활성화하여 인증 및 연결 설정을 구성하세요.", "keyPassphrase": "키 암호문", - "connectBtn": "연결하다", - "disconnectBtn": "연결을 끊으세요", + "connectBtn": "연결", + "disconnectBtn": "연결 해제", "basicInformation": "기본 정보", - "authDetailsSection": "인증 정보", + "authDetailsSection": "인증 세부 정보", "credTypeLabel": "유형", "hostsTab": "호스트", - "credentialsTab": "신임장", - "selectMultiple": "여러 개를 선택하세요", - "selectHosts": "호스트를 선택하세요", + "credentialsTab": "자격 증명", + "selectMultiple": "다중 선택", + "selectHosts": "호스트 선택", "connectionLabel": "연결", - "authenticationLabel": "입증", - "generateKeyPairTitle": "키 페어 생성", - "generateKeyPairDescription": "새로운 키 쌍을 생성하면 개인 키와 공개 키가 자동으로 채워집니다.", - "generateFromPrivateKey": "개인 키에서 생성", - "refreshBtn2": "새로 고치다", - "exitSelectionTitle": "선택 종료", - "exportAll": "모두 내보내기", + "authenticationLabel": "인증", + "generateKeyPairTitle": "키 쌍 생성", + "generateKeyPairDescription": "새 키 쌍을 생성하면 비공개 키와 공개 키가 자동으로 채워집니다.", + "generateFromPrivateKey": "비공개 키로 생성", + "refreshBtn2": "새로 고침", + "exitSelectionTitle": "선택 모드 종료", + "exportAll": "전체 내보내기", "exportForSharing": "공유용 내보내기", "addHostBtn2": "호스트 추가", "addCredentialBtn2": "자격 증명 추가", - "checkingHostStatuses": "호스트 상태를 확인하는 중...", + "checkingHostStatuses": "호스트 상태 확인 중...", "pinnedSection": "고정됨", - "hostsExported": "호스트 내보내기가 성공적으로 완료되었습니다.", - "hostsShareExported": "공유 가능한 호스트가 성공적으로 내보내졌습니다.", - "exportFailed": "호스트 내보내기에 실패했습니다", - "sampleDownloaded": "샘플 파일 다운로드됨", + "hostsExported": "호스트를 성공적으로 내보냈습니다.", + "hostsShareExported": "공유 가능한 호스트를 성공적으로 내보냈습니다.", + "exportFailed": "호스트 내보내기에 실패했습니다.", + "sampleDownloaded": "예제 파일이 다운로드되었습니다.", "failedToDeleteCredential2": "자격 증명 삭제에 실패했습니다.", "noFolderOption": "(폴더 없음)", - "nSelected": "{{count}} 선택됨", - "featuresMenu": "특징", - "moveMenu": "이동하다", - "connectSelected": "연결하다", + "nSelected": "{{count}}개 선택됨", + "featuresMenu": "기능", + "moveMenu": "이동", + "connectSelected": "연결", "cancelSelection": "취소", - "deployDialogDesc": "{{name}} 를 호스트의 authorized_keys에 배포합니다.", + "deployDialogDesc": "{{name}}을(를) 호스트의 authorized_keys에 배포합니다.", "targetHostLabel": "대상 호스트", - "selectHostOption": "호스트를 선택하세요...", + "selectHostOption": "호스트 선택...", "keyDeployedSuccess": "키가 성공적으로 배포되었습니다.", - "failedToDeployKey2": "키 배포에 실패했습니다", - "deletedCount": "삭제된 {{count}} 호스트", - "failedToDeleteCount": "{{count}} 호스트를 삭제하는 데 실패했습니다.", - "duplicatedHost": "\"{{name}} \"가 복제됨", - "failedToDuplicateHost": "호스트 복제에 실패했습니다.", - "updatedCount": "{{count}} 호스트가 업데이트되었습니다.", - "friendlyNameLabel": "친근한 이름", + "failedToDeployKey2": "키 배포에 실패했습니다.", + "deletedCount": "{{count}}개의 호스트를 삭제했습니다.", + "failedToDeleteCount": "{{count}}개의 호스트 삭제에 실패했습니다.", + "duplicatedHost": "중복된 호스트 \"{{name}}\"", + "failedToDuplicateHost": "호스트 복제 실패", + "updatedCount": "{{count}}개의 호스트를 업데이트했습니다", + "friendlyNameLabel": "별칭", "descriptionLabel": "설명", - "loadingHost": "호스트를 불러오는 중...", - "loadingHosts": "호스트를 불러오는 중...", - "loadingCredentials": "자격 증명을 불러오는 중...", + "loadingHost": "호스트 로드 중...", + "loadingHosts": "호스트 로드 중...", + "loadingCredentials": "인증 정보 로드 중...", "noHostsYet": "아직 호스트가 없습니다", - "noHostsMatchSearch": "검색 조건에 맞는 호스트가 없습니다.", + "noHostsMatchSearch": "검색과 일치하는 호스트가 없습니다", "hostNotFound": "호스트를 찾을 수 없습니다", - "searchHosts": "호스트를 검색하세요...", + "searchHosts": "호스트 검색...", "sortHosts": "호스트 정렬", - "sortDefault": "기본 주문", + "sortDefault": "기본 순서", "sortNameAsc": "이름 (A → Z)", "sortNameDesc": "이름 (Z → A)", - "sortIpAsc": "IP 주소(오름차순)", - "sortIpDesc": "IP 주소 (설명)", + "sortIpAsc": "IP 주소 (오름차순)", + "sortIpDesc": "IP 주소 (내림차순)", "sortOnlineFirst": "온라인 우선", "sortOfflineFirst": "오프라인 우선", - "sortPinnedFirst": "첫 번째 고정됨", - "filterHosts": "필터 호스트", - "filterClearAll": "필터 지우기", + "sortPinnedFirst": "고정 우선", + "filterHosts": "호스트 필터", + "filterClearAll": "필터 초기화", "filterStatusGroup": "상태", "filterOnline": "온라인", "filterOffline": "오프라인", @@ -1165,92 +1227,99 @@ "filterAuthGroup": "인증 유형", "filterAuthPassword": "비밀번호", "filterAuthKey": "SSH 키", - "filterAuthCredential": "신임장", + "filterAuthCredential": "인증 정보", "filterAuthNone": "없음", - "filterAuthOpkssh": "오프크시", - "filterProtocolGroup": "규약", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "프로토콜", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "텔넷", - "filterFeaturesGroup": "특징", - "filterFeatureTerminal": "단말기", + "filterFeaturesGroup": "기능", + "filterFeatureTerminal": "터미널", "filterFeatureFileManager": "파일 관리자", "filterFeatureTunnel": "터널", - "filterFeatureDocker": "도커", + "filterFeatureDocker": "Docker", "filterTagsGroup": "태그", "shareHost": "호스트 공유", "shareHostTitle": "공유: {{name}}", + "shareFolder": "공유 폴더", + "shareFolderTitle": "공유 폴더: {{name}}", + "folderSharedSuccessfully": "폴더에 공유된 {{count}} 호스트", + "failedToShareFolder": "폴더 공유에 실패했습니다", "sharing": { - "loadError": "공유 데이터를 불러오는 데 실패했습니다. 다시 시도해 주세요.", - "shareWithSection": "공유하기", + "loadError": "공유 데이터를 불러오지 못했습니다. 다시 시도해 주세요.", + "shareWithSection": "공유 대상", "usersTab": "사용자", "rolesTab": "역할", - "searchPlaceholder": "사용자 또는 역할을 검색하세요...", + "searchPlaceholder": "사용자 또는 역할 검색...", "noMatches": "일치하는 항목이 없습니다.", "permissionLevelLabel": "권한 수준", "levels": { "connect": { - "label": "연결하다", - "description": "터미널, 원격 데스크톱, 파일 관리자, 터널 및 Docker 세션만 사용 가능합니다. 호스트 구성에는 접근할 수 없습니다." + "label": "연결", + "description": "세션 열기만: 터미널, 원격 데스크톱, 파일 관리자, 터널 및 Docker. 호스트 구성에 액세스할 수 없습니다." }, "view": { - "label": "보다", - "description": "연결하고 호스트 구성을 확인하세요. 비밀 키는 절대 표시되지 않습니다." + "label": "보기", + "description": "연결 및 호스트 구성 보기. 비밀 정보는 표시되지 않습니다." }, "edit": { - "label": "편집하다", - "description": "호스트를 보고 수정할 수 있습니다. 비밀 키는 교체할 수 있지만 읽을 수는 없으며, 자격 증명 할당은 소유자만 사용할 수 있습니다." + "label": "편집", + "description": "보기 및 호스트 수정. 비밀 정보는 교체 가능하지만 읽을 수 없습니다. 자격 증명 할당은 소유자 전용입니다." }, "manage": { - "label": "관리하다", - "description": "호스트를 편집하고, 다른 사람과 공유하고, 권한 수준을 변경하고, 액세스 권한을 취소할 수 있습니다." + "label": "관리", + "description": "편집, 호스트 공유, 권한 수준 변경, 액세스 해지." } }, - "expiryLabel": "접근 만료", + "expiryLabel": "액세스 만료", "expiry": { - "never": "절대", + "never": "안 함", "oneHour": "1시간", "oneDay": "24시간", "sevenDays": "7일", "thirtyDays": "30일", - "custom": "관습" + "custom": "사용자 지정" }, - "customHoursPlaceholder": "접속 만료까지 남은 시간", - "shareButton": "공유하다", - "shareWithCount": "공유({{count}})", + "customHoursPlaceholder": "액세스 만료까지 시간", + "shareButton": "공유", + "shareWithCount": "공유 ({{count}})", "currentAccess": "현재 접근 권한", - "noAccessEntries": "이 호스트는 아직 공유되지 않았습니다.", - "grantedBy": "승인됨", - "expires": "만료됨", + "noAccessEntries": "이 호스트는 아직 공유되지 않았습니다", + "folderShareSummary": "이 폴더에 있는 {{shared}} 호스트 중 {{total}} 호스트가 공유되었습니다.", + "grantedBy": "공유자", + "expires": "만료", "expired": "만료됨", - "never": "절대", - "revoke": "취소", - "accessUpdated": "액세스 업데이트됨", - "accessUpdateFailed": "접근 권한 업데이트에 실패했습니다.", + "never": "만료 안 함", + "revoke": "철회", + "accessUpdated": "접근 권한이 업데이트되었습니다", + "accessUpdateFailed": "접근 권한 업데이트에 실패했습니다", "sharedBadge": "공유됨", - "sharedBadgeTooltip": "{{owner}} 님이 공유함 ({{level}} 접근 권한)", - "viewOnlyBanner": "이 호스트는 {{owner}} 님과 보기 권한으로 공유됩니다. 구성은 읽기 전용입니다.", - "sharedEditBanner": "이 호스트는 {{owner}} 님이 편집 권한으로 공유하고 있습니다. 변경 사항은 실제 호스트에 적용되며, 인증 참조는 소유자만 변경할 수 있습니다.", - "ownerOnlyControl": "호스트 소유자만 이 설정을 변경할 수 있습니다." + "sharedBadgeTooltip": "{{owner}}님이 공유함 ({{level}} 권한)", + "viewOnlyBanner": "이 호스트는 {{owner}}님이 보기 권한으로 공유했습니다. 설정은 읽기 전용입니다.", + "sharedEditBanner": "이 호스트는 {{owner}}님이 편집 권한으로 공유했습니다. 변경 사항은 실제 호스트에 적용되며, 인증 참조는 소유자만 변경할 수 있습니다.", + "ownerOnlyControl": "호스트 소유자만 변경할 수 있습니다" }, "guac": { "connection": "연결", - "authentication": "입증", + "authentication": "인증", "storedCredential": "저장된 자격 증명", - "noCredential": "자격증 없음 (직접 자격증명은 아래 참조)", - "authMethod": "인증 방식", + "noCredential": "자격 증명 없음 (직접 자격 증명 사용)", + "authMethod": "인증 방법", "authTypeDirect": "직접", - "authTypeCredential": "신임장", - "selectCredential": "자격증명을 선택하세요...", + "authTypeCredential": "자격 증명", + "authTypeNone": "없음", + "authTypeNoneDesc": "사용자 이름은 저장되지 않습니다. 접속할 때마다 사용자 이름과 비밀번호를 입력하라는 메시지가 표시되며, 해당 정보는 저장되지 않습니다.", + "selectCredential": "자격 증명 선택...", "connectionSettings": "연결 설정", - "displaySettings": "디스플레이 설정", + "displaySettings": "화면 설정", "audioSettings": "오디오 설정", "rdpPerformance": "RDP 성능", - "deviceRedirection": "기기 리디렉션", + "deviceRedirection": "장치 리디렉션", "session": "세션", "gateway": "게이트웨이", - "remoteApp": "원격 앱", + "remoteApp": "RemoteApp", "clipboard": "클립보드", "sessionRecording": "세션 녹화", "wakeOnLan": "Wake-on-LAN", @@ -1259,129 +1328,130 @@ "rdpPort": "RDP 포트", "username": "사용자 이름", "password": "비밀번호", + "passwordSaved": "비밀번호가 저장되었습니다. 변경하려면 입력하세요.", "domain": "도메인", "securityMode": "보안 모드", - "colorDepth": "색심도", + "colorDepth": "색 농도", "width": "너비", - "height": "키", + "height": "높이", "dpi": "DPI", "resizeMethod": "크기 조정 방법", - "clientName": "고객 이름", + "clientName": "클라이언트 이름", "initialProgram": "초기 프로그램", "serverLayout": "서버 레이아웃", "timezone": "시간대", - "loadBalanceInfo": "로드 밸런싱 정보 / 쿠키", - "loadBalanceInfoDesc": "RDS 팜 로드 밸런싱을 위한 RD 연결 브로커 쿠키(예: tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "로드 밸런스 정보 / 쿠키", + "loadBalanceInfoDesc": "RDS 팜 로드 밸런싱을 위한 RD 연결 브로커 쿠키 (예: tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacd 프록시", "guacdHostname": "guacd 호스트", "guacdHostnamePlaceholder": "전역 기본값", - "guacdPort": "과크드 항구", - "guacdProxyDesc": "이 연결에 대한 전역 guacd 인스턴스를 재정의합니다. 전역적으로 구성된 guacd를 사용하려면 비워 두십시오.", + "guacdPort": "guacd 포트", + "guacdProxyDesc": "이 연결에 대해 전역 guacd 인스턴스를 재정의합니다. 공란으로 두면 전역으로 구성된 guacd를 사용합니다.", "gatewayHostname": "게이트웨이 호스트 이름", "gatewayPort": "게이트웨이 포트", "gatewayUsername": "게이트웨이 사용자 이름", "gatewayPassword": "게이트웨이 비밀번호", "gatewayDomain": "게이트웨이 도메인", "remoteAppProgram": "RemoteApp 프로그램", - "workingDirectory": "업무 디렉토리", - "arguments": "논거", - "normalizeLineEndings": "줄 끝 문자 정규화", - "recordingPath": "기록 경로", - "recordingName": "녹음 이름", + "workingDirectory": "작업 디렉터리", + "arguments": "인수", + "normalizeLineEndings": "줄 끝 정규화", + "recordingPath": "녹화 경로", + "recordingName": "녹화 이름", "macAddress": "MAC 주소", - "broadcastAddress": "방송 주소", + "broadcastAddress": "브로드캐스트 주소", "udpPort": "UDP 포트", "waitTimeS": "대기 시간(초)", "driveName": "드라이브 이름", - "drivePath": "차량 통행로", + "drivePath": "드라이브 경로", "ignoreCertificate": "인증서 무시", - "ignoreCertificateDesc": "자체 서명 인증서를 사용하는 호스트에 대한 연결을 허용합니다.", - "forceLossless": "힘 손실 없음", - "forceLosslessDesc": "무손실 이미지 인코딩 강제 적용(더 높은 화질, 더 많은 대역폭)", + "ignoreCertificateDesc": "자체 서명 인증서가 있는 호스트에 연결 허용", + "forceLossless": "무손실 강제", + "forceLosslessDesc": "무손실 이미지 인코딩 강제 (더 높은 품질, 더 많은 대역폭)", "disableAudio": "오디오 비활성화", - "disableAudioDesc": "원격 세션의 모든 오디오를 음소거합니다.", - "enableAudioInput": "오디오 입력(마이크)을 활성화하세요.", - "enableAudioInputDesc": "로컬 마이크를 원격 세션으로 전달합니다.", - "wallpaper": "벽지", - "wallpaperDesc": "바탕 화면 배경 표시 (해제하면 성능이 향상됩니다)", + "disableAudioDesc": "원격 세션의 모든 오디오 음소거", + "enableAudioInput": "오디오 입력 활성화 (마이크)", + "enableAudioInputDesc": "로컬 마이크를 원격 세션으로 전달", + "wallpaper": "배경 화면", + "wallpaperDesc": "바탕 화면 배경 표시 (비활성화 시 성능 향상)", "theming": "테마", - "themingDesc": "시각적 테마 및 스타일을 활성화합니다.", - "fontSmoothing": "글꼴 부드럽게 처리", - "fontSmoothingDesc": "ClearType 글꼴 렌더링을 활성화합니다.", - "fullWindowDrag": "전체 창 드래그", - "fullWindowDragDesc": "드래그하는 동안 창 내용을 표시합니다.", + "themingDesc": "비주얼 테마 및 스타일 활성화", + "fontSmoothing": "글꼴 다듬기", + "fontSmoothingDesc": "ClearType 글꼴 렌더링 사용", + "fullWindowDrag": "전체 창 끌기", + "fullWindowDragDesc": "드래그하는 동안 창 내용 표시", "desktopComposition": "데스크톱 구성", - "desktopCompositionDesc": "Aero 유리 효과 활성화", + "desktopCompositionDesc": "Aero 글래스 효과 활성화", "menuAnimations": "메뉴 애니메이션", - "menuAnimationsDesc": "메뉴 페이드 및 슬라이드 애니메이션을 활성화합니다.", + "menuAnimationsDesc": "메뉴 페이드 및 슬라이드 애니메이션 활성화", "disableBitmapCaching": "비트맵 캐싱 비활성화", - "disableBitmapCachingDesc": "비트맵 캐시를 끄세요(오류 해결에 도움이 될 수 있습니다).", + "disableBitmapCachingDesc": "비트맵 캐시 끄기 (오류 해결에 도움이 될 수 있음)", "disableOffscreenCaching": "오프스크린 캐싱 비활성화", - "disableOffscreenCachingDesc": "오프스크린 캐시를 끄세요", + "disableOffscreenCachingDesc": "오프스크린 캐시 끄기", "disableGlyphCaching": "글리프 캐싱 비활성화", - "disableGlyphCachingDesc": "글리프 캐시를 끄세요", + "disableGlyphCachingDesc": "글리프 캐시 끄기", "enableGfx": "GFX 활성화", - "enableGfxDesc": "RemoteFX 그래픽 파이프라인을 사용하세요.", + "enableGfxDesc": "RemoteFX 그래픽 파이프라인 사용", "enablePrinting": "인쇄 활성화", - "enablePrintingDesc": "로컬 프린터를 원격 세션으로 리디렉션합니다.", + "enablePrintingDesc": "로컬 프린터를 원격 세션으로 리디렉션", "enableDriveRedirection": "드라이브 리디렉션 활성화", - "enableDriveRedirectionDesc": "원격 세션에서 로컬 폴더를 드라이브로 매핑합니다.", + "enableDriveRedirectionDesc": "로컬 폴더를 원격 세션에서 드라이브로 매핑", "createDrivePath": "드라이브 경로 생성", - "createDrivePathDesc": "폴더가 존재하지 않으면 자동으로 생성합니다.", + "createDrivePathDesc": "폴더가 없으면 자동 생성", "disableDownload": "다운로드 비활성화", - "disableDownloadDesc": "원격 세션에서 파일 다운로드를 방지합니다.", + "disableDownloadDesc": "원격 세션에서 파일 다운로드 방지", "disableUpload": "업로드 비활성화", - "disableUploadDesc": "원격 세션에 파일 업로드를 방지합니다.", + "disableUploadDesc": "원격 세션으로 파일 업로드 방지", "enableTouch": "터치 활성화", "enableTouchDesc": "터치 입력 전달 활성화", "consoleSession": "콘솔 세션", - "consoleSessionDesc": "새 세션을 생성하는 대신 콘솔(세션 0)에 연결하세요.", + "consoleSessionDesc": "새 세션 대신 콘솔(세션 0)에 연결", "sendWolPacket": "WOL 패킷 전송", - "sendWolPacketDesc": "연결하기 전에 매직 패킷을 보내서 이 호스트를 깨우세요.", + "sendWolPacketDesc": "연결 전에 호스트를 깨우기 위해 매직 패킷 전송", "disableCopy": "복사 비활성화", - "disableCopyDesc": "원격 세션에서 텍스트 복사를 방지합니다.", + "disableCopyDesc": "원격 세션에서 텍스트 복사 방지", "disablePaste": "붙여넣기 비활성화", - "disablePasteDesc": "원격 세션에 텍스트 붙여넣기를 방지합니다.", - "createPathIfMissing": "경로가 없으면 생성하세요", - "createPathIfMissingDesc": "녹음 디렉토리를 자동으로 생성합니다", + "disablePasteDesc": "원격 세션으로 텍스트 붙여넣기 방지", + "createPathIfMissing": "경로가 없으면 생성", + "createPathIfMissingDesc": "녹화 디렉터리 자동 생성", "excludeOutput": "출력 제외", - "excludeOutputDesc": "화면 출력은 기록하지 마십시오 (메타데이터만 기록).", + "excludeOutputDesc": "화면 출력 기록 안 함(메타데이터만)", "excludeMouse": "마우스 제외", - "excludeMouseDesc": "마우스 움직임을 기록하지 마세요.", - "includeKeystrokes": "키 입력을 포함하세요", - "includeKeystrokesDesc": "화면 출력 외에도 키 입력 내용을 그대로 기록합니다.", + "excludeMouseDesc": "마우스 움직임 기록 안 함", + "includeKeystrokes": "키 입력 포함", + "includeKeystrokesDesc": "화면 출력 외에 원시 키 입력 기록", "vncPort": "VNC 포트", "vncPassword": "VNC 비밀번호", - "vncUsernameOptional": "사용자 이름 (선택 사항)", - "vncLeaveBlank": "필요하지 않은 경우 비워 두십시오.", + "vncUsernameOptional": "사용자 이름(선택 사항)", + "vncLeaveBlank": "필요하지 않으면 비워 두세요", "cursorMode": "커서 모드", - "swapRedBlue": "빨간색/파란색 바꾸기", - "swapRedBlueDesc": "빨간색과 파란색 채널을 서로 바꿉니다(일부 색상 문제를 해결합니다).", + "swapRedBlue": "적색/청색 교환", + "swapRedBlueDesc": "적색과 청색 채널을 교환(일부 색상 문제 해결)", "readOnly": "읽기 전용", - "readOnlyDesc": "아무런 입력도 보내지 않고 원격 화면을 봅니다.", - "telnetPort": "텔넷 포트", + "readOnlyDesc": "입력을 보내지 않고 원격 화면 보기", + "telnetPort": "Telnet 포트", "terminalType": "터미널 유형", "fontName": "글꼴 이름", "fontSize": "글꼴 크기", "colorScheme": "색 구성표", "backspaceKey": "백스페이스 키", - "saveHostFirst": "먼저 호스트를 저장하세요.", - "sharingOptionsAfterSave": "호스트를 저장한 후에는 공유 옵션을 사용할 수 있습니다.", + "saveHostFirst": "호스트를 먼저 저장하세요.", + "sharingOptionsAfterSave": "공유 옵션은 호스트가 저장된 후 사용할 수 있습니다.", "permissionLevel": "권한 수준", "typeHeader": "유형", - "targetHeader": "목표", - "permissionHeader": "허가", + "targetHeader": "대상", + "permissionHeader": "권한", "cancelBtn": "취소", - "savingBtn": "절약...", + "savingBtn": "저장 중...", "updateHostBtn": "호스트 업데이트", "addHostBtn": "호스트 추가" } }, "commandPalette": { - "searchPlaceholder": "호스트, 명령어 또는 설정을 검색하세요...", - "quickActions": "빠른 조치", + "searchPlaceholder": "호스트, 명령 또는 설정 검색...", + "quickActions": "빠른 작업", "hostManager": "호스트 관리자", - "hostManagerDesc": "호스트를 관리, 추가 또는 편집합니다.", + "hostManagerDesc": "호스트 관리, 추가 또는 편집", "addNewHost": "새 호스트 추가", "addNewHostDesc": "새 호스트 등록", "adminSettings": "관리자 설정", @@ -1389,191 +1459,254 @@ "userProfile": "사용자 프로필", "userProfileDesc": "계정 및 환경설정 관리", "addCredential": "자격 증명 추가", - "addCredentialDesc": "SSH 키 또는 암호를 저장하세요", + "addCredentialDesc": "SSH 키 또는 비밀번호 저장", "tmuxMonitor": "Tmux 모니터", - "tmuxMonitorDesc": "호스트 전체에서 tmux 세션을 모니터링하세요.", + "tmuxMonitorDesc": "호스트 전반의 Tmux 세션 모니터링", "recentActivity": "최근 활동", "serversAndHosts": "서버 및 호스트", - "noHostsFound": "\"{{search}} \" 와 일치하는 호스트를 찾을 수 없습니다.", - "links": "모래밭", - "navigate": "탐색하기", - "select": "선택하다", - "toggleWith": "토글" + "noHostsFound": "\"{{search}}\"에 일치하는 호스트를 찾을 수 없습니다.", + "links": "링크", + "navigate": "이동", + "select": "선택", + "toggleWith": "전환 키:" }, "splitScreen": { - "paneEmpty": "패널 {{index}} - 비어 있음", - "noTabAssigned": "탭이 지정되지 않았습니다.", - "focusedPane": "활성 창" + "paneEmpty": "분할 창 {{index}} - 비어 있음", + "noTabAssigned": "할당된 탭 없음", + "focusedPane": "활성 분할 창" }, "connections": { "noConnections": "연결 없음", - "noConnectionsDesc": "터미널, 파일 관리자 또는 원격 데스크톱을 열어 연결 상태를 확인하세요.", - "connectedFor": "{{duration}}에 연결되었습니다.", + "noConnectionsDesc": "터미널, 파일 관리자 또는 원격 데스크톱을 열면 여기에 연결이 표시됩니다.", + "connectedFor": "{{duration}} 동안 연결됨", "connected": "연결됨", - "disconnected": "연결이 끊어졌습니다", - "closeTab": "닫기 탭", - "closeConnection": "닫기", - "forgetTab": "잊다", - "removeBackground": "제거하다", - "reconnect": "다시 연결", - "reopenTab": "다시 열다", - "sectionOpen": "열려 있는", - "sectionBackground": "배경", - "backgroundDesc": "연결이 끊어진 후에도 세션은 30분 동안 유지되며 다시 연결할 수 있습니다.", - "persisted": "백그라운드에서 지속됨", - "expiresIn": "{{duration}} 후에 만료됩니다.", - "search": "검색 연결...", - "noSearchResults": "검색 조건과 일치하는 결과가 없습니다.", - "rename": "세션 이름 변경" + "disconnected": "연결 끊김", + "closeTab": "탭 닫기", + "closeConnection": "연결 닫기", + "forgetTab": "지우기", + "removeBackground": "제거", + "reconnect": "재연결", + "reopenTab": "다시 열기", + "sectionOpen": "열림", + "sectionBackground": "백그라운드", + "backgroundDesc": "세션은 연결 해제 후 30분간 유지되며 다시 연결할 수 있습니다.", + "persisted": "백그라운드에 유지됨", + "expiresIn": "{{duration}} 후 만료", + "search": "연결 검색...", + "noSearchResults": "검색과 일치하는 연결이 없습니다.", + "rename": "세션 이름 바꾸기", + "sectionSharedWithMe": "나와 공유됨", + "sharedBy": "{{username}}님이 공유함", + "join": "가입하다", + "sharedSessionLabel": "{{hostName}} (공유됨)" + }, + "sessionSharing": { + "guestView": { + "loading": "공유 세션에 연결 중...", + "linkInvalid": "이 공유 링크는 유효하지 않거나, 만료되었거나, 취소되었습니다.", + "rateLimited": "시도 횟수가 너무 많습니다. 잠시 후 다시 시도해 주세요.", + "sessionEnded": "이번 세션이 종료되었습니다.", + "readOnlyBadge": "보기 전용" + }, + "modalTitle": "공유 세션", + "shareButton": "공유하다", + "notReadyToShare": "세션을 공유할 준비가 아직 되지 않았습니다.", + "modeTab": { + "link": "링크", + "user": "사용자" + }, + "linkModeDescription": "이 링크만 있으면 누구나 가입할 수 있으며, 계정은 필요하지 않습니다.", + "userModeDescription": "이미 해당 호스트에 접근 권한이 있는 특정 사용자와 공유하세요. 접근 권한이 없는 경우, 먼저 호스트를 공유하거나 링크를 사용하세요. 공유가 완료되면 해당 세션이 사용자의 '연결' 탭에 나타납니다.", + "permissionLevel": { + "label": "권한 수준", + "readOnly": "읽기 전용", + "readOnlyDescription": "실시간으로 세션을 시청할 수는 있지만, 입력하거나 상호 작용할 수는 없습니다.", + "readWrite": "읽기-쓰기", + "readWriteDescription": "소유자와 마찬가지로 세션에 입력하고 상호 작용할 수 있습니다." + }, + "expiryLabel": "링크 만료", + "createLinkButton": "링크 생성", + "createShareButton": "사용자와 공유", + "searchUsersPlaceholder": "사용자 검색...", + "noUsersFound": "사용자를 찾을 수 없습니다.", + "linkCreated": "공유 링크가 생성되었습니다", + "linkCopied": "링크가 클립보드에 복사되었습니다", + "copyLink": "링크 복사", + "shareCreated": "세션이 공유되었습니다. 상대방의 연결 탭에 표시됩니다.", + "shareFailed": "공유 폴더 생성에 실패했습니다.", + "userLacksHostAccess": "해당 사용자는 아직 이 호스트에 대한 접근 권한이 없습니다. 먼저 호스트를 공유하거나 링크를 사용하세요.", + "activeShares": "활성 주식", + "noActiveShares": "이번 세션에는 활성 주식이 없습니다.", + "revoke": "취소", + "revokeConfirmTitle": "이 지분을 취소하시겠습니까?", + "revokeConfirmDescription": "이 공유 폴더를 사용하는 사람은 누구든 즉시 접근 권한을 잃게 됩니다.", + "revoked": "공유 취소됨", + "revokeFailed": "공유 취소에 실패했습니다", + "joinCount": "{{count}} 참여", + "joinCount_other": "{{count}} 참여", + "expiresAt": "만료일 {{date}}", + "linkShareBadge": "링크", + "userShareBadge": "사용자: {{username}}", + "loadSharesFailed": "활성 공유 항목을 로드하는 데 실패했습니다." }, "guacamole": { "connecting": "{{type}} 세션에 연결 중...", "connectionError": "연결 오류", "connectionFailed": "연결 실패", - "failedToConnect": "연결 토큰을 가져오는 데 실패했습니다.", - "hostNotFound": "호스트를 찾을 수 없습니다", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "reconnect": "다시 연결", - "retry": "다시 해 보다", - "guacdUnavailable": "원격 데스크톱 서비스(guacd)를 사용할 수 없습니다. guacd가 실행 중이고 관리자 설정에서 올바르게 구성되어 있는지 확인하십시오.", + "failedToConnect": "연결 토큰을 가져오지 못했습니다.", + "hostNotFound": "호스트를 찾을 수 없음", + "noHostSelected": "선택된 호스트 없음", + "reconnect": "재연결", + "retry": "재시도", + "guacdUnavailable": "원격 데스크톱 서비스(guacd)를 사용할 수 없습니다. guacd가 실행 중이고 접근 가능하며 관리자 설정에서 올바르게 구성되었는지 확인하세요.", + "credentialPromptTitle": "RDP 자격 증명을 입력하십시오.", + "credentialPromptDescription": "이 호스트는 연결 시 자격 증명을 입력하라는 메시지를 표시하도록 설정되어 있습니다. 입력된 자격 증명은 해당 세션 동안에만 사용되며 저장되지 않습니다.", + "connect": "연결하다", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", "winL": "Win+L (화면 잠금)", - "winKey": "윈도우 키", - "ctrl": "Ctrl 키", + "winKey": "Windows 키", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "옮기다", - "win": "이기다", - "stickyActive": "{{key}} (잠금 - 클릭하여 해제)", - "stickyInactive": "{{key}} (클릭하여 잠그세요)", - "esc": "탈출하다", - "tab": "꼬리표", - "home": "집", - "end": "끝", - "pageUp": "페이지 위로", - "pageDown": "페이지 다운", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (고정됨 - 클릭하여 해제)", + "stickyInactive": "{{key}} (클릭하여 고정)", + "esc": "Esc", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", "arrowUp": "위쪽 화살표", "arrowDown": "아래쪽 화살표", "arrowLeft": "왼쪽 화살표", "arrowRight": "오른쪽 화살표", "fnToggle": "기능 키", "reconnect": "세션 재연결", - "collapse": "툴바 접기", - "expand": "확장 도구 모음", - "dragHandle": "드래그하여 위치를 변경하세요" + "collapse": "도구 모음 접기", + "expand": "도구 모음 펼치기", + "dragHandle": "드래그하여 위치 이동", + "switchToTrackpad": "트랙패드 모드로 전환하세요 (드래그하여 커서 이동, 탭하여 클릭).", + "switchToTouch": "터치 모드로 전환하세요 (클릭하려는 위치를 직접 탭하세요)." } }, "terminal": { "connect": "호스트에 연결", - "clear": "분명한", - "paste": "반죽", - "reconnect": "다시 연결", + "clear": "지우기", + "paste": "붙여넣기", + "reconnect": "재연결", "connectionLost": "연결이 끊어졌습니다", "connected": "연결됨", - "clipboardWriteFailed": "클립보드에 복사하는 데 실패했습니다. 페이지가 HTTPS 또는 localhost를 통해 제공되는지 확인하세요.", - "clipboardReadFailed": "클립보드에서 읽는 데 실패했습니다. 클립보드 권한이 부여되었는지 확인하십시오.", - "clipboardHttpWarning": "붙여넣기에는 HTTPS가 필요합니다. Ctrl+Shift+V를 사용하거나 Termix를 HTTPS를 통해 제공하십시오.", - "passwordPromptFillTitle": "저장된 비밀번호를 이 입력란에 입력하시겠습니까?", + "clipboardWriteFailed": "클립보드에 복사하지 못했습니다. 페이지가 HTTPS 또는 localhost에서 제공되는지 확인하세요.", + "clipboardReadFailed": "클립보드에서 읽지 못했습니다. 클립보드 권한이 허용되었는지 확인하세요.", + "clipboardHttpWarning": "붙여넣기는 HTTPS가 필요합니다. Ctrl+Shift+V를 사용하거나 Termix를 HTTPS로 제공하세요.", + "passwordPromptFillTitle": "저장된 비밀번호를 이 프롬프트에 입력하시겠습니까?", "unknownError": "알 수 없는 오류가 발생했습니다.", - "websocketError": "웹소켓 연결 오류", + "websocketError": "WebSocket 연결 오류", "connecting": "연결 중...", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "reconnecting": "다시 연결 중... ({{attempt}}/{{max}})", - "reconnected": "성공적으로 다시 연결되었습니다.", - "tmuxSessionCreated": "tmux 세션이 생성되었습니다: {{name}}", - "tmuxSessionAttached": "tmux 세션이 연결되었습니다: {{name}}", - "tmuxUnavailable": "원격 호스트에 tmux가 설치되어 있지 않으므로 표준 셸을 사용합니다.", + "noHostSelected": "선택된 호스트가 없습니다.", + "reconnecting": "재연결 중... ({{attempt}}/{{max}})", + "reconnected": "재연결 성공", + "tmuxSessionCreated": "tmux 세션 생성됨: {{name}}", + "tmuxSessionAttached": "tmux 세션 연결됨: {{name}}", + "tmuxUnavailable": "원격 호스트에 tmux가 설치되어 있지 않아 표준 셸로 대체합니다.", "tmuxSessionPickerTitle": "tmux 세션", - "tmuxSessionPickerDesc": "이 호스트에서 기존 tmux 세션이 발견되었습니다. 세션을 다시 연결하거나 새 세션을 생성하려면 하나를 선택하십시오.", + "tmuxSessionPickerDesc": "이 호스트에서 기존 tmux 세션을 찾았습니다. 다시 연결할 세션을 선택하거나 새 세션을 만드세요.", "tmuxWindows": "윈도우", - "tmuxWindowCount": "{{count}} 창", + "tmuxWindowCount": "{{count}}개 윈도우", "tmuxAttached": "연결된 클라이언트", - "tmuxAttachedCount": "{{count}} 첨부됨", - "tmuxLastActivity": "최근 활동", + "tmuxAttachedCount": "{{count}}개 연결됨", + "tmuxLastActivity": "마지막 활동", "tmuxTimeJustNow": "방금", - "tmuxTimeMinutes": "{{count}}몇 개월 전", + "tmuxTimeMinutes": "{{count}}분 전", "tmuxTimeHours": "{{count}}시간 전", "tmuxTimeDays": "{{count}}일 전", - "tmuxCreateNew": "새 세션을 시작하세요", - "tmuxCopyHint": "선택 영역을 조정한 후 Enter 키를 눌러 클립보드에 복사합니다.", + "tmuxCreateNew": "새 세션 시작", + "tmuxCopyHint": "선택 영역을 조정하고 Enter 키를 누르면 클립보드에 복사됩니다.", "tmuxDetach": "tmux 세션에서 분리", "tmuxDetached": "tmux 세션에서 분리됨", "maxReconnectAttemptsReached": "최대 재연결 시도 횟수에 도달했습니다.", - "closeTab": "닫다", + "closeTab": "닫기", "connectionTimeout": "연결 시간 초과", "terminalTitle": "터미널 - {{host}}", "terminalWithPath": "터미널 - {{host}}:{{path}}", "runTitle": "{{command}} 실행 중 - {{host}}", - "totpRequired": "2단계 인증이 필요합니다", + "totpRequired": "2단계 인증 필요", "totpCodeLabel": "인증 코드", - "totpVerify": "확인하다", - "warpgateAuthRequired": "워프게이트 인증 필요", + "totpVerify": "인증", + "mfaPromptRequired": "인증 필요", + "mfaPushRequired": "푸시 인증 필요", + "mfaMenuPlaceholder": "답변을 입력하세요", + "mfaWaitingApproval": "기기 승인을 기다리는 중입니다...", + "mfaSendRequest": "요청 보내기", + "warpgateAuthRequired": "Warpgate 인증 필요", "warpgateSecurityKey": "보안 키", "warpgateAuthUrl": "인증 URL", "warpgateOpenBrowser": "브라우저에서 열기", - "warpgateContinue": "인증이 완료되었습니다", - "opksshAuthRequired": "OPKSSH 인증이 필요합니다", - "opksshAuthDescription": "계속하려면 브라우저에서 인증을 완료하세요. 이 세션은 24시간 동안 유효합니다.", - "opksshOpenBrowser": "인증하려면 브라우저를 여세요", - "opksshWaitingForAuth": "브라우저에서 인증을 기다리는 중...", + "warpgateContinue": "인증 완료", + "opksshAuthRequired": "OPKSSH 인증 필요", + "opksshAuthDescription": "브라우저에서 인증을 완료하여 계속 진행하세요. 이 세션은 24시간 동안 유지됩니다.", + "opksshOpenBrowser": "브라우저로 인증하기", + "opksshWaitingForAuth": "브라우저에서 인증 대기 중...", "opksshAuthenticating": "인증 처리 중...", - "opksshTimeout": "인증 시간이 초과되었습니다. 다시 시도해 주세요.", - "opksshAuthFailed": "인증에 실패했습니다. 자격 증명을 확인하고 다시 시도해 주세요.", - "opksshSignInWith": "{{provider}}로 로그인하세요", + "opksshTimeout": "인증 시간이 초과되었습니다. 다시 시도하세요.", + "opksshAuthFailed": "인증에 실패했습니다. 자격 증명을 확인하고 다시 시도하세요.", + "opksshSignInWith": "{{provider}}로 로그인", "vaultAuthTitle": "Vault 로그인 필요", - "vaultAuthDescription": "HashiCorp Vault에 로그인하는 창이 열렸습니다. 로그인 절차를 완료하시면 연결이 자동으로 유지됩니다.", - "vaultAuthFailed": "볼트 인증에 실패했습니다. 다시 시도해 주세요.", + "vaultAuthDescription": "HashiCorp Vault 로그인을 위한 창이 열렸습니다. 해당 창에서 로그인을 완료하면 연결이 자동으로 계속됩니다.", + "vaultAuthFailed": "Vault 인증에 실패했습니다. 다시 시도하세요.", "vaultReopen": "로그인 창 다시 열기", - "sudoPasswordPopupTitle": "비밀번호를 입력하시겠습니까?", + "sudoPasswordPopupTitle": "비밀번호 입력", "linkDialogTitle": "링크 열기", - "linkDialogOpen": "열려 있는", + "linkDialogOpen": "열기", "linkDialogCopy": "복사", - "websocketAbnormalClose": "연결이 예기치 않게 종료되었습니다. 이는 리버스 프록시 또는 SSL 구성 문제 때문일 수 있습니다. 서버 로그를 확인하십시오.", + "websocketAbnormalClose": "연결이 예기치 않게 종료되었습니다. 리버스 프록시 또는 SSL 구성 문제일 수 있습니다. 서버 로그를 확인하세요.", "connectionLogTitle": "연결 로그", "connectionLogCopy": "로그를 클립보드에 복사", - "connectionLogEmpty": "아직 연결 로그가 없습니다.", + "connectionLogEmpty": "아직 연결 로그가 없습니다", "connectionLogWaiting": "연결 로그를 기다리는 중...", - "connectionLogCopied": "연결 로그가 클립보드에 복사되었습니다.", - "connectionLogCopyFailed": "로그를 클립보드에 복사하는 데 실패했습니다.", - "connectionRejected": "서버에서 연결이 거부되었습니다. 인증 및 네트워크 구성을 확인하십시오.", - "hostKeyRejected": "SSH 호스트 키 인증이 거부되었습니다. 연결이 취소되었습니다.", - "sessionTakenOver": "세션이 다른 탭에서 열려 있습니다. 다시 연결 중...", + "connectionLogCopied": "연결 로그가 클립보드에 복사되었습니다", + "connectionLogCopyFailed": "로그를 클립보드에 복사하지 못했습니다", + "connectionRejected": "서버에서 연결이 거부되었습니다. 인증 및 네트워크 구성을 확인하세요.", + "hostKeyRejected": "SSH 호스트 키 검증이 거부되었습니다. 연결이 취소되었습니다.", + "sessionTakenOver": "다른 탭에서 세션이 열렸습니다. 다시 연결 중...", "split": { - "splitTab": "분할 탭", + "splitTab": "탭 분할", "addToSplit": "분할에 추가", "removeFromSplit": "분할에서 제거" } }, "fileManager": { - "noHostSelected": "호스트가 선택되지 않았습니다.", + "noHostSelected": "선택된 호스트 없음", "initializingEditor": "편집기 초기화 중...", "file": "파일", - "folder": "접는 사람", + "folder": "폴더", "uploadFile": "파일 업로드", "downloadFile": "다운로드", - "extractArchive": "압축 해제", - "extractingArchive": "{{name}} 추출 중...", - "archiveExtractedSuccessfully": "{{name}} 가 성공적으로 추출되었습니다.", - "extractFailed": "추출 실패", + "extractArchive": "아카이브 추출", + "extractingArchive": "{{name}} 압축 해제 중...", + "archiveExtractedSuccessfully": "{{name}} 압축 해제 완료", + "extractFailed": "압축 해제 실패", "compressFile": "파일 압축", "compressFiles": "파일 압축", - "compressFilesDesc": "{{count}} 항목을 아카이브로 압축합니다.", + "compressFilesDesc": "{{count}}개 항목을 아카이브로 압축", "archiveName": "아카이브 이름", - "enterArchiveName": "아카이브 이름을 입력하세요...", + "enterArchiveName": "아카이브 이름 입력...", "compressionFormat": "압축 형식", - "selectedFiles": "선택된 파일", - "andMoreFiles": "그리고 {{count}} 더 보기...", - "compress": "압박 붕대", - "compressingFiles": "{{count}} 항목을 {{name}}로 압축 중...", - "filesCompressedSuccessfully": "{{name}} 이 성공적으로 생성되었습니다", + "selectedFiles": "선택한 파일", + "andMoreFiles": "그리고 {{count}}개 더...", + "compress": "압축", + "compressingFiles": "{{count}}개 항목을 {{name}}(으)로 압축 중...", + "filesCompressedSuccessfully": "{{name}} 생성 완료", "compressFailed": "압축 실패", - "edit": "편집하다", - "preview": "시사", - "previous": "이전의", + "edit": "편집", + "preview": "미리보기", + "previous": "이전", "next": "다음", - "pageXOfY": "{{total}}의 페이지 {{current}}", - "zoomOut": "축소하기", + "pageXOfY": "{{total}}페이지 중 {{current}}페이지", + "zoomOut": "축소", "zoomIn": "확대", "newFile": "새 파일", "newFolder": "새 폴더", @@ -1582,731 +1715,732 @@ "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": "파일을 여기에 드롭하여 업로드하세요", + "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": "파일을 검색하세요...", + "searchFiles": "파일 검색...", "upload": "업로드", - "selectHostToStart": "파일 관리를 시작할 호스트를 선택하십시오.", - "sshRequiredForFileManager": "파일 관리자는 SSH를 필요로 합니다. 이 호스트는 SSH가 활성화되어 있지 않습니다.", - "failedToConnect": "SSH 연결에 실패했습니다.", - "failedToLoadDirectory": "디렉토리를 불러오는 데 실패했습니다.", - "noSSHConnection": "SSH 연결을 사용할 수 없습니다.", + "selectHostToStart": "파일 관리를 시작할 호스트를 선택하세요", + "sshRequiredForFileManager": "파일 관리자에는 SSH가 필요합니다. 이 호스트에는 SSH가 활성화되어 있지 않습니다.", + "failedToConnect": "SSH 연결 실패", + "failedToLoadDirectory": "디렉터리 로드 실패", + "noSSHConnection": "SSH 연결을 사용할 수 없음", "copy": "복사", - "cut": "자르다", - "paste": "반죽", - "copyPath": "복사 경로", - "copyPaths": "복사 경로", + "cut": "잘라내기", + "paste": "붙여넣기", + "copyPath": "경로 복사", + "copyPaths": "경로 복사", "delete": "삭제", "properties": "속성", - "refresh": "새로 고치다", - "downloadFiles": "{{count}} 파일을 브라우저로 다운로드하세요", - "copyFiles": "{{count}} 항목을 복사하세요", - "cutFiles": "{{count}} 항목을 잘라내세요", - "deleteFiles": "{{count}} 항목을 삭제합니다.", - "filesCopiedToClipboard": "{{count}} 항목이 클립보드에 복사되었습니다", - "filesCutToClipboard": "{{count}} 항목을 클립보드에 복사합니다", + "refresh": "새로 고침", + "downloadFiles": "브라우저로 {{count}}개 파일 다운로드", + "copyFiles": "{{count}}개 항목 복사", + "cutFiles": "{{count}}개 항목 잘라내기", + "deleteFiles": "{{count}}개 항목 삭제", + "filesCopiedToClipboard": "{{count}}개 항목이 클립보드에 복사되었습니다", + "filesCutToClipboard": "{{count}}개 항목을 클립보드로 잘라냈습니다", "pathCopiedToClipboard": "경로가 클립보드에 복사되었습니다", - "pathsCopiedToClipboard": "{{count}} 경로가 클립보드에 복사되었습니다", - "failedToCopyPath": "클립보드에 경로를 복사하는 데 실패했습니다.", + "pathsCopiedToClipboard": "{{count}}개 경로가 클립보드에 복사되었습니다", + "failedToCopyPath": "클립보드에 경로를 복사하지 못했습니다", "copyFolderLink": "폴더 링크 복사", "copyCurrentFolderLink": "현재 폴더 링크 복사", - "folderLinkCopied": "폴더 링크가 클립보드에 복사되었습니다.", - "failedToCopyFolderLink": "폴더 링크 복사에 실패했습니다.", - "movedItems": "{{count}} 항목을 이동했습니다.", - "failedToDeleteItem": "항목 삭제에 실패했습니다", - "itemRenamedSuccessfully": "{{type}} 이름이 성공적으로 변경되었습니다.", - "failedToRenameItem": "항목 이름 변경에 실패했습니다", + "folderLinkCopied": "폴더 링크가 클립보드에 복사되었습니다", + "failedToCopyFolderLink": "폴더 링크를 복사하지 못했습니다", + "movedItems": "{{count}}개 항목을 이동했습니다", + "failedToDeleteItem": "항목을 삭제하지 못했습니다", + "itemRenamedSuccessfully": "{{type}} 이름이 변경되었습니다", + "failedToRenameItem": "항목 이름을 변경하지 못했습니다", "download": "다운로드", - "openExternalEditor": "외부에서 열기", - "chooseExternalEditor": "외부 편집기를 선택하세요", - "externalEditorSelected": "외부 편집기 선택됨", - "externalEditorDesktopOnly": "외부 편집기는 데스크톱 앱에서만 사용할 수 있습니다.", - "externalEditorOpened": "외부 편집기에서 열렸습니다. 거기서 저장하면 변경 사항이 서버에 다시 업로드됩니다.", - "failedToOpenExternalEditor": "외부 편집기를 열 수 없습니다.", - "failedToSelectExternalEditor": "외부 편집기를 선택하는 데 실패했습니다.", + "openExternalEditor": "외부 편집기로 열기", + "chooseExternalEditor": "외부 편집기 선택", + "externalEditorSelected": "외부 편집기가 선택되었습니다", + "externalEditorDesktopOnly": "외부 편집기는 데스크톱 앱에서만 사용할 수 있습니다", + "externalEditorOpened": "외부 편집기에서 열렸습니다. 저장하면 변경 사항이 서버에 업로드됩니다.", + "failedToOpenExternalEditor": "외부 편집기를 열지 못했습니다", + "failedToSelectExternalEditor": "외부 편집기를 선택하지 못했습니다", "permissions": "권한", "size": "크기", "modified": "수정됨", - "path": "길", - "confirmDelete": "{{name}}를 정말로 삭제하시겠습니까?", + "path": "경로", + "confirmDelete": "{{name}}을(를) 삭제하시겠습니까?", "permissionDenied": "권한이 거부되었습니다", "serverError": "서버 오류", - "fileSavedSuccessfully": "파일이 성공적으로 저장되었습니다.", - "failedToSaveFile": "파일 저장에 실패했습니다", - "confirmDeleteSingleItem": "\"{{name}} \"을 영구적으로 삭제하시겠습니까?", - "confirmDeleteMultipleItems": "{{count}} 항목을 영구적으로 삭제하시겠습니까?", - "confirmDeleteMultipleItemsWithFolders": "{{count}} 항목을 영구적으로 삭제하시겠습니까? 폴더와 그 내용물이 모두 포함됩니다.", - "confirmDeleteFolder": "폴더 \"{{name}}\"와 그 안의 모든 내용을 영구적으로 삭제하시겠습니까?", - "permanentDeleteWarning": "이 작업은 되돌릴 수 없습니다. 해당 항목은 서버에서 영구적으로 삭제됩니다.", - "recent": "최근의", + "fileSavedSuccessfully": "파일이 저장되었습니다", + "failedToSaveFile": "파일을 저장하지 못했습니다", + "confirmDeleteSingleItem": "\"{{name}}\"을(를) 영구적으로 삭제하시겠습니까?", + "confirmDeleteMultipleItems": "{{count}}개 항목을 영구적으로 삭제하시겠습니까?", + "confirmDeleteMultipleItemsWithFolders": "{{count}}개 항목을 영구적으로 삭제하시겠습니까? 폴더 및 해당 내용이 포함됩니다.", + "confirmDeleteFolder": "\"{{name}}\" 폴더와 모든 내용을 영구적으로 삭제하시겠습니까?", + "permanentDeleteWarning": "이 작업은 되돌릴 수 없습니다. 항목이 서버에서 영구적으로 삭제됩니다.", + "recent": "최근 항목", "pinned": "고정됨", - "folderShortcuts": "폴더 바로가기", - "failedToReconnectSSH": "SSH 세션 재연결에 실패했습니다.", - "openTerminalHere": "터미널을 여기에서 여세요", - "run": "달리다", - "openTerminalInFolder": "이 폴더에서 터미널을 엽니다.", - "openTerminalInFileLocation": "해당 파일 위치에서 터미널을 엽니다.", + "folderShortcuts": "폴더 바로 가기", + "failedToReconnectSSH": "SSH 세션 재연결에 실패했습니다", + "openTerminalHere": "여기서 터미널 열기", + "run": "실행", + "openTerminalInFolder": "이 폴더에서 터미널 열기", + "openTerminalInFileLocation": "파일 위치에서 터미널 열기", "runningFile": "실행 중 - {{file}}", - "onlyRunExecutableFiles": "실행 파일만 실행할 수 있습니다.", - "directories": "디렉토리", - "removedFromRecentFiles": "최근 파일에서 \"{{name}}\"를 제거했습니다.", + "onlyRunExecutableFiles": "실행 파일만 실행할 수 있습니다", + "directories": "디렉터리", + "removedFromRecentFiles": "최근 파일에서 \"{{name}}\" 제거됨", "removeFailed": "제거 실패", - "unpinnedSuccessfully": "\"{{name}}\" 고정 해제 성공", - "unpinFailed": "핀 해제 실패", - "removedShortcut": "바로가기 \"{{name}} \"를 제거했습니다.", - "removeShortcutFailed": "바로가기 제거 실패", - "clearedAllRecentFiles": "최근 파일을 모두 삭제했습니다.", + "unpinnedSuccessfully": "\"{{name}}\" 고정 해제 완료", + "unpinFailed": "고정 해제 실패", + "removedShortcut": "바로 가기 \"{{name}}\" 제거됨", + "removeShortcutFailed": "바로 가기 제거 실패", + "clearedAllRecentFiles": "모든 최근 파일 지움", "clearFailed": "지우기 실패", - "removeFromRecentFiles": "최근 파일에서 삭제", - "clearAllRecentFiles": "최근 파일을 모두 삭제합니다.", + "removeFromRecentFiles": "최근 파일에서 제거", + "clearAllRecentFiles": "모든 최근 파일 지우기", "unpinFile": "파일 고정 해제", - "removeShortcut": "바로가기 제거", - "pinFile": "핀 파일", - "addToShortcuts": "바로가기에 추가", + "removeShortcut": "바로 가기 제거", + "pinFile": "파일 고정", + "addToShortcuts": "바로 가기에 추가", "pasteFailed": "붙여넣기 실패", - "noUndoableActions": "되돌릴 수 없는 작업", - "undoCopySuccess": "실행 취소된 복사 작업: {{count}} 개의 복사된 파일이 삭제되었습니다.", - "undoCopyFailedDelete": "실행 취소 실패: 복사된 파일을 삭제할 수 없습니다.", - "undoCopyFailedNoInfo": "실행 취소 실패: 복사된 파일 정보를 찾을 수 없습니다.", - "undoMoveSuccess": "이동 취소 작업: {{count}} 파일을 원래 위치로 되돌렸습니다.", - "undoMoveFailedMove": "실행 취소 실패: 파일을 되돌릴 수 없습니다.", - "undoMoveFailedNoInfo": "실행 취소 실패: 이동된 파일 정보를 찾을 수 없습니다.", - "undoDeleteNotSupported": "삭제 작업은 되돌릴 수 없습니다. 파일이 서버에서 영구적으로 삭제되었습니다.", + "noUndoableActions": "실행 취소할 동작 없음", + "undoCopySuccess": "복사 작업 실행 취소: 복사된 파일 {{count}}개 삭제됨", + "undoCopyFailedDelete": "실행 취소 실패: 복사된 파일을 삭제할 수 없음", + "undoCopyFailedNoInfo": "실행 취소 실패: 복사된 파일 정보를 찾을 수 없음", + "undoMoveSuccess": "이동 작업 실행 취소: {{count}}개 파일을 원래 위치로 다시 이동했습니다", + "undoMoveFailedMove": "실행 취소 실패: 파일을 되돌릴 수 없음", + "undoMoveFailedNoInfo": "실행 취소 실패: 이동된 파일 정보를 찾을 수 없음", + "undoDeleteNotSupported": "삭제 작업은 실행 취소할 수 없습니다: 서버에서 파일이 영구 삭제됨", "undoTypeNotSupported": "지원되지 않는 실행 취소 작업 유형", - "undoOperationFailed": "실행 취소 작업이 실패했습니다.", + "undoOperationFailed": "실행 취소 작업 실패", "unknownError": "알 수 없는 오류", - "confirm": "확인하다", - "find": "찾다...", - "replace": "바꾸다", - "downloadInstead": "대신 다운로드하세요", + "confirm": "확인", + "find": "찾기...", + "replace": "바꾸기", + "downloadInstead": "대신 다운로드", "keyboardShortcuts": "키보드 단축키", "searchAndReplace": "검색 및 바꾸기", "editing": "편집", - "search": "찾다", + "search": "검색", "findNext": "다음 찾기", "findPrevious": "이전 찾기", - "save": "구하다", + "save": "저장", "selectAll": "모두 선택", - "undo": "끄르다", - "redo": "다시 하다", - "moveLineUp": "이동 라인", - "moveLineDown": "줄을 아래로 이동", - "toggleComment": "댓글 토글", + "undo": "실행 취소", + "redo": "다시 실행", + "moveLineUp": "줄 위로 이동", + "moveLineDown": "줄 아래로 이동", + "toggleComment": "주석 토글", "autoComplete": "자동 완성", - "imageLoadError": "이미지를 불러오는 데 실패했습니다.", - "startTyping": "입력을 시작하세요...", - "unknownSize": "크기 미상", + "imageLoadError": "이미지를 불러오지 못했습니다", + "startTyping": "입력하세요...", + "unknownSize": "알 수 없는 크기", "fileIsEmpty": "파일이 비어 있습니다", "largeFileWarning": "대용량 파일 경고", - "largeFileWarningDesc": "이 파일의 크기는 {{size}} 이므로 텍스트 모드로 열면 성능 문제가 발생할 수 있습니다.", - "fileNotFoundAndRemoved": "파일 \"{{name}}\"을 찾을 수 없어 최근/고정된 파일 목록에서 제거되었습니다.", - "failedToLoadFile": "파일 로드 실패: {{error}}", - "serverErrorOccurred": "서버 오류가 발생했습니다. 나중에 다시 시도해 주세요.", + "largeFileWarningDesc": "이 파일의 크기는 {{size}}이며, 텍스트로 열 경우 성능 문제가 발생할 수 있습니다.", + "fileNotFoundAndRemoved": "\"{{name}}\" 파일을 찾을 수 없어 최근/고정 파일에서 제거되었습니다.", + "failedToLoadFile": "파일을 불러오지 못했습니다: {{error}}", + "serverErrorOccurred": "서버 오류가 발생했습니다. 나중에 다시 시도하세요.", "autoSaveFailed": "자동 저장 실패", - "fileAutoSaved": "파일이 자동 저장되었습니다", - "moveFileFailed": "{{name}}를 이동하는 데 실패했습니다.", - "moveOperationFailed": "이동 작업이 실패했습니다.", - "canOnlyCompareFiles": "두 파일만 비교할 수 있습니다.", - "comparingFiles": "파일 비교: {{file1}} 및 {{file2}}", - "dragFailed": "드래그 작업이 실패했습니다.", - "filePinnedSuccessfully": "파일 \"{{name}}\"이 성공적으로 고정되었습니다.", + "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}} 이 성공적으로 다운로드되었습니다.", + "fileUnpinnedSuccessfully": "\"{{name}}\" 파일이 고정 해제되었습니다.", + "unpinFileFailed": "파일 고정 해제 실패", + "shortcutAddedSuccessfully": "폴더 바로가기 \"{{name}}\"이 추가되었습니다.", + "addShortcutFailed": "바로가기 추가 실패", + "operationCompletedSuccessfully": "{{count}}개 항목 {{operation}} 성공", + "operationCompleted": "{{count}}개 항목 {{operation}}", + "downloadFileSuccess": "파일 {{name}} 다운로드 성공", "downloadFileFailed": "다운로드 실패", "moveTo": "{{name}}로 이동", - "diffCompareWith": "{{name}}와 차이점을 비교합니다.", - "dragOutsideToDownload": "다운로드하려면 창 바깥쪽으로 드래그하세요({{count}} 파일).", + "diffCompareWith": "{{name}}와/과 Diff 비교", + "dragOutsideToDownload": "창 밖으로 드래그하여 다운로드 ({{count}}개 파일)", "newFolderDefault": "새 폴더", - "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} 항목을 {{target}}로 성공적으로 이동했습니다.", - "move": "이동하다", - "searchInFile": "파일에서 검색(Ctrl+F)", - "showKeyboardShortcuts": "키보드 단축키 표시", - "decreaseFontSize": "글꼴 크기를 줄이세요", - "increaseFontSize": "글꼴 크기를 키우세요", - "startWritingMarkdown": "마크다운 콘텐츠 작성을 시작하세요...", + "newFileDefault": "새 파일.txt", + "successfullyMovedItems": "{{count}}개 항목을 {{target}}에 성공적으로 이동했습니다.", + "move": "이동", + "searchInFile": "파일에서 검색 (Ctrl+F)", + "showKeyboardShortcuts": "키보드 단축키 보기", + "decreaseFontSize": "글꼴 크기 줄이기", + "increaseFontSize": "글꼴 크기 늘리기", + "startWritingMarkdown": "마크다운 내용 작성을 시작하세요...", "loadingFileComparison": "파일 비교 로딩 중...", - "reload": "재장전", - "compare": "비교하다", - "sideBySide": "나란히", + "reload": "새로 고침", + "compare": "비교", + "sideBySide": "나란히 보기", "inline": "인라인", "fileComparison": "파일 비교: {{file1}} vs {{file2}}", - "fileTooLarge": "파일 크기가 너무 큽니다: {{error}}", - "sshConnectionFailed": "SSH 연결에 실패했습니다. {{name}} ({{ip}}:{{port}} )에 대한 연결을 확인하십시오.", - "loadFileFailed": "파일 로드 실패: {{error}}", + "fileTooLarge": "파일이 너무 큽니다: {{error}}", + "sshConnectionFailed": "SSH 연결 실패. {{name}}({{ip}}:{{port}})에 대한 연결을 확인하세요.", + "loadFileFailed": "파일 불러오기 실패: {{error}}", "connecting": "연결 중...", - "connectedSuccessfully": "성공적으로 연결되었습니다", + "connectedSuccessfully": "연결 성공", "totpVerificationFailed": "TOTP 인증 실패", "warpgateVerificationFailed": "Warpgate 인증 실패", "authenticationFailed": "인증 실패", - "incorrectPassphrase": "암호가 잘못되었습니다. 다시 시도해 주세요.", + "incorrectPassphrase": "잘못된 암호입니다. 다시 시도하세요.", "verificationCodePrompt": "인증 코드:", "changePermissions": "권한 변경", "currentPermissions": "현재 권한", "owner": "소유자", "group": "그룹", "others": "기타", - "read": "읽다", - "write": "쓰다", - "execute": "실행하다", - "permissionsChangedSuccessfully": "권한이 성공적으로 변경되었습니다.", + "read": "읽기", + "write": "쓰기", + "execute": "실행", + "permissionsChangedSuccessfully": "권한이 성공적으로 변경되었습니다", "failedToChangePermissions": "권한 변경에 실패했습니다", "name": "이름", "sortByName": "이름", "sortByDate": "수정일", "sortBySize": "크기", - "ascending": "상승", - "descending": "하강", - "root": "뿌리", - "new": "새로운", + "ascending": "오름차순", + "descending": "내림차순", + "root": "루트", + "new": "새로 만들기", "sortBy": "정렬 기준", - "items": "품목", - "selected": "선택된", - "editor": "편집자", + "items": "항목", + "selected": "선택됨", + "editor": "편집기", "octal": "8진수", - "storage": "저장", + "storage": "저장소", "disk": "디스크", - "used": "사용된", - "of": "~의", - "toggleSidebar": "사이드바 토글", - "cannotLoadPdf": "PDF 파일을 불러올 수 없습니다.", - "pdfLoadError": "PDF 파일을 불러오는 중 오류가 발생했습니다.", - "loadingPdf": "PDF 파일을 불러오는 중...", - "loadingPage": "페이지를 불러오는 중..." + "used": "사용", + "of": "중", + "toggleSidebar": "사이드바 표시/숨기기", + "cannotLoadPdf": "PDF를 로드할 수 없습니다", + "pdfLoadError": "이 PDF 파일을 로드하는 중 오류가 발생했습니다.", + "loadingPdf": "PDF 로드 중...", + "loadingPage": "페이지 로드 중..." }, "transfer": { - "copyToHost": "호스트에 복사…", - "moveToHost": "호스트…로 이동", - "copyItemsToHost": "{{count}} 항목을… 호스트로 복사합니다.", - "moveItemsToHost": "{{count}} 항목을 호스트…로 이동합니다.", - "noHostsConnected": "다른 파일 관리자 호스트를 사용할 수 없습니다.", - "noHostsConnectedHint": "호스트 관리자에서 파일 관리자가 활성화된 SSH 호스트를 하나 더 추가하세요.", - "selectDestinationHost": "대상 호스트를 선택하세요", - "destinationPath": "목적지 경로", - "recentDestinations": "최근 여행지", - "collapseRecentDestinations": "최근 방문지 접기", - "expandRecentDestinations": "최근 방문지 펼치기", - "browseFolders": "대상 폴더를 찾아보세요", - "browseDestination": "경로를 찾아보거나 입력하세요.", + "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": "이체 완료", + "confirmMove": "이동", + "transferring": "전송 중…", + "compressing": "압축 중…", + "extracting": "압축 해제 중…", + "transferringItems": "{{total}}개 중 {{current}}개 항목 전송 중…", + "transferSuccess": "전송 완료", "transferError": "전송 실패", - "transferPartial": "전송이 완료되었지만 {{count}} 개의 오류가 발생했습니다.", + "transferPartial": "{{count}}개의 오류와 함께 전송 완료", "transferPartialHint": "전송할 수 없습니다: {{paths}}", - "itemsSummary": "{{count}} 항목", - "destMustBeDirectory": "대상은 여러 항목을 전송하기 위한 디렉토리여야 합니다.", - "selectThisFolder": "이 폴더를 선택하세요", - "browsePathWillBeCreated": "이 폴더는 아직 존재하지 않습니다. 전송이 시작되면 생성됩니다.", + "itemsSummary": "{{count}}개 항목", + "destMustBeDirectory": "여러 항목 전송 시 대상은 디렉터리여야 합니다.", + "selectThisFolder": "이 폴더 선택", + "browsePathWillBeCreated": "이 폴더가 아직 존재하지 않습니다. 전송 시작 시 생성됩니다.", "browsePathError": "대상 호스트에서 이 경로를 열 수 없습니다.", - "goUp": "위로 올라가세요", - "copyFolderToHost": "폴더를 호스트로 복사하세요…", + "goUp": "위로", + "copyFolderToHost": "폴더를 호스트로 복사…", "moveFolderToHost": "폴더를 호스트로 이동…", - "hostReady": "준비가 된", - "hostConnecting": "… 연결 중", + "hostReady": "준비됨", + "hostConnecting": "연결 중…", "hostDisconnected": "연결되지 않음", - "hostAuthRequired": "인증이 필요합니다. 먼저 이 호스트에서 파일 관리자를 여세요.", + "hostAuthRequired": "인증 필요 — 먼저 이 호스트에서 파일 관리자를 여십시오.", "hostConnectionFailed": "연결 실패", - "metricsTitle": "이체 시간", - "metricsPrepare": "목적지 준비: {{duration}}", - "metricsCompress": "원본에서 압축: {{duration}}", - "metricsHopSourceRead": "출처 → 서버: {{throughput}}", - "metricsHopDestSftpWrite": "서버 → 대상(SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "서버 → 대상(로컬): {{throughput}}", - "metricsTransfer": "종단 간: {{throughput}} ({{duration}})", + "metricsTitle": "전송 시간", + "metricsPrepare": "대상 준비: {{duration}}", + "metricsCompress": "소스에서 압축: {{duration}}", + "metricsHopSourceRead": "소스 → 서버: {{throughput}}", + "metricsHopDestSftpWrite": "서버 → 목적지 (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "서버 → 목적지 (로컬): {{throughput}}", + "metricsTransfer": "엔드 투 엔드: {{throughput}} ({{duration}})", "metricsExtract": "목적지에서 추출: {{duration}}", - "metricsSourceDelete": "원본에서 제거: {{duration}}", - "metricsTotal": "총계: {{duration}}", - "progressCompressing": "소스 호스트…에서 압축 중", - "progressExtracting": "대상…에서 추출 중", + "metricsSourceDelete": "소스에서 제거: {{duration}}", + "metricsTotal": "총 시간: {{duration}}", + "progressCompressing": "소스 호스트에서 압축 중…", + "progressExtracting": "목적지에서 추출 중…", "progressTransferring": "데이터 전송 중…", - "progressReconnecting": "… 다시 연결 중", - "parallelSegmentsLabel": "평행 환승 차선", - "parallelSegmentsOption": "{{count}} 차선", - "parallelSegmentsHint": "대용량 파일은 256MB 단위로 분할됩니다. 여러 레인을 사용하는 경우 (여러 전송을 동시에 시작하는 것처럼) 별도의 연결을 사용하여 전체 처리량을 높입니다.", - "progressTotalSpeed": "{{speed}} 총 ({{lanes}} 차선)", - "progressTransferringItems": "파일 전송 중 ({{current}} of {{total}})…", + "progressReconnecting": "재연결 중…", + "parallelSegmentsLabel": "병렬 전송 레인", + "parallelSegmentsOption": "{{count}}개 레인", + "parallelSegmentsHint": "대용량 파일은 256 MB 청크로 분할됩니다. 여러 레인은 별도 연결을 사용하여 (여러 전송을 시작하는 것처럼) 총 처리량을 높입니다.", + "progressTotalSpeed": "총 {{speed}} ({{lanes}}개 레인)", + "progressTransferringItems": "파일 전송 중 ({{current}}/{{total}})…", "progressBytes": "{{transferred}} / {{total}}", "progressItems": "{{current}} / {{total}} 파일", - "sourceNotDeletedPartial": "원본 파일은 보존되었습니다 (부분 전송).", + "sourceNotDeletedPartial": "소스 파일 유지 (부분 전송)", "jumpHostLimitation": "두 호스트 모두 Termix 서버에서 접근 가능해야 합니다. 호스트 간 직접 라우팅은 지원되지 않습니다.", "cancel": "취소", "methodLabel": "전송 방법", "methodAuto": "자동", - "methodTar": "타르 아카이브", + "methodTar": "Tar 아카이브", "methodItemSftp": "파일별 SFTP", - "methodAutoHint": "파일 개수, 크기 및 압축률에 따라 tar 또는 파일별 SFTP 전송 방식을 선택합니다. 단일 파일의 경우 항상 스트리밍 SFTP를 사용합니다.", - "methodTarHint": "원본에서 압축하고, 하나의 아카이브로 전송한 후, 대상에서 압축을 해제합니다. 양쪽 Unix 호스트 모두에 tar 명령어가 필요합니다.", + "methodAutoHint": "파일 수, 크기 및 압축 가능성을 기준으로 tar 또는 파일별 SFTP를 선택합니다. 단일 파일은 항상 스트리밍 SFTP를 사용합니다.", + "methodTarHint": "소스에서 압축, 하나의 아카이브 전송, 목적지에서 추출. 두 Unix 호스트에 tar가 필요합니다.", "methodItemSftpHint": "SFTP를 통해 각 파일을 개별적으로 전송합니다. Windows를 포함한 모든 호스트에서 작동합니다.", "methodPreviewLoading": "전송 방법 계산 중…", - "methodPreviewError": "전송 방법을 미리 볼 수 없습니다. 서버에서 전송을 시작하면 자동으로 방법을 선택합니다.", - "methodPreviewWillUseTar": "사용 대상: Tar 아카이브", - "methodPreviewWillUseItemSftp": "사용 방식: 파일별 SFTP", - "methodPreviewScanSummary": "{{fileCount}} 개 파일, {{totalSize}} 개 총계(소스 호스트에서 스캔됨).", - "methodItemSftpLimitation": "각 파일은 동일한 SFTP 스트림을 사용하여 단일 파일 복사 방식으로 순차적으로 처리됩니다. 모든 파일의 진행 상황이 합산되므로 파일 크기가 클수록 진행률 표시줄이 느리게 움직입니다.", + "methodPreviewError": "전송 방법을 미리 볼 수 없습니다. 시작 시 서버가 방법을 선택합니다.", + "methodPreviewWillUseTar": "사용 예정: Tar 아카이브", + "methodPreviewWillUseItemSftp": "사용 예정: 파일별 SFTP", + "methodPreviewScanSummary": "{{fileCount}}개 파일, 총 {{totalSize}} (소스 호스트에서 스캔됨).", + "methodItemSftpLimitation": "각 파일은 단일 파일 복사와 동일한 SFTP 스트림을 순차적으로 사용합니다. 진행률이 모든 파일에 걸쳐 결합되므로 대용량 파일에서는 진행 막대가 느리게 움직입니다.", "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입니다.", + "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를 설정합니다." + "auto_many_files": "자동: 다수의 파일({{fileCount}}) — tar로 파일별 오버헤드를 줄입니다.", + "auto_default": "자동: 이 세트에 대해 파일별 SFTP." }, "progressCancel": "취소", - "progressCancelling": "… 취소 중", - "progressStalled": "정체됨", + "progressCancelling": "취소 중…", + "progressStalled": "지연됨", "resumedHint": "다른 창에서 시작된 활성 전송에 다시 연결되었습니다.", - "transferCancelled": "이적 취소됨", - "transferCancelledCopyHint": "일부 파일이 대상 위치에 남아 있을 수 있습니다.", - "transferCancelledMoveHint": "대상 위치에 일부 파일이 남아 있을 수 있습니다. 원본 파일은 삭제되지 않았습니다.", - "cleanupDestFiles": "청소 목적지", - "cleanupDestFilesSuccess": "대상 위치에서 부분 파일을 제거했습니다.", - "cleanupDestFilesPartial": "일부 불완전한 파일이 삭제되지 않았습니다.", - "cleanupDestFilesNothing": "목적지에는 청소할 것이 아무것도 없습니다.", + "transferCancelled": "전송 취소됨", + "transferCancelledCopyHint": "대상에 부분 파일이 남아 있을 수 있습니다.", + "transferCancelledMoveHint": "대상에 부분 파일이 남아 있을 수 있습니다. 원본 파일은 제거되지 않았습니다.", + "cleanupDestFiles": "대상 정리", + "cleanupDestFilesSuccess": "대상에서 부분 파일을 제거했습니다.", + "cleanupDestFilesPartial": "일부 부분 파일을 제거할 수 없었습니다.", + "cleanupDestFilesNothing": "대상에 정리할 항목이 없습니다.", "cleanupDestFilesError": "정리 실패", - "retryTransfer": "다시 해 보다", + "retryTransfer": "재시도", "retryTransferError": "재시도 실패", - "transferFailedRetryHint": "대상에 일부 데이터가 남아 있습니다. 연결이 복구되면 재시도가 재개됩니다." + "transferFailedRetryHint": "대상에 부분 데이터가 유지되었습니다. 연결이 복구되면 다시 시도하면 이어서 전송됩니다." }, "tunnels": { "noSshTunnels": "SSH 터널 없음", "createFirstTunnelMessage": "아직 SSH 터널을 생성하지 않았습니다. 시작하려면 호스트 관리자에서 터널 연결을 구성하세요.", "connected": "연결됨", - "disconnected": "연결이 끊어졌습니다", + "disconnected": "연결 끊김", "connecting": "연결 중...", "error": "오류", "canceling": "취소 중...", - "connect": "연결하다", - "disconnect": "연결을 끊으세요", + "connect": "연결", + "disconnect": "연결 끊기", "cancel": "취소", "port": "포트", - "localPort": "현지 항구", + "localPort": "로컬 포트", "remotePort": "원격 포트", "currentHostPort": "현재 호스트 포트", "endpointPort": "엔드포인트 포트", "bindIp": "로컬 IP", - "endpointSshConfig": "엔드포인트 SSH 구성", + "endpointSshConfig": "엔드포인트 SSH 설정", "endpointSshHost": "엔드포인트 SSH 호스트", - "endpointSshHostPlaceholder": "구성된 호스트를 선택하세요", - "endpointSshHostRequired": "각 클라이언트 터널에 대해 엔드포인트 SSH 호스트를 선택하십시오.", - "attempt": "{{current}} {{max}} 시도", - "nextRetryIn": "다음 재시도 후 {{seconds}} 초", + "endpointSshHostPlaceholder": "설정된 호스트 선택", + "endpointSshHostRequired": "각 클라이언트 터널에 엔드포인트 SSH 호스트를 선택하세요.", + "attempt": "시도 {{current}}/{{max}}", + "nextRetryIn": "{{seconds}}초 후 재시도", "clientTunnels": "클라이언트 터널", "clientTunnel": "클라이언트 터널", "addClientTunnel": "클라이언트 터널 추가", - "noClientTunnels": "이 데스크톱에는 클라이언트 터널이 구성되어 있지 않습니다.", + "noClientTunnels": "이 데스크톱에 구성된 클라이언트 터널이 없습니다.", "tunnelName": "터널 이름", "remoteHost": "원격 호스트", - "autoStart": "자동 시동", - "clientAutoStartDesc": "데스크톱 클라이언트가 실행될 때 시작되어 연결 상태를 유지합니다.", - "clientManualStartDesc": "이 행의 시작 및 정지 버튼을 사용하십시오. Termix는 자동으로 열지 않습니다.", - "clientRemoteServerNote": "원격 포트 포워딩을 사용하려면 엔드포인트 SSH 서버에서 AllowTcpForwarding 및 GatewayPorts 설정이 필요할 수 있습니다. 이 데스크톱 연결이 끊어지면 원격 포트가 닫힙니다.", - "clientTunnelStarted": "클라이언트 터널이 시작되었습니다", - "clientTunnelStopped": "클라이언트 터널이 중지되었습니다.", - "tunnelTestSucceeded": "터널 시험 성공", + "autoStart": "자동 시작", + "clientAutoStartDesc": "이 데스크톱 클라이언트가 열리면 자동 시작되고 연결을 유지합니다.", + "clientManualStartDesc": "이 행에서 시작 및 중지를 사용하세요. Termix가 자동으로 열지 않습니다.", + "clientRemoteServerNote": "원격 포워딩은 엔드포인트 SSH 서버에서 AllowTcpForwarding 및 GatewayPorts 설정이 필요할 수 있습니다. 이 데스크톱 연결이 끊어지면 원격 포트가 닫힙니다.", + "clientTunnelStarted": "클라이언트 터널 시작됨", + "clientTunnelStopped": "클라이언트 터널 중지됨", + "tunnelTestSucceeded": "터널 테스트 성공", "tunnelTestFailed": "터널 테스트 실패", - "localSaved": "클라이언트 터널이 저장되었습니다", - "localSaveError": "로컬 클라이언트 터널을 저장하는 데 실패했습니다.", + "localSaved": "클라이언트 터널 저장됨", + "localSaveError": "로컬 클라이언트 터널 저장 실패", "invalidBindIp": "로컬 IP는 유효한 IPv4 주소여야 합니다.", - "invalidLocalTargetIp": "로컬 대상 IP 주소는 유효한 IPv4 주소여야 합니다.", + "invalidLocalTargetIp": "로컬 대상 IP는 유효한 IPv4 주소여야 합니다.", "invalidLocalPort": "로컬 포트는 1에서 65535 사이여야 합니다.", "invalidRemotePort": "원격 포트는 1에서 65535 사이여야 합니다.", "invalidLocalTargetPort": "로컬 대상 포트는 1에서 65535 사이여야 합니다.", "invalidEndpointPort": "엔드포인트 포트는 1에서 65535 사이여야 합니다.", - "duplicateAutoStartBind": "{{bind}}를 사용할 수 있는 자동 시작 클라이언트 터널은 하나뿐입니다.", - "manualControlError": "터널 상태 업데이트에 실패했습니다.", - "active": "활동적인", + "duplicateAutoStartBind": "자동 시작 클라이언트 터널 중 하나만 {{bind}}를 사용할 수 있습니다.", + "manualControlError": "터널 상태 업데이트 실패", + "active": "활성", "start": "시작", - "stop": "멈추다", - "test": "시험", - "type": "터널형", - "typeLocal": "로컬(-L)", - "typeRemote": "원격(-R)", - "typeDynamic": "동적(-D)", - "typeServerLocalDesc": "현재 호스트에서 엔드포인트까지.", - "typeServerRemoteDesc": "엔드포인트에서 현재 호스트로 돌아갑니다.", - "typeClientLocalDesc": "로컬 컴퓨터에서 엔드포인트까지.", - "typeClientRemoteDesc": "엔드포인트에서 로컬 컴퓨터로의 복귀.", - "typeClientDynamicDesc": "로컬 컴퓨터에 있는 양말.", - "typeDynamicDesc": "SOCKS5 CONNECT 트래픽을 SSH를 통해 전달합니다.", + "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": "현재 호스트 {{sourcePort}}의 SOCKS.", + "forwardDescriptionServerDynamic": "현재 호스트 {{sourcePort}}에서 SOCKS.", "forwardDescriptionClientLocal": "로컬 {{sourcePort}} → 원격 {{endpointPort}}.", "forwardDescriptionClientRemote": "원격 {{sourcePort}} → 로컬 {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS는 로컬 포트 {{sourcePort}}에 있습니다.", + "forwardDescriptionClientDynamic": "로컬 포트 {{sourcePort}}에서 SOCKS.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → 양말 (SOCKS) via {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS ({{endpoint}} 경유)", "autoNameClientLocal": "로컬 {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → 로컬 {{localPort}}", - "autoNameClientDynamic": "양말 {{localPort}} 을 통해 {{endpoint}}", - "route": "노선:", - "lastStarted": "마지막으로 시작함", - "lastTested": "최근 테스트 완료", + "autoNameClientDynamic": "SOCKS {{localPort}} ({{endpoint}} 경유)", + "route": "경로:", + "lastStarted": "마지막 시작", + "lastTested": "마지막 테스트", "lastError": "마지막 오류", "maxRetries": "최대 재시도 횟수", "maxRetriesDescription": "최대 재시도 횟수.", "retryInterval": "재시도 간격(초)", - "retryIntervalDescription": "재시도 시도 간 대기 시간.", - "local": "현지의", + "retryIntervalDescription": "재시도 사이 대기 시간.", + "local": "로컬", "remote": "원격", "destination": "목적지", - "host": "주인", - "mode": "방법", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "working": "일하고 있는..." + "host": "호스트", + "mode": "모드", + "noHostSelected": "선택된 호스트 없음", + "working": "작업 중..." }, "cardGrid": { "dragToMove": "드래그하여 이동", - "dragToResize": "드래그하여 크기를 조절하세요", + "dragToResize": "드래그하여 크기 조절", "changeWidth": "너비 변경", - "removeCard": "{{label}}를 제거하세요", - "addCard": "추가하다", - "columns": "컬럼", - "empty": "카드가 없습니다. 아래의 '추가' 기능을 사용하여 카드를 배치하세요." + "removeCard": "{{label}} 제거", + "addCard": "추가", + "columns": "열", + "empty": "카드 없음. 아래 추가를 사용하여 카드를 배치하세요." }, "hostMetrics": { "cpu": "CPU", "memory": "메모리", "disk": "디스크", - "network": "회로망", - "uptime": "가동 시간", + "network": "네트워크", + "uptime": "업타임", "processes": "프로세스", "available": "사용 가능", - "free": "무료", + "free": "여유", "connecting": "연결 중...", - "connectionFailed": "서버 연결에 실패했습니다.", - "naCpus": "해당 없음 CPU(들)", + "connectionFailed": "서버 연결 실패", + "naCpus": "N/A CPU", "cpuCores_one": "{{count}} 코어", "cpuCores_other": "{{count}} 코어", - "cpuUsage": "CPU 사용량", - "memoryUsage": "메모리 사용량", - "diskUsage": "디스크 사용량", + "cpuUsage": "CPU 사용률", + "memoryUsage": "메모리 사용률", + "diskUsage": "디스크 사용률", "temperature": "온도", - "highestTemperature": "최고 기온", - "failedToFetchHostConfig": "호스트 구성을 가져오는 데 실패했습니다.", + "highestTemperature": "최고 온도", + "failedToFetchHostConfig": "호스트 구성 가져오기 실패", "serverOffline": "서버 오프라인", - "cannotFetchMetrics": "오프라인 서버에서 메트릭을 가져올 수 없습니다.", + "cannotFetchMetrics": "오프라인 서버에서 메트릭 가져오기 불가", "totpFailed": "TOTP 인증 실패", - "noneAuthNotSupported": "Host Metrics는 '없음' 인증 유형을 지원하지 않습니다.", + "noneAuthNotSupported": "호스트 메트릭에서 'none' 인증 유형을 지원하지 않습니다.", "noHostSelected": "호스트가 선택되지 않았습니다.", - "load": "짐", + "load": "부하", "systemInfo": "시스템 정보", "hostname": "호스트 이름", "operatingSystem": "운영 체제", - "kernel": "핵심", + "kernel": "커널", "seconds": "초", "networkInterfaces": "네트워크 인터페이스", "noInterfacesFound": "네트워크 인터페이스를 찾을 수 없습니다.", "noProcessesFound": "프로세스를 찾을 수 없습니다.", - "processesTotal": "총", - "processesRunning": "달리기", + "processesTotal": "전체", + "processesRunning": "실행 중", "loginStats": "SSH 로그인 통계", "noRecentLoginData": "최근 로그인 데이터가 없습니다.", "executingQuickAction": "{{name}} 실행 중...", - "quickActionSuccess": "{{name}} 성공적으로 완료되었습니다", + "quickActionSuccess": "{{name}}이(가) 성공적으로 완료되었습니다.", "quickActionFailed": "{{name}} 실패", "quickActionError": "{{name}} 실행에 실패했습니다.", "ports": { - "title": "수신 포트", - "protocol": "규약", + "title": "수신 대기 포트", + "protocol": "프로토콜", "port": "포트", "address": "주소", "process": "프로세스", - "search": "검색 포트...", - "allProtocols": "모두", - "noData": "수신 포트 데이터 없음" + "search": "포트 검색...", + "allProtocols": "전체", + "noData": "수신 대기 포트 데이터가 없습니다." }, "firewall": { "title": "방화벽", - "inactive": "비활성화됨", + "inactive": "비활성", "policy": "정책", "rules": "규칙", - "noRules": "이 체인에는 규칙이 없습니다.", - "noData": "방화벽 데이터가 없습니다.", - "action": "행동", - "protocol": "프로토", + "noRules": "이 체인에 규칙이 없습니다.", + "noData": "방화벽 데이터를 사용할 수 없습니다.", + "action": "동작", + "protocol": "프로토콜", "port": "포트", - "source": "원천", - "anywhere": "어딘가에", - "chains": "쇠사슬" + "source": "소스", + "anywhere": "모든 곳", + "chains": "체인" }, - "loadAvg": "평균 부하", - "swap": "교환", - "architecture": "건축학", - "refresh": "새로 고치다", - "retry": "다시 해 보다", - "customize": "레이아웃 사용자 지정", - "reset": "다시 놓기", - "tabLive": "살다", - "editModeInstructions": "카드를 드래그하여 순서를 바꾸고, 아래쪽 가장자리를 드래그하여 크기를 조절하고, 너비 버튼을 사용하여 카드의 너비를 변경할 수 있습니다. 아래에서 카드를 추가하거나 삭제할 수 있습니다.", + "loadAvg": "로드 평균", + "swap": "스왑", + "architecture": "아키텍처", + "refresh": "새로 고침", + "retry": "재시도", + "customize": "레이아웃 사용자 정의", + "reset": "초기화", + "tabLive": "실시간", + "editModeInstructions": "카드를 드래그하여 재배열하고, 아래쪽 가장자리를 드래그하여 크기를 조정하며, 너비 버튼을 사용해 카드의 가로 폭을 변경하세요. 아래에서 카드를 추가하거나 제거할 수 있습니다.", "managers": { "services": "서비스", - "processInspector": "공정 검사관", + "processInspector": "프로세스 검사기", "logViewer": "로그 뷰어", "cron": "크론 작업", "packages": "패키지", "ssl": "SSL 인증서", "firewall": "방화벽", "users": "사용자 및 권한", - "healthCheck": "건강 검진", + "healthCheck": "상태 검사", "diskBreakdown": "디스크 분석", "systemdTimers": "타이머", - "topMemory": "메모리 기준 최고", + "topMemory": "메모리 사용량 상위", "noData": "데이터 없음", - "sudoHint": "관리자 권한으로 작업을 수행하려면 호스트 편집기에서 해당 호스트에 sudo 암호를 설정하십시오.", + "sudoHint": "호스트 편집기에서 이 호스트의 sudo 암호를 설정하여 권한이 필요한 작업을 활성화하세요.", "filter": "필터...", "start": "시작", - "stop": "멈추다", + "stop": "중지", "restart": "재시작", "actionDone": "{{name}} 업데이트됨", - "actionFailed": "작업이 실패했습니다", - "signalSent": "PID {{pid}}로 신호가 전송되었습니다.", - "killHint": "클릭: 종료(SIGTERM). 마우스 오른쪽 클릭: 강제 종료(SIGKILL).", - "working": "일하고 있는...", + "actionFailed": "작업 실패", + "signalSent": "PID {{pid}}에 시그널 전송됨", + "killHint": "클릭: 종료 (SIGTERM). 마우스 오른쪽 클릭: 강제 종료 (SIGKILL).", + "working": "작업 중...", "update": "업데이트", "upgradeAll": "모두 업그레이드", - "allUpToDate": "모든 것이 최신 상태입니다.", - "save": "구하다", + "allUpToDate": "모든 항목이 최신 상태입니다", + "save": "저장", "command": "명령", "enabled": "활성화됨", - "cronSaved": "Crontab이 업데이트되었습니다.", - "clients": "고객들", - "dryRun": "시험 운전", - "renew": "고쳐 쓰다", - "noAcmeClient": "이 호스트에서 ACME 클라이언트(certbot 또는 acme.sh)를 찾을 수 없습니다.", - "addInputRule": "입력 규칙을 추가합니다", - "firewallWarning": "변경 사항은 저장되기 전까지는 런타임에만 적용됩니다. 계정이 잠기지 않도록 주의하세요.", - "ruleApplied": "규칙 적용", - "invalidPort": "유효한 포트(1-65535)를 입력하십시오.", + "cronSaved": "Crontab이 업데이트되었습니다", + "clients": "클라이언트", + "dryRun": "테스트 실행", + "renew": "갱신", + "noAcmeClient": "이 호스트에 ACME 클라이언트(certbot 또는 acme.sh)가 없습니다.", + "addInputRule": "INPUT 규칙 추가", + "firewallWarning": "변경 사항은 저장하기 전까지 런타임에만 적용됩니다. 접근이 차단되지 않도록 주의하세요.", + "ruleApplied": "규칙이 적용되었습니다", + "invalidPort": "유효한 포트(1-65535)를 입력하세요", "newUsername": "새 사용자 이름", - "addUser": "추가하다", + "addUser": "추가", "deleteUser": "사용자 삭제", "noHealthChecks": "아직 상태 점검이 구성되지 않았습니다.", - "follow": "따르다", + "follow": "따라가기", "noLogData": "로그 출력이 없습니다.", - "tree": "나무", + "tree": "트리", "enableDisable": "부팅 시 활성화/비활성화", - "grantSudo": "그랜트 수두", - "revokeSudo": "sudo 권한 취소", - "sslIssueCert": "증명서 발급", + "grantSudo": "sudo 권한 부여", + "revokeSudo": "sudo 권한 회수", + "sslIssueCert": "인증서 발급", "sslExpired": "만료됨", - "sslInDays": "{{days}}d에서", - "sslNeedDomain": "도메인을 하나 이상 입력하세요", - "sslIssued": "증명서 발급됨", + "sslInDays": "{{days}}일 후", + "sslNeedDomain": "최소 하나의 도메인을 입력하세요", + "sslIssued": "인증서가 발급되었습니다", "sslDomainsPlaceholder": "example.com, www.example.com", - "sslHttpStandalone": "HTTP(독립형)", - "sslHttpWebroot": "HTTP(웹루트)", + "sslHttpStandalone": "HTTP (독립형)", + "sslHttpWebroot": "HTTP (웹루트)", "sslDns": "DNS", - "sslDnsProvider": "DNS 제공업체(예: 클라우드플레어)", - "sslIssueHint": "DNS 공급자 자격 증명은 호스트에 이미 구성되어 있어야 합니다.", - "sslRevoke": "자격증 취소", - "sslRevoked": "자격증이 취소되었습니다", - "sslRevokeConfirm": "\"{{name}}\" 인증서를 취소하고 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "healthRun": "달리다", + "sslDnsProvider": "DNS 공급자 (예: cloudflare)", + "sslIssueHint": "DNS 공급자 자격 증명이 호스트에 이미 구성되어 있어야 합니다.", + "sslRevoke": "인증서 폐기", + "sslRevoked": "인증서가 폐기되었습니다", + "sslRevokeConfirm": "인증서 \"{{name}}\"을(를) 폐기하고 제거하시겠습니까? 이 작업은 취소할 수 없습니다.", + "healthRun": "실행", "healthName": "이름", - "healthTarget": "호스트/주소", - "healthAddCheck": "체크 추가", - "healthSaved": "건강 검진 저장됨", - "healthMissingFields": "각 검사에는 이름과 대상이 필요합니다.", + "healthTarget": "호스트 / 주소", + "healthAddCheck": "검사 추가", + "healthSaved": "상태 검사가 저장되었습니다", + "healthMissingFields": "각 검사에는 이름과 대상이 필요합니다", "logFile": "파일", - "logUnit": "단위", - "logCustomPath": "/var/log 아래의 사용자 지정 경로(선택 사항)", - "logGrep": "필터 라인...", - "firewallPersist": "규칙을 유지하세요", - "firewallPersisted": "방화벽 규칙이 유지되었습니다", - "wireguard": "와이어가드", - "tailscale": "꼬리비늘", - "wgNotInstalled": "이 호스트에는 WireGuard가 설치되어 있지 않습니다.", - "wgNoInterfaces": "WireGuard 인터페이스가 구성되지 않았습니다.", - "wgInterfaceUp": "불러오다", - "wgInterfaceDown": "내려놓다", - "wgBringingUp": "{{name}} 를 불러오는 중...", - "wgBringingDown": "{{name}} 를 끌어내리는 중...", - "wgInterfaceUpDone": "{{name}} 가 올라갔습니다", - "wgInterfaceDownDone": "{{name}} 가 다운되었습니다", + "logUnit": "유닛", + "logCustomPath": "/var/log 아래의 사용자 지정 경로 (선택 사항)", + "logGrep": "줄 필터...", + "firewallPersist": "규칙 영구 저장", + "firewallPersisted": "방화벽 규칙이 영구 저장되었습니다", + "wireguard": "WireGuard", + "tailscale": "Tailscale", + "wgNotInstalled": "이 호스트에 WireGuard가 설치되어 있지 않습니다", + "wgNoInterfaces": "구성된 WireGuard 인터페이스가 없습니다", + "wgInterfaceUp": "올리기", + "wgInterfaceDown": "내리기", + "wgBringingUp": "{{name}} 올리는 중...", + "wgBringingDown": "{{name}} 내리는 중...", + "wgInterfaceUpDone": "{{name}}이(가) 올라왔습니다", + "wgInterfaceDownDone": "{{name}}이(가) 내려갔습니다", "wgListenPort": "포트", "wgPublicKey": "공개 키", "wgEndpoint": "엔드포인트", - "wgAllowedIPs": "허용된 IP 주소", - "wgLastHandshake": "마지막 악수", - "wgHandshakeNever": "절대", - "wgTransfer": "옮기다", - "tsNotInstalled": "이 호스트에는 Tailscale이 설치되어 있지 않습니다.", - "tsRunning": "달리기", - "tsStopped": "멈췄습니다", - "tsEnable": "연결하다", - "tsDisable": "연결을 끊으세요", + "wgAllowedIPs": "허용된 IP", + "wgLastHandshake": "마지막 핸드셰이크", + "wgHandshakeNever": "없음", + "wgTransfer": "전송", + "tsNotInstalled": "이 호스트에 Tailscale이 설치되어 있지 않습니다", + "tsRunning": "실행 중", + "tsStopped": "중지됨", + "tsEnable": "연결", + "tsDisable": "연결 해제", "tsEnabling": "Tailscale에 연결 중...", - "tsDisabling": "Tailscale 연결을 해제하는 중...", - "tsEnabled": "테일스케일 연결됨", - "tsDisabled": "테일스케일 연결이 끊어졌습니다", - "tsIPs": "테일스케일 IP", - "tsPeers": "동료", + "tsDisabling": "Tailscale 연결 해제 중...", + "tsEnabled": "Tailscale 연결됨", + "tsDisabled": "Tailscale 연결 해제됨", + "tsIPs": "Tailscale IP", + "tsPeers": "피어", "tsOnline": "온라인", "tsOffline": "오프라인", - "tsExitNode": "종료 노드", - "tsExitNodeActive": "종료 노드 활성", - "tsHostname": "호스트 이름" + "tsExitNode": "출구 노드", + "tsExitNodeActive": "출구 노드 활성", + "tsHostname": "호스트명" } }, "auth": { "tagline": "자체 호스팅 SSH 및 원격 데스크톱 관리", "loginTitle": "다시 오신 것을 환영합니다", - "registerTitle": "계정 생성", + "registerTitle": "계정 만들기", "forgotPassword": "비밀번호를 잊으셨나요?", - "rememberMe": "30일 동안 기기 정보를 기억해 두세요 (TOTP 포함).", + "rememberMe": "30일 동안 이 기기 기억하기 (TOTP 포함)", "noAccount": "계정이 없으신가요?", "hasAccount": "이미 계정이 있으신가요?", "twoFactorAuth": "2단계 인증", - "enterCode": "인증 코드를 입력하세요", - "backupCode": "또는 백업 코드를 사용하세요.", - "verifyCode": "확인 코드", + "enterCode": "인증 코드 입력", + "backupCode": "또는 백업 코드 사용", + "verifyCode": "코드 확인", "redirectingToApp": "앱으로 리디렉션 중...", - "sshAuthenticationRequired": "SSH 인증이 필요합니다", - "sshNoKeyboardInteractive": "키보드 기반 인증을 사용할 수 없습니다.", + "sshAuthenticationRequired": "SSH 인증 필요", + "sshNoKeyboardInteractive": "키보드 대화식 인증 사용 불가", "sshAuthenticationFailed": "인증 실패", "sshAuthenticationTimeout": "인증 시간 초과", - "sshNoKeyboardInteractiveDescription": "서버는 키보드 입력 방식의 인증을 지원하지 않습니다. 비밀번호 또는 SSH 키를 입력해 주세요.", - "sshAuthFailedDescription": "입력하신 정보가 올바르지 않습니다. 유효한 정보를 입력하여 다시 시도해 주세요.", + "sshNoKeyboardInteractiveDescription": "서버가 키보드 대화식 인증을 지원하지 않습니다. 비밀번호 또는 SSH 키를 제공해 주세요.", + "sshAuthFailedDescription": "제공된 자격 증명이 올바르지 않습니다. 올바른 자격 증명으로 다시 시도해 주세요.", "sshTimeoutDescription": "인증 시도 시간이 초과되었습니다. 다시 시도해 주세요.", - "sshProvideCredentialsDescription": "이 서버에 연결하려면 SSH 자격 증명을 입력하세요.", - "sshPasswordDescription": "이 SSH 연결에 사용할 비밀번호를 입력하십시오.", - "sshKeyPasswordDescription": "SSH 키가 암호화된 경우 여기에 암호를 입력하십시오.", - "passphraseRequired": "암호문이 필요합니다", - "passphraseRequiredDescription": "SSH 키가 암호화되어 있습니다. 키를 해제하려면 암호를 입력하십시오.", - "back": "뒤쪽에", + "sshProvideCredentialsDescription": "이 서버에 연결하려면 SSH 자격 증명을 제공해 주세요.", + "sshPasswordDescription": "SSH 연결의 비밀번호를 입력하세요.", + "sshKeyPasswordDescription": "SSH 키가 암호화된 경우, 여기에 암호를 입력하세요.", + "passphraseRequired": "암호 필요", + "passphraseRequiredDescription": "SSH 키가 암호화되었습니다. 잠금 해제를 위해 암호를 입력하세요.", + "back": "뒤로", "firstUser": "첫 번째 사용자", - "firstUserMessage": "당신은 첫 번째 사용자이며 관리자 권한을 부여받았습니다. 사이드바의 사용자 드롭다운 메뉴에서 관리자 설정을 확인할 수 있습니다. 오류라고 생각되면 Docker 로그를 확인하거나 GitHub에 이슈를 등록해 주세요.", + "firstUserMessage": "첫 번째 사용자이며 관리자로 설정됩니다. 사이드바 사용자 드롭다운에서 관리자 설정을 볼 수 있습니다. 실수라고 생각되면 Docker 로그를 확인하거나 GitHub 이슈를 생성하세요.", "external": "외부", - "loginWithExternal": "외부 제공업체로 로그인", - "loginWithExternalDesc": "구성된 외부 ID 공급자를 사용하여 로그인하세요.", - "externalNotSupportedInElectron": "Electron 앱에서는 아직 외부 인증이 지원되지 않습니다. OIDC 로그인을 위해서는 웹 버전을 이용해 주세요.", - "loginWithProvider": "{{name}}로 로그인하세요", - "orContinueWith": "또는 계속 진행하세요", + "loginWithExternal": "외부 제공자로 로그인", + "loginWithExternalDesc": "구성된 외부 ID 제공자를 사용하여 로그인", + "externalNotSupportedInElectron": "Electron 앱에서는 아직 외부 인증이 지원되지 않습니다. OIDC 로그인은 웹 버전을 이용하세요.", + "loginWithProvider": "{{name}}으로 로그인", + "orContinueWith": "또는 다음으로 계속하기", "ldapUsername": "LDAP 사용자 이름", "ldapPassword": "LDAP 비밀번호", "ldapSignIn": "로그인", "ldapLoginFailed": "LDAP 로그인 실패", "resetPasswordButton": "비밀번호 재설정", "sendResetCode": "재설정 코드 전송", - "resetCodeDesc": "사용자 이름을 입력하여 비밀번호 재설정 코드를 받으세요. 코드는 도커 컨테이너 로그에 기록됩니다.", + "resetCodeDesc": "비밀번호 재설정 코드를 받으려면 사용자 이름을 입력하세요. 코드는 Docker 컨테이너 로그에 기록됩니다.", "resetCode": "재설정 코드", - "verifyCodeButton": "확인 코드", - "enterResetCode": "Docker 컨테이너 로그에서 사용자에 대한 6자리 코드를 입력하십시오.", + "verifyCodeButton": "코드 확인", + "enterResetCode": "사용자의 Docker 컨테이너 로그에서 6자리 코드를 입력하세요:", "newPassword": "새 비밀번호", "confirmNewPassword": "비밀번호 확인", - "enterNewPassword": "사용자 계정의 새 비밀번호를 입력하세요:", - "signUp": "회원가입", + "enterNewPassword": "사용자의 새 비밀번호를 입력하세요:", + "signUp": "가입하기", "desktopApp": "데스크톱 앱", - "loggingInToDesktopApp": "데스크톱 앱에 로그인하기", - "loadingServer": "서버를 로딩 중...", - "dataLossWarning": "이러한 방식으로 비밀번호를 재설정하면 저장된 모든 SSH 호스트, 자격 증명 및 기타 암호화된 데이터가 삭제됩니다. 이 작업은 되돌릴 수 없습니다. 비밀번호를 잊어버렸고 로그인되어 있지 않은 경우에만 이 방법을 사용하십시오.", + "loggingInToDesktopApp": "데스크톱 앱에 로그인 중", + "loadingServer": "서버 로딩 중...", + "dataLossWarning": "이 방식으로 비밀번호를 재설정하면 저장된 모든 SSH 호스트, 자격 증명 및 기타 암호화된 데이터가 삭제됩니다. 이 작업은 되돌릴 수 없습니다. 비밀번호를 잊었고 로그인하지 않은 경우에만 사용하세요.", "authenticationDisabled": "인증 비활성화됨", - "authenticationDisabledDesc": "현재 모든 인증 방식이 비활성화되어 있습니다. 관리자에게 문의하십시오.", - "passwordLoginDisabledDesc": "비밀번호 로그인이 비활성화되었습니다. 패스키 또는 외부 인증 공급자를 사용하십시오.", - "signInWithPasskey": "패스키로 로그인하세요", - "passkeyLoginFailed": "암호키 로그인 실패", - "attemptsRemaining": "남은 시도 횟수 {{count}} 회", - "confirmResetDataWipe": "이 계정은 암호화 업그레이드 이후 로그인한 적이 없으므로 이전 비밀번호 없이는 저장된 데이터를 복구할 수 없습니다. 비밀번호를 재설정하면 호스트, 자격 증명 및 스니펫이 영구적으로 삭제됩니다. 계속하시겠습니까?" + "authenticationDisabledDesc": "현재 모든 인증 방법이 비활성화되어 있습니다. 관리자에게 문의하세요.", + "passwordLoginDisabledDesc": "비밀번호 로그인이 비활성화되었습니다. Passkey 또는 외부 인증 제공자를 사용하세요.", + "signInWithPasskey": "Passkey로 로그인", + "passkeyLoginFailed": "패스키 로그인 실패", + "attemptsRemaining": "{{count}}회 남음", + "confirmResetDataWipe": "이 계정은 암호화 업그레이드 이후 로그인하지 않아 이전 비밀번호 없이는 저장된 데이터를 복구할 수 없습니다. 초기화하면 호스트, 자격 증명 및 스니펫이 영구적으로 삭제됩니다. 계속하시겠습니까?" }, "hostKey": { "verifyNewHost": "SSH 호스트 키 확인", - "keyChangedWarning": "SSH 호스트 키가 변경되었습니다.", - "firstConnectionTitle": "이 호스트에 처음 연결합니다.", - "firstConnectionDescription": "이 호스트의 진위 여부를 확인할 수 없습니다. 지문이 예상과 일치하는지 확인하십시오.", - "keyChangedDescription": "이 서버의 호스트 키가 마지막 접속 이후 변경되었습니다. 이는 보안 문제를 나타낼 수 있습니다.", + "keyChangedWarning": "SSH 호스트 키 변경됨", + "firstConnectionTitle": "이 호스트에 처음 연결", + "firstConnectionDescription": "이 호스트의 신뢰성을 확인할 수 없습니다. 예상하는 지문과 일치하는지 확인하세요.", + "keyChangedDescription": "이 서버의 호스트 키가 마지막 연결 이후 변경되었습니다. 보안 문제일 수 있습니다.", "previousKey": "이전 키", - "newFingerprint": "새로운 지문", + "newFingerprint": "새 지문", "fingerprint": "지문", - "verifyInstructions": "이 호스트를 신뢰한다면 '수락'을 클릭하여 계속 진행하고 향후 연결을 위해 이 지문을 저장하세요.", + "verifyInstructions": "이 호스트를 신뢰한다면 수락을 클릭하여 계속하고 나중에 연결할 때 이 지문을 저장하세요.", "securityWarning": "보안 경고", "acceptAndContinue": "수락 및 계속", - "acceptNewKey": "새 키를 수락하고 계속 진행하세요" + "acceptNewKey": "새 키 수락 및 계속" }, "errors": { - "databaseConnection": "데이터베이스에 연결할 수 없습니다.", + "databaseConnection": "데이터베이스에 연결할 수 없습니다", "unknownError": "알 수 없는 오류", "loginFailed": "로그인 실패", - "failedPasswordReset": "비밀번호 재설정을 시작하는 데 실패했습니다.", - "failedVerifyCode": "재설정 코드 확인에 실패했습니다.", - "failedCompleteReset": "비밀번호 재설정을 완료하지 못했습니다.", + "failedPasswordReset": "비밀번호 재설정 시작 실패", + "failedVerifyCode": "재설정 코드 인증 실패", + "failedCompleteReset": "비밀번호 재설정 완료 실패", "invalidTotpCode": "잘못된 TOTP 코드", - "failedOidcLogin": "OIDC 로그인 시작에 실패했습니다.", - "silentSigninOidcUnavailable": "자동 로그인 요청이 있었지만 OIDC 로그인이 불가능합니다.", - "failedUserInfo": "로그인 후 사용자 정보를 가져오는 데 실패했습니다.", - "oidcAuthFailed": "OIDC 인증에 실패했습니다.", - "invalidAuthUrl": "백엔드에서 잘못된 인증 URL을 수신했습니다.", - "requiredField": "이 항목은 필수 입력 사항입니다.", - "minLength": "최소 길이는 {{min}}입니다.", + "failedOidcLogin": "OIDC 로그인 시작 실패", + "silentSigninOidcUnavailable": "자동 로그인이 요청되었지만 OIDC 로그인을 사용할 수 없습니다.", + "failedUserInfo": "로그인 후 사용자 정보를 가져오지 못했습니다", + "oidcAuthFailed": "OIDC 인증 실패", + "invalidAuthUrl": "백엔드에서 받은 잘못된 인증 URL", + "requiredField": "필수 입력란입니다", + "minLength": "최소 {{min}}자", "passwordMismatch": "비밀번호가 일치하지 않습니다", - "passwordLoginDisabled": "현재 사용자 이름/비밀번호 로그인 기능이 비활성화되어 있습니다.", - "sessionExpired": "세션이 만료되었습니다. 다시 로그인해 주세요.", - "totpRateLimited": "시도 횟수 제한: TOTP 인증 시도 횟수가 너무 많습니다. 나중에 다시 시도해 주세요.", - "totpRateLimitedWithTime": "시도 횟수 제한: TOTP 인증 시도 횟수가 너무 많습니다. 다시 시도하기 전에 {{time}} 초 동안 기다려 주세요.", - "resetCodeRateLimited": "시도 횟수 제한: 인증 시도 횟수가 너무 많습니다. 나중에 다시 시도해 주세요.", - "resetCodeRateLimitedWithTime": "시도 횟수 제한: 인증 시도 횟수가 너무 많습니다. 다시 시도하기 전에 {{time}} 초 동안 기다려 주세요.", + "passwordLoginDisabled": "현재 사용자 이름/비밀번호 로그인이 비활성화되어 있습니다", + "sessionExpired": "세션이 만료되었습니다. 다시 로그인해 주세요", + "totpRateLimited": "속도 제한: TOTP 인증 시도가 너무 많습니다. 나중에 다시 시도하세요.", + "totpRateLimitedWithTime": "속도 제한: TOTP 인증 시도가 너무 많습니다. {{time}}초 후에 다시 시도하세요.", + "resetCodeRateLimited": "속도 제한: 인증 시도가 너무 많습니다. 나중에 다시 시도하세요.", + "resetCodeRateLimitedWithTime": "속도 제한: 너무 많은 인증 시도가 있었습니다. 다시 시도하기 전에 {{time}}초 동안 기다려 주세요.", "authTokenSaveFailed": "인증 토큰 저장에 실패했습니다.", - "failedToLoadServer": "서버 로드 실패" + "failedToLoadServer": "서버를 불러오지 못했습니다.", + "remoteServerRequired": "원격 서버가 필요합니다. 이 연결 유형을 사용하려면 설정에서 원격 서버를 연결하세요." }, "messages": { - "registrationDisabled": "관리자에 의해 신규 계정 등록이 현재 비활성화되었습니다. 로그인하시거나 관리자에게 문의해 주세요.", - "userNotAllowed": "귀하의 계정으로는 등록이 불가능합니다. 관리자에게 문의하십시오.", - "databaseConnectionFailed": "데이터베이스 서버에 연결하지 못했습니다.", - "resetCodeSent": "Docker 로그에 전송된 재설정 코드", - "codeVerified": "코드 검증이 완료되었습니다.", - "passwordResetSuccess": "비밀번호 재설정 성공", + "registrationDisabled": "현재 관리자가 신규 계정 등록을 비활성화했습니다. 로그인하거나 관리자에게 문의하세요.", + "userNotAllowed": "계정에 등록 권한이 없습니다. 관리자에게 문의하세요.", + "databaseConnectionFailed": "데이터베이스 서버 연결에 실패했습니다.", + "resetCodeSent": "재설정 코드가 Docker 로그로 전송되었습니다.", + "codeVerified": "코드가 성공적으로 확인되었습니다.", + "passwordResetSuccess": "비밀번호가 성공적으로 재설정되었습니다.", "loginSuccess": "로그인 성공", - "registrationSuccess": "등록 완료" + "registrationSuccess": "등록 성공" }, "profile": { - "c2sTunnelConfigDesc": "구성된 SSH 호스트를 대상으로 하는 로컬 데스크톱 터널.", - "c2sTunnelPresets": "클라이언트 터널 사전 설정", - "c2sTunnelPresetsDesc": "이 데스크톱 클라이언트의 로컬 터널 목록을 명명된 서버 사전 설정으로 저장하거나, 사전 설정을 이 클라이언트로 다시 불러옵니다.", - "c2sTunnelPresetsUnavailable": "클라이언트 터널 사전 설정은 데스크톱 클라이언트에서만 사용할 수 있습니다.", - "c2sPresetName": "사전 설정 이름", - "c2sPresetNamePlaceholder": "클라이언트 사전 설정 이름", - "c2sPresetToLoad": "미리 설정하여 로드", - "c2sNoPresetSelected": "선택된 사전 설정이 없습니다.", - "c2sNoPresets": "저장된 프리셋이 없습니다.", - "c2sLoadPreset": "짐", - "c2sCurrentLocalConfig": "이 데스크톱에 {{count}} 로컬 클라이언트 터널이 구성되었습니다.", - "c2sPresetSyncNote": "사전 설정은 명시적인 스냅샷입니다. 사전 설정을 로드하면 이 데스크톱 클라이언트의 로컬 클라이언트 터널 목록이 대체됩니다.", - "c2sPresetSaved": "클라이언트 터널 사전 설정이 저장되었습니다.", - "c2sPresetLoaded": "클라이언트 터널 사전 설정이 로컬에 로드됨", - "c2sPresetRenamed": "클라이언트 터널 사전 설정 이름 변경됨", - "c2sPresetDeleted": "클라이언트 터널 사전 설정이 삭제되었습니다.", - "c2sPresetLoadError": "클라이언트 터널 사전 설정 불러오기에 실패했습니다." + "c2sTunnelConfigDesc": "구성된 SSH 호스트를 대상으로 하는 로컬 데스크톱 터널입니다.", + "c2sTunnelPresets": "클라이언트 터널 프리셋", + "c2sTunnelPresetsDesc": "이 데스크톱 클라이언트의 로컬 터널 목록을 명명된 서버 프리셋으로 저장하거나, 프리셋을 이 클라이언트에 다시 불러옵니다.", + "c2sTunnelPresetsUnavailable": "클라이언트 터널 프리셋은 데스크톱 클라이언트에서만 사용할 수 있습니다.", + "c2sPresetName": "프리셋 이름", + "c2sPresetNamePlaceholder": "클라이언트 프리셋 이름", + "c2sPresetToLoad": "불러올 프리셋", + "c2sNoPresetSelected": "프리셋이 선택되지 않음", + "c2sNoPresets": "저장된 프리셋이 없음", + "c2sLoadPreset": "불러오기", + "c2sCurrentLocalConfig": "이 데스크톱에 구성된 로컬 클라이언트 터널 {{count}}개.", + "c2sPresetSyncNote": "프리셋은 명시적 스냅샷입니다. 프리셋을 불러오면 이 데스크톱 클라이언트의 로컬 클라이언트 터널 목록이 대체됩니다.", + "c2sPresetSaved": "클라이언트 터널 프리셋이 저장되었습니다.", + "c2sPresetLoaded": "클라이언트 터널 프리셋이 로컬에 불러와졌습니다.", + "c2sPresetRenamed": "클라이언트 터널 프리셋의 이름이 변경되었습니다.", + "c2sPresetDeleted": "클라이언트 터널 프리셋이 삭제되었습니다.", + "c2sPresetLoadError": "클라이언트 터널 프리셋을 불러오지 못했습니다." }, "placeholders": { "maxRetries": "3", @@ -2314,474 +2448,491 @@ "language": "언어", "keyPassword": "키 비밀번호", "pastePrivateKey": "여기에 개인 키를 붙여넣으세요...", - "localListenerHost": "127.0.0.1 (현지에서 청취)", - "localTargetHost": "127.0.0.1 (이 컴퓨터의 대상 주소)", + "localListenerHost": "127.0.0.1 (로컬에서 수신 대기)", + "localTargetHost": "127.0.0.1 (이 컴퓨터의 대상)", "socksListenerHost": "127.0.0.1 (SOCKS 리스너)", "enterPassword": "비밀번호를 입력하세요", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "계기반", - "loading": "대시보드를 불러오는 중...", - "github": "깃허브", - "support": "지원하다", - "discord": "불화", + "title": "대시보드", + "loading": "대시보드 로딩 중...", + "github": "GitHub", + "support": "지원", + "discord": "Discord", "docs": "문서", - "donate": "기부하세요", + "donate": "기부", "serverOverview": "서버 개요", "version": "버전", - "upToDate": "최신 정보", - "updateAvailable": "업데이트 가능", + "upToDate": "최신 상태", + "updateAvailable": "업데이트 있음", "beta": "베타", "uptime": "가동 시간", - "database": "데이터 베이스", - "healthy": "건강한", + "database": "데이터베이스", + "healthy": "정상", "error": "오류", - "totalHosts": "총 호스트 수", - "totalTunnels": "토탈 터널", - "totalCredentials": "총 자격 증명", + "totalHosts": "전체 호스트", + "totalTunnels": "전체 터널", + "totalCredentials": "전체 자격 증명", "recentActivity": "최근 활동", - "reset": "다시 놓기", - "loadingRecentActivity": "최근 활동 불러오는 중...", + "reset": "초기화", + "loadingRecentActivity": "최근 활동 로딩 중...", "noRecentActivity": "최근 활동 없음", - "quickActions": "빠른 조치", + "quickActions": "빠른 작업", "addHost": "호스트 추가", "addCredential": "자격 증명 추가", "adminSettings": "관리자 설정", "userProfile": "사용자 프로필", "serverStats": "서버 통계", - "loadingServerStats": "서버 통계 불러오는 중...", - "noServerData": "서버 데이터가 없습니다.", + "loadingServerStats": "서버 통계 로딩 중...", + "noServerData": "서버 데이터 없음", "cpu": "CPU", - "ram": "숫양", - "customizeLayout": "대시보드 맞춤 설정", + "ram": "RAM", + "customizeLayout": "대시보드 사용자 지정", "dashboardSettings": "대시보드 설정", "enableDisableCards": "카드 활성화/비활성화", - "resetLayout": "기본값으로 재설정", + "resetLayout": "기본값으로 초기화", "serverOverviewCard": "서버 개요", "recentActivityCard": "최근 활동", "networkGraphCard": "네트워크 그래프", "networkGraph": "네트워크 그래프", - "quickActionsCard": "빠른 조치", + "quickActionsCard": "빠른 작업", "serverStatsCard": "서버 통계", - "panelMain": "기본", - "panelSide": "옆", - "justNow": "방금", + "panelMain": "메인", + "panelSide": "사이드", + "justNow": "방금 전", "serviceLinks": "서비스 링크", "homepagePreview": "홈페이지 미리보기" }, "donation": { - "title": "Termix를 즐겁게 사용하고 계신가요?", - "body": "Termix는 무료 오픈 소스 소프트웨어로, 두 명의 팀원이 여가 시간을 활용하여 개발 및 유지 관리하고 있습니다. 만약 Termix를 통해 기존에 사용하던 유료 도구를 대체하게 되었다면, 기부를 통해 호스팅 비용을 충당하고 지속적인 개발을 지원할 수 있습니다. 현재는 암호화폐로만 기부가 가능합니다.", - "milestones": "기부금은 SAML 및 Kubernetes 지원과 같은 기능을 구축하는 데 필요한 연구 및 학습 시간을 지원하는 데에도 사용됩니다. 기부 페이지에서 진행 상황을 확인하세요.", - "cta": "기부하세요", - "dismiss": "나중에요" + "title": "Termix가 마음에 드시나요?", + "body": "Termix는 자유 오픈 소스 소프트웨어로, 두 명의 팀이 여가 시간에 개발하고 유지 관리하고 있습니다. 기존에 유료로 사용하던 상용 도구를 대체했다면, 기부를 통해 호스팅 비용을 충당하고 개발을 지속할 수 있습니다. 현재 기부는 암호화폐로만 가능합니다.", + "milestones": "기부는 SAML 및 Kubernetes 지원과 같은 기능을 구축하는 데 필요한 연구와 학습 시간을 마련하는 데도 도움이 됩니다. 기부 페이지에서 진행 상황을 확인하세요.", + "cta": "기부하기", + "dismiss": "나중에 할게요" }, "dashboardTab": { - "stable": "안정적인", - "hostsOnline": "호스트 온라인", + "stable": "안정", + "hostsOnline": "온라인 호스트", "activeTunnels": "활성 터널", - "registerNewServer": "새 서버를 등록하세요", - "storeSshKeysOrPasswords": "SSH 키 또는 암호를 저장하세요", + "registerNewServer": "새 서버 등록", + "storeSshKeysOrPasswords": "SSH 키 또는 비밀번호 저장", "manageUsersAndRoles": "사용자 및 역할 관리", - "manageYourAccount": "계정 관리하기", + "manageYourAccount": "계정 관리", "hostStatus": "호스트 상태", - "noHostsConfigured": "구성된 호스트가 없습니다.", + "noHostsConfigured": "구성된 호스트 없음", "online": "온라인", "offline": "오프라인", "checking": "확인 중", "onlineLower": "온라인", - "nodes": "{{count}} 노드", - "add": "추가하다:", - "commandPalette": "커맨드 팔레트", + "nodes": "{{count}}개 노드", + "add": "추가:", + "commandPalette": "명령 팔레트", "done": "완료", - "editModeInstructions": "카드를 드래그하여 순서를 변경하세요. 열 구분선을 드래그하여 열 크기를 조정하세요. 카드의 아래쪽 가장자리를 드래그하여 높이를 조정하세요. 휴지통을 클릭하여 삭제하세요.", - "empty": "비어 있는", - "clear": "분명한", + "editModeInstructions": "카드를 드래그하여 재정렬 · 열 구분선을 드래그하여 열 너비 조절 · 카드 하단을 드래그하여 높이 조절 · 휴지통으로 제거", + "empty": "비어 있음", + "clear": "비우기", "serviceLinksTitle": "서비스 링크", - "serviceLinksEmpty": "아직 서비스 링크가 없습니다.", - "serviceLinksAddLabel": "상표", + "serviceLinksEmpty": "아직 서비스 링크 없음", + "serviceLinksAddLabel": "이름", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "추가하다", + "serviceLinksAdd": "추가", "serviceLinksLabelPlaceholder": "내 서비스", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "유효한 웹 주소를 입력하세요", - "serviceLinksAddFailed": "서비스 링크를 추가하는 데 실패했습니다.", + "serviceLinksInvalidUrl": "올바른 웹 주소를 입력하세요", + "serviceLinksAddFailed": "서비스 링크 추가 실패", "disk": "디스크", - "viewServerDetails": "서버 세부 정보 보기" + "viewServerDetails": "서버 세부정보 보기" }, "sessionLogs": { "title": "세션 로그", - "noLogs": "아직 세션 로그가 없습니다.", - "noLogsDesc": "세션 로깅을 시작하려면 호스트에서 세션 로깅을 활성화하십시오.", - "duration": "지속", + "noLogs": "아직 세션 로그 없음", + "noLogsDesc": "호스트에서 세션 로깅을 활성화하여 기록 시작", + "duration": "지속 시간", "viewLog": "로그 보기", "downloadLog": "다운로드", "deleteLog": "삭제", "confirmDelete": "이 세션 로그를 삭제하시겠습니까?", - "confirmDeleteDesc": "이 작업은 되돌릴 수 없습니다.", + "confirmDeleteDesc": "이 작업은 취소할 수 없습니다.", "copyContent": "복사", - "copied": "복사했어요!", - "loadError": "세션 로그를 불러오는 데 실패했습니다.", - "deleteError": "세션 로그 삭제에 실패했습니다.", - "filterByHost": "호스트별로 필터링..." + "copied": "복사됨!", + "loadError": "세션 로그 로드 실패", + "deleteError": "세션 로그 삭제 실패", + "filterByHost": "호스트로 필터링..." }, "networkGraph": { "addHost": "호스트 추가", "addGroup": "그룹 추가", "addLink": "링크 추가", "zoomIn": "확대", - "zoomOut": "축소하기", - "resetView": "보기 재설정", - "selectHost": "호스트를 선택하세요", - "chooseHost": "호스트를 선택하세요...", - "parentGroup": "학부모 단체", + "zoomOut": "축소", + "resetView": "보기 초기화", + "selectHost": "호스트 선택", + "chooseHost": "호스트 선택...", + "parentGroup": "상위 그룹", "noGroup": "그룹 없음", "groupName": "그룹 이름", "color": "색상", - "source": "원천", - "target": "목표", + "source": "소스", + "target": "대상", "moveToGroup": "그룹으로 이동", - "selectGroup": "그룹을 선택하세요...", + "selectGroup": "그룹 선택...", "addConnection": "연결 추가", - "hostDetails": "호스트 정보", + "hostDetails": "호스트 세부 정보", "removeFromGroup": "그룹에서 제거", - "addHostHere": "여기에 호스트를 추가하세요", - "editGroup": "편집 그룹", + "addHostHere": "여기에 호스트 추가", + "editGroup": "그룹 편집", "delete": "삭제", - "add": "추가하다", - "create": "만들다", - "move": "이동하다", - "connect": "연결하다", + "add": "추가", + "create": "생성", + "move": "이동", + "connect": "연결", "createGroup": "그룹 생성", "selectSourcePlaceholder": "소스 선택...", - "selectTargetPlaceholder": "대상을 선택하세요...", - "invalidFile": "잘못된 파일입니다", + "selectTargetPlaceholder": "대상 선택...", + "invalidFile": "잘못된 파일", "hostAlreadyExists": "호스트가 이미 토폴로지에 있습니다.", - "connectionExists": "이미 연결이 존재합니다", - "unknown": "알려지지 않은", + "connectionExists": "연결이 이미 존재합니다.", + "unknown": "알 수 없음", "name": "이름", "ip": "IP", "status": "상태", - "failedToAddNode": "노드를 추가하는 데 실패했습니다.", - "sourceDifferentFromTarget": "소스와 대상은 서로 달라야 합니다.", + "failedToAddNode": "노드를 추가하지 못했습니다.", + "sourceDifferentFromTarget": "소스와 대상은 달라야 합니다.", "exportJSON": "JSON 내보내기", "importJSON": "JSON 가져오기", - "terminal": "단말기", + "terminal": "터미널", "fileManager": "파일 관리자", "tunnel": "터널", - "docker": "도커", + "docker": "Docker", "serverStats": "호스트 메트릭", "hostMetrics": "호스트 메트릭", - "noNodes": "아직 노드가 없습니다." + "noNodes": "아직 노드 없음" }, "docker": { - "notEnabled": "이 호스트에서는 Docker가 활성화되어 있지 않습니다.", - "validating": "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": "항구", + "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": "자동 새로 고침", + "confirmRemoveContainer": "컨테이너 '{{name}}'을(를) 제거하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "runningContainerWarning": "경고: 이 컨테이너는 현재 실행 중입니다. 제거하면 먼저 컨테이너가 중지됩니다.", + "loadingContainers": "컨테이너 로딩 중...", + "manager": "Docker 관리자", + "autoRefresh": "자동 새로고침", "timestamps": "타임스탬프", - "lines": "윤곽", - "filterLogs": "로그 필터링...", - "refresh": "새로 고치다", + "lines": "라인", + "filterLogs": "로그 필터...", + "refresh": "새로고침", "download": "다운로드", - "clear": "분명한", - "logsDownloaded": "로그 파일이 성공적으로 다운로드되었습니다.", - "last50": "마지막 50개", - "last100": "마지막 100개", - "last500": "마지막 500개", - "last1000": "마지막 1000개", - "allLogs": "모든 로그", - "noLogsMatching": "\"{{query}} \" 와 일치하는 로그가 없습니다.", - "noLogsAvailable": "로그 파일이 없습니다.", + "clear": "지우기", + "logsDownloaded": "로그가 성공적으로 다운로드되었습니다.", + "last50": "최근 50개", + "last100": "최근 100개", + "last500": "최근 500개", + "last1000": "최근 1000개", + "allLogs": "전체 로그", + "noLogsMatching": "\"{{query}}\"와 일치하는 로그가 없습니다.", + "noLogsAvailable": "사용 가능한 로그가 없습니다.", "noContainersFound": "컨테이너를 찾을 수 없습니다.", - "noContainersFoundHint": "이 호스트에는 사용 가능한 Docker 컨테이너가 없습니다.", - "searchPlaceholder": "검색 컨테이너...", + "noContainersFoundHint": "이 호스트에 Docker 컨테이너가 없습니다.", + "searchPlaceholder": "컨테이너 검색...", "allStatuses": "모든 상태", - "stateRunning": "달리기", - "statePaused": "일시 중지됨", - "stateExited": "나갔습니다", - "stateRestarting": "재시작 중", - "noContainersMatchFilters": "필터 조건에 맞는 컨테이너가 없습니다.", + "stateRunning": "실행 중", + "statePaused": "일시 중지", + "stateExited": "종료됨", + "stateRestarting": "다시 시작 중", + "noContainersMatchFilters": "필터와 일치하는 컨테이너가 없습니다.", "noContainersMatchFiltersHint": "검색 또는 필터 조건을 조정해 보세요.", - "failedToFetchStats": "컨테이너 통계를 가져오는 데 실패했습니다.", - "containerNotRunning": "컨테이너가 실행 중이 아닙니다", - "startContainerToViewStats": "컨테이너를 시작하여 통계를 확인하세요.", - "loadingStats": "통계 자료를 불러오는 중...", + "failedToFetchStats": "컨테이너 통계를 가져오지 못했습니다.", + "containerNotRunning": "컨테이너가 실행 중이 아닙니다.", + "startContainerToViewStats": "통계를 보려면 컨테이너를 시작하세요.", + "loadingStats": "통계 로딩 중...", "errorLoadingStats": "통계 로딩 오류", - "noStatsAvailable": "이용 가능한 통계가 없습니다.", + "noStatsAvailable": "사용 가능한 통계가 없습니다.", "cpuUsage": "CPU 사용량", - "current": "현재의", + "current": "현재", "memoryUsage": "메모리 사용량", "networkIo": "네트워크 I/O", "input": "입력", - "output": "산출", + "output": "출력", "blockIo": "블록 I/O", - "read": "읽다", - "write": "쓰다", - "pids": "PIDs", + "read": "읽기", + "write": "쓰기", + "pids": "PID", "containerInformation": "컨테이너 정보", "name": "이름", "id": "ID", "state": "상태", - "containerMustBeRunning": "콘솔에 접속하려면 컨테이너가 실행 중이어야 합니다.", + "containerMustBeRunning": "컨테이너가 실행 중이어야 콘솔에 접근할 수 있습니다", "verificationCodePrompt": "인증 코드를 입력하세요", - "totpVerificationFailed": "TOTP 인증에 실패했습니다. 다시 시도해 주세요.", - "warpgateVerificationFailed": "워프게이트 인증에 실패했습니다. 다시 시도해 주세요.", + "totpVerificationFailed": "TOTP 인증에 실패했습니다. 다시 시도하세요.", + "warpgateVerificationFailed": "Warpgate 인증에 실패했습니다. 다시 시도하세요.", "connectedTo": "{{containerName}}에 연결됨", - "disconnected": "연결이 끊어졌습니다", + "disconnected": "연결 끊김", "consoleError": "콘솔 오류", "errorMessage": "오류: {{message}}", - "failedToConnect": "컨테이너에 연결하지 못했습니다.", + "failedToConnect": "컨테이너에 연결하지 못했습니다", "console": "콘솔", - "selectShell": "쉘을 선택하세요", - "bash": "세게 때리다", - "sh": "쉿", - "ash": "금연 건강 증진 협회", - "connect": "연결하다", - "disconnect": "연결을 끊으세요", + "selectShell": "셸 선택", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "연결", + "disconnect": "연결 해제", "notConnected": "연결되지 않음", - "clickToConnect": "연결을 클릭하여 셸 세션을 시작하세요.", + "clickToConnect": "연결을 클릭하여 셸 세션을 시작하세요", "connectingTo": "{{containerName}}에 연결 중...", - "containerNotFound": "컨테이너를 찾을 수 없습니다", + "containerNotFound": "컨테이너를 찾을 수 없음", "backToList": "목록으로 돌아가기", "logs": "로그", "stats": "통계", "consoleTab": "콘솔", - "startContainerToAccess": "콘솔에 접근하려면 컨테이너를 시작하세요." + "startContainerToAccess": "컨테이너를 시작하여 콘솔에 접근하세요" }, "admin": { - "sectionGeneral": "일반적인", + "sectionGeneral": "일반", "sectionOidc": "OIDC", - "sectionSso": "SSO 제공업체", - "ssoAddProvider": "공급자 추가", + "sectionSso": "SSO 제공자", + "ssoAddProvider": "제공자 추가", "ssoDocsLink": "문서 보기", "ssoProviderDocsLink": "문서 보기", - "ssoNoProviders": "SSO 공급자가 구성되지 않았습니다.", + "ssoNoProviders": "설정된 SSO 제공자가 없습니다.", "ssoProviderName": "표시 이름", - "ssoProviderType": "공급자 유형", - "ssoDeleteProvider": "공급자 삭제", - "ssoDeleteConfirm": "이 공급자를 삭제하시겠습니까? 연결된 사용자는 로그인할 수 없습니다.", + "ssoProviderType": "제공자 유형", + "ssoDeleteProvider": "제공자 삭제", + "ssoDeleteConfirm": "이 제공자를 삭제하시겠습니까? 연결된 사용자는 로그인할 수 없습니다.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", - "ssoTypeGithub": "깃허브", + "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", - "ssoEnabled": "활성화됨", - "ssoDisabled": "장애가 있는", - "ssoSaveProvider": "저장 제공자", - "ssoTestConnection": "테스트 연결", + "ssoEnabled": "사용", + "ssoDisabled": "사용 안 함", + "ssoSaveProvider": "제공자 저장", + "ssoTestConnection": "연결 테스트", "ssoEditProvider": "제공자 편집", "ldapHost": "LDAP 호스트", "ldapPort": "포트", - "ldapUseTls": "TLS(LDAPS)를 사용하세요.", - "ldapBindDn": "바인드 DN", - "ldapBindPassword": "바인드 비밀번호", - "ldapUserSearchBase": "사용자 검색 기반", + "ldapUseTls": "TLS 사용 (LDAPS)", + "ldapBindDn": "바인딩 DN", + "ldapBindPassword": "바인딩 암호", + "ldapUserSearchBase": "사용자 검색 기준", "ldapUserSearchFilter": "사용자 검색 필터", "ldapUsernameAttr": "사용자 이름 속성", "ldapDisplayNameAttr": "표시 이름 속성", "ldapGroupSearchBase": "그룹 검색 기준", "ldapAdminGroup": "관리자 그룹", - "ldapAllowedUsers": "허용된 사용자", + "ldapAllowedUsers": "허용 사용자", "sectionUsers": "사용자", "sectionSessions": "세션", "sectionRoles": "역할", - "sectionDatabase": "데이터 베이스", + "sectionDatabase": "데이터베이스", "sectionApiKeys": "API 키", "sectionAuditLog": "감사 로그", - "sectionSsl": "SSL / 렛츠인크립트", - "sslDescription": "Let's Encrypt에서 신뢰할 수 있는 SSL 인증서를 자동으로 발급하고 갱신합니다. 공개 도메인과 80번 포트 또는 DNS 액세스가 필요합니다.", + "sectionSsl": "SSL / Let's Encrypt", + "sslDescription": "Let's Encrypt에서 신뢰할 수 있는 SSL 인증서를 자동으로 발급 및 갱신합니다. 공개 도메인과 포트 80 또는 DNS 액세스가 필요합니다.", "sslDocsLink": "SSL 문서 보기", "sslDomain": "도메인", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "인증서의 공개 도메인 이름입니다.", + "sslDomainDesc": "인증서에 사용할 공개 도메인 이름입니다.", "sslEmail": "이메일", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encrypt 알림 및 계정 관련 문의는 이메일로 보내주세요.", - "sslChallengeType": "도전 유형", - "sslChallengeTypeDesc": "Let's Encrypt에 도메인 소유권을 증명하는 방법.", - "sslChallengeHttp": "HTTP(웹루트) - 인터넷에서 접근 가능한 80번 포트가 필요합니다.", - "sslChallengeDns": "DNS(클라우드플레어) - 클라우드플레어 API 토큰이 필요합니다.", - "sslCloudflareToken": "클라우드플레어 API 토큰", - "sslCloudflareTokenPlaceholder": "토큰을 입력하세요...", - "sslCloudflareTokenDesc": "도메인에 대해 Zone:DNS:Edit 권한이 있는 범위 지정 토큰입니다.", + "sslEmailDesc": "Let's Encrypt 알림 및 계정용 연락처 이메일입니다.", + "sslChallengeType": "인증 방식", + "sslChallengeTypeDesc": "Let's Encrypt에 도메인 소유권을 증명하는 방법입니다.", + "sslChallengeHttp": "HTTP(webroot) - 인터넷에서 포트 80에 액세스할 수 있어야 함", + "sslChallengeDns": "DNS(Cloudflare) - Cloudflare API 토큰 필요", + "sslCloudflareToken": "Cloudflare API 토큰", + "sslCloudflareTokenPlaceholder": "토큰 입력...", + "sslCloudflareTokenDesc": "도메인에 대한 Zone:DNS:Edit 권한이 있는 범위 지정 토큰입니다.", "sslCertStatus": "인증서 상태", "sslCertStatusNone": "인증서 없음", - "sslCertStatusValid": "유효한", - "sslCertStatusExpiring": "곧 만료됩니다", + "sslCertStatusValid": "유효함", + "sslCertStatusExpiring": "곧 만료됨", "sslCertStatusExpired": "만료됨", - "sslCertExpiresAt": "만료일 {{date}}", - "sslLastIssued": "마지막 발행일 {{date}}", - "sslRequestCert": "증명서 발급/갱신", - "sslRequestCertLoading": "인증서를 요청하는 중...", - "sslRequestCertSuccess": "인증서가 발급 및 설치 완료되었습니다.", - "sslRequestCertFailed": "인증서 요청 실패", + "sslCertExpiresAt": "만료일: {{date}}", + "sslLastIssued": "마지막 발급일: {{date}}", + "sslRequestCert": "인증서 발급 / 갱신", + "sslRequestCertLoading": "인증서 요청 중...", + "sslRequestCertSuccess": "인증서가 성공적으로 발급 및 설치되었습니다.", + "sslRequestCertFailed": "인증서 요청에 실패했습니다.", "sslSave": "설정 저장", "sslSaved": "SSL 설정이 저장되었습니다.", - "sslSaveFailed": "SSL 설정을 저장하는 데 실패했습니다.", - "sslRequiresDomain": "도메인과 이메일 주소는 필수입니다.", - "sslInfoNote": "인증서를 발급한 후 환경 변수에서 SSL을 활성화(ENABLE_SSL=true)하고 Termix를 다시 시작하십시오.", - "auditLogTotal": "{{total}} 총 항목 수", - "auditLogEmpty": "감사 로그 항목이 발견되지 않았습니다.", + "sslSaveFailed": "SSL 설정 저장에 실패했습니다.", + "sslRequiresDomain": "도메인과 이메일은 필수입니다.", + "sslInfoNote": "인증서를 발급한 후 환경 변수에서 SSL을 활성화하고(ENABLE_SSL=true) Termix를 다시 시작하세요.", + "sslManualOption": "수동 업로드 (인증서 업로드)", + "sslManualCert": "인증서(PEM)", + "sslManualCertPlaceholder": "-----시작 인증서-----", + "sslManualKey": "개인 키(PEM)", + "sslManualKeyPlaceholder": "-----개인 키 시작-----", + "sslManualDesc": "CA에서 요구하는 경우 전체 키 체인을 포함하여 기존 인증서와 개인 키를 붙여넣으세요.", + "sslManualUpload": "인증서 업로드 및 설치", + "sslManualUploadLoading": "인증서를 업로드하는 중...", + "sslManualUploadSuccess": "인증서가 성공적으로 업로드 및 설치되었습니다.", + "sslManualUploadFailed": "인증서 업로드 실패", + "sslManualRequiresFields": "인증서와 개인 키가 필요합니다.", + "auditLogTotal": "총 {{total}}개 항목", + "auditLogEmpty": "감사 로그 항목이 없습니다.", "auditLogSuccess": "성공", - "auditLogFailed": "실패한", - "auditLogClearFilters": "필터 지우기", - "auditLogPage": "{{page}} 페이지 ( {{totalPages}} 총{{total}} 페이지)", + "auditLogFailed": "실패", + "auditLogClearFilters": "필터 초기화", + "auditLogPage": "{{page}}/{{totalPages}}페이지 (총 {{total}}건)", "auditLogIp": "IP", "auditLogResourceId": "리소스 ID", "auditLogFilterUser": "사용자", - "auditLogFilterAction": "행동", + "auditLogFilterAction": "작업", "auditLogFilterResourceType": "리소스 유형", "auditLogFilterStatus": "상태", - "auditLogFilterFrom": "에서", - "auditLogFilterTo": "에게", - "auditLogFilterAll": "모두", + "auditLogFilterFrom": "시작", + "auditLogFilterTo": "종료", + "auditLogFilterAll": "전체", "allowRegistration": "사용자 등록 허용", - "allowRegistrationDesc": "신규 사용자가 사용자 이름과 비밀번호를 사용하여 직접 등록할 수 있도록 하세요.", + "allowRegistrationDesc": "새 사용자가 사용자 이름과 비밀번호로 자가 등록할 수 있도록 허용", "allowPasswordLogin": "비밀번호 로그인 허용", - "allowPasswordLoginDesc": "사용자 이름/비밀번호로 로그인", + "allowPasswordLoginDesc": "사용자 이름/비밀번호 로그인", "oidcAutoProvision": "OIDC 자동 프로비저닝", - "oidcAutoProvisionDesc": "OIDC/SSO 사용자의 경우 첫 로그인 시 계정을 자동으로 생성합니다(등록 설정 여부와 관계없음).", - "oidcSilentLoginDefault": "기본적으로 OIDC 로그인이 자동으로 실행됩니다.", - "oidcSilentLoginDefaultDesc": "방문할 때마다 자동으로 OIDC 로그인 페이지로 이동하여 로그인 양식을 완전히 건너뜁니다.", + "oidcAutoProvisionDesc": "최초 로그인 시 OIDC/SSO 사용자 계정 자동 생성 (등록 토글과 무관)", + "oidcSilentLoginDefault": "기본 자동 OIDC 로그인", + "oidcSilentLoginDefaultDesc": "방문 시마다 OIDC 로그인으로 자동 리디렉션, 로그인 양식 완전히 건너뜀", "allowPasswordReset": "비밀번호 재설정 허용", - "allowPasswordResetDesc": "Docker 로그를 통해 재설정 코드를 확인하세요.", - "commandHistoryEnabled": "명령 기록", - "commandHistoryEnabledDesc": "명령 기록 저장을 허용합니다. 이 옵션을 비활성화하면 호스트별 설정과 관계없이 기록이 저장되지 않습니다.", - "updateCommandHistoryFailed": "명령 기록 설정 업데이트에 실패했습니다.", - "sessionTimeout": "세션 시간 초과", + "allowPasswordResetDesc": "Docker 로그를 통해 재설정 코드 확인", + "commandHistoryEnabled": "명령어 히스토리", + "commandHistoryEnabledDesc": "명령어 히스토리 기록 허용. 비활성화 시 호스트별 설정과 무관하게 히스토리가 저장되지 않습니다.", + "updateCommandHistoryFailed": "명령어 히스토리 설정 업데이트 실패", + "analyticsEnabled": "익명 사용 통계 공유", + "analyticsEnabledDesc": "Termix 개선을 위해 사용자, 호스트 및 기능 사용량에 대한 익명의 일일 통계를 전송합니다. 개인 정보나 연결 정보는 절대 포함되지 않습니다.", + "updateAnalyticsFailed": "분석 설정 업데이트에 실패했습니다.", + "sessionSharingGloballyEnabled": "세션 공유 허용", + "sessionSharingGloballyEnabledDesc": "라이브 터미널, RDP, VNC 및 Telnet 세션을 인스턴스 전체에서 공유할 수 있도록 허용합니다. 이 기능을 비활성화하면 호스트별 공유 설정이 모두 무시됩니다.", + "updateSessionSharingFailed": "세션 공유 설정 업데이트에 실패했습니다.", + "sessionTimeout": "세션 타임아웃", "hours": "시간", "sessionTimeoutRange": "최소 1시간 · 최대 720시간", - "monitoringDefaults": "모니터링 기본 설정", + "monitoringDefaults": "모니터링 기본값", "statusCheck": "상태 확인", - "metrics": "측정 기준", - "sec": "비서", + "metrics": "메트릭", + "sec": "초", "logLevel": "로그 레벨", - "enableGuacamole": "과카몰리 활성화", + "enableGuacamole": "Guacamole 활성화", "enableGuacamoleDesc": "RDP/VNC 원격 데스크톱", "enableGuacamoleDocsLink": "문서 보기", "guacdUrl": "guacd URL", "tailscaleApiKey": "Tailscale API 키", "tailscaleApiKeyDescription": "호스트 편집기에서 장치 검색에 사용됩니다. tailscale.com/admin/settings/keys에서 키를 생성하세요.", "tailscaleApiKeyDocsLink": "문서 보기", - "oidcDescription": "SSO를 위해 OpenID Connect를 구성하십시오. * 표시된 필드는 필수 입력 사항입니다.", + "oidcDescription": "SSO를 위한 OpenID Connect를 구성합니다. * 표시된 필드는 필수입니다.", "oidcDocsLink": "문서 보기", "oidcClientId": "클라이언트 ID", - "oidcClientSecret": "고객 비밀", + "oidcClientSecret": "클라이언트 시크릿", "oidcAuthUrl": "인증 URL", - "oidcIssuerUrl": "발급자 URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "토큰 URL", "oidcUserIdentifier": "사용자 식별자 경로", "oidcDisplayName": "표시 이름 경로", "oidcScopes": "스코프", - "oidcUserinfoUrl": "사용자 정보 URL 재정의", + "oidcUserinfoUrl": "Userinfo URL 재정의", "oidcAllowedUsers": "허용된 사용자", - "oidcAllowedUsersDesc": "한 줄에 이메일 하나씩 보내주세요. 모든 이메일을 수신하려면 비워 두세요.", + "oidcAllowedUsersDesc": "한 줄에 이메일 하나씩 입력하세요. 비워두면 모두 허용됩니다.", "oidcAdminGroup": "관리자 그룹", - "oidcAdminGroupDesc": "이 그룹에 속한 사용자는 관리자 권한을 갖습니다. 그룹 동기화를 비활성화하려면 비워 두세요.", - "oidcGroupClaim": "집단 청구", - "oidcGroupClaimDesc": "선택 사항입니다. 사용자의 그룹 정보가 포함된 클레임 경로입니다. 기본값은 그룹, 역할, 그룹 순입니다. 사용자 지정 클레임을 사용하는 공급자(예: Zitadel)에 사용하세요.", - "oidcCaCert": "맞춤형 CA 인증서", - "oidcCaCertDesc": "선택 사항입니다. 개인 CA 또는 자체 서명 CA를 사용하는 OIDC 공급자를 위한 PEM 인코딩 CA 인증서입니다. 시스템 신뢰 저장소를 사용하려면 비워 두십시오.", - "removeOidc": "제거하다", - "usersCount": "{{count}} 사용자", - "createUser": "만들다", - "newRole": "새로운 역할", + "oidcAdminGroupDesc": "이 그룹의 사용자에게 관리자 권한이 부여됩니다. 비워두면 그룹 동기화가 비활성화됩니다.", + "oidcGroupClaim": "그룹 클레임", + "oidcGroupClaimDesc": "선택 사항입니다. 사용자 그룹이 포함된 클레임 경로입니다. 기본값은 groups, roles, group 순서로 검색됩니다. 사용자 정의 클레임이 있는 제공자(예: Zitadel)의 경우 이 값을 사용하세요.", + "oidcCaCert": "사용자 정의 CA 인증서", + "oidcCaCertDesc": "선택 사항입니다. 개인 또는 자체 서명 CA를 사용하는 OIDC 제공자를 위한 PEM 인코딩 CA 인증서입니다. 비워두면 시스템 신뢰 저장소가 사용됩니다.", + "removeOidc": "제거", + "usersCount": "{{count}}명의 사용자", + "createUser": "생성", + "newRole": "새 역할", "roleName": "이름", "roleDisplayName": "표시 이름", "roleDescription": "설명", - "rolesCount": "{{count}} 역할", - "createRole": "만들다", + "rolesCount": "{{count}}개의 역할", + "createRole": "생성", "creating": "생성 중...", - "exportDatabase": "내보내기 데이터베이스", - "exportDatabaseDesc": "모든 호스트, 자격 증명 및 설정의 백업을 다운로드하세요.", - "export": "내보내다", - "exporting": "수출 중...", + "exportDatabase": "데이터베이스 내보내기", + "exportDatabaseDesc": "모든 호스트, 자격 증명 및 설정의 백업을 다운로드합니다.", + "export": "내보내기", + "exporting": "내보내는 중...", "importDatabase": "데이터베이스 가져오기", - "importDatabaseDesc": ".sqlite 백업 파일에서 복원", + "importDatabaseDesc": ".sqlite 백업 파일에서 복원합니다.", "importDatabaseSelected": "선택됨: {{name}}", - "selectFile": "파일을 선택하세요", - "changeFile": "변화", - "import": "수입", + "selectFile": "파일 선택", + "changeFile": "변경", + "import": "가져오기", "importing": "가져오는 중...", - "apiKeysCount": "{{count}} 키", + "apiKeysCount": "{{count}}개의 키", "apiKeysDocsLink": "문서 보기", "newApiKey": "새 API 키", "apiKeyCreatedWarning": "키가 생성되었습니다. 지금 복사하세요. 다시 표시되지 않습니다.", "apiKeyName": "이름", "apiKeyUser": "사용자", - "apiKeySelectUser": "사용자를 선택하세요...", + "apiKeySelectUser": "사용자 선택...", "apiKeyExpiresAt": "만료일", - "createKey": "키를 생성합니다", - "apiKeyNoExpiry": "유효기간 없음", + "createKey": "키 생성", + "apiKeyNoExpiry": "만료 없음", "revokedBadge": "취소됨", "authTypeDual": "이중 인증", "authTypeOidc": "OIDC", - "authTypeLocal": "현지의", + "authTypeLocal": "로컬", "adminStatusAdministrator": "관리자", "adminStatusRegularUser": "일반 사용자", "adminBadge": "관리자", "systemBadge": "시스템", - "customBadge": "관습", - "youBadge": "너", - "sessionsActive": "{{count}} 활성", + "customBadge": "사용자 지정", + "youBadge": "나", + "sessionsActive": "{{count}}개 활성", "sessionActive": "활성: {{time}}", - "sessionExpires": "Exp: {{time}}", + "sessionExpires": "만료: {{time}}", "revokeAll": "모두", - "revokeAllSessionsSuccess": "해당 사용자의 모든 세션이 취소되었습니다.", - "revokeAllSessionsFailed": "세션 취소에 실패했습니다", + "revokeAllSessionsSuccess": "사용자의 모든 세션이 취소되었습니다.", + "revokeAllSessionsFailed": "세션 취소에 실패했습니다.", "revokeSessionFailed": "세션 취소에 실패했습니다.", "addRole": "역할 추가", - "noCustomRoles": "사용자 지정 역할이 정의되지 않았습니다.", - "removeRoleFailed": "역할 제거에 실패했습니다", - "assignRoleFailed": "역할 할당에 실패했습니다", - "deleteRoleFailed": "역할 삭제에 실패했습니다", + "noCustomRoles": "정의된 사용자 지정 역할이 없습니다.", + "removeRoleFailed": "역할 제거에 실패했습니다.", + "assignRoleFailed": "역할 할당에 실패했습니다.", + "deleteRoleFailed": "역할 삭제에 실패했습니다.", "userAdminAccess": "관리자", - "userAdminAccessDesc": "모든 관리자 설정에 대한 전체 액세스 권한", + "userAdminAccessDesc": "모든 관리자 설정에 대한 전체 액세스", "userRoles": "역할", "revokeAllUserSessions": "모든 세션 취소", - "revokeAllUserSessionsDesc": "모든 기기에서 강제로 다시 로그인합니다", + "revokeAllUserSessionsDesc": "모든 기기에서 재로그인 강제", "revoke": "취소", - "deleteUserWarning": "이 사용자를 삭제하면 영구적으로 삭제됩니다.", - "deleteUser": "삭제 {{username}}", + "deleteUserWarning": "이 사용자 삭제는 영구적입니다.", + "deleteUser": "{{username}} 삭제", "deleting": "삭제 중...", - "deleteUserFailed": "사용자 삭제에 실패했습니다", - "deleteUserSuccess": "사용자 \"{{username}}\" 삭제됨", - "deleteRoleSuccess": "역할 \"{{name}}\"이 삭제되었습니다", - "revokeKeySuccess": "키 \"{{name}}\" 취소됨", + "deleteUserFailed": "사용자 삭제에 실패했습니다.", + "deleteUserSuccess": "사용자 \"{{username}}\"이 삭제되었습니다.", + "deleteRoleSuccess": "역할 \"{{name}}\"이 삭제되었습니다.", + "revokeKeySuccess": "키 \"{{name}}\"이 취소되었습니다.", "revokeKeyFailed": "키 취소에 실패했습니다.", "copiedToClipboard": "클립보드에 복사됨", "done": "완료", "createUserTitle": "사용자 생성", - "createUserDesc": "새로운 로컬 계정을 만드세요.", + "createUserDesc": "새 로컬 계정을 생성합니다.", "createUserUsername": "사용자 이름", "createUserPassword": "비밀번호", - "createUserPasswordHint": "최소 6자 이상.", - "createUserEnterUsername": "사용자 이름을 입력하세요", - "createUserEnterPassword": "비밀번호를 입력하세요", + "createUserPasswordHint": "최소 6자", + "createUserEnterUsername": "사용자 이름 입력", + "createUserEnterPassword": "비밀번호 입력", "createUserSubmit": "사용자 생성", "editUserTitle": "사용자 관리: {{username}}", "editUserDesc": "역할, 관리자 상태, 세션 및 계정 설정을 편집합니다.", @@ -2789,500 +2940,561 @@ "editUserAuthType": "인증 유형", "editUserAdminStatus": "관리자 상태", "editUserUserId": "사용자 ID", - "linkAccountTitle": "계정 연동", - "linkAccountDesc": "OIDC 계정 {{username}} 을 기존 로컬 계정과 병합합니다.", - "linkAccountDescLocal": "로컬 계정 {{username}} 을 기존 OIDC 전용 계정에 연결합니다.", - "linkAccountWarningTitle": "이것은 다음과 같을 것입니다:", - "linkAccountEffect1": "OIDC 전용 계정을 삭제하세요", + "linkAccountTitle": "계정 연결", + "linkAccountDesc": "OIDC 계정 {{username}}을(를) 기존 로컬 계정과 병합합니다.", + "linkAccountDescLocal": "로컬 계정 {{username}}을(를) 기존 OIDC 전용 계정과 연결합니다.", + "linkAccountWarningTitle": "다음을 수행합니다:", + "linkAccountEffect1": "OIDC 전용 계정을 삭제합니다.", "linkAccountEffect2": "대상 계정에 OIDC 로그인을 추가합니다.", - "linkAccountEffect3": "OIDC 로그인과 비밀번호 로그인 모두 허용", + "linkAccountEffect3": "OIDC 및 비밀번호 로그인을 모두 허용합니다.", "linkAccountTargetUsername": "로컬 계정 사용자 이름", - "linkAccountTargetPlaceholder": "연결할 로컬 계정 사용자 이름을 입력하세요.", + "linkAccountTargetPlaceholder": "연결할 로컬 계정 사용자 이름 입력", "linkAccountOidcUsername": "OIDC 계정 사용자 이름", - "linkAccountOidcPlaceholder": "병합할 OIDC 전용 계정 사용자 이름을 입력하세요.", - "linkAccountOidcNotFound": "해당 사용자 이름으로 된 OIDC 전용 계정을 찾을 수 없습니다.", - "linkAccounts": "계정 연동", + "linkAccountOidcPlaceholder": "병합할 OIDC 전용 계정 사용자 이름 입력", + "linkAccountOidcNotFound": "해당 사용자 이름의 OIDC 전용 계정을 찾을 수 없습니다.", + "linkAccounts": "계정 연결", "linkAccountSuccess": "계정이 성공적으로 연결되었습니다.", - "linkAccountFailed": "계정 연결에 실패했습니다", + "linkAccountFailed": "계정 연결에 실패했습니다.", "linkAccountInProgress": "연결 중...", "unlinkAccountTitle": "OIDC 연결 해제", - "unlinkAccountDesc": "{{username}}에서 OIDC 인증을 제거하세요. 그러면 사용자는 비밀번호로만 로그인할 수 있습니다.", - "unlinkAccountWarning": "이렇게 하면 해당 계정에서 OIDC 로그인이 제거됩니다. 사용자가 계속 로그인하려면 비밀번호를 설정해야 합니다.", + "unlinkAccountDesc": "{{username}}에서 OIDC 인증을 제거합니다. 비밀번호로만 로그인할 수 있습니다.", + "unlinkAccountWarning": "이 계정에서 OIDC 로그인이 제거됩니다. 로그인을 계속하려면 비밀번호가 설정되어 있어야 합니다.", "unlinkAccount": "OIDC 연결 해제", "unlinkAccountInProgress": "연결 해제 중...", "unlinkAccountSuccess": "OIDC 연결이 성공적으로 해제되었습니다.", "unlinkAccountFailed": "OIDC 연결 해제에 실패했습니다.", - "saving": "절약...", + "saving": "저장 중...", "updateRegistrationFailed": "등록 설정 업데이트에 실패했습니다.", "updatePasswordLoginFailed": "비밀번호 로그인 설정 업데이트에 실패했습니다.", - "cannotDisablePasswordLoginWithTotp": "한 명 이상의 사용자에게 2단계 인증(2FA)이 활성화된 경우 비밀번호 로그인을 비활성화할 수 없습니다. 먼저 2단계 인증을 비활성화하십시오.", + "cannotDisablePasswordLoginWithTotp": "하나 이상의 사용자에게 2FA가 활성화되어 있는 동안에는 비밀번호 로그인을 비활성화할 수 없습니다. 먼저 2FA를 비활성화하세요.", "updateOidcAutoProvisionFailed": "OIDC 자동 프로비저닝 설정 업데이트에 실패했습니다.", - "updateOidcSilentLoginDefaultFailed": "OIDC 자동 로그인 설정 업데이트에 실패했습니다.", + "updateOidcSilentLoginDefaultFailed": "자동 OIDC 로그인 설정 업데이트에 실패했습니다.", "updatePasswordResetFailed": "비밀번호 재설정 설정 업데이트에 실패했습니다.", - "sessionTimeoutRange2": "세션 시간 초과는 1시간에서 720시간 사이여야 합니다.", - "sessionTimeoutSaved": "세션 시간 초과 저장됨", - "sessionTimeoutSaveFailed": "세션 시간 초과 저장 실패", + "sessionTimeoutRange2": "세션 제한 시간은 1시간 이상 720시간 이하여야 합니다.", + "sessionTimeoutSaved": "세션 타임아웃 저장됨", + "sessionTimeoutSaveFailed": "세션 타임아웃 저장 실패", "monitoringIntervalInvalid": "잘못된 간격 값", - "monitoringSaved": "모니터링 설정이 저장되었습니다.", - "monitoringSaveFailed": "모니터링 설정을 저장하는 데 실패했습니다.", - "metricsHistoryRetention": "측정 지표 기록 보존", - "metricsHistoryRetentionRange": "1일~90일", - "days": "날", - "guacamoleSaved": "과카몰리 설정이 저장되었습니다.", - "guacamoleSaveFailed": "과카몰리 설정 저장에 실패했습니다.", - "guacamoleUpdateFailed": "과카몰리 설정 업데이트에 실패했습니다.", - "tailscaleSettingsSaved": "테일스케일 설정이 저장되었습니다.", - "tailscaleSettingsSaveFailed": "Tailscale 설정을 저장하는 데 실패했습니다.", - "logLevelUpdateFailed": "로그 레벨 업데이트에 실패했습니다.", - "oidcSaved": "OIDC 구성이 저장되었습니다.", - "oidcSaveFailed": "OIDC 설정을 저장하는 데 실패했습니다.", - "oidcRemoved": "OIDC 구성이 제거되었습니다.", - "oidcRemoveFailed": "OIDC 설정을 제거하는 데 실패했습니다.", - "createUserRequired": "사용자 이름과 비밀번호가 필요합니다.", - "createUserPasswordTooShort": "비밀번호는 최소 6자 이상이어야 합니다.", - "createUserSuccess": "사용자 \"{{username}}\"이 생성했습니다", - "createUserFailed": "사용자 생성에 실패했습니다", - "updateAdminStatusFailed": "관리자 상태 업데이트에 실패했습니다.", - "allSessionsRevoked": "모든 세션이 취소되었습니다", - "revokeSessionsFailed": "세션 취소에 실패했습니다", + "monitoringSaved": "모니터링 설정 저장됨", + "monitoringSaveFailed": "모니터링 설정 저장 실패", + "metricsHistoryRetention": "메트릭 이력 보존 기간", + "metricsHistoryRetentionRange": "1~90일", + "days": "일", + "guacamoleSaved": "Guacamole 설정 저장됨", + "guacamoleSaveFailed": "Guacamole 설정 저장 실패", + "guacamoleUpdateFailed": "Guacamole 설정 업데이트 실패", + "tailscaleSettingsSaved": "Tailscale 설정 저장됨", + "tailscaleSettingsSaveFailed": "Tailscale 설정 저장 실패", + "logLevelUpdateFailed": "로그 레벨 업데이트 실패", + "oidcSaved": "OIDC 구성 저장됨", + "oidcSaveFailed": "OIDC 구성 저장 실패", + "oidcRemoved": "OIDC 구성 제거됨", + "oidcRemoveFailed": "OIDC 구성 제거 실패", + "createUserRequired": "사용자 이름과 비밀번호는 필수입니다", + "createUserPasswordTooShort": "비밀번호는 최소 6자 이상이어야 합니다", + "createUserSuccess": "사용자 \"{{username}}\" 생성됨", + "createUserFailed": "사용자 생성 실패", + "updateAdminStatusFailed": "관리자 상태 업데이트 실패", + "allSessionsRevoked": "모든 세션 해지됨", + "revokeSessionsFailed": "세션 해지 실패", "manageUserData": "사용자 데이터 관리", "backToUsers": "사용자 목록으로 돌아가기", "manageTabAccount": "계정", "manageTabHosts": "호스트", - "manageTabCredentials": "신임장", - "manageTabSnippets": "짧은 발췌", + "manageTabCredentials": "인증 정보", + "manageTabSnippets": "스니펫", "manageTabSessions": "세션", "manageTabDanger": "위험", - "manageEditorBack": "{{username}}의 데이터로 돌아가기", + "manageEditorBack": "{{username}}님의 데이터로 돌아가기", "dataLockedBadge": "잠김", - "dataLockedNotice": "{{username}}사용자의 데이터는 다음 로그인까지 잠금 상태로 유지됩니다. 그 전까지는 호스트, 자격 증명 및 스니펫을 보거나 편집할 수 없습니다.", + "dataLockedNotice": "{{username}}님의 데이터는 다음 로그인까지 잠금 상태로 유지됩니다. 그때까지 호스트, 자격 증명 및 스니펫을 보거나 편집할 수 없습니다.", "resetPasswordTitle": "비밀번호 재설정", "resetPasswordOidcOnly": "이 사용자는 외부 제공업체를 통해 로그인하며 비밀번호가 없습니다.", "resetPasswordPlaceholder": "새 비밀번호", - "resetPasswordBtn": "다시 놓기", + "resetPasswordBtn": "재설정", "resetPasswordWorking": "재설정 중...", - "resetPasswordSuccess": "비밀번호 재설정", + "resetPasswordSuccess": "비밀번호가 재설정되었습니다.", "resetPasswordSuccessWiped": "비밀번호가 재설정되었습니다. 사용자의 암호화된 데이터가 삭제되었습니다.", - "resetPasswordFailed": "비밀번호 재설정에 실패했습니다", - "resetPasswordConfirmWipe": "{{username}} 님은 암호화 업그레이드 이후 로그인하지 않았으므로 데이터를 복구할 수 없습니다. 지금 초기화하면 호스트, 자격 증명 및 스니펫이 삭제됩니다. 계속하시겠습니까?", + "resetPasswordFailed": "비밀번호 재설정에 실패했습니다.", + "resetPasswordConfirmWipe": "{{username}}님이 암호화 업그레이드 이후 로그인하지 않아 데이터를 복구할 수 없습니다. 지금 재설정하면 호스트, 자격 증명 및 스니펫이 삭제됩니다. 계속하시겠습니까?", "totpSectionTitle": "2단계 인증", - "totpStatusEnabled": "이 사용자에게 TOTP가 활성화되어 있습니다.", - "totpStatusDisabled": "이 사용자에게는 TOTP가 활성화되어 있지 않습니다.", - "disableTotp": "장애를 입히다", - "disableTotpConfirm": "{{username}}에 대한 2단계 인증을 비활성화하시겠습니까? 그러면 해당 사용자는 비밀번호만으로 로그인할 수 있습니다.", - "totpDisabledSuccess": "TOTP 비활성화됨", - "totpDisableFailed": "TOTP를 비활성화하는 데 실패했습니다.", + "totpStatusEnabled": "이 사용자에 대해 TOTP가 활성화되었습니다.", + "totpStatusDisabled": "이 사용자에 대해 TOTP가 비활성화되었습니다.", + "disableTotp": "비활성화", + "disableTotpConfirm": "{{username}}님의 2단계 인증을 비활성화하시겠습니까? 비밀번호만으로 로그인할 수 있게 됩니다.", + "totpDisabledSuccess": "TOTP가 비활성화되었습니다.", + "totpDisableFailed": "TOTP 비활성화에 실패했습니다.", "manageApiKeys": "API 키", "apiKeyNamePlaceholder": "키 이름", - "apiKeyCopyNotice": "이 키를 지금 복사하세요. 다시는 표시되지 않습니다.", + "apiKeyCopyNotice": "이 키를 지금 복사하세요. 다시 표시되지 않습니다.", "noApiKeysForUser": "API 키 없음", "apiKeyDeleteFailed": "API 키 삭제에 실패했습니다.", "exportUserData": "데이터 내보내기", - "exportUserDataDesc": "이 사용자의 호스트, 자격 증명 및 파일 관리자 데이터를 JSON 형식으로 다운로드합니다. 비밀 키는 복호화됩니다.", - "exportUserDataSuccess": "사용자 데이터 내보내기", + "exportUserDataDesc": "이 사용자의 호스트, 자격 증명 및 파일 관리자 데이터를 JSON으로 다운로드합니다. 비밀 정보는 복호화됩니다.", + "exportUserDataSuccess": "사용자 데이터가 내보내졌습니다.", "exportUserDataFailed": "사용자 데이터 내보내기에 실패했습니다.", - "hostsCount": "{{count}} 호스트", + "hostsCount": "호스트 {{count}}개", "addHostForUser": "호스트 추가", - "noHostsForUser": "이 사용자는 호스트가 없습니다.", - "connectToHost": "연결하다", - "deleteHostConfirm": "{{username}}에 속한 호스트 \"{{name}}\"를 삭제하시겠습니까?", - "hostDeletedSuccess": "호스트가 삭제했습니다", - "hostDeleteFailed": "호스트 삭제에 실패했습니다", - "credentialsCount": "{{count}} 자격 증명", + "noHostsForUser": "이 사용자에게 호스트가 없습니다.", + "connectToHost": "연결", + "deleteHostConfirm": "{{username}}님의 호스트 \"{{name}}\"을(를) 삭제하시겠습니까?", + "hostDeletedSuccess": "호스트가 삭제되었습니다.", + "hostDeleteFailed": "호스트 삭제에 실패했습니다.", + "credentialsCount": "자격 증명 {{count}}개", "addCredentialForUser": "자격 증명 추가", - "noCredentialsForUser": "이 사용자는 자격 증명이 없습니다.", - "deleteCredentialConfirm": "{{username}}에 속한 자격 증명 \"{{name}}\"을 삭제하시겠습니까?", - "credentialDeletedSuccess": "자격 증명이 삭제되었습니다.", - "credentialDeleteFailed": "자격 증명 삭제에 실패했습니다.", - "snippetsCount": "{{count}} 스니펫", - "addSnippetForUser": "코드 조각 추가", - "noSnippetsForUser": "이 사용자는 스니펫이 없습니다.", - "snippetRequiredFields": "스니펫 이름과 내용은 필수 입력 사항입니다.", + "noCredentialsForUser": "이 사용자는 자격 증명이 없습니다", + "deleteCredentialConfirm": "{{username}}님의 자격 증명 \"{{name}}\"을 삭제하시겠습니까?", + "credentialDeletedSuccess": "자격 증명이 삭제되었습니다", + "credentialDeleteFailed": "자격 증명 삭제에 실패했습니다", + "snippetsCount": "{{count}}개의 스니펫", + "addSnippetForUser": "스니펫 추가", + "noSnippetsForUser": "이 사용자는 스니펫이 없습니다", + "snippetRequiredFields": "스니펫 이름과 내용이 필요합니다", "snippetNamePlaceholder": "스니펫 이름", "snippetContentPlaceholder": "명령 내용", "snippetFolderPlaceholder": "폴더 (선택 사항)", - "snippetSaved": "코드 조각이 저장되었습니다.", - "snippetSaveFailed": "코드 조각을 저장하는 데 실패했습니다.", - "deleteSnippetConfirm": "{{username}}에 속하는 코드 조각 \"{{name}}\"을 삭제하시겠습니까?", - "snippetDeletedSuccess": "일부 내용이 삭제되었습니다.", - "snippetDeleteFailed": "코드 조각을 삭제하는 데 실패했습니다.", - "noSessionsForUser": "활성 세션 없음", - "deleteUserDangerDesc": "{{username}} 및 해당 데이터(호스트, 자격 증명, 스니펫, 기록)를 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다.", - "deleteUserConfirm": "{{username}} 와 그 모든 데이터를 영구적으로 삭제하시겠습니까?", - "deleteUserAdminBlocked": "이 사용자를 삭제하기 전에 관리자 권한을 제거하십시오.", - "createRoleRequired": "이름과 표시 이름은 필수 입력 사항입니다.", - "createRoleSuccess": "역할 \"{{name}}\"이 생성되었습니다", + "snippetSaved": "스니펫이 저장되었습니다", + "snippetSaveFailed": "스니펫 저장에 실패했습니다", + "deleteSnippetConfirm": "{{username}}님의 스니펫 \"{{name}}\"을 삭제하시겠습니까?", + "snippetDeletedSuccess": "스니펫이 삭제되었습니다", + "snippetDeleteFailed": "스니펫 삭제에 실패했습니다", + "noSessionsForUser": "활성 세션이 없습니다", + "deleteUserDangerDesc": "{{username}}님과 모든 데이터(호스트, 자격 증명, 스니펫, 기록)를 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다.", + "deleteUserConfirm": "{{username}}님과 모든 데이터를 영구적으로 삭제하시겠습니까?", + "deleteUserAdminBlocked": "이 사용자를 삭제하기 전에 관리자 권한을 제거하세요.", + "createRoleRequired": "이름과 표시 이름이 필요합니다", + "createRoleSuccess": "역할 \"{{name}}\"이(가) 생성되었습니다", "createRoleFailed": "역할 생성에 실패했습니다", - "apiKeyNameRequired": "키 이름은 필수 입력 사항입니다.", - "apiKeyUserRequired": "사용자 ID가 필요합니다.", - "apiKeyCreatedSuccess": "API 키 \"{{name}}\"가 생성되었습니다", - "apiKeyCreateFailed": "API 키 생성에 실패했습니다.", - "exportSuccess": "데이터베이스 내보내기가 성공적으로 완료되었습니다.", - "exportFailed": "데이터베이스 내보내기 실패", - "importSelectFile": "먼저 파일을 선택하세요.", - "importCompleted": "가져오기 완료: {{total}} 개 항목 가져오기 완료, {{skipped}} 개 항목 건너뛰기", + "apiKeyNameRequired": "키 이름이 필요합니다", + "apiKeyUserRequired": "사용자 ID가 필요합니다", + "apiKeyCreatedSuccess": "API 키 \"{{name}}\"이(가) 생성되었습니다", + "apiKeyCreateFailed": "API 키 생성에 실패했습니다", + "exportSuccess": "데이터베이스를 성공적으로 내보냈습니다", + "exportFailed": "데이터베이스 내보내기에 실패했습니다", + "importSelectFile": "먼저 파일을 선택하세요", + "importCompleted": "가져오기 완료: {{total}}개 가져옴, {{skipped}}개 건너뜀", "importFailed": "가져오기 실패: {{error}}", - "importError": "데이터베이스 가져오기 실패", - "sectionHostDefaults": "호스트 기본 설정", - "hostDefaultsDesc": "새 호스트를 생성할 때 설정이 자동으로 적용됩니다. 개별 호스트는 이러한 설정을 재정의할 수 있습니다.", + "importError": "데이터베이스 가져오기에 실패했습니다", + "sectionHostDefaults": "호스트 기본값", + "hostDefaultsDesc": "새 호스트를 생성할 때 자동으로 적용되는 설정입니다. 개별 호스트에서 이러한 설정을 재정의할 수 있습니다.", "hostDefaultsSocks5": "SOCKS5 프록시", - "hostDefaultsUseSocks5": "SOCKS5 프록시 활성화", - "hostDefaultsUseSocks5Desc": "모든 새 호스트에 SOCKS5 프록시를 미리 채워 넣으세요.", - "hostDefaultsSocks5Host": "프록시 호스트/포트", + "hostDefaultsUseSocks5": "SOCKS5 프록시 사용", + "hostDefaultsUseSocks5Desc": "모든 새 호스트에 SOCKS5 프록시 미리 채우기", + "hostDefaultsSocks5Host": "프록시 호스트 / 포트", "hostDefaultsSocks5Username": "프록시 사용자 이름", "hostDefaultsSocks5Password": "프록시 비밀번호", "hostDefaultsMetrics": "호스트 메트릭", "hostDefaultsMetricsEnabled": "메트릭 활성화", - "hostDefaultsMetricsEnabledDesc": "기본적으로 새 호스트의 CPU, 메모리 및 기타 통계 정보를 수집합니다.", + "hostDefaultsMetricsEnabledDesc": "기본적으로 새 호스트에서 CPU, 메모리 등 통계 수집", "hostDefaultsStatusCheckEnabled": "상태 확인 활성화", - "hostDefaultsStatusCheckEnabledDesc": "기본적으로 새 호스트의 온라인/오프라인 상태를 확인합니다.", - "hostDefaultsTerminal": "단말기", + "hostDefaultsStatusCheckEnabledDesc": "기본적으로 새 호스트의 온라인/오프라인 상태 확인", + "hostDefaultsTerminal": "터미널", "hostDefaultsSessionLogging": "세션 로깅", - "hostDefaultsSessionLoggingDesc": "새 호스트에서 터미널 세션을 기본적으로 기록합니다.", - "hostDefaultsCommandHistory": "명령 기록", - "hostDefaultsCommandHistoryDesc": "새 호스트에서 기본적으로 명령 기록을 추적합니다.", - "hostDefaultsSaved": "호스트 기본 설정이 저장되었습니다.", - "hostDefaultsSaveFailed": "호스트 기본 설정을 저장하는 데 실패했습니다.", + "hostDefaultsSessionLoggingDesc": "기본적으로 새 호스트에서 터미널 세션 기록", + "hostDefaultsCommandHistory": "명령어 히스토리", + "hostDefaultsCommandHistoryDesc": "기본적으로 새 호스트에서 명령어 히스토리 추적", + "hostDefaultsSaved": "호스트 기본값 저장됨", + "hostDefaultsSaveFailed": "호스트 기본값 저장 실패", "rolePermissions": { "count": "{{count}} 권한", - "editAction": "편집 권한", - "loadError": "권한 카탈로그를 불러오는 데 실패했습니다.", - "saved": "역할 권한이 저장되었습니다", - "saveError": "역할 권한 저장에 실패했습니다.", - "save": "구하다", - "saving": "절약..." + "editAction": "권한 편집", + "loadError": "권한 카탈로그 로드 실패", + "saved": "역할 권한 저장됨", + "saveError": "역할 권한 저장 실패", + "save": "저장", + "saving": "저장 중..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "주인", + "hostLabel": "호스트", "hostPlaceholder": "192.168.1.1 또는 example.com", "portLabel": "포트", "portPlaceholder": "22", "usernameLabel": "사용자 이름", "usernamePlaceholder": "사용자 이름", - "authLabel": "권한", + "authLabel": "인증", "passwordLabel": "비밀번호", "passwordPlaceholder": "비밀번호", "privateKeyLabel": "개인 키", - "privateKeyPlaceholder": "개인 키를 붙여넣으세요...", - "credentialLabel": "신임장", - "credentialPlaceholder": "저장된 자격 증명을 선택하세요", + "privateKeyPlaceholder": "개인 키 붙여넣기...", + "credentialLabel": "인증 정보", + "credentialPlaceholder": "저장된 인증 정보 선택...", "connectToTerminal": "터미널에 연결", "connectToFiles": "파일에 연결" }, "history": { - "noTerminalSelected": "터미널이 선택되지 않았습니다.", + "noTerminalSelected": "선택된 터미널 없음", "noTerminalSelectedHint": "SSH 터미널 탭을 열어 명령 기록을 확인하세요.", - "searchPlaceholder": "검색 기록...", + "searchPlaceholder": "기록 검색...", "clearAll": "모두 지우기", - "noHistoryEntries": "기록 없음", - "trackingDisabled": "이용 내역 추적 기능이 비활성화되었습니다.", - "trackingDisabledHint": "호스트 터미널 설정에서 해당 기능을 활성화하십시오." + "noHistoryEntries": "기록 항목 없음", + "trackingDisabled": "기록 추적이 비활성화됨", + "trackingDisabledHint": "호스트의 터미널 설정에서 활성화하세요." }, "sshTools": { - "keyRecordingTitle": "주요 녹음", - "recordToTerminals": "터미널에 녹음", + "keyRecordingTitle": "키 기록", + "recordToTerminals": "터미널에 기록", "selectAll": "모두", - "selectNone": "없음", - "noTerminalTabsOpen": "터미널 탭이 열려 있지 않습니다.", - "selectTerminalsAbove": "위의 터미널을 선택하세요", - "broadcastInputPlaceholder": "여기에 입력하여 키 입력을 전송하세요...", - "fillPassword": "비밀번호를 입력하세요", - "fillPasswordSuccess": "{{count}} 터미널에 비밀번호를 입력했습니다.", - "fillPasswordMissing": "선택한 단말기( {{count}} )에 저장된 비밀번호가 없습니다.", - "stopRecording": "녹화 중지", - "startRecording": "녹화 시작", + "selectNone": "전체 해제", + "noTerminalTabsOpen": "열린 터미널 탭 없음", + "selectTerminalsAbove": "위 터미널 선택", + "broadcastInputPlaceholder": "키 입력을 브로드캐스트하려면 여기에 입력...", + "fillPassword": "비밀번호 채우기", + "fillPasswordSuccess": "{{count}}개 터미널에 비밀번호를 채웠습니다.", + "fillPasswordMissing": "선택된 {{count}}개 터미널에 저장된 비밀번호가 없습니다.", + "stopRecording": "기록 중지", + "startRecording": "기록 시작", "settingsTitle": "설정", - "enableRightClickCopyPaste": "마우스 오른쪽 버튼을 클릭하여 복사/붙여넣기를 활성화합니다." + "enableRightClickCopyPaste": "오른쪽 클릭 복사/붙여넣기 활성화" }, "splitScreen": { - "layoutTitle": "공들여 나열한 것", - "selectLayoutAbove": "위의 레이아웃을 선택하세요", - "selectLayoutHint": "표시할 창의 개수를 선택하세요", - "panesTitle": "판", - "openTabsTitle": "열려있는 탭", - "dragTabsHint": "탭을 위쪽 창으로 드래그하거나 빠른 할당 기능을 사용하세요.", - "dropHere": "여기에 내려놓으세요", - "emptyPane": "비어 있는", - "dashboard": "계기반", - "clearSplitScreen": "선명한 분할 화면", - "quickAssign": "빠른 배정", + "layoutTitle": "레이아웃", + "selectLayoutAbove": "위에서 레이아웃 선택", + "selectLayoutHint": "표시할 창 개수를 선택하세요.", + "panesTitle": "분할 창", + "openTabsTitle": "열린 탭", + "dragTabsHint": "탭을 위 분할 창으로 드래그하거나 빠른 할당을 사용하세요.", + "dropHere": "여기에 놓기", + "emptyPane": "비어 있음", + "dashboard": "대시보드", + "clearSplitScreen": "분할 화면 지우기", + "quickAssign": "빠른 할당", "alreadyAssigned": "패널 {{index}}", - "splitTab": "분할 탭", + "splitTab": "탭 분할", "addToSplit": "분할에 추가", "removeFromSplit": "분할에서 제거", - "assignToPane": "창에 할당", + "assignToPane": "패널에 할당", "hotkeysTitle": "키보드 단축키", - "hotkeysSplitRight": "양방향 분할 토글", - "hotkeysSplitBelow": "3분할 모드 전환", - "hotkeysNavigatePane": "탐색 패널", + "hotkeysSplitRight": "2분할 전환", + "hotkeysSplitBelow": "3분할 전환", + "hotkeysNavigatePane": "패널 탐색", "hotkeysNextTab": "다음 탭", "hotkeysPrevTab": "이전 탭" }, "snippets": { - "title": "짧은 발췌", - "createSnippetTitle": "코드 조각 생성", - "createSnippetDescription": "빠른 실행을 위해 새로운 명령 스니펫을 만드세요.", + "title": "스니펫", + "createSnippetTitle": "스니펫 생성", + "createSnippetDescription": "빠른 실행을 위한 새 명령 스니펫을 생성합니다", "nameLabel": "이름", "namePlaceholder": "예: Nginx 재시작", "descriptionLabel": "설명", - "descriptionPlaceholder": "선택적 설명", - "optional": "선택 과목", - "folderLabel": "접는 사람", + "descriptionPlaceholder": "선택 사항 설명", + "optional": "선택 사항", + "folderLabel": "폴더", "noFolder": "폴더 없음 (미분류)", "commandLabel": "명령", "commandPlaceholder": "예: sudo systemctl restart nginx", "cancel": "취소", - "createSnippetButton": "코드 조각 생성", + "createSnippetButton": "스니펫 생성", "createFolderTitle": "폴더 생성", - "createFolderDescription": "메모들을 폴더별로 정리하세요", + "createFolderDescription": "스니펫을 폴더로 정리합니다", "folderNameLabel": "폴더 이름", "folderNamePlaceholder": "예: 시스템 명령, Docker 스크립트", "folderColorLabel": "폴더 색상", "folderIconLabel": "폴더 아이콘", - "previewLabel": "시사", + "previewLabel": "미리보기", "folderNameFallback": "폴더 이름", - "createFolderButton": "폴더 생성", + "createFolderButton": "폴더 만들기", "targetTerminals": "대상 터미널", - "selectAll": "모두", + "selectAll": "전체", "selectNone": "없음", - "noTerminalTabsOpen": "터미널 탭이 열려 있지 않습니다.", - "searchPlaceholder": "검색 스니펫...", - "newSnippet": "새로운 스니펫", + "noTerminalTabsOpen": "열린 터미널 탭 없음", + "searchPlaceholder": "스니펫 검색...", + "newSnippet": "새 스니펫", "newFolder": "새 폴더", - "run": "달리다", - "noSnippetsInFolder": "이 폴더에는 스니펫이 없습니다.", - "uncategorized": "분류되지 않음", - "editSnippetTitle": "코드 조각 편집", - "editSnippetDescription": "이 명령 조각을 업데이트하세요", - "saveSnippetButton": "변경 사항 저장", - "createSuccess": "코드 조각이 성공적으로 생성되었습니다.", - "createFailed": "코드 조각을 생성하는 데 실패했습니다.", - "updateSuccess": "코드 조각이 성공적으로 업데이트되었습니다.", - "updateFailed": "코드 조각 업데이트에 실패했습니다.", - "deleteFailed": "코드 조각을 삭제하는 데 실패했습니다.", - "folderCreateSuccess": "폴더가 성공적으로 생성되었습니다.", + "run": "실행", + "noSnippetsInFolder": "이 폴더에 스니펫이 없습니다", + "uncategorized": "미분류", + "editSnippetTitle": "스니펫 편집", + "editSnippetDescription": "이 명령 스니펫을 수정합니다", + "saveSnippetButton": "변경사항 저장", + "createSuccess": "스니펫이 생성되었습니다", + "createFailed": "스니펫 생성에 실패했습니다", + "updateSuccess": "스니펫이 업데이트되었습니다", + "updateFailed": "스니펫 업데이트에 실패했습니다", + "deleteFailed": "스니펫 삭제에 실패했습니다", + "folderCreateSuccess": "폴더가 생성되었습니다", "folderCreateFailed": "폴더 생성에 실패했습니다", "editFolderTitle": "폴더 편집", - "editFolderDescription": "이 폴더의 이름을 바꾸거나 모양을 변경하세요.", - "saveFolderButton": "변경 사항 저장", - "editFolder": "편집 폴더", + "editFolderDescription": "이 폴더의 이름을 바꾸거나 모양을 변경합니다", + "saveFolderButton": "변경사항 저장", + "editFolder": "폴더 편집", "deleteFolder": "폴더 삭제", - "folderDeleteSuccess": "폴더 \"{{name}}\"이 삭제되었습니다", + "folderDeleteSuccess": "폴더 \"{{name}}\"이(가) 삭제되었습니다", "folderDeleteFailed": "폴더 삭제에 실패했습니다", - "folderEditSuccess": "폴더가 성공적으로 업데이트되었습니다.", + "folderEditSuccess": "폴더가 업데이트되었습니다", "folderEditFailed": "폴더 업데이트에 실패했습니다", - "confirmRunMessage": "\"{{name}} \"를 실행하시겠습니까?", - "confirmRunButton": "달리다", - "runSuccess": "{{name}}\"을 {{count}} 터미널에서 실행했습니다.", - "copySuccess": "\"{{name}}\"를 클립보드에 복사했습니다.", + "confirmRunMessage": "\"{{name}}\"을(를) 실행하시겠습니까?", + "confirmRunButton": "실행", + "runSuccess": "\"{{name}}\"을(를) {{count}}개 터미널에서 실행했습니다", + "copySuccess": "\"{{name}}\"을(를) 클립보드에 복사했습니다", "shareTitle": "스니펫 공유", "shareUser": "사용자", "shareRole": "역할", - "selectUser": "사용자를 선택하세요...", - "selectRole": "역할을 선택하세요...", - "shareSuccess": "스니펫이 성공적으로 공유되었습니다.", - "shareFailed": "코드 조각 공유에 실패했습니다.", + "selectUser": "사용자 선택...", + "selectRole": "역할 선택...", + "shareSuccess": "스니펫이 성공적으로 공유되었습니다", + "shareFailed": "스니펫 공유 실패", "revokeSuccess": "접근 권한이 취소되었습니다", - "revokeFailed": "접근 권한 취소에 실패했습니다.", - "currentAccess": "현재 액세스", - "shareLoadError": "공유 데이터 불러오기에 실패했습니다", + "revokeFailed": "접근 권한 취소 실패", + "currentAccess": "현재 접근 권한", + "shareLoadError": "공유 데이터 불러오기 실패", "loading": "로딩 중...", - "close": "닫다", - "reorderFailed": "스니펫 순서를 저장하는 데 실패했습니다.", - "importExport": "수입/수출", + "close": "닫기", + "reorderFailed": "스니펫 순서 저장 실패", + "importExport": "가져오기 / 내보내기", "exportBtn": "JSON 내보내기", "importBtn": "JSON 가져오기", - "exportSuccess": "코드 조각이 성공적으로 내보내졌습니다.", - "exportFailed": "코드 조각 내보내기에 실패했습니다.", - "importTitle": "코드 조각 가져오기", - "importDescription": "Termix에서 내보낸 JSON 파일에서 코드 조각과 폴더를 가져옵니다.", - "importDropOrClick": "JSON 파일을 여기에 드롭하거나 클릭하여 찾아보세요.", + "exportSuccess": "스니펫을 성공적으로 내보냈습니다", + "exportFailed": "스니펫 내보내기 실패", + "importTitle": "스니펫 가져오기", + "importDescription": "Termix에서 내보낸 JSON 파일로부터 스니펫과 폴더를 가져옵니다", + "importDropOrClick": "JSON 파일을 여기에 놓거나 클릭하여 찾아보기", "importSelectedFile": "선택됨: {{name}}", - "importOverwrite": "동일한 이름과 폴더를 가진 기존 코드 조각을 덮어씁니다.", - "importStartBtn": "수입", - "importSuccess": "가져오기 완료: {{snippets}} 개의 스니펫이 추가되었고, {{updated}} 개가 업데이트되었으며, {{skipped}} 개가 건너뛰어졌고, {{folders}} 개의 폴더가 추가되었습니다.", - "importFailed": "코드 조각을 가져오는 데 실패했습니다.", - "importInvalidFile": "잘못된 파일입니다. 스니펫 또는 폴더 배열이 포함된 JSON 객체가 필요합니다.", + "importOverwrite": "같은 이름과 폴더의 기존 스니펫 덮어쓰기", + "importStartBtn": "가져오기", + "importSuccess": "가져오기 완료: 스니펫 {{snippets}}개 추가됨, {{updated}}개 업데이트됨, {{skipped}}개 건너뜀, 폴더 {{folders}}개 추가됨", + "importFailed": "스니펫 가져오기 실패", + "importInvalidFile": "잘못된 파일: 스니펫 또는 폴더 배열을 포함하는 JSON 객체가 필요합니다", "targetHostsLabel": "대상 호스트", - "targetHostsHint": "호스트가 터미널을 열지 않고도 이 코드 조각을 직접 실행할 수 있도록 지정합니다.", - "noHostsAvailable": "구성된 호스트가 없습니다.", + "targetHostsHint": "열린 터미널 없이 이 스니펫을 직접 실행할 호스트를 할당하세요.", + "noHostsAvailable": "구성된 호스트 없음", "clearTargetHosts": "모두 지우기", - "hasTargetHosts": "대상 호스트가 있습니다", - "runOnTargets": "목표물 실행", - "directRunSuccess": "Ran \"{{name}}\" on {{count}} host(s)", - "directRunPartialFail": "\"{{name}}\"가 하나 이상의 호스트에서 실패했습니다.", + "hasTargetHosts": "대상 호스트 있음", + "runOnTargets": "대상에서 실행", + "directRunSuccess": "\"{{name}}\"을(를) {{count}}개 호스트에서 실행했습니다", + "directRunPartialFail": "\"{{name}}\"이(가) 하나 이상의 호스트에서 실패했습니다", "executionResultTitle": "실행 결과: {{name}}", - "executionResultDescription": "각 대상 호스트에서 코드 조각을 실행한 결과입니다.", + "executionResultDescription": "각 대상 호스트에서 스니펫을 실행한 출력입니다.", "executionSuccess": "성공", - "executionFailed": "실패한" + "executionFailed": "실패" + }, + "keybindings": { + "title": "키보드 단축키", + "description": "터미널 복사, 붙여넣기 및 제어 단축키를 사용자 지정하거나, 키를 바인딩하여 텍스트를 전송하거나 코드 조각을 실행할 수 있습니다.", + "defaultsHeading": "내장 바로가기", + "customHeading": "사용자 지정 바로가기", + "addBinding": "바로가기 추가", + "addBindingTitle": "바로가기 추가", + "editBindingTitle": "바로가기 편집", + "loading": "로딩 중...", + "noCustomBindings": "아직 사용자 지정 바로가기가 없습니다.", + "defaultBadge": "기본", + "customizedBadge": "맞춤형", + "customize": "맞춤 설정", + "resetToDefault": "기본 설정으로 재설정", + "resetAllToDefaults": "모든 설정을 기본값으로 재설정합니다.", + "close": "닫다", + "cancel": "취소", + "saveBinding": "바로가기 저장", + "saveError": "키보드 단축키 저장에 실패했습니다.", + "pressKeysToRecord": "키 조합", + "pressKeysPlaceholder": "클릭하고 키를 누르세요", + "recording": "키를 누르세요...", + "comboRequiredError": "먼저 키 조합을 누르세요.", + "textRequiredError": "보낼 텍스트를 입력하세요", + "controlCodeRequiredError": "제어 코드에 한 글자를 입력하세요.", + "snippetRequiredError": "코드 조각을 선택하세요", + "conflictWarning": "이 조합은 이미 {{combo}}에 연결되어 있습니다. 저장하면 둘 다 발사됩니다.", + "actionLabel": "행동", + "actionCopy": "선택 복사", + "actionPaste": "반죽", + "actionSendControlCode": "Ctrl+문자 신호를 보내세요", + "actionSendText": "문자 보내기", + "actionRunSnippet": "기존 코드 조각을 실행하세요", + "controlCodeLabel": "편지", + "textLabel": "보낼 텍스트", + "appendEnterLabel": "전송 후 Enter 키를 누르세요.", + "snippetLabel": "단편", + "selectSnippetPlaceholder": "코드 조각을 선택하세요", + "orphanedSnippetWarning": "스니펫을 찾을 수 없습니다.", + "clipboardPermissionNote": "일부 브라우저에서는 처음 사용 시 클립보드 권한을 요청할 수 있습니다." }, "userProfile": { - "donateTitle": "Termix를 계속 유지하세요", - "donateDescription": "Termix는 무료이며 자체 호스팅되고, 몇몇 사람들이 여가 시간을 활용하여 개발했습니다. Termix가 시간이나 비용을 절약하는 데 도움이 되었다면 암호화폐 기부를 통해 지속적인 운영을 지원해 주세요.", - "donateMilestones": "기부금은 SAML, Kubernetes 및 에이전트 지원 구축에 필요한 연구 및 학습 시간을 지원하는 데 사용됩니다. 진행 상황을 확인하고 기부해 주세요.", + "donateTitle": "Termix 유지 지원", + "donateDescription": "Termix는 무료이며 자체 호스팅이 가능하고, 소수의 인원이 여가 시간에 개발한 프로젝트입니다. 시간이나 비용을 절약해 주었다면, 암호화폐 기부로 유지를 도와주세요.", + "donateMilestones": "기부금은 SAML, Kubernetes, Agent 지원을 구축하는 데 필요한 연구 및 학습 시간을 충당하는 데 도움이 됩니다. 진행 상황을 확인하고 기부해 주세요.", "donateButton": "암호화폐 기부", "storageModeLocal": "브라우저", - "storageModeCloud": "데이터 베이스", - "storageModeDescription": "브라우저는 설정을 해당 브라우저에만 저장합니다. 데이터베이스는 서버와 동기화되어 모든 기기에서 불러올 수 있습니다.", - "resetToDefaults": "기본 설정으로 재설정", - "resetToDefaultsSuccess": "설정이 기본값으로 초기화됩니다.", - "storageModeSwitch": "환경설정 저장소", + "storageModeCloud": "데이터베이스", + "storageModeDescription": "브라우저는 이 브라우저에만 설정을 저장합니다. 데이터베이스는 서버에 동기화되어 모든 기기에서 불러올 수 있습니다.", + "resetToDefaults": "기본값으로 초기화", + "resetToDefaultsSuccess": "설정이 기본값으로 초기화되었습니다.", + "storageModeSwitch": "설정 저장소", "sectionAccount": "계정", - "sectionAppearance": "모습", + "sectionAppearance": "화면 설정", "sectionSecurity": "보안", "sectionApiKeys": "API 키", + "sectionData": "데이터", "sectionC2sTunnels": "C2S 터널", "usernameLabel": "사용자 이름", "roleLabel": "역할", "roleAdministrator": "관리자", "authMethodLabel": "인증 방식", - "authMethodLocal": "현지의", - "twoFaLabel": "2FA", - "twoFaOn": "~에", - "twoFaOff": "끄다", + "authMethodLocal": "로컬", + "twoFaLabel": "2단계 인증", + "twoFaOn": "켜짐", + "twoFaOff": "꺼짐", "versionLabel": "버전", "betaProgramTitle": "베타 프로그램", - "betaProgramDescription": "매주 업데이트되는 `:beta` Docker 태그를 사용하여 출시 예정인 기능을 미리 사용해 보세요. 불안정한 환경이므로 프로덕션 환경에는 적합하지 않습니다.", + "betaProgramDescription": "주간 롤링 :beta Docker 태그를 통해 새로운 기능을 미리 사용해 보세요. 불안정하므로 프로덕션 환경에는 적합하지 않습니다.", "betaProgramFeedback": "버그를 발견하셨나요? 여기에서 신고해 주세요.", - "betaProgramLearnMore": "더 알아보기", + "betaProgramLearnMore": "자세히 알아보기", "deleteAccount": "계정 삭제", - "deleteAccountDescription": "계정을 영구적으로 삭제하세요", - "changeServerDescription": "다른 Termix 백엔드 서버로 전환하세요", + "deleteAccountDescription": "계정을 영구적으로 삭제합니다", + "changeServerDescription": "다른 Termix 백엔드 서버로 전환합니다", "deleteButton": "삭제", - "deleteAccountPermanent": "이 조치는 영구적이며 되돌릴 수 없습니다.", - "deleteAccountWarning": "모든 세션, 호스트, 자격 증명 및 설정이 영구적으로 삭제됩니다.", - "confirmPasswordDeletePlaceholder": "비밀번호를 입력하여 확인하세요.", + "deleteAccountPermanent": "이 작업은 영구적이며 취소할 수 없습니다.", + "deleteAccountWarning": "모든 세션, 호스트, 자격 증명, 설정이 영구적으로 삭제됩니다.", + "confirmPasswordDeletePlaceholder": "확인을 위해 비밀번호를 입력하세요.", "languageLabel": "언어", - "themeLabel": "주제", - "fontSizeLabel": "글꼴 크기", - "accentColorLabel": "포인트 색상", - "settingsTerminal": "단말기", + "themeLabel": "테마", + "fontSizeLabel": "글자 크기", + "accentColorLabel": "강조 색상", + "settingsTerminal": "터미널", "commandAutocomplete": "명령어 자동 완성", - "commandAutocompleteDesc": "입력하는 동안 자동 완성 기능을 표시합니다.", + "commandAutocompleteDesc": "입력 시 자동 완성 표시", + "keyboardShortcuts": "키보드 단축키", + "keyboardShortcutsDescription": "터미널 복사, 붙여넣기 및 명령 단축키를 사용자 지정하세요.", + "manageShortcuts": "관리하다", "terminalLinkBehavior": "터미널 링크 클릭", - "terminalLinkBehaviorDesc": "터미널에서 링크를 클릭했을 때의 기본 동작", - "historyTracking": "이력 추적", - "historyTrackingDesc": "추적 터미널 명령", - "commandPalette": "커맨드 팔레트", + "terminalLinkBehaviorDesc": "터미널에서 링크 클릭 시 기본 동작", + "historyTracking": "히스토리 추적", + "historyTrackingDesc": "터미널 명령어 추적", + "commandPalette": "명령 팔레트", "commandPaletteDesc": "키보드 단축키 활성화", "reopenTabsOnLogin": "로그인 시 탭 다시 열기", - "reopenTabsOnLoginDesc": "로그인하거나 페이지를 새로 고칠 때, 다른 기기에서 접속하더라도 열려 있던 탭을 복원할 수 있습니다.", - "confirmTabClose": "확인 탭 닫기", - "confirmTabCloseDesc": "터미널 탭을 닫기 전에 먼저 확인하세요.", + "reopenTabsOnLoginDesc": "로그인하거나 페이지를 새로 고칠 때 다른 기기에서 열었던 탭을 포함하여 열려 있던 모든 탭을 복원합니다.", + "confirmTabClose": "탭 닫기 확인", + "confirmTabCloseDesc": "터미널 탭을 닫기 전에 확인", "settingsSidebar": "사이드바", "showHostTags": "호스트 태그 표시", - "showHostTagsDesc": "호스트 목록에 태그를 표시합니다.", - "hostTrayOnClick": "호스트 작업 확장하려면 클릭하세요", + "showHostTagsDesc": "호스트 목록에 태그 표시", + "hostTrayOnClick": "클릭하여 호스트 작업 확장", "hostTrayOnClickDesc": "연결 버튼을 항상 표시하고, 마우스 오버 대신 클릭하여 관리 옵션을 확장합니다.", - "compactHostView": "간략한 호스트 보기", + "compactHostView": "간략 호스트 보기", "compactHostViewDesc": "각 호스트를 이름과 주소만 표시하는 한 줄로 축소합니다.", "statusColors": "실제 상태 색상", - "statusColorsDesc": "강조 색상 대신 녹색/빨간색을 사용하여 온라인/오프라인 상태를 나타내세요.", - "pinAppRail": "핀 앱 레일", - "pinAppRailDesc": "왼쪽 사이드바 앱 레일을 마우스 오버 시 확장되는 대신 항상 확장된 상태로 유지합니다.", + "statusColorsDesc": "강조 색상 대신 온라인/오프라인 상태에 녹색/빨간색을 사용합니다.", + "pinAppRail": "앱 레일 고정", + "pinAppRailDesc": "마우스 오버로 확장되지 않도록 왼쪽 사이드바 앱 레일을 항상 확장된 상태로 유지합니다.", "expandAppRailOnHover": "마우스 오버 시 앱 레일 확장", - "expandAppRailOnHoverDesc": "마우스 포인터가 왼쪽 사이드바 앱 레일 위로 이동할 때 해당 레일이 확장되도록 허용합니다.", - "settingsNavigation": "항해", - "navigationTabsDesc": "앱 레일에 표시할 탭을 선택하세요", - "settingsSnippets": "짧은 발췌", - "foldersCollapsed": "폴더 접힘", + "expandAppRailOnHoverDesc": "포인터를 올리면 왼쪽 사이드바 앱 레일이 확장되도록 허용합니다.", + "settingsNavigation": "탐색", + "navigationTabsDesc": "앱 레일에 표시할 탭 선택", + "settingsSnippets": "스니펫", + "foldersCollapsed": "폴더 접기", "foldersCollapsedDesc": "기본적으로 폴더 접기", "confirmExecution": "실행 확인", - "confirmExecutionDesc": "코드 조각을 실행하기 전에 확인하십시오.", + "confirmExecutionDesc": "스니펫 실행 전 확인", "settingsUpdates": "업데이트", "disableUpdateChecks": "업데이트 확인 비활성화", - "disableUpdateChecksDesc": "업데이트 확인을 중지하세요", + "disableUpdateChecksDesc": "업데이트 확인 중지", "totpAuthenticator": "TOTP 인증기", "totpEnabled": "2FA가 활성화되었습니다", - "totpDisabled": "로그인 보안을 강화하세요", - "disable": "장애를 입히다", - "enable": "할 수 있게 하다", + "totpDisabled": "추가 로그인 보안을 설정하세요", + "disable": "비활성화", + "enable": "활성화", "setupTotp": "TOTP 설정", "qrCode": "QR 코드", - "totpInstructions": "QR 코드를 스캔하거나 인증 앱에 비밀번호를 입력한 후 6자리 코드를 입력하세요.", + "totpInstructions": "QR 코드를 스캔하거나 인증기 앱에 비밀 키를 입력한 후 6자리 코드를 입력하세요", "totpCodePlaceholder": "000000", - "verify": "확인하다", + "verify": "확인", "changePassword": "비밀번호 변경", "currentPasswordLabel": "현재 비밀번호", "currentPasswordPlaceholder": "현재 비밀번호", "newPasswordLabel": "새 비밀번호", "newPasswordPlaceholder": "새 비밀번호", - "confirmPasswordLabel": "새 비밀번호를 확인하세요", - "confirmPasswordPlaceholder": "새 비밀번호를 확인하세요", + "confirmPasswordLabel": "새 비밀번호 확인", + "confirmPasswordPlaceholder": "새 비밀번호 확인", "updatePassword": "비밀번호 업데이트", "createApiKeyTitle": "API 키 생성", - "createApiKeyDescription": "프로그래밍 방식으로 접근하기 위한 새 API 키를 생성합니다.", + "createApiKeyDescription": "프로그래매틱 액세스를 위한 새 API 키를 생성합니다.", "apiKeyNameLabel": "이름", "apiKeyNamePlaceholder": "예: CI 파이프라인", "expiryDateLabel": "만료일", - "optional": "선택 과목", + "optional": "선택 사항", "cancel": "취소", - "createKey": "키를 생성합니다", - "apiKeyCount": "{{count}} 키", + "createKey": "키 생성", + "apiKeyCount": "{{count}}개 키", "newKey": "새 키", "noApiKeys": "아직 API 키가 없습니다.", - "apiKeyActive": "활동적인", - "apiKeyUsageHint": "열쇠를 함께 넣어주세요", - "apiKeyUsageHintHeader": "헤더.", - "apiKeyPermissionsHint": "키는 생성 사용자의 권한을 상속합니다.", + "apiKeyActive": "활성", + "apiKeyUsageHint": "키를 ", + "apiKeyUsageHintHeader": "헤더에 포함하세요.", + "apiKeyPermissionsHint": "키는 생성한 사용자의 권한을 상속합니다.", + "exportData": "내 데이터 내보내기", + "exportDataDesc": "호스트, 자격 증명 및 설정 백업을 다운로드하여 다른 장치로 전송하세요.", + "export": "내보내다", + "exporting": "수출 중...", + "importData": "내 데이터 가져오기", + "importDataDesc": ".sqlite 백업 파일에서 호스트, 자격 증명 및 설정을 복원하세요.", + "importDataSelected": "선택됨: {{name}}", + "selectFile": "파일을 선택하세요", + "changeFile": "변화", + "import": "수입", + "importing": "가져오는 중...", + "exportSuccess": "데이터 내보내기가 성공적으로 완료되었습니다.", + "exportFailed": "데이터 내보내기 실패", + "importSelectFile": "먼저 파일을 선택하세요.", + "importCompleted": "가져오기 완료: {{total}} 개 항목 가져오기 완료, {{skipped}} 개 항목 건너뛰기", + "importFailed": "가져오기 실패: {{error}}", "roleUser": "사용자", "authMethodDual": "이중 인증", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP 설정 시작에 실패했습니다.", - "totpEnter6Digits": "6자리 코드를 입력하세요", + "totpSetupFailed": "TOTP 설정 시작 실패", + "totpEnter6Digits": "6자리 코드 입력", "totpEnabledSuccess": "2단계 인증이 활성화되었습니다.", - "totpInvalidCode": "잘못된 코드입니다. 다시 시도해 주세요.", + "totpInvalidCode": "유효하지 않은 코드입니다. 다시 시도하세요.", "totpDisableInputRequired": "TOTP 코드 또는 비밀번호를 입력하세요.", "totpDisabledSuccess": "2단계 인증이 비활성화되었습니다.", - "totpDisableFailed": "2단계 인증 비활성화에 실패했습니다.", - "totpDisableTitle": "2단계 인증 비활성화", - "totpDisablePlaceholder": "TOTP 코드 또는 비밀번호를 입력하세요", - "totpDisableConfirm": "2단계 인증 비활성화", - "totpContinueVerify": "계속 확인", - "totpVerifyTitle": "확인 코드", + "totpDisableFailed": "2FA 비활성화 실패", + "totpDisableTitle": "2FA 비활성화", + "totpDisablePlaceholder": "TOTP 코드 또는 비밀번호 입력", + "totpDisableConfirm": "2FA 비활성화", + "totpContinueVerify": "인증 계속하기", + "totpVerifyTitle": "코드 확인", "totpBackupTitle": "백업 코드", - "totpDownloadBackup": "다운로드 백업 코드", + "totpDownloadBackup": "백업 코드 다운로드", "passkeys": "패스키", "passkeysDesc": "WebAuthn/FIDO2 자격 증명을 사용하여 비밀번호 없이 로그인하세요.", "passkeyName": "패스키 이름", - "passkeyUvPreferred": "우선의", - "passkeyUvRequired": "필수의", - "passkeyUvDiscouraged": "낙담한", - "addPasskey": "암호 추가", + "passkeyUvPreferred": "선호", + "passkeyUvRequired": "필수", + "passkeyUvDiscouraged": "권장 안 함", + "addPasskey": "패스키 추가", "noPasskeys": "등록된 패스키가 없습니다.", - "passkeyAdded": "암호가 추가되었습니다", - "passkeyAddFailed": "암호 키를 추가하는 데 실패했습니다.", - "passkeyDeleted": "암호가 삭제되었습니다", - "passkeyDeleteFailed": "암호키 삭제에 실패했습니다.", + "passkeyAdded": "패스키가 추가되었습니다.", + "passkeyAddFailed": "패스키 추가 실패", + "passkeyDeleted": "패스키가 삭제되었습니다.", + "passkeyDeleteFailed": "패스키 삭제 실패", "done": "완료", - "secretCopied": "비밀 정보가 클립보드에 복사되었습니다.", - "apiKeyNameRequired": "키 이름은 필수 입력 사항입니다.", - "apiKeyCreated": "API 키 \"{{name}}\"가 생성되었습니다", - "apiKeyCreateFailed": "API 키 생성에 실패했습니다.", + "secretCopied": "시크릿이 클립보드에 복사됨", + "apiKeyNameRequired": "API 키 이름이 필요합니다", + "apiKeyCreated": "API 키 \"{{name}}\" 생성됨", + "apiKeyCreateFailed": "API 키 생성 실패", "apiKeyUser": "사용자", - "apiKeyExpires": "만료됨", - "apiKeyRevoked": "API 키 \"{{name}}\"가 취소되었습니다.", - "apiKeyRevokeFailed": "API 키 해지에 실패했습니다.", - "passwordFieldsRequired": "현재 비밀번호와 새 비밀번호가 필요합니다.", + "apiKeyExpires": "만료", + "apiKeyRevoked": "API 키 \"{{name}}\" 폐기됨", + "apiKeyRevokeFailed": "API 키 폐기 실패", + "passwordFieldsRequired": "현재 비밀번호와 새 비밀번호가 필요합니다", "passwordMismatch": "비밀번호가 일치하지 않습니다", - "passwordTooShort": "비밀번호는 최소 6자 이상이어야 합니다.", - "passwordUpdated": "비밀번호가 성공적으로 업데이트되었습니다.", - "passwordUpdateFailed": "비밀번호 업데이트에 실패했습니다.", - "deletePasswordRequired": "계정을 삭제하려면 비밀번호가 필요합니다.", - "deleteFailed": "계정 삭제에 실패했습니다", + "passwordTooShort": "비밀번호는 6자 이상이어야 합니다", + "passwordUpdated": "비밀번호 업데이트 완료", + "passwordUpdateFailed": "비밀번호 업데이트 실패", + "deletePasswordRequired": "계정 삭제를 위해 비밀번호가 필요합니다", + "deleteFailed": "계정 삭제 실패", "deleting": "삭제 중...", "colorPickerTooltip": "색상 선택기 열기", - "themeSystem": "체계", - "themeLight": "빛", - "themeDark": "어두운", + "themeSystem": "시스템", + "themeLight": "라이트", + "themeDark": "다크", "themeDracula": "드라큘라", - "themeCatppuccin": "캣푸친", + "themeCatppuccin": "캣푸치노", "themeNord": "노르드", - "themeSolarized": "태양열 처리", - "themeTokyoNight": "도쿄의 밤", + "themeSolarized": "솔라라이즈드", + "themeTokyoNight": "도쿄 나이트", "themeOneDark": "원 다크", "themeGruvbox": "그루브박스" } @@ -3290,183 +3502,184 @@ }, "tmuxMonitor": { "title": "Tmux 모니터", - "failedToLoadHosts": "호스트를 로드하는 데 실패했습니다.", - "failedToLoad": "tmux 세션을 로드하는 데 실패했습니다.", - "tmuxUnavailable": "이 호스트에는 tmux가 설치되어 있지 않습니다.", - "noSessions": "이 호스트에서는 tmux 세션을 사용할 수 없습니다.", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "attached": "첨부된", - "detached": "떨어져 있는", - "attach": "붙이다", - "editTags": "태그 수정", + "failedToLoadHosts": "호스트 목록을 불러오지 못했습니다", + "failedToLoad": "tmux 세션을 불러오지 못했습니다", + "tmuxUnavailable": "이 호스트에 tmux가 설치되어 있지 않습니다", + "noSessions": "이 호스트에 tmux 세션이 없습니다", + "noHostSelected": "선택된 호스트가 없습니다", + "attached": "연결됨", + "detached": "분리됨", + "attach": "연결", + "editTags": "태그 편집", "tagsHint": "쉼표로 구분된 태그 (예: YOLO, lab, training)", - "tagsSaved": "태그가 저장되었습니다", - "tagsSaveFailed": "태그 저장에 실패했습니다", - "searchPlaceholder": "세션 간 검색 결과...", - "searchResults": "{{count}} 일치", + "tagsSaved": "태그 저장됨", + "tagsSaveFailed": "태그 저장 실패", + "searchPlaceholder": "세션 전체에서 출력 검색...", + "searchResults": "{{count}}개 일치", "searchFailed": "검색 실패", - "selectPaneHint": "출력을 미리 보려면 창을 선택하세요.", + "selectPaneHint": "출력을 미리 보려면 패널을 선택하세요", "closePreview": "미리보기 닫기", "newSession": "새 세션", - "newSessionHint": "세션 이름 (문자, 숫자, _ @ % + = -)", - "newSessionPlaceholder": "내 세션", - "create": "만들다", - "sessionCreated": "세션 \"{{name}}\"이 생성되었습니다", - "sessionCreateFailed": "세션 생성에 실패했습니다.", + "newSessionHint": "세션 이름 (영문자, 숫자, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "생성", + "sessionCreated": "세션 \"{{name}}\" 생성됨", + "sessionCreateFailed": "세션 생성 실패", "splitRight": "오른쪽으로 분할", - "splitDown": "분할", - "splitFailed": "화면 분할에 실패했습니다", + "splitDown": "아래로 분할", + "splitFailed": "패널 분할 실패", "newWindow": "새 창", - "windowCreateFailed": "창을 생성하는 데 실패했습니다.", - "attachSessionTooltip": "{{session}}에 첨부하세요", - "refresh": "새로 고치다", - "refreshFailed": "세션 새로 고침에 실패했습니다.", + "windowCreateFailed": "창 생성 실패", + "attachSessionTooltip": "{{session}}에 연결", + "refresh": "새로고침", + "refreshFailed": "세션 새로고침 실패", "collapseAll": "모두 접기", "expandAll": "모두 펼치기", - "moreActions": "추가 작업", + "moreActions": "더 보기", "sessionStats": "세션 통계", - "renameSessionTitle": "세션 이름 변경 \"{{name}}\"", - "rename": "이름 변경", - "sessionRenamed": "세션 이름이 \"{{name}} \"로 변경되었습니다.", - "sessionRenameFailed": "세션 이름 변경에 실패했습니다.", - "editTagsTitle": "\"{{name}} \"에 대한 태그를 편집하세요", - "killPane": "킬 패널", - "resizeTree": "드래그하여 크기를 조절하고, 두 번 클릭하면 원래 크기로 돌아갑니다.", - "reattach": "다시 연결(깨진 화면을 수정합니다)", - "killWindow": "킬 윈도우", - "killWindowTitle": "\"{{session}} \"의 킬 윈도우 {{index}}?", - "killWindowBody": "이 창의 모든 패널과 프로세스가 종료됩니다. 마지막 창을 닫으면 세션이 종료됩니다.", - "windowKillFailed": "창을 닫는 데 실패했습니다", + "renameSessionTitle": "세션 \"{{name}}\" 이름 바꾸기", + "rename": "이름 바꾸기", + "sessionRenamed": "세션 이름이 \"{{name}}\"(으)로 변경됨", + "sessionRenameFailed": "세션 이름 변경 실패", + "editTagsTitle": "\"{{name}}\"의 태그 편집", + "killPane": "Pane 종료", + "resizeTree": "드래그로 크기 조정 — 더블클릭하여 초기화", + "reattach": "재연결 (화면 깨짐 복구)", + "killWindow": "창 종료", + "killWindowTitle": "{{session}}의 창 {{index}}을(를) 종료하시겠습니까?", + "killWindowBody": "이 창의 모든 Pane과 프로세스가 종료됩니다. 마지막 창을 종료하면 세션이 끝납니다.", + "windowKillFailed": "창 종료 실패", "statusActivity": "활동", - "statusWindows": "윈도우", - "statusPanes": "창틀", + "statusWindows": "Windows", + "statusPanes": "Pane", "statusTags": "태그", - "killPaneTitle": "킬 패널 {{id}}?", - "killPaneBody": "이 창에서 실행 중인 프로세스가 종료됩니다. 마지막 창을 종료하면 해당 창이 닫힙니다.", - "paneKillFailed": "패널을 종료하는 데 실패했습니다.", - "killSessionTitle": "킬 세션 \"{{name}}\"?", - "killSessionBody": "이 세션의 모든 창과 실행 중인 프로세스가 종료됩니다. 이 작업은 되돌릴 수 없습니다.", - "kill": "죽이다", - "sessionKilled": "세션 \"{{name}}\"이 종료되었습니다", - "sessionKillFailed": "세션을 종료하는 데 실패했습니다.", - "hostUnreachable": "호스트에 연결할 수 없습니다. 호스트가 온라인 상태이고 접속 가능한지 확인하십시오.", - "noServer": "이 호스트에서는 tmux 서버가 실행되고 있지 않습니다.", - "searchTruncated": "부분 결과 — 검색은 각 패널의 마지막 {{lines}} 줄과 최대 {{panes}} 개의 패널을 포함합니다.", + "killPaneTitle": "Pane {{id}}을(를) 종료하시겠습니까?", + "killPaneBody": "이 Pane에서 실행 중인 프로세스가 종료됩니다. 마지막 Pane을 종료하면 해당 창이 닫힙니다.", + "paneKillFailed": "Pane 종료 실패", + "killSessionTitle": "세션 \"{{name}}\"을(를) 종료하시겠습니까?", + "killSessionBody": "이 세션의 모든 창과 실행 중인 프로세스가 종료됩니다. 이 작업은 취소할 수 없습니다.", + "kill": "종료", + "sessionKilled": "세션 \"{{name}}\"이(가) 종료되었습니다.", + "sessionKillFailed": "세션 종료 실패", + "hostUnreachable": "호스트에 연결할 수 없습니다. 온라인 상태이고 연결 가능한지 확인하세요.", + "noServer": "이 호스트에서 tmux 서버가 실행 중이 아닙니다.", + "searchTruncated": "일부 결과 — 각 Pane의 마지막 {{lines}}줄과 최대 {{panes}}개의 Pane을 검색합니다.", "closeSearchResults": "검색 결과 닫기", - "retry": "다시 해 보다", + "retry": "재시도", "noHosts": "사용 가능한 SSH 호스트가 없습니다.", - "noHostsHint": "SSH 호스트에서 Tmux 모니터 옵션을 활성화하면(호스트 관리자 → 터미널 탭) 해당 호스트의 tmux 세션을 모니터링할 수 있습니다.", - "tmuxInstallHint": "호스트에 다음 명령으로 설치하세요:", - "attachTooltip": "터미널을 열어 {{host}}로 이동하세요.", - "attachTooltipPane": "터미널을 열어 {{host}} — tmux 세션 {{session}}를 입력하세요.", + "noHostsHint": "SSH 호스트에서 Tmux Monitor 옵션을 활성화하여 (호스트 관리자 → 터미널 탭) tmux 세션을 모니터링하세요.", + "tmuxInstallHint": "호스트에 설치하려면:", + "attachTooltip": "{{host}}에 터미널 열기", + "attachTooltipPane": "{{host}}에 터미널 열기 — tmux 세션 {{session}}", "timeJustNow": "방금", - "timeMinutes": "{{count}}몇 개월 전", + "timeMinutes": "{{count}}분 전", "timeHours": "{{count}}시간 전", "timeDays": "{{count}}일 전" }, "mobileKeyboard": { - "shift": "옮기다", - "ctrl": "Ctrl 키", - "esc": "에스", - "tab": "꼬리표", + "shift": "Shift", + "ctrl": "Ctrl", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", "arrowUp": "위쪽 화살표", "arrowDown": "아래쪽 화살표", "arrowLeft": "왼쪽 화살표", "arrowRight": "오른쪽 화살표", - "home": "집", - "end": "끝", - "pageUp": "페이지업", + "home": "Home", + "end": "End", + "pageUp": "PgUp", "pageDown": "PgDn", - "delete": "델", - "editQuickKeys": "빠른 단축키 편집", - "quickKeysTitle": "단축키", - "quickKeysDesc": "삭제하려면 ×를 누르세요. 최대 8자까지 지원합니다.", + "delete": "Del", + "paste": "반죽", + "editQuickKeys": "빠른 키 편집", + "quickKeysTitle": "빠른 키", + "quickKeysDesc": "×를 탭하여 제거합니다. 최대 8자까지 지원합니다.", "quickKeyPlaceholder": "예: sudo ", - "addQuickKey": "추가하다", - "removeQuickKey": "제거하다", - "resetDefaults": "기본 설정으로 재설정", + "addQuickKey": "추가", + "removeQuickKey": "제거", + "resetDefaults": "기본값으로 초기화", "done": "완료" }, "serial": { - "title": "연속물", + "title": "시리얼", "portLabel": "포트", - "portPlaceholder": "/dev/ttyUSB0 또는 COM3", - "baudRateLabel": "보드율", + "portPlaceholder": "/dev/ttyUSB0 or COM3", + "baudRateLabel": "보레이트", "dataBitsLabel": "데이터", - "stopBitsLabel": "멈추다", - "parityLabel": "둥가", + "stopBitsLabel": "스톱", + "parityLabel": "패리티", "parityNone": "없음", - "parityEven": "심지어", - "parityOdd": "이상한", - "connect": "시리얼 포트에 연결", - "disconnect": "연결을 끊으세요", + "parityEven": "짝수", + "parityOdd": "홀수", + "connect": "시리얼 연결", + "disconnect": "연결 해제", "refreshPorts": "포트 새로 고침", - "connected": "{{path}} 에 {{baud}} 보드 속도로 연결됨", - "disconnected": "직렬 포트 연결이 끊어졌습니다", - "connectionError": "시리얼 포트를 열 수 없습니다.", - "wsError": "웹소켓 오류", + "connected": "{{path}}에 {{baud}} 보로 연결됨", + "disconnected": "시리얼 포트 연결 해제됨", + "connectionError": "시리얼 포트를 열지 못했습니다.", + "wsError": "WebSocket 오류", "errorNoServerUrl": "서버 URL이 구성되지 않았습니다.", - "notSupportedTitle": "시리얼 번호가 지원되지 않습니다.", - "notSupported": "직렬 연결을 사용하려면 웹 직렬 API를 지원하는 브라우저(Chrome, Edge 또는 Firefox 151 이상) 또는 Termix 데스크톱 앱이 필요합니다.", - "hideHint": "사용자 프로필 > 모양 > 사이드바 > 탐색에서 시리얼 탭을 숨길 수 있습니다.", - "browserPickerHint": "[연결]을 클릭하면 브라우저에 포트 선택기가 열리고 장치를 선택할 수 있습니다." + "notSupportedTitle": "시리얼 미지원", + "notSupported": "시리얼 연결에는 Web Serial API를 지원하는 브라우저(Chrome, Edge, Firefox 151+) 또는 Termix 데스크톱 앱이 필요합니다.", + "hideHint": "Serial 탭은 사용자 프로필 > 모양 > 사이드바 > 탐색에서 숨길 수 있습니다.", + "browserPickerHint": "연결 버튼을 클릭하면 브라우저에서 장치 선택을 위한 포트 선택기가 열립니다." }, "metricsHistory": { - "title": "측정 내역", - "historySuffix": "역사", + "title": "메트릭 기록", + "historySuffix": "기록", "cpuMemoryDisk": "CPU / 메모리 / 디스크", - "network": "회로망", + "network": "네트워크", "download": "RX", - "upload": "텍사스", - "noData": "해당 기간에 대한 기록 데이터가 없습니다.", - "custom": "관습", - "to": "에게", - "apply": "적용하다", + "upload": "TX", + "noData": "이 시간 범위에 대한 기록 데이터가 없습니다.", + "custom": "사용자 지정", + "to": "~", + "apply": "적용", "viewHistory": "기록 보기" }, "alerts": { "tabFirings": "알림", "tabRules": "규칙", "tabChannels": "채널", - "noFirings": "확인되지 않은 알림 없음", - "noRules": "설정된 경고 규칙이 없습니다.", - "noChannels": "설정된 알림 채널이 없습니다.", - "rulesDesc": "알림 규칙이 알림을 트리거합니다.", + "noFirings": "미확인 알림 없음", + "noRules": "구성된 알림 규칙 없음", + "noChannels": "구성된 알림 채널 없음", + "rulesDesc": "알림 규칙은 알림을 트리거합니다", "channelsDesc": "알림이 전송되는 위치", - "acknowledge": "인정하다", - "ackAll": "모두 승인", - "allAcknowledged": "모든 알림 확인 완료", - "ackFailed": "알림을 확인하지 못했습니다.", - "ackAllFailed": "모든 알림을 확인하지 못했습니다.", - "showAcknowledged": "모두 보기", - "hideAcknowledged": "숨기기", + "acknowledge": "확인", + "ackAll": "모두 확인", + "allAcknowledged": "모든 알림 확인됨", + "ackFailed": "알림 확인 실패", + "ackAllFailed": "모든 알림 확인 실패", + "showAcknowledged": "모두 표시", + "hideAcknowledged": "확인된 항목 숨기기", "addChannel": "채널 추가", "editChannel": "채널 편집", "channelName": "이름", "channelType": "유형", - "channelNameRequired": "이름은 필수 입력 사항입니다.", + "channelNameRequired": "이름은 필수입니다", "webhookUrl": "URL", - "webhookUrlRequired": "웹훅 URL은 필수 입력 사항입니다.", - "webhookDesc": "각 경고 발생 시 JSON 페이로드를 이 URL로 POST합니다.", + "webhookUrlRequired": "웹훅 URL은 필수입니다", + "webhookDesc": "알람이 발생할 때마다 이 URL로 JSON 페이로드를 POST합니다", "ntfyServer": "서버 URL", - "ntfyTopic": "주제", - "ntfyTopicRequired": "주제를 입력해주세요", - "ntfyToken": "액세스 토큰(선택 사항)", + "ntfyTopic": "토픽", + "ntfyTopicRequired": "토픽은 필수입니다", + "ntfyToken": "액세스 토큰 (선택 사항)", "channelSaveFailed": "채널 저장에 실패했습니다", - "test": "시험", - "testSent": "테스트 알림이 전송되었습니다.", - "testFailed": "테스트 알림 실패", - "addRule": "알림 규칙 추가", - "editRule": "알림 규칙 수정", + "test": "테스트", + "testSent": "테스트 알림이 전송되었습니다", + "testFailed": "테스트 알림 전송에 실패했습니다", + "addRule": "알람 규칙 추가", + "editRule": "알람 규칙 편집", "ruleName": "규칙 이름", - "ruleNameRequired": "이름은 필수 입력 사항입니다.", - "triggerType": "방아쇠", - "thresholdValue": "한계점 (%)", - "durationSeconds": "지속 시간(초, 0 = 즉시 발사)", - "cooldownMinutes": "재사용 대기 시간(분)", + "ruleNameRequired": "이름은 필수입니다", + "triggerType": "트리거", + "thresholdValue": "임계값 (%)", + "durationSeconds": "지속 시간 (초, 0 = 즉시 발생)", + "cooldownMinutes": "쿨다운 (분)", "channels": "알림 채널", - "noChannelsHint": "먼저 '채널' 탭에서 채널을 추가하세요.", + "noChannelsHint": "먼저 채널 탭에서 채널을 추가하세요", "ruleSaveFailed": "규칙 저장에 실패했습니다" } } diff --git a/src/ui/locales/translated/nl_NL.json b/src/ui/locales/translated/nl_NL.json index f70719a9..0f1d1792 100644 --- a/src/ui/locales/translated/nl_NL.json +++ b/src/ui/locales/translated/nl_NL.json @@ -389,6 +389,45 @@ "noSavedServers": "Geen opgeslagen servers", "removeServer": "Verwijderen" }, + "migrationNotice": { + "title": "Termix Desktop draait nu zelfstandig.", + "body1": "Deze app maakte voorheen rechtstreeks verbinding met uw Termix-server. Hij is herwerkt om volledig zelfstandig te werken, waarbij hosts en inloggegevens lokaal worden opgeslagen, zodat hij ook offline blijft functioneren. Tweewegsynchronisatie met een zelfgehoste Termix-server is nu optioneel.", + "body2": "Uw bestaande hosts en inloggegevens bevinden zich nog steeds op {{url}}. Schakel externe synchronisatie in en maak opnieuw verbinding met die server om ze te herstellen en beide in de toekomst gesynchroniseerd te houden.", + "dismiss": "Nu niet", + "setUpSync": "Externe synchronisatie instellen" + }, + "remoteSync": { + "title": "Synchronisatie op afstand", + "description": "U kunt deze desktop-app optioneel verbinden met een zelfgehoste Termix-server om uw hosts, inloggegevens en codefragmenten op verschillende apparaten te synchroniseren. De app werkt altijd volledig offline, ongeacht of u verbinding maakt of niet.", + "notConnected": "Niet verbonden", + "connected": "Aangesloten", + "connectedTo": "Verbonden met {{url}}", + "lastSynced": "Laatst gesynchroniseerd {{time}}", + "neverSynced": "Nooit gesynchroniseerd", + "syncError": "Synchronisatiefout: {{message}}", + "needsReauth": "Aanmelden verlopen", + "connectButton": "Verbinding maken met de server", + "disconnectButton": "Verbinding verbreken", + "syncNowButton": "Nu synchroniseren", + "syncing": "Synchroniseren...", + "serverUrl": "Server-URL", + "enterServerUrl": "Voer een server-URL in.", + "mustIncludeProtocol": "De server-URL moet beginnen met http:// of https://", + "connectionTestFailed": "Er kon geen Termix-server worden bereikt op die URL.", + "allowInvalidCertificate": "Ongeldig certificaat toestaan", + "allowInvalidCertificateDesc": "Uitsluitend te gebruiken voor vertrouwde, zelfgehoste servers met zelfondertekende certificaten of IP-adrescertificaten.", + "savedServers": "Opgeslagen servers", + "removeServer": "Verwijderen", + "continueButton": "Doorgaan", + "cancelButton": "Annuleren", + "signInTitle": "Meld je aan bij {{url}}", + "originTitle": "Verbindingsoorsprong", + "originDescription": "Kies de standaardlocatie voor SSH-verbindingen. Deze instelling kan per host worden gewijzigd.", + "originLocal": "Dit apparaat (lokaal netwerk)", + "originRemote": "Externe server", + "bannerReconnect": "Opnieuw verbinding maken", + "bannerMessage": "Externe synchronisatie vereist herverificatie." + }, "versionCheck": { "error": "Versiecontrolefout", "checkFailed": "Controleren op updates is mislukt.", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Gebruikersnaam voor het overschrijven van inloggegevens", "overrideCredentialUsernameDesc": "Gebruik de hierboven opgegeven gebruikersnaam in plaats van de gebruikersnaam van de inloggegevens.", "oidcUsernameHint": "Gebruik $oidc.preferred_username om uw OIDC-inlognaam te vervangen.", + "tailscaleUsernameHint": "Dit moet een Unix-gebruiker zijn die aan uw Tailscale-identiteit is toegekend in de SSH-toegangsregel van het tailnet, niet per se root.", "jumpHostChain": "Spring naar de hostketen", "portKnocking": "Havenkloppen", "addKnock": "Poort toevoegen", @@ -650,6 +690,11 @@ "delayAfterMs": "Vertraging na (ms)", "useSocks5Proxy": "Gebruik een SOCKS5-proxy", "useSocks5ProxyDesc": "De verbinding wordt via een proxyserver geleid.", + "connectionOrigin": "Verbindingsoorsprong", + "connectionOriginDesc": "Waar de SSH-verbinding van deze host vandaan komt. Overschrijft de algemene standaardinstelling van de desktop-app.", + "connectionOriginDefault": "Gebruik de standaardinstelling", + "connectionOriginLocal": "Dit apparaat (lokaal netwerk)", + "connectionOriginRemote": "Externe server", "proxyHost": "Proxy-host", "proxyPort": "Proxy-poort", "proxyUsername": "Proxy-gebruikersnaam", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Helder cyaan", "customThemeBrightWhite": "Helder wit", "customThemeResetTooltip": "Terugzetten naar standaardinstellingen", + "savedThemesLabel": "Opgeslagen thema's", + "saveAsGlobalTheme": "Opslaan als globaal thema", + "saveGlobalThemeNamePrompt": "Geef dit thema een naam.", + "saveGlobalThemeSuccess": "Thema opgeslagen", + "saveGlobalThemeError": "Het opslaan van het thema is mislukt.", + "applyGlobalThemeTooltip": "Pas dit thema toe", + "deleteGlobalThemeTooltip": "Verwijder dit thema", + "noSavedThemes": "Nog geen opgeslagen thema's", "syntaxHighlightingLabel": "Syntaxmarkering", "syntaxHighlightingDesc": "Kleur de terminaluitvoer (fouten, paden, IP-adressen, tijdstempels) in.", "syntaxHighlightingCategories": "Markeer categorieën", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Een tmux-sessie automatisch starten of eraan koppelen", "enableSessionLogging": "Sessieregistratie", "enableSessionLoggingDesc": "Sla de uitvoer van de terminalsessie op voor latere beoordeling.", + "allowSessionSharing": "Sessiedeling toestaan", + "allowSessionSharingDesc": "Sta toe dat live sessies op deze host via een link of met andere gebruikers worden gedeeld.", "enableCommandHistory": "Commandogeschiedenis", "enableCommandHistoryDesc": "Sla de commando's die in deze terminal worden uitgevoerd op voor de geschiedenis en automatisch aanvullen.", "linkClickBehaviorLabel": "Klikgedrag op links", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} geïmporteerd", "proxmoxResultUpdated": "{{count}} bijgewerkt", "proxmoxResultFailed": "{{count}} mislukt", + "proxmoxResultSkippedNoIp": "{{count}} overgeslagen (geen IP-adres gevonden)", "proxmoxImportComplete": "Proxmox-import voltooid: {{summary}}", "proxmoxDiscoveryFailed": "Ontdekking mislukt", "proxmoxImportFailed": "Importeren mislukt", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Het opslaan van de inloggegevens is mislukt.", "credentialNameRequired": "Voer een naam in voor de legitimatie.", "credentialAuthRequired": "Voeg een wachtwoord, een SSH-sleutel of beide toe.", + "createCredentialFromHostBtn": "Een inloggegeven aanmaken", + "createCredentialFromHostTitle": "Maak een inloggegeven aan vanuit de host.", + "createCredentialFromHostDesc": "Maak een herbruikbare, deelbare inloggegevensvermelding aan die vooraf is ingevuld met de huidige gebruikersnaam, het wachtwoord en/of de SSH-sleutel van deze host.", "backToHosts": "Terug naar hosts", "backToCredentials": "Terug naar inloggegevens", "pinned": "Vastgepind", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Gebruik / om niveaus te scheiden en geneste mappen te maken.", "folderColor": "Kleur", "folderIcon": "Icon", + "folderCredential": "Referentie", + "folderCredentialNone": "Geen inloggegevens toegewezen", + "folderCredentialHint": "Hosts in deze map die gebruikmaken van authenticatie met \"Opgeslagen referenties\" zonder hun eigen referenties te hebben geselecteerd, zullen deze referenties overnemen.", "folderPreview": "Voorbeeld", "folderNameFallback": "Naamloze map", "createFolderButton": "Map aanmaken", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tags", "shareHost": "Deel host", "shareHostTitle": "Delen: {{name}}", + "shareFolder": "Map delen", + "shareFolderTitle": "Map delen: {{name}}", + "folderSharedSuccessfully": "Gedeelde {{count}} host(s) in map", + "failedToShareFolder": "Map delen mislukt", "sharing": { "loadError": "Het laden van de deelgegevens is mislukt. Probeer het opnieuw.", "shareWithSection": "Delen met", @@ -1221,6 +1287,7 @@ "shareWithCount": "Delen ({{count}})", "currentAccess": "Huidige toegang", "noAccessEntries": "Deze host is nog niet gedeeld.", + "folderShareSummary": "Gedeelde {{shared}} van {{total}} host(s) in deze map", "grantedBy": "Toegekend door", "expires": "Verloopt", "expired": "Verlopen", @@ -1242,6 +1309,8 @@ "authMethod": "Authenticatiemethode", "authTypeDirect": "Direct", "authTypeCredential": "Referentie", + "authTypeNone": "Geen", + "authTypeNoneDesc": "Er worden geen inloggegevens opgeslagen. U wordt bij elke verbinding gevraagd om een gebruikersnaam en wachtwoord; deze worden niet bewaard.", "selectCredential": "Selecteer een certificaat...", "connectionSettings": "Verbindingsinstellingen", "displaySettings": "Weergave-instellingen", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-poort", "username": "Gebruikersnaam", "password": "Wachtwoord", + "passwordSaved": "Wachtwoord opgeslagen, typ om te wijzigen", "domain": "Domein", "securityMode": "Beveiligingsmodus", "colorDepth": "Kleurdiepte", @@ -1424,7 +1494,60 @@ "expiresIn": "Verloopt over {{duration}}", "search": "Zoek naar verbanden...", "noSearchResults": "Er zijn geen resultaten gevonden die overeenkomen met uw zoekopdracht.", - "rename": "Sessie hernoemen" + "rename": "Sessie hernoemen", + "sectionSharedWithMe": "Met mij gedeeld", + "sharedBy": "Gedeeld door {{username}}", + "join": "Meedoen", + "sharedSessionLabel": "{{hostName}} (gedeeld)" + }, + "sessionSharing": { + "guestView": { + "loading": "Verbinding maken met de gedeelde sessie...", + "linkInvalid": "Deze deelbare link is ongeldig, verlopen of ingetrokken.", + "rateLimited": "Te veel pogingen, probeer het over een tijdje opnieuw.", + "sessionEnded": "Deze sessie is afgelopen.", + "readOnlyBadge": "Alleen bekijken" + }, + "modalTitle": "Deel sessie", + "shareButton": "Deel", + "notReadyToShare": "De sessie is nog niet klaar om te delen.", + "modeTab": { + "link": "Link", + "user": "Gebruiker" + }, + "linkModeDescription": "Iedereen met deze link kan deelnemen, een account is niet nodig.", + "userModeDescription": "Deel de host met een specifieke gebruiker die er al toegang toe heeft. Als die gebruiker nog geen toegang heeft, deel de host dan eerst met hem of haar, of gebruik een link. Zodra de sessie is gedeeld, verschijnt deze in het tabblad 'Verbindingen' van de gebruiker.", + "permissionLevel": { + "label": "Toegangsniveau", + "readOnly": "Alleen-lezen", + "readOnlyDescription": "Je kunt de sessie live volgen, maar je kunt niet typen of interageren.", + "readWrite": "Lezen en schrijven", + "readWriteDescription": "Kan typen en interactie hebben met de sessie, net als de eigenaar." + }, + "expiryLabel": "Link verloopt", + "createLinkButton": "Link maken", + "createShareButton": "Delen met gebruiker", + "searchUsersPlaceholder": "Gebruikers doorzoeken...", + "noUsersFound": "Geen gebruikers gevonden", + "linkCreated": "Deellink aangemaakt", + "linkCopied": "Link gekopieerd naar het klembord.", + "copyLink": "Kopieer link", + "shareCreated": "De sessie is gedeeld. Deze verschijnt in hun tabblad 'Verbindingen'.", + "shareFailed": "Aanmaken van de share is mislukt", + "userLacksHostAccess": "Die gebruiker heeft nog geen toegang tot deze host. Deel de host eerst met hem of haar, of gebruik een link.", + "activeShares": "Actieve aandelen", + "noActiveShares": "Geen actieve aandelen voor deze sessie.", + "revoke": "Herroepen", + "revokeConfirmTitle": "Wilt u dit aandeel intrekken?", + "revokeConfirmDescription": "Iedereen die deze gedeelde map gebruikt, verliest onmiddellijk de toegang.", + "revoked": "Aandeel ingetrokken", + "revokeFailed": "Het intrekken van het aandeel is mislukt.", + "joinCount": "{{count}} join", + "joinCount_other": "{{count}} sluit zich aan", + "expiresAt": "Verloopt {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "Gebruiker: {{username}}", + "loadSharesFailed": "Het laden van actieve aandelen is mislukt." }, "guacamole": { "connecting": "Verbinding maken met de {{type}} sessie...", @@ -1436,6 +1559,9 @@ "reconnect": "Opnieuw verbinding maken", "retry": "Opnieuw proberen", "guacdUnavailable": "De service voor extern bureaublad (guacd) is niet beschikbaar. Zorg ervoor dat guacd actief en toegankelijk is en correct is geconfigureerd in de beheerdersinstellingen.", + "credentialPromptTitle": "Voer de RDP-inloggegevens in.", + "credentialPromptDescription": "Deze host is zo ingesteld dat er bij het verbinden om inloggegevens wordt gevraagd. Deze worden alleen voor deze sessie gebruikt en niet opgeslagen.", + "connect": "Verbinden", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Herverbindingssessie", "collapse": "Werkbalk inklappen", "expand": "Werkbalk uitklappen", - "dragHandle": "Sleep om te verplaatsen" + "dragHandle": "Sleep om te verplaatsen", + "switchToTrackpad": "Schakel over naar de trackpadmodus (sleep om de cursor te verplaatsen, tik om te klikken).", + "switchToTouch": "Schakel over naar de aanraakmodus (tik direct op de plek waar u wilt klikken)." } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Tweefactorauthenticatie vereist", "totpCodeLabel": "Verificatiecode", "totpVerify": "Verifiëren", + "mfaPromptRequired": "Authenticatie vereist", + "mfaPushRequired": "Push-authenticatie vereist", + "mfaMenuPlaceholder": "Voer uw antwoord in", + "mfaWaitingApproval": "Wachten op goedkeuring op uw apparaat...", + "mfaSendRequest": "Verzoek verzenden", "warpgateAuthRequired": "Authenticatie via Warpgate vereist", "warpgateSecurityKey": "Beveiligingssleutel", "warpgateAuthUrl": "Authenticatie-URL", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Snelheid beperkt: Te veel verificatiepogingen. Probeer het later opnieuw.", "resetCodeRateLimitedWithTime": "Snelheid beperkt: Te veel verificatiepogingen. Wacht {{time}} seconden voordat u het opnieuw probeert.", "authTokenSaveFailed": "Het opslaan van het authenticatietoken is mislukt.", - "failedToLoadServer": "Server laden mislukt" + "failedToLoadServer": "Server laden mislukt", + "remoteServerRequired": "Externe server vereist. Verbind een externe server in de instellingen om dit verbindingstype te gebruiken." }, "messages": { "registrationDisabled": "Het registreren van nieuwe accounts is momenteel uitgeschakeld door een beheerder. Log in of neem contact op met een beheerder.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Het opslaan van de SSL-instellingen is mislukt.", "sslRequiresDomain": "Domein en e-mailadres zijn verplicht.", "sslInfoNote": "Nadat u een certificaat hebt uitgegeven, schakelt u SSL in uw omgevingsvariabelen in (ENABLE_SSL=true) en herstart u Termix.", + "sslManualOption": "Handmatig (certificaat uploaden)", + "sslManualCert": "Certificaat (PEM)", + "sslManualCertPlaceholder": "-----BEGIN CERTIFICAAT-----", + "sslManualKey": "Privésleutel (PEM)", + "sslManualKeyPlaceholder": "-----BEGIN PRIVÉSLEUTEL-----", + "sslManualDesc": "Plak hier uw bestaande certificaat en privésleutel, inclusief de volledige certificaatketen indien vereist door uw certificeringsinstantie.", + "sslManualUpload": "Certificaat uploaden en installeren", + "sslManualUploadLoading": "Certificaat uploaden...", + "sslManualUploadSuccess": "Certificaat succesvol geüpload en geïnstalleerd", + "sslManualUploadFailed": "Het uploaden van het certificaat is mislukt.", + "sslManualRequiresFields": "Certificaat en privésleutel vereist.", "auditLogTotal": "{{total}} totaal aantal vermeldingen", "auditLogEmpty": "Geen auditlogboekvermeldingen gevonden", "auditLogSuccess": "Succes", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Commandogeschiedenis", "commandHistoryEnabledDesc": "Sta het vastleggen van de opdrachtgeschiedenis toe. Indien uitgeschakeld, wordt de geschiedenis niet opgeslagen, ongeacht de instellingen per host.", "updateCommandHistoryFailed": "Het bijwerken van de instelling voor de opdrachtgeschiedenis is mislukt.", + "analyticsEnabled": "Deel anonieme gebruiksstatistieken", + "analyticsEnabledDesc": "Verstuurt dagelijks anoniem een telling van gebruikers, hosts en functiegebruik om Termix te verbeteren. Er worden nooit persoonlijke gegevens of verbindingsdetails meegestuurd.", + "updateAnalyticsFailed": "Het bijwerken van de analyse-instellingen is mislukt.", + "sessionSharingGloballyEnabled": "Sessiedeling toestaan", + "sessionSharingGloballyEnabledDesc": "Sta toe dat live terminal-, RDP-, VNC- en Telnet-sessies instantiebreed worden gedeeld. Deze functie overschrijft elke instelling voor delen per host wanneer deze is uitgeschakeld.", + "updateSessionSharingFailed": "Het bijwerken van de instelling voor het delen van sessies is mislukt.", "sessionTimeout": "Sessietime-out", "hours": "uren", "sessionTimeoutRange": "Minimaal 1 uur · Maximaal 720 uur", @@ -3119,6 +3270,47 @@ "executionSuccess": "Succes", "executionFailed": "Mislukt" }, + "keybindings": { + "title": "Toetsenbord sneltoetsen", + "description": "Pas de sneltoetsen voor kopiëren, plakken en besturing in de terminal aan, of koppel toetsen aan het verzenden van tekst of het uitvoeren van een codefragment.", + "defaultsHeading": "Ingebouwde sneltoetsen", + "customHeading": "Aangepaste sneltoetsen", + "addBinding": "Snelkoppeling toevoegen", + "addBindingTitle": "Snelkoppeling toevoegen", + "editBindingTitle": "Snelkoppeling bewerken", + "loading": "Laden...", + "noCustomBindings": "Er zijn nog geen aangepaste sneltoetsen beschikbaar.", + "defaultBadge": "Standaard", + "customizedBadge": "Op maat gemaakt", + "customize": "Aanpassen", + "resetToDefault": "Terugzetten naar standaardinstellingen", + "resetAllToDefaults": "Alles terugzetten naar de standaardinstellingen", + "close": "Dichtbij", + "cancel": "Annuleren", + "saveBinding": "Snelkoppeling opslaan", + "saveError": "Het opslaan van de sneltoetsen is mislukt.", + "pressKeysToRecord": "Toetsencombinatie", + "pressKeysPlaceholder": "Klik en druk op de toetsen", + "recording": "Druk op de toetsen...", + "comboRequiredError": "Druk eerst op een toetsencombinatie.", + "textRequiredError": "Voer de tekst in die u wilt verzenden", + "controlCodeRequiredError": "Voer één letter in als besturingscode.", + "snippetRequiredError": "Selecteer een fragment", + "conflictWarning": "Deze combinatie is al gekoppeld aan {{combo}}. Door op te slaan zullen beide vuren.", + "actionLabel": "Actie", + "actionCopy": "Selectie kopiëren", + "actionPaste": "Plakken", + "actionSendControlCode": "Verzend het Ctrl+letter-signaal", + "actionSendText": "Sms verzenden", + "actionRunSnippet": "Voer het bestaande codefragment uit.", + "controlCodeLabel": "Brief", + "textLabel": "Tekst om te verzenden", + "appendEnterLabel": "Druk op Enter na het verzenden.", + "snippetLabel": "Fragment", + "selectSnippetPlaceholder": "Selecteer een fragment", + "orphanedSnippetWarning": "fragment niet gevonden", + "clipboardPermissionNote": "In sommige browsers kan bij het eerste gebruik om toestemming voor het gebruik van het klembord worden gevraagd." + }, "userProfile": { "donateTitle": "Houd Termix in leven", "donateDescription": "Termix is gratis, zelf te hosten en is ontwikkeld door slechts een paar mensen in hun vrije tijd. Als het je tijd of geld heeft bespaard, helpt een cryptodonatie om het project draaiende te houden.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Verschijning", "sectionSecurity": "Beveiliging", "sectionApiKeys": "API-sleutels", + "sectionData": "Gegevens", "sectionC2sTunnels": "C2S-tunnels", "usernameLabel": "Gebruikersnaam", "roleLabel": "Rol", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Opdracht automatisch aanvullen", "commandAutocompleteDesc": "Toon automatische aanvulling tijdens het typen", + "keyboardShortcuts": "Toetsenbord sneltoetsen", + "keyboardShortcutsDescription": "Pas de sneltoetsen voor kopiëren, plakken en opdrachten in de terminal aan.", + "manageShortcuts": "Beheren", "terminalLinkBehavior": "Terminallink Klik", "terminalLinkBehaviorDesc": "Standaardgedrag bij het klikken op links in de terminal", "historyTracking": "Geschiedenis bijhouden", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Voeg uw sleutel toe aan de", "apiKeyUsageHintHeader": "koptekst.", "apiKeyPermissionsHint": "Sleutels erven de machtigingen van de gebruiker die ze heeft aangemaakt.", + "exportData": "Mijn gegevens exporteren", + "exportDataDesc": "Download een back-up van uw hosts, inloggegevens en instellingen om deze over te zetten naar een ander apparaat.", + "export": "Exporteren", + "exporting": "Exporteren...", + "importData": "Mijn gegevens importeren", + "importDataDesc": "Herstel uw hosts, inloggegevens en instellingen vanuit een .sqlite-back-upbestand.", + "importDataSelected": "Geselecteerd: {{name}}", + "selectFile": "Selecteer bestand", + "changeFile": "Wijziging", + "import": "Importeren", + "importing": "Importeren...", + "exportSuccess": "Gegevens succesvol geëxporteerd", + "exportFailed": "Gegevensexport mislukt", + "importSelectFile": "Selecteer eerst een bestand.", + "importCompleted": "Import voltooid: {{total}} items geïmporteerd, {{skipped}} overgeslagen", + "importFailed": "Import mislukt: {{error}}", "roleUser": "Gebruiker", "authMethodDual": "Dubbele authenticatie", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Plakken", "editQuickKeys": "Sneltoetsen bewerken", "quickKeysTitle": "Sneltoetsen", "quickKeysDesc": "Tik op × om te verwijderen. Ondersteunt maximaal 8 tekens.", diff --git a/src/ui/locales/translated/no_NO.json b/src/ui/locales/translated/no_NO.json index bf625ac5..b58ed134 100644 --- a/src/ui/locales/translated/no_NO.json +++ b/src/ui/locales/translated/no_NO.json @@ -389,6 +389,45 @@ "noSavedServers": "Ingen lagrede servere", "removeServer": "Fjerne" }, + "migrationNotice": { + "title": "Termix Desktop kjører nå frittstående", + "body1": "Denne appen pleide å koble seg direkte til Termix-serveren din. Den har blitt omarbeidet til å kjøre helt alene, og lagrer verter og påloggingsinformasjon lokalt, slik at den fortsetter å fungere offline. Toveis synkronisering med en selvhostet Termix-server er nå valgfritt.", + "body2": "Dine eksisterende verter og påloggingsinformasjon er fortsatt på {{url}}. Slå på ekstern synkronisering og koble til den serveren på nytt for å få dem tilbake og holde begge synkroniserte fremover.", + "dismiss": "Ikke nå", + "setUpSync": "Konfigurer ekstern synkronisering" + }, + "remoteSync": { + "title": "Fjernsynkronisering", + "description": "Du kan eventuelt koble denne skrivebordsappen til en selvhostet Termix-server for å synkronisere verter, påloggingsinformasjon og kodebiter på tvers av enheter. Appen fungerer alltid helt offline, enten du kobler til eller ikke.", + "notConnected": "Ikke tilkoblet", + "connected": "Tilkoblet", + "connectedTo": "Koblet til {{url}}", + "lastSynced": "Sist synkronisert {{time}}", + "neverSynced": "Aldri synkronisert", + "syncError": "Synkroniseringsfeil: {{message}}", + "needsReauth": "Påloggingen er utløpt", + "connectButton": "Koble til server", + "disconnectButton": "Frakople", + "syncNowButton": "Synkroniser nå", + "syncing": "Synkroniserer...", + "serverUrl": "Server-URL", + "enterServerUrl": "Vennligst skriv inn en server-URL", + "mustIncludeProtocol": "Server-URL-en må starte med http:// eller https://", + "connectionTestFailed": "Kunne ikke nå en Termix-server på den URL-adressen", + "allowInvalidCertificate": "Tillat ugyldig sertifikat", + "allowInvalidCertificateDesc": "Bruk kun for klarerte, selvhostede servere med selvsignerte sertifikater eller IP-adressesertifikater.", + "savedServers": "Lagrede servere", + "removeServer": "Fjerne", + "continueButton": "Fortsette", + "cancelButton": "Kansellere", + "signInTitle": "Logg inn på {{url}}", + "originTitle": "Tilkoblingens opprinnelse", + "originDescription": "Velg hvor SSH-tilkoblinger kommer fra som standard. Dette kan overstyres per vert.", + "originLocal": "Denne enheten (lokalt nettverk)", + "originRemote": "Ekstern server", + "bannerReconnect": "Koble til igjen", + "bannerMessage": "Ekstern synkronisering trenger ny autentisering" + }, "versionCheck": { "error": "Versjonssjekkfeil", "checkFailed": "Kunne ikke sjekke etter oppdateringer", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Overstyr brukernavn for legitimasjon", "overrideCredentialUsernameDesc": "Bruk brukernavnet som er spesifisert ovenfor i stedet for brukernavnet til legitimasjonen", "oidcUsernameHint": "Bruk $oidc.preferred_username for å erstatte OIDC-påloggingsnavnet ditt.", + "tailscaleUsernameHint": "Dette må være en Unix-bruker som Tailscale-identiteten din er gitt i tailnets SSH ACL, ikke nødvendigvis root.", "jumpHostChain": "Hoppvertkjede", "portKnocking": "Port Knocking", "addKnock": "Legg til port", @@ -650,6 +690,11 @@ "delayAfterMs": "Forsinkelse etter (ms)", "useSocks5Proxy": "Bruk SOCKS5-proxyen", "useSocks5ProxyDesc": "Rute tilkoblingen via en proxy-server", + "connectionOrigin": "Tilkoblingens opprinnelse", + "connectionOriginDesc": "Hvor denne vertens SSH-tilkobling kommer fra. Overstyrer skrivebordsappens globale standard.", + "connectionOriginDefault": "Bruk standard", + "connectionOriginLocal": "Denne enheten (lokalt nettverk)", + "connectionOriginRemote": "Ekstern server", "proxyHost": "Proxy-vert", "proxyPort": "Proxy-port", "proxyUsername": "Proxy-brukernavn", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Lys cyan", "customThemeBrightWhite": "Klar hvit", "customThemeResetTooltip": "Tilbakestill til standardinnstillinger", + "savedThemesLabel": "Lagrede temaer", + "saveAsGlobalTheme": "Lagre som globalt tema", + "saveGlobalThemeNamePrompt": "Skriv inn et navn for dette temaet", + "saveGlobalThemeSuccess": "Tema lagret", + "saveGlobalThemeError": "Kunne ikke lagre temaet", + "applyGlobalThemeTooltip": "Bruk dette temaet", + "deleteGlobalThemeTooltip": "Slett dette temaet", + "noSavedThemes": "Ingen lagrede temaer ennå", "syntaxHighlightingLabel": "Syntaksutheving", "syntaxHighlightingDesc": "Fargelegg terminalutdata (feil, stier, IP-adresser, tidsstempler)", "syntaxHighlightingCategories": "Fremhev kategorier", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Start eller legg til tmux-økten automatisk", "enableSessionLogging": "Øktlogging", "enableSessionLoggingDesc": "Ta opp utdata fra terminaløkten for senere gjennomgang", + "allowSessionSharing": "Tillat øktdeling", + "allowSessionSharingDesc": "La direktesendte økter på denne verten deles via lenke eller med andre brukere", "enableCommandHistory": "Kommandohistorikk", "enableCommandHistoryDesc": "Record-kommandoer kjøres i denne terminalen for historikk og autofullføring", "linkClickBehaviorLabel": "Klikkatferd på lenker", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importert", "proxmoxResultUpdated": "{{count}} oppdatert", "proxmoxResultFailed": "{{count}} mislyktes", + "proxmoxResultSkippedNoIp": "{{count}} hoppet over (ingen IP-adresse funnet)", "proxmoxImportComplete": "Proxmox-import fullført: {{summary}}", "proxmoxDiscoveryFailed": "Oppdagelsen mislyktes", "proxmoxImportFailed": "Importen mislyktes", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Kunne ikke lagre legitimasjonen", "credentialNameRequired": "Vennligst skriv inn et navn for legitimasjonen", "credentialAuthRequired": "Legg til et passord, en SSH-nøkkel eller begge deler", + "createCredentialFromHostBtn": "Opprett legitimasjon", + "createCredentialFromHostTitle": "Opprett legitimasjon fra vert", + "createCredentialFromHostDesc": "Opprett en gjenbrukbar, delbar legitimasjonsoppføring forhåndsutfylt med denne vertens nåværende brukernavn, passord og/eller SSH-nøkkel.", "backToHosts": "Tilbake til verter", "backToCredentials": "Tilbake til legitimasjon", "pinned": "Festet", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Bruk / til å skille nivåer og opprette nestede mapper.", "folderColor": "Farge", "folderIcon": "Ikon", + "folderCredential": "Legitimasjon", + "folderCredentialNone": "Ingen legitimasjon tildelt", + "folderCredentialHint": "Verter i denne mappen som bruker autentiseringen «Lagret legitimasjon» uten å ha valgt sin egen legitimasjon, arver denne.", "folderPreview": "Forhåndsvisning", "folderNameFallback": "Mappe uten navn", "createFolderButton": "Opprett mappe", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tagger", "shareHost": "Del vert", "shareHostTitle": "Del: {{name}}", + "shareFolder": "Del mappe", + "shareFolderTitle": "Del mappe: {{name}}", + "folderSharedSuccessfully": "Delte {{count}} verter i mappen", + "failedToShareFolder": "Kunne ikke dele mappen", "sharing": { "loadError": "Kunne ikke laste inn delingsdataene. Prøv på nytt.", "shareWithSection": "Del med", @@ -1221,6 +1287,7 @@ "shareWithCount": "Del ({{count}})", "currentAccess": "Nåværende tilgang", "noAccessEntries": "Denne verten har ikke blitt delt ennå", + "folderShareSummary": "Delte {{shared}} av {{total}} verter i denne mappen", "grantedBy": "Tildelt av", "expires": "Utløper", "expired": "Utløpt", @@ -1242,6 +1309,8 @@ "authMethod": "Autentiseringsmetode", "authTypeDirect": "Direkte", "authTypeCredential": "Legitimasjon", + "authTypeNone": "Ingen", + "authTypeNoneDesc": "Ingen påloggingsinformasjon lagres. Du blir bedt om å oppgi brukernavn og passord hver gang du kobler til; de lagres ikke.", "selectCredential": "Velg en legitimasjon...", "connectionSettings": "Tilkoblingsinnstillinger", "displaySettings": "Skjerminnstillinger", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-port", "username": "Brukernavn", "password": "Passord", + "passwordSaved": "Passord lagret, skriv inn for å endre", "domain": "Domene", "securityMode": "Sikkerhetsmodus", "colorDepth": "Fargedybde", @@ -1424,7 +1494,60 @@ "expiresIn": "Utløper om {{duration}}", "search": "Søk etter tilkoblinger...", "noSearchResults": "Ingen forbindelser samsvarer med søket ditt", - "rename": "Gi nytt navn til økten" + "rename": "Gi nytt navn til økten", + "sectionSharedWithMe": "Delt med meg", + "sharedBy": "Delt av {{username}}", + "join": "Bli med", + "sharedSessionLabel": "{{hostName}} (delt)" + }, + "sessionSharing": { + "guestView": { + "loading": "Kobler til delt økt...", + "linkInvalid": "Denne delingslenken er ugyldig, utløpt eller har blitt tilbakekalt", + "rateLimited": "For mange forsøk, prøv igjen snart", + "sessionEnded": "Denne økten er avsluttet", + "readOnlyBadge": "Kun visning" + }, + "modalTitle": "Del økt", + "shareButton": "Dele", + "notReadyToShare": "Økten er ikke klar til deling ennå", + "modeTab": { + "link": "Lenke", + "user": "Bruker" + }, + "linkModeDescription": "Alle med denne lenken kan bli med, ingen konto kreves.", + "userModeDescription": "Del med en bestemt bruker som allerede har tilgang til denne verten. Hvis de ikke har tilgang ennå, del verten med dem først eller bruk en lenke i stedet. Når økten er delt, vises den i deres Tilkoblinger-fane.", + "permissionLevel": { + "label": "Tillatelsesnivå", + "readOnly": "Skrivebeskyttet", + "readOnlyDescription": "Kan se økten direkte, men kan ikke skrive eller samhandle.", + "readWrite": "Lese-skrive", + "readWriteDescription": "Kan skrive og samhandle med økten akkurat som eieren." + }, + "expiryLabel": "Utløpsdato for lenke", + "createLinkButton": "Opprett lenke", + "createShareButton": "Del med brukeren", + "searchUsersPlaceholder": "Søk etter brukere...", + "noUsersFound": "Ingen brukere funnet", + "linkCreated": "Delingslenke opprettet", + "linkCopied": "Lenke kopiert til utklippstavlen", + "copyLink": "Kopier lenke", + "shareCreated": "Økten er delt. Den vil vises i Tilkoblinger-fanen deres.", + "shareFailed": "Kunne ikke opprette delingen", + "userLacksHostAccess": "Den brukeren har ikke tilgang til denne verten ennå. Del verten med dem først, eller bruk en lenke i stedet.", + "activeShares": "Aktive aksjer", + "noActiveShares": "Ingen aktive delinger for denne økten", + "revoke": "Tilbakekalling", + "revokeConfirmTitle": "Tilbakekalle denne delingen?", + "revokeConfirmDescription": "Alle som bruker denne delingen vil miste tilgangen umiddelbart.", + "revoked": "Deling tilbakekalt", + "revokeFailed": "Kunne ikke tilbakekalle delingen", + "joinCount": "{{count}} bli med", + "joinCount_other": "{{count}} blir med", + "expiresAt": "Utløper {{date}}", + "linkShareBadge": "Lenke", + "userShareBadge": "Bruker: {{username}}", + "loadSharesFailed": "Kunne ikke laste inn aktive delinger" }, "guacamole": { "connecting": "Kobler til {{type}} økt...", @@ -1436,6 +1559,9 @@ "reconnect": "Koble til igjen", "retry": "Prøv på nytt", "guacdUnavailable": "Ekstern skrivebordstjeneste (guacd) er ikke tilgjengelig. Sørg for at guacd kjører, er tilgjengelig og riktig konfigurert i administratorinnstillingene.", + "credentialPromptTitle": "Skriv inn RDP-legitimasjon", + "credentialPromptDescription": "Denne verten er satt til å be om påloggingsinformasjon ved tilkobling. Disse brukes kun for denne økten og lagres ikke.", + "connect": "Koble til", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Koble til økten på nytt", "collapse": "Skjul verktøylinjen", "expand": "Utvid verktøylinjen", - "dragHandle": "Dra for å flytte" + "dragHandle": "Dra for å flytte", + "switchToTrackpad": "Bytt til styreflatemodus (dra for å flytte markøren, trykk for å klikke)", + "switchToTouch": "Bytt til berøringsmodus (trykk direkte der du vil klikke)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Tofaktorautentisering kreves", "totpCodeLabel": "Verifiseringskode", "totpVerify": "Verifisere", + "mfaPromptRequired": "Autentisering kreves", + "mfaPushRequired": "Push-autentisering kreves", + "mfaMenuPlaceholder": "Skriv inn svaret ditt", + "mfaWaitingApproval": "Venter på godkjenning på enheten din ...", + "mfaSendRequest": "Send forespørsel", "warpgateAuthRequired": "Warpgate-autentisering kreves", "warpgateSecurityKey": "Sikkerhetsnøkkel", "warpgateAuthUrl": "Autentiserings-URL", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Frekvens begrenset: For mange bekreftelsesforsøk. Prøv igjen senere.", "resetCodeRateLimitedWithTime": "Frekvens begrenset: For mange bekreftelsesforsøk. Vent {{time}} sekunder før du prøver på nytt.", "authTokenSaveFailed": "Kunne ikke lagre autentiseringstokenet", - "failedToLoadServer": "Kunne ikke laste inn serveren" + "failedToLoadServer": "Kunne ikke laste inn serveren", + "remoteServerRequired": "Ekstern server kreves. Koble til en ekstern server i Innstillinger for å bruke denne tilkoblingstypen." }, "messages": { "registrationDisabled": "Registrering av ny konto er for øyeblikket deaktivert av en administrator. Vennligst logg inn eller kontakt en administrator.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Kunne ikke lagre SSL-innstillingene", "sslRequiresDomain": "Domene og e-post er obligatorisk", "sslInfoNote": "Etter at du har utstedt et sertifikat, aktiver SSL i miljøvariablene dine (ENABLE_SSL=true) og start Termix på nytt.", + "sslManualOption": "Manual (last opp sertifikat)", + "sslManualCert": "Sertifikat (PEM)", + "sslManualCertPlaceholder": "-----STARTSERTIFIKAT-----", + "sslManualKey": "Privat nøkkel (PEM)", + "sslManualKeyPlaceholder": "-----START PRIVAT NØKKEL-----", + "sslManualDesc": "Lim inn ditt eksisterende sertifikat og din private nøkkel, inkludert en fullstendig kjede hvis det kreves av CA-en din.", + "sslManualUpload": "Last opp og installer sertifikat", + "sslManualUploadLoading": "Laster opp sertifikat...", + "sslManualUploadSuccess": "Sertifikatet er lastet opp og installert", + "sslManualUploadFailed": "Opplasting av sertifikat mislyktes", + "sslManualRequiresFields": "Sertifikat og privat nøkkel kreves", "auditLogTotal": "{{total}} totalt antall oppføringer", "auditLogEmpty": "Ingen oppføringer i revisjonsloggen funnet", "auditLogSuccess": "Suksess", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Kommandohistorikk", "commandHistoryEnabledDesc": "Tillat opptak av kommandohistorikk. Når den er deaktivert, lagres ikke historikken uavhengig av innstillinger per vert.", "updateCommandHistoryFailed": "Kunne ikke oppdatere innstillingen for kommandohistorikk", + "analyticsEnabled": "Del anonym bruksstatistikk", + "analyticsEnabledDesc": "Sender en anonym daglig telling av brukere, verter og funksjonsbruk for å forbedre Termix. Ingen personopplysninger eller tilkoblingsdetaljer inkluderes.", + "updateAnalyticsFailed": "Kunne ikke oppdatere analyseinnstillingen", + "sessionSharingGloballyEnabled": "Tillat øktdeling", + "sessionSharingGloballyEnabledDesc": "Tillat deling av Live Terminal-, RDP-, VNC- og Telnet-økter på tvers av hele instansen. Overstyrer alle delingsalternativer per vert når de er deaktivert.", + "updateSessionSharingFailed": "Kunne ikke oppdatere innstillingen for deling av økt", "sessionTimeout": "Tidsavbrudd for økt", "hours": "timer", "sessionTimeoutRange": "Min. 1 t · Maks. 720 t", @@ -3119,6 +3270,47 @@ "executionSuccess": "Suksess", "executionFailed": "Mislyktes" }, + "keybindings": { + "title": "Tastatursnarveier", + "description": "Tilpass snarveier for terminalkopiering, liming og kontroll, eller bind taster for å sende tekst eller kjøre et tekstutdrag.", + "defaultsHeading": "Innebygde snarveier", + "customHeading": "Tilpassede snarveier", + "addBinding": "Legg til snarvei", + "addBindingTitle": "Legg til snarvei", + "editBindingTitle": "Rediger snarvei", + "loading": "Laster inn...", + "noCustomBindings": "Ingen egendefinerte snarveier ennå", + "defaultBadge": "Misligholde", + "customizedBadge": "Tilpasset", + "customize": "Tilpass", + "resetToDefault": "Tilbakestill til standard", + "resetAllToDefaults": "Tilbakestill alle til standardinnstillinger", + "close": "Lukke", + "cancel": "Kansellere", + "saveBinding": "Lagre snarvei", + "saveError": "Kunne ikke lagre hurtigtaster", + "pressKeysToRecord": "Tastekombinasjon", + "pressKeysPlaceholder": "Klikk og trykk på tastene", + "recording": "Trykk på tastene...", + "comboRequiredError": "Trykk først på en tastekombinasjon", + "textRequiredError": "Skriv inn teksten som skal sendes", + "controlCodeRequiredError": "Skriv inn én bokstav for kontrollkoden", + "snippetRequiredError": "Velg et utdrag", + "conflictWarning": "Denne kombinasjonen er allerede bundet til {{combo}}. Lagring vil få begge til å aktiveres.", + "actionLabel": "Handling", + "actionCopy": "Kopier valg", + "actionPaste": "Lim inn", + "actionSendControlCode": "Send Ctrl+bokstavsignal", + "actionSendText": "Send tekstmelding", + "actionRunSnippet": "Kjør eksisterende kodebit", + "controlCodeLabel": "Brev", + "textLabel": "Tekst som skal sendes", + "appendEnterLabel": "Trykk Enter etter sending", + "snippetLabel": "Utdrag", + "selectSnippetPlaceholder": "Velg et utdrag", + "orphanedSnippetWarning": "utdraget ikke funnet", + "clipboardPermissionNote": "Kan be om tillatelse til utklippstavlen ved første gangs bruk i noen nettlesere." + }, "userProfile": { "donateTitle": "Hold Termix i live", "donateDescription": "Termix er gratis, selvhostet og bygget av bare noen få personer på fritiden. Hvis det har spart deg tid eller penger, bidrar en kryptodonasjon til å holde det gående.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Utseende", "sectionSecurity": "Sikkerhet", "sectionApiKeys": "API-nøkler", + "sectionData": "Data", "sectionC2sTunnels": "C2S-tunneler", "usernameLabel": "Brukernavn", "roleLabel": "Rolle", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Kommando Autofullfør", "commandAutocompleteDesc": "Vis autofullføring mens du skriver", + "keyboardShortcuts": "Tastatursnarveier", + "keyboardShortcutsDescription": "Tilpass snarveier for terminalkopiering, -liming og -kommandoer", + "manageShortcuts": "Administrer", "terminalLinkBehavior": "Klikk på terminallenken", "terminalLinkBehaviorDesc": "Standardoppførsel når du klikker på lenker i terminalen", "historyTracking": "Historikksporing", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Ta med nøkkelen din i", "apiKeyUsageHintHeader": "overskrift.", "apiKeyPermissionsHint": "Nøkler arver tillatelsene til den opprettende brukeren.", + "exportData": "Eksporter dataene mine", + "exportDataDesc": "Last ned en sikkerhetskopi av vertene, påloggingsinformasjonen og innstillingene dine for å overføre dem til en annen enhet.", + "export": "Eksport", + "exporting": "Eksporterer...", + "importData": "Importer dataene mine", + "importDataDesc": "Gjenopprett verter, påloggingsinformasjon og innstillinger fra en .sqlite-sikkerhetskopifil", + "importDataSelected": "Valgt: {{name}}", + "selectFile": "Velg fil", + "changeFile": "Endre", + "import": "Import", + "importing": "Importerer...", + "exportSuccess": "Dataene ble eksportert", + "exportFailed": "Dataeksport mislyktes", + "importSelectFile": "Vennligst velg en fil først", + "importCompleted": "Import fullført: {{total}} elementer importert, {{skipped}} hoppet over", + "importFailed": "Importen mislyktes: {{error}}", "roleUser": "Bruker", "authMethodDual": "Dobbel autentisering", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Lim inn", "editQuickKeys": "Rediger hurtigtaster", "quickKeysTitle": "Hurtigtaster", "quickKeysDesc": "Trykk på × for å fjerne. Støtter opptil 8 tegn.", diff --git a/src/ui/locales/translated/pl_PL.json b/src/ui/locales/translated/pl_PL.json index c784e101..5309fbdd 100644 --- a/src/ui/locales/translated/pl_PL.json +++ b/src/ui/locales/translated/pl_PL.json @@ -389,6 +389,45 @@ "noSavedServers": "Brak zapisanych serwerów", "removeServer": "Usunąć" }, + "migrationNotice": { + "title": "Termix Desktop działa teraz autonomicznie", + "body1": "Ta aplikacja łączyła się bezpośrednio z serwerem Termix. Została przeprojektowana tak, aby działać w pełni samodzielnie, przechowując hosty i dane uwierzytelniające lokalnie, dzięki czemu działa w trybie offline. Dwukierunkowa synchronizacja z samodzielnie hostowanym serwerem Termix jest teraz opcjonalna.", + "body2": "Twoje obecne hosty i dane uwierzytelniające są nadal na {{url}}. Włącz synchronizację zdalną i ponownie połącz się z tym serwerem, aby je przywrócić i zapewnić synchronizację w przyszłości.", + "dismiss": "Nie teraz", + "setUpSync": "Skonfiguruj synchronizację zdalną" + }, + "remoteSync": { + "title": "Zdalna synchronizacja", + "description": "Opcjonalnie połącz tę aplikację desktopową z samodzielnie hostowanym serwerem Termix, aby synchronizować hosty, dane uwierzytelniające i fragmenty kodu na różnych urządzeniach. Aplikacja zawsze działa w pełni offline, niezależnie od tego, czy masz połączenie z siecią, czy nie.", + "notConnected": "Nie połączony", + "connected": "Połączony", + "connectedTo": "Połączono z {{url}}", + "lastSynced": "Ostatnia synchronizacja {{time}}", + "neverSynced": "Nigdy nie zsynchronizowane", + "syncError": "Błąd synchronizacji: {{message}}", + "needsReauth": "Logowanie wygasło", + "connectButton": "Połącz się z serwerem", + "disconnectButton": "Odłączyć", + "syncNowButton": "Synchronizuj teraz", + "syncing": "Synchronizacja...", + "serverUrl": "Adres URL serwera", + "enterServerUrl": "Proszę podać adres URL serwera", + "mustIncludeProtocol": "Adres URL serwera musi zaczynać się od http:// lub https://", + "connectionTestFailed": "Nie można nawiązać połączenia z serwerem Termix pod tym adresem URL", + "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.", + "savedServers": "Zapisane serwery", + "removeServer": "Usunąć", + "continueButton": "Kontynuować", + "cancelButton": "Anulować", + "signInTitle": "Zaloguj się do {{url}}", + "originTitle": "Pochodzenie połączenia", + "originDescription": "Wybierz domyślne źródło połączeń SSH. Można to zmienić dla każdego hosta.", + "originLocal": "To urządzenie (sieć lokalna)", + "originRemote": "Serwer zdalny", + "bannerReconnect": "Na nowo połączyć", + "bannerMessage": "Zdalna synchronizacja wymaga ponownego uwierzytelnienia" + }, "versionCheck": { "error": "Błąd sprawdzania wersji", "checkFailed": "Nie udało się sprawdzić dostępności aktualizacji", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Zastąp nazwę użytkownika poświadczeń", "overrideCredentialUsernameDesc": "Użyj nazwy użytkownika określonej powyżej zamiast nazwy użytkownika podanej w poświadczeniu", "oidcUsernameHint": "Użyj $oidc.preferred_username, aby zastąpić swoją nazwę logowania OIDC.", + "tailscaleUsernameHint": "Musi to być użytkownik systemu Unix, któremu Twoja tożsamość Tailscale została przypisana na liście kontroli dostępu SSH sieci tailnet, niekoniecznie musi to być użytkownik root.", "jumpHostChain": "Łańcuch hosta skoku", "portKnocking": "Port Knocking", "addKnock": "Dodaj port", @@ -650,6 +690,11 @@ "delayAfterMs": "Opóźnienie po (ms)", "useSocks5Proxy": "Użyj serwera proxy SOCKS5", "useSocks5ProxyDesc": "Przekieruj połączenie przez serwer proxy", + "connectionOrigin": "Pochodzenie połączenia", + "connectionOriginDesc": "Skąd pochodzi połączenie SSH tego hosta. Zastępuje domyślne globalne ustawienie aplikacji na komputerze.", + "connectionOriginDefault": "Użyj domyślnego", + "connectionOriginLocal": "To urządzenie (sieć lokalna)", + "connectionOriginRemote": "Serwer zdalny", "proxyHost": "Host proxy", "proxyPort": "Port proxy", "proxyUsername": "Nazwa użytkownika proxy", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Jasny cyjan", "customThemeBrightWhite": "Jasna biel", "customThemeResetTooltip": "Przywróć ustawienia domyślne", + "savedThemesLabel": "Zapisane motywy", + "saveAsGlobalTheme": "Zapisz jako motyw globalny", + "saveGlobalThemeNamePrompt": "Wprowadź nazwę tego motywu", + "saveGlobalThemeSuccess": "Motyw zapisany", + "saveGlobalThemeError": "Nie udało się zapisać motywu", + "applyGlobalThemeTooltip": "Zastosuj ten motyw", + "deleteGlobalThemeTooltip": "Usuń ten motyw", + "noSavedThemes": "Brak zapisanych motywów", "syntaxHighlightingLabel": "Podświetlanie składni", "syntaxHighlightingDesc": "Kolorowanie danych wyjściowych terminala (błędy, ścieżki, adresy IP, znaczniki czasu)", "syntaxHighlightingCategories": "Podświetl kategorie", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Automatyczne uruchamianie lub dołączanie do sesji tmux", "enableSessionLogging": "Rejestrowanie sesji", "enableSessionLoggingDesc": "Rejestruj dane wyjściowe sesji terminalowej w celu późniejszego przejrzenia", + "allowSessionSharing": "Zezwól na udostępnianie sesji", + "allowSessionSharingDesc": "Zezwól na udostępnianie sesji na żywo na tym hoście za pośrednictwem łącza lub innym użytkownikom", "enableCommandHistory": "Historia dowodzenia", "enableCommandHistoryDesc": "Rejestruj polecenia uruchamiane w tym terminalu w celu historii i automatycznego uzupełniania", "linkClickBehaviorLabel": "Zachowanie kliknięcia linku", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} zaimportowano", "proxmoxResultUpdated": "{{count}} zaktualizowano", "proxmoxResultFailed": "{{count}} nie powiodło się", + "proxmoxResultSkippedNoIp": "{{count}} pominięto (nie znaleziono adresu IP)", "proxmoxImportComplete": "Zakończono importowanie Proxmox: {{summary}}", "proxmoxDiscoveryFailed": "Odkrycie nie powiodło się", "proxmoxImportFailed": "Import nie powiódł się", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Nie udało się zapisać danych uwierzytelniających", "credentialNameRequired": "Proszę podać nazwę poświadczenia", "credentialAuthRequired": "Dodaj hasło, klucz SSH lub oba", + "createCredentialFromHostBtn": "Utwórz poświadczenia", + "createCredentialFromHostTitle": "Utwórz poświadczenia z hosta", + "createCredentialFromHostDesc": "Utwórz wpis danych uwierzytelniających, który można wielokrotnie wykorzystywać i udostępniać, wstępnie wypełniony bieżącą nazwą użytkownika, hasłem i/lub kluczem SSH tego hosta.", "backToHosts": "Powrót do Gospodarzy", "backToCredentials": "Powrót do danych uwierzytelniających", "pinned": "Przypięte", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Użyj / do oddzielenia poziomów i utworzenia zagnieżdżonych folderów.", "folderColor": "Kolor", "folderIcon": "Ikona", + "folderCredential": "Mandat", + "folderCredentialNone": "Brak przypisanych danych uwierzytelniających", + "folderCredentialHint": "Hosty w tym folderze korzystające z uwierzytelniania „Zapisane dane uwierzytelniające” bez wybrania własnych danych uwierzytelniających odziedziczą to dane.", "folderPreview": "Zapowiedź", "folderNameFallback": "Folder bez tytułu", "createFolderButton": "Utwórz folder", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Tagi", "shareHost": "Udostępnij hosta", "shareHostTitle": "Udostępnij: {{name}}", + "shareFolder": "Udostępnij folder", + "shareFolderTitle": "Udostępnij folder: {{name}}", + "folderSharedSuccessfully": "Udostępniono {{count}} hostów w folderze", + "failedToShareFolder": "Nie udało się udostępnić folderu", "sharing": { "loadError": "Nie udało się załadować udostępnianych danych. Spróbuj ponownie.", "shareWithSection": "Podziel się z", @@ -1221,6 +1287,7 @@ "shareWithCount": "Udostępnij ({{count}})", "currentAccess": "Aktualny dostęp", "noAccessEntries": "Ten host nie został jeszcze udostępniony", + "folderShareSummary": "Udostępniono {{shared}} z {{total}} hostów w tym folderze", "grantedBy": "Przyznane przez", "expires": "Wygasa", "expired": "Wygasły", @@ -1242,6 +1309,8 @@ "authMethod": "Metoda uwierzytelniania", "authTypeDirect": "Bezpośredni", "authTypeCredential": "Mandat", + "authTypeNone": "Nic", + "authTypeNoneDesc": "Żadne dane uwierzytelniające nie są przechowywane. Przy każdym połączeniu zostaniesz poproszony o podanie nazwy użytkownika i hasła; dane te nie zostaną zapisane.", "selectCredential": "Wybierz dane uwierzytelniające...", "connectionSettings": "Ustawienia połączenia", "displaySettings": "Ustawienia wyświetlania", @@ -1259,6 +1328,7 @@ "rdpPort": "Port RDP", "username": "Nazwa użytkownika", "password": "Hasło", + "passwordSaved": "Hasło zapisane, wpisz, aby zmienić", "domain": "Domena", "securityMode": "Tryb bezpieczeństwa", "colorDepth": "Głębia koloru", @@ -1424,7 +1494,60 @@ "expiresIn": "Wygasa za {{duration}}", "search": "Wyszukaj połączenia...", "noSearchResults": "Brak połączeń odpowiadających Twojemu wyszukiwaniu", - "rename": "Zmień nazwę sesji" + "rename": "Zmień nazwę sesji", + "sectionSharedWithMe": "Udostępniono mi", + "sharedBy": "Udostępnione przez {{username}}", + "join": "Dołączyć", + "sharedSessionLabel": "{{hostName}} (udostępnione)" + }, + "sessionSharing": { + "guestView": { + "loading": "Łączenie z sesją współdzieloną...", + "linkInvalid": "Ten link do udostępniania jest nieprawidłowy, wygasł lub został odwołany", + "rateLimited": "Zbyt wiele prób, spróbuj ponownie za chwilę", + "sessionEnded": "Ta sesja dobiegła końca", + "readOnlyBadge": "Tylko do podglądu" + }, + "modalTitle": "Udostępnij sesję", + "shareButton": "Udział", + "notReadyToShare": "Sesja nie jest jeszcze gotowa do udostępnienia", + "modeTab": { + "link": "Połączyć", + "user": "Użytkownik" + }, + "linkModeDescription": "Każdy mający ten link może dołączyć, nie jest wymagane założenie konta.", + "userModeDescription": "Udostępnij dane konkretnemu użytkownikowi, który ma już dostęp do tego hosta. Jeśli jeszcze nie ma dostępu, najpierw udostępnij mu hosta lub skorzystaj z linku. Po udostępnieniu sesja pojawi się na jego karcie Połączenia.", + "permissionLevel": { + "label": "Poziom uprawnień", + "readOnly": "Tylko do odczytu", + "readOnlyDescription": "Można oglądać sesję na żywo, ale nie można pisać ani brać udziału w interakcji.", + "readWrite": "Odczyt-zapis", + "readWriteDescription": "Może pisać i uczestniczyć w sesji tak samo jak właściciel." + }, + "expiryLabel": "Wygaśnięcie łącza", + "createLinkButton": "Utwórz link", + "createShareButton": "Udostępnij użytkownikowi", + "searchUsersPlaceholder": "Wyszukaj użytkowników...", + "noUsersFound": "Nie znaleziono użytkowników", + "linkCreated": "Udostępnij link utworzony", + "linkCopied": "Link skopiowany do schowka", + "copyLink": "Kopiuj link", + "shareCreated": "Sesja udostępniona. Pojawi się na karcie Połączenia.", + "shareFailed": "Nie udało się utworzyć udziału", + "userLacksHostAccess": "Ten użytkownik nie ma jeszcze dostępu do tego hosta. Udostępnij mu najpierw hosta lub skorzystaj z linku.", + "activeShares": "Aktywne akcje", + "noActiveShares": "Brak aktywnych akcji w tej sesji", + "revoke": "Unieważnić", + "revokeConfirmTitle": "Cofnąć to udostępnienie?", + "revokeConfirmDescription": "Każda osoba korzystająca z tego udziału natychmiast utraci do niego dostęp.", + "revoked": "Udostępnij odwołano", + "revokeFailed": "Nie udało się odwołać udostępniania", + "joinCount": "{{count}} dołącz", + "joinCount_other": "{{count}} łączy", + "expiresAt": "Wygasa {{date}}", + "linkShareBadge": "Połączyć", + "userShareBadge": "Użytkownik: {{username}}", + "loadSharesFailed": "Nie udało się załadować aktywnych udziałów" }, "guacamole": { "connecting": "Łączenie z sesją {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Na nowo połączyć", "retry": "Spróbować ponownie", "guacdUnavailable": "Usługa pulpitu zdalnego (guacd) jest niedostępna. Upewnij się, że usługa guacd jest uruchomiona, dostępna i poprawnie skonfigurowana w ustawieniach administratora.", + "credentialPromptTitle": "Wprowadź dane uwierzytelniające RDP", + "credentialPromptDescription": "Ten host jest skonfigurowany tak, aby prosić o dane uwierzytelniające podczas łączenia. Są one używane tylko w tej sesji i nie są zapisywane.", + "connect": "Łączyć", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Ponowne połączenie sesji", "collapse": "Zwiń pasek narzędzi", "expand": "Rozwiń pasek narzędzi", - "dragHandle": "Przeciągnij, aby zmienić położenie" + "dragHandle": "Przeciągnij, aby zmienić położenie", + "switchToTrackpad": "Przełącz na tryb gładzika (przeciągnij, aby przesunąć kursor, dotknij, aby kliknąć)", + "switchToTouch": "Przełącz na tryb dotykowy (dotknij bezpośrednio miejsca, w którym chcesz kliknąć)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Wymagane uwierzytelnianie dwuskładnikowe", "totpCodeLabel": "Kod weryfikacyjny", "totpVerify": "Zweryfikować", + "mfaPromptRequired": "Wymagane uwierzytelnienie", + "mfaPushRequired": "Wymagane uwierzytelnianie push", + "mfaMenuPlaceholder": "Wprowadź swoją odpowiedź", + "mfaWaitingApproval": "Oczekiwanie na zatwierdzenie na Twoim urządzeniu...", + "mfaSendRequest": "Wyślij prośbę", "warpgateAuthRequired": "Wymagane uwierzytelnienie Warpgate", "warpgateSecurityKey": "Klucz bezpieczeństwa", "warpgateAuthUrl": "Adres URL uwierzytelniania", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Ograniczenie szybkości: 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 uwierzytelniającego", - "failedToLoadServer": "Nie udało się załadować serwera" + "failedToLoadServer": "Nie udało się załadować serwera", + "remoteServerRequired": "Wymagany jest serwer zdalny. Połącz się z serwerem zdalnym w Ustawieniach, aby korzystać z tego typu połączenia." }, "messages": { "registrationDisabled": "Rejestracja nowych kont jest obecnie wyłączona przez administratora. Zaloguj się lub skontaktuj się z administratorem.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Nie udało się zapisać ustawień SSL", "sslRequiresDomain": "Wymagane są domena i adres e-mail", "sslInfoNote": "Po wystawieniu certyfikatu włącz SSL w zmiennych środowiskowych (ENABLE_SSL=true) i uruchom ponownie Termix.", + "sslManualOption": "Instrukcja (przesyłanie certyfikatu)", + "sslManualCert": "Certyfikat (PEM)", + "sslManualCertPlaceholder": "-----ROZPOCZĄTEK CERTYFIKATU-----", + "sslManualKey": "Klucz prywatny (PEM)", + "sslManualKeyPlaceholder": "-----POCZĄTEK KLUCZA PRYWATNEGO-----", + "sslManualDesc": "Wklej istniejący certyfikat i klucz prywatny, łącznie z pełnym łańcuchem, jeśli jest wymagany przez urząd certyfikacji.", + "sslManualUpload": "Prześlij i zainstaluj certyfikat", + "sslManualUploadLoading": "Przesyłanie certyfikatu...", + "sslManualUploadSuccess": "Certyfikat został pomyślnie przesłany i zainstalowany", + "sslManualUploadFailed": "Nie udało się przesłać certyfikatu", + "sslManualRequiresFields": "Wymagany jest certyfikat i klucz prywatny", "auditLogTotal": "{{total}} całkowita liczba wpisów", "auditLogEmpty": "Nie znaleziono wpisów w dzienniku audytu", "auditLogSuccess": "Sukces", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Historia dowodzenia", "commandHistoryEnabledDesc": "Zezwól na rejestrowanie historii poleceń. Po wyłączeniu historia nie jest zapisywana, niezależnie od ustawień hosta.", "updateCommandHistoryFailed": "Nie udało się zaktualizować ustawień historii poleceń", + "analyticsEnabled": "Udostępnij anonimowe statystyki użytkowania", + "analyticsEnabledDesc": "Wysyła anonimowe, codzienne zestawienie użytkowników, hostów i wykorzystania funkcji, aby pomóc w ulepszaniu Termix. Nie zawiera ono żadnych danych osobowych ani szczegółów połączenia.", + "updateAnalyticsFailed": "Nie udało się zaktualizować ustawień analityki", + "sessionSharingGloballyEnabled": "Zezwól na udostępnianie sesji", + "sessionSharingGloballyEnabledDesc": "Zezwalaj na udostępnianie sesji terminala na żywo, RDP, VNC i Telnet w obrębie instancji. Po wyłączeniu zastępuje wszystkie przełączniki udostępniania dla poszczególnych hostów.", + "updateSessionSharingFailed": "Nie udało się zaktualizować ustawień udostępniania sesji", "sessionTimeout": "Przekroczenie limitu czasu sesji", "hours": "godziny", "sessionTimeoutRange": "Min. 1 godz. · Maks. 720 godz.", @@ -3119,6 +3270,47 @@ "executionSuccess": "Sukces", "executionFailed": "Przegrany" }, + "keybindings": { + "title": "Skróty klawiaturowe", + "description": "Dostosuj skróty klawiaturowe do kopiowania, wklejania i sterowania w terminalu lub powiąż klawisze, aby wysłać tekst lub uruchomić fragment kodu.", + "defaultsHeading": "Wbudowane skróty", + "customHeading": "Niestandardowe skróty", + "addBinding": "Dodaj skrót", + "addBindingTitle": "Dodaj skrót", + "editBindingTitle": "Edytuj skrót", + "loading": "Załadunek...", + "noCustomBindings": "Brak niestandardowych skrótów", + "defaultBadge": "Domyślny", + "customizedBadge": "Dostosowane", + "customize": "Dostosuj", + "resetToDefault": "Przywróć ustawienia domyślne", + "resetAllToDefaults": "Przywróć ustawienia domyślne", + "close": "Zamknąć", + "cancel": "Anulować", + "saveBinding": "Zapisz skrót", + "saveError": "Nie udało się zapisać skrótów klawiaturowych", + "pressKeysToRecord": "Kombinacja klawiszy", + "pressKeysPlaceholder": "Kliknij i naciśnij klawisze", + "recording": "Naciśnij klawisze...", + "comboRequiredError": "Najpierw naciśnij kombinację klawiszy", + "textRequiredError": "Wprowadź tekst do wysłania", + "controlCodeRequiredError": "Wprowadź pojedynczą literę kodu kontrolnego", + "snippetRequiredError": "Wybierz fragment", + "conflictWarning": "Ta kombinacja jest już przypisana do {{combo}}. Zapis spowoduje, że oba zapłoną.", + "actionLabel": "Działanie", + "actionCopy": "Kopiuj wybór", + "actionPaste": "Pasta", + "actionSendControlCode": "Wyślij sygnał Ctrl+litera", + "actionSendText": "Wyślij tekst", + "actionRunSnippet": "Uruchom istniejący fragment kodu", + "controlCodeLabel": "List", + "textLabel": "Tekst do wysłania", + "appendEnterLabel": "Naciśnij Enter po wysłaniu", + "snippetLabel": "Skrawek", + "selectSnippetPlaceholder": "Wybierz fragment", + "orphanedSnippetWarning": "fragment nie znaleziony", + "clipboardPermissionNote": "Przy pierwszym użyciu w niektórych przeglądarkach może zostać wyświetlony monit o zezwolenie na dostęp do schowka." + }, "userProfile": { "donateTitle": "Utrzymajmy Termix przy życiu", "donateDescription": "Termix jest darmowy, hostowany samodzielnie i tworzony przez zaledwie kilka osób w wolnym czasie. Jeśli zaoszczędziłeś czas lub pieniądze, darowizna w kryptowalutach pomoże Ci go utrzymać.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Wygląd", "sectionSecurity": "Bezpieczeństwo", "sectionApiKeys": "Klucze API", + "sectionData": "Dane", "sectionC2sTunnels": "Tunele C2S", "usernameLabel": "Nazwa użytkownika", "roleLabel": "Rola", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Automatyczne uzupełnianie poleceń", "commandAutocompleteDesc": "Pokaż autouzupełnianie podczas pisania", + "keyboardShortcuts": "Skróty klawiaturowe", + "keyboardShortcutsDescription": "Dostosuj skróty poleceń i kopiowania w terminalu", + "manageShortcuts": "Zarządzać", "terminalLinkBehavior": "Kliknij łącze terminala", "terminalLinkBehaviorDesc": "Domyślne zachowanie po kliknięciu linków w terminalu", "historyTracking": "Śledzenie historii", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Dołącz swój klucz do", "apiKeyUsageHintHeader": "chodnikowiec.", "apiKeyPermissionsHint": "Klucze dziedziczą uprawnienia użytkownika, który je stworzył.", + "exportData": "Eksportuj moje dane", + "exportDataDesc": "Pobierz kopię zapasową hostów, danych uwierzytelniających i ustawień, aby przenieść ją na inne urządzenie", + "export": "Eksport", + "exporting": "Eksportowanie...", + "importData": "Importuj moje dane", + "importDataDesc": "Przywróć hosty, poświadczenia i ustawienia z pliku kopii zapasowej .sqlite", + "importDataSelected": "Wybrano: {{name}}", + "selectFile": "Wybierz plik", + "changeFile": "Zmiana", + "import": "Import", + "importing": "Importowanie...", + "exportSuccess": "Dane zostały pomyślnie wyeksportowane", + "exportFailed": "Eksport danych nie powiódł się", + "importSelectFile": "Proszę najpierw wybrać plik", + "importCompleted": "Import zakończony: zaimportowano {{total}} elementów, pominięto {{skipped}}", + "importFailed": "Nieudany import: {{error}}", "roleUser": "Użytkownik", "authMethodDual": "Podwójna autoryzacja", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Pasta", "editQuickKeys": "Edytuj szybkie klawisze", "quickKeysTitle": "Szybkie klawisze", "quickKeysDesc": "Kliknij ×, aby usunąć. Obsługuje maksymalnie 8 znaków.", diff --git a/src/ui/locales/translated/pt_BR.json b/src/ui/locales/translated/pt_BR.json index 21652fb7..7f9f9ade 100644 --- a/src/ui/locales/translated/pt_BR.json +++ b/src/ui/locales/translated/pt_BR.json @@ -389,6 +389,45 @@ "noSavedServers": "Nenhum servidor salvo", "removeServer": "Remover" }, + "migrationNotice": { + "title": "O Termix Desktop agora funciona de forma independente.", + "body1": "Este aplicativo costumava se conectar diretamente ao seu servidor Termix. Ele foi reformulado para funcionar de forma totalmente independente, armazenando hosts e credenciais localmente para que continue funcionando offline. A sincronização bidirecional com um servidor Termix auto-hospedado agora é opcional.", + "body2": "Seus hosts e credenciais atuais ainda estão em {{url}}. Ative a Sincronização Remota e reconecte-se a esse servidor para recuperá-los e mantê-los sincronizados daqui para frente.", + "dismiss": "Agora não", + "setUpSync": "Configurar sincronização remota" + }, + "remoteSync": { + "title": "Sincronização remota", + "description": "Opcionalmente, conecte este aplicativo de desktop a um servidor Termix auto-hospedado para sincronizar seus hosts, credenciais e trechos de código entre dispositivos. O aplicativo funciona sempre totalmente offline, independentemente de estar conectado ou não.", + "notConnected": "Não conectado", + "connected": "Conectado", + "connectedTo": "Conectado a {{url}}", + "lastSynced": "Última sincronização {{time}}", + "neverSynced": "Nunca sincronizado", + "syncError": "Erro de sincronização: {{message}}", + "needsReauth": "Login expirou", + "connectButton": "Conectar ao servidor", + "disconnectButton": "Desconectar", + "syncNowButton": "Sincronizar agora", + "syncing": "Sincronizando...", + "serverUrl": "URL do servidor", + "enterServerUrl": "Por favor, insira um URL de servidor.", + "mustIncludeProtocol": "O URL do servidor deve começar com http:// ou https://", + "connectionTestFailed": "Não foi possível acessar um servidor Termix nesse URL.", + "allowInvalidCertificate": "Permitir certificado inválido", + "allowInvalidCertificateDesc": "Utilize somente em servidores autohospedados confiáveis com certificados autoassinados ou de endereço IP.", + "savedServers": "Servidores salvos", + "removeServer": "Remover", + "continueButton": "Continuar", + "cancelButton": "Cancelar", + "signInTitle": "Faça login em {{url}}", + "originTitle": "Origem da conexão", + "originDescription": "Por padrão, escolha de onde as conexões SSH se originam. Essa configuração pode ser alterada para cada host individualmente.", + "originLocal": "Este dispositivo (rede local)", + "originRemote": "Servidor remoto", + "bannerReconnect": "Reconectar", + "bannerMessage": "A sincronização remota requer nova autenticação." + }, "versionCheck": { "error": "Erro na verificação de versão", "checkFailed": "Falha ao verificar se há atualizações", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Substituir nome de usuário da credencial", "overrideCredentialUsernameDesc": "Use o nome de usuário especificado acima em vez do nome de usuário da credencial.", "oidcUsernameHint": "Use $oidc.preferred_username para substituir seu nome de login OIDC.", + "tailscaleUsernameHint": "Este deve ser um usuário Unix cuja identidade Tailscale é concedida na ACL SSH da tailnet, não necessariamente o usuário root.", "jumpHostChain": "Cadeia de anfitriões de salto", "portKnocking": "Batendo ao porto", "addKnock": "Adicionar porta", @@ -650,6 +690,11 @@ "delayAfterMs": "Atraso após (ms)", "useSocks5Proxy": "Usar proxy SOCKS5", "useSocks5ProxyDesc": "Encaminhar a conexão através de um servidor proxy", + "connectionOrigin": "Origem da conexão", + "connectionOriginDesc": "De onde se origina a conexão SSH deste host. Substitui o padrão global do aplicativo de desktop.", + "connectionOriginDefault": "Usar padrão", + "connectionOriginLocal": "Este dispositivo (rede local)", + "connectionOriginRemote": "Servidor remoto", "proxyHost": "Host proxy", "proxyPort": "Porta proxy", "proxyUsername": "Nome de usuário do proxy", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Ciano brilhante", "customThemeBrightWhite": "Branco brilhante", "customThemeResetTooltip": "Restaurar padrões de fábrica", + "savedThemesLabel": "Temas salvos", + "saveAsGlobalTheme": "Salvar como tema global", + "saveGlobalThemeNamePrompt": "Insira um nome para este tema.", + "saveGlobalThemeSuccess": "Tema salvo", + "saveGlobalThemeError": "Falha ao salvar o tema", + "applyGlobalThemeTooltip": "Aplique este tema", + "deleteGlobalThemeTooltip": "Excluir este tema", + "noSavedThemes": "Ainda não há temas salvos.", "syntaxHighlightingLabel": "Realce de sintaxe", "syntaxHighlightingDesc": "Colorir a saída do terminal (erros, caminhos, IPs, registros de data e hora)", "syntaxHighlightingCategories": "Categorias em destaque", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Iniciar ou conectar-se automaticamente a uma sessão do tmux", "enableSessionLogging": "Registro de sessão", "enableSessionLoggingDesc": "Registre a saída da sessão do terminal para revisão posterior.", + "allowSessionSharing": "Permitir compartilhamento de sessão", + "allowSessionSharingDesc": "Permitir que as sessões ao vivo neste servidor sejam compartilhadas por meio de link ou com outros usuários.", "enableCommandHistory": "Histórico de comandos", "enableCommandHistoryDesc": "Registre os comandos executados neste terminal para histórico e preenchimento automático.", "linkClickBehaviorLabel": "Comportamento de clique em links", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importado", "proxmoxResultUpdated": "{{count}} atualizado", "proxmoxResultFailed": "{{count}} falhou", + "proxmoxResultSkippedNoIp": "{{count}} ignorado (nenhum IP encontrado)", "proxmoxImportComplete": "Importação do Proxmox concluída: {{summary}}", "proxmoxDiscoveryFailed": "A descoberta falhou", "proxmoxImportFailed": "A importação falhou", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Falha ao salvar as credenciais", "credentialNameRequired": "Por favor, insira um nome para a credencial.", "credentialAuthRequired": "Adicione uma senha, uma chave SSH ou ambas.", + "createCredentialFromHostBtn": "Criar credencial", + "createCredentialFromHostTitle": "Criar credencial a partir do host", + "createCredentialFromHostDesc": "Crie uma entrada de credencial reutilizável e compartilhável, preenchida previamente com o nome de usuário, senha e/ou chave SSH atuais deste host.", "backToHosts": "Voltar para Anfitriões", "backToCredentials": "Voltar às credenciais", "pinned": "Fixado", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Use / para separar níveis e criar pastas aninhadas.", "folderColor": "Cor", "folderIcon": "Ícone", + "folderCredential": "Credencial", + "folderCredentialNone": "Nenhuma credencial atribuída", + "folderCredentialHint": "Os hosts nesta pasta que usam autenticação com \"Credenciais armazenadas\" sem que suas próprias credenciais estejam selecionadas herdarão esta.", "folderPreview": "Pré-visualização", "folderNameFallback": "Pasta sem título", "createFolderButton": "Criar pasta", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Etiquetas", "shareHost": "Compartilhar Host", "shareHostTitle": "Compartilhar: {{name}}", + "shareFolder": "Pasta compartilhada", + "shareFolderTitle": "Pasta compartilhada: {{name}}", + "folderSharedSuccessfully": "Host(ns) compartilhado(s) {{count}} na pasta", + "failedToShareFolder": "Falha ao compartilhar a pasta", "sharing": { "loadError": "Não foi possível carregar os dados de compartilhamento. Tente novamente.", "shareWithSection": "Compartilhe com", @@ -1221,6 +1287,7 @@ "shareWithCount": "Compartilhar ({{count}})", "currentAccess": "Acesso atual", "noAccessEntries": "Este host ainda não foi compartilhado.", + "folderShareSummary": "Compartilhado {{shared}} de {{total}} hosts nesta pasta", "grantedBy": "Concedido por", "expires": "Expira", "expired": "Expirado", @@ -1242,6 +1309,8 @@ "authMethod": "Método de autenticação", "authTypeDirect": "Direto", "authTypeCredential": "Credencial", + "authTypeNone": "Nenhum", + "authTypeNoneDesc": "Nenhuma credencial é armazenada. Você será solicitado a inserir um nome de usuário e uma senha sempre que se conectar; eles não são salvos.", "selectCredential": "Selecione uma credencial...", "connectionSettings": "Configurações de conexão", "displaySettings": "Configurações de exibição", @@ -1259,6 +1328,7 @@ "rdpPort": "Porta RDP", "username": "Nome de usuário", "password": "Senha", + "passwordSaved": "Senha salva, digite para alterar", "domain": "Domínio", "securityMode": "Modo de segurança", "colorDepth": "Profundidade de cor", @@ -1424,7 +1494,60 @@ "expiresIn": "Expira em {{duration}}", "search": "Buscar conexões...", "noSearchResults": "Nenhuma conexão corresponde à sua pesquisa.", - "rename": "Renomear sessão" + "rename": "Renomear sessão", + "sectionSharedWithMe": "Compartilhou comigo", + "sharedBy": "Compartilhado por {{username}}", + "join": "Juntar", + "sharedSessionLabel": "{{hostName}} (compartilhado)" + }, + "sessionSharing": { + "guestView": { + "loading": "Conectando à sessão compartilhada...", + "linkInvalid": "Este link de compartilhamento é inválido, expirou ou foi revogado.", + "rateLimited": "Muitas tentativas foram feitas, tente novamente em breve.", + "sessionEnded": "Esta sessão terminou.", + "readOnlyBadge": "Somente visualização" + }, + "modalTitle": "Sessão de compartilhamento", + "shareButton": "Compartilhar", + "notReadyToShare": "A sessão ainda não está pronta para ser compartilhada.", + "modeTab": { + "link": "Link", + "user": "Usuário" + }, + "linkModeDescription": "Qualquer pessoa com este link pode participar, sem necessidade de criar uma conta.", + "userModeDescription": "Compartilhe com um usuário específico que já tenha acesso a este host. Caso ele ainda não tenha acesso, compartilhe o host com ele primeiro ou use um link. Após o compartilhamento, a sessão aparecerá na aba Conexões do usuário.", + "permissionLevel": { + "label": "Nível de permissão", + "readOnly": "Somente leitura", + "readOnlyDescription": "É possível assistir à sessão ao vivo, mas não é possível digitar ou interagir.", + "readWrite": "Ler e escrever", + "readWriteDescription": "Pode digitar e interagir com a sessão da mesma forma que o proprietário." + }, + "expiryLabel": "Expiração do link", + "createLinkButton": "Criar link", + "createShareButton": "Compartilhar com o usuário", + "searchUsersPlaceholder": "Pesquisar usuários...", + "noUsersFound": "Nenhum usuário encontrado", + "linkCreated": "Compartilhar link criado", + "linkCopied": "Link copiado para a área de transferência", + "copyLink": "Copiar link", + "shareCreated": "Sessão compartilhada. Ela aparecerá na aba Conexões deles.", + "shareFailed": "Falha ao criar compartilhamento", + "userLacksHostAccess": "Esse usuário ainda não tem acesso a este host. Compartilhe o host com ele primeiro ou use um link.", + "activeShares": "Ações ativas", + "noActiveShares": "Nenhuma ação ativa nesta sessão.", + "revoke": "Revogar", + "revokeConfirmTitle": "Revogar essa participação?", + "revokeConfirmDescription": "Qualquer pessoa que usar este compartilhamento perderá o acesso imediatamente.", + "revoked": "Ações revogadas", + "revokeFailed": "Falha ao revogar a participação", + "joinCount": "{{count}} junte-se", + "joinCount_other": "{{count}} entra", + "expiresAt": "Expira em {{date}}", + "linkShareBadge": "Link", + "userShareBadge": "Usuário: {{username}}", + "loadSharesFailed": "Falha ao carregar compartilhamentos ativos" }, "guacamole": { "connecting": "Conectando à sessão {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Reconectar", "retry": "Tentar novamente", "guacdUnavailable": "O serviço de área de trabalho remota (guacd) não está disponível. Certifique-se de que o guacd esteja em execução, acessível e configurado corretamente nas configurações de administrador.", + "credentialPromptTitle": "Insira as credenciais RDP", + "credentialPromptDescription": "Este host está configurado para solicitar credenciais ao conectar. Elas são usadas apenas para esta sessão e não são salvas.", + "connect": "Conectar", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Sessão de reconexão", "collapse": "Recolher barra de ferramentas", "expand": "Expandir barra de ferramentas", - "dragHandle": "Arraste para reposicionar" + "dragHandle": "Arraste para reposicionar", + "switchToTrackpad": "Alternar para o modo trackpad (arraste para mover o cursor, toque para clicar)", + "switchToTouch": "Mude para o modo de toque (toque diretamente onde deseja clicar)." } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Autenticação de dois fatores obrigatória", "totpCodeLabel": "Código de verificação", "totpVerify": "Verificar", + "mfaPromptRequired": "Autenticação necessária", + "mfaPushRequired": "Autenticação Push necessária", + "mfaMenuPlaceholder": "Insira sua resposta", + "mfaWaitingApproval": "Aguardando aprovação no seu dispositivo...", + "mfaSendRequest": "Enviar solicitação", "warpgateAuthRequired": "Autenticação Warpgate necessária", "warpgateSecurityKey": "Chave de segurança", "warpgateAuthUrl": "URL de autenticação", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Taxa de tentativas limitada: Muitas tentativas de verificação. Tente novamente mais tarde.", "resetCodeRateLimitedWithTime": "Taxa de tentativas limitada: Muitas tentativas de verificação. Aguarde {{time}} segundos antes de tentar novamente.", "authTokenSaveFailed": "Falha ao salvar o token de autenticação", - "failedToLoadServer": "Falha ao carregar o servidor" + "failedToLoadServer": "Falha ao carregar o servidor", + "remoteServerRequired": "É necessário um servidor remoto. Conecte um servidor remoto nas Configurações para usar este tipo de conexão." }, "messages": { "registrationDisabled": "O cadastro de novas contas está temporariamente desativado por um administrador. Faça login ou entre em contato com um administrador.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Falha ao salvar as configurações SSL", "sslRequiresDomain": "Domínio e e-mail são obrigatórios.", "sslInfoNote": "Após emitir o certificado, habilite o SSL nas suas variáveis de ambiente (ENABLE_SSL=true) e reinicie o Termix.", + "sslManualOption": "Manual (envio do certificado)", + "sslManualCert": "Certificado (PEM)", + "sslManualCertPlaceholder": "-----INÍCIO DO CERTIFICADO-----", + "sslManualKey": "Chave privada (PEM)", + "sslManualKeyPlaceholder": "-----INÍCIO DA CHAVE PRIVADA-----", + "sslManualDesc": "Cole aqui seu certificado e chave privada existentes, incluindo a cadeia completa, se exigido pela sua Autoridade Certificadora.", + "sslManualUpload": "Carregar e instalar certificado", + "sslManualUploadLoading": "Enviando certificado...", + "sslManualUploadSuccess": "Certificado carregado e instalado com sucesso.", + "sslManualUploadFailed": "O envio do certificado falhou", + "sslManualRequiresFields": "É necessário certificado e chave privada.", "auditLogTotal": "{{total}} total de entradas", "auditLogEmpty": "Nenhuma entrada de log de auditoria encontrada.", "auditLogSuccess": "Sucesso", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Histórico de comandos", "commandHistoryEnabledDesc": "Permitir o registro do histórico de comandos. Quando desativado, o histórico não é salvo, independentemente das configurações de cada host.", "updateCommandHistoryFailed": "Falha ao atualizar a configuração do histórico de comandos", + "analyticsEnabled": "Compartilhar estatísticas de uso anônimas", + "analyticsEnabledDesc": "Envia uma contagem diária anônima de usuários, hosts e uso de recursos para ajudar a melhorar o Termix. Nenhum dado pessoal ou detalhe de conexão é incluído.", + "updateAnalyticsFailed": "Falha ao atualizar as configurações de análise", + "sessionSharingGloballyEnabled": "Permitir compartilhamento de sessão", + "sessionSharingGloballyEnabledDesc": "Permite que sessões de terminal ao vivo, RDP, VNC e Telnet sejam compartilhadas em toda a instância. Substitui todas as configurações de compartilhamento por host quando desativada.", + "updateSessionSharingFailed": "Falha ao atualizar as configurações de compartilhamento de sessão.", "sessionTimeout": "Tempo limite da sessão", "hours": "horas", "sessionTimeoutRange": "Mínimo 1h · Máximo 720h", @@ -3119,6 +3270,47 @@ "executionSuccess": "Sucesso", "executionFailed": "Fracassado" }, + "keybindings": { + "title": "Atalhos de teclado", + "description": "Personalize os atalhos de copiar, colar e controlar o terminal, ou associe teclas para enviar texto ou executar um trecho de código.", + "defaultsHeading": "Atalhos integrados", + "customHeading": "Atalhos personalizados", + "addBinding": "Adicionar atalho", + "addBindingTitle": "Adicionar atalho", + "editBindingTitle": "Atalho para edição", + "loading": "Carregando...", + "noCustomBindings": "Ainda não há atalhos personalizados.", + "defaultBadge": "Padrão", + "customizedBadge": "Personalizado", + "customize": "Personalizar", + "resetToDefault": "Restaurar para as configurações padrão", + "resetAllToDefaults": "Redefinir tudo para os padrões de fábrica", + "close": "Fechar", + "cancel": "Cancelar", + "saveBinding": "Atalho para salvar", + "saveError": "Falha ao salvar os atalhos de teclado.", + "pressKeysToRecord": "Combinação de teclas", + "pressKeysPlaceholder": "Clique e pressione as teclas.", + "recording": "Pressione as teclas...", + "comboRequiredError": "Pressione primeiro uma combinação de teclas", + "textRequiredError": "Digite o texto que deseja enviar.", + "controlCodeRequiredError": "Digite uma única letra para o código de controle.", + "snippetRequiredError": "Selecione um trecho", + "conflictWarning": "Esta combinação já está vinculada a {{combo}}. Salvar fará com que ambas disparem.", + "actionLabel": "Ação", + "actionCopy": "Copiar seleção", + "actionPaste": "Colar", + "actionSendControlCode": "Enviar sinal Ctrl+letra", + "actionSendText": "Enviar mensagem de texto", + "actionRunSnippet": "Executar trecho de código existente", + "controlCodeLabel": "Carta", + "textLabel": "Texto a enviar", + "appendEnterLabel": "Pressione Enter após enviar", + "snippetLabel": "Trecho", + "selectSnippetPlaceholder": "Selecione um trecho", + "orphanedSnippetWarning": "trecho não encontrado", + "clipboardPermissionNote": "Pode solicitar permissão para acessar a área de transferência no primeiro uso em alguns navegadores." + }, "userProfile": { "donateTitle": "Mantenha o Termix vivo", "donateDescription": "O Termix é gratuito, auto-hospedado e desenvolvido por apenas algumas pessoas em seu tempo livre. Se ele lhe economizou tempo ou dinheiro, uma doação em criptomoedas ajuda a mantê-lo funcionando.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Aparência", "sectionSecurity": "Segurança", "sectionApiKeys": "Chaves de API", + "sectionData": "Dados", "sectionC2sTunnels": "Túneis C2S", "usernameLabel": "Nome de usuário", "roleLabel": "Papel", @@ -3162,6 +3355,9 @@ "settingsTerminal": "terminal", "commandAutocomplete": "Preenchimento automático de comandos", "commandAutocompleteDesc": "Mostrar o recurso de autocompletar enquanto digita", + "keyboardShortcuts": "Atalhos de teclado", + "keyboardShortcutsDescription": "Personalize os atalhos de copiar, colar e comandos do terminal.", + "manageShortcuts": "Gerenciar", "terminalLinkBehavior": "Clique no link do terminal", "terminalLinkBehaviorDesc": "Comportamento padrão ao clicar em links no terminal.", "historyTracking": "Rastreamento de histórico", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Inclua sua chave no", "apiKeyUsageHintHeader": "cabeçalho.", "apiKeyPermissionsHint": "As chaves herdam as permissões do usuário que as criou.", + "exportData": "Exportar meus dados", + "exportDataDesc": "Faça o download de um backup dos seus hosts, credenciais e configurações para transferir para outro dispositivo.", + "export": "Exportar", + "exporting": "Exportador...", + "importData": "Importar meus dados", + "importDataDesc": "Restaure seus hosts, credenciais e configurações a partir de um arquivo de backup .sqlite.", + "importDataSelected": "Selecionado: {{name}}", + "selectFile": "Selecionar arquivo", + "changeFile": "Mudar", + "import": "Importar", + "importing": "Importando...", + "exportSuccess": "Dados exportados com sucesso", + "exportFailed": "A exportação de dados falhou", + "importSelectFile": "Por favor, selecione um arquivo primeiro.", + "importCompleted": "Importação concluída: {{total}} itens importados, {{skipped}} ignorados", + "importFailed": "A importação falhou: {{error}}", "roleUser": "Usuário", "authMethodDual": "Autenticação dupla", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Colar", "editQuickKeys": "Editar teclas de atalho", "quickKeysTitle": "Teclas de atalho", "quickKeysDesc": "Toque em × para remover. Suporta até 8 caracteres.", diff --git a/src/ui/locales/translated/pt_PT.json b/src/ui/locales/translated/pt_PT.json index e10abf75..b13e1042 100644 --- a/src/ui/locales/translated/pt_PT.json +++ b/src/ui/locales/translated/pt_PT.json @@ -1,186 +1,186 @@ { "termixId": { "title": "Termix ID", - "loadFailed": "Falha ao carregar o ID do Termix", - "claimTitle": "Reivindique o seu ID Termix", - "claimIntro": "Escolha um nome de utilizador exclusivo. As suas chaves públicas SSH serão publicadas num URL público que pode adicionar ao ficheiro authorized_keys de qualquer servidor.", - "handleLabel": "Lidar com", - "handlePlaceholder": "Alice", + "loadFailed": "Falha ao carregar o Termix ID", + "claimTitle": "Reivindicar o seu Termix ID", + "claimIntro": "Escolha um identificador único. As suas chaves públicas SSH serão publicadas num URL público que pode adicionar ao ficheiro authorized_keys de qualquer servidor.", + "handleLabel": "Identificador", + "handlePlaceholder": "alice", "checking": "A verificar…", "available": "Disponível", - "taken": "Já tomadas", - "invalidHandle": "Apenas letras minúsculas, números, hífens e sublinhados.", + "taken": "Já utilizado", + "invalidHandle": "Apenas letras minúsculas, números, - e _", "descriptionLabel": "Descrição (opcional)", - "descriptionPlaceholder": "Chaves do portátil e do telemóvel de trabalho", - "create": "Criar ID Termix", + "descriptionPlaceholder": "Chaves do portátil de trabalho e do telemóvel", + "create": "Criar Termix ID", "created": "Termix ID criado", - "createFailed": "Falha ao criar o ID Termix", - "deleteConfirm": "Apagar o seu ID Termix e todas as chaves publicadas? Os servidores que provisionou manterão as chaves até que as remova manualmente.", - "deleted": "ID do Termix eliminado", - "deleteFailed": "Falha ao eliminar o ID do Termix", - "copyFailed": "Falha na cópia", - "resolverUrlLabel": "URL do resolvedor público", - "provisionLabel": "Provisione um servidor", + "createFailed": "Falha ao criar o Termix ID", + "deleteConfirm": "Eliminar o seu Termix ID e todas as chaves publicadas? Os servidores que provisionou manterão as chaves até as remover manualmente.", + "deleted": "Termix ID eliminado", + "deleteFailed": "Falha ao eliminar o Termix ID", + "copyFailed": "Falha ao copiar", + "resolverUrlLabel": "URL público de resolução", + "provisionLabel": "Provisionar um servidor", "publishTitle": "Publicar uma chave pública", "generate": "Gerar", - "generateTooltip": "Gere um par de chaves Ed25519 — publica a chave pública e descarrega a chave privada para o seu dispositivo.", + "generateTooltip": "Gerar um par de chaves Ed25519 — publica a chave pública e descarrega a chave privada para o seu dispositivo", "saveToVault": "Guardar nas credenciais", - "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... utilizador@host", - "labelPlaceholder": "Rótulo (opcional)", + "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", + "labelPlaceholder": "Etiqueta (opcional)", "add": "Adicionar", - "importFromCredential": "Ou importar a partir de uma credencial armazenada.", + "importFromCredential": "Ou importar de uma credencial guardada", "keyPublished": "Chave publicada", - "addKeyFailed": "Falha ao adicionar a chave", - "generatedSaved": "O par de chaves foi gerado e guardado no seu cofre de credenciais. A chave privada também foi baixada.", - "generatedOnly": "Par de chaves gerado — chave privada descarregada (apenas apresentada uma vez).", - "generateFailed": "Falha ao gerar a chave", + "addKeyFailed": "Falha ao adicionar chave", + "generatedSaved": "Par de chaves gerado e guardado no cofre de credenciais. A chave privada também foi descarregada.", + "generatedOnly": "Par de chaves gerado — chave privada descarregada (exibida apenas uma vez).", + "generateFailed": "Falha ao gerar chave", "imported": "Chave importada da credencial", - "importFailed": "Falha ao importar a chave", - "noKeys": "Nenhuma chave pública foi ainda publicada.", + "importFailed": "Falha ao importar chave", + "noKeys": "Nenhuma chave pública publicada ainda.", "keysTitle": "Chaves publicadas", - "published": "Publicado", - "hidden": "Oculto", + "published": "Publicada", + "hidden": "Oculta", "keyRemoved": "Chave removida", - "removeKeyFailed": "Falha ao remover a chave", - "updateKeyFailed": "Falha ao atualizar a chave", + "removeKeyFailed": "Falha ao remover chave", + "updateKeyFailed": "Falha ao atualizar chave", "fromVault": "Do cofre de credenciais", - "linkedToTermixId": "Publicado via Termix ID", - "selectCredential": "Selecione uma credencial...", - "import": "Importação", - "caTitle": "Autoridade certificadora", - "caIntro": "Confie nesta Autoridade Certificadora (CA) num servidor e ela aceitará qualquer certificado que assine. Rode os certificados para revogar todos de uma só vez; os certificados também expiram automaticamente.", + "linkedToTermixId": "Publicada via Termix ID", + "selectCredential": "Selecionar uma credencial...", + "import": "Importar", + "caTitle": "Autoridade de certificação", + "caIntro": "Confie nesta CA num servidor e ele aceitará qualquer certificado que assinar. Rode para revogar tudo de uma só vez; os certificados também expiram automaticamente.", "caEnable": "Ativar CA", - "caEnabled": "CA habilitado", - "caCreateFailed": "Falha ao ativar a CA", - "caPublicKeyLabel": "chave pública CA", - "caTrustLabel": "Confie num servidor (execute como root)", - "caRotate": "Girar", - "caRotateConfirm": "Rodar a Autoridade Certificadora? Todos os certificados por ela assinados deixarão de ser aceites e terão de ser reemitidos.", - "caRotated": "Autoridade Certificadora rotacionada — certificados anteriores revogados", - "caRotateFailed": "Falha ao rodar o CA", + "caEnabled": "CA ativada", + "caCreateFailed": "Falha ao ativar CA", + "caPublicKeyLabel": "Chave pública da CA", + "caTrustLabel": "Confiar num servidor (executar como root)", + "caRotate": "Rodar", + "caRotateConfirm": "Rodar a CA? Todos os certificados que assinou deixarão de ser aceites e terão de ser reemitidos.", + "caRotated": "CA rodada — certificados anteriores revogados", + "caRotateFailed": "Falha ao rodar CA", "caDelete": "Remover CA", - "caDeleteConfirm": "Remover a autoridade certificadora?", - "caDeleted": "CA removido", - "caDeleteFailed": "Falha ao remover a CA", + "caDeleteConfirm": "Remover a CA?", + "caDeleted": "CA removida", + "caDeleteFailed": "Falha ao remover CA", "caValidityLabel": "Validade do certificado (dias)", "issueCert": "Certificado", - "issueCertTooltip": "Emita um certificado SSH para esta chave, assinado pela sua Autoridade Certificadora (CA).", - "certIssued": "Certificado emitido e baixado", - "certIssueFailed": "Falha na emissão do certificado" + "issueCertTooltip": "Emitir um certificado SSH para esta chave, assinado pela sua CA", + "certIssued": "Certificado emitido e transferido", + "certIssueFailed": "Falha ao emitir certificado" }, "credentials": { "folders": "Pastas", "folder": "Pasta", "password": "Palavra-passe", "key": "Chave", - "sshPrivateKey": "Chave privada SSH", + "sshPrivateKey": "Chave Privada SSH", "upload": "Carregar", - "keyPassword": "Senha da chave", + "keyPassword": "Palavra-passe da Chave", "sshKey": "Chave SSH", - "uploadPrivateKeyFile": "Carregar ficheiro de chave privada", - "searchCredentials": "Credenciais de pesquisa...", - "addCredential": "Adicionar credencial", + "uploadPrivateKeyFile": "Carregar Ficheiro de Chave Privada", + "searchCredentials": "Pesquisar credenciais...", + "addCredential": "Adicionar Credencial", "caCertificate": "Certificado CA (-cert.pub)", - "caCertificateDescription": "Opcional: Faça o upload ou cole o ficheiro de certificado assinado pela AC (ex.: id_ed25519-cert.pub). Obrigatório quando o seu servidor SSH utiliza autorização baseada em certificados.", + "caCertificateDescription": "Opcional: Carregar ou colar o ficheiro de certificado assinado pela CA (ex.: id_ed25519-cert.pub). Necessário quando o seu servidor SSH utiliza autorização baseada em certificados.", "uploadCertFile": "Carregar ficheiro -cert.pub", - "clearCert": "Claro", + "clearCert": "Limpar", "certLoaded": "Certificado carregado", "certPublicKeyLabel": "Certificado CA", "certTypeLabel": "Tipo de certificado", - "pasteOrUploadCert": "Cole ou carregue um certificado -cert.pub...", - "hasCaCert": "Possui certificado CA", - "noCaCert": "Sem certificado CA", - "noPublicKeyAvailable": "Sem chave pública disponível. Primeiro, abra o editor de credenciais.", + "pasteOrUploadCert": "Colar ou carregar um certificado -cert.pub...", + "hasCaCert": "Tem Certificado CA", + "noCaCert": "Sem Certificado CA", + "noPublicKeyAvailable": "Nenhuma chave pública disponível. Abra o editor de credenciais primeiro.", "deployCommandCopied": "Comando de implementação copiado", - "sortCredentials": "Classificar credenciais", - "sortDefault": "Ordem padrão", + "sortCredentials": "Ordenar Credenciais", + "sortDefault": "Ordem predefinida", "sortNameAsc": "Nome (A → Z)", "sortNameDesc": "Nome (Z → A)", "sortUsernameAsc": "Nome de utilizador (A → Z)", "sortUsernameDesc": "Nome de utilizador (Z → A)", - "filterCredentials": "Credenciais de filtro", - "filterClearAll": "Limpar filtros", + "filterCredentials": "Filtrar Credenciais", + "filterClearAll": "Limpar Filtros", "filterTypeGroup": "Tipo", "filterTypePassword": "Palavra-passe", "filterTypeKey": "Chave SSH", "filterTagsGroup": "Etiquetas" }, "homepage": { - "title": "Página inicial", + "title": "Página Inicial", "addWidget": "Adicionar widget", - "editWidget": "Widget de edição", - "deleteWidget": "Eliminar", - "widgetTypes": "Tipos de widgets", - "serviceLink": "Ligação de serviço", + "editWidget": "Editar widget", + "deleteWidget": "Apagar", + "widgetTypes": "Tipos de Widgets", + "serviceLink": "Link de Serviço", "folder": "Pasta", "clock": "Relógio", "notes": "Notas", - "hostStatus": "Estado do host", - "bookmarkList": "Favoritos", + "hostStatus": "Estado do Host", + "bookmarkList": "Marcadores", "zoomIn": "Ampliar", - "zoomOut": "Diminuir o zoom", - "resetView": "Redefinir visualização", - "lockLayout": "Configuração de fechadura", - "unlockLayout": "Desbloquear layout", - "noWidgets": "Clique com o botão direito do rato ou clique em + para adicionar o seu primeiro widget.", - "openFullView": "Abrir visualização completa", - "previewTitle": "Pré-visualização da página inicial", + "zoomOut": "Reduzir", + "resetView": "Repor Vista", + "lockLayout": "Bloquear Disposição", + "unlockLayout": "Desbloquear Disposição", + "noWidgets": "Clique com o botão direito ou em + para adicionar o seu primeiro widget", + "openFullView": "Abrir Vista Completa", + "previewTitle": "Pré-visualização da Página Inicial", "cancel": "Cancelar", "save": "Guardar", "title_label": "Título", "widgetTitlePlaceholder": "Título do widget (opcional)", "url": "URL", - "imageUrl": "URL da imagem personalizada", - "imageUrlHint": "Deixe em branco para usar o favicon do site automaticamente.", - "showImage": "Mostrar imagem", + "imageUrl": "URL de Imagem Personalizada", + "imageUrlHint": "Deixe em branco para usar automaticamente o favicon do site", + "showImage": "Mostrar Imagem", "description": "Descrição", "color": "Cor", "icon": "Ícone", - "expanded": "Expandido por defeito", - "timezone": "Fuso horário", + "expanded": "Expandido por predefinição", + "timezone": "Fuso Horário", "showSeconds": "Mostrar segundos", "format12h": "12 horas", "format24h": "24 horas", "content": "Conteúdo", - "backgroundColor": "Cor de fundo", - "host": "Anfitrião", + "backgroundColor": "Cor de Fundo", + "host": "Host", "showMetrics": "Mostrar métricas", - "links": "Links", - "addLink": "Adicionar link", + "links": "Hiperligações", + "addLink": "Adicionar ligação", "linkLabel": "Etiqueta", "linkUrl": "URL", "removeLink": "Remover", - "categoryLinks": "Links", - "categoryInfo": "Informações", + "categoryLinks": "Hiperligações", + "categoryInfo": "Info", "categorySystem": "Sistema", - "widgetServiceLinkDesc": "Um bloco clicável que direciona para um URL de serviço.", - "widgetFolderDesc": "Um contentor para agrupar widgets relacionados.", - "widgetClockDesc": "Um relógio em tempo real com fuso horário configurável.", - "widgetNotesDesc": "Um widget de notas em Markdown", - "widgetHostStatusDesc": "Apresenta a utilização de CPU, memória e disco em tempo real para um host SSH.", - "widgetBookmarkListDesc": "Lista de links rápidos", - "copyLink": "Copiar link", - "linkCopied": "Link copiado!", + "widgetServiceLinkDesc": "Um mosaico clicável que liga a um URL de serviço", + "widgetFolderDesc": "Um contentor para agrupar widgets relacionados", + "widgetClockDesc": "Um relógio em tempo real com fuso horário configurável", + "widgetNotesDesc": "Um widget de notas em markdown", + "widgetHostStatusDesc": "Mostra CPU, memória e disco em tempo real para um anfitrião SSH", + "widgetBookmarkListDesc": "Uma lista de hiperligações rápidas", + "copyLink": "Copiar ligação", + "linkCopied": "Ligação copiada!", "location": "Localização", "temperatureUnit": "Unidade de temperatura", - "showForecast": "Mostrar previsão para 3 dias", - "scrolling": "Permitir rolagem", + "showForecast": "Mostrar previsão de 3 dias", + "scrolling": "Permitir deslocamento", "feedUrl": "URL do feed", - "maxItems": "Número máximo de artigos", - "showDescription": "Descrição do programa", + "maxItems": "Itens máx.", + "showDescription": "Mostrar descrição", "widgetWeatherName": "Tempo", - "widgetWeatherDesc": "Previsão do tempo em direto para qualquer local.", - "widgetIframeName": "Incorporação de iFrame", - "widgetIframeDesc": "Incorpore qualquer URL num iframe.", + "widgetWeatherDesc": "Tempo em direto para qualquer localização", + "widgetIframeName": "Incorporação iFrame", + "widgetIframeDesc": "Incorpora qualquer URL num iframe", "widgetRssName": "Feed RSS", - "widgetRssDesc": "Visualizar itens de um feed RSS ou Atom", - "dragToFolder": "Arraste os widgets para aqui ou use o sinal + para adicionar.", + "widgetRssDesc": "Mostra itens de um feed RSS ou Atom", + "dragToFolder": "Arraste widgets aqui ou use + para adicionar", "showDisk": "Mostrar utilização do disco", "addToFolder": "Adicionar widget à pasta", - "noHostSelected": "Nenhum host selecionado", + "noHostSelected": "Nenhum anfitrião selecionado", "metricsNotAvailable": "Métricas não disponíveis", - "selectHost": "Selecione um host...", - "displayedMetrics": "Métricas apresentadas", + "selectHost": "Selecionar um host...", + "displayedMetrics": "Métricas Exibidas", "metricCpu": "CPU", "metricMemory": "Memória", "metricDisk": "Disco", @@ -192,11 +192,11 @@ "metricNetwork": "Rede", "metricProcesses": "Processos", "metricProcessesTotal": "total", - "metricProcessesRunning": "corrida", - "categoryMonitoring": "Monitoramento", - "loading": "Carregando...", - "noData": "Não há dados disponíveis.", - "allClear": "Tudo certo", + "metricProcessesRunning": "em execução", + "categoryMonitoring": "Monitorização", + "loading": "A carregar...", + "noData": "Sem dados disponíveis", + "allClear": "Tudo em ordem", "acknowledgeAlert": "Reconhecer", "noPingUrls": "Nenhum URL configurado", "pingLabel": "Etiqueta", @@ -204,209 +204,248 @@ "showLatency": "Mostrar latência", "refreshInterval": "Intervalo de atualização", "seconds": "segundos", - "filterActivityTypes": "Tipos de filtro", - "filterTypesHint": "Deixe em branco para mostrar tudo", - "showTimestamp": "Mostrar carimbo de data/hora", + "filterActivityTypes": "Filtrar tipos", + "filterTypesHint": "Deixar vazio para mostrar todos", + "showTimestamp": "Mostrar timestamp", "uptimeUnavailable": "Tempo de atividade indisponível", "uptimeLabel": "Tempo de atividade", "overviewVersion": "Versão", "overviewUpdate": "Atualizado", "overviewUpdateAvailable": "Atualização disponível", - "overviewDatabase": "Banco de dados", + "overviewDatabase": "Base de dados", "overviewUptime": "Tempo de atividade", - "noHosts": "Nenhum host configurado", - "hostGridHosts": "Anfitriões", - "hostGridAllHint": "Deixe em branco para mostrar todos os hosts.", + "noHosts": "Sem hosts configurados", + "hostGridHosts": "Hosts", + "hostGridAllHint": "Deixar em branco para mostrar todos os hosts", "columns": "Colunas", "showIp": "Mostrar endereço IP", "connectionType": "Tipo de ligação", - "layout": "Esquema", + "layout": "Layout", "showStatus": "Mostrar estado", - "showHostName": "Mostrar nome do apresentador", - "noDockerActivity": "Nenhuma atividade do Docker", - "noActivity": "Nenhuma atividade", - "showAcknowledged": "Mostrar reconhecido", + "showHostName": "Mostrar nome do host", + "noDockerActivity": "Sem atividade do Docker", + "noActivity": "Sem atividade", + "showAcknowledged": "Mostrar reconhecidos", "showCurrentValue": "Mostrar valor atual", "chartMetric": "Métrica", "metricRange": "Intervalo", "widgetMetricsChartName": "Gráfico de métricas", "widgetMetricsChartDesc": "Gráfico histórico de CPU, memória, disco ou rede para um host", - "widgetHostGridName": "Grade de anfitriões", - "widgetHostGridDesc": "Visualização em grelha dos estados dos hosts SSH", + "widgetHostGridName": "Grelha de hosts", + "widgetHostGridDesc": "Vista em grelha dos estados dos hosts SSH", "widgetAlertFeedName": "Feed de alertas", - "widgetAlertFeedDesc": "Disparos de alertas em tempo real com suporte de confirmação", + "widgetAlertFeedDesc": "Disparos de alertas em tempo real com suporte a reconhecimento", "widgetPingStatusName": "Estado do ping", "widgetPingStatusDesc": "Estado do ping HTTP para um ou mais URLs", "widgetRecentActivityName": "Atividade recente", - "widgetRecentActivityDesc": "Feed de rolagem com atividades recentes do Termix", + "widgetRecentActivityDesc": "Feed deslocável da atividade recente do Termix", "widgetTermixUptimeName": "Tempo de atividade do Termix", - "widgetTermixUptimeDesc": "Contador de tempo de atividade em tempo real para o servidor Termix", + "widgetTermixUptimeDesc": "Contador em tempo real do tempo de atividade do servidor Termix", "widgetSystemOverviewName": "Visão geral do sistema", - "widgetSystemOverviewDesc": "Visão geral da versão do Termix, integridade da base de dados e tempo de atividade", + "widgetSystemOverviewDesc": "Versão do Termix, saúde da base de dados e tempo de atividade de relance", "widgetSshQuickConnectName": "Ligação rápida SSH", "widgetSshQuickConnectDesc": "Botões de um clique para abrir sessões SSH", "widgetDockerActivityName": "Atividade do Docker", "widgetDockerActivityDesc": "Eventos recentes de contentores Docker em todos os hosts", "widgetCalendarName": "Calendário", - "widgetCalendarDesc": "Um calendário mensal com o dia de hoje em destaque.", - "widgetCountdownName": "Contagem regressiva", - "widgetCountdownDesc": "Cronómetro regressivo para uma data alvo", - "widgetSearchBarName": "Barra de pesquisa", - "widgetSearchBarDesc": "Widget de pesquisa rápida na Web", - "widgetTextBannerName": "Banner de texto", - "widgetTextBannerDesc": "Um rótulo ou cabeçalho de secção a negrito para o ecrã.", + "widgetCalendarDesc": "Um calendário mensal com o dia de hoje em destaque", + "widgetCountdownName": "Contagem Decrescente", + "widgetCountdownDesc": "Temporizador de contagem decrescente até uma data alvo", + "widgetSearchBarName": "Barra de Pesquisa", + "widgetSearchBarDesc": "Widget de pesquisa rápida na web", + "widgetTextBannerName": "Banner de Texto", + "widgetTextBannerDesc": "Um rótulo ou cabeçalho de secção em destaque para a tela", "widgetImageWidgetName": "Imagem", "widgetImageWidgetDesc": "Exibir uma imagem a partir de um URL", - "widgetMarkdownNotesName": "Notas em Markdown", - "widgetMarkdownNotesDesc": "Notas detalhadas com renderização Markdown incorporada.", - "widgetCustomApiName": "API personalizada", - "widgetCustomApiDesc": "Obtenha e apresente dados de qualquer API JSON.", - "widgetServiceGridName": "Grelha de serviço", - "widgetServiceGridDesc": "Uma grelha configurável de ligações de mosaico de serviço", - "widgetDashboardLinksName": "Ligações do painel de controle", - "widgetDashboardLinksDesc": "Apresente os seus links de serviço configurados no painel de controlo.", - "widgetSearchLinksName": "Atalhos de pesquisa", - "widgetSearchLinksDesc": "Botões de atalho de pesquisa rápida com entrada incorporada", - "widgetLinkTreeName": "Árvore do Link", - "widgetLinkTreeDesc": "Secções agrupadas de links com títulos", - "calMon": "Mo", - "calTue": "Tu", - "calWed": "Nós", - "calThu": "O", - "calFri": "Padre", - "calSat": "Sa", - "calSun": "Su", - "startOnMonday": "Comece a semana na segunda-feira", - "countdownNoDate": "Sem data-alvo definida.", - "countdownPast": "O evento já aconteceu.", + "widgetMarkdownNotesName": "Notas Markdown", + "widgetMarkdownNotesDesc": "Notas enriquecidas com renderização inline de Markdown", + "widgetCustomApiName": "API Personalizada", + "widgetCustomApiDesc": "Obter e exibir dados de qualquer API JSON", + "widgetServiceGridName": "Grelha de Serviços", + "widgetServiceGridDesc": "Uma grelha configurável de links de serviços em mosaico", + "widgetDashboardLinksName": "Links do Painel", + "widgetDashboardLinksDesc": "Exibir os seus links de serviço configurados a partir do painel", + "widgetSearchLinksName": "Atalhos de Pesquisa", + "widgetSearchLinksDesc": "Botões de atalho para pesquisa rápida com entrada inline", + "widgetLinkTreeName": "Árvore de Links", + "widgetLinkTreeDesc": "Secções agrupadas de links com cabeçalhos", + "calMon": "Seg", + "calTue": "Ter", + "calWed": "Qua", + "calThu": "Qui", + "calFri": "Sex", + "calSat": "Sáb", + "calSun": "Dom", + "startOnMonday": "Começar a semana na segunda-feira", + "countdownNoDate": "Nenhuma data alvo definida", + "countdownPast": "O evento já passou", "countdownDays": "dias", "countdownHours": "horas", "countdownMinutes": "min", "countdownSeconds": "seg", - "countdownLabel": "Etiqueta", - "countdownLabelPlaceholder": "Exemplo: Dia do Lançamento", - "countdownShowDays": "Dias de exibição", - "countdownShowHours": "Horário de funcionamento", - "targetDate": "Data-alvo", - "searchEngine": "Mecanismo de busca", - "customSearchUrl": "URL de pesquisa personalizada", - "searchPlaceholder": "Pesquisa...", + "countdownLabel": "Rótulo", + "countdownLabelPlaceholder": "p. ex. Dia de Lançamento", + "countdownShowDays": "Mostrar Dias", + "countdownShowHours": "Mostrar Horas", + "targetDate": "Data Alvo", + "searchEngine": "Motor de Pesquisa", + "customSearchUrl": "URL de Pesquisa Personalizada", + "searchPlaceholder": "Pesquisar...", "searchGo": "Ir", - "searchPlaceholderLabel": "Texto de espaço reservado", - "searchPlaceholderHint": "Texto apresentado dentro do campo de pesquisa", - "openInNewTab": "Abrir em novo separador", - "searchQueryPlaceholder": "Introduza a consulta...", + "searchPlaceholderLabel": "Texto do Placeholder", + "searchPlaceholderHint": "Texto apresentado no campo de pesquisa", + "openInNewTab": "Abrir num Novo Separador", + "searchQueryPlaceholder": "Introduzir consulta...", "noSearchShortcuts": "Nenhum atalho configurado", - "addSearchShortcut": "Adicionar atalho", - "fontSize": "Tamanho da fonte", - "textAlign": "Alinhamento de texto", - "fontWeight": "Peso da fonte", - "clearColor": "Cor transparente", - "imageFit": "Ajuste de imagem", - "imageLinkUrl": "URL do link", - "noImage": "Sem URL de imagem definido.", - "altText": "Texto alternativo", + "addSearchShortcut": "Adicionar Atalho", + "fontSize": "Tamanho da Letra", + "textAlign": "Alinhamento do Texto", + "fontWeight": "Peso da Fonte", + "clearColor": "Limpar Cor", + "imageFit": "Ajuste da Imagem", + "imageLinkUrl": "URL do Link", + "noImage": "Nenhum URL de imagem definido", + "altText": "Texto Alternativo", "altTextPlaceholder": "Descreva a imagem", "renderMarkdown": "Renderizar Markdown", - "displayMode": "Modo de visualização", - "displayField": "Campo de exibição", + "displayMode": "Modo de Exibição", + "displayField": "Campo de Exibição", "jsonPath": "Caminho JSON", "customApiLabel": "Etiqueta", - "customApiLabelPlaceholder": "Exemplo: Temperatura", + "customApiLabelPlaceholder": "p. ex. Temperatura", "customApiUnit": "Unidade", "customApiNoUrl": "Nenhum URL de API configurado", - "customApiError": "Falha ao procurar", - "customApiNotArray": "A resposta não é uma matriz.", - "addService": "Adicionar serviço", - "showLabels": "Mostrar rótulos", - "iconSize": "Tamanho do ícone", - "noDashboardLinks": "Sem ligação para o painel de controlo configurado", + "customApiError": "Falha ao obter", + "customApiNotArray": "A resposta não é um array", + "addService": "Adicionar Serviço", + "showLabels": "Mostrar Etiquetas", + "iconSize": "Tamanho do Ícone", + "noDashboardLinks": "Nenhuma ligação do painel configurada", "noLimit": "Sem limite", - "sectionHeading": "Título da secção", - "addSection": "Adicionar secção", - "compactMode": "Modo compacto", - "showDetailed": "Mostrar detalhes", - "selectHosts": "Selecionar anfitriões", - "allHosts": "Todos os anfitriões", + "sectionHeading": "Cabeçalho da Secção", + "addSection": "Adicionar Secção", + "compactMode": "Modo Compacto", + "showDetailed": "Mostrar Detalhes", + "selectHosts": "Selecionar Hosts", + "allHosts": "Todos os hosts", "listLayout": "Lista", - "gridLayout": "Rede", - "terminal": "terminal", - "files": "Arquivos", + "gridLayout": "Grelha", + "terminal": "Terminal", + "files": "Ficheiros", "docker": "Docker", "range15m": "15 minutos", "range1h": "1 hora", "range6h": "6 horas", "range24h": "24 horas", - "metricNetRx": "Download da rede", - "metricNetTx": "Envio de rede", - "severityFilter": "Filtro de gravidade", + "metricNetRx": "Download de Rede", + "metricNetTx": "Upload de Rede", + "severityFilter": "Filtro de Gravidade", "filterAll": "Tudo", - "accentColor": "Cor de destaque", + "accentColor": "Cor de Destaque", "widgetSshTerminalName": "Terminal SSH", - "widgetSshTerminalDesc": "Um terminal SSH incorporado ligado a um host configurado.", + "widgetSshTerminalDesc": "Um terminal SSH integrado ligado a um host configurado", "sshTerminalNoHost": "Nenhum host configurado", "sshTerminalConnect": "Ligar", - "sshTerminalAutoConnect": "Ligação automática sob carga", - "widgetQuickConnectName": "Conexão rápida", - "widgetQuickConnectDesc": "Botões de arranque com um clique para qualquer tipo de ligação nos seus hosts.", - "connectionTypes": "Tipos de ligação", - "connType_terminal": "terminal", - "connType_files": "Gestor de arquivos", + "sshTerminalAutoConnect": "Ligação automática ao carregar", + "widgetQuickConnectName": "Ligação Rápida", + "widgetQuickConnectDesc": "Botões de lançamento num clique para qualquer tipo de ligação em todos os seus hosts", + "connectionTypes": "Tipos de Ligação", + "connType_terminal": "Terminal", + "connType_files": "Gestor de Ficheiros", "connType_docker": "Docker", "connType_tunnel": "Túnel", - "connType_host-metrics": "Métricas do host", + "connType_host-metrics": "Métricas do Host", "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", - "widgetFileManagerName": "Gestor de arquivos", - "widgetFileManagerDesc": "Gestor de ficheiros SFTP integrado para um host configurado.", - "widgetDockerName": "Gestor de Docker", + "widgetFileManagerName": "Gestor de Ficheiros", + "widgetFileManagerDesc": "Gestor de ficheiros SFTP integrado para um host configurado", + "widgetDockerName": "Gestor Docker", "widgetDockerDesc": "Gestor de contentores Docker integrado para um host configurado", - "widgetTunnelName": "Gestor do Túnel", - "widgetTunnelDesc": "Gestor de túnel SSH integrado para um host configurado", + "widgetTunnelName": "Gestor de Túneis", + "widgetTunnelDesc": "Gestor de túneis SSH integrado para um host configurado", "widgetNoHostSelected": "Nenhum host configurado" }, "serverConfig": { - "title": "Configuração do servidor", - "description": "Configure o URL do servidor Termix para se ligar aos seus serviços de backend.", - "serverUrl": "URL do servidor", - "enterServerUrl": "Por favor, introduza um URL de servidor.", + "title": "Configuração do Servidor", + "description": "Configure o URL do servidor Termix para se ligar aos seus serviços de backend", + "serverUrl": "URL do Servidor", + "enterServerUrl": "Por favor, insira um URL do servidor", "saveFailed": "Falha ao guardar a configuração", "saveError": "Erro ao guardar a configuração", - "saving": "Salvando...", - "saveConfig": "Guardar configuração", - "helpText": "Introduza o URL onde o seu servidor Termix está a ser executado (por exemplo, http://localhost:30001 ou https://seu-servidor.com)", - "changeServer": "Alterar servidor", - "mustIncludeProtocol": "O URL do servidor deve começar por http:// ou https://", + "saving": "A guardar...", + "saveConfig": "Guardar Configuração", + "helpText": "Insira o URL onde o seu servidor Termix está a ser executado (ex: http://localhost:30001 ou https://your-server.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": "Utilizar servidor local", - "embeddedDesc": "Execute o Termix com o servidor local integrado (não é necessário um servidor remoto).", - "embeddedConnecting": "Ligar ao servidor local...", + "allowInvalidCertificateDesc": "Usar apenas para servidores auto-hospedados de confiança com certificados auto-assinados ou de endereço IP.", + "useEmbedded": "Usar Servidor Local", + "embeddedDesc": "Executar o Termix com o servidor local incorporado (não é necessário servidor remoto)", + "embeddedConnecting": "A ligar ao servidor local...", "embeddedNotReady": "O servidor local ainda não está pronto. Aguarde um momento e tente novamente.", - "localServer": "Servidor local", - "savedServers": "Servidores salvos", - "noSavedServers": "Nenhum servidor salvo", + "localServer": "Servidor Local", + "savedServers": "Servidores Guardados", + "noSavedServers": "Nenhum servidor guardado", "removeServer": "Remover" }, + "migrationNotice": { + "title": "O Termix Desktop funciona agora de forma independente.", + "body1": "Esta aplicação costumava ligar-se diretamente ao seu servidor Termix. Foi reformulado para funcionar de forma totalmente independente, armazenando hosts e credenciais localmente para que continue a funcionar offline. A sincronização bidirecional com um servidor Termix auto-hospedado é agora opcional.", + "body2": "Os seus hosts e credenciais atuais ainda estão em {{url}}. Ative a Sincronização Remota e volte a ligar-se a esse servidor para os recuperar e mantê-los sincronizados daqui para a frente.", + "dismiss": "Agora não", + "setUpSync": "Configurar sincronização remota" + }, + "remoteSync": { + "title": "Sincronização remota", + "description": "Opcionalmente, ligue esta aplicação de desktop a um servidor Termix auto-hospedado para sincronizar os seus hosts, credenciais e pedaços de código entre dispositivos. A aplicação funciona sempre totalmente offline, independentemente de estar ligado ou não.", + "notConnected": "Não conectado", + "connected": "Conectado", + "connectedTo": "Ligado a {{url}}", + "lastSynced": "Última sincronização {{time}}", + "neverSynced": "Nunca sincronizado", + "syncError": "Erro de sincronização: {{message}}", + "needsReauth": "Login expirou", + "connectButton": "Ligar ao servidor", + "disconnectButton": "Desconectar", + "syncNowButton": "Sincronizar agora", + "syncing": "Sincronizando...", + "serverUrl": "URL do servidor", + "enterServerUrl": "Por favor, introduza um URL de servidor.", + "mustIncludeProtocol": "O URL do servidor deve começar por http:// ou https://", + "connectionTestFailed": "Não foi possível aceder a um servidor Termix nesse URL.", + "allowInvalidCertificate": "Permitir certificado inválido", + "allowInvalidCertificateDesc": "Utilize apenas em servidores auto-hospedados fidedignos com certificados autoassinados ou de endereço IP.", + "savedServers": "Servidores salvos", + "removeServer": "Remover", + "continueButton": "Continuar", + "cancelButton": "Cancelar", + "signInTitle": "Faça login em {{url}}", + "originTitle": "Origem da ligação", + "originDescription": "Por predefinição, escolha de onde se originam as ligações SSH. Esta configuração pode ser alterada para cada host individualmente.", + "originLocal": "Este dispositivo (rede local)", + "originRemote": "Servidor remoto", + "bannerReconnect": "Reconectar", + "bannerMessage": "A sincronização remota requer nova autenticação." + }, "versionCheck": { - "error": "Erro na verificação da versão", - "checkFailed": "Falha ao verificar se existem atualizações", - "upToDate": "O aplicativo está atualizado.", + "error": "Erro na Verificação de Versão", + "checkFailed": "Falha ao verificar atualizações", + "upToDate": "A aplicação está atualizada", "currentVersion": "Está a executar a versão {{version}}", "updateAvailable": "Atualização disponível", - "newVersionAvailable": "Uma nova versão está disponível! Está a executar {{current}}, mas {{latest}} está disponível.", + "newVersionAvailable": "Está disponível uma nova versão! Está a executar {{current}}, mas a {{latest}} está disponível.", "betaVersion": "Versão Beta", - "betaVersionDesc": "Está a executar {{current}}, que é mais recente do que a versão estável mais recente {{latest}}.", - "releasedOn": "Lançado em {{date}}", - "downloadUpdate": "Baixar atualização", + "betaVersionDesc": "Está a executar a {{current}}, que é mais recente do que a última versão estável {{latest}}.", + "releasedOn": "Lançado a {{date}}", + "downloadUpdate": "Descarregar Atualização", "checking": "A verificar atualizações...", - "checkUpdates": "Verifique se existem atualizações", + "checkUpdates": "Verificar Atualizações", "checkingUpdates": "A verificar atualizações...", "updateRequired": "Atualização necessária" }, "common": { - "close": "Perto", + "close": "Fechar", "minimize": "Minimizar", "online": "Online", "offline": "Offline", @@ -417,31 +456,31 @@ "error": "Erro", "warning": "Aviso", "unsavedChanges": "Alterações não guardadas", - "dismiss": "Dispensar", - "loading": "Carregando...", + "dismiss": "Ignorar", + "loading": "A carregar...", "optional": "Opcional", "connect": "Ligar", "copied": "Copiado", - "connecting": "Ligando...", + "connecting": "A ligar...", "updateAvailable": "Atualização disponível", "appName": "Termix", - "openInNewTab": "Abrir em novo separador", + "openInNewTab": "Abrir num novo separador", "noReleases": "Sem lançamentos", - "updatesAndReleases": "Atualizações e lançamentos", + "updatesAndReleases": "Atualizações e Lançamentos", "newVersionAvailable": "Está disponível uma nova versão ({{version}}).", "failedToFetchUpdateInfo": "Falha ao obter informações de atualização", "preRelease": "Pré-lançamento", - "noReleasesFound": "Nenhuma versão encontrada.", + "noReleasesFound": "Nenhum lançamento encontrado.", "cancel": "Cancelar", "username": "Nome de utilizador", - "login": "Conecte-se", - "logout": "Sair", + "login": "Iniciar sessão", + "logout": "Terminar sessão", "register": "Registar", "password": "Palavra-passe", - "confirmPassword": "Confirme a sua senha", - "back": "Costas", + "confirmPassword": "Confirmar palavra-passe", + "back": "Voltar", "save": "Guardar", - "saving": "Salvando...", + "saving": "A guardar...", "delete": "Eliminar", "rename": "Renomear", "edit": "Editar", @@ -453,129 +492,129 @@ "previous": "Anterior", "refresh": "Atualizar", "language": "Idioma", - "checking": "Verificando...", - "checkingDatabase": "A verificar ligação com o banco de dados...", + "checking": "A verificar...", + "checkingDatabase": "A verificar a ligação à base de dados...", "checkingAuthentication": "A verificar a autenticação...", - "backendReconnected": "Ligação com o servidor restabelecida", - "connectionDegraded": "Perda de ligação com o servidor, recuperando…", + "backendReconnected": "Ligação ao servidor restabelecida", + "connectionDegraded": "Ligação ao servidor perdida, a recuperar…", "reload": "Recarregar", "remove": "Remover", "create": "Criar", - "update": "Atualização", - "copy": "Cópia", + "update": "Atualizar", + "copy": "Copiar", "copyFailed": "Falha ao copiar para a área de transferência", "maximize": "Maximizar", "restore": "Restaurar", "of": "de", - "saved": "Salvo", - "deleted": "Excluído", + "saved": "Guardado", + "deleted": "Eliminado", "deleteFailed": "Falha ao eliminar", "saveFailed": "Falha ao guardar", "required": "Obrigatório" }, "nav": { - "home": "Casa", - "terminal": "terminal", + "home": "Início", + "terminal": "Terminal", "docker": "Docker", "tunnels": "Túneis", - "fileManager": "Gestor de arquivos", - "serverStats": "Métricas do host", - "hostMetrics": "Métricas do host", - "admin": "Administrador", - "termixId": "EU IA", - "userProfile": "Perfil do utilizador", - "splitScreen": "Tela dividida", - "confirmClose": "Encerrar esta sessão ativa?", - "close": "Perto", + "fileManager": "Gestor de ficheiros", + "serverStats": "Métricas do Servidor", + "hostMetrics": "Métricas do Servidor", + "admin": "Administração", + "termixId": "ID", + "userProfile": "Perfil do Utilizador", + "splitScreen": "Ecrã Dividido", + "confirmClose": "Fechar esta sessão ativa?", + "close": "Fechar", "cancel": "Cancelar", "sshManager": "Gestor SSH", - "cannotSplitTab": "Não é possível dividir este separador.", + "cannotSplitTab": "Não é possível dividir este separador", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copiar palavra-passe", - "copySudoPassword": "Copiar password de sudo", + "copyPassword": "Copiar Palavra-passe", + "copySudoPassword": "Copiar Palavra-passe do Sudo", "passwordCopied": "Palavra-passe copiada para a área de transferência", "noPasswordAvailable": "Nenhuma palavra-passe disponível", "failedToCopyPassword": "Falha ao copiar a palavra-passe", "refreshTab": "Atualizar ligação", - "renameTab": "Renomear guia", - "openFileManager": "Abra o Gestor de Ficheiros", - "dashboard": "Painel", + "renameTab": "Renomear separador", + "openFileManager": "Abrir Gestor de Ficheiros", + "dashboard": "Painel de controlo", "networkGraph": "Gráfico de rede", "tmuxMonitor": "Monitor Tmux", "homepage": "Página inicial", - "quickConnect": "Conexão rápida", + "quickConnect": "Ligação Rápida", "sshTools": "Ferramentas SSH", - "history": "História", - "sessionLogs": "Registos de sessão", - "hosts": "Anfitriões", - "snippets": "Trechos", - "hostManager": "Gestor de Hospedagem", + "history": "Histórico", + "sessionLogs": "Registos de Sessão", + "hosts": "Máquinas", + "snippets": "Fragmentos", + "hostManager": "Gestor de Máquinas", "credentials": "Credenciais", - "connections": "Conexões", + "connections": "Ligações", "alerts": "Alertas", "serial": "Serial", "roleAdministrator": "Administrador", - "roleUser": "Usuário" + "roleUser": "Utilizador" }, "hosts": { - "hosts": "Anfitriões", - "noHosts": "Nenhum host SSH", + "hosts": "Máquinas", + "noHosts": "Sem máquinas SSH", "retry": "Tentar novamente", "refresh": "Atualizar", "optional": "Opcional", - "downloadSample": "Baixar amostra", + "downloadSample": "Transferir exemplo", "failedToDeleteHost": "Falha ao eliminar {{name}}", - "importSkipExisting": "Importar (ignorar existente)", + "importSkipExisting": "Importar (ignorar existentes)", "importSSHConfig": "Importar da configuração SSH", "connectionDetails": "Detalhes da ligação", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Área de Trabalho Remota", - "port": "Porto", + "remoteDesktop": "Ambiente de trabalho remoto", + "port": "Porta", "username": "Nome de utilizador", "folder": "Pasta", "tags": "Etiquetas", - "pin": "Pino", - "addHost": "Adicionar host", - "editHost": "Editar Anfitrião", - "cloneHost": "Anfitrião de clonagem", - "enableTerminal": "Ativar terminal", - "enableTunnel": "Ativar túnel", - "enableFileManager": "Ativar o Gestor de Ficheiros", + "pin": "Fixar", + "addHost": "Adicionar máquina", + "editHost": "Editar máquina", + "cloneHost": "Clonar máquina", + "enableTerminal": "Ativar Terminal", + "enableTunnel": "Ativar Túnel", + "enableFileManager": "Ativar Gestor de Ficheiros", "enableDocker": "Ativar Docker", - "defaultPath": "Caminho padrão", - "connection": "Conexão", + "defaultPath": "Caminho Predefinido", + "connection": "Ligação", "upload": "Carregar", "authentication": "Autenticação", "password": "Palavra-passe", "key": "Chave", "credential": "Credencial", "none": "Nenhum", - "sshPrivateKey": "Chave privada SSH", - "keyType": "Tipo de chave", - "uploadFile": "Carregar ficheiro", - "tabGeneral": "General", + "sshPrivateKey": "Chave Privada SSH", + "keyType": "Tipo de Chave", + "uploadFile": "Carregar Ficheiro", + "tabGeneral": "Geral", "tabSsh": "SSH", - "tabTerminal": "terminal", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Túneis", "tabDocker": "Docker", - "tabFiles": "Arquivos", - "tabStats": "Métricas do host", - "tabHostMetrics": "Métricas do host", + "tabFiles": "Ficheiros", + "tabStats": "Métricas do Host", + "tabHostMetrics": "Métricas do Host", "tabTelnet": "Telnet", - "tabSharing": "Compartilhamento", + "tabSharing": "Partilha", "tabAuthentication": "Autenticação", - "terminal": "terminal", + "terminal": "Terminal", "tunnel": "Túnel", - "fileManager": "Gestor de arquivos", - "serverStats": "Métricas do host", - "status": "Estatuto", + "fileManager": "Gestor de Ficheiros", + "serverStats": "Métricas do Host", + "status": "Estado", "folderRenamed": "Pasta \"{{oldName}}\" renomeada para \"{{newName}}\" com sucesso", "failedToRenameFolder": "Falha ao renomear a pasta", - "movedToFolder": "Movidos os {{count}} hosts para \"{{folder}}\"", - "editHostTooltip": "Editar anfitrião", + "movedToFolder": "Movido(s) {{count}} host(s) para \"{{folder}}\"", + "editHostTooltip": "Editar host", "statusChecks": "Verificações de estado", "metricsCollection": "Recolha de métricas", "metricsInterval": "Intervalo de recolha de métricas", @@ -583,166 +622,172 @@ "behavior": "Comportamento", "themePreview": "Pré-visualização do tema", "theme": "Tema", - "fontFamily": "Família de fontes", + "fontFamily": "Família da fonte", "fontSize": "Tamanho da fonte", - "letterSpacing": "Espaçamento entre letras", + "letterSpacing": "Espaçamento de letras", "lineHeight": "Altura da linha", "cursorStyle": "Estilo do cursor", "cursorBlink": "Piscar do cursor", "scrollbackBuffer": "Buffer de rolagem", - "bellStyle": "Estilo sino", - "rightClickSelectsWord": "Clicar com o botão direito do rato seleciona a palavra.", - "fastScrollModifier": "Modificador de rolagem rápida", - "fastScrollSensitivity": "Sensibilidade de rolagem rápida", - "sshAgentForwarding": "Encaminhamento do agente SSH", - "backspaceMode": "Modo Backspace", - "startupSnippet": "Excerto de startup", - "selectSnippet": "Selecione o excerto", - "forceKeyboardInteractive": "Forçar interação por teclado", + "bellStyle": "Estilo da campainha", + "rightClickSelectsWord": "Clique direito seleciona palavra", + "fastScrollModifier": "Modificador de deslocamento rápido", + "fastScrollSensitivity": "Sensibilidade do deslocamento rápido", + "sshAgentForwarding": "Reencaminhamento do agente SSH", + "backspaceMode": "Modo de retrocesso", + "startupSnippet": "Snippet de arranque", + "selectSnippet": "Selecionar snippet", + "forceKeyboardInteractive": "Forçar autenticação Keyboard-Interactive", "overrideCredentialUsername": "Substituir nome de utilizador da credencial", - "overrideCredentialUsernameDesc": "Utilize o nome de utilizador especificado acima em vez do nome de utilizador da credencial.", - "oidcUsernameHint": "Utilize $oidc.preferred_username para substituir o seu nome de login OIDC.", - "jumpHostChain": "Cadeia de hospedeiros de salto", - "portKnocking": "Batendo ao porto", + "overrideCredentialUsernameDesc": "Utilizar o nome de utilizador especificado acima em vez do nome de utilizador da credencial", + "oidcUsernameHint": "Utilize $oidc.preferred_username para substituir o seu nome de início de sessão OIDC.", + "tailscaleUsernameHint": "Este deve ser um utilizador Unix cuja identidade Tailscale é concedida na ACL SSH da tailnet, não necessariamente o utilizador root.", + "jumpHostChain": "Cadeia de hosts de salto", + "portKnocking": "Port Knocking", "addKnock": "Adicionar porta", "addProxyNode": "Adicionar nó", - "proxyNode": "Nó proxy", + "proxyNode": "Nó de proxy", "proxyType": "Tipo de proxy", "quickActions": "Ações rápidas", - "sudoPasswordAutoFill": "Preenchimento automático de senhas Sudo", - "sudoPassword": "Senha de sudo", - "keepaliveInterval": "Intervalo de Keepalive (ms)", + "sudoPasswordAutoFill": "Preenchimento Automático da Palavra-passe Sudo", + "sudoPassword": "Palavra-passe Sudo", + "keepaliveInterval": "Intervalo de keepalive (ms)", "moshCommand": "Comando MOSH", - "environmentVariables": "Variáveis de ambiente", - "addVariable": "Adicionar variável", + "environmentVariables": "Variáveis de Ambiente", + "addVariable": "Adicionar Variável", "docker": "Docker", - "copyTerminalUrl": "Copiar URL do terminal", + "copyTerminalUrl": "Copiar URL do Terminal", "copyFileManagerUrl": "Copiar URL do Gestor de Ficheiros", - "copyRemoteDesktopUrl": "Copiar URL da Área de Trabalho Remota", - "failedToConnect": "Falha ao ligar ao console", + "copyRemoteDesktopUrl": "Copiar URL do Ambiente de Trabalho Remoto", + "failedToConnect": "Falha ao ligar à consola", "connect": "Ligar", - "disconnect": "Desconectar", + "disconnect": "Desligar", "start": "Iniciar", - "enableStatusCheck": "Ativar verificação de estado", - "enableMetrics": "Ativar métricas", - "bulkUpdateFailed": "A atualização em massa falhou", - "selectAll": "Selecionar tudo", - "deselectAll": "Desmarcar tudo", + "enableStatusCheck": "Ativar Verificação de Estado", + "enableMetrics": "Ativar Métricas", + "bulkUpdateFailed": "Falha na atualização em massa", + "selectAll": "Selecionar Todos", + "deselectAll": "Desmarcar Todos", "protocols": "Protocolos", - "secureShell": "Secure Shell", + "secureShell": "Shell Seguro", "virtualNetwork": "Rede Virtual", "unencryptedShell": "Shell não encriptado", - "addressIp": "Endereço IP /", - "friendlyName": "Nome amigável", + "addressIp": "Endereço / IP", + "friendlyName": "Nome Amigável", "macAddress": "Endereço MAC", - "wolBroadcastAddress": "Discurso de transmissão da WoL", - "wolBroadcastAddressDesc": "Transmissão direcionada opcional para Docker/redes encaminhadas (ex.: 192.168.1.255). Deixe em branco para utilizar 255.255.255.255.", + "wolBroadcastAddress": "Endereço de broadcast WoL", + "wolBroadcastAddressDesc": "Broadcast direcionado opcional para Docker/redes encaminhadas (p. ex., 192.168.1.255). Deixar vazio para usar 255.255.255.255.", "folderAndAdvanced": "Pasta e Avançado", - "privateNotes": "Notas particulares", + "privateNotes": "Notas Privadas", "privateNotesPlaceholder": "Detalhes sobre este servidor...", - "pinToTop": "Fixar no topo", - "pinToTopDesc": "Mostre sempre este host no topo da lista.", - "portKnockingSequence": "Sequência de batida de portas", - "addKnockBtn": "Adicionar batida", - "noPortKnocking": "Sem configuração de port knocking.", - "knockPort": "Porta de encaixe", + "pinToTop": "Fixar no Topo", + "pinToTopDesc": "Mostrar sempre este host no topo da lista", + "portKnockingSequence": "Sequência de Port Knocking", + "addKnockBtn": "Adicionar Knock", + "noPortKnocking": "Nenhum port knocking configurado.", + "knockPort": "Porta Knock", "protocol": "Protocolo", "delayAfterMs": "Atraso após (ms)", - "useSocks5Proxy": "Utilizar proxy SOCKS5", - "useSocks5ProxyDesc": "Encaminhar a ligação através de um servidor proxy", - "proxyHost": "Host proxy", - "proxyPort": "Porta proxy", + "useSocks5Proxy": "Usar proxy SOCKS5", + "useSocks5ProxyDesc": "Encaminhar ligação através de um servidor proxy", + "connectionOrigin": "Origem da ligação", + "connectionOriginDesc": "De onde se origina a ligação SSH deste host. Substitui o padrão global da aplicação de desktop.", + "connectionOriginDefault": "Usar padrão", + "connectionOriginLocal": "Este dispositivo (rede local)", + "connectionOriginRemote": "Servidor remoto", + "proxyHost": "Host do proxy", + "proxyPort": "Porta do proxy", "proxyUsername": "Nome de utilizador do proxy", - "proxyPassword": "Senha do proxy", + "proxyPassword": "Palavra-passe do proxy", "proxySingleMode": "Proxy único", - "proxyChainMode": "Cadeia de Proxy", + "proxyChainMode": "Cadeia de proxy", "you": "Tu", - "jumpHostChainLabel": "Cadeia de hospedeiros de salto", - "addJumpBtn": "Adicionar salto", - "noJumpHosts": "Sem servidor de salto configurado.", - "selectAServer": "Selecione um servidor...", + "jumpHostChainLabel": "Cadeia de jump hosts", + "addJumpBtn": "Adicionar jump", + "noJumpHosts": "Nenhum jump host configurado.", + "selectAServer": "Selecionar um servidor...", "sshPort": "Porta SSH", "authMethod": "Método de autenticação", - "storedCredential": "Credencial armazenada", - "selectACredential": "Selecione uma credencial...", - "vaultProfile": "Perfil do subscritor do Vault", - "selectAVaultProfile": "Selecione um perfil do Vault...", - "vaultProfileHint": "As definições são provenientes do perfil partilhado; fará login no Vault via OIDC ao ligar. Nenhum segredo é armazenado.", + "storedCredential": "Credencial guardada", + "selectACredential": "Selecionar uma credencial...", + "vaultProfile": "Perfil de assinatura Vault", + "selectAVaultProfile": "Selecionar um perfil Vault...", + "vaultProfileHint": "As definições provêm do perfil partilhado; ao ligar, iniciará sessão no Vault via OIDC. Nenhum segredo é armazenado.", "vaultNewProfile": "Novo perfil", - "vaultManageProfiles": "Gerir perfis do Vault", - "vaultAddrLabel": "Endereço do cofre", - "vaultNamespaceLabel": "Espaço de nomes", - "vaultOidcMountLabel": "Montagem de autenticação OIDC", + "vaultManageProfiles": "Gerir perfis Vault", + "vaultAddrLabel": "Endereço Vault", + "vaultNamespaceLabel": "Namespace", + "vaultOidcMountLabel": "Mount de autenticação OIDC", "vaultOidcRoleLabel": "Função OIDC", - "vaultSshMountLabel": "Montagem de segredos SSH", + "vaultSshMountLabel": "Mount de segredos SSH", "vaultSshRoleLabel": "Função de assinatura SSH", "vaultValidPrincipalsLabel": "Principais válidos", "vaultKeyTypeLabel": "Tipo de chave efémera", - "vaultSharedLabel": "Partilhe com todos os utilizadores", + "vaultSharedLabel": "Partilhar com todos os utilizadores", "vaultCreateProfile": "Criar perfil", - "vaultProfileCreated": "Perfil do Cofre criado", - "vaultProfileSaved": "Perfil do Cofre guardado", - "vaultProfileDeleted": "Perfil do Cofre excluído", - "vaultProfileSaveFailed": "Falha ao guardar o perfil do Vault", - "vaultProfileDeleteFailed": "Falha ao eliminar o perfil do Vault", + "vaultProfileCreated": "Perfil Vault criado", + "vaultProfileSaved": "Perfil Vault guardado", + "vaultProfileDeleted": "Perfil Vault eliminado", + "vaultProfileSaveFailed": "Falha ao guardar perfil Vault", + "vaultProfileDeleteFailed": "Falha ao eliminar perfil Vault", "vaultSaveProfile": "Guardar perfil", - "vaultProfileValidationError": "O nome, o endereço do Vault e a função de assinatura SSH são obrigatórios.", - "vaultNoProfiles": "Ainda não existem perfis no Vault.", + "vaultProfileValidationError": "Nome, endereço Vault e função de signatário SSH são obrigatórios", + "vaultNoProfiles": "Ainda sem perfis Vault.", "vaultSharedBadge": "partilhado", "keyTypeLabel": "Tipo de chave", - "keyTypeAuto": "Detecção automática", + "keyTypeAuto": "Detetar automaticamente", "keyPasteTab": "Colar", "keyUploadTab": "Carregar", "keyFileLoaded": "Ficheiro de chave carregado", - "keyUploadClick": "Clique para fazer o upload dos ficheiros .pem/.key/.ppk", - "clearKey": "Chave limpa", - "keySaved": "Chave SSH guarda", - "keyReplaceNotice": "Cole uma nova chave abaixo para a substituir.", - "keyPassphraseSaved": "Palavra-passe guardada, digite para alterar", + "keyUploadClick": "Clique para carregar .pem / .key / .ppk", + "clearKey": "Limpar chave", + "keySaved": "Chave SSH guardada", + "keyReplaceNotice": "cole uma nova chave abaixo para substituí-la", + "keyPassphraseSaved": "Frase-passe guardada, escreva para alterar", "replaceKey": "Substituir chave", - "docsLink": "Ver documentos", + "docsLink": "Ver documentação", "opksshLabel": "OPKSSH", - "opksshDesc": "Faça login neste host utilizando o seu fornecedor de identidade em vez de uma palavra-passe ou chave. Requer a configuração do OPKSSH no servidor.", - "warpgateLabel": "Portal de Dobra", - "warpgateDesc": "Este host liga-se através de um proxy SSH Warpgate. O Termix tratará automaticamente do fluxo de aprovação baseado no browser após a autenticação.", + "opksshDesc": "Inicie sessão neste anfitrião usando o seu fornecedor de identidade em vez de uma palavra-passe ou chave. Requer que o OPKSSH esteja configurado no servidor.", + "warpgateLabel": "Warpgate Gateway", + "warpgateDesc": "Este anfitrião liga-se através de um proxy SSH Warpgate. O Termix irá gerir automaticamente o fluxo de aprovação baseado no navegador após a autenticação.", "agentLabel": "Agente SSH", - "agentDesc": "Autentique-se utilizando um agente SSH em execução no servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). O agente deve estar a ser executado na máquina onde o Termix está alojado.", + "agentDesc": "Autentique-se usando um agente SSH em execução no servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). O agente deve estar em execução na máquina onde o Termix está alojado.", "agentSocketPathLabel": "Caminho do socket do agente", - "agentSocketPathPlaceholder": "Deixe em branco para usar SSH_AUTH_SOCK", - "agentSocketPathHint": "Deixe em branco para deteção automática a partir da variável de ambiente SSH_AUTH_SOCK ou introduza um caminho de socket personalizado (por exemplo, /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "Selecione o dispositivo Tailscale", - "tailscaleDeviceSelectPlaceholder": "Selecione um dispositivo...", - "tailscaleNoApiKey": "Nenhuma chave API do Tailscale configurada. Adicione uma nas Definições de Administrador para ativar a descoberta de dispositivos.", - "tailscaleDocsLink": "Ver documentos", - "tailscaleLoadingDevices": "Dispositivos de carga...", - "tailscaleNoDevices": "Nenhum dispositivo encontrado na sua rede traseira.", - "tailscaleDeviceAutoFill": "Ao selecionar um dispositivo, o endereço IP do host será preenchido automaticamente.", - "forceKeyboardInteractiveLabel": "Teclado interativo Force", - "forceKeyboardInteractiveShortDesc": "Forçar a introdução manual da palavra-passe mesmo que as chaves estejam presentes.", - "allowLegacyAlgorithmsLabel": "Permitir algoritmos legados", - "allowLegacyAlgorithmsDesc": "Active os algoritmos SSH obsoletos (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) para ligações a dispositivos antigos que não podem ser atualizados.", + "agentSocketPathPlaceholder": "Deixar vazio para usar SSH_AUTH_SOCK", + "agentSocketPathHint": "Deixar vazio para detetar automaticamente a partir da variável de ambiente SSH_AUTH_SOCK, ou introduza um caminho de socket personalizado (ex.: /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Selecionar dispositivo Tailscale", + "tailscaleDeviceSelectPlaceholder": "Selecionar um dispositivo...", + "tailscaleNoApiKey": "Nenhuma chave de API Tailscale configurada. Adicione uma nas Definições de Administrador para ativar a descoberta de dispositivos.", + "tailscaleDocsLink": "Ver documentação", + "tailscaleLoadingDevices": "A carregar dispositivos...", + "tailscaleNoDevices": "Nenhum dispositivo encontrado na sua tailnet.", + "tailscaleDeviceAutoFill": "Selecionar um dispositivo preencherá automaticamente o endereço IP do host.", + "forceKeyboardInteractiveLabel": "Forçar Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Forçar a entrada manual da palavra-passe mesmo que existam chaves.", + "allowLegacyAlgorithmsLabel": "Permitir Algoritmos Legados", + "allowLegacyAlgorithmsDesc": "Ativar algoritmos SSH obsoletos (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) para ligações a dispositivos antigos que não podem ser atualizados.", "insecure": "Inseguro", - "terminalAppearance": "Aparência terminal", - "colorTheme": "Tema de cores", - "fontFamilyLabel": "Família de fontes", - "fontSizeLabel": "Tamanho da fonte", - "cursorStyleLabel": "Estilo do cursor", - "letterSpacingPx": "Espaçamento entre letras (px)", - "lineHeightLabel": "Altura da linha", - "bellStyleLabel": "Estilo sino", - "backspaceModeLabel": "Modo Backspace", - "cursorBlinking": "Cursor intermitente", - "cursorBlinkingDesc": "Active a animação de intermitência para o cursor do terminal.", - "rightClickSelectsWordLabel": "Clicar com o botão direito do rato seleciona a palavra", - "rightClickSelectsWordShortDesc": "Selecione a palavra sob o cursor clicando com o botão direito do rato.", - "backgroundImageLabel": "URL da imagem de fundo", + "terminalAppearance": "Aparência do Terminal", + "colorTheme": "Tema de Cores", + "fontFamilyLabel": "Família da Fonte", + "fontSizeLabel": "Tamanho da Fonte", + "cursorStyleLabel": "Estilo do Cursor", + "letterSpacingPx": "Espaçamento entre Letras (px)", + "lineHeightLabel": "Altura da Linha", + "bellStyleLabel": "Estilo do Alerta Sonoro", + "backspaceModeLabel": "Modo da Tecla Retrocesso", + "cursorBlinking": "Piscar do Cursor", + "cursorBlinkingDesc": "Ativar a animação de piscar para o cursor do terminal", + "rightClickSelectsWordLabel": "Clique Direito Seleciona Palavra", + "rightClickSelectsWordShortDesc": "Selecionar a palavra sob o cursor ao clicar com o botão direito", + "backgroundImageLabel": "URL da Imagem de Fundo", "backgroundImageDesc": "URL opcional para uma imagem de fundo do terminal", - "backgroundImageOpacityLabel": "Opacidade da imagem de fundo", - "customThemeColors": "Cores personalizadas", + "backgroundImageOpacityLabel": "Opacidade da Imagem de Fundo", + "customThemeColors": "Cores Personalizadas", "customThemeBackground": "Fundo", - "customThemeForeground": "Primeiro plano", + "customThemeForeground": "Primeiro Plano", "customThemeCursor": "Cursor", - "customThemeCursorAccent": "Acento do cursor", + "customThemeCursorAccent": "Destaque do Cursor", "customThemeSelection": "Seleção", "customThemeAnsiColors": "Cores ANSI", "customThemeBlack": "Preto", @@ -753,103 +798,113 @@ "customThemeMagenta": "Magenta", "customThemeCyan": "Ciano", "customThemeWhite": "Branco", - "customThemeBrightBlack": "Preto brilhante", - "customThemeBrightRed": "Vermelho vivo", - "customThemeBrightGreen": "Verde brilhante", - "customThemeBrightYellow": "Amarelo brilhante", - "customThemeBrightBlue": "Azul brilhante", - "customThemeBrightMagenta": "Magenta brilhante", - "customThemeBrightCyan": "Ciano brilhante", - "customThemeBrightWhite": "Branco brilhante", - "customThemeResetTooltip": "Restaurar as predefinições de fábrica", - "syntaxHighlightingLabel": "Realce de sintaxe", - "syntaxHighlightingDesc": "Colorir a saída do terminal (erros, caminhos, IPs, registos de data e hora)", - "syntaxHighlightingCategories": "Categorias em destaque", - "syntaxHighlightingCategoriesDesc": "Escolha que tipos de conteúdo colorir.", - "syntaxCategoryLogLevels": "Níveis de registo", - "syntaxCategoryLogLevelsDesc": "erro, aviso, informação, depuração, fatal", - "syntaxCategoryPaths": "Caminhos de Arquivo", - "syntaxCategoryPathsDesc": "/usr/share/doc, ~/ficheiro.txt", - "syntaxCategoryTimestamps": "Carimbos de data/hora", - "syntaxCategoryTimestampsDesc": "[12:34:56], 15/01/2024", + "customThemeBrightBlack": "Preto Brilhante", + "customThemeBrightRed": "Vermelho Brilhante", + "customThemeBrightGreen": "Verde Brilhante", + "customThemeBrightYellow": "Amarelo Brilhante", + "customThemeBrightBlue": "Azul Brilhante", + "customThemeBrightMagenta": "Magenta Brilhante", + "customThemeBrightCyan": "Ciano Brilhante", + "customThemeBrightWhite": "Branco Brilhante", + "customThemeResetTooltip": "Repor predefinições", + "savedThemesLabel": "Temas guardados", + "saveAsGlobalTheme": "Guardar como tema global", + "saveGlobalThemeNamePrompt": "Introduza um nome para este tema.", + "saveGlobalThemeSuccess": "Tema guardado", + "saveGlobalThemeError": "Falha ao guardar o tema", + "applyGlobalThemeTooltip": "Aplique este tema", + "deleteGlobalThemeTooltip": "Apagar este tema", + "noSavedThemes": "Ainda não há temas guardados.", + "syntaxHighlightingLabel": "Realce de Sintaxe", + "syntaxHighlightingDesc": "Colorir a saída do terminal (erros, caminhos, IPs, marcas temporais)", + "syntaxHighlightingCategories": "Categorias de Realce", + "syntaxHighlightingCategoriesDesc": "Escolher os tipos de conteúdo a colorir", + "syntaxCategoryLogLevels": "Níveis de Registo", + "syntaxCategoryLogLevelsDesc": "erro, aviso, info, depuração, fatal", + "syntaxCategoryPaths": "Caminhos de Ficheiros", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Marcas Temporais", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "Endereços IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URLs", - "syntaxCategoryUrlsDesc": "https://exemplo.com", - "syntaxCategoryNumbers": "Números rotulados", - "syntaxCategoryNumbersDesc": "porta 8080, saída 1, estado 404", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Números com Rótulo", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", "behaviorAndAdvanced": "Comportamento e Avançado", - "scrollbackBufferLabel": "Buffer de rolagem", - "scrollbackMaxLines": "Número máximo de linhas mantidas no histórico", - "sshAgentForwardingLabel": "Encaminhamento do agente SSH", - "sshAgentForwardingShortDesc": "Passe as suas chaves SSH locais para este host.", - "useSSHTitleLabel": "Utilizar o título da janela SSH", - "useSSHTitleDesc": "Atualize o título do separador com base no título da janela da shell, em vez do nome do host.", + "scrollbackBufferLabel": "Buffer de Retrocesso", + "scrollbackMaxLines": "Número máximo de linhas guardadas no histórico", + "sshAgentForwardingLabel": "Reencaminhamento do Agente SSH", + "sshAgentForwardingShortDesc": "Passar as suas chaves SSH locais para este anfitrião", + "useSSHTitleLabel": "Usar o Título da Janela SSH", + "useSSHTitleDesc": "Atualizar o título do separador a partir do título da janela do shell em vez do nome do host", "enableAutoMosh": "Ativar Auto-Mosh", - "enableAutoMoshDesc": "Prefira o Mosh ao SSH, se disponível.", - "enableAutoTmux": "Ativar o Auto-Tmux", - "enableAutoTmuxDesc": "Iniciar ou ligar automaticamente a uma sessão tmux", + "enableAutoMoshDesc": "Preferir Mosh a SSH, se disponível", + "enableAutoTmux": "Ativar Auto-Tmux", + "enableAutoTmuxDesc": "Iniciar ou anexar automaticamente a uma sessão tmux", "enableSessionLogging": "Registo de sessão", - "enableSessionLoggingDesc": "Registe a saída da sessão do terminal para revisão posterior.", + "enableSessionLoggingDesc": "Gravar a saída da sessão de terminal para revisão posterior", + "allowSessionSharing": "Permitir partilha de sessão", + "allowSessionSharingDesc": "Permitir que as sessões em direto neste servidor sejam partilhadas através de link ou com outros utilizadores.", "enableCommandHistory": "Histórico de comandos", - "enableCommandHistoryDesc": "Registe os comandos executados neste terminal para histórico e preenchimento automático.", - "linkClickBehaviorLabel": "Comportamento de clique em links", - "linkClickBehaviorDesc": "Controla o que acontece quando clica num link no terminal. Utilize 'Padrão' para seguir a configuração da aplicação.", - "linkClickBehaviorDefault": "Padrão (seguir as definições da aplicação)", - "linkClickBehaviorConfirm": "Exibir janela pop-up para abrir ou copiar", + "enableCommandHistoryDesc": "Gravar comandos executados neste terminal para histórico e autocompletar", + "linkClickBehaviorLabel": "Comportamento ao clicar em ligações", + "linkClickBehaviorDesc": "Controla o que acontece ao clicar numa ligação no terminal. Use 'Predefinido' para seguir a definição da aplicação.", + "linkClickBehaviorDefault": "Predefinido (seguir a definição da aplicação)", + "linkClickBehaviorConfirm": "Mostrar pop-up para abrir ou copiar", "linkClickBehaviorDirect": "Abrir diretamente", - "sudoPasswordAutoFillLabel": "Preenchimento automático de senha Sudo", - "sudoPasswordAutoFillShortDesc": "Fornecer automaticamente a password de sudo quando solicitado.", - "sudoPasswordAutoFillDesc": "Armazene uma palavra-passe de sudo para que as métricas do host, os prompts do terminal e outros possam executar comandos privilegiados automaticamente.", - "sudoPasswordLabel": "Senha de sudo", + "sudoPasswordAutoFillLabel": "Preenchimento automático da palavra-passe sudo", + "sudoPasswordAutoFillShortDesc": "Fornecer automaticamente a palavra-passe sudo quando solicitada", + "sudoPasswordAutoFillDesc": "Armazenar uma palavra-passe sudo para que as métricas do host, prompts do terminal e outros possam executar comandos com privilégios automaticamente.", + "sudoPasswordLabel": "Palavra-passe sudo", "environmentVariablesLabel": "Variáveis de ambiente", "addVariableBtn": "Adicionar variável", "noEnvVars": "Nenhuma variável de ambiente configurada.", - "fastScrollModifierLabel": "Modificador de rolagem rápida", - "fastScrollSensitivityLabel": "Sensibilidade de rolagem rápida", + "fastScrollModifierLabel": "Modificador de deslocamento rápido", + "fastScrollSensitivityLabel": "Sensibilidade do deslocamento rápido", "moshCommandLabel": "Comando Mosh", - "startupSnippetLabel": "Excerto de startup", + "startupSnippetLabel": "Fragmento de inicialização", "keepaliveIntervalLabel": "Intervalo de Keepalive (segundos)", - "maxKeepaliveMisses": "Max Keepalive falha", - "tunnelSettings": "Configurações do túnel", + "maxKeepaliveMisses": "Máximo de falhas de Keepalive", + "tunnelSettings": "Definições de túnel", "enableTunneling": "Ativar tunelamento", - "enableTunnelingDesc": "Ative a funcionalidade de túnel SSH para este host.", - "serverTunnelsSection": "Túneis de servidor", + "enableTunnelingDesc": "Ativar a funcionalidade de túnel SSH para este 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 num host acessível a partir deste).", - "tunnelModeRemoteDesc": "Reencaminhe uma porta no servidor remoto de volta para uma porta local na sua máquina.", - "tunnelModeDynamicDesc": "Crie um proxy SOCKS5 numa porta local para encaminhamento dinâmico de portas.", + "tunnelModeLocalDesc": "Reencaminhar uma porta local para uma porta no servidor remoto (ou um host acessível a partir dele).", + "tunnelModeRemoteDesc": "Reencaminhar uma porta no servidor remoto de volta para uma porta local na sua máquina.", + "tunnelModeDynamicDesc": "Criar um proxy SOCKS5 numa porta local para reencaminhamento dinâmico de portas.", "sameHost": "Este host (túnel direto)", - "endpointHost": "Anfitrião do ponto final", + "endpointHost": "Host de Destino", "endpointHostPlaceholder": "Mesmo host, host SSH ou host/IP acessível", - "endpointPort": "Porta de ponto final", - "bindHost": "Vincular host", - "sourcePort": "Porta de origem", - "maxRetries": "Número máximo de tentativas", - "retryIntervalS": "Intervalo de tentativas (s)", - "autoStartLabel": "Início automático", - "autoStartDesc": "Ligue este túnel automaticamente quando o host estiver carregado.", - "tunnelConnecting": "Túnel de ligação...", - "tunnelDisconnected": "Túnel desconectado", - "failedToConnectTunnel": "Falha ao ligar", + "endpointPort": "Porta de Destino", + "bindHost": "Host de Ligação", + "sourcePort": "Porta de Origem", + "maxRetries": "Tentativas Máximas", + "retryIntervalS": "Intervalo de Repetição (s)", + "autoStartLabel": "Início Automático", + "autoStartDesc": "Conectar automaticamente este túnel quando o host for carregado", + "tunnelConnecting": "Túnel a conectar...", + "tunnelDisconnected": "Túnel desligado", + "failedToConnectTunnel": "Falha ao conectar", "failedToDisconnectTunnel": "Falha ao desligar", - "dockerIntegration": "Integração do Docker", + "dockerIntegration": "Integração com Docker", "enableDockerMonitor": "Ativar Docker", - "enableDockerMonitorDesc": "Monitorize e gerencie contentores neste host via Docker.", - "containerRuntime": "Tempo de execução do contentor", - "containerRuntimeDesc": "Escolha a CLI utilizada para a gestão de contentores neste host.", + "enableDockerMonitorDesc": "Monitorizar e gerir contentores neste host via Docker", + "containerRuntime": "Runtime de Contentores", + "containerRuntimeDesc": "Escolha a CLI usada para a gestão de contentores neste host", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "Ativar monitor Tmux", - "enableTmuxMonitorDesc": "Apresente este host no Monitor Tmux e adicione as suas ações tmux à barra lateral.", + "enableTmuxMonitor": "Ativar Monitor Tmux", + "enableTmuxMonitorDesc": "Mostrar este host no Monitor Tmux e adicionar as suas ações tmux à barra lateral", "tabProxmox": "Proxmox", - "proxmoxIntegration": "Integração Proxmox", + "proxmoxIntegration": "Integração com Proxmox", "enableProxmox": "Ativar Proxmox", - "enableProxmoxDesc": "Marque este host como um nó Proxmox. Isto permite a descoberta e importação de máquinas virtuais diretamente deste host.", - "proxmoxDefaultAuthType": "Tipo de autenticação padrão", + "enableProxmoxDesc": "Marcar este host como um nó Proxmox. Permite a descoberta e importação de convidados diretamente a partir deste host.", + "proxmoxDefaultAuthType": "Tipo de Autenticação Padrão", "proxmoxDefaultAuthTypeDesc": "Método de autenticação aplicado aos hosts convidados importados. Escolha o tipo de autenticação que corresponde à forma como se liga aos seus convidados.", "authTypePassword": "Palavra-passe", "authTypeKey": "Chave SSH", @@ -857,316 +912,323 @@ "authTypeOpkssh": "OPKSSH", "authTypeNone": "Nenhum", "proxmoxDefaultCredential": "Credencial padrão", - "proxmoxDefaultCredentialDesc": "Credencial utilizada para hosts convidados importados quando o tipo de autenticação está definido como Credencial.", - "proxmoxWindowsDetection": "Detecção de Windows/RDP", - "proxmoxWindowsDetectionDesc": "Padrões de nomes separados por vírgulas que acionam o RDP em vez do SSH (sem distinção entre maiúsculas e minúsculas)", - "proxmoxDockerDetection": "Detecção de Docker", - "proxmoxDockerDetectionDesc": "Padrões de nomes separados por vírgulas que permitem ao Docker identificar máquinas virtuais.", - "proxmoxPreferredRanges": "Faixas de IP preferenciais", - "proxmoxPreferredRangesDesc": "Prefixos separados por vírgula por ordem de prioridade para a seleção de IP quando um convidado tem várias interfaces.", - "proxmoxAutoSync": "Sincronização automática de convidados", - "proxmoxAutoSyncDesc": "Descubra periodicamente este nó Proxmox e crie ou atualize hosts convidados importados enquanto a sua sessão estiver desbloqueada.", + "proxmoxDefaultCredentialDesc": "Credencial usada para os convidados importados quando o tipo de autenticação é definido como Credencial.", + "proxmoxWindowsDetection": "Deteção de Windows / RDP", + "proxmoxWindowsDetectionDesc": "Padrões de nomes separados por vírgulas que acionam RDP em vez de SSH (sem distinção entre maiúsculas/minúsculas)", + "proxmoxDockerDetection": "Deteção de Docker", + "proxmoxDockerDetectionDesc": "Padrões de nomes separados por vírgulas que ativam o Docker nos convidados que correspondam.", + "proxmoxPreferredRanges": "Intervalos de IP preferenciais", + "proxmoxPreferredRangesDesc": "Prefixos separados por vírgulas em ordem de prioridade para seleção de IP quando um convidado tem várias interfaces.", + "proxmoxAutoSync": "Sincronização automática", + "proxmoxAutoSyncDesc": "Descobrir periodicamente este nó Proxmox e criar ou atualizar os convidados importados enquanto a sessão estiver desbloqueada.", "proxmoxSyncInterval": "Intervalo de sincronização (minutos)", - "proxmoxSyncIntervalDesc": "Tempo mínimo de 5 minutos. O agendador ignora as sessões de utilizador bloqueadas.", - "proxmoxMarkMissing": "Marque os hóspedes ausentes", - "proxmoxMarkMissingDesc": "Adicione a etiqueta proxmox-missing quando uma máquina virtual previamente importada desaparecer, em vez de a eliminar.", + "proxmoxSyncIntervalDesc": "Mínimo de 5 minutos. O agendador ignora sessões de utilizador bloqueadas.", + "proxmoxMarkMissing": "Marcar convidados em falta", + "proxmoxMarkMissingDesc": "Adicionar a etiqueta proxmox-missing quando um convidado previamente importado desaparecer, em vez de o eliminar.", "proxmoxLastSync": "Última sincronização", "proxmoxLastSyncNever": "Ainda não sincronizado", - "proxmoxLastSyncNoResult": "Sem detalhe do resultado", - "proxmoxLastSyncSummary": "{{created}} criado, {{updated}} atualizado, {{markedMissing}} ausente, {{skipped}} ignorado", + "proxmoxLastSyncNoResult": "Sem detalhes do resultado", + "proxmoxLastSyncSummary": "{{created}} criados, {{updated}} atualizados, {{markedMissing}} em falta, {{skipped}} ignorados", "proxmoxLastSyncStatus": { "success": "Sucesso", "error": "Falhou", "pending": "Pendente" }, - "proxmoxDiscoverAction": "Descubra e importe hóspedes do Proxmox", + "proxmoxDiscoverAction": "Descobrir e importar convidados", "proxmoxImportTitle": "Importar do Proxmox", - "proxmoxSelectHost": "Selecione um host Proxmox…", + "proxmoxSelectHost": "Selecionar um host Proxmox…", "proxmoxDiscover": "Descobrir", - "proxmoxDiscovering": "Descobrir…", - "proxmoxDiscoverGuests": "Descubra os hóspedes", + "proxmoxDiscovering": "A descobrir…", + "proxmoxDiscoverGuests": "Descobrir convidados", "proxmoxGuestsSelected_one": "{{count}} convidado — {{selected}} selecionado", "proxmoxGuestsSelected_other": "{{count}} convidados — {{selected}} selecionados", - "proxmoxSelectAll": "Selecionar tudo", - "proxmoxDeselectAll": "Desmarque tudo", + "proxmoxSelectAll": "Selecionar todos", + "proxmoxDeselectAll": "Desmarcar todos", "proxmoxNoGuests": "Nenhum convidado encontrado neste nó Proxmox.", "proxmoxImportButton_one": "Importar {{count}} host", "proxmoxImportButton_other": "Importar {{count}} hosts", - "proxmoxResultImported": "{{count}} importado", - "proxmoxResultUpdated": "{{count}} atualizado", - "proxmoxResultFailed": "{{count}} falhou", - "proxmoxImportComplete": "Importação do Proxmox concluída: {{summary}}", - "proxmoxDiscoveryFailed": "A descoberta falhou", - "proxmoxImportFailed": "A importação falhou", - "enableFileManagerMonitor": "Ativar o Gestor de Ficheiros", - "enableFileManagerMonitorDesc": "Navegue e gerencie ficheiros neste host via SFTP.", - "scpLegacyLabel": "Modo Legado SCP", - "scpLegacyDesc": "Utilize a transferência de ficheiros legacy para servidores que não suportam o subsistema SFTP (por exemplo, servidores SSH incorporados ou mínimos).", - "defaultPathLabel": "Caminho padrão", - "fileManagerPathHint": "O diretório a abrir quando o gestor de ficheiros for iniciado para este host.", - "statusChecksLabel": "Verificações de estado", - "enableStatusChecks": "Ativar verificações de estado", - "enableStatusChecksDesc": "Periodicamente, faça ping a este host para verificar a disponibilidade.", - "useGlobalInterval": "Usar intervalo global", - "useGlobalIntervalDesc": "Substitua pelo intervalo de verificação de estado de todo o servidor.", - "checkIntervalS": "Intervalo de verificação(s)", + "proxmoxResultImported": "{{count}} importados", + "proxmoxResultUpdated": "{{count}} atualizados", + "proxmoxResultFailed": "{{count}} falhados", + "proxmoxResultSkippedNoIp": "{{count}} ignorado (nenhum IP encontrado)", + "proxmoxImportComplete": "Importação Proxmox concluída: {{summary}}", + "proxmoxDiscoveryFailed": "Descoberta falhou", + "proxmoxImportFailed": "Importação falhou", + "enableFileManagerMonitor": "Ativar Gestor de Ficheiros", + "enableFileManagerMonitorDesc": "Explorar e gerir ficheiros neste host via SFTP", + "scpLegacyLabel": "Modo SCP Legado", + "scpLegacyDesc": "Utilizar transferência de ficheiros legada para servidores que não suportam o subsistema SFTP (ex.: servidores SSH embebidos ou mínimos)", + "defaultPathLabel": "Caminho Predefinido", + "fileManagerPathHint": "O diretório a abrir quando o gestor de ficheiros for iniciado para este host", + "statusChecksLabel": "Verificações de Estado", + "enableStatusChecks": "Ativar Verificações de Estado", + "enableStatusChecksDesc": "Fazer ping periodicamente a este host para verificar a disponibilidade", + "useGlobalInterval": "Usar Intervalo Global", + "useGlobalIntervalDesc": "Substituir pelo intervalo de verificação de estado global do servidor", + "checkIntervalS": "Intervalo de Verificação (s)", "checkIntervalDesc": "Segundos entre cada ping de conectividade", - "metricsCollectionLabel": "Recolha de métricas", - "enableMetricsLabel": "Ativar métricas", - "enableMetricsDesc": "Recolher métricas de CPU, RAM, disco e outras deste host.", - "useGlobalMetrics": "Usar intervalo global", - "useGlobalMetricsDesc": "Substituir pelo intervalo de métricas de todo o servidor", - "metricsIntervalS": "Intervalo de métricas(s)", - "metricsIntervalDesc2": "Segundos entre instantâneos de métricas", + "metricsCollectionLabel": "Recolha de Métricas", + "enableMetricsLabel": "Ativar Métricas", + "enableMetricsDesc": "Recolher métricas de CPU, RAM, disco e outras deste host", + "useGlobalMetrics": "Usar Intervalo Global", + "useGlobalMetricsDesc": "Substituir pelo intervalo de métricas global do servidor", + "metricsIntervalS": "Intervalo de Métricas (s)", + "metricsIntervalDesc2": "Segundos entre cada registo de métricas", "visibleWidgets": "Widgets Visíveis", - "widgetsMovedToHostMetrics": "Agora, os cartões podem ser adicionados, organizados e redimensionados diretamente no separador Métricas do Anfitrião. Abra as Métricas do Host para este host e utilize a opção Personalizar para escolher quais os cartões que serão apresentados.", + "widgetsMovedToHostMetrics": "Os cartões são agora adicionados, organizados e redimensionados diretamente no separador Métricas do Host. Abra as Métricas do Host para este host e use Personalizar para escolher quais os cartões a mostrar.", "cpuUsageLabel": "Utilização da CPU", - "cpuUsageDesc": "Percentagem de CPU, médias de carga, gráfico de linhas", - "memoryLabel": "Uso de memória", - "memoryDesc": "Utilização de RAM, swap, cache", - "storageLabel": "Utilização do disco", - "storageDesc": "Utilização de disco por ponto de montagem", - "networkLabel": "Interfaces de rede", + "cpuUsageDesc": "Percentagem da CPU, médias de carga, gráfico de linha", + "memoryLabel": "Utilização da Memória", + "memoryDesc": "Utilização da RAM, swap, em cache", + "storageLabel": "Utilização do Disco", + "storageDesc": "Utilização do disco por ponto de montagem", + "networkLabel": "Interfaces de Rede", "networkDesc": "Lista de interfaces e largura de banda", - "uptimeLabel": "Tempo de atividade", - "uptimeDesc": "Tempo de atividade do sistema e tempo de arranque", - "systemInfoLabel": "Informações do sistema", - "systemInfoDesc": "SO, kernel, nome do host, arquitetura", - "recentLoginsLabel": "Logins recentes", - "recentLoginsDesc": "Eventos de login bem-sucedido e malsucedido", - "topProcessesLabel": "Principais processos", + "uptimeLabel": "Tempo de Atividade", + "uptimeDesc": "Tempo de atividade do sistema e hora de arranque", + "systemInfoLabel": "Informação do Sistema", + "systemInfoDesc": "SO, kernel, nome de máquina, arquitetura", + "recentLoginsLabel": "Inícios de Sessão Recentes", + "recentLoginsDesc": "Eventos de início de sessão bem-sucedidos e falhados", + "topProcessesLabel": "Principais Processos", "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Portas de escuta", + "listeningPortsLabel": "Portas à Escuta", "listeningPortsDesc": "Portas abertas com processo e estado", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, estado do SELinux", - "quickActionsLabel": "Ações rápidas", - "quickActionsToolbar": "As ações rápidas aparecem como botões na barra de ferramentas do Host Metrics para a execução de comandos com um único clique.", - "noQuickActions": "Nenhuma ação imediata ainda.", - "buttonLabel": "Rótulo do botão", - "selectSnippetPlaceholder": "Selecione o excerto...", - "addActionBtn": "Adicionar ação", - "hostSharedSuccessfully": "O anfitrião partilhou com sucesso.", + "firewallDesc": "Estado do Firewall, AppArmor, SELinux", + "quickActionsLabel": "Ações Rápidas", + "quickActionsToolbar": "As ações rápidas aparecem como botões na barra de ferramentas de Métricas do Host para execução de comandos com um clique.", + "noQuickActions": "Ainda não existem ações rápidas.", + "buttonLabel": "Texto do botão", + "selectSnippetPlaceholder": "Selecionar snippet...", + "addActionBtn": "Adicionar Ação", + "hostSharedSuccessfully": "Host partilhado com sucesso", "failedToShareHost": "Falha ao partilhar o host", "accessRevoked": "Acesso revogado", "failedToRevokeAccess": "Falha ao revogar o acesso", "cancelBtn": "Cancelar", - "savingBtn": "Salvando...", - "addHostBtn": "Adicionar host", - "hostUpdated": "O Host atualizou", - "hostCreated": "O anfitrião criou", + "savingBtn": "A guardar...", + "addHostBtn": "Adicionar Host", + "hostUpdated": "Host atualizado", + "hostCreated": "Host criado", "failedToSave": "Falha ao guardar o host", "credentialUpdated": "Credencial atualizada", "credentialCreated": "Credencial criada", - "failedToSaveCredential": "Falha ao guardar as credenciais", - "credentialNameRequired": "Por favor, introduza um nome para a credencial.", - "credentialAuthRequired": "Adicione uma palavra-passe, uma chave SSH ou ambas.", - "backToHosts": "Voltar para Anfitriões", - "backToCredentials": "Voltar às credenciais", + "failedToSaveCredential": "Falha ao guardar a credencial", + "credentialNameRequired": "Introduza um nome para a credencial", + "credentialAuthRequired": "Adicione uma palavra-passe, uma chave SSH, ou ambas", + "createCredentialFromHostBtn": "Criar credencial", + "createCredentialFromHostTitle": "Criar credencial a partir do host", + "createCredentialFromHostDesc": "Crie uma entrada de credencial reutilizável e partilhável, previamente preenchida com o nome de utilizador, palavra-passe e/ou chave SSH atuais deste host.", + "backToHosts": "Voltar aos hosts", + "backToCredentials": "Voltar às Credenciais", "pinned": "Fixado", "noHostsFound": "Nenhum host encontrado", - "tryDifferentTerm": "Experimente um termo diferente.", - "addFirstHost": "Adicione o seu primeiro host para começar.", + "tryDifferentTerm": "Experimente um termo diferente", + "addFirstHost": "Adicione o seu primeiro host para começar", "noCredentialsFound": "Nenhuma credencial encontrada", - "addCredentialBtn": "Adicionar credencial", - "updateCredentialBtn": "Atualizar credenciais", + "addCredentialBtn": "Adicionar Credencial", + "updateCredentialBtn": "Atualizar Credencial", "features": "Funcionalidades", - "noFolder": "(Sem pasta)", + "noFolder": "(Nenhuma pasta)", "deleteSelected": "Eliminar", - "exitSelection": "Seleção de saída", - "importSkip": "Importar (ignorar existente)", - "importOverwrite": "Importar (sobrescrever)", - "collapseBtn": "Colapso", - "importExportBtn": "Importação/Exportação", - "hostStatusesRefreshed": "Estado dos hosts atualizados", - "failedToRefreshHosts": "Falha ao atualizar os hosts", - "movedHostTo": "Movido {{host}} para \"{{folder}}\"", - "failedToMoveHost": "Falha ao mover o host", + "exitSelection": "Sair da seleção", + "importSkip": "Importar (ignorar existentes)", + "importOverwrite": "Importar (substituir)", + "collapseBtn": "Recolher", + "importExportBtn": "Importar / Exportar", + "hostStatusesRefreshed": "Estados dos hosts atualizados", + "failedToRefreshHosts": "Falha ao atualizar hosts", + "movedHostTo": "Moveu {{host}} para \"{{folder}}\"", + "failedToMoveHost": "Falha ao mover host", "folderRenamedTo": "Pasta renomeada para \"{{name}}\"", - "deletedFolder": "Pasta eliminada \"{{name}}\"", - "failedToDeleteFolder": "Falha ao eliminar a pasta", - "deleteAllInFolder": "Apagar todos os hosts em \"{{name}}\"? Esta ação não pode ser anulada.", - "folderPickerPlaceholder": "Sem pasta", - "folderPickerSearch": "Pesquise ou crie (utilize / para subpastas)...", - "folderPickerNone": "Sem pasta", + "deletedFolder": "Pasta \"{{name}}\" eliminada", + "failedToDeleteFolder": "Falha ao eliminar pasta", + "deleteAllInFolder": "Eliminar todos os hosts em \"{{name}}\"? Esta ação não pode ser desfeita.", + "folderPickerPlaceholder": "Nenhuma pasta", + "folderPickerSearch": "Pesquisar ou criar (use / para subpastas)...", + "folderPickerNone": "Nenhuma pasta", "folderPickerCreate": "Criar \"{{path}}\"", "folderPickerEmpty": "Nenhuma pasta correspondente", "newFolder": "Nova pasta", "createFolderTitle": "Criar pasta", - "editFolderTitle": "Pasta de edição", - "folderDialogDescription": "Escolha um nome, uma cor e um ícone. Utilize / para aninhar pastas.", + "editFolderTitle": "Editar pasta", + "folderDialogDescription": "Escolha um nome, cor e ícone. Use / para aninhar pastas.", "folderNameLabel": "Nome da pasta", - "folderNamePlaceholder": "Exemplo: Produção/Web", + "folderNamePlaceholder": "ex.: Produção/Web", "folderNestingHint": "Utilize / para separar níveis e criar pastas aninhadas.", "folderColor": "Cor", "folderIcon": "Ícone", + "folderCredential": "Credencial", + "folderCredentialNone": "Nenhuma credencial atribuída", + "folderCredentialHint": "Os hosts nesta pasta que utilizem autenticação com \"Credenciais armazenadas\" sem que as suas próprias credenciais estejam seleccionadas herdarão esta.", "folderPreview": "Pré-visualização", "folderNameFallback": "Pasta sem título", "createFolderButton": "Criar pasta", - "saveFolderButton": "Pasta de salvamento", + "saveFolderButton": "Guardar pasta", "cancel": "Cancelar", - "iconSearchPlaceholder": "Ícones de pesquisa...", + "iconSearchPlaceholder": "Pesquisar ícones...", "openAllSessions": "Abrir todas as sessões", - "editFolder": "Pasta de edição", - "deleteFolder": "Apagar pasta", + "editFolder": "Editar pasta", + "deleteFolder": "Eliminar pasta", "folderSaved": "Pasta guardada", - "failedToSaveFolder": "Falha ao guardar a pasta", - "folderDeleted": "Pasta eliminada \"{{name}}\"", - "deleteFolderConfirm": "Apagar \"{{name}}\" e os seus {{count}} hosts? Esta ação não pode ser anulada.", - "failedToMoveHosts": "Falha ao mover os hosts", + "failedToSaveFolder": "Falha ao guardar pasta", + "folderDeleted": "Pasta \"{{name}}\" eliminada", + "deleteFolderConfirm": "Eliminar \"{{name}}\" e os seus {{count}} host(s)? Esta ação não pode ser anulada.", + "failedToMoveHosts": "Falha ao mover hosts", "expandAll": "Expandir todas as pastas", "collapseAll": "Recolher todas as pastas", "moreActions": "Mais", "groupBy": "Agrupar por", "GroupByFolder": "Pasta", "GroupByTag": "Etiqueta", - "GroupByStatus": "Estatuto", + "GroupByStatus": "Estado", "GroupByProtocol": "Protocolo", "GroupByAuth": "Tipo de autenticação", "groupUngrouped": "Não agrupados", - "deletedHost": "Eliminado {{name}}", + "deletedHost": "{{name}} eliminado", "copiedToClipboard": "Copiado para a área de transferência", - "terminalUrlCopied": "URL do terminal copiado", - "fileManagerUrlCopied": "URL do Gestor de Ficheiros copiado", - "tunnelUrlCopied": "URL do túnel copiado", - "dockerUrlCopied": "URL do Docker copiado", - "hostMetricsUrlCopied": "URL do Host Metrics copiado", - "tmuxMonitorUrlCopied": "URL do monitor Tmux copiado", + "terminalUrlCopied": "URL do terminal copiada", + "fileManagerUrlCopied": "URL do gestor de ficheiros copiada", + "tunnelUrlCopied": "URL do túnel copiada", + "dockerUrlCopied": "URL do Docker copiada", + "hostMetricsUrlCopied": "URL das métricas do host copiada", + "tmuxMonitorUrlCopied": "URL do Monitor Tmux copiado", "rdpUrlCopied": "URL RDP copiado", "vncUrlCopied": "URL VNC copiado", - "telnetUrlCopied": "URL do Telnet copiado", - "remoteDesktopUrlCopied": "URL da Área de Trabalho Remota copiado", + "telnetUrlCopied": "URL Telnet copiado", + "remoteDesktopUrlCopied": "URL do Ambiente de Trabalho Remoto copiado", "expandActions": "Expandir ações", - "collapseActions": "Ações de cobrança", + "collapseActions": "Recolher ações", "wakeOnLanAction": "Wake on LAN", "wakeOnLanSuccess": "Pacote mágico enviado para {{name}}", - "wakeOnLanError": "Falha ao enviar o pacote mágico", - "cloneHostAction": "Anfitrião de clonagem", + "wakeOnLanError": "Falha ao enviar pacote mágico", + "cloneHostAction": "Clonar host", "copyAddress": "Copiar endereço", "copyLink": "Copiar link", - "copyTerminalUrlAction": "Copiar URL do terminal", + "copyTerminalUrlAction": "Copiar URL do Terminal", "copyFileManagerUrlAction": "Copiar URL do Gestor de Ficheiros", - "copyTunnelUrlAction": "Copiar URL do túnel", + "copyTunnelUrlAction": "Copiar URL do Túnel", "copyDockerUrlAction": "Copiar URL do Docker", - "copyHostMetricsUrlAction": "Copiar URL das métricas do host", - "copyTmuxMonitorUrlAction": "Copiar URL do Tmux Monitor", + "copyHostMetricsUrlAction": "Copiar URL das Métricas do host", + "copyTmuxMonitorUrlAction": "Copiar URL do Monitor Tmux", "copyRdpUrlAction": "Copiar URL RDP", - "copyVncUrlAction": "Copiar URL do VNC", - "copyTelnetUrlAction": "Copiar URL do Telnet", - "copyRemoteDesktopUrlAction": "Copiar URL da Área de Trabalho Remota", - "deleteCredentialConfirm": "Apagar credencial \"{{name}}\"?", - "deletedCredential": "Eliminado {{name}}", - "deploySSHKeyTitle": "Implantar chave SSH", - "deployingBtn": "Implantação...", - "deployBtn": "Colocar", - "failedToDeployKey": "Falha ao implantar a chave", - "deleteHostsConfirm": "Apagar {{count}} host{{plural}}? Isso não pode ser desfeito.", + "copyVncUrlAction": "Copiar URL VNC", + "copyTelnetUrlAction": "Copiar URL Telnet", + "copyRemoteDesktopUrlAction": "Copiar URL do Ambiente de Trabalho Remoto", + "deleteCredentialConfirm": "Eliminar credencial \"{{name}}\"?", + "deletedCredential": "Eliminada {{name}}", + "deploySSHKeyTitle": "Implementar chave SSH", + "deployingBtn": "A implementar...", + "deployBtn": "Implementar", + "failedToDeployKey": "Falha ao implementar chave", + "deleteHostsConfirm": "Eliminar {{count}} host{{plural}}? Esta ação não pode ser desfeita.", "movedToRoot": "Movido para a raiz", - "enableTerminalFeature": "Ativar terminal", - "disableTerminalFeature": "Desativar terminal", - "enableFilesFeature": "Ativar arquivos", - "disableFilesFeature": "Desativar ficheiros", - "enableTunnelsFeature": "Ativar túneis", - "disableTunnelsFeature": "Desativar túneis", + "enableTerminalFeature": "Ativar Terminal", + "disableTerminalFeature": "Desativar Terminal", + "enableFilesFeature": "Ativar Ficheiros", + "disableFilesFeature": "Desativar Ficheiros", + "enableTunnelsFeature": "Ativar Tunnels", + "disableTunnelsFeature": "Desativar Tunnels", "enableDockerFeature": "Ativar Docker", - "disableDockerFeature": "Desativar o Docker", + "disableDockerFeature": "Desativar Docker", "enableProxmoxFeature": "Ativar Proxmox", "disableProxmoxFeature": "Desativar Proxmox", - "addTagsPlaceholder": "Adicionar etiquetas...", + "addTagsPlaceholder": "Adicionar tags...", "authDetails": "Detalhes de autenticação", "credType": "Tipo", - "generateKeyPairDesc": "Gere um novo par de chaves; as chaves privada e pública serão preenchidas automaticamente.", - "generatingKey": "Gerando...", + "generateKeyPairDesc": "Gerar um novo par de chaves; as chaves privada e pública serão preenchidas automaticamente.", + "generatingKey": "A gerar...", "generateLabel": "Gerar {{label}}", "uploadFileBtn": "Carregar ficheiro", - "keyPassphraseOptional": "Palavra-chave de acesso (opcional)", - "sshPublicKeyOptional": "Chave pública SSH (opcional)", + "keyPassphraseOptional": "Frase-senha da chave (Opcional)", + "sshPublicKeyOptional": "Chave pública SSH (Opcional)", "publicKeyGenerated": "Chave pública gerada", "failedToGeneratePublicKey": "Falha ao derivar a chave pública", "publicKeyCopied": "Chave pública copiada", - "keyPairGenerated": "{{label}} par de chaves gerado", - "failedToGenerateKeyPair": "Falha ao gerar o par de chaves", - "searchHostsPlaceholder": "Pesquisar hosts, endereços, tags…", - "searchCredentialsPlaceholder": "Credenciais de pesquisa…", + "keyPairGenerated": "Par de chaves {{label}} gerado", + "failedToGenerateKeyPair": "Falha ao gerar par de chaves", + "searchHostsPlaceholder": "Pesquisar anfitriões, endereços, tags…", + "searchCredentialsPlaceholder": "Pesquisar credenciais…", "refreshBtn": "Atualizar", - "addTag": "Adicionar etiquetas...", + "addTag": "Adicionar tags...", "deleteConfirmBtn": "Eliminar", - "tunnelRequirementsText": "O servidor SSH deve ter as seguintes definições definidas em /etc/ssh/sshd_config: GatewayPorts yes, AllowTcpForwarding yes e PermitRootLogin yes.", - "deleteHostConfirm": "Apagar \"{{name}}\"?", - "enableAtLeastOneProtocol": "Active pelo menos um dos protocolos acima referidos para configurar as definições de autenticação e ligação.", - "keyPassphrase": "Senha da chave", + "tunnelRequirementsText": "O servidor SSH deve ter GatewayPorts yes, AllowTcpForwarding yes e PermitRootLogin yes definidos em /etc/ssh/sshd_config.", + "deleteHostConfirm": "Eliminar \"{{name}}\"?", + "enableAtLeastOneProtocol": "Ative pelo menos um protocolo acima para configurar as definições de autenticação e ligação.", + "keyPassphrase": "Frase-senha da chave", "connectBtn": "Ligar", - "disconnectBtn": "Desconectar", - "basicInformation": "Informações básicas", + "disconnectBtn": "Desligar", + "basicInformation": "Informação básica", "authDetailsSection": "Detalhes de autenticação", "credTypeLabel": "Tipo", "hostsTab": "Anfitriões", "credentialsTab": "Credenciais", - "selectMultiple": "Selecione várias opções", - "selectHosts": "Selecione os anfitriões", - "connectionLabel": "Conexão", + "selectMultiple": "Selecionar vários", + "selectHosts": "Selecionar hosts", + "connectionLabel": "Ligação", "authenticationLabel": "Autenticação", "generateKeyPairTitle": "Gerar par de chaves", - "generateKeyPairDescription": "Gere um novo par de chaves; as chaves privada e pública serão preenchidas automaticamente.", + "generateKeyPairDescription": "Gerar um novo par de chaves, as chaves privada e pública serão preenchidas automaticamente.", "generateFromPrivateKey": "Gerar a partir da chave privada", "refreshBtn2": "Atualizar", - "exitSelectionTitle": "Seleção de saída", + "exitSelectionTitle": "Sair da seleção", "exportAll": "Exportar tudo", "exportForSharing": "Exportar para partilha", "addHostBtn2": "Adicionar host", "addCredentialBtn2": "Adicionar credencial", "checkingHostStatuses": "A verificar o estado dos hosts...", - "pinnedSection": "Fixado", - "hostsExported": "Os hosts foram exportados com sucesso.", + "pinnedSection": "Fixados", + "hostsExported": "Hosts exportados com sucesso", "hostsShareExported": "Hosts partilháveis exportados com sucesso", "exportFailed": "Falha ao exportar hosts", - "sampleDownloaded": "Ficheiro de amostra baixado", - "failedToDeleteCredential2": "Falha ao eliminar credencial", + "sampleDownloaded": "Ficheiro de exemplo descarregado", + "failedToDeleteCredential2": "Falha ao eliminar a credencial", "noFolderOption": "(Sem pasta)", - "nSelected": "{{count}} selecionado", + "nSelected": "{{count}} selecionado(s)", "featuresMenu": "Funcionalidades", "moveMenu": "Mover", "connectSelected": "Ligar", "cancelSelection": "Cancelar", - "deployDialogDesc": "Implemente {{name}} nas chaves autorizadas de um host.", - "targetHostLabel": "Anfitrião alvo", - "selectHostOption": "Selecione um host...", - "keyDeployedSuccess": "Chave implantada com sucesso", - "failedToDeployKey2": "Falha ao implantar a chave", - "deletedCount": "Hosts eliminados {{count}}", + "deployDialogDesc": "Implementar {{name}} no ficheiro authorized_keys de um host.", + "targetHostLabel": "Host de destino", + "selectHostOption": "Selecionar um host...", + "keyDeployedSuccess": "Chave implementada com sucesso", + "failedToDeployKey2": "Falha ao implementar a chave", + "deletedCount": "Eliminados {{count}} hosts", "failedToDeleteCount": "Falha ao eliminar {{count}} hosts", "duplicatedHost": "Duplicado \"{{name}}\"", "failedToDuplicateHost": "Falha ao duplicar o host", - "updatedCount": "Hosts atualizados {{count}}", - "friendlyNameLabel": "Nome amigável", + "updatedCount": "Atualizados {{count}} hosts", + "friendlyNameLabel": "Nome Amigável", "descriptionLabel": "Descrição", - "loadingHost": "Carregando host...", - "loadingHosts": "Carregando hosts...", + "loadingHost": "A carregar o host...", + "loadingHosts": "A carregar hosts...", "loadingCredentials": "A carregar credenciais...", - "noHostsYet": "Ainda não há anfitriões.", - "noHostsMatchSearch": "Nenhum host corresponde à sua pesquisa.", + "noHostsYet": "Ainda sem hosts", + "noHostsMatchSearch": "Nenhum host corresponde à sua pesquisa", "hostNotFound": "Host não encontrado", "searchHosts": "Pesquisar hosts...", - "sortHosts": "Classificar hosts", - "sortDefault": "Ordem padrão", + "sortHosts": "Ordenar 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", + "sortIpDesc": "Endereço IP (Desc)", + "sortOnlineFirst": "Online primeiro", + "sortOfflineFirst": "Offline primeiro", + "sortPinnedFirst": "Fixados primeiro", + "filterHosts": "Filtrar Hosts", + "filterClearAll": "Limpar Filtros", + "filterStatusGroup": "Estado", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fixado", - "filterAuthGroup": "Tipo de autenticação", + "filterPinned": "Fixados", + "filterAuthGroup": "Tipo de Autenticação", "filterAuthPassword": "Palavra-passe", "filterAuthKey": "Chave SSH", "filterAuthCredential": "Credencial", - "filterAuthNone": "Nenhum", + "filterAuthNone": "Nenhuma", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocolo", "filterProtocolSsh": "SSH", @@ -1174,37 +1236,41 @@ "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", "filterFeaturesGroup": "Funcionalidades", - "filterFeatureTerminal": "terminal", - "filterFeatureFileManager": "Gestor de arquivos", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "Gestor de Ficheiros", "filterFeatureTunnel": "Túnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Etiquetas", - "shareHost": "Partilhar Host", + "shareHost": "Partilhar Anfitrião", "shareHostTitle": "Partilhar: {{name}}", + "shareFolder": "Pasta partilhada", + "shareFolderTitle": "Pasta partilhada: {{name}}", + "folderSharedSuccessfully": "Host(ns) partilhado(s) {{count}} na pasta", + "failedToShareFolder": "Falha ao partilhar a pasta", "sharing": { - "loadError": "Não foi possível carregar os dados de partilha. Tente novamente.", - "shareWithSection": "Partilhe com", - "usersTab": "Usuários", + "loadError": "Erro ao carregar dados de partilha. Tente novamente.", + "shareWithSection": "Partilhar com", + "usersTab": "Utilizadores", "rolesTab": "Funções", - "searchPlaceholder": "Pesquisar utilizadores ou funções...", + "searchPlaceholder": "Procurar utilizadores ou funções...", "noMatches": "Nenhuma correspondência encontrada", "permissionLevelLabel": "Nível de permissão", "levels": { "connect": { "label": "Ligar", - "description": "Apenas sessões abertas: terminal, ambiente de trabalho remoto, gestor de ficheiros, túneis e Docker. Sem acesso à configuração do host." + "description": "Abrir apenas sessões: terminal, ambiente de trabalho remoto, gestor de ficheiros, túneis e Docker. Sem acesso à configuração do anfitrião." }, "view": { "label": "Ver", - "description": "Ligue-se e veja a configuração do host. Os segredos nunca são exibidos." + "description": "Ligar e ver a configuração do anfitrião. Os segredos nunca são mostrados." }, "edit": { "label": "Editar", - "description": "Visualize e modifique o host. Os segredos podem ser substituídos, mas nunca lidos; as atribuições de credenciais permanecem exclusivas do proprietário." + "description": "Ver e modificar o anfitrião. Os segredos podem ser substituídos, mas nunca lidos; as atribuições de credenciais permanecem apenas para o proprietário." }, "manage": { "label": "Gerir", - "description": "Edite, partilhe o host com outras pessoas, altere os níveis de permissão e revogue o acesso." + "description": "Editar, além de partilhar o anfitrião com outras pessoas, alterar os níveis de permissão e revogar o acesso." } }, "expiryLabel": "Expiração do acesso", @@ -1216,11 +1282,12 @@ "thirtyDays": "30 dias", "custom": "Personalizado" }, - "customHoursPlaceholder": "Horas restantes até o acesso expirar", + "customHoursPlaceholder": "Horas até à expiração do acesso", "shareButton": "Partilhar", "shareWithCount": "Partilhar ({{count}})", "currentAccess": "Acesso atual", - "noAccessEntries": "Este host ainda não foi partilhado.", + "noAccessEntries": "Este anfitrião ainda não foi partilhado", + "folderShareSummary": "Partilhado {{shared}} de {{total}} hosts nesta pasta", "grantedBy": "Concedido por", "expires": "Expira", "expired": "Expirado", @@ -1228,37 +1295,40 @@ "revoke": "Revogar", "accessUpdated": "Acesso atualizado", "accessUpdateFailed": "Falha ao atualizar o acesso", - "sharedBadge": "Compartilhado", - "sharedBadgeTooltip": "Partilhado por {{owner}} ({{level}} acesso)", - "viewOnlyBanner": "Este host foi partilhado consigo por {{owner}} com acesso de visualização. A configuração é de leitura apenas.", - "sharedEditBanner": "Este host foi partilhado consigo por {{owner}} com acesso de edição. As alterações aplicam-se ao host real; as referências de autenticação só podem ser alteradas pelo proprietário.", - "ownerOnlyControl": "Apenas o proprietário do host pode alterar isso." + "sharedBadge": "Partilhado", + "sharedBadgeTooltip": "Partilhado por {{owner}} (acesso de {{level}})", + "viewOnlyBanner": "Este anfitrião é partilhado consigo por {{owner}} com acesso de visualização. A configuração é só de leitura.", + "sharedEditBanner": "Este anfitrião é partilhado consigo por {{owner}} com acesso de edição. As alterações aplicam-se ao anfitrião real; as referências de autenticação só podem ser alteradas pelo proprietário.", + "ownerOnlyControl": "Apenas o proprietário do anfitrião pode alterar isto" }, "guac": { - "connection": "Conexão", + "connection": "Ligação", "authentication": "Autenticação", - "storedCredential": "Credencial armazenada", - "noCredential": "Sem credenciais (credenciais diretas abaixo)", - "authMethod": "Método de autenticação", - "authTypeDirect": "Direto", + "storedCredential": "Credencial Armazenada", + "noCredential": "Sem credencial (credenciais diretas abaixo)", + "authMethod": "Método de Autenticação", + "authTypeDirect": "Direta", "authTypeCredential": "Credencial", - "selectCredential": "Selecione uma credencial...", - "connectionSettings": "Configurações de ligação", - "displaySettings": "Configurações de visualização", - "audioSettings": "Configurações de áudio", + "authTypeNone": "Nenhum", + "authTypeNoneDesc": "Nenhuma credencial é armazenada. Ser-lhe-á pedido que insira um nome de utilizador e uma senha sempre que se conectar; não são salvos.", + "selectCredential": "Selecionar uma credencial...", + "connectionSettings": "Definições de Ligação", + "displaySettings": "Definições de Ecrã", + "audioSettings": "Definições de Áudio", "rdpPerformance": "Desempenho do RDP", - "deviceRedirection": "Redireccionamento de Dispositivos", + "deviceRedirection": "Redirecionamento de Dispositivos", "session": "Sessão", - "gateway": "Portal", - "remoteApp": "Aplicativo remoto", - "clipboard": "Área de transferência", - "sessionRecording": "Gravação da sessão", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Área de Transferência", + "sessionRecording": "Gravação de Sessão", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Configurações VNC", - "terminalSettings": "Configurações do terminal", + "vncSettings": "Definições VNC", + "terminalSettings": "Definições do Terminal", "rdpPort": "Porta RDP", "username": "Nome de utilizador", "password": "Palavra-passe", + "passwordSaved": "Palavra-passe guardada, digite para alterar", "domain": "Domínio", "securityMode": "Modo de segurança", "colorDepth": "Profundidade de cor", @@ -1267,512 +1337,575 @@ "dpi": "DPI", "resizeMethod": "Método de redimensionamento", "clientName": "Nome do cliente", - "initialProgram": "Programa Inicial", + "initialProgram": "Programa inicial", "serverLayout": "Layout do servidor", "timezone": "Fuso horário", - "loadBalanceInfo": "Informação de balanceamento de carga / Cookies", - "loadBalanceInfoDesc": "Cookie do agente de ligação RD para o balanceamento de carga do RDS farm (ex: tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "Informação de balanceamento de carga / Cookie", + "loadBalanceInfoDesc": "Cookie do RD Connection Broker para balanceamento de carga em farms RDS (ex: tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "Proxy guacd", - "guacdHostname": "guacd Host", + "guacdHostname": "Host guacd", "guacdHostnamePlaceholder": "Padrão global", - "guacdPort": "Porto guacd", - "guacdProxyDesc": "Substitua a instância global do guacd para esta ligação. Deixe em branco para utilizar o guacd configurado globalmente.", - "gatewayHostname": "Nome do host do gateway", - "gatewayPort": "Porta de entrada", + "guacdPort": "Porta guacd", + "guacdProxyDesc": "Substituir a instância guacd global para esta ligação. Deixe em branco para usar o guacd configurado globalmente.", + "gatewayHostname": "Nome de host do gateway", + "gatewayPort": "Porta do gateway", "gatewayUsername": "Nome de utilizador do gateway", - "gatewayPassword": "Senha do Gateway", - "gatewayDomain": "Domínio de entrada", + "gatewayPassword": "Palavra-passe do gateway", + "gatewayDomain": "Domínio do gateway", "remoteAppProgram": "Programa RemoteApp", - "workingDirectory": "Diretório de Trabalho", + "workingDirectory": "Diretório de trabalho", "arguments": "Argumentos", - "normalizeLineEndings": "Normalizar as quebras de linha", - "recordingPath": "Caminho de gravação", + "normalizeLineEndings": "Normalizar fins de linha", + "recordingPath": "Caminho da gravação", "recordingName": "Nome da gravação", "macAddress": "Endereço MAC", - "broadcastAddress": "Discurso de transmissão", + "broadcastAddress": "Endereço de broadcast", "udpPort": "Porta UDP", - "waitTimeS": "Tempo de espera (s)", - "driveName": "Nome da unidade", - "drivePath": "Percurso de condução", - "ignoreCertificate": "Ignorar certificado", - "ignoreCertificateDesc": "Permitir ligações a hosts com certificados autoassinados", - "forceLossless": "Força sem perdas", + "waitTimeS": "Tempo de Espera (s)", + "driveName": "Nome da Unidade", + "drivePath": "Caminho da Unidade", + "ignoreCertificate": "Ignorar Certificado", + "ignoreCertificateDesc": "Permitir ligações a hosts com certificados auto-assinados", + "forceLossless": "Forçar sem perdas", "forceLosslessDesc": "Forçar codificação de imagem sem perdas (maior qualidade, mais largura de banda)", - "disableAudio": "Desativar áudio", - "disableAudioDesc": "Silenciar todo o áudio da sessão remota.", - "enableAudioInput": "Ativar entrada de áudio (microfone)", - "enableAudioInputDesc": "Encaminhar o microfone local para a sessão remota.", - "wallpaper": "Papel de parede", - "wallpaperDesc": "Exibir papel de parede do ambiente de trabalho (desativar melhora o desempenho)", - "theming": "Tematização", + "disableAudio": "Desativar Áudio", + "disableAudioDesc": "Silenciar todo o áudio da sessão remota", + "enableAudioInput": "Ativar Entrada de Áudio (Microfone)", + "enableAudioInputDesc": "Encaminhar microfone local para a sessão remota", + "wallpaper": "Fundo de ecrã", + "wallpaperDesc": "Mostrar fundo de ecrã (desativar melhora o desempenho)", + "theming": "Temas", "themingDesc": "Ativar temas e estilos visuais", - "fontSmoothing": "Suavização de fontes", - "fontSmoothingDesc": "Ativar renderização de tipos de letra ClearType", - "fullWindowDrag": "Arrastar janela inteira", - "fullWindowDragDesc": "Exibir o conteúdo da janela enquanto arrasta", - "desktopComposition": "Composição de mesa", - "desktopCompositionDesc": "Ative os efeitos de vidro Aero", - "menuAnimations": "Animações de menu", - "menuAnimationsDesc": "Ativar animações de transição e de deslizamento do menu", - "disableBitmapCaching": "Desativar a cache de bitmaps", - "disableBitmapCachingDesc": "Desative a cache de bitmaps (pode ajudar a resolver problemas técnicos).", - "disableOffscreenCaching": "Desativar o cache fora do ecrã", - "disableOffscreenCachingDesc": "Desative o cache fora do ecrã", - "disableGlyphCaching": "Desativar a cache de glifos", - "disableGlyphCachingDesc": "Desative a cache de glifos", + "fontSmoothing": "Suavização de Fontes", + "fontSmoothingDesc": "Ativar renderização de fontes ClearType", + "fullWindowDrag": "Arrasto de Janela Completo", + "fullWindowDragDesc": "Mostrar conteúdo da janela ao arrastar", + "desktopComposition": "Composição do ambiente de trabalho", + "desktopCompositionDesc": "Ativar efeitos de transparência Aero", + "menuAnimations": "Animações de Menu", + "menuAnimationsDesc": "Ativar animações de desvanecimento e deslizamento de menus", + "disableBitmapCaching": "Desativar cache de bitmaps", + "disableBitmapCachingDesc": "Desativar a cache de bitmaps (pode ajudar com falhas)", + "disableOffscreenCaching": "Desativar cache fora do ecrã", + "disableOffscreenCachingDesc": "Desativar a cache fora do ecrã", + "disableGlyphCaching": "Desativar cache de glifos", + "disableGlyphCachingDesc": "Desativar a cache de glifos", "enableGfx": "Ativar GFX", - "enableGfxDesc": "Utilize o pipeline gráfico RemoteFX", - "enablePrinting": "Ativar impressão", - "enablePrintingDesc": "Redirecionar as impressoras locais para a sessão remota.", - "enableDriveRedirection": "Ativar o redireccionamento de unidades", - "enableDriveRedirectionDesc": "Mapear uma pasta local como uma unidade na sessão remota.", - "createDrivePath": "Criar caminho de unidade", - "createDrivePathDesc": "Criar automaticamente a pasta caso esta não exista.", - "disableDownload": "Desativar download", - "disableDownloadDesc": "Impedir o download de ficheiros da sessão remota.", - "disableUpload": "Desativar o upload", - "disableUploadDesc": "Impedir o envio de ficheiros para a sessão remota.", - "enableTouch": "Ativar toque", - "enableTouchDesc": "Ativar o encaminhamento de entrada por toque", - "consoleSession": "Sessão do console", - "consoleSessionDesc": "Ligue-se à consola (sessão 0) em vez de uma nova sessão.", - "sendWolPacket": "Enviar pacote WOL", - "sendWolPacketDesc": "Envie um pacote mágico para ativar este host antes de se ligar.", - "disableCopy": "Desativar cópia", + "enableGfxDesc": "Usar pipeline gráfico RemoteFX", + "enablePrinting": "Ativar Impressão", + "enablePrintingDesc": "Redirecionar impressoras locais para a sessão remota", + "enableDriveRedirection": "Ativar Redirecionamento de Unidades", + "enableDriveRedirectionDesc": "Mapear uma pasta local como unidade na sessão remota", + "createDrivePath": "Criar Caminho de Unidade", + "createDrivePathDesc": "Criar automaticamente a pasta se não existir", + "disableDownload": "Desativar Download", + "disableDownloadDesc": "Impedir o download de ficheiros da sessão remota", + "disableUpload": "Desativar Upload", + "disableUploadDesc": "Impedir o envio de ficheiros para a sessão remota", + "enableTouch": "Ativar Toque", + "enableTouchDesc": "Ativar o reencaminhamento de entrada tátil", + "consoleSession": "Sessão de Consola", + "consoleSessionDesc": "Ligar à consola (sessão 0) em vez de uma nova sessão", + "sendWolPacket": "Enviar Pacote WOL", + "sendWolPacketDesc": "Enviar um pacote mágico para despertar este host antes de ligar", + "disableCopy": "Desativar Cópia", "disableCopyDesc": "Impedir a cópia de texto da sessão remota", - "disablePaste": "Desativar colagem", - "disablePasteDesc": "Impedir a colagem de texto na sessão remota.", - "createPathIfMissing": "Criar caminho se estiver em falta", + "disablePaste": "Desativar Colagem", + "disablePasteDesc": "Impedir a colagem de texto na sessão remota", + "createPathIfMissing": "Criar caminho se não existir", "createPathIfMissingDesc": "Criar automaticamente o diretório de gravação", - "excludeOutput": "Apagar saída", - "excludeOutputDesc": "Não gravar a saída do ecrã (apenas metadados).", - "excludeMouse": "Apagar o rato", - "excludeMouseDesc": "Não grave movimentos do rato", - "includeKeystrokes": "Incluir teclas premidas", - "includeKeystrokesDesc": "Registe as teclas digitadas na sua forma original, além da saída no ecrã.", + "excludeOutput": "Excluir Saída", + "excludeOutputDesc": "Não gravar a saída do ecrã (apenas metadados)", + "excludeMouse": "Excluir Rato", + "excludeMouseDesc": "Não gravar os movimentos do rato", + "includeKeystrokes": "Incluir Teclas", + "includeKeystrokesDesc": "Gravar as teclas premidas em bruto, além da saída do ecrã", "vncPort": "Porta VNC", "vncPassword": "Palavra-passe VNC", "vncUsernameOptional": "Nome de utilizador (opcional)", - "vncLeaveBlank": "Deixe em branco se não for necessário.", - "cursorMode": "Modo Cursor", + "vncLeaveBlank": "Deixar em branco se não for necessário", + "cursorMode": "Modo do Cursor", "swapRedBlue": "Trocar Vermelho/Azul", - "swapRedBlueDesc": "Mudar os canais de cor vermelho e azul resolve alguns problemas de cor.", - "readOnly": "Somente leitura", - "readOnlyDesc": "Visualize o ecrã remoto sem enviar nenhum comando.", + "swapRedBlueDesc": "Trocar os canais de cor vermelha e azul (corrige alguns problemas de cor)", + "readOnly": "Apenas leitura", + "readOnlyDesc": "Visualizar o ecrã remoto sem enviar qualquer entrada", "telnetPort": "Porta Telnet", - "terminalType": "Tipo de terminal", - "fontName": "Nome da fonte", - "fontSize": "Tamanho da fonte", - "colorScheme": "Esquema de cores", + "terminalType": "Tipo de Terminal", + "fontName": "Nome da Fonte", + "fontSize": "Tamanho da Fonte", + "colorScheme": "Esquema de Cores", "backspaceKey": "Tecla Backspace", - "saveHostFirst": "Primeiro, guarde o host.", - "sharingOptionsAfterSave": "As opções de partilha ficam disponíveis depois de o anfitrião ser guardado.", - "permissionLevel": "Nível de permissão", + "saveHostFirst": "Guarde o host primeiro.", + "sharingOptionsAfterSave": "As opções de partilha estão disponíveis após o host ter sido guardado.", + "permissionLevel": "Nível de Permissão", "typeHeader": "Tipo", - "targetHeader": "Objetivo", + "targetHeader": "Destino", "permissionHeader": "Permissão", "cancelBtn": "Cancelar", - "savingBtn": "Salvando...", + "savingBtn": "A guardar...", "updateHostBtn": "Atualizar Host", - "addHostBtn": "Adicionar host" + "addHostBtn": "Adicionar Host" } }, "commandPalette": { "searchPlaceholder": "Pesquisar hosts, comandos ou definições...", - "quickActions": "Ações rápidas", - "hostManager": "Gestor de Hospedagem", + "quickActions": "Ações Rápidas", + "hostManager": "Gestor de Hosts", "hostManagerDesc": "Gerir, adicionar ou editar hosts", - "addNewHost": "Adicionar novo host", - "addNewHostDesc": "Registe um novo anfitrião", - "adminSettings": "Configurações do administrador", - "adminSettingsDesc": "Configure as preferências do sistema e os utilizadores.", - "userProfile": "Perfil do utilizador", - "userProfileDesc": "Faça a gestão da sua conta e preferências.", - "addCredential": "Adicionar credencial", - "addCredentialDesc": "Armazene chaves ou palavras-passe SSH", + "addNewHost": "Adicionar Novo Host", + "addNewHostDesc": "Registar um novo host", + "adminSettings": "Definições de Administrador", + "adminSettingsDesc": "Configurar preferências do sistema e utilizadores", + "userProfile": "Perfil do Utilizador", + "userProfileDesc": "Gerir a sua conta e preferências", + "addCredential": "Adicionar Credencial", + "addCredentialDesc": "Guardar chaves SSH ou palavras-passe", "tmuxMonitor": "Monitor Tmux", - "tmuxMonitorDesc": "Monitorize as sessões tmux nos seus hosts.", - "recentActivity": "Atividade recente", + "tmuxMonitorDesc": "Monitorizar sessões tmux nos seus hosts", + "recentActivity": "Atividade Recente", "serversAndHosts": "Servidores e Hosts", - "noHostsFound": "Não foi encontrado nenhum host que corresponda a \"{{search}}\"", - "links": "Links", + "noHostsFound": "Nenhum host encontrado que corresponda a \"{{search}}\"", + "links": "Ligações", "navigate": "Navegar", "select": "Selecionar", "toggleWith": "Alternar com" }, "splitScreen": { "paneEmpty": "Painel {{index}} - vazio", - "noTabAssigned": "Nenhuma aba atribuída", + "noTabAssigned": "Nenhum separador atribuído", "focusedPane": "Painel ativo" }, "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", + "noConnectionsDesc": "Abra um terminal, gestor de ficheiros ou desktop remoto para ver as ligações aqui", + "connectedFor": "Ligado há {{duration}}", + "connected": "Ligado", + "disconnected": "Desligado", + "closeTab": "Fechar separador", + "closeConnection": "Fechar ligação", + "forgetTab": "Esquecer", "removeBackground": "Remover", - "reconnect": "Reconectar", + "reconnect": "Religar", "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", + "sectionOpen": "Abertas", + "sectionBackground": "Em segundo plano", + "backgroundDesc": "As sessões persistem durante 30 minutos após a desligação e podem ser religadas.", + "persisted": "Persistida em segundo plano", "expiresIn": "Expira em {{duration}}", - "search": "Procurar ligações...", - "noSearchResults": "Nenhuma ligação corresponde à sua pesquisa.", - "rename": "Renomear sessão" + "search": "Pesquisar ligações...", + "noSearchResults": "Nenhuma ligação corresponde à sua pesquisa", + "rename": "Renomear sessão", + "sectionSharedWithMe": "Partilhou comigo", + "sharedBy": "Partilhado por {{username}}", + "join": "Juntar", + "sharedSessionLabel": "{{hostName}} (partilhado)" + }, + "sessionSharing": { + "guestView": { + "loading": "Ligar à sessão partilhada...", + "linkInvalid": "Este link de partilha é inválido, expirou ou foi revogado.", + "rateLimited": "Muitas tentativas foram feitas, tente novamente em breve.", + "sessionEnded": "Esta sessão terminou.", + "readOnlyBadge": "Somente visualização" + }, + "modalTitle": "Sessão de partilha", + "shareButton": "Partilhar", + "notReadyToShare": "A sessão ainda não está pronta para ser partilhada.", + "modeTab": { + "link": "Ligação", + "user": "Usuário" + }, + "linkModeDescription": "Qualquer pessoa com este link pode participar, sem necessidade de criar conta.", + "userModeDescription": "Partilhe com um utilizador específico que já tenha acesso a este host. Caso ainda não tenha acesso, partilhe primeiro o host com ele ou utilize um link. Após a partilha, a sessão aparecerá no separador Conexões do utilizador.", + "permissionLevel": { + "label": "Nível de permissão", + "readOnly": "Somente leitura", + "readOnlyDescription": "É possível assistir à sessão em direto, mas não é possível digitar ou interagir.", + "readWrite": "Ler e escrever", + "readWriteDescription": "Pode digitar e interagir com a sessão da mesma forma que o proprietário." + }, + "expiryLabel": "Expiração do link", + "createLinkButton": "Criar link", + "createShareButton": "Partilhar com o usuário", + "searchUsersPlaceholder": "Pesquisar utilizadores...", + "noUsersFound": "Nenhum utilizador encontrado", + "linkCreated": "Partilhar link criado", + "linkCopied": "Ligação copiada para a área de transferência", + "copyLink": "Copiar link", + "shareCreated": "Sessão partilhada. Ela aparecerá no separador Conexões deles.", + "shareFailed": "Falha ao criar partilha", + "userLacksHostAccess": "Esse utilizador ainda não tem acesso a este host. Partilhe o host com ele primeiro ou use um link.", + "activeShares": "Ações ativas", + "noActiveShares": "Sem ação ativa nesta sessão.", + "revoke": "Revogar", + "revokeConfirmTitle": "Revogar essa participação?", + "revokeConfirmDescription": "Qualquer pessoa que utilize esta partilha perderá o acesso imediatamente.", + "revoked": "Ações revogadas", + "revokeFailed": "Falha ao revogar a participação", + "joinCount": "{{count}} junte-se", + "joinCount_other": "{{count}} entra", + "expiresAt": "Expira em {{date}}", + "linkShareBadge": "Ligação", + "userShareBadge": "Utilizador: {{username}}", + "loadSharesFailed": "Falha ao carregar partilhas ativas" }, "guacamole": { "connecting": "A ligar à sessão {{type}}...", "connectionError": "Erro de ligação", "connectionFailed": "Falha na ligação", "failedToConnect": "Falha ao obter o token de ligação", - "hostNotFound": "Host não encontrado", - "noHostSelected": "Nenhum host selecionado", - "reconnect": "Reconectar", - "retry": "Tentar novamente", - "guacdUnavailable": "O serviço de ambiente de trabalho remoto (guacd) não está disponível. Certifique-se de que o guacd está em execução, acessível e configurado corretamente nas definições de administrador.", + "hostNotFound": "Servidor não encontrado", + "noHostSelected": "Nenhum servidor selecionado", + "reconnect": "Religar", + "retry": "Repetir", + "guacdUnavailable": "O serviço de ambiente de trabalho remoto (guacd) não está disponível. Certifique-se de que o guacd está em execução, acessível e devidamente configurado nas definições de administração.", + "credentialPromptTitle": "Introduza as credenciais RDP", + "credentialPromptDescription": "Este host está configurado para solicitar credenciais ao ligar. São utilizadas apenas para esta sessão e não são guardadas.", + "connect": "Ligar", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", "winL": "Win+L (Bloquear ecrã)", "winKey": "Tecla Windows", "ctrl": "Ctrl", - "alt": "Alternativa", - "shift": "Turno", - "win": "Ganhar", + "alt": "Alt", + "shift": "Shift", + "win": "Win", "stickyActive": "{{key}} (bloqueado - clique para libertar)", "stickyInactive": "{{key}} (clique para bloquear)", - "esc": "Escapar", - "tab": "Aba", - "home": "Casa", - "end": "Fim", - "pageUp": "Página acima", - "pageDown": "Página abaixo", - "arrowUp": "Seta para cima", - "arrowDown": "Seta para baixo", - "arrowLeft": "Seta para a esquerda", - "arrowRight": "Seta para a direita", - "fnToggle": "Teclas de função", - "reconnect": "Sessão de reconexão", - "collapse": "Recolher barra de ferramentas", - "expand": "Expandir barra de ferramentas", - "dragHandle": "Arraste para reposicionar" + "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": "Teclas de Função", + "reconnect": "Reconectar Sessão", + "collapse": "Recolher barra", + "expand": "Expandir barra", + "dragHandle": "Arraste para reposicionar", + "switchToTrackpad": "Mudar para o modo trackpad (arraste para mover o cursor, toque para clicar)", + "switchToTouch": "Mude para o modo de toque (toque diretamente onde pretende clicar)." } }, "terminal": { - "connect": "Ligar ao host", - "clear": "Claro", + "connect": "Ligar 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 está a ser servida via HTTPS ou localhost.", - "clipboardReadFailed": "Não foi possível ler o conteúdo da área de transferência. Certifique-se de que as permissões de acesso à área de transferência estão concedidas.", - "clipboardHttpWarning": "A função Paste requer HTTPS. Utilize Ctrl+Shift+V ou utilize HTTPS para aceder ao Termix.", - "passwordPromptFillTitle": "Introduza a palavra-passe guardada neste campo?", + "connectionLost": "Ligação perdida", + "connected": "Ligado", + "clipboardWriteFailed": "Erro ao copiar para a área de transferência. Certifique-se de que a página é servida via HTTPS ou localhost.", + "clipboardReadFailed": "Erro 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 o Termix via HTTPS.", + "passwordPromptFillTitle": "Preencher a palavra-passe guardada neste pedido?", "unknownError": "Ocorreu um erro desconhecido.", "websocketError": "Erro de ligação WebSocket", - "connecting": "Ligando...", + "connecting": "A ligar...", "noHostSelected": "Nenhum host selecionado", - "reconnecting": "A reconectar... ({{attempt}}/{{max}})", - "reconnected": "Reconectado com sucesso", + "reconnecting": "A restabelecer ligação... ({{attempt}}/{{max}})", + "reconnected": "Ligação restabelecida com sucesso", "tmuxSessionCreated": "Sessão tmux criada: {{name}}", "tmuxSessionAttached": "Sessão tmux anexada: {{name}}", - "tmuxUnavailable": "O tmux não está instalado no host remoto, utilizando o shell padrão como alternativa.", + "tmuxUnavailable": "O tmux não está instalado no host remoto, a reverter para shell padrão", "tmuxSessionPickerTitle": "Sessões tmux", - "tmuxSessionPickerDesc": "Foram encontradas sessões tmux existentes neste host. Selecione uma para voltar a ligar ou crie uma nova sessão.", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "Foram encontradas sessões tmux neste host. Selecione uma para retomar ou crie uma nova sessão.", + "tmuxWindows": "Janelas", "tmuxWindowCount": "{{count}} janela", - "tmuxAttached": "Clientes vinculados", + "tmuxAttached": "Clientes anexados", "tmuxAttachedCount": "{{count}} anexado", "tmuxLastActivity": "Última atividade", "tmuxTimeJustNow": "agora mesmo", - "tmuxTimeMinutes": "{{count}}m atrás", - "tmuxTimeHours": "{{count}}há h", - "tmuxTimeDays": "{{count}}há alguns dias", + "tmuxTimeMinutes": "há {{count}} min", + "tmuxTimeHours": "há {{count}} h", + "tmuxTimeDays": "há {{count}} d", "tmuxCreateNew": "Iniciar nova sessão", - "tmuxCopyHint": "Ajuste a seleção e prima Enter para copiar para a área de transferência.", - "tmuxDetach": "Desligar da sessão tmux", - "tmuxDetached": "Sessão desconectada do tmux", - "maxReconnectAttemptsReached": "Número máximo de tentativas de religação atingido", - "closeTab": "Perto", - "connectionTimeout": "Tempo limite de ligação", + "tmuxCopyHint": "Ajuste a seleção e prima Enter para copiar para a área de transferência", + "tmuxDetach": "Desanexar da sessão tmux", + "tmuxDetached": "Desanexado da sessão tmux", + "maxReconnectAttemptsReached": "Número máximo de tentativas de restabelecimento de ligação atingido", + "closeTab": "Fechar", + "connectionTimeout": "Tempo limite da ligação", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", "runTitle": "A executar {{command}} - {{host}}", - "totpRequired": "Autenticação de dois fatores obrigatória", + "totpRequired": "Autenticação de dois fatores necessária", "totpCodeLabel": "Código de verificação", "totpVerify": "Verificar", + "mfaPromptRequired": "Autenticação necessária", + "mfaPushRequired": "Autenticação Push necessária", + "mfaMenuPlaceholder": "Insira a sua resposta", + "mfaWaitingApproval": "Aguarda aprovação no seu dispositivo...", + "mfaSendRequest": "Enviar solicitação", "warpgateAuthRequired": "Autenticação Warpgate necessária", "warpgateSecurityKey": "Chave de segurança", "warpgateAuthUrl": "URL de autenticação", - "warpgateOpenBrowser": "Abrir no browser", - "warpgateContinue": "Concluí a autenticação.", + "warpgateOpenBrowser": "Abrir no navegador", + "warpgateContinue": "Concluí a autenticação", "opksshAuthRequired": "Autenticação OPKSSH necessária", - "opksshAuthDescription": "Para continuar, complete a autenticação no seu browser. Esta sessão manter-se-á válida durante 24 horas.", - "opksshOpenBrowser": "Abra o browser para autenticar.", - "opksshWaitingForAuth": "Aguarda autenticação no browser...", - "opksshAuthenticating": "Processamento de autenticação...", + "opksshAuthDescription": "Conclua a autenticação no seu navegador para continuar. Esta sessão permanecerá válida por 24 horas.", + "opksshOpenBrowser": "Abrir navegador para autenticar", + "opksshWaitingForAuth": "A aguardar autenticação no navegador...", + "opksshAuthenticating": "A processar autenticação...", "opksshTimeout": "A autenticação expirou. Tente novamente.", "opksshAuthFailed": "A autenticação falhou. Verifique as suas credenciais e tente novamente.", "opksshSignInWith": "Iniciar sessão com {{provider}}", - "vaultAuthTitle": "É necessário fazer login no cofre.", - "vaultAuthDescription": "Foi aberta uma janela para fazer login no HashiCorp Vault. Conclua o login; esta ligação será estabelecida automaticamente.", - "vaultAuthFailed": "A autenticação do cofre falhou. Tente novamente.", - "vaultReopen": "Reabrir janela de login", - "sudoPasswordPopupTitle": "Introduza a senha?", - "linkDialogTitle": "Abrir link", - "linkDialogOpen": "Aberto", - "linkDialogCopy": "Cópia", - "websocketAbnormalClose": "A ligação foi encerrada inesperadamente. Isto pode ter ocorrido devido a um problema de configuração de proxy reverso ou SSL. Verifique os registos do servidor.", - "connectionLogTitle": "Registo de conexão", + "vaultAuthTitle": "Início de sessão no Vault necessário", + "vaultAuthDescription": "Foi aberta uma janela para iniciar sessão no HashiCorp Vault. Conclua o início de sessão aí; esta ligação continuará automaticamente.", + "vaultAuthFailed": "A autenticação no Vault falhou. Tente novamente.", + "vaultReopen": "Reabrir janela de início de sessão", + "sudoPasswordPopupTitle": "Inserir Password?", + "linkDialogTitle": "Abrir Ligação", + "linkDialogOpen": "Abrir", + "linkDialogCopy": "Copiar", + "websocketAbnormalClose": "A ligação foi fechada inesperadamente. Isto pode dever-se a um problema de configuração do proxy reverso ou SSL. Verifique os registos do servidor.", + "connectionLogTitle": "Registo da Ligação", "connectionLogCopy": "Copiar registos para a área de transferência", - "connectionLogEmpty": "Ainda não há registos de ligação.", - "connectionLogWaiting": "Aguardam-se os registos de conexão...", - "connectionLogCopied": "Registos de ligação copiados para a área de transferência", - "connectionLogCopyFailed": "Falha ao copiar os registos para a área de transferência", + "connectionLogEmpty": "Ainda não há registos da ligação", + "connectionLogWaiting": "A aguardar registos da ligação...", + "connectionLogCopied": "Registos da ligação copiados para a área de transferência", + "connectionLogCopyFailed": "Falha ao copiar registos para a área de transferência", "connectionRejected": "Ligação rejeitada pelo servidor. Verifique a sua autenticação e configuração de rede.", - "hostKeyRejected": "Verificação da chave SSH do host rejeitada. Ligação cancelada.", - "sessionTakenOver": "A sessão foi aberta noutra aba. Reconectando...", + "hostKeyRejected": "Verificação da chave de host SSH rejeitada. Ligação cancelada.", + "sessionTakenOver": "A sessão foi aberta noutro separador. A restabelecer ligação...", "split": { - "splitTab": "Aba dividida", - "addToSplit": "Adicionar à divisão", - "removeFromSplit": "Remover da divisão" + "splitTab": "Dividir Separador", + "addToSplit": "Adicionar à Divisão", + "removeFromSplit": "Remover da Divisão" } }, "fileManager": { "noHostSelected": "Nenhum host selecionado", - "initializingEditor": "Inicializando o editor...", + "initializingEditor": "A inicializar editor...", "file": "Ficheiro", "folder": "Pasta", - "uploadFile": "Carregar ficheiro", - "downloadFile": "Transferir", - "extractArchive": "Extrair ficheiro", - "extractingArchive": "Extraindo {{name}}...", + "uploadFile": "Carregar Ficheiro", + "downloadFile": "Descarregar", + "extractArchive": "Extrair Arquivo", + "extractingArchive": "A extrair {{name}}...", "archiveExtractedSuccessfully": "{{name}} extraído com sucesso", - "extractFailed": "A extração falhou", - "compressFile": "Compactar ficheiro", - "compressFiles": "Comprimir ficheiros", - "compressFilesDesc": "Comprima {{count}} itens num ficheiro", - "archiveName": "Nome do ficheiro", - "enterArchiveName": "Introduza o nome do ficheiro...", + "extractFailed": "Extração falhou", + "compressFile": "Comprimir Ficheiro", + "compressFiles": "Comprimir Ficheiros", + "compressFilesDesc": "Comprimir {{count}} itens num arquivo", + "archiveName": "Nome do arquivo", + "enterArchiveName": "Introduza o nome do arquivo...", "compressionFormat": "Formato de compressão", - "selectedFiles": "Arquivos selecionados", - "andMoreFiles": "e {{count}} mais...", - "compress": "Compressa", - "compressingFiles": "Comprimir {{count}} itens em {{name}}...", + "selectedFiles": "Ficheiros selecionados", + "andMoreFiles": "e mais {{count}}...", + "compress": "Comprimir", + "compressingFiles": "A comprimir {{count}} itens em {{name}}...", "filesCompressedSuccessfully": "{{name}} criado com sucesso", - "compressFailed": "A compressão falhou", + "compressFailed": "Compressão falhou", "edit": "Editar", - "preview": "Pré-visualização", + "preview": "Pré-visualizar", "previous": "Anterior", "next": "Seguinte", "pageXOfY": "Página {{current}} de {{total}}", - "zoomOut": "Diminuir o zoom", + "zoomOut": "Reduzir", "zoomIn": "Ampliar", - "newFile": "Novo ficheiro", - "newFolder": "Nova pasta", + "newFile": "Novo Ficheiro", + "newFolder": "Nova Pasta", "rename": "Renomear", - "uploading": "Carregando...", - "uploadingFile": "Carregando {{name}}...", + "uploading": "A enviar...", + "uploadingFile": "A enviar {{name}}...", "fileName": "Nome do ficheiro", "folderName": "Nome da pasta", "fileUploadedSuccessfully": "Ficheiro \"{{name}}\" enviado com sucesso", - "failedToUploadFile": "Falha ao carregar o ficheiro", + "failedToUploadFile": "Falha ao enviar ficheiro", "fileDownloadedSuccessfully": "Ficheiro \"{{name}}\" descarregado com sucesso", - "failedToDownloadFile": "Falha ao descarregar o ficheiro", + "failedToDownloadFile": "Falha ao descarregar ficheiro", "fileCreatedSuccessfully": "Ficheiro \"{{name}}\" criado com sucesso", "folderCreatedSuccessfully": "Pasta \"{{name}}\" criada com sucesso", - "failedToCreateItem": "Falha ao criar o item", - "operationFailed": "A operação {{operation}} falhou para {{name}}: {{error}}", - "failedToResolveSymlink": "Falha ao resolver o link simbólico", - "itemsDeletedSuccessfully": "{{count}} itens eliminados com sucesso", + "failedToCreateItem": "Falha ao criar item", + "operationFailed": "Operação {{operation}} falhou para {{name}}: {{error}}", + "failedToResolveSymlink": "Falha ao resolver ligação simbólica", + "itemsDeletedSuccessfully": "{{count}} itens eliminados com êxito", "failedToDeleteItems": "Falha ao eliminar itens", "sudoPasswordRequired": "Palavra-passe de administrador necessária", - "enterSudoPassword": "Introduza a palavra-passe de sudo para continuar esta operação.", - "sudoPassword": "Senha de sudo", - "sudoOperationFailed": "A operação sudo falhou", - "sudoAuthFailed": "Falha na autenticação sudo", - "dragFilesToUpload": "Arraste e largue os ficheiros aqui para fazer o upload.", + "enterSudoPassword": "Introduza a palavra-passe sudo para continuar esta operação", + "sudoPassword": "Palavra-passe sudo", + "sudoOperationFailed": "Operação sudo falhou", + "sudoAuthFailed": "Autenticação sudo falhou", + "dragFilesToUpload": "Arraste ficheiros aqui para carregar", "emptyFolder": "Esta pasta está vazia", - "searchFiles": "Pesquisar ficheiros...", + "searchFiles": "Procurar ficheiros...", "upload": "Carregar", - "selectHostToStart": "Selecione um host para iniciar a gestão de ficheiros.", - "sshRequiredForFileManager": "O gestor de ficheiros requer SSH. Este host não tem o SSH ativado.", + "selectHostToStart": "Selecione uma máquina para iniciar a gestão de ficheiros", + "sshRequiredForFileManager": "O gestor de ficheiros requer SSH. Esta máquina não tem SSH ativado.", "failedToConnect": "Falha ao ligar ao SSH", - "failedToLoadDirectory": "Falha ao carregar o diretório", + "failedToLoadDirectory": "Falha ao carregar diretório", "noSSHConnection": "Nenhuma ligação SSH disponível", - "copy": "Cópia", - "cut": "Corte", + "copy": "Copiar", + "cut": "Cortar", "paste": "Colar", "copyPath": "Copiar caminho", "copyPaths": "Copiar caminhos", "delete": "Eliminar", "properties": "Propriedades", "refresh": "Atualizar", - "downloadFiles": "Descarregue {{count}} ficheiros para o browser", + "downloadFiles": "Descarregar {{count}} ficheiros para o navegador", "copyFiles": "Copiar {{count}} itens", - "cutFiles": "Recortar {{count}} itens", - "deleteFiles": "Apagar {{count}} itens", + "cutFiles": "Cortar {{count}} itens", + "deleteFiles": "Eliminar {{count}} itens", "filesCopiedToClipboard": "{{count}} itens copiados para a área de transferência", - "filesCutToClipboard": "{{count}} itens copiados para a área de transferência", + "filesCutToClipboard": "{{count}} itens cortados para a área de transferência", "pathCopiedToClipboard": "Caminho copiado para a área de transferência", "pathsCopiedToClipboard": "{{count}} caminhos copiados para a área de transferência", "failedToCopyPath": "Falha ao copiar o caminho para a área de transferência", - "copyFolderLink": "Copiar link para a pasta", + "copyFolderLink": "Copiar ligação para a pasta", "copyCurrentFolderLink": "Copiar ligação para a pasta atual", "folderLinkCopied": "Ligação da pasta copiada para a área de transferência", - "failedToCopyFolderLink": "Falha ao copiar o link da pasta", - "movedItems": "Itens movidos {{count}}", + "failedToCopyFolderLink": "Falha ao copiar a ligação da pasta", + "movedItems": "{{count}} itens movidos", "failedToDeleteItem": "Falha ao eliminar o item", - "itemRenamedSuccessfully": "{{type}} renomeado com sucesso", + "itemRenamedSuccessfully": "Renomeação de {{type}} bem-sucedida.", "failedToRenameItem": "Falha ao renomear o item", "download": "Transferir", "openExternalEditor": "Abrir externamente", - "chooseExternalEditor": "Escolha um editor externo.", + "chooseExternalEditor": "Escolher editor externo", "externalEditorSelected": "Editor externo selecionado", - "externalEditorDesktopOnly": "O editor externo está apenas disponível na aplicação para desktop.", - "externalEditorOpened": "Aberto num editor externo. Guardar aqui enviará as alterações de volta para o servidor.", - "failedToOpenExternalEditor": "Não foi possível abrir o editor externo.", - "failedToSelectExternalEditor": "Não foi possível selecionar o editor externo.", + "externalEditorDesktopOnly": "O editor externo está disponível apenas na aplicação de ambiente de trabalho", + "externalEditorOpened": "Aberto no editor externo. Ao guardar, as alterações serão enviadas de volta para o servidor.", + "failedToOpenExternalEditor": "Falha ao abrir o editor externo", + "failedToSelectExternalEditor": "Falha ao selecionar o editor externo", "permissions": "Permissões", "size": "Tamanho", "modified": "Modificado", - "path": "Percurso", - "confirmDelete": "Tem a certeza que pretende eliminar {{name}}?", + "path": "Caminho", + "confirmDelete": "Tem a certeza de que pretende eliminar {{name}}?", "permissionDenied": "Permissão negada", "serverError": "Erro do servidor", - "fileSavedSuccessfully": "Ficheiro salvo com sucesso", + "fileSavedSuccessfully": "Ficheiro guardado com sucesso", "failedToSaveFile": "Falha ao guardar o ficheiro", "confirmDeleteSingleItem": "Tem a certeza de que pretende eliminar permanentemente \"{{name}}\"?", "confirmDeleteMultipleItems": "Tem a certeza de que pretende eliminar permanentemente {{count}} itens?", - "confirmDeleteMultipleItemsWithFolders": "Tem a certeza de que pretende eliminar permanentemente {{count}} itens? Isto inclui pastas e os seus conteúdos.", + "confirmDeleteMultipleItemsWithFolders": "Tem a certeza de que pretende eliminar permanentemente {{count}} itens? Isto inclui pastas e o 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 anulada. O(s) item(s) será(ão) eliminado(s) permanentemente do servidor.", - "recent": "Recente", - "pinned": "Fixado", + "permanentDeleteWarning": "Esta ação não pode ser anulada. Os itens serão permanentemente eliminados do servidor.", + "recent": "Recentes", + "pinned": "Fixados", "folderShortcuts": "Atalhos de pastas", - "failedToReconnectSSH": "Falha ao voltar a ligar a sessão SSH", - "openTerminalHere": "Abra o terminal aqui", - "run": "Correr", - "openTerminalInFolder": "Abra o terminal nesta pasta.", - "openTerminalInFileLocation": "Abra o terminal no local do ficheiro.", - "runningFile": "A correr - {{file}}", - "onlyRunExecutableFiles": "Só é possível executar ficheiros executáveis.", - "directories": "Diretórios", - "removedFromRecentFiles": "Removemos \"{{name}}\" dos ficheiros recentes", - "removeFailed": "Remover falha", - "unpinnedSuccessfully": "Desfixado \"{{name}}\" com sucesso", - "unpinFailed": "Desafixar falhou", - "removedShortcut": "Atalho removido \"{{name}}\"", - "removeShortcutFailed": "A remoção do atalho falhou", - "clearedAllRecentFiles": "Apaguei todos os ficheiros recentes.", - "clearFailed": "Limpar falhou", + "failedToReconnectSSH": "Falha ao restabelecer a sessão SSH", + "openTerminalHere": "Abrir Terminal Aqui", + "run": "Executar", + "openTerminalInFolder": "Abrir Terminal Nesta Pasta", + "openTerminalInFileLocation": "Abrir Terminal na Localização do Ficheiro", + "runningFile": "Em execução - {{file}}", + "onlyRunExecutableFiles": "Só pode executar ficheiros executáveis", + "directories": "Pastas", + "removedFromRecentFiles": "Removido \"{{name}}\" dos ficheiros recentes", + "removeFailed": "Falha ao remover", + "unpinnedSuccessfully": "\"{{name}}\" desafixado com sucesso", + "unpinFailed": "Falha ao desafixar", + "removedShortcut": "Atalho \"{{name}}\" removido", + "removeShortcutFailed": "Falha ao remover atalho", + "clearedAllRecentFiles": "Limpos todos os ficheiros recentes", + "clearFailed": "Falha ao limpar", "removeFromRecentFiles": "Remover dos ficheiros recentes", "clearAllRecentFiles": "Limpar todos os ficheiros recentes", "unpinFile": "Desafixar ficheiro", "removeShortcut": "Remover atalho", - "pinFile": "Arquivo PIN", + "pinFile": "Afixar ficheiro", "addToShortcuts": "Adicionar aos atalhos", - "pasteFailed": "A colagem falhou", - "noUndoableActions": "Ações não podem ser desfeitas", - "undoCopySuccess": "Operação de cópia desfeita: Ficheiros copiados eliminados {{count}}", - "undoCopyFailedDelete": "Falha ao desfazer: Não foi possível eliminar nenhum ficheiro copiado.", - "undoCopyFailedNoInfo": "A ação de anulação falhou: Não foi possível encontrar as informações do ficheiro copiado.", - "undoMoveSuccess": "Operação de movimentação desfeita: {{count}} os ficheiros foram movidos de volta para o local original.", - "undoMoveFailedMove": "Falha ao desfazer: Não foi possível mover nenhum ficheiro de volta.", - "undoMoveFailedNoInfo": "A ação de anulação falhou: Não foi possível encontrar as informações do ficheiro movido.", - "undoDeleteNotSupported": "A operação de eliminação não pode ser anulada: os ficheiros foram eliminados permanentemente do servidor.", - "undoTypeNotSupported": "Tipo de operação de anulação não suportado", - "undoOperationFailed": "A operação de anulação falhou", + "pasteFailed": "Falha ao colar", + "noUndoableActions": "Sem ações para desfazer", + "undoCopySuccess": "Desfeita a operação de cópia: Eliminados {{count}} ficheiros copiados", + "undoCopyFailedDelete": "Falha ao desfazer: Não foi possível eliminar os ficheiros copiados", + "undoCopyFailedNoInfo": "Falha ao desfazer: Não foi possível encontrar a informação dos ficheiros copiados", + "undoMoveSuccess": "Desfeita a operação de mover: {{count}} ficheiros movidos de volta para o local original", + "undoMoveFailedMove": "Falha ao desfazer: Não foi possível mover nenhum ficheiro de volta", + "undoMoveFailedNoInfo": "Falha ao desfazer: Não foi possível encontrar a informação dos ficheiros movidos", + "undoDeleteNotSupported": "A operação de eliminar não pode ser desfeita: Os ficheiros foram eliminados permanentemente do servidor", + "undoTypeNotSupported": "Tipo de operação de desfazer não suportado", + "undoOperationFailed": "Falha ao desfazer a operação", "unknownError": "Erro desconhecido", "confirm": "Confirmar", - "find": "Encontrar...", + "find": "Procurar...", "replace": "Substituir", - "downloadInstead": "Descarregue em vez de descarregar.", + "downloadInstead": "Transferir em alternativa", "keyboardShortcuts": "Atalhos de teclado", - "searchAndReplace": "Localizar e substituir", + "searchAndReplace": "Pesquisar e substituir", "editing": "Edição", - "search": "Pesquisa", - "findNext": "Encontre a seguir", - "findPrevious": "Encontrar Anterior", + "search": "Pesquisar", + "findNext": "Localizar seguinte", + "findPrevious": "Localizar anterior", "save": "Guardar", "selectAll": "Selecionar tudo", "undo": "Desfazer", "redo": "Refazer", - "moveLineUp": "Mova a fila para cima", - "moveLineDown": "Deslizar a linha para baixo", + "moveLineUp": "Mover linha para cima", + "moveLineDown": "Mover linha para baixo", "toggleComment": "Alternar comentário", - "autoComplete": "Preenchimento automático", - "imageLoadError": "Falha ao carregar a imagem", - "startTyping": "Comece a digitar...", + "autoComplete": "Autocompletar", + "imageLoadError": "Erro ao carregar a imagem", + "startTyping": "Comece a escrever...", "unknownSize": "Tamanho desconhecido", "fileIsEmpty": "O ficheiro está vazio", - "largeFileWarning": "Aviso de Arquivo Grande", - "largeFileWarningDesc": "Este ficheiro tem {{size}} de tamanho, o que pode causar problemas de desempenho quando é aberto como texto.", + "largeFileWarning": "Aviso de ficheiro grande", + "largeFileWarningDesc": "Este ficheiro tem {{size}} de tamanho, o que poderá causar problemas de desempenho ao ser aberto como texto.", "fileNotFoundAndRemoved": "O ficheiro \"{{name}}\" não foi encontrado e foi removido dos ficheiros recentes/fixados", - "failedToLoadFile": "Falha ao carregar o ficheiro: {{error}}", - "serverErrorOccurred": "Ocorreu um erro no servidor. Tente novamente mais tarde.", - "autoSaveFailed": "O salvamento automático falhou", - "fileAutoSaved": "Ficheiro salvo automaticamente", + "failedToLoadFile": "Erro ao carregar o ficheiro: {{error}}", + "serverErrorOccurred": "Ocorreu um erro no servidor. Por favor, tente novamente mais tarde.", + "autoSaveFailed": "Falha na gravação automática", + "fileAutoSaved": "Ficheiro gravado automaticamente", "moveFileFailed": "Falha ao mover {{name}}", - "moveOperationFailed": "A operação de movimentação falhou", - "canOnlyCompareFiles": "Só é possível comparar dois ficheiros.", - "comparingFiles": "Comparação de ficheiros: {{file1}} e {{file2}}", - "dragFailed": "A operação de arrastar falhou", + "moveOperationFailed": "Falha na operação de movimentação", + "canOnlyCompareFiles": "Só é possível comparar dois ficheiros", + "comparingFiles": "A comparar ficheiros: {{file1}} e {{file2}}", + "dragFailed": "Falha ao arrastar", "filePinnedSuccessfully": "Ficheiro \"{{name}}\" fixado com sucesso", - "pinFileFailed": "Falha ao fixar o ficheiro", + "pinFileFailed": "Erro ao fixar o ficheiro", "fileUnpinnedSuccessfully": "Ficheiro \"{{name}}\" desafixado com sucesso", - "unpinFileFailed": "Falha ao desafixar o ficheiro", - "shortcutAddedSuccessfully": "Atalho de pasta \"{{name}}\" adicionado com sucesso", + "unpinFileFailed": "Falha ao desafixar ficheiro", + "shortcutAddedSuccessfully": "Atalho para a pasta \"{{name}}\" adicionado com sucesso", "addShortcutFailed": "Falha ao adicionar atalho", "operationCompletedSuccessfully": "{{operation}} {{count}} itens com sucesso", "operationCompleted": "{{operation}} {{count}} itens", "downloadFileSuccess": "Ficheiro {{name}} descarregado com sucesso", - "downloadFileFailed": "Falha no download", + "downloadFileFailed": "Falha ao descarregar", "moveTo": "Mover para {{name}}", - "diffCompareWith": "Diff comparar com {{name}}", + "diffCompareWith": "Comparar diferenças com {{name}}", "dragOutsideToDownload": "Arraste para fora da janela para descarregar ({{count}} ficheiros)", - "newFolderDefault": "Nova pasta", - "newFileDefault": "NovoArquivo.txt", - "successfullyMovedItems": "Movidos com sucesso {{count}} itens para {{target}}", + "newFolderDefault": "NovaPasta", + "newFileDefault": "NovoFicheiro.txt", + "successfullyMovedItems": "{{count}} itens movidos com sucesso para {{target}}", "move": "Mover", - "searchInFile": "Pesquisar no ficheiro (Ctrl+F)", + "searchInFile": "Procurar no ficheiro (Ctrl+F)", "showKeyboardShortcuts": "Mostrar atalhos de teclado", - "decreaseFontSize": "Diminuir o tamanho da letra", - "increaseFontSize": "Aumentar o tamanho da letra", - "startWritingMarkdown": "Comece a escrever o seu conteúdo em Markdown...", - "loadingFileComparison": "Carregando comparação de ficheiros...", + "decreaseFontSize": "Diminuir tamanho da letra", + "increaseFontSize": "Aumentar tamanho da letra", + "startWritingMarkdown": "Comece a escrever o seu conteúdo em markdown...", + "loadingFileComparison": "A carregar comparação de ficheiros...", "reload": "Recarregar", "compare": "Comparar", "sideBySide": "Lado a lado", "inline": "Em linha", "fileComparison": "Comparação de ficheiros: {{file1}} vs {{file2}}", - "fileTooLarge": "Ficheiro muito grande: {{error}}", - "sshConnectionFailed": "A ligação SSH falhou. Verifique a sua ligação a {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Falha ao carregar o ficheiro: {{error}}", - "connecting": "Ligando...", - "connectedSuccessfully": "Ligação estabelecida com sucesso.", - "totpVerificationFailed": "A verificação TOTP falhou.", - "warpgateVerificationFailed": "A autenticação do portal de guerra falhou.", - "authenticationFailed": "A autenticação falhou.", - "incorrectPassphrase": "Palavra-passe incorreta. Tente novamente.", + "fileTooLarge": "Ficheiro demasiado grande: {{error}}", + "sshConnectionFailed": "Falha na ligação SSH. Verifique a sua ligação a {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Falha ao carregar ficheiro: {{error}}", + "connecting": "A ligar...", + "connectedSuccessfully": "Ligado com sucesso", + "totpVerificationFailed": "Falha na verificação TOTP", + "warpgateVerificationFailed": "Falha na autenticação Warpgate", + "authenticationFailed": "Falha na autenticação", + "incorrectPassphrase": "Frase-passe incorreta. Tente novamente.", "verificationCodePrompt": "Código de verificação:", - "changePermissions": "Alterar permissões", - "currentPermissions": "Permissões atuais", + "changePermissions": "Alterar Permissões", + "currentPermissions": "Permissões Atuais", "owner": "Proprietário", "group": "Grupo", "others": "Outros", - "read": "Ler", - "write": "Escrever", - "execute": "Executar", + "read": "Leitura", + "write": "Escrita", + "execute": "Execução", "permissionsChangedSuccessfully": "Permissões alteradas com sucesso", - "failedToChangePermissions": "Falha ao alterar as permissões", + "failedToChangePermissions": "Falha ao alterar permissões", "name": "Nome", "sortByName": "Nome", "sortByDate": "Data de modificação", @@ -1782,16 +1915,16 @@ "root": "Raiz", "new": "Novo", "sortBy": "Ordenar por", - "items": "Artigos", - "selected": "Selecionado", + "items": "Itens", + "selected": "Selecionados", "editor": "Editor", "octal": "Octal", "storage": "Armazenamento", "disk": "Disco", - "used": "Usado", + "used": "Utilizado", "of": "de", - "toggleSidebar": "Alternar barra lateral", - "cannotLoadPdf": "Não foi possível carregar o PDF.", + "toggleSidebar": "Alternar Barra Lateral", + "cannotLoadPdf": "Não é possível carregar o PDF", "pdfLoadError": "Ocorreu um erro ao carregar este ficheiro PDF.", "loadingPdf": "A carregar PDF...", "loadingPage": "A carregar página..." @@ -1801,199 +1934,199 @@ "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.", + "noHostsConnected": "Não há outros hosts do gestor 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", + "selectDestinationHost": "Selecionar 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", + "browseFolders": "Navegar pelas pastas de destino", + "browseDestination": "Navegar ou introduzir o caminho", + "confirmCopy": "Copiar", "confirmMove": "Mover", - "transferring": "Transferindo…", - "compressing": "Comprimindo…", - "extracting": "Extração…", - "transferringItems": "Transferir {{current}} de {{total}} itens…", + "transferring": "A transferir…", + "compressing": "A comprimir…", + "extracting": "A extrair…", + "transferringItems": "A transferir {{current}} de {{total}} itens…", "transferSuccess": "Transferência concluída", - "transferError": "A transferência falhou", + "transferError": "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.", + "itemsSummary": "{{count}} itens", + "destMustBeDirectory": "O destino deve ser uma pasta para transferências de vários itens.", + "selectThisFolder": "Selecionar esta pasta", + "browsePathWillBeCreated": "Esta pasta ainda não existe. Será criada quando a transferência for iniciada.", "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.", + "hostConnecting": "A ligar…", + "hostDisconnected": "Desligado", + "hostAuthRequired": "Autenticação necessária — abra o Gestor de Ficheiros neste host primeiro", "hostConnectionFailed": "Falha na ligação", - "metricsTitle": "Horários de transferência", + "metricsTitle": "Tempos de transferência", "metricsPrepare": "Preparar destino: {{duration}}", - "metricsCompress": "Comprimir na origem: {{duration}}", + "metricsCompress": "Compressão 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}}", + "metricsTransfer": "Ponta a ponta: {{throughput}} ({{duration}})", + "metricsExtract": "Extração no destino: {{duration}}", + "metricsSourceDelete": "Remoção da origem: {{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": "A comprimir no host de origem…", + "progressExtracting": "A extrair no destino…", + "progressTransferring": "A transferir dados…", + "progressReconnecting": "A reconectar…", + "parallelSegmentsLabel": "Vias de transferência paralelas", + "parallelSegmentsOption": "{{count}} vias", + "parallelSegmentsHint": "Os ficheiros grandes são divididos em partes de 256 MB. Múltiplas vias utilizam ligações separadas (como iniciar várias transferências) para um maior débito total.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} vias)", + "progressTransferringItems": "A transferir ficheiros ({{current}} de {{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.", + "jumpHostLimitation": "Ambos os hosts têm de ser acessíveis a partir do servidor Termix. O encaminhamento direto entre hosts não é suportado.", "cancel": "Cancelar", "methodLabel": "Método de transferência", - "methodAuto": "Auto", + "methodAuto": "Automático", "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.", + "methodAutoHint": "Escolhe tar ou SFTP por ficheiro com base no número de ficheiros, tamanho e compressibilidade. Os ficheiros únicos usam sempre SFTP por streaming.", + "methodTarHint": "Comprime na origem, transfere um arquivo, extrai no destino. Requer tar em ambos os hosts Unix.", + "methodItemSftpHint": "Transfere cada ficheiro individualmente por SFTP. Funciona em todos os hosts, incluindo Windows.", + "methodPreviewLoading": "A calcular o método de transferência…", + "methodPreviewError": "Não foi possível pré-visualizar o método de transferência. O servidor ainda escolherá um método quando iniciar.", + "methodPreviewWillUseTar": "Utilizará: Arquivo Tar", + "methodPreviewWillUseItemSftp": "Utilizará: SFTP por ficheiro", + "methodPreviewScanSummary": "{{fileCount}} ficheiros, {{totalSize}} no total (analisados no host de origem).", + "methodItemSftpLimitation": "Cada ficheiro utiliza o mesmo stream SFTP como uma cópia de um único ficheiro, um após o outro. O progresso é combinado para todos os ficheiros, pelo que a barra se move lentamente durante ficheiros grandes.", "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 envolvido é o Windows — o 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": "Optou por SFTP ficheiro a ficheiro.", + "user_tar": "Optou por ficheiro tar.", + "tar_unavailable": "O tar não está disponível num ou em ambos os anfitriões — será usado SFTP ficheiro a ficheiro.", + "windows_host": "Está envolvido um anfitrião Windows — o tar não é utilizado.", + "auto_multi_large": "Automático: vários ficheiros, incluindo um grande ({{largestSize}}) com dados compressíveis — o tar agrupa numa transferência única.", + "auto_single_large_in_archive": "Automático: um ficheiro grande ({{largestSize}}) neste conjunto — SFTP ficheiro a ficheiro.", + "auto_many_incompressible": "Automático: dados maioritariamente incompressíveis — SFTP ficheiro a ficheiro.", + "auto_many_files": "Automático: muitos ficheiros ({{fileCount}}) — o tar reduz a sobrecarga por ficheiro.", + "auto_default": "Automático: SFTP ficheiro a ficheiro para este conjunto." }, "progressCancel": "Cancelar", - "progressCancelling": "Cancelando…", - "progressStalled": "Paralisado", - "resumedHint": "Religado a uma transferência ativa iniciada noutra janela.", + "progressCancelling": "A cancelar…", + "progressStalled": "Parado", + "resumedHint": "Reconectado 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", + "transferCancelledCopyHint": "Poderão permanecer ficheiros parciais no destino.", + "transferCancelledMoveHint": "Poderão permanecer ficheiros parciais no destino. Os ficheiros de origem não foram removidos.", + "cleanupDestFiles": "Limpar destino", "cleanupDestFilesSuccess": "Ficheiros parciais removidos do destino.", - "cleanupDestFilesPartial": "Alguns ficheiros parciais não puderam ser removidos.", + "cleanupDestFilesPartial": "Não foi possível remover alguns ficheiros parciais.", "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." + "cleanupDestFilesError": "A limpeza falhou.", + "retryTransfer": "Repetir", + "retryTransferError": "A repetição falhou.", + "transferFailedRetryHint": "Foram mantidos dados parciais no destino. A repetição será retomada quando a ligação for restabelecida." }, "tunnels": { "noSshTunnels": "Sem túneis SSH", - "createFirstTunnelMessage": "Ainda não criou nenhum túnel SSH. Configure as ligações de túnel no Gestor de Hosts para começar.", - "connected": "Conectado", - "disconnected": "Desconectado", - "connecting": "Ligando...", + "createFirstTunnelMessage": "Ainda não criou nenhum túnel SSH. Configure as ligações de túnel no Gestor de Anfitriões para começar.", + "connected": "Ligado", + "disconnected": "Desligado", + "connecting": "A ligar...", "error": "Erro", - "canceling": "Cancelando...", + "canceling": "A cancelar...", "connect": "Ligar", - "disconnect": "Desconectar", + "disconnect": "Desligar", "cancel": "Cancelar", - "port": "Porto", - "localPort": "Porto local", + "port": "Porta", + "localPort": "Porta local", "remotePort": "Porta remota", "currentHostPort": "Porta do host atual", - "endpointPort": "Porta de ponto final", + "endpointPort": "Porta do endpoint", "bindIp": "IP local", "endpointSshConfig": "Configuração SSH do endpoint", - "endpointSshHost": "Host SSH de ponto final", - "endpointSshHostPlaceholder": "Selecione um host configurado", - "endpointSshHostRequired": "Selecione um host SSH de ponto final para cada túnel de cliente.", + "endpointSshHost": "Host SSH do endpoint", + "endpointSshHostPlaceholder": "Selecionar um host configurado", + "endpointSshHostRequired": "Selecione um host SSH do endpoint para cada túnel de cliente.", "attempt": "Tentativa {{current}} de {{max}}", "nextRetryIn": "Próxima tentativa em {{seconds}} segundos", - "clientTunnels": "Túneis para clientes", - "clientTunnel": "Túnel do Cliente", + "clientTunnels": "Túneis de cliente", + "clientTunnel": "Túnel de cliente", "addClientTunnel": "Adicionar túnel de cliente", - "noClientTunnels": "Nenhum túnel de cliente configurado nesta área de trabalho.", + "noClientTunnels": "Nenhum túnel de cliente configurado neste desktop.", "tunnelName": "Nome do túnel", "remoteHost": "Host remoto", - "autoStart": "Início automático", - "clientAutoStartDesc": "Inicia quando este cliente de ambiente de trabalho é aberto e permanece ligado.", - "clientManualStartDesc": "Utilize os botões Iniciar e Parar a partir desta linha. O Termix não os abrirá automaticamente.", - "clientRemoteServerNote": "O encaminhamento remoto pode exigir as permissões AllowTcpForwarding e GatewayPorts no servidor SSH do endpoint. A porta remota é fechada quando esta área de trabalho se desliga.", - "clientTunnelStarted": "O túnel do cliente foi iniciado.", - "clientTunnelStopped": "O túnel do cliente foi interrompido.", - "tunnelTestSucceeded": "O teste no túnel foi bem-sucedido.", - "tunnelTestFailed": "O teste do túnel falhou", - "localSaved": "Túneis de clientes guardados", - "localSaveError": "Falha ao guardar os túneis do cliente local.", - "invalidBindIp": "O endereço IP local deve ser um endereço IPv4 válido.", - "invalidLocalTargetIp": "O endereço IP de destino local deve ser um endereço IPv4 válido.", + "autoStart": "Arranque automático", + "clientAutoStartDesc": "Inicia quando este cliente de desktop abre e permanece ligado.", + "clientManualStartDesc": "Utilize Iniciar e Parar nesta linha. O Termix não o abrirá automaticamente.", + "clientRemoteServerNote": "O reencaminhamento remoto pode exigir AllowTcpForwarding e GatewayPorts no servidor SSH do endpoint. A porta remota fecha quando este desktop se desliga.", + "clientTunnelStarted": "Túnel de cliente iniciado", + "clientTunnelStopped": "Túnel de cliente parado", + "tunnelTestSucceeded": "Teste do túnel bem-sucedido", + "tunnelTestFailed": "Teste do túnel falhou", + "localSaved": "Túneis de cliente guardados", + "localSaveError": "Falha ao guardar os túneis de cliente locais", + "invalidBindIp": "O IP local deve ser um endereço IPv4 válido.", + "invalidLocalTargetIp": "O IP de destino local deve ser um endereço IPv4 válido.", "invalidLocalPort": "A porta local deve estar entre 1 e 65535.", "invalidRemotePort": "A porta remota deve estar entre 1 e 65535.", "invalidLocalTargetPort": "A porta de destino local deve estar entre 1 e 65535.", - "invalidEndpointPort": "A porta do ponto final deve estar entre 1 e 65535.", - "duplicateAutoStartBind": "Apenas um túnel de cliente de arranque automático pode utilizar {{bind}}.", + "invalidEndpointPort": "A porta do endpoint deve estar entre 1 e 65535.", + "duplicateAutoStartBind": "Apenas um túnel de cliente com arranque automático pode utilizar {{bind}}.", "manualControlError": "Falha ao atualizar o estado do túnel.", "active": "Ativo", "start": "Iniciar", "stop": "Parar", - "test": "Teste", - "type": "Tipo de túnel", + "test": "Testar", + "type": "Tipo de Túnel", "typeLocal": "Local (-L)", "typeRemote": "Remoto (-R)", "typeDynamic": "Dinâmico (-D)", - "typeServerLocalDesc": "Host atual para endpoint.", - "typeServerRemoteDesc": "Retorno do ponto final para o host atual.", - "typeClientLocalDesc": "Computador local para ponto final.", - "typeClientRemoteDesc": "O ponto final regressa ao computador local.", + "typeServerLocalDesc": "Do host atual para o endpoint.", + "typeServerRemoteDesc": "Do endpoint de volta para o host atual.", + "typeClientLocalDesc": "Do computador local para o endpoint.", + "typeClientRemoteDesc": "Do endpoint de volta para o computador local.", "typeClientDynamicDesc": "SOCKS no computador local.", - "typeDynamicDesc": "Encaminhar tráfego SOCKS5 CONNECT através de SSH", + "typeDynamicDesc": "Encaminha tráfego SOCKS5 CONNECT via SSH", "forwardDescriptionServerLocal": "Host atual {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Ponto final {{endpointPort}} → host atual {{sourcePort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host atual {{sourcePort}}.", "forwardDescriptionServerDynamic": "SOCKS no host atual {{sourcePort}}.", "forwardDescriptionClientLocal": "Local {{sourcePort}} → remoto {{endpointPort}}.", "forwardDescriptionClientRemote": "Remoto {{sourcePort}} → local {{endpointPort}}.", "forwardDescriptionClientDynamic": "SOCKS na porta local {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → MEIAS via {{endpoint}}", - "autoNameClientLocal": "Localização {{localPort}} → {{endpoint}} {{remotePort}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", - "autoNameClientDynamic": "MEIAS {{localPort}} via {{endpoint}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", "route": "Rota:", - "lastStarted": "Última vez iniciado", + "lastStarted": "Último início", "lastTested": "Último teste", "lastError": "Último erro", - "maxRetries": "Número máximo de tentativas", + "maxRetries": "Máx. tentativas", "maxRetriesDescription": "Número máximo de tentativas.", - "retryInterval": "Intervalo de tentativas (segundos)", - "retryIntervalDescription": "Tempo de espera entre tentativas de repetição.", + "retryInterval": "Intervalo de repetição (segundos)", + "retryIntervalDescription": "Tempo de espera entre tentativas.", "local": "Local", "remote": "Remoto", "destination": "Destino", - "host": "Anfitrião", + "host": "Servidor", "mode": "Modo", - "noHostSelected": "Nenhum host selecionado", - "working": "Trabalho..." + "noHostSelected": "Nenhum servidor selecionado", + "working": "A trabalhar..." }, "cardGrid": { "dragToMove": "Arraste para mover", - "dragToResize": "Arraste para redimensionar.", + "dragToResize": "Arraste para redimensionar", "changeWidth": "Alterar largura", "removeCard": "Remover {{label}}", "addCard": "Adicionar", "columns": "Colunas", - "empty": "Sem cartões. Utilize a opção \"Adicionar\" abaixo para inserir cartões." + "empty": "Nenhum cartão. Utilize Adicionar abaixo para colocar cartões." }, "hostMetrics": { "cpu": "CPU", @@ -2004,174 +2137,174 @@ "processes": "Processos", "available": "Disponível", "free": "Livre", - "connecting": "Ligando...", + "connecting": "A ligar...", "connectionFailed": "Falha ao ligar ao servidor", - "naCpus": "N/A CPU(s)", + "naCpus": "N/D CPU(s)", "cpuCores_one": "{{count}} Núcleo", "cpuCores_other": "{{count}} Núcleos", "cpuUsage": "Utilização da CPU", - "memoryUsage": "Uso de memória", + "memoryUsage": "Utilização da memória", "diskUsage": "Utilização do disco", "temperature": "Temperatura", - "highestTemperature": "Temperatura mais elevada", - "failedToFetchHostConfig": "Falha ao obter a configuração do host", + "highestTemperature": "Temperatura máxima", + "failedToFetchHostConfig": "Falha ao obter a configuração do servidor", "serverOffline": "Servidor offline", - "cannotFetchMetrics": "Não foi possível obter métricas do servidor offline.", - "totpFailed": "A verificação TOTP falhou.", - "noneAuthNotSupported": "O Host Metrics não suporta o tipo de autenticação 'nenhum'.", + "cannotFetchMetrics": "Não é possível obter métricas do servidor offline", + "totpFailed": "Falha na verificação TOTP", + "noneAuthNotSupported": "Host Metrics não suporta o tipo de autenticação 'none'.", "noHostSelected": "Nenhum host selecionado", "load": "Carga", - "systemInfo": "Informações do sistema", + "systemInfo": "Informação do Sistema", "hostname": "Nome do host", - "operatingSystem": "Sistema operativo", + "operatingSystem": "Sistema Operativo", "kernel": "Kernel", "seconds": "segundos", - "networkInterfaces": "Interfaces de rede", + "networkInterfaces": "Interfaces de Rede", "noInterfacesFound": "Nenhuma interface de rede encontrada", "noProcessesFound": "Nenhum processo encontrado", "processesTotal": "total", - "processesRunning": "corrida", - "loginStats": "Estatísticas de login SSH", - "noRecentLoginData": "Sem dados de login recente", + "processesRunning": "em execução", + "loginStats": "Estatísticas de início de sessão SSH", + "noRecentLoginData": "Sem dados de início de sessão recentes", "executingQuickAction": "A executar {{name}}...", "quickActionSuccess": "{{name}} concluído com sucesso", "quickActionFailed": "{{name}} falhou", "quickActionError": "Falha ao executar {{name}}", "ports": { - "title": "Portas de escuta", + "title": "Portas em escuta", "protocol": "Protocolo", - "port": "Porto", - "address": "Morada", + "port": "Porta", + "address": "Endereço", "process": "Processo", - "search": "Portas de pesquisa...", - "allProtocols": "Tudo", - "noData": "Sem dados de portas de escuta" + "search": "Pesquisar portas...", + "allProtocols": "Todos", + "noData": "Sem dados de portas em escuta" }, "firewall": { "title": "Firewall", "inactive": "Inativo", "policy": "Política", "rules": "regras", - "noRules": "Não existem regras nesta corrente.", - "noData": "Não existem dados de firewall disponíveis.", + "noRules": "Sem regras nesta cadeia", + "noData": "Sem dados de firewall disponíveis", "action": "Ação", "protocol": "Proto", - "port": "Porto", - "source": "Fonte", - "anywhere": "Em qualquer lugar", - "chains": "correntes" + "port": "Porta", + "source": "Origem", + "anywhere": "Qualquer lugar", + "chains": "cadeias" }, - "loadAvg": "Carga média", - "swap": "Permuta", + "loadAvg": "Carga Média", + "swap": "Swap", "architecture": "Arquitetura", "refresh": "Atualizar", "retry": "Tentar novamente", "customize": "Personalizar layout", - "reset": "Reiniciar", - "tabLive": "Vivo", - "editModeInstructions": "Arraste os cartões para os reorganizar, arraste a margem inferior para os redimensionar e utilize o botão de largura para alterar a extensão de um cartão. Adicione ou remova cartões abaixo.", + "reset": "Redefinir", + "tabLive": "Ao Vivo", + "editModeInstructions": "Arraste os cartões para reorganizar, arraste a borda inferior para redimensionar e use o botão de largura para alterar a largura do cartão. Adicione ou remova cartões abaixo.", "managers": { "services": "Serviços", "processInspector": "Inspetor de Processos", "logViewer": "Visualizador de Registos", - "cron": "Tarefas agendadas", + "cron": "Tarefas Cron", "packages": "Pacotes", "ssl": "Certificados SSL", "firewall": "Firewall", - "users": "Utilizadores e permissões", - "healthCheck": "Exames de saúde", - "diskBreakdown": "Avaria do disco", + "users": "Utilizadores e Permissões", + "healthCheck": "Verificações de Estado", + "diskBreakdown": "Análise de Disco", "systemdTimers": "Temporizadores", - "topMemory": "Topo por memória", + "topMemory": "Top por Memória", "noData": "Sem dados", "sudoHint": "Defina uma palavra-passe sudo para este host no editor de hosts para ativar ações privilegiadas.", - "filter": "Filtro...", + "filter": "Filtrar...", "start": "Iniciar", "stop": "Parar", "restart": "Reiniciar", "actionDone": "{{name}} atualizado", "actionFailed": "A ação falhou", - "signalSent": "Sinal enviado para PID {{pid}}", - "killHint": "Clique em: terminar (SIGTERM). Clique com o botão direito: forçar o encerramento (SIGKILL).", - "working": "Trabalho...", - "update": "Atualização", + "signalSent": "Sinal enviado para o PID {{pid}}", + "killHint": "Clique: terminar (SIGTERM). Clique direito: forçar encerramento (SIGKILL).", + "working": "A trabalhar...", + "update": "Atualizar", "upgradeAll": "Atualizar tudo", - "allUpToDate": "Tudo está atualizado.", + "allUpToDate": "Tudo está atualizado", "save": "Guardar", "command": "Comando", - "enabled": "Habilitado", + "enabled": "Ativado", "cronSaved": "Crontab atualizado", "clients": "Clientes", - "dryRun": "Ensaio geral", + "dryRun": "Simulação", "renew": "Renovar", - "noAcmeClient": "Não foi encontrado nenhum cliente ACME (certbot ou acme.sh) neste host.", - "addInputRule": "Adicionar regra de ENTRADA", - "firewallWarning": "As alterações são válidas apenas em tempo de execução até serem persistidas. Tenha cuidado para não bloquear o seu próprio acesso.", + "noAcmeClient": "Nenhum cliente ACME (certbot ou acme.sh) encontrado neste servidor.", + "addInputRule": "Adicionar regra INPUT", + "firewallWarning": "As alterações são efetuadas apenas em execução até serem guardadas. Tenha cuidado para não ficar bloqueado.", "ruleApplied": "Regra aplicada", "invalidPort": "Introduza uma porta válida (1-65535)", "newUsername": "Novo nome de utilizador", "addUser": "Adicionar", - "deleteUser": "Apagar utilizador", - "noHealthChecks": "Nenhuma verificação de integridade configurada ainda.", + "deleteUser": "Eliminar utilizador", + "noHealthChecks": "Nenhuma verificação de estado configurada ainda.", "follow": "Seguir", - "noLogData": "Sem saída de log.", + "noLogData": "Sem saída de registo.", "tree": "Árvore", - "enableDisable": "Ativar/desativar no arranque", + "enableDisable": "Ativar / desativar no arranque", "grantSudo": "Conceder sudo", - "revokeSudo": "Revogar o sudo", + "revokeSudo": "Revogar sudo", "sslIssueCert": "Emitir certificado", "sslExpired": "Expirado", "sslInDays": "em {{days}}d", - "sslNeedDomain": "Introduza pelo menos um domínio.", + "sslNeedDomain": "Introduza pelo menos um domínio", "sslIssued": "Certificado emitido", - "sslDomainsPlaceholder": "exemplo.com, www.exemplo.com", - "sslHttpStandalone": "HTTP (trabalhador independente)", - "sslHttpWebroot": "HTTP (raiz web)", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (autónomo)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "fornecedor de DNS (ex: Cloudflare)", - "sslIssueHint": "As credenciais do fornecedor de DNS já devem estar configuradas no host.", + "sslDnsProvider": "Fornecedor DNS (ex. cloudflare)", + "sslIssueHint": "As credenciais do fornecedor DNS já devem estar configuradas no host.", "sslRevoke": "Revogar certificado", "sslRevoked": "Certificado revogado", - "sslRevokeConfirm": "Revogar e remover o certificado \"{{name}}\"? Isso não pode ser desfeito.", - "healthRun": "Correr", + "sslRevokeConfirm": "Revogar e remover o certificado \"{{name}}\"? Esta ação não pode ser anulada.", + "healthRun": "Executar", "healthName": "Nome", - "healthTarget": "Endereço do host", + "healthTarget": "Host / endereço", "healthAddCheck": "Adicionar verificação", - "healthSaved": "Exames de saúde guardados", - "healthMissingFields": "Cada verificação necessita de um nome e de um alvo.", + "healthSaved": "Verificações de saúde guardadas", + "healthMissingFields": "Cada verificação necessita de um nome e um destino", "logFile": "Ficheiro", "logUnit": "Unidade", "logCustomPath": "Caminho personalizado em /var/log (opcional)", - "logGrep": "Linhas de filtro...", - "firewallPersist": "Regras persistentes", - "firewallPersisted": "As regras da firewall persistiram.", + "logGrep": "Filtrar linhas...", + "firewallPersist": "Persistir regras", + "firewallPersisted": "Regras de firewall persistidas", "wireguard": "WireGuard", - "tailscale": "Escama da cauda", - "wgNotInstalled": "O WireGuard não está instalado neste host.", + "tailscale": "Tailscale", + "wgNotInstalled": "O WireGuard não está instalado neste host", "wgNoInterfaces": "Nenhuma interface WireGuard configurada", - "wgInterfaceUp": "Levantar a questão", - "wgInterfaceDown": "Derrubem", - "wgBringingUp": "Trazendo {{name}} para cima...", - "wgBringingDown": "Derrubando {{name}}...", - "wgInterfaceUpDone": "{{name}} está ativo", - "wgInterfaceDownDone": "{{name}} está inativo", - "wgListenPort": "Porto", + "wgInterfaceUp": "Ativar", + "wgInterfaceDown": "Desativar", + "wgBringingUp": "A ativar {{name}}...", + "wgBringingDown": "A desativar {{name}}...", + "wgInterfaceUpDone": "{{name}} está ativada", + "wgInterfaceDownDone": "{{name}} está desativada", + "wgListenPort": "Porta", "wgPublicKey": "Chave pública", - "wgEndpoint": "Ponto final", + "wgEndpoint": "Endpoint", "wgAllowedIPs": "IPs permitidos", - "wgLastHandshake": "Último aperto de mão", + "wgLastHandshake": "Último handshake", "wgHandshakeNever": "Nunca", - "wgTransfer": "Transferir", - "tsNotInstalled": "O Tailscale não está instalado neste host.", - "tsRunning": "Corrida", - "tsStopped": "Parou", + "wgTransfer": "Transferência", + "tsNotInstalled": "O Tailscale não está instalado neste host", + "tsRunning": "A executar", + "tsStopped": "Parado", "tsEnable": "Ligar", - "tsDisable": "Desconectar", - "tsEnabling": "Ligar-se à Tailscale...", - "tsDisabling": "Desligar-se do Tailscale...", - "tsEnabled": "Escama de cauda ligada", - "tsDisabled": "Escama de cauda desconectada", - "tsIPs": "IPs da Tailscale", + "tsDisable": "Desligar", + "tsEnabling": "A ligar ao Tailscale...", + "tsDisabling": "A desligar do Tailscale...", + "tsEnabled": "Tailscale ligado", + "tsDisabled": "Tailscale desligado", + "tsIPs": "IPs do Tailscale", "tsPeers": "Pares", "tsOnline": "Online", "tsOffline": "Offline", @@ -2181,71 +2314,71 @@ } }, "auth": { - "tagline": "SSH auto-hospedado e gestão de desktop remoto", - "loginTitle": "bem vindo de volta", - "registerTitle": "Criar uma conta", - "forgotPassword": "Esqueceu-se da sua senha?", - "rememberMe": "Lembre-se do dispositivo durante 30 dias (incluindo TOTP)", - "noAccount": "Não tem conta?", - "hasAccount": "Já tem conta?", + "tagline": "Gestão auto-hospedada de SSH e ambiente de trabalho remoto", + "loginTitle": "Bem-vindo de volta", + "registerTitle": "Criar Conta", + "forgotPassword": "Esqueceu-se da palavra-passe?", + "rememberMe": "Lembrar dispositivo por 30 dias (inclui TOTP)", + "noAccount": "Não tem uma conta?", + "hasAccount": "Já tem uma conta?", "twoFactorAuth": "Autenticação de dois fatores", "enterCode": "Introduza o código de verificação", - "backupCode": "Ou utilize o código de backup.", - "verifyCode": "Código de verificação", - "redirectingToApp": "Redirecionando para a aplicação...", + "backupCode": "Ou utilize código de backup", + "verifyCode": "Verificar Código", + "redirectingToApp": "A redirecionar para a aplicação...", "sshAuthenticationRequired": "Autenticação SSH necessária", - "sshNoKeyboardInteractive": "Autenticação interativa por teclado indisponível", - "sshAuthenticationFailed": "Autenticação falhou", - "sshAuthenticationTimeout": "Tempo limite de autenticação", - "sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação interativa por teclado. Por favor, forneça a sua palavra-passe ou chave SSH.", + "sshNoKeyboardInteractive": "Autenticação por teclado interativo indisponível", + "sshAuthenticationFailed": "Falha na autenticação", + "sshAuthenticationTimeout": "Tempo limite de autenticação excedido", + "sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação por teclado interativo. Por favor, forneça a sua palavra-passe 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.", + "sshTimeoutDescription": "A tentativa de autenticação expirou. Por favor, tente novamente.", "sshProvideCredentialsDescription": "Por favor, forneça as suas credenciais SSH para se ligar a este servidor.", "sshPasswordDescription": "Introduza a palavra-passe para esta ligação SSH.", - "sshKeyPasswordDescription": "Se a sua chave SSH estiver encriptada, insira aqui a palavra-passe.", - "passphraseRequired": "Senha obrigatória", - "passphraseRequiredDescription": "A chave SSH está encriptada. Introduza a palavra-passe para desbloqueá-la.", - "back": "Costas", - "firstUser": "Primeiro utilizador", - "firstUserMessage": "É o primeiro utilizador e será nomeado administrador. Pode visualizar as definições de administrador no menu suspenso de utilizadores na barra lateral. Se acha que isto foi um erro, verifique os logs do Docker ou crie uma issue no GitHub.", + "sshKeyPasswordDescription": "Se a sua chave SSH estiver encriptada, introduza a frase-senha aqui.", + "passphraseRequired": "Frase-senha obrigatória", + "passphraseRequiredDescription": "A chave SSH está encriptada. Introduza a frase-senha para a desbloquear.", + "back": "Voltar", + "firstUser": "Primeiro Utilizador", + "firstUserMessage": "É o primeiro utilizador e será configurado como administrador. Pode ver as definições de administrador no menu suspenso do utilizador na barra lateral. Se pensa que isto é um erro, verifique os logs do Docker, ou crie um issue no GitHub.", "external": "Externo", - "loginWithExternal": "Login com fornecedor externo", - "loginWithExternalDesc": "Faça login utilizando o fornecedor de identidade externo configurado.", - "externalNotSupportedInElectron": "A autenticação externa ainda não é suportada na aplicação Electron. Por favor utilize a versão web para o login OIDC.", + "loginWithExternal": "Iniciar sessão com fornecedor externo", + "loginWithExternalDesc": "Inicie sessão utilizando o seu fornecedor de identidade externo configurado", + "externalNotSupportedInElectron": "A autenticação externa ainda não é suportada na aplicação Electron. Utilize a versão web para iniciar sessão via OIDC.", "loginWithProvider": "Iniciar sessão com {{name}}", - "orContinueWith": "ou continue com", + "orContinueWith": "ou continuar com", "ldapUsername": "Nome de utilizador LDAP", "ldapPassword": "Palavra-passe LDAP", - "ldapSignIn": "Entrar", - "ldapLoginFailed": "Falha no login LDAP", + "ldapSignIn": "Iniciar sessão", + "ldapLoginFailed": "Falha no início de sessão LDAP", "resetPasswordButton": "Redefinir palavra-passe", - "sendResetCode": "Enviar código de reinicialização", - "resetCodeDesc": "Introduza o seu nome de utilizador para receber um código de redefinição de palavra-passe. O código será registado nos logs do contentor Docker.", + "sendResetCode": "Enviar código de reposição", + "resetCodeDesc": "Introduza o seu nome de utilizador para receber um código de reposição de palavra-passe. O código será registado nos logs do contentor Docker.", "resetCode": "Código de reposição", - "verifyCodeButton": "Código de verificação", - "enterResetCode": "Introduza o código de 6 dígitos dos registos do contentor Docker para o utilizador:", - "newPassword": "Nova Senha", - "confirmNewPassword": "Confirme a sua senha", - "enterNewPassword": "Introduza a sua nova senha de utilizador:", - "signUp": "Inscrever-se", - "desktopApp": "Aplicação para desktop", - "loggingInToDesktopApp": "Aceder ao aplicativo para desktop", - "loadingServer": "Servidor de carregamento...", - "dataLossWarning": "Redefinir a sua palavra-passe desta forma apagará todos os seus hosts SSH guardados, credenciais e outros dados encriptados. Esta ação é irreversível. Utilize este método apenas se se tiver esquecido da sua senha e não estiver conectado.", + "verifyCodeButton": "Verificar código", + "enterResetCode": "Introduza o código de 6 dígitos dos logs do contentor Docker para o utilizador:", + "newPassword": "Nova palavra-passe", + "confirmNewPassword": "Confirmar palavra-passe", + "enterNewPassword": "Introduza a nova palavra-passe para o utilizador:", + "signUp": "Registar", + "desktopApp": "Aplicação de ambiente de trabalho", + "loggingInToDesktopApp": "A iniciar sessão na aplicação de ambiente de trabalho", + "loadingServer": "A carregar servidor...", + "dataLossWarning": "Redefinir a sua palavra-passe desta forma irá eliminar todos os anfitriões SSH guardados, credenciais e outros dados encriptados. Esta ação não pode ser desfeita. Utilize apenas se se esqueceu da sua palavra-passe e não tem sessão iniciada.", "authenticationDisabled": "Autenticação desativada", - "authenticationDisabledDesc": "Todos os métodos de autenticação estão desativados de momento. Entre em contacto com o administrador.", - "passwordLoginDisabledDesc": "O login com palavra-passe está desativado. Utilize uma chave de acesso ou um fornecedor de autenticação externo.", - "signInWithPasskey": "Faça login com a chave de acesso.", - "passkeyLoginFailed": "Falha no login com a chave de acesso", - "attemptsRemaining": "{{count}} restantes tentativas", - "confirmResetDataWipe": "Esta conta não foi acedida desde a atualização da encriptação, pelo que os seus dados armazenados não podem ser recuperados sem a palavra-passe antiga. A reposição eliminará permanentemente os seus hosts, credenciais e trechos de código. Continuar?" + "authenticationDisabledDesc": "Todos os métodos de autenticação estão atualmente desativados. Contacte o seu administrador.", + "passwordLoginDisabledDesc": "O início de sessão com palavra-passe está desativado. Utilize uma passkey ou um fornecedor de autenticação externo.", + "signInWithPasskey": "Iniciar sessão com passkey", + "passkeyLoginFailed": "Falha no início de sessão com passkey", + "attemptsRemaining": "{{count}} tentativas restantes", + "confirmResetDataWipe": "Esta conta não iniciou sessão desde a atualização da encriptação, pelo que os dados armazenados não podem ser recuperados sem a palavra-passe antiga. A reposição irá eliminar permanentemente os seus hosts, credenciais e snippets. Continuar?" }, "hostKey": { "verifyNewHost": "Verificar chave de host SSH", - "keyChangedWarning": "Chave do host SSH alterada", - "firstConnectionTitle": "Primeira vez que me ligo a este host.", - "firstConnectionDescription": "Não foi possível estabelecer a autenticidade deste host. Verifique se a impressão digital corresponde ao esperado.", - "keyChangedDescription": "A chave do host para este servidor foi alterada desde a sua última ligação. Isto pode indicar um problema de segurança.", + "keyChangedWarning": "Chave de host SSH alterada", + "firstConnectionTitle": "Primeira ligação a este host", + "firstConnectionDescription": "Não é possível estabelecer a autenticidade deste host. Verifique se a impressão digital corresponde ao esperado.", + "keyChangedDescription": "A chave de host deste servidor foi alterada desde a sua última ligação. Isto pode indicar um problema de segurança.", "previousKey": "Chave anterior", "newFingerprint": "Nova impressão digital", "fingerprint": "Impressão digital", @@ -2255,231 +2388,232 @@ "acceptNewKey": "Aceitar nova chave e continuar" }, "errors": { - "databaseConnection": "Não foi possível ligar ao banco de dados.", + "databaseConnection": "Não foi possível ligar à base de dados", "unknownError": "Erro desconhecido", - "loginFailed": "falha no login", - "failedPasswordReset": "Falha ao iniciar a redefinição da palavra-passe", - "failedVerifyCode": "Falha ao verificar o código de reinicialização", - "failedCompleteReset": "Falha ao concluir a redefinição da palavra-passe.", + "loginFailed": "Falha no início de sessão", + "failedPasswordReset": "Falha ao iniciar a reposição da palavra-passe", + "failedVerifyCode": "Falha ao verificar o código de reposição", + "failedCompleteReset": "Falha ao concluir a reposição da palavra-passe", "invalidTotpCode": "Código TOTP inválido", - "failedOidcLogin": "Falha ao iniciar o login OIDC", - "silentSigninOidcUnavailable": "Foi solicitada a entrada silenciosa, mas o login OIDC não está disponível.", - "failedUserInfo": "Falha ao obter informações do utilizador após o login", + "failedOidcLogin": "Falha ao iniciar sessão OIDC", + "silentSigninOidcUnavailable": "Foi solicitado um início de sessão silencioso, mas o início de sessão OIDC não está disponível.", + "failedUserInfo": "Falha ao obter informações do utilizador após o início de sessão", "oidcAuthFailed": "Falha na autenticação OIDC", - "invalidAuthUrl": "URL de autorização inválido recebido do backend.", - "requiredField": "Este campo é de preenchimento obrigatório", + "invalidAuthUrl": "URL de autorização inválida recebida do backend", + "requiredField": "Este campo é obrigatório", "minLength": "O comprimento mínimo é {{min}}", - "passwordMismatch": "As palavras-passe não coincidem.", - "passwordLoginDisabled": "O login com nome de utilizador/palavra-passe está desativado no momento.", + "passwordMismatch": "As palavras-passe não coincidem", + "passwordLoginDisabled": "O início de sessão com nome de utilizador/palavra-passe está atualmente desativado", "sessionExpired": "Sessão expirada - inicie sessão novamente", - "totpRateLimited": "Taxa de tentativas limitada: Foram feitas 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 de tentativas limitada: Muitas tentativas de verificação. Tente novamente mais tarde.", - "resetCodeRateLimitedWithTime": "Taxa de tentativas limitada: Muitas tentativas de verificação. Aguarde {{time}} segundos antes de tentar novamente.", + "totpRateLimited": "Limite de tentativas atingido: demasiadas tentativas de verificação TOTP. Tente novamente mais tarde.", + "totpRateLimitedWithTime": "Limite de tentativas atingido: demasiadas tentativas de verificação TOTP. Aguarde {{time}} segundos antes de tentar novamente.", + "resetCodeRateLimited": "Limite de tentativas atingido: demasiadas tentativas de verificação. Tente novamente mais tarde.", + "resetCodeRateLimitedWithTime": "Limite de tentativas: demasiadas tentativas de verificação. Por favor, aguarde {{time}} segundos antes de tentar novamente.", "authTokenSaveFailed": "Falha ao guardar o token de autenticação", - "failedToLoadServer": "Falha ao carregar o servidor" + "failedToLoadServer": "Falha ao carregar o servidor", + "remoteServerRequired": "É necessário um servidor remoto. Ligue um servidor remoto nas Definições para utilizar este tipo de ligação." }, "messages": { - "registrationDisabled": "O registo de novas contas está temporariamente desativado por um administrador. Faça login ou contacte um administrador.", - "userNotAllowed": "A sua conta não está autorizada a registar-se. Entre em contacto com um administrador.", + "registrationDisabled": "O registo de novas contas está atualmente desativado por um administrador. Por favor, inicie sessão ou contacte um administrador.", + "userNotAllowed": "A sua conta não está autorizada a registar-se. Por favor, contacte um administrador.", "databaseConnectionFailed": "Falha ao ligar ao servidor de base de dados", - "resetCodeSent": "Código de reinicialização enviado para os registos do Docker", + "resetCodeSent": "Código de reposição enviado para os registos do Docker", "codeVerified": "Código verificado com sucesso", - "passwordResetSuccess": "Palavra-passe redefinida com sucesso.", - "loginSuccess": "Login realizado com sucesso", - "registrationSuccess": "Registo concluído com sucesso" + "passwordResetSuccess": "Palavra-passe redefinida com sucesso", + "loginSuccess": "Início de sessão bem-sucedido", + "registrationSuccess": "Registo bem-sucedido" }, "profile": { - "c2sTunnelConfigDesc": "Túneis de desktop locais direcionados para hosts SSH configurados.", - "c2sTunnelPresets": "Configurações predefinidas do túnel do cliente", - "c2sTunnelPresetsDesc": "Guarde a lista de túneis locais deste cliente de ambiente de trabalho como uma predefinição de servidor nomeada ou carregue uma predefinição de volta neste cliente.", - "c2sTunnelPresetsUnavailable": "As definições predefinidas de túnel do cliente apenas estão disponíveis no cliente para desktop.", - "c2sPresetName": "Nome predefinido", - "c2sPresetNamePlaceholder": "Nome predefinido do cliente", - "c2sPresetToLoad": "Predefinido para carregar", + "c2sTunnelConfigDesc": "Túneis locais do ambiente de trabalho direcionados para hosts SSH configurados.", + "c2sTunnelPresets": "Predefinições de túneis do cliente", + "c2sTunnelPresetsDesc": "Guarde a lista de túneis locais deste cliente de ambiente de trabalho como uma predefinição de servidor nomeada ou carregue uma predefinição de volta para este cliente.", + "c2sTunnelPresetsUnavailable": "As predefinições de túneis do cliente estão disponíveis apenas no cliente de ambiente de trabalho.", + "c2sPresetName": "Nome da predefinição", + "c2sPresetNamePlaceholder": "Nome da predefinição do cliente", + "c2sPresetToLoad": "Predefinição a carregar", "c2sNoPresetSelected": "Nenhuma predefinição selecionada", "c2sNoPresets": "Nenhuma predefinição guardada", - "c2sLoadPreset": "Carga", - "c2sCurrentLocalConfig": "{{count}} Túneis de cliente local configurados nesta área de trabalho.", - "c2sPresetSyncNote": "As predefinições são instantâneos explícitos; carregar uma substitui a lista de túneis de clientes locais deste cliente de desktop.", - "c2sPresetSaved": "Configuração predefinida do túnel do cliente guardada", - "c2sPresetLoaded": "Configuração predefinida do túnel do cliente carregada localmente", - "c2sPresetRenamed": "A predefinição do túnel do cliente foi renomeada.", - "c2sPresetDeleted": "Configuração do túnel do cliente excluída", - "c2sPresetLoadError": "Falha ao carregar as definições predefinidas do túnel do cliente." + "c2sLoadPreset": "Carregar", + "c2sCurrentLocalConfig": "{{count}} túnel/túneis de cliente local configurado(s) neste ambiente de trabalho.", + "c2sPresetSyncNote": "As predefinições são instantâneos explícitos; carregar uma substitui a lista de túneis de cliente local deste cliente de ambiente de trabalho.", + "c2sPresetSaved": "Predefinição de túnel do cliente guardada", + "c2sPresetLoaded": "Predefinição de túnel do cliente carregada localmente", + "c2sPresetRenamed": "Predefinição de túnel do cliente renomeada", + "c2sPresetDeleted": "Predefinição de túnel do cliente eliminada", + "c2sPresetLoadError": "Falha ao carregar as predefinições de túneis do cliente" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "Idioma", - "keyPassword": "senha chave", + "keyPassword": "palavra-passe da chave", "pastePrivateKey": "Cole aqui a sua chave privada...", - "localListenerHost": "127.0.0.1 (ouvir localmente)", + "localListenerHost": "127.0.0.1 (escuta localmente)", "localTargetHost": "127.0.0.1 (alvo neste computador)", - "socksListenerHost": "127.0.0.1 (ouvinte SOCKS)", - "enterPassword": "Introduza a sua senha", + "socksListenerHost": "127.0.0.1 (escuta SOCKS)", + "enterPassword": "Introduza a sua palavra-passe", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Painel", - "loading": "Carregando painel...", + "title": "Painel de Controlo", + "loading": "A carregar painel de controlo...", "github": "GitHub", - "support": "Apoio", - "discord": "Discórdia", - "docs": "Documentos", + "support": "Suporte", + "discord": "Discord", + "docs": "Documentação", "donate": "Doar", - "serverOverview": "Visão geral do servidor", + "serverOverview": "Visão Geral do Servidor", "version": "Versão", "upToDate": "Atualizado", - "updateAvailable": "Atualização disponível", + "updateAvailable": "Atualização Disponível", "beta": "Beta", - "uptime": "Tempo de atividade", - "database": "Banco de dados", - "healthy": "Saudável", + "uptime": "Tempo de Atividade", + "database": "Base de Dados", + "healthy": "Operacional", "error": "Erro", - "totalHosts": "Total de anfitriões", - "totalTunnels": "Total de túneis", - "totalCredentials": "Credenciais Totais", - "recentActivity": "Atividade recente", - "reset": "Reiniciar", - "loadingRecentActivity": "A carregar atividades recentes...", - "noRecentActivity": "Nenhuma atividade recente", - "quickActions": "Ações rápidas", - "addHost": "Adicionar host", - "addCredential": "Adicionar credencial", - "adminSettings": "Configurações do administrador", - "userProfile": "Perfil do utilizador", - "serverStats": "Estatísticas do servidor", + "totalHosts": "Total de Hosts", + "totalTunnels": "Total de Túneis", + "totalCredentials": "Total de Credenciais", + "recentActivity": "Atividade Recente", + "reset": "Repor", + "loadingRecentActivity": "A carregar atividade recente...", + "noRecentActivity": "Sem atividade recente", + "quickActions": "Ações Rápidas", + "addHost": "Adicionar Host", + "addCredential": "Adicionar Credencial", + "adminSettings": "Definições de Administrador", + "userProfile": "Perfil do Utilizador", + "serverStats": "Estatísticas do Servidor", "loadingServerStats": "A carregar estatísticas do servidor...", - "noServerData": "Não existem dados disponíveis no servidor.", + "noServerData": "Nenhum dado do servidor disponível", "cpu": "CPU", - "ram": "BATER", - "customizeLayout": "Personalize o painel de controlo", - "dashboardSettings": "Configurações do painel", + "ram": "RAM", + "customizeLayout": "Personalizar Dashboard", + "dashboardSettings": "Definições do Dashboard", "enableDisableCards": "Ativar/Desativar Cartões", - "resetLayout": "Restaurar as predefinições de fábrica", - "serverOverviewCard": "Visão geral do servidor", - "recentActivityCard": "Atividade recente", - "networkGraphCard": "Gráfico de rede", - "networkGraph": "Gráfico de rede", - "quickActionsCard": "Ações rápidas", - "serverStatsCard": "Estatísticas do servidor", + "resetLayout": "Repor para a Predefinição", + "serverOverviewCard": "Visão Geral do Servidor", + "recentActivityCard": "Atividade Recente", + "networkGraphCard": "Gráfico de Rede", + "networkGraph": "Gráfico de Rede", + "quickActionsCard": "Ações Rápidas", + "serverStatsCard": "Estatísticas do Servidor", "panelMain": "Principal", - "panelSide": "Lado", + "panelSide": "Lateral", "justNow": "agora mesmo", - "serviceLinks": "Links de serviço", - "homepagePreview": "Pré-visualização da página inicial" + "serviceLinks": "Links de Serviço", + "homepagePreview": "Pré-visualização da Página Inicial" }, "donation": { - "title": "Gosta do Termix?", - "body": "O Termix é gratuito e de código aberto, desenvolvido e mantido por uma equipa de duas pessoas no nosso tempo livre. Se substituir uma ferramenta comercial pela qual pagaria, um donativo ajuda a cobrir os custos de alojamento e a manter o desenvolvimento em curso. Neste momento, as doações são aceites apenas em criptomoedas.", - "milestones": "Os donativos também ajudam a financiar o tempo necessário para pesquisar e aprender o que é necessário para desenvolver recursos como o apoio ao SAML e ao Kubernetes. Veja o progresso na página de donativos.", + "title": "Está a gostar do Termix?", + "body": "O Termix é gratuito e de código aberto, desenvolvido e mantido por uma equipa de duas pessoas nos nossos tempos livres. Se substituiu uma ferramenta comercial pela qual estaria a pagar, um donativo ajuda a cobrir os custos de alojamento e mantém o desenvolvimento ativo. De momento, os donativos são apenas em criptomoedas.", + "milestones": "Os donativos também ajudam a financiar o tempo para pesquisar e aprender o necessário para criar funcionalidades como suporte para SAML e Kubernetes. Veja o progresso na página de donativos.", "cta": "Doar", "dismiss": "Talvez mais tarde" }, "dashboardTab": { "stable": "ESTÁVEL", - "hostsOnline": "Anfitriões Online", - "activeTunnels": "Túneis ativos", - "registerNewServer": "Registe um novo servidor", - "storeSshKeysOrPasswords": "Armazene chaves ou palavras-passe SSH", + "hostsOnline": "Hosts Online", + "activeTunnels": "Túneis Ativos", + "registerNewServer": "Registar um novo servidor", + "storeSshKeysOrPasswords": "Armazenar chaves SSH ou palavras-passe", "manageUsersAndRoles": "Gerir utilizadores e funções", "manageYourAccount": "Gerir a sua conta", - "hostStatus": "Estado do host", + "hostStatus": "Estado do Host", "noHostsConfigured": "Nenhum host configurado", "online": "ONLINE", "offline": "OFFLINE", - "checking": "VERIFICANDO", + "checking": "A VERIFICAR", "onlineLower": "Online", "nodes": "{{count}} nós", "add": "Adicionar:", - "commandPalette": "Paleta de comandos", + "commandPalette": "Paleta de Comandos", "done": "Concluído", - "editModeInstructions": "Arraste os cartões para os reordenar · Arraste o separador de colunas para redimensionar as colunas · Arraste a margem inferior de um cartão para redimensionar a sua altura · Caixote do lixo para remover", + "editModeInstructions": "Arraste cartões para reordenar · Arraste o divisor de coluna para redimensionar colunas · Arraste a borda inferior de um cartão para redimensionar a sua altura · Lixo para remover", "empty": "Vazio", - "clear": "Claro", - "serviceLinksTitle": "Links de serviço", - "serviceLinksEmpty": "Ainda não existem ligações de serviço.", + "clear": "Limpar", + "serviceLinksTitle": "Links de Serviço", + "serviceLinksEmpty": "Ainda sem links de serviço", "serviceLinksAddLabel": "Etiqueta", "serviceLinksAddUrl": "URL", "serviceLinksAdd": "Adicionar", "serviceLinksLabelPlaceholder": "O Meu Serviço", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "Introduza um endereço web válido.", - "serviceLinksAddFailed": "Falha ao adicionar o link de serviço", + "serviceLinksInvalidUrl": "Insira um endereço web válido", + "serviceLinksAddFailed": "Falha ao adicionar link de serviço", "disk": "Disco", "viewServerDetails": "Ver detalhes do servidor" }, "sessionLogs": { - "title": "Registos de sessão", - "noLogs": "Ainda não há registos de sessão.", - "noLogsDesc": "Ative o registo de sessão num host para iniciar a gravação.", + "title": "Registos de Sessão", + "noLogs": "Ainda sem registos de sessão", + "noLogsDesc": "Ative o registo de sessão num host para começar a gravar", "duration": "Duração", "viewLog": "Ver registo", "downloadLog": "Transferir", "deleteLog": "Eliminar", - "confirmDelete": "Apagar este registo de sessão?", - "confirmDeleteDesc": "Esta ação não pode ser anulada.", - "copyContent": "Cópia", + "confirmDelete": "Eliminar este registo de sessão?", + "confirmDeleteDesc": "Esta ação não pode ser desfeita.", + "copyContent": "Copiar", "copied": "Copiado!", - "loadError": "Falha ao carregar os registos da sessão.", - "deleteError": "Falha ao eliminar o registo da sessão", + "loadError": "Falha ao carregar registos de sessão", + "deleteError": "Falha ao eliminar registo de sessão", "filterByHost": "Filtrar por host..." }, "networkGraph": { - "addHost": "Adicionar host", - "addGroup": "Adicionar grupo", - "addLink": "Adicionar link", + "addHost": "Adicionar Host", + "addGroup": "Adicionar Grupo", + "addLink": "Adicionar Ligação", "zoomIn": "Ampliar", - "zoomOut": "Diminuir o zoom", - "resetView": "Redefinir visualização", - "selectHost": "Selecione o host", - "chooseHost": "Escolha um anfitrião...", - "parentGroup": "Grupo de Pais", + "zoomOut": "Reduzir", + "resetView": "Repor Vista", + "selectHost": "Selecionar Host", + "chooseHost": "Escolher um host...", + "parentGroup": "Grupo Pai", "noGroup": "Nenhum grupo", - "groupName": "Nome do grupo", + "groupName": "Nome do Grupo", "color": "Cor", - "source": "Fonte", - "target": "Objetivo", - "moveToGroup": "Deslocar para o grupo", - "selectGroup": "Selecione o grupo...", - "addConnection": "Adicionar ligação", - "hostDetails": "Detalhes do anfitrião", - "removeFromGroup": "Remover do grupo", - "addHostHere": "Adicione o host aqui", - "editGroup": "Editar grupo", + "source": "Origem", + "target": "Destino", + "moveToGroup": "Mover para Grupo", + "selectGroup": "Selecionar grupo...", + "addConnection": "Adicionar Ligação", + "hostDetails": "Detalhes do Host", + "removeFromGroup": "Remover do Grupo", + "addHostHere": "Adicionar Host Aqui", + "editGroup": "Editar Grupo", "delete": "Eliminar", "add": "Adicionar", "create": "Criar", "move": "Mover", "connect": "Ligar", - "createGroup": "Criar grupo", - "selectSourcePlaceholder": "Selecione a fonte...", - "selectTargetPlaceholder": "Selecione o alvo...", - "invalidFile": "Arquivo inválido", + "createGroup": "Criar Grupo", + "selectSourcePlaceholder": "Selecionar Origem...", + "selectTargetPlaceholder": "Selecionar Destino...", + "invalidFile": "Ficheiro Inválido", "hostAlreadyExists": "O host já está na topologia.", - "connectionExists": "A ligação já existe.", + "connectionExists": "A ligação já existe", "unknown": "Desconhecido", "name": "Nome", "ip": "IP", - "status": "Estatuto", + "status": "Estado", "failedToAddNode": "Falha ao adicionar nó", - "sourceDifferentFromTarget": "A origem e o destino devem ser diferentes.", + "sourceDifferentFromTarget": "A origem e o destino devem ser diferentes", "exportJSON": "Exportar JSON", "importJSON": "Importar JSON", - "terminal": "terminal", - "fileManager": "Gestor de arquivos", + "terminal": "Terminal", + "fileManager": "Gestor de ficheiros", "tunnel": "Túnel", "docker": "Docker", "serverStats": "Métricas do host", "hostMetrics": "Métricas do host", - "noNodes": "Ainda não há nós." + "noNodes": "Ainda sem nós" }, "docker": { - "notEnabled": "O Docker não está ativado para este host.", - "validating": "Validando o Docker...", - "connecting": "Ligando...", + "notEnabled": "Docker não está ativado para este host", + "validating": "A validar o Docker...", + "connecting": "A ligar...", "error": "Erro", "version": "Docker {{version}}", "connectionFailed": "Falha ao ligar ao Docker", @@ -2489,984 +2623,1063 @@ "failedToStopContainer": "Falha ao parar o contentor {{name}}", "containerRestarted": "Contentor {{name}} reiniciado", "failedToRestartContainer": "Falha ao reiniciar o contentor {{name}}", - "containerPaused": "Contentor {{name}} pausado", - "containerUnpaused": "Contentor {{name}} despausado", - "failedToTogglePauseContainer": "Falha ao alternar o estado de pausa para o contentor {{name}}", + "containerPaused": "Contentor {{name}} suspenso", + "containerUnpaused": "Contentor {{name}} retomado", + "failedToTogglePauseContainer": "Falha ao alternar estado de pausa do contentor {{name}}", "containerRemoved": "Contentor {{name}} removido", "failedToRemoveContainer": "Falha ao remover o contentor {{name}}", "image": "Imagem", - "ports": "Portos", + "ports": "Portas", "noPorts": "Sem portas", "start": "Iniciar", - "confirmRemoveContainer": "Tem a certeza de que pretende remover o contentor '{{name}}'? Esta ação não pode ser anulada.", - "runningContainerWarning": "Aviso: Este contentor está em execução. Removê-lo interromperá o contentor primeiro.", - "loadingContainers": "Carregamento de contentores...", - "manager": "Gestor de Docker", + "confirmRemoveContainer": "Tem a certeza de que pretende remover o contentor '{{name}}'? Esta ação não pode ser desfeita.", + "runningContainerWarning": "Aviso: Este contentor está em execução. Removê-lo irá pará-lo primeiro.", + "loadingContainers": "A carregar contentores...", + "manager": "Gestor Docker", "autoRefresh": "Atualização automática", - "timestamps": "Carimbos de data/hora", + "timestamps": "Marcas de tempo", "lines": "Linhas", "filterLogs": "Filtrar registos...", "refresh": "Atualizar", "download": "Transferir", - "clear": "Claro", - "logsDownloaded": "Registos baixados com sucesso", + "clear": "Limpar", + "logsDownloaded": "Registos transferidos com sucesso", "last50": "Últimos 50", "last100": "Últimos 100", "last500": "Últimos 500", "last1000": "Últimos 1000", "allLogs": "Todos os registos", - "noLogsMatching": "Nenhum registo correspondente a \"{{query}}\"", + "noLogsMatching": "Nenhum registo corresponde a \"{{query}}\"", "noLogsAvailable": "Nenhum registo disponível", "noContainersFound": "Nenhum contentor encontrado", - "noContainersFoundHint": "Não existem contentores Docker disponíveis neste host.", - "searchPlaceholder": "Contentores de pesquisa...", + "noContainersFoundHint": "Não existem contentores Docker disponíveis neste host", + "searchPlaceholder": "Pesquisar contentores...", "allStatuses": "Todos os estados", - "stateRunning": "Corrida", + "stateRunning": "Em execução", "statePaused": "Em pausa", - "stateExited": "Saiu", - "stateRestarting": "Reiniciando", - "noContainersMatchFilters": "Nenhum recipiente corresponde aos seus filtros.", - "noContainersMatchFiltersHint": "Tente ajustar os seus critérios de pesquisa ou de filtro.", - "failedToFetchStats": "Falha ao obter as estatísticas do contentor.", - "containerNotRunning": "Contentor não está em execução", - "startContainerToViewStats": "Inicie o contentor para visualizar as estatísticas.", - "loadingStats": "Carregando estatísticas...", - "errorLoadingStats": "Estatísticas de carregamento de erros", - "noStatsAvailable": "Não existem estatísticas disponíveis.", + "stateExited": "Terminado", + "stateRestarting": "A reiniciar", + "noContainersMatchFilters": "Nenhum contentor corresponde aos seus filtros", + "noContainersMatchFiltersHint": "Tente ajustar os seus critérios de pesquisa ou filtro", + "failedToFetchStats": "Falha ao obter estatísticas do contentor", + "containerNotRunning": "O contentor não está em execução", + "startContainerToViewStats": "Inicie o contentor para ver as estatísticas", + "loadingStats": "A carregar estatísticas...", + "errorLoadingStats": "Erro ao carregar estatísticas", + "noStatsAvailable": "Nenhuma estatística disponível", "cpuUsage": "Utilização da CPU", "current": "Atual", - "memoryUsage": "Uso de memória", - "networkIo": "E/S de rede", + "memoryUsage": "Utilização da memória", + "networkIo": "I/O de rede", "input": "Entrada", "output": "Saída", - "blockIo": "Bloco de E/S", - "read": "Ler", - "write": "Escrever", + "blockIo": "E/S de blocos", + "read": "Leitura", + "write": "Escrita", "pids": "PIDs", "containerInformation": "Informações do contentor", "name": "Nome", - "id": "EU IA", + "id": "ID", "state": "Estado", - "containerMustBeRunning": "O contentor precisa de estar em execução para aceder ao console.", + "containerMustBeRunning": "O contentor tem de estar em execução para aceder à consola", "verificationCodePrompt": "Introduza o código de verificação", - "totpVerificationFailed": "A verificação TOTP falhou. Tente novamente.", - "warpgateVerificationFailed": "A autenticação no portal de guerra falhou. Por favor, tente novamente.", + "totpVerificationFailed": "A verificação TOTP falhou. Por favor, tente novamente.", + "warpgateVerificationFailed": "A autenticação Warpgate falhou. Por favor, tente novamente.", "connectedTo": "Ligado a {{containerName}}", - "disconnected": "Desconectado", - "consoleError": "Erro no console", + "disconnected": "Desligado", + "consoleError": "Erro da consola", "errorMessage": "Erro: {{message}}", "failedToConnect": "Falha ao ligar ao contentor", "console": "Consola", - "selectShell": "Selecione shell", + "selectShell": "Selecionar shell", "bash": "Bash", "sh": "sh", - "ash": "cinza", + "ash": "ash", "connect": "Ligar", - "disconnect": "Desconectar", - "notConnected": "Não conectado", - "clickToConnect": "Clique em ligar para iniciar uma sessão de shell.", - "connectingTo": "Ligando a {{containerName}}...", + "disconnect": "Desligar", + "notConnected": "Não ligado", + "clickToConnect": "Clique em Ligar para iniciar uma sessão shell", + "connectingTo": "A ligar a {{containerName}}...", "containerNotFound": "Contentor não encontrado", "backToList": "Voltar à lista", - "logs": "Registros", + "logs": "Registos", "stats": "Estatísticas", "consoleTab": "Consola", - "startContainerToAccess": "Inicie o contentor para aceder ao console." + "startContainerToAccess": "Inicie o contentor para aceder à consola" }, "admin": { - "sectionGeneral": "General", + "sectionGeneral": "Geral", "sectionOidc": "OIDC", - "sectionSso": "Fornecedores de SSO", - "ssoAddProvider": "Adicionar provedor", - "ssoDocsLink": "Ver documentos", - "ssoProviderDocsLink": "Ver documentos", - "ssoNoProviders": "Sem fornecedor de SSO configurado.", - "ssoProviderName": "Nome de exibição", - "ssoProviderType": "Tipo de fornecedor", - "ssoDeleteProvider": "Apagar provedor", - "ssoDeleteConfirm": "Apagar este fornecedor? Os utilizadores associados não poderão fazer login.", + "sectionSso": "Provedores de SSO", + "ssoAddProvider": "Adicionar Provedor", + "ssoDocsLink": "Ver docs", + "ssoProviderDocsLink": "Ver docs", + "ssoNoProviders": "Nenhum provedor de SSO configurado.", + "ssoProviderName": "Nome de apresentação", + "ssoProviderType": "Tipo de Provedor", + "ssoDeleteProvider": "Eliminar Provedor", + "ssoDeleteConfirm": "Eliminar este provedor? Os utilizadores associados não poderão iniciar sessão.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", - "ssoEnabled": "Habilitado", + "ssoEnabled": "Ativado", "ssoDisabled": "Desativado", - "ssoSaveProvider": "Guardar provedor", - "ssoTestConnection": "Conexão de teste", - "ssoEditProvider": "Editar provedor", - "ldapHost": "Anfitrião LDAP", - "ldapPort": "Porto", - "ldapUseTls": "Utilizar TLS (LDAPS)", - "ldapBindDn": "Vincular DN", - "ldapBindPassword": "Senha de vinculação", + "ssoSaveProvider": "Guardar Provedor", + "ssoTestConnection": "Testar Ligação", + "ssoEditProvider": "Editar Provedor", + "ldapHost": "Servidor LDAP", + "ldapPort": "Porta", + "ldapUseTls": "Usar TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Palavra-passe de Bind", "ldapUserSearchBase": "Base de pesquisa de utilizadores", - "ldapUserSearchFilter": "Filtro de pesquisa do utilizador", + "ldapUserSearchFilter": "Filtro de pesquisa de utilizadores", "ldapUsernameAttr": "Atributo de nome de utilizador", - "ldapDisplayNameAttr": "Atributo de nome de visualização", - "ldapGroupSearchBase": "Base de pesquisa de grupo", - "ldapAdminGroup": "Grupo de Administração", - "ldapAllowedUsers": "Utilizadores autorizados", - "sectionUsers": "Usuários", + "ldapDisplayNameAttr": "Atributo de nome de apresentação", + "ldapGroupSearchBase": "Base de pesquisa de grupos", + "ldapAdminGroup": "Grupo de administradores", + "ldapAllowedUsers": "Utilizadores permitidos", + "sectionUsers": "Utilizadores", "sectionSessions": "Sessões", "sectionRoles": "Funções", - "sectionDatabase": "Banco de dados", + "sectionDatabase": "Base de Dados", "sectionApiKeys": "Chaves API", "sectionAuditLog": "Registo de auditoria", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Emita e renove automaticamente um certificado SSL fidedigno da Let's Encrypt. Requer um domínio público e acesso à porta 80 ou DNS.", + "sslDescription": "Emitir e renovar automaticamente um certificado SSL confiável da Let's Encrypt. Requer um domínio público e acesso à porta 80 ou DNS.", "sslDocsLink": "Ver documentação SSL", "sslDomain": "Domínio", - "sslDomainPlaceholder": "termix.exemplo.com", - "sslDomainDesc": "O nome de domínio público do certificado.", + "sslDomainPlaceholder": "termix.example.com", + "sslDomainDesc": "O nome de domínio público para o certificado.", "sslEmail": "E-mail", - "sslEmailPlaceholder": "admin@exemplo.com", + "sslEmailPlaceholder": "admin@example.com", "sslEmailDesc": "E-mail de contacto para notificações e conta Let's Encrypt.", "sslChallengeType": "Tipo de desafio", - "sslChallengeTypeDesc": "Como comprovar a propriedade de um domínio para o Let's Encrypt.", - "sslChallengeHttp": "HTTP (raiz web) - requer que a porta 80 esteja acessível através da internet.", - "sslChallengeDns": "DNS (Cloudflare) - requer um token API do Cloudflare", - "sslCloudflareToken": "Token da API do Cloudflare", - "sslCloudflareTokenPlaceholder": "Introduza o token...", - "sslCloudflareTokenDesc": "Token com âmbito definido e permissão Zone:DNS:Edit para o seu domínio.", + "sslChallengeTypeDesc": "Como provar a propriedade do domínio à Let's Encrypt.", + "sslChallengeHttp": "HTTP (webroot) - requer que a porta 80 esteja acessível a partir da internet", + "sslChallengeDns": "DNS (Cloudflare) - requer um token de API da Cloudflare", + "sslCloudflareToken": "Token de API da Cloudflare", + "sslCloudflareTokenPlaceholder": "Introduzir token...", + "sslCloudflareTokenDesc": "Token com âmbito e permissão Zone:DNS:Edit para o seu domínio.", "sslCertStatus": "Estado do certificado", "sslCertStatusNone": "Sem certificado", "sslCertStatusValid": "Válido", - "sslCertStatusExpiring": "Expira em breve", + "sslCertStatusExpiring": "A expirar em breve", "sslCertStatusExpired": "Expirado", - "sslCertExpiresAt": "Expira em {{date}}", - "sslLastIssued": "Última edição {{date}}", - "sslRequestCert": "Emitir/Renovar Certificado", - "sslRequestCertLoading": "Solicitar certificado...", - "sslRequestCertSuccess": "Certificado emitido e instalado com sucesso.", - "sslRequestCertFailed": "A solicitação de certificado falhou", - "sslSave": "Guardar configurações", - "sslSaved": "Configurações SSL guardadas", - "sslSaveFailed": "Falha ao guardar as definições SSL", - "sslRequiresDomain": "O domínio e o e-mail são obrigatórios.", - "sslInfoNote": "Após emitir o certificado, ative o SSL nas suas variáveis de ambiente (ENABLE_SSL=true) e reinicie o Termix.", - "auditLogTotal": "{{total}} total de entradas", - "auditLogEmpty": "Nenhuma entrada de registo de auditoria encontrada.", + "sslCertExpiresAt": "Expira a {{date}}", + "sslLastIssued": "Última emissão a {{date}}", + "sslRequestCert": "Emitir / Renovar certificado", + "sslRequestCertLoading": "A solicitar certificado...", + "sslRequestCertSuccess": "Certificado emitido e instalado com sucesso", + "sslRequestCertFailed": "Pedido de certificado falhou", + "sslSave": "Guardar definições", + "sslSaved": "Definições SSL guardadas", + "sslSaveFailed": "Falha ao guardar definições SSL", + "sslRequiresDomain": "Domínio e e-mail são obrigatórios", + "sslInfoNote": "Após emitir um certificado, ative o SSL nas variáveis de ambiente (ENABLE_SSL=true) e reinicie o Termix.", + "sslManualOption": "Manual (envio do certificado)", + "sslManualCert": "Certificado (PEM)", + "sslManualCertPlaceholder": "-----INÍCIO DO CERTIFICADO-----", + "sslManualKey": "Chave privada (PEM)", + "sslManualKeyPlaceholder": "-----INÍCIO DA CHAVE PRIVADA-----", + "sslManualDesc": "Cole aqui o seu certificado e chave privada existentes, incluindo a cadeia completa, se tal for exigido pela sua Autoridade Certificadora.", + "sslManualUpload": "Carregar e instalar certificado", + "sslManualUploadLoading": "Envio de certificado...", + "sslManualUploadSuccess": "Certificado carregado e instalado com sucesso.", + "sslManualUploadFailed": "O envio do certificado falhou", + "sslManualRequiresFields": "É necessário certificado e chave privada.", + "auditLogTotal": "{{total}} entradas no total", + "auditLogEmpty": "Nenhuma entrada de registo de auditoria encontrada", "auditLogSuccess": "Sucesso", "auditLogFailed": "Falhou", - "auditLogClearFilters": "Limpar filtros", + "auditLogClearFilters": "Limpar Filtros", "auditLogPage": "Página {{page}} de {{totalPages}} ({{total}} total)", "auditLogIp": "IP", - "auditLogResourceId": "ID do recurso", - "auditLogFilterUser": "Usuário", + "auditLogResourceId": "ID do Recurso", + "auditLogFilterUser": "Utilizador", "auditLogFilterAction": "Ação", - "auditLogFilterResourceType": "Tipo de recurso", - "auditLogFilterStatus": "Estatuto", + "auditLogFilterResourceType": "Tipo de Recurso", + "auditLogFilterStatus": "Estado", "auditLogFilterFrom": "De", - "auditLogFilterTo": "Para", - "auditLogFilterAll": "Tudo", - "allowRegistration": "Permitir o registo de usuários", - "allowRegistrationDesc": "Permita que novos utilizadores se registem automaticamente com um nome de utilizador e palavra-passe.", - "allowPasswordLogin": "Permitir login com password", - "allowPasswordLoginDesc": "Login com nome de utilizador/palavra-passe", - "oidcAutoProvision": "Provisionamento automático de OIDC", - "oidcAutoProvisionDesc": "Criar automaticamente contas para utilizadores OIDC/SSO no primeiro login (independentemente da ativação/desativação do registo).", - "oidcSilentLoginDefault": "Login OIDC silencioso por defeito", - "oidcSilentLoginDefaultDesc": "Redireccionamento automático para o login OIDC em todas as visitas, ignorando completamente o formulário de login.", - "allowPasswordReset": "Permitir redefinição de palavra-passe", - "allowPasswordResetDesc": "Repor código através dos logs do Docker", - "commandHistoryEnabled": "Histórico de comandos", - "commandHistoryEnabledDesc": "Permitir o registo do histórico de comandos. Quando desativado, o histórico não é guardado, independentemente das definições de cada host.", - "updateCommandHistoryFailed": "Falha ao atualizar a configuração do histórico de comandos", - "sessionTimeout": "Tempo limite da sessão", + "auditLogFilterTo": "Até", + "auditLogFilterAll": "Todos", + "allowRegistration": "Permitir Registo de Utilizadores", + "allowRegistrationDesc": "Permitir que novos utilizadores se auto-registem com nome de utilizador e palavra-passe", + "allowPasswordLogin": "Permitir Início de Sessão com Palavra-passe", + "allowPasswordLoginDesc": "Início de sessão com nome de utilizador/palavra-passe", + "oidcAutoProvision": "Aprovisionamento Automático OIDC", + "oidcAutoProvisionDesc": "Criar automaticamente contas para utilizadores OIDC/SSO no primeiro início de sessão (independente da opção de registo)", + "oidcSilentLoginDefault": "Início de Sessão Silencioso OIDC por Defeito", + "oidcSilentLoginDefaultDesc": "Redirecionar automaticamente para o início de sessão OIDC em cada visita, ignorando completamente o formulário de início de sessão", + "allowPasswordReset": "Permitir Reposição de Palavra-passe", + "allowPasswordResetDesc": "Código de reposição via registos do Docker", + "commandHistoryEnabled": "Histórico de Comandos", + "commandHistoryEnabledDesc": "Permitir o registo do histórico de comandos. Quando desativado, o histórico não é guardado independentemente das definições por máquina.", + "updateCommandHistoryFailed": "Falha ao atualizar a definição de histórico de comandos", + "analyticsEnabled": "Partilhar estatísticas de uso anónimas", + "analyticsEnabledDesc": "Envia uma contagem diária anónima de utilizadores, hosts e utilização de recursos para ajudar a melhorar o Termix. Nenhum dado pessoal ou detalhe de ligação é incluído.", + "updateAnalyticsFailed": "Falha ao atualizar as definições de análise", + "sessionSharingGloballyEnabled": "Permitir partilha de sessão", + "sessionSharingGloballyEnabledDesc": "Permite que as sessões de terminal ao vivo, RDP, VNC e Telnet sejam partilhadas em toda a instância. Substitui todas as definições de partilha por host quando desativada.", + "updateSessionSharingFailed": "Falha ao atualizar as definições de partilha de sessão.", + "sessionTimeout": "Tempo Limite da Sessão", "hours": "horas", - "sessionTimeoutRange": "Mínimo 1h · Máximo 720h", - "monitoringDefaults": "Monitorização de padrões", - "statusCheck": "Verificação de estado", + "sessionTimeoutRange": "Mín. 1h · Máx. 720h", + "monitoringDefaults": "Padrões de Monitorização", + "statusCheck": "Verificação de Estado", "metrics": "Métricas", "sec": "seg", - "logLevel": "Nível de registo", + "logLevel": "Nível de Registo", "enableGuacamole": "Ativar Guacamole", "enableGuacamoleDesc": "Ambiente de trabalho remoto RDP/VNC", - "enableGuacamoleDocsLink": "Ver documentos", + "enableGuacamoleDocsLink": "Ver documentação", "guacdUrl": "URL do guacd", - "tailscaleApiKey": "Chave API Tailscale", - "tailscaleApiKeyDescription": "Utilizado para a descoberta de dispositivos no editor de hosts. Gere uma chave em tailscale.com/admin/settings/keys.", - "tailscaleApiKeyDocsLink": "Ver documentos", - "oidcDescription": "Configure o OpenID Connect para SSO. Os campos assinalados com * são de preenchimento obrigatório.", - "oidcDocsLink": "Ver documentos", + "tailscaleApiKey": "Chave de API do Tailscale", + "tailscaleApiKeyDescription": "Utilizada para deteção de dispositivos no editor de anfitriões. Gere uma chave em tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "Ver documentação", + "oidcDescription": "Configurar o OpenID Connect para SSO. Os campos marcados com * são obrigatórios.", + "oidcDocsLink": "Ver documentação", "oidcClientId": "ID do cliente", "oidcClientSecret": "Segredo do cliente", "oidcAuthUrl": "URL de autorização", "oidcIssuerUrl": "URL do emissor", "oidcTokenUrl": "URL do token", "oidcUserIdentifier": "Caminho do identificador do utilizador", - "oidcDisplayName": "Nome de visualização Caminho", - "oidcScopes": "Escopos", - "oidcUserinfoUrl": "Substituir URL de informações do utilizador", - "oidcAllowedUsers": "Utilizadores autorizados", - "oidcAllowedUsersDesc": "Um e-mail por linha. Deixe em branco para permitir todos.", - "oidcAdminGroup": "Grupo de Administração", - "oidcAdminGroupDesc": "Os utilizadores deste grupo têm privilégios de administrador. Deixe em branco para desativar a sincronização do grupo.", - "oidcGroupClaim": "Reivindicação de grupo", - "oidcGroupClaimDesc": "Opcional. O caminho da declaração que contém os grupos do utilizador. O padrão é grupos, funções e depois grupo. Utilize isto para fornecedores com uma declaração personalizada (por exemplo, Zitadel).", + "oidcDisplayName": "Caminho do nome de exibição", + "oidcScopes": "Âmbitos", + "oidcUserinfoUrl": "Substituir URL do Userinfo", + "oidcAllowedUsers": "Utilizadores permitidos", + "oidcAllowedUsersDesc": "Um email por linha. Deixe em branco para permitir todos.", + "oidcAdminGroup": "Grupo de administração", + "oidcAdminGroupDesc": "Os utilizadores neste grupo recebem privilégios de administrador. Deixe em branco para desativar a sincronização de grupos.", + "oidcGroupClaim": "Atributo de grupo", + "oidcGroupClaimDesc": "Opcional. O caminho do atributo que contém os grupos do utilizador. Predefinido para groups, roles e depois group. Utilize para fornecedores com um atributo personalizado (ex.: Zitadel).", "oidcCaCert": "Certificado CA personalizado", - "oidcCaCertDesc": "Opcional. Certificado CA codificado em PEM para fornecedores OIDC que utilizam uma CA privada ou auto-assinada. Deixe em branco para utilizar o repositório de certificados fidedignos do sistema.", + "oidcCaCertDesc": "Opcional. Certificado CA codificado em PEM para fornecedores OIDC que utilizam uma CA privada ou autoassinada. Deixe em branco para utilizar o repositório de confiança do sistema.", "removeOidc": "Remover", "usersCount": "{{count}} utilizadores", "createUser": "Criar", - "newRole": "Novo cargo", + "newRole": "Nova função", "roleName": "Nome", "roleDisplayName": "Nome de exibição", "roleDescription": "Descrição", "rolesCount": "{{count}} funções", "createRole": "Criar", - "creating": "Criando...", - "exportDatabase": "Exportar base de dados", - "exportDatabaseDesc": "Faça o download de um backup de todos os hosts, credenciais e definições.", + "creating": "A criar...", + "exportDatabase": "Exportar Base de Dados", + "exportDatabaseDesc": "Transferir uma cópia de segurança de todos os hosts, credenciais e definições", "export": "Exportar", - "exporting": "Exportador...", - "importDatabase": "Importar base de dados", - "importDatabaseDesc": "Restaurar a partir de um ficheiro de cópia de segurança . sqlite", + "exporting": "A exportar...", + "importDatabase": "Importar Base de Dados", + "importDatabaseDesc": "Restaurar a partir de um ficheiro de cópia de segurança .sqlite", "importDatabaseSelected": "Selecionado: {{name}}", - "selectFile": "Selecionar ficheiro", - "changeFile": "Mudar", - "import": "Importação", - "importing": "Importando...", - "apiKeysCount": "{{count}} teclas", - "apiKeysDocsLink": "Ver documentos", - "newApiKey": "Nova chave API", - "apiKeyCreatedWarning": "Chave criada - copie-a agora, não voltará a ser apresentada.", + "selectFile": "Selecionar Ficheiro", + "changeFile": "Alterar", + "import": "Importar", + "importing": "A importar...", + "apiKeysCount": "{{count}} chaves", + "apiKeysDocsLink": "Ver docs", + "newApiKey": "Nova Chave API", + "apiKeyCreatedWarning": "Chave criada - copie-a agora, não será mostrada novamente.", "apiKeyName": "Nome", - "apiKeyUser": "Usuário", - "apiKeySelectUser": "Selecione um utilizador...", + "apiKeyUser": "Utilizador", + "apiKeySelectUser": "Selecionar um utilizador...", "apiKeyExpiresAt": "Expira em", - "createKey": "Criar chave", - "apiKeyNoExpiry": "Sem data de validade", - "revokedBadge": "REVOGADO", - "authTypeDual": "Autenticação dupla", + "createKey": "Criar Chave", + "apiKeyNoExpiry": "Sem expiração", + "revokedBadge": "REVOGADA", + "authTypeDual": "Autenticação Dupla", "authTypeOidc": "OIDC", "authTypeLocal": "Local", "adminStatusAdministrator": "Administrador", - "adminStatusRegularUser": "Usuário regular", + "adminStatusRegularUser": "Utilizador Regular", "adminBadge": "ADMIN", - "systemBadge": "SISTEMA", + "systemBadge": "SYS", "customBadge": "PERSONALIZADO", - "youBadge": "TU", - "sessionsActive": "{{count}} ativo", - "sessionActive": "Ativo: {{time}}", + "youBadge": "VOCÊ", + "sessionsActive": "{{count}} ativas", + "sessionActive": "Ativa: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tudo", - "revokeAllSessionsSuccess": "Todas as sessões de utilizador foram revogadas.", + "revokeAll": "Todas", + "revokeAllSessionsSuccess": "Todas as sessões do utilizador foram revogadas.", "revokeAllSessionsFailed": "Falha ao revogar sessões", "revokeSessionFailed": "Falha ao revogar a sessão", "addRole": "Adicionar função", "noCustomRoles": "Nenhuma função personalizada definida.", "removeRoleFailed": "Falha ao remover a função", - "assignRoleFailed": "Falha ao atribuir função", + "assignRoleFailed": "Falha ao atribuir a função", "deleteRoleFailed": "Falha ao eliminar a função", "userAdminAccess": "Administrador", "userAdminAccessDesc": "Acesso total a todas as definições de administrador.", "userRoles": "Funções", "revokeAllUserSessions": "Revogar todas as sessões", - "revokeAllUserSessionsDesc": "Forçar novo login em todos os dispositivos", + "revokeAllUserSessionsDesc": "Forçar novo início de sessão em todos os dispositivos.", "revoke": "Revogar", "deleteUserWarning": "A eliminação deste utilizador é permanente.", - "deleteUser": "Apagar {{username}}", - "deleting": "Excluindo...", - "deleteUserFailed": "Falha ao eliminar utilizador", - "deleteUserSuccess": "Utilizador \"{{username}}\" eliminado", - "deleteRoleSuccess": "Função \"{{name}}\" eliminada", - "revokeKeySuccess": "Chave \"{{name}}\" revogada", + "deleteUser": "Eliminar {{username}}", + "deleting": "A eliminar...", + "deleteUserFailed": "Falha ao eliminar o utilizador", + "deleteUserSuccess": "Utilizador \"{{username}}\" eliminado.", + "deleteRoleSuccess": "Função \"{{name}}\" eliminada.", + "revokeKeySuccess": "Chave \"{{name}}\" revogada.", "revokeKeyFailed": "Falha ao revogar a chave", - "copiedToClipboard": "Copiado para a área de transferência", + "copiedToClipboard": "Copiado para a área de transferência.", "done": "Concluído", - "createUserTitle": "Criar utilizador", - "createUserDesc": "Crie uma nova conta local.", + "createUserTitle": "Criar Utilizador", + "createUserDesc": "Criar uma nova conta local.", "createUserUsername": "Nome de utilizador", "createUserPassword": "Palavra-passe", - "createUserPasswordHint": "Mínimo de 6 caracteres.", - "createUserEnterUsername": "Introduza o nome de utilizador", - "createUserEnterPassword": "Introduza a senha", - "createUserSubmit": "Criar utilizador", - "editUserTitle": "Gerir utilizador: {{username}}", - "editUserDesc": "Edite as funções, o estado de administrador, as sessões e as definições da conta.", + "createUserPasswordHint": "Mínimo 6 caracteres.", + "createUserEnterUsername": "Introduzir nome de utilizador", + "createUserEnterPassword": "Introduzir palavra-passe", + "createUserSubmit": "Criar Utilizador", + "editUserTitle": "Gerir Utilizador: {{username}}", + "editUserDesc": "Editar funções, estado de administrador, sessões e definições da conta.", "editUserUsername": "Nome de utilizador", "editUserAuthType": "Tipo de autenticação", - "editUserAdminStatus": "Estado do administrador", - "editUserUserId": "ID do utilizador", - "linkAccountTitle": "Vincular contas", - "linkAccountDesc": "Mesclar conta OIDC {{username}} com uma conta local existente.", - "linkAccountDescLocal": "Ligue uma conta local {{username}} a uma conta existente apenas OIDC.", + "editUserAdminStatus": "Estado de administrador", + "editUserUserId": "ID de utilizador", + "linkAccountTitle": "Associar contas", + "linkAccountDesc": "Fundir a conta OIDC {{username}} com uma conta local existente.", + "linkAccountDescLocal": "Associar uma conta local {{username}} a uma conta exclusivamente OIDC existente.", "linkAccountWarningTitle": "Isto irá:", - "linkAccountEffect1": "Apague a conta exclusiva do OIDC.", - "linkAccountEffect2": "Adicionar login OIDC à conta de destino", - "linkAccountEffect3": "Permitir login via OIDC e password", + "linkAccountEffect1": "Eliminar a conta exclusivamente OIDC", + "linkAccountEffect2": "Adicionar início de sessão OIDC à conta de destino", + "linkAccountEffect3": "Permitir início de sessão por OIDC e palavra-passe", "linkAccountTargetUsername": "Nome de utilizador da conta local", - "linkAccountTargetPlaceholder": "Introduza o nome de utilizador da conta local para ligar.", + "linkAccountTargetPlaceholder": "Introduza o nome de utilizador da conta local a associar", "linkAccountOidcUsername": "Nome de utilizador da conta OIDC", - "linkAccountOidcPlaceholder": "Introduza o nome de utilizador da conta exclusiva do OIDC para fundir.", - "linkAccountOidcNotFound": "Não foi encontrada nenhuma conta exclusiva para OIDC com este nome de utilizador.", - "linkAccounts": "Vincular contas", - "linkAccountSuccess": "Contas vinculadas com sucesso", - "linkAccountFailed": "Falha ao ligar as contas", - "linkAccountInProgress": "Ligando...", - "unlinkAccountTitle": "Desvincular OIDC", - "unlinkAccountDesc": "Remova a autenticação OIDC de {{username}}. Só poderão fazer login com a palavra-passe.", - "unlinkAccountWarning": "Isto removerá o login OIDC desta conta. O utilizador precisa de ter uma palavra-passe definida para continuar a fazer login.", - "unlinkAccount": "Desvincular OIDC", - "unlinkAccountInProgress": "Desvinculando...", - "unlinkAccountSuccess": "OIDC desvinculado com sucesso.", - "unlinkAccountFailed": "Falha ao desvincular o OIDC", - "saving": "Salvando...", - "updateRegistrationFailed": "Falha ao atualizar as definições de registo", - "updatePasswordLoginFailed": "Falha ao atualizar as definições de início de sessão da palavra-passe.", - "cannotDisablePasswordLoginWithTotp": "Não é possível desativar o início de sessão por palavra-passe enquanto a autenticação de dois fatores (2FA) estiver ativada para um ou mais utilizadores. Desative primeiro a 2FA.", - "updateOidcAutoProvisionFailed": "Falha ao atualizar a configuração de aprovisionamento automático do OIDC", - "updateOidcSilentLoginDefaultFailed": "Falha ao atualizar a configuração de início de sessão silencioso do OIDC", - "updatePasswordResetFailed": "Falha ao atualizar as definições de redefinição de palavra-passe", - "sessionTimeoutRange2": "O tempo limite da sessão deve estar entre 1 e 720 horas.", - "sessionTimeoutSaved": "Tempo limite da sessão guardado", - "sessionTimeoutSaveFailed": "Falha ao guardar o tempo limite da sessão", + "linkAccountOidcPlaceholder": "Introduza o nome de utilizador da conta exclusivamente OIDC a fundir", + "linkAccountOidcNotFound": "Nenhuma conta exclusivamente OIDC encontrada com esse nome de utilizador", + "linkAccounts": "Associar contas", + "linkAccountSuccess": "Contas associadas com sucesso", + "linkAccountFailed": "Falha ao associar contas", + "linkAccountInProgress": "A associar...", + "unlinkAccountTitle": "Desassociar OIDC", + "unlinkAccountDesc": "Remover a autenticação OIDC de {{username}}. Poderá iniciar sessão apenas com a sua palavra-passe.", + "unlinkAccountWarning": "Isto irá remover o início de sessão OIDC desta conta. O utilizador deve ter uma palavra-passe definida para continuar a iniciar sessão.", + "unlinkAccount": "Desassociar OIDC", + "unlinkAccountInProgress": "A desassociar...", + "unlinkAccountSuccess": "OIDC desassociado com sucesso", + "unlinkAccountFailed": "Falha ao desassociar OIDC", + "saving": "A guardar...", + "updateRegistrationFailed": "Falha ao atualizar a definição de registo", + "updatePasswordLoginFailed": "Falha ao atualizar a definição de início de sessão por palavra-passe", + "cannotDisablePasswordLoginWithTotp": "Não é possível desativar o início de sessão por palavra-passe enquanto a 2FA estiver ativa para um ou mais utilizadores. Desative a 2FA primeiro.", + "updateOidcAutoProvisionFailed": "Falha ao atualizar a definição de provisionamento automático OIDC", + "updateOidcSilentLoginDefaultFailed": "Falha ao atualizar a definição de início de sessão silencioso OIDC", + "updatePasswordResetFailed": "Falha ao atualizar a definição de reposição de palavra-passe", + "sessionTimeoutRange2": "O tempo limite da sessão deve estar entre 1 e 720 horas", + "sessionTimeoutSaved": "Timeout da sessão guardado", + "sessionTimeoutSaveFailed": "Falha ao guardar timeout da sessão", "monitoringIntervalInvalid": "Valores de intervalo inválidos", - "monitoringSaved": "Configurações de monitorização guardadas", - "monitoringSaveFailed": "Falha ao guardar as definições de monitorização", + "monitoringSaved": "Definições de monitorização guardadas", + "monitoringSaveFailed": "Falha ao guardar definições de monitorização", "metricsHistoryRetention": "Retenção do histórico de métricas", "metricsHistoryRetentionRange": "1 a 90 dias", "days": "dias", - "guacamoleSaved": "Configurações do guacamole guardadas", - "guacamoleSaveFailed": "Falha ao guardar as definições do Guacamole", - "guacamoleUpdateFailed": "Falha ao atualizar as definições do Guacamole", - "tailscaleSettingsSaved": "Configurações da balança de cauda guardadas", - "tailscaleSettingsSaveFailed": "Falha ao guardar as definições do Tailscale", - "logLevelUpdateFailed": "Falha ao atualizar o nível de registo.", + "guacamoleSaved": "Definições do Guacamole guardadas", + "guacamoleSaveFailed": "Falha ao guardar definições do Guacamole", + "guacamoleUpdateFailed": "Falha ao atualizar definição do Guacamole", + "tailscaleSettingsSaved": "Definições do Tailscale guardadas", + "tailscaleSettingsSaveFailed": "Falha ao guardar definições do Tailscale", + "logLevelUpdateFailed": "Falha ao atualizar nível de registo", "oidcSaved": "Configuração OIDC guardada", - "oidcSaveFailed": "Falha ao guardar a configuração OIDC", + "oidcSaveFailed": "Falha ao guardar configuração OIDC", "oidcRemoved": "Configuração OIDC removida", - "oidcRemoveFailed": "Falha ao remover a configuração OIDC", - "createUserRequired": "É necessário nome de utilizador e palavra-passe.", - "createUserPasswordTooShort": "A palavra-passe deve ter pelo menos 6 caracteres.", - "createUserSuccess": "Utilizador \"{{username}}\" criou", + "oidcRemoveFailed": "Falha ao remover configuração OIDC", + "createUserRequired": "Nome de utilizador e palavra-passe são obrigatórios", + "createUserPasswordTooShort": "A palavra-passe deve ter pelo menos 6 caracteres", + "createUserSuccess": "Utilizador \"{{username}}\" criado", "createUserFailed": "Falha ao criar utilizador", - "updateAdminStatusFailed": "Falha ao atualizar o estado de administrador", - "allSessionsRevoked": "Todas as sessões foram revogadas.", + "updateAdminStatusFailed": "Falha ao atualizar estado de administrador", + "allSessionsRevoked": "Todas as sessões revogadas", "revokeSessionsFailed": "Falha ao revogar sessões", "manageUserData": "Gerir dados do utilizador", "backToUsers": "Voltar aos utilizadores", "manageTabAccount": "Conta", - "manageTabHosts": "Anfitriões", + "manageTabHosts": "Máquinas", "manageTabCredentials": "Credenciais", - "manageTabSnippets": "Trechos", + "manageTabSnippets": "Snippets", "manageTabSessions": "Sessões", "manageTabDanger": "Perigo", "manageEditorBack": "Voltar aos dados de {{username}}", - "dataLockedBadge": "TRANCADO", - "dataLockedNotice": "Os dados de {{username}}permanecem bloqueados até ao próximo login. Os seus hosts, credenciais e pedaços de código não podem ser visualizados ou editados até então.", + "dataLockedBadge": "BLOQUEADO", + "dataLockedNotice": "Os dados de {{username}} permanecem bloqueados até ao próximo início de sessão. As suas máquinas, credenciais e fragmentos não podem ser visualizados nem editados até lá.", "resetPasswordTitle": "Redefinir palavra-passe", - "resetPasswordOidcOnly": "Este utilizador faz login através de um fornecedor externo e não possui palavra-passe.", - "resetPasswordPlaceholder": "Nova Senha", - "resetPasswordBtn": "Reiniciar", - "resetPasswordWorking": "Reiniciando...", - "resetPasswordSuccess": "Redefinição de palavra-passe", - "resetPasswordSuccessWiped": "Redefinição de palavra-passe. Os dados encriptados do utilizador foram apagados.", + "resetPasswordOidcOnly": "Este utilizador autentica-se através de um fornecedor externo e não tem palavra-passe.", + "resetPasswordPlaceholder": "Nova palavra-passe", + "resetPasswordBtn": "Redefinir", + "resetPasswordWorking": "A redefinir...", + "resetPasswordSuccess": "Palavra-passe redefinida", + "resetPasswordSuccessWiped": "Palavra-passe redefinida. Os dados encriptados do utilizador foram eliminados.", "resetPasswordFailed": "Falha ao redefinir a palavra-passe", - "resetPasswordConfirmWipe": "{{username}} não fez login desde a atualização de encriptação, pelo que os seus dados não podem ser recuperados. O reset irá agora excluir os seus hosts, credenciais e trechos de código. Continuar?", + "resetPasswordConfirmWipe": "{{username}} não iniciou sessão desde a atualização da encriptação, pelo que os seus dados não podem ser recuperados. Redefinir agora eliminará as suas máquinas, credenciais e fragmentos. Continuar?", "totpSectionTitle": "Autenticação de dois fatores", - "totpStatusEnabled": "O TOTP está ativado para este utilizador.", - "totpStatusDisabled": "O TOTP não está ativado para este utilizador.", + "totpStatusEnabled": "TOTP está ativado para este utilizador", + "totpStatusDisabled": "TOTP não está ativado para este utilizador", "disableTotp": "Desativar", - "disableTotpConfirm": "Desativar a autenticação de dois fatores para {{username}}? Poderão fazer login apenas com a palavra-passe.", + "disableTotpConfirm": "Desativar a autenticação de dois fatores para {{username}}? Poderá iniciar sessão apenas com a sua palavra-passe.", "totpDisabledSuccess": "TOTP desativado", - "totpDisableFailed": "Falha ao desativar o TOTP", - "manageApiKeys": "Chaves API", + "totpDisableFailed": "Falha ao desativar TOTP", + "manageApiKeys": "Chaves de API", "apiKeyNamePlaceholder": "Nome da chave", - "apiKeyCopyNotice": "Copie esta chave agora, não voltará a ser apresentada.", - "noApiKeysForUser": "Sem chaves API", - "apiKeyDeleteFailed": "Falha ao eliminar a chave API", + "apiKeyCopyNotice": "Copie esta chave agora, não será mostrada novamente.", + "noApiKeysForUser": "Sem chaves de API", + "apiKeyDeleteFailed": "Falha ao eliminar a chave de API", "exportUserData": "Exportação de dados", - "exportUserDataDesc": "Transfira os hosts, as credenciais e os dados do gestor de ficheiros deste utilizador em formato JSON. Os segredos estão desencriptados.", + "exportUserDataDesc": "Descarregar as máquinas, credenciais e dados do gestor de ficheiros deste utilizador como JSON. Os segredos são desencriptados.", "exportUserDataSuccess": "Dados do utilizador exportados", - "exportUserDataFailed": "Falha ao exportar dados do utilizador", - "hostsCount": "{{count}} anfitriões", - "addHostForUser": "Adicionar host", - "noHostsForUser": "Este utilizador não possui hosts.", + "exportUserDataFailed": "Falha ao exportar os dados do utilizador", + "hostsCount": "{{count}} máquinas", + "addHostForUser": "Adicionar máquina", + "noHostsForUser": "Este utilizador não tem máquinas", "connectToHost": "Ligar", - "deleteHostConfirm": "Apagar host \"{{name}}\" pertencente a {{username}}?", - "hostDeletedSuccess": "Host excluído", - "hostDeleteFailed": "Falha ao eliminar o host", + "deleteHostConfirm": "Eliminar a máquina \"{{name}}\" pertencente a {{username}}?", + "hostDeletedSuccess": "Máquina eliminada", + "hostDeleteFailed": "Falha ao eliminar a máquina", "credentialsCount": "{{count}} credenciais", "addCredentialForUser": "Adicionar credencial", - "noCredentialsForUser": "Este utilizador não possui credenciais.", - "deleteCredentialConfirm": "Apagar credencial \"{{name}}\" pertencente a {{username}}?", - "credentialDeletedSuccess": "Credencial excluída", + "noCredentialsForUser": "Este utilizador não tem credenciais", + "deleteCredentialConfirm": "Eliminar credencial \"{{name}}\" de {{username}}?", + "credentialDeletedSuccess": "Credencial eliminada", "credentialDeleteFailed": "Falha ao eliminar credencial", - "snippetsCount": "{{count}} excertos", - "addSnippetForUser": "Adicionar trecho de código", - "noSnippetsForUser": "Este utilizador não possui trechos de código.", - "snippetRequiredFields": "O nome e o conteúdo do excerto são obrigatórios.", - "snippetNamePlaceholder": "Nome do excerto", + "snippetsCount": "{{count}} snippets", + "addSnippetForUser": "Adicionar snippet", + "noSnippetsForUser": "Este utilizador não tem snippets", + "snippetRequiredFields": "O nome e o conteúdo do snippet são obrigatórios", + "snippetNamePlaceholder": "Nome do snippet", "snippetContentPlaceholder": "Conteúdo do comando", "snippetFolderPlaceholder": "Pasta (opcional)", - "snippetSaved": "Trecho salvo", - "snippetSaveFailed": "Falha ao guardar o trecho de código", - "deleteSnippetConfirm": "Apagar excerto \"{{name}}\" pertencente a {{username}}?", - "snippetDeletedSuccess": "Trecho excluído", - "snippetDeleteFailed": "Falha ao eliminar o trecho", - "noSessionsForUser": "Nenhuma sessão ativa", - "deleteUserDangerDesc": "Apague permanentemente {{username}} e todos os seus dados (hosts, credenciais, excertos de código, histórico). Esta ação é irreversível.", - "deleteUserConfirm": "Apagar permanentemente {{username}} e todos os seus dados?", - "deleteUserAdminBlocked": "Remova o estado de administrador antes de eliminar este utilizador.", - "createRoleRequired": "O nome e o nome de exibição são obrigatórios.", + "snippetSaved": "Snippet guardado", + "snippetSaveFailed": "Falha ao guardar snippet", + "deleteSnippetConfirm": "Eliminar snippet \"{{name}}\" de {{username}}?", + "snippetDeletedSuccess": "Snippet eliminado", + "snippetDeleteFailed": "Falha ao eliminar snippet", + "noSessionsForUser": "Sem sessões ativas", + "deleteUserDangerDesc": "Eliminar permanentemente {{username}} e todos os seus dados (hosts, credenciais, snippets, histórico). Esta ação não pode ser desfeita.", + "deleteUserConfirm": "Eliminar permanentemente {{username}} e todos os seus dados?", + "deleteUserAdminBlocked": "Remova o estatuto de administrador antes de eliminar este utilizador.", + "createRoleRequired": "O nome e o nome de exibição são obrigatórios", "createRoleSuccess": "Função \"{{name}}\" criada", - "createRoleFailed": "Falha ao criar a função", - "apiKeyNameRequired": "É necessário nomear a chave.", - "apiKeyUserRequired": "É necessário o ID de utilizador.", - "apiKeyCreatedSuccess": "Chave API \"{{name}}\" criada", - "apiKeyCreateFailed": "Falha ao criar a chave API", - "exportSuccess": "Base de dados exportada com sucesso.", - "exportFailed": "A exportação da base de dados falhou", - "importSelectFile": "Por favor, selecione primeiro um ficheiro.", - "importCompleted": "Importação concluída: {{total}} artigos importados, {{skipped}} ignorados", - "importFailed": "A importação falhou: {{error}}", - "importError": "A importação da base de dados falhou", - "sectionHostDefaults": "Configurações padrão do host", - "hostDefaultsDesc": "As definições são aplicadas automaticamente ao criar um novo host. Os hosts individuais ainda podem substituí-las.", + "createRoleFailed": "Falha ao criar função", + "apiKeyNameRequired": "O nome da chave é obrigatório", + "apiKeyUserRequired": "O ID do utilizador é obrigatório", + "apiKeyCreatedSuccess": "Chave de API \"{{name}}\" criada", + "apiKeyCreateFailed": "Falha ao criar chave de API", + "exportSuccess": "Base de dados exportada com sucesso", + "exportFailed": "Falha na exportação da base de dados", + "importSelectFile": "Selecione um ficheiro primeiro", + "importCompleted": "Importação concluída: {{total}} itens importados, {{skipped}} ignorados", + "importFailed": "Importação falhou: {{error}}", + "importError": "Falha na importação da base de dados", + "sectionHostDefaults": "Predefinições de Host", + "hostDefaultsDesc": "Definições aplicadas automaticamente ao criar um novo host. Os hosts individuais podem substituí-las.", "hostDefaultsSocks5": "Proxy SOCKS5", - "hostDefaultsUseSocks5": "Ativar proxy SOCKS5", - "hostDefaultsUseSocks5Desc": "Preencher previamente o proxy SOCKS5 em todos os novos hosts.", - "hostDefaultsSocks5Host": "Host/Porta do proxy", + "hostDefaultsUseSocks5": "Ativar Proxy SOCKS5", + "hostDefaultsUseSocks5Desc": "Preencher automaticamente o proxy SOCKS5 em todos os novos hosts", + "hostDefaultsSocks5Host": "Host / Porta do proxy", "hostDefaultsSocks5Username": "Nome de utilizador do proxy", - "hostDefaultsSocks5Password": "Senha do proxy", - "hostDefaultsMetrics": "Métricas do host", - "hostDefaultsMetricsEnabled": "Ativar métricas", - "hostDefaultsMetricsEnabledDesc": "Recolher por defeito estatísticas de CPU, memória e outros dados em novos hosts.", - "hostDefaultsStatusCheckEnabled": "Ativar verificação de estado", - "hostDefaultsStatusCheckEnabledDesc": "Verificar o estado online/offline de novos hosts por defeito", - "hostDefaultsTerminal": "terminal", - "hostDefaultsSessionLogging": "Registo de sessão", - "hostDefaultsSessionLoggingDesc": "Por predefinição, as sessões de terminal em novos hosts são gravadas por predefinição.", - "hostDefaultsCommandHistory": "Histórico de comandos", - "hostDefaultsCommandHistoryDesc": "Por predefinição, acompanhe o histórico de comandos em novos hosts.", - "hostDefaultsSaved": "Configurações padrão do host guardadas", - "hostDefaultsSaveFailed": "Falha ao guardar as definições padrão do host", + "hostDefaultsSocks5Password": "Palavra-passe do proxy", + "hostDefaultsMetrics": "Métricas do Host", + "hostDefaultsMetricsEnabled": "Ativar Métricas", + "hostDefaultsMetricsEnabledDesc": "Recolher CPU, memória e outras estatísticas nos novos hosts por predefinição", + "hostDefaultsStatusCheckEnabled": "Ativar Verificação de Estado", + "hostDefaultsStatusCheckEnabledDesc": "Consultar o estado online/offline nos novos hosts por predefinição", + "hostDefaultsTerminal": "Terminal", + "hostDefaultsSessionLogging": "Registo de Sessão", + "hostDefaultsSessionLoggingDesc": "Gravar sessões de terminal nos novos hosts por predefinição", + "hostDefaultsCommandHistory": "Histórico de Comandos", + "hostDefaultsCommandHistoryDesc": "Acompanhar o histórico de comandos nos novos hosts por predefinição", + "hostDefaultsSaved": "Predefinições do host guardadas", + "hostDefaultsSaveFailed": "Falha ao guardar as predefinições do host", "rolePermissions": { "count": "{{count}} permissões", "editAction": "Editar permissões", "loadError": "Falha ao carregar o catálogo de permissões", - "saved": "Permissões de função guardadas", - "saveError": "Falha ao guardar as permissões de função.", + "saved": "Permissões do cargo guardadas", + "saveError": "Falha ao guardar as permissões do cargo", "save": "Guardar", - "saving": "Salvando..." + "saving": "A guardar..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Anfitrião", - "hostPlaceholder": "192.168.1.1 ou exemplo.com", - "portLabel": "Porto", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Porta", "portPlaceholder": "22", "usernameLabel": "Nome de utilizador", - "usernamePlaceholder": "nome de utilizador", - "authLabel": "Aut.", + "usernamePlaceholder": "utilizador", + "authLabel": "Autenticação", "passwordLabel": "Palavra-passe", "passwordPlaceholder": "palavra-passe", - "privateKeyLabel": "Chave privada", - "privateKeyPlaceholder": "Cole a chave privada...", + "privateKeyLabel": "Chave Privada", + "privateKeyPlaceholder": "Colar chave privada...", "credentialLabel": "Credencial", - "credentialPlaceholder": "Selecione uma credencial guardada", - "connectToTerminal": "Ligar ao terminal", - "connectToFiles": "Ligar aos arquivos" + "credentialPlaceholder": "Selecionar uma credencial guardada", + "connectToTerminal": "Ligar ao Terminal", + "connectToFiles": "Ligar aos Ficheiros" }, "history": { "noTerminalSelected": "Nenhum terminal selecionado", - "noTerminalSelectedHint": "Abra um separador do terminal SSH para visualizar o seu histórico de comandos.", - "searchPlaceholder": "Histórico de pesquisa...", - "clearAll": "Limpar tudo", - "noHistoryEntries": "Sem entrada no histórico.", - "trackingDisabled": "O rastreio do histórico está desativado.", - "trackingDisabledHint": "Ative-o nas definições do terminal do host." + "noTerminalSelectedHint": "Abra um separador de terminal SSH para ver o histórico de comandos", + "searchPlaceholder": "Pesquisar histórico...", + "clearAll": "Limpar Tudo", + "noHistoryEntries": "Sem entradas no histórico", + "trackingDisabled": "O rastreio do histórico está desativado", + "trackingDisabledHint": "Ative-o nas definições do terminal do anfitrião." }, "sshTools": { - "keyRecordingTitle": "Gravação principal", - "recordToTerminals": "Gravar nos terminais", - "selectAll": "Tudo", + "keyRecordingTitle": "Gravação de Teclas", + "recordToTerminals": "Gravar para terminais", + "selectAll": "Todos", "selectNone": "Nenhum", - "noTerminalTabsOpen": "Nenhuma aba do terminal aberta", - "selectTerminalsAbove": "Selecione os terminais acima.", - "broadcastInputPlaceholder": "Digite aqui para transmitir as teclas premidas...", - "fillPassword": "Preencha a palavra-passe", - "fillPasswordSuccess": "Palavra-passe introduzida no(s) terminal(ais) {{count}}", + "noTerminalTabsOpen": "Nenhum separador de terminal aberto", + "selectTerminalsAbove": "Selecionar terminais acima", + "broadcastInputPlaceholder": "Digite aqui para difundir as teclas premidas...", + "fillPassword": "Preencher palavra-passe", + "fillPasswordSuccess": "Palavra-passe preenchida em {{count}} terminal(is)", "fillPasswordMissing": "Nenhuma palavra-passe guardada para {{count}} terminal(is) selecionado(s)", - "stopRecording": "Parar gravação", - "startRecording": "Iniciar gravação", - "settingsTitle": "Configurações", - "enableRightClickCopyPaste": "Ativar copiar/colar com o botão direito do rato" + "stopRecording": "Parar Gravação", + "startRecording": "Iniciar Gravação", + "settingsTitle": "Definições", + "enableRightClickCopyPaste": "Ativar copiar/colar com clique direito" }, "splitScreen": { "layoutTitle": "Esquema", - "selectLayoutAbove": "Selecione um layout acima.", - "selectLayoutHint": "Escolha quantos painéis apresentar.", + "selectLayoutAbove": "Selecionar um esquema 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", + "openTabsTitle": "Separadores abertos", + "dragTabsHint": "Arraste os separadores para os painéis acima ou use Atribuição Rápida", + "dropHere": "Largar aqui", "emptyPane": "Vazio", "dashboard": "Painel", - "clearSplitScreen": "Limpar ecrã dividido", - "quickAssign": "Atribuição rápida", + "clearSplitScreen": "Limpar Ecrã Dividido", + "quickAssign": "Atribuição Rápida", "alreadyAssigned": "Painel {{index}}", - "splitTab": "Aba dividida", - "addToSplit": "Adicionar à divisão", - "removeFromSplit": "Remover da divisão", + "splitTab": "Dividir Separador", + "addToSplit": "Adicionar à Divisão", + "removeFromSplit": "Remover da Divisão", "assignToPane": "Atribuir ao painel", - "hotkeysTitle": "Atalhos de teclado", - "hotkeysSplitRight": "Alternar a divisão em 2 vias", - "hotkeysSplitBelow": "Alternar a divisão em 3 vias", - "hotkeysNavigatePane": "Navegar pelos painéis", - "hotkeysNextTab": "Próxima aba", - "hotkeysPrevTab": "Aba anterior" + "hotkeysTitle": "Atalhos de Teclado", + "hotkeysSplitRight": "Alternar divisão de 2 painéis", + "hotkeysSplitBelow": "Alternar divisão de 3 painéis", + "hotkeysNavigatePane": "Navegar entre painéis", + "hotkeysNextTab": "Separador seguinte", + "hotkeysPrevTab": "Separador anterior" }, "snippets": { - "title": "Trechos", - "createSnippetTitle": "Criar excerto de código", - "createSnippetDescription": "Crie um novo trecho de comando para uma execução rápida.", + "title": "Fragmentos", + "createSnippetTitle": "Criar Fragmento", + "createSnippetDescription": "Criar um novo fragmento de comando para execução rápida", "nameLabel": "Nome", - "namePlaceholder": "Por exemplo, reinicie o Nginx.", + "namePlaceholder": "p. ex., Reiniciar Nginx", "descriptionLabel": "Descrição", "descriptionPlaceholder": "Descrição opcional", "optional": "Opcional", "folderLabel": "Pasta", "noFolder": "Sem pasta (Não categorizado)", "commandLabel": "Comando", - "commandPlaceholder": "Por exemplo, sudo systemctl restart nginx", + "commandPlaceholder": "p. ex., sudo systemctl restart nginx", "cancel": "Cancelar", - "createSnippetButton": "Criar excerto de código", - "createFolderTitle": "Criar pasta", - "createFolderDescription": "Organize os seus excertos em pastas.", - "folderNameLabel": "Nome da pasta", - "folderNamePlaceholder": "Por exemplo, comandos do sistema, scripts Docker.", - "folderColorLabel": "Pasta colorida", - "folderIconLabel": "Ícone de pasta", + "createSnippetButton": "Criar Fragmento", + "createFolderTitle": "Criar Pasta", + "createFolderDescription": "Organize os seus fragmentos em pastas", + "folderNameLabel": "Nome da Pasta", + "folderNamePlaceholder": "p. ex., Comandos de Sistema, Scripts Docker", + "folderColorLabel": "Cor da Pasta", + "folderIconLabel": "Ícone da Pasta", "previewLabel": "Pré-visualização", - "folderNameFallback": "Nome da pasta", - "createFolderButton": "Criar pasta", - "targetTerminals": "Terminais de destino", - "selectAll": "Tudo", + "folderNameFallback": "Nome da Pasta", + "createFolderButton": "Criar Pasta", + "targetTerminals": "Terminais de Destino", + "selectAll": "Todos", "selectNone": "Nenhum", - "noTerminalTabsOpen": "Nenhuma aba do terminal aberta", - "searchPlaceholder": "Excertos de pesquisa...", - "newSnippet": "Novo trecho", - "newFolder": "Nova pasta", - "run": "Correr", - "noSnippetsInFolder": "Não existem trechos de código nesta pasta.", + "noTerminalTabsOpen": "Nenhum separador de terminal aberto", + "searchPlaceholder": "Pesquisar snippets...", + "newSnippet": "Novo Snippet", + "newFolder": "Nova Pasta", + "run": "Executar", + "noSnippetsInFolder": "Nenhum snippet nesta pasta", "uncategorized": "Sem categoria", - "editSnippetTitle": "Editar trecho", - "editSnippetDescription": "Atualize este trecho de comando", - "saveSnippetButton": "Guardar alterações", - "createSuccess": "Excerto criado com sucesso", - "createFailed": "Falha ao criar o excerto de código", - "updateSuccess": "Excerto de código atualizado com sucesso", - "updateFailed": "Falha ao atualizar o excerto de código", - "deleteFailed": "Falha ao eliminar o trecho", + "editSnippetTitle": "Editar Snippet", + "editSnippetDescription": "Atualizar este snippet de comando", + "saveSnippetButton": "Guardar Alterações", + "createSuccess": "Snippet criado com sucesso", + "createFailed": "Falha ao criar snippet", + "updateSuccess": "Snippet atualizado com sucesso", + "updateFailed": "Falha ao atualizar snippet", + "deleteFailed": "Falha ao eliminar snippet", "folderCreateSuccess": "Pasta criada com sucesso", - "folderCreateFailed": "Falha ao criar a pasta", - "editFolderTitle": "Pasta de edição", - "editFolderDescription": "Renomeie ou altere o aspeto desta pasta.", - "saveFolderButton": "Guardar alterações", - "editFolder": "Pasta de edição", - "deleteFolder": "Apagar pasta", + "folderCreateFailed": "Falha ao criar pasta", + "editFolderTitle": "Editar Pasta", + "editFolderDescription": "Renomear ou alterar a aparência desta pasta", + "saveFolderButton": "Guardar Alterações", + "editFolder": "Editar pasta", + "deleteFolder": "Eliminar pasta", "folderDeleteSuccess": "Pasta \"{{name}}\" eliminada", - "folderDeleteFailed": "Falha ao eliminar a pasta", + "folderDeleteFailed": "Falha ao eliminar pasta", "folderEditSuccess": "Pasta atualizada com sucesso", - "folderEditFailed": "Falha ao atualizar a pasta", + "folderEditFailed": "Falha ao atualizar pasta", "confirmRunMessage": "Executar \"{{name}}\"?", - "confirmRunButton": "Correr", + "confirmRunButton": "Executar", "runSuccess": "Executado \"{{name}}\" em {{count}} terminal(is)", "copySuccess": "Copiado \"{{name}}\" para a área de transferência", - "shareTitle": "Partilhar trecho", - "shareUser": "Usuário", - "shareRole": "Papel", - "selectUser": "Selecione um utilizador...", - "selectRole": "Selecione uma função...", - "shareSuccess": "Excerto partilhado com sucesso", - "shareFailed": "Falha ao partilhar o trecho", + "shareTitle": "Partilhar Snippet", + "shareUser": "Utilizador", + "shareRole": "Função", + "selectUser": "Selecionar um utilizador...", + "selectRole": "Selecionar uma função...", + "shareSuccess": "Fragmento partilhado com sucesso", + "shareFailed": "Falha ao partilhar fragmento", "revokeSuccess": "Acesso revogado", - "revokeFailed": "Falha ao revogar o acesso", - "currentAccess": "Acesso atual", - "shareLoadError": "Falha ao carregar os dados partilhados.", - "loading": "Carregando...", - "close": "Perto", - "reorderFailed": "Falha ao guardar a ordem do trecho de código", - "importExport": "Importação/Exportação", + "revokeFailed": "Falha ao revogar acesso", + "currentAccess": "Acesso Atual", + "shareLoadError": "Falha ao carregar dados de partilha", + "loading": "A carregar...", + "close": "Fechar", + "reorderFailed": "Falha ao guardar ordem dos fragmentos", + "importExport": "Importar / Exportar", "exportBtn": "Exportar JSON", "importBtn": "Importar JSON", - "exportSuccess": "Trechos exportados com sucesso", - "exportFailed": "Falha ao exportar excertos de código.", - "importTitle": "Importar excertos", - "importDescription": "Importe excertos e pastas de um ficheiro JSON exportado pelo Termix.", - "importDropOrClick": "Arraste e largue um ficheiro JSON aqui ou clique para navegar.", + "exportSuccess": "Fragmentos exportados com sucesso", + "exportFailed": "Falha ao exportar fragmentos", + "importTitle": "Importar Fragmentos", + "importDescription": "Importar fragmentos e pastas de um ficheiro JSON exportado pelo Termix", + "importDropOrClick": "Arraste um ficheiro JSON aqui ou clique para procurar", "importSelectedFile": "Selecionado: {{name}}", - "importOverwrite": "Sobrescrever excertos de código existentes com o mesmo nome e pasta.", - "importStartBtn": "Importação", - "importSuccess": "Importação concluída: {{snippets}} excerto(s) adicionado(s), {{updated}} atualizado(s), {{skipped}} ignorado(s), {{folders}} pasta(s) adicionada(s)", - "importFailed": "Falha ao importar excertos de código.", - "importInvalidFile": "Ficheiro inválido: esperava-se um objeto JSON com excertos ou arrays de pastas.", - "targetHostsLabel": "Hosts de destino", - "targetHostsHint": "Atribua hosts para executar este pedaço de código diretamente, sem a necessidade de um terminal aberto.", - "noHostsAvailable": "Nenhum host configurado", + "importOverwrite": "Substituir fragmentos existentes com o mesmo nome e pasta", + "importStartBtn": "Importar", + "importSuccess": "Importação concluída: {{snippets}} fragmento(s) adicionado(s), {{updated}} atualizado(s), {{skipped}} ignorado(s), {{folders}} pasta(s) adicionada(s)", + "importFailed": "Falha ao importar fragmentos", + "importInvalidFile": "Ficheiro inválido: esperado um objeto JSON com arrays de fragmentos ou pastas", + "targetHostsLabel": "Máquinas de Destino", + "targetHostsHint": "Atribuir máquinas para executar este fragmento diretamente sem um terminal aberto.", + "noHostsAvailable": "Nenhuma máquina configurada", "clearTargetHosts": "Limpar tudo", - "hasTargetHosts": "Possui hosts de destino", - "runOnTargets": "Corrida com base em objetivos", - "directRunSuccess": "Executado \"{{name}}\" em {{count}} host(s)", - "directRunPartialFail": "\"{{name}}\" falhou num ou mais hosts", - "executionResultTitle": "Resultados da execução: {{name}}", - "executionResultDescription": "Resultado da execução do trecho de código em cada host de destino.", + "hasTargetHosts": "Possui máquinas de destino", + "runOnTargets": "Executar nos Destinos", + "directRunSuccess": "Executou \"{{name}}\" em {{count}} máquina(s)", + "directRunPartialFail": "\"{{name}}\" falhou numa ou mais máquinas", + "executionResultTitle": "Resultados da Execução: {{name}}", + "executionResultDescription": "Saída da execução do fragmento em cada host de destino.", "executionSuccess": "Sucesso", "executionFailed": "Falhou" }, + "keybindings": { + "title": "Atalhos de teclado", + "description": "Personalize os atalhos de copiar, colar e controlar o terminal, ou associe teclas para enviar texto ou executar um trecho de código.", + "defaultsHeading": "Atalhos integrados", + "customHeading": "Atalhos personalizados", + "addBinding": "Adicionar atalho", + "addBindingTitle": "Adicionar atalho", + "editBindingTitle": "Atalho para edição", + "loading": "Carregando...", + "noCustomBindings": "Ainda não existem atalhos personalizados.", + "defaultBadge": "Predefinição", + "customizedBadge": "Personalizado", + "customize": "Personalizar", + "resetToDefault": "Restaurar para as definições padrão", + "resetAllToDefaults": "Repor tudo para os valores de fábrica", + "close": "Perto", + "cancel": "Cancelar", + "saveBinding": "Atalho para guardar", + "saveError": "Falha ao guardar os atalhos de teclado.", + "pressKeysToRecord": "Combinação de teclas", + "pressKeysPlaceholder": "Clique e pressione as teclas.", + "recording": "Pressione as teclas...", + "comboRequiredError": "Pressione primeiro uma combinação de teclas", + "textRequiredError": "Introduza o texto que pretende enviar.", + "controlCodeRequiredError": "Introduza uma única letra para o código de controlo.", + "snippetRequiredError": "Selecione um excerto", + "conflictWarning": "Esta combinação já está ligada a {{combo}}. Guardar fará com que ambas disparem.", + "actionLabel": "Ação", + "actionCopy": "Copiar seleção", + "actionPaste": "Colar", + "actionSendControlCode": "Enviar sinal Ctrl+letra", + "actionSendText": "Enviar mensagem de texto", + "actionRunSnippet": "Executar excerto de código existente", + "controlCodeLabel": "Carta", + "textLabel": "Texto a enviar", + "appendEnterLabel": "Prima Enter após enviar", + "snippetLabel": "Trecho", + "selectSnippetPlaceholder": "Selecione um excerto", + "orphanedSnippetWarning": "trecho não encontrado", + "clipboardPermissionNote": "Pode solicitar permissão para aceder à área de transferência na primeira utilização em alguns navegadores." + }, "userProfile": { - "donateTitle": "Mantenha o Termix vivo", - "donateDescription": "O Termix é gratuito, auto-hospedado e desenvolvido por apenas algumas pessoas no seu tempo livre. Se ele lhe poupou tempo ou dinheiro, uma doação em criptomoedas ajuda a mantê-lo em funcionamento.", - "donateMilestones": "Os donativos ajudam a financiar o tempo necessário para pesquisar e aprender o que é necessário para desenvolver o apoio para SAML, Kubernetes e Agentes. Veja o progresso e doe.", - "donateButton": "Doe criptomoedas", + "donateTitle": "Mantenha o Termix ativo", + "donateDescription": "O Termix é gratuito, auto-hospedado e desenvolvido por apenas algumas pessoas no seu tempo livre. Se lhe poupou tempo ou dinheiro, uma doação em criptomoeda ajuda a mantê-lo a funcionar.", + "donateMilestones": "As doações ajudam a financiar o tempo para investigar e aprender o que é necessário para implementar suporte para SAML, Kubernetes e Agente. Veja o progresso e doe.", + "donateButton": "Doar criptomoeda", "storageModeLocal": "Navegador", - "storageModeCloud": "Banco de dados", - "storageModeDescription": "O navegador armazena as definições apenas neste navegador. A base de dados é sincronizada com o servidor e carregada em qualquer dispositivo.", - "resetToDefaults": "Restaurar as predefinições de fábrica", - "resetToDefaultsSuccess": "As definições foram repostas para os padrões de fábrica.", - "storageModeSwitch": "Armazenamento de preferências", + "storageModeCloud": "Base de Dados", + "storageModeDescription": "O navegador guarda as definições apenas neste navegador. A base de dados sincroniza com o servidor e carrega em qualquer dispositivo.", + "resetToDefaults": "Repor Predefinições", + "resetToDefaultsSuccess": "Definições repostas para as predefinições.", + "storageModeSwitch": "Armazenamento de Preferências", "sectionAccount": "Conta", - "sectionAppearance": "Aspeto", + "sectionAppearance": "Aparência", "sectionSecurity": "Segurança", "sectionApiKeys": "Chaves API", + "sectionData": "Dados", "sectionC2sTunnels": "Túneis C2S", "usernameLabel": "Nome de utilizador", - "roleLabel": "Papel", + "roleLabel": "Função", "roleAdministrator": "Administrador", - "authMethodLabel": "Método de autenticação", + "authMethodLabel": "Método de Autenticação", "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Em", - "twoFaOff": "Desligado", + "twoFaOn": "Ativado", + "twoFaOff": "Desativado", "versionLabel": "Versão", "betaProgramTitle": "Programa Beta", - "betaProgramDescription": "Experimente antecipadamente os próximos recursos com a etiqueta Docker :beta, distribuída semanalmente. Instável, não recomendada para produção.", - "betaProgramFeedback": "Encontrou um erro? Reporte-o aqui.", - "betaProgramLearnMore": "Saber mais", - "deleteAccount": "Apagar conta", - "deleteAccountDescription": "Apagar a sua conta permanentemente", - "changeServerDescription": "Mude para um servidor de backend Termix diferente.", + "betaProgramDescription": "Experimente novas funcionalidades antecipadamente com a tag Docker semanal :beta. Instável, não para produção.", + "betaProgramFeedback": "Encontrou um bug? Reporte-o aqui.", + "betaProgramLearnMore": "Saber Mais", + "deleteAccount": "Eliminar Conta", + "deleteAccountDescription": "Eliminar permanentemente a sua conta", + "changeServerDescription": "Mudar para um servidor de backend Termix diferente", "deleteButton": "Eliminar", - "deleteAccountPermanent": "Esta ação é permanente e não pode ser desfeita.", - "deleteAccountWarning": "Todas as sessões, hosts, credenciais e definições serão eliminadas permanentemente.", - "confirmPasswordDeletePlaceholder": "Introduza a sua senha para confirmar.", + "deleteAccountPermanent": "Esta ação é permanente e não pode ser anulada.", + "deleteAccountWarning": "Todas as sessões, anfitriões, credenciais e configurações serão permanentemente eliminadas.", + "confirmPasswordDeletePlaceholder": "Introduza a sua palavra-passe para confirmar", "languageLabel": "Idioma", "themeLabel": "Tema", "fontSizeLabel": "Tamanho da fonte", "accentColorLabel": "Cor de destaque", - "settingsTerminal": "terminal", - "commandAutocomplete": "Preenchimento automático de comandos", - "commandAutocompleteDesc": "Mostrar a funcionalidade de autocompletar enquanto digita", - "terminalLinkBehavior": "Clique no link do terminal", - "terminalLinkBehaviorDesc": "Comportamento padrão ao clicar em links no terminal.", - "historyTracking": "Rastreamento de histórico", - "historyTrackingDesc": "Rastrear comandos do terminal", - "commandPalette": "Paleta de comandos", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Autocompletar comandos", + "commandAutocompleteDesc": "Mostrar autocompletamento durante a digitação", + "keyboardShortcuts": "Atalhos de teclado", + "keyboardShortcutsDescription": "Personalize os atalhos de copiar, colar e os comandos do terminal.", + "manageShortcuts": "Gerir", + "terminalLinkBehavior": "Clique em links do terminal", + "terminalLinkBehaviorDesc": "Comportamento predefinido ao clicar em links no terminal", + "historyTracking": "Registo de histórico", + "historyTrackingDesc": "Registar comandos do terminal", + "commandPalette": "Paleta de Comandos", "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 o separador", - "confirmTabCloseDesc": "Pergunte antes de fechar os separadores do terminal.", + "reopenTabsOnLogin": "Reabrir separadores ao iniciar sessão", + "reopenTabsOnLoginDesc": "Restaurar os separadores abertos ao iniciar sessão ou atualizar a página, mesmo a partir de outro dispositivo", + "confirmTabClose": "Confirmar fecho de separador", + "confirmTabCloseDesc": "Perguntar antes de fechar separadores do terminal", "settingsSidebar": "Barra lateral", - "showHostTags": "Tags do apresentador", - "showHostTagsDesc": "Exibir etiquetas na lista de hosts", - "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.", - "compactHostView": "Visão compacta do host", - "compactHostViewDesc": "Recolha cada host para uma única linha, mostrando apenas o seu nome e endereço.", + "showHostTags": "Mostrar etiquetas de anfitrião", + "showHostTagsDesc": "Apresentar etiquetas na lista de anfitriões", + "hostTrayOnClick": "Clicar para expandir 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 rato", + "compactHostView": "Vista compacta de anfitriões", + "compactHostViewDesc": "Reduzir cada anfitrião a uma única linha mostrando apenas o nome e o endereço", "statusColors": "Cores de estado reais", - "statusColorsDesc": "Utilize verde/vermelho para indicar o estado online/offline em vez da cor de destaque.", - "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.", - "expandAppRailOnHover": "Expandir o App Rail ao passar o cursor", - "expandAppRailOnHoverDesc": "Permitir que a barra lateral esquerda se expanda quando o ponteiro passa sobre ela.", + "statusColorsDesc": "Usar verde/vermelho para estado online/offline em vez da cor de destaque", + "pinAppRail": "Fixar barra de aplicações", + "pinAppRailDesc": "Manter a barra de aplicações da barra lateral esquerda sempre expandida em vez de expandir ao passar o rato", + "expandAppRailOnHover": "Expandir barra de aplicações ao passar o rato", + "expandAppRailOnHoverDesc": "Permitir que a barra de aplicações da barra lateral esquerda se expanda quando o ponteiro passa sobre ela", "settingsNavigation": "Navegação", - "navigationTabsDesc": "Escolha quais os separadores que aparecem na barra lateral da aplicação.", - "settingsSnippets": "Trechos", + "navigationTabsDesc": "Escolha quais separadores aparecem na barra lateral da aplicação", + "settingsSnippets": "Fragmentos", "foldersCollapsed": "Pastas recolhidas", - "foldersCollapsedDesc": "Recolher pastas por defeito", + "foldersCollapsedDesc": "Recolher pastas por predefinição", "confirmExecution": "Confirmar execução", - "confirmExecutionDesc": "Confirme antes de executar os excertos de código.", + "confirmExecutionDesc": "Confirmar antes de executar fragmentos", "settingsUpdates": "Atualizações", - "disableUpdateChecks": "Desativar verificações de atualização", - "disableUpdateChecksDesc": "Pare de verificar se existem atualizações.", + "disableUpdateChecks": "Desativar verificações de atualizações", + "disableUpdateChecksDesc": "Parar de verificar atualizações", "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "A autenticação de dois fatores está ativada.", - "totpDisabled": "Adicione segurança extra ao login.", + "totpEnabled": "2FA está ativada", + "totpDisabled": "Adicionar segurança extra de início de sessão", "disable": "Desativar", "enable": "Ativar", "setupTotp": "Configurar TOTP", "qrCode": "Código QR", - "totpInstructions": "Digitalize o código QR ou introduza a palavra-passe na sua aplicação autenticadora e, em seguida, introduza o código de 6 dígitos.", + "totpInstructions": "Leia o código QR ou introduza o segredo na sua aplicação autenticadora e depois introduza o código de 6 dígitos", "totpCodePlaceholder": "000000", "verify": "Verificar", - "changePassword": "Alterar a palavra-passe", - "currentPasswordLabel": "Senha atual", - "currentPasswordPlaceholder": "Senha atual", - "newPasswordLabel": "Nova Senha", - "newPasswordPlaceholder": "Nova Senha", - "confirmPasswordLabel": "Confirmar nova palavra-chave", - "confirmPasswordPlaceholder": "Confirme a nova palavra-passe", + "changePassword": "Alterar palavra-passe", + "currentPasswordLabel": "Palavra-passe atual", + "currentPasswordPlaceholder": "Palavra-passe atual", + "newPasswordLabel": "Nova palavra-passe", + "newPasswordPlaceholder": "Nova palavra-passe", + "confirmPasswordLabel": "Confirmar nova palavra-passe", + "confirmPasswordPlaceholder": "Confirmar nova palavra-passe", "updatePassword": "Atualizar palavra-passe", "createApiKeyTitle": "Criar chave API", - "createApiKeyDescription": "Gere uma nova chave API para acesso programático.", + "createApiKeyDescription": "Gerar uma nova chave API para acesso programático.", "apiKeyNameLabel": "Nome", - "apiKeyNamePlaceholder": "Exemplo: Pipeline de CI", + "apiKeyNamePlaceholder": "ex.: CI Pipeline", "expiryDateLabel": "Data de validade", "optional": "opcional", "cancel": "Cancelar", "createKey": "Criar chave", - "apiKeyCount": "{{count}} teclas", - "newKey": "Nova chave", - "noApiKeys": "Ainda não existem chaves API.", - "apiKeyActive": "Ativo", + "apiKeyCount": "{{count}} chaves", + "newKey": "Nova Chave", + "noApiKeys": "Ainda sem chaves de API.", + "apiKeyActive": "Ativa", "apiKeyUsageHint": "Inclua a sua chave no", "apiKeyUsageHintHeader": "cabeçalho.", "apiKeyPermissionsHint": "As chaves herdam as permissões do utilizador que as criou.", - "roleUser": "Usuário", - "authMethodDual": "Autenticação dupla", + "exportData": "Exportar os meus dados", + "exportDataDesc": "Faça o download de um backup dos seus hosts, credenciais e definições para transferir para outro dispositivo.", + "export": "Exportar", + "exporting": "Exportador...", + "importData": "Importar os meus dados", + "importDataDesc": "Restaure os seus hosts, credenciais e definições a partir de um ficheiro de cópia de segurança . sqlite.", + "importDataSelected": "Selecionado: {{name}}", + "selectFile": "Selecionar ficheiro", + "changeFile": "Mudar", + "import": "Importação", + "importing": "Importando...", + "exportSuccess": "Dados exportados com sucesso", + "exportFailed": "A exportação de dados falhou", + "importSelectFile": "Por favor, selecione primeiro um ficheiro.", + "importCompleted": "Importação concluída: {{total}} artigos importados, {{skipped}} ignorados", + "importFailed": "A importação falhou: {{error}}", + "roleUser": "Utilizador", + "authMethodDual": "Autenticação Dupla", "authMethodOidc": "OIDC", - "totpSetupFailed": "Falha ao iniciar a configuração TOTP", - "totpEnter6Digits": "Introduza um código de 6 dígitos", + "totpSetupFailed": "Falha ao iniciar a configuração de TOTP", + "totpEnter6Digits": "Insira um código de 6 dígitos", "totpEnabledSuccess": "Autenticação de dois fatores ativada", - "totpInvalidCode": "Código inválido, tente novamente.", - "totpDisableInputRequired": "Introduza o seu código TOTP ou palavra-passe.", + "totpInvalidCode": "Código inválido, tente novamente", + "totpDisableInputRequired": "Insira o seu código TOTP ou palavra-passe", "totpDisabledSuccess": "Autenticação de dois fatores desativada", - "totpDisableFailed": "Falha ao desativar a autenticação de dois fatores (2FA).", - "totpDisableTitle": "Desative a autenticação de dois fatores (2FA).", - "totpDisablePlaceholder": "Introduza o código TOTP ou a palavra-passe.", - "totpDisableConfirm": "Desative a autenticação de dois fatores (2FA).", - "totpContinueVerify": "Continuar a verificar", - "totpVerifyTitle": "Código de verificação", - "totpBackupTitle": "Códigos de cópia de segurança", - "totpDownloadBackup": "Baixar códigos de backup", - "passkeys": "Senhas", - "passkeysDesc": "Utilize as credenciais WebAuthn/FIDO2 para iniciar sessão sem palavra-passe.", - "passkeyName": "Nome da chave de acesso", - "passkeyUvPreferred": "Preferido", + "totpDisableFailed": "Falha ao desativar 2FA", + "totpDisableTitle": "Desativar 2FA", + "totpDisablePlaceholder": "Insira o código TOTP ou palavra-passe", + "totpDisableConfirm": "Desativar 2FA", + "totpContinueVerify": "Continuar para Verificação", + "totpVerifyTitle": "Verificar Código", + "totpBackupTitle": "Códigos de Backup", + "totpDownloadBackup": "Descarregar Códigos de Backup", + "passkeys": "Passkeys", + "passkeysDesc": "Utilize credenciais WebAuthn/FIDO2 para iniciar sessão sem palavra-passe", + "passkeyName": "Nome da passkey", + "passkeyUvPreferred": "Preferencial", "passkeyUvRequired": "Obrigatório", - "passkeyUvDiscouraged": "Desencorajado", - "addPasskey": "Adicionar chave de acesso", - "noPasskeys": "Sem chave de acesso registada.", - "passkeyAdded": "Senha adicionada", - "passkeyAddFailed": "Falha ao adicionar a chave de acesso.", - "passkeyDeleted": "Senha excluída", - "passkeyDeleteFailed": "Falha ao eliminar a chave de acesso.", + "passkeyUvDiscouraged": "Desaconselhado", + "addPasskey": "Adicionar Passkey", + "noPasskeys": "Nenhuma passkey registada.", + "passkeyAdded": "Passkey adicionada", + "passkeyAddFailed": "Falha ao adicionar passkey", + "passkeyDeleted": "Passkey eliminada", + "passkeyDeleteFailed": "Falha ao eliminar chave de acesso", "done": "Concluído", "secretCopied": "Segredo copiado para a área de transferência", - "apiKeyNameRequired": "É necessário nomear a chave.", - "apiKeyCreated": "Chave API \"{{name}}\" criada", - "apiKeyCreateFailed": "Falha ao criar a chave API", - "apiKeyUser": "Usuário", + "apiKeyNameRequired": "Nome da chave é obrigatório", + "apiKeyCreated": "Chave de API \"{{name}}\" criada", + "apiKeyCreateFailed": "Falha ao criar chave de API", + "apiKeyUser": "Utilizador", "apiKeyExpires": "Expira", - "apiKeyRevoked": "Chave API \"{{name}}\" revogada", - "apiKeyRevokeFailed": "Falha ao revogar a chave API", - "passwordFieldsRequired": "As palavras-passe atuais e novas são obrigatórias.", - "passwordMismatch": "As palavras-passe não coincidem.", - "passwordTooShort": "A palavra-passe deve ter pelo menos 6 caracteres.", + "apiKeyRevoked": "Chave de API \"{{name}}\" revogada", + "apiKeyRevokeFailed": "Falha ao revogar chave de API", + "passwordFieldsRequired": "As palavras-passe atual e nova são obrigatórias", + "passwordMismatch": "As palavras-passe não coincidem", + "passwordTooShort": "A palavra-passe deve ter pelo menos 6 caracteres", "passwordUpdated": "Palavra-passe atualizada com sucesso", - "passwordUpdateFailed": "Falha ao atualizar a palavra-passe", - "deletePasswordRequired": "É necessário utilizar a palavra-passe para eliminar a sua conta.", - "deleteFailed": "Falha ao eliminar a conta", - "deleting": "Excluindo...", - "colorPickerTooltip": "Abra o seletor de cores", + "passwordUpdateFailed": "Falha ao atualizar palavra-passe", + "deletePasswordRequired": "É necessária a palavra-passe para eliminar a sua conta", + "deleteFailed": "Falha ao eliminar conta", + "deleting": "A eliminar...", + "colorPickerTooltip": "Abrir seletor de cores", "themeSystem": "Sistema", - "themeLight": "Luz", + "themeLight": "Claro", "themeDark": "Escuro", - "themeDracula": "Drácula", - "themeCatppuccin": "Catppuccino", - "themeNord": "Norte", - "themeSolarized": "Solarizado", - "themeTokyoNight": "Noite de Tóquio", - "themeOneDark": "Um Escuro", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { "title": "Monitor Tmux", - "failedToLoadHosts": "Falha ao carregar os hosts", - "failedToLoad": "Falha ao carregar as sessões tmux", - "tmuxUnavailable": "O tmux não está instalado neste host.", - "noSessions": "Não existem sessões tmux neste host.", - "noHostSelected": "Nenhum host selecionado", + "failedToLoadHosts": "Falha ao carregar anfitriões", + "failedToLoad": "Falha ao carregar sessões tmux", + "tmuxUnavailable": "tmux não está instalado neste anfitrião", + "noSessions": "Sem sessões tmux neste anfitrião", + "noHostSelected": "Nenhum anfitrião selecionado", "attached": "Anexado", - "detached": "Desapegado", + "detached": "Desanexado", "attach": "Anexar", "editTags": "Editar etiquetas", - "tagsHint": "Etiquetas separadas por vírgulas (ex: YOLO, laboratório, formação)", + "tagsHint": "Etiquetas separadas por vírgulas (ex: YOLO, lab, training)", "tagsSaved": "Etiquetas guardadas", - "tagsSaveFailed": "Falha ao guardar as tags", - "searchPlaceholder": "Resultados do inquérito em todas as sessões...", + "tagsSaveFailed": "Falha ao guardar etiquetas", + "searchPlaceholder": "Pesquisar saída em todas as sessões...", "searchResults": "{{count}} correspondências", - "searchFailed": "A busca falhou", - "selectPaneHint": "Selecione um painel para visualizar a sua saída.", + "searchFailed": "Pesquisa falhou", + "selectPaneHint": "Selecione um painel para pré-visualizar a sua saída", "closePreview": "Fechar pré-visualização", "newSession": "Nova sessão", "newSessionHint": "Nome da sessão (letras, dígitos, _ @ % + = -)", - "newSessionPlaceholder": "minha-sessão", + "newSessionPlaceholder": "my-session", "create": "Criar", "sessionCreated": "Sessão \"{{name}}\" criada", "sessionCreateFailed": "Falha ao criar sessão", "splitRight": "Dividir à direita", - "splitDown": "Dividir em partes", - "splitFailed": "Falha ao dividir o painel", + "splitDown": "Dividir para baixo", + "splitFailed": "Falha ao dividir painel", "newWindow": "Nova janela", - "windowCreateFailed": "Falha ao criar a janela", + "windowCreateFailed": "Falha ao criar janela", "attachSessionTooltip": "Anexar a {{session}}", "refresh": "Atualizar", - "refreshFailed": "Falha ao atualizar as sessões", - "collapseAll": "Recolher tudo o", + "refreshFailed": "Falha ao atualizar sessões", + "collapseAll": "Recolher tudo", "expandAll": "Expandir tudo", "moreActions": "Mais ações", "sessionStats": "Estatísticas da sessão", "renameSessionTitle": "Renomear sessão \"{{name}}\"", "rename": "Renomear", "sessionRenamed": "Sessão renomeada para \"{{name}}\"", - "sessionRenameFailed": "Falha ao renomear a sessão", - "editTagsTitle": "Editar as etiquetas para \"{{name}}\"", - "killPane": "Painel de eliminação", - "resizeTree": "Arraste para redimensionar — clique duas vezes para repor", - "reattach": "Reconectar (corrige uma visualização distorcida)", - "killWindow": "Janela de eliminação", - "killWindowTitle": "Fechar janela {{index}} de \"{{session}}\"?", - "killWindowBody": "Todos os painéis e processos nesta janela serão encerrados. Fechar a última janela finaliza a sessão.", - "windowKillFailed": "Falha ao fechar a janela", + "sessionRenameFailed": "Falha ao renomear sessão", + "editTagsTitle": "Editar etiquetas para \"{{name}}\"", + "killPane": "Terminar painel", + "resizeTree": "Arraste para redimensionar — clique duplo para repor", + "reattach": "Reanexar (corrige uma visualização distorcida)", + "killWindow": "Terminar janela", + "killWindowTitle": "Terminar janela {{index}} de \"{{session}}\"?", + "killWindowBody": "Todos os painéis e processos nesta janela serão terminados. Terminar a última janela encerra a sessão.", + "windowKillFailed": "Falha ao terminar janela", "statusActivity": "Atividade", - "statusWindows": "Windows", + "statusWindows": "Janelas", "statusPanes": "painéis", "statusTags": "Etiquetas", - "killPaneTitle": "Painel de fecho {{id}}?", - "killPaneBody": "O processo em execução neste painel será terminado. Fechar o último painel fecha a janela correspondente.", - "paneKillFailed": "Falha ao desligar o painel", - "killSessionTitle": "Sessão de encerramento \"{{name}}\"?", - "killSessionBody": "Todas as janelas e processos em execução nesta sessão serão encerrados. Esta ação é irreversível.", - "kill": "Morte", - "sessionKilled": "Sessão \"{{name}}\" encerrada", - "sessionKillFailed": "Falha ao terminar a sessão", - "hostUnreachable": "Não foi possível ligar ao host. Verifique se está online e acessível.", - "noServer": "Não existe nenhum servidor tmux em execução neste host.", - "searchTruncated": "Resultados parciais — a pesquisa abrange as últimas {{lines}} linhas de cada painel e no máximo {{panes}} painéis.", + "killPaneTitle": "Terminar painel {{id}}?", + "killPaneBody": "O processo em execução neste painel será terminado. Terminar o último painel fecha a sua janela.", + "paneKillFailed": "Falha ao terminar painel", + "killSessionTitle": "Terminar sessão \"{{name}}\"?", + "killSessionBody": "Todas as janelas e processos em execução nesta sessão serão terminados. Isto não pode ser desfeito.", + "kill": "Terminar", + "sessionKilled": "Sessão \"{{name}}\" terminada", + "sessionKillFailed": "Falha ao terminar sessão", + "hostUnreachable": "Não foi possível ligar ao anfitrião. Verifique se está online e acessível.", + "noServer": "Não há nenhum servidor tmux em execução neste anfitrião.", + "searchTruncated": "Resultados parciais — a pesquisa abrange as últimas {{lines}} linhas de cada painel e, no máximo, {{panes}} painéis.", "closeSearchResults": "Fechar resultados da pesquisa", "retry": "Tentar novamente", - "noHosts": "Não existem hosts SSH disponíveis.", - "noHostsHint": "Active a opção Monitor do Tmux num host SSH (Gestor de Hosts → separador Terminal) para monitorizar as suas sessões do tmux.", - "tmuxInstallHint": "Instale-o no host com:", - "attachTooltip": "Abra um terminal para {{host}}", - "attachTooltipPane": "Abra um terminal para {{host}} — sessão tmux {{session}}", + "noHosts": "Nenhum anfitrião SSH disponível", + "noHostsHint": "Ative a opção Monitor Tmux num anfitrião SSH (Gestor de Anfitriões → separador Terminal) para monitorizar as suas sessões tmux.", + "tmuxInstallHint": "Instale-o no anfitrião com:", + "attachTooltip": "Abrir um terminal para {{host}}", + "attachTooltipPane": "Abrir um terminal para {{host}} — sessão tmux {{session}}", "timeJustNow": "agora mesmo", - "timeMinutes": "{{count}}m atrás", - "timeHours": "{{count}}há h", - "timeDays": "{{count}}há alguns dias" + "timeMinutes": "há {{count}} min", + "timeHours": "há {{count}} h", + "timeDays": "há {{count}} d" }, "mobileKeyboard": { - "shift": "Turno", + "shift": "Shift", "ctrl": "Ctrl", "esc": "Esc", - "tab": "Aba", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "Seta para cima", - "arrowDown": "Seta para baixo", - "arrowLeft": "Seta para a esquerda", - "arrowRight": "Seta para a direita", - "home": "Casa", + "arrowUp": "Seta para Cima", + "arrowDown": "Seta para Baixo", + "arrowLeft": "Seta para a Esquerda", + "arrowRight": "Seta para a Direita", + "home": "Início", "end": "Fim", "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", - "editQuickKeys": "Editar teclas de atalho", - "quickKeysTitle": "Teclas de atalho", - "quickKeysDesc": "Toque em × para remover. Suporta até 8 caracteres.", - "quickKeyPlaceholder": "por exemplo sudo ", + "paste": "Colar", + "editQuickKeys": "Editar teclas rápidas", + "quickKeysTitle": "Teclas Rápidas", + "quickKeysDesc": "Toque no × para remover. Suporta até 8 caracteres.", + "quickKeyPlaceholder": "ex: sudo ", "addQuickKey": "Adicionar", "removeQuickKey": "Remover", - "resetDefaults": "Restaurar as predefinições de fábrica", - "done": "Concluído" + "resetDefaults": "Restaurar predefinições", + "done": "Concluir" }, "serial": { "title": "Serial", - "portLabel": "Porto", + "portLabel": "Porta", "portPlaceholder": "/dev/ttyUSB0 ou COM3", - "baudRateLabel": "Taxa de transmissão (Baud rate)", + "baudRateLabel": "Taxa Baud", "dataBitsLabel": "Dados", - "stopBitsLabel": "Parar", + "stopBitsLabel": "Stop", "parityLabel": "Paridade", - "parityNone": "Nenhum", - "parityEven": "Igual", - "parityOdd": "Estranho", - "connect": "Ligar à porta serial", - "disconnect": "Desconectar", + "parityNone": "Nenhuma", + "parityEven": "Par", + "parityOdd": "Ímpar", + "connect": "Ligar à Serial", + "disconnect": "Desligar", "refreshPorts": "Atualizar portas", - "connected": "Ligado a {{path}} à taxa de transmissão de {{baud}}", - "disconnected": "Porta série desconectada", + "connected": "Ligado a {{path}} a {{baud}} baud", + "disconnected": "Porta série desligada", "connectionError": "Falha ao abrir a porta série", - "wsError": "Erro no WebSocket", + "wsError": "Erro de WebSocket", "errorNoServerUrl": "Nenhum URL de servidor configurado", - "notSupportedTitle": "Serial não suportado", - "notSupported": "As ligações série requerem um browser com suporte para a API Web Serial (Chrome, Edge ou Firefox 151+) ou a aplicação Termix para desktop.", - "hideHint": "Pode ocultar o separador Serial em Perfil do utilizador > Aparência > Barra lateral > Navegação.", - "browserPickerHint": "Clique em Ligar e o seu browser abrirá um seletor de portas para que possa escolher o dispositivo." + "notSupportedTitle": "Série não suportada", + "notSupported": "As ligações série requerem um navegador com suporte à Web Serial API (Chrome, Edge ou Firefox 151+) ou a aplicação de ambiente de trabalho Termix.", + "hideHint": "Pode ocultar o separador Série em Perfil do Utilizador > Aparência > Barra Lateral > Navegação.", + "browserPickerHint": "Clique em Ligar e o seu navegador abrirá um seletor de porta para escolher o dispositivo." }, "metricsHistory": { - "title": "Histórico de métricas", - "historySuffix": "História", + "title": "Histórico de Métricas", + "historySuffix": "Histórico", "cpuMemoryDisk": "CPU / Memória / Disco", "network": "Rede", "download": "RX", "upload": "TX", - "noData": "Não existem dados históricos disponíveis para este período.", + "noData": "Sem dados de histórico disponíveis para este intervalo de tempo.", "custom": "Personalizado", - "to": "para", - "apply": "Candidatar-se", - "viewHistory": "Ver histórico" + "to": "a", + "apply": "Aplicar", + "viewHistory": "Ver Histórico" }, "alerts": { "tabFirings": "Alertas", "tabRules": "Regras", "tabChannels": "Canais", - "noFirings": "Nenhum alerta não reconhecido", + "noFirings": "Nenhum alerta por reconhecer", "noRules": "Nenhuma regra de alerta configurada", "noChannels": "Nenhum canal de notificação configurado", - "rulesDesc": "As regras de alerta acionam notificações.", - "channelsDesc": "Para onde são enviadas as notificações de alerta", + "rulesDesc": "As regras de alerta disparam notificações", + "channelsDesc": "Onde as notificações de alerta são enviadas", "acknowledge": "Reconhecer", - "ackAll": "Concordo com tudo", - "allAcknowledged": "Todos os alertas foram reconhecidos.", - "ackFailed": "Falha no reconhecimento do alerta", - "ackAllFailed": "Não reconheceu todos os alertas.", - "showAcknowledged": "Mostrar tudo", - "hideAcknowledged": "Esconder Acked", - "addChannel": "Adicionar canal", - "editChannel": "Editar canal", + "ackAll": "Reconhecer Todos", + "allAcknowledged": "Todos os alertas reconhecidos", + "ackFailed": "Falha ao reconhecer o alerta", + "ackAllFailed": "Falha ao reconhecer todos os alertas", + "showAcknowledged": "Mostrar Todos", + "hideAcknowledged": "Ocultar Reconhecidos", + "addChannel": "Adicionar Canal", + "editChannel": "Editar Canal", "channelName": "Nome", "channelType": "Tipo", - "channelNameRequired": "O nome é obrigatório.", + "channelNameRequired": "O nome é obrigatório", "webhookUrl": "URL", - "webhookUrlRequired": "O URL do webhook é obrigatório.", - "webhookDesc": "Envie um payload JSON para este URL a cada alerta disparado.", + "webhookUrlRequired": "O URL do webhook é obrigatório", + "webhookDesc": "Enviar payload JSON via POST para este URL em cada disparo do alerta", "ntfyServer": "URL do servidor", "ntfyTopic": "Tópico", - "ntfyTopicRequired": "É necessário definir o tema.", + "ntfyTopicRequired": "O tópico é obrigatório", "ntfyToken": "Token de acesso (opcional)", "channelSaveFailed": "Falha ao guardar o canal", - "test": "Teste", + "test": "Testar", "testSent": "Notificação de teste enviada", "testFailed": "Falha na notificação de teste", "addRule": "Adicionar regra de alerta", "editRule": "Editar regra de alerta", "ruleName": "Nome da regra", - "ruleNameRequired": "O nome é obrigatório.", + "ruleNameRequired": "O nome é obrigatório", "triggerType": "Gatilho", "thresholdValue": "Limiar (%)", "durationSeconds": "Duração (segundos, 0 = disparar imediatamente)", - "cooldownMinutes": "Tempo de arrefecimento (minutos)", + "cooldownMinutes": "Período de arrefecimento (minutos)", "channels": "Canais de notificação", - "noChannelsHint": "Adicione os canais primeiro no separador Canais.", + "noChannelsHint": "Adicione canais primeiro no separador Canais", "ruleSaveFailed": "Falha ao guardar a regra" } } diff --git a/src/ui/locales/translated/ro_RO.json b/src/ui/locales/translated/ro_RO.json index 319c87b5..8410c4ba 100644 --- a/src/ui/locales/translated/ro_RO.json +++ b/src/ui/locales/translated/ro_RO.json @@ -389,6 +389,45 @@ "noSavedServers": "Niciun server salvat", "removeServer": "Elimina" }, + "migrationNotice": { + "title": "Termix Desktop rulează acum independent", + "body1": "Această aplicație se conecta direct la serverul Termix. A fost reelaborată pentru a rula complet independent, stocând gazdele și acreditările local, astfel încât să funcționeze în continuare offline. Sincronizarea bidirecțională cu un server Termix auto-găzduit este acum opțională.", + "body2": "Gazdele și acreditările existente sunt încă pe {{url}}. Activați sincronizarea la distanță și reconectați-vă la serverul respectiv pentru a le reactiva și a le menține sincronizate de acum înainte.", + "dismiss": "Nu acum", + "setUpSync": "Configurați sincronizarea la distanță" + }, + "remoteSync": { + "title": "Sincronizare la distanță", + "description": "Opțional, conectați această aplicație desktop la un server Termix auto-găzduit pentru a sincroniza gazdele, acreditările și fragmentele de date pe toate dispozitivele. Aplicația funcționează întotdeauna complet offline, indiferent dacă vă conectați sau nu.", + "notConnected": "Neconectat", + "connected": "Conectat", + "connectedTo": "Conectat la {{url}}", + "lastSynced": "Ultima sincronizare {{time}}", + "neverSynced": "Nu s-a sincronizat niciodată", + "syncError": "Eroare de sincronizare: {{message}}", + "needsReauth": "Conectarea a expirat", + "connectButton": "Conectare la server", + "disconnectButton": "Deconecta", + "syncNowButton": "Sincronizează acum", + "syncing": "Se sincronizează...", + "serverUrl": "URL-ul serverului", + "enterServerUrl": "Introduceți adresa URL a serverului", + "mustIncludeProtocol": "URL-ul serverului trebuie să înceapă cu http:// sau https://", + "connectionTestFailed": "Nu s-a putut accesa un server Termix la adresa URL respectivă.", + "allowInvalidCertificate": "Permiteți certificatul nevalid", + "allowInvalidCertificateDesc": "A se utiliza numai pentru servere auto-găzduite de încredere, cu certificate autosemnate sau cu adresă IP.", + "savedServers": "Servere salvate", + "removeServer": "Elimina", + "continueButton": "Continua", + "cancelButton": "Anula", + "signInTitle": "Conectați-vă la {{url}}", + "originTitle": "Originea conexiunii", + "originDescription": "Alegeți în mod implicit de unde provin conexiunile SSH. Această opțiune poate fi ignorată pentru fiecare gazdă.", + "originLocal": "Acest dispozitiv (rețea locală)", + "originRemote": "Server la distanță", + "bannerReconnect": "Reconectați-vă", + "bannerMessage": "Sincronizarea la distanță necesită reautentificare" + }, "versionCheck": { "error": "Eroare la verificarea versiunii", "checkFailed": "Nu s-a putut verifica dacă există actualizări", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Suprascrierea acreditărilor de utilizator", "overrideCredentialUsernameDesc": "Folosește numele de utilizator specificat mai sus în locul numelui de utilizator al acreditării", "oidcUsernameHint": "Folosește $oidc.preferred_username pentru a înlocui numele de utilizator OIDC.", + "tailscaleUsernameHint": "Acesta trebuie să fie un utilizator Unix căruia îi este acordată identitatea Tailscale în ACL-ul SSH al rețelei tailnet, nu neapărat root.", "jumpHostChain": "Lanțul gazdă Jump", "portKnocking": "Lovitura în port", "addKnock": "Adăugați port", @@ -650,6 +690,11 @@ "delayAfterMs": "Întârziere după (ms)", "useSocks5Proxy": "Folosește proxy-ul SOCKS5", "useSocks5ProxyDesc": "Dirijați conexiunea printr-un server proxy", + "connectionOrigin": "Originea conexiunii", + "connectionOriginDesc": "De unde provine conexiunea SSH a acestei gazde. Suprascrie setarea globală implicită a aplicației desktop.", + "connectionOriginDefault": "Utilizați implicit", + "connectionOriginLocal": "Acest dispozitiv (rețea locală)", + "connectionOriginRemote": "Server la distanță", "proxyHost": "Gazdă proxy", "proxyPort": "Port proxy", "proxyUsername": "Nume de utilizator proxy", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Cian strălucitor", "customThemeBrightWhite": "Alb strălucitor", "customThemeResetTooltip": "Resetare la setările implicite", + "savedThemesLabel": "Teme salvate", + "saveAsGlobalTheme": "Salvați ca temă globală", + "saveGlobalThemeNamePrompt": "Introduceți un nume pentru această temă", + "saveGlobalThemeSuccess": "Tema a fost salvată", + "saveGlobalThemeError": "Nu s-a putut salva tema", + "applyGlobalThemeTooltip": "Aplică această temă", + "deleteGlobalThemeTooltip": "Ștergeți această temă", + "noSavedThemes": "Nicio temă salvată încă", "syntaxHighlightingLabel": "Evidențierea sintaxei", "syntaxHighlightingDesc": "Colorează ieșirea terminalului (erori, căi, IP-uri, timestamp-uri)", "syntaxHighlightingCategories": "Evidențiați categoriile", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Lansează sau atașează automat la sesiunea tmux", "enableSessionLogging": "Înregistrarea sesiunilor", "enableSessionLoggingDesc": "Înregistrați ieșirea sesiunii terminale pentru o revizuire ulterioară", + "allowSessionSharing": "Permiteți partajarea sesiunii", + "allowSessionSharingDesc": "Permiteți partajarea sesiunilor live de pe această gazdă prin link sau cu alți utilizatori", "enableCommandHistory": "Istoricul comenzilor", "enableCommandHistoryDesc": "Înregistrați comenzile executate în acest terminal pentru istoric și completare automată", "linkClickBehaviorLabel": "Comportamentul clicurilor pe link", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importat", "proxmoxResultUpdated": "{{count}} actualizat", "proxmoxResultFailed": "{{count}} a eșuat", + "proxmoxResultSkippedNoIp": "{{count}} omis (nu a fost găsită nicio adresă IP)", "proxmoxImportComplete": "Import Proxmox finalizat: {{summary}}", "proxmoxDiscoveryFailed": "Descoperirea a eșuat", "proxmoxImportFailed": "Importul a eșuat", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Nu s-a putut salva acreditările", "credentialNameRequired": "Vă rugăm să introduceți un nume pentru acreditare", "credentialAuthRequired": "Adăugați o parolă, o cheie SSH sau ambele", + "createCredentialFromHostBtn": "Creați acreditări", + "createCredentialFromHostTitle": "Creați acreditări de la gazdă", + "createCredentialFromHostDesc": "Creați o intrare de acreditări reutilizabilă și partajabilă, preumplută cu numele de utilizator, parola și/sau cheia SSH actuale ale acestei gazde.", "backToHosts": "Înapoi la Gazde", "backToCredentials": "Înapoi la acreditări", "pinned": "Fixat", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Folosește / pentru a separa niveluri și a crea foldere imbricate.", "folderColor": "Culoare", "folderIcon": "Pictogramă", + "folderCredential": "Acreditare", + "folderCredentialNone": "Nicio acreditare atribuită", + "folderCredentialHint": "Gazdele din acest folder care utilizează autentificarea „Credențiale stocate” fără a fi selectate propriile credențiale vor moșteni aceasta.", "folderPreview": "Previzualizare", "folderNameFallback": "Dosar fără titlu", "createFolderButton": "Creați un folder", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Etichete", "shareHost": "Gazdă partajată", "shareHostTitle": "Distribuie: {{name}}", + "shareFolder": "Partajați folderul", + "shareFolderTitle": "Partajați folderul: {{name}}", + "folderSharedSuccessfully": "Gazdă(e) {{count}} partajată(e) în folder", + "failedToShareFolder": "Nu s-a putut partaja folderul", "sharing": { "loadError": "Nu s-au putut încărca datele de partajare. Vă rugăm să încercați din nou.", "shareWithSection": "Distribuiți cu", @@ -1221,6 +1287,7 @@ "shareWithCount": "Distribuie ({{count}})", "currentAccess": "Acces curent", "noAccessEntries": "Această gazdă nu a fost încă partajată", + "folderShareSummary": "Gazdă(e) {{shared}} partajată din {{total}} în acest folder", "grantedBy": "Acordat de", "expires": "Expiră", "expired": "Expirat", @@ -1242,6 +1309,8 @@ "authMethod": "Metodă de autentificare", "authTypeDirect": "Direct", "authTypeCredential": "Acreditare", + "authTypeNone": "Nici unul", + "authTypeNoneDesc": "Nu sunt stocate acreditări. Vi se va solicita un nume de utilizator și o parolă de fiecare dată când vă conectați; acestea nu sunt salvate.", "selectCredential": "Selectați o acreditare...", "connectionSettings": "Setări de conectare", "displaySettings": "Setări de afișare", @@ -1259,6 +1328,7 @@ "rdpPort": "Port RDP", "username": "Nume de utilizator", "password": "Parolă", + "passwordSaved": "Parolă salvată, tastați pentru a o modifica", "domain": "Domeniu", "securityMode": "Mod de securitate", "colorDepth": "Adâncimea culorii", @@ -1424,7 +1494,60 @@ "expiresIn": "Expiră în {{duration}}", "search": "Căutați conexiuni...", "noSearchResults": "Nicio conexiune nu corespunde căutării dvs.", - "rename": "Redenumiți sesiunea" + "rename": "Redenumiți sesiunea", + "sectionSharedWithMe": "Distribuit cu mine", + "sharedBy": "Distribuit de {{username}}", + "join": "Alătură-te", + "sharedSessionLabel": "{{hostName}} (partajat)" + }, + "sessionSharing": { + "guestView": { + "loading": "Conectare la sesiunea partajată...", + "linkInvalid": "Acest link de partajare este invalid, a expirat sau a fost revocat.", + "rateLimited": "Prea multe încercări, vă rugăm să încercați din nou în scurt timp", + "sessionEnded": "Această sesiune s-a încheiat", + "readOnlyBadge": "Doar vizualizare" + }, + "modalTitle": "Partajați sesiunea", + "shareButton": "Distribuie", + "notReadyToShare": "Sesiunea nu este încă gata de partajare", + "modeTab": { + "link": "Legătură", + "user": "Utilizator" + }, + "linkModeDescription": "Oricine are acest link se poate alătura, nu este necesar un cont.", + "userModeDescription": "Partajați cu un anumit utilizator care are deja acces la această gazdă. Dacă nu are încă acces, partajați mai întâi gazda cu acesta sau utilizați un link în schimb. După partajare, sesiunea apare în fila Conexiuni.", + "permissionLevel": { + "label": "Nivel de permisiune", + "readOnly": "Doar citire", + "readOnlyDescription": "Pot urmări sesiunea în direct, dar nu pot tasta sau interacționa.", + "readWrite": "Citire-scriere", + "readWriteDescription": "Poate tasta și interacționa cu sesiunea la fel ca proprietarul." + }, + "expiryLabel": "Expirarea linkului", + "createLinkButton": "Creați un link", + "createShareButton": "Partajează cu utilizatorul", + "searchUsersPlaceholder": "Căutați utilizatori...", + "noUsersFound": "Nu au fost găsiți utilizatori", + "linkCreated": "Link de partajare creat", + "linkCopied": "Linkul a fost copiat în clipboard", + "copyLink": "Copiază linkul", + "shareCreated": "Sesiune partajată. Va apărea în fila Conexiuni.", + "shareFailed": "Nu s-a putut crea partajarea", + "userLacksHostAccess": "Utilizatorul respectiv nu are încă acces la această gazdă. Partajați mai întâi gazda cu acesta sau folosiți un link în schimb.", + "activeShares": "Acțiuni active", + "noActiveShares": "Nicio partajare activă pentru această sesiune", + "revoke": "Revoca", + "revokeConfirmTitle": "Revocați această partajare?", + "revokeConfirmDescription": "Oricine folosește această partajare va pierde accesul imediat.", + "revoked": "Acțiune revocată", + "revokeFailed": "Nu s-a putut revoca partajarea", + "joinCount": "{{count}} alătură-te", + "joinCount_other": "{{count}} se alătură", + "expiresAt": "Expiră la {{date}}", + "linkShareBadge": "Legătură", + "userShareBadge": "Utilizator: {{username}}", + "loadSharesFailed": "Nu s-au putut încărca partajările active" }, "guacamole": { "connecting": "Se conectează la sesiunea {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Reconectați-vă", "retry": "Reîncercați", "guacdUnavailable": "Serviciul desktop la distanță (guacd) nu este disponibil. Asigurați-vă că guacd rulează, este accesibil și configurat corect în setările de administrare.", + "credentialPromptTitle": "Introduceți acreditările RDP", + "credentialPromptDescription": "Această gazdă este configurată să solicite acreditări la conectare. Acestea sunt utilizate doar pentru această sesiune și nu sunt salvate.", + "connect": "Conectează-te", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Sesiune de reconectare", "collapse": "Restrângeți bara de instrumente", "expand": "Extindeți bara de instrumente", - "dragHandle": "Trageți pentru a repoziționa" + "dragHandle": "Trageți pentru a repoziționa", + "switchToTrackpad": "Comutare la modul trackpad (tragere pentru mutarea cursorului, atingere pentru clic)", + "switchToTouch": "Comutare la modul tactil (atingere direct unde doriți să faceți clic)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Autentificare cu doi factori necesară", "totpCodeLabel": "Cod de verificare", "totpVerify": "Verifica", + "mfaPromptRequired": "Autentificare necesară", + "mfaPushRequired": "Autentificare push necesară", + "mfaMenuPlaceholder": "Introduceți răspunsul dvs.", + "mfaWaitingApproval": "Se așteaptă aprobarea pe dispozitivul dvs....", + "mfaSendRequest": "Trimiteți cererea", "warpgateAuthRequired": "Autentificare Warpgate necesară", "warpgateSecurityKey": "Cheie de securitate", "warpgateAuthUrl": "URL de autentificare", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Rată limitată: Prea multe încercări de verificare. Vă rugăm să încercați din nou mai târziu.", "resetCodeRateLimitedWithTime": "Rată limitată: Prea multe încercări de verificare. Vă rugăm să așteptați {{time}} secunde înainte de a încerca din nou.", "authTokenSaveFailed": "Nu s-a putut salva jetonul de autentificare", - "failedToLoadServer": "Nu s-a putut încărca serverul" + "failedToLoadServer": "Nu s-a putut încărca serverul", + "remoteServerRequired": "Este necesar un server la distanță. Conectați un server la distanță în Setări pentru a utiliza acest tip de conexiune." }, "messages": { "registrationDisabled": "Înregistrarea unui cont nou este dezactivată în prezent de un administrator. Vă rugăm să vă conectați sau să contactați un administrator.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Setările SSL nu au putut fi salvate.", "sslRequiresDomain": "Domeniul și adresa de e-mail sunt obligatorii", "sslInfoNote": "După emiterea unui certificat, activați SSL în variabilele de mediu (ENABLE_SSL=true) și reporniți Termix.", + "sslManualOption": "Manual (încărcare certificat)", + "sslManualCert": "Certificat (PEM)", + "sslManualCertPlaceholder": "-----ÎNCEPERE CERTIFICAT-----", + "sslManualKey": "Cheie privată (PEM)", + "sslManualKeyPlaceholder": "-----ÎNCEPERE CHEIE PRIVATĂ-----", + "sslManualDesc": "Lipiți certificatul și cheia privată existente, inclusiv un lanț complet, dacă este necesar de către autoritatea de certificare.", + "sslManualUpload": "Încărcați și instalați certificatul", + "sslManualUploadLoading": "Se încarcă certificatul...", + "sslManualUploadSuccess": "Certificatul a fost încărcat și instalat cu succes", + "sslManualUploadFailed": "Încărcarea certificatului a eșuat", + "sslManualRequiresFields": "Certificatul și cheia privată sunt necesare", "auditLogTotal": "{{total}} total intrări", "auditLogEmpty": "Nu au fost găsite intrări în jurnalul de audit", "auditLogSuccess": "Succes", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Istoricul comenzilor", "commandHistoryEnabledDesc": "Permite înregistrarea istoricului comenzilor. Când este dezactivată, istoricul nu este salvat, indiferent de setările pentru fiecare gazdă.", "updateCommandHistoryFailed": "Nu s-a putut actualiza setarea istoricului comenzilor", + "analyticsEnabled": "Distribuiți statistici de utilizare anonime", + "analyticsEnabledDesc": "Trimite un număr zilnic anonim de utilizatori, gazde și utilizare a funcțiilor pentru a ajuta la îmbunătățirea Termix. Nu sunt incluse niciodată date personale sau detalii de conexiune.", + "updateAnalyticsFailed": "Setările de analiză nu au putut fi actualizate", + "sessionSharingGloballyEnabled": "Permiteți partajarea sesiunii", + "sessionSharingGloballyEnabledDesc": "Permite partajarea sesiunilor live de terminal, RDP, VNC și Telnet la nivel de instanță. Suprascrie fiecare comutare de partajare per gazdă atunci când este dezactivată.", + "updateSessionSharingFailed": "Setările de partajare a sesiunii nu au putut fi actualizate.", "sessionTimeout": "Expirare sesiune", "hours": "ore", "sessionTimeoutRange": "Min 1 oră · Max 720 ore", @@ -3119,6 +3270,47 @@ "executionSuccess": "Succes", "executionFailed": "Eșuat" }, + "keybindings": { + "title": "Comenzi rapide de la tastatură", + "description": "Personalizați comenzile rapide de copiere, lipire și control din terminal sau asociați taste pentru a trimite text sau a rula un fragment de cod.", + "defaultsHeading": "Comenzi rapide încorporate", + "customHeading": "Comenzi rapide personalizate", + "addBinding": "Adăugați o comandă rapidă", + "addBindingTitle": "Adăugați o comandă rapidă", + "editBindingTitle": "Editare comandă rapidă", + "loading": "Încărcare...", + "noCustomBindings": "Încă nu există comenzi rapide personalizate", + "defaultBadge": "Implicit", + "customizedBadge": "Personalizat", + "customize": "Personalizare", + "resetToDefault": "Resetare la valorile implicite", + "resetAllToDefaults": "Resetați toate la valorile implicite", + "close": "Aproape", + "cancel": "Anula", + "saveBinding": "Salvați comanda rapidă", + "saveError": "Nu s-au putut salva comenzile rapide de la tastatură", + "pressKeysToRecord": "Combinație de taste", + "pressKeysPlaceholder": "Faceți clic și apăsați tastele", + "recording": "Apăsați tastele...", + "comboRequiredError": "Apăsați mai întâi o combinație de taste", + "textRequiredError": "Introduceți textul de trimis", + "controlCodeRequiredError": "Introduceți o singură literă pentru codul de control", + "snippetRequiredError": "Selectați un fragment", + "conflictWarning": "Această combinație este deja legată de {{combo}}. Salvarea va face ca ambele să se declanșeze.", + "actionLabel": "Acţiune", + "actionCopy": "Copiați selecția", + "actionPaste": "Pastă", + "actionSendControlCode": "Trimite semnal Ctrl+literă", + "actionSendText": "Trimiteți mesaj text", + "actionRunSnippet": "Execută fragmentul existent", + "controlCodeLabel": "Scrisoare", + "textLabel": "Text de trimis", + "appendEnterLabel": "Apăsați Enter după trimitere", + "snippetLabel": "Fragment", + "selectSnippetPlaceholder": "Selectați un fragment", + "orphanedSnippetWarning": "fragmentul nu a fost găsit", + "clipboardPermissionNote": "În unele browsere, este posibil să solicite permisiunea pentru clipboard la prima utilizare." + }, "userProfile": { "donateTitle": "Menține Termix în viață", "donateDescription": "Termix este gratuit, găzduit în regim self-hosting și construit de doar câțiva oameni în timpul lor liber. Dacă ți-a economisit timp sau bani, o donație în criptomonede ajută la menținerea activității.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Aspect", "sectionSecurity": "Securitate", "sectionApiKeys": "Chei API", + "sectionData": "Date", "sectionC2sTunnels": "Tuneluri C2S", "usernameLabel": "Nume de utilizator", "roleLabel": "Rol", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Completare automată a comenzilor", "commandAutocompleteDesc": "Afișează completarea automată în timpul tastării", + "keyboardShortcuts": "Comenzi rapide de la tastatură", + "keyboardShortcutsDescription": "Personalizați comenzile rapide pentru copiere, lipire și comenzi în terminal", + "manageShortcuts": "Gestionează", "terminalLinkBehavior": "Clic pe linkul terminalului", "terminalLinkBehaviorDesc": "Comportament implicit la clic pe linkuri în terminal", "historyTracking": "Urmărirea istoricului", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Includeți cheia dvs. în", "apiKeyUsageHintHeader": "antet.", "apiKeyPermissionsHint": "Cheile moștenesc permisiunile utilizatorului care le creează.", + "exportData": "Exportați datele mele", + "exportDataDesc": "Descărcați o copie de rezervă a gazdelor, acreditărilor și setărilor pentru a o transfera pe un alt dispozitiv", + "export": "Export", + "exporting": "Exportator...", + "importData": "Importați datele mele", + "importDataDesc": "Restaurați gazdele, acreditările și setările dintr-un fișier de rezervă .sqlite", + "importDataSelected": "Selectat: {{name}}", + "selectFile": "Selectați fișierul", + "changeFile": "Schimba", + "import": "Import", + "importing": "Importare...", + "exportSuccess": "Datele au fost exportate cu succes", + "exportFailed": "Exportul datelor 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": "Importul a eșuat: {{error}}", "roleUser": "Utilizator", "authMethodDual": "Autentificare duală", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PagUp", "pageDown": "Pagină următoare", "delete": "Șterge", + "paste": "Pastă", "editQuickKeys": "Editați tastele rapide", "quickKeysTitle": "Taste rapide", "quickKeysDesc": "Atingeți × pentru a elimina. Acceptă până la 8 caractere.", diff --git a/src/ui/locales/translated/ru_RU.json b/src/ui/locales/translated/ru_RU.json index 05c15e93..e5c724f0 100644 --- a/src/ui/locales/translated/ru_RU.json +++ b/src/ui/locales/translated/ru_RU.json @@ -2,103 +2,103 @@ "termixId": { "title": "Termix ID", "loadFailed": "Не удалось загрузить Termix ID", - "claimTitle": "Получить Termix ID", - "claimIntro": "Выберите уникальный идентификатор. Ваши открытые SSH-ключи будут опубликованы по общедоступному URL-адресу, который можно добавить в файл authorized_keys любого сервера.", + "claimTitle": "Зарегистрируйте свой Termix ID", + "claimIntro": "Выберите уникальный идентификатор. Ваши открытые ключи SSH будут опубликованы по общедоступному URL, который вы сможете добавить в файл authorized_keys на любом сервере.", "handleLabel": "Идентификатор", "handlePlaceholder": "alice", "checking": "Проверка…", "available": "Доступно", - "taken": "Уже занят", + "taken": "Уже занято", "invalidHandle": "Только строчные буквы, цифры, - и _", "descriptionLabel": "Описание (необязательно)", - "descriptionPlaceholder": "SSH-ключи рабочего ноутбука и телефона", + "descriptionPlaceholder": "Ключи рабочего ноутбука и телефона", "create": "Создать Termix ID", "created": "Termix ID создан", "createFailed": "Не удалось создать Termix ID", - "deleteConfirm": "Удалить ваш Termix ID и все опубликованные ключи? Серверы, на которые вы добавили ключи, сохранят их, пока вы не удалите их вручную.", - "deleted": "Termix ID удален", + "deleteConfirm": "Удалить свой Termix ID и все опубликованные ключи? Серверы, которые вы настроили, сохранят ключи, пока вы не удалите их вручную.", + "deleted": "Termix ID удалён", "deleteFailed": "Не удалось удалить Termix ID", - "copyFailed": "Ошибка копирования", - "resolverUrlLabel": "Публичный URL-адрес резолвера", - "provisionLabel": "Подготовка сервера", + "copyFailed": "Не удалось скопировать", + "resolverUrlLabel": "Общедоступный URL резолвера", + "provisionLabel": "Настроить сервер", "publishTitle": "Опубликовать открытый ключ", "generate": "Сгенерировать", - "generateTooltip": "Сгенерируйте пару ключей Ed25519 — опубликуйте открытый ключ и загрузите закрытый ключ на ваше устройство.", - "saveToVault": "Сохранить в учетные данные", + "generateTooltip": "Сгенерировать пару ключей Ed25519 — публикует открытый ключ и загружает закрытый ключ на ваше устройство", + "saveToVault": "Сохранить в учётные данные", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Метка (необязательно)", "add": "Добавить", - "importFromCredential": "Или импортируйте данные из сохраненных учетных данных.", + "importFromCredential": "Или импортировать из сохранённых учётных данных", "keyPublished": "Ключ опубликован", - "addKeyFailed": "Не удалось добавить ключ.", - "generatedSaved": "Пара ключей сгенерирована и сохранена в хранилище ваших учетных данных. Закрытый ключ также загружен.", + "addKeyFailed": "Не удалось добавить ключ", + "generatedSaved": "Пара ключей сгенерирована и сохранена в хранилище учётных данных. Закрытый ключ также загружен.", "generatedOnly": "Пара ключей сгенерирована — закрытый ключ загружен (отображается только один раз).", - "generateFailed": "Не удалось сгенерировать ключ.", - "imported": "Ключ импортирован из учетных данных.", - "importFailed": "Не удалось импортировать ключ.", - "noKeys": "Открытые ключи пока не опубликованы.", + "generateFailed": "Не удалось сгенерировать ключ", + "imported": "Ключ импортирован из учётных данных", + "importFailed": "Не удалось импортировать ключ", + "noKeys": "Нет опубликованных открытых ключей.", "keysTitle": "Опубликованные ключи", - "published": "Опубликовано", - "hidden": "Скрытый", + "published": "Опубликован", + "hidden": "Скрыт", "keyRemoved": "Ключ удалён", "removeKeyFailed": "Не удалось удалить ключ", - "updateKeyFailed": "Не удалось обновить ключ.", - "fromVault": "Из хранилища учетных данных", + "updateKeyFailed": "Не удалось обновить ключ", + "fromVault": "Из хранилища учётных данных", "linkedToTermixId": "Опубликовано через Termix ID", - "selectCredential": "Выберите учетные данные...", - "import": "Импорт", + "selectCredential": "Выберите учётные данные…", + "import": "Импортировать", "caTitle": "Центр сертификации", - "caIntro": "Доверьте этот CA-сертификат серверу, и он будет принимать любые сертификаты, подписанные вами. Выполните ротацию, чтобы сразу отозвать все сертификаты; кроме того, сертификаты автоматически истекают.", - "caEnable": "Включить CA", - "caEnabled": "CA включен", - "caCreateFailed": "Не удалось включить CA.", - "caPublicKeyLabel": "Открытый ключ CA", - "caTrustLabel": "Доверьтесь серверу (запустите его от имени root).", - "caRotate": "Сменить", - "caRotateConfirm": "Если вы хотите сменить центр сертификации (ЦС), все подписанные им сертификаты перестанут приниматься и должны быть перевыпущены.", - "caRotated": "Центр сертификации был перезапущен — предыдущие сертификаты отозваны.", - "caRotateFailed": "Не удалось повернуть CA", - "caDelete": "Удалить CA", + "caIntro": "Доверьте этот центр сертификации на сервере, и он будет принимать любые сертификаты, подписанные вами. Ротация мгновенно отзывает все сертификаты; также сертификаты истекают автоматически.", + "caEnable": "Включить ЦС", + "caEnabled": "ЦС включён", + "caCreateFailed": "Не удалось включить ЦС", + "caPublicKeyLabel": "Открытый ключ ЦС", + "caTrustLabel": "Доверие на сервере (запустите от root)", + "caRotate": "Ротировать", + "caRotateConfirm": "Ротировать ЦС? Все выданные им сертификаты перестанут приниматься и потребуют перевыпуска.", + "caRotated": "ЦС ротирован — предыдущие сертификаты отозваны", + "caRotateFailed": "Не удалось ротировать ЦС", + "caDelete": "Удалить ЦС", "caDeleteConfirm": "Удалить центр сертификации?", - "caDeleted": "CA удален", - "caDeleteFailed": "Не удалось удалить CA", - "caValidityLabel": "Срок действия сертификата (в днях)", - "issueCert": "Выдать сертификат", - "issueCertTooltip": "Выдайте для этого ключа SSH-сертификат, подписанный вашим центром сертификации.", - "certIssued": "Сертификат выдан и загружен.", - "certIssueFailed": "Не удалось выдать сертификат." + "caDeleted": "ЦС удалён", + "caDeleteFailed": "Не удалось удалить ЦС", + "caValidityLabel": "Срок действия сертификата (дней)", + "issueCert": "Сертификат", + "issueCertTooltip": "Выпустить SSH-сертификат для этого ключа, подписанный вашим ЦС", + "certIssued": "Сертификат выпущен и загружен", + "certIssueFailed": "Не удалось выпустить сертификат" }, "credentials": { "folders": "Папки", "folder": "Папка", "password": "Пароль", "key": "Ключ", - "sshPrivateKey": "Закрытый ключ SSH", + "sshPrivateKey": "SSH-приватный ключ", "upload": "Загрузить", "keyPassword": "Пароль ключа", - "sshKey": "Ключ SSH", - "uploadPrivateKeyFile": "Загрузите файл закрытого ключа.", - "searchCredentials": "Поиск учетных данных...", - "addCredential": "Добавить учетные данные", - "caCertificate": "Сертификат CA (-cert.pub)", - "caCertificateDescription": "Необязательно: загрузите или вставьте файл сертификата, подписанного центром сертификации (например, id_ed25519-cert.pub). Обязательно, если ваш SSH-сервер использует авторизацию на основе сертификатов.", - "uploadCertFile": "Загрузите файл -cert.pub", + "sshKey": "SSH-ключ", + "uploadPrivateKeyFile": "Загрузить файл приватного ключа", + "searchCredentials": "Поиск учётных данных...", + "addCredential": "Добавить учётные данные", + "caCertificate": "CA-сертификат (-cert.pub)", + "caCertificateDescription": "Необязательно: загрузите или вставьте файл сертификата, подписанного CA (например, id_ed25519-cert.pub). Требуется, если ваш SSH-сервер использует авторизацию на основе сертификатов.", + "uploadCertFile": "Загрузить файл -cert.pub", "clearCert": "Очистить", "certLoaded": "Сертификат загружен", - "certPublicKeyLabel": "Сертификат CA", + "certPublicKeyLabel": "CA-сертификат", "certTypeLabel": "Тип сертификата", - "pasteOrUploadCert": "Вставьте или загрузите сертификат из файла -cert.pub...", - "hasCaCert": "Имеет сертификат CA.", - "noCaCert": "Сертификат CA не требуется.", - "noPublicKeyAvailable": "Открытый ключ недоступен. Сначала откройте редактор учетных данных.", - "deployCommandCopied": "Команда развертывания скопирована.", - "sortCredentials": "Сортировка учетных данных", + "pasteOrUploadCert": "Вставьте или загрузите сертификат -cert.pub...", + "hasCaCert": "Имеет CA-сертификат", + "noCaCert": "Нет CA-сертификата", + "noPublicKeyAvailable": "Публичный ключ недоступен. Сначала откройте редактор учётных данных.", + "deployCommandCopied": "Команда для развёртывания скопирована", + "sortCredentials": "Сортировка учётных данных", "sortDefault": "Порядок по умолчанию", - "sortNameAsc": "Имя (от А до Я)", - "sortNameDesc": "Имя (Z → A)", - "sortUsernameAsc": "Имя пользователя (от А до Я)", - "sortUsernameDesc": "Имя пользователя (Z → A)", - "filterCredentials": "Фильтрация учетных данных", + "sortNameAsc": "Имя (А → Я)", + "sortNameDesc": "Имя (Я → А)", + "sortUsernameAsc": "Имя пользователя (А → Я)", + "sortUsernameDesc": "Имя пользователя (Я → А)", + "filterCredentials": "Фильтр учётных данных", "filterClearAll": "Очистить фильтры", "filterTypeGroup": "Тип", "filterTypePassword": "Пароль", @@ -108,83 +108,83 @@ "homepage": { "title": "Главная страница", "addWidget": "Добавить виджет", - "editWidget": "Редактировать виджет", + "editWidget": "Изменить виджет", "deleteWidget": "Удалить", "widgetTypes": "Типы виджетов", - "serviceLink": "Сервисная ссылка", + "serviceLink": "Ссылка на сервис", "folder": "Папка", "clock": "Часы", - "notes": "Примечания", + "notes": "Заметки", "hostStatus": "Статус хоста", "bookmarkList": "Закладки", - "zoomIn": "Увеличить масштаб", - "zoomOut": "Уменьшить масштаб", + "zoomIn": "Приблизить", + "zoomOut": "Отдалить", "resetView": "Сбросить вид", - "lockLayout": "Закрепить макет", + "lockLayout": "Заблокировать макет", "unlockLayout": "Разблокировать макет", - "noWidgets": "Щелкните правой кнопкой мыши или нажмите +, чтобы добавить первый виджет.", - "openFullView": "Открыть полный просмотр", - "previewTitle": "Предварительный просмотр главной страницы", + "noWidgets": "Нажмите правой кнопкой мыши или на +, чтобы добавить первый виджет", + "openFullView": "Открыть полный вид", + "previewTitle": "Предпросмотр домашней страницы", "cancel": "Отмена", "save": "Сохранить", - "title_label": "Заголовок", - "widgetTitlePlaceholder": "Заголовок виджета (необязательно)", + "title_label": "Название", + "widgetTitlePlaceholder": "Название виджета (необязательно)", "url": "URL", "imageUrl": "Пользовательский URL изображения", - "imageUrlHint": "Оставьте поле пустым, чтобы значок сайта использовался автоматически.", - "showImage": "Показать изображение", + "imageUrlHint": "Оставьте пустым, чтобы автоматически использовать favicon сайта", + "showImage": "Показывать изображение", "description": "Описание", "color": "Цвет", "icon": "Значок", "expanded": "Развернуто по умолчанию", "timezone": "Часовой пояс", - "showSeconds": "Показать секунды", + "showSeconds": "Показывать секунды", "format12h": "12-часовой", - "format24h": "24 часа", - "content": "Содержание", + "format24h": "24-часовой", + "content": "Содержимое", "backgroundColor": "Цвет фона", "host": "Хост", "showMetrics": "Показать метрики", "links": "Ссылки", "addLink": "Добавить ссылку", - "linkLabel": "Этикетка", + "linkLabel": "Название", "linkUrl": "URL", "removeLink": "Удалить", "categoryLinks": "Ссылки", "categoryInfo": "Информация", "categorySystem": "Система", - "widgetServiceLinkDesc": "Интерактивная плитка, ведущая на URL-адрес сервиса.", - "widgetFolderDesc": "Контейнер для группировки связанных виджетов.", - "widgetClockDesc": "Часы в реальном времени с настраиваемым часовым поясом.", + "widgetServiceLinkDesc": "Кликабельная плитка, ведущая на URL сервиса", + "widgetFolderDesc": "Контейнер для группировки связанных виджетов", + "widgetClockDesc": "Часы с настраиваемым часовым поясом", "widgetNotesDesc": "Виджет для заметок в формате Markdown", - "widgetHostStatusDesc": "Отображает текущее состояние ЦП, памяти и дискового пространства для SSH-хоста.", + "widgetHostStatusDesc": "Показывает текущую загрузку ЦП, памяти и диска для SSH-хоста", "widgetBookmarkListDesc": "Список быстрых ссылок", - "copyLink": "Скопировать ссылку", + "copyLink": "Копировать ссылку", "linkCopied": "Ссылка скопирована!", - "location": "Расположение", - "temperatureUnit": "Единица измерения температуры", - "showForecast": "Показать прогноз на 3 дня", + "location": "Местоположение", + "temperatureUnit": "Единица температуры", + "showForecast": "Показывать прогноз на 3 дня", "scrolling": "Разрешить прокрутку", "feedUrl": "URL ленты", - "maxItems": "Максимальное количество объектов", - "showDescription": "Показать описание", + "maxItems": "Макс. элементов", + "showDescription": "Показывать описание", "widgetWeatherName": "Погода", - "widgetWeatherDesc": "Актуальная информация о погоде в любом месте.", + "widgetWeatherDesc": "Актуальная погода для любого местоположения", "widgetIframeName": "Встраивание iFrame", - "widgetIframeDesc": "Встраивайте любой URL-адрес в iframe.", + "widgetIframeDesc": "Встраивает любой URL в iframe", "widgetRssName": "RSS-лента", - "widgetRssDesc": "Отображение элементов из RSS- или Atom-ленты.", - "dragToFolder": "Перетащите виджеты сюда или используйте + для добавления.", - "showDisk": "Показать использование диска", + "widgetRssDesc": "Отображает элементы из ленты RSS или Atom", + "dragToFolder": "Перетащите виджеты сюда или нажмите +, чтобы добавить", + "showDisk": "Показывать использование диска", "addToFolder": "Добавить виджет в папку", "noHostSelected": "Хост не выбран", - "metricsNotAvailable": "Показатели недоступны", + "metricsNotAvailable": "Метрики недоступны", "selectHost": "Выберите хост...", "displayedMetrics": "Отображаемые метрики", - "metricCpu": "Процессор", + "metricCpu": "ЦП", "metricMemory": "Память", "metricDisk": "Диск", - "metricUptime": "Время безотказной работы", + "metricUptime": "Время работы", "metricSystem": "Система", "metricOs": "ОС", "metricKernel": "Ядро", @@ -195,138 +195,138 @@ "metricProcessesRunning": "запущено", "categoryMonitoring": "Мониторинг", "loading": "Загрузка...", - "noData": "Данные отсутствуют.", - "allClear": "Все чисто", + "noData": "Нет данных", + "allClear": "Всё в порядке", "acknowledgeAlert": "Подтвердить", "noPingUrls": "URL-адреса не настроены", - "pingLabel": "Этикетка", + "pingLabel": "Метка", "addPingUrl": "Добавить URL", "showLatency": "Показать задержку", "refreshInterval": "Интервал обновления", "seconds": "секунд", - "filterActivityTypes": "Типы фильтров", - "filterTypesHint": "Оставьте поле пустым, чтобы отобразить все", - "showTimestamp": "Показать временную метку", + "filterActivityTypes": "Типы фильтрации", + "filterTypesHint": "Оставьте пустым, чтобы показать все", + "showTimestamp": "Показывать метку времени", "uptimeUnavailable": "Время работы недоступно", - "uptimeLabel": "Время безотказной работы", + "uptimeLabel": "Время работы", "overviewVersion": "Версия", - "overviewUpdate": "До настоящего времени", + "overviewUpdate": "Актуально", "overviewUpdateAvailable": "Доступно обновление", "overviewDatabase": "База данных", - "overviewUptime": "Время безотказной работы", - "noHosts": "Хосты не настроены", + "overviewUptime": "Время работы", + "noHosts": "Нет настроенных хостов", "hostGridHosts": "Хосты", - "hostGridAllHint": "Оставьте поле пустым, чтобы отобразить все хосты.", + "hostGridAllHint": "Оставьте пустым, чтобы показать все хосты", "columns": "Колонки", "showIp": "Показать IP-адрес", "connectionType": "Тип подключения", "layout": "Макет", "showStatus": "Показать статус", "showHostName": "Показать имя хоста", - "noDockerActivity": "Нет активности Docker.", + "noDockerActivity": "Нет активности Docker", "noActivity": "Нет активности", - "showAcknowledged": "Показать подтверждено", - "showCurrentValue": "Показать текущую стоимость", + "showAcknowledged": "Показать подтверждённые", + "showCurrentValue": "Показать текущее значение", "chartMetric": "Метрика", "metricRange": "Диапазон", - "widgetMetricsChartName": "Диаграмма показателей", - "widgetMetricsChartDesc": "Историческая диаграмма использования ЦП, памяти, диска или сети для хоста.", + "widgetMetricsChartName": "График метрик", + "widgetMetricsChartDesc": "График истории использования ЦП, памяти, диска или сети для хоста", "widgetHostGridName": "Сетка хостов", - "widgetHostGridDesc": "Табличное отображение статусов SSH-хостов", + "widgetHostGridDesc": "Представление статусов хостов SSH в виде сетки", "widgetAlertFeedName": "Лента оповещений", - "widgetAlertFeedDesc": "Лента оповещений в реальном времени с возможностью подтверждения", - "widgetPingStatusName": "Статус пинга", - "widgetPingStatusDesc": "Статус HTTP-пинга для одного или нескольких URL-адресов", + "widgetAlertFeedDesc": "Живые срабатывания оповещений с возможностью подтверждения", + "widgetPingStatusName": "Статус ping", + "widgetPingStatusDesc": "Статус HTTP ping для одного или нескольких URL", "widgetRecentActivityName": "Недавняя активность", "widgetRecentActivityDesc": "Прокручиваемая лента недавней активности Termix", - "widgetTermixUptimeName": "Termix Uptime", - "widgetTermixUptimeDesc": "Счетчик времени безотказной работы сервера Termix в реальном времени", + "widgetTermixUptimeName": "Время работы Termix", + "widgetTermixUptimeDesc": "Счётчик времени работы сервера Termix в реальном времени", "widgetSystemOverviewName": "Обзор системы", - "widgetSystemOverviewDesc": "Краткий обзор версии Termix, состояния базы данных и времени безотказной работы.", + "widgetSystemOverviewDesc": "Краткий обзор: версия Termix, состояние базы данных и время работы", "widgetSshQuickConnectName": "Быстрое подключение по SSH", - "widgetSshQuickConnectDesc": "Кнопки для открытия SSH-сессий одним нажатием", - "widgetDockerActivityName": "Docker Activity", - "widgetDockerActivityDesc": "Последние события Docker-контейнеров на всех хостах", + "widgetSshQuickConnectDesc": "Кнопки для открытия сеансов SSH в один клик", + "widgetDockerActivityName": "Активность Docker", + "widgetDockerActivityDesc": "Недавние события контейнеров Docker на всех хостах", "widgetCalendarName": "Календарь", - "widgetCalendarDesc": "Ежемесячный календарь с выделенным сегодняшним днем.", - "widgetCountdownName": "Обратный отсчет", - "widgetCountdownDesc": "Таймер обратного отсчета до целевой даты", + "widgetCalendarDesc": "Ежемесячный календарь с выделенным сегодняшним днём", + "widgetCountdownName": "Обратный отсчёт", + "widgetCountdownDesc": "Таймер обратного отсчёта до целевой даты", "widgetSearchBarName": "Строка поиска", "widgetSearchBarDesc": "Виджет быстрого веб-поиска", "widgetTextBannerName": "Текстовый баннер", - "widgetTextBannerDesc": "Выделенный заголовок или заголовок раздела для холста.", + "widgetTextBannerDesc": "Полужирная надпись или заголовок раздела для холста", "widgetImageWidgetName": "Изображение", - "widgetImageWidgetDesc": "Отобразить изображение по URL-адресу", - "widgetMarkdownNotesName": "Заметки по разметке Markdown", - "widgetMarkdownNotesDesc": "Форматированные заметки с поддержкой встроенной Markdown-разметки", + "widgetImageWidgetDesc": "Отображать изображение по URL", + "widgetMarkdownNotesName": "Заметки Markdown", + "widgetMarkdownNotesDesc": "Расширенные заметки со встроенным рендерингом Markdown", "widgetCustomApiName": "Пользовательский API", - "widgetCustomApiDesc": "Получение и отображение данных из любого JSON API", - "widgetServiceGridName": "Сервисная сеть", - "widgetServiceGridDesc": "Настраиваемая сетка ссылок на сервисные плитки", - "widgetDashboardLinksName": "Ссылки на панели управления", - "widgetDashboardLinksDesc": "Отобразите настроенные вами ссылки на сервисы на панели управления.", - "widgetSearchLinksName": "Ярлыки поиска", + "widgetCustomApiDesc": "Получать и отображать данные из любого JSON API", + "widgetServiceGridName": "Сетка сервисов", + "widgetServiceGridDesc": "Настраиваемая сетка плиток-ссылок на сервисы", + "widgetDashboardLinksName": "Ссылки панели управления", + "widgetDashboardLinksDesc": "Отображение настроенных ссылок на сервисы с панели управления", + "widgetSearchLinksName": "Поисковые ярлыки", "widgetSearchLinksDesc": "Кнопки быстрого поиска со встроенным полем ввода", "widgetLinkTreeName": "Дерево ссылок", "widgetLinkTreeDesc": "Сгруппированные разделы ссылок с заголовками", - "calMon": "Мо", - "calTue": "Ту", - "calWed": "Мы", - "calThu": "Т", - "calFri": "Фр", - "calSat": "Са", - "calSun": "Су", - "startOnMonday": "Начинайте неделю в понедельник", - "countdownNoDate": "Целевая дата не установлена.", - "countdownPast": "Событие прошло.", - "countdownDays": "дни", - "countdownHours": "часов", + "calMon": "Пн", + "calTue": "Вт", + "calWed": "Ср", + "calThu": "Чт", + "calFri": "Пт", + "calSat": "Сб", + "calSun": "Вс", + "startOnMonday": "Начинать неделю с понедельника", + "countdownNoDate": "Целевая дата не задана", + "countdownPast": "Событие прошло", + "countdownDays": "дн.", + "countdownHours": "ч", "countdownMinutes": "мин", - "countdownSeconds": "сек", - "countdownLabel": "Этикетка", - "countdownLabelPlaceholder": "например, день запуска", - "countdownShowDays": "Показывать дни", - "countdownShowHours": "Показывать часы", + "countdownSeconds": "с", + "countdownLabel": "Название", + "countdownLabelPlaceholder": "например, День запуска", + "countdownShowDays": "Показать дни", + "countdownShowHours": "Показать часы", "targetDate": "Целевая дата", "searchEngine": "Поисковая система", - "customSearchUrl": "Пользовательский URL-адрес поиска", + "customSearchUrl": "Пользовательский URL поиска", "searchPlaceholder": "Поиск...", - "searchGo": "Перейти", - "searchPlaceholderLabel": "Текст-заполнитель", - "searchPlaceholderHint": "Текст, отображаемый внутри поля поиска.", - "openInNewTab": "Открыть в новой вкладке", + "searchGo": "Искать", + "searchPlaceholderLabel": "Текст подсказки", + "searchPlaceholderHint": "Текст внутри поля поиска", + "openInNewTab": "Открывать в новой вкладке", "searchQueryPlaceholder": "Введите запрос...", - "noSearchShortcuts": "Сочетания клавиш не настроены", + "noSearchShortcuts": "Нет ярлыков поиска", "addSearchShortcut": "Добавить ярлык", "fontSize": "Размер шрифта", "textAlign": "Выравнивание текста", - "fontWeight": "Толщина шрифта", - "clearColor": "Чистый цвет", - "imageFit": "Соответствие образу", - "imageLinkUrl": "URL-адрес ссылки", - "noImage": "URL изображения не указан", + "fontWeight": "Жирность шрифта", + "clearColor": "Очистить цвет", + "imageFit": "Вписывание изображения", + "imageLinkUrl": "URL ссылки", + "noImage": "URL изображения не задан", "altText": "Альтернативный текст", "altTextPlaceholder": "Опишите изображение", - "renderMarkdown": "Отобразить Markdown", + "renderMarkdown": "Отображать Markdown", "displayMode": "Режим отображения", - "displayField": "Поле отображения", + "displayField": "Отображаемое поле", "jsonPath": "Путь JSON", - "customApiLabel": "Этикетка", - "customApiLabelPlaceholder": "например, температура", - "customApiUnit": "Единица", + "customApiLabel": "Название", + "customApiLabelPlaceholder": "например, Температура", + "customApiUnit": "Единица измерения", "customApiNoUrl": "URL API не настроен", - "customApiError": "Не удалось получить данные.", - "customApiNotArray": "Ответ не является массивом.", - "addService": "Добавить услугу", - "showLabels": "Показать метки", - "iconSize": "Размер значка", - "noDashboardLinks": "Ссылки на панели управления не настроены", - "noLimit": "Без ограничений", + "customApiError": "Не удалось загрузить данные", + "customApiNotArray": "Ответ не является массивом", + "addService": "Добавить сервис", + "showLabels": "Показывать метки", + "iconSize": "Размер иконок", + "noDashboardLinks": "Нет настроенных ссылок на панель", + "noLimit": "Без лимита", "sectionHeading": "Заголовок раздела", "addSection": "Добавить раздел", "compactMode": "Компактный режим", "showDetailed": "Показать подробности", - "selectHosts": "Выберите хосты", + "selectHosts": "Выбрать хосты", "allHosts": "Все хосты", "listLayout": "Список", "gridLayout": "Сетка", @@ -337,19 +337,19 @@ "range1h": "1 час", "range6h": "6 часов", "range24h": "24 часа", - "metricNetRx": "Загрузка из сети", - "metricNetTx": "Загрузка в сеть", - "severityFilter": "Фильтр серьезности", + "metricNetRx": "Входящий", + "metricNetTx": "Исходящий", + "severityFilter": "Фильтр серьёзности", "filterAll": "Все", "accentColor": "Акцентный цвет", - "widgetSshTerminalName": "Терминал SSH", - "widgetSshTerminalDesc": "Встроенный SSH-терминал с подключением к настроенному хосту", + "widgetSshTerminalName": "SSH Терминал", + "widgetSshTerminalDesc": "Встроенный SSH-терминал, подключённый к настроенному хосту", "sshTerminalNoHost": "Хост не настроен", "sshTerminalConnect": "Подключиться", - "sshTerminalAutoConnect": "Автоматическое подключение под нагрузкой", - "widgetQuickConnectName": "Быстрый вход", - "widgetQuickConnectDesc": "Кнопки запуска в один клик для любого типа подключения на всех ваших хостах.", - "connectionTypes": "Типы подключения", + "sshTerminalAutoConnect": "Автоподключение при загрузке", + "widgetQuickConnectName": "Быстрое подключение", + "widgetQuickConnectDesc": "Кнопки для быстрого запуска одним кликом любого типа подключения к вашим хостам", + "connectionTypes": "Типы подключений", "connType_terminal": "Терминал", "connType_files": "Файловый менеджер", "connType_docker": "Docker", @@ -357,88 +357,127 @@ "connType_host-metrics": "Метрики хоста", "connType_rdp": "RDP", "connType_vnc": "VNC", - "connType_telnet": "Телнет", + "connType_telnet": "Telnet", "widgetFileManagerName": "Файловый менеджер", - "widgetFileManagerDesc": "Встроенный файловый менеджер SFTP для настроенного хоста", + "widgetFileManagerDesc": "Встроенный файловый менеджер по SFTP для настроенного хоста", "widgetDockerName": "Менеджер Docker", "widgetDockerDesc": "Встроенный менеджер контейнеров Docker для настроенного хоста", - "widgetTunnelName": "Менеджер туннеля", + "widgetTunnelName": "Менеджер туннелей", "widgetTunnelDesc": "Встроенный менеджер SSH-туннелей для настроенного хоста", "widgetNoHostSelected": "Хост не настроен" }, "serverConfig": { "title": "Конфигурация сервера", - "description": "Настройте URL-адрес сервера Termix для подключения к вашим внутренним службам.", + "description": "Настройте URL сервера Termix для подключения к вашим внутренним службам", "serverUrl": "URL сервера", - "enterServerUrl": "Пожалуйста, введите URL-адрес сервера.", - "saveFailed": "Не удалось сохранить конфигурацию.", + "enterServerUrl": "Пожалуйста, введите URL сервера", + "saveFailed": "Не удалось сохранить конфигурацию", "saveError": "Ошибка сохранения конфигурации", "saving": "Сохранение...", - "saveConfig": "Сохранение конфигурации", - "helpText": "Введите URL-адрес, по которому работает ваш сервер Termix (например, http://localhost:30001 или https://your-server.com).", + "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": "Локальный сервер", + "savedServers": "Сохранённые серверы", + "noSavedServers": "Нет сохранённых серверов", + "removeServer": "Удалить" + }, + "migrationNotice": { + "title": "Теперь Termix Desktop работает в автономном режиме.", + "body1": "Ранее это приложение подключалось напрямую к вашему серверу Termix. Теперь оно полностью автономно и хранит хосты и учетные данные локально, обеспечивая работу в автономном режиме. Двусторонняя синхронизация с собственным сервером Termix теперь является необязательной.", + "body2": "Ваши существующие хосты и учетные данные по-прежнему находятся на {{url}}. Включите удаленную синхронизацию и переподключитесь к этому серверу, чтобы восстановить их и обеспечить синхронизацию в дальнейшем.", + "dismiss": "Не сейчас", + "setUpSync": "Настройка удаленной синхронизации" + }, + "remoteSync": { + "title": "Удалённая синхронизация", + "description": "При желании вы можете подключить это настольное приложение к собственному серверу Termix для синхронизации хостов, учетных данных и фрагментов кода между устройствами. Приложение всегда работает полностью в автономном режиме, независимо от того, подключены вы к серверу или нет.", + "notConnected": "Не подключено", + "connected": "Подключено", + "connectedTo": "Подключено к {{url}}", + "lastSynced": "Последняя синхронизация {{time}}", + "neverSynced": "Никогда не синхронизировалось", + "syncError": "Ошибка синхронизации: {{message}}", + "needsReauth": "Срок действия авторизации истек.", + "connectButton": "Подключиться к серверу", + "disconnectButton": "Отключить", + "syncNowButton": "Синхронизировать сейчас", + "syncing": "Синхронизация...", + "serverUrl": "URL сервера", + "enterServerUrl": "Пожалуйста, введите URL-адрес сервера.", "mustIncludeProtocol": "URL-адрес сервера должен начинаться с http:// или https://", + "connectionTestFailed": "Не удалось подключиться к серверу Termix по указанному URL-адресу.", "allowInvalidCertificate": "Разрешить использование недействительного сертификата", "allowInvalidCertificateDesc": "Использовать только для доверенных серверов, размещенных на собственном сервере и имеющих самоподписанные сертификаты или сертификаты IP-адресов.", - "useEmbedded": "Использовать локальный сервер", - "embeddedDesc": "Запускайте Termix с помощью встроенного локального сервера (удаленный сервер не требуется).", - "embeddedConnecting": "Подключение к локальному серверу...", - "embeddedNotReady": "Локальный сервер пока не готов. Подождите немного и попробуйте снова.", - "localServer": "Локальный сервер", "savedServers": "Сохраненные серверы", - "noSavedServers": "Сохраненные серверы отсутствуют", - "removeServer": "Удалить" + "removeServer": "Удалять", + "continueButton": "Продолжать", + "cancelButton": "Отмена", + "signInTitle": "Войдите в {{url}}", + "originTitle": "Источник соединения", + "originDescription": "По умолчанию выбирается место, откуда исходят SSH-соединения. Это можно изменить для каждого хоста отдельно.", + "originLocal": "Это устройство (локальная сеть)", + "originRemote": "Удаленный сервер", + "bannerReconnect": "Переподключитесь", + "bannerMessage": "Для удалённой синхронизации требуется повторная аутентификация." }, "versionCheck": { "error": "Ошибка проверки версии", - "checkFailed": "Не удалось проверить наличие обновлений.", - "upToDate": "Приложение обновлено.", + "checkFailed": "Не удалось проверить обновления", + "upToDate": "Приложение актуально", "currentVersion": "Вы используете версию {{version}}", "updateAvailable": "Доступно обновление", "newVersionAvailable": "Доступна новая версия! Вы используете {{current}}, но доступна версия {{latest}}.", "betaVersion": "Бета-версия", - "betaVersionDesc": "Вы используете версию {{current}}, которая новее последней стабильной версии {{latest}}.", + "betaVersionDesc": "Вы используете {{current}}, которая новее последней стабильной версии {{latest}}.", "releasedOn": "Выпущено {{date}}", "downloadUpdate": "Скачать обновление", - "checking": "Проверяем наличие обновлений...", - "checkUpdates": "Проверьте наличие обновлений.", - "checkingUpdates": "Проверяем наличие обновлений...", + "checking": "Проверка обновлений...", + "checkUpdates": "Проверить обновления", + "checkingUpdates": "Проверка обновлений...", "updateRequired": "Требуется обновление" }, "common": { "close": "Закрыть", "minimize": "Свернуть", - "online": "Онлайн", - "offline": "Офлайн", - "unknown": "Неизвестный", + "online": "В сети", + "offline": "Не в сети", + "unknown": "Неизвестно", "continue": "Продолжить", "maintenance": "Обслуживание", - "degraded": "Снижена производительность", + "degraded": "Деградировано", "error": "Ошибка", "warning": "Предупреждение", - "unsavedChanges": "Несохраненные изменения", + "unsavedChanges": "Несохранённые изменения", "dismiss": "Скрыть", "loading": "Загрузка...", - "optional": "Необязательный", + "optional": "Необязательно", "connect": "Подключиться", "copied": "Скопировано", "connecting": "Подключение...", "updateAvailable": "Доступно обновление", - "appName": "Термикс", + "appName": "Termix", "openInNewTab": "Открыть в новой вкладке", - "noReleases": "Нет релизов", - "updatesAndReleases": "Обновления и релизы", + "noReleases": "Нет выпусков", + "updatesAndReleases": "Обновления и выпуски", "newVersionAvailable": "Доступна новая версия ({{version}}).", - "failedToFetchUpdateInfo": "Не удалось получить информацию об обновлении.", - "preRelease": "Предварительный релиз", + "failedToFetchUpdateInfo": "Не удалось получить информацию об обновлении", + "preRelease": "Предварительная версия", "noReleasesFound": "Релизы не найдены.", "cancel": "Отмена", "username": "Имя пользователя", "login": "Войти", "logout": "Выйти", - "register": "Зарегистрироваться", + "register": "Регистрация", "password": "Пароль", - "confirmPassword": "Подтвердите пароль", + "confirmPassword": "Подтверждение пароля", "back": "Назад", "save": "Сохранить", "saving": "Сохранение...", @@ -449,21 +488,21 @@ "confirm": "Подтвердить", "no": "Нет", "or": "ИЛИ", - "next": "Следующий", + "next": "Далее", "previous": "Предыдущий", "refresh": "Обновить", "language": "Язык", "checking": "Проверка...", "checkingDatabase": "Проверка подключения к базе данных...", "checkingAuthentication": "Проверка аутентификации...", - "backendReconnected": "Соединение с сервером восстановлено.", - "connectionDegraded": "Соединение с сервером потеряно, восстанавливаем…", + "backendReconnected": "Подключение к серверу восстановлено", + "connectionDegraded": "Подключение к серверу потеряно, восстановление…", "reload": "Перезагрузить", "remove": "Удалить", "create": "Создать", "update": "Обновить", - "copy": "Копия", - "copyFailed": "Не удалось скопировать в буфер обмена.", + "copy": "Копировать", + "copyFailed": "Не удалось скопировать в буфер обмена", "maximize": "Развернуть", "restore": "Восстановить", "of": "из", @@ -471,65 +510,65 @@ "deleted": "Удалено", "deleteFailed": "Не удалось удалить", "saveFailed": "Не удалось сохранить", - "required": "Необходимый" + "required": "Обязательно" }, "nav": { - "home": "Home", + "home": "Главная", "terminal": "Терминал", "docker": "Docker", "tunnels": "Туннели", "fileManager": "Файловый менеджер", "serverStats": "Метрики хоста", "hostMetrics": "Метрики хоста", - "admin": "Администратор", + "admin": "Администрирование", "termixId": "ID", - "userProfile": "Профиль", - "splitScreen": "Сплит экрана", - "confirmClose": "Закрыть активную сессию?", + "userProfile": "Профиль пользователя", + "splitScreen": "Разделить экран", + "confirmClose": "Закрыть активный сеанс?", "close": "Закрыть", "cancel": "Отмена", - "sshManager": "Менеджер SSH", + "sshManager": "SSH-менеджер", "cannotSplitTab": "Невозможно разделить эту вкладку", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Скопировать пароль", - "copySudoPassword": "Скопировать пароль Sudo", + "copyPassword": "Копировать пароль", + "copySudoPassword": "Копировать пароль sudo", "passwordCopied": "Пароль скопирован в буфер обмена", "noPasswordAvailable": "Пароль недоступен", - "failedToCopyPassword": "Не удалось скопировать пароль.", + "failedToCopyPassword": "Не удалось скопировать пароль", "refreshTab": "Обновить соединение", "renameTab": "Переименовать вкладку", "openFileManager": "Открыть файловый менеджер", - "dashboard": "Панель управления", - "networkGraph": "Карта сети", + "dashboard": "Панель мониторинга", + "networkGraph": "Сетевой график", "tmuxMonitor": "Монитор Tmux", - "homepage": "Главная страница", + "homepage": "Главная", "quickConnect": "Быстрое подключение", "sshTools": "Инструменты SSH", "history": "История", "sessionLogs": "Журналы сессий", "hosts": "Хосты", - "snippets": "Фрагменты кода", - "hostManager": "Менеджер хоста", - "credentials": "Учётные данные", - "connections": "Сессии", + "snippets": "Сниппеты", + "hostManager": "Менеджер хостов", + "credentials": "Учетные данные", + "connections": "Подключения", "alerts": "Оповещения", - "serial": "COM-порт", + "serial": "Последовательный порт", "roleAdministrator": "Администратор", "roleUser": "Пользователь" }, "hosts": { "hosts": "Хосты", "noHosts": "Нет SSH-хостов", - "retry": "Повторить попытку", + "retry": "Повторить", "refresh": "Обновить", - "optional": "Необязательный", + "optional": "Опционально", "downloadSample": "Скачать образец", "failedToDeleteHost": "Не удалось удалить {{name}}", - "importSkipExisting": "Импорт (пропустить существующие)", + "importSkipExisting": "Импорт (пропускать существующие)", "importSSHConfig": "Импорт из конфигурации SSH", - "connectionDetails": "Данные подключения", + "connectionDetails": "Детали подключения", "ssh": "SSH", - "telnet": "Телнет", + "telnet": "Telnet", "remoteDesktop": "Удаленный рабочий стол", "port": "Порт", "username": "Имя пользователя", @@ -538,23 +577,23 @@ "pin": "Закрепить", "addHost": "Добавить хост", "editHost": "Редактировать хост", - "cloneHost": "Клон хоста", + "cloneHost": "Клонировать хост", "enableTerminal": "Включить терминал", "enableTunnel": "Включить туннель", "enableFileManager": "Включить файловый менеджер", "enableDocker": "Включить Docker", "defaultPath": "Путь по умолчанию", - "connection": "Соединение", + "connection": "Подключение", "upload": "Загрузить", "authentication": "Аутентификация", "password": "Пароль", "key": "Ключ", "credential": "Учётные данные", - "none": "Никто", - "sshPrivateKey": "Закрытый ключ SSH", + "none": "Нет", + "sshPrivateKey": "Приватный ключ SSH", "keyType": "Тип ключа", "uploadFile": "Загрузить файл", - "tabGeneral": "Общий", + "tabGeneral": "Общие", "tabSsh": "SSH", "tabTerminal": "Терминал", "tabRdp": "RDP", @@ -564,7 +603,7 @@ "tabFiles": "Файлы", "tabStats": "Метрики хоста", "tabHostMetrics": "Метрики хоста", - "tabTelnet": "Телнет", + "tabTelnet": "Telnet", "tabSharing": "Общий доступ", "tabAuthentication": "Аутентификация", "terminal": "Терминал", @@ -573,419 +612,439 @@ "serverStats": "Метрики хоста", "status": "Статус", "folderRenamed": "Папка \"{{oldName}}\" успешно переименована в \"{{newName}}\"", - "failedToRenameFolder": "Не удалось переименовать папку.", - "movedToFolder": "Перемещены хосты {{count}} в \"{{folder}}\"", + "failedToRenameFolder": "Не удалось переименовать папку", + "movedToFolder": "Перемещено {{count}} хост(а/ов) в \"{{folder}}\"", "editHostTooltip": "Редактировать хост", "statusChecks": "Проверки статуса", "metricsCollection": "Сбор метрик", "metricsInterval": "Интервал сбора метрик", - "metricsIntervalDesc": "Как часто следует собирать статистику сервера (от 5 секунд до 1 часа)?", + "metricsIntervalDesc": "Как часто собирать статистику сервера (5с - 1ч)", "behavior": "Поведение", - "themePreview": "Предварительный просмотр темы", + "themePreview": "Предпросмотр темы", "theme": "Тема", "fontFamily": "Семейство шрифтов", "fontSize": "Размер шрифта", "letterSpacing": "Межбуквенный интервал", - "lineHeight": "Высота строки", + "lineHeight": "Межстрочный интервал", "cursorStyle": "Стиль курсора", - "cursorBlink": "Мерцание курсора", + "cursorBlink": "Мигание курсора", "scrollbackBuffer": "Буфер прокрутки", "bellStyle": "Стиль звукового сигнала", - "rightClickSelectsWord": "Щелчок правой кнопкой мыши выделяет слово", + "rightClickSelectsWord": "Выделять слово правой кнопкой", "fastScrollModifier": "Модификатор быстрой прокрутки", "fastScrollSensitivity": "Чувствительность быстрой прокрутки", - "sshAgentForwarding": "Переадресация агента SSH", + "sshAgentForwarding": "Проброс SSH-агента", "backspaceMode": "Режим Backspace", - "startupSnippet": "Фрагмент запуска", - "selectSnippet": "Выбрать фрагмент", - "forceKeyboardInteractive": "Принудительное использование клавиатуры", - "overrideCredentialUsername": "Переопределение имени пользователя учетных данных", - "overrideCredentialUsernameDesc": "Вместо имени пользователя, указанного в учетных данных, используйте имя пользователя, указанное выше.", - "oidcUsernameHint": "Используйте $oidc.preferred_username для замены вашего имени пользователя OIDC.", - "jumpHostChain": "Цепочка переходов хостов", - "portKnocking": "Port knocking", + "startupSnippet": "Сниппет запуска", + "selectSnippet": "Выбрать сниппет", + "forceKeyboardInteractive": "Принудительно Keyboard-Interactive", + "overrideCredentialUsername": "Переопределить имя пользователя учётных данных", + "overrideCredentialUsernameDesc": "Использовать указанное выше имя пользователя вместо имени из учётных данных", + "oidcUsernameHint": "Используйте $oidc.preferred_username для подстановки вашего имени входа OIDC.", + "tailscaleUsernameHint": "Это должен быть пользователь Unix, которому предоставлена идентификация Tailscale в списке контроля доступа SSH сети TailNet, а не обязательно root.", + "jumpHostChain": "Цепочка Jump Host", + "portKnocking": "Port Knocking", "addKnock": "Добавить порт", "addProxyNode": "Добавить узел", "proxyNode": "Прокси-узел", "proxyType": "Тип прокси", "quickActions": "Быстрые действия", - "sudoPasswordAutoFill": "Автозаполнение паролей Sudo", - "sudoPassword": "Пароль Sudo", - "keepaliveInterval": "Интервал проверки активности (мс)", - "moshCommand": "Команда Mosh", - "environmentVariables": "Переменные среды", + "sudoPasswordAutoFill": "Автозаполнение пароля sudo", + "sudoPassword": "Пароль sudo", + "keepaliveInterval": "Интервал keepalive (мс)", + "moshCommand": "Команда MOSH", + "environmentVariables": "Переменные окружения", "addVariable": "Добавить переменную", "docker": "Docker", - "copyTerminalUrl": "Скопировать URL терминала", - "copyFileManagerUrl": "Скопировать URL-адрес файлового менеджера", - "copyRemoteDesktopUrl": "Скопировать URL-адрес удаленного рабочего стола", + "copyTerminalUrl": "Копировать URL терминала", + "copyFileManagerUrl": "Копировать URL файлового менеджера", + "copyRemoteDesktopUrl": "Копировать URL удаленного рабочего стола", "failedToConnect": "Не удалось подключиться к консоли", "connect": "Подключиться", - "disconnect": "Отключить", + "disconnect": "Отключиться", "start": "Запустить", - "enableStatusCheck": "Включить проверку состояния", + "enableStatusCheck": "Включить проверку статуса", "enableMetrics": "Включить метрики", - "bulkUpdateFailed": "Сбой массового обновления", + "bulkUpdateFailed": "Массовое обновление не удалось", "selectAll": "Выбрать все", - "deselectAll": "Снять выделение со всех объектов", + "deselectAll": "Снять выделение со всех", "protocols": "Протоколы", - "secureShell": "Secure Shell", + "secureShell": "SSH", "virtualNetwork": "Виртуальная сеть", "unencryptedShell": "Незашифрованная оболочка", - "addressIp": "Адрес / IP-адрес", - "friendlyName": "Дружественное имя", + "addressIp": "Адрес / IP", + "friendlyName": "Понятное имя", "macAddress": "MAC-адрес", "wolBroadcastAddress": "Широковещательный адрес WoL", - "wolBroadcastAddressDesc": "Дополнительная опция для направленной широковещательной рассылки в сетях Docker/маршрутизируемых сетях (например, 192.168.1.255). Оставьте поле пустым, чтобы использовать 255.255.255.255.", - "folderAndAdvanced": "Папка и расширенные настройки", + "wolBroadcastAddressDesc": "Опциональный направленный широковещательный адрес для сетей Docker/маршрутизируемых сетей (например, 192.168.1.255). Оставьте пустым, чтобы использовать 255.255.255.255.", + "folderAndAdvanced": "Папка и Дополнительно", "privateNotes": "Личные заметки", - "privateNotesPlaceholder": "Подробная информация об этом сервере...", + "privateNotesPlaceholder": "Подробности об этом сервере...", "pinToTop": "Закрепить вверху", - "pinToTopDesc": "Этот хост всегда должен отображаться вверху списка.", - "portKnockingSequence": "Последовательность port knocking", - "addKnockBtn": "Добавить стук", - "noPortKnocking": "Функция \"вызова порта\" не настроена.", - "knockPort": "Порт Port Knocking", + "pinToTopDesc": "Всегда показывать этот хост вверху списка", + "portKnockingSequence": "Последовательность порт-нокинга", + "addKnockBtn": "Добавить нок", + "noPortKnocking": "Порт-нокинг не настроен.", + "knockPort": "Knock-порт", "protocol": "Протокол", - "delayAfterMs": "Задержка (мс)", - "useSocks5Proxy": "Используйте прокси SOCKS5", - "useSocks5ProxyDesc": "Маршрутизация соединения через прокси-сервер", + "delayAfterMs": "Задержка после (мс)", + "useSocks5Proxy": "Использовать SOCKS5-прокси", + "useSocks5ProxyDesc": "Маршрутизировать подключение через прокси-сервер", + "connectionOrigin": "Источник соединения", + "connectionOriginDesc": "Место, откуда устанавливается SSH-соединение с этим хостом. Переопределяет глобальное значение по умолчанию для настольного приложения.", + "connectionOriginDefault": "Использовать значение по умолчанию", + "connectionOriginLocal": "Это устройство (локальная сеть)", + "connectionOriginRemote": "Удаленный сервер", "proxyHost": "Прокси-хост", "proxyPort": "Прокси-порт", "proxyUsername": "Имя пользователя прокси", "proxyPassword": "Пароль прокси", - "proxySingleMode": "Единый прокси", + "proxySingleMode": "Одиночный прокси", "proxyChainMode": "Цепочка прокси", "you": "Вы", - "jumpHostChainLabel": "Цепочка переходов хостов", - "addJumpBtn": "Добавить прыжок", - "noJumpHosts": "Переходные хосты не настроены.", + "jumpHostChainLabel": "Цепочка транзитных хостов", + "addJumpBtn": "Добавить Jump", + "noJumpHosts": "Транзитные хосты не настроены.", "selectAServer": "Выберите сервер...", - "sshPort": "Порт SSH", + "sshPort": "SSH-порт", "authMethod": "Метод аутентификации", - "storedCredential": "Сохраненные учетные данные", - "selectACredential": "Выберите учетные данные...", - "vaultProfile": "Профиль подписанта хранилища", - "selectAVaultProfile": "Выберите профиль хранилища...", - "vaultProfileHint": "Настройки берутся из общего профиля; при подключении вы будете входить в Vault через OIDC. Секретные данные не хранятся.", + "storedCredential": "Сохранённые учётные данные", + "selectACredential": "Выберите учётные данные...", + "vaultProfile": "Профиль подписчика Vault", + "selectAVaultProfile": "Выберите профиль Vault...", + "vaultProfileHint": "Настройки берутся из общего профиля; при подключении вы войдёте в Vault через OIDC. Секреты не хранятся.", "vaultNewProfile": "Новый профиль", - "vaultManageProfiles": "Управление профилями хранилища", - "vaultAddrLabel": "Адрес хранилища", - "vaultNamespaceLabel": "Пространство имен", - "vaultOidcMountLabel": "OIDC Auth Mount", + "vaultManageProfiles": "Управление профилями Vault", + "vaultAddrLabel": "Адрес Vault", + "vaultNamespaceLabel": "Пространство имён", + "vaultOidcMountLabel": "Монтирование аутентификации OIDC", "vaultOidcRoleLabel": "Роль OIDC", - "vaultSshMountLabel": "SSH Secrets Mount", - "vaultSshRoleLabel": "Роль подписанта SSH", - "vaultValidPrincipalsLabel": "Действительные принципы", + "vaultSshMountLabel": "Монтирование секретов SSH", + "vaultSshRoleLabel": "Роль подписчика SSH", + "vaultValidPrincipalsLabel": "Допустимые принципалы", "vaultKeyTypeLabel": "Тип эфемерного ключа", - "vaultSharedLabel": "Поделиться со всеми пользователями", + "vaultSharedLabel": "Общий доступ для всех пользователей", "vaultCreateProfile": "Создать профиль", - "vaultProfileCreated": "Профиль хранилища создан", - "vaultProfileSaved": "Профиль хранилища сохранен", - "vaultProfileDeleted": "Профиль хранилища удален", - "vaultProfileSaveFailed": "Не удалось сохранить профиль хранилища.", - "vaultProfileDeleteFailed": "Не удалось удалить профиль хранилища.", + "vaultProfileCreated": "Профиль Vault создан", + "vaultProfileSaved": "Профиль Vault сохранён", + "vaultProfileDeleted": "Профиль Vault удалён", + "vaultProfileSaveFailed": "Не удалось сохранить профиль Vault", + "vaultProfileDeleteFailed": "Не удалось удалить профиль Vault", "vaultSaveProfile": "Сохранить профиль", - "vaultProfileValidationError": "Необходимо указать имя, адрес хранилища и роль подписанта SSH.", - "vaultNoProfiles": "Профилей хранилища пока нет.", + "vaultProfileValidationError": "Требуются имя, адрес Vault и роль SSH подписанта", + "vaultNoProfiles": "Пока нет профилей Vault.", "vaultSharedBadge": "общий", "keyTypeLabel": "Тип ключа", - "keyTypeAuto": "Автоматическое определение", + "keyTypeAuto": "Автоопределение", "keyPasteTab": "Вставить", "keyUploadTab": "Загрузить", "keyFileLoaded": "Файл ключа загружен", - "keyUploadClick": "Нажмите, чтобы загрузить файлы .pem / .key / .ppk", + "keyUploadClick": "Нажмите, чтобы загрузить .pem / .key / .ppk", "clearKey": "Очистить ключ", - "keySaved": "Сохранён SSH-ключ", - "keyReplaceNotice": "Вставьте новый ключ ниже, чтобы заменить его.", - "keyPassphraseSaved": "Кодовая фраза сохранена, введите для изменения.", + "keySaved": "SSH-ключ сохранён", + "keyReplaceNotice": "вставьте новый ключ ниже, чтобы заменить его", + "keyPassphraseSaved": "Парольная фраза сохранена, введите для изменения", "replaceKey": "Заменить ключ", - "docsLink": "Просмотреть документацию", + "docsLink": "Документация", "opksshLabel": "OPKSSH", - "opksshDesc": "Войдите на этот хост, используя свой поставщик идентификации вместо пароля или ключа. Требуется настройка OPKSSH на сервере.", - "warpgateLabel": "Warpgate Gateway", - "warpgateDesc": "Этот хост подключается через SSH-прокси Warpgate. Termix автоматически обработает процесс подтверждения через браузер после аутентификации.", - "agentLabel": "Агент SSH", - "agentDesc": "Аутентификация должна осуществляться с помощью SSH-агента, работающего на сервере Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агент должен быть запущен на машине, где размещен Termix.", + "opksshDesc": "Входите на этот хост через своего провайдера идентификации вместо пароля или ключа. Требуется настроенный OPKSSH на сервере.", + "warpgateLabel": "Шлюз Warpgate", + "warpgateDesc": "Этот хост подключается через SSH-прокси Warpgate. Termix автоматически обработает процесс подтверждения в браузере после аутентификации.", + "agentLabel": "SSH-агент", + "agentDesc": "Аутентификация с помощью SSH-агента, запущенного на сервере Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агент должен работать на машине, где размещён Termix.", "agentSocketPathLabel": "Путь к сокету агента", - "agentSocketPathPlaceholder": "Оставьте поле пустым, чтобы использовать SSH_AUTH_SOCK.", - "agentSocketPathHint": "Оставьте поле пустым для автоматического определения пути к сокету из переменной среды SSH_AUTH_SOCK или введите собственный путь к сокету (например, /run/user/1000/gnupg/S.gpg-agent.ssh).", + "agentSocketPathPlaceholder": "Оставьте пустым для использования SSH_AUTH_SOCK", + "agentSocketPathHint": "Оставьте пустым для автоопределения из переменной окружения SSH_AUTH_SOCK или укажите свой путь к сокету (например, /run/user/1000/gnupg/S.gpg-agent.ssh).", "tailscaleDeviceSelect": "Выберите устройство Tailscale", "tailscaleDeviceSelectPlaceholder": "Выберите устройство...", "tailscaleNoApiKey": "Ключ API Tailscale не настроен. Добавьте его в настройках администратора, чтобы включить обнаружение устройств.", - "tailscaleDocsLink": "Просмотреть документацию", - "tailscaleLoadingDevices": "Загрузочные устройства...", - "tailscaleNoDevices": "В вашей сети устройств не обнаружено.", + "tailscaleDocsLink": "Документация", + "tailscaleLoadingDevices": "Загрузка устройств...", + "tailscaleNoDevices": "В вашем tailnet не найдено устройств.", "tailscaleDeviceAutoFill": "При выборе устройства IP-адрес хоста будет заполнен автоматически.", - "forceKeyboardInteractiveLabel": "Принудительное использование клавиатуры", - "forceKeyboardInteractiveShortDesc": "Принудительный ручной ввод пароля, даже если ключи присутствуют.", - "allowLegacyAlgorithmsLabel": "Разрешить использование устаревших алгоритмов", - "allowLegacyAlgorithmsDesc": "Включите устаревшие алгоритмы SSH (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) для подключений к старым устройствам, которые невозможно обновить.", - "insecure": "Ненадежный", + "forceKeyboardInteractiveLabel": "Принудительный Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Принудительный ввод пароля вручную, даже если ключи присутствуют", + "allowLegacyAlgorithmsLabel": "Разрешить устаревшие алгоритмы", + "allowLegacyAlgorithmsDesc": "Включить устаревшие алгоритмы SSH (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) для подключения к старым устройствам, которые нельзя обновить.", + "insecure": "Небезопасный", "terminalAppearance": "Внешний вид терминала", "colorTheme": "Цветовая тема", "fontFamilyLabel": "Семейство шрифтов", "fontSizeLabel": "Размер шрифта", "cursorStyleLabel": "Стиль курсора", - "letterSpacingPx": "Межбуквенный интервал (px)", + "letterSpacingPx": "Межбуквенное расстояние (px)", "lineHeightLabel": "Высота строки", - "bellStyleLabel": "Стиль звукового сигнала", + "bellStyleLabel": "Стиль сигнала", "backspaceModeLabel": "Режим Backspace", "cursorBlinking": "Мигание курсора", - "cursorBlinkingDesc": "Включить анимацию мигания для курсора терминала", - "rightClickSelectsWordLabel": "Щелчок правой кнопкой мыши выделяет слово", - "rightClickSelectsWordShortDesc": "Выделите слово под курсором, щелкнув по нему правой кнопкой мыши.", + "cursorBlinkingDesc": "Включить анимацию мигания курсора терминала", + "rightClickSelectsWordLabel": "Правый клик выделяет слово", + "rightClickSelectsWordShortDesc": "Выделять слово под курсором при правом клике", "backgroundImageLabel": "URL фонового изображения", - "backgroundImageDesc": "Дополнительный URL-адрес для фонового изображения терминала", + "backgroundImageDesc": "Необязательный URL для фонового изображения терминала", "backgroundImageOpacityLabel": "Прозрачность фонового изображения", "customThemeColors": "Пользовательские цвета", "customThemeBackground": "Фон", - "customThemeForeground": "Передний план", + "customThemeForeground": "Текст", "customThemeCursor": "Курсор", "customThemeCursorAccent": "Акцент курсора", - "customThemeSelection": "Выбор", + "customThemeSelection": "Выделение", "customThemeAnsiColors": "Цвета ANSI", - "customThemeBlack": "Черный", + "customThemeBlack": "Чёрный", "customThemeRed": "Красный", - "customThemeGreen": "Зеленый", - "customThemeYellow": "Желтый", + "customThemeGreen": "Зелёный", + "customThemeYellow": "Жёлтый", "customThemeBlue": "Синий", "customThemeMagenta": "Пурпурный", "customThemeCyan": "Голубой", "customThemeWhite": "Белый", - "customThemeBrightBlack": "Ярко-черный", + "customThemeBrightBlack": "Ярко-чёрный", "customThemeBrightRed": "Ярко-красный", "customThemeBrightGreen": "Ярко-зелёный", "customThemeBrightYellow": "Ярко-жёлтый", "customThemeBrightBlue": "Ярко-синий", - "customThemeBrightMagenta": "Яркий пурпурный", - "customThemeBrightCyan": "Яркий голубой", + "customThemeBrightMagenta": "Ярко-пурпурный", + "customThemeBrightCyan": "Ярко-голубой", "customThemeBrightWhite": "Ярко-белый", "customThemeResetTooltip": "Сбросить до значений по умолчанию", + "savedThemesLabel": "Сохраненные темы", + "saveAsGlobalTheme": "Сохранить как глобальную тему", + "saveGlobalThemeNamePrompt": "Введите название для этой темы.", + "saveGlobalThemeSuccess": "Тема сохранена", + "saveGlobalThemeError": "Не удалось сохранить тему.", + "applyGlobalThemeTooltip": "Примените эту тему", + "deleteGlobalThemeTooltip": "Удалить эту тему", + "noSavedThemes": "Сохранённых тем пока нет.", "syntaxHighlightingLabel": "Подсветка синтаксиса", - "syntaxHighlightingDesc": "Раскрашивание вывода терминала (ошибки, пути, IP-адреса, временные метки)", - "syntaxHighlightingCategories": "Выделить категории", - "syntaxHighlightingCategoriesDesc": "Выберите, какие типы контента следует раскрасить.", - "syntaxCategoryLogLevels": "Уровни логарифма", - "syntaxCategoryLogLevelsDesc": "ошибка, предупреждение, информация, отладка, фатальная ошибка", + "syntaxHighlightingDesc": "Раскрашивать вывод терминала (ошибки, пути, IP-адреса, временные метки)", + "syntaxHighlightingCategories": "Категории подсветки", + "syntaxHighlightingCategoriesDesc": "Выберите типы содержимого для раскрашивания", + "syntaxCategoryLogLevels": "Уровни логирования", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "Пути к файлам", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "Временные метки", "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "IP-адреса", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", - "syntaxCategoryUrls": "URL-адреса", + "syntaxCategoryUrls": "URLs", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "Пронумерованные цифры", - "syntaxCategoryNumbersDesc": "порт 8080, выход 1, статус 404", - "behaviorAndAdvanced": "Поведение и продвинутый уровень", + "syntaxCategoryNumbers": "Числа с метками", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Поведение и дополнительные настройки", "scrollbackBufferLabel": "Буфер прокрутки", - "scrollbackMaxLines": "Максимальное количество строк, сохраняемых в истории.", - "sshAgentForwardingLabel": "Переадресация агента SSH", - "sshAgentForwardingShortDesc": "Передайте свои локальные SSH-ключи этому хосту.", + "scrollbackMaxLines": "Максимальное количество строк, сохраняемых в истории", + "sshAgentForwardingLabel": "Проброс SSH-агента", + "sshAgentForwardingShortDesc": "Передавать локальные SSH-ключи на этот хост", "useSSHTitleLabel": "Использовать заголовок окна SSH", - "useSSHTitleDesc": "Вместо имени хоста измените заголовок вкладки на заголовок окна оболочки.", - "enableAutoMosh": "Включить автомош", - "enableAutoMoshDesc": "Если доступен Mosh, предпочтительнее использовать SSH.", + "useSSHTitleDesc": "Обновлять заголовок вкладки из названия окна оболочки вместо имени хоста", + "enableAutoMosh": "Включить Auto-Mosh", + "enableAutoMoshDesc": "Предпочитать Mosh вместо SSH, если доступен", "enableAutoTmux": "Включить Auto-Tmux", - "enableAutoTmuxDesc": "Автоматический запуск или подключение к сессии tmux", - "enableSessionLogging": "Ведение журнала сессий", - "enableSessionLoggingDesc": "Запишите вывод терминала для последующего анализа.", + "enableAutoTmuxDesc": "Автоматически запускать или подключаться к сессии tmux", + "enableSessionLogging": "Логирование сессии", + "enableSessionLoggingDesc": "Записывать вывод терминальной сессии для последующего просмотра", + "allowSessionSharing": "Разрешить совместное использование сеанса", + "allowSessionSharingDesc": "Разрешите делиться записями онлайн-сессий на этом хостинге по ссылке или с другими пользователями.", "enableCommandHistory": "История команд", - "enableCommandHistoryDesc": "Сохранять выполненные в этом терминале команды для истории и автодополнения", + "enableCommandHistoryDesc": "Сохранять команды, выполняемые в этом терминале, для истории и автодополнения", "linkClickBehaviorLabel": "Поведение при клике по ссылке", - "linkClickBehaviorDesc": "Управляет действиями при нажатии на ссылку в терминале. Используйте значение «По умолчанию», чтобы следовать настройкам на уровне приложения.", - "linkClickBehaviorDefault": "По умолчанию (в соответствии с настройками приложения)", - "linkClickBehaviorConfirm": "Показать окно открытия или копирования", - "linkClickBehaviorDirect": "Открыть напрямую", - "sudoPasswordAutoFillLabel": "Автозаполнение паролей Sudo", - "sudoPasswordAutoFillShortDesc": "Автоматически вводить пароль sudo при появлении соответствующего запроса.", - "sudoPasswordAutoFillDesc": "Сохраните пароль для команды sudo, чтобы метрики хоста, командная строка терминала и другие средства могли автоматически выполнять привилегированные команды.", - "sudoPasswordLabel": "Пароль Sudo", - "environmentVariablesLabel": "Переменные среды", + "linkClickBehaviorDesc": "Определяет, что происходит при клике по ссылке в терминале. Используйте «По умолчанию», чтобы следовать настройкам приложения.", + "linkClickBehaviorDefault": "По умолчанию (следовать настройкам приложения)", + "linkClickBehaviorConfirm": "Показывать всплывающее окно для открытия или копирования", + "linkClickBehaviorDirect": "Открывать напрямую", + "sudoPasswordAutoFillLabel": "Автозаполнение пароля sudo", + "sudoPasswordAutoFillShortDesc": "Автоматически вводить пароль sudo при запросе", + "sudoPasswordAutoFillDesc": "Хранить пароль sudo, чтобы метрики хоста, запросы терминала и другие могли автоматически выполнять привилегированные команды.", + "sudoPasswordLabel": "Пароль sudo", + "environmentVariablesLabel": "Переменные окружения", "addVariableBtn": "Добавить переменную", - "noEnvVars": "Переменные среды не настроены.", + "noEnvVars": "Переменные окружения не настроены.", "fastScrollModifierLabel": "Модификатор быстрой прокрутки", "fastScrollSensitivityLabel": "Чувствительность быстрой прокрутки", "moshCommandLabel": "Команда Mosh", "startupSnippetLabel": "Фрагмент запуска", - "keepaliveIntervalLabel": "Интервал проверки активности (секунды)", - "maxKeepaliveMisses": "Максимальное количество пропущенных проверок активности", + "keepaliveIntervalLabel": "Интервал keepalive (секунд)", + "maxKeepaliveMisses": "Макс. пропусков keepalive", "tunnelSettings": "Настройки туннеля", "enableTunneling": "Включить туннелирование", - "enableTunnelingDesc": "Включить функцию SSH-туннелирования для этого хоста.", + "enableTunnelingDesc": "Включить функциональность SSH-туннеля для этого хоста", "serverTunnelsSection": "Серверные туннели", "addTunnelBtn": "Добавить туннель", "noTunnelsConfigured": "Туннели не настроены.", "tunnelLabel": "Туннель {{number}}", - "tunnelType": "Туннельный тип", - "tunnelModeLocalDesc": "Перенаправьте локальный порт на порт удаленного сервера (или хоста, доступного с него).", - "tunnelModeRemoteDesc": "Перенаправьте порт на удаленном сервере на локальный порт на вашем компьютере.", - "tunnelModeDynamicDesc": "Создайте SOCKS5-прокси на локальном порту для динамической переадресации портов.", + "tunnelType": "Тип туннеля", + "tunnelModeLocalDesc": "Пробросить локальный порт на порт удалённого сервера (или хоста, доступного с него).", + "tunnelModeRemoteDesc": "Пробросить порт на удалённом сервере на локальный порт вашей машины.", + "tunnelModeDynamicDesc": "Создать SOCKS5-прокси на локальном порту для динамического пробрасывания портов.", "sameHost": "Этот хост (прямой туннель)", - "endpointHost": "Хост конечной точки", - "endpointHostPlaceholder": "Тот же хост, хост SSH или доступный хост/IP-адрес", - "endpointPort": "Порт конечной точки", - "bindHost": "Привязать хост", + "endpointHost": "Целевой хост", + "endpointHostPlaceholder": "Тот же хост, SSH-хост или доступный хост/IP-адрес", + "endpointPort": "Целевой порт", + "bindHost": "Хост для привязки", "sourcePort": "Исходный порт", - "maxRetries": "Максимальное количество повторных попыток", - "retryIntervalS": "Интервал повторной попытки (с)", + "maxRetries": "Макс. число попыток", + "retryIntervalS": "Интервал повтора (с)", "autoStartLabel": "Автозапуск", - "autoStartDesc": "Автоматически подключать этот туннель при загрузке хоста.", - "tunnelConnecting": "Туннель, соединяющий...", - "tunnelDisconnected": "Туннель отключен", + "autoStartDesc": "Автоматически подключать этот туннель при загрузке хоста", + "tunnelConnecting": "Туннель подключается...", + "tunnelDisconnected": "Туннель отключён", "failedToConnectTunnel": "Не удалось подключиться", "failedToDisconnectTunnel": "Не удалось отключиться", - "dockerIntegration": "Интеграция с Docker", + "dockerIntegration": "Интеграция Docker", "enableDockerMonitor": "Включить Docker", - "enableDockerMonitorDesc": "Мониторинг и управление контейнерами на этом хосте с помощью Docker.", + "enableDockerMonitorDesc": "Отслеживать и управлять контейнерами на этом хосте через Docker", "containerRuntime": "Среда выполнения контейнеров", - "containerRuntimeDesc": "Выберите интерфейс командной строки, используемый для управления контейнерами на этом хосте.", + "containerRuntimeDesc": "Выберите CLI, используемый для управления контейнерами на этом хосте", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", "enableTmuxMonitor": "Включить монитор Tmux", - "enableTmuxMonitorDesc": "Отобразить этот хост в мониторе Tmux и добавить его действия Tmux на боковую панель.", + "enableTmuxMonitorDesc": "Показывать этот хост в Мониторе Tmux и добавлять действия tmux в боковую панель", "tabProxmox": "Proxmox", - "proxmoxIntegration": "Интеграция с Proxmox", + "proxmoxIntegration": "Интеграция Proxmox", "enableProxmox": "Включить Proxmox", - "enableProxmoxDesc": "Отметьте этот хост как узел Proxmox. Это позволит обнаруживать и импортировать гостевые системы непосредственно с этого хоста.", + "enableProxmoxDesc": "Пометить этот хост как узел Proxmox. Включает обнаружение и импорт гостевых систем непосредственно с этого хоста.", "proxmoxDefaultAuthType": "Тип аутентификации по умолчанию", - "proxmoxDefaultAuthTypeDesc": "Метод аутентификации, применяемый к импортированным гостевым хостам. Выберите тип аутентификации, соответствующий способу подключения к вашим гостям.", + "proxmoxDefaultAuthTypeDesc": "Метод аутентификации, применяемый к импортированным гостевым хостам. Выберите тип аутентификации, соответствующий способу подключения к гостевым хостам.", "authTypePassword": "Пароль", - "authTypeKey": "Ключ SSH", + "authTypeKey": "SSH-ключ", "authTypeCredential": "Учётные данные", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "Никто", - "proxmoxDefaultCredential": "Учетные данные по умолчанию", - "proxmoxDefaultCredentialDesc": "Учетные данные используются для импортированных гостевых хостов, если для типа аутентификации установлено значение «Учетные данные».", + "authTypeNone": "Нет", + "proxmoxDefaultCredential": "Учётные данные по умолчанию", + "proxmoxDefaultCredentialDesc": "Учётные данные, используемые для импортированных гостевых хостов, когда в качестве типа аутентификации выбраны «Учётные данные».", "proxmoxWindowsDetection": "Обнаружение Windows / RDP", - "proxmoxWindowsDetectionDesc": "Шаблоны имен, разделенные запятыми, которые запускают RDP вместо SSH (регистронечувствительны).", - "proxmoxDockerDetection": "обнаружение Docker", - "proxmoxDockerDetectionDesc": "Шаблоны имен, разделенные запятыми, которые позволяют Docker сопоставлять гостевые системы.", - "proxmoxPreferredRanges": "Предпочтительные диапазоны IP-адресов", - "proxmoxPreferredRangesDesc": "Префиксы, разделённые запятыми, в порядке приоритета для выбора IP-адреса, когда у гостевой системы несколько сетевых интерфейсов.", - "proxmoxAutoSync": "Автоматическая синхронизация гостей", - "proxmoxAutoSyncDesc": "Периодически устанавливайте соединение с этим узлом Proxmox и создавайте или обновляйте импортированные гостевые хосты, пока ваша сессия разблокирована.", - "proxmoxSyncInterval": "Интервал синхронизации (минуты)", - "proxmoxSyncIntervalDesc": "Минимум 5 минут. Планировщик пропускает заблокированные пользовательские сессии.", - "proxmoxMarkMissing": "Отметьте отсутствующих гостей", - "proxmoxMarkMissingDesc": "Вместо удаления ранее импортированного гостя, добавьте тег proxmox-missing.", + "proxmoxWindowsDetectionDesc": "Шаблоны имён через запятую, при совпадении с которыми используется RDP вместо SSH (без учёта регистра).", + "proxmoxDockerDetection": "Обнаружение Docker", + "proxmoxDockerDetectionDesc": "Шаблоны имён через запятую, при совпадении с которыми для гостя включается Docker.", + "proxmoxPreferredRanges": "Предпочтительные IP-диапазоны", + "proxmoxPreferredRangesDesc": "Список префиксов через запятую в порядке приоритета для выбора IP-адреса, если у гостя несколько интерфейсов.", + "proxmoxAutoSync": "Автосинхронизация гостей", + "proxmoxAutoSyncDesc": "Периодически обнаруживать этот узел Proxmox и создавать или обновлять импортированные гостевые хосты, пока ваш сеанс разблокирован.", + "proxmoxSyncInterval": "Интервал синхронизации (мин.)", + "proxmoxSyncIntervalDesc": "Минимум 5 минут. Планировщик пропускает заблокированные сеансы.", + "proxmoxMarkMissing": "Помечать отсутствующих гостей", + "proxmoxMarkMissingDesc": "Добавлять тег proxmox-missing, когда ранее импортированный гость исчезает, вместо его удаления.", "proxmoxLastSync": "Последняя синхронизация", - "proxmoxLastSyncNever": "Еще не синхронизировано", - "proxmoxLastSyncNoResult": "Результаты отсутствуют.", + "proxmoxLastSyncNever": "Не выполнялась", + "proxmoxLastSyncNoResult": "Нет сведений о результате", "proxmoxLastSyncSummary": "{{created}} создано, {{updated}} обновлено, {{markedMissing}} отсутствует, {{skipped}} пропущено", "proxmoxLastSyncStatus": { - "success": "Успех", - "error": "Неуспешный", - "pending": "В ожидании" + "success": "Успешно", + "error": "Ошибка", + "pending": "Ожидание" }, - "proxmoxDiscoverAction": "Обнаружение и импорт гостевых систем Proxmox", + "proxmoxDiscoverAction": "Обнаружить и импортировать гостей Proxmox", "proxmoxImportTitle": "Импорт из Proxmox", "proxmoxSelectHost": "Выберите хост Proxmox…", - "proxmoxDiscover": "Найти", + "proxmoxDiscover": "Обнаружить", "proxmoxDiscovering": "Обнаружение…", - "proxmoxDiscoverGuests": "Обнаружить гостевые системы", - "proxmoxGuestsSelected_one": "{{count}} гость — {{selected}} выбран", - "proxmoxGuestsSelected_other": "{{count}} гостей — {{selected}} выбраны", - "proxmoxSelectAll": "Выбрать все", - "proxmoxDeselectAll": "Снять выделение со всех объектов", - "proxmoxNoGuests": "На этом узле Proxmox не обнаружено ни одного гостя.", - "proxmoxImportButton_one": "Импорт {{count}} хоста", - "proxmoxImportButton_other": "Импорт {{count}} хостов", + "proxmoxDiscoverGuests": "Обнаружить гостей", + "proxmoxGuestsSelected_one": "{{count}} гость — {{selected}} выбрано", + "proxmoxGuestsSelected_other": "{{count}} гостей — {{selected}} выбрано", + "proxmoxSelectAll": "Выбрать всё", + "proxmoxDeselectAll": "Снять выделение", + "proxmoxNoGuests": "На этом узле Proxmox гости не найдены.", + "proxmoxImportButton_one": "Импортировать {{count}} хост", + "proxmoxImportButton_other": "Импортировать {{count}} хостов", "proxmoxResultImported": "{{count}} импортировано", "proxmoxResultUpdated": "{{count}} обновлено", "proxmoxResultFailed": "{{count}} не удалось", - "proxmoxImportComplete": "Импорт в Proxmox завершен: {{summary}}", - "proxmoxDiscoveryFailed": "Не удалось обнаружить", + "proxmoxResultSkippedNoIp": "{{count}} пропущено (IP-адрес не найден)", + "proxmoxImportComplete": "Импорт Proxmox завершен: {{summary}}", + "proxmoxDiscoveryFailed": "Обнаружение не удалось", "proxmoxImportFailed": "Импорт не удался", "enableFileManagerMonitor": "Включить файловый менеджер", - "enableFileManagerMonitorDesc": "Просматривайте и управляйте файлами на этом хосте по протоколу SFTP.", + "enableFileManagerMonitorDesc": "Просмотр и управление файлами на этом хосте через SFTP", "scpLegacyLabel": "Устаревший режим SCP", - "scpLegacyDesc": "Для серверов, не поддерживающих подсистему SFTP (например, встроенных или минималистичных SSH-серверов), используйте устаревшие методы передачи файлов.", + "scpLegacyDesc": "Использовать устаревший протокол передачи файлов для серверов, не поддерживающих подсистему SFTP (например, встроенные или минимальные SSH-серверы).", "defaultPathLabel": "Путь по умолчанию", - "fileManagerPathHint": "Каталог, который будет открыт при запуске файлового менеджера для данного хоста.", - "statusChecksLabel": "Проверки статуса", - "enableStatusChecks": "Включить проверку состояния", - "enableStatusChecksDesc": "Периодически отправляйте ping-запросы на этот хост для проверки его доступности.", - "useGlobalInterval": "Используйте глобальный интервал", - "useGlobalIntervalDesc": "Переопределите интервал проверки состояния на уровне всего сервера.", - "checkIntervalS": "Контрольный интервал (с)", - "checkIntervalDesc": "Секунды между каждым запросом на подключение", + "fileManagerPathHint": "Каталог, который открывается при запуске файлового менеджера для этого хоста.", + "statusChecksLabel": "Проверки состояния", + "enableStatusChecks": "Включить проверки состояния", + "enableStatusChecksDesc": "Периодически пинговать этот хост для проверки доступности", + "useGlobalInterval": "Использовать глобальный интервал", + "useGlobalIntervalDesc": "Переопределить глобальным интервалом проверки состояния сервера", + "checkIntervalS": "Интервал проверки (с)", + "checkIntervalDesc": "Секунд между проверками связи", "metricsCollectionLabel": "Сбор метрик", "enableMetricsLabel": "Включить метрики", - "enableMetricsDesc": "Собирайте метрики ЦП, ОЗУ, диска и другие показатели с этого хоста.", - "useGlobalMetrics": "Используйте глобальный интервал", - "useGlobalMetricsDesc": "Укажите интервал сбора метрик для всего сервера.", - "metricsIntervalS": "Интервал метрик (с)", - "metricsIntervalDesc2": "Секунды между снимками метрики", + "enableMetricsDesc": "Собирать метрики ЦП, ОЗУ, диска и другие с этого хоста", + "useGlobalMetrics": "Использовать глобальный интервал", + "useGlobalMetricsDesc": "Переопределить глобальным интервалом сбора метрик сервера", + "metricsIntervalS": "Интервал сбора метрик (с)", + "metricsIntervalDesc2": "Секунд между снимками метрик", "visibleWidgets": "Видимые виджеты", - "widgetsMovedToHostMetrics": "Теперь карточки добавляются, упорядочиваются и изменяют размер непосредственно на вкладке «Метрики хоста». Откройте «Метрики хоста» для этого хоста и используйте функцию «Настроить», чтобы выбрать, какие карточки будут отображаться.", + "widgetsMovedToHostMetrics": "Карточки теперь добавляются, упорядочиваются и изменяются в размере непосредственно на вкладке «Метрики хоста». Откройте метрики для этого хоста и используйте «Настроить», чтобы выбрать отображаемые карточки.", "cpuUsageLabel": "Использование ЦП", - "cpuUsageDesc": "Процент использования ЦП, средняя загрузка, спарклайн-график", - "memoryLabel": "Использование ОЗУ", - "memoryDesc": "Использование оперативной памяти, файла подкачки, кэша", + "cpuUsageDesc": "Процент загрузки ЦП, средние значения нагрузки, график-спарклайн", + "memoryLabel": "Использование памяти", + "memoryDesc": "Использование ОЗУ, подкачка, кэш", "storageLabel": "Использование диска", - "storageDesc": "Использование дискового пространства на точку монтирования", + "storageDesc": "Использование диска по точкам монтирования", "networkLabel": "Сетевые интерфейсы", "networkDesc": "Список интерфейсов и пропускная способность", - "uptimeLabel": "Время безотказной работы", - "uptimeDesc": "Время безотказной работы системы и время загрузки", + "uptimeLabel": "Время работы", + "uptimeDesc": "Время работы системы и время загрузки", "systemInfoLabel": "Информация о системе", "systemInfoDesc": "ОС, ядро, имя хоста, архитектура", - "recentLoginsLabel": "Недавние входы в систему", - "recentLoginsDesc": "События успешного и неудачного входа в систему", - "topProcessesLabel": "Основные процессы", - "topProcessesDesc": "PID, CPU%, MEM%, команда", - "listeningPortsLabel": "Порты прослушивания", - "listeningPortsDesc": "Открытие портов с указанием процесса и состояния.", + "recentLoginsLabel": "Недавние входы", + "recentLoginsDesc": "Успешные и неудачные попытки входа", + "topProcessesLabel": "Топ процессов", + "topProcessesDesc": "PID, %ЦП, %памяти, команда", + "listeningPortsLabel": "Прослушиваемые порты", + "listeningPortsDesc": "Открытые порты с процессом и состоянием", "firewallLabel": "Брандмауэр", - "firewallDesc": "Состояние брандмауэра, AppArmor, SELinux", + "firewallDesc": "Статус брандмауэра, AppArmor, SELinux", "quickActionsLabel": "Быстрые действия", - "quickActionsToolbar": "Быстрые действия отображаются в виде кнопок на панели инструментов «Метрики хоста» для выполнения команд одним щелчком мыши.", - "noQuickActions": "Пока никаких быстрых действий не предпринято.", - "buttonLabel": "Метка кнопки", - "selectSnippetPlaceholder": "Выделите фрагмент...", + "quickActionsToolbar": "Быстрые действия отображаются в виде кнопок на панели инструментов метрик хоста для выполнения команд в один клик.", + "noQuickActions": "Пока нет быстрых действий.", + "buttonLabel": "Надпись кнопки", + "selectSnippetPlaceholder": "Выбрать сниппет...", "addActionBtn": "Добавить действие", - "hostSharedSuccessfully": "Хост успешно предоставлен.", - "failedToShareHost": "Не удалось предоставить общий доступ к хосту.", + "hostSharedSuccessfully": "Хост успешно открыт для общего доступа", + "failedToShareHost": "Не удалось открыть хост для общего доступа", "accessRevoked": "Доступ отозван", "failedToRevokeAccess": "Не удалось отозвать доступ", "cancelBtn": "Отмена", "savingBtn": "Сохранение...", "addHostBtn": "Добавить хост", - "hostUpdated": "Хост обновлен", - "hostCreated": "Создан хост", + "hostUpdated": "Хост обновлён", + "hostCreated": "Хост создан", "failedToSave": "Не удалось сохранить хост", - "credentialUpdated": "Учетные данные обновлены.", - "credentialCreated": "Учетные данные созданы.", - "failedToSaveCredential": "Не удалось сохранить учетные данные.", - "credentialNameRequired": "Пожалуйста, введите имя для учетных данных.", - "credentialAuthRequired": "Добавьте пароль, SSH-ключ или и то, и другое.", - "backToHosts": "Вернуться к списку хостов", - "backToCredentials": "Вернуться к учетным данным", - "pinned": "Закреплено", + "credentialUpdated": "Учётные данные обновлены", + "credentialCreated": "Учётные данные созданы", + "failedToSaveCredential": "Не удалось сохранить учётные данные", + "credentialNameRequired": "Требуется имя учётных данных", + "credentialAuthRequired": "Добавьте пароль, SSH-ключ или и то, и другое", + "createCredentialFromHostBtn": "Создать учетные данные", + "createCredentialFromHostTitle": "Создание учетных данных с хоста", + "createCredentialFromHostDesc": "Создайте многоразовую, доступную для совместного использования запись учетных данных, предварительно заполненную текущим именем пользователя, паролем и/или SSH-ключом этого хоста.", + "backToHosts": "Назад к хостам", + "backToCredentials": "Назад к учётным данным", + "pinned": "Закреплённые", "noHostsFound": "Хосты не найдены", - "tryDifferentTerm": "Попробуйте другой термин.", - "addFirstHost": "Добавьте своего первого хоста, чтобы начать работу.", - "noCredentialsFound": "Учетные данные не найдены", - "addCredentialBtn": "Добавить учетные данные", - "updateCredentialBtn": "Обновить учетные данные", + "tryDifferentTerm": "Попробуйте другой запрос", + "addFirstHost": "Добавьте первый хост, чтобы начать", + "noCredentialsFound": "Учётные данные не найдены", + "addCredentialBtn": "Добавить учётные данные", + "updateCredentialBtn": "Обновить учётные данные", "features": "Функции", - "noFolder": "(Без папки)", + "noFolder": "(Нет папки)", "deleteSelected": "Удалить", - "exitSelection": "Выбор выхода", + "exitSelection": "Отменить выбор", "importSkip": "Импорт (пропустить существующие)", - "importOverwrite": "Импорт (перезапись)", + "importOverwrite": "Импорт (перезаписать)", "collapseBtn": "Свернуть", "importExportBtn": "Импорт / Экспорт", - "hostStatusesRefreshed": "Статусы хостов обновлены.", - "failedToRefreshHosts": "Не удалось обновить хосты.", - "movedHostTo": "Перемещено {{host}} в \"{{folder}}\"", - "failedToMoveHost": "Не удалось переместить хост.", + "hostStatusesRefreshed": "Статусы хостов обновлены", + "failedToRefreshHosts": "Не удалось обновить хосты", + "movedHostTo": "Хост {{host}} перемещён в \"{{folder}}\"", + "failedToMoveHost": "Не удалось переместить хост", "folderRenamedTo": "Папка переименована в \"{{name}}\"", - "deletedFolder": "Удалённая папка \"{{name}}\"", - "failedToDeleteFolder": "Не удалось удалить папку.", - "deleteAllInFolder": "Удалить все хосты в \"{{name}}\"? Это действие необратимо.", + "deletedFolder": "Папка \"{{name}}\" удалена", + "failedToDeleteFolder": "Не удалось удалить папку", + "deleteAllInFolder": "Удалить все хосты в папке \"{{name}}\"? Это действие нельзя отменить.", "folderPickerPlaceholder": "Нет папки", "folderPickerSearch": "Поиск или создание (используйте / для подпапок)...", "folderPickerNone": "Нет папки", @@ -994,74 +1053,77 @@ "newFolder": "Новая папка", "createFolderTitle": "Создать папку", "editFolderTitle": "Редактировать папку", - "folderDialogDescription": "Выберите имя, цвет и значок. Используйте / для вложенности папок.", - "folderNameLabel": "Название папки", - "folderNamePlaceholder": "например, производство/веб-разработка", - "folderNestingHint": "Используйте символ / для разделения уровней и создания вложенных папок.", + "folderDialogDescription": "Выберите имя, цвет и значок. Используйте / для вложенных папок.", + "folderNameLabel": "Имя папки", + "folderNamePlaceholder": "например Production/Web", + "folderNestingHint": "Используйте / для разделения уровней и создания вложенных папок.", "folderColor": "Цвет", "folderIcon": "Значок", - "folderPreview": "Предварительный просмотр", - "folderNameFallback": "Папка без названия", + "folderCredential": "Удостоверение личности", + "folderCredentialNone": "Учетные данные не назначены", + "folderCredentialHint": "Хосты в этой папке, использующие аутентификацию \"Сохраненные учетные данные\" без выбора собственных учетных данных, унаследуют эти данные.", + "folderPreview": "Предпросмотр", + "folderNameFallback": "Безымянная папка", "createFolderButton": "Создать папку", - "saveFolderButton": "Сохраните папку", + "saveFolderButton": "Сохранить папку", "cancel": "Отмена", - "iconSearchPlaceholder": "Значки поиска...", + "iconSearchPlaceholder": "Поиск значков...", "openAllSessions": "Открыть все сессии", "editFolder": "Редактировать папку", "deleteFolder": "Удалить папку", "folderSaved": "Папка сохранена", - "failedToSaveFolder": "Не удалось сохранить папку.", - "folderDeleted": "Удалённая папка \"{{name}}\"", - "deleteFolderConfirm": "Удалить \"{{name}}\" и его {{count}} хост(ы)? Это действие необратимо.", - "failedToMoveHosts": "Не удалось переместить хосты.", + "failedToSaveFolder": "Не удалось сохранить папку", + "folderDeleted": "Удалена папка \"{{name}}\"", + "deleteFolderConfirm": "Удалить \"{{name}}\" и {{count}} хост(ов)? Это действие необратимо.", + "failedToMoveHosts": "Не удалось переместить хосты", "expandAll": "Развернуть все папки", "collapseAll": "Свернуть все папки", - "moreActions": "Более", - "groupBy": "Сгруппировать по", + "moreActions": "Ещё", + "groupBy": "Группировать по", "GroupByFolder": "Папка", - "GroupByTag": "Ярлык", + "GroupByTag": "Тег", "GroupByStatus": "Статус", "GroupByProtocol": "Протокол", - "GroupByAuth": "Тип авторизации", - "groupUngrouped": "Негруппированный", - "deletedHost": "Удалено {{name}}", + "GroupByAuth": "Тип аутентификации", + "groupUngrouped": "Несгруппированные", + "deletedHost": "Удалён {{name}}", "copiedToClipboard": "Скопировано в буфер обмена", - "terminalUrlCopied": "URL терминала скопирован.", - "fileManagerUrlCopied": "URL-адрес файлового менеджера скопирован.", - "tunnelUrlCopied": "URL туннеля скопирован.", - "dockerUrlCopied": "URL-адрес Docker скопирован.", - "hostMetricsUrlCopied": "URL-адрес метрик хоста скопирован.", - "tmuxMonitorUrlCopied": "Ссылка на монитор Tmux скопирована.", - "rdpUrlCopied": "URL-адрес RDP скопирован", - "vncUrlCopied": "Скопирована ссылка VNC.", - "telnetUrlCopied": "URL-адрес Telnet скопирован.", - "remoteDesktopUrlCopied": "Скопирована ссылка на удаленный рабочий стол.", + "terminalUrlCopied": "URL терминала скопирован", + "fileManagerUrlCopied": "URL файлового менеджера скопирован", + "tunnelUrlCopied": "URL туннеля скопирован", + "dockerUrlCopied": "URL Docker скопирован", + "hostMetricsUrlCopied": "URL метрик хоста скопирован", + "tmuxMonitorUrlCopied": "Скопирован URL Tmux Monitor", + "rdpUrlCopied": "Скопирован URL RDP", + "vncUrlCopied": "Скопирован URL VNC", + "telnetUrlCopied": "Скопирован URL Telnet", + "remoteDesktopUrlCopied": "Скопирован URL удаленного рабочего стола", "expandActions": "Развернуть действия", "collapseActions": "Свернуть действия", - "wakeOnLanAction": "Пробуждение по локальной сети", - "wakeOnLanSuccess": "Волшебный пакет отправлен на {{name}}", - "wakeOnLanError": "Не удалось отправить волшебный пакет.", - "cloneHostAction": "Клон хоста", - "copyAddress": "Скопировать адрес", - "copyLink": "Скопировать ссылку", - "copyTerminalUrlAction": "Скопировать URL терминала", - "copyFileManagerUrlAction": "Скопировать URL файлового менеджера", - "copyTunnelUrlAction": "Скопировать URL туннеля", - "copyDockerUrlAction": "Скопировать URL-адрес Docker", - "copyHostMetricsUrlAction": "Скопировать URL-адрес метрик хоста", - "copyTmuxMonitorUrlAction": "Скопировать URL-адрес монитора Tmux", - "copyRdpUrlAction": "Скопировать URL-адрес RDP", - "copyVncUrlAction": "Скопировать URL VNC", - "copyTelnetUrlAction": "Скопировать URL-адрес Telnet", - "copyRemoteDesktopUrlAction": "Скопировать URL-адрес удаленного рабочего стола", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic Packet отправлен на {{name}}", + "wakeOnLanError": "Не удалось отправить Magic Packet", + "cloneHostAction": "Клонировать хост", + "copyAddress": "Копировать адрес", + "copyLink": "Копировать ссылку", + "copyTerminalUrlAction": "Копировать URL терминала", + "copyFileManagerUrlAction": "Копировать URL файлового менеджера", + "copyTunnelUrlAction": "Копировать URL туннеля", + "copyDockerUrlAction": "Копировать URL Docker", + "copyHostMetricsUrlAction": "Копировать URL метрик хоста", + "copyTmuxMonitorUrlAction": "Копировать URL Tmux Monitor", + "copyRdpUrlAction": "Копировать URL RDP", + "copyVncUrlAction": "Копировать URL VNC", + "copyTelnetUrlAction": "Копировать URL Telnet", + "copyRemoteDesktopUrlAction": "Копировать URL удаленного рабочего стола", "deleteCredentialConfirm": "Удалить учетные данные \"{{name}}\"?", "deletedCredential": "Удалено {{name}}", "deploySSHKeyTitle": "Развернуть SSH-ключ", "deployingBtn": "Развертывание...", "deployBtn": "Развернуть", - "failedToDeployKey": "Не удалось развернуть ключ.", - "deleteHostsConfirm": "Удалить хост {{count}}{{plural}}? Это действие необратимо.", - "movedToRoot": "Перемещено в корневой каталог", + "failedToDeployKey": "Не удалось развернуть ключ", + "deleteHostsConfirm": "Удалить {{count}} хост{{plural}}? Это действие нельзя отменить.", + "movedToRoot": "Перемещено в корень", "enableTerminalFeature": "Включить терминал", "disableTerminalFeature": "Отключить терминал", "enableFilesFeature": "Включить файлы", @@ -1075,53 +1137,53 @@ "addTagsPlaceholder": "Добавить теги...", "authDetails": "Данные аутентификации", "credType": "Тип", - "generateKeyPairDesc": "Сгенерируйте новую пару ключей; закрытый и открытый ключи будут заполнены автоматически.", + "generateKeyPairDesc": "Создать новую пару ключей, закрытый и публичный ключи будут заполнены автоматически.", "generatingKey": "Генерация...", - "generateLabel": "Сгенерировать {{label}}", + "generateLabel": "Создать {{label}}", "uploadFileBtn": "Загрузить файл", - "keyPassphraseOptional": "Ключевая кодовая фраза (необязательно)", - "sshPublicKeyOptional": "Открытый ключ SSH (необязательно)", - "publicKeyGenerated": "Сгенерирован открытый ключ", - "failedToGeneratePublicKey": "Не удалось получить открытый ключ.", - "publicKeyCopied": "Открытый ключ скопирован", - "keyPairGenerated": "{{label}} сгенерирована пара ключей", - "failedToGenerateKeyPair": "Не удалось сгенерировать пару ключей.", + "keyPassphraseOptional": "Парольная фраза ключа (необязательно)", + "sshPublicKeyOptional": "Публичный ключ SSH (необязательно)", + "publicKeyGenerated": "Публичный ключ создан", + "failedToGeneratePublicKey": "Не удалось получить публичный ключ", + "publicKeyCopied": "Публичный ключ скопирован", + "keyPairGenerated": "Пара ключей {{label}} создана", + "failedToGenerateKeyPair": "Не удалось создать пару ключей", "searchHostsPlaceholder": "Поиск хостов, адресов, тегов…", - "searchCredentialsPlaceholder": "Учетные данные для поиска…", + "searchCredentialsPlaceholder": "Поиск учётных данных…", "refreshBtn": "Обновить", "addTag": "Добавить теги...", "deleteConfirmBtn": "Удалить", - "tunnelRequirementsText": "В файле /etc/ssh/sshd_config для SSH-сервера должны быть установлены следующие параметры: GatewayPorts yes, AllowTcpForwarding yes и PermitRootLogin yes.", + "tunnelRequirementsText": "На SSH-сервере должны быть установлены параметры GatewayPorts yes, AllowTcpForwarding yes и PermitRootLogin yes в /etc/ssh/sshd_config.", "deleteHostConfirm": "Удалить \"{{name}}\"?", - "enableAtLeastOneProtocol": "Включите хотя бы один из указанных выше протоколов, чтобы настроить параметры аутентификации и подключения.", - "keyPassphrase": "Ключевая кодовая фраза", + "enableAtLeastOneProtocol": "Включите хотя бы один протокол выше, чтобы настроить параметры аутентификации и подключения.", + "keyPassphrase": "Парольная фраза ключа", "connectBtn": "Подключиться", - "disconnectBtn": "Отключить", + "disconnectBtn": "Отключиться", "basicInformation": "Основная информация", "authDetailsSection": "Данные аутентификации", "credTypeLabel": "Тип", "hostsTab": "Хосты", - "credentialsTab": "Учётные данные", - "selectMultiple": "Выберите несколько", - "selectHosts": "Выберите хосты", - "connectionLabel": "Соединение", + "credentialsTab": "Учетные данные", + "selectMultiple": "Выбрать несколько", + "selectHosts": "Выбрать хосты", + "connectionLabel": "Подключение", "authenticationLabel": "Аутентификация", "generateKeyPairTitle": "Сгенерировать пару ключей", - "generateKeyPairDescription": "Сгенерируйте новую пару ключей; закрытый и открытый ключи будут заполнены автоматически.", + "generateKeyPairDescription": "Сгенерировать новую пару ключей, закрытый и открытый ключи будут заполнены автоматически.", "generateFromPrivateKey": "Сгенерировать из закрытого ключа", "refreshBtn2": "Обновить", - "exitSelectionTitle": "Выбор выхода", - "exportAll": "Экспорт всех", - "exportForSharing": "Экспорт для обмена", + "exitSelectionTitle": "Выйти из выбора", + "exportAll": "Экспортировать всё", + "exportForSharing": "Экспортировать для обмена", "addHostBtn2": "Добавить хост", - "addCredentialBtn2": "Добавить учетные данные", - "checkingHostStatuses": "Проверка состояния хостов...", - "pinnedSection": "Закреплено", - "hostsExported": "Хосты успешно экспортированы.", - "hostsShareExported": "Доступные для совместного использования хосты успешно экспортированы.", - "exportFailed": "Не удалось экспортировать хосты.", + "addCredentialBtn2": "Добавить учётные данные", + "checkingHostStatuses": "Проверка статусов хостов...", + "pinnedSection": "Закреплённые", + "hostsExported": "Хосты успешно экспортированы", + "hostsShareExported": "Хосты для обмена успешно экспортированы", + "exportFailed": "Не удалось экспортировать хосты", "sampleDownloaded": "Образец файла загружен", - "failedToDeleteCredential2": "Не удалось удалить учетные данные.", + "failedToDeleteCredential2": "Не удалось удалить учётные данные", "noFolderOption": "(Без папки)", "nSelected": "{{count}} выбрано", "featuresMenu": "Функции", @@ -1131,58 +1193,62 @@ "deployDialogDesc": "Развернуть {{name}} в authorized_keys хоста.", "targetHostLabel": "Целевой хост", "selectHostOption": "Выберите хост...", - "keyDeployedSuccess": "Ключ успешно развернут.", - "failedToDeployKey2": "Не удалось развернуть ключ.", - "deletedCount": "Удалены {{count}} хостов", + "keyDeployedSuccess": "Ключ успешно развёрнут", + "failedToDeployKey2": "Не удалось развернуть ключ", + "deletedCount": "Удалено {{count}} хостов", "failedToDeleteCount": "Не удалось удалить {{count}} хостов", - "duplicatedHost": "Дублированный \"{{name}}\"", - "failedToDuplicateHost": "Не удалось дублировать хост.", - "updatedCount": "Обновлены хосты {{count}}", + "duplicatedHost": "\"{{name}}\" дублирован", + "failedToDuplicateHost": "Не удалось дублировать хост", + "updatedCount": "Обновлено хостов: {{count}}", "friendlyNameLabel": "Дружественное имя", "descriptionLabel": "Описание", "loadingHost": "Загрузка хоста...", "loadingHosts": "Загрузка хостов...", - "loadingCredentials": "Загрузка учетных данных...", - "noHostsYet": "Хостов пока нет.", - "noHostsMatchSearch": "Поиск не дал результатов.", + "loadingCredentials": "Загрузка учётных данных...", + "noHostsYet": "Пока нет хостов", + "noHostsMatchSearch": "Нет хостов по вашему запросу", "hostNotFound": "Хост не найден", "searchHosts": "Поиск хостов...", "sortHosts": "Сортировка хостов", - "sortDefault": "Порядок по умолчанию", - "sortNameAsc": "Имя (от А до Я)", - "sortNameDesc": "Имя (Z → A)", - "sortIpAsc": "IP-адрес (восходящий)", - "sortIpDesc": "IP-адрес (описание)", - "sortOnlineFirst": "Онлайн прежде всего", - "sortOfflineFirst": "Офлайн-первый", - "sortPinnedFirst": "Закреплено первым", - "filterHosts": "Фильтрация хостов", + "sortDefault": "По умолчанию", + "sortNameAsc": "Имя (А → Я)", + "sortNameDesc": "Имя (Я → А)", + "sortIpAsc": "IP-адрес (по возрастанию)", + "sortIpDesc": "IP-адрес (по убыванию)", + "sortOnlineFirst": "Сначала онлайн", + "sortOfflineFirst": "Сначала офлайн", + "sortPinnedFirst": "Сначала закреплённые", + "filterHosts": "Фильтр хостов", "filterClearAll": "Очистить фильтры", "filterStatusGroup": "Статус", "filterOnline": "Онлайн", "filterOffline": "Офлайн", - "filterPinned": "Закреплено", + "filterPinned": "Закреплённые", "filterAuthGroup": "Тип аутентификации", "filterAuthPassword": "Пароль", - "filterAuthKey": "Ключ SSH", + "filterAuthKey": "SSH-ключ", "filterAuthCredential": "Учётные данные", - "filterAuthNone": "Никто", + "filterAuthNone": "Нет", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Протокол", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Телнет", + "filterProtocolTelnet": "Telnet", "filterFeaturesGroup": "Функции", "filterFeatureTerminal": "Терминал", "filterFeatureFileManager": "Файловый менеджер", "filterFeatureTunnel": "Туннель", "filterFeatureDocker": "Docker", "filterTagsGroup": "Теги", - "shareHost": "Поделиться хостом", + "shareHost": "Поделиться", "shareHostTitle": "Поделиться: {{name}}", + "shareFolder": "Поделиться папкой", + "shareFolderTitle": "Общая папка: {{name}}", + "folderSharedSuccessfully": "Общие хосты {{count}} в папке", + "failedToShareFolder": "Не удалось предоставить общий доступ к папке.", "sharing": { - "loadError": "Не удалось загрузить данные для обмена. Пожалуйста, попробуйте еще раз.", + "loadError": "Не удалось загрузить данные общего доступа. Попробуйте снова.", "shareWithSection": "Поделиться с", "usersTab": "Пользователи", "rolesTab": "Роли", @@ -1191,20 +1257,20 @@ "permissionLevelLabel": "Уровень доступа", "levels": { "connect": { - "label": "Соединять", - "description": "Открыты только следующие сессии: терминал, удалённый рабочий стол, файловый менеджер, туннели и Docker. Доступ к конфигурации хоста запрещён." + "label": "Подключение", + "description": "Только открытие сеансов: терминал, удалённый рабочий стол, файловый менеджер, туннели и Docker. Без доступа к конфигурации хоста." }, "view": { - "label": "Вид", - "description": "Подключитесь и просмотрите конфигурацию хоста. Секретные данные никогда не отображаются." + "label": "Просмотр", + "description": "Подключение и просмотр конфигурации хоста. Секреты не отображаются." }, "edit": { - "label": "Редактировать", - "description": "Просмотр и изменение хоста. Секреты можно заменить, но их нельзя прочитать; назначение учетных данных остается только у владельца." + "label": "Редактирование", + "description": "Просмотр и изменение хоста. Секреты можно заменить, но нельзя прочитать; назначения учётных данных остаются только у владельца." }, "manage": { - "label": "Управлять", - "description": "Редактировать, а также предоставлять доступ к хосту другим пользователям, изменять уровни разрешений и отзывать доступ." + "label": "Управление", + "description": "Редактирование, а также предоставление доступа другим, изменение уровней доступа и отзыв доступа." } }, "expiryLabel": "Срок действия доступа", @@ -1214,51 +1280,55 @@ "oneDay": "24 часа", "sevenDays": "7 дней", "thirtyDays": "30 дней", - "custom": "Обычай" + "custom": "Настроить" }, - "customHoursPlaceholder": "Осталось часов до истечения срока доступа", - "shareButton": "Делиться", + "customHoursPlaceholder": "Часов до окончания срока доступа", + "shareButton": "Поделиться", "shareWithCount": "Поделиться ({{count}})", "currentAccess": "Текущий доступ", - "noAccessEntries": "Этот хост еще не был предоставлен другим пользователям.", - "grantedBy": "Утверждено", - "expires": "Срок действия истекает", - "expired": "Истекший", + "noAccessEntries": "Данный хост еще не предоставлен другим пользователям", + "folderShareSummary": "Общий доступ к {{shared}} хостам {{total}} в этой папке", + "grantedBy": "Кем предоставлен", + "expires": "Истекает", + "expired": "Истек", "never": "Никогда", - "revoke": "Отменить", + "revoke": "Отозвать", "accessUpdated": "Доступ обновлен", "accessUpdateFailed": "Не удалось обновить доступ", "sharedBadge": "Общий", - "sharedBadgeTooltip": "Доступ предоставлен пользователями {{owner}} ({{level}}).", - "viewOnlyBanner": "Этот хост предоставлен вам пользователем {{owner}} с правами просмотра. Конфигурация доступна только для чтения.", - "sharedEditBanner": "Этот хост предоставлен вам пользователем {{owner}} с правами редактирования. Изменения применяются к реальному хосту; ссылки аутентификации может изменить только владелец.", - "ownerOnlyControl": "Изменить это может только владелец хостинга." + "sharedBadgeTooltip": "Предоставлен пользователем {{owner}} (доступ: {{level}})", + "viewOnlyBanner": "Этот хост предоставлен вам пользователем {{owner}} с доступом только для просмотра. Конфигурация доступна только для чтения.", + "sharedEditBanner": "Этот хост предоставлен вам пользователем {{owner}} с доступом на редактирование. Изменения применяются к реальному хосту; параметры аутентификации может изменять только владелец.", + "ownerOnlyControl": "Только владелец хоста может изменять это" }, "guac": { - "connection": "Соединение", + "connection": "Подключение", "authentication": "Аутентификация", "storedCredential": "Сохраненные учетные данные", - "noCredential": "Без учетных данных (прямые учетные данные указаны ниже)", + "noCredential": "Нет учетных данных (прямые данные ниже)", "authMethod": "Метод аутентификации", "authTypeDirect": "Прямой", - "authTypeCredential": "Учётные данные", + "authTypeCredential": "Учетные данные", + "authTypeNone": "Никто", + "authTypeNoneDesc": "Учетные данные не сохраняются. При каждом подключении вам будет предлагаться ввести имя пользователя и пароль; они не сохраняются.", "selectCredential": "Выберите учетные данные...", - "connectionSettings": "Настройки подключения", - "displaySettings": "Настройки дисплея", - "audioSettings": "Настройки звука", + "connectionSettings": "Параметры подключения", + "displaySettings": "Параметры отображения", + "audioSettings": "Параметры звука", "rdpPerformance": "Производительность RDP", - "deviceRedirection": "Перенаправление устройства", + "deviceRedirection": "Перенаправление устройств", "session": "Сессия", "gateway": "Шлюз", "remoteApp": "RemoteApp", "clipboard": "Буфер обмена", "sessionRecording": "Запись сессии", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Настройки VNC", - "terminalSettings": "Настройки терминала", + "vncSettings": "Параметры VNC", + "terminalSettings": "Параметры терминала", "rdpPort": "Порт RDP", "username": "Имя пользователя", "password": "Пароль", + "passwordSaved": "Пароль сохранен, введите для изменения.", "domain": "Домен", "securityMode": "Режим безопасности", "colorDepth": "Глубина цвета", @@ -1268,15 +1338,15 @@ "resizeMethod": "Метод изменения размера", "clientName": "Имя клиента", "initialProgram": "Начальная программа", - "serverLayout": "Схема расположения серверов", + "serverLayout": "Раскладка сервера", "timezone": "Часовой пояс", - "loadBalanceInfo": "Информация о балансе нагрузки / Файлы cookie", - "loadBalanceInfoDesc": "Файл cookie брокера подключений RD для балансировки нагрузки фермы RDS (например, tsv://MS Terminal Services Plugin.1.CollectionName)", - "guacdProxy": "guacd Proxy", - "guacdHostname": "guacd Host", - "guacdHostnamePlaceholder": "Глобальные значения по умолчанию", + "loadBalanceInfo": "Информация о балансировке нагрузки / Cookie", + "loadBalanceInfoDesc": "Cookie брокера подключений к удаленному рабочему столу для балансировки нагрузки фермы RDS (например, tsv://MS Terminal Services Plugin.1.CollectionName)", + "guacdProxy": "Прокси guacd", + "guacdHostname": "Хост guacd", + "guacdHostnamePlaceholder": "Глобальное значение по умолчанию", "guacdPort": "Порт guacd", - "guacdProxyDesc": "Переопределите глобальный экземпляр guacd для этого соединения. Оставьте поле пустым, чтобы использовать глобально настроенный guacd.", + "guacdProxyDesc": "Переопределить глобальный экземпляр guacd для этого соединения. Оставьте пустым для использования глобально настроенного guacd.", "gatewayHostname": "Имя хоста шлюза", "gatewayPort": "Порт шлюза", "gatewayUsername": "Имя пользователя шлюза", @@ -1285,80 +1355,80 @@ "remoteAppProgram": "Программа RemoteApp", "workingDirectory": "Рабочий каталог", "arguments": "Аргументы", - "normalizeLineEndings": "Нормализация символов конца строки", + "normalizeLineEndings": "Нормализовать окончания строк", "recordingPath": "Путь записи", - "recordingName": "Название записи", + "recordingName": "Имя записи", "macAddress": "MAC-адрес", - "broadcastAddress": "Обращение в эфире", + "broadcastAddress": "Широковещательный адрес", "udpPort": "Порт UDP", "waitTimeS": "Время ожидания (с)", - "driveName": "Название диска", + "driveName": "Имя диска", "drivePath": "Путь к диску", "ignoreCertificate": "Игнорировать сертификат", - "ignoreCertificateDesc": "Разрешите подключения к хостам с самоподписанными сертификатами.", - "forceLossless": "Сила без потерь", - "forceLosslessDesc": "Принудительное кодирование изображений без потерь (более высокое качество, большая пропускная способность)", + "ignoreCertificateDesc": "Разрешить подключения к хостам с самоподписанными сертификатами", + "forceLossless": "Принудительно без потерь", + "forceLosslessDesc": "Принудительное кодирование изображений без потерь (более высокое качество, больший расход трафика)", "disableAudio": "Отключить звук", - "disableAudioDesc": "Отключить весь звук в удалённой сессии", + "disableAudioDesc": "Отключить весь звук в удаленном сеансе", "enableAudioInput": "Включить аудиовход (микрофон)", - "enableAudioInputDesc": "Перенаправьте сигнал локального микрофона на удаленную сессию.", + "enableAudioInputDesc": "Передавать локальный микрофон в удаленный сеанс", "wallpaper": "Обои", - "wallpaperDesc": "Отображать обои рабочего стола (отключение повышает производительность)", - "theming": "Тематическое оформление", - "themingDesc": "Включите визуальные темы и стили.", - "fontSmoothing": "Сглаживание шрифта", - "fontSmoothingDesc": "Включить отображение шрифтов ClearType", - "fullWindowDrag": "Перетаскивание окна на весь экран", - "fullWindowDragDesc": "Отображение содержимого окна во время перетаскивания", + "wallpaperDesc": "Показывать обои рабочего стола (отключение повышает производительность)", + "theming": "Темы", + "themingDesc": "Включить визуальные темы и стили", + "fontSmoothing": "Сглаживание шрифтов", + "fontSmoothingDesc": "Включить рендеринг шрифтов ClearType", + "fullWindowDrag": "Перетаскивание окон с содержимым", + "fullWindowDragDesc": "Показывать содержимое окна при перетаскивании", "desktopComposition": "Композиция рабочего стола", - "desktopCompositionDesc": "Включить эффекты стекла Aero", - "menuAnimations": "Анимация меню", - "menuAnimationsDesc": "Включить анимацию затухания и скольжения меню.", - "disableBitmapCaching": "Отключить кэширование растровых изображений", - "disableBitmapCachingDesc": "Отключите кэширование растровых изображений (это может помочь устранить сбои).", - "disableOffscreenCaching": "Отключить кэширование вне экрана", - "disableOffscreenCachingDesc": "Отключить кэширование вне экрана", - "disableGlyphCaching": "Отключить кэширование глифов", - "disableGlyphCachingDesc": "Отключите кэш глифов", + "desktopCompositionDesc": "Включить эффекты Aero Glass", + "menuAnimations": "Анимации меню", + "menuAnimationsDesc": "Включить анимацию исчезновения и сдвига меню", + "disableBitmapCaching": "Отключить кеширование растровых изображений", + "disableBitmapCachingDesc": "Отключить кеш растровых изображений (может помочь при сбоях)", + "disableOffscreenCaching": "Отключить внеэкранное кеширование", + "disableOffscreenCachingDesc": "Отключить внеэкранный кеш", + "disableGlyphCaching": "Отключить кеширование глифов", + "disableGlyphCachingDesc": "Отключить кеш глифов", "enableGfx": "Включить GFX", - "enableGfxDesc": "Используйте графический конвейер RemoteFX.", + "enableGfxDesc": "Использовать графический конвейер RemoteFX", "enablePrinting": "Включить печать", - "enablePrintingDesc": "Перенаправьте локальные принтеры на удаленную сессию.", + "enablePrintingDesc": "Перенаправлять локальные принтеры в удаленный сеанс", "enableDriveRedirection": "Включить перенаправление дисков", - "enableDriveRedirectionDesc": "Подключить локальную папку в качестве диска в удалённом сеансе.", + "enableDriveRedirectionDesc": "Подключать локальную папку как диск в удаленном сеансе", "createDrivePath": "Создать путь к диску", - "createDrivePathDesc": "Если папка не существует, она будет создана автоматически.", + "createDrivePathDesc": "Автоматически создать папку, если она не существует", "disableDownload": "Отключить загрузку", - "disableDownloadDesc": "Запретить загрузку файлов из удаленной сессии.", - "disableUpload": "Отключить загрузку", - "disableUploadDesc": "Запретить загрузку файлов в удаленную сессию.", - "enableTouch": "Включить сенсорное управление", - "enableTouchDesc": "Включить переадресацию сенсорного ввода", - "consoleSession": "Консольная сессия", - "consoleSessionDesc": "Вместо создания новой сессии подключитесь к консоли (сессия 0).", - "sendWolPacket": "Отправить пакет WOL", - "sendWolPacketDesc": "Отправьте «магический пакет», чтобы разбудить этот хост перед подключением.", + "disableDownloadDesc": "Запретить загрузку файлов из удаленного сеанса", + "disableUpload": "Отключить выгрузку", + "disableUploadDesc": "Запретить выгрузку файлов в удаленный сеанс", + "enableTouch": "Включить сенсорный ввод", + "enableTouchDesc": "Включить передачу сенсорного ввода", + "consoleSession": "Консольный сеанс", + "consoleSessionDesc": "Подключиться к консоли (сеанс 0) вместо создания нового сеанса", + "sendWolPacket": "Отправить WOL-пакет", + "sendWolPacketDesc": "Отправить magic-пакет для пробуждения хоста перед подключением", "disableCopy": "Отключить копирование", - "disableCopyDesc": "Предотвратить копирование текста из удаленной сессии.", + "disableCopyDesc": "Запретить копирование текста из удаленного сеанса", "disablePaste": "Отключить вставку", - "disablePasteDesc": "Предотвратить вставку текста в удалённую сессию.", - "createPathIfMissing": "Создайте путь, если он отсутствует.", - "createPathIfMissingDesc": "Автоматически создать каталог для записи", + "disablePasteDesc": "Запретить вставку текста в удаленный сеанс", + "createPathIfMissing": "Создать путь, если отсутствует", + "createPathIfMissingDesc": "Автоматически создать каталог для записей", "excludeOutput": "Исключить вывод", - "excludeOutputDesc": "Не записывайте вывод на экран (только метаданные).", + "excludeOutputDesc": "Не записывать вывод экрана (только метаданные)", "excludeMouse": "Исключить мышь", - "excludeMouseDesc": "Не записывайте движения мыши.", - "includeKeystrokes": "Включить нажатия клавиш", - "includeKeystrokesDesc": "Записывайте не только вывод на экран, но и сами нажатия клавиш.", + "excludeMouseDesc": "Не записывать движения мыши", + "includeKeystrokes": "Записывать нажатия", + "includeKeystrokesDesc": "Записывать необработанные нажатия клавиш в дополнение к выводу экрана", "vncPort": "Порт VNC", "vncPassword": "Пароль VNC", "vncUsernameOptional": "Имя пользователя (необязательно)", - "vncLeaveBlank": "Оставьте поле пустым, если оно не требуется.", + "vncLeaveBlank": "Оставьте пустым, если не требуется", "cursorMode": "Режим курсора", - "swapRedBlue": "Поменять местами красный и синий", - "swapRedBlueDesc": "Поменяйте местами красный и синий цветовые каналы (это исправит некоторые проблемы с цветопередачей).", - "readOnly": "Только для чтения", - "readOnlyDesc": "Просматривайте удаленный экран, не отправляя никаких входных данных.", + "swapRedBlue": "Поменять красный/синий", + "swapRedBlueDesc": "Обменять красный и синий цветовые каналы (исправляет некоторые проблемы с цветом)", + "readOnly": "Только чтение", + "readOnlyDesc": "Просмотр удаленного экрана без отправки ввода", "telnetPort": "Порт Telnet", "terminalType": "Тип терминала", "fontName": "Название шрифта", @@ -1366,8 +1436,8 @@ "colorScheme": "Цветовая схема", "backspaceKey": "Клавиша Backspace", "saveHostFirst": "Сначала сохраните хост.", - "sharingOptionsAfterSave": "Параметры доступа становятся доступны после сохранения хоста.", - "permissionLevel": "Уровень доступа", + "sharingOptionsAfterSave": "Параметры общего доступа будут доступны после сохранения хоста.", + "permissionLevel": "Уровень разрешений", "typeHeader": "Тип", "targetHeader": "Цель", "permissionHeader": "Разрешение", @@ -1380,88 +1450,146 @@ "commandPalette": { "searchPlaceholder": "Поиск хостов, команд или настроек...", "quickActions": "Быстрые действия", - "hostManager": "Менеджер хоста", - "hostManagerDesc": "Управляйте, добавляйте и редактируйте хосты.", + "hostManager": "Менеджер хостов", + "hostManagerDesc": "Управление, добавление и редактирование хостов", "addNewHost": "Добавить новый хост", - "addNewHostDesc": "Зарегистрируйте нового хоста", + "addNewHostDesc": "Зарегистрировать новый хост", "adminSettings": "Настройки администратора", - "adminSettingsDesc": "Настройка системных параметров и пользователей.", + "adminSettingsDesc": "Настройка системных параметров и пользователей", "userProfile": "Профиль пользователя", - "userProfileDesc": "Управляйте своей учетной записью и настройками.", - "addCredential": "Добавить учетные данные", - "addCredentialDesc": "Храните SSH-ключи или пароли.", - "tmuxMonitor": "Монитор Tmux", - "tmuxMonitorDesc": "Отслеживайте сессии tmux на всех ваших хостах.", - "recentActivity": "Последние события", + "userProfileDesc": "Управление учётной записью и настройками", + "addCredential": "Добавить учётные данные", + "addCredentialDesc": "Хранение SSH-ключей или паролей", + "tmuxMonitor": "Мониторинг Tmux", + "tmuxMonitorDesc": "Мониторинг сессий tmux на ваших хостах", + "recentActivity": "Недавняя активность", "serversAndHosts": "Серверы и хосты", - "noHostsFound": "Соответствующие хосты \"{{search}}\" не найдены", + "noHostsFound": "Не найдено хостов, соответствующих \"{{search}}\"", "links": "Ссылки", - "navigate": "Навигация", + "navigate": "Перейти", "select": "Выбрать", "toggleWith": "Переключить с помощью" }, "splitScreen": { - "paneEmpty": "Панель {{index}} - пустая", + "paneEmpty": "Панель {{index}} пуста", "noTabAssigned": "Вкладка не назначена", "focusedPane": "Активная панель" }, "connections": { "noConnections": "Нет подключений", - "noConnectionsDesc": "Откройте терминал, файловый менеджер или удаленный рабочий стол, чтобы увидеть подключения здесь.", - "connectedFor": "Подключено для {{duration}}", + "noConnectionsDesc": "Откройте терминал, файловый менеджер или удалённый рабочий стол для отображения подключений", + "connectedFor": "Подключено {{duration}}", "connected": "Подключено", "disconnected": "Отключено", "closeTab": "Закрыть вкладку", - "closeConnection": "Закрыть соединение", + "closeConnection": "Закрыть подключение", "forgetTab": "Забыть", "removeBackground": "Удалить", "reconnect": "Переподключиться", "reopenTab": "Открыть заново", - "sectionOpen": "Открыть", - "sectionBackground": "Фон", - "backgroundDesc": "Сессии сохраняются в течение 30 минут после отключения и могут быть восстановлены.", + "sectionOpen": "Открытые", + "sectionBackground": "Фоновые", + "backgroundDesc": "Сессии сохраняются в течение 30 минут после отключения и могут быть переподключены.", "persisted": "Сохранено в фоне", - "expiresIn": "Срок действия истекает через {{duration}}", - "search": "Поиск сессий...", - "noSearchResults": "Поисковые запросы не соответствуют вашим запросам.", - "rename": "Переименовать сессию" + "expiresIn": "Истекает через {{duration}}", + "search": "Поиск подключений...", + "noSearchResults": "Нет подключений, соответствующих поиску", + "rename": "Переименовать сессию", + "sectionSharedWithMe": "Поделился со мной", + "sharedBy": "Поделился {{username}}", + "join": "Присоединиться", + "sharedSessionLabel": "{{hostName}} (совместно)" + }, + "sessionSharing": { + "guestView": { + "loading": "Подключение к общей сессии...", + "linkInvalid": "Данная ссылка для общего доступа недействительна, истек срок ее действия или она была аннулирована.", + "rateLimited": "Слишком много попыток, пожалуйста, попробуйте еще раз чуть позже.", + "sessionEnded": "Данная сессия завершена.", + "readOnlyBadge": "Только для просмотра" + }, + "modalTitle": "Совместное использование сессии", + "shareButton": "Делиться", + "notReadyToShare": "Сессия пока не готова к публикации.", + "modeTab": { + "link": "Связь", + "user": "Пользователь" + }, + "linkModeDescription": "Присоединиться может любой, у кого есть эта ссылка, регистрация не требуется.", + "userModeDescription": "Предоставьте доступ конкретному пользователю, у которого уже есть доступ к этому хосту. Если у него еще нет доступа, сначала предоставьте ему доступ к хосту или используйте ссылку. После предоставления доступа сессия отобразится на вкладке «Подключения» этого пользователя.", + "permissionLevel": { + "label": "Уровень доступа", + "readOnly": "Только для чтения", + "readOnlyDescription": "Можно наблюдать за сессией в прямом эфире, но нельзя печатать или взаимодействовать с ней.", + "readWrite": "Чтение-запись", + "readWriteDescription": "Может печатать и взаимодействовать с сессией так же, как и её владелец." + }, + "expiryLabel": "Срок действия ссылки истекает", + "createLinkButton": "Создать ссылку", + "createShareButton": "Поделиться с пользователем", + "searchUsersPlaceholder": "Поиск пользователей...", + "noUsersFound": "Пользователи не найдены", + "linkCreated": "Ссылка для обмена создана", + "linkCopied": "Ссылка скопирована в буфер обмена", + "copyLink": "Скопировать ссылку", + "shareCreated": "Сессия была проведена совместно. Она отобразится на вкладке «Подключения».", + "shareFailed": "Не удалось создать общий ресурс.", + "userLacksHostAccess": "У этого пользователя пока нет доступа к данному хосту. Сначала предоставьте ему доступ к хосту или используйте ссылку.", + "activeShares": "Активные акции", + "noActiveShares": "В текущей торговой сессии нет активных акций.", + "revoke": "Отменить", + "revokeConfirmTitle": "Отозвать эту публикацию?", + "revokeConfirmDescription": "Любой, кто воспользуется этой ссылкой, немедленно потеряет к ней доступ.", + "revoked": "Акция аннулирована", + "revokeFailed": "Не удалось отозвать акцию.", + "joinCount": "{{count}} присоединиться", + "joinCount_other": "{{count}} присоединяется", + "expiresAt": "Срок действия истекает {{date}}", + "linkShareBadge": "Связь", + "userShareBadge": "Пользователь: {{username}}", + "loadSharesFailed": "Не удалось загрузить активные акции." }, "guacamole": { "connecting": "Подключение к сессии {{type}}...", "connectionError": "Ошибка подключения", - "connectionFailed": "Соединение не удалось.", - "failedToConnect": "Не удалось получить токен подключения.", + "connectionFailed": "Подключение не удалось", + "failedToConnect": "Не удалось получить токен подключения", "hostNotFound": "Хост не найден", "noHostSelected": "Хост не выбран", "reconnect": "Переподключиться", - "retry": "Повторить попытку", - "guacdUnavailable": "Служба удаленного рабочего стола (guacd) недоступна. Убедитесь, что guacd запущена, доступна и правильно настроена в параметрах администратора.", + "retry": "Повторить", + "guacdUnavailable": "Служба удалённого рабочего стола (guacd) недоступна. Убедитесь, что guacd запущен, доступен и правильно настроен в настройках администратора.", + "credentialPromptTitle": "Введите учетные данные RDP.", + "credentialPromptDescription": "На этом хосте настроен запрос учетных данных при подключении. Они используются только в течение этой сессии и не сохраняются.", + "connect": "Соединять", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Экран блокировки)", - "winKey": "Ключ Windows", + "winL": "Win+L (Блокировка экрана)", + "winKey": "Клавиша Windows", "ctrl": "Ctrl", - "alt": "Альт", - "shift": "Сдвиг", + "alt": "Alt", + "shift": "Shift", "win": "Win", - "stickyActive": "{{key}} (защелкнуто - нажмите, чтобы отпустить)", - "stickyInactive": "{{key}} (нажмите, чтобы защелкнуть)", - "esc": "Esc", - "tab": "Вкладка", + "stickyActive": "{{key}} (зафиксирована – кликните для отключения)", + "stickyInactive": "{{key}} (кликните для фиксации)", + "esc": "Escape", + "tab": "Tab", "home": "Home", - "end": "Конец", - "pageUp": "Страница вверх", - "pageDown": "Вниз страницы", - "arrowUp": "Стрелка вверх", - "arrowDown": "Стрела вниз", - "arrowLeft": "Стрелка влево", - "arrowRight": "Стрелка вправо", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", "fnToggle": "Функциональные клавиши", - "reconnect": "Переподключить сессию", - "collapse": "Свернуть панель инструментов", - "expand": "Развернуть панель инструментов", - "dragHandle": "Перетащите для изменения положения." + "reconnect": "Переподключить сеанс", + "collapse": "Свернуть панель", + "expand": "Развернуть панель", + "dragHandle": "Перетащите для перемещения", + "switchToTrackpad": "Переключитесь в режим трекпада (перетащите для перемещения курсора, коснитесь для щелчка).", + "switchToTouch": "Переключитесь в сенсорный режим (коснитесь непосредственно того места, куда хотите нажать)." } }, "terminal": { @@ -1471,78 +1599,83 @@ "reconnect": "Переподключиться", "connectionLost": "Соединение потеряно", "connected": "Подключено", - "clipboardWriteFailed": "Не удалось скопировать в буфер обмена. Убедитесь, что страница загружается по протоколу HTTPS или с локального сервера.", - "clipboardReadFailed": "Не удалось прочитать данные из буфера обмена. Убедитесь, что права доступа к буферу обмена предоставлены.", - "clipboardHttpWarning": "Для вставки требуется HTTPS. Используйте Ctrl+Shift+V или разместите Termix по протоколу HTTPS.", - "passwordPromptFillTitle": "Ввести сохраненный пароль в это поле?", - "unknownError": "Произошла неизвестная ошибка.", + "clipboardWriteFailed": "Не удалось скопировать в буфер обмена. Убедитесь, что страница загружена по HTTPS или с localhost.", + "clipboardReadFailed": "Не удалось прочитать из буфера обмена. Убедитесь, что предоставлены разрешения на доступ к буферу обмена.", + "clipboardHttpWarning": "Вставка требует HTTPS. Используйте Ctrl+Shift+V или обслуживайте Termix через HTTPS.", + "passwordPromptFillTitle": "Вставить сохраненный пароль в этот запрос?", + "unknownError": "Произошла неизвестная ошибка", "websocketError": "Ошибка подключения WebSocket", "connecting": "Подключение...", - "noHostSelected": "Хост не выбран", + "noHostSelected": "Не выбран хост", "reconnecting": "Переподключение... ({{attempt}}/{{max}})", - "reconnected": "Переподключение выполнено успешно", - "tmuxSessionCreated": "Создана сессия tmux: {{name}}", - "tmuxSessionAttached": "Прикреплена сессия tmux: {{name}}", - "tmuxUnavailable": "tmux не установлен на удаленном хосте, поэтому используется стандартная оболочка.", + "reconnected": "Успешно переподключено", + "tmuxSessionCreated": "Сессия tmux создана: {{name}}", + "tmuxSessionAttached": "Сессия tmux подключена: {{name}}", + "tmuxUnavailable": "tmux не установлен на удалённом хосте, переход на стандартную оболочку", "tmuxSessionPickerTitle": "Сессии tmux", - "tmuxSessionPickerDesc": "На этом хосте обнаружены существующие сессии tmux. Выберите одну для повторного подключения или создания новой сессии.", + "tmuxSessionPickerDesc": "Найдены существующие сессии tmux на этом хосте. Выберите одну для переподключения или создайте новую сессию.", "tmuxWindows": "Окна", "tmuxWindowCount": "{{count}} окно", - "tmuxAttached": "Прикрепленные клиенты", - "tmuxAttachedCount": "{{count}} прикреплено", + "tmuxAttached": "Подключённые клиенты", + "tmuxAttachedCount": "{{count}} подключено", "tmuxLastActivity": "Последняя активность", - "tmuxTimeJustNow": "прямо сейчас", + "tmuxTimeJustNow": "только что", "tmuxTimeMinutes": "{{count}}м назад", "tmuxTimeHours": "{{count}}ч назад", - "tmuxTimeDays": "{{count}}дней назад", + "tmuxTimeDays": "{{count}}д назад", "tmuxCreateNew": "Начать новую сессию", - "tmuxCopyHint": "Отрегулируйте выделение и нажмите Enter, чтобы скопировать в буфер обмена.", - "tmuxDetach": "Отключитесь от сессии tmux.", - "tmuxDetached": "Отключение от сессии tmux", + "tmuxCopyHint": "Скорректируйте выделение и нажмите Enter, чтобы скопировать в буфер обмена", + "tmuxDetach": "Отключиться от сессии tmux", + "tmuxDetached": "Отключён от сессии tmux", "maxReconnectAttemptsReached": "Достигнуто максимальное количество попыток переподключения", "closeTab": "Закрыть", - "connectionTimeout": "Таймаут соединения", + "connectionTimeout": "Тайм-аут подключения", "terminalTitle": "Терминал - {{host}}", "terminalWithPath": "Терминал - {{host}}:{{path}}", - "runTitle": "Запуск {{command}} - {{host}}", - "totpRequired": "Требуется двухфакторная аутентификация.", - "totpCodeLabel": "Проверочный код", - "totpVerify": "Проверить", + "runTitle": "Выполнение {{command}} - {{host}}", + "totpRequired": "Требуется двухфакторная аутентификация", + "totpCodeLabel": "Код подтверждения", + "totpVerify": "Подтвердить", + "mfaPromptRequired": "Требуется аутентификация", + "mfaPushRequired": "Требуется push-аутентификация", + "mfaMenuPlaceholder": "Введите свой ответ", + "mfaWaitingApproval": "Ожидание подтверждения на вашем устройстве...", + "mfaSendRequest": "Отправить запрос", "warpgateAuthRequired": "Требуется аутентификация Warpgate", "warpgateSecurityKey": "Ключ безопасности", - "warpgateAuthUrl": "URL-адрес аутентификации", + "warpgateAuthUrl": "URL аутентификации", "warpgateOpenBrowser": "Открыть в браузере", - "warpgateContinue": "Я завершил аутентификацию.", - "opksshAuthRequired": "Требуется аутентификация OPKSSH.", - "opksshAuthDescription": "Для продолжения необходимо пройти полную аутентификацию в вашем браузере. Данная сессия будет действительна в течение 24 часов.", - "opksshOpenBrowser": "Откройте браузер для аутентификации.", + "warpgateContinue": "Аутентификация завершена", + "opksshAuthRequired": "Требуется аутентификация OPKSSH", + "opksshAuthDescription": "Завершите аутентификацию в браузере, чтобы продолжить. Сеанс будет действителен в течение 24 часов.", + "opksshOpenBrowser": "Открыть браузер для аутентификации", "opksshWaitingForAuth": "Ожидание аутентификации в браузере...", "opksshAuthenticating": "Обработка аутентификации...", - "opksshTimeout": "Истекло время аутентификации. Пожалуйста, попробуйте еще раз.", - "opksshAuthFailed": "Аутентификация не удалась. Пожалуйста, проверьте свои учетные данные и повторите попытку.", - "opksshSignInWith": "Войдите с помощью {{provider}}", - "vaultAuthTitle": "Требуется вход в хранилище.", - "vaultAuthDescription": "Открылось окно для входа в HashiCorp Vault. Завершите вход; это соединение будет продолжено автоматически.", - "vaultAuthFailed": "Аутентификация хранилища не удалась. Пожалуйста, попробуйте еще раз.", - "vaultReopen": "Снова откройте окно входа в систему.", + "opksshTimeout": "Время аутентификации истекло. Попробуйте снова.", + "opksshAuthFailed": "Аутентификация не удалась. Проверьте учётные данные и попробуйте снова.", + "opksshSignInWith": "Войти через {{provider}}", + "vaultAuthTitle": "Требуется вход в Vault", + "vaultAuthDescription": "Открыто окно для входа в HashiCorp Vault. Завершите вход там; подключение продолжится автоматически.", + "vaultAuthFailed": "Аутентификация Vault не удалась. Попробуйте снова.", + "vaultReopen": "Повторно открыть окно входа", "sudoPasswordPopupTitle": "Вставить пароль?", "linkDialogTitle": "Открыть ссылку", "linkDialogOpen": "Открыть", - "linkDialogCopy": "Скопировать", - "websocketAbnormalClose": "Соединение неожиданно разорвано. Это может быть связано с проблемами обратного прокси-сервера или конфигурации SSL. Пожалуйста, проверьте журналы сервера.", - "connectionLogTitle": "Журнал подключений", - "connectionLogCopy": "Скопировать журналы в буфер обмена", - "connectionLogEmpty": "Журналы подключений пока отсутствуют.", - "connectionLogWaiting": "Ожидание журналов подключений...", - "connectionLogCopied": "Журналы подключений скопированы в буфер обмена", - "connectionLogCopyFailed": "Не удалось скопировать логи в буфер обмена.", - "connectionRejected": "Соединение отклонено сервером. Пожалуйста, проверьте настройки аутентификации и сети.", - "hostKeyRejected": "Проверка ключа хоста SSH отклонена. Соединение разорвано.", - "sessionTakenOver": "Сессия открыта в другой вкладке. Переподключение...", + "linkDialogCopy": "Копировать", + "websocketAbnormalClose": "Соединение неожиданно закрыто. Возможно, проблема с обратным прокси или конфигурацией SSL. Проверьте журналы сервера.", + "connectionLogTitle": "Журнал подключения", + "connectionLogCopy": "Копировать журналы в буфер обмена", + "connectionLogEmpty": "Журналы подключения пока отсутствуют", + "connectionLogWaiting": "Ожидание журналов подключения...", + "connectionLogCopied": "Журналы подключения скопированы в буфер обмена", + "connectionLogCopyFailed": "Не удалось скопировать журналы в буфер обмена", + "connectionRejected": "Подключение отклонено сервером. Проверьте аутентификацию и сетевые настройки.", + "hostKeyRejected": "Проверка ключа хоста SSH отклонена. Подключение отменено.", + "sessionTakenOver": "Сеанс был открыт в другой вкладке. Переподключение...", "split": { "splitTab": "Разделить вкладку", - "addToSplit": "Добавить в раздел", - "removeFromSplit": "Удалить из Сплита" + "addToSplit": "Добавить в разделённую область", + "removeFromSplit": "Убрать из разделённой области" } }, "fileManager": { @@ -1553,416 +1686,416 @@ "uploadFile": "Загрузить файл", "downloadFile": "Скачать", "extractArchive": "Извлечь архив", - "extractingArchive": "Извлечение {{name}}...", - "archiveExtractedSuccessfully": "{{name}} успешно извлечено", - "extractFailed": "Извлечение не удалось.", + "extractingArchive": "Распаковка {{name}}...", + "archiveExtractedSuccessfully": "{{name}} успешно распакован", + "extractFailed": "Ошибка распаковки", "compressFile": "Сжать файл", "compressFiles": "Сжать файлы", - "compressFilesDesc": "Сжать {{count}} объектов в архив", - "archiveName": "Название архива", - "enterArchiveName": "Введите название архива...", + "compressFilesDesc": "Сжать {{count}} элементов в архив", + "archiveName": "Имя архива", + "enterArchiveName": "Введите имя архива...", "compressionFormat": "Формат сжатия", "selectedFiles": "Выбранные файлы", - "andMoreFiles": "и {{count}} ещё...", + "andMoreFiles": "и ещё {{count}}...", "compress": "Сжать", - "compressingFiles": "Сжатие {{count}} объектов в {{name}}...", - "filesCompressedSuccessfully": "{{name}} успешно создано", - "compressFailed": "Сжатие не удалось", + "compressingFiles": "Сжатие {{count}} элементов в {{name}}...", + "filesCompressedSuccessfully": "{{name}} успешно создан", + "compressFailed": "Ошибка сжатия", "edit": "Редактировать", - "preview": "Предварительный просмотр", - "previous": "Предыдущий", - "next": "Следующий", + "preview": "Предпросмотр", + "previous": "Назад", + "next": "Далее", "pageXOfY": "Страница {{current}} из {{total}}", - "zoomOut": "Уменьшить масштаб", - "zoomIn": "Увеличить масштаб", + "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": "Эта папка пуста.", + "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": "Копия", + "selectHostToStart": "Выберите хост для начала управления файлами", + "sshRequiredForFileManager": "Файловому менеджеру требуется SSH. У этого хоста SSH не включен.", + "failedToConnect": "Не удалось подключиться к SSH", + "failedToLoadDirectory": "Не удалось загрузить каталог", + "noSSHConnection": "Нет доступного SSH-подключения", + "copy": "Копировать", "cut": "Вырезать", "paste": "Вставить", - "copyPath": "Скопировать путь", + "copyPath": "Копировать путь", "copyPaths": "Копировать пути", "delete": "Удалить", "properties": "Свойства", "refresh": "Обновить", "downloadFiles": "Скачать {{count}} файлов в браузер", - "copyFiles": "Копировать {{count}} объектов", - "cutFiles": "Вырезать {{count}} объектов", - "deleteFiles": "Удалить {{count}} объектов", - "filesCopiedToClipboard": "{{count}} объекты скопированы в буфер обмена", - "filesCutToClipboard": "{{count}} элементы, вырезанные в буфер обмена", - "pathCopiedToClipboard": "Путь скопирован в буфер обмена.", - "pathsCopiedToClipboard": "{{count}} пути скопированы в буфер обмена", - "failedToCopyPath": "Не удалось скопировать путь в буфер обмена.", + "copyFiles": "Копировать {{count}} элементов", + "cutFiles": "Вырезать {{count}} элементов", + "deleteFiles": "Удалить {{count}} элементов", + "filesCopiedToClipboard": "{{count}} элементов скопировано в буфер обмена", + "filesCutToClipboard": "{{count}} элементов вырезано в буфер обмена", + "pathCopiedToClipboard": "Путь скопирован в буфер обмена", + "pathsCopiedToClipboard": "{{count}} путей скопировано в буфер обмена", + "failedToCopyPath": "Не удалось скопировать путь в буфер обмена", "copyFolderLink": "Скопировать ссылку на папку", - "copyCurrentFolderLink": "Скопировать ссылку в текущую папку", - "folderLinkCopied": "Ссылка на папку скопирована в буфер обмена.", - "failedToCopyFolderLink": "Не удалось скопировать ссылку на папку.", - "movedItems": "Перемещено {{count}} объектов", - "failedToDeleteItem": "Не удалось удалить объект", - "itemRenamedSuccessfully": "{{type}} успешно переименовано", - "failedToRenameItem": "Не удалось переименовать объект", + "copyCurrentFolderLink": "Скопировать ссылку на текущую папку", + "folderLinkCopied": "Ссылка на папку скопирована в буфер обмена", + "failedToCopyFolderLink": "Не удалось скопировать ссылку на папку", + "movedItems": "Перемещено {{count}} элементов", + "failedToDeleteItem": "Не удалось удалить элемент", + "itemRenamedSuccessfully": "{{type}} успешно переименован(а)", + "failedToRenameItem": "Не удалось переименовать элемент", "download": "Скачать", - "openExternalEditor": "Открыть извне", - "chooseExternalEditor": "Выберите внешний редактор", - "externalEditorSelected": "Выбран внешний редактор", - "externalEditorDesktopOnly": "Внешний редактор доступен только в настольном приложении.", - "externalEditorOpened": "Открыто во внешнем редакторе. Сохранение там загрузит изменения обратно на сервер.", - "failedToOpenExternalEditor": "Не удалось открыть внешний редактор.", - "failedToSelectExternalEditor": "Не удалось выбрать внешнего редактора.", - "permissions": "Разрешения", + "openExternalEditor": "Открыть во внешнем редакторе", + "chooseExternalEditor": "Выбрать внешний редактор", + "externalEditorSelected": "Внешний редактор выбран", + "externalEditorDesktopOnly": "Внешний редактор доступен только в настольном приложении", + "externalEditorOpened": "Открыт во внешнем редакторе. Сохранение в нём загрузит изменения обратно на сервер.", + "failedToOpenExternalEditor": "Не удалось открыть внешний редактор", + "failedToSelectExternalEditor": "Не удалось выбрать внешний редактор", + "permissions": "Права доступа", "size": "Размер", "modified": "Изменён", "path": "Путь", "confirmDelete": "Вы уверены, что хотите удалить {{name}}?", - "permissionDenied": "Доступ запрещен", + "permissionDenied": "Доступ запрещён", "serverError": "Ошибка сервера", - "fileSavedSuccessfully": "Файл успешно сохранен.", - "failedToSaveFile": "Не удалось сохранить файл.", - "confirmDeleteSingleItem": "Вы уверены, что хотите навсегда удалить \"{{name}}\"?", - "confirmDeleteMultipleItems": "Вы уверены, что хотите навсегда удалить {{count}} объектов?", - "confirmDeleteMultipleItemsWithFolders": "Вы уверены, что хотите навсегда удалить {{count}} объектов? Это включает папки и их содержимое.", - "confirmDeleteFolder": "Вы уверены, что хотите навсегда удалить папку \"{{name}}\" и всё её содержимое?", - "permanentDeleteWarning": "Это действие необратимо. Указанный(е) объект(ы) будут безвозвратно удалены с сервера.", + "fileSavedSuccessfully": "Файл успешно сохранён", + "failedToSaveFile": "Не удалось сохранить файл", + "confirmDeleteSingleItem": "Вы уверены, что хотите безвозвратно удалить \"{{name}}\"?", + "confirmDeleteMultipleItems": "Вы уверены, что хотите безвозвратно удалить {{count}} элементов?", + "confirmDeleteMultipleItemsWithFolders": "Вы уверены, что хотите безвозвратно удалить {{count}} элементов? Включая папки и их содержимое.", + "confirmDeleteFolder": "Вы уверены, что хотите безвозвратно удалить папку \"{{name}}\" и всё её содержимое?", + "permanentDeleteWarning": "Это действие необратимо. Элемент(ы) будут безвозвратно удалены с сервера.", "recent": "Недавние", - "pinned": "Закреплено", + "pinned": "Закреплённые", "folderShortcuts": "Ярлыки папок", - "failedToReconnectSSH": "Не удалось повторно подключиться к SSH-сессии.", - "openTerminalHere": "Откройте терминал здесь", + "failedToReconnectSSH": "Не удалось переподключить SSH-сессию", + "openTerminalHere": "Открыть терминал здесь", "run": "Запустить", - "openTerminalInFolder": "Откройте Терминал в этой папке", - "openTerminalInFileLocation": "Откройте терминал по адресу расположения файла.", + "openTerminalInFolder": "Открыть терминал в этой папке", + "openTerminalInFileLocation": "Открыть терминал в расположении файла", "runningFile": "Выполняется - {{file}}", - "onlyRunExecutableFiles": "Можно запускать только исполняемые файлы.", - "directories": "Справочники", - "removedFromRecentFiles": "\"{{name}}\" удален из недавних файлов", - "removeFailed": "Ошибка удаления", - "unpinnedSuccessfully": "Откреплено \"{{name}}\" успешно", - "unpinFailed": "Открепление не удалось", - "removedShortcut": "Удалён ярлык \"{{name}}\"", - "removeShortcutFailed": "Удаление ярлыка не удалось.", - "clearedAllRecentFiles": "Удалены все последние файлы.", + "onlyRunExecutableFiles": "Можно запускать только исполняемые файлы", + "directories": "Каталоги", + "removedFromRecentFiles": "Удалено \"{{name}}\" из недавних файлов", + "removeFailed": "Не удалось удалить", + "unpinnedSuccessfully": "Успешно откреплено \"{{name}}\"", + "unpinFailed": "Не удалось открепить", + "removedShortcut": "Ярлык \"{{name}}\" удалён", + "removeShortcutFailed": "Не удалось удалить ярлык", + "clearedAllRecentFiles": "Очищен список недавних файлов", "clearFailed": "Очистка не удалась", - "removeFromRecentFiles": "Удалить из последних файлов", - "clearAllRecentFiles": "Удалите все недавно использованные файлы.", + "removeFromRecentFiles": "Удалить из списка недавних файлов", + "clearAllRecentFiles": "Очистить список недавних файлов", "unpinFile": "Открепить файл", "removeShortcut": "Удалить ярлык", "pinFile": "Закрепить файл", "addToShortcuts": "Добавить в ярлыки", "pasteFailed": "Вставка не удалась", - "noUndoableActions": "Нет необратимых действий", - "undoCopySuccess": "Операция копирования отменена: Удалены скопированные файлы {{count}}", - "undoCopyFailedDelete": "Отмена не удалась: Не удалось удалить скопированные файлы.", - "undoCopyFailedNoInfo": "Отмена не удалась: не удалось найти информацию о скопированном файле.", - "undoMoveSuccess": "Операция перемещения отменена: файлы {{count}} перемещены обратно в исходное местоположение.", - "undoMoveFailedMove": "Отмена не удалась: Не удалось переместить файлы обратно.", - "undoMoveFailedNoInfo": "Отмена не удалась: не удалось найти информацию о перемещенном файле.", - "undoDeleteNotSupported": "Операция удаления необратима: файлы были безвозвратно удалены с сервера.", + "noUndoableActions": "Нет действий для отмены", + "undoCopySuccess": "Отмена копирования: удалено {{count}} скопированных файлов", + "undoCopyFailedDelete": "Отмена не удалась: не удалось удалить скопированные файлы", + "undoCopyFailedNoInfo": "Отмена не удалась: не найдена информация о скопированных файлах", + "undoMoveSuccess": "Отмена перемещения: {{count}} файлов возвращено в исходное расположение", + "undoMoveFailedMove": "Отмена не удалась: не удалось вернуть файлы", + "undoMoveFailedNoInfo": "Отмена не удалась: не найдена информация о перемещённых файлах", + "undoDeleteNotSupported": "Операцию удаления нельзя отменить: файлы навсегда удалены с сервера", "undoTypeNotSupported": "Неподдерживаемый тип операции отмены", - "undoOperationFailed": "Операция отмены не удалась.", + "undoOperationFailed": "Операция отмены не удалась", "unknownError": "Неизвестная ошибка", "confirm": "Подтвердить", - "find": "Находить...", + "find": "Поиск...", "replace": "Заменить", "downloadInstead": "Скачать вместо", - "keyboardShortcuts": "Клавиатурные сочетания", + "keyboardShortcuts": "Горячие клавиши", "searchAndReplace": "Поиск и замена", "editing": "Редактирование", "search": "Поиск", "findNext": "Найти далее", "findPrevious": "Найти предыдущий", "save": "Сохранить", - "selectAll": "Выбрать все", + "selectAll": "Выделить всё", "undo": "Отменить", "redo": "Повторить", "moveLineUp": "Переместить строку вверх", "moveLineDown": "Переместить строку вниз", - "toggleComment": "Скрыть/Показать комментарий", - "autoComplete": "Автозаполнение", + "toggleComment": "Переключить комментарий", + "autoComplete": "Автодополнение", "imageLoadError": "Не удалось загрузить изображение", - "startTyping": "Начните печатать...", + "startTyping": "Начните вводить...", "unknownSize": "Неизвестный размер", "fileIsEmpty": "Файл пуст", - "largeFileWarning": "Предупреждение о больших файлах", - "largeFileWarningDesc": "Размер этого файла составляет {{size}} , что может вызвать проблемы с производительностью при открытии в текстовом формате.", - "fileNotFoundAndRemoved": "Файл \"{{name}}\" не найден и был удален из списка последних/закрепленных файлов.", + "largeFileWarning": "Предупреждение о большом файле", + "largeFileWarningDesc": "Размер этого файла {{size}}, что может вызвать проблемы с производительностью при открытии в текстовом виде.", + "fileNotFoundAndRemoved": "Файл \"{{name}}\" не найден и был удалён из недавних/закреплённых файлов", "failedToLoadFile": "Не удалось загрузить файл: {{error}}", - "serverErrorOccurred": "Произошла ошибка сервера. Пожалуйста, попробуйте позже.", + "serverErrorOccurred": "Произошла ошибка сервера. Попробуйте позже.", "autoSaveFailed": "Автосохранение не удалось", - "fileAutoSaved": "Файл автоматически сохранен", + "fileAutoSaved": "Файл автосохранён", "moveFileFailed": "Не удалось переместить {{name}}", - "moveOperationFailed": "Операция перемещения не удалась.", - "canOnlyCompareFiles": "Можно сравнить только два файла.", + "moveOperationFailed": "Операция перемещения не удалась", + "canOnlyCompareFiles": "Можно сравнивать только два файла", "comparingFiles": "Сравнение файлов: {{file1}} и {{file2}}", - "dragFailed": "Операция перетаскивания не удалась.", - "filePinnedSuccessfully": "Файл \"{{name}}\" успешно закреплен", + "dragFailed": "Операция перетаскивания не удалась", + "filePinnedSuccessfully": "Файл \"{{name}}\" успешно закреплён", "pinFileFailed": "Не удалось закрепить файл", - "fileUnpinnedSuccessfully": "Файл \"{{name}}\" успешно откреплен", - "unpinFileFailed": "Не удалось открепить файл.", + "fileUnpinnedSuccessfully": "Файл \"{{name}}\" успешно откреплён", + "unpinFileFailed": "Не удалось открепить файл", "shortcutAddedSuccessfully": "Ярлык папки \"{{name}}\" успешно добавлен", - "addShortcutFailed": "Не удалось добавить ярлык.", - "operationCompletedSuccessfully": "{{operation}} {{count}} успешно добавлены объекты", - "operationCompleted": "{{operation}} {{count}} предметов", + "addShortcutFailed": "Не удалось добавить ярлык", + "operationCompletedSuccessfully": "{{operation}} {{count}} элементов успешно", + "operationCompleted": "{{operation}} {{count}} элементов", "downloadFileSuccess": "Файл {{name}} успешно загружен", - "downloadFileFailed": "Загрузка не удалась.", - "moveTo": "Переместиться в {{name}}", - "diffCompareWith": "Сравните с {{name}}", - "dragOutsideToDownload": "Перетащите за пределы окна, чтобы загрузить ({{count}} файлов)", + "downloadFileFailed": "Не удалось загрузить", + "moveTo": "Переместить в {{name}}", + "diffCompareWith": "Сравнить с {{name}}", + "dragOutsideToDownload": "Перетащите за пределы окна для загрузки ({{count}} файлов)", "newFolderDefault": "Новая папка", - "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Успешно перемещено {{count}} объектов в {{target}}", + "newFileDefault": "Новый файл.txt", + "successfullyMovedItems": "Успешно перемещено {{count}} элементов в {{target}}", "move": "Переместить", "searchInFile": "Поиск в файле (Ctrl+F)", "showKeyboardShortcuts": "Показать сочетания клавиш", "decreaseFontSize": "Уменьшить размер шрифта", - "increaseFontSize": "Увеличьте размер шрифта", - "startWritingMarkdown": "Начните создавать контент в формате Markdown...", + "increaseFontSize": "Увеличить размер шрифта", + "startWritingMarkdown": "Начните писать Markdown-контент...", "loadingFileComparison": "Загрузка сравнения файлов...", "reload": "Перезагрузить", "compare": "Сравнить", "sideBySide": "Бок о бок", "inline": "Встроенный", - "fileComparison": "Сравнение файлов: {{file1}} против {{file2}}", + "fileComparison": "Сравнение файлов: {{file1}} и {{file2}}", "fileTooLarge": "Файл слишком большой: {{error}}", - "sshConnectionFailed": "SSH-соединение не удалось. Пожалуйста, проверьте соединение с {{name}} ({{ip}}:{{port}})", + "sshConnectionFailed": "Ошибка SSH-подключения. Проверьте подключение к {{name}} ({{ip}}:{{port}})", "loadFileFailed": "Не удалось загрузить файл: {{error}}", "connecting": "Подключение...", - "connectedSuccessfully": "Соединение установлено успешно.", - "totpVerificationFailed": "Проверка TOTP не удалась.", - "warpgateVerificationFailed": "Аутентификация Warpgate не удалась.", - "authenticationFailed": "Аутентификация не удалась", - "incorrectPassphrase": "Неверная парольная фраза. Пожалуйста, попробуйте еще раз.", - "verificationCodePrompt": "Проверочный код:", + "connectedSuccessfully": "Подключено успешно", + "totpVerificationFailed": "Ошибка проверки TOTP", + "warpgateVerificationFailed": "Ошибка аутентификации Warpgate", + "authenticationFailed": "Ошибка аутентификации", + "incorrectPassphrase": "Неверная парольная фраза. Попробуйте еще раз.", + "verificationCodePrompt": "Код подтверждения:", "changePermissions": "Изменить права доступа", "currentPermissions": "Текущие права доступа", "owner": "Владелец", "group": "Группа", - "others": "Другие", + "others": "Остальные", "read": "Чтение", "write": "Запись", - "execute": "Выполнить", - "permissionsChangedSuccessfully": "Права доступа успешно изменены.", - "failedToChangePermissions": "Не удалось изменить права доступа.", + "execute": "Выполнение", + "permissionsChangedSuccessfully": "Права доступа успешно изменены", + "failedToChangePermissions": "Не удалось изменить права доступа", "name": "Имя", - "sortByName": "Имя", - "sortByDate": "Дата изменения", - "sortBySize": "Размер", + "sortByName": "По имени", + "sortByDate": "По дате изменения", + "sortBySize": "По размеру", "ascending": "По возрастанию", "descending": "По убыванию", - "root": "Корневая", - "new": "Новый", - "sortBy": "Сортировать по", - "items": "Объектов", + "root": "Корень", + "new": "Создать", + "sortBy": "Сортировка", + "items": "Элементов", "selected": "Выбрано", "editor": "Редактор", "octal": "Восьмеричный", "storage": "Хранилище", "disk": "Диск", - "used": "Использовано", + "used": "Занято", "of": "из", "toggleSidebar": "Переключить боковую панель", - "cannotLoadPdf": "Не удаётся загрузить PDF-файл.", - "pdfLoadError": "Произошла ошибка при загрузке этого PDF-файла.", - "loadingPdf": "Загрузка PDF-файла...", + "cannotLoadPdf": "Не удаётся загрузить PDF", + "pdfLoadError": "Ошибка при загрузке этого PDF-файла.", + "loadingPdf": "Загрузка PDF...", "loadingPage": "Загрузка страницы..." }, "transfer": { - "copyToHost": "Скопировать на хост…", + "copyToHost": "Копировать на хост…", "moveToHost": "Переместить на хост…", - "copyItemsToHost": "Скопировать {{count}} объектов на хост…", - "moveItemsToHost": "Переместить {{count}} объектов на хост…", - "noHostsConnected": "Других хостов для файловых менеджеров нет в наличии.", - "noHostsConnectedHint": "Добавьте еще один SSH-хост с включенным файловым менеджером в диспетчере хостов.", + "copyItemsToHost": "Копировать {{count}} элементов на хост…", + "moveItemsToHost": "Переместить {{count}} элементов на хост…", + "noHostsConnected": "Нет других доступных хостов с файловым менеджером.", + "noHostsConnectedHint": "Добавьте другой SSH-хост с включенным Файловым Менеджером в Менеджере Хостов.", "selectDestinationHost": "Выберите целевой хост", "destinationPath": "Путь назначения", - "recentDestinations": "Недавние направления", - "collapseRecentDestinations": "Свернуть список последних направлений", - "expandRecentDestinations": "Развернуть список недавно открытых направлений", - "browseFolders": "Просмотрите папки назначения", - "browseDestination": "Просмотрите или введите путь", - "confirmCopy": "Копия", + "recentDestinations": "Недавние назначения", + "collapseRecentDestinations": "Свернуть недавние назначения", + "expandRecentDestinations": "Развернуть недавние назначения", + "browseFolders": "Обзор папок назначения", + "browseDestination": "Обзор или ввод пути", + "confirmCopy": "Копировать", "confirmMove": "Переместить", "transferring": "Передача…", "compressing": "Сжатие…", "extracting": "Извлечение…", - "transferringItems": "Передача {{current}} объектов {{total}}…", + "transferringItems": "Передача {{current}} из {{total}} элементов…", "transferSuccess": "Передача завершена", - "transferError": "Передача не удалась", - "transferPartial": "Передача завершена с ошибками {{count}}", + "transferError": "Ошибка передачи", + "transferPartial": "Передача завершена с {{count}} ошибками", "transferPartialHint": "Не удалось передать: {{paths}}", - "itemsSummary": "{{count}} объекты", - "destMustBeDirectory": "В качестве пункта назначения должен быть каталог для передачи нескольких объектов.", - "selectThisFolder": "Выберите эту папку", - "browsePathWillBeCreated": "Эта папка пока не существует. Она будет создана при начале передачи.", + "itemsSummary": "{{count}} элементов", + "destMustBeDirectory": "Для передачи нескольких элементов путь назначения должен быть папкой", + "selectThisFolder": "Выбрать эту папку", + "browsePathWillBeCreated": "Эта папка ещё не существует. Она будет создана при запуске передачи.", "browsePathError": "Не удалось открыть этот путь на целевом хосте.", "goUp": "Вверх", - "copyFolderToHost": "Скопируйте папку на хост…", + "copyFolderToHost": "Копировать папку на хост…", "moveFolderToHost": "Переместить папку на хост…", - "hostReady": "Готовый", + "hostReady": "Готов", "hostConnecting": "Подключение…", - "hostDisconnected": "Не подключено", - "hostAuthRequired": "Требуется аутентификация — сначала откройте файловый менеджер на этом хосте.", - "hostConnectionFailed": "Соединение не удалось.", + "hostDisconnected": "Не подключен", + "hostAuthRequired": "Требуется аутентификация — сначала откройте Файловый Менеджер на этом хосте", + "hostConnectionFailed": "Ошибка подключения", "metricsTitle": "Время передачи", - "metricsPrepare": "Подготовьте пункт назначения: {{duration}}", - "metricsCompress": "Сжатие по источнику: {{duration}}", + "metricsPrepare": "Подготовка назначения: {{duration}}", + "metricsCompress": "Сжатие на источнике: {{duration}}", "metricsHopSourceRead": "Источник → сервер: {{throughput}}", "metricsHopDestSftpWrite": "Сервер → назначение (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Сервер → назначение (локальное): {{throughput}}", - "metricsTransfer": "Сквозное сканирование: {{throughput}} ({{duration}})", - "metricsExtract": "Выгрузка в пункте назначения: {{duration}}", - "metricsSourceDelete": "Удалить из источника: {{duration}}", - "metricsTotal": "Итого: {{duration}}", + "metricsHopDestLocalWrite": "Сервер → назначение (локально): {{throughput}}", + "metricsTransfer": "Сквозной: {{throughput}} ({{duration}})", + "metricsExtract": "Распаковка на назначении: {{duration}}", + "metricsSourceDelete": "Удаление с источника: {{duration}}", + "metricsTotal": "Всего: {{duration}}", "progressCompressing": "Сжатие на исходном хосте…", - "progressExtracting": "Извлечение в целевом месте…", + "progressExtracting": "Распаковка на назначении…", "progressTransferring": "Передача данных…", - "progressReconnecting": "Воссоединение…", + "progressReconnecting": "Переподключение…", "parallelSegmentsLabel": "Параллельные каналы передачи", - "parallelSegmentsOption": "{{count}} полосы", - "parallelSegmentsHint": "Большие файлы разбиваются на фрагменты по 256 МБ. Для увеличения общей пропускной способности используются несколько каналов связи (например, запуск нескольких передач одновременно).", - "progressTotalSpeed": "{{speed}} всего ({{lanes}} полос)", + "parallelSegmentsOption": "{{count}} каналов", + "parallelSegmentsHint": "Большие файлы разбиваются на фрагменты по 256 МБ. Несколько каналов используют отдельные соединения (как при запуске нескольких передач) для повышения общей пропускной способности.", + "progressTotalSpeed": "{{speed}} всего ({{lanes}} каналов)", "progressTransferringItems": "Передача файлов ({{current}} из {{total}})…", "progressBytes": "{{transferred}} / {{total}}", "progressItems": "{{current}} / {{total}} файлов", - "sourceNotDeletedPartial": "Исходные файлы сохранены (частичная передача).", + "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 для каждого файла.", + "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, что и при копировании одного файла. Прогресс рассчитывается для всех файлов вместе, поэтому при больших файлах индикатор может двигаться медленно.", + "methodItemSftpLimitation": "Каждый файл использует один и тот же поток SFTP, как при копировании одного файла, один за другим. Прогресс объединяется по всем файлам, поэтому индикатор движется медленно при больших файлах.", "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": "Вы выбрали пофайловый 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 для этого набора." }, "progressCancel": "Отмена", "progressCancelling": "Отмена…", "progressStalled": "Зависло", - "resumedHint": "Восстановлено соединение с активной передачей данных, начатой в другом окне.", - "transferCancelled": "Трансфер отменен", - "transferCancelledCopyHint": "Неполные файлы могут остаться на целевом устройстве.", - "transferCancelledMoveHint": "В целевом хранилище могут остаться неполные файлы. Исходные файлы не были удалены.", - "cleanupDestFiles": "Очистка места назначения", - "cleanupDestFilesSuccess": "Из целевого хранилища удалены частично скопированные файлы.", - "cleanupDestFilesPartial": "Некоторые частично скопированные файлы удалить не удалось.", - "cleanupDestFilesNothing": "На месте назначения ничего не нужно убирать.", + "resumedHint": "Переподключено к активной передаче, запущенной в другом окне.", + "transferCancelled": "Передача отменена", + "transferCancelledCopyHint": "Частично переданные файлы могут остаться в месте назначения.", + "transferCancelledMoveHint": "Частично переданные файлы могут остаться в месте назначения. Исходные файлы не были удалены.", + "cleanupDestFiles": "Очистить место назначения", + "cleanupDestFilesSuccess": "Частично переданные файлы удалены из места назначения", + "cleanupDestFilesPartial": "Некоторые частично переданные файлы не удалось удалить", + "cleanupDestFilesNothing": "В месте назначения нечего очищать", "cleanupDestFilesError": "Очистка не удалась", - "retryTransfer": "Повторить попытку", - "retryTransferError": "Повторная попытка не удалась.", - "transferFailedRetryHint": "Частичные данные были сохранены на целевом устройстве. Повторная попытка будет предпринята после восстановления соединения." + "retryTransfer": "Повторить", + "retryTransferError": "Повтор не удался", + "transferFailedRetryHint": "Частичные данные сохранены в месте назначения. Повтор возобновится, когда соединение восстановится." }, "tunnels": { "noSshTunnels": "Нет SSH-туннелей", - "createFirstTunnelMessage": "Вы еще не создали ни одного SSH-туннеля. Для начала настройте туннельные соединения в Host Manager.", + "createFirstTunnelMessage": "Вы ещё не создали ни одного SSH-туннеля. Настройте туннельные соединения в Менеджере хостов, чтобы начать.", "connected": "Подключено", "disconnected": "Отключено", "connecting": "Подключение...", "error": "Ошибка", "canceling": "Отмена...", - "connect": "Подключиться", + "connect": "Подключить", "disconnect": "Отключить", "cancel": "Отмена", "port": "Порт", "localPort": "Локальный порт", "remotePort": "Удаленный порт", - "currentHostPort": "Текущий порт хоста", + "currentHostPort": "Порт текущего хоста", "endpointPort": "Порт конечной точки", - "bindIp": "Локальный IP-адрес", + "bindIp": "Локальный IP", "endpointSshConfig": "Конфигурация SSH конечной точки", - "endpointSshHost": "Конечная точка SSH-хост", + "endpointSshHost": "Хост SSH конечной точки", "endpointSshHostPlaceholder": "Выберите настроенный хост", - "endpointSshHostRequired": "Для каждого клиентского туннеля выберите конечный SSH-хост.", + "endpointSshHostRequired": "Выберите хост SSH конечной точки для каждого клиентского туннеля.", "attempt": "Попытка {{current}} из {{max}}", - "nextRetryIn": "Следующая попытка через {{seconds}} секунд", + "nextRetryIn": "Следующая попытка через {{seconds}} сек.", "clientTunnels": "Клиентские туннели", "clientTunnel": "Клиентский туннель", "addClientTunnel": "Добавить клиентский туннель", - "noClientTunnels": "На этом рабочем столе не настроены клиентские туннели.", - "tunnelName": "Название туннеля", + "noClientTunnels": "На этом рабочем столе не настроено ни одного клиентского туннеля.", + "tunnelName": "Имя туннеля", "remoteHost": "Удаленный хост", "autoStart": "Автозапуск", - "clientAutoStartDesc": "Запускается при открытии этого настольного клиента и поддерживает с ним постоянное соединение.", - "clientManualStartDesc": "Используйте кнопки «Пуск» и «Стоп» из этой строки. Termix не откроет его автоматически.", - "clientRemoteServerNote": "Для удаленной переадресации может потребоваться установка параметров AllowTcpForwarding и GatewayPorts на SSH-сервере конечной точки. Удаленный порт закрывается при отключении этого рабочего стола.", + "clientAutoStartDesc": "Запускается при открытии этого клиента рабочего стола и остается подключенным.", + "clientManualStartDesc": "Используйте «Запустить» и «Остановить» в этой строке. Termix не будет запускать его автоматически.", + "clientRemoteServerNote": "Удаленная переадресация может потребовать AllowTcpForwarding и GatewayPorts на конечном SSH-сервере. Удаленный порт закрывается при отключении этого рабочего стола.", "clientTunnelStarted": "Клиентский туннель запущен", "clientTunnelStopped": "Клиентский туннель остановлен", - "tunnelTestSucceeded": "Туннельные испытания прошли успешно.", - "tunnelTestFailed": "Проверка туннеля не пройдена.", + "tunnelTestSucceeded": "Проверка туннеля успешна", + "tunnelTestFailed": "Проверка туннеля не удалась", "localSaved": "Клиентские туннели сохранены", - "localSaveError": "Не удалось сохранить локальные клиентские туннели.", - "invalidBindIp": "Локальный IP-адрес должен быть действительным IPv4-адресом.", - "invalidLocalTargetIp": "Целевой IP-адрес должен быть действительным IPv4-адресом.", + "localSaveError": "Не удалось сохранить локальные клиентские туннели", + "invalidBindIp": "Локальный IP-адрес должен быть действительным адресом IPv4.", + "invalidLocalTargetIp": "Локальный целевой IP-адрес должен быть действительным адресом IPv4.", "invalidLocalPort": "Локальный порт должен быть в диапазоне от 1 до 65535.", - "invalidRemotePort": "Удаленный порт должен находиться в диапазоне от 1 до 65535.", - "invalidLocalTargetPort": "Локальный целевой порт должен находиться в диапазоне от 1 до 65535.", - "invalidEndpointPort": "Порт конечной точки должен находиться в диапазоне от 1 до 65535.", - "duplicateAutoStartBind": "Только один клиентский туннель с автоматическим запуском может использовать {{bind}}.", + "invalidRemotePort": "Удаленный порт должен быть в диапазоне от 1 до 65535.", + "invalidLocalTargetPort": "Локальный целевой порт должен быть в диапазоне от 1 до 65535.", + "invalidEndpointPort": "Порт конечной точки должен быть в диапазоне от 1 до 65535.", + "duplicateAutoStartBind": "Только один клиентский туннель с автозапуском может использовать {{bind}}.", "manualControlError": "Не удалось обновить состояние туннеля.", - "active": "Активный", + "active": "Активные", "start": "Запустить", "stop": "Остановить", - "test": "Тест", - "type": "Туннельный тип", + "test": "Проверить", + "type": "Тип туннеля", "typeLocal": "Локальный (-L)", "typeRemote": "Удалённый (-R)", "typeDynamic": "Динамический (-D)", - "typeServerLocalDesc": "Текущий хост — конечная точка.", - "typeServerRemoteDesc": "Обратная связь с текущим хостом.", - "typeClientLocalDesc": "Локальный компьютер — конечная точка.", - "typeClientRemoteDesc": "Конечная точка подключения — локальный компьютер.", + "typeServerLocalDesc": "С текущего хоста на конечную точку.", + "typeServerRemoteDesc": "С конечной точки обратно на текущий хост.", + "typeClientLocalDesc": "С локального компьютера на конечную точку.", + "typeClientRemoteDesc": "С конечной точки обратно на локальный компьютер.", "typeClientDynamicDesc": "SOCKS на локальном компьютере.", - "typeDynamicDesc": "Переадресация трафика SOCKS5 CONNECT через SSH.", + "typeDynamicDesc": "Проброс трафика SOCKS5 CONNECT через SSH", "forwardDescriptionServerLocal": "Текущий хост {{sourcePort}} → конечная точка {{endpointPort}}.", "forwardDescriptionServerRemote": "Конечная точка {{endpointPort}} → текущий хост {{sourcePort}}.", "forwardDescriptionServerDynamic": "SOCKS на текущем хосте {{sourcePort}}.", - "forwardDescriptionClientLocal": "Локальный {{sourcePort}} → удаленный {{endpointPort}}.", - "forwardDescriptionClientRemote": "Удаленный {{sourcePort}} → локальный {{endpointPort}}.", + "forwardDescriptionClientLocal": "Локальный {{sourcePort}} → удалённый {{endpointPort}}.", + "forwardDescriptionClientRemote": "Удалённый {{sourcePort}} → локальный {{endpointPort}}.", "forwardDescriptionClientDynamic": "SOCKS на локальном порту {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", @@ -1972,188 +2105,188 @@ "autoNameClientDynamic": "SOCKS {{localPort}} через {{endpoint}}", "route": "Маршрут:", "lastStarted": "Последний запуск", - "lastTested": "Последнее тестирование", + "lastTested": "Последняя проверка", "lastError": "Последняя ошибка", - "maxRetries": "Максимальное количество повторных попыток", - "maxRetriesDescription": "Максимальное количество попыток повтора.", - "retryInterval": "Интервал повторной попытки (секунды)", + "maxRetries": "Макс. попыток", + "maxRetriesDescription": "Максимальное количество повторных попыток.", + "retryInterval": "Интервал повтора (секунд)", "retryIntervalDescription": "Время ожидания между повторными попытками.", "local": "Локальный", - "remote": "Удаленный", - "destination": "Место назначения", + "remote": "Удалённый", + "destination": "Назначение", "host": "Хост", "mode": "Режим", "noHostSelected": "Хост не выбран", - "working": "Работающий..." + "working": "Выполняется..." }, "cardGrid": { "dragToMove": "Перетащите для перемещения", - "dragToResize": "Перетащите для изменения размера.", + "dragToResize": "Перетащите для изменения размера", "changeWidth": "Изменить ширину", "removeCard": "Удалить {{label}}", "addCard": "Добавить", - "columns": "Колонки", - "empty": "Карточки не требуются. Используйте кнопку «Добавить» ниже, чтобы разместить карточки." + "columns": "Столбцы", + "empty": "Нет карточек. Используйте кнопку «Добавить» ниже, чтобы разместить карточки." }, "hostMetrics": { - "cpu": "Процессор", + "cpu": "ЦП", "memory": "Память", "disk": "Диск", "network": "Сеть", - "uptime": "Время безотказной работы", + "uptime": "Время работы", "processes": "Процессы", "available": "Доступно", "free": "Свободно", "connecting": "Подключение...", - "connectionFailed": "Не удалось подключиться к серверу.", - "naCpus": "N/A CPU(s)", - "cpuCores_one": "{{count}} Ядро", - "cpuCores_other": "{{count}} Ядра", + "connectionFailed": "Не удалось подключиться к серверу", + "naCpus": "Н/Д ЦП", + "cpuCores_one": "{{count}} ядро", + "cpuCores_other": "{{count}} ядер", "cpuUsage": "Использование ЦП", "memoryUsage": "Использование памяти", "diskUsage": "Использование диска", "temperature": "Температура", "highestTemperature": "Максимальная температура", - "failedToFetchHostConfig": "Не удалось получить конфигурацию хоста.", - "serverOffline": "Сервер отключен", - "cannotFetchMetrics": "Не удаётся получить метрики с недоступного сервера.", - "totpFailed": "Проверка TOTP не удалась.", + "failedToFetchHostConfig": "Не удалось получить конфигурацию хоста", + "serverOffline": "Сервер недоступен", + "cannotFetchMetrics": "Не удалось загрузить метрики с недоступного сервера", + "totpFailed": "Проверка TOTP не пройдена", "noneAuthNotSupported": "Host Metrics не поддерживает тип аутентификации 'none'.", "noHostSelected": "Хост не выбран", - "load": "Загрузить", + "load": "Нагрузка", "systemInfo": "Информация о системе", "hostname": "Имя хоста", "operatingSystem": "Операционная система", "kernel": "Ядро", - "seconds": "секунд", + "seconds": "секунды", "networkInterfaces": "Сетевые интерфейсы", - "noInterfacesFound": "Сетевые интерфейсы не обнаружены", + "noInterfacesFound": "Сетевые интерфейсы не найдены", "noProcessesFound": "Процессы не найдены", - "processesTotal": "общий", - "processesRunning": "бег", - "loginStats": "Статистика входа по SSH", - "noRecentLoginData": "Нет данных о недавних входах в систему.", - "executingQuickAction": "Выполнение {{name}}...", + "processesTotal": "всего", + "processesRunning": "выполняется", + "loginStats": "Статистика входов SSH", + "noRecentLoginData": "Нет данных о недавних входах", + "executingQuickAction": "Выполняется {{name}}...", "quickActionSuccess": "{{name}} успешно завершено", - "quickActionFailed": "{{name}} провалился", + "quickActionFailed": "{{name}} не удалось", "quickActionError": "Не удалось выполнить {{name}}", "ports": { - "title": "Порты прослушивания", + "title": "Прослушиваемые порты", "protocol": "Протокол", "port": "Порт", "address": "Адрес", "process": "Процесс", "search": "Поиск портов...", "allProtocols": "Все", - "noData": "Нет данных о портах прослушивания." + "noData": "Нет данных о прослушиваемых портах" }, "firewall": { "title": "Брандмауэр", "inactive": "Неактивен", "policy": "Политика", "rules": "правила", - "noRules": "В этой цепочке нет правил.", - "noData": "Данные брандмауэра отсутствуют.", + "noRules": "Нет правил в этой цепочке", + "noData": "Нет данных о брандмауэре", "action": "Действие", "protocol": "Протокол", "port": "Порт", "source": "Источник", - "anywhere": "В любом месте", + "anywhere": "Любое", "chains": "цепочки" }, - "loadAvg": "Средняя загрузка", - "swap": "Swap", + "loadAvg": "Средняя нагрузка", + "swap": "Своп", "architecture": "Архитектура", "refresh": "Обновить", - "retry": "Повторить попытку", + "retry": "Повторить", "customize": "Настроить макет", "reset": "Сбросить", - "tabLive": "В реальном времени", - "editModeInstructions": "Перетаскивайте карточки, чтобы изменить их порядок, перетаскивайте нижний край, чтобы изменить размер, и используйте кнопку ширины, чтобы изменить размер карточки. Добавляйте или удаляйте карточки ниже.", + "tabLive": "Онлайн", + "editModeInstructions": "Перетаскивайте карточки для изменения порядка, перетаскивайте нижний край для изменения размера и используйте кнопку ширины, чтобы изменить занимаемое пространство. Добавляйте или удаляйте карточки ниже.", "managers": { - "services": "Сервисы", + "services": "Службы", "processInspector": "Инспектор процессов", - "logViewer": "Просмотрщик журналов", + "logViewer": "Просмотр журналов", "cron": "Задания Cron", "packages": "Пакеты", "ssl": "SSL-сертификаты", "firewall": "Брандмауэр", "users": "Пользователи и разрешения", - "healthCheck": "Проверка состояния", - "diskBreakdown": "Разбивка диска", + "healthCheck": "Проверки состояния", + "diskBreakdown": "Разбивка дисков", "systemdTimers": "Таймеры", - "topMemory": "Сверху по памяти", + "topMemory": "Топ по памяти", "noData": "Нет данных", - "sudoHint": "Установите пароль sudo для этого хоста в редакторе хостов, чтобы разрешить выполнение привилегированных действий.", + "sudoHint": "Установите пароль sudo для этого хоста в редакторе хостов, чтобы включить привилегированные действия.", "filter": "Фильтр...", "start": "Запустить", "stop": "Остановить", "restart": "Перезапустить", "actionDone": "{{name}} обновлено", - "actionFailed": "Действие не удалось.", - "signalSent": "Сигнал отправлен ПИД-регулятору {{pid}}", - "killHint": "Щелчок мышью: завершить процесс (SIGTERM). Щелчок правой кнопкой мыши: принудительно завершить процесс (SIGKILL).", - "working": "Работающий...", + "actionFailed": "Действие не удалось", + "signalSent": "Сигнал отправлен PID {{pid}}", + "killHint": "Нажмите: завершить (SIGTERM). Правый клик: принудительно завершить (SIGKILL).", + "working": "Выполняется...", "update": "Обновить", - "upgradeAll": "Обновить все", - "allUpToDate": "Всё актуально.", + "upgradeAll": "Обновить всё", + "allUpToDate": "Всё актуально", "save": "Сохранить", "command": "Команда", "enabled": "Включено", - "cronSaved": "Обновление Crontab", + "cronSaved": "Crontab обновлён", "clients": "Клиенты", "dryRun": "Пробный запуск", - "renew": "Обновить", - "noAcmeClient": "На этом хосте не найден клиент ACME (certbot или acme.sh).", - "addInputRule": "Добавить правило ввода", - "firewallWarning": "Изменения действуют только во время работы, пока не будут сохранены. Будьте осторожны, чтобы не потерять доступ.", - "ruleApplied": "Применено правило", - "invalidPort": "Введите действительный порт (1-65535)", + "renew": "Продлить", + "noAcmeClient": "На этом хосте не найден ACME-клиент (certbot или acme.sh).", + "addInputRule": "Добавить правило INPUT", + "firewallWarning": "Изменения действуют только до перезагрузки, пока не сохранены. Будьте осторожны, чтобы не заблокировать себе доступ.", + "ruleApplied": "Правило применено", + "invalidPort": "Введите допустимый порт (1–65535)", "newUsername": "Новое имя пользователя", "addUser": "Добавить", "deleteUser": "Удалить пользователя", - "noHealthChecks": "Пока не настроены никакие проверки состояния здоровья.", - "follow": "Отслеживать", - "noLogData": "В логах ничего не отображается.", + "noHealthChecks": "Проверки работоспособности ещё не настроены.", + "follow": "Следить", + "noLogData": "Нет данных журнала.", "tree": "Дерево", - "enableDisable": "Включать/отключать при загрузке", - "grantSudo": "Предоставить права sudo", + "enableDisable": "Включить/отключить при загрузке", + "grantSudo": "Предоставить sudo", "revokeSudo": "Отозвать sudo", - "sslIssueCert": "Выдать сертификат", - "sslExpired": "Истекший", - "sslInDays": "в {{days}}д", - "sslNeedDomain": "Введите хотя бы один домен.", - "sslIssued": "Сертификат выдан", + "sslIssueCert": "Выпустить сертификат", + "sslExpired": "Истёк", + "sslInDays": "через {{days}}д", + "sslNeedDomain": "Введите хотя бы один домен", + "sslIssued": "Сертификат выпущен", "sslDomainsPlaceholder": "example.com, www.example.com", "sslHttpStandalone": "HTTP (автономный)", - "sslHttpWebroot": "HTTP (корневой каталог)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNS-провайдер (например, Cloudflare)", + "sslDnsProvider": "DNS-провайдер (например, cloudflare)", "sslIssueHint": "Учетные данные DNS-провайдера должны быть уже настроены на хосте.", "sslRevoke": "Отозвать сертификат", - "sslRevoked": "Сертификат аннулирован", - "sslRevokeConfirm": "Отозвать и удалить сертификат \"{{name}}\"? Это невозможно отменить.", + "sslRevoked": "Сертификат отозван", + "sslRevokeConfirm": "Отозвать и удалить сертификат \"{{name}}\"? Это действие нельзя отменить.", "healthRun": "Запустить", - "healthName": "Имя", + "healthName": "Название", "healthTarget": "Хост / адрес", "healthAddCheck": "Добавить проверку", - "healthSaved": "Сохранены медицинские осмотры", - "healthMissingFields": "Для каждой проверки необходимо указать название и цель.", + "healthSaved": "Проверки работоспособности сохранены", + "healthMissingFields": "Для каждой проверки нужны название и цель", "logFile": "Файл", - "logUnit": "Единица", - "logCustomPath": "Укажите путь в каталоге /var/log (необязательно)", - "logGrep": "Фильтрующие линии...", - "firewallPersist": "Сохранять правила", + "logUnit": "Юнит", + "logCustomPath": "Произвольный путь в /var/log (необязательно)", + "logGrep": "Фильтр строк...", + "firewallPersist": "Сохранить правила", "firewallPersisted": "Правила брандмауэра сохранены", "wireguard": "WireGuard", "tailscale": "Tailscale", - "wgNotInstalled": "На этом хосте не установлен WireGuard.", - "wgNoInterfaces": "Интерфейсы WireGuard не настроены", + "wgNotInstalled": "WireGuard не установлен на этом хосте", + "wgNoInterfaces": "Не настроено ни одного интерфейса WireGuard", "wgInterfaceUp": "Поднять", "wgInterfaceDown": "Отключить", - "wgBringingUp": "Включение {{name}}...", - "wgBringingDown": "Отключение {{name}}...", - "wgInterfaceUpDone": "{{name}} включен", + "wgBringingUp": "Поднимается {{name}}...", + "wgBringingDown": "Отключается {{name}}...", + "wgInterfaceUpDone": "{{name}} поднят", "wgInterfaceDownDone": "{{name}} отключен", "wgListenPort": "Порт", "wgPublicKey": "Открытый ключ", @@ -2161,17 +2294,17 @@ "wgAllowedIPs": "Разрешенные IP-адреса", "wgLastHandshake": "Последнее рукопожатие", "wgHandshakeNever": "Никогда", - "wgTransfer": "Передача", - "tsNotInstalled": "На этом хосте не установлена программа Tailscale.", - "tsRunning": "Выполняется", - "tsStopped": "Остановлено", - "tsEnable": "Подключиться", + "wgTransfer": "Передано", + "tsNotInstalled": "Tailscale не установлен на этом хосте", + "tsRunning": "Запущен", + "tsStopped": "Остановлен", + "tsEnable": "Подключить", "tsDisable": "Отключить", "tsEnabling": "Подключение к Tailscale...", "tsDisabling": "Отключение от Tailscale...", "tsEnabled": "Tailscale подключён", "tsDisabled": "Tailscale отключён", - "tsIPs": "IP-блоки Tailscale", + "tsIPs": "IP-адреса Tailscale", "tsPeers": "Пиры", "tsOnline": "Онлайн", "tsOffline": "Офлайн", @@ -2181,48 +2314,48 @@ } }, "auth": { - "tagline": "Самостоятельно размещаемые SSH-серверы и системы удаленного управления рабочим столом", - "loginTitle": "Добро пожаловать", - "registerTitle": "Зарегистрироваться", + "tagline": "Собственное управление SSH и удалёнными рабочими столами", + "loginTitle": "С возвращением", + "registerTitle": "Создать учётную запись", "forgotPassword": "Забыли пароль?", "rememberMe": "Запомнить устройство на 30 дней (включая TOTP)", - "noAccount": "У вас нет аккаунта?", - "hasAccount": "У вас уже есть аккаунт?", + "noAccount": "Нет учётной записи?", + "hasAccount": "Уже есть учётная запись?", "twoFactorAuth": "Двухфакторная аутентификация", "enterCode": "Введите проверочный код", "backupCode": "Или используйте резервный код", - "verifyCode": "Подтвердите код", + "verifyCode": "Проверить код", "redirectingToApp": "Перенаправление в приложение...", - "sshAuthenticationRequired": "Требуется аутентификация SSH.", - "sshNoKeyboardInteractive": "Интерактивная аутентификация с помощью клавиатуры недоступна", + "sshAuthenticationRequired": "Требуется аутентификация SSH", + "sshNoKeyboardInteractive": "Интерактивная аутентификация с клавиатурой недоступна", "sshAuthenticationFailed": "Аутентификация не удалась", - "sshAuthenticationTimeout": "Истекло время ожидания аутентификации", - "sshNoKeyboardInteractiveDescription": "Сервер не поддерживает интерактивную аутентификацию с клавиатуры. Пожалуйста, укажите свой пароль или SSH-ключ.", - "sshAuthFailedDescription": "Предоставленные учетные данные неверны. Пожалуйста, попробуйте еще раз с действительными учетными данными.", - "sshTimeoutDescription": "Попытка аутентификации завершилась по истечении времени ожидания. Пожалуйста, попробуйте снова.", - "sshProvideCredentialsDescription": "Пожалуйста, укажите свои учетные данные SSH для подключения к этому серверу.", + "sshAuthenticationTimeout": "Время аутентификации истекло", + "sshNoKeyboardInteractiveDescription": "Сервер не поддерживает интерактивную аутентификацию с клавиатурой. Пожалуйста, укажите пароль или SSH-ключ.", + "sshAuthFailedDescription": "Предоставленные учётные данные неверны. Пожалуйста, попробуйте снова с правильными данными.", + "sshTimeoutDescription": "Время попытки аутентификации истекло. Пожалуйста, попробуйте снова.", + "sshProvideCredentialsDescription": "Пожалуйста, предоставьте ваши учётные данные SSH для подключения к этому серверу.", "sshPasswordDescription": "Введите пароль для этого SSH-соединения.", "sshKeyPasswordDescription": "Если ваш SSH-ключ зашифрован, введите парольную фразу здесь.", "passphraseRequired": "Требуется парольная фраза", - "passphraseRequiredDescription": "SSH-ключ зашифрован. Пожалуйста, введите парольную фразу для его разблокировки.", + "passphraseRequiredDescription": "SSH-ключ зашифрован. Введите парольную фразу для его разблокировки.", "back": "Назад", "firstUser": "Первый пользователь", - "firstUserMessage": "Вы первый пользователь и будете назначены администратором. Настройки администратора можно посмотреть в выпадающем списке пользователей на боковой панели. Если вы считаете, что это ошибка, проверьте логи Docker или создайте проблему на GitHub.", + "firstUserMessage": "Вы первый пользователь и получите права администратора. Настройки администратора доступны в выпадающем меню пользователя на боковой панели. Если вы считаете это ошибкой, проверьте логи Docker или создайте задачу на GitHub.", "external": "Внешний", - "loginWithExternal": "Вход через внешнего поставщика", - "loginWithExternalDesc": "Войдите в систему, используя настроенный вами внешний поставщик идентификации.", - "externalNotSupportedInElectron": "Внешняя аутентификация в приложении Electron пока не поддерживается. Для входа через OIDC используйте веб-версию.", - "loginWithProvider": "Войти с помощью {{name}}", - "orContinueWith": "или продолжить", + "loginWithExternal": "Войти через внешнего провайдера", + "loginWithExternalDesc": "Войти, используя настроенного внешнего провайдера идентификации", + "externalNotSupportedInElectron": "Внешняя аутентификация пока не поддерживается в приложении Electron. Пожалуйста, используйте веб-версию для входа через OIDC.", + "loginWithProvider": "Войти через {{name}}", + "orContinueWith": "или продолжить с", "ldapUsername": "Имя пользователя LDAP", "ldapPassword": "Пароль LDAP", "ldapSignIn": "Войти", - "ldapLoginFailed": "Не удалось войти в LDAP.", + "ldapLoginFailed": "Ошибка входа LDAP", "resetPasswordButton": "Сбросить пароль", "sendResetCode": "Отправить код сброса", - "resetCodeDesc": "Введите ваше имя пользователя, чтобы получить код для сброса пароля. Код будет записан в логи контейнера Docker.", + "resetCodeDesc": "Введите имя пользователя, чтобы получить код сброса пароля. Код будет записан в логи контейнера Docker.", "resetCode": "Код сброса", - "verifyCodeButton": "Подтвердите код", + "verifyCodeButton": "Проверить код", "enterResetCode": "Введите 6-значный код из логов контейнера Docker для пользователя:", "newPassword": "Новый пароль", "confirmNewPassword": "Подтвердите пароль", @@ -2231,93 +2364,94 @@ "desktopApp": "Настольное приложение", "loggingInToDesktopApp": "Вход в настольное приложение", "loadingServer": "Загрузка сервера...", - "dataLossWarning": "Сброс пароля таким способом удалит все сохраненные SSH-хосты, учетные данные и другие зашифрованные данные. Это действие необратимо. Используйте этот способ только в том случае, если вы забыли свой пароль и не вошли в систему.", + "dataLossWarning": "Сброс пароля таким образом удалит все сохранённые SSH-хосты, учётные данные и другие зашифрованные данные. Это действие нельзя отменить. Используйте только если вы забыли пароль и не вошли в систему.", "authenticationDisabled": "Аутентификация отключена", - "authenticationDisabledDesc": "В настоящее время все методы аутентификации отключены. Пожалуйста, свяжитесь с администратором.", - "passwordLoginDisabledDesc": "Вход по паролю отключен. Используйте пароль или внешний источник аутентификации.", - "signInWithPasskey": "Войдите с помощью пароля.", - "passkeyLoginFailed": "Не удалось войти с помощью пароля.", - "attemptsRemaining": "{{count}} попыток осталось", - "confirmResetDataWipe": "Эта учетная запись не использовалась с момента обновления шифрования, поэтому ее сохраненные данные невозможно восстановить без старого пароля. Сброс пароля приведет к безвозвратному удалению ее хостов, учетных данных и фрагментов кода. Продолжить?" + "authenticationDisabledDesc": "Все методы аутентификации в настоящее время отключены. Обратитесь к администратору.", + "passwordLoginDisabledDesc": "Вход по паролю отключён. Используйте ключ доступа или внешнего провайдера аутентификации.", + "signInWithPasskey": "Войти с ключом доступа", + "passkeyLoginFailed": "Вход по ключу не удался", + "attemptsRemaining": "Осталось попыток: {{count}}", + "confirmResetDataWipe": "Эта учетная запись не входила с момента обновления шифрования, поэтому ее сохраненные данные не могут быть восстановлены без старого пароля. Сброс навсегда удалит ее хосты, учетные данные и сниппеты. Продолжить?" }, "hostKey": { - "verifyNewHost": "Проверьте ключ хоста SSH.", - "keyChangedWarning": "Ключ хоста SSH изменен.", - "firstConnectionTitle": "Первое подключение к этому хосту.", - "firstConnectionDescription": "Подлинность этого хоста установить невозможно. Убедитесь, что отпечаток соответствует вашим ожиданиям.", - "keyChangedDescription": "Ключ хоста для этого сервера изменился с момента вашего последнего подключения. Это может указывать на проблему с безопасностью.", + "verifyNewHost": "Проверить SSH-ключ хоста", + "keyChangedWarning": "Изменен ключ хоста SSH", + "firstConnectionTitle": "Первое подключение к этому хосту", + "firstConnectionDescription": "Подлинность этого хоста не может быть установлена. Проверьте, соответствует ли отпечаток ожидаемому.", + "keyChangedDescription": "Ключ хоста для этого сервера изменился с момента последнего подключения. Это может указывать на проблему с безопасностью.", "previousKey": "Предыдущий ключ", "newFingerprint": "Новый отпечаток", "fingerprint": "Отпечаток", - "verifyInstructions": "Если вы доверяете этому хосту, нажмите «Принять», чтобы продолжить и сохранить этот отпечаток для будущих подключений.", - "securityWarning": "Предупреждение о безопасности", + "verifyInstructions": "Если вы доверяете этому хосту, нажмите «Принять», чтобы продолжить и сохранить отпечаток для следующих подключений.", + "securityWarning": "Предупреждение безопасности", "acceptAndContinue": "Принять и продолжить", "acceptNewKey": "Принять новый ключ и продолжить" }, "errors": { - "databaseConnection": "Не удалось подключиться к базе данных.", + "databaseConnection": "Не удалось подключиться к базе данных", "unknownError": "Неизвестная ошибка", - "loginFailed": "Ошибка входа", - "failedPasswordReset": "Не удалось инициировать сброс пароля.", + "loginFailed": "Не удалось войти", + "failedPasswordReset": "Не удалось инициировать сброс пароля", "failedVerifyCode": "Не удалось проверить код сброса", "failedCompleteReset": "Не удалось завершить сброс пароля", "invalidTotpCode": "Неверный TOTP-код", - "failedOidcLogin": "Не удалось запустить вход в OIDC.", - "silentSigninOidcUnavailable": "Была запрошена бесшумная авторизация, но вход через OIDC недоступен.", - "failedUserInfo": "Не удалось получить информацию о пользователе после входа в систему.", + "failedOidcLogin": "Не удалось начать вход через OIDC", + "silentSigninOidcUnavailable": "Запрошен автоматический вход, но OIDC-вход недоступен.", + "failedUserInfo": "Не удалось получить информацию о пользователе после входа", "oidcAuthFailed": "Аутентификация OIDC не удалась", - "invalidAuthUrl": "Получен недействительный URL-адрес авторизации от бэкэнда.", - "requiredField": "Это поле обязательно к заполнению", - "minLength": "Минимальная длина {{min}}", + "invalidAuthUrl": "Получен некорректный URL авторизации от серверной части", + "requiredField": "Обязательное поле", + "minLength": "Минимальная длина: {{min}}", "passwordMismatch": "Пароли не совпадают", - "passwordLoginDisabled": "Вход по имени пользователя/паролю в данный момент отключен.", - "sessionExpired": "Срок действия сессии истек — пожалуйста, войдите снова.", - "totpRateLimited": "Ограничение по количеству запросов: Слишком много попыток подтверждения TOTP. Пожалуйста, попробуйте позже.", - "totpRateLimitedWithTime": "Ограничение количества попыток: Слишком много попыток подтверждения TOTP. Пожалуйста, подождите {{time}} секунд, прежде чем повторить попытку.", - "resetCodeRateLimited": "Ограничение по количеству попыток: Слишком много попыток подтверждения. Пожалуйста, попробуйте позже.", - "resetCodeRateLimitedWithTime": "Ограничение количества попыток: Слишком много попыток подтверждения. Пожалуйста, подождите {{time}} секунд, прежде чем повторить попытку.", - "authTokenSaveFailed": "Не удалось сохранить токен аутентификации.", - "failedToLoadServer": "Не удалось загрузить сервер." + "passwordLoginDisabled": "Вход по логину и паролю в данный момент отключён", + "sessionExpired": "Сеанс истёк — войдите заново", + "totpRateLimited": "Превышена частота запросов: слишком много попыток проверки TOTP. Повторите попытку позже.", + "totpRateLimitedWithTime": "Подождите {{time}} секунд, прежде чем повторить попытку.", + "resetCodeRateLimited": "Превышена частота запросов: слишком много попыток проверки. Пожалуйста, попробуйте позже.", + "resetCodeRateLimitedWithTime": "Превышена частота запросов: слишком много попыток подтверждения. Подождите {{time}} секунд, прежде чем повторить попытку.", + "authTokenSaveFailed": "Не удалось сохранить токен аутентификации", + "failedToLoadServer": "Не удалось загрузить сервер", + "remoteServerRequired": "Требуется удаленный сервер. Для использования этого типа подключения подключите удаленный сервер в настройках." }, "messages": { - "registrationDisabled": "В настоящее время регистрация новых учетных записей отключена администратором. Пожалуйста, войдите в систему или свяжитесь с администратором.", - "userNotAllowed": "Ваша учетная запись не авторизована для регистрации. Пожалуйста, свяжитесь с администратором.", - "databaseConnectionFailed": "Не удалось подключиться к серверу базы данных.", - "resetCodeSent": "Код сброса отправлен в логи Docker.", - "codeVerified": "Код успешно проверен.", - "passwordResetSuccess": "Пароль успешно сброшен.", - "loginSuccess": "Вход выполнен успешно.", - "registrationSuccess": "Регистрация прошла успешно." + "registrationDisabled": "Регистрация новых учетных записей отключена администратором. Войдите в систему или обратитесь к администратору.", + "userNotAllowed": "Ваша учетная запись не авторизована для регистрации. Обратитесь к администратору.", + "databaseConnectionFailed": "Не удалось подключиться к серверу базы данных", + "resetCodeSent": "Код сброса отправлен в логи Docker", + "codeVerified": "Код успешно проверен", + "passwordResetSuccess": "Пароль успешно сброшен", + "loginSuccess": "Вход выполнен успешно", + "registrationSuccess": "Регистрация успешно завершена" }, "profile": { - "c2sTunnelConfigDesc": "Локальные туннели для рабочих столов, нацеленные на настроенные SSH-хосты.", - "c2sTunnelPresets": "Настройки клиентского туннеля", - "c2sTunnelPresetsDesc": "Сохраните локальный список туннелей этого настольного клиента в качестве именованного сервера или загрузите предустановку обратно в этот клиент.", - "c2sTunnelPresetsUnavailable": "Настройки клиентского туннеля доступны только в настольном клиенте.", - "c2sPresetName": "Название предустановки", - "c2sPresetNamePlaceholder": "Название предустановленного клиента", - "c2sPresetToLoad": "Предварительные настройки для загрузки", - "c2sNoPresetSelected": "Предварительно заданные параметры не выбраны", - "c2sNoPresets": "Предустановки не сохранены", + "c2sTunnelConfigDesc": "Локальные туннели рабочего стола, нацеленные на настроенные хосты SSH.", + "c2sTunnelPresets": "Предустановки клиентских туннелей", + "c2sTunnelPresetsDesc": "Сохраните список локальных туннелей этого клиента как именованную предустановку на сервере или загрузите предустановку обратно в клиент.", + "c2sTunnelPresetsUnavailable": "Предустановки клиентских туннелей доступны только в десктопном клиенте.", + "c2sPresetName": "Имя предустановки", + "c2sPresetNamePlaceholder": "Имя клиентской предустановки", + "c2sPresetToLoad": "Предустановка для загрузки", + "c2sNoPresetSelected": "Предустановка не выбрана", + "c2sNoPresets": "Нет сохраненных предустановок", "c2sLoadPreset": "Загрузить", - "c2sCurrentLocalConfig": "{{count}} На этом рабочем столе настроены локальные клиентские туннели.", - "c2sPresetSyncNote": "Предустановки представляют собой конкретные снимки состояния; загрузка одной из них заменяет локальный список клиентских туннелей этого настольного клиента.", - "c2sPresetSaved": "Сохранены предустановленные параметры клиентского туннеля.", - "c2sPresetLoaded": "Предварительные настройки клиентского туннеля загружены локально.", - "c2sPresetRenamed": "Настройки клиентского туннеля переименованы.", - "c2sPresetDeleted": "Предварительные настройки клиентского туннеля удалены.", - "c2sPresetLoadError": "Не удалось загрузить настройки клиентского туннеля." + "c2sCurrentLocalConfig": "На этом рабочем столе настроено {{count}} локальных клиентских туннелей.", + "c2sPresetSyncNote": "Предустановки — это явные снимки; загрузка одной заменяет список локальных клиентских туннелей этого десктопного клиента.", + "c2sPresetSaved": "Предустановка клиентского туннеля сохранена", + "c2sPresetLoaded": "Предустановка клиентского туннеля загружена локально", + "c2sPresetRenamed": "Предустановка клиентского туннеля переименована", + "c2sPresetDeleted": "Предустановка клиентского туннеля удалена", + "c2sPresetLoadError": "Не удалось загрузить предустановки клиентских туннелей" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "Язык", - "keyPassword": "ключ пароль", - "pastePrivateKey": "Вставьте сюда свой закрытый ключ...", - "localListenerHost": "127.0.0.1 (прослушивание локально)", + "keyPassword": "пароль ключа", + "pastePrivateKey": "Вставьте свой закрытый ключ сюда...", + "localListenerHost": "127.0.0.1 (слушать локально)", "localTargetHost": "127.0.0.1 (цель на этом компьютере)", - "socksListenerHost": "127.0.0.1 (слушатель SOCKS)", - "enterPassword": "Введите свой пароль", + "socksListenerHost": "127.0.0.1 (SOCKS-слушатель)", + "enterPassword": "Введите пароль", "defaultPort": "22", "defaultEndpointPort": "224" }, @@ -2327,122 +2461,122 @@ "github": "GitHub", "support": "Поддержка", "discord": "Discord", - "docs": "Документы", + "docs": "Документация", "donate": "Пожертвовать", "serverOverview": "Обзор сервера", "version": "Версия", - "upToDate": "До настоящего времени", + "upToDate": "Актуальная версия", "updateAvailable": "Доступно обновление", "beta": "Бета", - "uptime": "Время безотказной работы", + "uptime": "Время работы", "database": "База данных", - "healthy": "Здоровый", + "healthy": "Исправен", "error": "Ошибка", "totalHosts": "Всего хостов", "totalTunnels": "Всего туннелей", - "totalCredentials": "Полный набор учетных данных", - "recentActivity": "Последние события", - "reset": "Сбросить", - "loadingRecentActivity": "Загрузка последних событий...", + "totalCredentials": "Всего учётных данных", + "recentActivity": "Недавняя активность", + "reset": "Сброс", + "loadingRecentActivity": "Загрузка недавней активности...", "noRecentActivity": "Нет недавней активности", "quickActions": "Быстрые действия", "addHost": "Добавить хост", - "addCredential": "Добавить учетные данные", + "addCredential": "Добавить учётные данные", "adminSettings": "Настройки администратора", "userProfile": "Профиль пользователя", "serverStats": "Статистика сервера", "loadingServerStats": "Загрузка статистики сервера...", - "noServerData": "Данные сервера отсутствуют.", - "cpu": "Процессор", - "ram": "ОЗУ", - "customizeLayout": "Настройка панели управления", - "dashboardSettings": "Настройки панели управления", - "enableDisableCards": "Включить/отключить карты", - "resetLayout": "Сброс до значений по умолчанию", + "noServerData": "Нет данных о сервере", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Настроить дашборд", + "dashboardSettings": "Настройки дашборда", + "enableDisableCards": "Включить/отключить карточки", + "resetLayout": "Сбросить по умолчанию", "serverOverviewCard": "Обзор сервера", - "recentActivityCard": "Последние события", - "networkGraphCard": "Карта сети", - "networkGraph": "Карта сети", + "recentActivityCard": "Недавняя активность", + "networkGraphCard": "Сетевой график", + "networkGraph": "Сетевой график", "quickActionsCard": "Быстрые действия", "serverStatsCard": "Статистика сервера", - "panelMain": "Основной", - "panelSide": "Сторона", - "justNow": "прямо сейчас", + "panelMain": "Главная", + "panelSide": "Боковая", + "justNow": "только что", "serviceLinks": "Ссылки на сервисы", - "homepagePreview": "Предварительный просмотр главной страницы" + "homepagePreview": "Предпросмотр главной страницы" }, "donation": { - "title": "Вам нравится Termix?", - "body": "Termix — бесплатный проект с открытым исходным кодом, разработанный и поддерживаемый командой из двух человек в свободное время. Если он заменит коммерческий инструмент, за который вы бы платили, пожертвование поможет покрыть расходы на хостинг и продолжить разработку. На данный момент пожертвования принимаются только в криптовалюте.", - "milestones": "Пожертвования также помогают финансировать исследования и изучение того, что необходимо для разработки таких функций, как поддержка SAML и Kubernetes. Следите за ходом работ на странице пожертвований.", + "title": "Нравится Termix?", + "body": "Termix — бесплатный и с открытым исходным кодом, разрабатывается и поддерживается командой из двух человек в свободное время. Если он заменил коммерческий инструмент, за который вам пришлось бы платить, пожертвование поможет покрыть расходы на хостинг и продолжить разработку. На данный момент пожертвования принимаются только в криптовалюте.", + "milestones": "Пожертвования также помогают финансировать время на исследование и изучение необходимого для создания таких функций, как поддержка SAML и Kubernetes. Смотрите прогресс на странице пожертвований.", "cta": "Пожертвовать", - "dismiss": "Возможно, позже" + "dismiss": "Возможно позже" }, "dashboardTab": { - "stable": "СТАБИЛЬНЫЙ", + "stable": "СТАБИЛЬНАЯ", "hostsOnline": "Хосты онлайн", "activeTunnels": "Активные туннели", - "registerNewServer": "Зарегистрируйте новый сервер", - "storeSshKeysOrPasswords": "Храните SSH-ключи или пароли.", - "manageUsersAndRoles": "Управление пользователями и ролями.", - "manageYourAccount": "Управление учетной записью", + "registerNewServer": "Зарегистрировать новый сервер", + "storeSshKeysOrPasswords": "Хранить SSH-ключи или пароли", + "manageUsersAndRoles": "Управлять пользователями и ролями", + "manageYourAccount": "Управлять аккаунтом", "hostStatus": "Статус хоста", - "noHostsConfigured": "Хосты не настроены", + "noHostsConfigured": "Нет настроенных хостов", "online": "ОНЛАЙН", - "offline": "ОФЛАЙН", + "offline": "ОФФЛАЙН", "checking": "ПРОВЕРКА", "onlineLower": "Онлайн", "nodes": "{{count}} узлов", "add": "Добавить:", - "commandPalette": "Палитра команд", + "commandPalette": "Командная палитра", "done": "Готово", - "editModeInstructions": "Перетаскивайте карточки, чтобы изменить их порядок · Перетаскивайте разделитель столбцов, чтобы изменить их ширину · Перетаскивайте нижний край карточки, чтобы изменить её высоту · Перетащите в корзину, чтобы удалить", - "empty": "Пустой", + "editModeInstructions": "Перетаскивайте карточки для изменения порядка · Перетащите разделитель столбцов для изменения ширины · Перетащите нижний край карточки для изменения высоты · Корзина для удаления", + "empty": "Пусто", "clear": "Очистить", - "serviceLinksTitle": "Ссылки на сервисы", - "serviceLinksEmpty": "Ссылки на сервисы пока отсутствуют.", - "serviceLinksAddLabel": "Метка", + "serviceLinksTitle": "Сервисные ссылки", + "serviceLinksEmpty": "Пока нет сервисных ссылок", + "serviceLinksAddLabel": "Название", "serviceLinksAddUrl": "URL", "serviceLinksAdd": "Добавить", - "serviceLinksLabelPlaceholder": "Моя услуга", + "serviceLinksLabelPlaceholder": "Мой сервис", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", "serviceLinksInvalidUrl": "Введите действительный веб-адрес", - "serviceLinksAddFailed": "Не удалось добавить ссылку на сервис.", + "serviceLinksAddFailed": "Не удалось добавить сервисную ссылку", "disk": "Диск", "viewServerDetails": "Просмотреть сведения о сервере" }, "sessionLogs": { - "title": "Журналы сессий", - "noLogs": "Журналы сессий пока отсутствуют.", - "noLogsDesc": "Включите ведение журнала сессий на хосте, чтобы начать запись.", + "title": "Журналы сеансов", + "noLogs": "Пока нет журналов сеансов", + "noLogsDesc": "Включите ведение журнала сеансов на хосте, чтобы начать запись", "duration": "Продолжительность", "viewLog": "Просмотреть журнал", "downloadLog": "Скачать", "deleteLog": "Удалить", - "confirmDelete": "Удалить этот журнал сеанса?", - "confirmDeleteDesc": "Это действие необратимо.", - "copyContent": "Копия", + "confirmDelete": "Удалить этот журнал сеансов?", + "confirmDeleteDesc": "Это действие нельзя отменить.", + "copyContent": "Копировать", "copied": "Скопировано!", - "loadError": "Не удалось загрузить журналы сессии.", - "deleteError": "Не удалось удалить журнал сессии.", - "filterByHost": "Фильтрация по хосту..." + "loadError": "Не удалось загрузить журналы сеансов", + "deleteError": "Не удалось удалить журнал сеанса", + "filterByHost": "Фильтровать по хосту..." }, "networkGraph": { "addHost": "Добавить хост", "addGroup": "Добавить группу", - "addLink": "Добавить ссылку", - "zoomIn": "Увеличить масштаб", - "zoomOut": "Уменьшить масштаб", + "addLink": "Добавить связь", + "zoomIn": "Приблизить", + "zoomOut": "Отдалить", "resetView": "Сбросить вид", - "selectHost": "Выберите хост", + "selectHost": "Выбрать хост", "chooseHost": "Выберите хост...", "parentGroup": "Родительская группа", - "noGroup": "Нет группы", - "groupName": "Название группы", + "noGroup": "Без группы", + "groupName": "Имя группы", "color": "Цвет", "source": "Источник", "target": "Цель", - "moveToGroup": "Перейти в группу", + "moveToGroup": "Переместить в группу", "selectGroup": "Выберите группу...", "addConnection": "Добавить соединение", "hostDetails": "Сведения о хосте", @@ -2453,20 +2587,20 @@ "add": "Добавить", "create": "Создать", "move": "Переместить", - "connect": "Подключиться", + "connect": "Соединить", "createGroup": "Создать группу", "selectSourcePlaceholder": "Выберите источник...", "selectTargetPlaceholder": "Выберите цель...", - "invalidFile": "Недействительный файл", - "hostAlreadyExists": "Хост уже находится в топологии.", + "invalidFile": "Неверный файл", + "hostAlreadyExists": "Хост уже присутствует в топологии", "connectionExists": "Соединение уже существует", - "unknown": "Неизвестный", + "unknown": "Неизвестно", "name": "Имя", "ip": "IP", "status": "Статус", - "failedToAddNode": "Не удалось добавить узел.", - "sourceDifferentFromTarget": "Источник и цель должны быть разными.", - "exportJSON": "Экспорт в формате JSON", + "failedToAddNode": "Не удалось добавить узел", + "sourceDifferentFromTarget": "Источник и цель должны различаться", + "exportJSON": "Экспорт JSON", "importJSON": "Импорт JSON", "terminal": "Терминал", "fileManager": "Файловый менеджер", @@ -2474,15 +2608,15 @@ "docker": "Docker", "serverStats": "Метрики хоста", "hostMetrics": "Метрики хоста", - "noNodes": "Узлов пока нет." + "noNodes": "Пока нет узлов" }, "docker": { - "notEnabled": "Docker не включен для этого хоста.", + "notEnabled": "Docker не включен для этого хоста", "validating": "Проверка Docker...", "connecting": "Подключение...", "error": "Ошибка", "version": "Docker {{version}}", - "connectionFailed": "Не удалось подключиться к Docker.", + "connectionFailed": "Не удалось подключиться к Docker", "containerStarted": "Контейнер {{name}} запущен", "failedToStartContainer": "Не удалось запустить контейнер {{name}}", "containerStopped": "Контейнер {{name}} остановлен", @@ -2490,293 +2624,310 @@ "containerRestarted": "Контейнер {{name}} перезапущен", "failedToRestartContainer": "Не удалось перезапустить контейнер {{name}}", "containerPaused": "Контейнер {{name}} приостановлен", - "containerUnpaused": "Контейнер {{name}} не приостановлен", - "failedToTogglePauseContainer": "Не удалось переключить состояние паузы для контейнера {{name}}", - "containerRemoved": "Контейнер {{name}} удален", + "containerUnpaused": "Контейнер {{name}} возобновлён", + "failedToTogglePauseContainer": "Не удалось изменить состояние приостановки для контейнера {{name}}", + "containerRemoved": "Контейнер {{name}} удалён", "failedToRemoveContainer": "Не удалось удалить контейнер {{name}}", - "image": "Изображение", + "image": "Образ", "ports": "Порты", "noPorts": "Нет портов", "start": "Запустить", - "confirmRemoveContainer": "Вы уверены, что хотите удалить контейнер '{{name}}'? Это действие необратимо.", - "runningContainerWarning": "Внимание: Этот контейнер в данный момент запущен. Удаление контейнера сначала остановит основной контейнер.", + "confirmRemoveContainer": "Вы уверены, что хотите удалить контейнер «{{name}}»? Это действие нельзя отменить.", + "runningContainerWarning": "Предупреждение: этот контейнер в настоящее время работает. При удалении он будет сначала остановлен.", "loadingContainers": "Загрузка контейнеров...", "manager": "Менеджер Docker", - "autoRefresh": "Автоматическое обновление", - "timestamps": "Временные метки", - "lines": "Линии", - "filterLogs": "Фильтрация журналов...", + "autoRefresh": "Автообновление", + "timestamps": "Отметки времени", + "lines": "Строки", + "filterLogs": "Фильтровать логи...", "refresh": "Обновить", "download": "Скачать", "clear": "Очистить", - "logsDownloaded": "Журналы успешно загружены.", + "logsDownloaded": "Логи успешно скачаны", "last50": "Последние 50", "last100": "Последние 100", "last500": "Последние 500", "last1000": "Последние 1000", - "allLogs": "Все журналы", - "noLogsMatching": "Нет записей, соответствующих \"{{query}}\"", - "noLogsAvailable": "Журналы отсутствуют", + "allLogs": "Все логи", + "noLogsMatching": "Нет логов, соответствующих \"{{query}}\"", + "noLogsAvailable": "Логи отсутствуют", "noContainersFound": "Контейнеры не найдены", - "noContainersFoundHint": "На этом хосте контейнеры Docker недоступны.", - "searchPlaceholder": "Обыск контейнеров...", - "allStatuses": "Все статусы", - "stateRunning": "Выполняется", - "statePaused": "Приостановлено", - "stateExited": "Вышел", - "stateRestarting": "Перезапуск", - "noContainersMatchFilters": "Ни один контейнер не подходит к вашим фильтрам.", - "noContainersMatchFiltersHint": "Попробуйте скорректировать критерии поиска или фильтрации.", - "failedToFetchStats": "Не удалось получить статистику по контейнеру.", + "noContainersFoundHint": "На этом хосте нет Docker-контейнеров", + "searchPlaceholder": "Поиск контейнеров...", + "allStatuses": "Все состояния", + "stateRunning": "Запущен", + "statePaused": "Приостановлен", + "stateExited": "Завершён", + "stateRestarting": "Перезапускается", + "noContainersMatchFilters": "Нет контейнеров, соответствующих фильтрам", + "noContainersMatchFiltersHint": "Попробуйте изменить параметры поиска или фильтры", + "failedToFetchStats": "Не удалось получить статистику контейнера", "containerNotRunning": "Контейнер не запущен", - "startContainerToViewStats": "Запустите контейнер, чтобы просмотреть статистику.", + "startContainerToViewStats": "Запустите контейнер, чтобы посмотреть статистику", "loadingStats": "Загрузка статистики...", - "errorLoadingStats": "Статистика ошибок загрузки", - "noStatsAvailable": "Статистические данные отсутствуют.", + "errorLoadingStats": "Ошибка загрузки статистики", + "noStatsAvailable": "Статистика недоступна", "cpuUsage": "Использование ЦП", "current": "Текущий", "memoryUsage": "Использование памяти", "networkIo": "Сетевой ввод-вывод", - "input": "Вход", - "output": "Выход", + "input": "Ввод", + "output": "Вывод", "blockIo": "Блочный ввод-вывод", "read": "Чтение", "write": "Запись", - "pids": "Идентификаторы процессов", + "pids": "PID", "containerInformation": "Информация о контейнере", "name": "Имя", "id": "ID", "state": "Состояние", - "containerMustBeRunning": "Для доступа к консоли контейнер должен быть запущен.", - "verificationCodePrompt": "Введите проверочный код", - "totpVerificationFailed": "Проверка TOTP не удалась. Пожалуйста, попробуйте еще раз.", - "warpgateVerificationFailed": "Аутентификация Warpgate не удалась. Пожалуйста, попробуйте еще раз.", + "containerMustBeRunning": "Для доступа к консоли контейнер должен быть запущен", + "verificationCodePrompt": "Введите код подтверждения", + "totpVerificationFailed": "Ошибка проверки TOTP. Попробуйте еще раз.", + "warpgateVerificationFailed": "Ошибка аутентификации Warpgate. Попробуйте еще раз.", "connectedTo": "Подключено к {{containerName}}", "disconnected": "Отключено", "consoleError": "Ошибка консоли", "errorMessage": "Ошибка: {{message}}", - "failedToConnect": "Не удалось подключиться к контейнеру.", + "failedToConnect": "Не удалось подключиться к контейнеру", "console": "Консоль", "selectShell": "Выберите оболочку", "bash": "Bash", "sh": "sh", "ash": "ash", "connect": "Подключиться", - "disconnect": "Отключить", + "disconnect": "Отключиться", "notConnected": "Не подключено", - "clickToConnect": "Нажмите «Подключиться», чтобы начать работу в терминале", + "clickToConnect": "Нажмите «Подключиться», чтобы запустить сеанс оболочки", "connectingTo": "Подключение к {{containerName}}...", "containerNotFound": "Контейнер не найден", "backToList": "Вернуться к списку", - "logs": "Журналы", + "logs": "Логи", "stats": "Статистика", "consoleTab": "Консоль", - "startContainerToAccess": "Запустите контейнер, чтобы получить доступ к консоли." + "startContainerToAccess": "Запустите контейнер, чтобы получить доступ к консоли" }, "admin": { - "sectionGeneral": "Общий", + "sectionGeneral": "Общие", "sectionOidc": "OIDC", - "sectionSso": "Поставщики SSO", - "ssoAddProvider": "Добавить поставщика", - "ssoDocsLink": "Просмотреть документацию", - "ssoProviderDocsLink": "Просмотреть документацию", - "ssoNoProviders": "Поставщики SSO не настроены.", + "sectionSso": "Провайдеры SSO", + "ssoAddProvider": "Добавить провайдера", + "ssoDocsLink": "Документация", + "ssoProviderDocsLink": "Документация", + "ssoNoProviders": "Нет настроенных провайдеров SSO.", "ssoProviderName": "Отображаемое имя", - "ssoProviderType": "Тип поставщика", - "ssoDeleteProvider": "Удалить поставщика", - "ssoDeleteConfirm": "Удалить этого поставщика услуг? Связанные пользователи не смогут войти в систему.", + "ssoProviderType": "Тип провайдера", + "ssoDeleteProvider": "Удалить провайдера", + "ssoDeleteConfirm": "Удалить провайдера? Связанные пользователи не смогут войти.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", "ssoEnabled": "Включено", "ssoDisabled": "Отключено", - "ssoSaveProvider": "Сохранить поставщика", + "ssoSaveProvider": "Сохранить провайдера", "ssoTestConnection": "Проверить соединение", - "ssoEditProvider": "Поставщик редактирования", - "ldapHost": "Хост LDAP", + "ssoEditProvider": "Редактировать провайдера", + "ldapHost": "LDAP-сервер", "ldapPort": "Порт", - "ldapUseTls": "Используйте TLS (LDAP).", - "ldapBindDn": "DN привязки", + "ldapUseTls": "Использовать TLS (LDAPS)", + "ldapBindDn": "Bind DN", "ldapBindPassword": "Пароль привязки", "ldapUserSearchBase": "База поиска пользователей", - "ldapUserSearchFilter": "Фильтр поиска пользователя", + "ldapUserSearchFilter": "Фильтр поиска пользователей", "ldapUsernameAttr": "Атрибут имени пользователя", "ldapDisplayNameAttr": "Атрибут отображаемого имени", - "ldapGroupSearchBase": "База группового поиска", + "ldapGroupSearchBase": "База поиска групп", "ldapAdminGroup": "Группа администраторов", "ldapAllowedUsers": "Разрешенные пользователи", "sectionUsers": "Пользователи", "sectionSessions": "Сессии", "sectionRoles": "Роли", "sectionDatabase": "База данных", - "sectionApiKeys": "Ключи API", + "sectionApiKeys": "API-ключи", "sectionAuditLog": "Журнал аудита", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Автоматическая выдача и продление доверенного SSL-сертификата от Let's Encrypt. Требуется общедоступный домен и доступ к порту 80 или DNS.", - "sslDocsLink": "Просмотреть документацию по SSL", + "sslDescription": "Автоматически выпускать и обновлять доверенный SSL-сертификат от Let’s Encrypt. Требуется общедоступный домен и доступ к порту 80 или DNS.", + "sslDocsLink": "Документация SSL", "sslDomain": "Домен", "sslDomainPlaceholder": "termix.example.com", "sslDomainDesc": "Общедоступное доменное имя для сертификата.", - "sslEmail": "Электронная почта", + "sslEmail": "Email", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Для получения уведомлений от Let's Encrypt и управления учетной записью используйте электронный адрес.", + "sslEmailDesc": "Контактный email для уведомлений Let's Encrypt и учетной записи.", "sslChallengeType": "Тип проверки", - "sslChallengeTypeDesc": "Как подтвердить право владения доменом для Let's Encrypt.", - "sslChallengeHttp": "HTTP (корневой каталог веб-сервера) — требуется доступ к порту 80 из интернета.", - "sslChallengeDns": "DNS (Cloudflare) — требуется токен API Cloudflare.", + "sslChallengeTypeDesc": "Способ подтверждения владения доменом для Let’s Encrypt.", + "sslChallengeHttp": "HTTP (webroot) — требуется доступ к порту 80 из интернета", + "sslChallengeDns": "DNS (Cloudflare) — требуется API-токен Cloudflare", "sslCloudflareToken": "API-токен Cloudflare", "sslCloudflareTokenPlaceholder": "Введите токен...", - "sslCloudflareTokenDesc": "Токен с областью действия и разрешением Zone:DNS:Edit для вашего домена.", + "sslCloudflareTokenDesc": "Токен с ограниченной областью действия и разрешением Zone:DNS:Edit для вашего домена.", "sslCertStatus": "Статус сертификата", "sslCertStatusNone": "Сертификат отсутствует", "sslCertStatusValid": "Действительный", - "sslCertStatusExpiring": "Срок действия скоро истекает", - "sslCertStatusExpired": "Истекший", - "sslCertExpiresAt": "Срок действия истекает {{date}}", + "sslCertStatusExpiring": "Скоро истекает", + "sslCertStatusExpired": "Истек", + "sslCertExpiresAt": "Истекает {{date}}", "sslLastIssued": "Последний выпуск {{date}}", - "sslRequestCert": "Выдача/продление сертификата", + "sslRequestCert": "Выпустить / Обновить сертификат", "sslRequestCertLoading": "Запрос сертификата...", - "sslRequestCertSuccess": "Сертификат выдан и успешно установлен.", - "sslRequestCertFailed": "Запрос сертификата не удался.", - "sslSave": "Сохраните настройки", - "sslSaved": "Сохранены настройки SSL", - "sslSaveFailed": "Не удалось сохранить настройки SSL.", - "sslRequiresDomain": "Требуется указать домен и адрес электронной почты.", - "sslInfoNote": "После выдачи сертификата включите SSL в переменных среды (ENABLE_SSL=true) и перезапустите Termix.", - "auditLogTotal": "{{total}} всего записей", - "auditLogEmpty": "Записей в журнале аудита не обнаружено.", - "auditLogSuccess": "Успех", - "auditLogFailed": "Неуспешный", + "sslRequestCertSuccess": "Сертификат успешно выпущен и установлен", + "sslRequestCertFailed": "Ошибка запроса сертификата", + "sslSave": "Сохранить настройки", + "sslSaved": "Настройки SSL сохранены", + "sslSaveFailed": "Не удалось сохранить настройки SSL", + "sslRequiresDomain": "Требуются домен и email", + "sslInfoNote": "После выпуска сертификата включите SSL в переменных окружения (ENABLE_SSL=true) и перезапустите Termix.", + "sslManualOption": "Ручная загрузка сертификата", + "sslManualCert": "Сертификат (PEM)", + "sslManualCertPlaceholder": "-----НАЧАЛО ПОЛУЧЕНИЯ СЕРТИФИКАТА-----", + "sslManualKey": "Закрытый ключ (PEM)", + "sslManualKeyPlaceholder": "-----НАЧАЛО ПРИВАТНОГО КЛЮЧА-----", + "sslManualDesc": "Вставьте свой существующий сертификат и закрытый ключ, включая полную цепочку, если это требуется вашим центром сертификации.", + "sslManualUpload": "Загрузка и установка сертификата", + "sslManualUploadLoading": "Загрузка сертификата...", + "sslManualUploadSuccess": "Сертификат успешно загружен и установлен.", + "sslManualUploadFailed": "Загрузка сертификата не удалась.", + "sslManualRequiresFields": "Для работы требуются сертификат и закрытый ключ.", + "auditLogTotal": "Всего записей: {{total}}", + "auditLogEmpty": "Нет записей в журнале аудита", + "auditLogSuccess": "Успешно", + "auditLogFailed": "Неудачно", "auditLogClearFilters": "Очистить фильтры", - "auditLogPage": "Страница {{page}} из {{totalPages}} (всего{{total}})", + "auditLogPage": "Страница {{page}} из {{totalPages}} (всего {{total}})", "auditLogIp": "IP", - "auditLogResourceId": "Идентификатор ресурса", + "auditLogResourceId": "ID ресурса", "auditLogFilterUser": "Пользователь", "auditLogFilterAction": "Действие", "auditLogFilterResourceType": "Тип ресурса", "auditLogFilterStatus": "Статус", - "auditLogFilterFrom": "От", - "auditLogFilterTo": "К", + "auditLogFilterFrom": "С", + "auditLogFilterTo": "По", "auditLogFilterAll": "Все", "allowRegistration": "Разрешить регистрацию пользователей", - "allowRegistrationDesc": "Предоставьте новым пользователям возможность самостоятельно регистрироваться, используя имя пользователя и пароль.", + "allowRegistrationDesc": "Позволяет новым пользователям регистрироваться, указывая имя пользователя и пароль", "allowPasswordLogin": "Разрешить вход по паролю", - "allowPasswordLoginDesc": "Вход по имени пользователя/паролю", - "oidcAutoProvision": "Автоматическая настройка OIDC", - "oidcAutoProvisionDesc": "Автоматическое создание учетных записей для пользователей OIDC/SSO при первом входе в систему (независимо от включения функции регистрации).", - "oidcSilentLoginDefault": "По умолчанию включен бесшумный вход в OIDC.", - "oidcSilentLoginDefaultDesc": "Автоматическое перенаправление на страницу входа в OIDC при каждом посещении, без заполнения формы авторизации.", + "allowPasswordLoginDesc": "Вход по имени пользователя и паролю", + "oidcAutoProvision": "Автоматическое создание учётных записей OIDC", + "oidcAutoProvisionDesc": "Автоматически создавать учётные записи для пользователей OIDC/SSO при первом входе (независимо от переключателя регистрации)", + "oidcSilentLoginDefault": "Автоматический вход OIDC по умолчанию", + "oidcSilentLoginDefaultDesc": "Автоматически перенаправлять на страницу входа OIDC при каждом посещении, полностью пропуская форму входа", "allowPasswordReset": "Разрешить сброс пароля", - "allowPasswordResetDesc": "Сброс кода через логи Docker", + "allowPasswordResetDesc": "Код сброса в логах Docker", "commandHistoryEnabled": "История команд", - "commandHistoryEnabledDesc": "Разрешить запись истории команд. Если эта функция отключена, история не сохраняется независимо от настроек для каждого хоста.", - "updateCommandHistoryFailed": "Не удалось обновить настройки истории команд.", + "commandHistoryEnabledDesc": "Включить запись истории команд. При отключении история не сохраняется независимо от настроек хостов.", + "updateCommandHistoryFailed": "Не удалось обновить настройку истории команд", + "analyticsEnabled": "Делитесь анонимной статистикой использования.", + "analyticsEnabledDesc": "Ежедневно отправляет анонимный подсчет пользователей, хостов и использования функций, чтобы помочь улучшить Termix. Никакие личные данные или сведения о подключении никогда не включаются.", + "updateAnalyticsFailed": "Не удалось обновить настройки аналитики.", + "sessionSharingGloballyEnabled": "Разрешить совместное использование сеанса", + "sessionSharingGloballyEnabledDesc": "Разрешить общий доступ к сеансам Live Terminal, RDP, VNC и Telnet для всего экземпляра. При отключении этой функции отменяет все настройки общего доступа для отдельных хостов.", + "updateSessionSharingFailed": "Не удалось обновить настройки совместного доступа к сессии.", "sessionTimeout": "Тайм-аут сессии", - "hours": "часы", + "hours": "часов", "sessionTimeoutRange": "Мин. 1 ч · Макс. 720 ч", - "monitoringDefaults": "Настройки мониторинга по умолчанию", - "statusCheck": "Проверка статуса", + "monitoringDefaults": "Мониторинг по умолчанию", + "statusCheck": "Проверка состояния", "metrics": "Метрики", "sec": "сек", - "logLevel": "Уровень лога", + "logLevel": "Уровень логирования", "enableGuacamole": "Включить Guacamole", "enableGuacamoleDesc": "Удаленный рабочий стол RDP/VNC", - "enableGuacamoleDocsLink": "Просмотреть документацию", + "enableGuacamoleDocsLink": "Документация", "guacdUrl": "URL guacd", - "tailscaleApiKey": "Ключ API Tailscale", - "tailscaleApiKeyDescription": "Используется для обнаружения устройств в редакторе хостов. Создайте ключ на tailscale.com/admin/settings/keys.", - "tailscaleApiKeyDocsLink": "Просмотреть документацию", - "oidcDescription": "Настройте OpenID Connect для единого входа (SSO). Поля, отмеченные *, обязательны для заполнения.", - "oidcDocsLink": "Просмотреть документацию", - "oidcClientId": "Идентификатор клиента", - "oidcClientSecret": "Секрет клиента", + "tailscaleApiKey": "API-ключ Tailscale", + "tailscaleApiKeyDescription": "Используется для обнаружения устройств в редакторе хостов. Создайте ключ на странице tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "Документация", + "oidcDescription": "Настройка OpenID Connect для единого входа (SSO). Поля, отмеченные *, обязательны.", + "oidcDocsLink": "Документация", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", "oidcAuthUrl": "URL авторизации", - "oidcIssuerUrl": "URL эмитента", + "oidcIssuerUrl": "URL издателя", "oidcTokenUrl": "URL токена", - "oidcUserIdentifier": "Путь идентификатора пользователя", - "oidcDisplayName": "Отображаемый путь к имени", - "oidcScopes": "Области применения", - "oidcUserinfoUrl": "Переопределить URL-адрес информации о пользователе", + "oidcUserIdentifier": "Путь к идентификатору пользователя", + "oidcDisplayName": "Путь к отображаемому имени", + "oidcScopes": "Области", + "oidcUserinfoUrl": "Переопределить URL Userinfo", "oidcAllowedUsers": "Разрешенные пользователи", - "oidcAllowedUsersDesc": "Один адрес электронной почты на строку. Оставьте поле пустым, чтобы разрешить все адреса.", + "oidcAllowedUsersDesc": "По одному email на строку. Оставьте пустым, чтобы разрешить всем.", "oidcAdminGroup": "Группа администраторов", - "oidcAdminGroupDesc": "Пользователям в этой группе предоставляются права администратора. Оставьте поле пустым, чтобы отключить синхронизацию групп.", - "oidcGroupClaim": "Поле группы", - "oidcGroupClaimDesc": "Необязательный параметр. Путь к условию, содержащему группы пользователя. По умолчанию: группы, роли, затем группа. Используйте это для поставщиков с пользовательским условием (например, Zitadel).", - "oidcCaCert": "Пользовательский сертификат центра сертификации", - "oidcCaCertDesc": "Необязательно. Сертификат центра сертификации в формате PEM для поставщиков OIDC, использующих частный или самоподписанный центр сертификации. Оставьте поле пустым, чтобы использовать системное хранилище доверенных сертификатов.", + "oidcAdminGroupDesc": "Пользователи в этой группе получают права администратора. Оставьте пустым, чтобы отключить синхронизацию групп.", + "oidcGroupClaim": "Утверждение группы", + "oidcGroupClaimDesc": "Необязательно. Путь к утверждению, содержащему группы пользователя. По умолчанию проверяются groups, roles, затем group. Используйте для провайдеров с настраиваемым утверждением (например, Zitadel).", + "oidcCaCert": "Пользовательский сертификат ЦС", + "oidcCaCertDesc": "Необязательно. Сертификат ЦС в формате PEM для провайдеров OIDC, использующих частный или самоподписанный ЦС. Оставьте пустым, чтобы использовать системное хранилище доверенных сертификатов.", "removeOidc": "Удалить", "usersCount": "{{count}} пользователей", "createUser": "Создать", "newRole": "Новая роль", - "roleName": "Имя", + "roleName": "Название", "roleDisplayName": "Отображаемое имя", "roleDescription": "Описание", - "rolesCount": "{{count}} роли", + "rolesCount": "{{count}} ролей", "createRole": "Создать", "creating": "Создание...", "exportDatabase": "Экспорт базы данных", - "exportDatabaseDesc": "Загрузите резервную копию всех хостов, учетных данных и настроек.", - "export": "Экспорт", - "exporting": "Экспорт...", + "exportDatabaseDesc": "Скачать резервную копию всех хостов, учётных данных и настроек", + "export": "Экспортировать", + "exporting": "Экспортирование...", "importDatabase": "Импорт базы данных", - "importDatabaseDesc": "Восстановление из файла резервной копии .sqlite", + "importDatabaseDesc": "Восстановить из файла резервной копии .sqlite", "importDatabaseSelected": "Выбрано: {{name}}", - "selectFile": "Выберите файл", + "selectFile": "Выбрать файл", "changeFile": "Изменить", - "import": "Импорт", - "importing": "Импорт...", - "apiKeysCount": "{{count}} ключи", - "apiKeysDocsLink": "Просмотреть документацию", + "import": "Импортировать", + "importing": "Импортирование...", + "apiKeysCount": "{{count}} ключей", + "apiKeysDocsLink": "Документация", "newApiKey": "Новый ключ API", - "apiKeyCreatedWarning": "Ключ создан — скопируйте его сейчас, он больше не будет отображаться.", + "apiKeyCreatedWarning": "Ключ создан — скопируйте его сейчас, больше он показан не будет.", "apiKeyName": "Имя", "apiKeyUser": "Пользователь", "apiKeySelectUser": "Выберите пользователя...", - "apiKeyExpiresAt": "Срок действия истекает в", + "apiKeyExpiresAt": "Действует до", "createKey": "Создать ключ", - "apiKeyNoExpiry": "Срок действия не ограничен.", - "revokedBadge": "ОТМЕНЕНО", - "authTypeDual": "Двойная аутентификация", + "apiKeyNoExpiry": "Без срока действия", + "revokedBadge": "ОТОЗВАН", + "authTypeDual": "Двухфакторная аутентификация", "authTypeOidc": "OIDC", - "authTypeLocal": "Локальный", + "authTypeLocal": "Локальная", "adminStatusAdministrator": "Администратор", "adminStatusRegularUser": "Обычный пользователь", - "adminBadge": "АДМИНИСТРАТОР", - "systemBadge": "СИСТЕМА", - "customBadge": "ОБЫЧАЙ", + "adminBadge": "АДМИН", + "systemBadge": "СИСТ", + "customBadge": "ПОЛЬЗ.", "youBadge": "ВЫ", - "sessionsActive": "{{count}} активный", - "sessionActive": "Активно: {{time}}", - "sessionExpires": "Exp: {{time}}", + "sessionsActive": "{{count}} активных", + "sessionActive": "Активна: {{time}}", + "sessionExpires": "Истекает: {{time}}", "revokeAll": "Все", - "revokeAllSessionsSuccess": "Все сессии пользователя аннулированы.", - "revokeAllSessionsFailed": "Не удалось отменить сессии.", - "revokeSessionFailed": "Не удалось отменить сессию.", + "revokeAllSessionsSuccess": "Все сеансы пользователя отозваны", + "revokeAllSessionsFailed": "Не удалось отозвать сеансы", + "revokeSessionFailed": "Не удалось отозвать сеанс", "addRole": "Добавить роль", - "noCustomRoles": "Пользовательские роли не определены", + "noCustomRoles": "Нет пользовательских ролей", "removeRoleFailed": "Не удалось удалить роль", "assignRoleFailed": "Не удалось назначить роль", - "deleteRoleFailed": "Не удалось удалить роль.", + "deleteRoleFailed": "Не удалось удалить роль", "userAdminAccess": "Администратор", - "userAdminAccessDesc": "Полный доступ ко всем административным настройкам.", + "userAdminAccessDesc": "Полный доступ ко всем настройкам администратора", "userRoles": "Роли", - "revokeAllUserSessions": "Отменить все сессии", - "revokeAllUserSessionsDesc": "Принудительный повторный вход в систему на всех устройствах", + "revokeAllUserSessions": "Отозвать все сеансы", + "revokeAllUserSessionsDesc": "Принудительный повторный вход на всех устройствах", "revoke": "Отозвать", - "deleteUserWarning": "Удаление этого пользователя будет безвозвратным.", + "deleteUserWarning": "Удаление этого пользователя необратимо.", "deleteUser": "Удалить {{username}}", "deleting": "Удаление...", - "deleteUserFailed": "Не удалось удалить пользователя.", - "deleteUserSuccess": "Пользователь \"{{username}}\" удален", + "deleteUserFailed": "Не удалось удалить пользователя", + "deleteUserSuccess": "Пользователь \"{{username}}\" удалён", "deleteRoleSuccess": "Роль \"{{name}}\" удалена", "revokeKeySuccess": "Ключ \"{{name}}\" отозван", - "revokeKeyFailed": "Не удалось отозвать ключ.", + "revokeKeyFailed": "Не удалось отозвать ключ", "copiedToClipboard": "Скопировано в буфер обмена", "done": "Готово", "createUserTitle": "Создать пользователя", - "createUserDesc": "Создайте новую локальную учетную запись.", + "createUserDesc": "Создать новую локальную учётную запись.", "createUserUsername": "Имя пользователя", "createUserPassword": "Пароль", "createUserPasswordHint": "Минимум 6 символов.", @@ -2784,173 +2935,173 @@ "createUserEnterPassword": "Введите пароль", "createUserSubmit": "Создать пользователя", "editUserTitle": "Управление пользователем: {{username}}", - "editUserDesc": "Редактируйте роли, статус администратора, сессии и настройки учетной записи.", + "editUserDesc": "Редактирование ролей, статуса администратора, сеансов и настроек учётной записи.", "editUserUsername": "Имя пользователя", "editUserAuthType": "Тип аутентификации", "editUserAdminStatus": "Статус администратора", "editUserUserId": "ID пользователя", "linkAccountTitle": "Связать аккаунты", - "linkAccountDesc": "Объедините учетную запись OIDC {{username}} с существующей локальной учетной записью.", - "linkAccountDescLocal": "Свяжите локальную учетную запись {{username}} с существующей учетной записью, поддерживающей только OIDC.", - "linkAccountWarningTitle": "Это позволит:", - "linkAccountEffect1": "Удалите учетную запись, предназначенную только для OIDC.", - "linkAccountEffect2": "Добавьте вход через OIDC в целевую учетную запись.", - "linkAccountEffect3": "Разрешите вход как по протоколу OIDC, так и по паролю.", - "linkAccountTargetUsername": "Имя пользователя локальной учетной записи", - "linkAccountTargetPlaceholder": "Введите имя пользователя локальной учетной записи, которую нужно связать с...", - "linkAccountOidcUsername": "Имя пользователя учетной записи OIDC", - "linkAccountOidcPlaceholder": "Введите имя пользователя учетной записи, предназначенной только для OIDC, для объединения.", - "linkAccountOidcNotFound": "Учетная запись OIDC с таким именем пользователя не найдена.", + "linkAccountDesc": "Объединить OIDC-аккаунт {{username}} с существующим локальным аккаунтом.", + "linkAccountDescLocal": "Связать локальный аккаунт {{username}} с существующим OIDC-аккаунтом.", + "linkAccountWarningTitle": "Будет выполнено:", + "linkAccountEffect1": "Удалить OIDC-аккаунт", + "linkAccountEffect2": "Добавить OIDC-вход в целевой аккаунт", + "linkAccountEffect3": "Разрешить вход как через OIDC, так и по паролю", + "linkAccountTargetUsername": "Имя пользователя локального аккаунта", + "linkAccountTargetPlaceholder": "Введите имя пользователя локального аккаунта для связывания", + "linkAccountOidcUsername": "Имя пользователя OIDC-аккаунта", + "linkAccountOidcPlaceholder": "Введите имя пользователя OIDC-аккаунта для объединения", + "linkAccountOidcNotFound": "Не найден аккаунт, использующий только OIDC, с таким именем пользователя.", "linkAccounts": "Связать аккаунты", - "linkAccountSuccess": "Учетные записи успешно связаны.", - "linkAccountFailed": "Не удалось связать учетные записи.", + "linkAccountSuccess": "Аккаунты успешно связаны", + "linkAccountFailed": "Не удалось связать аккаунты", "linkAccountInProgress": "Связывание...", - "unlinkAccountTitle": "Отключить OIDC", - "unlinkAccountDesc": "Удалите аутентификацию OIDC из {{username}}. Они смогут войти в систему только с помощью своего пароля.", - "unlinkAccountWarning": "Это удалит возможность входа в OIDC для данной учетной записи. Для продолжения входа в систему пользователю необходимо установить пароль.", - "unlinkAccount": "Отключить OIDC", - "unlinkAccountInProgress": "Отмена связи...", - "unlinkAccountSuccess": "OIDC успешно отсоединен.", - "unlinkAccountFailed": "Не удалось разорвать связь OIDC", + "unlinkAccountTitle": "Отвязать OIDC", + "unlinkAccountDesc": "Удалить OIDC-аутентификацию для {{username}}. Они смогут входить только по паролю.", + "unlinkAccountWarning": "Это удалит OIDC-вход из этого аккаунта. Пользователь должен иметь установленный пароль для входа.", + "unlinkAccount": "Отвязать OIDC", + "unlinkAccountInProgress": "Отвязывание...", + "unlinkAccountSuccess": "OIDC успешно отвязан", + "unlinkAccountFailed": "Не удалось отвязать OIDC", "saving": "Сохранение...", - "updateRegistrationFailed": "Не удалось обновить настройки регистрации.", - "updatePasswordLoginFailed": "Не удалось обновить настройки входа по паролю.", - "cannotDisablePasswordLoginWithTotp": "Невозможно отключить вход по паролю, если двухфакторная аутентификация включена для одного или нескольких пользователей. Сначала отключите двухфакторную аутентификацию.", - "updateOidcAutoProvisionFailed": "Не удалось обновить настройки автоматической настройки OIDC.", - "updateOidcSilentLoginDefaultFailed": "Не удалось обновить настройки бесшумного входа в OIDC.", - "updatePasswordResetFailed": "Не удалось обновить настройки сброса пароля.", - "sessionTimeoutRange2": "Время ожидания сессии должно составлять от 1 до 720 часов.", - "sessionTimeoutSaved": "Сохранено время ожидания сессии", - "sessionTimeoutSaveFailed": "Не удалось сохранить сеанс, истекло время ожидания", - "monitoringIntervalInvalid": "Недопустимые значения интервала", - "monitoringSaved": "Сохранены настройки мониторинга.", - "monitoringSaveFailed": "Не удалось сохранить настройки мониторинга.", - "metricsHistoryRetention": "Сохранение истории метрик", + "updateRegistrationFailed": "Не удалось обновить настройки регистрации", + "updatePasswordLoginFailed": "Не удалось обновить настройки входа по паролю", + "cannotDisablePasswordLoginWithTotp": "Нельзя отключить вход по паролю, если у одного или нескольких пользователей включена 2FA. Сначала отключите 2FA.", + "updateOidcAutoProvisionFailed": "Не удалось обновить настройки автоматического создания пользователей через OIDC", + "updateOidcSilentLoginDefaultFailed": "Не удалось обновить настройки автоматического входа через OIDC", + "updatePasswordResetFailed": "Не удалось обновить настройки сброса пароля", + "sessionTimeoutRange2": "Тайм-аут сеанса должен быть от 1 до 720 часов", + "sessionTimeoutSaved": "Таймаут сессии сохранён", + "sessionTimeoutSaveFailed": "Не удалось сохранить таймаут сессии", + "monitoringIntervalInvalid": "Недопустимые значения интервалов", + "monitoringSaved": "Настройки мониторинга сохранены", + "monitoringSaveFailed": "Не удалось сохранить настройки мониторинга", + "metricsHistoryRetention": "Срок хранения метрик", "metricsHistoryRetentionRange": "от 1 до 90 дней", - "days": "дни", + "days": "дней", "guacamoleSaved": "Настройки Guacamole сохранены", "guacamoleSaveFailed": "Не удалось сохранить настройки Guacamole", - "guacamoleUpdateFailed": "Не удалось обновить настройки Guacamole", - "tailscaleSettingsSaved": "Сохранены настройки масштаба хвоста", - "tailscaleSettingsSaveFailed": "Не удалось сохранить настройки масштаба хвоста.", - "logLevelUpdateFailed": "Не удалось обновить уровень логирования.", - "oidcSaved": "Конфигурация OIDC сохранена.", - "oidcSaveFailed": "Не удалось сохранить конфигурацию OIDC.", + "guacamoleUpdateFailed": "Не удалось обновить настройку Guacamole", + "tailscaleSettingsSaved": "Настройки Tailscale сохранены", + "tailscaleSettingsSaveFailed": "Не удалось сохранить настройки Tailscale", + "logLevelUpdateFailed": "Не удалось обновить уровень журналирования", + "oidcSaved": "Конфигурация OIDC сохранена", + "oidcSaveFailed": "Не удалось сохранить конфигурацию OIDC", "oidcRemoved": "Конфигурация OIDC удалена", - "oidcRemoveFailed": "Не удалось удалить конфигурацию OIDC.", - "createUserRequired": "Требуется имя пользователя и пароль.", - "createUserPasswordTooShort": "Пароль должен состоять как минимум из 6 символов.", - "createUserSuccess": "Пользователь \"{{username}}\" создал", - "createUserFailed": "Не удалось создать пользователя.", - "updateAdminStatusFailed": "Не удалось обновить статус администратора.", - "allSessionsRevoked": "Все сеансы отозваны", - "revokeSessionsFailed": "Не удалось отменить сессии.", - "manageUserData": "Управление пользовательскими данными", - "backToUsers": "Вернуться к пользователям", - "manageTabAccount": "Счет", - "manageTabHosts": "Хозяева", - "manageTabCredentials": "Реквизиты для входа", - "manageTabSnippets": "Фрагменты", + "oidcRemoveFailed": "Не удалось удалить конфигурацию OIDC", + "createUserRequired": "Имя пользователя и пароль обязательны", + "createUserPasswordTooShort": "Пароль должен содержать не менее 6 символов", + "createUserSuccess": "Пользователь \"{{username}}\" создан", + "createUserFailed": "Не удалось создать пользователя", + "updateAdminStatusFailed": "Не удалось обновить статус администратора", + "allSessionsRevoked": "Все сессии завершены", + "revokeSessionsFailed": "Не удалось завершить сессии", + "manageUserData": "Управление данными пользователя", + "backToUsers": "Назад к пользователям", + "manageTabAccount": "Учётная запись", + "manageTabHosts": "Хосты", + "manageTabCredentials": "Учётные данные", + "manageTabSnippets": "Сниппеты", "manageTabSessions": "Сессии", "manageTabDanger": "Опасность", - "manageEditorBack": "Вернуться к данным {{username}}", + "manageEditorBack": "Назад к данным {{username}}", "dataLockedBadge": "ЗАБЛОКИРОВАНО", - "dataLockedNotice": "Данные пользователя {{username}}остаются заблокированными до следующего входа в систему. До этого момента его хосты, учетные данные и фрагменты кода нельзя просмотреть или отредактировать.", - "resetPasswordTitle": "Сбросить пароль", - "resetPasswordOidcOnly": "Этот пользователь входит в систему через стороннего провайдера и не имеет пароля.", + "dataLockedNotice": "Данные {{username}} остаются заблокированными до следующего входа. Хосты, учётные данные и сниппеты нельзя просмотреть или отредактировать до тех пор.", + "resetPasswordTitle": "Сброс пароля", + "resetPasswordOidcOnly": "Этот пользователь входит через внешнего провайдера и не имеет пароля.", "resetPasswordPlaceholder": "Новый пароль", - "resetPasswordBtn": "Перезагрузить", + "resetPasswordBtn": "Сбросить", "resetPasswordWorking": "Сброс...", - "resetPasswordSuccess": "Сброс пароля", - "resetPasswordSuccessWiped": "Пароль сброшен. Зашифрованные данные пользователя были удалены.", + "resetPasswordSuccess": "Пароль сброшен", + "resetPasswordSuccessWiped": "Пароль сброшен. Зашифрованные данные пользователя удалены.", "resetPasswordFailed": "Не удалось сбросить пароль", - "resetPasswordConfirmWipe": "{{username}} не входил в систему с момента обновления шифрования, поэтому его данные восстановить невозможно. Сброс настроек приведет к удалению его хостов, учетных данных и фрагментов кода. Продолжить?", + "resetPasswordConfirmWipe": "{{username}} не входил в систему после обновления шифрования, поэтому его данные не могут быть восстановлены. При сбросе будут удалены его хосты, учётные данные и сниппеты. Продолжить?", "totpSectionTitle": "Двухфакторная аутентификация", - "totpStatusEnabled": "Для этого пользователя включена функция TOTP.", - "totpStatusDisabled": "Для этого пользователя функция TOTP отключена.", - "disableTotp": "Запрещать", - "disableTotpConfirm": "Отключить двухфакторную аутентификацию для {{username}}? Они смогут войти в систему, используя только свой пароль.", - "totpDisabledSuccess": "TOTP отключен", - "totpDisableFailed": "Не удалось отключить TOTP.", - "manageApiKeys": "Ключи API", + "totpStatusEnabled": "TOTP включён для этого пользователя", + "totpStatusDisabled": "TOTP не включён для этого пользователя", + "disableTotp": "Отключить", + "disableTotpConfirm": "Отключить двухфакторную аутентификацию для {{username}}? После этого он сможет входить только с паролем.", + "totpDisabledSuccess": "TOTP отключён", + "totpDisableFailed": "Не удалось отключить TOTP", + "manageApiKeys": "API-ключи", "apiKeyNamePlaceholder": "Имя ключа", - "apiKeyCopyNotice": "Скопируйте этот ключ сейчас, он больше не будет отображаться.", - "noApiKeysForUser": "Ключи API отсутствуют", - "apiKeyDeleteFailed": "Не удалось удалить ключ API.", + "apiKeyCopyNotice": "Скопируйте этот ключ сейчас, он больше не будет показан.", + "noApiKeysForUser": "Нет API-ключей", + "apiKeyDeleteFailed": "Не удалось удалить API-ключ", "exportUserData": "Экспорт данных", - "exportUserDataDesc": "Загрузите данные о хостах, учетные данные и файловый менеджер этого пользователя в формате JSON. Секретные данные будут расшифрованы.", - "exportUserDataSuccess": "Экспорт пользовательских данных", - "exportUserDataFailed": "Не удалось экспортировать данные пользователя.", - "hostsCount": "{{count}} хосты", + "exportUserDataDesc": "Скачать хосты, учётные данные и данные файлового менеджера этого пользователя в формате JSON. Секреты расшифрованы.", + "exportUserDataSuccess": "Данные пользователя экспортированы", + "exportUserDataFailed": "Не удалось экспортировать данные пользователя", + "hostsCount": "{{count}} хостов", "addHostForUser": "Добавить хост", - "noHostsForUser": "У этого пользователя нет хостов.", - "connectToHost": "Соединять", - "deleteHostConfirm": "Удалить хост \"{{name}}\", принадлежащий {{username}}?", - "hostDeletedSuccess": "Хост удален", + "noHostsForUser": "У этого пользователя нет хостов", + "connectToHost": "Подключиться", + "deleteHostConfirm": "Удалить хост \"{{name}}\", принадлежащий пользователю {{username}}?", + "hostDeletedSuccess": "Хост удалён", "hostDeleteFailed": "Не удалось удалить хост", - "credentialsCount": "{{count}} учетные данные", - "addCredentialForUser": "Добавить учетные данные", - "noCredentialsForUser": "У этого пользователя нет учетных данных.", - "deleteCredentialConfirm": "Удалить учетные данные \"{{name}}\", принадлежащие {{username}}?", - "credentialDeletedSuccess": "Учетные данные удалены", - "credentialDeleteFailed": "Не удалось удалить учетные данные.", - "snippetsCount": "{{count}} фрагменты", - "addSnippetForUser": "Добавить фрагмент", - "noSnippetsForUser": "У этого пользователя нет фрагментов кода.", - "snippetRequiredFields": "Название и содержимое фрагмента текста обязательны для заполнения.", - "snippetNamePlaceholder": "Название фрагмента", + "credentialsCount": "{{count}} учётных данных", + "addCredentialForUser": "Добавить учётные данные", + "noCredentialsForUser": "У этого пользователя нет учётных данных", + "deleteCredentialConfirm": "Удалить учётные данные \"{{name}}\", принадлежащие {{username}}?", + "credentialDeletedSuccess": "Учётные данные удалены", + "credentialDeleteFailed": "Не удалось удалить учётные данные", + "snippetsCount": "{{count}} сниппетов", + "addSnippetForUser": "Добавить сниппет", + "noSnippetsForUser": "У этого пользователя нет сниппетов", + "snippetRequiredFields": "Имя и содержимое сниппета обязательны", + "snippetNamePlaceholder": "Имя сниппета", "snippetContentPlaceholder": "Содержимое команды", "snippetFolderPlaceholder": "Папка (необязательно)", - "snippetSaved": "Сохранённый фрагмент", - "snippetSaveFailed": "Не удалось сохранить фрагмент", - "deleteSnippetConfirm": "Удалить фрагмент \"{{name}}\", относящийся к {{username}}?", - "snippetDeletedSuccess": "Фрагмент удален", - "snippetDeleteFailed": "Не удалось удалить фрагмент", - "noSessionsForUser": "Активных сессий нет.", - "deleteUserDangerDesc": "Безвозвратно удалить {{username}} и все его данные (хостинги, учетные данные, фрагменты кода, историю). Это действие необратимо.", - "deleteUserConfirm": "Удалить навсегда {{username}} и все их данные?", - "deleteUserAdminBlocked": "Перед удалением этого пользователя необходимо снять с него статус администратора.", - "createRoleRequired": "Имя и отображаемое имя обязательны для заполнения.", - "createRoleSuccess": "Создана роль \"{{name}}", + "snippetSaved": "Сниппет сохранён", + "snippetSaveFailed": "Не удалось сохранить сниппет", + "deleteSnippetConfirm": "Удалить сниппет \"{{name}}\", принадлежащий {{username}}?", + "snippetDeletedSuccess": "Сниппет удалён", + "snippetDeleteFailed": "Не удалось удалить сниппет", + "noSessionsForUser": "Нет активных сессий", + "deleteUserDangerDesc": "Безвозвратно удалить {{username}} и все его данные (хосты, учётные данные, сниппеты, историю). Это действие нельзя отменить.", + "deleteUserConfirm": "Безвозвратно удалить {{username}} и все его данные?", + "deleteUserAdminBlocked": "Сначала снимите статус администратора, прежде чем удалять этого пользователя.", + "createRoleRequired": "Имя и отображаемое имя обязательны", + "createRoleSuccess": "Роль \"{{name}}\" создана", "createRoleFailed": "Не удалось создать роль", - "apiKeyNameRequired": "Имя ключа обязательно для ввода.", - "apiKeyUserRequired": "Требуется идентификатор пользователя.", - "apiKeyCreatedSuccess": "Создан ключ API \"{{name}}", - "apiKeyCreateFailed": "Не удалось создать ключ API.", - "exportSuccess": "База данных успешно экспортирована.", - "exportFailed": "Экспорт базы данных не удался.", - "importSelectFile": "Пожалуйста, сначала выберите файл.", - "importCompleted": "Импорт завершен: импортировано {{total}} объектов, {{skipped}} пропущено", - "importFailed": "Импорт не удался: {{error}}", - "importError": "Импорт базы данных не удался.", - "sectionHostDefaults": "Настройки хоста по умолчанию", - "hostDefaultsDesc": "Настройки применяются автоматически при создании нового хоста. Отдельные хосты по-прежнему могут переопределять эти настройки.", - "hostDefaultsSocks5": "SOCKS5 Proxy", - "hostDefaultsUseSocks5": "Включить прокси SOCKS5", - "hostDefaultsUseSocks5Desc": "Предварительное заполнение SOCKS5-прокси на всех новых хостах", - "hostDefaultsSocks5Host": "Прокси-сервер / Порт", + "apiKeyNameRequired": "Имя ключа обязательно", + "apiKeyUserRequired": "Требуется ID пользователя", + "apiKeyCreatedSuccess": "API-ключ \"{{name}}\" создан", + "apiKeyCreateFailed": "Не удалось создать API-ключ", + "exportSuccess": "База данных успешно экспортирована", + "exportFailed": "Не удалось экспортировать базу данных", + "importSelectFile": "Сначала выберите файл", + "importCompleted": "Импорт завершён: импортировано {{total}} элементов, пропущено {{skipped}}", + "importFailed": "Ошибка импорта: {{error}}", + "importError": "Не удалось импортировать базу данных", + "sectionHostDefaults": "Настройки хостов по умолчанию", + "hostDefaultsDesc": "Параметры, автоматически применяемые при создании нового хоста. Их можно переопределить для отдельных хостов.", + "hostDefaultsSocks5": "SOCKS5-прокси", + "hostDefaultsUseSocks5": "Включить SOCKS5-прокси", + "hostDefaultsUseSocks5Desc": "Предварительно заполнять SOCKS5-прокси для всех новых хостов", + "hostDefaultsSocks5Host": "Хост / порт прокси", "hostDefaultsSocks5Username": "Имя пользователя прокси", "hostDefaultsSocks5Password": "Пароль прокси", "hostDefaultsMetrics": "Метрики хоста", "hostDefaultsMetricsEnabled": "Включить метрики", - "hostDefaultsMetricsEnabledDesc": "По умолчанию осуществляется сбор статистики по процессору, памяти и другим параметрам на новых хостах.", + "hostDefaultsMetricsEnabledDesc": "По умолчанию собирать данные о ЦП, памяти и другую статистику с новых хостов", "hostDefaultsStatusCheckEnabled": "Включить проверку состояния", - "hostDefaultsStatusCheckEnabledDesc": "По умолчанию запрашивать статус онлайн/офлайн новых хостов.", + "hostDefaultsStatusCheckEnabledDesc": "По умолчанию опрашивать состояние новых хостов (онлайн/офлайн)", "hostDefaultsTerminal": "Терминал", - "hostDefaultsSessionLogging": "Ведение журнала сессий", - "hostDefaultsSessionLoggingDesc": "Запись терминальных сессий на новых хостах по умолчанию.", + "hostDefaultsSessionLogging": "Журналирование сеансов", + "hostDefaultsSessionLoggingDesc": "По умолчанию записывать терминальные сеансы на новых хостах", "hostDefaultsCommandHistory": "История команд", - "hostDefaultsCommandHistoryDesc": "Отслеживание истории команд на новых хостах по умолчанию.", - "hostDefaultsSaved": "Сохранены настройки хоста по умолчанию", - "hostDefaultsSaveFailed": "Не удалось сохранить настройки хоста по умолчанию.", + "hostDefaultsCommandHistoryDesc": "По умолчанию отслеживать историю команд на новых хостах", + "hostDefaultsSaved": "Настройки хостов по умолчанию сохранены", + "hostDefaultsSaveFailed": "Не удалось сохранить настройки хостов по умолчанию", "rolePermissions": { - "count": "{{count}} разрешения", - "editAction": "Редактировать права доступа", - "loadError": "Не удалось загрузить каталог разрешений.", - "saved": "Права доступа к ролям сохранены", - "saveError": "Не удалось сохранить права доступа к ролям.", - "save": "Сохранять", - "saving": "Сохранение..." + "count": "{{count}} разрешений", + "editAction": "Изменить разрешения", + "loadError": "Не удалось загрузить каталог разрешений", + "saved": "Разрешения роли сохранены", + "saveError": "Не удалось сохранить разрешения роли", + "save": "Сохранить", + "saving": "Сохранение…" } }, "newUi": { @@ -2966,246 +3117,291 @@ "passwordLabel": "Пароль", "passwordPlaceholder": "пароль", "privateKeyLabel": "Закрытый ключ", - "privateKeyPlaceholder": "Вставьте закрытый ключ...", - "credentialLabel": "Учётные данные", - "credentialPlaceholder": "Выберите сохраненные учетные данные", + "privateKeyPlaceholder": "Вставьте приватный ключ...", + "credentialLabel": "Учетные данные", + "credentialPlaceholder": "Выберите сохранённые учётные данные", "connectToTerminal": "Подключиться к терминалу", "connectToFiles": "Подключиться к файлам" }, "history": { "noTerminalSelected": "Терминал не выбран", - "noTerminalSelectedHint": "Откройте вкладку SSH-терминала, чтобы просмотреть историю команд.", - "searchPlaceholder": "История поиска...", - "clearAll": "Очистить все", - "noHistoryEntries": "Нет записей в истории.", - "trackingDisabled": "Отслеживание истории отключено.", - "trackingDisabledHint": "Включите эту функцию в настройках терминала хоста." + "noTerminalSelectedHint": "Откройте вкладку SSH-терминала, чтобы просмотреть историю команд", + "searchPlaceholder": "Поиск по истории...", + "clearAll": "Очистить всё", + "noHistoryEntries": "Нет записей в истории", + "trackingDisabled": "Отслеживание истории отключено", + "trackingDisabledHint": "Включите его в настройках терминала хоста." }, "sshTools": { - "keyRecordingTitle": "Запись ключевых моментов", - "recordToTerminals": "Запись на терминалы", + "keyRecordingTitle": "Запись клавиш", + "recordToTerminals": "Записать в терминалы", "selectAll": "Все", - "selectNone": "Никто", - "noTerminalTabsOpen": "Вкладки терминала не открыты", + "selectNone": "Ни одного", + "noTerminalTabsOpen": "Нет открытых вкладок терминала", "selectTerminalsAbove": "Выберите терминалы выше", - "broadcastInputPlaceholder": "Введите здесь, чтобы транслировать нажатия клавиш...", - "fillPassword": "Введите пароль", - "fillPasswordSuccess": "Введен пароль в терминал(ы) {{count}}", - "fillPasswordMissing": "Для выбранного(ых) терминала(ов) {{count}} не сохранен(-ов) пароль.", + "broadcastInputPlaceholder": "Введите текст для трансляции нажатий...", + "fillPassword": "Заполнить пароль", + "fillPasswordSuccess": "Пароль введён в {{count}} терминалах", + "fillPasswordMissing": "Нет сохранённого пароля для {{count}} выбранных терминалов", "stopRecording": "Остановить запись", "startRecording": "Начать запись", "settingsTitle": "Настройки", - "enableRightClickCopyPaste": "Включить копирование/вставку по щелчку правой кнопкой мыши" + "enableRightClickCopyPaste": "Включить копирование/вставку правой кнопкой мыши" }, "splitScreen": { "layoutTitle": "Макет", "selectLayoutAbove": "Выберите макет выше", - "selectLayoutHint": "Выберите количество отображаемых панелей.", + "selectLayoutHint": "Выберите количество отображаемых панелей", "panesTitle": "Панели", - "openTabsTitle": "Открыть вкладки", - "dragTabsHint": "Перетащите вкладки в панели выше или воспользуйтесь функцией быстрого назначения.", - "dropHere": "Опустите сюда", - "emptyPane": "Пустой", + "openTabsTitle": "Открытые вкладки", + "dragTabsHint": "Перетащите вкладки в панели выше или используйте быстрое назначение", + "dropHere": "Перетащите сюда", + "emptyPane": "Пусто", "dashboard": "Панель управления", - "clearSplitScreen": "Очистить разделённый экран", + "clearSplitScreen": "Убрать разделение", "quickAssign": "Быстрое назначение", "alreadyAssigned": "Панель {{index}}", "splitTab": "Разделить вкладку", - "addToSplit": "Добавить в раздел", - "removeFromSplit": "Удалить из Сплита", + "addToSplit": "Добавить в разделение", + "removeFromSplit": "Убрать из разделения", "assignToPane": "Назначить на панель", - "hotkeysTitle": "Клавиатурные сочетания", - "hotkeysSplitRight": "Переключить разделение на две части", - "hotkeysSplitBelow": "Переключить разделение на 3 части", - "hotkeysNavigatePane": "Перемещение по панелям", + "hotkeysTitle": "Горячие клавиши", + "hotkeysSplitRight": "Переключить разделение на две панели", + "hotkeysSplitBelow": "Переключить разделение на три панели", + "hotkeysNavigatePane": "Навигация по панелям", "hotkeysNextTab": "Следующая вкладка", "hotkeysPrevTab": "Предыдущая вкладка" }, "snippets": { - "title": "Фрагменты кода", - "createSnippetTitle": "Создать фрагмент", - "createSnippetDescription": "Создайте новый фрагмент команды для быстрого выполнения.", - "nameLabel": "Имя", - "namePlaceholder": "например, перезапустите Nginx.", + "title": "Сниппеты", + "createSnippetTitle": "Создать сниппет", + "createSnippetDescription": "Создать новый сниппет команды для быстрого выполнения", + "nameLabel": "Название", + "namePlaceholder": "например, Перезапустить Nginx", "descriptionLabel": "Описание", - "descriptionPlaceholder": "Дополнительное описание", - "optional": "Необязательный", + "descriptionPlaceholder": "Необязательное описание", + "optional": "Необязательно", "folderLabel": "Папка", "noFolder": "Нет папки (Без категории)", "commandLabel": "Команда", "commandPlaceholder": "например, sudo systemctl restart nginx", "cancel": "Отмена", - "createSnippetButton": "Создать фрагмент", + "createSnippetButton": "Создать сниппет", "createFolderTitle": "Создать папку", - "createFolderDescription": "Организуйте свои фрагменты кода по папкам.", + "createFolderDescription": "Организуйте сниппеты по папкам", "folderNameLabel": "Название папки", - "folderNamePlaceholder": "например, системные команды, скрипты Docker.", + "folderNamePlaceholder": "например, Системные команды, Скрипты Docker", "folderColorLabel": "Цвет папки", "folderIconLabel": "Значок папки", - "previewLabel": "Предварительный просмотр", + "previewLabel": "Предпросмотр", "folderNameFallback": "Название папки", "createFolderButton": "Создать папку", "targetTerminals": "Целевые терминалы", "selectAll": "Все", - "selectNone": "Никто", - "noTerminalTabsOpen": "Вкладки терминала не открыты", - "searchPlaceholder": "Поиск фрагментов кода...", - "newSnippet": "Новый фрагмент", + "selectNone": "Ничего", + "noTerminalTabsOpen": "Нет открытых вкладок терминала", + "searchPlaceholder": "Поиск сниппетов...", + "newSnippet": "Новый сниппет", "newFolder": "Новая папка", "run": "Запустить", - "noSnippetsInFolder": "В этой папке нет фрагментов кода.", + "noSnippetsInFolder": "В этой папке нет сниппетов", "uncategorized": "Без категории", - "editSnippetTitle": "Редактировать фрагмент", - "editSnippetDescription": "Обновите этот фрагмент команды.", + "editSnippetTitle": "Редактировать сниппет", + "editSnippetDescription": "Обновить этот командный сниппет", "saveSnippetButton": "Сохранить изменения", - "createSuccess": "Фрагмент кода успешно создан.", - "createFailed": "Не удалось создать фрагмент", - "updateSuccess": "Фрагмент кода успешно обновлен.", - "updateFailed": "Не удалось обновить фрагмент кода.", - "deleteFailed": "Не удалось удалить фрагмент", - "folderCreateSuccess": "Папка успешно создана.", - "folderCreateFailed": "Не удалось создать папку.", + "createSuccess": "Сниппет успешно создан", + "createFailed": "Не удалось создать сниппет", + "updateSuccess": "Сниппет успешно обновлён", + "updateFailed": "Не удалось обновить сниппет", + "deleteFailed": "Не удалось удалить сниппет", + "folderCreateSuccess": "Папка успешно создана", + "folderCreateFailed": "Не удалось создать папку", "editFolderTitle": "Редактировать папку", "editFolderDescription": "Переименовать или изменить внешний вид этой папки", "saveFolderButton": "Сохранить изменения", "editFolder": "Редактировать папку", "deleteFolder": "Удалить папку", "folderDeleteSuccess": "Папка \"{{name}}\" удалена", - "folderDeleteFailed": "Не удалось удалить папку.", - "folderEditSuccess": "Папка успешно обновлена.", - "folderEditFailed": "Не удалось обновить папку.", + "folderDeleteFailed": "Не удалось удалить папку", + "folderEditSuccess": "Папка успешно обновлена", + "folderEditFailed": "Не удалось обновить папку", "confirmRunMessage": "Запустить \"{{name}}\"?", "confirmRunButton": "Запустить", - "runSuccess": "Выполнено \"{{name}}\" в {{count}} терминале(ах)", - "copySuccess": "Скопировано \"{{name}}\" в буфер обмена", - "shareTitle": "Поделиться фрагментом", + "runSuccess": "Запущен \"{{name}}\" в {{count}} терминале(ах)", + "copySuccess": "Сниппет \"{{name}}\" скопирован в буфер обмена", + "shareTitle": "Поделиться сниппетом", "shareUser": "Пользователь", "shareRole": "Роль", "selectUser": "Выберите пользователя...", "selectRole": "Выберите роль...", - "shareSuccess": "Фрагмент успешно опубликован.", - "shareFailed": "Не удалось поделиться фрагментом", + "shareSuccess": "Сниппет успешно предоставлен в общий доступ", + "shareFailed": "Не удалось предоставить сниппет в общий доступ", "revokeSuccess": "Доступ отозван", "revokeFailed": "Не удалось отозвать доступ", "currentAccess": "Текущий доступ", - "shareLoadError": "Не удалось загрузить и распределить данные.", + "shareLoadError": "Не удалось загрузить данные общего доступа", "loading": "Загрузка...", "close": "Закрыть", - "reorderFailed": "Не удалось сохранить порядок фрагментов.", + "reorderFailed": "Не удалось сохранить порядок сниппетов", "importExport": "Импорт / Экспорт", - "exportBtn": "Экспорт в формате JSON", + "exportBtn": "Экспорт JSON", "importBtn": "Импорт JSON", - "exportSuccess": "Фрагменты кода успешно экспортированы", - "exportFailed": "Не удалось экспортировать фрагменты кода", - "importTitle": "Импорт фрагментов", - "importDescription": "Импорт фрагментов кода и папок из JSON-файла, экспортированного Termix.", - "importDropOrClick": "Перетащите JSON-файл сюда или нажмите, чтобы выбрать нужный.", + "exportSuccess": "Сниппеты успешно экспортированы", + "exportFailed": "Не удалось экспортировать сниппеты", + "importTitle": "Импорт сниппетов", + "importDescription": "Импорт сниппетов и папок из JSON-файла, экспортированного Termix", + "importDropOrClick": "Перетащите JSON-файл сюда или нажмите, чтобы выбрать", "importSelectedFile": "Выбрано: {{name}}", - "importOverwrite": "Замените существующие фрагменты кода с тем же именем и папкой", - "importStartBtn": "Импорт", - "importSuccess": "Импорт завершен: {{snippets}} фрагмент(ы) добавлены, {{updated}} обновлен, {{skipped}} пропущен, {{folders}} папка(и) добавлены", - "importFailed": "Не удалось импортировать фрагменты кода", - "importInvalidFile": "Недопустимый файл: ожидался объект JSON с фрагментами кода или массивами папок.", + "importOverwrite": "Перезаписывать существующие сниппеты с одинаковыми именами и папками", + "importStartBtn": "Импортировать", + "importSuccess": "Импорт завершён: добавлено сниппетов: {{snippets}}, обновлено: {{updated}}, пропущено: {{skipped}}, добавлено папок: {{folders}}", + "importFailed": "Не удалось импортировать сниппеты", + "importInvalidFile": "Неверный файл: ожидается JSON-объект с массивами snippets или folders", "targetHostsLabel": "Целевые хосты", - "targetHostsHint": "Назначьте хосты для запуска этого фрагмента кода напрямую, без открытого терминала.", - "noHostsAvailable": "Хосты не настроены", - "clearTargetHosts": "Очистить все", + "targetHostsHint": "Назначьте хосты для запуска этого сниппета напрямую, без открытого терминала.", + "noHostsAvailable": "Нет настроенных хостов", + "clearTargetHosts": "Очистить всё", "hasTargetHosts": "Имеет целевые хосты", - "runOnTargets": "Выполнено на целевых объектах", - "directRunSuccess": "Выполнено \"{{name}}\" на {{count}} хосте(ах)", - "directRunPartialFail": "\"{{name}}\" не удалось на одном или нескольких хостах", + "runOnTargets": "Запустить на целевых хостах", + "directRunSuccess": "Запущен \"{{name}}\" на {{count}} хосте(ах)", + "directRunPartialFail": "\"{{name}}\" не удалось запустить на одном или нескольких хостах", "executionResultTitle": "Результаты выполнения: {{name}}", - "executionResultDescription": "Результат выполнения фрагмента кода на каждом целевом хосте.", + "executionResultDescription": "Вывод выполнения сниппета на каждом целевом хосте.", "executionSuccess": "Успех", - "executionFailed": "Неуспешный" + "executionFailed": "Ошибка" + }, + "keybindings": { + "title": "Клавиатурные сочетания", + "description": "Настройте сочетания клавиш для копирования, вставки и управления в терминале, а также назначьте клавиши для отправки текста или запуска фрагмента кода.", + "defaultsHeading": "Встроенные сочетания клавиш", + "customHeading": "Пользовательские сочетания клавиш", + "addBinding": "Добавить ярлык", + "addBindingTitle": "Добавить ярлык", + "editBindingTitle": "Редактировать ярлык", + "loading": "Загрузка...", + "noCustomBindings": "Пользовательские сочетания клавиш пока отсутствуют.", + "defaultBadge": "По умолчанию", + "customizedBadge": "Индивидуальный", + "customize": "Настроить", + "resetToDefault": "Сбросить до значений по умолчанию", + "resetAllToDefaults": "Сбросить все настройки до значений по умолчанию", + "close": "Закрывать", + "cancel": "Отмена", + "saveBinding": "Сохранить ярлык", + "saveError": "Не удалось сохранить сочетания клавиш.", + "pressKeysToRecord": "Комбинация клавиш", + "pressKeysPlaceholder": "Нажмите и удерживайте клавиши.", + "recording": "Нажимайте клавиши...", + "comboRequiredError": "Сначала нажмите комбинацию клавиш.", + "textRequiredError": "Введите текст для отправки", + "controlCodeRequiredError": "Введите одну букву в качестве управляющего кода.", + "snippetRequiredError": "Выберите фрагмент", + "conflictWarning": "Эта комбинация уже привязана к {{combo}}. Сохранение активирует обе комбинации.", + "actionLabel": "Действие", + "actionCopy": "Скопировать выделенный фрагмент", + "actionPaste": "Вставить", + "actionSendControlCode": "Отправить сигнал Ctrl+буква", + "actionSendText": "Отправить текст", + "actionRunSnippet": "Запустить существующий фрагмент", + "controlCodeLabel": "Письмо", + "textLabel": "Текст для отправки", + "appendEnterLabel": "Нажмите Enter после отправки", + "snippetLabel": "Фрагмент", + "selectSnippetPlaceholder": "Выберите фрагмент", + "orphanedSnippetWarning": "фрагмент не найден", + "clipboardPermissionNote": "В некоторых браузерах при первом использовании может запрашиваться разрешение на использование буфера обмена." }, "userProfile": { - "donateTitle": "Поддержите Termix!", - "donateDescription": "Termix — бесплатный, самодостаточный сервис, созданный всего несколькими людьми в свободное время. Если он сэкономил вам время или деньги, пожертвование в криптовалюте поможет продолжить его работу.", - "donateMilestones": "Пожертвования помогают финансировать исследования и изучение того, что необходимо для создания поддержки SAML, Kubernetes и агентов. Следите за прогрессом и делайте пожертвования.", + "donateTitle": "Поддержите Termix", + "donateDescription": "Termix бесплатный, размещается самостоятельно и разрабатывается лишь несколькими людьми в свободное время. Если он сэкономил вам время или деньги, пожертвование в криптовалюте поможет продолжить развитие.", + "donateMilestones": "Пожертвования помогают оплатить время на исследование и обучение, необходимое для создания поддержки SAML, Kubernetes и Agent. Следите за прогрессом и поддержите.", "donateButton": "Пожертвовать криптовалюту", "storageModeLocal": "Браузер", "storageModeCloud": "База данных", - "storageModeDescription": "Браузер сохраняет настройки только в этом браузере. База данных синхронизируется с сервером и загружается на любое устройство.", - "resetToDefaults": "Сбросить до значений по умолчанию", + "storageModeDescription": "Браузер сохраняет настройки только в этом браузере. База данных синхронизируется с сервером и загружается на любом устройстве.", + "resetToDefaults": "Сбросить настройки по умолчанию", "resetToDefaultsSuccess": "Настройки сброшены до значений по умолчанию.", - "storageModeSwitch": "Место хранения настроек", - "sectionAccount": "Аккаунт", - "sectionAppearance": "Оформление", + "storageModeSwitch": "Хранилище настроек", + "sectionAccount": "Учетная запись", + "sectionAppearance": "Внешний вид", "sectionSecurity": "Безопасность", - "sectionApiKeys": "Ключи API", - "sectionC2sTunnels": "Туннели C2S", + "sectionApiKeys": "API-ключи", + "sectionData": "Данные", + "sectionC2sTunnels": "C2S-туннели", "usernameLabel": "Имя пользователя", "roleLabel": "Роль", "roleAdministrator": "Администратор", "authMethodLabel": "Метод аутентификации", "authMethodLocal": "Локальный", "twoFaLabel": "2FA", - "twoFaOn": "На", - "twoFaOff": "Выключенный", + "twoFaOn": "Включено", + "twoFaOff": "Отключено", "versionLabel": "Версия", "betaProgramTitle": "Бета-программа", - "betaProgramDescription": "Оцените новые функции на ранней стадии разработки, используя еженедельный тег Docker :beta. Нестабильная версия, не предназначена для использования в продакшене.", + "betaProgramDescription": "Попробуйте будущие функции раньше с помощью еженедельного тега :beta Docker. Нестабильно, не для продакшена.", "betaProgramFeedback": "Нашли ошибку? Сообщите о ней здесь.", - "betaProgramLearnMore": "Узнать больше", - "deleteAccount": "Удалить аккаунт", - "deleteAccountDescription": "Удалить свой аккаунт навсегда", - "changeServerDescription": "Переключитесь на другой сервер Termix.", + "betaProgramLearnMore": "Подробнее", + "deleteAccount": "Удалить учетную запись", + "deleteAccountDescription": "Безвозвратно удалить вашу учетную запись", + "changeServerDescription": "Переключиться на другой сервер Termix", "deleteButton": "Удалить", - "deleteAccountPermanent": "Это действие является необратимым и не подлежит отмене.", - "deleteAccountWarning": "Все сессии, хосты, учетные данные и настройки будут безвозвратно удалены.", - "confirmPasswordDeletePlaceholder": "Введите свой пароль для подтверждения.", + "deleteAccountPermanent": "Это действие необратимо и не может быть отменено.", + "deleteAccountWarning": "Все сеансы, хосты, учётные данные и настройки будут безвозвратно удалены.", + "confirmPasswordDeletePlaceholder": "Введите пароль для подтверждения", "languageLabel": "Язык", "themeLabel": "Тема", "fontSizeLabel": "Размер шрифта", "accentColorLabel": "Акцентный цвет", "settingsTerminal": "Терминал", - "commandAutocomplete": "Автозаполнение команд", - "commandAutocompleteDesc": "Отображать автозаполнение во время набора текста", - "terminalLinkBehavior": "Клик по ссылкам в терминале", - "terminalLinkBehaviorDesc": "Поведение по умолчанию при нажатии на ссылки в терминале", - "historyTracking": "Отслеживание истории", - "historyTrackingDesc": "Отслеживание команд терминала", + "commandAutocomplete": "Автодополнение команд", + "commandAutocompleteDesc": "Показывать автодополнение при вводе", + "keyboardShortcuts": "Клавиатурные сочетания", + "keyboardShortcutsDescription": "Настройте сочетания клавиш для копирования, вставки и команд в терминале.", + "manageShortcuts": "Управлять", + "terminalLinkBehavior": "Клик по ссылке в терминале", + "terminalLinkBehaviorDesc": "Поведение по умолчанию при клике по ссылкам в терминале", + "historyTracking": "История команд", + "historyTrackingDesc": "Отслеживать команды терминала", "commandPalette": "Палитра команд", "commandPaletteDesc": "Включить сочетание клавиш", - "reopenTabsOnLogin": "Открывайте вкладки повторно при входе в систему.", - "reopenTabsOnLoginDesc": "Восстанавливайте открытые вкладки при входе в систему или обновлении страницы, даже с другого устройства.", - "confirmTabClose": "Подтвердить закрытие вкладки", - "confirmTabCloseDesc": "Спросите разрешения, прежде чем закрывать вкладки терминала.", + "reopenTabsOnLogin": "Восстанавливать вкладки при входе", + "reopenTabsOnLoginDesc": "Восстанавливать открытые вкладки при входе или обновлении страницы, в том числе с другого устройства", + "confirmTabClose": "Подтверждение закрытия вкладки", + "confirmTabCloseDesc": "Запрашивать подтверждение перед закрытием вкладок терминала", "settingsSidebar": "Боковая панель", - "showHostTags": "Показать теги хоста", + "showHostTags": "Показывать теги хостов", "showHostTagsDesc": "Отображать теги в списке хостов", - "hostTrayOnClick": "Нажмите, чтобы открыть действия хоста", - "hostTrayOnClickDesc": "Всегда показывать кнопки подключения; раскрывать управление по нажатию, а не при наведении", - "compactHostView": "Компактный вид хоста", - "compactHostViewDesc": "Сверните список всех хостов в одну строку, отображающую только его имя и адрес.", - "statusColors": "Реальные цвета статуса", - "statusColorsDesc": "Вместо акцентного цвета используйте зеленый/красный для обозначения статуса онлайн/офлайн.", + "hostTrayOnClick": "Разворачивать действия хоста по клику", + "hostTrayOnClickDesc": "Всегда показывать кнопки подключения; клик для разворачивания параметров управления вместо наведения", + "compactHostView": "Компактный вид хостов", + "compactHostViewDesc": "Сворачивать каждый хост в одну строку, показывая только имя и адрес", + "statusColors": "Цвета состояния", + "statusColorsDesc": "Использовать зелёный/красный для отображения статуса онлайн/офлайн вместо акцентного цвета", "pinAppRail": "Закрепить панель приложений", - "pinAppRailDesc": "Левая боковая панель приложения всегда должна быть развернута, а не расширяться при наведении курсора.", - "expandAppRailOnHover": "Развернуть панель приложений при наведении курсора", - "expandAppRailOnHoverDesc": "Разрешите левой боковой панели приложения расширяться при наведении на неё указателя мыши.", + "pinAppRailDesc": "Оставлять левую боковую панель приложений всегда развёрнутой, а не разворачивать при наведении", + "expandAppRailOnHover": "Разворачивать панель приложений при наведении", + "expandAppRailOnHoverDesc": "Позволять левой боковой панели приложений разворачиваться при наведении указателя", "settingsNavigation": "Навигация", - "navigationTabsDesc": "Выберите, какие вкладки будут отображаться в панели приложений.", - "settingsSnippets": "Фрагменты кода", - "foldersCollapsed": "Папки свернуты", - "foldersCollapsedDesc": "Папки по умолчанию сворачиваются.", - "confirmExecution": "Подтвердить выполнение", - "confirmExecutionDesc": "Перед запуском фрагментов кода подтвердите выполнение.", + "navigationTabsDesc": "Выберите вкладки для отображения в боковой панели приложения", + "settingsSnippets": "Сниппеты", + "foldersCollapsed": "Сворачивать папки", + "foldersCollapsedDesc": "Сворачивать папки по умолчанию", + "confirmExecution": "Подтверждать выполнение", + "confirmExecutionDesc": "Подтверждать перед запуском сниппетов", "settingsUpdates": "Обновления", "disableUpdateChecks": "Отключить проверку обновлений", - "disableUpdateChecksDesc": "Прекратите проверку обновлений.", - "totpAuthenticator": "TOTP-аутентификатор", - "totpEnabled": "Двухфакторная аутентификация включена.", - "totpDisabled": "Добавить дополнительную защиту при входе в систему.", - "disable": "Запрещать", + "disableUpdateChecksDesc": "Не проверять наличие обновлений", + "totpAuthenticator": "Аутентификатор TOTP", + "totpEnabled": "2FA включена", + "totpDisabled": "Добавить дополнительную защиту", + "disable": "Отключить", "enable": "Включить", - "setupTotp": "Настройка TOTP", + "setupTotp": "Настроить TOTP", "qrCode": "QR-код", - "totpInstructions": "Отсканируйте QR-код или введите секретный код в приложении-аутентификаторе, затем введите 6-значный код.", + "totpInstructions": "Сканируйте QR-код или введите секрет в аутентификаторе, затем введите 6-значный код", "totpCodePlaceholder": "000000", "verify": "Проверить", - "changePassword": "Изменить пароль", + "changePassword": "Сменить пароль", "currentPasswordLabel": "Текущий пароль", "currentPasswordPlaceholder": "Текущий пароль", "newPasswordLabel": "Новый пароль", @@ -3213,76 +3409,92 @@ "confirmPasswordLabel": "Подтвердите новый пароль", "confirmPasswordPlaceholder": "Подтвердите новый пароль", "updatePassword": "Обновить пароль", - "createApiKeyTitle": "Создать ключ API", - "createApiKeyDescription": "Сгенерируйте новый API-ключ для программного доступа.", + "createApiKeyTitle": "Создать API-ключ", + "createApiKeyDescription": "Создать новый API-ключ для программного доступа.", "apiKeyNameLabel": "Имя", - "apiKeyNamePlaceholder": "например, конвейер непрерывной интеграции (CI Pipeline)", - "expiryDateLabel": "Дата истечения срока действия", - "optional": "необязательный", + "apiKeyNamePlaceholder": "например, CI Pipeline", + "expiryDateLabel": "Срок действия", + "optional": "необязательно", "cancel": "Отмена", "createKey": "Создать ключ", - "apiKeyCount": "{{count}} ключи", + "apiKeyCount": "{{count}} ключей", "newKey": "Новый ключ", - "noApiKeys": "API-ключи пока отсутствуют.", - "apiKeyActive": "Активный", - "apiKeyUsageHint": "Вложите свой ключ в", - "apiKeyUsageHintHeader": "заголовок.", - "apiKeyPermissionsHint": "Ключи наследуют права доступа пользователя, создавшего их.", + "noApiKeys": "Нет ключей API.", + "apiKeyActive": "Активен", + "apiKeyUsageHint": "Укажите ваш ключ в", + "apiKeyUsageHintHeader": "заголовке.", + "apiKeyPermissionsHint": "Ключи наследуют разрешения создавшего пользователя.", + "exportData": "Экспорт моих данных", + "exportDataDesc": "Загрузите резервную копию ваших хостов, учетных данных и настроек, чтобы перенести их на другое устройство.", + "export": "Экспорт", + "exporting": "Экспорт...", + "importData": "Импортировать мои данные", + "importDataDesc": "Восстановите хосты, учетные данные и настройки из файла резервной копии .sqlite.", + "importDataSelected": "Выбрано: {{name}}", + "selectFile": "Выберите файл", + "changeFile": "Изменять", + "import": "Импорт", + "importing": "Импорт...", + "exportSuccess": "Данные успешно экспортированы.", + "exportFailed": "Экспорт данных не удался", + "importSelectFile": "Пожалуйста, сначала выберите файл.", + "importCompleted": "Импорт завершен: импортировано {{total}} элементов, {{skipped}} пропущено", + "importFailed": "Импорт не удался: {{error}}", "roleUser": "Пользователь", "authMethodDual": "Двойная аутентификация", "authMethodOidc": "OIDC", - "totpSetupFailed": "Не удалось запустить настройку TOTP.", + "totpSetupFailed": "Не удалось запустить настройку TOTP", "totpEnter6Digits": "Введите 6-значный код", - "totpEnabledSuccess": "Включена двухфакторная аутентификация", - "totpInvalidCode": "Неверный код, пожалуйста, попробуйте еще раз.", - "totpDisableInputRequired": "Введите свой TOTP-код или пароль.", + "totpEnabledSuccess": "Двухфакторная аутентификация включена", + "totpInvalidCode": "Неверный код, попробуйте снова", + "totpDisableInputRequired": "Введите код TOTP или пароль", "totpDisabledSuccess": "Двухфакторная аутентификация отключена", - "totpDisableFailed": "Не удалось отключить двухфакторную аутентификацию.", - "totpDisableTitle": "Отключить двухфакторную аутентификацию", - "totpDisablePlaceholder": "Введите TOTP-код или пароль", - "totpDisableConfirm": "Отключить двухфакторную аутентификацию", + "totpDisableFailed": "Не удалось отключить 2FA", + "totpDisableTitle": "Отключить 2FA", + "totpDisablePlaceholder": "Введите код TOTP или пароль", + "totpDisableConfirm": "Отключить 2FA", "totpContinueVerify": "Продолжить проверку", - "totpVerifyTitle": "Подтвердите код", + "totpVerifyTitle": "Проверка кода", "totpBackupTitle": "Резервные коды", "totpDownloadBackup": "Скачать резервные коды", "passkeys": "Ключи доступа", - "passkeysDesc": "Используйте учетные данные WebAuthn/FIDO2 для входа в систему без пароля.", - "passkeyName": "Имя пароля", - "passkeyUvPreferred": "Предпочтительный", - "passkeyUvRequired": "Необходимый", + "passkeysDesc": "Используйте учётные данные WebAuthn/FIDO2 для входа без пароля", + "passkeyName": "Название ключа доступа", + "passkeyUvPreferred": "Предпочтительно", + "passkeyUvRequired": "Обязательно", "passkeyUvDiscouraged": "Не рекомендуется", "addPasskey": "Добавить ключ доступа", - "noPasskeys": "Пароли не зарегистрированы.", - "passkeyAdded": "Добавлен пароль", - "passkeyAddFailed": "Не удалось добавить пароль.", - "passkeyDeleted": "Пароль удален", - "passkeyDeleteFailed": "Не удалось удалить пароль.", + "noPasskeys": "Нет зарегистрированных ключей доступа.", + "passkeyAdded": "Ключ доступа добавлен", + "passkeyAddFailed": "Не удалось добавить ключ доступа", + "passkeyDeleted": "Ключ доступа удалён", + "passkeyDeleteFailed": "Ошибка удаления ключа доступа", "done": "Готово", - "secretCopied": "Секретный ключ скопирован в буфер обмена", - "apiKeyNameRequired": "Имя ключа обязательно для ввода.", - "apiKeyCreated": "Создан ключ API \"{{name}}", - "apiKeyCreateFailed": "Не удалось создать ключ API.", + "secretCopied": "Секрет скопирован в буфер обмена", + "apiKeyNameRequired": "Требуется имя ключа", + "apiKeyCreated": "API-ключ «{{name}}» создан", + "apiKeyCreateFailed": "Ошибка создания API-ключа", "apiKeyUser": "Пользователь", - "apiKeyExpires": "Срок действия истекает", - "apiKeyRevoked": "Ключ API \"{{name}}\" отозван", - "apiKeyRevokeFailed": "Не удалось отозвать ключ API.", - "passwordFieldsRequired": "Требуется указать текущий и новый пароль.", + "apiKeyExpires": "Истекает", + "apiKeyRevoked": "API-ключ «{{name}}» отозван", + "apiKeyRevokeFailed": "Ошибка отзыва API-ключа", + "passwordFieldsRequired": "Требуются текущий и новый пароль", "passwordMismatch": "Пароли не совпадают", - "passwordTooShort": "Пароль должен состоять как минимум из 6 символов.", - "passwordUpdated": "Пароль успешно обновлен.", - "passwordUpdateFailed": "Не удалось обновить пароль", - "deletePasswordRequired": "Для удаления вашей учетной записи требуется пароль.", - "deleteFailed": "Не удалось удалить учетную запись.", + "passwordTooShort": "Пароль должен содержать не менее 6 символов", + "passwordUpdated": "Пароль успешно обновлен", + "passwordUpdateFailed": "Ошибка обновления пароля", + "deletePasswordRequired": "Для удаления аккаунта требуется пароль", + "deleteFailed": "Ошибка удаления аккаунта", "deleting": "Удаление...", "colorPickerTooltip": "Открыть палитру цветов", - "themeSystem": "Система", - "themeLight": "Свет", - "themeDark": "Темный", + "themeSystem": "Системная", + "themeLight": "Светлая", + "themeDark": "Темная", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", "themeSolarized": "Solarized", - "themeTokyoNight": "Токийская ночь", + "themeTokyoNight": "Tokyo Night", "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } @@ -3290,79 +3502,79 @@ }, "tmuxMonitor": { "title": "Монитор Tmux", - "failedToLoadHosts": "Не удалось загрузить хосты.", - "failedToLoad": "Не удалось загрузить сессии tmux.", - "tmuxUnavailable": "tmux не установлен на этом хосте.", - "noSessions": "На этом хосте нет сессий tmux.", + "failedToLoadHosts": "Ошибка загрузки хостов", + "failedToLoad": "Ошибка загрузки сессий tmux", + "tmuxUnavailable": "tmux не установлен на этом хосте", + "noSessions": "Нет сессий tmux на этом хосте", "noHostSelected": "Хост не выбран", - "attached": "Прикреплён", - "detached": "Отсоединено", - "attach": "Прикрепить", - "editTags": "Редактировать теги", - "tagsHint": "Теги, разделённые запятыми (например, YOLO, lab, training)", - "tagsSaved": "Сохраненные теги", - "tagsSaveFailed": "Не удалось сохранить теги.", - "searchPlaceholder": "Результаты поиска по всем сессиям...", - "searchResults": "{{count}} матчей", + "attached": "Присоединён", + "detached": "Отсоединён", + "attach": "Присоединить", + "editTags": "Изменить теги", + "tagsHint": "Теги через запятую (например, YOLO, lab, training)", + "tagsSaved": "Теги сохранены", + "tagsSaveFailed": "Не удалось сохранить теги", + "searchPlaceholder": "Поиск вывода по сессиям...", + "searchResults": "{{count}} совпадений", "searchFailed": "Поиск не удался", - "selectPaneHint": "Выберите панель, чтобы просмотреть результат.", - "closePreview": "Закрыть предварительный просмотр", + "selectPaneHint": "Выберите панель для предпросмотра её вывода", + "closePreview": "Закрыть предпросмотр", "newSession": "Новая сессия", - "newSessionHint": "Название сессии (буквы, цифры, _ @ % + = -)", - "newSessionPlaceholder": "моя сессия", + "newSessionHint": "Имя сессии (буквы, цифры, _ @ % + = -)", + "newSessionPlaceholder": "my-session", "create": "Создать", - "sessionCreated": "Создана сессия \"{{name}}", - "sessionCreateFailed": "Не удалось создать сессию.", + "sessionCreated": "Сессия \"{{name}}\" создана", + "sessionCreateFailed": "Не удалось создать сессию", "splitRight": "Разделить вправо", - "splitDown": "Разделить", + "splitDown": "Разделить вниз", "splitFailed": "Не удалось разделить панель", "newWindow": "Новое окно", - "windowCreateFailed": "Не удалось создать окно.", - "attachSessionTooltip": "Прикрепить к {{session}}", + "windowCreateFailed": "Не удалось создать окно", + "attachSessionTooltip": "Присоединиться к {{session}}", "refresh": "Обновить", - "refreshFailed": "Не удалось обновить сессии.", - "collapseAll": "Свернуть все", - "expandAll": "Развернуть все", + "refreshFailed": "Не удалось обновить сессии", + "collapseAll": "Свернуть всё", + "expandAll": "Развернуть всё", "moreActions": "Дополнительные действия", "sessionStats": "Статистика сессии", "renameSessionTitle": "Переименовать сессию \"{{name}}\"", "rename": "Переименовать", "sessionRenamed": "Сессия переименована в \"{{name}}\"", - "sessionRenameFailed": "Не удалось переименовать сессию.", - "editTagsTitle": "Редактировать теги для \"{{name}}\"", + "sessionRenameFailed": "Не удалось переименовать сессию", + "editTagsTitle": "Изменить теги для \"{{name}}\"", "killPane": "Завершить панель", - "resizeTree": "Перетащите для изменения размера — дважды щелкните для сброса.", - "reattach": "Повторно прикрепите (исправляет искаженное изображение)", + "resizeTree": "Перетащите, чтобы изменить размер — двойной клик для сброса", + "reattach": "Переподключиться (исправляет искажённое отображение)", "killWindow": "Завершить окно", - "killWindowTitle": "Окно уничтожения {{index}} из \"{{session}}\"?", - "killWindowBody": "Все окна и процессы в этом окне будут завершены. Завершение работы последнего окна завершает сессию.", + "killWindowTitle": "Завершить окно {{index}} сессии \"{{session}}\"?", + "killWindowBody": "Все панели и процессы в этом окне будут завершены. Завершение последнего окна завершит сессию.", "windowKillFailed": "Не удалось завершить окно", "statusActivity": "Активность", "statusWindows": "Окна", "statusPanes": "панели", "statusTags": "Теги", - "killPaneTitle": "Панель закрытия {{id}}?", - "killPaneBody": "Процесс, запущенный в этой панели, будет завершен. Завершение работы последней панели закроет ее окно.", - "paneKillFailed": "Не удалось закрыть панель", + "killPaneTitle": "Завершить панель {{id}}?", + "killPaneBody": "Процесс, запущенный в этой панели, будет завершён. Завершение последней панели закроет её окно.", + "paneKillFailed": "Не удалось завершить панель", "killSessionTitle": "Завершить сессию \"{{name}}\"?", - "killSessionBody": "Все окна и запущенные процессы в этой сессии будут завершены. Это действие необратимо.", + "killSessionBody": "Все окна и запущенные процессы в этой сессии будут завершены. Это действие нельзя отменить.", "kill": "Завершить", "sessionKilled": "Сессия \"{{name}}\" завершена", "sessionKillFailed": "Не удалось завершить сессию", - "hostUnreachable": "Не удалось подключиться к хосту. Убедитесь, что он находится в сети и доступен.", + "hostUnreachable": "Не удалось подключиться к хосту. Проверьте, что он онлайн и доступен.", "noServer": "На этом хосте не запущен сервер tmux.", "searchTruncated": "Частичные результаты — поиск охватывает последние {{lines}} строк каждой панели и не более {{panes}} панелей.", "closeSearchResults": "Закрыть результаты поиска", - "retry": "Повторить попытку", - "noHosts": "Нет доступных SSH-хостов.", - "noHostsHint": "Включите параметр Tmux Monitor на SSH-хосте (Менеджер хостов → вкладка Терминал), чтобы отслеживать его tmux-сессии.", + "retry": "Повторить", + "noHosts": "Нет доступных SSH-хостов", + "noHostsHint": "Включите опцию Tmux Monitor для SSH-хоста (Host Manager → вкладка Terminal), чтобы отслеживать его tmux-сессии.", "tmuxInstallHint": "Установите его на хост с помощью:", - "attachTooltip": "Откройте терминал, чтобы перейти к {{host}}", - "attachTooltipPane": "Откройте терминал и перейдите в сессию tmux: {{host}} — tmux session {{session}}", - "timeJustNow": "прямо сейчас", + "attachTooltip": "Открыть терминал на {{host}}", + "attachTooltipPane": "Открыть терминал на {{host}} — tmux-сессия {{session}}", + "timeJustNow": "только что", "timeMinutes": "{{count}}м назад", "timeHours": "{{count}}ч назад", - "timeDays": "{{count}}дней назад" + "timeDays": "{{count}}д назад" }, "mobileKeyboard": { "shift": "Shift", @@ -3370,58 +3582,59 @@ "esc": "Esc", "tab": "Tab", "backTab": "⇥", - "arrowUp": "Стрелка вверх", - "arrowDown": "Стрелка вниз", - "arrowLeft": "Стрелка влево", - "arrowRight": "Стрелка вправо", + "arrowUp": "Вверх", + "arrowDown": "Вниз", + "arrowLeft": "Влево", + "arrowRight": "Вправо", "home": "Home", "end": "End", "pageUp": "PgUp", "pageDown": "PgDn", - "delete": "Дел", - "editQuickKeys": "Редактировать быстрые клавиши", + "delete": "Del", + "paste": "Вставить", + "editQuickKeys": "Редактировать", "quickKeysTitle": "Быстрые клавиши", "quickKeysDesc": "Нажмите × для удаления. Поддерживается до 8 символов.", - "quickKeyPlaceholder": "например sudo ", + "quickKeyPlaceholder": "напр. sudo ", "addQuickKey": "Добавить", "removeQuickKey": "Удалить", - "resetDefaults": "Сбросить до значений по умолчанию", + "resetDefaults": "Сбросить по умолчанию", "done": "Готово" }, "serial": { "title": "Последовательный порт", "portLabel": "Порт", - "portPlaceholder": "/dev/ttyUSB0 или COM3", - "baudRateLabel": "Скорость передачи данных", + "portPlaceholder": "/dev/ttyUSB0 or COM3", + "baudRateLabel": "Скорость передачи", "dataBitsLabel": "Биты данных", "stopBitsLabel": "Стоп-биты", - "parityLabel": "Паритет", + "parityLabel": "Чётность", "parityNone": "Нет", "parityEven": "Чётный", "parityOdd": "Нечётный", - "connect": "Подключиться к последовательному порту", - "disconnect": "Отключить", + "connect": "Подключиться", + "disconnect": "Отключиться", "refreshPorts": "Обновить порты", - "connected": "Подключено к {{path}} со скоростью {{baud}} бод", + "connected": "Подключено к {{path}} на скорости {{baud}} бод", "disconnected": "Последовательный порт отключен", - "connectionError": "Не удалось открыть последовательный порт.", + "connectionError": "Ошибка открытия последовательного порта", "wsError": "Ошибка WebSocket", - "errorNoServerUrl": "URL-адрес сервера не настроен", + "errorNoServerUrl": "Не настроен URL сервера", "notSupportedTitle": "Последовательный порт не поддерживается", - "notSupported": "Для последовательного соединения требуется браузер с поддержкой Web Serial API (Chrome, Edge или Firefox 151+) или настольное приложение Termix.", - "hideHint": "Вкладку «Серийный номер» можно скрыть в разделе «Профиль пользователя» > «Внешний вид» > «Боковая панель» > «Навигация».", - "browserPickerHint": "Нажмите «Подключить», и в вашем браузере откроется окно выбора порта, где вы сможете выбрать устройство." + "notSupported": "Последовательные подключения требуют браузер с поддержкой Web Serial API (Chrome, Edge или Firefox 151+) или десктопное приложение Termix.", + "hideHint": "Вы можете скрыть вкладку «Serial» в разделе «Профиль пользователя» > «Оформление» > «Боковая панель» > «Навигация».", + "browserPickerHint": "Нажмите «Подключиться», и браузер откроет окно выбора порта для выбора устройства." }, "metricsHistory": { "title": "История метрик", "historySuffix": "История", - "cpuMemoryDisk": "Процессор / Память / Диск", + "cpuMemoryDisk": "ЦП / Память / Диск", "network": "Сеть", "download": "RX", "upload": "TX", - "noData": "Данные за этот период времени отсутствуют.", - "custom": "Произвольный", - "to": "к", + "noData": "Нет исторических данных за этот период.", + "custom": "Пользовательский", + "to": "по", "apply": "Применить", "viewHistory": "Просмотреть историю" }, @@ -3429,44 +3642,44 @@ "tabFirings": "Оповещения", "tabRules": "Правила", "tabChannels": "Каналы", - "noFirings": "Нет неподтвержденных уведомлений", - "noRules": "Правила оповещения не настроены", - "noChannels": "Каналы уведомлений не настроены", - "rulesDesc": "Правила оповещения запускают уведомления.", + "noFirings": "Нет неподтверждённых оповещений", + "noRules": "Не настроены правила оповещений", + "noChannels": "Не настроены каналы уведомлений", + "rulesDesc": "Правила оповещений инициируют уведомления", "channelsDesc": "Куда отправляются уведомления об оповещениях", "acknowledge": "Подтвердить", "ackAll": "Подтвердить все", - "allAcknowledged": "Все оповещения подтверждены.", - "ackFailed": "Не подтвердил оповещение", - "ackAllFailed": "Не удалось подтвердить все оповещения.", + "allAcknowledged": "Все оповещения подтверждены", + "ackFailed": "Не удалось подтвердить оповещение", + "ackAllFailed": "Не удалось подтвердить все оповещения", "showAcknowledged": "Показать все", "hideAcknowledged": "Скрыть подтверждённые", "addChannel": "Добавить канал", - "editChannel": "Редактировать канал", - "channelName": "Имя", + "editChannel": "Изменить канал", + "channelName": "Название", "channelType": "Тип", - "channelNameRequired": "Имя обязательно для указания.", + "channelNameRequired": "Название обязательно", "webhookUrl": "URL", - "webhookUrlRequired": "URL веб-перехватчика обязателен", - "webhookDesc": "Отправляйте JSON-данные по этому URL-адресу при каждом срабатывании оповещения.", + "webhookUrlRequired": "URL вебхука обязателен", + "webhookDesc": "Отправлять JSON-данные методом POST на этот URL при каждом срабатывании оповещения", "ntfyServer": "URL сервера", "ntfyTopic": "Тема", "ntfyTopicRequired": "Тема обязательна", "ntfyToken": "Токен доступа (необязательно)", "channelSaveFailed": "Не удалось сохранить канал", - "test": "Тест", - "testSent": "Уведомление о тестировании отправлено.", - "testFailed": "Тестовое уведомление не удалось", + "test": "Проверить", + "testSent": "Тестовое уведомление отправлено", + "testFailed": "Ошибка отправки тестового уведомления", "addRule": "Добавить правило оповещения", - "editRule": "Изменить правило оповещения", - "ruleName": "Название правила", - "ruleNameRequired": "Имя обязательно для указания.", - "triggerType": "Тип триггера", + "editRule": "Редактировать правило оповещения", + "ruleName": "Имя правила", + "ruleNameRequired": "Имя обязательно", + "triggerType": "Триггер", "thresholdValue": "Порог (%)", - "durationSeconds": "Длительность (секунды, 0 = немедленный выстрел)", - "cooldownMinutes": "Время восстановления (минуты)", + "durationSeconds": "Длительность (секунд, 0 = сработать немедленно)", + "cooldownMinutes": "Задержка повторения (минут)", "channels": "Каналы уведомлений", - "noChannelsHint": "Сначала добавьте каналы на вкладке «Каналы».", - "ruleSaveFailed": "Правило не удалось сохранить" + "noChannelsHint": "Сначала добавьте каналы на вкладке «Каналы»", + "ruleSaveFailed": "Не удалось сохранить правило" } } diff --git a/src/ui/locales/translated/sr_SP.json b/src/ui/locales/translated/sr_SP.json index 4f083dad..964b9fc1 100644 --- a/src/ui/locales/translated/sr_SP.json +++ b/src/ui/locales/translated/sr_SP.json @@ -389,6 +389,45 @@ "noSavedServers": "Нема сачуваних сервера", "removeServer": "Уклони" }, + "migrationNotice": { + "title": "Termix Desktop сада ради самостално", + "body1": "Ова апликација се раније директно повезивала са вашим Termix сервером. Прерађена је тако да ради потпуно самостално, чувајући хостове и акредитиве локално како би наставила да ради ван мреже. Двосмерна синхронизација са самостално хостованим Termix сервером је сада опционална.", + "body2": "Ваши постојећи хостови и акредитиви су и даље на {{url}}. Укључите даљинску синхронизацију и поново се повежите са тим сервером да бисте их вратили и одржали синхронизацију у будућности.", + "dismiss": "Не сада", + "setUpSync": "Подешавање даљинске синхронизације" + }, + "remoteSync": { + "title": "Даљинска синхронизација", + "description": "Опционо, повежите ову десктоп апликацију са самостално хостованим Termix сервером да бисте синхронизовали своје хостове, акредитиве и исечке кода на свим уређајима. Апликација увек ради потпуно ван мреже, без обзира да ли се повежете или не.", + "notConnected": "Није повезано", + "connected": "Повезано", + "connectedTo": "Повезано са {{url}}", + "lastSynced": "Последња синхронизација {{time}}", + "neverSynced": "Никада није синхронизовано", + "syncError": "Грешка у синхронизацији: {{message}}", + "needsReauth": "Пријава је истекла", + "connectButton": "Повежи се са сервером", + "disconnectButton": "Прекини везу", + "syncNowButton": "Синхронизуј сада", + "syncing": "Синхронизација...", + "serverUrl": "URL сервера", + "enterServerUrl": "Молимо вас да унесете URL адресу сервера", + "mustIncludeProtocol": "URL сервера мора да почиње са http:// или https://", + "connectionTestFailed": "Није могуће доћи до Termix сервера на тој URL адреси", + "allowInvalidCertificate": "Дозволи неважећи сертификат", + "allowInvalidCertificateDesc": "Користите само за поуздане самохостоване сервере са самопотписаним сертификатима или сертификатима за IP адресу.", + "savedServers": "Сачувани сервери", + "removeServer": "Уклони", + "continueButton": "Настави", + "cancelButton": "Откажи", + "signInTitle": "Пријавите се на {{url}}", + "originTitle": "Порекло везе", + "originDescription": "Изаберите одакле SSH везе потичу подразумевано. Ово се може заменити за сваки хост.", + "originLocal": "Овај уређај (локална мрежа)", + "originRemote": "Удаљени сервер", + "bannerReconnect": "Поново се повежите", + "bannerMessage": "Даљинска синхронизација захтева поновну аутентификацију" + }, "versionCheck": { "error": "Грешка при провери верзије", "checkFailed": "Провера ажурирања није успела", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Замени корисничко име акредитива", "overrideCredentialUsernameDesc": "Користите корисничко име наведено горе уместо корисничког имена акредитива", "oidcUsernameHint": "Користите $oidc.preferred_username да бисте заменили своје OIDC корисничко име.", + "tailscaleUsernameHint": "Ово мора бити Unix корисник чији је идентитет на Tailscale-у додељен у SSH ACL-у Tailnet-а, не нужно root.", "jumpHostChain": "Скок ланца хоста", "portKnocking": "Лук Нокинг", "addKnock": "Додај порт", @@ -650,6 +690,11 @@ "delayAfterMs": "Кашњење после (ms)", "useSocks5Proxy": "Користите SOCKS5 прокси", "useSocks5ProxyDesc": "Усмерите везу преко прокси сервера", + "connectionOrigin": "Порекло везе", + "connectionOriginDesc": "Одакле потиче SSH веза овог хоста. Замењује глобално подразумевано подешавање десктоп апликације.", + "connectionOriginDefault": "Користи подразумевано", + "connectionOriginLocal": "Овај уређај (локална мрежа)", + "connectionOriginRemote": "Удаљени сервер", "proxyHost": "Прокси хост", "proxyPort": "Прокси порт", "proxyUsername": "Корисничко име проксија", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Светло цијан", "customThemeBrightWhite": "Светло бела", "customThemeResetTooltip": "Ресетуј на подразумеване вредности", + "savedThemesLabel": "Сачуване теме", + "saveAsGlobalTheme": "Сачувај као глобалну тему", + "saveGlobalThemeNamePrompt": "Унесите име за ову тему", + "saveGlobalThemeSuccess": "Тема је сачувана", + "saveGlobalThemeError": "Чување теме није успело", + "applyGlobalThemeTooltip": "Примени ову тему", + "deleteGlobalThemeTooltip": "Обриши ову тему", + "noSavedThemes": "Још нема сачуваних тема", "syntaxHighlightingLabel": "Истицање синтаксе", "syntaxHighlightingDesc": "Обоји излаз терминала (грешке, путање, IP адресе, временске ознаке)", "syntaxHighlightingCategories": "Истакните категорије", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Аутоматски покрени или прикључи се tmux сесији", "enableSessionLogging": "Записивање сесије", "enableSessionLoggingDesc": "Снимите излаз терминалне сесије за каснији преглед", + "allowSessionSharing": "Дозволи дељење сесије", + "allowSessionSharingDesc": "Дозволите дељење сесија уживо на овом хосту путем линка или са другим корисницима", "enableCommandHistory": "Историја команди", "enableCommandHistoryDesc": "Снимање команди које се извршавају у овом терминалу за историју и аутоматско довршавање", "linkClickBehaviorLabel": "Понашање при клику на линк", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} увезено", "proxmoxResultUpdated": "{{count}} ажурирано", "proxmoxResultFailed": "{{count}} није успело", + "proxmoxResultSkippedNoIp": "{{count}} прескочено (није пронађена IP адреса)", "proxmoxImportComplete": "Увоз Proxmox-а завршен: {{summary}}", "proxmoxDiscoveryFailed": "Откривање није успело", "proxmoxImportFailed": "Увоз није успео", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Чување акредитива није успело", "credentialNameRequired": "Унесите име за акредитив", "credentialAuthRequired": "Додајте лозинку, SSH кључ или обоје", + "createCredentialFromHostBtn": "Креирај акредитив", + "createCredentialFromHostTitle": "Креирај акредитив од хоста", + "createCredentialFromHostDesc": "Направите унос акредитива за вишекратну употребу и дељење, унапред попуњен тренутним корисничким именом, лозинком и/или SSH кључем овог хоста.", "backToHosts": "Назад на хостове", "backToCredentials": "Назад на акредитиве", "pinned": "Закачено", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Користите / да бисте раздвојили нивое и креирали угнежђене фасцикле.", "folderColor": "Боја", "folderIcon": "Икона", + "folderCredential": "Акредитив", + "folderCredentialNone": "Није додељен акредитив", + "folderCredentialHint": "Домаћини у овој фасцикли који користе ауторизацију „Сачувани акредитиви“ без изабраних сопствених акредитива наследиће овај.", "folderPreview": "Преглед", "folderNameFallback": "Неименована фасцикла", "createFolderButton": "Направи фасциклу", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Ознаке", "shareHost": "Дели хост", "shareHostTitle": "Подели: {{name}}", + "shareFolder": "Дели фасциклу", + "shareFolderTitle": "Дели фасциклу: {{name}}", + "folderSharedSuccessfully": "Дељено {{count}} хост(ова) у фолдеру", + "failedToShareFolder": "Дељење фасцикле није успело", "sharing": { "loadError": "Учитавање података о дељењу није успело. Молимо покушајте поново.", "shareWithSection": "Подели са", @@ -1221,6 +1287,7 @@ "shareWithCount": "Подели ({{count}})", "currentAccess": "Тренутни приступ", "noAccessEntries": "Овај хост још није дељен", + "folderShareSummary": "Дељено {{shared}} од {{total}} хост(ова) у овој фасцикли", "grantedBy": "Додељено од стране", "expires": "Истиче", "expired": "Истекло", @@ -1242,6 +1309,8 @@ "authMethod": "Метода овлашћења", "authTypeDirect": "Директно", "authTypeCredential": "Акредитив", + "authTypeNone": "Ниједан", + "authTypeNoneDesc": "Акредитиви се не чувају. Сваки пут када се повежете, биће вам затражено корисничко име и лозинка; они се не чувају.", "selectCredential": "Изаберите акредитив...", "connectionSettings": "Подешавања везе", "displaySettings": "Подешавања екрана", @@ -1259,6 +1328,7 @@ "rdpPort": "РДП порт", "username": "Корисничко име", "password": "Лозинка", + "passwordSaved": "Лозинка сачувана, унесите текст да бисте је променили", "domain": "Домен", "securityMode": "Безбедносни режим", "colorDepth": "Дубина боје", @@ -1424,7 +1494,60 @@ "expiresIn": "Истиче за {{duration}}", "search": "Претражи везе...", "noSearchResults": "Ниједна веза не одговара вашој претрази", - "rename": "Преименуј сесију" + "rename": "Преименуј сесију", + "sectionSharedWithMe": "Дељено са мном", + "sharedBy": "Дели {{username}}", + "join": "Придружи се", + "sharedSessionLabel": "{{hostName}} (дељено)" + }, + "sessionSharing": { + "guestView": { + "loading": "Повезивање са дељеном сесијом...", + "linkInvalid": "Овај линк за дељење је неважећи, истекао је или је опозван", + "rateLimited": "Превише покушаја, покушајте поново ускоро", + "sessionEnded": "Ова сесија је завршена", + "readOnlyBadge": "Само преглед" + }, + "modalTitle": "Дели сесију", + "shareButton": "Дели", + "notReadyToShare": "Сесија још није спремна за дељење", + "modeTab": { + "link": "Линк", + "user": "Корисник" + }, + "linkModeDescription": "Свако ко има овај линк може да се придружи, није потребан налог.", + "userModeDescription": "Делите са одређеним корисником који већ има приступ овом хосту. Ако још немају приступ, прво поделите хост са њима или користите линк. Након дељења, сесија се појављује на њиховој картици „Везе“.", + "permissionLevel": { + "label": "Ниво дозволе", + "readOnly": "Само за читање", + "readOnlyDescription": "Може да гледа сесију уживо, али не може да куца или да комуницира.", + "readWrite": "Читање и писање", + "readWriteDescription": "Може да куца и интерагује са сесијом баш као и власник." + }, + "expiryLabel": "Истек линка", + "createLinkButton": "Направи линк", + "createShareButton": "Подели са корисником", + "searchUsersPlaceholder": "Претражи кориснике...", + "noUsersFound": "Није пронађен ниједан корисник", + "linkCreated": "Линк за дељење је направљен", + "linkCopied": "Линк је копиран у међуспремник", + "copyLink": "Копирај линк", + "shareCreated": "Сесија је дељена. Појавиће се на њиховој картици „Везе“.", + "shareFailed": "Није успело креирање дељења", + "userLacksHostAccess": "Тај корисник још увек нема приступ овом хосту. Прво поделите хост са њим или користите линк.", + "activeShares": "Активне акције", + "noActiveShares": "Нема активних дељења за ову сесију", + "revoke": "Поништи", + "revokeConfirmTitle": "Да ли желите да опозовете ово дељење?", + "revokeConfirmDescription": "Свако ко користи ово дељење ће одмах изгубити приступ.", + "revoked": "Дељење опозвано", + "revokeFailed": "Опозивање дељења није успело", + "joinCount": "{{count}} придружи се", + "joinCount_other": "{{count}} придруживања", + "expiresAt": "Истиче {{date}}", + "linkShareBadge": "Линк", + "userShareBadge": "Корисник: {{username}}", + "loadSharesFailed": "Учитавање активних дељења није успело" }, "guacamole": { "connecting": "Повезивање са {{type}} сесијом...", @@ -1436,6 +1559,9 @@ "reconnect": "Поново се повежите", "retry": "Покушај поново", "guacdUnavailable": "Услуга удаљеног рачунара (guacd) није доступна. Молимо вас да се уверите да је guacd покренут, доступан и правилно конфигурисан у администраторским подешавањима.", + "credentialPromptTitle": "Унесите RDP акредитиве", + "credentialPromptDescription": "Овај хост је подешен да тражи акредитиве при повезивању. Они се користе само за ову сесију и не чувају се.", + "connect": "Повежи се", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Поновно повезивање сесије", "collapse": "Савиј траку са алаткама", "expand": "Прошири траку са алаткама", - "dragHandle": "Превуците да бисте променили положај" + "dragHandle": "Превуците да бисте променили положај", + "switchToTrackpad": "Пребаците се у режим додирне табле (превуците да бисте померили курсор, додирните да бисте кликнули)", + "switchToTouch": "Пребаците се на режим додира (додирните директно место где желите да кликнете)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Потребна је двофакторска аутентификација", "totpCodeLabel": "Верификациони код", "totpVerify": "Верификуј", + "mfaPromptRequired": "Потребна је аутентификација", + "mfaPushRequired": "Потребна је аутентификација путем поруке", + "mfaMenuPlaceholder": "Унесите свој одговор", + "mfaWaitingApproval": "Чекање одобрења на вашем уређају...", + "mfaSendRequest": "Пошаљи захтев", "warpgateAuthRequired": "Потребна је аутентификација Варпгејта", "warpgateSecurityKey": "Безбедносни кључ", "warpgateAuthUrl": "URL адреса за аутентификацију", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Ограничена брзина: Превише покушаја верификације. Молимо покушајте поново касније.", "resetCodeRateLimitedWithTime": "Ограничена брзина: Превише покушаја верификације. Молимо сачекајте {{time}} секунди пре него што покушате поново.", "authTokenSaveFailed": "Чување токена за аутентификацију није успело", - "failedToLoadServer": "Учитавање сервера није успело" + "failedToLoadServer": "Учитавање сервера није успело", + "remoteServerRequired": "Потребан је удаљени сервер. Повежите се са удаљеним сервером у Подешавањима да бисте користили овај тип везе." }, "messages": { "registrationDisabled": "Регистрација новог налога је тренутно онемогућена од стране администратора. Молимо вас да се пријавите или контактирате администратора.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Чување SSL подешавања није успело", "sslRequiresDomain": "Домен и имејл су обавезни", "sslInfoNote": "Након издавања сертификата, омогућите SSL у променљивим окружења (ENABLE_SSL=true) и поново покрените Termix.", + "sslManualOption": "Ручно (отпремање сертификата)", + "sslManualCert": "Сертификат (ПЕМ)", + "sslManualCertPlaceholder": "-----ПОЧЕТАК СЕРТИФИКАТА-----", + "sslManualKey": "Приватни кључ (PEM)", + "sslManualKeyPlaceholder": "-----ПОЧЕТАК ПРИВАТНОГ КЉУЧА-----", + "sslManualDesc": "Налепите постојећи сертификат и приватни кључ, укључујући цео ланац ако то захтева ваш ЦА.", + "sslManualUpload": "Сертификат за отпремање и инсталирање", + "sslManualUploadLoading": "Отпремање сертификата...", + "sslManualUploadSuccess": "Сертификат је успешно отпремљен и инсталиран", + "sslManualUploadFailed": "Отпремање сертификата није успело", + "sslManualRequiresFields": "Потребни су сертификат и приватни кључ", "auditLogTotal": "{{total}} укупно уноса", "auditLogEmpty": "Нису пронађени уноси у евиденцију ревизије", "auditLogSuccess": "Успех", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Историја команди", "commandHistoryEnabledDesc": "Дозволи снимање историје команди. Када је онемогућено, историја се не чува без обзира на подешавања за сваки хост.", "updateCommandHistoryFailed": "Ажурирање подешавања историје команди није успело", + "analyticsEnabled": "Делите анонимну статистику коришћења", + "analyticsEnabledDesc": "Шаље анонимни дневни број корисника, хостова и коришћења функција како би се побољшао Termix. Никада се не укључују лични подаци или детаљи везе.", + "updateAnalyticsFailed": "Ажурирање подешавања аналитике није успело", + "sessionSharingGloballyEnabled": "Дозволи дељење сесије", + "sessionSharingGloballyEnabledDesc": "Дозвољава дељење сесија уживо терминала, RDP, VNC и Telnet сесија на нивоу целе инстанце. Замењује сваки прекидач за дељење по хосту када је онемогућен.", + "updateSessionSharingFailed": "Ажурирање подешавања дељења сесије није успело", "sessionTimeout": "Временско ограничење сесије", "hours": "сати", "sessionTimeoutRange": "Мин. 1 сат · Макс. 720 сати", @@ -3119,6 +3270,47 @@ "executionSuccess": "Успех", "executionFailed": "Неуспешно" }, + "keybindings": { + "title": "Пречице на тастатури", + "description": "Прилагодите пречице за копирање, лепљење и контролу терминала или повежите тастере за слање текста или покретање исечка кода.", + "defaultsHeading": "Уграђене пречице", + "customHeading": "Прилагођене пречице", + "addBinding": "Додај пречицу", + "addBindingTitle": "Додај пречицу", + "editBindingTitle": "Измени пречицу", + "loading": "Учитавање...", + "noCustomBindings": "Још нема прилагођених пречица", + "defaultBadge": "Подразумевано", + "customizedBadge": "Прилагођено", + "customize": "Прилагоди", + "resetToDefault": "Ресетуј на подразумеване вредности", + "resetAllToDefaults": "Ресетуј све на подразумеване вредности", + "close": "Затвори", + "cancel": "Откажи", + "saveBinding": "Сачувај пречицу", + "saveError": "Чување пречица на тастатури није успело", + "pressKeysToRecord": "Комбинација тастера", + "pressKeysPlaceholder": "Кликните и притисните тастере", + "recording": "Притисните тастере...", + "comboRequiredError": "Прво притисните комбинацију тастера", + "textRequiredError": "Унесите текст за слање", + "controlCodeRequiredError": "Унесите једно слово за контролни код", + "snippetRequiredError": "Изаберите исечак", + "conflictWarning": "Ова комбинација је већ повезана са {{combo}}. Чување ће активирати обе.", + "actionLabel": "Акција", + "actionCopy": "Копирај избор", + "actionPaste": "Залепи", + "actionSendControlCode": "Пошаљи сигнал Ctrl+слово", + "actionSendText": "Пошаљи текст", + "actionRunSnippet": "Покрени постојећи исечак кода", + "controlCodeLabel": "Писмо", + "textLabel": "Текст за слање", + "appendEnterLabel": "Притисните Ентер након слања", + "snippetLabel": "Исечак", + "selectSnippetPlaceholder": "Изаберите исечак", + "orphanedSnippetWarning": "фрагмент није пронађен", + "clipboardPermissionNote": "Може затражити дозволу за међуспремник при првој употреби у неким прегледачима." + }, "userProfile": { "donateTitle": "Нека Термикс остане жив", "donateDescription": "Termix је бесплатан, самостално хостован и изградило га је само неколико људи у своје слободно време. Ако вам је уштедео време или новац, донација криптовалута помаже да се одржи.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Изглед", "sectionSecurity": "Безбедност", "sectionApiKeys": "API кључеви", + "sectionData": "Подаци", "sectionC2sTunnels": "C2S тунели", "usernameLabel": "Корисничко име", "roleLabel": "Улога", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Терминал", "commandAutocomplete": "Аутоматско довршавање команди", "commandAutocompleteDesc": "Прикажи аутоматско довршавање током куцања", + "keyboardShortcuts": "Пречице на тастатури", + "keyboardShortcutsDescription": "Прилагодите пречице за копирање, лепљење и команде у терминалу", + "manageShortcuts": "Управљај", "terminalLinkBehavior": "Кликните на линк терминала", "terminalLinkBehaviorDesc": "Подразумевано понашање при клику на линкове у терминалу", "historyTracking": "Праћење историје", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Укључите свој кључ у", "apiKeyUsageHintHeader": "заглавље.", "apiKeyPermissionsHint": "Кључеви наслеђују дозволе корисника који их креира.", + "exportData": "Извези моје податке", + "exportDataDesc": "Преузмите резервну копију својих хостова, акредитива и подешавања да бисте их пренели на други уређај", + "export": "Извоз", + "exporting": "Извоз...", + "importData": "Увези моје податке", + "importDataDesc": "Вратите своје хостове, акредитиве и подешавања из .sqlite датотеке резервне копије", + "importDataSelected": "Изабрано: {{name}}", + "selectFile": "Изаберите датотеку", + "changeFile": "Промена", + "import": "Увоз", + "importing": "Увоз...", + "exportSuccess": "Подаци су успешно извезени", + "exportFailed": "Извоз података није успео", + "importSelectFile": "Прво изаберите датотеку", + "importCompleted": "Увоз завршен: {{total}} ставки је увезено, {{skipped}} прескочено", + "importFailed": "Увоз није успео: {{error}}", "roleUser": "Корисник", "authMethodDual": "Двострука аутентификација", "authMethodOidc": "ОИДЦ", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "ПгДн", "delete": "Дел", + "paste": "Залепи", "editQuickKeys": "Уреди брзе тастере", "quickKeysTitle": "Брзи тастери", "quickKeysDesc": "Додирните × да бисте уклонили. Подржава до 8 знакова.", diff --git a/src/ui/locales/translated/sv_SE.json b/src/ui/locales/translated/sv_SE.json index c1a26974..0dfe00cf 100644 --- a/src/ui/locales/translated/sv_SE.json +++ b/src/ui/locales/translated/sv_SE.json @@ -389,6 +389,45 @@ "noSavedServers": "Inga sparade servrar", "removeServer": "Ta bort" }, + "migrationNotice": { + "title": "Termix Desktop körs nu fristående", + "body1": "Den här appen brukade ansluta direkt till din Termix-server. Den har omarbetats för att köras helt självständigt och lagrar värdar och inloggningsuppgifter lokalt så att den fortsätter att fungera offline. Tvåvägssynkronisering med en egen Termix-server är nu valfritt.", + "body2": "Dina befintliga värdar och inloggningsuppgifter finns fortfarande på {{url}}. Aktivera fjärrsynkronisering och återanslut till servern för att få tillbaka dem och hålla båda synkroniserade framöver.", + "dismiss": "Inte nu", + "setUpSync": "Konfigurera fjärrsynkronisering" + }, + "remoteSync": { + "title": "Fjärrsynkronisering", + "description": "Anslut alternativt den här skrivbordsappen till en självhostad Termix-server för att synkronisera dina värdar, inloggningsuppgifter och kodavsnitt mellan enheter. Appen fungerar alltid helt offline oavsett om du ansluter eller inte.", + "notConnected": "Inte ansluten", + "connected": "Ansluten", + "connectedTo": "Ansluten till {{url}}", + "lastSynced": "Senast synkroniserad {{time}}", + "neverSynced": "Aldrig synkroniserad", + "syncError": "Synkroniseringsfel: {{message}}", + "needsReauth": "Inloggningen har upphört", + "connectButton": "Anslut till servern", + "disconnectButton": "Koppla från", + "syncNowButton": "Synkronisera nu", + "syncing": "Synkroniserar...", + "serverUrl": "Server-URL", + "enterServerUrl": "Ange en server-URL", + "mustIncludeProtocol": "Serverns URL måste börja med http:// eller https://", + "connectionTestFailed": "Kunde inte nå en Termix-server på den URL:en", + "allowInvalidCertificate": "Tillåt ogiltigt certifikat", + "allowInvalidCertificateDesc": "Använd endast för betrodda, självhostade servrar med självsignerade certifikat eller IP-adresscertifikat.", + "savedServers": "Sparade servrar", + "removeServer": "Ta bort", + "continueButton": "Fortsätta", + "cancelButton": "Avboka", + "signInTitle": "Logga in på {{url}}", + "originTitle": "Anslutningens ursprung", + "originDescription": "Välj var SSH-anslutningar kommer ifrån som standard. Detta kan åsidosättas per värd.", + "originLocal": "Den här enheten (lokalt nätverk)", + "originRemote": "Fjärrserver", + "bannerReconnect": "Återanslut", + "bannerMessage": "Fjärrsynkronisering behöver autentiseras på nytt" + }, "versionCheck": { "error": "Versionskontrollfel", "checkFailed": "Misslyckades med att söka efter uppdateringar", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Åsidosätt användarnamn för inloggningsuppgifter", "overrideCredentialUsernameDesc": "Använd användarnamnet som anges ovan istället för användarnamnet för inloggningsuppgifterna", "oidcUsernameHint": "Använd $oidc.preferred_username för att ersätta ditt OIDC-inloggningsnamn.", + "tailscaleUsernameHint": "Detta måste vara en Unix-användare som din Tailscale-identitet är beviljad i tailnätets SSH ACL, inte nödvändigtvis root.", "jumpHostChain": "Hoppa värdkedja", "portKnocking": "Port Knocking", "addKnock": "Lägg till port", @@ -650,6 +690,11 @@ "delayAfterMs": "Fördröjning efter (ms)", "useSocks5Proxy": "Använd SOCKS5-proxy", "useSocks5ProxyDesc": "Led anslutning via en proxyserver", + "connectionOrigin": "Anslutningens ursprung", + "connectionOriginDesc": "Varifrån den här värdens SSH-anslutning kommer. Åsidosätter skrivbordsappens globala standardvärde.", + "connectionOriginDefault": "Använd standard", + "connectionOriginLocal": "Den här enheten (lokalt nätverk)", + "connectionOriginRemote": "Fjärrserver", "proxyHost": "Proxyvärd", "proxyPort": "Proxyport", "proxyUsername": "Proxy-användarnamn", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Ljus cyan", "customThemeBrightWhite": "Klarvitt", "customThemeResetTooltip": "Återställ till standardinställningarna", + "savedThemesLabel": "Sparade teman", + "saveAsGlobalTheme": "Spara som globalt tema", + "saveGlobalThemeNamePrompt": "Ange ett namn för detta tema", + "saveGlobalThemeSuccess": "Temat sparat", + "saveGlobalThemeError": "Misslyckades med att spara temat", + "applyGlobalThemeTooltip": "Använd detta tema", + "deleteGlobalThemeTooltip": "Ta bort det här temat", + "noSavedThemes": "Inga sparade teman ännu", "syntaxHighlightingLabel": "Syntaxmarkering", "syntaxHighlightingDesc": "Färglägg terminalutdata (fel, sökvägar, IP-adresser, tidsstämplar)", "syntaxHighlightingCategories": "Markera kategorier", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Starta eller anslut automatiskt till tmux-session", "enableSessionLogging": "Sessionsloggning", "enableSessionLoggingDesc": "Spela in utdata från terminalsessionen för senare granskning", + "allowSessionSharing": "Tillåt sessionsdelning", + "allowSessionSharingDesc": "Tillåt att livesessioner på den här värden delas via länk eller med andra användare", "enableCommandHistory": "Kommandohistorik", "enableCommandHistoryDesc": "Record-kommandon körs i den här terminalen för historik och autofullständig ifyllning", "linkClickBehaviorLabel": "Länkklickbeteende", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} importerad", "proxmoxResultUpdated": "{{count}} uppdaterad", "proxmoxResultFailed": "{{count}} misslyckades", + "proxmoxResultSkippedNoIp": "{{count}} hoppades över (ingen IP-adress hittades)", "proxmoxImportComplete": "Proxmox-import klar: {{summary}}", "proxmoxDiscoveryFailed": "Upptäckten misslyckades", "proxmoxImportFailed": "Importen misslyckades", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Misslyckades med att spara inloggningsuppgifterna", "credentialNameRequired": "Ange ett namn för autentiseringsuppgifterna", "credentialAuthRequired": "Lägg till ett lösenord, en SSH-nyckel eller båda", + "createCredentialFromHostBtn": "Skapa autentiseringsuppgifter", + "createCredentialFromHostTitle": "Skapa autentiseringsuppgifter från värden", + "createCredentialFromHostDesc": "Skapa en återanvändbar, delbar autentiseringsuppgift som är förifylld med värdens nuvarande användarnamn, lösenord och/eller SSH-nyckel.", "backToHosts": "Tillbaka till värdar", "backToCredentials": "Tillbaka till inloggningsuppgifter", "pinned": "Fäst", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Använd / för att separera nivåer och skapa kapslade mappar.", "folderColor": "Färg", "folderIcon": "Ikon", + "folderCredential": "Referenser", + "folderCredentialNone": "Ingen inloggningsuppgifter har tilldelats", + "folderCredentialHint": "Värdar i den här mappen som använder autentiseringsuppgifterna \"Lagrad autentiseringsuppgift\" utan att ha valt sina egna autentiseringsuppgifter kommer att ärva den här.", "folderPreview": "Förhandsvisning", "folderNameFallback": "Namnlös mapp", "createFolderButton": "Skapa mapp", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Taggar", "shareHost": "Dela värd", "shareHostTitle": "Dela: {{name}}", + "shareFolder": "Dela mapp", + "shareFolderTitle": "Dela mapp: {{name}}", + "folderSharedSuccessfully": "Delade {{count}} värd(ar) i mappen", + "failedToShareFolder": "Misslyckades med att dela mappen", "sharing": { "loadError": "Misslyckades med att ladda delningsdata. Försök igen.", "shareWithSection": "Dela med", @@ -1221,6 +1287,7 @@ "shareWithCount": "Dela ({{count}})", "currentAccess": "Nuvarande åtkomst", "noAccessEntries": "Denna värd har inte delats ännu", + "folderShareSummary": "Delad {{shared}} av {{total}} värd(ar) i den här mappen", "grantedBy": "Beviljad av", "expires": "Utgår", "expired": "Utgången", @@ -1242,6 +1309,8 @@ "authMethod": "Autentiseringsmetod", "authTypeDirect": "Direkt", "authTypeCredential": "Referenser", + "authTypeNone": "Ingen", + "authTypeNoneDesc": "Inga inloggningsuppgifter lagras. Du kommer att bli ombedd att ange ett användarnamn och lösenord varje gång du ansluter; de sparas inte.", "selectCredential": "Välj en behörighetsinloggning...", "connectionSettings": "Anslutningsinställningar", "displaySettings": "Skärminställningar", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-port", "username": "Användarnamn", "password": "Lösenord", + "passwordSaved": "Lösenordet sparats, skriv för att ändra", "domain": "Domän", "securityMode": "Säkerhetsläge", "colorDepth": "Färgdjup", @@ -1424,7 +1494,60 @@ "expiresIn": "Utgår om {{duration}}", "search": "Sök efter kopplingar...", "noSearchResults": "Inga kontakter matchar din sökning", - "rename": "Byt namn på sessionen" + "rename": "Byt namn på sessionen", + "sectionSharedWithMe": "Delade med mig", + "sharedBy": "Delat av {{username}}", + "join": "Ansluta sig till", + "sharedSessionLabel": "{{hostName}} (delad)" + }, + "sessionSharing": { + "guestView": { + "loading": "Ansluter till delad session...", + "linkInvalid": "Den här delningslänken är ogiltig, har löpt ut eller har återkallats", + "rateLimited": "För många försök, försök igen snart", + "sessionEnded": "Den här sessionen är avslutad", + "readOnlyBadge": "Endast visning" + }, + "modalTitle": "Dela session", + "shareButton": "Dela", + "notReadyToShare": "Sessionen är inte redo att delas ännu", + "modeTab": { + "link": "Länk", + "user": "Användare" + }, + "linkModeDescription": "Vem som helst med den här länken kan gå med, inget konto krävs.", + "userModeDescription": "Dela med en specifik användare som redan har åtkomst till den här värden. Om de inte har åtkomst ännu, dela värden med dem först eller använd en länk istället. När sessionen har delats visas den på deras flik Anslutningar.", + "permissionLevel": { + "label": "Behörighetsnivå", + "readOnly": "Skrivskyddad", + "readOnlyDescription": "Kan titta på sessionen live men kan inte skriva eller interagera.", + "readWrite": "Läs-skriv", + "readWriteDescription": "Kan skriva och interagera med sessionen precis som ägaren." + }, + "expiryLabel": "Länkens utgångsdatum", + "createLinkButton": "Skapa länk", + "createShareButton": "Dela med användaren", + "searchUsersPlaceholder": "Sök användare...", + "noUsersFound": "Inga användare hittades", + "linkCreated": "Delningslänk skapad", + "linkCopied": "Länk kopierad till urklipp", + "copyLink": "Kopiera länk", + "shareCreated": "Sessionen delade. Den kommer att visas på fliken Anslutningar.", + "shareFailed": "Misslyckades med att skapa delningen", + "userLacksHostAccess": "Den användaren har inte åtkomst till den här värden ännu. Dela värden med dem först, eller använd en länk istället.", + "activeShares": "Aktiva aktier", + "noActiveShares": "Inga aktiva delningar för den här sessionen", + "revoke": "Återkalla", + "revokeConfirmTitle": "Återkalla den här delningen?", + "revokeConfirmDescription": "Alla som använder den här delningen kommer att förlora åtkomsten omedelbart.", + "revoked": "Delning återkallad", + "revokeFailed": "Misslyckades med att återkalla delningen", + "joinCount": "{{count}} gå med", + "joinCount_other": "{{count}} ansluter sig", + "expiresAt": "Utgångsdatum {{date}}", + "linkShareBadge": "Länk", + "userShareBadge": "Användare: {{username}}", + "loadSharesFailed": "Misslyckades med att läsa in aktiva delningar" }, "guacamole": { "connecting": "Ansluter till {{type}} session...", @@ -1436,6 +1559,9 @@ "reconnect": "Återanslut", "retry": "Försöka igen", "guacdUnavailable": "Fjärrskrivbordstjänsten (guacd) är inte tillgänglig. Se till att guacd körs, är tillgängligt och korrekt konfigurerat i administratörsinställningarna.", + "credentialPromptTitle": "Ange RDP-inloggningsuppgifter", + "credentialPromptDescription": "Den här värden är inställd på att be om inloggningsuppgifter vid anslutning. De används endast för den här sessionen och sparas inte.", + "connect": "Ansluta", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Återanslut sessionen", "collapse": "Komprimera verktygsfältet", "expand": "Expandera verktygsfältet", - "dragHandle": "Dra för att flytta" + "dragHandle": "Dra för att flytta", + "switchToTrackpad": "Växla till styrplatteläge (dra för att flytta markören, tryck för att klicka)", + "switchToTouch": "Växla till pekläge (tryck direkt där du vill klicka)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Tvåfaktorsautentisering krävs", "totpCodeLabel": "Verifieringskod", "totpVerify": "Kontrollera", + "mfaPromptRequired": "Autentisering krävs", + "mfaPushRequired": "Push-autentisering krävs", + "mfaMenuPlaceholder": "Ange ditt svar", + "mfaWaitingApproval": "Väntar på godkännande på din enhet...", + "mfaSendRequest": "Skicka förfrågan", "warpgateAuthRequired": "Warpgate-autentisering krävs", "warpgateSecurityKey": "Säkerhetsnyckel", "warpgateAuthUrl": "Autentiserings-URL", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Begränsad hastighet: För många verifieringsförsök. Försök igen senare.", "resetCodeRateLimitedWithTime": "Begränsad hastighet: För många verifieringsförsök. Vänta {{time}} sekunder innan du försöker igen.", "authTokenSaveFailed": "Misslyckades med att spara autentiseringstoken", - "failedToLoadServer": "Misslyckades med att ladda servern" + "failedToLoadServer": "Misslyckades med att ladda servern", + "remoteServerRequired": "Fjärrserver krävs. Anslut en fjärrserver i Inställningar för att använda den här anslutningstypen." }, "messages": { "registrationDisabled": "Registrering av nytt konto är för närvarande inaktiverat av en administratör. Vänligen logga in eller kontakta en administratör.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Kunde inte spara SSL-inställningarna", "sslRequiresDomain": "Domän och e-postadress krävs", "sslInfoNote": "Efter att du har utfärdat ett certifikat, aktivera SSL i dina miljövariabler (ENABLE_SSL=true) och starta om Termix.", + "sslManualOption": "Manual (ladda upp certifikat)", + "sslManualCert": "Certifikat (PEM)", + "sslManualCertPlaceholder": "-----BEGIN CERTIFIKAT-----", + "sslManualKey": "Privat nyckel (PEM)", + "sslManualKeyPlaceholder": "-----BEGIN PRIVAT NYCKEL-----", + "sslManualDesc": "Klistra in ditt befintliga certifikat och din privata nyckel, inklusive en fullständig kedja om det krävs av din CA.", + "sslManualUpload": "Ladda upp och installera certifikat", + "sslManualUploadLoading": "Laddar upp certifikat...", + "sslManualUploadSuccess": "Certifikatet har laddats upp och installerats", + "sslManualUploadFailed": "Uppladdning av certifikat misslyckades", + "sslManualRequiresFields": "Certifikat och privat nyckel krävs", "auditLogTotal": "{{total}} totala antalet poster", "auditLogEmpty": "Inga poster i granskningsloggen hittades", "auditLogSuccess": "Framgång", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Kommandohistorik", "commandHistoryEnabledDesc": "Tillåt inspelning av kommandohistorik. När det är inaktiverat sparas inte historiken oavsett inställningarna per värd.", "updateCommandHistoryFailed": "Misslyckades med att uppdatera inställningen för kommandohistorik", + "analyticsEnabled": "Dela anonym användningsstatistik", + "analyticsEnabledDesc": "Skickar en anonym daglig räkning av användare, värdar och funktionsanvändning för att förbättra Termix. Inga personuppgifter eller anslutningsdetaljer inkluderas någonsin.", + "updateAnalyticsFailed": "Det gick inte att uppdatera analysinställningen", + "sessionSharingGloballyEnabled": "Tillåt sessionsdelning", + "sessionSharingGloballyEnabledDesc": "Tillåt att liveterminal-, RDP-, VNC- och Telnet-sessioner delas instansövergripande. Åsidosätter alla delningsknappar per värd när de är inaktiverade.", + "updateSessionSharingFailed": "Det gick inte att uppdatera inställningen för sessionsdelning", "sessionTimeout": "Sessionstimeout", "hours": "timmar", "sessionTimeoutRange": "Min 1 timme · Max 720 timmar", @@ -3119,6 +3270,47 @@ "executionSuccess": "Framgång", "executionFailed": "Misslyckades" }, + "keybindings": { + "title": "Kortkommandon", + "description": "Anpassa kortkommandon för att kopiera, klistra in och kontrollera terminalen, eller bind tangenter för att skicka text eller köra ett kodavsnitt.", + "defaultsHeading": "Inbyggda genvägar", + "customHeading": "Anpassade genvägar", + "addBinding": "Lägg till genväg", + "addBindingTitle": "Lägg till genväg", + "editBindingTitle": "Redigera genväg", + "loading": "Belastning...", + "noCustomBindings": "Inga anpassade genvägar ännu", + "defaultBadge": "Standard", + "customizedBadge": "Anpassad", + "customize": "Anpassa", + "resetToDefault": "Återställ till standard", + "resetAllToDefaults": "Återställ alla till standardinställningarna", + "close": "Nära", + "cancel": "Avboka", + "saveBinding": "Spara genväg", + "saveError": "Kunde inte spara kortkommandon", + "pressKeysToRecord": "Tangentkombination", + "pressKeysPlaceholder": "Klicka och tryck på tangenterna", + "recording": "Tryck på tangenterna...", + "comboRequiredError": "Tryck först på en tangentkombination", + "textRequiredError": "Ange texten som ska skickas", + "controlCodeRequiredError": "Ange en enda bokstav för kontrollkoden", + "snippetRequiredError": "Välj ett utdrag", + "conflictWarning": "Denna kombination är redan bunden till {{combo}}. Om du sparar den aktiveras båda.", + "actionLabel": "Handling", + "actionCopy": "Kopiera val", + "actionPaste": "Klistra", + "actionSendControlCode": "Skicka Ctrl+bokstavssignal", + "actionSendText": "Skicka sms", + "actionRunSnippet": "Kör befintligt kodavsnitt", + "controlCodeLabel": "Brev", + "textLabel": "SMS att skicka", + "appendEnterLabel": "Tryck Enter efter att du skickat", + "snippetLabel": "Utdrag", + "selectSnippetPlaceholder": "Välj ett utdrag", + "orphanedSnippetWarning": "utdraget hittades inte", + "clipboardPermissionNote": "Kan be om behörighet för urklipp vid första användningen i vissa webbläsare." + }, "userProfile": { "donateTitle": "Håll Termix vid liv", "donateDescription": "Termix är gratis, självhostat och byggt av endast ett fåtal personer på sin fritid. Om det har sparat dig tid eller pengar, hjälper en kryptodonation till att hålla det igång.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Utseende", "sectionSecurity": "Säkerhet", "sectionApiKeys": "API-nycklar", + "sectionData": "Data", "sectionC2sTunnels": "C2S-tunnlar", "usernameLabel": "Användarnamn", "roleLabel": "Roll", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Terminal", "commandAutocomplete": "Kommando Autokomplettering", "commandAutocompleteDesc": "Visa autokomplettering medan du skriver", + "keyboardShortcuts": "Kortkommandon", + "keyboardShortcutsDescription": "Anpassa kortkommandon för terminalkopiering, klistra in och kommandon", + "manageShortcuts": "Hantera", "terminalLinkBehavior": "Klicka på terminallänken", "terminalLinkBehaviorDesc": "Standardbeteende vid klick på länkar i terminalen", "historyTracking": "Historikspårning", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Lägg till din nyckel i", "apiKeyUsageHintHeader": "rubrik.", "apiKeyPermissionsHint": "Nycklar ärver behörigheterna från den skapande användaren.", + "exportData": "Exportera mina data", + "exportDataDesc": "Ladda ner en säkerhetskopia av dina värdar, inloggningsuppgifter och inställningar för att överföra till en annan enhet.", + "export": "Exportera", + "exporting": "Exporterar...", + "importData": "Importera mina data", + "importDataDesc": "Återställ dina värdar, inloggningsuppgifter och inställningar från en .sqlite-säkerhetskopia", + "importDataSelected": "Vald: {{name}}", + "selectFile": "Välj fil", + "changeFile": "Ändra", + "import": "Importera", + "importing": "Importerar...", + "exportSuccess": "Data exporterades", + "exportFailed": "Dataexport misslyckades", + "importSelectFile": "Välj en fil först", + "importCompleted": "Importen är klar: {{total}} objekt har importerats, {{skipped}} hoppades över", + "importFailed": "Importen misslyckades: {{error}}", "roleUser": "Användare", "authMethodDual": "Dubbel autentisering", "authMethodOidc": "OIDC", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Klistra", "editQuickKeys": "Redigera snabbknappar", "quickKeysTitle": "Snabbtangenter", "quickKeysDesc": "Tryck på × för att ta bort. Stöder upp till 8 tecken.", diff --git a/src/ui/locales/translated/th_TH.json b/src/ui/locales/translated/th_TH.json index 6a99c25a..0ce290d9 100644 --- a/src/ui/locales/translated/th_TH.json +++ b/src/ui/locales/translated/th_TH.json @@ -389,6 +389,45 @@ "noSavedServers": "ไม่มีเซิร์ฟเวอร์ที่บันทึกไว้", "removeServer": "ลบ" }, + "migrationNotice": { + "title": "ตอนนี้ Termix Desktop สามารถใช้งานแบบสแตนด์อโลนได้แล้ว", + "body1": "แอปนี้เดิมทีเชื่อมต่อโดยตรงกับเซิร์ฟเวอร์ Termix ของคุณ แต่ตอนนี้ได้รับการปรับปรุงใหม่ให้ทำงานได้ด้วยตัวเองอย่างสมบูรณ์ โดยจัดเก็บโฮสต์และข้อมูลรับรองไว้ในเครื่อง ทำให้ยังคงทำงานได้แม้ในขณะออฟไลน์ การซิงค์ข้อมูลแบบสองทางกับเซิร์ฟเวอร์ Termix ที่คุณโฮสต์เองนั้นเป็นตัวเลือกเสริมแล้ว", + "body2": "โฮสต์และข้อมูลรับรองที่มีอยู่ของคุณยังคงอยู่บน {{url}}เปิดใช้งานการซิงค์ระยะไกลและเชื่อมต่อกับเซิร์ฟเวอร์นั้นอีกครั้งเพื่อนำข้อมูลกลับมาและทำให้ทั้งสองซิงค์กันต่อไป", + "dismiss": "ไม่ใช่ตอนนี้", + "setUpSync": "ตั้งค่าการซิงค์ระยะไกล" + }, + "remoteSync": { + "title": "การซิงค์ระยะไกล", + "description": "คุณสามารถเชื่อมต่อแอปพลิเคชันบนเดสก์ท็อปนี้กับเซิร์ฟเวอร์ Termix ที่คุณโฮสต์เองเพื่อซิงค์โฮสต์ ข้อมูลรับรอง และโค้ดตัวอย่างระหว่างอุปกรณ์ต่างๆ ได้ แอปพลิเคชันนี้ทำงานได้อย่างสมบูรณ์ในโหมดออฟไลน์เสมอ ไม่ว่าคุณจะเชื่อมต่อหรือไม่ก็ตาม", + "notConnected": "ไม่ได้เชื่อมต่อ", + "connected": "เชื่อมต่อแล้ว", + "connectedTo": "เชื่อมต่อกับ {{url}}", + "lastSynced": "ซิงค์ครั้งล่าสุด {{time}}", + "neverSynced": "ไม่เคยซิงค์", + "syncError": "ข้อผิดพลาดในการซิงค์: {{message}}", + "needsReauth": "การเข้าสู่ระบบหมดอายุแล้ว", + "connectButton": "เชื่อมต่อกับเซิร์ฟเวอร์", + "disconnectButton": "ตัดการเชื่อมต่อ", + "syncNowButton": "ซิงค์เดี๋ยวนี้", + "syncing": "กำลังซิงค์...", + "serverUrl": "URL ของเซิร์ฟเวอร์", + "enterServerUrl": "โปรดป้อน URL ของเซิร์ฟเวอร์", + "mustIncludeProtocol": "URL ของเซิร์ฟเวอร์ต้องขึ้นต้นด้วย http:// หรือ https://", + "connectionTestFailed": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ Termix ที่ URL นั้นได้", + "allowInvalidCertificate": "อนุญาตให้ใช้ใบรับรองที่ไม่ถูกต้อง", + "allowInvalidCertificateDesc": "ใช้ได้เฉพาะกับเซิร์ฟเวอร์ที่ดูแลเองที่เชื่อถือได้ ซึ่งมีใบรับรองแบบลงนามเองหรือใบรับรองตามที่อยู่ IP เท่านั้น", + "savedServers": "เซิร์ฟเวอร์ที่บันทึกไว้", + "removeServer": "ลบ", + "continueButton": "ดำเนินการต่อ", + "cancelButton": "ยกเลิก", + "signInTitle": "เข้าสู่ระบบที่ {{url}}", + "originTitle": "จุดเริ่มต้นของการเชื่อมต่อ", + "originDescription": "เลือกต้นทางของการเชื่อมต่อ SSH ตามค่าเริ่มต้น สามารถเปลี่ยนแปลงการตั้งค่านี้ได้สำหรับแต่ละโฮสต์", + "originLocal": "อุปกรณ์นี้ (เครือข่ายท้องถิ่น)", + "originRemote": "เซิร์ฟเวอร์ระยะไกล", + "bannerReconnect": "เชื่อมต่อใหม่", + "bannerMessage": "การซิงค์ระยะไกลจำเป็นต้องยืนยันตัวตนอีกครั้ง" + }, "versionCheck": { "error": "ข้อผิดพลาดในการตรวจสอบเวอร์ชัน", "checkFailed": "ไม่สามารถตรวจสอบการอัปเดตได้", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "แทนที่ชื่อผู้ใช้ข้อมูลประจำตัว", "overrideCredentialUsernameDesc": "ใช้ชื่อผู้ใช้ที่ระบุไว้ข้างต้นแทนชื่อผู้ใช้ของข้อมูลประจำตัว", "oidcUsernameHint": "ใช้ $oidc.preferred_username แทนชื่อผู้ใช้ OIDC ของคุณ", + "tailscaleUsernameHint": "ผู้ใช้งานนี้ต้องเป็นผู้ใช้ Unix ที่ได้รับสิทธิ์การใช้งาน Tailscale ใน SSH ACL ของ tailnet ไม่จำเป็นต้องเป็น root เสมอไป", "jumpHostChain": "ห่วงโซ่ Jump Host", "portKnocking": "การเคาะพอร์ต", "addKnock": "เพิ่มพอร์ต", @@ -650,6 +690,11 @@ "delayAfterMs": "เวลาหน่วงหลังจาก (มิลลิวินาที)", "useSocks5Proxy": "ใช้พร็อกซี SOCKS5", "useSocks5ProxyDesc": "การเชื่อมต่อเส้นทางผ่านพร็อกซีเซิร์ฟเวอร์", + "connectionOrigin": "จุดเริ่มต้นของการเชื่อมต่อ", + "connectionOriginDesc": "ระบุตำแหน่งที่การเชื่อมต่อ SSH ของโฮสต์นี้เริ่มต้นขึ้น ซึ่งจะแทนที่ค่าเริ่มต้นทั่วไปของแอปพลิเคชันบนเดสก์ท็อป", + "connectionOriginDefault": "ใช้ค่าเริ่มต้น", + "connectionOriginLocal": "อุปกรณ์นี้ (เครือข่ายท้องถิ่น)", + "connectionOriginRemote": "เซิร์ฟเวอร์ระยะไกล", "proxyHost": "พร็อกซีโฮสต์", "proxyPort": "พอร์ตพร็อกซี", "proxyUsername": "ชื่อผู้ใช้พร็อกซี", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "สีฟ้าสดใส", "customThemeBrightWhite": "สีขาวสว่าง", "customThemeResetTooltip": "รีเซ็ตเป็นการตั้งค่าเริ่มต้น", + "savedThemesLabel": "ธีมที่บันทึกไว้", + "saveAsGlobalTheme": "บันทึกเป็นธีมทั่วโลก", + "saveGlobalThemeNamePrompt": "ตั้งชื่อให้กับธีมนี้", + "saveGlobalThemeSuccess": "บันทึกธีมแล้ว", + "saveGlobalThemeError": "ไม่สามารถบันทึกธีมได้", + "applyGlobalThemeTooltip": "ใช้ธีมนี้", + "deleteGlobalThemeTooltip": "ลบธีมนี้", + "noSavedThemes": "ยังไม่มีธีมที่บันทึกไว้", "syntaxHighlightingLabel": "การเน้นไวยากรณ์", "syntaxHighlightingDesc": "ใส่สีให้กับข้อมูลที่แสดงในเทอร์มินัล (ข้อผิดพลาด เส้นทาง ที่อยู่ IP และเวลา)", "syntaxHighlightingCategories": "หมวดหมู่เด่น", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "เปิดใช้งานหรือเชื่อมต่อกับเซสชัน tmux โดยอัตโนมัติ", "enableSessionLogging": "การบันทึกเซสชัน", "enableSessionLoggingDesc": "บันทึกผลลัพธ์จากเทอร์มินัลเพื่อตรวจสอบในภายหลัง", + "allowSessionSharing": "อนุญาตการแชร์เซสชัน", + "allowSessionSharingDesc": "อนุญาตให้แชร์เซสชันสดบนโฮสต์นี้ผ่านลิงก์หรือกับผู้ใช้รายอื่น", "enableCommandHistory": "ประวัติคำสั่ง", "enableCommandHistoryDesc": "บันทึกคำสั่งที่เรียกใช้ในเทอร์มินัลนี้เพื่อดูประวัติและฟังก์ชันเติมคำอัตโนมัติ", "linkClickBehaviorLabel": "พฤติกรรมการคลิกลิงก์", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} นำเข้าแล้ว", "proxmoxResultUpdated": "{{count}} อัปเดตแล้ว", "proxmoxResultFailed": "{{count}} ล้มเหลว", + "proxmoxResultSkippedNoIp": "{{count}} ข้ามไป (ไม่พบ IP)", "proxmoxImportComplete": "การนำเข้า Proxmox เสร็จสมบูรณ์: {{summary}}", "proxmoxDiscoveryFailed": "การค้นพบล้มเหลว", "proxmoxImportFailed": "การนำเข้าล้มเหลว", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "ไม่สามารถบันทึกข้อมูลประจำตัวได้", "credentialNameRequired": "โปรดระบุชื่อสำหรับข้อมูลประจำตัว", "credentialAuthRequired": "เพิ่มรหัสผ่าน คีย์ SSH หรือทั้งสองอย่าง", + "createCredentialFromHostBtn": "สร้างข้อมูลรับรอง", + "createCredentialFromHostTitle": "สร้างข้อมูลรับรองจากโฮสต์", + "createCredentialFromHostDesc": "สร้างรายการข้อมูลประจำตัวที่สามารถนำกลับมาใช้ใหม่และแบ่งปันได้ โดยกรอกข้อมูลล่วงหน้าด้วยชื่อผู้ใช้ รหัสผ่าน และ/หรือคีย์ SSH ปัจจุบันของโฮสต์นี้", "backToHosts": "กลับไปที่หน้าโฮสต์", "backToCredentials": "กลับไปที่ข้อมูลประจำตัว", "pinned": "ปักหมุด", @@ -1000,6 +1059,9 @@ "folderNestingHint": "ใช้เครื่องหมาย / เพื่อแยกแต่ละระดับและสร้างโฟลเดอร์ย่อย", "folderColor": "สี", "folderIcon": "ไอคอน", + "folderCredential": "ใบรับรอง", + "folderCredentialNone": "ไม่มีการกำหนดข้อมูลประจำตัว", + "folderCredentialHint": "โฮสต์ในโฟลเดอร์นี้ที่ใช้การตรวจสอบสิทธิ์ \"ข้อมูลรับรองที่จัดเก็บไว้\" โดยไม่ได้เลือกข้อมูลรับรองของตนเอง จะได้รับข้อมูลรับรองนี้มาโดยอัตโนมัติ", "folderPreview": "ตัวอย่าง", "folderNameFallback": "โฟลเดอร์ที่ไม่มีชื่อ", "createFolderButton": "สร้างโฟลเดอร์", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "แท็ก", "shareHost": "แชร์โฮสต์", "shareHostTitle": "แชร์: {{name}}", + "shareFolder": "โฟลเดอร์ที่แชร์", + "shareFolderTitle": "โฟลเดอร์ที่แชร์: {{name}}", + "folderSharedSuccessfully": "โฮสต์ที่แชร์ {{count}} ในโฟลเดอร์", + "failedToShareFolder": "ไม่สามารถแชร์โฟลเดอร์ได้", "sharing": { "loadError": "ไม่สามารถโหลดข้อมูลการแชร์ได้ โปรดลองอีกครั้ง", "shareWithSection": "แบ่งปันกับ", @@ -1221,6 +1287,7 @@ "shareWithCount": "แชร์ ({{count}})", "currentAccess": "การเข้าถึงปัจจุบัน", "noAccessEntries": "โฮสต์นี้ยังไม่ได้ถูกแชร์", + "folderShareSummary": "แชร์ {{shared}} ของ {{total}} โฮสต์ในโฟลเดอร์นี้", "grantedBy": "ได้รับอนุญาตจาก", "expires": "หมดอายุ", "expired": "หมดอายุ", @@ -1242,6 +1309,8 @@ "authMethod": "วิธีการตรวจสอบสิทธิ์", "authTypeDirect": "โดยตรง", "authTypeCredential": "ใบรับรอง", + "authTypeNone": "ไม่มี", + "authTypeNoneDesc": "ไม่มีการจัดเก็บข้อมูลประจำตัวใดๆ คุณจะต้องป้อนชื่อผู้ใช้และรหัสผ่านทุกครั้งที่เชื่อมต่อ ซึ่งจะไม่ถูกบันทึกไว้", "selectCredential": "เลือกข้อมูลประจำตัว...", "connectionSettings": "การตั้งค่าการเชื่อมต่อ", "displaySettings": "การตั้งค่าการแสดงผล", @@ -1259,6 +1328,7 @@ "rdpPort": "พอร์ต RDP", "username": "ชื่อผู้ใช้", "password": "รหัสผ่าน", + "passwordSaved": "บันทึกรหัสผ่านแล้ว กรุณาพิมพ์เพื่อเปลี่ยนรหัสผ่าน", "domain": "โดเมน", "securityMode": "โหมดความปลอดภัย", "colorDepth": "ความลึกของสี", @@ -1424,7 +1494,60 @@ "expiresIn": "หมดอายุใน {{duration}}", "search": "ค้นหาการเชื่อมต่อ...", "noSearchResults": "ไม่พบผลลัพธ์ที่ตรงกับการค้นหาของคุณ", - "rename": "เปลี่ยนชื่อเซสชัน" + "rename": "เปลี่ยนชื่อเซสชัน", + "sectionSharedWithMe": "แชร์กับฉัน", + "sharedBy": "แชร์โดย {{username}}", + "join": "เข้าร่วม", + "sharedSessionLabel": "{{hostName}} (แชร์)" + }, + "sessionSharing": { + "guestView": { + "loading": "กำลังเชื่อมต่อกับเซสชันที่ใช้ร่วมกัน...", + "linkInvalid": "ลิงก์แชร์นี้ไม่ถูกต้อง หมดอายุ หรือถูกยกเลิกแล้ว", + "rateLimited": "พยายามหลายครั้งเกินไป โปรดลองใหม่อีกครั้งในภายหลัง", + "sessionEnded": "การประชุมครั้งนี้สิ้นสุดลงแล้ว", + "readOnlyBadge": "ดูได้อย่างเดียว" + }, + "modalTitle": "เซสชั่นการแบ่งปัน", + "shareButton": "แบ่งปัน", + "notReadyToShare": "เซสชั่นยังไม่พร้อมให้แชร์", + "modeTab": { + "link": "ลิงก์", + "user": "ผู้ใช้" + }, + "linkModeDescription": "ใครก็ตามที่มีลิงก์นี้สามารถเข้าร่วมได้ ไม่จำเป็นต้องมีบัญชีผู้ใช้", + "userModeDescription": "แชร์กับผู้ใช้เฉพาะรายที่มีสิทธิ์เข้าถึงโฮสต์นี้อยู่แล้ว หากพวกเขายังไม่มีสิทธิ์เข้าถึง ให้แชร์โฮสต์กับพวกเขาก่อน หรือใช้ลิงก์แทน เมื่อแชร์แล้ว เซสชันจะปรากฏในแท็บการเชื่อมต่อของพวกเขา", + "permissionLevel": { + "label": "ระดับการอนุญาต", + "readOnly": "อ่านอย่างเดียว", + "readOnlyDescription": "สามารถรับชมการประชุมสดได้ แต่ไม่สามารถพิมพ์หรือโต้ตอบได้", + "readWrite": "อ่าน-เขียน", + "readWriteDescription": "สามารถพิมพ์และโต้ตอบกับเซสชันได้เหมือนกับเจ้าของเซสชัน" + }, + "expiryLabel": "ลิงก์หมดอายุ", + "createLinkButton": "สร้างลิงก์", + "createShareButton": "แชร์กับผู้ใช้", + "searchUsersPlaceholder": "ค้นหาผู้ใช้...", + "noUsersFound": "ไม่พบผู้ใช้งาน", + "linkCreated": "สร้างลิงก์สำหรับแชร์แล้ว", + "linkCopied": "คัดลอกลิงก์ไปยังคลิปบอร์ดแล้ว", + "copyLink": "คัดลิงก์", + "shareCreated": "แชร์เซสชันแล้ว เซสชันนั้นจะปรากฏในแท็บการเชื่อมต่อของพวกเขา", + "shareFailed": "ไม่สามารถสร้างการแชร์ได้", + "userLacksHostAccess": "ผู้ใช้รายนั้นยังไม่มีสิทธิ์เข้าถึงโฮสต์นี้ โปรดแชร์โฮสต์ให้พวกเขาก่อน หรือใช้ลิงก์แทน", + "activeShares": "หุ้นที่ใช้งานอยู่", + "noActiveShares": "ไม่มีหุ้นที่เปิดซื้อขายในรอบนี้", + "revoke": "ถอน", + "revokeConfirmTitle": "ต้องการยกเลิกการแชร์นี้หรือไม่?", + "revokeConfirmDescription": "ใครก็ตามที่ใช้ลิงก์นี้จะเสียสิทธิ์การเข้าถึงทันที", + "revoked": "หุ้นถูกเพิกถอน", + "revokeFailed": "ไม่สามารถเพิกถอนหุ้นได้", + "joinCount": "{{count}} เข้าร่วม", + "joinCount_other": "{{count}} เข้าร่วม", + "expiresAt": "หมดอายุ {{date}}", + "linkShareBadge": "ลิงก์", + "userShareBadge": "ผู้ใช้: {{username}}", + "loadSharesFailed": "ไม่สามารถโหลดหุ้นที่ใช้งานอยู่ได้" }, "guacamole": { "connecting": "กำลังเชื่อมต่อกับเซสชัน {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "เชื่อมต่อใหม่", "retry": "ลองใหม่อีกครั้ง", "guacdUnavailable": "บริการเดสก์ท็อประยะไกล (guacd) ไม่พร้อมใช้งาน โปรดตรวจสอบให้แน่ใจว่า guacd กำลังทำงาน สามารถเข้าถึงได้ และกำหนดค่าอย่างถูกต้องในการตั้งค่าผู้ดูแลระบบ", + "credentialPromptTitle": "ป้อนข้อมูลรับรอง RDP", + "credentialPromptDescription": "โฮสต์นี้ตั้งค่าให้ขอข้อมูลรับรองเมื่อเชื่อมต่อ ข้อมูลเหล่านี้จะใช้สำหรับเซสชันนี้เท่านั้นและจะไม่ถูกบันทึกไว้", + "connect": "เชื่อมต่อ", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "เซสชั่นเชื่อมต่อใหม่", "collapse": "ยุบแถบเครื่องมือ", "expand": "ขยายแถบเครื่องมือ", - "dragHandle": "ลากเพื่อจัดตำแหน่งใหม่" + "dragHandle": "ลากเพื่อจัดตำแหน่งใหม่", + "switchToTrackpad": "เปลี่ยนไปใช้โหมดแทร็กแพด (ลากเพื่อเลื่อนเคอร์เซอร์ แตะเพื่อคลิก)", + "switchToTouch": "เปลี่ยนเป็นโหมดสัมผัส (แตะตรงจุดที่คุณต้องการคลิก)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "ต้องใช้การยืนยันตัวตนสองขั้นตอน", "totpCodeLabel": "รหัสยืนยัน", "totpVerify": "ตรวจสอบ", + "mfaPromptRequired": "ต้องยืนยันตัวตน", + "mfaPushRequired": "ต้องมีการตรวจสอบสิทธิ์แบบพุช", + "mfaMenuPlaceholder": "กรุณาป้อนคำตอบของคุณ", + "mfaWaitingApproval": "กำลังรอการอนุมัติบนอุปกรณ์ของคุณ...", + "mfaSendRequest": "ส่งคำขอ", "warpgateAuthRequired": "ต้องยืนยันตัวตนผ่านวาร์ปเกต", "warpgateSecurityKey": "รหัสความปลอดภัย", "warpgateAuthUrl": "URL สำหรับการตรวจสอบสิทธิ์", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "จำกัดจำนวนครั้งในการยืนยัน: มีการพยายามยืนยันมากเกินไป โปรดลองใหม่อีกครั้งในภายหลัง", "resetCodeRateLimitedWithTime": "จำกัดจำนวนครั้ง: มีการพยายามยืนยันมากเกินไป โปรดรอ {{time}} วินาทีก่อนลองอีกครั้ง", "authTokenSaveFailed": "ไม่สามารถบันทึกโทเค็นการตรวจสอบสิทธิ์ได้", - "failedToLoadServer": "ไม่สามารถโหลดเซิร์ฟเวอร์ได้" + "failedToLoadServer": "ไม่สามารถโหลดเซิร์ฟเวอร์ได้", + "remoteServerRequired": "จำเป็นต้องใช้เซิร์ฟเวอร์ระยะไกล เชื่อมต่อเซิร์ฟเวอร์ระยะไกลในการตั้งค่าเพื่อใช้ประเภทการเชื่อมต่อนี้" }, "messages": { "registrationDisabled": "ขณะนี้ผู้ดูแลระบบได้ปิดใช้งานการลงทะเบียนบัญชีใหม่แล้ว โปรดเข้าสู่ระบบหรือติดต่อผู้ดูแลระบบ", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "ไม่สามารถบันทึกการตั้งค่า SSL ได้", "sslRequiresDomain": "ต้องระบุโดเมนและอีเมล", "sslInfoNote": "หลังจากออกใบรับรองแล้ว ให้เปิดใช้งาน SSL ในตัวแปรสภาพแวดล้อมของคุณ (ENABLE_SSL=true) และรีสตาร์ท Termix", + "sslManualOption": "คู่มือ (อัปโหลดใบรับรอง)", + "sslManualCert": "ใบรับรอง (PEM)", + "sslManualCertPlaceholder": "-----เริ่มต้นใบรับรอง-----", + "sslManualKey": "กุญแจส่วนตัว (PEM)", + "sslManualKeyPlaceholder": "-----เริ่มรหัสส่วนตัว-----", + "sslManualDesc": "โปรดวางใบรับรองและรหัสส่วนตัวที่มีอยู่ของคุณ รวมถึงห่วงโซ่ใบรับรองแบบเต็มหากหน่วยงานออกใบรับรอง (CA) ของคุณต้องการ", + "sslManualUpload": "อัปโหลดและติดตั้งใบรับรอง", + "sslManualUploadLoading": "กำลังอัปโหลดใบรับรอง...", + "sslManualUploadSuccess": "อัปโหลดและติดตั้งใบรับรองเรียบร้อยแล้ว", + "sslManualUploadFailed": "การอัปโหลดใบรับรองล้มเหลว", + "sslManualRequiresFields": "ต้องใช้ใบรับรองและรหัสส่วนตัว", "auditLogTotal": "{{total}} จำนวนรายการทั้งหมด", "auditLogEmpty": "ไม่พบรายการบันทึกการตรวจสอบใดๆ", "auditLogSuccess": "ความสำเร็จ", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "ประวัติคำสั่ง", "commandHistoryEnabledDesc": "อนุญาตให้บันทึกประวัติคำสั่ง เมื่อปิดใช้งาน ประวัติจะไม่ถูกบันทึกไม่ว่าการตั้งค่าต่อโฮสต์จะเป็นอย่างไรก็ตาม", "updateCommandHistoryFailed": "ไม่สามารถอัปเดตการตั้งค่าประวัติคำสั่งได้", + "analyticsEnabled": "แบ่งปันสถิติการใช้งานแบบไม่ระบุตัวตน", + "analyticsEnabledDesc": "ส่งรายงานจำนวนผู้ใช้ โฮสต์ และการใช้งานฟีเจอร์รายวันแบบไม่ระบุตัวตน เพื่อช่วยปรับปรุง Termix ไม่มีข้อมูลส่วนบุคคลหรือรายละเอียดการเชื่อมต่อใดๆ รวมอยู่ด้วย", + "updateAnalyticsFailed": "ไม่สามารถอัปเดตการตั้งค่าการวิเคราะห์ได้", + "sessionSharingGloballyEnabled": "อนุญาตการแชร์เซสชัน", + "sessionSharingGloballyEnabledDesc": "อนุญาตให้แชร์เซสชันเทอร์มินัลสด, RDP, VNC และ Telnet ทั่วทั้งอินสแตนซ์ การตั้งค่านี้จะแทนที่การตั้งค่าการแชร์ต่อโฮสต์ทั้งหมดเมื่อปิดใช้งานอยู่", + "updateSessionSharingFailed": "ไม่สามารถอัปเดตการตั้งค่าการแชร์เซสชันได้", "sessionTimeout": "หมดเวลาเซสชัน", "hours": "ชั่วโมง", "sessionTimeoutRange": "ขั้นต่ำ 1 ชั่วโมง · ขั้นสูงสุด 720 ชั่วโมง", @@ -3119,6 +3270,47 @@ "executionSuccess": "ความสำเร็จ", "executionFailed": "ล้มเหลว" }, + "keybindings": { + "title": "แป้นพิมพ์ลัด", + "description": "ปรับแต่งปุ่มลัดสำหรับการคัดลอก วาง และควบคุมในเทอร์มินัล หรือกำหนดปุ่มเพื่อส่งข้อความหรือเรียกใช้โค้ดตัวอย่าง", + "defaultsHeading": "ทางลัดในตัว", + "customHeading": "ทางลัดแบบกำหนดเอง", + "addBinding": "เพิ่มทางลัด", + "addBindingTitle": "เพิ่มทางลัด", + "editBindingTitle": "แก้ไขทางลัด", + "loading": "กำลังโหลด...", + "noCustomBindings": "ยังไม่มีปุ่มลัดที่กำหนดเอง", + "defaultBadge": "ค่าเริ่มต้น", + "customizedBadge": "ปรับแต่งตามต้องการ", + "customize": "ปรับแต่ง", + "resetToDefault": "รีเซ็ตเป็นค่าเริ่มต้น", + "resetAllToDefaults": "รีเซ็ตทุกอย่างเป็นการตั้งค่าเริ่มต้น", + "close": "ปิด", + "cancel": "ยกเลิก", + "saveBinding": "บันทึกทางลัด", + "saveError": "ไม่สามารถบันทึกปุ่มลัดได้", + "pressKeysToRecord": "ปุ่มกด", + "pressKeysPlaceholder": "คลิกและกดปุ่ม", + "recording": "กดปุ่ม...", + "comboRequiredError": "กดปุ่มคีย์ผสมก่อน", + "textRequiredError": "ป้อนข้อความที่จะส่ง", + "controlCodeRequiredError": "ป้อนตัวอักษรตัวเดียวสำหรับรหัสควบคุม", + "snippetRequiredError": "เลือกส่วนย่อย", + "conflictWarning": "การรวมกันนี้ถูกผูกไว้กับ {{combo}}แล้ว การบันทึกจะทำให้ทั้งสองอย่างทำงาน", + "actionLabel": "การกระทำ", + "actionCopy": "คัดลอกการเลือก", + "actionPaste": "แปะ", + "actionSendControlCode": "ส่งสัญญาณ Ctrl+ตัวอักษร", + "actionSendText": "ส่งข้อความ", + "actionRunSnippet": "เรียกใช้โค้ดตัวอย่างที่มีอยู่", + "controlCodeLabel": "จดหมาย", + "textLabel": "ข้อความที่จะส่ง", + "appendEnterLabel": "กด Enter หลังจากส่งแล้ว", + "snippetLabel": "ส่วนหนึ่ง", + "selectSnippetPlaceholder": "เลือกส่วนย่อย", + "orphanedSnippetWarning": "ไม่พบส่วนย่อย", + "clipboardPermissionNote": "ในบางเบราว์เซอร์ อาจขออนุญาตคัดลอกและวางข้อมูลลงในคลิปบอร์ดเมื่อใช้งานครั้งแรก" + }, "userProfile": { "donateTitle": "รักษา Termix ให้คงอยู่ต่อไป", "donateDescription": "Termix เป็นโปรแกรมฟรี ติดตั้งบนเซิร์ฟเวอร์ของคุณเอง และสร้างขึ้นโดยคนเพียงไม่กี่คนในเวลาว่าง หากโปรแกรมนี้ช่วยประหยัดเวลาหรือเงินของคุณ การบริจาคด้วยคริปโตเคอร์เรนซีจะช่วยให้โปรแกรมนี้ดำเนินต่อไปได้", @@ -3134,6 +3326,7 @@ "sectionAppearance": "รูปร่าง", "sectionSecurity": "ความปลอดภัย", "sectionApiKeys": "คีย์ API", + "sectionData": "ข้อมูล", "sectionC2sTunnels": "อุโมงค์ C2S", "usernameLabel": "ชื่อผู้ใช้", "roleLabel": "บทบาท", @@ -3162,6 +3355,9 @@ "settingsTerminal": "เทอร์มินัล", "commandAutocomplete": "การเติมคำสั่งอัตโนมัติ", "commandAutocompleteDesc": "แสดงคำแนะนำอัตโนมัติขณะพิมพ์", + "keyboardShortcuts": "แป้นพิมพ์ลัด", + "keyboardShortcutsDescription": "ปรับแต่งการคัดลอก วาง และปุ่มลัดคำสั่งในเทอร์มินัล", + "manageShortcuts": "จัดการ", "terminalLinkBehavior": "คลิกลิงก์เทอร์มินัล", "terminalLinkBehaviorDesc": "พฤติกรรมเริ่มต้นเมื่อคลิกลิงก์ในเทอร์มินัล", "historyTracking": "การติดตามประวัติ", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "ใส่กุญแจของคุณลงไปด้วย", "apiKeyUsageHintHeader": "ส่วนหัว", "apiKeyPermissionsHint": "คีย์จะได้รับสิทธิ์การใช้งานจากผู้ใช้ที่สร้างคีย์นั้นขึ้นมา", + "exportData": "ส่งออกข้อมูลของฉัน", + "exportDataDesc": "ดาวน์โหลดไฟล์สำรองข้อมูลโฮสต์ ข้อมูลประจำตัว และการตั้งค่าของคุณเพื่อถ่ายโอนไปยังอุปกรณ์อื่น", + "export": "ส่งออก", + "exporting": "กำลังส่งออก...", + "importData": "นำเข้าข้อมูลของฉัน", + "importDataDesc": "กู้คืนข้อมูลโฮสต์ ข้อมูลประจำตัว และการตั้งค่าของคุณจากไฟล์สำรองข้อมูล .sqlite", + "importDataSelected": "เลือกแล้ว: {{name}}", + "selectFile": "เลือกไฟล์", + "changeFile": "เปลี่ยน", + "import": "นำเข้า", + "importing": "กำลังนำเข้า...", + "exportSuccess": "ส่งออกข้อมูลสำเร็จแล้ว", + "exportFailed": "การส่งออกข้อมูลล้มเหลว", + "importSelectFile": "โปรดเลือกไฟล์ก่อน", + "importCompleted": "การนำเข้าเสร็จสมบูรณ์: นำเข้า {{total}} รายการ ข้าม {{skipped}} รายการ", + "importFailed": "การนำเข้าล้มเหลว: {{error}}", "roleUser": "ผู้ใช้", "authMethodDual": "การตรวจสอบสิทธิ์แบบคู่", "authMethodOidc": "โอไอดีซี", @@ -3379,6 +3591,7 @@ "pageUp": "พีจีอัพ", "pageDown": "พีจีดีเอ็น", "delete": "เดล", + "paste": "แปะ", "editQuickKeys": "แก้ไขปุ่มลัด", "quickKeysTitle": "ปุ่มลัด", "quickKeysDesc": "แตะ × เพื่อลบ รองรับได้สูงสุด 8 ตัวอักษร", diff --git a/src/ui/locales/translated/tr_TR.json b/src/ui/locales/translated/tr_TR.json index 4b384670..e89ece0b 100644 --- a/src/ui/locales/translated/tr_TR.json +++ b/src/ui/locales/translated/tr_TR.json @@ -1,358 +1,358 @@ { "termixId": { "title": "Termix ID", - "loadFailed": "Termix ID yüklenemedi.", - "claimTitle": "Termix kimliğinizi alın.", - "claimIntro": "Benzersiz bir kullanıcı adı seçin. SSH genel anahtarlarınız, herhangi bir sunucunun authorized_keys dosyasına ekleyebileceğiniz herkese açık bir URL'de yayınlanacaktır.", - "handleLabel": "Halletmek", - "handlePlaceholder": "Alice", - "checking": "… kontrol ediliyor", - "available": "Mevcut", - "taken": "Zaten alındı", - "invalidHandle": "Sadece küçük harfler, rakamlar, - ve _ karakterleri.", + "loadFailed": "Termix ID yüklenemedi", + "claimTitle": "Termix ID'nizi alın", + "claimIntro": "Benzersiz bir kullanıcı adı seçin. SSH genel anahtarlarınız, herhangi bir sunucunun authorized_keys dosyasına ekleyebileceğiniz herkese açık bir URL'de yayımlanacaktır.", + "handleLabel": "Kullanıcı Adı", + "handlePlaceholder": "alice", + "checking": "Kontrol ediliyor…", + "available": "Kullanılabilir", + "taken": "Zaten alınmış", + "invalidHandle": "Yalnızca küçük harfler, rakamlar, - ve _ kullanılabilir", "descriptionLabel": "Açıklama (isteğe bağlı)", - "descriptionPlaceholder": "İş dizüstü bilgisayarı ve telefon tuşları", - "create": "Termix Kimliği Oluştur", + "descriptionPlaceholder": "İş bilgisayarı ve telefon anahtarları", + "create": "Termix ID Oluştur", "created": "Termix ID oluşturuldu", - "createFailed": "Termix kimliği oluşturulamadı.", - "deleteConfirm": "Termix kimliğinizi ve yayınlanmış tüm anahtarları silmek mi istiyorsunuz? Sağladığınız sunucular, siz manuel olarak kaldırmadığınız sürece anahtarları saklayacaktır.", - "deleted": "Termix kimliği silindi", - "deleteFailed": "Termix ID'sini silme işlemi başarısız oldu.", - "copyFailed": "Kopyalama başarısız oldu", - "resolverUrlLabel": "Genel çözümleyici URL'si", - "provisionLabel": "Bir sunucu tahsis edin", - "publishTitle": "Herkese açık bir anahtar yayınlayın.", + "createFailed": "Termix ID oluşturulamadı", + "deleteConfirm": "Termix ID'nizi ve yayımlanmış tüm anahtarları silmek istediğinize emin misiniz? Hazırladığınız sunucular, anahtarları siz manuel olarak kaldırana kadar saklayacaktır.", + "deleted": "Termix ID silindi", + "deleteFailed": "Termix ID silinemedi", + "copyFailed": "Kopyalama başarısız", + "resolverUrlLabel": "Genel çözümleyici URL", + "provisionLabel": "Sunucu hazırla", + "publishTitle": "Genel anahtar yayımla", "generate": "Oluştur", - "generateTooltip": "Ed25519 anahtar çifti oluşturun — genel anahtarı yayınlar ve özel anahtarı cihazınıza indirir.", + "generateTooltip": "Ed25519 anahtar çifti oluştur — genel anahtarı yayımlar ve özel anahtarı cihazınıza indirir", "saveToVault": "Kimlik Bilgilerine Kaydet", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Etiket (isteğe bağlı)", - "add": "Eklemek", - "importFromCredential": "Veya kayıtlı bir kimlik bilgisinden içe aktarın.", - "keyPublished": "Anahtar yayınlandı", - "addKeyFailed": "Anahtar ekleme başarısız oldu.", - "generatedSaved": "Anahtar çifti oluşturuldu ve kimlik bilgilerinizin bulunduğu kasaya kaydedildi. Özel anahtar da indirildi.", + "add": "Ekle", + "importFromCredential": "Veya kayıtlı bir kimlik bilgisinden içe aktar", + "keyPublished": "Anahtar yayımlandı", + "addKeyFailed": "Anahtar eklenemedi", + "generatedSaved": "Anahtar çifti oluşturuldu ve kimlik bilgileri kasanıza kaydedildi. Özel anahtar da indirildi.", "generatedOnly": "Anahtar çifti oluşturuldu — özel anahtar indirildi (yalnızca bir kez gösterilir).", - "generateFailed": "Anahtar oluşturulamadı.", - "imported": "Kimlik bilgilerinden içe aktarılan anahtar", - "importFailed": "Anahtar içe aktarılamadı.", - "noKeys": "Henüz hiçbir açık anahtar yayınlanmadı.", + "generateFailed": "Anahtar oluşturulamadı", + "imported": "Anahtar kimlik bilgisinden içe aktarıldı", + "importFailed": "Anahtar içe aktarılamadı", + "noKeys": "Henüz yayınlanmış genel anahtar yok.", "keysTitle": "Yayınlanan anahtarlar", "published": "Yayınlandı", - "hidden": "Gizlenmiş", - "keyRemoved": "Anahtar çıkarıldı", - "removeKeyFailed": "Anahtarı kaldırma başarısız oldu.", - "updateKeyFailed": "Anahtar güncellenemedi.", + "hidden": "Gizli", + "keyRemoved": "Anahtar kaldırıldı", + "removeKeyFailed": "Anahtar kaldırılamadı", + "updateKeyFailed": "Anahtar güncellenemedi", "fromVault": "Kimlik bilgileri kasasından", - "linkedToTermixId": "Termix ID aracılığıyla yayınlandı", - "selectCredential": "Bir yeterlilik belgesi seçin...", - "import": "İçe aktarmak", + "linkedToTermixId": "Termix ID ile yayınlandı", + "selectCredential": "Bir kimlik bilgisi seçin...", + "import": "İçe aktar", "caTitle": "Sertifika yetkilisi", - "caIntro": "Bir sunucuda bu CA'ya güvenin ve imzaladığınız herhangi bir sertifikayı kabul edecektir. Her şeyi aynı anda iptal etmek için döndürün; sertifikalar da kendiliğinden sona erer.", - "caEnable": "CA'yı etkinleştirin", + "caIntro": "Bu CA'yı bir sunucuda güvenilir yapın, imzaladığınız tüm sertifikaları kabul eder. Tek seferde tümünü iptal etmek için döndürün; sertifikalar ayrıca kendiliğinden süresi dolar.", + "caEnable": "CA'yı etkinleştir", "caEnabled": "CA etkinleştirildi", "caCreateFailed": "CA etkinleştirilemedi", "caPublicKeyLabel": "CA genel anahtarı", - "caTrustLabel": "Sunucuya güvenin (root olarak çalıştırın)", + "caTrustLabel": "Sunucuda güven (root olarak çalıştırın)", "caRotate": "Döndür", - "caRotateConfirm": "Sertifika yetkilisi değiştirilecek mi? İmzaladığı her sertifika artık kabul edilmeyecek ve yeniden düzenlenmesi gerekecektir.", - "caRotated": "CA değiştirildi — önceki sertifikalar iptal edildi", - "caRotateFailed": "CA'yı döndürme başarısız oldu.", - "caDelete": "CA'yı Kaldır", - "caDeleteConfirm": "Sertifika yetkilisini kaldırmak mı?", + "caRotateConfirm": "CA'yı döndürmek istediğinize emin misiniz? İmzaladığı tüm sertifikalar kabul edilmeyecek ve yeniden düzenlenmeleri gerekecek.", + "caRotated": "CA döndürüldü — önceki sertifikalar iptal edildi", + "caRotateFailed": "CA döndürülemedi", + "caDelete": "CA'yı kaldır", + "caDeleteConfirm": "Sertifika yetkilisi kaldırılsın mı?", "caDeleted": "CA kaldırıldı", - "caDeleteFailed": "CA'yı kaldırma işlemi başarısız oldu.", - "caValidityLabel": "Sertifika geçerlilik süresi (gün)", + "caDeleteFailed": "CA kaldırılamadı", + "caValidityLabel": "Sertifika geçerliliği (gün)", "issueCert": "Sertifika", - "issueCertTooltip": "Bu anahtar için, sertifika yetkiliniz (CA) tarafından imzalanmış bir SSH sertifikası düzenleyin.", - "certIssued": "Sertifika düzenlendi ve indirildi.", - "certIssueFailed": "Sertifika düzenleme başarısız oldu." + "issueCertTooltip": "Bu anahtar için CA'nız tarafından imzalanmış bir SSH sertifikası düzenleyin", + "certIssued": "Sertifika düzenlendi ve indirildi", + "certIssueFailed": "Sertifika düzenlenemedi" }, "credentials": { "folders": "Klasörler", - "folder": "Dosya", - "password": "Şifre", + "folder": "Klasör", + "password": "Parola", "key": "Anahtar", "sshPrivateKey": "SSH Özel Anahtarı", - "upload": "Yüklemek", + "upload": "Yükle", "keyPassword": "Anahtar Parolası", "sshKey": "SSH Anahtarı", "uploadPrivateKeyFile": "Özel Anahtar Dosyasını Yükle", - "searchCredentials": "Arama kimlik bilgileri...", + "searchCredentials": "Kimlik bilgilerini ara...", "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", + "caCertificateDescription": "İsteğe bağlı: CA imzalı sertifika dosyasını (ör. id_ed25519-cert.pub) yükleyin veya yapıştırın. SSH sunucunuz sertifika tabanlı yetkilendirme kullandığında gereklidir.", + "uploadCertFile": "-cert.pub Dosyasını Yükle", + "clearCert": "Temizle", "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.", + "pasteOrUploadCert": "Bir -cert.pub sertifikası yapıştırın veya yükleyin...", + "hasCaCert": "CA Sertifikası Var", "noCaCert": "CA Sertifikası Yok", - "noPublicKeyAvailable": "Herkese açık anahtar mevcut değil. Öncelikle kimlik bilgisi düzenleyicisini açın.", + "noPublicKeyAvailable": "Genel anahtar mevcut değil. Önce kimlik bilgisi düzenleyicisini açın.", "deployCommandCopied": "Dağıtım komutu kopyalandı", "sortCredentials": "Kimlik Bilgilerini Sırala", - "sortDefault": "Varsayılan Sipariş", - "sortNameAsc": "İsim (A → Z)", - "sortNameDesc": "İsim (Z → A)", + "sortDefault": "Varsayılan Sıra", + "sortNameAsc": "Ad (A → Z)", + "sortNameDesc": "Ad (Z → A)", "sortUsernameAsc": "Kullanıcı Adı (A → Z)", - "sortUsernameDesc": "Kullanıcı adı (Z → A)", - "filterCredentials": "Filtre Kimlik Bilgileri", + "sortUsernameDesc": "Kullanıcı Adı (Z → A)", + "filterCredentials": "Kimlik Bilgilerini Filtrele", "filterClearAll": "Filtreleri Temizle", - "filterTypeGroup": "Tip", - "filterTypePassword": "Şifre", + "filterTypeGroup": "Tür", + "filterTypePassword": "Parola", "filterTypeKey": "SSH Anahtarı", "filterTagsGroup": "Etiketler" }, "homepage": { "title": "Ana Sayfa", - "addWidget": "Widget Ekle", - "editWidget": "Widget'ı Düzenle", - "deleteWidget": "Silmek", + "addWidget": "Bileşen Ekle", + "editWidget": "Bileşeni Düzenle", + "deleteWidget": "Sil", "widgetTypes": "Widget Türleri", "serviceLink": "Servis Bağlantısı", - "folder": "Dosya", + "folder": "Klasör", "clock": "Saat", "notes": "Notlar", "hostStatus": "Sunucu Durumu", - "bookmarkList": "Yer işaretleri", + "bookmarkList": "Yer İmleri", "zoomIn": "Yakınlaştır", "zoomOut": "Uzaklaştır", "resetView": "Görünümü Sıfırla", - "lockLayout": "Kilit Düzeni", - "unlockLayout": "Düzeni Kilidi Aç", - "noWidgets": "İlk widget'ınızı eklemek için sağ tıklayın veya + işaretine tıklayın.", + "lockLayout": "Düzeni Kilitle", + "unlockLayout": "Düzen Kilidini Aç", + "noWidgets": "İlk widget'ınızı eklemek için sağ tıklayın veya + simgesine tıklayın", "openFullView": "Tam Görünümü Aç", "previewTitle": "Ana Sayfa Önizlemesi", - "cancel": "İptal etmek", - "save": "Kaydetmek", + "cancel": "İptal", + "save": "Kaydet", "title_label": "Başlık", "widgetTitlePlaceholder": "Widget başlığı (isteğe bağlı)", "url": "URL", "imageUrl": "Özel Resim URL'si", - "imageUrlHint": "Site simgesini otomatik olarak kullanmak için boş bırakın.", + "imageUrlHint": "Site favicon'unu otomatik olarak kullanmak için boş bırakın", "showImage": "Resmi Göster", - "description": "Tanım", + "description": "Açıklama", "color": "Renk", "icon": "Simge", "expanded": "Varsayılan olarak genişletilmiş", - "timezone": "Saat dilimi", + "timezone": "Zaman Dilimi", "showSeconds": "Saniyeleri göster", - "format12h": "12 saat", - "format24h": "24 saat", + "format12h": "12 saatlik", + "format24h": "24 saatlik", "content": "İçerik", "backgroundColor": "Arka Plan Rengi", - "host": "Ev sahibi", - "showMetrics": "Ölçümleri Göster", + "host": "Sunucu", + "showMetrics": "Metrikleri Göster", "links": "Bağlantılar", "addLink": "Bağlantı Ekle", "linkLabel": "Etiket", "linkUrl": "URL", - "removeLink": "Kaldırmak", + "removeLink": "Kaldır", "categoryLinks": "Bağlantılar", "categoryInfo": "Bilgi", "categorySystem": "Sistem", - "widgetServiceLinkDesc": "Hizmet URL'sine bağlantı veren tıklanabilir bir kutucuk.", - "widgetFolderDesc": "Birbiriyle ilişkili bileşenleri gruplandırmak için bir kapsayıcı.", - "widgetClockDesc": "Ayarlanabilir saat dilimine sahip canlı saat", - "widgetNotesDesc": "Markdown notları widget'ı", - "widgetHostStatusDesc": "SSH sunucusunun canlı CPU, bellek ve disk kullanımını gösterir.", - "widgetBookmarkListDesc": "Hızlı bağlantılar listesi", + "widgetServiceLinkDesc": "Bir servis URL'sine bağlanan tıklanabilir bir döşeme", + "widgetFolderDesc": "İlgili widget'ları gruplandırmak için bir kapsayıcı", + "widgetClockDesc": "Yapılandırılabilir saat dilimine sahip canlı bir saat", + "widgetNotesDesc": "Markdown notlar widget'ı", + "widgetHostStatusDesc": "Bir SSH sunucusu için canlı CPU, bellek ve disk kullanımını gösterir", + "widgetBookmarkListDesc": "Hızlı bağlantıların bir listesi", "copyLink": "Bağlantıyı Kopyala", "linkCopied": "Bağlantı kopyalandı!", "location": "Konum", "temperatureUnit": "Sıcaklık Birimi", - "showForecast": "3 günlük hava tahminini göster", + "showForecast": "3 günlük tahmini göster", "scrolling": "Kaydırmaya İzin Ver", - "feedUrl": "Besleme URL'si", - "maxItems": "Maksimum Ürün Sayısı", + "feedUrl": "Akış URL'si", + "maxItems": "Maksimum Öğe Sayısı", "showDescription": "Açıklamayı Göster", - "widgetWeatherName": "Hava durumu", + "widgetWeatherName": "Hava Durumu", "widgetWeatherDesc": "Herhangi bir konum için canlı hava durumu", - "widgetIframeName": "iFrame Gömme", - "widgetIframeDesc": "Herhangi bir URL'yi bir iframe içine yerleştirin.", - "widgetRssName": "RSS Beslemesi", + "widgetIframeName": "iFrame Yerleştirme", + "widgetIframeDesc": "Herhangi bir URL'yi iframe içine göm", + "widgetRssName": "RSS Akışı", "widgetRssDesc": "RSS veya Atom akışından öğeleri görüntüle", - "dragToFolder": "Widget'ları buraya sürükleyin veya eklemek için + işaretini kullanın.", + "dragToFolder": "Widget'ları buraya sürükleyin veya eklemek için + kullanın", "showDisk": "Disk Kullanımını Göster", "addToFolder": "Widget'ı klasöre ekle", - "noHostSelected": "Sunucu seçilmedi.", - "metricsNotAvailable": "Ölçümler mevcut değil.", + "noHostSelected": "Sunucu seçilmedi", + "metricsNotAvailable": "Metrikler kullanılamıyor", "selectHost": "Bir sunucu seçin...", - "displayedMetrics": "Görüntülenen Metrikler", - "metricCpu": "İşlemci", - "metricMemory": "Hafıza", + "displayedMetrics": "Gösterilen Metrikler", + "metricCpu": "CPU", + "metricMemory": "Bellek", "metricDisk": "Disk", - "metricUptime": "Çalışma süresi", + "metricUptime": "Çalışma Süresi", "metricSystem": "Sistem", - "metricOs": "İşletim sistemi", + "metricOs": "İşletim Sistemi", "metricKernel": "Çekirdek", - "metricHostname": "Ana bilgisayar adı", + "metricHostname": "Ana Bilgisayar Adı", "metricNetwork": "Ağ", - "metricProcesses": "Süreçler", + "metricProcesses": "İşlemler", "metricProcessesTotal": "toplam", - "metricProcessesRunning": "koşma", + "metricProcessesRunning": "çalışan", "categoryMonitoring": "İzleme", "loading": "Yükleniyor...", - "noData": "Veri mevcut değil.", - "allClear": "Her şey yolunda", - "acknowledgeAlert": "Kabullenmek", - "noPingUrls": "Hiçbir URL yapılandırılmadı.", + "noData": "Veri yok", + "allClear": "Her şey normal", + "acknowledgeAlert": "Onayla", + "noPingUrls": "Hiçbir URL yapılandırılmamış", "pingLabel": "Etiket", - "addPingUrl": "URL ekle", + "addPingUrl": "URL Ekle", "showLatency": "Gecikmeyi Göster", "refreshInterval": "Yenileme Aralığı", - "seconds": "saniyeler", + "seconds": "saniye", "filterActivityTypes": "Filtre Türleri", - "filterTypesHint": "Tümünü göstermek için boş bırakın.", - "showTimestamp": "Zaman damgasını göster", - "uptimeUnavailable": "Çalışma süresi bilgisi mevcut değil.", - "uptimeLabel": "Çalışma süresi", + "filterTypesHint": "Tümünü göstermek için boş bırakın", + "showTimestamp": "Zaman Damgasını Göster", + "uptimeUnavailable": "Çalışma süresi kullanılamıyor", + "uptimeLabel": "Çalışma Süresi", "overviewVersion": "Sürüm", "overviewUpdate": "Güncel", - "overviewUpdateAvailable": "Güncelleme mevcut.", + "overviewUpdateAvailable": "Güncelleme mevcut", "overviewDatabase": "Veritabanı", - "overviewUptime": "Çalışma süresi", - "noHosts": "Yapılandırılmış sunucu yok", - "hostGridHosts": "Ev sahipleri", - "hostGridAllHint": "Tüm sunucuları göstermek için boş bırakın.", + "overviewUptime": "Çalışma Süresi", + "noHosts": "Hiçbir sunucu yapılandırılmamış", + "hostGridHosts": "Sunucular", + "hostGridAllHint": "Tüm sunucuları göstermek için boş bırakın", "columns": "Sütunlar", "showIp": "IP Adresini Göster", "connectionType": "Bağlantı Türü", "layout": "Düzen", "showStatus": "Durumu Göster", "showHostName": "Sunucu Adını Göster", - "noDockerActivity": "Docker etkinliği yok.", - "noActivity": "Hiçbir etkinlik yok.", - "showAcknowledged": "Onaylandı Göster", - "showCurrentValue": "Mevcut Değeri Göster", + "noDockerActivity": "Docker etkinliği yok", + "noActivity": "Etkinlik yok", + "showAcknowledged": "Onaylananları Göster", + "showCurrentValue": "Geçerli Değeri Göster", "chartMetric": "Metrik", - "metricRange": "Menzil", - "widgetMetricsChartName": "Metrikler Tablosu", - "widgetMetricsChartDesc": "Bir sunucuya ait geçmişe dönük CPU, bellek, disk veya ağ grafiği.", - "widgetHostGridName": "Ana Bilgisayar Izgarası", - "widgetHostGridDesc": "SSH sunucu durumlarının tablo görünümü", + "metricRange": "Aralık", + "widgetMetricsChartName": "Metrik Grafiği", + "widgetMetricsChartDesc": "Bir sunucu için geçmiş CPU, bellek, disk veya ağ grafiği", + "widgetHostGridName": "Sunucu Izgarası", + "widgetHostGridDesc": "SSH sunucu durumlarının ızgara görünümü", "widgetAlertFeedName": "Uyarı Akışı", - "widgetAlertFeedDesc": "Onay desteğiyle canlı uyarı tetiklemeleri.", + "widgetAlertFeedDesc": "Onay desteği ile canlı uyarı tetiklemeleri", "widgetPingStatusName": "Ping Durumu", "widgetPingStatusDesc": "Bir veya daha fazla URL için HTTP ping durumu", - "widgetRecentActivityName": "Son Aktiviteler", - "widgetRecentActivityDesc": "Termix'in son etkinliklerinin kaydırılabilir akışı.", + "widgetRecentActivityName": "Son Etkinlikler", + "widgetRecentActivityDesc": "Son Termix etkinliklerinin kaydırılabilir akışı", "widgetTermixUptimeName": "Termix Çalışma Süresi", "widgetTermixUptimeDesc": "Termix sunucusu için canlı çalışma süresi sayacı", - "widgetSystemOverviewName": "Sistem Genel Bakışı", - "widgetSystemOverviewDesc": "Termix sürümü, veritabanı durumu ve çalışma süresi hakkında genel bilgiler.", + "widgetSystemOverviewName": "Sistem Özeti", + "widgetSystemOverviewDesc": "Termix sürümü, veritabanı sağlığı ve çalışma süresi bir bakışta", "widgetSshQuickConnectName": "SSH Hızlı Bağlantı", - "widgetSshQuickConnectDesc": "SSH oturumlarını açmak için tek tıklamalık düğmeler.", + "widgetSshQuickConnectDesc": "SSH oturumları açmak için tek tıklamalı düğmeler", "widgetDockerActivityName": "Docker Etkinliği", - "widgetDockerActivityDesc": "Tüm sunucularda son zamanlarda meydana gelen Docker konteyner olayları", + "widgetDockerActivityDesc": "Tüm sunuculardaki son Docker konteyner olayları", "widgetCalendarName": "Takvim", - "widgetCalendarDesc": "Bugünün gününün vurgulandığı aylık takvim.", - "widgetCountdownName": "Geri sayım", + "widgetCalendarDesc": "Bugünün vurgulandığı aylık takvim", + "widgetCountdownName": "Geri Sayım", "widgetCountdownDesc": "Hedef tarihe geri sayım sayacı", "widgetSearchBarName": "Arama Çubuğu", - "widgetSearchBarDesc": "Hızlı web arama aracı", - "widgetTextBannerName": "Metin Afişi", - "widgetTextBannerDesc": "Tuval için kalın yazılmış bir etiket veya bölüm başlığı.", - "widgetImageWidgetName": "Görüntü", - "widgetImageWidgetDesc": "Bir URL'den resim görüntüle", + "widgetSearchBarDesc": "Hızlı web araması aracı", + "widgetTextBannerName": "Metin Başlığı", + "widgetTextBannerDesc": "Tuval için kalın etiket veya bölüm başlığı", + "widgetImageWidgetName": "Görsel", + "widgetImageWidgetDesc": "Bir URL'den görsel göster", "widgetMarkdownNotesName": "Markdown Notları", - "widgetMarkdownNotesDesc": "Satır içi Markdown görüntüleme özelliğine sahip zengin notlar", + "widgetMarkdownNotesDesc": "Satır içi markdown işlemeli zengin notlar", "widgetCustomApiName": "Özel API", - "widgetCustomApiDesc": "Herhangi bir JSON API'sinden veri alın ve görüntüleyin.", - "widgetServiceGridName": "Hizmet Şebekesi", - "widgetServiceGridDesc": "Yapılandırılabilir bir hizmet kutusu bağlantı ızgarası", - "widgetDashboardLinksName": "Kontrol Paneli Bağlantıları", - "widgetDashboardLinksDesc": "Kontrol panelinden yapılandırılmış servis bağlantılarınızı görüntüleyin.", + "widgetCustomApiDesc": "Herhangi bir JSON API'den veri al ve göster", + "widgetServiceGridName": "Servis Izgarası", + "widgetServiceGridDesc": "Özelleştirilebilir servis kutucukları ızgarası", + "widgetDashboardLinksName": "Pano Bağlantıları", + "widgetDashboardLinksDesc": "Panodan yapılandırdığınız servis bağlantılarını göster", "widgetSearchLinksName": "Arama Kısayolları", - "widgetSearchLinksDesc": "Satır içi giriş özelliğine sahip hızlı arama kısayol düğmeleri", + "widgetSearchLinksDesc": "Satır içi girişli hızlı arama kısayol düğmeleri", "widgetLinkTreeName": "Bağlantı Ağacı", - "widgetLinkTreeDesc": "Başlıklar içeren gruplandırılmış bağlantı bölümleri", - "calMon": "Mo", - "calTue": "Salı", - "calWed": "Biz", - "calThu": "Bu", - "calFri": "Fr", - "calSat": "Sa", - "calSun": "Su", - "startOnMonday": "Haftaya Pazartesi günü başlayın", - "countdownNoDate": "Hedef tarih belirlenmedi.", - "countdownPast": "Etkinlik sona erdi.", - "countdownDays": "günler", - "countdownHours": "saat", - "countdownMinutes": "dakika", - "countdownSeconds": "saniye", + "widgetLinkTreeDesc": "Başlıklarla gruplandırılmış bağlantı bölümleri", + "calMon": "Pzt", + "calTue": "Sal", + "calWed": "Çar", + "calThu": "Per", + "calFri": "Cum", + "calSat": "Cmt", + "calSun": "Paz", + "startOnMonday": "Haftayı Pazartesi başlat", + "countdownNoDate": "Hedef tarih ayarlanmadı", + "countdownPast": "Etkinlik geçmişte kaldı", + "countdownDays": "gün", + "countdownHours": "sa", + "countdownMinutes": "dk", + "countdownSeconds": "sn", "countdownLabel": "Etiket", - "countdownLabelPlaceholder": "Örneğin Lansman Günü", - "countdownShowDays": "Gösteri Günleri", - "countdownShowHours": "Gösteri Saatleri", - "targetDate": "Hedef tarih", + "countdownLabelPlaceholder": "Örn. Lansman Günü", + "countdownShowDays": "Günleri Göster", + "countdownShowHours": "Saatleri Göster", + "targetDate": "Hedef Tarih", "searchEngine": "Arama Motoru", "customSearchUrl": "Özel Arama URL'si", - "searchPlaceholder": "Aramak...", - "searchGo": "Gitmek", - "searchPlaceholderLabel": "Yer tutucu metin", - "searchPlaceholderHint": "Arama kutusuna gösterilen metin", - "openInNewTab": "Yeni sekmede aç", + "searchPlaceholder": "Ara...", + "searchGo": "Git", + "searchPlaceholderLabel": "Yer Tutucu Metin", + "searchPlaceholderHint": "Arama girişinde gösterilen metin", + "openInNewTab": "Yeni Sekmede Aç", "searchQueryPlaceholder": "Sorgu girin...", - "noSearchShortcuts": "Hiçbir kısayol yapılandırılmadı.", + "noSearchShortcuts": "Hiç kısayol yapılandırılmadı", "addSearchShortcut": "Kısayol Ekle", "fontSize": "Yazı Tipi Boyutu", "textAlign": "Metin Hizalama", "fontWeight": "Yazı Tipi Kalınlığı", - "clearColor": "Şeffaf Renk", - "imageFit": "Görüntü Uygunluğu", + "clearColor": "Rengi Temizle", + "imageFit": "Görüntü Sığdırma", "imageLinkUrl": "Bağlantı URL'si", - "noImage": "Resim URL'si ayarlanmadı.", + "noImage": "Görüntü URL'si ayarlanmadı", "altText": "Alternatif Metin", - "altTextPlaceholder": "Görüntüyü tanımlayın.", - "renderMarkdown": "Markdown'ı Oluştur", - "displayMode": "Ekran Modu", + "altTextPlaceholder": "Görüntüyü tanımlayın", + "renderMarkdown": "Markdown'ı Görüntüle", + "displayMode": "Görüntüleme Modu", "displayField": "Görüntüleme Alanı", "jsonPath": "JSON Yolu", "customApiLabel": "Etiket", - "customApiLabelPlaceholder": "Örneğin Sıcaklık", + "customApiLabelPlaceholder": "Örn. Sıcaklık", "customApiUnit": "Birim", - "customApiNoUrl": "Hiçbir API URL'si yapılandırılmadı.", - "customApiError": "Alınamadı", - "customApiNotArray": "Yanıt bir dizi değil.", - "addService": "Hizmet Ekle", + "customApiNoUrl": "API URL'si yapılandırılmadı", + "customApiError": "Getirme başarısız", + "customApiNotArray": "Yanıt bir dizi değil", + "addService": "Servis Ekle", "showLabels": "Etiketleri Göster", "iconSize": "Simge Boyutu", - "noDashboardLinks": "Hiçbir kontrol paneli bağlantısı yapılandırılmamış.", + "noDashboardLinks": "Hiçbir pano bağlantısı yapılandırılmamış", "noLimit": "Sınır yok", "sectionHeading": "Bölüm Başlığı", "addSection": "Bölüm Ekle", "compactMode": "Kompakt Mod", "showDetailed": "Ayrıntılı Göster", - "selectHosts": "Sunucuları Seçin", - "allHosts": "Tüm ev sahipleri", + "selectHosts": "Sunucuları Seç", + "allHosts": "Tüm sunucular", "listLayout": "Liste", "gridLayout": "Izgara", - "terminal": "terminal", + "terminal": "Terminal", "files": "Dosyalar", - "docker": "Liman işçisi", + "docker": "Docker", "range15m": "15 dakika", "range1h": "1 saat", "range6h": "6 saat", "range24h": "24 saat", - "metricNetRx": "İnternet İndirme", + "metricNetRx": "Net İndirme", "metricNetTx": "Net Yükleme", - "severityFilter": "Ciddiyet Filtresi", - "filterAll": "Tüm", + "severityFilter": "Önem Derecesi Filtresi", + "filterAll": "Tümü", "accentColor": "Vurgu Rengi", - "widgetSshTerminalName": "SSH Terminal", - "widgetSshTerminalDesc": "Yapılandırılmış bir sunucuya bağlı, satır içi bir SSH terminali.", - "sshTerminalNoHost": "Hiçbir sunucu yapılandırılmadı.", - "sshTerminalConnect": "Bağlamak", - "sshTerminalAutoConnect": "Yük altında otomatik bağlantı", + "widgetSshTerminalName": "SSH Terminali", + "widgetSshTerminalDesc": "Yapılandırılmış bir sunucuya bağlı satır içi SSH terminali", + "sshTerminalNoHost": "Hiçbir sunucu yapılandırılmamış", + "sshTerminalConnect": "Bağlan", + "sshTerminalAutoConnect": "Yüklendiğinde otomatik bağlan", "widgetQuickConnectName": "Hızlı Bağlantı", - "widgetQuickConnectDesc": "Sunucularınız genelinde her türlü bağlantı türü için tek tıklamayla başlatma düğmeleri.", + "widgetQuickConnectDesc": "Sunucularınızda herhangi bir bağlantı türü için tek tıklamayla başlatma düğmeleri", "connectionTypes": "Bağlantı Türleri", - "connType_terminal": "terminal", + "connType_terminal": "Terminal", "connType_files": "Dosya Yöneticisi", - "connType_docker": "Liman işçisi", + "connType_docker": "Docker", "connType_tunnel": "Tünel", "connType_host-metrics": "Sunucu Metrikleri", "connType_rdp": "RDP", @@ -361,155 +361,194 @@ "widgetFileManagerName": "Dosya Yöneticisi", "widgetFileManagerDesc": "Yapılandırılmış bir sunucu için gömülü SFTP dosya yöneticisi", "widgetDockerName": "Docker Yöneticisi", - "widgetDockerDesc": "Yapılandırılmış bir sunucu için gömülü Docker konteyner yöneticisi.", + "widgetDockerDesc": "Yapılandırılmış bir sunucu için gömülü Docker konteyner yöneticisi", "widgetTunnelName": "Tünel Yöneticisi", "widgetTunnelDesc": "Yapılandırılmış bir sunucu için gömülü SSH tünel yöneticisi", - "widgetNoHostSelected": "Hiçbir sunucu yapılandırılmadı." + "widgetNoHostSelected": "Hiçbir sunucu yapılandırılmadı" }, "serverConfig": { "title": "Sunucu Yapılandırması", - "description": "Termix sunucu URL'sini arka uç servislerinize bağlanacak şekilde yapılandırın.", + "description": "Arka uç hizmetlerinize bağlanmak için Termix sunucu URL'sini 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": "Kaydediliyor...", + "saveConfig": "Yapılandırmayı Kaydet", + "helpText": "Termix sunucunuzun çalıştığı URL'yi girin (ör. http://localhost:30001 veya https://sunucunuz.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ı olan güvenilir, kendi barındırdığınız sunucular için kullanın.", + "useEmbedded": "Yerel Sunucu Kullan", + "embeddedDesc": "Termix'i yerleşik yerel sunucu ile çalıştırın (uzak sunucuya gerek yok)", + "embeddedConnecting": "Yerel sunucuya bağlanılıyor...", + "embeddedNotReady": "Yerel sunucu henüz hazır değil. Lütfen biraz bekleyip tekrar deneyin.", + "localServer": "Yerel Sunucu", + "savedServers": "Kayıtlı Sunucular", + "noSavedServers": "Kayıtlı sunucu yok", + "removeServer": "Kaldır" + }, + "migrationNotice": { + "title": "Termix Desktop artık bağımsız olarak çalışıyor.", + "body1": "Bu uygulama eskiden doğrudan Termix sunucunuza bağlanıyordu. Artık tamamen kendi başına çalışacak şekilde yeniden tasarlandı; sunucuları ve kimlik bilgilerini yerel olarak depolayarak çevrimdışı çalışmaya devam ediyor. Kendi sunucunuzda barındırdığınız Termix sunucusuyla çift yönlü senkronizasyon artık isteğe bağlıdır.", + "body2": "Mevcut sunucularınız ve kimlik bilgileriniz hala {{url}}üzerindedir. Uzaktan Senkronizasyonu açın ve bunları geri getirmek ve ileride ikisinin de senkronize kalmasını sağlamak için o sunucuya yeniden bağlanın.", + "dismiss": "Şimdi değil", + "setUpSync": "Uzaktan Senkronizasyonu Ayarla" + }, + "remoteSync": { + "title": "Uzaktan Senkronizasyon", + "description": "İsteğe bağlı olarak, bu masaüstü uygulamasını kendi barındırdığınız bir Termix sunucusuna bağlayarak sunucularınızı, kimlik bilgilerinizi ve kod parçacıklarınızı cihazlar arasında senkronize edebilirsiniz. Uygulama, bağlantı kurup kurmamanızdan bağımsız olarak her zaman tamamen çevrimdışı çalışır.", + "notConnected": "Bağlı değil", + "connected": "Bağlı", + "connectedTo": "{{url}} ile bağlantılı", + "lastSynced": "Son senkronizasyon {{time}}", + "neverSynced": "Hiç senkronize edilmedi", + "syncError": "Senkronizasyon hatası: {{message}}", + "needsReauth": "Oturum açma süresi doldu", + "connectButton": "Sunucuya bağlan", + "disconnectButton": "Bağlantıyı kes", + "syncNowButton": "Şimdi Senkronize Et", + "syncing": "Senkronizasyon...", "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.", + "connectionTestFailed": "Belirtilen URL'deki Termix sunucusuna ulaşılamadı.", "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", "savedServers": "Kaydedilen Sunucular", - "noSavedServers": "Kaydedilmiş sunucu yok.", - "removeServer": "Kaldırmak" + "removeServer": "Kaldırmak", + "continueButton": "Devam etmek", + "cancelButton": "İptal etmek", + "signInTitle": "{{url}} adresine giriş yapın.", + "originTitle": "Bağlantı Kaynağı", + "originDescription": "SSH bağlantılarının varsayılan olarak nereden kaynaklanacağını seçin. Bu ayar her sunucu için geçersiz kılınabilir.", + "originLocal": "Bu cihaz (yerel ağ)", + "originRemote": "Uzak sunucu", + "bannerReconnect": "Yeniden bağlan", + "bannerMessage": "Uzaktan senkronizasyon için yeniden kimlik doğrulaması gerekiyor." }, "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.", + "error": "Sürüm Denetleme Hatası", + "checkFailed": "Güncellemeler denetlenemedi", + "upToDate": "Uygulama güncel", + "currentVersion": "Sürüm {{version}} kullanı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.", + "newVersionAvailable": "Yeni bir sürüm mevcut! Şu anda {{current}} sürümünü kullanı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ı", + "betaVersionDesc": "Şu anda {{current}} sürümünü kullanıyorsunuz, bu sürüm en son kararlı sürüm {{latest}}'den daha yeni.", + "releasedOn": "{{date}} tarihinde yayımlandı", "downloadUpdate": "Güncellemeyi İndir", "checking": "Güncellemeler kontrol ediliyor...", - "checkUpdates": "Güncellemeleri kontrol edin", + "checkUpdates": "Güncellemeleri Kontrol Et", "checkingUpdates": "Güncellemeler kontrol ediliyor...", "updateRequired": "Güncelleme Gerekli" }, "common": { - "close": "Kapalı", - "minimize": "En aza indirge", + "close": "Kapat", + "minimize": "Simge Durumuna Küçült", "online": "Çevrimiçi", "offline": "Çevrimdışı", - "unknown": "Bilinmiyor", - "continue": "Devam etmek", + "unknown": "Bilinmeyen", + "continue": "Devam", "maintenance": "Bakım", - "degraded": "Bozulmuş", + "degraded": "Kısıtlı", "error": "Hata", "warning": "Uyarı", "unsavedChanges": "Kaydedilmemiş değişiklikler", - "dismiss": "Azletmek", + "dismiss": "Kapat", "loading": "Yükleniyor...", "optional": "İsteğe bağlı", - "connect": "Bağlamak", + "connect": "Bağlan", "copied": "Kopyalandı", "connecting": "Bağlanıyor...", "updateAvailable": "Güncelleme Mevcut", "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 içerik bulunamadı.", - "cancel": "İptal etmek", + "openInNewTab": "Yeni Sekmede Aç", + "noReleases": "Sürüm Yok", + "updatesAndReleases": "Güncellemeler ve Sürümler", + "newVersionAvailable": "Yeni bir sürüm ({{version}}) mevcut.", + "failedToFetchUpdateInfo": "Güncelleme bilgisi alınamadı", + "preRelease": "Ön sürüm", + "noReleasesFound": "Sürüm bulunamadı.", + "cancel": "İptal", "username": "Kullanıcı adı", - "login": "Giriş yapmak", - "logout": "Çıkış yap", - "register": "Kayıt olmak", - "password": "Şifre", - "confirmPassword": "Şifreyi Onayla", + "login": "Giriş Yap", + "logout": "Çıkış Yap", + "register": "Kaydol", + "password": "Parola", + "confirmPassword": "Parolayı Onayla", "back": "Geri", - "save": "Kaydetmek", - "saving": "Tasarruf...", - "delete": "Silmek", - "rename": "Yeniden isimlendirmek", - "edit": "Düzenlemek", - "add": "Eklemek", - "confirm": "Onaylamak", - "no": "HAYIR", + "save": "Kaydet", + "saving": "Kaydediliyor...", + "delete": "Sil", + "rename": "Yeniden Adlandır", + "edit": "Düzenle", + "add": "Ekle", + "confirm": "Onayla", + "no": "Hayır", "or": "VEYA", - "next": "Sonraki", - "previous": "Öncesi", + "next": "İleri", + "previous": "Önceki", "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", + "backendReconnected": "Sunucu bağlantısı geri yüklendi", + "connectionDegraded": "Sunucu bağlantısı kesildi, yeniden bağlanılıyor…", + "reload": "Yeniden Yükle", + "remove": "Kaldır", + "create": "Oluştur", + "update": "Güncelle", "copy": "Kopyala", - "copyFailed": "Panoya kopyalama başarısız oldu.", - "maximize": "Maksimize et", - "restore": "Eski haline getirmek", - "of": "ile ilgili", + "copyFailed": "Panoya kopyalanamadı", + "maximize": "Büyüt", + "restore": "Geri Yükle", + "of": "/", "saved": "Kaydedildi", "deleted": "Silindi", - "deleteFailed": "Silme işlemi başarısız oldu.", - "saveFailed": "Kaydetme başarısız oldu", - "required": "Gerekli" + "deleteFailed": "Silinemedi", + "saveFailed": "Kaydedilemedi", + "required": "Zorunlu" }, "nav": { - "home": "Ev", - "terminal": "terminal", - "docker": "Liman işçisi", + "home": "Ana Sayfa", + "terminal": "Terminal", + "docker": "Docker", "tunnels": "Tüneller", "fileManager": "Dosya Yöneticisi", "serverStats": "Sunucu Metrikleri", "hostMetrics": "Sunucu Metrikleri", "admin": "Yönetici", - "termixId": "İD", + "termixId": "Kimlik", "userProfile": "Kullanıcı Profili", - "splitScreen": "Bölünmüş Ekran", - "confirmClose": "Bu aktif oturumu kapatmak ister misiniz?", - "close": "Kapalı", - "cancel": "İptal etmek", + "splitScreen": "Ekranı Böl", + "confirmClose": "Bu etkin oturum kapatılsın mı?", + "close": "Kapat", + "cancel": "İptal", "sshManager": "SSH Yöneticisi", - "cannotSplitTab": "Bu sekmeyi bölemezsiniz.", + "cannotSplitTab": "Bu sekme bölünemez", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Şifreyi Kopyala", + "copyPassword": "Parolayı 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", - "renameTab": "Sekmeyi yeniden adlandır", + "noPasswordAvailable": "Parola bulunamadı", + "failedToCopyPassword": "Parola kopyalanamadı", + "refreshTab": "Bağlantıyı Yenile", + "renameTab": "Sekmeyi Yeniden Adlandır", "openFileManager": "Dosya Yöneticisini Aç", - "dashboard": "Kontrol Paneli", + "dashboard": "Dashboard", "networkGraph": "Ağ Grafiği", - "tmuxMonitor": "Tmux Monitörü", + "tmuxMonitor": "Tmux İzleyicisi", "homepage": "Ana Sayfa", "quickConnect": "Hızlı Bağlantı", "sshTools": "SSH Araçları", - "history": "Tarih", - "sessionLogs": "Oturum Günlükleri", - "hosts": "Ev sahipleri", - "snippets": "Kısa bölümler", - "hostManager": "Sunucu Yöneticisi", + "history": "Geçmiş", + "sessionLogs": "Oturum Kayıtları", + "hosts": "Ana Bilgisayarlar", + "snippets": "Kod Parçacıkları", + "hostManager": "Ana Bilgisayar Yöneticisi", "credentials": "Kimlik Bilgileri", "connections": "Bağlantılar", "alerts": "Uyarılar", @@ -518,229 +557,235 @@ "roleUser": "Kullanıcı" }, "hosts": { - "hosts": "Ev sahipleri", - "noHosts": "SSH Sunucusu Yok", - "retry": "Tekrar dene", + "hosts": "Ana Bilgisayarlar", + "noHosts": "SSH Ana Bilgisayarı Yok", + "retry": "Yeniden Dene", "refresh": "Yenile", - "optional": "İsteğe bağlı", + "optional": "İsteğe Bağlı", "downloadSample": "Örnek İndir", "failedToDeleteHost": "{{name}} silinemedi", - "importSkipExisting": "İçe aktar (mevcut olanı atla)", + "importSkipExisting": "İçe Aktar (mevcutları atla)", "importSSHConfig": "SSH yapılandırmasından içe aktar", "connectionDetails": "Bağlantı Ayrıntıları", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Uzaktan Masaüstü", - "port": "Liman", - "username": "Kullanıcı adı", - "folder": "Dosya", + "remoteDesktop": "Uzak Masaüstü", + "port": "Bağlantı Noktası", + "username": "Kullanıcı Adı", + "folder": "Klasör", "tags": "Etiketler", - "pin": "Pin", - "addHost": "Sunucu Ekle", - "editHost": "Sunucuyu Düzenle", - "cloneHost": "Klon Ana Bilgisayar", + "pin": "Sabitle", + "addHost": "Ana Bilgisayar Ekle", + "editHost": "Ana Bilgisayarı Düzenle", + "cloneHost": "Ana Bilgisayarı Kopyala", "enableTerminal": "Terminali Etkinleştir", "enableTunnel": "Tüneli Etkinleştir", "enableFileManager": "Dosya Yöneticisini Etkinleştir", - "enableDocker": "Docker'ı etkinleştirin", + "enableDocker": "Docker'ı Etkinleştir", "defaultPath": "Varsayılan Yol", "connection": "Bağlantı", - "upload": "Yüklemek", - "authentication": "Kimlik doğrulama", - "password": "Şifre", + "upload": "Yükle", + "authentication": "Kimlik Doğrulama", + "password": "Parola", "key": "Anahtar", - "credential": "Kimlik belgesi", + "credential": "Kimlik Bilgisi", "none": "Hiçbiri", "sshPrivateKey": "SSH Özel Anahtarı", "keyType": "Anahtar Türü", "uploadFile": "Dosya Yükle", "tabGeneral": "Genel", "tabSsh": "SSH", - "tabTerminal": "terminal", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Tüneller", - "tabDocker": "Liman işçisi", + "tabDocker": "Docker", "tabFiles": "Dosyalar", "tabStats": "Sunucu Metrikleri", "tabHostMetrics": "Sunucu Metrikleri", "tabTelnet": "Telnet", "tabSharing": "Paylaşım", - "tabAuthentication": "Kimlik doğrulama", - "terminal": "terminal", + "tabAuthentication": "Kimlik Doğrulama", + "terminal": "Terminal", "tunnel": "Tünel", "fileManager": "Dosya Yöneticisi", "serverStats": "Sunucu Metrikleri", "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": "{{count}} ana bilgisayar(lar) \"{{folder}} \" konumuna taşındı", + "folderRenamed": "\"{{oldName}}\" klasörü başarıyla \"{{newName}}\" olarak yeniden adlandırıldı.", + "failedToRenameFolder": "Klasör yeniden adlandırılamadı.", + "movedToFolder": "{{count}} sunucu \"{{folder}}\" klasörüne taşındı.", "editHostTooltip": "Sunucuyu düzenle", - "statusChecks": "Durum Kontrolleri", - "metricsCollection": "Metriklerin Toplanması", + "statusChecks": "Durum Denetimleri", + "metricsCollection": "Ölçüm Toplama", "metricsInterval": "Ölçüm Toplama Aralığı", - "metricsIntervalDesc": "Sunucu istatistikleri ne sıklıkla toplanmalı (5 saniye - 1 saat)?", + "metricsIntervalDesc": "Sunucu istatistiklerinin ne sıklıkta toplanacağı (5s - 1h)", "behavior": "Davranış", "themePreview": "Tema Önizlemesi", "theme": "Tema", "fontFamily": "Yazı Tipi Ailesi", "fontSize": "Yazı Tipi Boyutu", - "letterSpacing": "Harf Aralığı", - "lineHeight": "Çizgi Yüksekliği", + "letterSpacing": "Harf Boşluğu", + "lineHeight": "Satır Yüksekliği", "cursorStyle": "İmleç Stili", - "cursorBlink": "İmleç Yanıp Sönmesi", + "cursorBlink": "İmleç Yanıp Sönme", "scrollbackBuffer": "Geri Kaydırma Tamponu", - "bellStyle": "Çan Stili", - "rightClickSelectsWord": "Sağ Tıklama Word Seçer", - "fastScrollModifier": "Hızlı Kaydırma Değiştirici", + "bellStyle": "Zil Stili", + "rightClickSelectsWord": "Sağ Tık Kelimeyi Seçer", + "fastScrollModifier": "Hızlı Kaydırma Değiştiricisi", "fastScrollSensitivity": "Hızlı Kaydırma Hassasiyeti", - "sshAgentForwarding": "SSH Aracısı Yönlendirmesi", + "sshAgentForwarding": "SSH Aracı Yönlendirme", "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.", - "oidcUsernameHint": "OIDC oturum açma adınızı değiştirmek için $oidc.preferred_username kullanın.", - "jumpHostChain": "Ana Bilgisayar Zincirini Atla", - "portKnocking": "Liman Vuruşu", - "addKnock": "Bağlantı Noktası Ekle", + "startupSnippet": "Başlangıç Parçacığı", + "selectSnippet": "Parçacık Seç", + "forceKeyboardInteractive": "Klavye Etkileşimini Zorunlu Kıl", + "overrideCredentialUsername": "Kimlik Kullanıcı Adını Geçersiz Kıl", + "overrideCredentialUsernameDesc": "Kimlik bilgisinin kullanıcı adı yerine yukarıda belirtilen kullanıcı adını kullan", + "oidcUsernameHint": "OIDC oturum adınızı kullanmak için $oidc.preferred_username yazın.", + "tailscaleUsernameHint": "Bu, Tailscale kimliğinizin tailnet'in SSH erişim kontrol listesinde (ACL) verildiği bir Unix kullanıcısı olmalıdır, mutlaka root olması gerekmez.", + "jumpHostChain": "Atlama Sunucu Zinciri", + "portKnocking": "Port Vuruşu", + "addKnock": "Port Ekle", "addProxyNode": "Düğüm Ekle", "proxyNode": "Proxy Düğümü", - "proxyType": "Vekil Türü", - "quickActions": "Hızlı İşlemler", + "proxyType": "Proxy Türü", + "quickActions": "Hızlı Eylemler", "sudoPasswordAutoFill": "Sudo Parolası Otomatik Doldurma", "sudoPassword": "Sudo Parolası", - "keepaliveInterval": "Bağlantıyı Sürdürme Aralığı (ms)", + "keepaliveInterval": "Canlı Tutma Aralığı (ms)", "moshCommand": "MOSH Komutu", - "environmentVariables": "Çevresel Değişkenler", + "environmentVariables": "Ortam Değişkenleri", "addVariable": "Değişken Ekle", - "docker": "Liman işçisi", + "docker": "Docker", "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ıç", + "failedToConnect": "Konsola bağlanılamadı", + "connect": "Bağlan", + "disconnect": "Bağlantıyı Kes", + "start": "Başlat", "enableStatusCheck": "Durum Kontrolünü Etkinleştir", - "enableMetrics": "Ölçümleri Etkinleştir", + "enableMetrics": "Metrikleri Etkinleştir", "bulkUpdateFailed": "Toplu güncelleme başarısız oldu", "selectAll": "Tümünü Seç", - "deselectAll": "Tümünü Seçimi Kaldır", + "deselectAll": "Tüm Seçimi Kaldır", "protocols": "Protokoller", - "secureShell": "Güvenli Kabuk", + "secureShell": "Secure Shell", "virtualNetwork": "Sanal Ağ", - "unencryptedShell": "Şifrelenmemiş kabuk", + "unencryptedShell": "Şifrelenmemiş Shell", "addressIp": "Adres / IP", - "friendlyName": "Dostça İsim", + "friendlyName": "Kolay Ad", "macAddress": "MAC Adresi", - "wolBroadcastAddress": "WoL Yayın Konuşması", - "wolBroadcastAddressDesc": "Docker/yönlendirilmiş ağlar için isteğe bağlı yönlendirilmiş yayın (örneğin 192.168.1.255). 255.255.255.255 kullanmak için boş bırakın.", + "wolBroadcastAddress": "WoL Yayın Adresi", + "wolBroadcastAddressDesc": "Docker/yönlendirilmiş ağlar için isteğe bağlı yönlendirilmiş yayın (ör. 192.168.1.255). 255.255.255.255 kullanmak için boş bırakın.", "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ı", + "privateNotesPlaceholder": "Bu sunucuyla ilgili ayrıntılar...", + "pinToTop": "Üste Sabitle", + "pinToTopDesc": "Bu sunucuyu her zaman listenin en üstünde göster", + "portKnockingSequence": "Port Knocking Sırası", + "addKnockBtn": "Knock Ekle", + "noPortKnocking": "Port knocking yapılandırılmamış.", + "knockPort": "Knock Portu", "protocol": "Protokol", - "delayAfterMs": "Gecikme Sonrası (ms)", - "useSocks5Proxy": "SOCKS5 Proxy kullanın", - "useSocks5ProxyDesc": "Bağlantıyı bir proxy sunucusu üzerinden yönlendirin.", + "delayAfterMs": "Gecikme (ms)", + "useSocks5Proxy": "SOCKS5 Proxy Kullan", + "useSocks5ProxyDesc": "Bağlantıyı bir proxy sunucusu üzerinden yönlendir", + "connectionOrigin": "Bağlantı Kaynağı", + "connectionOriginDesc": "Bu sunucunun SSH bağlantısının nereden kaynaklandığını belirtir. Masaüstü uygulamasının genel varsayılan ayarını geçersiz kılar.", + "connectionOriginDefault": "Varsayılanı kullan", + "connectionOriginLocal": "Bu cihaz (yerel ağ)", + "connectionOriginRemote": "Uzak sunucu", "proxyHost": "Proxy Sunucusu", - "proxyPort": "Proxy Port", + "proxyPort": "Proxy Portu", "proxyUsername": "Proxy Kullanıcı Adı", "proxyPassword": "Proxy Parolası", - "proxySingleMode": "Tek Vekil", - "proxyChainMode": "Vekil Zinciri", - "you": "Sen", - "jumpHostChainLabel": "Ana Bilgisayar Zincirini Atla", + "proxySingleMode": "Tek Proxy", + "proxyChainMode": "Proxy Zinciri", + "you": "Siz", + "jumpHostChainLabel": "Atlama Sunucusu Zinciri", "addJumpBtn": "Atlama Ekle", "noJumpHosts": "Hiçbir atlama sunucusu yapılandırılmamış.", "selectAServer": "Bir sunucu seçin...", - "sshPort": "SSH Bağlantı Noktası", + "sshPort": "SSH Portu", "authMethod": "Kimlik Doğrulama Yöntemi", - "storedCredential": "Kaydedilmiş Kimlik Bilgileri", - "selectACredential": "Bir yeterlilik belgesi seçin...", - "vaultProfile": "Kasa İmzalayıcı Profili", + "storedCredential": "Kayıtlı Kimlik", + "selectACredential": "Bir kimlik seçin...", + "vaultProfile": "Vault İmzalayıcı Profili", "selectAVaultProfile": "Bir Vault profili seçin...", - "vaultProfileHint": "Ayarlar paylaşılan profilden alınır; bağlandığınızda OIDC üzerinden Vault'a giriş yapacaksınız. Hiçbir gizli bilgi saklanmaz.", + "vaultProfileHint": "Ayarlar paylaşılan profilden gelir; bağlandığınızda OIDC ile Vault'ta oturum açarsınız. Hiçbir gizli bilgi saklanmaz.", "vaultNewProfile": "Yeni profil", - "vaultManageProfiles": "Vault profillerini yönetin", - "vaultAddrLabel": "Kasa Adresi", - "vaultNamespaceLabel": "Ad alanı", - "vaultOidcMountLabel": "OIDC Yetkilendirme Montajı", - "vaultOidcRoleLabel": "OIDC'nin Rolü", - "vaultSshMountLabel": "SSH Secrets Mount", + "vaultManageProfiles": "Vault Profillerini Yönet", + "vaultAddrLabel": "Vault Adresi", + "vaultNamespaceLabel": "Ad Alanı", + "vaultOidcMountLabel": "OIDC Kimlik Doğrulama Bağlama", + "vaultOidcRoleLabel": "OIDC Rolü", + "vaultSshMountLabel": "SSH Gizli Dizin Bağlama", "vaultSshRoleLabel": "SSH İmzalayıcı Rolü", - "vaultValidPrincipalsLabel": "Geçerli İlkeler", + "vaultValidPrincipalsLabel": "Geçerli Asıllar", "vaultKeyTypeLabel": "Geçici Anahtar Türü", - "vaultSharedLabel": "Tüm kullanıcılarla paylaş", + "vaultSharedLabel": "Tüm Kullanıcılarla Paylaş", "vaultCreateProfile": "Profil oluştur", - "vaultProfileCreated": "Kasa profili oluşturuldu", - "vaultProfileSaved": "Kasa profili kaydedildi.", - "vaultProfileDeleted": "Kasa profili silindi", - "vaultProfileSaveFailed": "Vault profilini kaydetme başarısız oldu.", - "vaultProfileDeleteFailed": "Vault profilini silme işlemi başarısız oldu.", + "vaultProfileCreated": "Vault profili oluşturuldu", + "vaultProfileSaved": "Vault profili kaydedildi", + "vaultProfileDeleted": "Vault profili silindi", + "vaultProfileSaveFailed": "Vault profili kaydedilemedi", + "vaultProfileDeleteFailed": "Vault profili silinemedi", "vaultSaveProfile": "Profili kaydet", - "vaultProfileValidationError": "İsim, Vault adresi ve SSH imzalayıcı rolü zorunludur.", + "vaultProfileValidationError": "Ad, Vault adresi ve SSH imzalama rolü gereklidir", "vaultNoProfiles": "Henüz Vault profili yok.", "vaultSharedBadge": "paylaşılan", "keyTypeLabel": "Anahtar Türü", - "keyTypeAuto": "Otomatik Algılama", + "keyTypeAuto": "Otomatik Algıla", "keyPasteTab": "Yapıştır", - "keyUploadTab": "Yüklemek", + "keyUploadTab": "Yükle", "keyFileLoaded": "Anahtar dosyası yüklendi", - "keyUploadClick": ".pem / .key / .ppk dosyalarını yüklemek için tıklayın.", + "keyUploadClick": ".pem / .key / .ppk 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", - "docsLink": "Belgeleri görüntüle", + "keyReplaceNotice": "Değiştirmek için aşağıya yeni bir anahtar yapıştırın", + "keyPassphraseSaved": "Parola kaydedildi, değiştirmek için yazın", + "replaceKey": "Anahtarı değiştir", + "docsLink": "Dokümanları görüntüle", "opksshLabel": "OPKSSH", - "opksshDesc": "Bu sunucuya parola veya anahtar yerine kimlik sağlayıcınızı kullanarak giriş yapın. Sunucuda OPKSSH kurulumu gereklidir.", - "warpgateLabel": "Warpgate Geçidi", - "warpgateDesc": "Bu sunucu, Warpgate SSH proxy'si üzerinden bağlanır. Termix, kimlik doğrulamasından sonra tarayıcı tabanlı onay akışını otomatik olarak halledecektir.", - "agentLabel": "SSH Aracısı", - "agentDesc": "Termix sunucusunda çalışan bir SSH aracısı (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) kullanarak kimlik doğrulaması yapın. Aracının, Termix'in barındırıldığı makinede çalışıyor olması gerekir.", - "agentSocketPathLabel": "Aracı Soket Yolu", - "agentSocketPathPlaceholder": "SSH_AUTH_SOCK kullanmak için boş bırakın.", - "agentSocketPathHint": "SSH_AUTH_SOCK ortam değişkeninden otomatik algılama için boş bırakın veya özel bir soket yolu girin (örneğin /run/user/1000/gnupg/S.gpg-agent.ssh).", - "tailscaleDeviceSelect": "Tailscale cihazını seçin.", + "opksshDesc": "Parola veya anahtar yerine kimlik sağlayıcınızı kullanarak bu sunucuda oturum açın. Sunucuda OPKSSH kurulumu gerektirir.", + "warpgateLabel": "Warpgate Ağ Geçidi", + "warpgateDesc": "Bu sunucu bir Warpgate SSH proxy'si üzerinden bağlanır. Termix, kimlik doğrulamasından sonra tarayıcı tabanlı onay akışını otomatik olarak yönetir.", + "agentLabel": "SSH Ajanı", + "agentDesc": "Termix sunucusunda çalışan bir SSH ajanı kullanarak kimlik doğrulayın (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Ajan, Termix'in barındırıldığı makinede çalışıyor olmalıdır.", + "agentSocketPathLabel": "Ajan Soket Yolu", + "agentSocketPathPlaceholder": "SSH_AUTH_SOCK kullanmak için boş bırakın", + "agentSocketPathHint": "SSH_AUTH_SOCK ortam değişkeninden otomatik algılamak için boş bırakın veya özel bir soket yolu girin (örn. /run/user/1000/gnupg/S.gpg-agent.ssh).", + "tailscaleDeviceSelect": "Tailscale cihazı seçin", "tailscaleDeviceSelectPlaceholder": "Bir cihaz seçin...", "tailscaleNoApiKey": "Tailscale API anahtarı yapılandırılmamış. Cihaz keşfini etkinleştirmek için Yönetici Ayarları'ndan bir tane ekleyin.", - "tailscaleDocsLink": "Belgeleri görüntüle", + "tailscaleDocsLink": "Dokümanları görüntüle", "tailscaleLoadingDevices": "Aygıtlar yükleniyor...", - "tailscaleNoDevices": "Ağınızda hiçbir cihaz bulunamadı.", - "tailscaleDeviceAutoFill": "Bir cihaz seçtiğinizde, ana bilgisayarın IP adresi otomatik olarak doldurulacaktır.", + "tailscaleNoDevices": "Tailnet'inizde aygıt bulunamadı.", + "tailscaleDeviceAutoFill": "Bir aygıt seçmek, host IP adresini otomatik dolduracaktır.", "forceKeyboardInteractiveLabel": "Klavye Etkileşimini Zorla", - "forceKeyboardInteractiveShortDesc": "Tuşlar mevcut olsa bile manuel parola girişini zorunlu kıl", + "forceKeyboardInteractiveShortDesc": "Anahtarlar mevcut olsa bile manuel parola girişini zorla", "allowLegacyAlgorithmsLabel": "Eski Algoritmalara İzin Ver", - "allowLegacyAlgorithmsDesc": "Yükseltilemeyen eski cihazlara bağlantılar için kullanımdan kaldırılmış SSH algoritmalarını (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) etkinleştirin.", + "allowLegacyAlgorithmsDesc": "Yükseltilemeyen eski aygıtlara bağlantılar için kullanımdan kaldırılmış SSH algoritmalarını (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) etkinleştir.", "insecure": "Güvensiz", - "terminalAppearance": "Terminal Görünüm", + "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.", - "backgroundImageLabel": "Arka Plan Resmi URL'si", - "backgroundImageDesc": "Terminal arka plan resmi için isteğe bağlı URL", - "backgroundImageOpacityLabel": "Arka Plan Görüntüsü Şeffaflığı", + "lineHeightLabel": "Satır Yüksekliği", + "bellStyleLabel": "Zil Stili", + "backspaceModeLabel": "Backspace Modu", + "cursorBlinking": "İmleç Yanıp Sönmesi", + "cursorBlinkingDesc": "Terminal imleci için yanıp sönme animasyonunu etkinleştir", + "rightClickSelectsWordLabel": "Sağ Tık Kelime Seçer", + "rightClickSelectsWordShortDesc": "Sağ tıklandığında imlecin altındaki kelimeyi seç", + "backgroundImageLabel": "Arka Plan Resmi URL", + "backgroundImageDesc": "Bir terminal arka plan resmi için isteğe bağlı URL", + "backgroundImageOpacityLabel": "Arka Plan Resmi Opaklığı", "customThemeColors": "Özel Renkler", - "customThemeBackground": "Arka plan", - "customThemeForeground": "Ön plan", + "customThemeBackground": "Arka Plan", + "customThemeForeground": "Ön Plan", "customThemeCursor": "İmleç", "customThemeCursorAccent": "İmleç Vurgusu", "customThemeSelection": "Seçim", @@ -750,317 +795,334 @@ "customThemeGreen": "Yeşil", "customThemeYellow": "Sarı", "customThemeBlue": "Mavi", - "customThemeMagenta": "Macenta", - "customThemeCyan": "Camgöbeği", + "customThemeMagenta": "Magenta", + "customThemeCyan": "Cyan", "customThemeWhite": "Beyaz", "customThemeBrightBlack": "Parlak Siyah", "customThemeBrightRed": "Parlak Kırmızı", "customThemeBrightGreen": "Parlak Yeşil", "customThemeBrightYellow": "Parlak Sarı", "customThemeBrightBlue": "Parlak Mavi", - "customThemeBrightMagenta": "Parlak Macenta", - "customThemeBrightCyan": "Parlak Camgöbeği", + "customThemeBrightMagenta": "Parlak Magenta", + "customThemeBrightCyan": "Parlak Cyan", "customThemeBrightWhite": "Parlak Beyaz", - "customThemeResetTooltip": "Varsayılan ayarlara sıfırla", - "syntaxHighlightingLabel": "Sözdizimi Vurgulama", - "syntaxHighlightingDesc": "Terminal çıktısını renklendir (hatalar, yollar, IP adresleri, zaman damgaları)", - "syntaxHighlightingCategories": "Öne Çıkan Kategoriler", - "syntaxHighlightingCategoriesDesc": "Hangi içerik türlerini renklendireceğinizi seçin.", - "syntaxCategoryLogLevels": "Günlük Seviyeleri", - "syntaxCategoryLogLevelsDesc": "hata, uyarı, bilgi, hata ayıklama, ölümcül", + "customThemeResetTooltip": "Varsayılanlara sıfırla", + "savedThemesLabel": "Kaydedilen Temalar", + "saveAsGlobalTheme": "Küresel Tema Olarak Kaydet", + "saveGlobalThemeNamePrompt": "Bu tema için bir ad girin.", + "saveGlobalThemeSuccess": "Tema kaydedildi", + "saveGlobalThemeError": "Tema kaydedilemedi.", + "applyGlobalThemeTooltip": "Bu temayı uygulayın", + "deleteGlobalThemeTooltip": "Bu temayı sil", + "noSavedThemes": "Henüz kaydedilmiş tema yok.", + "syntaxHighlightingLabel": "Söz Dizimi Vurgulama", + "syntaxHighlightingDesc": "Terminal çıktısını renklendir (hatalar, yollar, IP'ler, zaman damgaları)", + "syntaxHighlightingCategories": "Vurgu Kategorileri", + "syntaxHighlightingCategoriesDesc": "Renklendirilecek içerik türlerini seç", + "syntaxCategoryLogLevels": "Günlük Düzeyleri", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "Dosya Yolları", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", - "syntaxCategoryTimestamps": "Zaman damgaları", + "syntaxCategoryTimestamps": "Zaman Damgaları", "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "IP Adresleri", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URL'ler", "syntaxCategoryUrlsDesc": "https://example.com", "syntaxCategoryNumbers": "Etiketli Sayılar", - "syntaxCategoryNumbersDesc": "port 8080, çıkış 1, durum 404", - "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.", - "useSSHTitleLabel": "SSH Pencere Başlığını Kullanın", - "useSSHTitleDesc": "Sekme başlığını ana bilgisayar adından değil, kabuk penceresinin başlığından güncelleyin.", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Davranış ve Gelişmiş", + "scrollbackBufferLabel": "Kaydırma Arabelleği", + "scrollbackMaxLines": "Geçmişte saklanan maksimum satır sayısı", + "sshAgentForwardingLabel": "SSH Aracısı Yönlendirme", + "sshAgentForwardingShortDesc": "Yerel SSH anahtarlarınızı bu sunucuya ilet", + "useSSHTitleLabel": "SSH Pencere Başlığını Kullan", + "useSSHTitleDesc": "Sekme başlığını, ana bilgisayar adı yerine kabuğun pencere başlığından güncelle", "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", - "enableSessionLogging": "Oturum Günlüğü", - "enableSessionLoggingDesc": "Terminal oturum çıktısını daha sonra incelemek üzere kaydedin.", + "enableAutoMoshDesc": "Varsa, SSH yerine Mosh'u tercih et", + "enableAutoTmux": "Otomatik Tmux'u Etkinleştir", + "enableAutoTmuxDesc": "Tmux oturumunu otomatik olarak başlat veya mevcut oturuma bağlan", + "enableSessionLogging": "Oturum Kaydı", + "enableSessionLoggingDesc": "Daha sonra gözden geçirmek için terminal oturumu çıktısını kaydet", + "allowSessionSharing": "Oturum Paylaşımına İzin Ver", + "allowSessionSharingDesc": "Bu sunucudaki canlı oturumların bağlantı yoluyla veya diğer kullanıcılarla paylaşılmasına izin verin.", "enableCommandHistory": "Komut Geçmişi", - "enableCommandHistoryDesc": "Bu terminalde çalıştırılan komutları geçmiş kaydı ve otomatik tamamlama için kaydedin.", + "enableCommandHistoryDesc": "Geçmiş ve otomatik tamamlama için bu terminalde çalıştırılan komutları kaydet", "linkClickBehaviorLabel": "Bağlantı Tıklama Davranışı", - "linkClickBehaviorDesc": "Terminalde bir bağlantıya tıkladığınızda ne olacağını kontrol eder. Uygulama düzeyindeki ayarı izlemek için 'Varsayılan'ı kullanın.", - "linkClickBehaviorDefault": "Varsayılan (uygulama ayarlarını takip et)", - "linkClickBehaviorConfirm": "Açmak veya kopyalamak için açılır pencereyi göster", + "linkClickBehaviorDesc": "Terminalde bir bağlantıya tıkladığınızda ne olacağını kontrol eder. Uygulama düzeyindeki ayarı takip etmek için 'Varsayılan'ı kullanın.", + "linkClickBehaviorDefault": "Varsayılan (uygulama ayarını takip et)", + "linkClickBehaviorConfirm": "Açmak veya kopyalamak için açılır pencere göster", "linkClickBehaviorDirect": "Doğrudan aç", "sudoPasswordAutoFillLabel": "Sudo Parolası Otomatik Doldurma", - "sudoPasswordAutoFillShortDesc": "İstendiğinde otomatik olarak sudo parolasını girin.", - "sudoPasswordAutoFillDesc": "Sudo parolasını saklayarak, sunucu ölçümlerinin, terminal komut istemlerinin ve diğerlerinin ayrıcalıklı komutları otomatik olarak çalıştırmasını sağlayın.", + "sudoPasswordAutoFillShortDesc": "İstendiğinde sudo parolasını otomatik olarak sağla", + "sudoPasswordAutoFillDesc": "Sudo parolasını saklayın; böylece ana bilgisayar metrikleri, terminal istemleri ve diğer bileşenler ayrıcalıklı komutları otomatik olarak çalıştırabilir.", "sudoPasswordLabel": "Sudo Parolası", - "environmentVariablesLabel": "Çevresel Değişkenler", + "environmentVariablesLabel": "Ortam Değişkenleri", "addVariableBtn": "Değişken Ekle", - "noEnvVars": "Hiçbir ortam değişkeni yapılandırılmamış.", - "fastScrollModifierLabel": "Hızlı Kaydırma Değiştirici", + "noEnvVars": "Yapılandırılmış ortam değişkeni yok.", + "fastScrollModifierLabel": "Hızlı Kaydırma Değiştiricisi", "fastScrollSensitivityLabel": "Hızlı Kaydırma Hassasiyeti", - "moshCommandLabel": "Mosh Komutanlığı", - "startupSnippetLabel": "Başlangıç Kodu Parçası", - "keepaliveIntervalLabel": "Keepalive Aralığı (saniye)", - "maxKeepaliveMisses": "Max Keepalive'ı özlüyor", + "moshCommandLabel": "Mosh Komutu", + "startupSnippetLabel": "Başlangıç Betiği", + "keepaliveIntervalLabel": "Canlı Tutma Aralığı (saniye)", + "maxKeepaliveMisses": "Maksimum Keepalive Kaçırma", "tunnelSettings": "Tünel Ayarları", - "enableTunneling": "Tünelleme özelliğini etkinleştirin", - "enableTunnelingDesc": "Bu sunucu için SSH tüneli işlevini etkinleştirin.", + "enableTunneling": "Tünellemeyi Etkinleştir", + "enableTunnelingDesc": "Bu ana bilgisayar için SSH tünel işlevselliğini etkinleştir", "serverTunnelsSection": "Sunucu Tünelleri", "addTunnelBtn": "Tünel Ekle", - "noTunnelsConfigured": "Hiçbir tünel yapılandırılmamış.", + "noTunnelsConfigured": "Yapılandırılmış tünel yok.", "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", - "endpointHostPlaceholder": "Aynı sunucu, SSH sunucusu veya erişilebilir sunucu/IP adresi", - "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ı...", + "tunnelType": "Tünel Türü", + "tunnelModeLocalDesc": "Yerel bir portu uzak sunucudaki bir porta (veya ondan erişilebilen bir sunucuya) yönlendirin.", + "tunnelModeRemoteDesc": "Uzak sunucudaki bir portu, makinenizdeki yerel bir porta geri yönlendirin.", + "tunnelModeDynamicDesc": "Dinamik port yönlendirme için yerel bir portta bir SOCKS5 proxy oluşturun.", + "sameHost": "Bu ana bilgisayar (doğrudan tünel)", + "endpointHost": "Hedef Ana Bilgisayar", + "endpointHostPlaceholder": "Aynı ana bilgisayar, SSH ana bilgisayarı veya erişilebilir ana bilgisayar/IP", + "endpointPort": "Hedef Port", + "bindHost": "Bağlanma Ana Bilgisayarı", + "sourcePort": "Kaynak Port", + "maxRetries": "Maksimum Yeniden Deneme", + "retryIntervalS": "Yeniden Deneme Aralığı (sn)", + "autoStartLabel": "Otomatik başlat", + "autoStartDesc": "Ana bilgisayar yüklendiğinde bu tüneli otomatik olarak bağla", + "tunnelConnecting": "Tünel bağlanıyor...", "tunnelDisconnected": "Tünel bağlantısı kesildi", - "failedToConnectTunnel": "Bağlantı kurulamadı.", - "failedToDisconnectTunnel": "Bağlantı kesilemedi", + "failedToConnectTunnel": "Bağlantı başarısız oldu", + "failedToDisconnectTunnel": "Bağlantı kesme başarısız oldu", "dockerIntegration": "Docker Entegrasyonu", - "enableDockerMonitor": "Docker'ı etkinleştirin", - "enableDockerMonitorDesc": "Bu sunucudaki konteynerleri Docker aracılığıyla izleyin ve yönetin.", - "containerRuntime": "Konteyner Çalışma Zamanı", - "containerRuntimeDesc": "Bu sunucuda kapsayıcı yönetimi için kullanılan CLI'yı seçin.", - "containerRuntimeDocker": "Liman işçisi", + "enableDockerMonitor": "Docker'ı Etkinleştir", + "enableDockerMonitorDesc": "Docker aracılığıyla bu ana bilgisayardaki kapsayıcıları izle ve yönet", + "containerRuntime": "Kapsayıcı Çalışma Zamanı", + "containerRuntimeDesc": "Bu ana bilgisayarda kapsayıcı yönetimi için kullanılan CLI'yi seçin", + "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "Tmux Monitörünü Etkinleştir", - "enableTmuxMonitorDesc": "Bu sunucuyu Tmux Monitöründe göster ve Tmux eylemlerini kenar çubuğuna ekle.", + "enableTmuxMonitor": "Tmux İzleyiciyi Etkinleştir", + "enableTmuxMonitorDesc": "Bu ana bilgisayarı Tmux İzleyicide göster ve tmux eylemlerini kenar çubuğuna ekle", "tabProxmox": "Proxmox", "proxmoxIntegration": "Proxmox Entegrasyonu", - "enableProxmox": "Proxmox'u etkinleştirin", - "enableProxmoxDesc": "Bu sunucuyu bir Proxmox düğümü olarak işaretleyin. Bu sunucudan doğrudan konuk dosyaların keşfedilmesini ve içe aktarılmasını sağlar.", + "enableProxmox": "Proxmox'u Etkinleştir", + "enableProxmoxDesc": "Bu ana bilgisayarı bir Proxmox düğümü olarak işaretle. Bu ana bilgisayardan doğrudan konuk keşfi ve içe aktarmayı etkinleştirir.", "proxmoxDefaultAuthType": "Varsayılan Kimlik Doğrulama Türü", - "proxmoxDefaultAuthTypeDesc": "İçe aktarılan konuk sunuculara uygulanan kimlik doğrulama yöntemi. Konuklarınıza nasıl bağlandığınıza uygun kimlik doğrulama türünü seçin.", - "authTypePassword": "Şifre", + "proxmoxDefaultAuthTypeDesc": "İçe aktarılan konuk ana bilgisayarlara uygulanan kimlik doğrulama yöntemi. Konuklarınıza nasıl bağlandığınıza uygun kimlik doğrulama türünü seçin.", + "authTypePassword": "Parola", "authTypeKey": "SSH Anahtarı", - "authTypeCredential": "Kimlik belgesi", + "authTypeCredential": "Kimlik Bilgisi", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "Hiçbiri", + "authTypeNone": "Yok", "proxmoxDefaultCredential": "Varsayılan Kimlik Bilgisi", - "proxmoxDefaultCredentialDesc": "Kimlik doğrulama türü \"Kimlik Bilgisi\" olarak ayarlandığında, içe aktarılan konuk sunucular için kullanılan kimlik bilgisi.", + "proxmoxDefaultCredentialDesc": "Kimlik doğrulama türü Kimlik Bilgisi olarak ayarlandığında içe aktarılan misafir ana makineleri için kullanılan kimlik bilgisi.", "proxmoxWindowsDetection": "Windows / RDP algılama", - "proxmoxWindowsDetectionDesc": "SSH yerine RDP'yi tetikleyen virgülle ayrılmış isim kalıpları (büyük/küçük harf duyarlılığı yok)", - "proxmoxDockerDetection": "Docker tespiti", - "proxmoxDockerDetectionDesc": "Docker'ın konukları eşleştirmesini sağlayan virgülle ayrılmış ad kalıpları.", + "proxmoxWindowsDetectionDesc": "SSH yerine RDP'yi tetikleyen virgülle ayrılmış ad desenleri (büyük/küçük harf duyarsız)", + "proxmoxDockerDetection": "Docker algılama", + "proxmoxDockerDetectionDesc": "Eşleşen misafirler için Docker'ı etkinleştiren virgülle ayrılmış ad desenleri", "proxmoxPreferredRanges": "Tercih edilen IP aralıkları", - "proxmoxPreferredRangesDesc": "Misafir işletim sisteminin birden fazla arayüzü olduğunda IP seçimi için öncelik sırasına göre virgülle ayrılmış ön ekler.", - "proxmoxAutoSync": "Konukları otomatik olarak senkronize et", - "proxmoxAutoSyncDesc": "Oturumunuzun kilidi açıkken, periyodik olarak bu Proxmox düğümünü keşfedin ve içe aktarılan konuk sunucuları oluşturun veya güncelleyin.", - "proxmoxSyncInterval": "Senkronizasyon aralığı (dakika)", - "proxmoxSyncIntervalDesc": "Minimum 5 dakika. Zamanlayıcı, kilitli kullanıcı oturumlarını atlar.", - "proxmoxMarkMissing": "Mark'ın kayıp misafirleri", - "proxmoxMarkMissingDesc": "Daha önce içe aktarılan bir konuk işletim sistemi kaybolduğunda, onu silmek yerine proxmox-missing etiketi ekleyin.", - "proxmoxLastSync": "Son senkronizasyon", - "proxmoxLastSyncNever": "Henüz senkronize edilmedi", - "proxmoxLastSyncNoResult": "Sonuç bilgisi yok.", - "proxmoxLastSyncSummary": "{{created}} oluşturuldu, {{updated}} güncellendi, {{markedMissing}} eksik, {{skipped}} atlandı", + "proxmoxPreferredRangesDesc": "Bir misafirde birden fazla arayüz olduğunda IP seçimi için öncelik sırasına göre virgülle ayrılmış önekler", + "proxmoxAutoSync": "Misafirleri otomatik eşitle", + "proxmoxAutoSyncDesc": "Oturumunuz açıkken bu Proxmox düğümünü periyodik olarak keşfet ve içe aktarılan misafir ana makinelerini oluştur veya güncelle.", + "proxmoxSyncInterval": "Eşitleme aralığı (dakika)", + "proxmoxSyncIntervalDesc": "En az 5 dakika. Zamanlayıcı kilitli kullanıcı oturumlarını atlar.", + "proxmoxMarkMissing": "Kayıp misafirleri işaretle", + "proxmoxMarkMissingDesc": "Daha önce içe aktarılan bir misafir kaybolduğunda onu silmek yerine bir proxmox-missing etiketi ekle.", + "proxmoxLastSync": "Son eşitleme", + "proxmoxLastSyncNever": "Henüz eşitlenmedi", + "proxmoxLastSyncNoResult": "Sonuç detayı yok", + "proxmoxLastSyncSummary": "{{created}} oluşturuldu, {{updated}} güncellendi, {{markedMissing}} kayıp işaretlendi, {{skipped}} atlandı", "proxmoxLastSyncStatus": { - "success": "Başarı", - "error": "Arızalı", - "pending": "Askıda olması" + "success": "Başarılı", + "error": "Başarısız", + "pending": "Beklemede" }, - "proxmoxDiscoverAction": "Proxmox misafirlerini keşfedin ve içe aktarın", + "proxmoxDiscoverAction": "Proxmox misafirlerini keşfet ve içe aktar", "proxmoxImportTitle": "Proxmox'tan içe aktar", - "proxmoxSelectHost": "Bir Proxmox sunucusu seçin…", - "proxmoxDiscover": "Keşfetmek", - "proxmoxDiscovering": "…'ı Keşfetmek", - "proxmoxDiscoverGuests": "Konukları keşfedin", - "proxmoxGuestsSelected_one": "{{count}} misafir — {{selected}} seçildi", - "proxmoxGuestsSelected_other": "{{count}} misafir — {{selected}} seçildi", + "proxmoxSelectHost": "Bir Proxmox ana makinesi seç…", + "proxmoxDiscover": "Keşfet", + "proxmoxDiscovering": "Keşfediliyor…", + "proxmoxDiscoverGuests": "Misafirleri keşfet", + "proxmoxGuestsSelected_one": "{{count}} misafir — {{selected}} seçili", + "proxmoxGuestsSelected_other": "{{count}} misafir — {{selected}} seçili", "proxmoxSelectAll": "Tümünü seç", - "proxmoxDeselectAll": "Tüm seçimleri kaldır", - "proxmoxNoGuests": "Bu Proxmox düğümünde hiçbir misafir bulunamadı.", - "proxmoxImportButton_one": "{{count}} ana bilgisayarı içe aktar", - "proxmoxImportButton_other": "{{count}} ana bilgisayarlarını içe aktar", + "proxmoxDeselectAll": "Tüm seçimi kaldır", + "proxmoxNoGuests": "Bu Proxmox düğümünde hiç misafir bulunamadı.", + "proxmoxImportButton_one": "{{count}} ana makineyi içe aktar", + "proxmoxImportButton_other": "{{count}} sunucuyu içe aktar", "proxmoxResultImported": "{{count}} içe aktarıldı", "proxmoxResultUpdated": "{{count}} güncellendi", - "proxmoxResultFailed": "{{count}} başarısız oldu", - "proxmoxImportComplete": "Proxmox içe aktarma işlemi tamamlandı: {{summary}}", - "proxmoxDiscoveryFailed": "Keşif başarısız oldu", - "proxmoxImportFailed": "İçe aktarma başarısız oldu", + "proxmoxResultFailed": "{{count}} başarısız", + "proxmoxResultSkippedNoIp": "{{count}} atlandı (IP adresi bulunamadı)", + "proxmoxImportComplete": "Proxmox içe aktarma tamamlandı: {{summary}}", + "proxmoxDiscoveryFailed": "Keşif başarısız", + "proxmoxImportFailed": "İçe aktarma başarısız", "enableFileManagerMonitor": "Dosya Yöneticisini Etkinleştir", - "enableFileManagerMonitorDesc": "Bu sunucudaki dosyaları SFTP üzerinden tarayın ve yönetin.", + "enableFileManagerMonitorDesc": "Bu sunucudaki dosyalara SFTP üzerinden göz atın ve yönetin", "scpLegacyLabel": "SCP Eski Modu", - "scpLegacyDesc": "SFTP alt sistemini desteklemeyen sunucular (örneğin gömülü veya minimal SSH sunucuları) için eski dosya aktarım yöntemini kullanın.", + "scpLegacyDesc": "SFTP alt sistemini desteklemeyen sunucular için eski dosya aktarımını kullan (ör. gömülü veya minimal SSH sunucuları).", "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 diğer ölçümleri 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", + "fileManagerPathHint": "Dosya yöneticisi bu sunucu için başlatıldığında açılacak dizin.", + "statusChecksLabel": "Durum Denetimleri", + "enableStatusChecks": "Durum Denetimlerini Etkinleştir", + "enableStatusChecksDesc": "Kullanılabilirliği doğrulamak için bu sunucuya düzenli olarak ping at", + "useGlobalInterval": "Genel Aralığı Kullan", + "useGlobalIntervalDesc": "Sunucu geneli durum denetim aralığını geçersiz kıl", + "checkIntervalS": "Denetim Aralığı (sn)", + "checkIntervalDesc": "Her bağlantı pingi arasındaki saniye", + "metricsCollectionLabel": "Metrik Toplama", + "enableMetricsLabel": "Metrikleri Etkinleştir", + "enableMetricsDesc": "Bu sunucudan CPU, RAM, disk ve diğer metrikleri topla", + "useGlobalMetrics": "Genel Aralığı Kullan", + "useGlobalMetricsDesc": "Sunucu geneli metrik aralığını geçersiz kıl", + "metricsIntervalS": "Metrik Aralığı (sn)", + "metricsIntervalDesc2": "Metrik anlık görüntüleri arasındaki saniye", "visibleWidgets": "Görünür Widget'lar", - "widgetsMovedToHostMetrics": "Kartlar artık doğrudan Sunucu Metrikleri sekmesinden ekleniyor, düzenleniyor ve boyutlandırılıyor. Bu sunucu için Sunucu Metrikleri'ni açın ve hangi kartların gösterileceğini seçmek için Özelleştir'i kullanın.", + "widgetsMovedToHostMetrics": "Kartlar artık doğrudan Sunucu Metrikleri sekmesinde eklenir, düzenlenir ve yeniden boyutlandırılır. Bu sunucu için Sunucu Metrikleri'ni açın ve hangi kartların gösterileceğini seçmek için Özelleştir'i kullanın.", "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", + "memoryDesc": "RAM kullanımı, takas, önbellek", "storageLabel": "Disk Kullanımı", - "storageDesc": "Bağlantı noktası başına disk kullanımı", + "storageDesc": "Bağlama 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", + "uptimeLabel": "Çalışma Süresi", + "uptimeDesc": "Sistem çalışma süresi ve başlangıç zamanı", + "systemInfoLabel": "Sistem Bilgisi", "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", + "topProcessesLabel": "En Yoğun Süreçler", "topProcessesDesc": "PID, CPU%, MEM%, komut", - "listeningPortsLabel": "Dinleme Portları", - "listeningPortsDesc": "İşlem ve durum ile açık portlar", + "listeningPortsLabel": "Dinlenen Bağlantı Noktaları", + "listeningPortsDesc": "Süreç ve durumla açık bağlantı noktaları", "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 Ana Bilgisayar Metrikleri araç çubuğunda düğmeler olarak görünür.", - "noQuickActions": "Henüz hızlı bir işlem yapılmadı.", + "quickActionsLabel": "Hızlı Eylemler", + "quickActionsToolbar": "Hızlı eylemler, tek tıklamayla komut çalıştırmak için Ana Bilgisayar Metrikleri araç çubuğunda düğme olarak görünür.", + "noQuickActions": "Henüz hızlı eylem yok.", "buttonLabel": "Düğme etiketi", - "selectSnippetPlaceholder": "Kod parçasını seçin...", + "selectSnippetPlaceholder": "Parçacık seç...", "addActionBtn": "Eylem Ekle", - "hostSharedSuccessfully": "Sunucu başarıyla paylaşıldı.", - "failedToShareHost": "Sunucuyu paylaşma başarısız oldu.", + "hostSharedSuccessfully": "Ana bilgisayar başarıyla paylaşıldı", + "failedToShareHost": "Ana bilgisayar paylaşılamadı", "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", + "failedToRevokeAccess": "Erişim iptal edilemedi", + "cancelBtn": "İptal", + "savingBtn": "Kaydediliyor...", + "addHostBtn": "Ana Bilgisayar Ekle", + "hostUpdated": "Ana bilgisayar güncellendi", + "hostCreated": "Ana bilgisayar oluşturuldu", + "failedToSave": "Ana bilgisayar kaydedilemedi", + "credentialUpdated": "Kimlik bilgisi güncellendi", "credentialCreated": "Kimlik bilgisi oluşturuldu", - "failedToSaveCredential": "Kimlik bilgilerini kaydetme başarısız oldu.", - "credentialNameRequired": "Lütfen kimlik belgesi için bir isim girin.", - "credentialAuthRequired": "Parola, SSH anahtarı veya her ikisini de ekleyin.", - "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ı.", + "failedToSaveCredential": "Kimlik bilgisi kaydedilemedi", + "credentialNameRequired": "Kimlik bilgisi için bir ad girin", + "credentialAuthRequired": "Bir parola, SSH anahtarı veya her ikisini ekleyin", + "createCredentialFromHostBtn": "Kimlik Bilgisi Oluştur", + "createCredentialFromHostTitle": "Sunucudan Kimlik Bilgisi Oluştur", + "createCredentialFromHostDesc": "Bu sunucunun mevcut kullanıcı adı, parolası ve/veya SSH anahtarıyla önceden doldurulmuş, yeniden kullanılabilir ve paylaşılabilir bir kimlik bilgisi girişi oluşturun.", + "backToHosts": "Sunuculara Dön", + "backToCredentials": "Kimlik Bilgilerine Dön", + "pinned": "Sabitlenmiş", + "noHostsFound": "Hiç sunucu bulunamadı", + "tryDifferentTerm": "Farklı bir terim deneyin", + "addFirstHost": "Başlamak için ilk sunucunuzu ekleyin", + "noCredentialsFound": "Hiç kimlik bilgisi bulunamadı", "addCredentialBtn": "Kimlik Bilgisi Ekle", - "updateCredentialBtn": "Kimlik Bilgilerini Güncelle", + "updateCredentialBtn": "Kimlik Bilgisini 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.", + "deleteSelected": "Sil", + "exitSelection": "Seçimden Çık", + "importSkip": "İçe Aktar (mevcut olanları atla)", + "importOverwrite": "İçe Aktar (üzerine yaz)", + "collapseBtn": "Daralt", + "importExportBtn": "İçe Aktar / Dışa Aktar", + "hostStatusesRefreshed": "Sunucu durumları yenilendi", + "failedToRefreshHosts": "Sunucular yenilenemedi", + "movedHostTo": "{{host}}, \"{{folder}}\" klasörüne taşındı", + "failedToMoveHost": "Sunucu taşınamadı", + "folderRenamedTo": "Klasör adı \"{{name}}\" olarak değiştirildi", + "deletedFolder": "\"{{name}}\" klasörü silindi", + "failedToDeleteFolder": "Klasör silinemedi", + "deleteAllInFolder": "\"{{name}}\" klasöründeki tüm sunucuları sil? Bu işlem geri alınamaz.", "folderPickerPlaceholder": "Klasör yok", - "folderPickerSearch": "Arama yapın veya alt klasörler oluşturun (alt klasörler için / kullanın)...", + "folderPickerSearch": "Ara veya oluştur (alt klasörler için / kullanın)...", "folderPickerNone": "Klasör yok", - "folderPickerCreate": "\"{{path}} \" oluşturun", - "folderPickerEmpty": "Eşleşen klasör bulunamadı.", + "folderPickerCreate": "\"{{path}}\" oluştur", + "folderPickerEmpty": "Eşleşen klasör yok", "newFolder": "Yeni klasör", "createFolderTitle": "Klasör oluştur", "editFolderTitle": "Klasörü düzenle", - "folderDialogDescription": "Bir ad, renk ve simge seçin. Klasörleri iç içe yerleştirmek için / işaretini kullanın.", + "folderDialogDescription": "Bir ad, renk ve simge seçin. Alt klasörler için / kullanın.", "folderNameLabel": "Klasör adı", - "folderNamePlaceholder": "Örneğin Üretim/Web", - "folderNestingHint": "Seviyeleri ayırmak ve iç içe klasörler oluşturmak için / işaretini kullanın.", + "folderNamePlaceholder": "örn. Üretim/Web", + "folderNestingHint": "Seviyeleri ayırmak ve iç içe klasörler oluşturmak için / kullanın.", "folderColor": "Renk", "folderIcon": "Simge", + "folderCredential": "Kimlik belgesi", + "folderCredentialNone": "Herhangi bir yetki belgesi atanmadı.", + "folderCredentialHint": "Bu klasördeki, kendi kimlik bilgilerini seçmeden \"Kaydedilmiş kimlik bilgisi\" kimlik doğrulamasını kullanan sunucular, bu kimlik bilgisini devralacaktır.", "folderPreview": "Önizleme", - "folderNameFallback": "Başlıksız klasör", + "folderNameFallback": "Adsız klasör", "createFolderButton": "Klasör oluştur", - "saveFolderButton": "Kaydet klasörü", - "cancel": "İptal etmek", - "iconSearchPlaceholder": "Arama simgeleri...", + "saveFolderButton": "Klasörü kaydet", + "cancel": "İptal", + "iconSearchPlaceholder": "Simge ara...", "openAllSessions": "Tüm oturumları aç", "editFolder": "Klasörü düzenle", "deleteFolder": "Klasörü sil", "folderSaved": "Klasör kaydedildi", - "failedToSaveFolder": "Klasör kaydedilemedi.", - "folderDeleted": "Silinen klasör \"{{name}}\"", - "deleteFolderConfirm": "\"{{name}}\" ve {{count}} ana bilgisayarlarını silmek mi istiyorsunuz? Bu işlem geri alınamaz.", - "failedToMoveHosts": "Sunucuları taşıma başarısız oldu.", + "failedToSaveFolder": "Klasör kaydedilemedi", + "folderDeleted": "\"{{name}}\" klasörü silindi", + "deleteFolderConfirm": "\"{{name}}\" klasörünü ve {{count}} ana makineyi sil? Bu işlem geri alınamaz.", + "failedToMoveHosts": "Ana makineler taşınamadı", "expandAll": "Tüm klasörleri genişlet", "collapseAll": "Tüm klasörleri daralt", - "moreActions": "Daha", + "moreActions": "Daha Fazla", "groupBy": "Gruplandır", - "GroupByFolder": "Dosya", + "GroupByFolder": "Klasör", "GroupByTag": "Etiket", "GroupByStatus": "Durum", "GroupByProtocol": "Protokol", "GroupByAuth": "Kimlik doğrulama türü", "groupUngrouped": "Gruplandırılmamış", - "deletedHost": "Silindi {{name}}", + "deletedHost": "{{name}} silindi", "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ı", - "hostMetricsUrlCopied": "Host Metrics URL'si kopyalandı", - "tmuxMonitorUrlCopied": "Tmux İzleme 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ı", + "hostMetricsUrlCopied": "Ana makine metrikleri URL'si kopyalandı", + "tmuxMonitorUrlCopied": "Tmux Monitor URL kopyalandı", + "rdpUrlCopied": "RDP URL kopyalandı", + "vncUrlCopied": "VNC URL kopyalandı", + "telnetUrlCopied": "Telnet URL kopyalandı", + "remoteDesktopUrlCopied": "Uzak Masaüstü URL kopyalandı", "expandActions": "Eylemleri genişlet", - "collapseActions": "İşlemleri daralt", + "collapseActions": "Eylemleri daralt", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "{{name}} adresine sihirli paket gönderildi.", - "wakeOnLanError": "Sihirli paket gönderilemedi.", - "cloneHostAction": "Klon Ana Bilgisayar", + "wakeOnLanSuccess": "Sihirli paket {{name}} adresine gönderildi", + "wakeOnLanError": "Sihirli paket gönderilemedi", + "cloneHostAction": "Sunucuyu Klonla", "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", - "copyHostMetricsUrlAction": "Host Metrics URL'sini Kopyala", - "copyTmuxMonitorUrlAction": "Tmux İzleme URL'sini Kopyala", - "copyRdpUrlAction": "RDP URL'sini kopyala", - "copyVncUrlAction": "VNC URL'sini kopyala", - "copyTelnetUrlAction": "Telnet URL'sini kopyala", + "copyDockerUrlAction": "Docker URL'sini Kopyala", + "copyHostMetricsUrlAction": "Sunucu Metrikleri URL'sini Kopyala", + "copyTmuxMonitorUrlAction": "Tmux Monitor 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...", + "deleteCredentialConfirm": "\"{{name}}\" kimlik bilgisi silinsin mi?", + "deletedCredential": "{{name}} silindi", + "deploySSHKeyTitle": "SSH Anahtarı Dağıt", + "deployingBtn": "Dağıtılı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.", + "failedToDeployKey": "Anahtar dağıtılamadı", + "deleteHostsConfirm": "{{count}} sunucu{{plural}} silinsin mi? Bu işlem geri alınamaz.", "movedToRoot": "Kök dizine taşındı", "enableTerminalFeature": "Terminali Etkinleştir", "disableTerminalFeature": "Terminali Devre Dışı Bırak", @@ -1068,105 +1130,105 @@ "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", - "enableProxmoxFeature": "Proxmox'u etkinleştirin", - "disableProxmoxFeature": "Proxmox'u devre dışı bırakın", + "enableDockerFeature": "Docker'ı Etkinleştir", + "disableDockerFeature": "Docker'ı Devre Dışı Bırak", + "enableProxmoxFeature": "Proxmox'u Etkinleştir", + "disableProxmoxFeature": "Proxmox'u 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.", + "authDetails": "Kimlik Doğrulama Detayları", + "credType": "Tür", + "generateKeyPairDesc": "Yeni bir anahtar çifti oluşturun, özel ve genel anahtarlar otomatik olarak doldurulur.", "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.", + "generateLabel": "{{label}} Oluştur", + "uploadFileBtn": "Dosya Yükle", + "keyPassphraseOptional": "Anahtar Parolası (İsteğe Bağlı)", + "sshPublicKeyOptional": "SSH Genel Anahtar (İsteğe Bağlı)", + "publicKeyGenerated": "Genel anahtar oluşturuldu", + "failedToGeneratePublicKey": "Genel 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…", + "failedToGenerateKeyPair": "Anahtar çifti oluşturulamadı", + "searchHostsPlaceholder": "Hostları, adresleri, etiketleri ara…", + "searchCredentialsPlaceholder": "Kimlik bilgilerini ara…", "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?", + "addTag": "Etiket Ekle...", + "deleteConfirmBtn": "Sil", + "tunnelRequirementsText": "SSH sunucusunda /etc/ssh/sshd_config dosyasında GatewayPorts yes, AllowTcpForwarding yes ve PermitRootLogin yes ayarlanmış olmalıdır.", + "deleteHostConfirm": "\"{{name}}\" silinsin 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", + "keyPassphrase": "Anahtar Parolası", + "connectBtn": "Bağlan", + "disconnectBtn": "Bağlantıyı Kes", "basicInformation": "Temel Bilgiler", - "authDetailsSection": "Kimlik Doğrulama Ayrıntıları", - "credTypeLabel": "Tip", - "hostsTab": "Ev sahipleri", + "authDetailsSection": "Kimlik Doğrulama Detayları", + "credTypeLabel": "Tür", + "hostsTab": "Hostlar", "credentialsTab": "Kimlik Bilgileri", - "selectMultiple": "Birden fazla seçenek seçin", - "selectHosts": "Sunucuları seçin", + "selectMultiple": "Birden fazla seç", + "selectHosts": "Sunucuları seç", "connectionLabel": "Bağlantı", - "authenticationLabel": "Kimlik doğrulama", + "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.", + "generateKeyPairDescription": "Yeni bir anahtar çifti oluştur, özel ve açık anahtarlar otomatik olarak doldurulacak.", "generateFromPrivateKey": "Özel Anahtardan Oluştur", "refreshBtn2": "Yenile", - "exitSelectionTitle": "Çıkış seçimi", + "exitSelectionTitle": "Seçimden çık", "exportAll": "Tümünü Dışa Aktar", "exportForSharing": "Paylaşım için 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ı.", - "hostsShareExported": "Paylaşılabilir 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.", + "pinnedSection": "Sabitlenmiş", + "hostsExported": "Sunucular başarıyla dışa aktarıldı", + "hostsShareExported": "Paylaşılabilir sunucular başarıyla dışa aktarıldı", + "exportFailed": "Sunucular dışa aktarılamadı", + "sampleDownloaded": "Örnek dosya indirildi", + "failedToDeleteCredential2": "Kimlik bilgisi silinemedi", "noFolderOption": "(Klasör yok)", - "nSelected": "{{count}} seçildi", + "nSelected": "{{count}} seçili", "featuresMenu": "Özellikler", - "moveMenu": "Taşınmak", - "connectSelected": "Bağlamak", - "cancelSelection": "İptal etmek", - "deployDialogDesc": "{{name}} öğesini bir sunucunun authorized_keys'ine dağıtın.", + "moveMenu": "Taşı", + "connectSelected": "Bağlan", + "cancelSelection": "İptal", + "deployDialogDesc": "{{name}} anahtarını bir sunucunun authorized_keys dosyasına dağıt.", "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...", + "keyDeployedSuccess": "Anahtar başarıyla dağıtıldı", + "failedToDeployKey2": "Anahtar dağıtılamadı", + "deletedCount": "{{count}} sunucu silindi", + "failedToDeleteCount": "{{count}} sunucu silinemedi", + "duplicatedHost": "\"{{name}}\" çoğaltıldı", + "failedToDuplicateHost": "Host kopyalanamadı", + "updatedCount": "{{count}} host güncellendi", + "friendlyNameLabel": "Gösterim Adı", + "descriptionLabel": "Açıklama", + "loadingHost": "Host yükleniyor...", + "loadingHosts": "Hostlar 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)", + "noHostsYet": "Henüz host yok", + "noHostsMatchSearch": "Aramanızla eşleşen host yok", + "hostNotFound": "Host bulunamadı", + "searchHosts": "Host ara...", + "sortHosts": "Hostları Sırala", + "sortDefault": "Varsayılan Sıralama", + "sortNameAsc": "Ad (A → Z)", + "sortNameDesc": "Ad (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", + "sortIpDesc": "IP Adresi (Azalan)", + "sortOnlineFirst": "Önce Çevrimiçi", + "sortOfflineFirst": "Önce Çevrimdışı", + "sortPinnedFirst": "Önce Sabitlenmiş", + "filterHosts": "Hostları Filtrele", "filterClearAll": "Filtreleri Temizle", "filterStatusGroup": "Durum", "filterOnline": "Çevrimiçi", "filterOffline": "Çevrimdışı", - "filterPinned": "Sabitlendi", + "filterPinned": "Sabitlenmiş", "filterAuthGroup": "Kimlik Doğrulama Türü", - "filterAuthPassword": "Şifre", + "filterAuthPassword": "Parola", "filterAuthKey": "SSH Anahtarı", - "filterAuthCredential": "Kimlik belgesi", - "filterAuthNone": "Hiçbiri", + "filterAuthCredential": "Kimlik Bilgisi", + "filterAuthNone": "Yok", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protokol", "filterProtocolSsh": "SSH", @@ -1174,1257 +1236,1329 @@ "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", "filterFeaturesGroup": "Özellikler", - "filterFeatureTerminal": "terminal", + "filterFeatureTerminal": "Terminal", "filterFeatureFileManager": "Dosya Yöneticisi", "filterFeatureTunnel": "Tünel", - "filterFeatureDocker": "Liman işçisi", + "filterFeatureDocker": "Docker", "filterTagsGroup": "Etiketler", - "shareHost": "Paylaşımlı Sunucu", + "shareHost": "Sunucuyu Paylaş", "shareHostTitle": "Paylaş: {{name}}", + "shareFolder": "Paylaşılan Klasör", + "shareFolderTitle": "Paylaşılan klasör: {{name}}", + "folderSharedSuccessfully": "Klasörde paylaşılan {{count}} ana bilgisayar(lar)", + "failedToShareFolder": "Klasör paylaşımı başarısız oldu.", "sharing": { "loadError": "Paylaşım verileri yüklenemedi. Lütfen tekrar deneyin.", - "shareWithSection": "Şununla paylaş", + "shareWithSection": "Kimlerle paylaş", "usersTab": "Kullanıcılar", "rolesTab": "Roller", - "searchPlaceholder": "Kullanıcıları veya rolleri ara...", - "noMatches": "Eşleşme bulunamadı.", - "permissionLevelLabel": "İzin seviyesi", + "searchPlaceholder": "Kullanıcı veya rol ara...", + "noMatches": "Eşleşme bulunamadı", + "permissionLevelLabel": "İzin düzeyi", "levels": { "connect": { - "label": "Bağlamak", - "description": "Yalnızca açık oturumlar: terminal, uzak masaüstü, dosya yöneticisi, tüneller ve Docker. Sunucu yapılandırmasına erişim yok." + "label": "Bağlan", + "description": "Yalnızca oturum açar: terminal, uzak masaüstü, dosya yöneticisi, tüneller ve Docker. Sunucu yapılandırmasına erişim yok." }, "view": { - "label": "Görüş", - "description": "Bağlanın ve sunucu yapılandırmasını görün. Gizli bilgiler asla gösterilmez." + "label": "Görüntüle", + "description": "Bağlan iznine ek olarak sunucu yapılandırmasını görebilir. Gizli bilgiler asla gösterilmez." }, "edit": { - "label": "Düzenlemek", - "description": "Sunucuyu görüntüleyin ve değiştirin. Gizli bilgiler değiştirilebilir ancak asla okunamaz; kimlik bilgisi atamaları yalnızca sahibine özel kalır." + "label": "Düzenle", + "description": "Görüntüleme iznine ek olarak sunucuyu düzenleyebilir. Gizli bilgiler değiştirilebilir ancak asla okunamaz; kimlik bilgisi atamaları yalnızca sahibi tarafından yönetilebilir." }, "manage": { - "label": "Üstesinden gelmek", - "description": "Sunucuyu düzenleyin, başkalarıyla paylaşın, izin seviyelerini değiştirin ve erişimi iptal edin." + "label": "Yönet", + "description": "Düzenleme iznine ek olarak sunucuyu başkalarıyla paylaşabilir, izin düzeylerini değiştirebilir ve erişimi iptal edebilir." } }, - "expiryLabel": "Erişim süresinin dolması", + "expiryLabel": "Erişim süresi sonu", "expiry": { - "never": "Asla", + "never": "Süresiz", "oneHour": "1 saat", "oneDay": "24 saat", "sevenDays": "7 gün", "thirtyDays": "30 gün", - "custom": "Gelenek" + "custom": "Özel" }, - "customHoursPlaceholder": "Erişim süresinin dolmasına kalan saatler", - "shareButton": "Paylaşmak", + "customHoursPlaceholder": "Erişim süresi (saat)", + "shareButton": "Paylaş", "shareWithCount": "Paylaş ({{count}})", "currentAccess": "Mevcut erişim", - "noAccessEntries": "Bu sunucu henüz paylaşılmadı.", - "grantedBy": "tarafından verilmiştir.", - "expires": "Süresi doluyor", - "expired": "Günü geçmiş", - "never": "Asla", - "revoke": "Geri çekmek", - "accessUpdated": "Güncellenmiş erişim", - "accessUpdateFailed": "Erişim bilgilerini güncelleme başarısız oldu.", + "noAccessEntries": "Bu sunucu henüz paylaşılmadı", + "folderShareSummary": "Bu klasördeki {{total}} ana bilgisayarının paylaşılan {{shared}} dosyası", + "grantedBy": "Veren", + "expires": "Bitiş", + "expired": "Süresi dolmuş", + "never": "Hiçbir zaman", + "revoke": "Geri al", + "accessUpdated": "Erişim güncellendi", + "accessUpdateFailed": "Erişim güncellenemedi", "sharedBadge": "Paylaşıldı", - "sharedBadgeTooltip": "{{owner}} tarafından paylaşıldı ({{level}} erişim)", - "viewOnlyBanner": "Bu sunucu, {{owner}} tarafından sizinle görüntüleme erişimiyle paylaşılıyor. Yapılandırma yalnızca okunabilir durumdadır.", - "sharedEditBanner": "Bu sunucu, düzenleme erişimiyle {{owner}} tarafından sizinle paylaşılmaktadır. Değişiklikler gerçek sunucuya uygulanır; kimlik doğrulama referansları yalnızca sahibi tarafından değiştirilebilir.", - "ownerOnlyControl": "Bunu yalnızca ev sahibi değiştirebilir." + "sharedBadgeTooltip": "{{owner}} tarafından paylaşıldı ({{level}} erişimi)", + "viewOnlyBanner": "Bu sunucu {{owner}} tarafından görüntüleme erişimiyle paylaşıldı. Yapılandırma salt okunur.", + "sharedEditBanner": "Bu sunucu {{owner}} tarafından düzenleme erişimiyle paylaşıldı. Değişiklikler gerçek sunucuya uygulanır; kimlik doğrulama referansları yalnızca sahibi tarafından değiştirilebilir.", + "ownerOnlyControl": "Bunu yalnızca sunucu sahibi değiştirebilir" }, "guac": { "connection": "Bağlantı", "authentication": "Kimlik doğrulama", - "storedCredential": "Kaydedilmiş Kimlik Bilgileri", - "noCredential": "Herhangi bir kimlik belgesi gerekmiyor (doğrudan kimlik bilgileri aşağıda).", - "authMethod": "Kimlik Doğrulama Yöntemi", + "storedCredential": "Kayıtlı kimlik bilgisi", + "noCredential": "Kimlik bilgisi yok (aşağıda doğrudan kimlik bilgileri)", + "authMethod": "Kimlik doğrulama yöntemi", "authTypeDirect": "Doğrudan", - "authTypeCredential": "Kimlik belgesi", - "selectCredential": "Bir yeterlilik belgesi seçin...", - "connectionSettings": "Bağlantı Ayarları", - "displaySettings": "Ekran Ayarları", - "audioSettings": "Ses Ayarları", - "rdpPerformance": "RDP Performansı", - "deviceRedirection": "Cihaz Yönlendirmesi", + "authTypeCredential": "Kimlik bilgisi", + "authTypeNone": "Hiçbiri", + "authTypeNoneDesc": "Hiçbir kimlik bilgisi saklanmaz. Her bağlantı kurduğunuzda sizden kullanıcı adı ve şifre istenecektir; bunlar kaydedilmez.", + "selectCredential": "Bir kimlik bilgisi seçin...", + "connectionSettings": "Bağlantı ayarları", + "displaySettings": "Görüntü ayarları", + "audioSettings": "Ses ayarları", + "rdpPerformance": "RDP performansı", + "deviceRedirection": "Cihaz yönlendirme", "session": "Oturum", - "gateway": "Geçit", + "gateway": "Ağ geçidi", "remoteApp": "RemoteApp", "clipboard": "Pano", - "sessionRecording": "Oturum Kaydı", - "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC Ayarları", - "terminalSettings": "Terminal Ayarları", - "rdpPort": "RDP Bağlantı Noktası", - "username": "Kullanıcı adı", - "password": "Şifre", - "domain": "İhtisas", + "sessionRecording": "Oturum kaydı", + "wakeOnLan": "LAN üzerinden uyandırma", + "vncSettings": "VNC ayarları", + "terminalSettings": "Terminal ayarları", + "rdpPort": "RDP Portu", + "username": "Kullanıcı Adı", + "password": "Parola", + "passwordSaved": "Parola kaydedildi, değiştirmek için tuşlayın.", + "domain": "Etki Alanı", "securityMode": "Güvenlik Modu", "colorDepth": "Renk Derinliği", "width": "Genişlik", "height": "Yükseklik", "dpi": "DPI", "resizeMethod": "Yeniden Boyutlandırma Yöntemi", - "clientName": "Müşteri Adı", + "clientName": "İstemci Adı", "initialProgram": "Başlangıç Programı", "serverLayout": "Sunucu Düzeni", - "timezone": "Saat dilimi", + "timezone": "Saat Dilimi", "loadBalanceInfo": "Yük Dengeleme Bilgisi / Çerez", - "loadBalanceInfoDesc": "RDS çiftliği yük dengelemesi için RD Bağlantı Aracısı çerezi (örneğin tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfoDesc": "RDS grubu yük dengelemesi için RD Bağlantı Aracısı çerezi (ör. tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacd Proxy", - "guacdHostname": "guacd Host", - "guacdHostnamePlaceholder": "Küresel varsayılan", - "guacdPort": "Guacd Limanı", - "guacdProxyDesc": "Bu bağlantı için genel guacd örneğini geçersiz kılın. Genel olarak yapılandırılmış guacd'yi kullanmak için boş bırakın.", + "guacdHostname": "guacd Sunucusu", + "guacdHostnamePlaceholder": "Genel varsayılan", + "guacdPort": "guacd Portu", + "guacdProxyDesc": "Bu bağlantı için genel guacd örneğini geçersiz kıl. Genel olarak yapılandırılmış guacd'yi kullanmak için boş bırak.", "gatewayHostname": "Ağ Geçidi Ana Bilgisayar Adı", - "gatewayPort": "Ağ Geçidi Bağlantı Noktası", + "gatewayPort": "Ağ Geçidi Portu", "gatewayUsername": "Ağ Geçidi Kullanıcı Adı", - "gatewayPassword": "Ağ Geçidi Şifresi", - "gatewayDomain": "Ağ Geçidi Alanı", + "gatewayPassword": "Ağ Geçidi Parolası", + "gatewayDomain": "Ağ Geçidi Etki Alanı", "remoteAppProgram": "RemoteApp Programı", "workingDirectory": "Çalışma Dizini", - "arguments": "Tartışmalar", + "arguments": "Argümanlar", "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)", + "udpPort": "UDP Portu", + "waitTimeS": "Bekleme Süresi (sn)", "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)", + "drivePath": "Sürücü Yolu", + "ignoreCertificate": "Sertifikayı Yoksay", + "ignoreCertificateDesc": "Kendinden imzalı sertifikalara sahip sunuculara bağlantılara izin ver", + "forceLossless": "Kayıpsız Zorla", + "forceLosslessDesc": "Kayıpsız görüntü kodlamasını 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", + "disableAudioDesc": "Uzak oturumdaki tüm sesleri kapat", + "enableAudioInput": "Ses Girişini Etkinleştir (Mikrofon)", + "enableAudioInputDesc": "Yerel mikrofonu uzak oturuma yönlendir", "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", + "theming": "Tema", + "themingDesc": "Görsel temaları ve stilleri etkinleştir", "fontSmoothing": "Yazı Tipi Yumuşatma", - "fontSmoothingDesc": "ClearType yazı tipinin oluşturulmasını etkinleştirin", + "fontSmoothingDesc": "ClearType yazı tipi işlemeyi etkinleştir", "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", + "desktopComposition": "Masaüstü Birleştirme", + "desktopCompositionDesc": "Aero cam efektlerini etkinleştir", "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.", + "menuAnimationsDesc": "Menü solma ve kayma animasyonlarını etkinleştir", + "disableBitmapCaching": "Bitmap Önbelleğini Devre Dışı Bırak", + "disableBitmapCachingDesc": "Bitmap önbelleğini kapat (grafik bozulmalarına yardımcı olabilir)", + "disableOffscreenCaching": "Ekran Dışı Önbelleğini Devre Dışı Bırak", + "disableOffscreenCachingDesc": "Ekran dışı önbelleğini kapat", + "disableGlyphCaching": "Glif Önbelleğini Devre Dışı Bırak", + "disableGlyphCachingDesc": "Glif önbelleğini kapat", + "enableGfx": "GFX'i Etkinleştir", + "enableGfxDesc": "RemoteFX grafik işlem hattını kullan", "enablePrinting": "Yazdırmayı Etkinleştir", - "enablePrintingDesc": "Yerel yazıcıları uzak oturuma yönlendirin.", + "enablePrintingDesc": "Yerel yazıcıları uzak oturuma yönlendir", "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.", + "enableDriveRedirectionDesc": "Yerel bir klasörü uzak oturumda sürücü olarak eşleştir", + "createDrivePath": "Sürücü Yolu Oluştur", + "createDrivePathDesc": "Klasör mevcut değilse otomatik oluştur", "disableDownload": "İndirmeyi Devre Dışı Bırak", - "disableDownloadDesc": "Uzak oturumdan dosya indirilmesini engelleyin.", + "disableDownloadDesc": "Uzak oturumdan dosya indirmeyi engelle", "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", + "disableUploadDesc": "Uzak oturuma dosya yüklemeyi engelle", + "enableTouch": "Dokunmayı Etkinleştir", + "enableTouchDesc": "Dokunma girişi iletimini etkinleştir", "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.", + "consoleSessionDesc": "Yeni oturum yerine konsola (oturum 0) bağlan", + "sendWolPacket": "WOL Paketi Gönder", + "sendWolPacketDesc": "Bağlanmadan önce bu sunucuyu uyandırmak için sihirli paket gönder", "disableCopy": "Kopyalamayı Devre Dışı Bırak", - "disableCopyDesc": "Uzak oturumdan metin kopyalamayı engelleyin.", + "disableCopyDesc": "Uzak oturumdan metin kopyalamayı engelle", "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", + "disablePasteDesc": "Uzak oturuma metin yapıştırmayı engelle", + "createPathIfMissing": "Yol Yoksa Oluştur", + "createPathIfMissingDesc": "Kayıt dizinini otomatik oluştur", "excludeOutput": "Çıktıyı Hariç Tut", - "excludeOutputDesc": "Ekran çıktısını kaydetmeyin (yalnızca meta veriler).", + "excludeOutputDesc": "Ekran çıktısını kaydetme (yalnızca meta veri)", "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", + "excludeMouseDesc": "Fare hareketlerini kaydetme", + "includeKeystrokes": "Tuş Vuruşlarını Dahil Et", + "includeKeystrokesDesc": "Ekran çıktısına ek olarak ham tuş vuruşlarını kaydet", + "vncPort": "VNC Portu", + "vncPassword": "VNC Parolası", "vncUsernameOptional": "Kullanıcı adı (isteğe bağlı)", - "vncLeaveBlank": "Gerekli değilse boş bırakın.", + "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.", - "telnetPort": "Telnet Port", - "terminalType": "Terminal Tipi", + "swapRedBlue": "Kırmızı ve Maviyi Değiştir", + "swapRedBlueDesc": "Kırmızı ve mavi renk kanallarını değiştir (bazı renk sorunlarını giderir)", + "readOnly": "Salt okunur", + "readOnlyDesc": "Hiçbir giriş göndermeden uzak ekranı görüntüle", + "telnetPort": "Telnet Portu", + "terminalType": "Terminal Türü", "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.", - "permissionLevel": "İzin Seviyesi", - "typeHeader": "Tip", + "backspaceKey": "Geri Silme Tuşu", + "saveHostFirst": "Önce ana makineyi kaydedin.", + "sharingOptionsAfterSave": "Paylaşım seçenekleri ana makine kaydedildikten sonra kullanılabilir.", + "permissionLevel": "İzin Düzeyi", + "typeHeader": "Tür", "targetHeader": "Hedef", "permissionHeader": "İzin", - "cancelBtn": "İptal etmek", - "savingBtn": "Tasarruf...", - "updateHostBtn": "Sunucuyu Güncelle", - "addHostBtn": "Sunucu Ekle" + "cancelBtn": "İptal", + "savingBtn": "Kaydediliyor...", + "updateHostBtn": "Ana Makineyi Güncelle", + "addHostBtn": "Ana Makine Ekle" } }, "commandPalette": { - "searchPlaceholder": "Sunucuları, komutları veya ayarları arayın...", + "searchPlaceholder": "Ana makineleri, komutları veya ayarları ara...", "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", + "hostManager": "Ana Makine Yöneticisi", + "hostManagerDesc": "Ana makineleri yönet, ekle veya düzenle", + "addNewHost": "Yeni Ana Makine Ekle", + "addNewHostDesc": "Yeni bir ana makine kaydet", "adminSettings": "Yönetici Ayarları", - "adminSettingsDesc": "Sistem tercihlerini ve kullanıcıları yapılandırın.", + "adminSettingsDesc": "Sistem tercihlerini ve kullanıcıları yapılandır", "userProfile": "Kullanıcı Profili", - "userProfileDesc": "Hesabınızı ve tercihlerinizi yönetin", + "userProfileDesc": "Hesabını ve tercihlerini yönet", "addCredential": "Kimlik Bilgisi Ekle", - "addCredentialDesc": "SSH anahtarlarını veya şifrelerini saklayın.", - "tmuxMonitor": "Tmux Monitörü", - "tmuxMonitorDesc": "Sunucularınızdaki tmux oturumlarını izleyin.", - "recentActivity": "Son Aktiviteler", - "serversAndHosts": "Sunucular ve Hostlar", - "noHostsFound": "\"{{search}} \" ile eşleşen hiçbir sunucu bulunamadı.", + "addCredentialDesc": "SSH anahtarları veya parolaları sakla", + "tmuxMonitor": "Tmux İzleyici", + "tmuxMonitorDesc": "Ana makinelerinizdeki tmux oturumlarını izle", + "recentActivity": "Son Etkinlik", + "serversAndHosts": "Sunucular ve Ana Makineler", + "noHostsFound": "\"{{search}}\" ile eşleşen ana makineler bulunamadı", "links": "Bağlantılar", - "navigate": "Gezinti", - "select": "Seçme", - "toggleWith": "ile değiştir" + "navigate": "Gezin", + "select": "Seç", + "toggleWith": "Geçiş Yap" }, "splitScreen": { "paneEmpty": "Bölme {{index}} - boş", - "noTabAssigned": "Hiçbir sekme atanmadı.", - "focusedPane": "Aktif bölme" + "noTabAssigned": "Sekme atanmadı", + "focusedPane": "Etkin bölme" }, "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.", + "noConnections": "Bağlantı yok", + "noConnectionsDesc": "Burada bağlantıları görmek için bir terminal, dosya yöneticisi veya uzak masaüstü açın.", + "connectedFor": "{{duration}} süredir bağlı", "connected": "Bağlı", "disconnected": "Bağlantı kesildi", "closeTab": "Sekmeyi kapat", - "closeConnection": "Yakın bağlantı", - "forgetTab": "Unutmak", - "removeBackground": "Kaldırmak", + "closeConnection": "Bağlantıyı kapat", + "forgetTab": "Unut", + "removeBackground": "Kaldır", "reconnect": "Yeniden bağlan", - "reopenTab": "Yeniden açın", + "reopenTab": "Yeniden aç", "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ı.", - "rename": "Oturumu yeniden adlandır" + "sectionBackground": "Arka Plan", + "backgroundDesc": "Oturumlar bağlantı kesildikten sonra 30 dakika boyunca arka planda kalır ve yeniden bağlanabilir.", + "persisted": "Arka planda saklanıyor", + "expiresIn": "{{duration}} sonra süresi dolacak", + "search": "Bağlantıları ara...", + "noSearchResults": "Aramanızla eşleşen bağlantı yok", + "rename": "Oturumu yeniden adlandır", + "sectionSharedWithMe": "Benimle paylaşıldı", + "sharedBy": "{{username}} tarafından paylaşıldı", + "join": "Katılmak", + "sharedSessionLabel": "{{hostName}} (paylaşılan)" + }, + "sessionSharing": { + "guestView": { + "loading": "Paylaşılan oturuma bağlanılıyor...", + "linkInvalid": "Bu paylaşım bağlantısı geçersiz, süresi dolmuş veya iptal edilmiş.", + "rateLimited": "Çok fazla deneme yapıldı, lütfen kısa süre sonra tekrar deneyin.", + "sessionEnded": "Bu oturum sona erdi.", + "readOnlyBadge": "Yalnızca görüntüle" + }, + "modalTitle": "Oturumu paylaş", + "shareButton": "Paylaşmak", + "notReadyToShare": "Oturum henüz paylaşılmaya hazır değil.", + "modeTab": { + "link": "Bağlantı", + "user": "Kullanıcı" + }, + "linkModeDescription": "Bu bağlantıya sahip herkes katılabilir, hesap gerekmez.", + "userModeDescription": "Bu sunucuya zaten erişimi olan belirli bir kullanıcıyla paylaşın. Henüz erişimleri yoksa, önce sunucuyu onlarla paylaşın veya bunun yerine bir bağlantı kullanın. Paylaşıldıktan sonra, oturum Bağlantılar sekmesinde görünür.", + "permissionLevel": { + "label": "İzin seviyesi", + "readOnly": "salt okunur", + "readOnlyDescription": "Oturumu canlı olarak izleyebilirsiniz ancak yazı yazamaz veya etkileşimde bulunamazsınız.", + "readWrite": "Okuma-yazma", + "readWriteDescription": "Oturum sahibi gibi yazabilir ve oturumla etkileşim kurabilirsiniz." + }, + "expiryLabel": "Bağlantı süresinin dolması", + "createLinkButton": "Bağlantı oluştur", + "createShareButton": "Kullanıcıyla paylaş", + "searchUsersPlaceholder": "Kullanıcıları ara...", + "noUsersFound": "Hiç kullanıcı bulunamadı.", + "linkCreated": "Paylaşım bağlantısı oluşturuldu.", + "linkCopied": "Bağlantı panoya kopyalandı", + "copyLink": "Bağlantıyı kopyala", + "shareCreated": "Oturum paylaşıldı. Bu, onların Bağlantılar sekmesinde görünecektir.", + "shareFailed": "Paylaşım oluşturulamadı.", + "userLacksHostAccess": "Bu kullanıcının henüz bu sunucuya erişimi yok. Öncelikle sunucuyu onlarla paylaşın veya bunun yerine bir bağlantı kullanın.", + "activeShares": "Aktif hisseler", + "noActiveShares": "Bu oturum için aktif hisse senedi bulunmamaktadır.", + "revoke": "Geri çekmek", + "revokeConfirmTitle": "Bu hisseyi iptal etmek ister misiniz?", + "revokeConfirmDescription": "Bu paylaşımı kullanan herkes erişimini anında kaybedecektir.", + "revoked": "Hisseler geri alındı", + "revokeFailed": "Hisseleri geri alma işlemi başarısız oldu.", + "joinCount": "{{count}} katıl", + "joinCount_other": "{{count}} birleşiyor", + "expiresAt": "Süresi doluyor {{date}}", + "linkShareBadge": "Bağlantı", + "userShareBadge": "Kullanıcı: {{username}}", + "loadSharesFailed": "Aktif paylaşımlar yüklenemedi." }, "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.", + "connectionFailed": "Bağlantı başarısız", + "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.", + "retry": "Yeniden 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 düzgün yapılandırıldığından emin olun.", + "credentialPromptTitle": "RDP Kimlik Bilgilerini Girin", + "credentialPromptDescription": "Bu sunucu, bağlantı kurulurken kimlik bilgilerini isteyecek şekilde ayarlanmıştır. Bu bilgiler yalnızca bu oturum için kullanılır ve kaydedilmez.", + "connect": "Bağlamak", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Kilit Ekranı)", - "winKey": "Windows Anahtarı", + "winL": "Win+L (Ekranı Kilitle)", + "winKey": "Windows Tuşu", "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ğı", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (sabitlenmiş - bırakmak için tıklayın)", + "stickyInactive": "{{key}} (sabitlemek için tıklayın)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", "arrowUp": "Yukarı Ok", "arrowDown": "Aşağı Ok", - "arrowLeft": "Ok Sol", + "arrowLeft": "Sol Ok", "arrowRight": "Sağ Ok", "fnToggle": "Fonksiyon Tuşları", - "reconnect": "Yeniden Bağlantı Oturumu", + "reconnect": "Oturumu Yeniden Bağla", "collapse": "Araç çubuğunu daralt", - "expand": "Araç çubuğunu genişletin", - "dragHandle": "Konumunu değiştirmek için sürükleyin." + "expand": "Araç çubuğunu genişlet", + "dragHandle": "Yeniden konumlandırmak için sürükle", + "switchToTrackpad": "İzleme dörtlüsü moduna geçin (imleci hareket ettirmek için sürükleyin, tıklamak için dokunun)", + "switchToTouch": "Dokunmatik moda geçin (tıklamak istediğiniz yere doğrudan dokunun)" } }, "terminal": { - "connect": "Sunucuya bağlan", - "clear": "Temizlemek", + "connect": "Sunucuya Bağlan", + "clear": "Temizle", "paste": "Yapıştır", - "reconnect": "Yeniden bağlan", + "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.", - "passwordPromptFillTitle": "Kaydedilen şifreyi bu alana girin lütfen.", - "unknownError": "Bilinmeyen bir hata oluştu.", + "connected": "Bağlandı", + "clipboardWriteFailed": "Panoya kopyalama başarısız oldu. Sayfanın HTTPS veya localhost üzerinden sunulduğundan emin olun.", + "clipboardReadFailed": "Panodan okuma başarısız oldu. Pano izinlerinin verildiğinden emin olun.", + "clipboardHttpWarning": "Yapıştırma HTTPS gerektirir. Ctrl+Shift+V kullanın veya Termix'i HTTPS üzerinden sunun.", + "passwordPromptFillTitle": "Kayıtlı parola bu isteme doldurulsun mu?", + "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.", + "noHostSelected": "Hiçbir sunucu seçilmedi", + "reconnecting": "Yeniden bağlanıyor... ({{attempt}}/{{max}})", + "reconnected": "Başarıyla yeniden bağlandı", "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.", + "tmuxSessionAttached": "tmux oturumuna bağlanıldı: {{name}}", + "tmuxUnavailable": "Uzak sunucuda tmux yüklü değil, standart kabuğa 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.", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "Bu sunucuda mevcut tmux oturumları bulundu. Yeniden bağlanmak için birini seçin veya yeni bir oturum oluşturun.", + "tmuxWindows": "Pencereler", "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", + "tmuxAttached": "Bağlı istemciler", + "tmuxAttachedCount": "{{count}} bağlı", + "tmuxLastActivity": "Son etkinlik", + "tmuxTimeJustNow": "az önce", + "tmuxTimeMinutes": "{{count}}dk önce", + "tmuxTimeHours": "{{count}}s önce", + "tmuxTimeDays": "{{count}}g ö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ı", + "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ğlanma denemesine ulaşıldı", + "closeTab": "Kapat", "connectionTimeout": "Bağlantı zaman aşımı", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Çalıştırılıyor {{command}} - {{host}}", + "runTitle": "Çalıştırılıyor: {{command}} - {{host}}", "totpRequired": "İki Faktörlü Kimlik Doğrulama Gerekli", "totpCodeLabel": "Doğrulama Kodu", - "totpVerify": "Doğrulamak", + "totpVerify": "Doğrula", + "mfaPromptRequired": "Kimlik Doğrulama Gerekli", + "mfaPushRequired": "Anlık Kimlik Doğrulama Gerekli", + "mfaMenuPlaceholder": "Yanıtınızı girin", + "mfaWaitingApproval": "Cihazınız için onay bekleniyor...", + "mfaSendRequest": "İstek Gönder", "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.", + "warpgateOpenBrowser": "Tarayıcıda Aç", + "warpgateContinue": "Kimlik Doğrulamayı 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.", + "opksshAuthDescription": "Devam etmek için tarayıcıda kimlik doğrulamasını tamamlayın. Bu oturum 24 saat geçerli olacak.", + "opksshOpenBrowser": "Kimlik Doğrulaması için Tarayıcı Aç", "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", - "vaultAuthTitle": "Kasa oturum açma işlemi gereklidir.", - "vaultAuthDescription": "HashiCorp Vault'a giriş yapmak için bir pencere açıldı. Lütfen orada giriş işlemini tamamlayın; bağlantı otomatik olarak devam edecektir.", - "vaultAuthFailed": "Kasa kimlik doğrulaması başarısız oldu. Lütfen tekrar deneyin.", - "vaultReopen": "Giriş penceresini yeniden açın", - "sudoPasswordPopupTitle": "Şifrenizi girin?", + "opksshAuthenticating": "Kimlik doğrulaması işleniyor...", + "opksshTimeout": "Kimlik doğrulaması zaman aşımına uğradı. Lütfen tekrar deneyin.", + "opksshAuthFailed": "Kimlik doğrulaması başarısız oldu. Lütfen kimlik bilgilerinizi kontrol edip tekrar deneyin.", + "opksshSignInWith": "{{provider}} ile oturum aç", + "vaultAuthTitle": "Vault Oturum Açması Gerekli", + "vaultAuthDescription": "HashiCorp Vault'ta oturum açmak için bir pencere açıldı. Orada oturum açmayı tamamlayın; bu bağlantı otomatik olarak devam edecek.", + "vaultAuthFailed": "Vault kimlik doğrulaması başarısız oldu. Lütfen tekrar deneyin.", + "vaultReopen": "Oturum açma penceresini yeniden aç", + "sudoPasswordPopupTitle": "Parola Girin?", "linkDialogTitle": "Bağlantıyı Aç", - "linkDialogOpen": "Açık", + "linkDialogOpen": "Aç", "linkDialogCopy": "Kopyala", - "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.", + "websocketAbnormalClose": "Bağlantı beklenmedik şekilde kapandı. Bu, ters proxy veya SSL yapılandırma sorunundan kaynaklanabilir. 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.", + "connectionLogEmpty": "Henüz bağlantı günlüğü 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.", + "connectionLogCopyFailed": "Günlükler panoya kopyalanamadı", + "connectionRejected": "Bağlantı sunucu tarafından reddedildi. Lütfen kimlik doğrulamanızı ve ağ yapılandırmanızı kontrol edin.", + "hostKeyRejected": "SSH sunucu anahtarı doğrulaması reddedildi. Bağlantı iptal edildi.", "sessionTakenOver": "Oturum başka bir sekmede açıldı. Yeniden bağlanılıyor...", "split": { - "splitTab": "Bölünmüş Sekme", - "addToSplit": "Bölünmüş bölüme ekle", - "removeFromSplit": "Bölünmüş halden çıkarın" + "splitTab": "Sekmeyi Böl", + "addToSplit": "Bölünmüşe Ekle", + "removeFromSplit": "Bölünmüşten Kaldır" } }, "fileManager": { - "noHostSelected": "Sunucu seçilmedi.", - "initializingEditor": "Editör başlatılıyor...", + "noHostSelected": "Hiçbir sunucu seçilmedi", + "initializingEditor": "Düzenleyici başlatılıyor...", "file": "Dosya", - "folder": "Dosya", + "folder": "Klasör", "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.", + "downloadFile": "İndir", + "extractArchive": "Arşivi Çıkar", + "extractingArchive": "{{name}} çıkartılıyor...", + "archiveExtractedSuccessfully": "{{name}} başarıyla çıkartıldı.", + "extractFailed": "Çıkartma başarısız.", "compressFile": "Dosyayı Sıkıştır", "compressFiles": "Dosyaları Sıkıştır", - "compressFilesDesc": "{{count}} öğeyi bir arşive sıkıştırın", + "compressFilesDesc": "{{count}} öğeyi arşive sıkıştır", "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", + "compressionFormat": "Sıkıştırma Biçimi", + "selectedFiles": "Seçili dosyalar", + "andMoreFiles": "ve {{count}} tane daha...", + "compress": "Sıkıştır", + "compressingFiles": "{{count}} öğe {{name}} içine sıkıştırılıyor...", + "filesCompressedSuccessfully": "{{name}} başarıyla oluşturuldu.", + "compressFailed": "Sıkıştırma başarısız.", + "edit": "Düzenle", + "preview": "Önizle", + "previous": "Önceki", "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", + "rename": "Yeniden Adlandır", "uploading": "Yükleniyor...", "uploadingFile": "{{name}} yükleniyor...", - "fileName": "Dosya adı", + "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.", + "fileUploadedSuccessfully": "\"{{name}}\" dosyası başarıyla yüklendi.", + "failedToUploadFile": "Dosya yüklenemedi.", + "fileDownloadedSuccessfully": "\"{{name}}\" dosyası başarıyla indirildi.", + "failedToDownloadFile": "Dosya indirilemedi.", + "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: {{error}}", + "failedToResolveSymlink": "Sembolik bağlantı çözümlenemedi", "itemsDeletedSuccessfully": "{{count}} öğe başarıyla silindi", - "failedToDeleteItems": "Öğeleri silme işlemi başarısız oldu.", + "failedToDeleteItems": "Öğeler silinemedi", "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.", + "enterSudoPassword": "Bu işleme devam etmek için sudo parolasını girin", + "sudoPassword": "Sudo parolası", + "sudoOperationFailed": "Sudo işlemi başarısız", + "sudoAuthFailed": "Sudo kimlik doğrulaması başarısız", + "dragFilesToUpload": "Yüklemek için dosyaları buraya bırakın", + "emptyFolder": "Bu klasör boş", + "searchFiles": "Dosya ara...", + "upload": "Yükle", + "selectHostToStart": "Dosya yönetimini başlatmak için bir sunucu seçin", + "sshRequiredForFileManager": "Dosya yöneticisi SSH gerektirir. Bu sunucuda SSH etkin değil.", + "failedToConnect": "SSH'ye bağlanılamadı", + "failedToLoadDirectory": "Dizin yüklenemedi", + "noSSHConnection": "SSH bağlantısı yok", "copy": "Kopyala", - "cut": "Kesmek", + "cut": "Kes", "paste": "Yapıştır", - "copyPath": "Kopyala Yolu", - "copyPaths": "Kopyala Yolları", - "delete": "Silmek", + "copyPath": "Yolu Kopyala", + "copyPaths": "Yolları Kopyala", + "delete": "Sil", "properties": "Özellikler", "refresh": "Yenile", - "downloadFiles": "{{count}} dosyayı Tarayıcıya İndir", + "downloadFiles": "{{count}} dosyayı tarayıcıya indir", "copyFiles": "{{count}} öğeyi kopyala", "cutFiles": "{{count}} öğeyi kes", "deleteFiles": "{{count}} öğeyi sil", "filesCopiedToClipboard": "{{count}} öğe panoya kopyalandı", - "filesCutToClipboard": "{{count}} öğe panoya kopyalandı", + "filesCutToClipboard": "{{count}} öğe panoya kesildi", "pathCopiedToClipboard": "Yol panoya kopyalandı", - "pathsCopiedToClipboard": "{{count}} yollar panoya kopyalandı", - "failedToCopyPath": "Yol kopyalanıp panoya aktarılamadı.", - "copyFolderLink": "Klasöre Bağlantıyı Kopyala", - "copyCurrentFolderLink": "Geçerli Klasöre Bağlantıyı Kopyala", + "pathsCopiedToClipboard": "{{count}} yol panoya kopyalandı", + "failedToCopyPath": "Yol panoya kopyalanamadı", + "copyFolderLink": "Klasör Bağlantısını Kopyala", + "copyCurrentFolderLink": "Geçerli Klasörün Bağlantısını Kopyala", "folderLinkCopied": "Klasör bağlantısı panoya kopyalandı", - "failedToCopyFolderLink": "Klasör bağlantısı kopyalanamadı.", + "failedToCopyFolderLink": "Klasör bağlantısı kopyalanamadı", "movedItems": "{{count}} öğe taşındı", - "failedToDeleteItem": "Öğeyi silme işlemi başarısız oldu.", + "failedToDeleteItem": "Öğe silinemedi", "itemRenamedSuccessfully": "{{type}} başarıyla yeniden adlandırıldı", - "failedToRenameItem": "Öğeyi yeniden adlandırma başarısız oldu.", - "download": "İndirmek", - "openExternalEditor": "Harici olarak açın", - "chooseExternalEditor": "Harici düzenleyiciyi seçin", - "externalEditorSelected": "Harici editör seçildi", - "externalEditorDesktopOnly": "Harici düzenleyici yalnızca masaüstü uygulamasında mevcuttur.", - "externalEditorOpened": "Harici bir düzenleyicide açıldı. Orada kaydetmek, değişiklikleri sunucuya geri yükleyecektir.", - "failedToOpenExternalEditor": "Harici düzenleyici açılamadı.", - "failedToSelectExternalEditor": "Harici düzenleyici seçilemedi.", + "failedToRenameItem": "Öğe yeniden adlandırılamadı", + "download": "İndir", + "openExternalEditor": "Harici Olarak Aç", + "chooseExternalEditor": "Harici düzenleyici seç", + "externalEditorSelected": "Harici düzenleyici seçildi", + "externalEditorDesktopOnly": "Harici düzenleyici yalnızca masaüstü uygulamasında kullanılabilir", + "externalEditorOpened": "Harici düzenleyicide açıldı. Orada kaydetmek değişiklikleri sunucuya yükler.", + "failedToOpenExternalEditor": "Harici düzenleyici açılamadı", + "failedToSelectExternalEditor": "Harici düzenleyici seçilemedi", "permissions": "İzinler", "size": "Boyut", - "modified": "Değiştirildi", + "modified": "Değiştirilme", "path": "Yol", - "confirmDelete": "{{name}} öğesini silmek istediğinizden emin misiniz?", + "confirmDelete": "{{name}} öğesini silmek istediğinize 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", + "fileSavedSuccessfully": "Dosya başarıyla kaydedildi", + "failedToSaveFile": "Dosya kaydedilemedi", + "confirmDeleteSingleItem": "\"{{name}}\" öğesini kalıcı olarak silmek istediğinize emin misiniz?", + "confirmDeleteMultipleItems": "{{count}} öğeyi kalıcı olarak silmek istediğinize emin misiniz?", + "confirmDeleteMultipleItemsWithFolders": "{{count}} öğeyi kalıcı olarak silmek istediğinize emin misiniz? Bu, klasörleri ve içeriklerini de kapsar.", + "confirmDeleteFolder": "\"{{name}}\" klasörünü ve tüm içeriğini kalıcı olarak silmek istediğinize emin misiniz?", + "permanentDeleteWarning": "Bu işlem geri alınamaz. Öğe(ler) sunucudan kalıcı olarak silinecek.", + "recent": "Son Kullanılanlar", + "pinned": "Sabitlenmiş", "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.", + "failedToReconnectSSH": "SSH oturumuna yeniden bağlanılamadı", + "openTerminalHere": "Terminali Burada Aç", + "run": "Çalıştır", + "openTerminalInFolder": "Bu Klasörde Terminal Aç", + "openTerminalInFileLocation": "Dosya Konumunda Terminal Aç", + "runningFile": "Çalıştırılıyor - {{file}}", + "onlyRunExecutableFiles": "Yalnızca çalıştırılabilir dosyalar çalıştırılabilir", "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ş öğe kaldırıldı", - "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 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", + "removedFromRecentFiles": "\"{{name}}\" son dosyalardan kaldırıldı", + "removeFailed": "Kaldırma başarısız", + "unpinnedSuccessfully": "\"{{name}}\" sabitlemesi başarıyla kaldırıldı", + "unpinFailed": "Sabitleme kaldırma başarısız", + "removedShortcut": "\"{{name}}\" kısayolu kaldırıldı", + "removeShortcutFailed": "Kısayol kaldırma başarısız", + "clearedAllRecentFiles": "Tüm son dosyalar temizlendi", + "clearFailed": "Temizleme başarısız", + "removeFromRecentFiles": "Son dosyalardan kaldır", + "clearAllRecentFiles": "Tüm son dosyaları temizle", + "unpinFile": "Dosya sabitlemesini kaldır", "removeShortcut": "Kısayolu kaldır", - "pinFile": "Pin dosyası", + "pinFile": "Dosyayı sabitle", "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", + "pasteFailed": "Yapıştırma başarısız", + "noUndoableActions": "Geri alınabilir işlem yok", + "undoCopySuccess": "Kopyalama işlemi geri alındı: {{count}} kopyalanmış dosya silindi", + "undoCopyFailedDelete": "Geri alma başarısız: Hiçbir kopyalanmış dosya silinemedi", + "undoCopyFailedNoInfo": "Geri alma başarısız: Kopyalanmış dosya bilgisi bulunamadı", + "undoMoveSuccess": "Taşıma işlemi geri alındı: {{count}} dosya orijinal konumuna geri taşındı", + "undoMoveFailedMove": "Geri alma başarısız: Hiçbir dosya geri taşınamadı", + "undoMoveFailedNoInfo": "Geri alma başarısız: Taşınmış dosya bilgisi bulunamadı", + "undoDeleteNotSupported": "Silme işlemi geri alınamaz: Dosyalar sunucudan kalıcı olarak silindi", + "undoTypeNotSupported": "Desteklenmeyen geri alma işlem türü", + "undoOperationFailed": "Geri alma işlemi başarısız", "unknownError": "Bilinmeyen hata", - "confirm": "Onaylamak", - "find": "Bulmak...", - "replace": "Yer değiştirmek", - "downloadInstead": "Bunun yerine indirin", + "confirm": "Onayla", + "find": "Bul...", + "replace": "Değiştir", + "downloadInstead": "Bunun Yerine İndir", "keyboardShortcuts": "Klavye Kısayolları", - "searchAndReplace": "Arama ve Değiştirme", + "searchAndReplace": "Ara ve Değiştir", "editing": "Düzenleme", - "search": "Aramak", + "search": "Ara", "findNext": "Sonrakini Bul", "findPrevious": "Öncekini Bul", - "save": "Kaydetmek", + "save": "Kaydet", "selectAll": "Tümünü Seç", - "undo": "Geri al", - "redo": "Tekrarla", - "moveLineUp": "Hareket Sıralaması", + "undo": "Geri Al", + "redo": "Yinele", + "moveLineUp": "Satırı Yukarı Taşı", "moveLineDown": "Satırı Aşağı Taşı", "toggleComment": "Yorumu Aç/Kapat", - "autoComplete": "Otomatik Tamamlama", - "imageLoadError": "Görüntü yüklenemedi.", + "autoComplete": "Otomatik Tamamla", + "imageLoadError": "Görüntü yüklenemedi", "startTyping": "Yazmaya başlayın...", - "unknownSize": "Boyutu bilinmiyor", - "fileIsEmpty": "Dosya boş.", + "unknownSize": "Bilinmeyen boyut", + "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ı.", + "largeFileWarningDesc": "Bu dosya {{size}} boyutunda, metin olarak açıldığında performans sorunlarına neden olabilir.", + "fileNotFoundAndRemoved": "\"{{name}}\" dosyası bulunamadı ve son kullanılanlar/sabitlenmiş dosyalardan 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", + "autoSaveFailed": "Otomatik kaydetme başarısız oldu", + "fileAutoSaved": "Dosya otomatik 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.", + "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ı.", + "pinFileFailed": "Dosya sabitleme başarısız oldu", + "fileUnpinnedSuccessfully": "\"{{name}}\" dosyasının sabitlemesi başarıyla kaldırıldı", + "unpinFileFailed": "Sabitlenmiş dosya 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.", + "addShortcutFailed": "Kısayol eklenemedi", + "operationCompletedSuccessfully": "{{count}} öğe başarıyla {{operation}}", + "operationCompleted": "{{count}} öğe {{operation}}", + "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)", + "moveTo": "{{name}} konumuna taşı", + "diffCompareWith": "{{name}} ile diff karşılaştır", + "dragOutsideToDownload": "İndirmek için pencere dışına sürükleyin ({{count}} dosya)", + "newFolderDefault": "Yeni Klasör", + "newFileDefault": "Yeni Dosya.txt", + "successfullyMovedItems": "{{count}} öğe başarıyla {{target}} konumuna taşındı", + "move": "Taşı", + "searchInFile": "Dosyada ara (Ctrl+F)", "showKeyboardShortcuts": "Klavye kısayollarını göster", - "decreaseFontSize": "Yazı tipi boyutunu küçültün", + "decreaseFontSize": "Yazı tipi boyutunu küçült", "increaseFontSize": "Yazı tipi boyutunu büyüt", "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}}", + "compare": "Karşılaştır", + "sideBySide": "Yan Yana", + "inline": "Satır İçi", + "fileComparison": "Dosya Karşılaştırması: {{file1}} vs {{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.", + "sshConnectionFailed": "SSH bağlantısı başarısız oldu. Lütfen {{name}} ({{ip}}:{{port}}) ile bağlantınızı kontrol edin", "loadFileFailed": "Dosya yüklenemedi: {{error}}", "connecting": "Bağlanıyor...", - "connectedSuccessfully": "Bağlantı başarıyla kuruldu.", + "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", - "incorrectPassphrase": "Geçersiz parola. Lütfen tekrar deneyin.", + "incorrectPassphrase": "Yanlış parola. Lütfen tekrar deneyin.", "verificationCodePrompt": "Doğrulama kodu:", "changePermissions": "İzinleri Değiştir", "currentPermissions": "Mevcut İzinler", - "owner": "Mal sahibi", + "owner": "Sahip", "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", + "read": "Okuma", + "write": "Yazma", + "execute": "Çalıştırma", + "permissionsChangedSuccessfully": "İzinler başarıyla değiştirildi", + "failedToChangePermissions": "İzinler değiştirilemedi", + "name": "Ad", + "sortByName": "Ad", "sortByDate": "Değiştirilme Tarihi", "sortBySize": "Boyut", - "ascending": "Yükselen", - "descending": "İnen", + "ascending": "Artan", + "descending": "Azalan", "root": "Kök", "new": "Yeni", - "sortBy": "Göre sırala", + "sortBy": "Sırala", "items": "Öğeler", - "selected": "Seçildi", - "editor": "Editör", - "octal": "Sekizli", - "storage": "Depolamak", + "selected": "Seçili", + "editor": "Düzenleyici", + "octal": "Sekizlik", + "storage": "Depolama", "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.", + "used": "Kullanılan", + "of": " / ", + "toggleSidebar": "Kenar Çubuğunu Göster/Gizle", + "cannotLoadPdf": "PDF yüklenemiyor", + "pdfLoadError": "Bu PDF dosyası yüklenirken bir hata oluştu.", "loadingPdf": "PDF yükleniyor...", "loadingPage": "Sayfa yükleniyor..." }, "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", + "moveToHost": "Sunucuya taşı…", + "copyItemsToHost": "{{count}} öğeyi sunucuya kopyala…", + "moveItemsToHost": "{{count}} öğeyi ana bilgisayara taşı…", + "noHostsConnected": "Kullanılabilir başka dosya yöneticisi ana bilgisayarı yok.", + "noHostsConnectedHint": "Ana Bilgisayar Yöneticisi'nde Dosya Yöneticisi etkinleştirilmiş başka bir SSH ana bilgisayarı ekleyin.", + "selectDestinationHost": "Hedef ana bilgisayarı 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", + "recentDestinations": "Son hedefler", + "collapseRecentDestinations": "Son hedefleri daralt", + "expandRecentDestinations": "Son hedefleri genişlet", + "browseFolders": "Hedef klasörlere göz at", + "browseDestination": "Göz at veya yol gir", "confirmCopy": "Kopyala", - "confirmMove": "Taşınmak", - "transferring": "… aktarılıyor", - "compressing": "… sıkıştırılıyor", - "extracting": "… çıkarılıyor", - "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}}", + "confirmMove": "Taşı", + "transferring": "Aktarılıyor…", + "compressing": "Sıkıştırılıyor…", + "extracting": "Çıkartılıyor…", + "transferringItems": "{{current}} / {{total}} öğe aktarılıyor…", + "transferSuccess": "Aktarım tamamlandı", + "transferError": "Aktarım başarısız", + "transferPartial": "Aktarım {{count}} hatayla tamamlandı", + "transferPartialHint": "Aktarılamadı: {{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şı…", + "destMustBeDirectory": "Çoklu öğe aktarımları için hedef bir dizin olmalıdır", + "selectThisFolder": "Bu klasörü seç", + "browsePathWillBeCreated": "Bu klasör henüz mevcut değil. Aktarım başladığında oluşturulacak.", + "browsePathError": "Hedef ana bilgisayarda bu yol açılamadı.", + "goUp": "Yukarı", + "copyFolderToHost": "Klasörü ana bilgisayara kopyala…", + "moveFolderToHost": "Klasörü ana bilgisayara taşı…", "hostReady": "Hazır", - "hostConnecting": "… Bağlanıyor", + "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}}", + "hostAuthRequired": "Kimlik doğrulama gerekli — önce bu ana bilgisayarda Dosya Yöneticisi'ni açın", + "hostConnectionFailed": "Bağlantı başarısız", + "metricsTitle": "Aktarım zamanlamaları", + "metricsPrepare": "Hedefi hazırla: {{duration}}", + "metricsCompress": "Kaynakta sıkıştırma: {{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}}", + "metricsExtract": "Hedefte çıkarma: {{duration}}", + "metricsSourceDelete": "Kaynaktan silme: {{duration}}", "metricsTotal": "Toplam: {{duration}}", - "progressCompressing": "Kaynak sunucuda sıkıştırma işlemi…", - "progressExtracting": "Hedefe çıkarılıyor…", + "progressCompressing": "Kaynak sunucuda sıkıştırılıyor…", + "progressExtracting": "Hedefte çı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)", + "progressReconnecting": "Yeniden bağlanılıyor…", + "parallelSegmentsLabel": "Paralel aktarım yolları", + "parallelSegmentsOption": "{{count}} yol", + "parallelSegmentsHint": "Büyük dosyalar 256 MB'lık parçalara bölünür. Çoklu yollar, daha yüksek toplam aktarım hızı için ayrı bağlantılar kullanır (birden fazla aktarım başlatmak gibi).", + "progressTotalSpeed": "{{speed}} toplam ({{lanes}} yol)", "progressTransferringItems": "Dosyalar aktarılıyor ({{current}} / {{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", + "progressItems": "{{current}} / {{total}} dosya", + "sourceNotDeletedPartial": "Kaynak dosyalar korundu (kısmi aktarım)", + "jumpHostLimitation": "Her iki sunucuya da Termix sunucusundan erişilebilir olmalıdır. Sunucular arası doğrudan yönlendirme desteklenmez.", + "cancel": "İptal", "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.", + "methodAutoHint": "Dosya sayısı, boyutu ve sıkıştırılabilirliğe bağlı olarak tar veya dosya başına SFTP seçer. Tek dosyalar her zaman akış SFTP'si kullanır.", + "methodTarHint": "Kaynakta sıkıştır, tek arşiv aktar, hedefte çıkar. Her iki Unix sunucuda tar gerektirir.", + "methodItemSftpHint": "Her dosyayı SFTP üzerinden tek tek aktarır. Windows dahil tüm sunucularda çalışır.", + "methodPreviewLoading": "Aktarım yöntemi hesaplanıyor…", + "methodPreviewError": "Aktarım yöntemi önizlenemedi. Başlattığınızda sunucu yine de bir yöntem seçecektir.", + "methodPreviewWillUseTar": "Kullanılacak: Tar arşivi", + "methodPreviewWillUseItemSftp": "Kullanılacak: Dosya başına SFTP", + "methodPreviewScanSummary": "{{fileCount}} dosya, toplam {{totalSize}} (kaynak sunucuda tarandı).", + "methodItemSftpLimitation": "Her dosya, tek bir dosya kopyasıyla aynı SFTP akışını sırayla kullanır. İlerleme tüm dosyalar için birleştirilmiştir, bu nedenle büyük dosyalar sırasında çubuk yavaş hareket eder.", "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": "Dosya bazında SFTP seçtiniz.", + "user_tar": "Tar arşivi seçtiniz.", + "tar_unavailable": "Bir veya her iki ana bilgisayarda Tar kullanılamıyor — bunun yerine dosya bazında SFTP kullanılacak.", + "windows_host": "Windows ana bilgisayarı söz konusu — tar kullanılmaz.", + "auto_multi_large": "Otomatik: sıkıştırılabilir veriye sahip büyük bir dosya ({{largestSize}}) dahil birden çok dosya — tar tek bir aktarımda birleştirir.", + "auto_single_large_in_archive": "Otomatik: bu kümede bir büyük dosya ({{largestSize}}) — dosya bazında SFTP.", + "auto_many_incompressible": "Otomatik: çoğunlukla sıkıştırılamayan veri — dosya bazında SFTP.", + "auto_many_files": "Otomatik: çok sayıda dosya ({{fileCount}}) — tar dosya başına ek yükü azaltır.", + "auto_default": "Otomatik: bu küme için dosya bazında SFTP." }, - "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": "İptal", + "progressCancelling": "İptal ediliyor…", + "progressStalled": "Askıda", + "resumedHint": "Başka bir pencerede başlatılan aktif bir aktarıma yeniden bağlanıldı.", + "transferCancelled": "Aktarım iptal edildi", + "transferCancelledCopyHint": "Hedefte kısmi dosyalar kalabilir.", + "transferCancelledMoveHint": "Hedefte kısmi dosyalar kalabilir. Kaynak dosyalar silinmedi.", + "cleanupDestFiles": "Hedefi temizle", + "cleanupDestFilesSuccess": "Hedefteki kısmi dosyalar kaldırıldı", + "cleanupDestFilesPartial": "Bazı kısmi dosyalar kaldırılamadı", + "cleanupDestFilesNothing": "Hedefte temizlenecek bir şey yok", + "cleanupDestFilesError": "Temizleme başarısız oldu", + "retryTransfer": "Yeniden dene", + "retryTransferError": "Yeniden deneme başarısız oldu", + "transferFailedRetryHint": "Hedefte kısmi veri tutuldu. Bağlantı geri geldiğinde yeniden deneme devam edecek." }, "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.", + "noSshTunnels": "SSH Tüneli yok", + "createFirstTunnelMessage": "Henüz hiç 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", + "canceling": "İptal ediliyor...", + "connect": "Bağlan", "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ı", + "cancel": "İptal", + "port": "Port", + "localPort": "Yerel Port", + "remotePort": "Uzak Port", + "currentHostPort": "Geçerli Sunucu Portu", + "endpointPort": "Uç Nokta Portu", "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", + "endpointSshHostPlaceholder": "Yapılandırılmış bir sunucu seçin", "endpointSshHostRequired": "Her istemci tüneli için bir uç nokta SSH sunucusu seçin.", - "attempt": "{{max}}'in {{current}} denemesi", + "attempt": "Deneme {{current}}/{{max}}", "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.", + "noClientTunnels": "Bu masaüstünde hiçbir istemci tüneli yapılandırılmamış.", "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.", + "autoStart": "Otomatik Başlat", + "clientAutoStartDesc": "Bu masaüstü istemcisi açıldığında başlar ve bağlı kalır.", + "clientManualStartDesc": "Bu satırdaki Başlat ve Durdur'u kullanın. Termix otomatik olarak açmaz.", + "clientRemoteServerNote": "Uzak yönlendirme, uç nokta SSH sunucusunda AllowTcpForwarding ve GatewayPorts 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", + "tunnelTestSucceeded": "Tünel testi başarılı", + "tunnelTestFailed": "Tünel testi başarısız", "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.", + "localSaveError": "Yerel istemci tünelleri kaydedilemedi", + "invalidBindIp": "Yerel IP geçerli bir IPv4 adresi olmalıdır.", + "invalidLocalTargetIp": "Yerel hedef IP geçerli bir IPv4 adresi olmalıdır.", + "invalidLocalPort": "Yerel port 1 ile 65535 arasında olmalıdır.", + "invalidRemotePort": "Uzak port 1 ile 65535 arasında olmalıdır.", + "invalidLocalTargetPort": "Yerel hedef portu 1 ile 65535 arasında olmalıdır.", + "invalidEndpointPort": "Uç nokta portu 1 ile 65535 arasında olmalıdır.", + "duplicateAutoStartBind": "Yalnızca bir otomatik başlatmalı istemci tüneli {{bind}} kullanabilir.", + "manualControlError": "Tünel durumu güncellenemedi.", "active": "Aktif", - "start": "Başlangıç", - "stop": "Durmak", - "test": "Test", - "type": "Tünel Tipi", + "start": "Başlat", + "stop": "Durdur", + "test": "Test Et", + "type": "Tünel Türü", "typeLocal": "Yerel (-L)", - "typeRemote": "Uzaktan (-R)", + "typeRemote": "Uzak (-R)", "typeDynamic": "Dinamik (-D)", - "typeServerLocalDesc": "Mevcut sunucudan uç noktaya.", - "typeServerRemoteDesc": "Uç nokta mevcut sunucuya geri dönüyor.", + "typeServerLocalDesc": "Geçerli sunucudan uç noktaya.", + "typeServerRemoteDesc": "Uç noktadan geçerli sunucuya.", "typeClientLocalDesc": "Yerel bilgisayardan uç noktaya.", - "typeClientRemoteDesc": "Uç nokta yerel bilgisayara geri döndü.", + "typeClientRemoteDesc": "Uç noktadan yerel bilgisayara.", "typeClientDynamicDesc": "Yerel bilgisayarda SOCKS.", - "typeDynamicDesc": "SOCKS5 CONNECT trafiğini SSH üzerinden ilet", + "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}}.", + "forwardDescriptionServerDynamic": "Geçerli sunucuda SOCKS {{sourcePort}}.", "forwardDescriptionClientLocal": "Yerel {{sourcePort}} → uzak {{endpointPort}}.", "forwardDescriptionClientRemote": "Uzak {{sourcePort}} → yerel {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS yerel portta {{sourcePort}}.", + "forwardDescriptionClientDynamic": "Yerel port {{sourcePort}} üzerinde SOCKS.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → Çoraplar {{endpoint}} aracılığıyla", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → {{endpoint}} üzerinden SOCKS", "autoNameClientLocal": "Yerel {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → yerel {{localPort}}", - "autoNameClientDynamic": "ÇORAPLAR {{localPort}} aracılığıyla {{endpoint}}", + "autoNameClientDynamic": "SOCKS {{localPort}} üzerinden {{endpoint}}", "route": "Rota:", - "lastStarted": "Son başlangıç", - "lastTested": "Son test tarihi:", + "lastStarted": "Son başlatma", + "lastTested": "Son test", "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.", + "maxRetries": "Maks. Yeniden Deneme", + "maxRetriesDescription": "En fazla yeniden deneme sayısı.", + "retryInterval": "Yeniden Deneme Aralığı (saniye)", + "retryIntervalDescription": "Yeniden denemeler arasında beklenecek süre.", "local": "Yerel", "remote": "Uzak", - "destination": "Varış noktası", - "host": "Ev sahibi", + "destination": "Hedef", + "host": "Host", "mode": "Mod", - "noHostSelected": "Sunucu seçilmedi.", - "working": "Çalışma..." + "noHostSelected": "Hiçbir host seçilmedi", + "working": "Çalışıyor..." }, "cardGrid": { - "dragToMove": "Taşımak için sürükleyin", - "dragToResize": "Boyutunu değiştirmek için sürükleyin.", + "dragToMove": "Taşımak için sürükle", + "dragToResize": "Yeniden boyutlandırmak için sürükle", "changeWidth": "Genişliği değiştir", - "removeCard": "{{label}}'ı kaldır", - "addCard": "Eklemek", + "removeCard": "{{label}} kaldır", + "addCard": "Ekle", "columns": "Sütunlar", - "empty": "Kart yok. Kart eklemek için aşağıdaki Ekle seçeneğini kullanın." + "empty": "Kart yok. Kart eklemek için aşağıdaki Ekle'yi kullanın." }, "hostMetrics": { - "cpu": "İşlemci", - "memory": "Hafıza", + "cpu": "CPU", + "memory": "Bellek", "disk": "Disk", "network": "Ağ", - "uptime": "Çalışma süresi", - "processes": "Süreçler", - "available": "Mevcut", - "free": "Özgür", + "uptime": "Çalışma Süresi", + "processes": "İşlemler", + "available": "Kullanılabilir", + "free": "Boş", "connecting": "Bağlanıyor...", - "connectionFailed": "Sunucuya bağlanılamadı.", - "naCpus": "N/A CPU(lar)", + "connectionFailed": "Sunucuya bağlanılamadı", + "naCpus": "CPU yok", "cpuCores_one": "{{count}} Çekirdek", - "cpuCores_other": "{{count}} Çekirdekler", + "cpuCores_other": "{{count}} Çekirdek", "cpuUsage": "CPU Kullanımı", "memoryUsage": "Bellek Kullanımı", "diskUsage": "Disk Kullanımı", "temperature": "Sıcaklık", "highestTemperature": "En yüksek sıcaklık", - "failedToFetchHostConfig": "Ana bilgisayar yapılandırması alınamadı.", + "failedToFetchHostConfig": "Host 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": "Host Metrics, 'none' kimlik doğrulama türünü desteklemiyor.", - "noHostSelected": "Sunucu seçilmedi.", + "cannotFetchMetrics": "Çevrimdışı sunucudan ölçümler alınamaz", + "totpFailed": "TOTP doğrulaması başarısız", + "noneAuthNotSupported": "Sunucu Metrikleri 'none' kimlik doğrulama türünü desteklemiyor.", + "noHostSelected": "Sunucu seçilmedi", "load": "Yük", - "systemInfo": "Sistem Bilgileri", - "hostname": "Ana bilgisayar adı", + "systemInfo": "Sistem Bilgisi", + "hostname": "Sunucu Adı", "operatingSystem": "İşletim Sistemi", "kernel": "Çekirdek", - "seconds": "saniyeler", + "seconds": "saniye", "networkInterfaces": "Ağ Arayüzleri", - "noInterfacesFound": "Ağ arayüzü bulunamadı.", - "noProcessesFound": "Hiçbir işlem bulunamadı.", + "noInterfacesFound": "Ağ arayüzleri bulunamadı", + "noProcessesFound": "Hiç işlem bulunamadı", "processesTotal": "toplam", - "processesRunning": "koşma", + "processesRunning": "çalışan", "loginStats": "SSH Giriş İstatistikleri", - "noRecentLoginData": "Son oturum açma verisi yok.", - "executingQuickAction": "{{name}} yürütülüyor...", + "noRecentLoginData": "Son giriş verisi yok", + "executingQuickAction": "{{name}} çalıştırılıyor...", "quickActionSuccess": "{{name}} başarıyla tamamlandı", "quickActionFailed": "{{name}} başarısız oldu", - "quickActionError": "{{name}} yürütülemedi", + "quickActionError": "{{name}} çalıştırılamadı", "ports": { - "title": "Dinleme Portları", + "title": "Dinlenen Portlar", "protocol": "Protokol", - "port": "Liman", + "port": "Port", "address": "Adres", "process": "İşlem", - "search": "Limanları ara...", - "allProtocols": "Tüm", - "noData": "Dinleme portlarına ait veri yok." + "search": "Portları ara...", + "allProtocols": "Tümü", + "noData": "Dinlenen port verisi yok" }, "firewall": { "title": "Güvenlik Duvarı", - "inactive": "Etkin değil", + "inactive": "Etkin Değil", "policy": "Politika", - "rules": "tüzük", - "noRules": "Bu zincirde hiçbir kural yok.", - "noData": "Güvenlik duvarı verisi mevcut değil.", - "action": "Aksiyon", - "protocol": "Proto", - "port": "Liman", + "rules": "kurallar", + "noRules": "Bu zincirde kural yok", + "noData": "Güvenlik duvarı verisi bulunamadı", + "action": "Eylem", + "protocol": "Protokol", + "port": "Port", "source": "Kaynak", - "anywhere": "Herhangi bir yer", + "anywhere": "Her yer", "chains": "zincirler" }, - "loadAvg": "Yük Ortalaması", + "loadAvg": "Ortalama Yük", "swap": "Takas", "architecture": "Mimari", "refresh": "Yenile", - "retry": "Tekrar dene", - "customize": "Düzeni özelleştirin", + "retry": "Yeniden dene", + "customize": "Düzeni özelleştir", "reset": "Sıfırla", "tabLive": "Canlı", - "editModeInstructions": "Kartları sürükleyerek yeniden düzenleyin, alt kenarı sürükleyerek boyutunu değiştirin ve genişlik düğmesini kullanarak kartın genişliğini değiştirin. Alt kısma kart ekleyin veya kaldırın.", + "editModeInstructions": "Kartları yeniden düzenlemek için sürükleyin, yeniden boyutlandırmak için alt kenarı sürükleyin ve bir kartın genişliğini değiştirmek için genişlik düğmesini kullanın. Aşağıdan kart ekleyin veya kaldırın.", "managers": { "services": "Hizmetler", - "processInspector": "Süreç Denetçisi", + "processInspector": "İşlem Denetçisi", "logViewer": "Günlük Görüntüleyici", - "cron": "Cron İşleri", + "cron": "Cron Görevleri", "packages": "Paketler", "ssl": "SSL Sertifikaları", "firewall": "Güvenlik Duvarı", "users": "Kullanıcılar ve İzinler", "healthCheck": "Sağlık Kontrolleri", - "diskBreakdown": "Disk Arızası", + "diskBreakdown": "Disk Dağılımı", "systemdTimers": "Zamanlayıcılar", - "topMemory": "Hafıza Sırasına Göre En İyi", + "topMemory": "Bellek Sıralaması", "noData": "Veri yok", - "sudoHint": "Ayrıcalıklı işlemleri etkinleştirmek için sunucu düzenleyicisinde bu sunucu için bir sudo parolası belirleyin.", + "sudoHint": "Ayrıcalıklı eylemleri etkinleştirmek için host düzenleyicisinde bu host için bir sudo parolası belirleyin.", "filter": "Filtrele...", - "start": "Başlangıç", - "stop": "Durmak", - "restart": "Tekrar başlat", + "start": "Başlat", + "stop": "Durdur", + "restart": "Yeniden Başlat", "actionDone": "{{name}} güncellendi", - "actionFailed": "İşlem başarısız oldu", - "signalSent": "PID'ye {{pid}} sinyal gönderildi.", - "killHint": "Tıklayın: sonlandır (SIGTERM). Sağ tıklayın: zorla sonlandır (SIGKILL).", - "working": "Çalışma...", - "update": "Güncelleme", + "actionFailed": "Eylem başarısız oldu", + "signalSent": "PID {{pid}}'e sinyal gönderildi", + "killHint": "Tıklama: sonlandır (SIGTERM). Sağ tıklama: zorla sonlandır (SIGKILL).", + "working": "Çalışıyor...", + "update": "Güncelle", "upgradeAll": "Tümünü yükselt", - "allUpToDate": "Her şey güncel.", - "save": "Kaydetmek", - "command": "Emretmek", - "enabled": "Etkinleştirilmiş", + "allUpToDate": "Her şey güncel", + "save": "Kaydet", + "command": "Komut", + "enabled": "Etkin", "cronSaved": "Crontab güncellendi", - "clients": "Müşteriler", - "dryRun": "Deneme sürüşü", + "clients": "İstemciler", + "dryRun": "Deneme çalıştırması", "renew": "Yenile", "noAcmeClient": "Bu sunucuda ACME istemcisi (certbot veya acme.sh) bulunamadı.", - "addInputRule": "GİRİŞ kuralı ekle", - "firewallWarning": "Değişiklikler, kalıcı hale getirilene kadar yalnızca çalışma zamanında geçerlidir. Kendinizi sistemden kilitlememeye dikkat edin.", - "ruleApplied": "Uygulanan kural", - "invalidPort": "Geçerli bir port numarası girin (1-65535)", + "addInputRule": "INPUT kuralı ekle", + "firewallWarning": "Değişiklikler, kalıcı hale getirilene kadar yalnızca çalışma zamanında geçerlidir. Kendinizi kilitlememeye dikkat edin.", + "ruleApplied": "Kural uygulandı", + "invalidPort": "Geçerli bir bağlantı noktası girin (1-65535)", "newUsername": "Yeni kullanıcı adı", - "addUser": "Eklemek", + "addUser": "Ekle", "deleteUser": "Kullanıcıyı sil", - "noHealthChecks": "Henüz hiçbir sağlık kontrolü yapılandırılmadı.", - "follow": "Takip etmek", - "noLogData": "Günlük kaydı çıktısı yok.", + "noHealthChecks": "Henüz yapılandırılmış bir sağlık denetimi yok.", + "follow": "Takip et", + "noLogData": "Günlük çıktısı yok.", "tree": "Ağaç", "enableDisable": "Önyüklemede etkinleştir / devre dışı bırak", - "grantSudo": "Grant sudo", - "revokeSudo": "sudo'yu iptal et", - "sslIssueCert": "Sertifika düzenleme", - "sslExpired": "Günü geçmiş", - "sslInDays": "{{days}}d içinde", + "grantSudo": "Sudo yetkisi ver", + "revokeSudo": "Sudo yetkisini kaldır", + "sslIssueCert": "Sertifika oluştur", + "sslExpired": "Süresi dolmuş", + "sslInDays": "{{days}} gün içinde", "sslNeedDomain": "En az bir alan adı girin", - "sslIssued": "Sertifika düzenlendi.", + "sslIssued": "Sertifika oluşturuldu", "sslDomainsPlaceholder": "example.com, www.example.com", "sslHttpStandalone": "HTTP (bağımsız)", "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNS sağlayıcısı (örneğin Cloudflare)", - "sslIssueHint": "DNS sağlayıcı kimlik bilgilerinin sunucuda önceden yapılandırılmış olması gerekir.", + "sslDnsProvider": "DNS sağlayıcısı (örn. cloudflare)", + "sslIssueHint": "DNS sağlayıcı kimlik bilgileri sunucuda zaten yapılandırılmış olmalıdır.", "sslRevoke": "Sertifikayı iptal et", - "sslRevoked": "Sertifika iptal edildi.", - "sslRevokeConfirm": "\"{{name}}\" sertifikasını iptal edip kaldırmak mı istiyorsunuz? Bu işlem geri alınamaz.", - "healthRun": "Koşmak", - "healthName": "İsim", + "sslRevoked": "Sertifika iptal edildi", + "sslRevokeConfirm": "\"{{name}}\" sertifikası iptal edilip kaldırılsın mı? Bu işlem geri alınamaz.", + "healthRun": "Çalıştır", + "healthName": "Ad", "healthTarget": "Sunucu / adres", "healthAddCheck": "Kontrol ekle", - "healthSaved": "Sağlık kontrolleri kaydedildi.", - "healthMissingFields": "Her çekin bir adı ve hedefi olması gerekir.", + "healthSaved": "Sağlık kontrolleri kaydedildi", + "healthMissingFields": "Her kontrol bir ad ve hedef gerektirir", "logFile": "Dosya", "logUnit": "Birim", "logCustomPath": "/var/log altında özel yol (isteğe bağlı)", - "logGrep": "Filtre hatları...", - "firewallPersist": "Kuralları sürdür", - "firewallPersisted": "Güvenlik duvarı kuralları kalıcı hale getirildi.", + "logGrep": "Satırları filtrele...", + "firewallPersist": "Kuralları kalıcı yap", + "firewallPersisted": "Güvenlik duvarı kuralları kalıcı hale getirildi", "wireguard": "WireGuard", - "tailscale": "Kuyruk pulu", - "wgNotInstalled": "Bu sunucuda WireGuard yüklü değil.", - "wgNoInterfaces": "Hiçbir WireGuard arayüzü yapılandırılmamış.", - "wgInterfaceUp": "Yetiştirmek", - "wgInterfaceDown": "Aşağı indirin", - "wgBringingUp": "{{name}} 'ı getiriyoruz...", - "wgBringingDown": "{{name}} 'ı aşağı indiriyoruz...", - "wgInterfaceUpDone": "{{name}} yukarıda", - "wgInterfaceDownDone": "{{name}} kapalı", - "wgListenPort": "Liman", - "wgPublicKey": "Genel anahtar", + "tailscale": "Tailscale", + "wgNotInstalled": "Bu sunucuda WireGuard yüklü değil", + "wgNoInterfaces": "Yapılandırılmış WireGuard arayüzü yok", + "wgInterfaceUp": "Başlat", + "wgInterfaceDown": "Durdur", + "wgBringingUp": "{{name}} başlatılıyor...", + "wgBringingDown": "{{name}} durduruluyor...", + "wgInterfaceUpDone": "{{name}} çalışıyor", + "wgInterfaceDownDone": "{{name}} çalışmıyor", + "wgListenPort": "Port", + "wgPublicKey": "Açık anahtar", "wgEndpoint": "Uç nokta", - "wgAllowedIPs": "İzin verilen IP adresleri", + "wgAllowedIPs": "İzin verilen IP'ler", "wgLastHandshake": "Son el sıkışma", - "wgHandshakeNever": "Asla", + "wgHandshakeNever": "Hiçbir zaman", "wgTransfer": "Transfer", - "tsNotInstalled": "Bu sunucuda Tailscale yüklü değil.", - "tsRunning": "Koşma", + "tsNotInstalled": "Bu sunucuda Tailscale yüklü değil", + "tsRunning": "Çalışıyor", "tsStopped": "Durduruldu", - "tsEnable": "Bağlamak", - "tsDisable": "Bağlantıyı kes", - "tsEnabling": "Tailscale'e bağlanılıyor...", + "tsEnable": "Bağlan", + "tsDisable": "Bağlantıyı Kes", + "tsEnabling": "Tailscale'e bağlanıyor...", "tsDisabling": "Tailscale bağlantısı kesiliyor...", - "tsEnabled": "Kuyruk pulu bağlantılı", - "tsDisabled": "Kuyruk pulu bağlantısı kesildi", + "tsEnabled": "Tailscale bağlandı", + "tsDisabled": "Tailscale bağlantısı kesildi", "tsIPs": "Tailscale IP'leri", - "tsPeers": "Akranlar", + "tsPeers": "Eşler", "tsOnline": "Çevrimiçi", "tsOffline": "Çevrimdışı", "tsExitNode": "Çıkış düğümü", - "tsExitNodeActive": "Çıkış düğümü aktif", + "tsExitNodeActive": "Çıkış düğümü etkin", "tsHostname": "Ana bilgisayar adı" } }, "auth": { - "tagline": "Kendi sunucunuzda barındırılan SSH ve uzaktan masaüstü yönetimi", - "loginTitle": "tekrar hoşgeldiniz", - "registerTitle": "Hesap oluşturmak", + "tagline": "Kendi SSH ve uzak masaüstü yönetiminizi barındırın", + "loginTitle": "Tekrar hoş geldiniz", + "registerTitle": "Hesap Oluştur", "forgotPassword": "Parolanızı mı unuttunuz?", - "rememberMe": "Cihazı 30 Gün Boyunca Hatırlayın (TOTP Dahil)", + "rememberMe": "Cihazı 30 Gün Hatırla (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.", + "backupCode": "Veya yedek kodu 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.", + "sshNoKeyboardInteractive": "Klavye-Etkileşimli Kimlik Doğrulama Kullanılamıyor", + "sshAuthenticationFailed": "Kimlik Doğrulama Başarısız", "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.", + "sshNoKeyboardInteractiveDescription": "Sunucu klavye-etkileşimli kimlik doğrulamayı desteklemiyor. Lütfen parolanızı veya SSH anahtarınızı sağlayın.", + "sshAuthFailedDescription": "Sağlanan kimlik bilgileri yanlış. 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.", + "sshProvideCredentialsDescription": "Bu sunucuya bağlanmak için SSH kimlik bilgilerinizi sağlayın.", + "sshPasswordDescription": "Bu SSH bağlantısı için parolayı girin.", + "sshKeyPasswordDescription": "SSH anahtarınız şifrelenmişse, anahtar parolasını buraya girin.", "passphraseRequired": "Parola Gerekli", - "passphraseRequiredDescription": "SSH anahtarı şifrelenmiştir. Lütfen kilidi açmak için parola girin.", + "passphraseRequiredDescription": "SSH anahtarı şifrelenmiş. Kilidini açmak için lütfen parolayı 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.", + "firstUserMessage": "İlk kullanıcı sizsiniz ve yönetici olarak atanacaksınız. Yönetici ayarlarını kenar çubuğundaki 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 bir GitHub sorunu oluşturun.", "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.", - "loginWithProvider": "{{name}} ile giriş yapın", - "orContinueWith": "veya devam edin", + "loginWithExternalDesc": "Yapılandırdığınız harici kimlik sağlayıcısını kullanarak giriş yapın.", + "externalNotSupportedInElectron": "Harici kimlik doğrulaması henüz Electron uygulamasında desteklenmiyor. OIDC ile giriş yapmak için lütfen web sürümünü kullanın.", + "loginWithProvider": "{{name}} ile giriş yap", + "orContinueWith": "veya şununla devam et:", "ldapUsername": "LDAP Kullanıcı Adı", - "ldapPassword": "LDAP Şifresi", - "ldapSignIn": "Giriş Yap", - "ldapLoginFailed": "LDAP oturum açma işlemi başarısız oldu", - "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.", + "ldapPassword": "LDAP Parolası", + "ldapSignIn": "Oturum Aç", + "ldapLoginFailed": "LDAP oturum açma başarısız.", + "resetPasswordButton": "Parolayı Sıfırla", + "sendResetCode": "Sıfırlama Kodu Gönder", + "resetCodeDesc": "Parola sıfırlama kodu almak için kullanıcı adınızı girin. Kod, docker konteyner günlüklerine yazılacak.", "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", + "enterResetCode": "Kullanıcı için docker konteyner günlüklerindeki 6 haneli kodu girin:", + "newPassword": "Yeni Parola", + "confirmNewPassword": "Parolayı Onayla", + "enterNewPassword": "Kullanıcı için yeni parolanızı girin:", + "signUp": "Kaydol", "desktopApp": "Masaüstü Uygulaması", - "loggingInToDesktopApp": "Masaüstü uygulamasına giriş yapılıyor.", + "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.", - "passwordLoginDisabledDesc": "Parola ile giriş devre dışı bırakılmıştır. Lütfen bir parola anahtarı veya harici bir kimlik doğrulama sağlayıcısı kullanın.", - "signInWithPasskey": "Parola ile giriş yapın", - "passkeyLoginFailed": "Parola girişi başarısız oldu.", + "dataLossWarning": "Parolanızı bu şekilde sıfırlamak, kaydedilmiş tüm SSH ana bilgisayarlarınızı, kimlik bilgilerinizi ve diğer şifrelenmiş verilerinizi silecektir. Bu işlem geri alınamaz. Yalnızca parolanızı unuttuysanız ve oturum açmadıysanız kullanın.", + "authenticationDisabled": "Kimlik Doğrulama Devre Dışı", + "authenticationDisabledDesc": "Tüm kimlik doğrulama yöntemleri şu anda devre dışı. Lütfen yöneticinizle iletişime geçin.", + "passwordLoginDisabledDesc": "Parola ile oturum açma devre dışı. Passkey veya harici bir kimlik doğrulama sağlayıcısı kullanın.", + "signInWithPasskey": "passkey ile oturum aç", + "passkeyLoginFailed": "Passkey girişi başarısız oldu", "attemptsRemaining": "{{count}} deneme kaldı", - "confirmResetDataWipe": "Şifreleme güncellemesinden bu yana bu hesaba giriş yapılmadı, bu nedenle eski parola olmadan saklanan veriler kurtarılamaz. Sıfırlama işlemi, ana bilgisayarlarını, kimlik bilgilerini ve kod parçacıklarını kalıcı olarak silecektir. Devam edilsin mi?" + "confirmResetDataWipe": "Bu hesap, şifreleme güncellemesinden sonra oturum açmadığından, saklanan verileri eski şifre olmadan kurtarılamaz. Sıfırlama, sunucuları, kimlik bilgilerini ve parçacıkları kalıcı olarak siler. Devam edilsin mi?" }, "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.", + "verifyNewHost": "SSH Sunucu Anahtarını Doğrula", + "keyChangedWarning": "SSH Sunucu Anahtarı Değişti", + "firstConnectionTitle": "Bu sunucuya ilk kez bağlanılıyor", + "firstConnectionDescription": "Bu sunucunun kimliği doğrulanamıyor. Parmak izinin beklediğiniz ile eşleştiğinden emin olun.", + "keyChangedDescription": "Bu sunucunun anahtarı, son bağlantınızdan bu yana değişti. Bu bir güvenlik sorununa işaret ediyor olabilir.", "previousKey": "Önceki Anahtar", "newFingerprint": "Yeni Parmak İzi", - "fingerprint": "Parmak izi", + "fingerprint": "Parmak İzi", "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" }, "errors": { - "databaseConnection": "Veritabanına bağlanılamadı.", + "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ı.", + "loginFailed": "Giriş başarısız oldu", + "failedPasswordReset": "Şifre sıfırlama başlatılamadı", + "failedVerifyCode": "Sıfırlama kodu doğrulanamadı", + "failedCompleteReset": "Şifre sıfırlama 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.", + "failedOidcLogin": "OIDC girişi başlatılamadı", + "silentSigninOidcUnavailable": "Sessiz oturum açma istendi, ancak OIDC girişi kullanılamıyor.", + "failedUserInfo": "Giriş sonrası kullanıcı bilgileri alınamadı", "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.", + "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." + "passwordMismatch": "Şifreler eşleşmiyor", + "passwordLoginDisabled": "Kullanıcı adı/şifre ile giriş şu anda devre dışı", + "sessionExpired": "Oturum süresi doldu - lütfen tekrar giriş yapın", + "totpRateLimited": "Hız sınırlaması: Çok fazla TOTP doğrulama denemesi. Lütfen daha sonra tekrar deneyin.", + "totpRateLimitedWithTime": "Hız sınırlaması: Çok fazla TOTP doğrulama denemesi. Lütfen tekrar denemeden önce {{time}} saniye bekleyin.", + "resetCodeRateLimited": "Hız sınırlaması: Çok fazla doğrulama denemesi. Lütfen daha sonra tekrar deneyin.", + "resetCodeRateLimitedWithTime": "Hız sınırlaması: Çok fazla doğrulama denemesi. Lütfen tekrar denemeden önce {{time}} saniye bekleyin.", + "authTokenSaveFailed": "Kimlik doğrulama belirteci kaydedilemedi", + "failedToLoadServer": "Sunucu yüklenemedi", + "remoteServerRequired": "Uzak sunucu gereklidir. Bu bağlantı türünü kullanmak için Ayarlar'dan bir uzak sunucu bağlayın." }, "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ı.", + "registrationDisabled": "Yeni hesap kaydı şu anda bir yönetici tarafından devre dışı bırakıldı. Lütfen giriş yapın veya bir yöneticiyle iletişime geçin.", + "userNotAllowed": "Hesabınızın kayıt yapma yetkisi yok. Lütfen bir yöneticiyle iletişime geçin.", + "databaseConnectionFailed": "Veritabanı sunucusuna bağlanılamadı", + "resetCodeSent": "Sıfırlama kodu Docker günlüklerine gönderildi", + "codeVerified": "Kod başarıyla doğrulandı", + "passwordResetSuccess": "Parola başarıyla sıfırlandı", "loginSuccess": "Giriş başarılı", - "registrationSuccess": "Kayıt işlemi başarılı." + "registrationSuccess": "Kayıt başarılı" }, "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.", + "c2sTunnelConfigDesc": "Yapılandırılmış SSH ana makinelerini hedefleyen yerel masaüstü tünelleri.", + "c2sTunnelPresets": "İstemci Tünel Ön Ayarları", + "c2sTunnelPresetsDesc": "Bu masaüstü istemcinin yerel tünel listesini adlandırılmış bir sunucu ön ayarı olarak kaydedin veya bir ön ayarı bu istemciye yükleyin.", + "c2sTunnelPresetsUnavailable": "İstemci tünel ön ayarları yalnızca masaüstü istemcisinde kullanılabilir.", "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", + "c2sPresetNamePlaceholder": "İstemci ön ayar adı", + "c2sPresetToLoad": "Yüklenecek Ön Ayar", + "c2sNoPresetSelected": "Ön ayar seçilmedi", + "c2sNoPresets": "Kayıtlı ön ayar yok", + "c2sLoadPreset": "Yükle", "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." + "c2sPresetSyncNote": "Ön ayarlar belirli anlık görüntülerdir; birini yüklemek, bu masaüstü istemcinin yerel istemci tünel listesini değiştirir.", + "c2sPresetSaved": "İstemci tünel ön ayarı kaydedildi", + "c2sPresetLoaded": "İstemci tünel ön ayarı yerel olarak yüklendi", + "c2sPresetRenamed": "İstemci tünel ön ayarı yeniden adlandırıldı", + "c2sPresetDeleted": "İstemci tünel ön ayarı silindi", + "c2sPresetLoadError": "İstemci tünel ön ayarları yüklenemedi" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "Dil", - "keyPassword": "anahtar şifresi", + "keyPassword": "anahtar parolası", "pastePrivateKey": "Özel anahtarınızı buraya yapıştırın...", - "localListenerHost": "127.0.0.1 (yerel olarak dinleyin)", + "localListenerHost": "127.0.0.1 (yerel olarak dinle)", "localTargetHost": "127.0.0.1 (bu bilgisayardaki hedef)", - "socksListenerHost": "127.0.0.1 (Çorap dinleyicisi)", - "enterPassword": "Şifrenizi girin", + "socksListenerHost": "127.0.0.1 (SOCKS dinleyici)", + "enterPassword": "Parolanızı girin", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Kontrol Paneli", - "loading": "Kontrol paneli yükleniyor...", + "title": "Gösterge Paneli", + "loading": "Gösterge paneli yükleniyor...", "github": "GitHub", "support": "Destek", "discord": "Discord", "docs": "Belgeler", - "donate": "Bağış yapmak", - "serverOverview": "Sunucuya Genel Bakış", + "donate": "Bağış Yap", + "serverOverview": "Sunucu Genel Görünümü", "version": "Sürüm", "upToDate": "Güncel", "updateAvailable": "Güncelleme Mevcut", "beta": "Beta", - "uptime": "Çalışma süresi", + "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", + "totalHosts": "Toplam Sunucu", + "totalTunnels": "Toplam Tünel", + "totalCredentials": "Toplam Kimlik Bilgisi", + "recentActivity": "Son Etkinlikler", "reset": "Sıfırla", "loadingRecentActivity": "Son etkinlikler yükleniyor...", - "noRecentActivity": "Son zamanlarda herhangi bir aktivite yok.", - "quickActions": "Hızlı İşlemler", + "noRecentActivity": "Son etkinlik yok", + "quickActions": "Hızlı Eylemler", "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ı", + "noServerData": "Sunucu verisi mevcut değil", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Panoyu Özelleştir", + "dashboardSettings": "Pano Ayarları", "enableDisableCards": "Kartları Etkinleştir/Devre Dışı Bırak", - "resetLayout": "Varsayılan ayarlara sıfırla", + "resetLayout": "Varsayılana Sıfırla", "serverOverviewCard": "Sunucuya Genel Bakış", - "recentActivityCard": "Son Aktiviteler", + "recentActivityCard": "Son Etkinlik", "networkGraphCard": "Ağ Grafiği", "networkGraph": "Ağ Grafiği", - "quickActionsCard": "Hızlı İşlemler", + "quickActionsCard": "Hızlı Eylemler", "serverStatsCard": "Sunucu İstatistikleri", "panelMain": "Ana", - "panelSide": "Taraf", - "justNow": "Şu anda", + "panelSide": "Kenar", + "justNow": "az önce", "serviceLinks": "Hizmet Bağlantıları", "homepagePreview": "Ana Sayfa Önizlemesi" }, "donation": { - "title": "Termix'ten keyif alıyor musunuz?", - "body": "Termix, boş zamanlarımızda iki kişilik bir ekip tarafından geliştirilen ve sürdürülen ücretsiz ve açık kaynaklı bir platformdur. Eğer daha önce ücretli olarak kullandığınız ticari bir aracın yerini aldıysa, bağışlarınız barındırma maliyetlerini karşılamaya ve geliştirmenin devam etmesine yardımcı olur. Şu an itibariyle bağışlar yalnızca kripto para birimleriyle kabul edilmektedir.", - "milestones": "Bağışlar ayrıca SAML ve Kubernetes desteği gibi özelliklerin geliştirilmesi için gereken araştırma ve öğrenme süreçlerine harcanan zamana da katkı sağlıyor. Bağış sayfasından gelişmeler hakkında bilgi edinebilirsiniz.", - "cta": "Bağış yapmak", - "dismiss": "Belki daha sonra" + "title": "Termix'i Beğendiniz mi?", + "body": "Termix, boş zamanlarımızda iki kişilik bir ekip tarafından oluşturulan ve sürdürülen ücretsiz ve açık kaynaklı bir uygulamadır. Eğer aksi takdirde ödeme yapacağınız ticari bir aracın yerini aldıysa, bağışlar barındırma maliyetlerini karşılamaya ve geliştirmenin devam etmesine yardımcı olur. Şu an için bağışlar yalnızca kripto para ile kabul edilmektedir.", + "milestones": "Bağışlar ayrıca SAML ve Kubernetes desteği gibi özelliklerin oluşturulması için gerekenleri araştırmak ve öğrenmek için zaman ayırmaya da yardımcı olur. Bağış sayfasındaki ilerlemeyi görebilirsiniz.", + "cta": "Bağış Yap", + "dismiss": "Belki Daha Sonra" }, "dashboardTab": { - "stable": "STABİL", + "stable": "KARARLI", "hostsOnline": "Çevrimiçi Sunucular", - "activeTunnels": "Aktif Tüneller", + "activeTunnels": "Etkin 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", + "storeSshKeysOrPasswords": "SSH anahtarlarını veya parolaları sakla", + "manageUsersAndRoles": "Kullanıcıları ve rolleri yönet", + "manageYourAccount": "Hesabını yönet", "hostStatus": "Sunucu Durumu", - "noHostsConfigured": "Yapılandırılmış sunucu yok", + "noHostsConfigured": "Hiçbir sunucu yapılandırılmadı", "online": "ÇEVRİMİÇİ", "offline": "ÇEVRİMDIŞI", - "checking": "KONTROL EDİLİYOR", - "onlineLower": "Çevrimiçi", - "nodes": "{{count}} düğümler", - "add": "Eklemek:", + "checking": "DENETLENİYOR", + "onlineLower": "çevrimiçi", + "nodes": "{{count}} düğüm", + "add": "Ekle:", "commandPalette": "Komut Paleti", - "done": "Tamamlamak", - "editModeInstructions": "Kartları sürükleyerek sıralarını değiştirin · 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", + "done": "Tamam", + "editModeInstructions": "Kartları sürükleyerek sıralayın · Sütun genişliğini değiştirmek için sütun ayırıcıyı sürükleyin · Kartın yüksekliğini ayarlamak için alt kenarını sürükleyin · Kaldırmak için çöp kutusuna atın", "empty": "Boş", - "clear": "Temizlemek", - "serviceLinksTitle": "Hizmet Bağlantıları", - "serviceLinksEmpty": "Henüz servis bağlantısı yok.", + "clear": "Temizle", + "serviceLinksTitle": "Servis Bağlantıları", + "serviceLinksEmpty": "Henüz servis bağlantısı yok", "serviceLinksAddLabel": "Etiket", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "Eklemek", + "serviceLinksAdd": "Ekle", "serviceLinksLabelPlaceholder": "Hizmetim", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "Geçerli bir web adresi girin.", - "serviceLinksAddFailed": "Hizmet bağlantısı eklenemedi.", + "serviceLinksInvalidUrl": "Geçerli bir web adresi girin", + "serviceLinksAddFailed": "Servis bağlantısı eklenemedi", "disk": "Disk", "viewServerDetails": "Sunucu ayrıntılarını görüntüle" }, "sessionLogs": { "title": "Oturum Günlükleri", - "noLogs": "Henüz oturum kaydı yok.", - "noLogsDesc": "Oturum kaydını başlatmak için sunucuda oturum kaydını etkinleştirin.", + "noLogs": "Henüz oturum günlüğü yok", + "noLogsDesc": "Kayda başlamak için bir sunucuda oturum günlüğünü etkinleştirin", "duration": "Süre", "viewLog": "Günlüğü görüntüle", - "downloadLog": "İndirmek", - "deleteLog": "Silmek", - "confirmDelete": "Bu oturum günlüğünü silmek ister misiniz?", + "downloadLog": "İndir", + "deleteLog": "Sil", + "confirmDelete": "Bu oturum günlüğü silinsin mi?", "confirmDeleteDesc": "Bu işlem geri alınamaz.", "copyContent": "Kopyala", "copied": "Kopyalandı!", - "loadError": "Oturum günlükleri yüklenemedi.", - "deleteError": "Oturum günlüğünü silme işlemi başarısız oldu.", + "loadError": "Oturum günlükleri yüklenemedi", + "deleteError": "Oturum günlüğü silinemedi", "filterByHost": "Sunucuya göre filtrele..." }, "networkGraph": { @@ -2434,9 +2568,9 @@ "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", + "selectHost": "Sunucu Seç", + "chooseHost": "Bir sunucu seçin...", + "parentGroup": "Üst Grup", "noGroup": "Grup Yok", "groupName": "Grup Adı", "color": "Renk", @@ -2445,133 +2579,133 @@ "moveToGroup": "Gruba Taşı", "selectGroup": "Grup seçin...", "addConnection": "Bağlantı Ekle", - "hostDetails": "Ev Sahibi Bilgileri", + "hostDetails": "Sunucu Detayları", "removeFromGroup": "Gruptan Kaldır", - "addHostHere": "Sunucu Ekle", - "editGroup": "Grup Düzenleme", - "delete": "Silmek", - "add": "Eklemek", - "create": "Yaratmak", - "move": "Taşınmak", - "connect": "Bağlamak", + "addHostHere": "Buraya Sunucu Ekle", + "editGroup": "Grubu Düzenle", + "delete": "Sil", + "add": "Ekle", + "create": "Oluştur", + "move": "Taşı", + "connect": "Bağlan", "createGroup": "Grup Oluştur", "selectSourcePlaceholder": "Kaynak Seçin...", - "selectTargetPlaceholder": "Hedefi Seç...", + "selectTargetPlaceholder": "Hedef Seçin...", "invalidFile": "Geçersiz Dosya", - "hostAlreadyExists": "Sunucu zaten topolojide yer alıyor.", - "connectionExists": "Bağlantı zaten mevcut.", + "hostAlreadyExists": "Sunucu zaten topolojide var", + "connectionExists": "Bağlantı zaten var", "unknown": "Bilinmiyor", - "name": "İsim", + "name": "Ad", "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", + "failedToAddNode": "Düğüm eklenemedi", + "sourceDifferentFromTarget": "Kaynak ve hedef farklı olmalıdır", + "exportJSON": "JSON Dışa Aktar", + "importJSON": "JSON İçe Aktar", + "terminal": "Terminal", "fileManager": "Dosya Yöneticisi", "tunnel": "Tünel", - "docker": "Liman işçisi", + "docker": "Docker", "serverStats": "Sunucu Metrikleri", "hostMetrics": "Sunucu Metrikleri", - "noNodes": "Henüz düğüm yok." + "noNodes": "Henüz düğüm yok" }, "docker": { - "notEnabled": "Bu sunucu için Docker etkinleştirilmemiş.", - "validating": "Docker doğrulaması yapılıyor...", + "notEnabled": "Bu sunucu için Docker etkinleştirilmemiş", + "validating": "Docker doğrulanıyor...", "connecting": "Bağlanıyor...", "error": "Hata", "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.", + "connectionFailed": "Docker'a bağlanılamadı", + "containerStarted": "{{name}} konteyneri başlatıldı", + "failedToStartContainer": "{{name}} konteyneri başlatılamadı", + "containerStopped": "{{name}} konteyneri durduruldu", + "failedToStopContainer": "{{name}} konteyneri durdurulamadı", + "containerRestarted": "{{name}} konteyneri yeniden başlatıldı", + "failedToRestartContainer": "{{name}} konteyneri yeniden başlatılamadı", + "containerPaused": "{{name}} konteyneri duraklatıldı", + "containerUnpaused": "{{name}} konteyneri devam ettirildi", + "failedToTogglePauseContainer": "{{name}} konteyneri için duraklatma durumu değiştirilemedi", + "containerRemoved": "{{name}} konteyneri kaldırıldı", + "failedToRemoveContainer": "{{name}} konteyneri kaldırılamadı", + "image": "İmaj", + "ports": "Portlar", + "noPorts": "Port yok", + "start": "Başlat", + "confirmRemoveContainer": "'{{name}}' konteynerini kaldırmak istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "runningContainerWarning": "Uyarı: Bu konteyner şu anda çalışıyor. Kaldırma işlemi önce konteyneri durduracak.", "loadingContainers": "Konteynerler yükleniyor...", "manager": "Docker Yöneticisi", - "autoRefresh": "Otomatik Yenileme", - "timestamps": "Zaman damgaları", - "lines": "Çizgiler", + "autoRefresh": "Otomatik Yenile", + "timestamps": "Zaman Damgaları", + "lines": "Satırlar", "filterLogs": "Günlükleri filtrele...", "refresh": "Yenile", - "download": "İndirmek", - "clear": "Temizlemek", - "logsDownloaded": "Günlük dosyaları başarıyla indirildi.", + "download": "İndir", + "clear": "Temizle", + "logsDownloaded": "Günlükler 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...", + "allLogs": "Tüm Günlükler", + "noLogsMatching": "\"{{query}}\" ile eşleşen günlük yok", + "noLogsAvailable": "Günlükler mevcut değil", + "noContainersFound": "Konteyner bulunamadı", + "noContainersFoundHint": "Bu ana bilgisayarda Docker konteyneri bulunmuyor", + "searchPlaceholder": "Konteyner ara...", "allStatuses": "Tüm Durumlar", - "stateRunning": "Koşma", + "stateRunning": "Çalışıyor", "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.", + "stateExited": "Durduruldu", + "stateRestarting": "Yeniden başlatılıyor", + "noContainersMatchFilters": "Filtrelere uyan konteyner yok", + "noContainersMatchFiltersHint": "Aramanızı veya filtre ölçütlerinizi 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": "Mevcut istatistik yok.", + "errorLoadingStats": "İstatistikler yüklenirken hata oluştu", + "noStatsAvailable": "İstatistikler mevcut değil", "cpuUsage": "CPU Kullanımı", - "current": "Akım", + "current": "Anlık", "memoryUsage": "Bellek Kullanımı", "networkIo": "Ağ G/Ç", "input": "Giriş", "output": "Çıktı", "blockIo": "Blok G/Ç", - "read": "Okumak", - "write": "Yazmak", + "read": "Okuma", + "write": "Yazma", "pids": "PID'ler", "containerInformation": "Konteyner Bilgileri", - "name": "İsim", - "id": "İD", + "name": "Ad", + "id": "ID", "state": "Durum", - "containerMustBeRunning": "Konsola erişmek için konteynerin çalışır durumda olması gerekir.", + "containerMustBeRunning": "Konsola erişmek için konteyner çalışıyor olmalıdır", "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ı", + "connectedTo": "{{containerName}} ile bağlantı kuruldu", "disconnected": "Bağlantı kesildi", "consoleError": "Konsol hatası", "errorMessage": "Hata: {{message}}", - "failedToConnect": "Konteynere bağlanılamadı.", + "failedToConnect": "Konteynere bağlanılamadı", "console": "Konsol", - "selectShell": "Kabuk seçin", + "selectShell": "Kabuk seç", "bash": "Bash", - "sh": "ş", - "ash": "kül", - "connect": "Bağlamak", + "sh": "sh", + "ash": "ash", + "connect": "Bağlan", "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", + "clickToConnect": "Kabuk oturumu başlatmak için bağlan düğmesine tıklayın", + "connectingTo": "{{containerName}} konteynerine bağlanılıyor...", + "containerNotFound": "Konteyner bulunamadı", + "backToList": "Listeye Dön", "logs": "Günlükler", "stats": "İstatistikler", "consoleTab": "Konsol", - "startContainerToAccess": "Konsola erişmek için konteyneri başlatın." + "startContainerToAccess": "Konsola erişmek için konteyneri başlatın" }, "admin": { "sectionGeneral": "Genel", @@ -2581,28 +2715,28 @@ "ssoDocsLink": "Belgeleri görüntüle", "ssoProviderDocsLink": "Belgeleri görüntüle", "ssoNoProviders": "Hiçbir SSO sağlayıcısı yapılandırılmamış.", - "ssoProviderName": "Ekran adı", + "ssoProviderName": "Görünen Ad", "ssoProviderType": "Sağlayıcı Türü", "ssoDeleteProvider": "Sağlayıcıyı Sil", - "ssoDeleteConfirm": "Bu sağlayıcıyı silmek mi istiyorsunuz? İlişkili kullanıcılar giriş yapamayacak.", + "ssoDeleteConfirm": "Bu sağlayıcı silinsin mi? İlişkili kullanıcılar oturum açamaz.", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", - "ssoEnabled": "Etkinleştirilmiş", - "ssoDisabled": "Engelli", + "ssoEnabled": "Etkin", + "ssoDisabled": "Devre Dışı", "ssoSaveProvider": "Sağlayıcıyı Kaydet", - "ssoTestConnection": "Test Bağlantısı", + "ssoTestConnection": "Bağlantıyı Test Et", "ssoEditProvider": "Sağlayıcıyı Düzenle", "ldapHost": "LDAP Sunucusu", - "ldapPort": "Liman", - "ldapUseTls": "TLS (LDAPS) kullanın.", - "ldapBindDn": "DN'yi bağla", - "ldapBindPassword": "Bağlama Parolası", + "ldapPort": "Port", + "ldapUseTls": "TLS Kullan (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Parolası", "ldapUserSearchBase": "Kullanıcı Arama Tabanı", - "ldapUserSearchFilter": "Kullanıcı Arama Filtresi", - "ldapUsernameAttr": "Kullanıcı Adı Özelliği", - "ldapDisplayNameAttr": "Görüntü Adı Özelliği", + "ldapUserSearchFilter": "Kullanıcı Arama Süzgeci", + "ldapUsernameAttr": "Kullanıcı Adı Özniteliği", + "ldapDisplayNameAttr": "Görünen Ad Özniteliği", "ldapGroupSearchBase": "Grup Arama Tabanı", "ldapAdminGroup": "Yönetici Grubu", "ldapAllowedUsers": "İzin Verilen Kullanıcılar", @@ -2613,529 +2747,588 @@ "sectionApiKeys": "API Anahtarları", "sectionAuditLog": "Denetim Günlüğü", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Let's Encrypt'ten güvenilir bir SSL sertifikasını otomatik olarak oluşturun ve yenileyin. Genel bir alan adı ve 80 numaralı bağlantı noktası veya DNS erişimi gerektirir.", + "sslDescription": "Let's Encrypt'ten güvenilir bir SSL sertifikasını otomatik olarak alın ve yenileyin. Genel bir alan adı ve 80 numaralı bağlantı noktası veya DNS erişimi gerektirir.", "sslDocsLink": "SSL belgelerini görüntüle", - "sslDomain": "İhtisas", + "sslDomain": "Alan Adı", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "Sertifikanın kamuya açık alan adı.", + "sslDomainDesc": "Sertifika için genel alan adı.", "sslEmail": "E-posta", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encrypt bildirimleri ve hesap bilgileri için iletişim e-postası.", - "sslChallengeType": "Meydan Okuma Türü", - "sslChallengeTypeDesc": "Let's Encrypt'e alan adı sahipliğini nasıl kanıtlayabilirsiniz?", - "sslChallengeHttp": "HTTP (webroot) - internetten erişilebilen 80 numaralı bağlantı noktasını gerektirir.", - "sslChallengeDns": "DNS (Cloudflare) - Cloudflare API belirteci gerektirir.", - "sslCloudflareToken": "Cloudflare API Token", - "sslCloudflareTokenPlaceholder": "Jetonunuzu girin...", + "sslEmailDesc": "Let's Encrypt bildirimleri ve hesabı için iletişim e-postası.", + "sslChallengeType": "Doğrulama Türü", + "sslChallengeTypeDesc": "Let's Encrypt'e alan adı sahipliğini kanıtlama yöntemi.", + "sslChallengeHttp": "HTTP (webroot) - 80 numaralı bağlantı noktasının internetten erişilebilir olmasını gerektirir", + "sslChallengeDns": "DNS (Cloudflare) - bir Cloudflare API belirteci gerektirir", + "sslCloudflareToken": "Cloudflare API Belirteci", + "sslCloudflareTokenPlaceholder": "Belirteci girin...", "sslCloudflareTokenDesc": "Alan adınız için Zone:DNS:Edit iznine sahip kapsamlı belirteç.", "sslCertStatus": "Sertifika Durumu", - "sslCertStatusNone": "Sertifika yok.", + "sslCertStatusNone": "Sertifika yok", "sslCertStatusValid": "Geçerli", - "sslCertStatusExpiring": "Yakında sona erecek", - "sslCertStatusExpired": "Günü geçmiş", - "sslCertExpiresAt": "Süresi doluyor {{date}}", - "sslLastIssued": "Son yayınlanma tarihi {{date}}", - "sslRequestCert": "Sertifika Düzenleme / Yenileme", - "sslRequestCertLoading": "Sertifika talebi...", - "sslRequestCertSuccess": "Sertifika başarıyla düzenlendi ve yüklendi.", - "sslRequestCertFailed": "Sertifika talebi başarısız oldu.", + "sslCertStatusExpiring": "Yakında süresi dolacak", + "sslCertStatusExpired": "Süresi dolmuş", + "sslCertExpiresAt": "Son kullanma: {{date}}", + "sslLastIssued": "Son veriliş: {{date}}", + "sslRequestCert": "Sertifika Ver / Yenile", + "sslRequestCertLoading": "Sertifika isteniyor...", + "sslRequestCertSuccess": "Sertifika başarıyla verildi ve kuruldu", + "sslRequestCertFailed": "Sertifika talebi başarısız oldu", "sslSave": "Ayarları Kaydet", - "sslSaved": "SSL ayarları kaydedildi.", - "sslSaveFailed": "SSL ayarları kaydedilemedi.", - "sslRequiresDomain": "Alan adı ve e-posta adresi gereklidir.", - "sslInfoNote": "Sertifikayı oluşturduktan sonra, ortam değişkenlerinizde SSL'yi etkinleştirin (ENABLE_SSL=true) ve Termix'i yeniden başlatın.", - "auditLogTotal": "{{total}} toplam giriş", - "auditLogEmpty": "Denetim günlüğü kaydı bulunamadı.", - "auditLogSuccess": "Başarı", - "auditLogFailed": "Arızalı", + "sslSaved": "SSL ayarları kaydedildi", + "sslSaveFailed": "SSL ayarları kaydedilemedi", + "sslRequiresDomain": "Alan adı ve e-posta gereklidir", + "sslInfoNote": "Sertifikayı verdikten sonra, ortam değişkenlerinizde SSL'yi etkinleştirin (ENABLE_SSL=true) ve Termix'i yeniden başlatın.", + "sslManualOption": "Manuel (sertifika yükleme)", + "sslManualCert": "Sertifika (PEM)", + "sslManualCertPlaceholder": "-----SERTİFİKA BAŞLANGICI-----", + "sslManualKey": "Özel Anahtar (PEM)", + "sslManualKeyPlaceholder": "-----ÖZEL ANAHTAR BAŞLANGICI-----", + "sslManualDesc": "Sertifika yetkiliniz (CA) tarafından isteniyorsa, tam zincir de dahil olmak üzere mevcut sertifikanızı ve özel anahtarınızı yapıştırın.", + "sslManualUpload": "Sertifika Yükle ve Kur", + "sslManualUploadLoading": "Sertifika yükleniyor...", + "sslManualUploadSuccess": "Sertifika başarıyla yüklendi ve kuruldu.", + "sslManualUploadFailed": "Sertifika yükleme başarısız oldu", + "sslManualRequiresFields": "Sertifika ve özel anahtar gereklidir.", + "auditLogTotal": "Toplam {{total}} giriş", + "auditLogEmpty": "Denetim kaydı bulunamadı", + "auditLogSuccess": "Başarılı", + "auditLogFailed": "Başarısız", "auditLogClearFilters": "Filtreleri Temizle", - "auditLogPage": "Sayfa {{page}} / {{totalPages}} ({{total}} toplam)", + "auditLogPage": "Sayfa {{page}} / {{totalPages}} (Toplam {{total}})", "auditLogIp": "IP", - "auditLogResourceId": "Kaynak Kimliği", + "auditLogResourceId": "Kaynak ID", "auditLogFilterUser": "Kullanıcı", - "auditLogFilterAction": "Aksiyon", + "auditLogFilterAction": "İşlem", "auditLogFilterResourceType": "Kaynak Türü", "auditLogFilterStatus": "Durum", - "auditLogFilterFrom": "İtibaren", - "auditLogFilterTo": "İle", - "auditLogFilterAll": "Tüm", + "auditLogFilterFrom": "Başlangıç", + "auditLogFilterTo": "Bitiş", + "auditLogFilterAll": "Tümü", "allowRegistration": "Kullanıcı Kaydına İzin Ver", - "allowRegistrationDesc": "Yeni kullanıcıların kullanıcı adı ve şifre ile kendi kendilerine kayıt olmalarına izin verin.", - "allowPasswordLogin": "Şifreyle Girişe İzin Ver", - "allowPasswordLoginDesc": "Kullanıcı adı/şifre ile giriş yapın", + "allowRegistrationDesc": "Yeni kullanıcıların kullanıcı adı ve parola ile kendilerini kaydetmesine izin ver", + "allowPasswordLogin": "Parola ile Girişe İzin Ver", + "allowPasswordLoginDesc": "Kullanıcı adı/parola ile giriş", "oidcAutoProvision": "OIDC Otomatik Sağlama", - "oidcAutoProvisionDesc": "OIDC/SSO kullanıcıları için ilk girişlerinde otomatik hesap oluşturma (kayıt seçeneğinden bağımsız olarak)", - "oidcSilentLoginDefault": "Varsayılan olarak sessiz OIDC oturum açma", - "oidcSilentLoginDefaultDesc": "Her ziyaretinizde otomatik olarak OIDC giriş sayfasına yönlendirin, giriş formunu tamamen atlayın.", + "oidcAutoProvisionDesc": "OIDC/SSO kullanıcıları için ilk girişte otomatik hesap oluştur (kayıt ayarından bağımsız)", + "oidcSilentLoginDefault": "Varsayılan Olarak Sessiz OIDC Girişi", + "oidcSilentLoginDefaultDesc": "Her ziyarette otomatik olarak OIDC girişine yönlendir, giriş formunu tamamen atla", "allowPasswordReset": "Parola Sıfırlamaya İzin Ver", - "allowPasswordResetDesc": "Docker günlükleri aracılığıyla kodu sıfırla", + "allowPasswordResetDesc": "Docker günlükleri aracılığıyla sıfırlama kodu", "commandHistoryEnabled": "Komut Geçmişi", - "commandHistoryEnabledDesc": "Komut geçmişi kaydına izin verin. Devre dışı bırakıldığında, sunucu başına ayarlardan bağımsız olarak geçmiş kaydedilmez.", - "updateCommandHistoryFailed": "Komut geçmişi ayarı güncellenemedi.", + "commandHistoryEnabledDesc": "Komut geçmişi kaydına izin ver. Devre dışı bırakıldığında, host başına ayarlara bakılmaksızın geçmiş kaydedilmez.", + "updateCommandHistoryFailed": "Komut geçmişi ayarı güncellenemedi", + "analyticsEnabled": "Anonim Kullanım İstatistiklerini Paylaşın", + "analyticsEnabledDesc": "Termix'i geliştirmeye yardımcı olmak için kullanıcı, sunucu ve özellik kullanımına ilişkin anonim günlük sayım gönderir. Hiçbir kişisel veri veya bağlantı bilgisi asla dahil edilmez.", + "updateAnalyticsFailed": "Analiz ayarlarını güncelleme başarısız oldu.", + "sessionSharingGloballyEnabled": "Oturum Paylaşımına İzin Ver", + "sessionSharingGloballyEnabledDesc": "Canlı terminal, RDP, VNC ve Telnet oturumlarının tüm örnek genelinde paylaşılmasına izin verin. Devre dışı bırakıldığında, sunucu başına paylaşım ayarlarının tümünü geçersiz kılar.", + "updateSessionSharingFailed": "Oturum paylaşım ayarı güncellenemedi.", "sessionTimeout": "Oturum Zaman Aşımı", "hours": "saat", - "sessionTimeoutRange": "Minimum 1 saat · Maksimum 720 saat", - "monitoringDefaults": "Varsayılan Ayarları İzleme", + "sessionTimeoutRange": "En az 1 saat · En fazla 720 saat", + "monitoringDefaults": "İzleme Varsayılanları", "statusCheck": "Durum Kontrolü", "metrics": "Metrikler", - "sec": "saniye", - "logLevel": "Günlük Seviyesi", - "enableGuacamole": "Guacamole'yi etkinleştirin", - "enableGuacamoleDesc": "RDP/VNC uzaktan masaüstü", + "sec": "sn", + "logLevel": "Günlük Düzeyi", + "enableGuacamole": "Guacamole'yi Etkinleştir", + "enableGuacamoleDesc": "RDP/VNC uzak masaüstü", "enableGuacamoleDocsLink": "Belgeleri görüntüle", - "guacdUrl": "guacd URL", + "guacdUrl": "guacd URL'si", "tailscaleApiKey": "Tailscale API Anahtarı", - "tailscaleApiKeyDescription": "Ana bilgisayar düzenleyicisinde cihaz keşfi için kullanılır. tailscale.com/admin/settings/keys adresinden bir anahtar oluşturun.", + "tailscaleApiKeyDescription": "Host düzenleyicisinde cihaz keşfi için kullanılır. tailscale.com/admin/settings/keys adresinden bir anahtar oluşturun.", "tailscaleApiKeyDocsLink": "Belgeleri görüntüle", - "oidcDescription": "OpenID Connect'i SSO için yapılandırın. * ile işaretlenmiş alanlar zorunludur.", + "oidcDescription": "SSO için OpenID Connect yapılandırması. * ile işaretli alanlar zorunludur.", "oidcDocsLink": "Belgeleri görüntüle", - "oidcClientId": "Müşteri Kimliği", - "oidcClientSecret": "Müşteri Gizli Bilgileri", + "oidcClientId": "İstemci Kimliği", + "oidcClientSecret": "İstemci Sırrı", "oidcAuthUrl": "Yetkilendirme URL'si", - "oidcIssuerUrl": "Yayıncı URL'si", - "oidcTokenUrl": "Token URL", + "oidcIssuerUrl": "Sağlayıcı URL'si", + "oidcTokenUrl": "Belirteç URL'si", "oidcUserIdentifier": "Kullanıcı Tanımlayıcı Yolu", - "oidcDisplayName": "Görüntü Adı Yolu", + "oidcDisplayName": "Görünen Ad Yolu", "oidcScopes": "Kapsamlar", - "oidcUserinfoUrl": "Kullanıcı bilgisi URL'sini geçersiz kıl", + "oidcUserinfoUrl": "Userinfo 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.", + "oidcAllowedUsersDesc": "Her satıra bir e-posta. Tümüne izin vermek için boş bırakın.", "oidcAdminGroup": "Yönetici Grubu", - "oidcAdminGroupDesc": "Bu gruptaki kullanıcılara yönetici yetkisi verilir. Grup senkronizasyonunu devre dışı bırakmak için boş bırakın.", - "oidcGroupClaim": "Grup Tazminatı", - "oidcGroupClaimDesc": "İsteğe bağlı. Kullanıcının gruplarını içeren talep yolu. Varsayılan olarak gruplar, roller, ardından grup şeklindedir. Özel talebe sahip sağlayıcılar için (örneğin Zitadel) bunu kullanın.", + "oidcAdminGroupDesc": "Bu gruptaki kullanıcılara yönetici yetkisi verilir. Grup eşitlemeyi devre dışı bırakmak için boş bırakın.", + "oidcGroupClaim": "Grup Talebi", + "oidcGroupClaimDesc": "İsteğe bağlı. Kullanıcının gruplarını içeren talep yolu. Varsayılan olarak groups, roles, ardından group kullanılır. Özel talep kullanan sağlayıcılar için bunu kullanın (örn. Zitadel).", "oidcCaCert": "Özel CA Sertifikası", - "oidcCaCertDesc": "İsteğe bağlı. Özel veya kendi imzasını taşıyan bir CA kullanan OIDC sağlayıcıları için PEM kodlu CA sertifikası. Sistem güven deposunu kullanmak için boş bırakın.", - "removeOidc": "Kaldırmak", - "usersCount": "{{count}} kullanıcılar", - "createUser": "Yaratmak", + "oidcCaCertDesc": "İsteğe bağlı. Özel veya kendinden imzalı CA kullanan OIDC sağlayıcıları için PEM kodlu CA sertifikası. Sistem güven deposunu kullanmak için boş bırakın.", + "removeOidc": "Kaldır", + "usersCount": "{{count}} kullanıcı", + "createUser": "Oluştur", "newRole": "Yeni Rol", - "roleName": "İsim", - "roleDisplayName": "Ekran adı", - "roleDescription": "Tanım", - "rolesCount": "{{count}} roller", - "createRole": "Yaratmak", + "roleName": "Ad", + "roleDisplayName": "Görünen Ad", + "roleDescription": "Açıklama", + "rolesCount": "{{count}} rol", + "createRole": "Oluştur", "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...", + "exportDatabaseDesc": "Tüm sunucuların, kimlik bilgilerinin ve ayarların yedeğini indir", + "export": "Dışa Aktar", + "exporting": "Dışa aktarılıyor...", "importDatabase": "Veritabanını İçe Aktar", - "importDatabaseDesc": ".sqlite yedekleme dosyasından geri yükleme", + "importDatabaseDesc": "Bir .sqlite yedek dosyasından geri yükle", "importDatabaseSelected": "Seçilen: {{name}}", "selectFile": "Dosya Seç", - "changeFile": "Değiştirmek", - "import": "İçe aktarmak", + "changeFile": "Değiştir", + "import": "İçe Aktar", "importing": "İçe aktarılıyor...", - "apiKeysCount": "{{count}} anahtarlar", - "apiKeysDocsLink": "Belgeleri görüntüle", + "apiKeysCount": "{{count}} anahtar", + "apiKeysDocsLink": "Belgelere bak", "newApiKey": "Yeni API Anahtarı", "apiKeyCreatedWarning": "Anahtar oluşturuldu - şimdi kopyalayın, bir daha gösterilmeyecek.", - "apiKeyName": "İsim", + "apiKeyName": "Ad", "apiKeyUser": "Kullanıcı", "apiKeySelectUser": "Bir kullanıcı seçin...", - "apiKeyExpiresAt": "Son Geçerlilik Tarihi", + "apiKeyExpiresAt": "Bitiş Tarihi", "createKey": "Anahtar Oluştur", - "apiKeyNoExpiry": "Son kullanma tarihi yok", + "apiKeyNoExpiry": "Süresiz", "revokedBadge": "İPTAL EDİLDİ", - "authTypeDual": "Çift Kimlik Doğrulama", + "authTypeDual": "Çift Doğrulama", "authTypeOidc": "OIDC", "authTypeLocal": "Yerel", "adminStatusAdministrator": "Yönetici", - "adminStatusRegularUser": "Düzenli Kullanıcı", + "adminStatusRegularUser": "Normal Kullanıcı", "adminBadge": "YÖNETİCİ", "systemBadge": "SYS", - "customBadge": "GELENEK", - "youBadge": "SEN", - "sessionsActive": "{{count}} aktif", - "sessionActive": "Aktif: {{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.", + "customBadge": "ÖZEL", + "youBadge": "SİZ", + "sessionsActive": "{{count}} etkin", + "sessionActive": "Etkin: {{time}}", + "sessionExpires": "Bitiş: {{time}}", + "revokeAll": "Tümü", + "revokeAllSessionsSuccess": "Kullanıcının tüm oturumları iptal edildi", + "revokeAllSessionsFailed": "Oturumlar iptal edilemedi", + "revokeSessionFailed": "Oturum iptal edilemedi", "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.", + "noCustomRoles": "Özel rol tanımlanmamış", + "removeRoleFailed": "Rol kaldırılamadı", + "assignRoleFailed": "Rol atanamadı", + "deleteRoleFailed": "Rol silinemedi", "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", + "revokeAllUserSessionsDesc": "Tüm cihazlarda yeniden girişi zorla", + "revoke": "İptal Et", "deleteUserWarning": "Bu kullanıcının silinmesi kalıcıdır.", - "deleteUser": "{{username}} silin", + "deleteUser": "{{username}} sil", "deleting": "Siliniyor...", - "deleteUserFailed": "Kullanıcı silme işlemi başarısız oldu.", - "deleteUserSuccess": "Kullanıcı \"{{username}}\" silindi", + "deleteUserFailed": "Kullanıcı silinemedi", + "deleteUserSuccess": "\"{{username}}\" kullanıcısı silindi", "deleteRoleSuccess": "\"{{name}}\" rolü silindi", "revokeKeySuccess": "\"{{name}}\" anahtarı iptal edildi", - "revokeKeyFailed": "Anahtarı iptal etme başarısız oldu.", + "revokeKeyFailed": "Anahtar iptal edilemedi", "copiedToClipboard": "Panoya kopyalandı", - "done": "Tamamlamak", + "done": "Tamam", "createUserTitle": "Kullanıcı Oluştur", - "createUserDesc": "Yeni bir yerel hesap oluşturun.", + "createUserDesc": "Yeni bir yerel hesap oluştur.", "createUserUsername": "Kullanıcı adı", - "createUserPassword": "Şifre", + "createUserPassword": "Parola", "createUserPasswordHint": "En az 6 karakter.", - "createUserEnterUsername": "Kullanıcı adınızı girin", - "createUserEnterPassword": "Şifrenizi girin", + "createUserEnterUsername": "Kullanıcı adını girin", + "createUserEnterPassword": "Parolayı 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ı", + "editUserUsername": "Kullanıcı Adı", "editUserAuthType": "Kimlik Doğrulama Türü", "editUserAdminStatus": "Yönetici Durumu", - "editUserUserId": "Kullanıcı kimliği", + "editUserUserId": "Kullanıcı Kimliği", "linkAccountTitle": "Hesapları Bağla", - "linkAccountDesc": "OIDC hesabını {{username}} mevcut bir yerel hesapla birleştirin.", - "linkAccountDescLocal": "Yerel bir hesabı {{username}} mevcut yalnızca OIDC hesabıyla bağlayın.", - "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.", + "linkAccountDesc": "OIDC hesabını {{username}} mevcut bir yerel hesapla birleştir.", + "linkAccountDescLocal": "Yerel hesabı {{username}} mevcut bir yalnızca OIDC hesabıyla bağla.", + "linkAccountWarningTitle": "Bu işlem şunları yapacak:", + "linkAccountEffect1": "Yalnızca OIDC hesabını sil", + "linkAccountEffect2": "Hedef hesaba OIDC girişi ekle", + "linkAccountEffect3": "Hem OIDC hem de parola girişine izin ver", "linkAccountTargetUsername": "Yerel Hesap Kullanıcı Adı", - "linkAccountTargetPlaceholder": "Bağlantı kurmak istediğiniz yerel hesap kullanıcı adını girin.", + "linkAccountTargetPlaceholder": "Bağlanacak yerel hesap kullanıcı adını girin", "linkAccountOidcUsername": "OIDC Hesap Kullanıcı Adı", - "linkAccountOidcPlaceholder": "Birleştirmek istediğiniz yalnızca OIDC hesabının kullanıcı adını girin.", - "linkAccountOidcNotFound": "Bu kullanıcı adıyla OIDC'ye özel hesap bulunamadı.", + "linkAccountOidcPlaceholder": "Birleştirilecek yalnızca OIDC hesap kullanıcı adını girin", + "linkAccountOidcNotFound": "Bu kullanıcı adıyla bir yalnızca OIDC hesabı bulunamadı", "linkAccounts": "Hesapları Bağla", - "linkAccountSuccess": "Hesaplar başarıyla bağlandı.", - "linkAccountFailed": "Hesapları bağlama başarısız oldu.", - "linkAccountInProgress": "Bağlantı kuruluyor...", - "unlinkAccountTitle": "OIDC bağlantısını kaldır", - "unlinkAccountDesc": "{{username}}adresinden OIDC kimlik doğrulamasını kaldırın. Artık yalnızca şifreleriyle giriş yapabilecekler.", - "unlinkAccountWarning": "Bu işlem, bu hesaptan OIDC oturum açma işlemini kaldıracaktır. Kullanıcının oturum açmaya devam edebilmesi için bir parola belirlemiş olması gerekir.", - "unlinkAccount": "OIDC bağlantısını kes", - "unlinkAccountInProgress": "Bağlantı kesiliyor...", - "unlinkAccountSuccess": "OIDC bağlantısı başarıyla kesildi.", - "unlinkAccountFailed": "OIDC bağlantısını kaldırma işlemi başarısız oldu.", - "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.", - "cannotDisablePasswordLoginWithTotp": "Bir veya daha fazla kullanıcı için 2FA etkinleştirilmişken parola ile giriş devre dışı bırakılamaz. Öncelikle 2FA'yı devre dışı bırakın.", - "updateOidcAutoProvisionFailed": "OIDC otomatik sağlama ayarı güncellenemedi.", - "updateOidcSilentLoginDefaultFailed": "Sessiz OIDC oturum açma ayarını güncelleme başarısız oldu.", - "updatePasswordResetFailed": "Parola sıfırlama ayarı güncellenemedi.", - "sessionTimeoutRange2": "Oturum zaman aşımı 1 ile 720 saat arasında olmalıdır.", + "linkAccountSuccess": "Hesaplar başarıyla bağlandı", + "linkAccountFailed": "Hesaplar bağlanamadı", + "linkAccountInProgress": "Bağlanıyor...", + "unlinkAccountTitle": "OIDC Bağlantısını Kaldır", + "unlinkAccountDesc": "{{username}} için OIDC kimlik doğrulamasını kaldır. Yalnızca parolayla giriş yapabilecek.", + "unlinkAccountWarning": "Bu işlem, bu hesaptan OIDC girişini kaldıracak. Kullanıcının giriş yapmaya devam edebilmesi için bir parola belirlemiş olması gerekir.", + "unlinkAccount": "OIDC Bağlantısını Kaldır", + "unlinkAccountInProgress": "Bağlantı kaldırılıyor...", + "unlinkAccountSuccess": "OIDC bağlantısı başarıyla kaldırıldı", + "unlinkAccountFailed": "OIDC bağlantısı kaldırılamadı", + "saving": "Kaydediliyor...", + "updateRegistrationFailed": "Kayıt ayarı güncellenemedi", + "updatePasswordLoginFailed": "Parola girişi ayarı güncellenemedi", + "cannotDisablePasswordLoginWithTotp": "Bir veya daha fazla kullanıcı için 2FA etkinken parola girişi devre dışı bırakılamaz. Önce 2FA'yı devre dışı bırakın.", + "updateOidcAutoProvisionFailed": "OIDC otomatik sağlama ayarı güncellenemedi", + "updateOidcSilentLoginDefaultFailed": "Sessiz OIDC girişi 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.", + "sessionTimeoutSaveFailed": "Oturum zaman aşımı kaydedilemedi", "monitoringIntervalInvalid": "Geçersiz aralık değerleri", - "monitoringSaved": "İzleme ayarları kaydedildi.", - "monitoringSaveFailed": "İzleme ayarları kaydedilemedi.", + "monitoringSaved": "İzleme ayarları kaydedildi", + "monitoringSaveFailed": "İzleme ayarları kaydedilemedi", "metricsHistoryRetention": "Metrik Geçmişi Saklama", "metricsHistoryRetentionRange": "1 ila 90 gün", - "days": "günler", - "guacamoleSaved": "Guacamole ayarları kaydedildi.", - "guacamoleSaveFailed": "Guacamole ayarları kaydedilemedi.", - "guacamoleUpdateFailed": "Guacamole ayarlarını güncelleme başarısız oldu.", - "tailscaleSettingsSaved": "Kuyruk pulu ayarları kaydedildi.", - "tailscaleSettingsSaveFailed": "Tailscale ayarları kaydedilemedi.", - "logLevelUpdateFailed": "Günlük düzeyi güncellenemedi.", - "oidcSaved": "OIDC yapılandırması kaydedildi.", - "oidcSaveFailed": "OIDC yapılandırması kaydedilemedi.", + "days": "gün", + "guacamoleSaved": "Guacamole ayarları kaydedildi", + "guacamoleSaveFailed": "Guacamole ayarları kaydedilemedi", + "guacamoleUpdateFailed": "Guacamole ayarı güncellenemedi", + "tailscaleSettingsSaved": "Tailscale ayarları kaydedildi", + "tailscaleSettingsSaveFailed": "Tailscale ayarları kaydedilemedi", + "logLevelUpdateFailed": "Günlük seviyesi 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.", - "manageUserData": "Kullanıcı verilerini yönetin", - "backToUsers": "Kullanıcılara geri dön", + "oidcRemoveFailed": "OIDC yapılandırması kaldırılamadı", + "createUserRequired": "Kullanıcı adı ve parola zorunludur", + "createUserPasswordTooShort": "Parola en az 6 karakter olmalıdır", + "createUserSuccess": "Kullanıcı \"{{username}}\" oluşturuldu", + "createUserFailed": "Kullanıcı oluşturulamadı", + "updateAdminStatusFailed": "Yönetici durumu güncellenemedi", + "allSessionsRevoked": "Tüm oturumlar iptal edildi", + "revokeSessionsFailed": "Oturumlar iptal edilemedi", + "manageUserData": "Kullanıcı verilerini yönet", + "backToUsers": "Kullanıcılara dön", "manageTabAccount": "Hesap", - "manageTabHosts": "Ev sahipleri", - "manageTabCredentials": "Kimlik Bilgileri", - "manageTabSnippets": "Kısa bölümler", + "manageTabHosts": "Sunucular", + "manageTabCredentials": "Kimlik bilgileri", + "manageTabSnippets": "Parçacıklar", "manageTabSessions": "Oturumlar", "manageTabDanger": "Tehlike", - "manageEditorBack": "{{username}}'ın verilerine geri dön", + "manageEditorBack": "{{username}} verilerine dön", "dataLockedBadge": "KİLİTLİ", - "dataLockedNotice": "{{username}}'ın verileri, bir sonraki girişlerine kadar kilitli kalır. O zamana kadar sunucuları, kimlik bilgileri ve kod parçacıkları görüntülenemez veya düzenlenemez.", - "resetPasswordTitle": "Şifreyi Sıfırla", - "resetPasswordOidcOnly": "Bu kullanıcı harici bir sağlayıcı aracılığıyla giriş yapıyor ve şifresi yok.", - "resetPasswordPlaceholder": "Yeni Şifre", + "dataLockedNotice": "{{username}} kullanıcısının verileri, bir sonraki oturum açana kadar kilitli kalır. Bu süre zarfında ana makineleri, kimlik bilgileri ve kod parçacıkları görüntülenemez veya düzenlenemez.", + "resetPasswordTitle": "Parolayı Sıfırla", + "resetPasswordOidcOnly": "Bu kullanıcı harici bir sağlayıcı üzerinden oturum açar ve parolası yoktur.", + "resetPasswordPlaceholder": "Yeni parola", "resetPasswordBtn": "Sıfırla", "resetPasswordWorking": "Sıfırlanıyor...", - "resetPasswordSuccess": "Şifre sıfırlama", - "resetPasswordSuccessWiped": "Parola sıfırlama. Kullanıcının şifrelenmiş verileri silindi.", - "resetPasswordFailed": "Parola sıfırlama başarısız oldu.", - "resetPasswordConfirmWipe": "{{username}} şifreleme yükseltmesinden beri giriş yapmadı, bu nedenle verileri kurtarılamaz. Şimdi sıfırlama işlemi, ana bilgisayarlarını, kimlik bilgilerini ve kod parçacıklarını silecektir. Devam edilsin mi?", + "resetPasswordSuccess": "Parola sıfırlandı", + "resetPasswordSuccessWiped": "Parola sıfırlandı. Kullanıcının şifrelenmiş verileri silindi.", + "resetPasswordFailed": "Parola sıfırlanamadı", + "resetPasswordConfirmWipe": "{{username}} şifreleme yükseltmesinden bu yana oturum açmadı, bu nedenle verileri kurtarılamaz. Şimdi sıfırlamak ana makinelerini, kimlik bilgilerini ve kod parçacıklarını siler. Devam edilsin mi?", "totpSectionTitle": "İki Faktörlü Kimlik Doğrulama", - "totpStatusEnabled": "Bu kullanıcı için TOTP etkinleştirilmiştir.", - "totpStatusDisabled": "Bu kullanıcı için TOTP etkinleştirilmemiş.", - "disableTotp": "Devre dışı bırakmak", - "disableTotpConfirm": "{{username}}için iki faktörlü kimlik doğrulamayı devre dışı bırak? Sadece şifreleriyle giriş yapabilecekler.", + "totpStatusEnabled": "Bu kullanıcı için TOTP etkin", + "totpStatusDisabled": "Bu kullanıcı için TOTP etkin değil", + "disableTotp": "Devre Dışı Bırak", + "disableTotpConfirm": "{{username}} için iki faktörlü kimlik doğrulama devre dışı bırakılsın mı? Yalnızca parolasıyla oturum açabilecek.", "totpDisabledSuccess": "TOTP devre dışı bırakıldı", - "totpDisableFailed": "TOTP devre dışı bırakılamadı.", + "totpDisableFailed": "TOTP devre dışı bırakılamadı", "manageApiKeys": "API Anahtarları", "apiKeyNamePlaceholder": "Anahtar adı", - "apiKeyCopyNotice": "Bu anahtarı şimdi kopyalayın, bir daha gösterilmeyecek.", + "apiKeyCopyNotice": "Bu anahtarı şimdi kopyalayın, bir daha gösterilmeyecektir.", "noApiKeysForUser": "API anahtarı yok", - "apiKeyDeleteFailed": "API anahtarını silme işlemi başarısız oldu.", + "apiKeyDeleteFailed": "API anahtarı silinemedi", "exportUserData": "Veri Dışa Aktarımı", - "exportUserDataDesc": "Bu kullanıcının sunucularını, kimlik bilgilerini ve dosya yöneticisi verilerini JSON formatında indirin. Gizli bilgiler şifresi çözülmüştür.", + "exportUserDataDesc": "Bu kullanıcının ana makinelerini, kimlik bilgilerini ve dosya yöneticisi verilerini JSON olarak indir. Gizli bilgilerin şifreleri çözülür.", "exportUserDataSuccess": "Kullanıcı verileri dışa aktarıldı", - "exportUserDataFailed": "Kullanıcı verilerini dışa aktarma başarısız oldu.", - "hostsCount": "{{count}} sunucular", - "addHostForUser": "Sunucu Ekle", - "noHostsForUser": "Bu kullanıcının hiç sunucusu yok.", - "connectToHost": "Bağlamak", - "deleteHostConfirm": "{{username}}'e ait \"{{name}}\" sunucusunu silmek mi istiyorsunuz?", - "hostDeletedSuccess": "Sunucu silindi", - "hostDeleteFailed": "Sunucuyu silme işlemi başarısız oldu.", - "credentialsCount": "{{count}} kimlik bilgileri", + "exportUserDataFailed": "Kullanıcı verileri dışa aktarılamadı", + "hostsCount": "{{count}} ana makine", + "addHostForUser": "Ana Makine Ekle", + "noHostsForUser": "Bu kullanıcının ana makinesi yok", + "connectToHost": "Bağlan", + "deleteHostConfirm": "\"{{name}}\" adlı {{username}} kullanıcısına ait ana makine silinsin mi?", + "hostDeletedSuccess": "Ana makine silindi", + "hostDeleteFailed": "Ana makine silinemedi", + "credentialsCount": "{{count}} kimlik bilgisi", "addCredentialForUser": "Kimlik Bilgisi Ekle", - "noCredentialsForUser": "Bu kullanıcının kimlik bilgileri yok.", - "deleteCredentialConfirm": "{{username}}'e ait \"{{name}}\" kimlik bilgilerini silmek mi istiyorsunuz?", + "noCredentialsForUser": "Bu kullanıcının kimlik bilgisi yok", + "deleteCredentialConfirm": "{{username}} kullanıcısına ait \"{{name}}\" kimlik bilgisi silinsin mi?", "credentialDeletedSuccess": "Kimlik bilgisi silindi", - "credentialDeleteFailed": "Kimlik bilgilerini silme işlemi başarısız oldu.", - "snippetsCount": "{{count}} parçalar", + "credentialDeleteFailed": "Kimlik bilgisi silinemedi", + "snippetsCount": "{{count}} kod parçası", "addSnippetForUser": "Kod Parçası Ekle", - "noSnippetsForUser": "Bu kullanıcının hiç özeti yok.", - "snippetRequiredFields": "Kod parçasının adı ve içeriği zorunludur.", - "snippetNamePlaceholder": "Parça adı", + "noSnippetsForUser": "Bu kullanıcıya ait kod parçası yok", + "snippetRequiredFields": "Kod parçası adı ve içeriği zorunludur", + "snippetNamePlaceholder": "Kod parçası adı", "snippetContentPlaceholder": "Komut içeriği", "snippetFolderPlaceholder": "Klasör (isteğe bağlı)", - "snippetSaved": "Kısa bölüm kaydedildi", - "snippetSaveFailed": "Kod parçacığı kaydedilemedi.", - "deleteSnippetConfirm": "{{username}}'e ait \"{{name}}\" kod parçasını silmek mi istiyorsunuz?", + "snippetSaved": "Kod parçası kaydedildi", + "snippetSaveFailed": "Kod parçası kaydedilemedi", + "deleteSnippetConfirm": "{{username}} kullanıcısına ait \"{{name}}\" kod parçası silinsin mi?", "snippetDeletedSuccess": "Kod parçası silindi", - "snippetDeleteFailed": "Kod parçasını silme işlemi başarısız oldu.", - "noSessionsForUser": "Aktif oturum yok", - "deleteUserDangerDesc": "{{username}} ve tüm verileri (ana bilgisayarlar, kimlik bilgileri, kod parçacıkları, geçmiş) kalıcı olarak silin. Bu işlem geri alınamaz.", - "deleteUserConfirm": "{{username}} ve tüm verilerini kalıcı olarak silmek mi?", - "deleteUserAdminBlocked": "Bu kullanıcıyı silmeden önce yönetici statüsünü kaldırın.", - "createRoleRequired": "Ad ve görünen ad zorunludur.", + "snippetDeleteFailed": "Kod parçası silinemedi", + "noSessionsForUser": "Etkin oturum yok", + "deleteUserDangerDesc": "{{username}} kullanıcısını ve tüm verilerini (ana bilgisayarlar, kimlik bilgileri, kod parçaları, geçmiş) kalıcı olarak silin. Bu işlem geri alınamaz.", + "deleteUserConfirm": "{{username}} kullanıcısı ve tüm verileri kalıcı olarak silinsin mi?", + "deleteUserAdminBlocked": "Bu kullanıcıyı silmeden önce yönetici yetkisini kaldırın.", + "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.", - "sectionHostDefaults": "Sunucu Varsayılanları", - "hostDefaultsDesc": "Yeni bir sunucu oluşturulurken ayarlar otomatik olarak uygulanır. Ancak, sunucular bu ayarları kendileri değiştirebilir.", + "createRoleFailed": "Rol oluşturulamadı", + "apiKeyNameRequired": "Anahtar adı zorunludur", + "apiKeyUserRequired": "Kullanıcı ID zorunludur", + "apiKeyCreatedSuccess": "\"{{name}}\" API anahtarı oluşturuldu", + "apiKeyCreateFailed": "API anahtarı oluşturulamadı", + "exportSuccess": "Veritabanı başarıyla dışa aktarıldı", + "exportFailed": "Veritabanı dışa aktarılamadı", + "importSelectFile": "Lütfen önce bir dosya seçin", + "importCompleted": "İçe aktarma tamamlandı: {{total}} öğe aktarıldı, {{skipped}} atlandı", + "importFailed": "İçe aktarma başarısız: {{error}}", + "importError": "Veritabanı içe aktarılamadı", + "sectionHostDefaults": "Ana Bilgisayar Varsayılanları", + "hostDefaultsDesc": "Yeni bir ana bilgisayar oluştururken otomatik olarak uygulanan ayarlar. Bağımsız ana bilgisayarlar bu ayarların üzerine yazabilir.", "hostDefaultsSocks5": "SOCKS5 Proxy", - "hostDefaultsUseSocks5": "SOCKS5 Proxy'yi etkinleştirin", - "hostDefaultsUseSocks5Desc": "Yeni sunucuların tümünde SOCKS5 proxy'sini önceden doldurun.", + "hostDefaultsUseSocks5": "SOCKS5 Proxy'yi Etkinleştir", + "hostDefaultsUseSocks5Desc": "Tüm yeni ana bilgisayarlarda SOCKS5 proxy'yi önceden doldur", "hostDefaultsSocks5Host": "Proxy Sunucusu / Bağlantı Noktası", "hostDefaultsSocks5Username": "Proxy Kullanıcı Adı", "hostDefaultsSocks5Password": "Proxy Parolası", - "hostDefaultsMetrics": "Sunucu Metrikleri", + "hostDefaultsMetrics": "Ana Bilgisayar Ölçümleri", "hostDefaultsMetricsEnabled": "Ölçümleri Etkinleştir", - "hostDefaultsMetricsEnabledDesc": "Varsayılan olarak yeni sunucularda CPU, bellek ve diğer istatistikleri topla.", - "hostDefaultsStatusCheckEnabled": "Durum Kontrolünü Etkinleştir", - "hostDefaultsStatusCheckEnabledDesc": "Yeni sunucularda varsayılan olarak çevrimiçi/çevrimdışı durumunu sorgula.", - "hostDefaultsTerminal": "terminal", + "hostDefaultsMetricsEnabledDesc": "Yeni ana bilgisayarlarda varsayılan olarak CPU, bellek ve diğer istatistikleri topla", + "hostDefaultsStatusCheckEnabled": "Durum Denetimini Etkinleştir", + "hostDefaultsStatusCheckEnabledDesc": "Yeni ana bilgisayarlarda varsayılan olarak çevrimiçi/çevrimdışı durumunu sorgula", + "hostDefaultsTerminal": "Terminal", "hostDefaultsSessionLogging": "Oturum Günlüğü", - "hostDefaultsSessionLoggingDesc": "Yeni sunucularda terminal oturumlarını varsayılan olarak kaydet", + "hostDefaultsSessionLoggingDesc": "Yeni ana bilgisayarlarda varsayılan olarak terminal oturumlarını kaydet", "hostDefaultsCommandHistory": "Komut Geçmişi", - "hostDefaultsCommandHistoryDesc": "Yeni sunucularda komut geçmişini varsayılan olarak izle.", - "hostDefaultsSaved": "Sunucu varsayılan ayarları kaydedildi.", - "hostDefaultsSaveFailed": "Sunucu varsayılan ayarları kaydedilemedi.", + "hostDefaultsCommandHistoryDesc": "Yeni ana bilgisayarlarda varsayılan olarak komut geçmişini izle", + "hostDefaultsSaved": "Ana bilgisayar varsayılanları kaydedildi", + "hostDefaultsSaveFailed": "Ana bilgisayar varsayılanları kaydedilemedi", "rolePermissions": { - "count": "{{count}} izinler", - "editAction": "Düzenleme izinleri", - "loadError": "İzin kataloğu yüklenemedi.", - "saved": "Rol izinleri kaydedildi.", - "saveError": "Rol izinleri kaydedilemedi.", - "save": "Kaydetmek", - "saving": "Tasarruf..." + "count": "{{count}} izin", + "editAction": "İzinleri düzenle", + "loadError": "İzin kataloğu yüklenemedi", + "saved": "Rol izinleri kaydedildi", + "saveError": "Rol izinleri kaydedilemedi", + "save": "Kaydet", + "saving": "Kaydediliyor..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Ev sahibi", + "hostLabel": "Ana Bilgisayar", "hostPlaceholder": "192.168.1.1 veya example.com", - "portLabel": "Liman", + "portLabel": "Bağlantı Noktası", "portPlaceholder": "22", - "usernameLabel": "Kullanıcı adı", + "usernameLabel": "Kullanıcı Adı", "usernamePlaceholder": "kullanıcı adı", - "authLabel": "Yetki", - "passwordLabel": "Şifre", - "passwordPlaceholder": "şifre", + "authLabel": "Kimlik Doğrulama", + "passwordLabel": "Parola", + "passwordPlaceholder": "parola", "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" + "privateKeyPlaceholder": "Özel anahtarı yapıştır...", + "credentialLabel": "Kimlik Bilgisi", + "credentialPlaceholder": "Kayıtlı bir kimlik bilgisi seçin", + "connectToTerminal": "Terminale Bağlan", + "connectToFiles": "Dosyalara Bağlan" }, "history": { - "noTerminalSelected": "Terminal seçilmedi.", - "noTerminalSelectedHint": "SSH terminal sekmesini açarak komut geçmişini görüntüleyin.", - "searchPlaceholder": "Arama geçmişi...", + "noTerminalSelected": "Terminal seçilmedi", + "noTerminalSelectedHint": "Komut geçmişini görüntülemek için bir SSH terminal sekmesi açın", + "searchPlaceholder": "Geçmişte ara...", "clearAll": "Tümünü Temizle", - "noHistoryEntries": "Geçmiş kaydı yok.", - "trackingDisabled": "Geçmiş izleme devre dışı bırakıldı", - "trackingDisabledHint": "Bunu sunucu terminal ayarlarından etkinleştirin." + "noHistoryEntries": "Geçmiş girdisi yok", + "trackingDisabled": "Geçmiş takibi kapalı", + "trackingDisabledHint": "Bunu sunucunun terminal ayarlarından etkinleştirin." }, "sshTools": { - "keyRecordingTitle": "Anahtar Kaydı", - "recordToTerminals": "Terminallere kayıt yapın", - "selectAll": "Tüm", + "keyRecordingTitle": "Tuş Kaydı", + "recordToTerminals": "Terminallere Kaydet", + "selectAll": "Tümü", "selectNone": "Hiçbiri", - "noTerminalTabsOpen": "Açık terminal sekmesi yok.", - "selectTerminalsAbove": "Yukarıdaki terminalleri seçin", + "noTerminalTabsOpen": "Açık terminal sekmesi yok", + "selectTerminalsAbove": "Yukarıdaki terminalleri seç", "broadcastInputPlaceholder": "Tuş vuruşlarını yayınlamak için buraya yazın...", - "fillPassword": "Şifreyi girin", - "fillPasswordSuccess": "{{count}} terminal(ler)ine parola girildi.", - "fillPasswordMissing": "{{count}} seçili terminal(ler) için kaydedilmiş parola yok.", + "fillPassword": "Parolayı doldur", + "fillPasswordSuccess": "Parola {{count}} terminale dolduruldu", + "fillPasswordMissing": "Seçili {{count}} terminal için kayıtlı parola yok", "stopRecording": "Kaydı Durdur", "startRecording": "Kaydı Başlat", "settingsTitle": "Ayarlar", - "enableRightClickCopyPaste": "Sağ tıklama ile kopyala/yapıştır özelliğini etkinleştirin." + "enableRightClickCopyPaste": "Sağ tık kopyala/yapıştırmayı etkinleştir" }, "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", + "selectLayoutAbove": "Yukarıdan bir düzen seçin", + "selectLayoutHint": "Kaç bölme gösterileceğini seçin", + "panesTitle": "Bölmeler", "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", + "dragTabsHint": "Sekmeleri yukarıdaki bölmelere sürükleyin veya Hızlı Ata'yı kullanın", + "dropHere": "Buraya bırak", "emptyPane": "Boş", - "dashboard": "Kontrol Paneli", - "clearSplitScreen": "Ekranı Bölünmüş Hale Getir", + "dashboard": "Gösterge Paneli", + "clearSplitScreen": "Bölünmüş Ekranı Temizle", "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", + "splitTab": "Sekmeyi Böl", + "addToSplit": "Bölmeye Ekle", + "removeFromSplit": "Bölmeden Kaldır", + "assignToPane": "Bölmeye Ata", "hotkeysTitle": "Klavye Kısayolları", - "hotkeysSplitRight": "İki yönlü bölmeyi aç/kapat", - "hotkeysSplitBelow": "3 yollu bölmeyi aç/kapat", - "hotkeysNavigatePane": "Paneller arasında gezinme", + "hotkeysSplitRight": "2-yönlü bölmeyi aç/kapat", + "hotkeysSplitBelow": "3-yönlü bölmeyi aç/kapat", + "hotkeysNavigatePane": "Bölmeler arasında gezin", "hotkeysNextTab": "Sonraki sekme", "hotkeysPrevTab": "Önceki sekme" }, "snippets": { - "title": "Kısa bölümler", - "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", + "title": "Parçacıklar", + "createSnippetTitle": "Parçacık Oluştur", + "createSnippetDescription": "Hızlı çalıştırma için yeni bir komut parçacığı oluşturun", + "nameLabel": "Ad", + "namePlaceholder": "ör. Nginx'i Yeniden Başlat", + "descriptionLabel": "Açıklama", "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", + "folderLabel": "Klasör", + "noFolder": "Klasör yok (Kategorisiz)", + "commandLabel": "Komut", + "commandPlaceholder": "ör. sudo systemctl restart nginx", + "cancel": "İptal", + "createSnippetButton": "Parçacık Oluştur", "createFolderTitle": "Klasör Oluştur", - "createFolderDescription": "Kod parçacıklarınızı klasörler halinde düzenleyin.", + "createFolderDescription": "Parçacıklarınızı klasörlerde düzenleyin", "folderNameLabel": "Klasör Adı", - "folderNamePlaceholder": "Örneğin, Sistem Komutları, Docker Komut Dosyaları", + "folderNamePlaceholder": "ör. 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", + "selectAll": "Tümü", "selectNone": "Hiçbiri", - "noTerminalTabsOpen": "Açık terminal sekmesi yok.", - "searchPlaceholder": "Arama sonuçları...", - "newSnippet": "Yeni Parça", + "noTerminalTabsOpen": "Açık terminal sekmesi yok", + "searchPlaceholder": "Parçacıkları ara...", + "newSnippet": "Yeni Parçacık", "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.", + "run": "Çalıştır", + "noSnippetsInFolder": "Bu klasörde parçacık yok", + "uncategorized": "Kategorisiz", + "editSnippetTitle": "Parçacığı Düzenle", + "editSnippetDescription": "Bu komut parçacığını güncelle", "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ı.", + "createSuccess": "Parçacık başarıyla oluşturuldu", + "createFailed": "Parçacık oluşturulamadı", + "updateSuccess": "Parçacık başarıyla güncellendi", + "updateFailed": "Parçacık güncellenemedi", + "deleteFailed": "Parçacık silinemedi", + "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.", + "editFolderDescription": "Bu klasörü yeniden adlandırın veya görünümünü değiştirin", "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ı", + "folderDeleteSuccess": "\"{{name}}\" klasörü silindi", + "folderDeleteFailed": "Klasör silinemedi", + "folderEditSuccess": "Klasör başarıyla güncellendi", + "folderEditFailed": "Klasör güncellenemedi", + "confirmRunMessage": "\"{{name}}\" çalıştırılsın mı?", + "confirmRunButton": "Çalıştır", + "runSuccess": "\"{{name}}\" {{count}} terminalde çalıştırıldı", "copySuccess": "\"{{name}}\" panoya kopyalandı", - "shareTitle": "Paylaşılan Parça", + "shareTitle": "Parçacığı Paylaş", "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.", + "selectUser": "Kullanıcı seç...", + "selectRole": "Rol seç...", + "shareSuccess": "Kod parçacığı başarıyla paylaşıldı", + "shareFailed": "Kod parçacığı paylaşılamadı", "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.", + "revokeFailed": "Erişim iptal edilemedi", + "currentAccess": "Geçerli Erişim", + "shareLoadError": "Paylaşım verisi yüklenemedi", "loading": "Yükleniyor...", - "close": "Kapalı", - "reorderFailed": "Kod parçacığının sırasını kaydetme başarısız oldu.", - "importExport": "İthalat / İhracat", - "exportBtn": "JSON'u Dışa Aktar", - "importBtn": "JSON'u içe aktar", - "exportSuccess": "Kod parçacıkları başarıyla dışa aktarıldı.", - "exportFailed": "Kod parçacıklarını dışa aktarma başarısız oldu.", - "importTitle": "Kod Parçalarını İçe Aktar", - "importDescription": "Termix tarafından dışa aktarılan bir JSON dosyasından kod parçacıklarını ve klasörleri içe aktarın.", - "importDropOrClick": "Buraya bir JSON dosyası sürükleyip bırakın veya göz atmak için tıklayın.", - "importSelectedFile": "Seçilen: {{name}}", - "importOverwrite": "Aynı ada ve klasöre sahip mevcut kod parçacıklarının üzerine yazın.", - "importStartBtn": "İçe aktarmak", - "importSuccess": "İçe aktarma tamamlandı: {{snippets}} kod parçası eklendi, {{updated}} güncellendi, {{skipped}} atlandı, {{folders}} klasör eklendi", - "importFailed": "Kod parçacıklarını içe aktarma başarısız oldu.", - "importInvalidFile": "Geçersiz dosya: kod parçacıkları veya klasör dizileri içeren bir JSON nesnesi bekleniyor.", + "close": "Kapat", + "reorderFailed": "Kod parçacığı sıralaması kaydedilemedi", + "importExport": "İçe / Dışa Aktar", + "exportBtn": "JSON Dışa Aktar", + "importBtn": "JSON İçe Aktar", + "exportSuccess": "Kod parçacıkları başarıyla dışa aktarıldı", + "exportFailed": "Kod parçacıkları dışa aktarılamadı", + "importTitle": "Kod Parçacıklarını İçe Aktar", + "importDescription": "Termix tarafından dışa aktarılan bir JSON dosyasından kod parçacıklarını ve klasörleri içe aktar", + "importDropOrClick": "Bir JSON dosyasını buraya sürükleyin veya göz atmak için tıklayın", + "importSelectedFile": "Seçili: {{name}}", + "importOverwrite": "Aynı ada ve klasöre sahip mevcut kod parçacıklarının üzerine yaz", + "importStartBtn": "İçe Aktar", + "importSuccess": "İçe aktarma tamamlandı: {{snippets}} kod parçacığı eklendi, {{updated}} güncellendi, {{skipped}} atlandı, {{folders}} klasör eklendi", + "importFailed": "Kod parçacıkları içe aktarılamadı", + "importInvalidFile": "Geçersiz dosya: snippets veya folders dizileri içeren bir JSON nesnesi bekleniyor", "targetHostsLabel": "Hedef Sunucular", - "targetHostsHint": "Terminal açmaya gerek kalmadan bu kod parçasını doğrudan çalıştırmak için sunucular atayın.", + "targetHostsHint": "Bu kod parçacığını açık bir terminal olmadan doğrudan çalıştırmak için sunucular atayın.", "noHostsAvailable": "Yapılandırılmış sunucu yok", "clearTargetHosts": "Tümünü temizle", "hasTargetHosts": "Hedef sunuculara sahip", - "runOnTargets": "Hedeflere göre hareket edin", - "directRunSuccess": "\"{{name}}\" \" komutunu {{count}} ana bilgisayarda çalıştırdı(lar).", + "runOnTargets": "Hedeflerde Çalıştır", + "directRunSuccess": "{{count}} sunucuda \"{{name}}\" çalıştırıldı", "directRunPartialFail": "\"{{name}}\" bir veya daha fazla sunucuda başarısız oldu", - "executionResultTitle": "Yürütme Sonuçları: {{name}}", - "executionResultDescription": "Kod parçacığının her hedef sunucuda çalıştırılmasının çıktısı.", - "executionSuccess": "Başarı", - "executionFailed": "Arızalı" + "executionResultTitle": "Çalıştırma Sonuçları: {{name}}", + "executionResultDescription": "Her hedef sunucuda parçacığı çalıştırmanın çıktısı.", + "executionSuccess": "Başarılı", + "executionFailed": "Başarısız" + }, + "keybindings": { + "title": "Klavye Kısayolları", + "description": "Terminaldeki kopyalama, yapıştırma ve kontrol kısayollarını özelleştirin veya metin göndermek ya da kod parçacığı çalıştırmak için tuş atamaları yapın.", + "defaultsHeading": "Dahili Kısayollar", + "customHeading": "Özel Kısayollar", + "addBinding": "Kısayol Ekle", + "addBindingTitle": "Kısayol Ekle", + "editBindingTitle": "Kısayolu Düzenle", + "loading": "Yükleniyor...", + "noCustomBindings": "Henüz özel kısayollar yok.", + "defaultBadge": "Varsayılan", + "customizedBadge": "Özelleştirilmiş", + "customize": "Özelleştirmek", + "resetToDefault": "Varsayılan ayarlara sıfırla", + "resetAllToDefaults": "Tümünü varsayılan ayarlara sıfırla", + "close": "Kapalı", + "cancel": "İptal etmek", + "saveBinding": "Kısayolu Kaydet", + "saveError": "Klavye kısayollarını kaydetme başarısız oldu.", + "pressKeysToRecord": "Tuş kombinasyonu", + "pressKeysPlaceholder": "Tıklayın ve tuşlara basın", + "recording": "Tuşlara basın...", + "comboRequiredError": "Önce bir tuş kombinasyonuna basın.", + "textRequiredError": "Göndermek istediğiniz metni girin.", + "controlCodeRequiredError": "Kontrol kodu için tek bir harf girin.", + "snippetRequiredError": "Bir parça seçin", + "conflictWarning": "Bu kombinasyon zaten {{combo}}ile ilişkilendirilmiş durumda. Kaydetme işlemi her ikisinin de çalışmasını sağlayacaktır.", + "actionLabel": "Aksiyon", + "actionCopy": "Seçimi kopyala", + "actionPaste": "Yapıştır", + "actionSendControlCode": "Ctrl+harf sinyali gönder", + "actionSendText": "Mesaj gönder", + "actionRunSnippet": "Mevcut kod parçasını çalıştır", + "controlCodeLabel": "Mektup", + "textLabel": "Gönderilecek metin", + "appendEnterLabel": "Gönderdikten sonra Enter tuşuna basın.", + "snippetLabel": "Alıntı", + "selectSnippetPlaceholder": "Bir parça seçin", + "orphanedSnippetWarning": "Kod parçası bulunamadı", + "clipboardPermissionNote": "Bazı tarayıcılarda ilk kullanımda panoya kopyalama izni isteyebilir." }, "userProfile": { - "donateTitle": "Termix'i hayatta tutun", - "donateDescription": "Termix ücretsizdir, kendi sunucunuzda barındırır ve sadece birkaç kişi tarafından boş zamanlarında geliştirilmiştir. Eğer size zaman veya para kazandırdıysa, kripto para bağışı projenin devam etmesine yardımcı olur.", - "donateMilestones": "Bağışlar, SAML, Kubernetes ve Agent desteği oluşturmak için gerekenleri araştırmak ve öğrenmek için harcanan zamana katkı sağlıyor. Gelişmeleri takip edin ve bağış yapın.", - "donateButton": "Kripto para bağışı yapın", + "donateTitle": "Termix'i canlı tutun", + "donateDescription": "Termix ücretsiz, kendi sunucunuzda çalıştırılabilir ve boş zamanlarında yalnızca birkaç kişi tarafından geliştiriliyor. Size zaman veya para kazandırdıysa, kripto bağışlarıyla devamlılığını destekleyin.", + "donateMilestones": "Bağışlar, SAML, Kubernetes ve Ajan desteği oluşturmak için gereken araştırma ve öğrenme süresini finanse etmeye yardımcı olur. İlerlemeyi görün ve bağış yapın.", + "donateButton": "Kripto bağış yap", "storageModeLocal": "Tarayıcı", "storageModeCloud": "Veritabanı", - "storageModeDescription": "Tarayıcı ayarları yalnızca bu tarayıcıda saklar. Veritabanı sunucuyla senkronize olur ve herhangi bir cihazda yüklenir.", - "resetToDefaults": "Varsayılan ayarlara sıfırla", - "resetToDefaultsSuccess": "Ayarlar varsayılan değerlere sıfırlandı.", + "storageModeDescription": "Tarayıcı, ayarları yalnızca bu tarayıcıda saklar. Veritabanı sunucuyla eşitlenir ve her cihazda yüklenir.", + "resetToDefaults": "Varsayılanlara Sıfırla", + "resetToDefaultsSuccess": "Ayarlar varsayılanlara sıfırlandı.", "storageModeSwitch": "Tercih Depolama", "sectionAccount": "Hesap", - "sectionAppearance": "Dış görünüş", + "sectionAppearance": "Görünüm", "sectionSecurity": "Güvenlik", "sectionApiKeys": "API Anahtarları", + "sectionData": "Veri", "sectionC2sTunnels": "C2S Tünelleri", - "usernameLabel": "Kullanıcı adı", + "usernameLabel": "Kullanıcı Adı", "roleLabel": "Rol", "roleAdministrator": "Yönetici", "authMethodLabel": "Kimlik Doğrulama Yöntemi", @@ -3145,328 +3338,348 @@ "twoFaOff": "Kapalı", "versionLabel": "Sürüm", "betaProgramTitle": "Beta Programı", - "betaProgramDescription": "Haftalık olarak güncellenen :beta Docker etiketiyle yeni özellikleri erkenden deneyin. Kararsızdır, üretim ortamı için uygun değildir.", - "betaProgramFeedback": "Bir hata mı buldunuz? Buradan bildirin.", - "betaProgramLearnMore": "Daha fazla bilgi edin", + "betaProgramDescription": "Haftalık güncellenen :beta Docker etiketiyle yeni özellikleri erkenden deneyin. Kararsız, üretim ortamı için uygun değil.", + "betaProgramFeedback": "Hata mı buldunuz? Buradan bildirin.", + "betaProgramLearnMore": "Daha Fazla Bilgi", "deleteAccount": "Hesabı Sil", - "deleteAccountDescription": "Hesabınızı kalıcı olarak silin.", - "changeServerDescription": "Farklı bir Termix arka uç sunucusuna geçin.", - "deleteButton": "Silmek", + "deleteAccountDescription": "Hesabınızı kalıcı olarak silin", + "changeServerDescription": "Farklı bir Termix arka uç sunucusuna geçin", + "deleteButton": "Sil", "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.", + "deleteAccountWarning": "Tüm oturumlar, sunucular, kimlik bilgileri ve ayarlar kalıcı olarak silinecek.", + "confirmPasswordDeletePlaceholder": "Onaylamak için parolanızı girin", "languageLabel": "Dil", "themeLabel": "Tema", "fontSizeLabel": "Yazı Tipi Boyutu", "accentColorLabel": "Vurgu Rengi", - "settingsTerminal": "terminal", + "settingsTerminal": "Terminal", "commandAutocomplete": "Komut Otomatik Tamamlama", - "commandAutocompleteDesc": "Yazarken otomatik tamamlama özelliğini göster", - "terminalLinkBehavior": "Terminal Bağlantısına Tıklayın", + "commandAutocompleteDesc": "Yazarken otomatik tamamlama göster", + "keyboardShortcuts": "Klavye Kısayolları", + "keyboardShortcutsDescription": "Terminaldeki kopyalama, yapıştırma ve komut kısayollarını özelleştirin.", + "manageShortcuts": "Üstesinden gelmek", + "terminalLinkBehavior": "Terminal Bağlantı Tıklama", "terminalLinkBehaviorDesc": "Terminalde bağlantılara tıklandığında varsayılan davranış", - "historyTracking": "Geçmiş Takibi", + "historyTracking": "Geçmiş İzleme", "historyTrackingDesc": "Terminal komutlarını izle", "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.", + "reopenTabsOnLogin": "Oturum Açıldığında Sekmeleri Yeniden Aç", + "reopenTabsOnLoginDesc": "Oturum açtığınızda veya sayfayı yenilediğinizde, başka bir cihazdan bile açık sekmeleri geri yükle", + "confirmTabClose": "Sekme Kapatmayı Onayla", + "confirmTabCloseDesc": "Terminal sekmelerini kapatmadan önce sor", "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.", - "compactHostView": "Kompakt Ana Bilgisayar Görünümü", - "compactHostViewDesc": "Her sunucuyu yalnızca adını ve adresini gösteren tek bir satıra daraltın.", + "showHostTagsDesc": "Etiketleri sunucu listesinde göster", + "hostTrayOnClick": "Sunucu Eylemlerini Genişletmek için Tıkla", + "hostTrayOnClickDesc": "Her zaman bağlantı düğmelerini göster; üzerine gelmek yerine, yönetim seçeneklerini genişletmek için tıkla", + "compactHostView": "Sıkıştırılmış Sunucu Görünümü", + "compactHostViewDesc": "Her sunucuyu yalnızca adını ve adresini gösteren tek satıra daralt", "statusColors": "Gerçek Durum Renkleri", - "statusColorsDesc": "Çevrimiçi/çevrimdışı durumunu belirtmek için vurgu rengi yerine yeşil/kırmızı kullanı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.", - "expandAppRailOnHover": "Üzerine gelindiğinde uygulama çubuğunu genişletin.", - "expandAppRailOnHoverDesc": "Fare imleci üzerine geldiğinde sol kenar çubuğundaki uygulama çubuğunun genişlemesine izin verin.", - "settingsNavigation": "Navigasyon", - "navigationTabsDesc": "Uygulama çubuğunda hangi sekmelerin görüneceğini seçin.", - "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.", + "statusColorsDesc": "Vurgu rengi yerine çevrimiçi/çevrimdışı durum için yeşil/kırmızı kullan", + "pinAppRail": "Uygulama Rayını Sabitle", + "pinAppRailDesc": "Farenin üzerine gelindiğinde genişletmek yerine sol kenar çubuğu uygulama rayını her zaman geniş tut", + "expandAppRailOnHover": "Fareyle Üzerine Gelince Uygulama Rayını Genişlet", + "expandAppRailOnHoverDesc": "İmleç üzerine geldiğinde sol kenar çubuğu uygulama rayının genişlemesine izin ver", + "settingsNavigation": "Gezinti", + "navigationTabsDesc": "Uygulama rayında hangi sekmelerin görüneceğini seçin", + "settingsSnippets": "Parçacıklar", + "foldersCollapsed": "Klasörler Kapalı", + "foldersCollapsedDesc": "Klasörler varsayılan olarak kapalı olsun", + "confirmExecution": "Çalıştırmayı Onayla", + "confirmExecutionDesc": "Parçacıkları ç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.", + "disableUpdateChecks": "Güncelleme Denetimlerini Devre Dışı Bırak", + "disableUpdateChecksDesc": "Güncellemeleri denetlemeyi durdurun", "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", + "totpEnabled": "2FA etkin", + "totpDisabled": "Ek giriş güvenliği ekleyin", + "disable": "Devre Dışı Bırak", + "enable": "Etkinleştir", + "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.", + "totpInstructions": "QR kodunu tarayın veya kimlik doğrulama uygulamanıza gizli anahtarı girin, ardından 6 haneli kodu girin", "totpCodePlaceholder": "000000", - "verify": "Doğrulamak", - "changePassword": "Şifre değiştir", + "verify": "Doğrula", + "changePassword": "Şifre Değiştir", "currentPasswordLabel": "Mevcut Şifre", - "currentPasswordPlaceholder": "Mevcut Şifre", + "currentPasswordPlaceholder": "Mevcut şifre", "newPasswordLabel": "Yeni Şifre", - "newPasswordPlaceholder": "Yeni Şifre", + "newPasswordPlaceholder": "Yeni şifre", "confirmPasswordLabel": "Yeni Şifreyi Onayla", - "confirmPasswordPlaceholder": "Yeni şifreyi onaylayın", + "confirmPasswordPlaceholder": "Yeni şifreyi onayla", "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", + "apiKeyNameLabel": "Ad", + "apiKeyNamePlaceholder": "örn. CI Pipeline", + "expiryDateLabel": "Son Kullanma Tarihi", "optional": "isteğe bağlı", - "cancel": "İptal etmek", + "cancel": "İptal", "createKey": "Anahtar Oluştur", - "apiKeyCount": "{{count}} anahtarlar", + "apiKeyCount": "{{count}} anahtar", "newKey": "Yeni Anahtar", - "noApiKeys": "Henüz API anahtarları yok.", - "apiKeyActive": "Aktif", - "apiKeyUsageHint": "Anahtarınızı içine koyun.", - "apiKeyUsageHintHeader": "başlık.", + "noApiKeys": "Henüz API anahtarı yok.", + "apiKeyActive": "Etkin", + "apiKeyUsageHint": "Anahtarınızı", + "apiKeyUsageHintHeader": "başlığa ekleyin.", "apiKeyPermissionsHint": "Anahtarlar, oluşturan kullanıcının izinlerini devralır.", + "exportData": "Verilerimi Dışa Aktar", + "exportDataDesc": "Sunucularınızın, kimlik bilgilerinizin ve ayarlarınızın yedeğini indirerek başka bir cihaza aktarın.", + "export": "İhracat", + "exporting": "İhracat...", + "importData": "Verilerimi İçe Aktar", + "importDataDesc": "Sunucularınızı, kimlik bilgilerinizi ve ayarlarınızı bir .sqlite yedekleme dosyasından geri yükleyin.", + "importDataSelected": "Seçilen: {{name}}", + "selectFile": "Dosya Seç", + "changeFile": "Değiştirmek", + "import": "İçe aktarmak", + "importing": "İçe aktarılıyor...", + "exportSuccess": "Veriler başarıyla dışa aktarıldı.", + "exportFailed": "Veri 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}}", "roleUser": "Kullanıcı", "authMethodDual": "Çift Kimlik Doğrulama", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP kurulumu başlatılamadı.", + "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.", + "totpInvalidCode": "Geçersiz kod, lütfen tekrar deneyin", + "totpDisableInputRequired": "TOTP kodunuzu veya parolanızı 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", + "totpDisableFailed": "2FA devre dışı bırakılamadı", + "totpDisableTitle": "2FA'yı Devre Dışı Bırak", + "totpDisablePlaceholder": "TOTP kodu veya parola girin", + "totpDisableConfirm": "2FA'yı Devre Dışı Bırak", + "totpContinueVerify": "Doğrulamaya Devam Et", "totpVerifyTitle": "Kodu Doğrula", - "totpBackupTitle": "Yedekleme Kodları", - "totpDownloadBackup": "Yedekleme Kodlarını İndir", - "passkeys": "Parolalar", - "passkeysDesc": "WebAuthn/FIDO2 kimlik bilgilerini kullanarak parola kullanmadan oturum açabilirsiniz.", - "passkeyName": "Parola adı", + "totpBackupTitle": "Yedek Kodlar", + "totpDownloadBackup": "Yedek Kodları İndir", + "passkeys": "Geçiş Anahtarları", + "passkeysDesc": "Parola olmadan oturum açmak için WebAuthn/FIDO2 kimlik bilgilerini kullanın", + "passkeyName": "Geçiş anahtarı adı", "passkeyUvPreferred": "Tercih edilen", "passkeyUvRequired": "Gerekli", - "passkeyUvDiscouraged": "Cesareti kırılmış", - "addPasskey": "Parola Ekle", - "noPasskeys": "Kayıtlı parola yok.", - "passkeyAdded": "Parola eklendi.", - "passkeyAddFailed": "Parola ekleme başarısız oldu.", - "passkeyDeleted": "Parola silindi", - "passkeyDeleteFailed": "Parola silme işlemi başarısız oldu.", - "done": "Tamamlamak", - "secretCopied": "Gizli bilgi panoya kopyalandı", - "apiKeyNameRequired": "Anahtar adı gereklidir.", + "passkeyUvDiscouraged": "Önerilmeyen", + "addPasskey": "Geçiş Anahtarı Ekle", + "noPasskeys": "Kayıtlı geçiş anahtarı yok.", + "passkeyAdded": "Geçiş anahtarı eklendi", + "passkeyAddFailed": "Geçiş anahtarı eklenemedi", + "passkeyDeleted": "Geçiş anahtarı silindi", + "passkeyDeleteFailed": "Geçiş anahtarı silinemedi", + "done": "Tamam", + "secretCopied": "Gizli anahtar panoya kopyalandı", + "apiKeyNameRequired": "Anahtar adı zorunludur", "apiKeyCreated": "API anahtarı \"{{name}}\" oluşturuldu", - "apiKeyCreateFailed": "API anahtarı oluşturulamadı.", + "apiKeyCreateFailed": "API anahtarı oluşturulamadı", "apiKeyUser": "Kullanıcı", - "apiKeyExpires": "Süresi doluyor", + "apiKeyExpires": "Bitiş", "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.", + "apiKeyRevokeFailed": "API anahtarı iptal edilemedi", + "passwordFieldsRequired": "Mevcut ve yeni parolalar zorunludur", + "passwordMismatch": "Parolalar eşleşmiyor", + "passwordTooShort": "Parola en az 6 karakter olmalıdır", + "passwordUpdated": "Parola başarıyla güncellendi", + "passwordUpdateFailed": "Parola güncellenemedi", + "deletePasswordRequired": "Hesabınızı silmek için parola zorunludur", + "deleteFailed": "Hesap silinemedi", "deleting": "Siliniyor...", - "colorPickerTooltip": "Renk seçiciyi açın", + "colorPickerTooltip": "Renk seçiciyi aç", "themeSystem": "Sistem", - "themeLight": "Işık", - "themeDark": "Karanlık", - "themeDracula": "Drakula", + "themeLight": "Açık", + "themeDark": "Koyu", + "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 Monitörü", - "failedToLoadHosts": "Sunucular yüklenemedi.", - "failedToLoad": "Tmux oturumları yüklenemedi.", - "tmuxUnavailable": "Bu sunucuda tmux yüklü değil.", - "noSessions": "Bu sunucuda tmux oturumu yok.", - "noHostSelected": "Sunucu seçilmedi.", - "attached": "Ekli", - "detached": "Müstakil", - "attach": "Eklemek", + "title": "Tmux İzleyici", + "failedToLoadHosts": "Sunucular yüklenemedi", + "failedToLoad": "Tmux oturumları yüklenemedi", + "tmuxUnavailable": "Bu sunucuda tmux yüklü değil", + "noSessions": "Bu sunucuda tmux oturumu yok", + "noHostSelected": "Sunucu seçilmedi", + "attached": "Bağlı", + "detached": "Bağlantısız", + "attach": "Bağlan", "editTags": "Etiketleri düzenle", - "tagsHint": "Virgülle ayrılmış etiketler (örneğin YOLO, laboratuvar, eğitim)", + "tagsHint": "Virgülle ayrılmış etiketler (örn. YOLO, lab, training)", "tagsSaved": "Etiketler kaydedildi", - "tagsSaveFailed": "Etiketler kaydedilemedi.", - "searchPlaceholder": "Oturumlar arası arama çıktısı...", - "searchResults": "{{count}} eşleşmeler", - "searchFailed": "Arama başarısız oldu", - "selectPaneHint": "Çıktısını önizlemek için bir bölme seçin.", + "tagsSaveFailed": "Etiketler kaydedilemedi", + "searchPlaceholder": "Oturumlar arasında çıktı ara...", + "searchResults": "{{count}} eşleşme", + "searchFailed": "Arama başarısız", + "selectPaneHint": "Çıktısını önizlemek için bir bölme seçin", "closePreview": "Önizlemeyi kapat", "newSession": "Yeni oturum", "newSessionHint": "Oturum adı (harfler, rakamlar, _ @ % + = -)", - "newSessionPlaceholder": "benim oturumum", - "create": "Yaratmak", + "newSessionPlaceholder": "my-session", + "create": "Oluştur", "sessionCreated": "Oturum \"{{name}}\" oluşturuldu", - "sessionCreateFailed": "Oturum oluşturulamadı.", - "splitRight": "Sağa doğru bölünmüş", - "splitDown": "Aşağıya doğru bölündü", - "splitFailed": "Bölme işlemi başarısız oldu", + "sessionCreateFailed": "Oturum oluşturulamadı", + "splitRight": "Sağa böl", + "splitDown": "Aşağı böl", + "splitFailed": "Bölme başarısız", "newWindow": "Yeni pencere", - "windowCreateFailed": "Pencere oluşturulamadı.", - "attachSessionTooltip": "{{session}}'a ekle", + "windowCreateFailed": "Pencere oluşturulamadı", + "attachSessionTooltip": "{{session}} oturumuna bağlan", "refresh": "Yenile", - "refreshFailed": "Oturumları yenileme başarısız oldu.", + "refreshFailed": "Yenileme başarısız", "collapseAll": "Tümünü daralt", "expandAll": "Tümünü genişlet", - "moreActions": "Daha fazla işlem", + "moreActions": "Diğer işlemler", "sessionStats": "Oturum istatistikleri", "renameSessionTitle": "Oturumu yeniden adlandır \"{{name}}\"", - "rename": "Yeniden isimlendirmek", - "sessionRenamed": "Oturumun adı \"{{name}} \" olarak değiştirildi.", - "sessionRenameFailed": "Oturumu yeniden adlandırma başarısız oldu.", - "editTagsTitle": "\"{{name}} \" için etiketleri düzenle", - "killPane": "Öldürme paneli", - "resizeTree": "Boyutunu değiştirmek için sürükleyin — sıfırlamak için çift tıklayın", - "reattach": "Yeniden ekle (bozuk görüntüyü düzeltir)", - "killWindow": "Pencereyi öldür", - "killWindowTitle": "\"{{session}} \" penceresini kapat {{index}}?", - "killWindowBody": "Bu penceredeki her bölme ve işlem sonlandırılacaktır. Son pencereyi kapatmak oturumu sonlandırır.", - "windowKillFailed": "Pencereyi kapatma başarısız oldu.", - "statusActivity": "Aktivite", - "statusWindows": "Windows", - "statusPanes": "camlar", + "rename": "Yeniden adlandır", + "sessionRenamed": "Oturum \"{{name}}\" olarak yeniden adlandırıldı", + "sessionRenameFailed": "Oturum yeniden adlandırılamadı", + "editTagsTitle": "\"{{name}}\" için etiketleri düzenle", + "killPane": "Paneyi sonlandır", + "resizeTree": "Yeniden boyutlandırmak için sürükle — sıfırlamak için çift tıkla", + "reattach": "Yeniden bağlan (bozuk görünümü düzeltir)", + "killWindow": "Pencereyi sonlandır", + "killWindowTitle": "\"{{session}}\" oturumundaki {{index}} numaralı pencere sonlandırılsın mı?", + "killWindowBody": "Bu penceredeki tüm paneler ve işlemler sonlandırılacak. Son pencereyi sonlandırmak oturumu sona erdirir.", + "windowKillFailed": "Pencere sonlandırılamadı", + "statusActivity": "Etkinlik", + "statusWindows": "Pencereler", + "statusPanes": "Paneler", "statusTags": "Etiketler", - "killPaneTitle": "Paneli öldür {{id}}?", - "killPaneBody": "Bu bölmede çalışan işlem sonlandırılacaktır. Son bölmeyi kapatmak, o bölmenin penceresini de kapatır.", - "paneKillFailed": "Pencereyi kapatma başarısız oldu.", - "killSessionTitle": "Oturumu sonlandır \"{{name}}\"?", - "killSessionBody": "Bu oturumdaki tüm pencereler ve çalışan işlemler sonlandırılacaktır. Bu işlem geri alınamaz.", - "kill": "Öldürmek", - "sessionKilled": "Oturum \"{{name}}\" öldürüldü", - "sessionKillFailed": "Oturumu sonlandırma başarısız oldu.", - "hostUnreachable": "Sunucuya bağlanılamadı. Lütfen çevrimiçi ve erişilebilir olduğundan emin olun.", - "noServer": "Bu sunucuda çalışan bir tmux sunucusu yok.", - "searchTruncated": "Kısmi sonuçlar — arama, her bölmenin son {{lines}} satırını ve en fazla {{panes}} bölmeyi kapsar.", + "killPaneTitle": "{{id}} numaralı pane sonlandırılsın mı?", + "killPaneBody": "Bu panede çalışan işlem sonlandırılacak. Son paneyi sonlandırmak penceresini kapatır.", + "paneKillFailed": "Pane sonlandırılamadı", + "killSessionTitle": "\"{{name}}\" oturumu sonlandırılsın mı?", + "killSessionBody": "Bu oturumdaki tüm pencereler ve çalışan işlemler sonlandırılacak. Bu işlem geri alınamaz.", + "kill": "Sonlandır", + "sessionKilled": "\"{{name}}\" oturumu sonlandırıldı", + "sessionKillFailed": "Oturum sonlandırılamadı", + "hostUnreachable": "Ana bilgisayara bağlanılamadı. Çevrimiçi ve erişilebilir olduğunu kontrol edin.", + "noServer": "Bu ana bilgisayarda tmux sunucusu çalışmıyor.", + "searchTruncated": "Kısmi sonuçlar — arama her panenin son {{lines}} satırını ve en fazla {{panes}} paneyi kapsar.", "closeSearchResults": "Arama sonuçlarını kapat", - "retry": "Tekrar dene", - "noHosts": "Kullanılabilir SSH sunucusu yok.", - "noHostsHint": "SSH sunucusunda (Sunucu Yöneticisi → Terminal sekmesi) Tmux oturumlarını izlemek için Tmux İzleme seçeneğini etkinleştirin.", + "retry": "Yeniden dene", + "noHosts": "Kullanılabilir SSH ana bilgisayarı yok", + "noHostsHint": "Tmux oturumlarını izlemek için bir SSH ana bilgisayarında Tmux Monitor seçeneğini etkinleştirin (Ana Bilgisayar Yöneticisi → Terminal sekmesi).", "tmuxInstallHint": "Ana bilgisayara şu komutla yükleyin:", - "attachTooltip": "{{host}} için bir terminal açın.", - "attachTooltipPane": "{{host}} adresine bir terminal açın — tmux oturumu {{session}}", - "timeJustNow": "Şu anda", - "timeMinutes": "{{count}}m önce", - "timeHours": "{{count}}saat önce", - "timeDays": "{{count}}gün önce" + "attachTooltip": "{{host}} için bir terminal aç", + "attachTooltipPane": "{{host}} için bir terminal aç — tmux oturumu {{session}}", + "timeJustNow": "az önce", + "timeMinutes": "{{count}} dakika önce", + "timeHours": "{{count}} saat önce", + "timeDays": "{{count}} gün önce" }, "mobileKeyboard": { - "shift": "Vardiya", + "shift": "Shift", "ctrl": "Ctrl", "esc": "Esc", - "tab": "Sekme", + "tab": "Tab", "backTab": "⇥", "arrowUp": "Yukarı Ok", "arrowDown": "Aşağı Ok", - "arrowLeft": "Ok Sol", + "arrowLeft": "Sol Ok", "arrowRight": "Sağ Ok", - "home": "Ev", - "end": "Son", - "pageUp": "Sayfa Yukarı", + "home": "Home", + "end": "End", + "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Yapıştır", "editQuickKeys": "Hızlı tuşları düzenle", "quickKeysTitle": "Hızlı Tuşlar", - "quickKeysDesc": "Silmek için × simgesine dokunun. En fazla 8 karakter desteklenir.", - "quickKeyPlaceholder": "örneğin sudo ", - "addQuickKey": "Eklemek", - "removeQuickKey": "Kaldırmak", - "resetDefaults": "Varsayılan ayarlara sıfırla", - "done": "Tamamlamak" + "quickKeysDesc": "Kaldırmak için × işaretine dokunun. En fazla 8 karakter desteklenir.", + "quickKeyPlaceholder": "ör. sudo ", + "addQuickKey": "Ekle", + "removeQuickKey": "Kaldır", + "resetDefaults": "Varsayılanlara sıfırla", + "done": "Tamam" }, "serial": { "title": "Seri", - "portLabel": "Liman", + "portLabel": "Port", "portPlaceholder": "/dev/ttyUSB0 veya COM3", "baudRateLabel": "Baud Hızı", "dataBitsLabel": "Veri", - "stopBitsLabel": "Durmak", - "parityLabel": "Eşitlik", - "parityNone": "Hiçbiri", - "parityEven": "Eşit", - "parityOdd": "Garip", - "connect": "Seri porta bağlan", - "disconnect": "Bağlantıyı kes", - "refreshPorts": "Yenileme portları", - "connected": "{{path}} 'ya {{baud}} baud hızında bağlandı.", - "disconnected": "Seri port bağlantısı kesildi", - "connectionError": "Seri port açılamadı.", + "stopBitsLabel": "Durdurma", + "parityLabel": "Eşlik", + "parityNone": "Yok", + "parityEven": "Çift", + "parityOdd": "Tek", + "connect": "Seri Bağlantıya Bağlan", + "disconnect": "Bağlantıyı Kes", + "refreshPorts": "Portları yenile", + "connected": "{{path}} ile {{baud}} baud hızında bağlandı.", + "disconnected": "Seri port bağlantısı kesildi.", + "connectionError": "Seri bağlantı noktası açılamadı", "wsError": "WebSocket hatası", - "errorNoServerUrl": "Sunucu URL'si yapılandırılmamış.", - "notSupportedTitle": "Seri numarası desteklenmiyor.", - "notSupported": "Seri bağlantılar için Web Serial API desteği olan bir tarayıcı (Chrome, Edge veya Firefox 151+) veya Termix masaüstü uygulaması gereklidir.", - "hideHint": "Seri numarası sekmesini Kullanıcı Profili > Görünüm > Kenar Çubuğu > Gezinme menüsünden gizleyebilirsiniz.", - "browserPickerHint": "Bağlan'a tıklayın ve tarayıcınız, cihazı seçebileceğiniz bir bağlantı noktası seçici açacaktır." + "errorNoServerUrl": "Sunucu URL'si yapılandırılmamış", + "notSupportedTitle": "Seri Bağlantı Desteklenmiyor", + "notSupported": "Seri bağlantılar, Web Serial API desteği olan bir tarayıcı (Chrome, Edge veya Firefox 151+) ya da Termix masaüstü uygulaması gerektirir.", + "hideHint": "Seri sekmesini Kullanıcı Profili > Görünüm > Kenar Çubuğu > Gezinme altından gizleyebilirsiniz.", + "browserPickerHint": "Bağlan'a tıklayın, tarayıcınız cihazı seçmek için bir bağlantı noktası seçici açacaktır." }, "metricsHistory": { - "title": "Ölçüm Geçmişi", - "historySuffix": "Tarih", - "cpuMemoryDisk": "İşlemci / Bellek / Disk", + "title": "Metrik Geçmişi", + "historySuffix": "Geçmiş", + "cpuMemoryDisk": "CPU / Bellek / Disk", "network": "Ağ", "download": "RX", "upload": "TX", - "noData": "Bu zaman aralığı için geçmişe ait veri bulunmamaktadır.", - "custom": "Gelenek", + "noData": "Bu zaman aralığı için geçmiş verisi yok.", + "custom": "Özel", "to": "ile", "apply": "Uygula", "viewHistory": "Geçmişi Görüntüle" }, "alerts": { "tabFirings": "Uyarılar", - "tabRules": "Tüzük", + "tabRules": "Kurallar", "tabChannels": "Kanallar", "noFirings": "Onaylanmamış uyarı yok", - "noRules": "Hiçbir uyarı kuralı yapılandırılmadı.", - "noChannels": "Hiçbir bildirim kanalı yapılandırılmamış.", - "rulesDesc": "Uyarı kuralları bildirimleri tetikler.", + "noRules": "Uyarı kuralı yapılandırılmamış", + "noChannels": "Bildirim kanalı yapılandırılmamış", + "rulesDesc": "Uyarı kuralları bildirimleri tetikler", "channelsDesc": "Uyarı bildirimlerinin gönderildiği yer", - "acknowledge": "Kabullenmek", + "acknowledge": "Onayla", "ackAll": "Tümünü Onayla", - "allAcknowledged": "Tüm uyarılar onaylandı.", - "ackFailed": "Uyarıyı onaylamada başarısız olundu.", - "ackAllFailed": "Tüm uyarıları onaylamada başarısız olundu.", + "allAcknowledged": "Tüm uyarılar onaylandı", + "ackFailed": "Uyarı onaylanamadı", + "ackAllFailed": "Tüm uyarılar onaylanamadı", "showAcknowledged": "Tümünü Göster", - "hideAcknowledged": "Onaylanmış olanı gizle", + "hideAcknowledged": "Onaylananları Gizle", "addChannel": "Kanal Ekle", "editChannel": "Kanalı Düzenle", - "channelName": "İsim", - "channelType": "Tip", - "channelNameRequired": "İsim zorunludur.", + "channelName": "Ad", + "channelType": "Tür", + "channelNameRequired": "Ad zorunludur", "webhookUrl": "URL", - "webhookUrlRequired": "Webhook URL'si gereklidir.", - "webhookDesc": "Her uyarı tetiklendiğinde bu URL'ye JSON verisi gönderin.", + "webhookUrlRequired": "Webhook URL zorunludur", + "webhookDesc": "Her uyarı tetiklendiğinde bu URL'ye JSON verisi POST edilir", "ntfyServer": "Sunucu URL'si", - "ntfyTopic": "Başlık", - "ntfyTopicRequired": "Konu zorunludur.", - "ntfyToken": "Erişim Belirteci (isteğe bağlı)", - "channelSaveFailed": "Kanal kaydedilemedi.", - "test": "Test", + "ntfyTopic": "Konu", + "ntfyTopicRequired": "Konu zorunludur", + "ntfyToken": "Erişim Anahtarı (isteğe bağlı)", + "channelSaveFailed": "Kanal kaydedilemedi", + "test": "Test Et", "testSent": "Test bildirimi gönderildi", - "testFailed": "Test bildirimi başarısız oldu.", + "testFailed": "Test bildirimi başarısız oldu", "addRule": "Uyarı Kuralı Ekle", "editRule": "Uyarı Kuralını Düzenle", "ruleName": "Kural Adı", - "ruleNameRequired": "İsim zorunludur.", - "triggerType": "Tetiklemek", + "ruleNameRequired": "Ad zorunludur", + "triggerType": "Tetikleyici", "thresholdValue": "Eşik (%)", - "durationSeconds": "Süre (saniye, 0 = hemen ateşle)", - "cooldownMinutes": "Soğuma süresi (dakika)", + "durationSeconds": "Süre (saniye, 0 = hemen tetikle)", + "cooldownMinutes": "Bekleme süresi (dakika)", "channels": "Bildirim Kanalları", - "noChannelsHint": "Öncelikle Kanallar sekmesinden kanal ekleyin.", - "ruleSaveFailed": "Kural kaydedilemedi." + "noChannelsHint": "Önce Kanallar sekmesinde kanal ekleyin", + "ruleSaveFailed": "Kural kaydedilemedi" } } diff --git a/src/ui/locales/translated/uk_UA.json b/src/ui/locales/translated/uk_UA.json index c5d5bc08..6aa86dcc 100644 --- a/src/ui/locales/translated/uk_UA.json +++ b/src/ui/locales/translated/uk_UA.json @@ -389,6 +389,45 @@ "noSavedServers": "Немає збережених серверів", "removeServer": "Видалити" }, + "migrationNotice": { + "title": "Termix Desktop тепер працює автономно", + "body1": "Цей додаток раніше підключався безпосередньо до вашого сервера Termix. Його було перероблено, щоб він працював повністю самостійно, зберігаючи хости та облікові дані локально, щоб він міг працювати офлайн. Двостороння синхронізація з власним сервером Termix тепер необов'язкова.", + "body2": "Ваші існуючі хости та облікові дані все ще на {{url}}. Увімкніть віддалену синхронізацію та повторно підключіться до цього сервера, щоб відновити їх та підтримувати синхронізацію надалі.", + "dismiss": "Не зараз", + "setUpSync": "Налаштування віддаленої синхронізації" + }, + "remoteSync": { + "title": "Віддалена синхронізація", + "description": "За бажанням, підключіть цей настільний додаток до окремого сервера Termix, щоб синхронізувати свої хости, облікові дані та фрагменти коду на всіх пристроях. Додаток завжди працює повністю офлайн, незалежно від того, чи підключено ви його до мережі.", + "notConnected": "Не підключено", + "connected": "Підключено", + "connectedTo": "Підключено до {{url}}", + "lastSynced": "Остання синхронізація {{time}}", + "neverSynced": "Ніколи не синхронізувалося", + "syncError": "Помилка синхронізації: {{message}}", + "needsReauth": "Термін дії входу минув", + "connectButton": "Підключення до сервера", + "disconnectButton": "Відключитися", + "syncNowButton": "Синхронізувати зараз", + "syncing": "Синхронізація...", + "serverUrl": "URL-адреса сервера", + "enterServerUrl": "Будь ласка, введіть URL-адресу сервера", + "mustIncludeProtocol": "URL-адреса сервера має починатися з http:// або https://", + "connectionTestFailed": "Не вдалося зв'язатися із сервером Termix за цією URL-адресою", + "allowInvalidCertificate": "Дозволити недійсний сертифікат", + "allowInvalidCertificateDesc": "Використовуйте лише для довірених серверів із власним хостингом та самопідписаними сертифікатами або сертифікатами IP-адреси.", + "savedServers": "Збережені сервери", + "removeServer": "Видалити", + "continueButton": "Продовжити", + "cancelButton": "Скасувати", + "signInTitle": "Увійти до {{url}}", + "originTitle": "Джерело з'єднання", + "originDescription": "Виберіть, звідки за замовчуванням створюються SSH-з’єднання. Це можна змінити для кожного хоста окремо.", + "originLocal": "Цей пристрій (локальна мережа)", + "originRemote": "Віддалений сервер", + "bannerReconnect": "Знову підключитися", + "bannerMessage": "Віддалена синхронізація потребує повторної автентифікації" + }, "versionCheck": { "error": "Помилка перевірки версії", "checkFailed": "Не вдалося перевірити наявність оновлень", @@ -602,6 +641,7 @@ "overrideCredentialUsername": "Перезаписати ім'я користувача облікових даних", "overrideCredentialUsernameDesc": "Використовуйте вказане вище ім'я користувача замість імені користувача облікових даних", "oidcUsernameHint": "Використовуйте $oidc.preferred_username замість вашого імені користувача OIDC.", + "tailscaleUsernameHint": "Це має бути користувач Unix, якому надано ідентифікатор Tailscale у SSH ACL tailnet, не обов'язково root.", "jumpHostChain": "Ланцюг хоста стрибка", "portKnocking": "Порт-Нокінґ", "addKnock": "Додати порт", @@ -650,6 +690,11 @@ "delayAfterMs": "Затримка після (мс)", "useSocks5Proxy": "Використовуйте проксі-сервер SOCKS5", "useSocks5ProxyDesc": "Маршрутизувати з'єднання через проксі-сервер", + "connectionOrigin": "Джерело з'єднання", + "connectionOriginDesc": "Звідки походить SSH-з’єднання цього хоста. Замінює глобальне налаштування за замовчуванням для програми для робочого столу.", + "connectionOriginDefault": "Використовувати за замовчуванням", + "connectionOriginLocal": "Цей пристрій (локальна мережа)", + "connectionOriginRemote": "Віддалений сервер", "proxyHost": "Проксі-хост", "proxyPort": "Проксі-порт", "proxyUsername": "Ім'я користувача проксі-сервера", @@ -762,6 +807,14 @@ "customThemeBrightCyan": "Яскравий блакитний", "customThemeBrightWhite": "Яскраво-білий", "customThemeResetTooltip": "Скинути до налаштувань за замовчуванням", + "savedThemesLabel": "Збережені теми", + "saveAsGlobalTheme": "Зберегти як глобальну тему", + "saveGlobalThemeNamePrompt": "Введіть назву для цієї теми", + "saveGlobalThemeSuccess": "Тему збережено", + "saveGlobalThemeError": "Не вдалося зберегти тему", + "applyGlobalThemeTooltip": "Застосувати цю тему", + "deleteGlobalThemeTooltip": "Видалити цю тему", + "noSavedThemes": "Ще немає збережених тем", "syntaxHighlightingLabel": "Підсвічування синтаксису", "syntaxHighlightingDesc": "Розфарбувати вивід терміналу (помилки, шляхи, IP-адреси, позначки часу)", "syntaxHighlightingCategories": "Виділіть категорії", @@ -791,6 +844,8 @@ "enableAutoTmuxDesc": "Автоматично запускати або приєднуватися до сеансу tmux", "enableSessionLogging": "Ведення журналу сеансу", "enableSessionLoggingDesc": "Запис виводу сеансу терміналу для подальшого перегляду", + "allowSessionSharing": "Дозволити спільний доступ до сеансу", + "allowSessionSharingDesc": "Дозволити ділитися прямими трансляціями на цьому хості через посилання або з іншими користувачами", "enableCommandHistory": "Історія команд", "enableCommandHistoryDesc": "Запис команд, що виконуються в цьому терміналі, для історії та автозаповнення", "linkClickBehaviorLabel": "Поведінка кліків посилань", @@ -895,6 +950,7 @@ "proxmoxResultImported": "{{count}} імпортовано", "proxmoxResultUpdated": "{{count}} оновлено", "proxmoxResultFailed": "{{count}} не вдалося", + "proxmoxResultSkippedNoIp": "{{count}} пропущено (IP-адресу не знайдено)", "proxmoxImportComplete": "Імпорт Proxmox завершено: {{summary}}", "proxmoxDiscoveryFailed": "Не вдалося знайти", "proxmoxImportFailed": "Імпорт не вдалося", @@ -961,6 +1017,9 @@ "failedToSaveCredential": "Не вдалося зберегти облікові дані", "credentialNameRequired": "Будь ласка, введіть ім'я для облікових даних", "credentialAuthRequired": "Додайте пароль, ключ SSH або обидва", + "createCredentialFromHostBtn": "Створити облікові дані", + "createCredentialFromHostTitle": "Створити облікові дані від хоста", + "createCredentialFromHostDesc": "Створіть багаторазовий, спільний запис облікових даних, попередньо заповнений поточним іменем користувача, паролем та/або ключем SSH цього хоста.", "backToHosts": "Назад до хостів", "backToCredentials": "Повернутися до облікових даних", "pinned": "Закріплено", @@ -1000,6 +1059,9 @@ "folderNestingHint": "Використовуйте / для розділення рівнів та створення вкладених папок.", "folderColor": "Колір", "folderIcon": "Значок", + "folderCredential": "Посвідчення", + "folderCredentialNone": "Облікові дані не призначено", + "folderCredentialHint": "Хости в цій папці, які використовують автентифікацію \"Збережені облікові дані\" без вибраних власних облікових даних, успадкують ці дані.", "folderPreview": "Попередній перегляд", "folderNameFallback": "Папка без назви", "createFolderButton": "Створити папку", @@ -1181,6 +1243,10 @@ "filterTagsGroup": "Теги", "shareHost": "Спільний хост", "shareHostTitle": "Поділитися: {{name}}", + "shareFolder": "Спільний доступ до папки", + "shareFolderTitle": "Папка для спільного доступу: {{name}}", + "folderSharedSuccessfully": "Спільні хости {{count}} у папці", + "failedToShareFolder": "Не вдалося поділитися папкою", "sharing": { "loadError": "Не вдалося завантажити дані для спільного доступу. Спробуйте ще раз.", "shareWithSection": "Поділитися з", @@ -1221,6 +1287,7 @@ "shareWithCount": "Поділитися ({{count}})", "currentAccess": "Поточний доступ", "noAccessEntries": "Цей хост ще не був спільним", + "folderShareSummary": "Спільні хости {{shared}} з {{total}} у цій папці", "grantedBy": "Надано", "expires": "Термін дії закінчується", "expired": "Термін дії минув", @@ -1242,6 +1309,8 @@ "authMethod": "Метод автентифікації", "authTypeDirect": "Прямий", "authTypeCredential": "Посвідчення", + "authTypeNone": "Жоден", + "authTypeNoneDesc": "Облікові дані не зберігаються. Вам буде запропоновано ввести ім’я користувача та пароль щоразу під час підключення; вони не зберігаються.", "selectCredential": "Виберіть облікові дані...", "connectionSettings": "Налаштування підключення", "displaySettings": "Налаштування дисплея", @@ -1259,6 +1328,7 @@ "rdpPort": "RDP-порт", "username": "Ім'я користувача", "password": "Пароль", + "passwordSaved": "Пароль збережено, введіть, щоб змінити", "domain": "Домен", "securityMode": "Режим безпеки", "colorDepth": "Глибина кольору", @@ -1424,7 +1494,60 @@ "expiresIn": "Термін дії закінчується через {{duration}}", "search": "Пошук з'єднань...", "noSearchResults": "Немає з'єднань, що відповідають вашому пошуку", - "rename": "Перейменувати сеанс" + "rename": "Перейменувати сеанс", + "sectionSharedWithMe": "Поділилися зі мною", + "sharedBy": "Поділився користувачем {{username}}", + "join": "Приєднатися", + "sharedSessionLabel": "{{hostName}} (спільний доступ)" + }, + "sessionSharing": { + "guestView": { + "loading": "Підключення до спільного сеансу...", + "linkInvalid": "Це посилання для спільного доступу недійсне, термін його дії минув або його було відкликано", + "rateLimited": "Забагато спроб, спробуйте ще раз пізніше", + "sessionEnded": "Цей сеанс завершився", + "readOnlyBadge": "Тільки перегляд" + }, + "modalTitle": "Поділитися сеансом", + "shareButton": "Поділитися", + "notReadyToShare": "Сеанс ще не готовий до поширення", + "modeTab": { + "link": "Посилання", + "user": "Користувач" + }, + "linkModeDescription": "Приєднатися може кожен, хто має це посилання, обліковий запис не потрібен.", + "userModeDescription": "Надайте доступ певному користувачеві, який уже має доступ до цього хоста. Якщо у нього ще немає доступу, спочатку надайте йому доступ до хоста або скористайтеся посиланням. Після надання доступу сеанс відображається на його вкладці «Підключення».", + "permissionLevel": { + "label": "Рівень дозволів", + "readOnly": "Тільки для читання", + "readOnlyDescription": "Можна дивитися сеанс у прямому ефірі, але не можна друкувати чи взаємодіяти.", + "readWrite": "Читання-запис", + "readWriteDescription": "Можна друкувати та взаємодіяти з сеансом так само, як і його власник." + }, + "expiryLabel": "Термін дії посилання", + "createLinkButton": "Створити посилання", + "createShareButton": "Поділитися з користувачем", + "searchUsersPlaceholder": "Пошук користувачів...", + "noUsersFound": "Користувачів не знайдено", + "linkCreated": "Посилання для спільного доступу створено", + "linkCopied": "Посилання скопійовано в буфер обміну", + "copyLink": "Копіювати посилання", + "shareCreated": "Сеанс спільний. Він відображатиметься на вкладці «Зв’язки».", + "shareFailed": "Не вдалося створити спільний доступ", + "userLacksHostAccess": "Цей користувач ще не має доступу до цього хоста. Спочатку надайте йому доступ до хоста або скористайтеся посиланням.", + "activeShares": "Активні акції", + "noActiveShares": "Немає активних поширень для цього сеансу", + "revoke": "Скасувати", + "revokeConfirmTitle": "Скасувати цей спільний доступ?", + "revokeConfirmDescription": "Будь-хто, хто користується цим ресурсом спільного доступу, негайно втратить доступ.", + "revoked": "Акції анульовано", + "revokeFailed": "Не вдалося скасувати спільний доступ", + "joinCount": "{{count}} приєднатися", + "joinCount_other": "{{count}} приєднується", + "expiresAt": "Термін дії закінчується {{date}}", + "linkShareBadge": "Посилання", + "userShareBadge": "Користувач: {{username}}", + "loadSharesFailed": "Не вдалося завантажити активні спільні ресурси" }, "guacamole": { "connecting": "Підключення до сеансу {{type}}...", @@ -1436,6 +1559,9 @@ "reconnect": "Знову підключитися", "retry": "Повторити спробу", "guacdUnavailable": "Служба віддаленого робочого столу (guacd) недоступна. Переконайтеся, що guacd запущено, доступно та правильно налаштовано в налаштуваннях адміністратора.", + "credentialPromptTitle": "Введіть облікові дані RDP", + "credentialPromptDescription": "Цей хост налаштовано на запит облікових даних під час підключення. Вони використовуються лише для цього сеансу та не зберігаються.", + "connect": "Підключитися", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", @@ -1461,7 +1587,9 @@ "reconnect": "Сеанс повторного підключення", "collapse": "Згорнути панель інструментів", "expand": "Розгорнути панель інструментів", - "dragHandle": "Перетягніть, щоб змінити положення" + "dragHandle": "Перетягніть, щоб змінити положення", + "switchToTrackpad": "Перехід у режим трекпада (перетягування для переміщення курсора, торкання для клацання)", + "switchToTouch": "Перемикання в сенсорний режим (торкайтеся безпосередньо там, де потрібно клацнути)" } }, "terminal": { @@ -1508,6 +1636,11 @@ "totpRequired": "Потрібна двофакторна автентифікація", "totpCodeLabel": "Код підтвердження", "totpVerify": "Перевірити", + "mfaPromptRequired": "Потрібна автентифікація", + "mfaPushRequired": "Потрібна автентифікація push-повідомлень", + "mfaMenuPlaceholder": "Введіть свою відповідь", + "mfaWaitingApproval": "Очікування схвалення на вашому пристрої...", + "mfaSendRequest": "Надіслати запит", "warpgateAuthRequired": "Потрібна автентифікація Warpgate", "warpgateSecurityKey": "Ключ безпеки", "warpgateAuthUrl": "URL-адреса автентифікації", @@ -2277,7 +2410,8 @@ "resetCodeRateLimited": "Обмеження швидкості: Забагато спроб перевірки. Будь ласка, спробуйте пізніше.", "resetCodeRateLimitedWithTime": "Обмеження швидкості: Забагато спроб перевірки. Будь ласка, зачекайте {{time}} секунд, перш ніж повторити спробу.", "authTokenSaveFailed": "Не вдалося зберегти токен автентифікації", - "failedToLoadServer": "Не вдалося завантажити сервер" + "failedToLoadServer": "Не вдалося завантажити сервер", + "remoteServerRequired": "Потрібен віддалений сервер. Підключіться до віддаленого сервера в налаштуваннях, щоб використовувати цей тип підключення." }, "messages": { "registrationDisabled": "Реєстрацію нового облікового запису наразі вимкнено адміністратором. Будь ласка, увійдіть або зверніться до адміністратора.", @@ -2644,6 +2778,17 @@ "sslSaveFailed": "Не вдалося зберегти налаштування SSL", "sslRequiresDomain": "Домен та електронна адреса обов'язкові", "sslInfoNote": "Після видачі сертифіката увімкніть SSL у змінних середовища (ENABLE_SSL=true) та перезапустіть Termix.", + "sslManualOption": "Інструкція (сертифікат завантаження)", + "sslManualCert": "Сертифікат (PEM)", + "sslManualCertPlaceholder": "-----ПОЧАТИ СЕРТИФІКАТ-----", + "sslManualKey": "Закритий ключ (PEM)", + "sslManualKeyPlaceholder": "-----ПОЧАТИ ПРИВАТНИЙ КЛЮЧ-----", + "sslManualDesc": "Вставте свій існуючий сертифікат і закритий ключ, включаючи повний ланцюжок, якщо цього вимагає ваш центр сертифікації.", + "sslManualUpload": "Сертифікат завантаження та встановлення", + "sslManualUploadLoading": "Завантаження сертифіката...", + "sslManualUploadSuccess": "Сертифікат успішно завантажено та встановлено", + "sslManualUploadFailed": "Не вдалося завантажити сертифікат", + "sslManualRequiresFields": "Потрібні сертифікат та закритий ключ", "auditLogTotal": "{{total}} всього записів", "auditLogEmpty": "Записів журналу аудиту не знайдено", "auditLogSuccess": "Успіх", @@ -2672,6 +2817,12 @@ "commandHistoryEnabled": "Історія команд", "commandHistoryEnabledDesc": "Дозволити запис історії команд. Якщо вимкнено, історія не зберігається незалежно від налаштувань для кожного хоста.", "updateCommandHistoryFailed": "Не вдалося оновити налаштування історії команд", + "analyticsEnabled": "Поділитися анонімною статистикою використання", + "analyticsEnabledDesc": "Надсилає анонімний щоденний підрахунок користувачів, хостів та використання функцій, щоб допомогти покращити Termix. Жодні особисті дані чи деталі з’єднання ніколи не включаються.", + "updateAnalyticsFailed": "Не вдалося оновити налаштування аналітики", + "sessionSharingGloballyEnabled": "Дозволити спільний доступ до сеансу", + "sessionSharingGloballyEnabledDesc": "Дозволити спільний доступ до сеансів терміналу, RDP, VNC та Telnet для всього екземпляра. Перевизначає кожен перемикач спільного доступу для кожного хоста, якщо його вимкнено.", + "updateSessionSharingFailed": "Не вдалося оновити налаштування спільного доступу до сеансу", "sessionTimeout": "Час очікування сеансу", "hours": "години", "sessionTimeoutRange": "Мін. 1 год · Макс. 720 год", @@ -3119,6 +3270,47 @@ "executionSuccess": "Успіх", "executionFailed": "Не вдалося" }, + "keybindings": { + "title": "Комбінації клавіш", + "description": "Налаштуйте комбінації клавіш для копіювання, вставки та керування терміналом або прив’яжіть клавіші для надсилання тексту чи запуску фрагмента коду.", + "defaultsHeading": "Вбудовані комбінації клавіш", + "customHeading": "Користувацькі скорочення", + "addBinding": "Додати ярлик", + "addBindingTitle": "Додати ярлик", + "editBindingTitle": "Редагувати скорочення", + "loading": "Завантаження...", + "noCustomBindings": "Поки що немає користувацьких комбінацій клавіш", + "defaultBadge": "За замовчуванням", + "customizedBadge": "Налаштовані", + "customize": "Налаштувати", + "resetToDefault": "Скинути до заводських налаштувань", + "resetAllToDefaults": "Скинути всі налаштування до заводських", + "close": "Закрити", + "cancel": "Скасувати", + "saveBinding": "Зберегти ярлик", + "saveError": "Не вдалося зберегти комбінації клавіш", + "pressKeysToRecord": "Комбінація клавіш", + "pressKeysPlaceholder": "Клацайте та натискайте клавіші", + "recording": "Натискайте клавіші...", + "comboRequiredError": "Спочатку натисніть комбінацію клавіш", + "textRequiredError": "Введіть текст для надсилання", + "controlCodeRequiredError": "Введіть одну літеру для контрольного коду", + "snippetRequiredError": "Виберіть фрагмент", + "conflictWarning": "Ця комбінація вже пов'язана з {{combo}}. Збереження призведе до спрацювання обох.", + "actionLabel": "Дія", + "actionCopy": "Копіювати виділення", + "actionPaste": "Вставити", + "actionSendControlCode": "Надіслати сигнал Ctrl+літера", + "actionSendText": "Надіслати текст", + "actionRunSnippet": "Запустити існуючий фрагмент коду", + "controlCodeLabel": "Лист", + "textLabel": "Текст для надсилання", + "appendEnterLabel": "Натисніть Enter після відправлення", + "snippetLabel": "Фрагмент", + "selectSnippetPlaceholder": "Виберіть фрагмент", + "orphanedSnippetWarning": "фрагмент не знайдено", + "clipboardPermissionNote": "У деяких браузерах може запитуватися дозвіл на використання буфера обміну під час першого використання." + }, "userProfile": { "donateTitle": "Збережи Термікс живим", "donateDescription": "Termix безкоштовний, розміщується самостійно та створений лише кількома людьми у вільний час. Якщо це заощадило вам час або гроші, пожертва у криптовалюті допоможе йому продовжувати роботу.", @@ -3134,6 +3326,7 @@ "sectionAppearance": "Зовнішній вигляд", "sectionSecurity": "Безпека", "sectionApiKeys": "Ключі API", + "sectionData": "Дані", "sectionC2sTunnels": "Тунелі C2S", "usernameLabel": "Ім'я користувача", "roleLabel": "Роль", @@ -3162,6 +3355,9 @@ "settingsTerminal": "Термінал", "commandAutocomplete": "Автозаповнення команд", "commandAutocompleteDesc": "Показувати автозаповнення під час введення тексту", + "keyboardShortcuts": "Комбінації клавіш", + "keyboardShortcutsDescription": "Налаштування скорочень команд, копіювання, вставки та інших команд у терміналі", + "manageShortcuts": "Керувати", "terminalLinkBehavior": "Натисніть посилання терміналу", "terminalLinkBehaviorDesc": "Поведінка за замовчуванням під час натискання посилань у терміналі", "historyTracking": "Відстеження історії", @@ -3228,6 +3424,22 @@ "apiKeyUsageHint": "Додайте свій ключ до", "apiKeyUsageHintHeader": "заголовок.", "apiKeyPermissionsHint": "Ключі успадковують дозволи користувача, який їх створює.", + "exportData": "Експорт моїх даних", + "exportDataDesc": "Завантажте резервну копію своїх хостів, облікових даних та налаштувань для перенесення на інший пристрій", + "export": "Експорт", + "exporting": "Експорт...", + "importData": "Імпортувати мої дані", + "importDataDesc": "Відновіть свої хости, облікові дані та налаштування з резервного файлу .sqlite", + "importDataSelected": "Вибрано: {{name}}", + "selectFile": "Виберіть файл", + "changeFile": "Зміна", + "import": "Імпорт", + "importing": "Імпорт...", + "exportSuccess": "Дані успішно експортовано", + "exportFailed": "Не вдалося експортувати дані", + "importSelectFile": "Спочатку виберіть файл", + "importCompleted": "Імпорт завершено: імпортовано {{total}} елементів, пропущено {{skipped}}", + "importFailed": "Помилка імпорту: {{error}}", "roleUser": "Користувач", "authMethodDual": "Подвійна автентифікація", "authMethodOidc": "ОІДК", @@ -3379,6 +3591,7 @@ "pageUp": "PgUp", "pageDown": "ПгДн", "delete": "Дел", + "paste": "Вставити", "editQuickKeys": "Редагувати швидкі клавіші", "quickKeysTitle": "Швидкі клавіші", "quickKeysDesc": "Натисніть ×, щоб видалити. Підтримує до 8 символів.", diff --git a/src/ui/locales/translated/vi_VN.json b/src/ui/locales/translated/vi_VN.json index 1e6f650a..ec4cd1ff 100644 --- a/src/ui/locales/translated/vi_VN.json +++ b/src/ui/locales/translated/vi_VN.json @@ -1,562 +1,601 @@ { "termixId": { - "title": "Mã định danh Termix", - "loadFailed": "Không thể tải ID Termix", - "claimTitle": "Xác nhận ID Termix của bạn", - "claimIntro": "Hãy chọn một tên người dùng duy nhất. Khóa công khai SSH của bạn sẽ được công bố tại một URL công khai mà bạn có thể thêm vào tệp authorized_keys của bất kỳ máy chủ nào.", - "handleLabel": "Xử lý", - "handlePlaceholder": "Alice", + "title": "Termix ID", + "loadFailed": "Tải Termix ID thất bại", + "claimTitle": "Nhận Termix ID của bạn", + "claimIntro": "Chọn một tên người dùng duy nhất. Khóa công khai SSH của bạn sẽ được công bố tại một URL công khai mà bạn có thể thêm vào tệp authorized_keys của bất kỳ máy chủ nào.", + "handleLabel": "Tên người dùng", + "handlePlaceholder": "alice", "checking": "Đang kiểm tra…", "available": "Có sẵn", - "taken": "Đã chụp", - "invalidHandle": "Chỉ chữ cái viết thường, số và dấu gạch dưới (-)", + "taken": "Đã được sử dụng", + "invalidHandle": "Chỉ chữ thường, số, - và _", "descriptionLabel": "Mô tả (tùy chọn)", - "descriptionPlaceholder": "Bàn phím máy tính xách tay và điện thoại làm việc", - "create": "Tạo ID Termix", - "created": "ID Termix đã được tạo", - "createFailed": "Không thể tạo ID Termix.", - "deleteConfirm": "Bạn muốn xóa ID Termix và tất cả các khóa đã công bố? Các máy chủ bạn đã thiết lập sẽ giữ lại các khóa cho đến khi bạn xóa chúng theo cách thủ công.", - "deleted": "ID Termix đã bị xóa", - "deleteFailed": "Không thể xóa ID Termix.", + "descriptionPlaceholder": "Khóa laptop & điện thoại", + "create": "Tạo Termix ID", + "created": "Đã tạo Termix ID", + "createFailed": "Tạo Termix ID thất bại", + "deleteConfirm": "Xóa Termix ID và tất cả khóa đã công bố? Các máy chủ bạn đã cung cấp sẽ vẫn giữ khóa cho đến khi bạn xóa thủ công.", + "deleted": "Đã xóa Termix ID", + "deleteFailed": "Xóa Termix ID thất bại", "copyFailed": "Sao chép thất bại", - "resolverUrlLabel": "URL trình phân giải công khai", + "resolverUrlLabel": "URL phân giải công khai", "provisionLabel": "Cung cấp máy chủ", "publishTitle": "Công bố khóa công khai", - "generate": "Phát ra", - "generateTooltip": "Tạo cặp khóa Ed25519 — công bố khóa công khai và tải khóa riêng tư xuống thiết bị của bạn.", - "saveToVault": "Lưu vào thông tin đăng nhập", + "generate": "Tạo", + "generateTooltip": "Tạo cặp khóa Ed25519 — công bố khóa công khai và tải khóa riêng về thiết bị của bạn", + "saveToVault": "Lưu vào Kho thông tin", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "Nhãn (tùy chọn)", - "add": "Thêm vào", - "importFromCredential": "Hoặc nhập thông tin xác thực đã lưu trữ.", - "keyPublished": "Khóa đã được công bố", - "addKeyFailed": "Không thể thêm khóa", - "generatedSaved": "Cặp khóa đã được tạo và lưu vào kho lưu trữ thông tin xác thực của bạn. Khóa riêng tư cũng đã được tải xuống.", - "generatedOnly": "Cặp khóa đã được tạo — khóa riêng tư đã được tải xuống (chỉ hiển thị một lần).", - "generateFailed": "Không thể tạo khóa", - "imported": "Khóa được nhập từ thông tin xác thực", - "importFailed": "Không thể nhập khóa", - "noKeys": "Chưa có khóa công khai nào được công bố.", + "add": "Thêm", + "importFromCredential": "Hoặc nhập từ một thông tin đăng nhập đã lưu", + "keyPublished": "Đã công bố khóa", + "addKeyFailed": "Thêm khóa thất bại", + "generatedSaved": "Đã tạo cặp khóa và lưu vào kho thông tin đăng nhập của bạn. Khóa riêng cũng đã được tải xuống.", + "generatedOnly": "Đã tạo cặp khóa — khóa riêng đã được tải xuống (chỉ hiển thị một lần).", + "generateFailed": "Tạo khóa thất bại", + "imported": "Đã nhập khóa từ thông tin đăng nhập", + "importFailed": "Nhập khóa thất bại", + "noKeys": "Chưa có khóa công khai được công bố.", "keysTitle": "Khóa đã công bố", - "published": "Đã xuất bản", - "hidden": "Ẩn giấu", - "keyRemoved": "Chìa khóa đã bị xóa", - "removeKeyFailed": "Không thể xóa khóa", - "updateKeyFailed": "Không thể cập nhật khóa", - "fromVault": "Từ kho lưu trữ thông tin xác thực", - "linkedToTermixId": "Được xuất bản thông qua Termix ID", + "published": "Đã công bố", + "hidden": "Ẩn", + "keyRemoved": "Đã xóa khóa", + "removeKeyFailed": "Xóa khóa thất bại", + "updateKeyFailed": "Cập nhật khóa thất bại", + "fromVault": "Từ kho thông tin xác thực", + "linkedToTermixId": "Công bố qua Termix ID", "selectCredential": "Chọn thông tin xác thực...", - "import": "Nhập khẩu", - "caTitle": "Cơ quan cấp chứng chỉ", - "caIntro": "Hãy tin tưởng CA này trên máy chủ và nó sẽ chấp nhận bất kỳ chứng chỉ nào bạn ký. Hãy xoay vòng để thu hồi tất cả cùng một lúc; các chứng chỉ cũng sẽ tự động hết hạn.", - "caEnable": "Kích hoạt CA", - "caEnabled": "CA đã bật", - "caCreateFailed": "Không thể kích hoạt CA", + "import": "Nhập", + "caTitle": "Cơ quan chứng thực", + "caIntro": "Tin cậy CA này trên máy chủ và nó chấp nhận mọi chứng chỉ do bạn ký. Xoay để thu hồi tất cả cùng lúc; chứng chỉ cũng tự hết hạn.", + "caEnable": "Bật CA", + "caEnabled": "Đã bật CA", + "caCreateFailed": "Bật CA thất bại", "caPublicKeyLabel": "Khóa công khai CA", - "caTrustLabel": "Tin cậy máy chủ (chạy với quyền root)", - "caRotate": "Quay", - "caRotateConfirm": "Xoay vòng CA? Tất cả các chứng chỉ đã ký sẽ không còn được chấp nhận và phải được cấp lại.", - "caRotated": "CA đã được xoay vòng — các chứng chỉ trước đó đã bị thu hồi", - "caRotateFailed": "Không thể xoay CA", - "caDelete": "Xóa CA", - "caDeleteConfirm": "Xóa chứng chỉ cơ quan cấp chứng chỉ?", - "caDeleted": "CA đã bị xóa", - "caDeleteFailed": "Không thể gỡ bỏ CA", - "caValidityLabel": "Thời hạn hiệu lực của chứng chỉ (ngày)", - "issueCert": "Giấy chứng nhận", - "issueCertTooltip": "Cấp chứng chỉ SSH cho khóa này, được ký bởi CA của bạn.", + "caTrustLabel": "Tin cậy trên máy chủ (chạy với quyền root)", + "caRotate": "Xoay", + "caRotateConfirm": "Xoay CA? Mọi chứng chỉ do CA này ký sẽ không còn được chấp nhận và phải được cấp lại.", + "caRotated": "Đã xoay CA — các chứng chỉ trước bị thu hồi", + "caRotateFailed": "Xoay CA thất bại", + "caDelete": "Gỡ bỏ CA", + "caDeleteConfirm": "Gỡ bỏ cơ quan chứng thực?", + "caDeleted": "Đã gỡ CA", + "caDeleteFailed": "Gỡ CA thất bại", + "caValidityLabel": "Hiệu lực chứng chỉ (ngày)", + "issueCert": "Chứng chỉ", + "issueCertTooltip": "Cấp chứng chỉ SSH cho khóa này, được ký bởi CA của bạn", "certIssued": "Chứng chỉ đã được cấp và tải xuống", - "certIssueFailed": "Không thể cấp chứng chỉ" + "certIssueFailed": "Cấp chứng chỉ thất bại" }, "credentials": { "folders": "Thư mục", "folder": "Thư mục", "password": "Mật khẩu", - "key": "Chìa khóa", + "key": "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", + "uploadPrivateKeyFile": "Tải lên tệp khóa riêng", + "searchCredentials": "Tìm kiếm chứng chỉ...", + "addCredential": "Thêm chứng chỉ", "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ỉ.", + "caCertificateDescription": "Tùy chọn: Tải lên hoặc dán tệp chứng chỉ do CA ký (ví dụ: id_ed25519-cert.pub). Cần 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", + "clearCert": "Xóa", + "certLoaded": "Đã tải chứng chỉ", "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", - "noPublicKeyAvailable": "Không có khóa công khai. Vui lòng mở trình chỉnh sửa thông tin xác thực trước.", - "deployCommandCopied": "Lệnh triển khai đã được sao chép", - "sortCredentials": "Sắp xếp thông tin đăng nhập", + "noPublicKeyAvailable": "Không có khóa công khai. Hãy mở trình chỉnh sửa chứng chỉ trước.", + "deployCommandCopied": "Đã sao chép lệnh triển khai", + "sortCredentials": "Sắp xếp chứng chỉ", "sortDefault": "Thứ tự mặc định", "sortNameAsc": "Tên (A → Z)", "sortNameDesc": "Tên (Z → A)", "sortUsernameAsc": "Tên người dùng (A → Z)", "sortUsernameDesc": "Tên người dùng (Z → A)", - "filterCredentials": "Lọc thông tin đăng nhập", + "filterCredentials": "Lọc chứng chỉ", "filterClearAll": "Xóa bộ lọc", - "filterTypeGroup": "Kiểu", + "filterTypeGroup": "Loại", "filterTypePassword": "Mật khẩu", "filterTypeKey": "Khóa SSH", "filterTagsGroup": "Thẻ" }, "homepage": { "title": "Trang chủ", - "addWidget": "Thêm tiện ích", - "editWidget": "Chỉnh sửa tiện ích", - "deleteWidget": "Xóa bỏ", - "widgetTypes": "Các loại tiện ích", + "addWidget": "Thêm widget", + "editWidget": "Sửa widget", + "deleteWidget": "Xóa", + "widgetTypes": "Loại widget", "serviceLink": "Liên kết dịch vụ", "folder": "Thư mục", - "clock": "Cái đồng hồ", + "clock": "Đồng hồ", "notes": "Ghi chú", "hostStatus": "Trạng thái máy chủ", "bookmarkList": "Dấu trang", "zoomIn": "Phóng to", "zoomOut": "Thu nhỏ", "resetView": "Đặt lại chế độ xem", - "lockLayout": "Bố cục khóa", + "lockLayout": "Khóa bố cục", "unlockLayout": "Mở khóa bố cục", - "noWidgets": "Nhấp chuột phải hoặc nhấp vào + để thêm tiện ích đầu tiên của bạn.", - "openFullView": "Mở chế độ xem toàn màn hình", + "noWidgets": "Nhấp chuột phải hoặc nhấn + để thêm widget đầu tiên", + "openFullView": "Mở toàn màn hình", "previewTitle": "Xem trước trang chủ", - "cancel": "Hủy bỏ", - "save": "Cứu", + "cancel": "Hủy", + "save": "Lưu", "title_label": "Tiêu đề", - "widgetTitlePlaceholder": "Tiêu đề tiện ích (tùy chọn)", + "widgetTitlePlaceholder": "Tiêu đề widget (tùy chọn)", "url": "URL", "imageUrl": "URL hình ảnh tùy chỉnh", - "imageUrlHint": "Để trống nếu muốn sử dụng biểu tượng favicon tự động của trang web.", + "imageUrlHint": "Để trống để tự động dùng favicon của trang", "showImage": "Hiển thị hình ảnh", - "description": "Sự miêu tả", + "description": "Mô tả", "color": "Màu sắc", "icon": "Biểu tượng", - "expanded": "Được mở rộng theo mặc định", + "expanded": "Mở rộng mặc định", "timezone": "Múi giờ", "showSeconds": "Hiển thị giây", "format12h": "12 giờ", "format24h": "24 giờ", "content": "Nội dung", "backgroundColor": "Màu nền", - "host": "Chủ nhà", - "showMetrics": "Hiển thị số liệu", + "host": "Máy chủ", + "showMetrics": "Hiển thị chỉ số", "links": "Liên kết", "addLink": "Thêm liên kết", "linkLabel": "Nhãn", "linkUrl": "URL", - "removeLink": "Di dời", + "removeLink": "Xóa", "categoryLinks": "Liên kết", "categoryInfo": "Thông tin", "categorySystem": "Hệ thống", - "widgetServiceLinkDesc": "Một ô có thể nhấp chuột dẫn đến URL dịch vụ.", - "widgetFolderDesc": "Một vùng chứa để nhóm các tiện ích liên quan lại với nhau.", - "widgetClockDesc": "Đồng hồ thời gian thực với múi giờ có thể cấu hình.", - "widgetNotesDesc": "Tiện ích ghi chú Markdown", - "widgetHostStatusDesc": "Hiển thị thông tin CPU, bộ nhớ và ổ đĩa trực tiếp của máy chủ SSH.", - "widgetBookmarkListDesc": "Danh sách các liên kết nhanh", + "widgetServiceLinkDesc": "Ô nhấp được để liên kết đến URL dịch vụ", + "widgetFolderDesc": "Hộp chứa để nhóm các widget liên quan", + "widgetClockDesc": "Đồng hồ trực tiếp với múi giờ tùy chỉnh", + "widgetNotesDesc": "Widget ghi chú markdown", + "widgetHostStatusDesc": "Hiển thị trực tiếp CPU, bộ nhớ và ổ đĩa cho máy chủ SSH", + "widgetBookmarkListDesc": "Danh sách liên kết nhanh", "copyLink": "Sao chép liên kết", "linkCopied": "Đã sao chép liên kết!", "location": "Vị trí", "temperatureUnit": "Đơn vị nhiệt độ", "showForecast": "Hiển thị dự báo 3 ngày", "scrolling": "Cho phép cuộn", - "feedUrl": "URL nguồn cấp dữ liệu", - "maxItems": "Số lượng vật phẩm tối đa", + "feedUrl": "URL nguồn cấp", + "maxItems": "Số mục tối đa", "showDescription": "Hiển thị mô tả", "widgetWeatherName": "Thời tiết", - "widgetWeatherDesc": "Thời tiết trực tiếp cho bất kỳ địa điểm nào", + "widgetWeatherDesc": "Thời tiết trực tiếp cho mọi vị trí", "widgetIframeName": "Nhúng iFrame", "widgetIframeDesc": "Nhúng bất kỳ URL nào vào iframe", - "widgetRssName": "Nguồn cấp dữ liệu RSS", - "widgetRssDesc": "Hiển thị các mục từ nguồn cấp dữ liệu RSS hoặc Atom.", - "dragToFolder": "Kéo các tiện ích vào đây hoặc sử dụng dấu + để thêm.", + "widgetRssName": "Nguồn RSS", + "widgetRssDesc": "Hiển thị các mục từ nguồn RSS hoặc Atom", + "dragToFolder": "Kéo widget vào đây hoặc dùng + để thêm", "showDisk": "Hiển thị mức sử dụng ổ đĩa", - "addToFolder": "Thêm tiện ích vào thư mục", - "noHostSelected": "Chưa chọn máy chủ nào", - "metricsNotAvailable": "Số liệu không có sẵn", + "addToFolder": "Thêm widget vào thư mục", + "noHostSelected": "Chưa chọn máy chủ", + "metricsNotAvailable": "Chỉ số không khả dụng", "selectHost": "Chọn máy chủ...", - "displayedMetrics": "Các chỉ số được hiển thị", + "displayedMetrics": "Chỉ số hiển thị", "metricCpu": "CPU", - "metricMemory": "Ký ức", + "metricMemory": "Bộ nhớ", "metricDisk": "Đĩa", "metricUptime": "Thời gian hoạt động", "metricSystem": "Hệ thống", "metricOs": "Hệ điều hành", - "metricKernel": "Hạt nhân", + "metricKernel": "Nhân", "metricHostname": "Tên máy chủ", "metricNetwork": "Mạng", - "metricProcesses": "Quy trình", - "metricProcessesTotal": "tổng cộng", + "metricProcesses": "Tiến trình", + "metricProcessesTotal": "tổng", "metricProcessesRunning": "đang chạy", "categoryMonitoring": "Giám sát", "loading": "Đang tải...", - "noData": "Không có dữ liệu nào khả dụng.", - "allClear": "Mọi thứ ổn", - "acknowledgeAlert": "Thừa nhận", - "noPingUrls": "Chưa có URL nào được cấu hình", + "noData": "Không có dữ liệu", + "allClear": "Tất cả ổn", + "acknowledgeAlert": "Xác nhận", + "noPingUrls": "Chưa cấu hình URL", "pingLabel": "Nhãn", "addPingUrl": "Thêm URL", "showLatency": "Hiển thị độ trễ", - "refreshInterval": "Khoảng thời gian làm mới", + "refreshInterval": "Chu kỳ làm mới", "seconds": "giây", - "filterActivityTypes": "Các loại bộ lọc", + "filterActivityTypes": "Loại hoạt động", "filterTypesHint": "Để trống để hiển thị tất cả", "showTimestamp": "Hiển thị dấu thời gian", - "uptimeUnavailable": "Thời gian hoạt động không khả dụng", + "uptimeUnavailable": "Không có thời gian hoạt động", "uptimeLabel": "Thời gian hoạt động", "overviewVersion": "Phiên bản", - "overviewUpdate": "Đã cập nhật", - "overviewUpdateAvailable": "Đã có bản cập nhật", + "overviewUpdate": "Đã cập nhật mới nhất", + "overviewUpdateAvailable": "Có bản cập nhật", "overviewDatabase": "Cơ sở dữ liệu", "overviewUptime": "Thời gian hoạt động", - "noHosts": "Không có máy chủ nào được cấu hình", - "hostGridHosts": "Người dẫn chương trình", - "hostGridAllHint": "Để trống để hiển thị tất cả các máy chủ", + "noHosts": "Chưa cấu hình máy chủ nào", + "hostGridHosts": "Máy chủ", + "hostGridAllHint": "Để trống để hiển thị tất cả máy chủ", "columns": "Cột", "showIp": "Hiển thị địa chỉ IP", - "connectionType": "Loại kết nối", - "layout": "Cách trình bày", + "connectionType": "Kiểu kết nối", + "layout": "Bố cục", "showStatus": "Hiển thị trạng thái", - "showHostName": "Tên người dẫn chương trình", - "noDockerActivity": "Không có hoạt động Docker nào.", - "noActivity": "Không có hoạt động nào", - "showAcknowledged": "Hiển thị Đã xác nhận", + "showHostName": "Hiển thị tên máy chủ", + "noDockerActivity": "Không có hoạt động Docker", + "noActivity": "Không có hoạt động", + "showAcknowledged": "Hiển thị đã xác nhận", "showCurrentValue": "Hiển thị giá trị hiện tại", - "chartMetric": "Số liệu", + "chartMetric": "Chỉ số", "metricRange": "Phạm vi", - "widgetMetricsChartName": "Biểu đồ số liệu", - "widgetMetricsChartDesc": "Biểu đồ lịch sử về CPU, bộ nhớ, ổ đĩa hoặc mạng của máy chủ", + "widgetMetricsChartName": "Biểu đồ chỉ số", + "widgetMetricsChartDesc": "Biểu đồ lịch sử CPU, bộ nhớ, ổ đĩa hoặc mạng cho một máy chủ", "widgetHostGridName": "Lưới máy chủ", - "widgetHostGridDesc": "Hiển thị dạng lưới trạng thái máy chủ SSH", - "widgetAlertFeedName": "Thông báo", - "widgetAlertFeedDesc": "Thông báo khẩn cấp trực tiếp kèm xác nhận hỗ trợ", - "widgetPingStatusName": "Trạng thái Ping", + "widgetHostGridDesc": "Hiển thị trạng thái máy chủ SSH dạng lưới", + "widgetAlertFeedName": "Luồng cảnh báo", + "widgetAlertFeedDesc": "Cảnh báo trực tiếp kèm hỗ trợ xác nhận", + "widgetPingStatusName": "Trạng thái ping", "widgetPingStatusDesc": "Trạng thái ping HTTP cho một hoặc nhiều URL", "widgetRecentActivityName": "Hoạt động gần đây", - "widgetRecentActivityDesc": "Hiển thị hoạt động gần đây của Termix dưới dạng bảng tin cuộn.", - "widgetTermixUptimeName": "Thời gian hoạt động của Termix", - "widgetTermixUptimeDesc": "Bộ đếm thời gian hoạt động trực tiếp của máy chủ Termix", + "widgetRecentActivityDesc": "Luồng cuộn hiển thị hoạt động Termix gần đây", + "widgetTermixUptimeName": "Thời gian hoạt động Termix", + "widgetTermixUptimeDesc": "Bộ đếm uptime trực tiếp cho máy chủ Termix", "widgetSystemOverviewName": "Tổng quan hệ thống", - "widgetSystemOverviewDesc": "Tổng quan về phiên bản Termix, tình trạng cơ sở dữ liệu và thời gian hoạt động.", + "widgetSystemOverviewDesc": "Phiên bản Termix, tình trạng cơ sở dữ liệu và uptime trong nháy mắt", "widgetSshQuickConnectName": "Kết nối nhanh SSH", - "widgetSshQuickConnectDesc": "Các nút bấm một lần để mở phiên SSH", + "widgetSshQuickConnectDesc": "Nút bấm một lần để mở phiên SSH", "widgetDockerActivityName": "Hoạt động Docker", - "widgetDockerActivityDesc": "Các sự kiện gần đây của container Docker trên tất cả các máy chủ", + "widgetDockerActivityDesc": "Các sự kiện container Docker gần đây trên tất cả máy chủ", "widgetCalendarName": "Lịch", - "widgetCalendarDesc": "Lịch tháng với ngày hôm nay được đánh dấu nổi bật.", + "widgetCalendarDesc": "Lịch tháng có đánh dấu ngày hôm nay", "widgetCountdownName": "Đếm ngược", - "widgetCountdownDesc": "Bộ đếm ngược đến ngày mục tiêu", + "widgetCountdownDesc": "Đồng hồ đếm ngược đến ngày mục tiêu", "widgetSearchBarName": "Thanh tìm kiếm", - "widgetSearchBarDesc": "Tiện ích tìm kiếm web nhanh", + "widgetSearchBarDesc": "Widget tìm kiếm web nhanh", "widgetTextBannerName": "Biểu ngữ văn bản", - "widgetTextBannerDesc": "Một nhãn in đậm hoặc tiêu đề phần cho khung vẽ", + "widgetTextBannerDesc": "Nhãn đậm hoặc tiêu đề phần cho canvas", "widgetImageWidgetName": "Hình ảnh", - "widgetImageWidgetDesc": "Hiển thị hình ảnh từ một URL", + "widgetImageWidgetDesc": "Hiển thị hình ảnh từ URL", "widgetMarkdownNotesName": "Ghi chú Markdown", - "widgetMarkdownNotesDesc": "Ghi chú phong phú với khả năng hiển thị Markdown nội tuyến.", - "widgetCustomApiName": "API tùy chỉnh", - "widgetCustomApiDesc": "Truy xuất và hiển thị dữ liệu từ bất kỳ API JSON nào.", - "widgetServiceGridName": "Lưới dịch vụ", - "widgetServiceGridDesc": "Một lưới các liên kết ô dịch vụ có thể cấu hình", - "widgetDashboardLinksName": "Liên kết bảng điều khiển", - "widgetDashboardLinksDesc": "Hiển thị các liên kết dịch vụ đã cấu hình của bạn từ bảng điều khiển.", - "widgetSearchLinksName": "Phím tắt tìm kiếm", - "widgetSearchLinksDesc": "Các nút tìm kiếm nhanh với chức năng nhập liệu trực tiếp", - "widgetLinkTreeName": "Cây liên kết", - "widgetLinkTreeDesc": "Các phần liên kết được nhóm lại với tiêu đề.", - "calMon": "Mo", - "calTue": "Thứ Ba", - "calWed": "Chúng tôi", - "calThu": "Th", - "calFri": "Cha", - "calSat": "Sa", - "calSun": "Su", - "startOnMonday": "Bắt đầu tuần mới vào thứ Hai", - "countdownNoDate": "Chưa có ngày mục tiêu nào được đặt ra.", - "countdownPast": "Sự kiện đã kết thúc", + "widgetMarkdownNotesDesc": "Ghi chú phong phú hiển thị markdown trực tiếp", + "widgetCustomApiName": "API Tùy chỉnh", + "widgetCustomApiDesc": "Lấy và hiển thị dữ liệu từ bất kỳ API JSON nào", + "widgetServiceGridName": "Lưới Dịch vụ", + "widgetServiceGridDesc": "Lưới các liên kết ô dịch vụ có thể cấu hình", + "widgetDashboardLinksName": "Liên kết Bảng điều khiển", + "widgetDashboardLinksDesc": "Hiển thị các liên kết dịch vụ đã cấu hình từ bảng điều khiển", + "widgetSearchLinksName": "Lối tắt tìm kiếm", + "widgetSearchLinksDesc": "Các nút lối tắt tìm kiếm nhanh với nhập liệu trực tiếp", + "widgetLinkTreeName": "Cây Liên kết", + "widgetLinkTreeDesc": "Các phần liên kết được nhóm với tiêu đề", + "calMon": "T2", + "calTue": "T3", + "calWed": "T4", + "calThu": "T5", + "calFri": "T6", + "calSat": "T7", + "calSun": "CN", + "startOnMonday": "Bắt đầu tuần từ Thứ 2", + "countdownNoDate": "Chưa đặt ngày mục tiêu", + "countdownPast": "Sự kiện đã qua", "countdownDays": "ngày", "countdownHours": "giờ", "countdownMinutes": "phút", "countdownSeconds": "giây", "countdownLabel": "Nhãn", - "countdownLabelPlaceholder": "ví dụ Ngày ra mắt", - "countdownShowDays": "Ngày hội triển lãm", - "countdownShowHours": "Giờ chiếu phim", + "countdownLabelPlaceholder": "Ví dụ: Ngày ra mắt", + "countdownShowDays": "Hiển thị ngày", + "countdownShowHours": "Hiển thị giờ", "targetDate": "Ngày mục tiêu", "searchEngine": "Công cụ tìm kiếm", "customSearchUrl": "URL tìm kiếm tùy chỉnh", "searchPlaceholder": "Tìm kiếm...", - "searchGo": "Đi", - "searchPlaceholderLabel": "Văn bản giữ chỗ", - "searchPlaceholderHint": "Văn bản hiển thị bên trong ô tìm kiếm", + "searchGo": "Tìm", + "searchPlaceholderLabel": "Văn bản gợi ý", + "searchPlaceholderHint": "Văn bản hiển thị trong ô tìm kiếm", "openInNewTab": "Mở trong tab mới", "searchQueryPlaceholder": "Nhập truy vấn...", - "noSearchShortcuts": "Không có lối tắt nào được cấu hình", - "addSearchShortcut": "Thêm lối tắt", - "fontSize": "Kích thước phông chữ", + "noSearchShortcuts": "Chưa có phím tắt nào được cấu hình", + "addSearchShortcut": "Thêm phím tắt", + "fontSize": "Cỡ chữ", "textAlign": "Căn chỉnh văn bản", - "fontWeight": "Độ dày phông chữ", - "clearColor": "Màu trong suốt", - "imageFit": "Hình ảnh vừa vặn", + "fontWeight": "Độ đậm chữ", + "clearColor": "Xóa màu", + "imageFit": "Kiểu vừa ảnh", "imageLinkUrl": "URL liên kết", - "noImage": "Chưa thiết lập URL hình ảnh.", + "noImage": "Chưa đặt URL hình ảnh", "altText": "Văn bản thay thế", "altTextPlaceholder": "Mô tả hình ảnh", - "renderMarkdown": "Hiển thị Markdown", + "renderMarkdown": "Kết xuất Markdown", "displayMode": "Chế độ hiển thị", "displayField": "Trường hiển thị", "jsonPath": "Đường dẫn JSON", "customApiLabel": "Nhãn", - "customApiLabelPlaceholder": "ví dụ Nhiệt độ", + "customApiLabelPlaceholder": "Ví dụ: Nhiệt độ", "customApiUnit": "Đơn vị", - "customApiNoUrl": "Chưa có URL API nào được cấu hình.", - "customApiError": "Không thể tải về", + "customApiNoUrl": "Chưa cấu hình URL API", + "customApiError": "Không tải được", "customApiNotArray": "Phản hồi không phải là một mảng", "addService": "Thêm dịch vụ", "showLabels": "Hiển thị nhãn", "iconSize": "Kích thước biểu tượng", - "noDashboardLinks": "Không có liên kết bảng điều khiển nào được cấu hình.", + "noDashboardLinks": "Chưa có liên kết bảng điều khiển", "noLimit": "Không giới hạn", "sectionHeading": "Tiêu đề phần", "addSection": "Thêm phần", - "compactMode": "Chế độ nhỏ gọn", + "compactMode": "Chế độ thu gọn", "showDetailed": "Hiển thị chi tiết", "selectHosts": "Chọn máy chủ", - "allHosts": "Tất cả người chủ trì", + "allHosts": "Tất cả máy chủ", "listLayout": "Danh sách", "gridLayout": "Lưới", - "terminal": "Phần cuối", + "terminal": "Terminal", "files": "Tệp", "docker": "Docker", "range15m": "15 phút", "range1h": "1 giờ", "range6h": "6 giờ", "range24h": "24 giờ", - "metricNetRx": "Tải xuống mạng", - "metricNetTx": "Tải lên mạng", - "severityFilter": "Bộ lọc mức độ nghiêm trọng", + "metricNetRx": "Tải xuống", + "metricNetTx": "Tải lên", + "severityFilter": "Bộ lọc mức độ", "filterAll": "Tất cả", "accentColor": "Màu nhấn", - "widgetSshTerminalName": "Thiết bị đầu cuối SSH", - "widgetSshTerminalDesc": "Một thiết bị đầu cuối SSH nội tuyến được kết nối với máy chủ đã được cấu hình.", - "sshTerminalNoHost": "Không có máy chủ nào được cấu hình", + "widgetSshTerminalName": "SSH Terminal", + "widgetSshTerminalDesc": "Một terminal SSH nội tuyến kết nối tới máy chủ đã cấu hình", + "sshTerminalNoHost": "Chưa cấu hình máy chủ", "sshTerminalConnect": "Kết nối", "sshTerminalAutoConnect": "Tự động kết nối khi tải", "widgetQuickConnectName": "Kết nối nhanh", - "widgetQuickConnectDesc": "Các nút khởi chạy chỉ bằng một cú nhấp chuột cho bất kỳ loại kết nối nào trên các máy chủ của bạn.", - "connectionTypes": "Các loại kết nối", - "connType_terminal": "Phần cuối", - "connType_files": "Trình quản lý tập tin", + "widgetQuickConnectDesc": "Nút khởi chạy một nhấp cho mọi loại kết nối trên các máy chủ của bạn", + "connectionTypes": "Loại kết nối", + "connType_terminal": "Terminal", + "connType_files": "Quản lý tệp", "connType_docker": "Docker", "connType_tunnel": "Đường hầm", - "connType_host-metrics": "Số liệu máy chủ", + "connType_host-metrics": "Thông số Máy chủ", "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", - "widgetFileManagerName": "Trình quản lý tập tin", - "widgetFileManagerDesc": "Trình quản lý tập tin SFTP nhúng cho máy chủ đã cấu hình", - "widgetDockerName": "Trình quản lý Docker", - "widgetDockerDesc": "Trình quản lý vùng chứa Docker nhúng cho máy chủ đã cấu hình", - "widgetTunnelName": "Quản lý đường hầm", - "widgetTunnelDesc": "Trình quản lý đường hầm SSH nhúng cho máy chủ đã cấu hình", - "widgetNoHostSelected": "Không có máy chủ nào được cấu hình" + "widgetFileManagerName": "Quản lý Tệp", + "widgetFileManagerDesc": "Trình quản lý tệp SFTP tích hợp cho máy chủ đã cấu hình", + "widgetDockerName": "Quản lý Docker", + "widgetDockerDesc": "Trình quản lý container Docker tích hợp cho máy chủ đã cấu hình", + "widgetTunnelName": "Quản lý Đường hầm", + "widgetTunnelDesc": "Trình quản lý đường hầm SSH tích hợp cho máy chủ đã cấu hình", + "widgetNoHostSelected": "Chưa cấu hình máy chủ" }, "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.", + "title": "Cấu hình Máy chủ", + "description": "Cấu hình URL máy chủ Termix để kết nối đến các dịch vụ backend 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", + "saveFailed": "Lưu cấu hình thất bại", "saveError": "Lỗi khi lưu cấu hình", "saving": "Đang lưu...", - "saveConfig": "Lưu cấu hình", + "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ủ", + "changeServer": "Đổ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).", + "allowInvalidCertificateDesc": "Chỉ 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": "Dùng Máy chủ Cục bộ", + "embeddedDesc": "Chạy Termix với máy chủ cục bộ tích hợp (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ộ", - "savedServers": "Máy chủ đã lưu", + "localServer": "Máy chủ Cục bộ", + "savedServers": "Máy chủ Đã lưu", "noSavedServers": "Không có máy chủ nào được lưu", - "removeServer": "Di dời" + "removeServer": "Xóa" + }, + "migrationNotice": { + "title": "Termix Desktop hiện đã có thể chạy độc lập.", + "body1": "Ứng dụng này trước đây kết nối trực tiếp với máy chủ Termix của bạn. Giờ đây, nó đã được thiết kế lại để hoạt động hoàn toàn độc lập, lưu trữ máy chủ và thông tin đăng nhập cục bộ để tiếp tục hoạt động ngoại tuyến. Đồng bộ hóa hai chiều với máy chủ Termix tự lưu trữ hiện là tùy chọn.", + "body2": "Máy chủ và thông tin đăng nhập hiện tại của bạn vẫn nằm trên {{url}}. Hãy bật Đồng bộ hóa từ xa và kết nối lại với máy chủ đó để khôi phục chúng và giữ cho cả hai luôn được đồng bộ trong tương lai.", + "dismiss": "Không phải bây giờ", + "setUpSync": "Thiết lập đồng bộ hóa từ xa" + }, + "remoteSync": { + "title": "Đồng bộ từ xa", + "description": "Bạn có thể tùy chọn kết nối ứng dụng máy tính để bàn này với máy chủ Termix tự lưu trữ để đồng bộ hóa máy chủ, thông tin đăng nhập và đoạn mã trên các thiết bị. Ứng dụng luôn hoạt động hoàn toàn ngoại tuyến cho dù bạn có kết nối mạng hay không.", + "notConnected": "Không kết nối", + "connected": "Đã kết nối", + "connectedTo": "Đã kết nối với {{url}}", + "lastSynced": "Lần đồng bộ cuối cùng {{time}}", + "neverSynced": "Chưa bao giờ đồng bộ", + "syncError": "Lỗi đồng bộ: {{message}}", + "needsReauth": "Đăng nhập đã hết hạn", + "connectButton": "Kết nối với máy chủ", + "disconnectButton": "Ngắt kết nối", + "syncNowButton": "Đồng bộ ngay", + "syncing": "Đang đồng bộ hóa...", + "serverUrl": "URL máy chủ", + "enterServerUrl": "Vui lòng nhập URL máy chủ", + "mustIncludeProtocol": "URL máy chủ phải bắt đầu bằng http:// hoặc https://", + "connectionTestFailed": "Không thể kết nối đến máy chủ Termix tại địa chỉ URL đó.", + "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.", + "savedServers": "Máy chủ đã lưu", + "removeServer": "Di dời", + "continueButton": "Tiếp tục", + "cancelButton": "Hủy bỏ", + "signInTitle": "Đăng nhập vào {{url}}", + "originTitle": "Nguồn gốc kết nối", + "originDescription": "Chọn nơi mặc định các kết nối SSH được tạo ra. Có thể thay đổi cài đặt này cho từng máy chủ.", + "originLocal": "Thiết bị này (mạng cục bộ)", + "originRemote": "Máy chủ từ xa", + "bannerReconnect": "Kết nối lại", + "bannerMessage": "Đồng bộ hóa từ xa cần xác thực lại." }, "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}}.", + "error": "Lỗi Kiểm tra Phiên bản", + "checkFailed": "Kiểm tra cập nhật thất bại", + "upToDate": "Ứng dụng đã là phiên bản mới nhất", + "currentVersion": "Bạn đang chạy phiên bản {{version}}", + "updateAvailable": "Có bản cập nhật", + "newVersionAvailable": "Đã có phiên bản mới! Bạn đang chạy {{current}}, nhưng đã có {{latest}}.", + "betaVersion": "Phiên bản beta", + "betaVersionDesc": "Bạn đang chạy {{current}}, 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" + "updateRequired": "Yêu cầu cập nhật" }, "common": { "close": "Đóng", - "minimize": "Giảm thiểu", + "minimize": "Thu nhỏ", "online": "Trực tuyến", "offline": "Ngoại tuyến", - "unknown": "Không rõ", + "unknown": "Không xác định", "continue": "Tiếp tục", - "maintenance": "BẢO TRÌ", - "degraded": "bị xuống cấp", + "maintenance": "Bảo trì", + "degraded": "Suy giảm", "error": "Lỗi", "warning": "Cảnh báo", - "unsavedChanges": "Thay đổi chưa được lưu", - "dismiss": "Miễn nhiệm", + "unsavedChanges": "Thay đổi chưa lưu", + "dismiss": "Bỏ qua", "loading": "Đang tải...", - "optional": "Không bắt buộc", + "optional": "Tùy chọn", "connect": "Kết nối", "copied": "Đã sao chép", "connecting": "Đang kết nối...", - "updateAvailable": "Đã có bản cập nhật", + "updateAvailable": "Có bản cập nhật", "appName": "Termix", "openInNewTab": "Mở trong tab mới", - "noReleases": "Không có bản phát hành nào", + "noReleases": "Không có bản phát hành", "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ỏ", + "newVersionAvailable": "Đã có phiên bản mới ({{version}}).", + "failedToFetchUpdateInfo": "Không thể lấy thông tin cập nhật", + "preRelease": "Tiền phát hành", + "noReleasesFound": "Không tìm thấy bản phát hành.", + "cancel": "Hủy", "username": "Tên người dùng", "login": "Đăng nhập", "logout": "Đăng xuất", "register": "Đăng ký", "password": "Mật khẩu", "confirmPassword": "Xác nhận mật khẩu", - "back": "Mặt sau", - "save": "Cứu", + "back": "Quay lại", + "save": "Lưu", "saving": "Đang lưu...", - "delete": "Xóa bỏ", + "delete": "Xóa", "rename": "Đổi tên", - "edit": "Biên tập", - "add": "Thêm vào", + "edit": "Chỉnh sửa", + "add": "Thêm", "confirm": "Xác nhận", - "no": "KHÔNG", + "no": "Không", "or": "HOẶC", - "next": "Kế tiếp", + "next": "Tiếp theo", "previous": "Trước", - "refresh": "Làm cho khỏe lại", + "refresh": "Làm mới", "language": "Ngôn ngữ", "checking": "Đang kiểm tra...", - "checkingDatabase": "Kiểm tra kết nối cơ sở dữ liệu...", + "checkingDatabase": "Đang 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…", + "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", + "remove": "Xóa", + "create": "Tạo", "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", + "copyFailed": "Không thể sao chép vào bảng tạm", + "maximize": "Phóng to", "restore": "Khôi phục", - "of": "của", + "of": "trên", "saved": "Đã lưu", "deleted": "Đã xóa", "deleteFailed": "Không thể xóa", "saveFailed": "Không thể lưu", - "required": "Yêu cầu" + "required": "Bắt buộc" }, "nav": { "home": "Trang chủ", - "terminal": "Phần cuối", + "terminal": "Terminal", "docker": "Docker", "tunnels": "Đường hầm", - "fileManager": "Trình quản lý tập tin", - "serverStats": "Số liệu máy chủ", - "hostMetrics": "Số liệu máy chủ", - "admin": "Quản trị viên", - "termixId": "NHẬN DẠNG", + "fileManager": "Quản lý tệp", + "serverStats": "Chỉ số máy chủ", + "hostMetrics": "Chỉ số máy chủ", + "admin": "Quản trị", + "termixId": "ID", "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?", + "confirmClose": "Đóng phiên đang hoạt động?", "close": "Đóng", - "cancel": "Hủy bỏ", + "cancel": "Hủy", "sshManager": "Trình quản lý SSH", "cannotSplitTab": "Không thể chia tab này", "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", + "passwordCopied": "Đã sao chép mật khẩu", + "noPasswordAvailable": "Không có mật khẩu", "failedToCopyPassword": "Không thể sao chép mật khẩu", "refreshTab": "Làm mới kết nối", "renameTab": "Đổi tên tab", - "openFileManager": "Mở trình quản lý tệp", + "openFileManager": "Mở quản lý tệp", "dashboard": "Bảng điều khiển", - "networkGraph": "Đồ thị mạng", - "tmuxMonitor": "Tmux Monitor", + "networkGraph": "Biểu đồ mạng", + "tmuxMonitor": "Giám sát Tmux", "homepage": "Trang chủ", "quickConnect": "Kết nối nhanh", "sshTools": "Công cụ SSH", "history": "Lịch sử", "sessionLogs": "Nhật ký phiên", - "hosts": "Người dẫn chương trình", - "snippets": "Những đoạn trích", + "hosts": "Máy chủ", + "snippets": "Đoạn mã", "hostManager": "Quản lý máy chủ", - "credentials": "Thông tin xác thực", + "credentials": "Thông tin đăng nhập", "connections": "Kết nối", - "alerts": "Thông báo", - "serial": "Số nối tiếp", + "alerts": "Cảnh báo", + "serial": "Nối tiếp", "roleAdministrator": "Quản trị viên", "roleUser": "Người dùng" }, "hosts": { - "hosts": "Người dẫn chương trình", + "hosts": "Máy chủ", "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ó)", + "refresh": "Làm mới", + "optional": "Tùy chọn", + "downloadSample": "Tải mẫu", + "failedToDeleteHost": "Xóa {{name}} thất bại", + "importSkipExisting": "Nhập (bỏ qua các mục đã có)", "importSSHConfig": "Nhập từ cấu hình SSH", "connectionDetails": "Chi tiết kết nối", "ssh": "SSH", "telnet": "Telnet", "remoteDesktop": "Máy tính từ xa", - "port": "Cảng", - "username": "Tên người dùng", + "port": "Cổng", + "username": "Tên đăng nhập", "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", + "editHost": "Sửa máy chủ", + "cloneHost": "Sao chép máy chủ", + "enableTerminal": "Bật Terminal", + "enableTunnel": "Bật Tunnel", + "enableFileManager": "Bật Trình Quản Lý Tệp", "enableDocker": "Bật Docker", "defaultPath": "Đường dẫn mặc định", - "connection": "Sự liên quan", + "connection": "Kết nối", "upload": "Tải lên", "authentication": "Xác thực", "password": "Mật khẩu", - "key": "Chìa khóa", - "credential": "Chứng chỉ", + "key": "Khóa", + "credential": "Thông tin xác thực", "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", + "uploadFile": "Tải tệp lên", + "tabGeneral": "Chung", "tabSsh": "SSH", - "tabTerminal": "Phần cuối", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Đường hầm", @@ -567,596 +606,619 @@ "tabTelnet": "Telnet", "tabSharing": "Chia sẻ", "tabAuthentication": "Xác thực", - "terminal": "Phần cuối", + "terminal": "Terminal", "tunnel": "Đường hầm", - "fileManager": "Trình quản lý tập tin", + "fileManager": "Trình quản lý tệp", "serverStats": "Số liệu 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": "Đã di chuyển {{count}} máy chủ đến \"{{folder}}\"", + "folderRenamed": "Đã đổi tên thư mục \"{{oldName}}\" thành \"{{newName}}\" thành công", + "failedToRenameFolder": "Không thể đổi tên thư mục", + "movedToFolder": "Đã chuyển {{count}} máy chủ vào thư mục \"{{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ờ)", + "metricsCollection": "Thu thập chỉ số", + "metricsInterval": "Khoảng thời gian thu thập chỉ số", + "metricsIntervalDesc": "Tần suất thu thập thống kê máy chủ (5s - 1h)", "behavior": "Hành vi", - "themePreview": "Xem trước giao diện", + "themePreview": "Xem trước chủ đề", "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ữ", + "fontFamily": "Phông chữ", + "fontSize": "Cỡ chữ", + "letterSpacing": "Khoảng cách ký tự", "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", + "rightClickSelectsWord": "Nhấp chuột phải chọn từ", + "fastScrollModifier": "Phím bổ trợ 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.", + "sshAgentForwarding": "Chuyển tiếp SSH Agent", + "backspaceMode": "Chế độ Backspace", + "startupSnippet": "Đoạn mã khởi động", + "selectSnippet": "Chọn đoạn mã", + "forceKeyboardInteractive": "Buộc dùng Keyboard-Interactive", + "overrideCredentialUsername": "Ghi đè tên đăng nhập thông tin xác thực", + "overrideCredentialUsernameDesc": "Sử dụng tên đăng nhập ở trên thay vì tên trong thông tin xác thực", "oidcUsernameHint": "Sử dụng $oidc.preferred_username để thay thế tên đăng nhập OIDC của bạn.", - "jumpHostChain": "Chuỗi máy chủ nhảy", - "portKnocking": "Tiếng gõ cửa cảng", + "tailscaleUsernameHint": "Đây phải là người dùng Unix mà danh tính Tailscale của bạn được cấp trong ACL SSH của tailnet, không nhất thiết phải là người dùng root.", + "jumpHostChain": "Chuỗi máy chủ trung gian", + "portKnocking": "Port Knocking", "addKnock": "Thêm cổng", "addProxyNode": "Thêm nút", - "proxyNode": "Nút ủy quyền", - "proxyType": "Loại Proxy", + "proxyNode": "Nút proxy", + "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", + "keepaliveInterval": "Khoảng thời gian Keepalive (ms)", + "moshCommand": "Lệnh MOSH", "environmentVariables": "Biến môi trường", "addVariable": "Thêm biến", "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.", + "copyTerminalUrl": "Sao chép URL Terminal", + "copyFileManagerUrl": "Sao chép URL Trình quản lý tệp", + "copyRemoteDesktopUrl": "Sao chép URL Máy tính từ xa", + "failedToConnect": "Không thể kết nối tới bảng điều khiển", "connect": "Kết nối", "disconnect": "Ngắt kết nối", - "start": "Bắt đầu", + "start": "Khởi động", "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", + "enableMetrics": "Bật chỉ số", + "bulkUpdateFailed": "Cập nhật hàng loạt không thành công", "selectAll": "Chọn tất cả", "deselectAll": "Bỏ chọn tất cả", "protocols": "Giao thức", - "secureShell": "Vỏ bảo mật", + "secureShell": "Secure Shell", "virtualNetwork": "Mạng ảo", - "unencryptedShell": "Vỏ không mã hóa", + "unencryptedShell": "Shell không mã hóa", "addressIp": "Địa chỉ / IP", - "friendlyName": "Tên thân thiện", + "friendlyName": "Tên hiển thị", "macAddress": "Địa chỉ MAC", - "wolBroadcastAddress": "Địa chỉ phát sóng của WoL", - "wolBroadcastAddressDesc": "Tùy chọn phát sóng trực tiếp cho mạng Docker/mạng định tuyến (ví dụ: 192.168.1.255). Để trống nếu muốn sử dụng 255.255.255.255.", + "wolBroadcastAddress": "Địa chỉ Broadcast WoL", + "wolBroadcastAddressDesc": "Địa chỉ broadcast hướng tùy chọn cho mạng Docker/định tuyến (ví dụ: 192.168.1.255). Để trống để sử dụng 255.255.255.255.", "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", + "privateNotes": "Ghi chú riêng tư", + "privateNotesPlaceholder": "Chi tiết về máy chủ này...", + "pinToTop": "Ghim lên đầu", + "pinToTopDesc": "Luôn hiển thị máy chủ này ở đầu danh sách", + "portKnockingSequence": "Chuỗi Port Knocking", "addKnockBtn": "Thêm Knock", - "noPortKnocking": "Không có cấu hình nào cho việc gõ cổng.", - "knockPort": "Cổng gõ", + "noPortKnocking": "Không có cấu hình port knocking.", + "knockPort": "Cổng knock", "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", + "useSocks5Proxy": "Dùng proxy SOCKS5", + "useSocks5ProxyDesc": "Định tuyến kết nối qua máy chủ proxy", + "connectionOrigin": "Nguồn gốc kết nối", + "connectionOriginDesc": "Nguồn gốc kết nối SSH của máy chủ này. Ghi đè lên cài đặt mặc định toàn cục của ứng dụng trên máy tính để bàn.", + "connectionOriginDefault": "Sử dụng mặc định", + "connectionOriginLocal": "Thiết bị này (mạng cục bộ)", + "connectionOriginRemote": "Máy chủ từ xa", + "proxyHost": "Máy chủ proxy", + "proxyPort": "Cổng proxy", + "proxyUsername": "Tên người dùng proxy", + "proxyPassword": "Mật khẩu proxy", + "proxySingleMode": "Proxy đơn", + "proxyChainMode": "Chuỗi proxy", "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.", + "jumpHostChainLabel": "Chuỗi máy chủ trung gian", + "addJumpBtn": "Thêm máy trung gian", + "noJumpHosts": "Không có máy chủ trung gian nào được cấu hình.", "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", + "storedCredential": "Thông tin xác thực đã lưu", "selectACredential": "Chọn thông tin xác thực...", "vaultProfile": "Hồ sơ người ký Vault", - "selectAVaultProfile": "Chọn một hồ sơ Vault...", - "vaultProfileHint": "Các thiết lập được lấy từ hồ sơ dùng chung; bạn sẽ đăng nhập vào Vault thông qua OIDC khi kết nối. Không có thông tin bí mật nào được lưu trữ.", + "selectAVaultProfile": "Chọn hồ sơ Vault...", + "vaultProfileHint": "Cài đặt từ hồ sơ dùng chung; bạn sẽ đăng nhập Vault qua OIDC khi kết nối. Không lưu bí mật.", "vaultNewProfile": "Hồ sơ mới", "vaultManageProfiles": "Quản lý hồ sơ Vault", - "vaultAddrLabel": "Địa chỉ kho tiền", + "vaultAddrLabel": "Địa chỉ Vault", "vaultNamespaceLabel": "Không gian tên", - "vaultOidcMountLabel": "Gắn kết xác thực OIDC", - "vaultOidcRoleLabel": "Vai trò của OIDC", - "vaultSshMountLabel": "Gắn kết bí mật SSH", - "vaultSshRoleLabel": "Vai trò người ký SSH", - "vaultValidPrincipalsLabel": "Nguyên tắc hợp lệ", + "vaultOidcMountLabel": "Điểm gắn xác thực OIDC", + "vaultOidcRoleLabel": "Vai trò OIDC", + "vaultSshMountLabel": "Điểm gắn bí mật SSH", + "vaultSshRoleLabel": "Vai trò ký SSH", + "vaultValidPrincipalsLabel": "Các danh tính hợp lệ", "vaultKeyTypeLabel": "Loại khóa tạm thời", "vaultSharedLabel": "Chia sẻ với tất cả người dùng", "vaultCreateProfile": "Tạo hồ sơ", - "vaultProfileCreated": "Hồ sơ Vault đã được tạo", - "vaultProfileSaved": "Hồ sơ Vault đã được lưu.", - "vaultProfileDeleted": "Hồ sơ Vault đã bị xóa", - "vaultProfileSaveFailed": "Không thể lưu hồ sơ Vault.", - "vaultProfileDeleteFailed": "Không thể xóa hồ sơ Vault.", + "vaultProfileCreated": "Đã tạo hồ sơ Vault", + "vaultProfileSaved": "Đã lưu hồ sơ Vault", + "vaultProfileDeleted": "Đã xóa hồ sơ Vault", + "vaultProfileSaveFailed": "Không thể lưu hồ sơ Vault", + "vaultProfileDeleteFailed": "Không thể xóa hồ sơ Vault", "vaultSaveProfile": "Lưu hồ sơ", - "vaultProfileValidationError": "Tên, địa chỉ Vault và vai trò người ký SSH là bắt buộc.", + "vaultProfileValidationError": "Tên, địa chỉ Vault và vai trò ký SSH là bắt buộc", "vaultNoProfiles": "Chưa có hồ sơ Vault nào.", - "vaultSharedBadge": "đã chia sẻ", + "vaultSharedBadge": "được chia sẻ", "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", + "keyFileLoaded": "Đã tải tệp khóa", + "keyUploadClick": "Nhấn để tải lên .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", + "keySaved": "Đã lưu khóa SSH", + "keyReplaceNotice": "dán khóa mới bên dưới để thay thế", + "keyPassphraseSaved": "Đã lưu cụm mật khẩu, nhập để thay đổi", + "replaceKey": "Thay khóa", "docsLink": "Xem tài liệu", "opksshLabel": "OPKSSH", - "opksshDesc": "Đăng nhập vào máy chủ này bằng nhà cung cấp danh tính của bạn thay vì mật khẩu hoặc khóa. Yêu cầu thiết lập OPKSSH trên máy chủ.", + "opksshDesc": "Đăng nhập vào máy chủ này bằng nhà cung cấp danh tính thay vì mật khẩu hoặc khóa. Yêu cầu OPKSSH được thiết lập trên máy chủ.", "warpgateLabel": "Cổng Warpgate", - "warpgateDesc": "Máy chủ này kết nối thông qua máy chủ proxy SSH Warpgate. Termix sẽ tự động xử lý quy trình phê duyệt dựa trên trình duyệt sau khi xác thực.", + "warpgateDesc": "Máy chủ này kết nối thông qua proxy SSH Warpgate. Termix sẽ tự động xử lý luồng phê duyệt trên trình duyệt sau khi xác thực.", "agentLabel": "SSH Agent", - "agentDesc": "Xác thực bằng cách sử dụng tác nhân SSH chạy trên máy chủ Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Tác nhân này phải đang chạy trên máy chủ lưu trữ Termix.", - "agentSocketPathLabel": "Đường dẫn ổ cắm tác nhân", - "agentSocketPathPlaceholder": "Để trống nếu muốn sử dụng SSH_AUTH_SOCK", - "agentSocketPathHint": "Để trống để hệ thống tự động nhận diện từ biến môi trường SSH_AUTH_SOCK, hoặc nhập đường dẫn socket tùy chỉnh (ví dụ: /run/user/1000/gnupg/S.gpg-agent.ssh).", + "agentDesc": "Xác thực bằng SSH agent chạy trên máy chủ Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agent phải đang chạy trên máy chạy Termix.", + "agentSocketPathLabel": "Đường dẫn socket của Agent", + "agentSocketPathPlaceholder": "Để trống để sử dụng SSH_AUTH_SOCK", + "agentSocketPathHint": "Để trống để tự động phát hiện từ biến môi trường SSH_AUTH_SOCK, hoặc nhập đường dẫn socket tùy chỉnh (ví dụ: /run/user/1000/gnupg/S.gpg-agent.ssh).", "tailscaleDeviceSelect": "Chọn thiết bị Tailscale", "tailscaleDeviceSelectPlaceholder": "Chọn thiết bị...", - "tailscaleNoApiKey": "Chưa cấu hình khóa API Tailscale. Thêm khóa này trong Cài đặt quản trị để cho phép phát hiện thiết bị.", + "tailscaleNoApiKey": "Chưa cấu hình khóa API Tailscale. Thêm một khóa trong Cài đặt Quản trị để bật khám phá thiết bị.", "tailscaleDocsLink": "Xem tài liệu", "tailscaleLoadingDevices": "Đang tải thiết bị...", - "tailscaleNoDevices": "Không tìm thấy thiết bị nào trong mạng lưới đuôi xe của bạn.", - "tailscaleDeviceAutoFill": "Việc chọn thiết bị sẽ tự động điền địa chỉ IP của máy chủ.", - "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.", - "allowLegacyAlgorithmsLabel": "Cho phép các thuật toán cũ", - "allowLegacyAlgorithmsDesc": "Kích hoạt các thuật toán SSH đã lỗi thời (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) cho các kết nối đến các thiết bị cũ không thể nâng cấp.", + "tailscaleNoDevices": "Không tìm thấy thiết bị nào trong tailnet của bạn.", + "tailscaleDeviceAutoFill": "Chọn thiết bị sẽ tự động điền địa chỉ IP máy chủ.", + "forceKeyboardInteractiveLabel": "Bắt buộc Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Buộc nhập mật khẩu thủ công ngay cả khi có khóa", + "allowLegacyAlgorithmsLabel": "Cho phép thuật toán cũ", + "allowLegacyAlgorithmsDesc": "Kích hoạt các thuật toán SSH đã lỗi thời (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) cho kết nối đến các thiết bị cũ không thể nâng cấp.", "insecure": "Không an toàn", - "terminalAppearance": "Hình thức đầu cuối", - "colorTheme": "Chủ đề màu sắc", + "terminalAppearance": "Giao diện terminal", + "colorTheme": "Chủ đề màu", "fontFamilyLabel": "Họ phông chữ", - "fontSizeLabel": "Kích thước phông chữ", + "fontSizeLabel": "Cỡ chữ", "cursorStyleLabel": "Kiểu con trỏ", - "letterSpacingPx": "Khoảng cách giữa các chữ cái (px)", + "letterSpacingPx": "Khoảng cách chữ (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.", - "backgroundImageLabel": "URL hình nền", - "backgroundImageDesc": "URL tùy chọn cho hình nền cửa sổ terminal", - "backgroundImageOpacityLabel": "Độ mờ của hình nền", - "customThemeColors": "Màu sắc tùy chỉnh", - "customThemeBackground": "Lý lịch", - "customThemeForeground": "Tiền cảnh", + "backspaceModeLabel": "Chế độ Backspace", + "cursorBlinking": "Nhấp nháy con trỏ", + "cursorBlinkingDesc": "Bật hiệu ứng nhấp nháy cho con trỏ terminal", + "rightClickSelectsWordLabel": "Nhấp chuột phải chọn từ", + "rightClickSelectsWordShortDesc": "Chọn từ dưới con trỏ khi nhấp chuột phải", + "backgroundImageLabel": "URL ảnh nền", + "backgroundImageDesc": "URL tùy chọn cho ảnh nền terminal", + "backgroundImageOpacityLabel": "Độ mờ ảnh nền", + "customThemeColors": "Màu tùy chỉnh", + "customThemeBackground": "Nền", + "customThemeForeground": "Màu chữ", "customThemeCursor": "Con trỏ", - "customThemeCursorAccent": "Dấu nhấn con trỏ", - "customThemeSelection": "Lựa chọn", + "customThemeCursorAccent": "Điểm nhấn con trỏ", + "customThemeSelection": "Vùng chọn", "customThemeAnsiColors": "Màu ANSI", "customThemeBlack": "Đen", - "customThemeRed": "Màu đỏ", - "customThemeGreen": "Màu xanh lá", - "customThemeYellow": "Màu vàng", - "customThemeBlue": "Màu xanh da trời", - "customThemeMagenta": "Màu hồng tím", - "customThemeCyan": "Xanh lam", + "customThemeRed": "Đỏ", + "customThemeGreen": "Xanh lục", + "customThemeYellow": "Vàng", + "customThemeBlue": "Xanh dương", + "customThemeMagenta": "Đỏ tươi", + "customThemeCyan": "Xanh lơ", "customThemeWhite": "Trắng", - "customThemeBrightBlack": "Màu đen sáng", - "customThemeBrightRed": "Màu đỏ tươi", - "customThemeBrightGreen": "Xanh lá cây tươi sáng", - "customThemeBrightYellow": "Màu vàng tươi", - "customThemeBrightBlue": "Màu xanh lam sáng", - "customThemeBrightMagenta": "Màu hồng tươi", - "customThemeBrightCyan": "Xanh lam sáng", + "customThemeBrightBlack": "Đen sáng", + "customThemeBrightRed": "Đỏ sáng", + "customThemeBrightGreen": "Xanh lá sáng", + "customThemeBrightYellow": "Vàng sáng", + "customThemeBrightBlue": "Xanh dương sáng", + "customThemeBrightMagenta": "Đỏ tươi sáng", + "customThemeBrightCyan": "Xanh lơ sáng", "customThemeBrightWhite": "Trắng sáng", - "customThemeResetTooltip": "Khôi phục về cài đặt mặc định", + "customThemeResetTooltip": "Đặt lại về mặc định", + "savedThemesLabel": "Chủ đề đã lưu", + "saveAsGlobalTheme": "Lưu dưới dạng Chủ đề toàn cầu", + "saveGlobalThemeNamePrompt": "Nhập tên cho chủ đề này", + "saveGlobalThemeSuccess": "Chủ đề đã được lưu", + "saveGlobalThemeError": "Không thể lưu chủ đề", + "applyGlobalThemeTooltip": "Áp dụng chủ đề này", + "deleteGlobalThemeTooltip": "Xóa chủ đề này", + "noSavedThemes": "Chưa có chủ đề nào được lưu.", "syntaxHighlightingLabel": "Tô sáng cú pháp", - "syntaxHighlightingDesc": "Tô màu cho đầu ra của thiết bị đầu cuối (lỗi, đường dẫn, địa chỉ IP, dấu thời gian)", - "syntaxHighlightingCategories": "Các danh mục nổi bật", + "syntaxHighlightingDesc": "Tô màu đầu ra terminal (lỗi, đường dẫn, IP, dấu thời gian)", + "syntaxHighlightingCategories": "Danh mục tô sáng", "syntaxHighlightingCategoriesDesc": "Chọn loại nội dung cần tô màu", - "syntaxCategoryLogLevels": "Mức độ ghi nhật ký", - "syntaxCategoryLogLevelsDesc": "lỗi, cảnh báo, thông tin, gỡ lỗi, nghiêm trọng", + "syntaxCategoryLogLevels": "Mức log", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "Đường dẫn tệp", "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "Dấu thời gian", - "syntaxCategoryTimestampsDesc": "[12:34:56], 15/01/2024", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", "syntaxCategoryIpAddresses": "Địa chỉ IP", "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "Số được đánh dấu", - "syntaxCategoryNumbersDesc": "cổng 8080, lối ra 1, trạng thái 404", + "syntaxCategoryNumbers": "Số có nhãn", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", "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.", + "scrollbackMaxLines": "Số dòng tối đa được giữ trong lịch sử", + "sshAgentForwardingLabel": "Chuyển tiếp SSH Agent", + "sshAgentForwardingShortDesc": "Chuyển khóa SSH cục bộ của bạn đến máy chủ này", "useSSHTitleLabel": "Sử dụng tiêu đề cửa sổ SSH", - "useSSHTitleDesc": "Cập nhật tiêu đề tab từ tiêu đề cửa sổ shell thay vì tên máy chủ.", - "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.", + "useSSHTitleDesc": "Cập nhật tiêu đề tab từ tiêu đề cửa sổ shell thay vì tên máy chủ", + "enableAutoMosh": "Kích hoạt Auto-Mosh", + "enableAutoMoshDesc": "Ưu tiên Mosh hơn SSH nếu có", + "enableAutoTmux": "Kích hoạt Auto-Tmux", + "enableAutoTmuxDesc": "Tự động khởi chạy hoặc kết nối phiên tmux", "enableSessionLogging": "Ghi nhật ký phiên", - "enableSessionLoggingDesc": "Ghi lại kết quả phiên làm việc trên thiết bị đầu cuối để xem lại sau này.", + "enableSessionLoggingDesc": "Ghi lại đầu ra phiên terminal để xem lại sau", + "allowSessionSharing": "Cho phép chia sẻ phiên", + "allowSessionSharingDesc": "Cho phép chia sẻ các phiên trực tiếp trên máy chủ này qua liên kết hoặc với những người dùng khác.", "enableCommandHistory": "Lịch sử lệnh", - "enableCommandHistoryDesc": "Ghi lại các lệnh đã chạy trong cửa sổ terminal này để lưu lịch sử và tự động hoàn thành.", - "linkClickBehaviorLabel": "Hành vi nhấp chuột vào liên kết", - "linkClickBehaviorDesc": "Tùy chỉnh những gì xảy ra khi bạn nhấp vào một liên kết trong cửa sổ dòng lệnh. Sử dụng 'Mặc định' để tuân theo cài đặt ở cấp độ ứng dụng.", - "linkClickBehaviorDefault": "Mặc định (theo cài đặt ứng dụng)", - "linkClickBehaviorConfirm": "Hiển thị cửa sổ bật lên để mở hoặc sao chép", + "enableCommandHistoryDesc": "Ghi lại các lệnh chạy trong terminal này để dùng cho lịch sử và tự động hoàn thành", + "linkClickBehaviorLabel": "Hành vi nhấp liên kết", + "linkClickBehaviorDesc": "Điều khiển hành động khi nhấp liên kết trong terminal. Dùng 'Mặc định' để theo cài đặt cấp ứng dụng.", + "linkClickBehaviorDefault": "Mặc định (tuân theo cài đặt ứng dụng)", + "linkClickBehaviorConfirm": "Hiển thị popup để mở hoặc sao chép", "linkClickBehaviorDirect": "Mở trực tiếp", - "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.", - "sudoPasswordAutoFillDesc": "Hãy lưu trữ mật khẩu sudo để các chỉ số máy chủ, lời nhắc trên thiết bị đầu cuối và các ứng dụng khác có thể tự động chạy các lệnh có quyền quản trị.", - "sudoPasswordLabel": "Mật khẩu Sudo", + "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", + "sudoPasswordAutoFillDesc": "Lưu trữ mật khẩu sudo để các số liệu máy chủ, lời nhắc terminal và các thành phần khác có thể chạy lệnh đặc quyền tự động.", + "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", + "noEnvVars": "Chưa cấu hình biến môi trường nào.", + "fastScrollModifierLabel": "Phím bổ trợ 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)", - "maxKeepaliveMisses": "Max Keepalive Misses", + "moshCommandLabel": "Lệnh Mosh", + "startupSnippetLabel": "Đoạn mã khởi động", + "keepaliveIntervalLabel": "Khoảng thời gian Keepalive (giây)", + "maxKeepaliveMisses": "Số lần bỏ lỡ Keepalive tối đa", "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.", + "enableTunneling": "Kích hoạt đườ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.", + "noTunnelsConfigured": "Chưa cấu hình đường hầm nào.", "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.", + "tunnelModeLocalDesc": "Chuyển tiếp một cổng cục bộ đến một cổng trên máy chủ từ xa (hoặc máy chủ có thể truy cập từ đó).", + "tunnelModeRemoteDesc": "Chuyển tiếp một cổng trên máy chủ từ xa về một cổng cục bộ trên máy của bạn.", + "tunnelModeDynamicDesc": "Tạo 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", - "endpointHostPlaceholder": "Cùng một máy chủ, máy chủ SSH hoặc máy chủ/địa chỉ IP có thể truy cập được", - "endpointPort": "Cổng điểm cuối", - "bindHost": "Liên kết máy chủ", + "endpointHost": "Máy chủ đích", + "endpointHostPlaceholder": "Cùng máy chủ, máy chủ SSH, hoặc máy chủ/IP có thể truy cập", + "endpointPort": "Cổng đích", + "bindHost": "Máy chủ bind", "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", + "autoStartDesc": "Tự động kết nối đường hầm này khi máy chủ được tải", + "tunnelConnecting": "Đang kết nối đường hầm...", + "tunnelDisconnected": "Đã ngắt kết nối đường hầm", + "failedToConnectTunnel": "Kết nối thất bại", + "failedToDisconnectTunnel": "Ngắt kết nối thất bạ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.", - "containerRuntime": "Thời gian chạy container", - "containerRuntimeDesc": "Chọn giao diện dòng lệnh (CLI) được sử dụng để quản lý container trên máy chủ này.", + "enableDockerMonitorDesc": "Giám sát và quản lý container trên máy chủ này qua Docker", + "containerRuntime": "Môi trường chạy container", + "containerRuntimeDesc": "Chọn CLI dùng để quản lý container trên máy chủ này", "containerRuntimeDocker": "Docker", - "containerRuntimePodman": "Người Pod", + "containerRuntimePodman": "Podman", "enableTmuxMonitor": "Bật Tmux Monitor", - "enableTmuxMonitorDesc": "Hiển thị máy chủ này trong Tmux Monitor và thêm các hành động tmux của nó vào thanh bên.", + "enableTmuxMonitorDesc": "Hiển thị máy chủ này trong Tmux Monitor và thêm các hành động tmux của nó vào thanh bên", "tabProxmox": "Proxmox", "proxmoxIntegration": "Tích hợp Proxmox", - "enableProxmox": "Kích hoạt Proxmox", - "enableProxmoxDesc": "Đánh dấu máy chủ này là một nút Proxmox. Cho phép phát hiện và nhập máy ảo khách trực tiếp từ máy chủ này.", + "enableProxmox": "Bật Proxmox", + "enableProxmoxDesc": "Đánh dấu máy chủ này là một node Proxmox. Cho phép khám phá và nhập khách trực tiếp từ máy chủ này.", "proxmoxDefaultAuthType": "Loại xác thực mặc định", - "proxmoxDefaultAuthTypeDesc": "Phương thức xác thực được áp dụng cho các khách mời đã nhập. Hãy chọn loại xác thực phù hợp với cách bạn kết nối với khách của mình.", + "proxmoxDefaultAuthTypeDesc": "Phương thức xác thực áp dụng cho các máy khách được nhập. Chọn loại xác thực phù hợp với cách bạn kết nối đến máy khách của mình.", "authTypePassword": "Mật khẩu", "authTypeKey": "Khóa SSH", - "authTypeCredential": "Chứng chỉ", + "authTypeCredential": "Thông tin xác thực", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "Không có", + "authTypeNone": "Không", "proxmoxDefaultCredential": "Thông tin đăng nhập mặc định", - "proxmoxDefaultCredentialDesc": "Thông tin xác thực được sử dụng cho các máy chủ khách được nhập khẩu khi loại xác thực được đặt thành Thông tin xác thực.", + "proxmoxDefaultCredentialDesc": "Thông tin xác thực dùng cho máy khách được nhập khi phương thức xác thực là Credential.", "proxmoxWindowsDetection": "Phát hiện Windows / RDP", - "proxmoxWindowsDetectionDesc": "Các mẫu tên được phân tách bằng dấu phẩy sẽ kích hoạt RDP thay vì SSH (không phân biệt chữ hoa chữ thường)", + "proxmoxWindowsDetectionDesc": "Các mẫu tên cách nhau bằng dấu phẩy để kích hoạt RDP thay vì SSH (không phân biệt hoa thường).", "proxmoxDockerDetection": "Phát hiện Docker", - "proxmoxDockerDetectionDesc": "Các mẫu tên được phân tách bằng dấu phẩy cho phép Docker khớp với các máy khách.", + "proxmoxDockerDetectionDesc": "Các mẫu tên cách nhau bằng dấu phẩy để bật Docker cho các máy khách phù hợp.", "proxmoxPreferredRanges": "Dải IP ưu tiên", - "proxmoxPreferredRangesDesc": "Các tiền tố được phân tách bằng dấu phẩy theo thứ tự ưu tiên để lựa chọn địa chỉ IP khi khách có nhiều giao diện.", - "proxmoxAutoSync": "Tự động đồng bộ khách", - "proxmoxAutoSyncDesc": "Định kỳ tìm kiếm nút Proxmox này và tạo hoặc cập nhật các máy chủ khách đã nhập trong khi phiên làm việc của bạn đang được mở khóa.", - "proxmoxSyncInterval": "Khoảng thời gian đồng bộ (phút)", - "proxmoxSyncIntervalDesc": "Tối thiểu 5 phút. Trình lập lịch sẽ bỏ qua các phiên người dùng bị khóa.", - "proxmoxMarkMissing": "Đánh dấu những vị khách vắng mặt", - "proxmoxMarkMissingDesc": "Thêm thẻ proxmox-missing khi khách ảo được nhập trước đó biến mất thay vì xóa nó.", - "proxmoxLastSync": "Lần đồng bộ cuối cùng", - "proxmoxLastSyncNever": "Chưa được đồng bộ hóa", - "proxmoxLastSyncNoResult": "Không có thông tin chi tiết về kết quả.", - "proxmoxLastSyncSummary": "{{created}} đã tạo, {{updated}} đã cập nhật, {{markedMissing}} bị thiếu, {{skipped}} đã bỏ qua", + "proxmoxPreferredRangesDesc": "Các tiền tố cách nhau bằng dấu phẩy theo thứ tự ưu tiên để chọn IP khi máy khách có nhiều giao diện mạng.", + "proxmoxAutoSync": "Tự động đồng bộ máy khách", + "proxmoxAutoSyncDesc": "Định kỳ khám phá nút Proxmox này và tạo hoặc cập nhật máy khách đã nhập khi phiên của bạn được mở khóa.", + "proxmoxSyncInterval": "Khoảng đồng bộ (phút)", + "proxmoxSyncIntervalDesc": "Tối thiểu 5 phút. Bộ lập lịch bỏ qua các phiên người dùng bị khóa.", + "proxmoxMarkMissing": "Đánh dấu máy khách bị thiếu", + "proxmoxMarkMissingDesc": "Thêm thẻ proxmox-missing khi máy khách đã nhập trước đó biến mất thay vì xóa nó.", + "proxmoxLastSync": "Lần đồng bộ cuối", + "proxmoxLastSyncNever": "Chưa đồng bộ", + "proxmoxLastSyncNoResult": "Không có chi tiết kết quả", + "proxmoxLastSyncSummary": "Đã tạo {{created}}, cập nhật {{updated}}, thiếu {{markedMissing}}, bỏ qua {{skipped}}", "proxmoxLastSyncStatus": { "success": "Thành công", "error": "Thất bại", - "pending": "Chưa giải quyết" + "pending": "Đang chờ" }, - "proxmoxDiscoverAction": "Khám phá và nhập khách Proxmox", + "proxmoxDiscoverAction": "Khám phá và nhập máy khách Proxmox", "proxmoxImportTitle": "Nhập từ Proxmox", "proxmoxSelectHost": "Chọn máy chủ Proxmox…", - "proxmoxDiscover": "Phát hiện", - "proxmoxDiscovering": "Khám phá…", - "proxmoxDiscoverGuests": "Khám phá khách mời", - "proxmoxGuestsSelected_one": "{{count}} khách — {{selected}} đã chọn", - "proxmoxGuestsSelected_other": "{{count}} khách — {{selected}} đã chọn", + "proxmoxDiscover": "Khám phá", + "proxmoxDiscovering": "Đang khám phá…", + "proxmoxDiscoverGuests": "Khám phá máy khách", + "proxmoxGuestsSelected_one": "{{count}} máy khách — đã chọn {{selected}}", + "proxmoxGuestsSelected_other": "{{count}} máy khách — đã chọn {{selected}}", "proxmoxSelectAll": "Chọn tất cả", "proxmoxDeselectAll": "Bỏ chọn tất cả", - "proxmoxNoGuests": "Không tìm thấy khách nào trên máy chủ Proxmox này.", + "proxmoxNoGuests": "Không tìm thấy máy khách nào trên nút Proxmox này.", "proxmoxImportButton_one": "Nhập {{count}} máy chủ", "proxmoxImportButton_other": "Nhập {{count}} máy chủ", - "proxmoxResultImported": "{{count}} đã nhập", - "proxmoxResultUpdated": "{{count}} đã cập nhật", + "proxmoxResultImported": "Đã nhập {{count}}", + "proxmoxResultUpdated": "Đã cập nhật {{count}}", "proxmoxResultFailed": "{{count}} thất bại", - "proxmoxImportComplete": "Quá trình nhập Proxmox hoàn tất: {{summary}}", - "proxmoxDiscoveryFailed": "Quá trình khám phá thất bại", - "proxmoxImportFailed": "Nhập khẩu thất bại", - "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 thông qua SFTP.", - "scpLegacyLabel": "Chế độ kế thừa SCP", - "scpLegacyDesc": "Hãy sử dụng phương thức truyền tập tin cũ cho các máy chủ không hỗ trợ hệ thống con SFTP (ví dụ: máy chủ SSH nhúng hoặc tối giản).", + "proxmoxResultSkippedNoIp": "{{count}} đã bỏ qua (không tìm thấy địa chỉ IP)", + "proxmoxImportComplete": "Hoàn tất nhập từ Proxmox: {{summary}}", + "proxmoxDiscoveryFailed": "Khám phá thất bại", + "proxmoxImportFailed": "Nhập thất bại", + "enableFileManagerMonitor": "Bật Trình quản lý tập tin", + "enableFileManagerMonitorDesc": "Duyệt và quản lý tập tin trên máy này qua SFTP", + "scpLegacyLabel": "Chế độ SCP cũ", + "scpLegacyDesc": "Sử dụng truyền tập tin kiểu cũ cho các máy chủ không hỗ trợ hệ thống con SFTP (ví dụ: máy chủ SSH nhúng hoặc tối thiểu).", "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.", + "fileManagerPathHint": "Thư mục sẽ mở khi khởi chạy Trình quản lý tập tin cho máy 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", + "enableStatusChecks": "Bật Kiểm tra trạng thái", + "enableStatusChecksDesc": "Định kỳ ping máy này để xác minh tính khả dụng", + "useGlobalInterval": "Dùng khoảng thời gian toàn cục", "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", + "checkIntervalS": "Khoảng kiểm tra (giây)", + "checkIntervalDesc": "Số giây giữa mỗi lần ping kết nối", "metricsCollectionLabel": "Thu thập số liệu", - "enableMetricsLabel": "Bật số liệu", - "enableMetricsDesc": "Thu thập thông tin về CPU, RAM, ổ đĩa và các chỉ số khác 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ị", - "widgetsMovedToHostMetrics": "Giờ đây, các thẻ được thêm, sắp xếp và thay đổi kích thước trực tiếp trong tab Thống kê máy chủ. Mở Thống kê máy chủ cho máy chủ này và sử dụng Tùy chỉnh để chọn các thẻ được 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", + "enableMetricsLabel": "Bật thu thập số liệu", + "enableMetricsDesc": "Thu thập CPU, RAM, đĩa và các số liệu khác từ máy này", + "useGlobalMetrics": "Dùng khoảng thời gian toàn cục", + "useGlobalMetricsDesc": "Ghi đè bằng khoảng thời gian thu thập số liệu toàn máy chủ", + "metricsIntervalS": "Khoảng thu thập số liệu (giây)", + "metricsIntervalDesc2": "Số giây giữa các lần chụp số liệu", + "visibleWidgets": "Widget hiển thị", + "widgetsMovedToHostMetrics": "Các thẻ hiện được thêm, sắp xếp và thay đổi kích thước trực tiếp trong tab Số liệu máy. Mở Số liệu máy cho máy này và dùng Tùy chỉnh để chọn thẻ hiển thị.", + "cpuUsageLabel": "Sử dụng CPU", + "cpuUsageDesc": "Phần trăm CPU, tải trung bình, biểu đồ sparkline", + "memoryLabel": "Sử dụng bộ nhớ", + "memoryDesc": "Sử dụng RAM, swap, cache", + "storageLabel": "Sử dụng đĩa", + "storageDesc": "Sử dụng đĩa theo đ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", + "uptimeDesc": "Thời gian hoạt động và thời điểm 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", + "systemInfoDesc": "Hệ điều hành, nhân, tên máy, 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", + "topProcessesLabel": "Tiến 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", + "listeningPortsLabel": "Cổng đang lắng nghe", + "listeningPortsDesc": "Cổng mở với tiến 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ụ Host Metrics để 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.", + "quickActionsLabel": "Hành động nhanh", + "quickActionsToolbar": "Hành động nhanh xuất hiện dưới dạng nút trên thanh công cụ Chỉ số máy để thực thi lệnh một lần nhấp.", + "noQuickActions": "Chưa có hành động nhanh 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ỏ", + "hostSharedSuccessfully": "Đã chia sẻ máy thành công", + "failedToShareHost": "Chia sẻ máy thất bại", + "accessRevoked": "Đã thu hồi quyền truy cập", + "failedToRevokeAccess": "Thu hồi quyền truy cập thất bại", + "cancelBtn": "Hủy", "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", - "credentialNameRequired": "Vui lòng nhập tên cho thông tin xác thực.", - "credentialAuthRequired": "Thêm mật khẩu, khóa SSH hoặc cả hai.", - "backToHosts": "Trở lại trang Chủ nhà", - "backToCredentials": "Trở lại trang Thông tin đăng nhập", + "addHostBtn": "Thêm máy", + "hostUpdated": "Đã cập nhật máy", + "hostCreated": "Đã tạo máy", + "failedToSave": "Lưu máy thất bại", + "credentialUpdated": "Đã cập nhật thông tin xác thực", + "credentialCreated": "Đã tạo thông tin xác thực", + "failedToSaveCredential": "Lưu thông tin xác thực thất bại", + "credentialNameRequired": "Vui lòng nhập tên cho thông tin xác thực", + "credentialAuthRequired": "Thêm mật khẩu, khóa SSH, hoặc cả hai", + "createCredentialFromHostBtn": "Tạo thông tin xác thực", + "createCredentialFromHostTitle": "Tạo thông tin xác thực từ máy chủ", + "createCredentialFromHostDesc": "Tạo một mục thông tin đăng nhập có thể tái sử dụng và chia sẻ, được điền sẵn tên người dùng, mật khẩu và/hoặc khóa SSH hiện tại của máy chủ này.", + "backToHosts": "Quay lại Máy chủ", + "backToCredentials": "Quay lại Chứng thực", "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", + "noHostsFound": "Không tìm thấy máy chủ", + "tryDifferentTerm": "Thử từ khóa khác", + "addFirstHost": "Thêm máy chủ đầu tiên để bắt đầu", + "noCredentialsFound": "Không tìm thấy thông tin xác thực", + "addCredentialBtn": "Thêm Chứng thực", + "updateCredentialBtn": "Cập nhật Chứng thực", + "features": "Tính nă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 khẩu (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", + "deleteSelected": "Xóa", + "exitSelection": "Thoát chọn", + "importSkip": "Nhập (bỏ qua mục đã có)", + "importOverwrite": "Nhập (ghi đè)", + "collapseBtn": "Thu gọn", + "importExportBtn": "Nhập / Xuất", + "hostStatusesRefreshed": "Đã làm mới trạng thái máy chủ", "failedToRefreshHosts": "Không thể làm mới máy chủ", - "movedHostTo": "Đã chuyển {{host}} sang \"{{folder}}\"", + "movedHostTo": "Đã di chuyển {{host}} vào \"{{folder}}\"", "failedToMoveHost": "Không thể di chuyển máy chủ", - "folderRenamedTo": "Thư mục được đổi tên thành \"{{name}}\"", + "folderRenamedTo": "Đã đổi tên thư mục 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.", + "deleteAllInFolder": "Xóa tất cả máy chủ trong \"{{name}}\"? Hành động này không thể hoàn tác.", "folderPickerPlaceholder": "Không có thư mục", - "folderPickerSearch": "Tìm kiếm hoặc tạo (sử dụng / cho thư mục con)...", + "folderPickerSearch": "Tìm kiếm hoặc tạo mới (dùng / cho thư mục con)...", "folderPickerNone": "Không có thư mục", "folderPickerCreate": "Tạo \"{{path}}\"", "folderPickerEmpty": "Không tìm thấy thư mục phù hợp", "newFolder": "Thư mục mới", "createFolderTitle": "Tạo thư mục", - "editFolderTitle": "Chỉnh sửa thư mục", - "folderDialogDescription": "Chọn tên, màu sắc và biểu tượng. Sử dụng / để lồng các thư mục.", + "editFolderTitle": "Sửa thư mục", + "folderDialogDescription": "Chọn tên, màu sắc và biểu tượng. Dùng / để lồng thư mục.", "folderNameLabel": "Tên thư mục", - "folderNamePlaceholder": "Ví dụ: Sản xuất/Web", - "folderNestingHint": "Sử dụng dấu gạch chéo / để phân tách các cấp độ và tạo thư mục lồng nhau.", - "folderColor": "Màu sắc", + "folderNamePlaceholder": "VD: Production/Web", + "folderNestingHint": "Sử dụng / để phân cách các cấp và tạo thư mục lồng.", + "folderColor": "Màu", "folderIcon": "Biểu tượng", + "folderCredential": "Chứng chỉ", + "folderCredentialNone": "Chưa được cấp chứng chỉ.", + "folderCredentialHint": "Các máy chủ trong thư mục này sử dụng phương thức xác thực \"Thông tin đăng nhập đã lưu\" mà không chọn thông tin đăng nhập riêng sẽ kế thừa thông tin đăng nhập này.", "folderPreview": "Xem trước", - "folderNameFallback": "Thư mục không có tiêu đề", + "folderNameFallback": "Thư mục không tên", "createFolderButton": "Tạo thư mục", "saveFolderButton": "Lưu thư mục", - "cancel": "Hủy bỏ", - "iconSearchPlaceholder": "Biểu tượng tìm kiếm...", - "openAllSessions": "Mở tất cả các phiên", + "cancel": "Hủy", + "iconSearchPlaceholder": "Tìm kiếm biểu tượng...", + "openAllSessions": "Mở tất cả phiên", "editFolder": "Chỉnh sửa thư mục", "deleteFolder": "Xóa thư mục", - "folderSaved": "Thư mục đã được lưu", + "folderSaved": "Đã lưu thư mục", "failedToSaveFolder": "Không thể lưu thư mục", "folderDeleted": "Đã xóa thư mục \"{{name}}\"", - "deleteFolderConfirm": "Xóa \"{{name}}\" và (các) máy chủ {{count}} của nó? Thao tác này không thể hoàn tác.", + "deleteFolderConfirm": "Xóa \"{{name}}\" và {{count}} máy chủ trong đó? Thao tác này không thể hoàn tác.", "failedToMoveHosts": "Không thể di chuyển máy chủ", - "expandAll": "Mở rộng tất cả các thư mục", - "collapseAll": "Thu gọn tất cả các thư mục", - "moreActions": "Hơn", + "expandAll": "Mở rộng tất cả thư mục", + "collapseAll": "Thu gọn tất cả thư mục", + "moreActions": "Khác", "groupBy": "Nhóm theo", "GroupByFolder": "Thư mục", - "GroupByTag": "Nhãn", + "GroupByTag": "Thẻ", "GroupByStatus": "Trạng thái", "GroupByProtocol": "Giao thức", "GroupByAuth": "Loại xác thực", - "groupUngrouped": "Chưa được nhóm", + "groupUngrouped": "Không nhóm", "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", - "hostMetricsUrlCopied": "URL số liệu máy chủ đã được sao chép", - "tmuxMonitorUrlCopied": "URL của Tmux Monitor đã đượ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", + "copiedToClipboard": "Đã sao chép vào bộ nhớ tạm", + "terminalUrlCopied": "Đã sao chép URL Terminal", + "fileManagerUrlCopied": "Đã sao chép URL Trình quản lý tệp", + "tunnelUrlCopied": "Đã sao chép URL Đường hầm", + "dockerUrlCopied": "Đã sao chép URL Docker", + "hostMetricsUrlCopied": "Đã sao chép URL Chỉ số máy chủ", + "tmuxMonitorUrlCopied": "Đã sao chép URL Tmux Monitor", + "rdpUrlCopied": "Đã sao chép URL RDP", + "vncUrlCopied": "Đã sao chép URL VNC", + "telnetUrlCopied": "Đã sao chép URL Telnet", + "remoteDesktopUrlCopied": "Đã sao chép URL Remote Desktop", + "expandActions": "Mở rộng thao tác", + "collapseActions": "Thu gọn thao tác", "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", + "wakeOnLanSuccess": "Đã gửi gói tin Magic đến {{name}}", + "wakeOnLanError": "Không thể gửi gói tin Magic", + "cloneHostAction": "Sao chép Host", "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", + "copyTerminalUrlAction": "Sao chép URL Terminal", + "copyFileManagerUrlAction": "Sao chép URL File Manager", + "copyTunnelUrlAction": "Sao chép URL Tunnel", "copyDockerUrlAction": "Sao chép URL Docker", - "copyHostMetricsUrlAction": "Sao chép URL số liệu máy chủ", - "copyTmuxMonitorUrlAction": "Sao chép URL của Tmux Monitor", + "copyHostMetricsUrlAction": "Sao chép URL Host Metrics", + "copyTmuxMonitorUrlAction": "Sao chép URL Tmux Monitor", "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", + "copyRemoteDesktopUrlAction": "Sao chép URL Remote Desktop", "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", - "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", + "failedToDeployKey": "Không thể triển khai khóa", + "deleteHostsConfirm": "Xóa {{count}} host{{plural}}? Hành động này không thể hoàn tác.", + "movedToRoot": "Đã chuyển về thư mục gốc", + "enableTerminalFeature": "Bật Terminal", + "disableTerminalFeature": "Tắt Terminal", + "enableFilesFeature": "Bật Files", + "disableFilesFeature": "Tắt Files", + "enableTunnelsFeature": "Bật Tunnels", + "disableTunnelsFeature": "Tắt Tunnels", "enableDockerFeature": "Bật Docker", - "disableDockerFeature": "Vô hiệu hóa Docker", - "enableProxmoxFeature": "Kích hoạt Proxmox", - "disableProxmoxFeature": "Vô hiệu hóa Proxmox", + "disableDockerFeature": "Tắt Docker", + "enableProxmoxFeature": "Bật Proxmox", + "disableProxmoxFeature": "Tắt Proxmox", "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.", + "authDetails": "Chi tiết xác thực", + "credType": "Loại", + "generateKeyPairDesc": "Tạo cặp khóa mới, khóa riêng tư và công khai sẽ được tự động điền.", "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", + "uploadFileBtn": "Tải tệp lên", + "keyPassphraseOptional": "Cụm mật khẩu khóa (Tuỳ chọn)", + "sshPublicKeyOptional": "Khóa công khai SSH (Tuỳ chọn)", + "publicKeyGenerated": "Đã tạo khóa công khai", + "failedToGeneratePublicKey": "Không thể suy ra khóa công khai", + "publicKeyCopied": "Đã sao chép khóa công khai", + "keyPairGenerated": "Đã tạo cặp khóa {{label}}", "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", + "searchCredentialsPlaceholder": "Tìm kiếm thông tin xác thực…", + "refreshBtn": "Làm mớ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", + "deleteConfirmBtn": "Xoá", + "tunnelRequirementsText": "Máy chủ SSH phải có GatewayPorts yes, AllowTcpForwarding yes, và PermitRootLogin yes được đặt trong /etc/ssh/sshd_config.", + "deleteHostConfirm": "Xoá \"{{name}}\"?", + "enableAtLeastOneProtocol": "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": "Cụm mật khẩu khóa", "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", + "authDetailsSection": "Chi tiết xác thực", + "credTypeLabel": "Loại", + "hostsTab": "Máy chủ", "credentialsTab": "Thông tin xác thực", "selectMultiple": "Chọn nhiều", "selectHosts": "Chọn máy chủ", - "connectionLabel": "Sự liên quan", + "connectionLabel": "Kết nối", "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", + "generateKeyPairDescription": "Tạo cặp khóa mới, cả khóa riêng tư và khóa công khai sẽ được tự động điền.", + "generateFromPrivateKey": "Tạo từ khóa riêng tư", + "refreshBtn2": "Làm mới", + "exitSelectionTitle": "Thoát chọn", "exportAll": "Xuất tất cả", "exportForSharing": "Xuất để chia sẻ", "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ủ...", + "addCredentialBtn2": "Thêm thông tin xác thực", + "checkingHostStatuses": "Đang 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", - "hostsShareExported": "Các máy chủ dùng chung đã đượ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", + "hostsExported": "Đã xuất máy chủ thành công", + "hostsShareExported": "Đã xuất máy chủ có thể chia sẻ thành công", + "exportFailed": "Xuất máy chủ thất bại", + "sampleDownloaded": "Đã tải tệp mẫu", + "failedToDeleteCredential2": "Xóa thông tin xác thực thất bại", "noFolderOption": "(Không có thư mục)", "nSelected": "{{count}} đã chọn", - "featuresMenu": "Đặc trưng", + "featuresMenu": "Tính năng", "moveMenu": "Di chuyển", "connectSelected": "Kết nối", - "cancelSelection": "Hủy bỏ", + "cancelSelection": "Hủy", "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.", + "targetHostLabel": "Máy chủ đích", + "selectHostOption": "Chọn một máy chủ...", + "keyDeployedSuccess": "Đã triển khai khóa thành công", + "failedToDeployKey2": "Triển khai khóa thất bại", "deletedCount": "Đã xóa {{count}} máy chủ", - "failedToDeleteCount": "Không thể xóa {{count}} máy chủ", - "duplicatedHost": "Đã sao chép \"{{name}}\"", + "failedToDeleteCount": "Xóa {{count}} máy chủ thất bại", + "duplicatedHost": "Đã trùng lặ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ả", + "friendlyNameLabel": "Tên hiển thị", + "descriptionLabel": "Mô 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...", + "loadingHosts": "Đang tải các máy chủ...", + "loadingCredentials": "Đang tải thông tin xác thực...", "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", + "noHostsMatchSearch": "Không có máy chủ nào khớ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ủ", + "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ủ", + "sortIpDesc": "Địa chỉ IP (Giảm dần)", + "sortOnlineFirst": "Trực tuyến trước", + "sortOfflineFirst": "Ngoại tuyến trước", + "sortPinnedFirst": "Đã ghim trước", + "filterHosts": "Lọc Máy chủ", "filterClearAll": "Xóa bộ lọc", "filterStatusGroup": "Trạng thái", "filterOnline": "Trực tuyến", @@ -1165,22 +1227,26 @@ "filterAuthGroup": "Loại xác thực", "filterAuthPassword": "Mật khẩu", "filterAuthKey": "Khóa SSH", - "filterAuthCredential": "Chứng chỉ", - "filterAuthNone": "Không có", + "filterAuthCredential": "Thông tin xác thực", + "filterAuthNone": "Không", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Giao thức", "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": "Tính năng", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "Trình quản lý file", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Thẻ", "shareHost": "Chia sẻ máy chủ", "shareHostTitle": "Chia sẻ: {{name}}", + "shareFolder": "Chia sẻ thư mục", + "shareFolderTitle": "Chia sẻ thư mục: {{name}}", + "folderSharedSuccessfully": "Máy chủ dùng chung {{count}} trong thư mục", + "failedToShareFolder": "Không thể chia sẻ thư mục", "sharing": { "loadError": "Không thể tải dữ liệu chia sẻ. Vui lòng thử lại.", "shareWithSection": "Chia sẻ với", @@ -1188,23 +1254,23 @@ "rolesTab": "Vai trò", "searchPlaceholder": "Tìm kiếm người dùng hoặc vai trò...", "noMatches": "Không tìm thấy kết quả phù hợp", - "permissionLevelLabel": "Cấp độ quyền hạn", + "permissionLevelLabel": "Cấp độ quyền", "levels": { "connect": { "label": "Kết nối", - "description": "Chỉ các phiên mở: terminal, máy tính từ xa, trình quản lý tập tin, đường hầm và Docker. Không có quyền truy cập vào cấu hình máy chủ." + "description": "Chỉ mở phiên: terminal, remote desktop, trình quản lý file, tunnel và Docker. Không truy cập cấu hình máy chủ." }, "view": { "label": "Xem", - "description": "Kết nối và xem cấu hình máy chủ. Thông tin bí mật sẽ không bao giờ được hiển thị." + "description": "Kết nối, cùng xem cấu hình máy chủ. Bí mật không bao giờ được hiển thị." }, "edit": { - "label": "Biên tập", - "description": "Xem và chỉnh sửa máy chủ. Có thể thay thế các thông tin bí mật nhưng không bao giờ đọc được; việc gán thông tin xác thực chỉ dành riêng cho chủ sở hữu." + "label": "Chỉnh sửa", + "description": "Xem, và sửa đổi máy chủ. Bí mật có thể được thay thế nhưng không bao giờ đọc; việc gán thông tin xác thực chỉ thuộc về chủ sở hữu." }, "manage": { "label": "Quản lý", - "description": "Chỉnh sửa, chia sẻ máy chủ với người khác, thay đổi cấp độ quyền và thu hồi quyền truy cập." + "description": "Chỉnh sửa, và chia sẻ máy chủ với người khác, thay đổi cấp độ quyền và thu hồi quyền truy cập." } }, "expiryLabel": "Hết hạn truy cập", @@ -1214,164 +1280,168 @@ "oneDay": "24 giờ", "sevenDays": "7 ngày", "thirtyDays": "30 ngày", - "custom": "Phong tục" + "custom": "Tùy chỉnh" }, - "customHoursPlaceholder": "Còn vài giờ nữa là quyền truy cập hết hạn.", + "customHoursPlaceholder": "Số giờ cho đến khi hết hạn truy cập", "shareButton": "Chia sẻ", "shareWithCount": "Chia sẻ ({{count}})", - "currentAccess": "Quyền truy cập hiện tại", - "noAccessEntries": "Máy chủ này chưa được chia sẻ.", + "currentAccess": "Truy cập hiện tại", + "noAccessEntries": "Máy chủ này chưa được chia sẻ", + "folderShareSummary": "Được chia sẻ {{shared}} trong số {{total}} máy chủ trong thư mục này", "grantedBy": "Được cấp bởi", "expires": "Hết hạn", - "expired": "Hết hạn", + "expired": "Đã hết hạn", "never": "Không bao giờ", "revoke": "Thu hồi", - "accessUpdated": "Truy cập thông tin cập nhật", - "accessUpdateFailed": "Không thể cập nhật quyền truy cập", - "sharedBadge": "Chia sẻ", - "sharedBadgeTooltip": "Được chia sẻ bởi {{owner}} (quyền truy cập{{level}})", - "viewOnlyBanner": "Máy chủ này được chia sẻ với bạn bởi {{owner}} với quyền truy cập xem. Cấu hình chỉ có thể đọc.", - "sharedEditBanner": "Máy chủ này được chia sẻ với bạn bởi {{owner}} với quyền chỉnh sửa. Các thay đổi sẽ áp dụng cho máy chủ thực; chỉ chủ sở hữu mới có thể thay đổi các tham chiếu xác thực.", - "ownerOnlyControl": "Chỉ chủ sở hữu máy chủ mới có thể thay đổi điều này." + "accessUpdated": "Đã cập nhật quyền truy cập", + "accessUpdateFailed": "Cập nhật quyền truy cập thất bại", + "sharedBadge": "Đã chia sẻ", + "sharedBadgeTooltip": "Được chia sẻ bởi {{owner}} (quyền truy cập {{level}})", + "viewOnlyBanner": "Máy chủ này được {{owner}} chia sẻ cho bạn với quyền xem. Cấu hình chỉ đọc.", + "sharedEditBanner": "Máy chủ này được {{owner}} chia sẻ cho bạn với quyền chỉnh sửa. Thay đổi áp dụng cho máy chủ thực; tham chiếu xác thực chỉ có thể được thay đổi bởi chủ sở hữu.", + "ownerOnlyControl": "Chỉ chủ sở hữu máy chủ mới có thể thay đổi" }, "guac": { - "connection": "Sự liên quan", + "connection": "Kết nối", "authentication": "Xác thực", - "storedCredential": "Thông tin đăng nhập đã lưu", - "noCredential": "Không có thông tin xác thực (thông tin xác thực trực tiếp bên dưới)", + "storedCredential": "Thông tin xác thực đã lưu", + "noCredential": "Không có thông tin xác thực (thông tin trực tiếp bên dưới)", "authMethod": "Phương thức xác thực", "authTypeDirect": "Trực tiếp", - "authTypeCredential": "Chứng chỉ", + "authTypeCredential": "Thông tin xác thực", + "authTypeNone": "Không có", + "authTypeNoneDesc": "Thông tin đăng nhập của bạn không được lưu trữ. Bạn sẽ được yêu cầu nhập tên người dùng và mật khẩu mỗi khi kết nối; chúng không được lưu lại.", "selectCredential": "Chọn thông tin 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", + "session": "Phiên làm việc", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Ghi phiên", "wakeOnLan": "Wake-on-LAN", "vncSettings": "Cài đặt VNC", - "terminalSettings": "Cài đặt thiết bị đầu cuối", - "rdpPort": "Cảng RDP", + "terminalSettings": "Cài đặt Terminal", + "rdpPort": "Cổng RDP", "username": "Tên người dùng", "password": "Mật khẩu", - "domain": "Lãnh địa", + "passwordSaved": "Mật khẩu đã được lưu, nhập để thay đổi", + "domain": "Miền", "securityMode": "Chế độ bảo mật", "colorDepth": "Độ sâu màu", "width": "Chiều rộng", "height": "Chiều cao", "dpi": "DPI", - "resizeMethod": "Phương pháp thay đổi kích thước", - "clientName": "Tên khách hàng", + "resizeMethod": "Phương thức thay đổi kích thước", + "clientName": "Tên máy khách", "initialProgram": "Chương trình ban đầu", "serverLayout": "Bố cục máy chủ", "timezone": "Múi giờ", "loadBalanceInfo": "Thông tin cân bằng tải / Cookie", - "loadBalanceInfoDesc": "Cookie RD Connection Broker dùng cho việc cân bằng tải hệ thống RDS farm (ví dụ: tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfoDesc": "Cookie RD Connection Broker dùng cho cân bằng tải cụm RDS (vd: tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "Proxy guacd", "guacdHostname": "Máy chủ guacd", - "guacdHostnamePlaceholder": "Mặc định toàn cầu", - "guacdPort": "Cảng Guacad", - "guacdProxyDesc": "Ghi đè cấu hình guacd toàn cục cho kết nối này. Để trống nếu muốn sử dụng cấu hình guacd toàn cục.", - "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", + "guacdHostnamePlaceholder": "Mặc định toàn cục", + "guacdPort": "Cổng guacd", + "guacdProxyDesc": "Ghi đè phiên bản guacd toàn cục cho kết nối này. Để trống để sử dụng guacd được cấu hình toàn cục.", + "gatewayHostname": "Tên máy chủ gateway", + "gatewayPort": "Cổng gateway", + "gatewayUsername": "Tên người dùng gateway", + "gatewayPassword": "Mật khẩu gateway", + "gatewayDomain": "Miền gateway", "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", + "arguments": "Tham số", + "normalizeLineEndings": "Chuẩn hóa kết thúc dòng", "recordingPath": "Đường dẫn ghi", "recordingName": "Tên bản ghi", "macAddress": "Địa chỉ MAC", - "broadcastAddress": "Địa chỉ phát sóng", + "broadcastAddress": "Địa chỉ broadcast", "udpPort": "Cổng UDP", - "waitTimeS": "Thời gian chờ (giây)", + "waitTimeS": "Thời gian chờ (s)", "driveName": "Tên ổ đĩa", - "drivePath": "Đường lái xe", + "drivePath": "Đường dẫn ổ đĩa", "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)", + "ignoreCertificateDesc": "Cho phép kết nối đến máy chủ có chứng chỉ tự ký", + "forceLossless": "Ép buộc Lossless", + "forceLosslessDesc": "Ép buộc mã hóa ảnh không mất dữ liệu (chất lượng cao hơn, tốn băng thông)", "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", + "disableAudioDesc": "Tắt tất cả âm thanh từ phiên từ xa", + "enableAudioInput": "Bật đầu vào âm thanh (micro)", + "enableAudioInputDesc": "Chuyển tiếp micro cục bộ đến phiên 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)", + "wallpaperDesc": "Hiển thị hình nền desktop (tắt để cải thiện hiệu suất)", "theming": "Chủ đề", - "themingDesc": "Cho phép sử dụng các chủ đề và kiểu dáng trực quan.", + "themingDesc": "Bật 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", + "fontSmoothingDesc": "Bật kết xuất phông chữ ClearType", + "fullWindowDrag": "Kéo toàn bộ cửa sổ", + "fullWindowDragDesc": "Hiển thị nội dung cửa sổ khi kéo", + "desktopComposition": "Tổ hợp màn hình nền", + "desktopCompositionDesc": "Bật hiệu ứng kính Aero", + "menuAnimations": "Hiệu ứng menu", + "menuAnimationsDesc": "Bật hiệu ứng mờ và trượt của menu", + "disableBitmapCaching": "Tắt bộ nhớ đệm bitmap", + "disableBitmapCachingDesc": "Tắt bộ nhớ đệm bitmap (có thể giúp tránh lỗi hiển thị)", + "disableOffscreenCaching": "Tắt bộ nhớ đệm ngoài màn hình", + "disableOffscreenCachingDesc": "Tắt bộ nhớ đệm ngoài màn hình", + "disableGlyphCaching": "Tắt bộ nhớ đệm glyph", + "disableGlyphCachingDesc": "Tắt bộ nhớ đệm glyph", "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.", + "enableGfxDesc": "Sử dụng pipeline đồ họa RemoteFX", + "enablePrinting": "Bật in", + "enablePrintingDesc": "Chuyển hướng máy in cục bộ đến phiên từ xa", + "enableDriveRedirection": "Bật chuyển hướng ổ đĩa", + "enableDriveRedirectionDesc": "Ánh xạ thư mục cục bộ thành ổ đĩa trong phiên 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 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", + "createDrivePathDesc": "Tự động tạo thư mục nếu chưa tồn tại", + "disableDownload": "Tắt tải xuống", + "disableDownloadDesc": "Ngăn tải file từ phiên từ xa", + "disableUpload": "Tắt tải lên", + "disableUploadDesc": "Ngăn tải file lên phiên 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.", + "enableTouchDesc": "Bật chuyển tiếp đầu vào cảm ứng", + "consoleSession": "Phiên console", + "consoleSessionDesc": "Kết nối tới console (phiên 0) thay vì 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", + "sendWolPacketDesc": "Gửi gói magic để đánh thức máy trước khi kết nối", + "disableCopy": "Tắt sao chép", + "disableCopyDesc": "Ngăn sao chép văn bản từ phiên từ xa", + "disablePaste": "Tắt dán", + "disablePasteDesc": "Ngăn dán văn bản vào phiên từ xa", + "createPathIfMissing": "Tạo đường dẫn nếu thiếu", + "createPathIfMissingDesc": "Tự động tạo thư mục ghi hình", "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).", + "excludeOutputDesc": "Không ghi hình màn hình (chỉ metadata)", "excludeMouse": "Loại trừ chuột", "excludeMouseDesc": "Không ghi lại chuyển động chuột", - "includeKeystrokes": "Bao gồm các thao tác gõ 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.", + "includeKeystrokes": "Bao gồm thao tác phím", + "includeKeystrokesDesc": "Ghi lại thao tác phím thô cùng với đầu ra 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.", + "vncLeaveBlank": "Để trống nếu không cần", "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)", + "swapRedBlue": "Hoán đổi Đỏ/Xanh", + "swapRedBlueDesc": "Hoán đổi kênh màu đỏ và xanh (khắc phục một số lỗi màu)", "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.", + "readOnlyDesc": "Xem màn hình từ xa mà không gửi đầu vào", "telnetPort": "Cổng Telnet", - "terminalType": "Loại thiết bị đầu cuối", + "terminalType": "Loại terminal", "fontName": "Tên phông chữ", - "fontSize": "Kích thước phông chữ", + "fontSize": "Cỡ chữ", "colorScheme": "Bảng màu", - "backspaceKey": "Phím xóa lùi", - "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.", - "permissionLevel": "Cấp độ quyền hạn", - "typeHeader": "Kiểu", + "backspaceKey": "Phím Backspace", + "saveHostFirst": "Lưu máy chủ trước.", + "sharingOptionsAfterSave": "Tùy chọn chia sẻ khả dụng sau khi máy chủ đã được lưu.", + "permissionLevel": "Mức quyền", + "typeHeader": "Loại", "targetHeader": "Mục tiêu", - "permissionHeader": "Sự cho phép", - "cancelBtn": "Hủy bỏ", + "permissionHeader": "Quyền", + "cancelBtn": "Hủy", "savingBtn": "Đang lưu...", "updateHostBtn": "Cập nhật máy chủ", "addHostBtn": "Thêm máy chủ" @@ -1379,416 +1449,479 @@ }, "commandPalette": { "searchPlaceholder": "Tìm kiếm máy chủ, lệnh hoặc cài đặt...", - "quickActions": "Thao tác nhanh", + "quickActions": "Hành động nhanh", "hostManager": "Quản lý máy chủ", - "hostManagerDesc": "Quản lý, thêm hoặc chỉnh sửa máy chủ", + "hostManagerDesc": "Quản lý, thêm hoặc 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", + "addCredential": "Thêm thông tin xác thực", "addCredentialDesc": "Lưu trữ khóa SSH hoặc mật khẩu", - "tmuxMonitor": "Tmux Monitor", - "tmuxMonitorDesc": "Theo dõi các phiên tmux trên các máy chủ của bạn.", + "tmuxMonitor": "Giám sát Tmux", + "tmuxMonitorDesc": "Giám sát phiên tmux trên các máy chủ", "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}}\"", + "serversAndHosts": "Máy chủ & Host", + "noHostsFound": "Không tìm thấy máy chủ nào khớp với \"{{search}}\"", "links": "Liên kết", "navigate": "Điều hướng", - "select": "Lựa chọn", + "select": "Chọn", "toggleWith": "Chuyển đổi với" }, "splitScreen": { - "paneEmpty": "Pane {{index}} - trống", - "noTabAssigned": "Không có tab nào được chỉ định", - "focusedPane": "Khung đang hoạt động" + "paneEmpty": "Ngăn {{index}} - trống", + "noTabAssigned": "Không có tab nào được gán", + "focusedPane": "Ngăn đang hoạt động" }, "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}}", + "noConnections": "Không có kết nối nào", + "noConnectionsDesc": "Mở terminal, trình quản lý tệp hoặc máy tính từ xa để xem kết nối tại đây", + "connectedFor": "Đã kết nối trong {{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", + "closeConnection": "Đóng kết nối", "forgetTab": "Quên", - "removeBackground": "Di dời", + "removeBackground": "Xóa", "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.", - "rename": "Đổi tên phiên" + "reopenTab": "Mở lại", + "sectionOpen": "Đang mở", + "sectionBackground": "Nền", + "backgroundDesc": "Phiên vẫn tồn tại trong 30 phút sau khi ngắt kết nối và có thể kết nối lại.", + "persisted": "Đã duy trì trong nền", + "expiresIn": "Hết hạn trong {{duration}}", + "search": "Tìm kiếm kết nối...", + "noSearchResults": "Không có kết nối nào khớp với tìm kiếm của bạn", + "rename": "Đổi tên phiên", + "sectionSharedWithMe": "Đã chia sẻ với tôi", + "sharedBy": "Được chia sẻ bởi {{username}}", + "join": "Tham gia", + "sharedSessionLabel": "{{hostName}} (được chia sẻ)" + }, + "sessionSharing": { + "guestView": { + "loading": "Đang kết nối đến phiên chia sẻ...", + "linkInvalid": "Liên kết chia sẻ này không hợp lệ, đã hết hạn hoặc đã bị thu hồi.", + "rateLimited": "Đã thử quá nhiều lần, vui lòng thử lại sau.", + "sessionEnded": "Phiên này đã kết thúc", + "readOnlyBadge": "Chỉ xem" + }, + "modalTitle": "Chia sẻ phiên họp", + "shareButton": "Chia sẻ", + "notReadyToShare": "Buổi học chưa sẵn sàng để chia sẻ.", + "modeTab": { + "link": "Liên kết", + "user": "Người dùng" + }, + "linkModeDescription": "Bất kỳ ai có liên kết này đều có thể tham gia, không cần tài khoản.", + "userModeDescription": "Chia sẻ với một người dùng cụ thể đã có quyền truy cập vào máy chủ này. Nếu họ chưa có quyền truy cập, hãy chia sẻ máy chủ với họ trước hoặc sử dụng liên kết thay thế. Sau khi chia sẻ, phiên làm việc sẽ xuất hiện trong tab Kết nối của họ.", + "permissionLevel": { + "label": "Cấp độ quyền hạn", + "readOnly": "Chỉ đọc", + "readOnlyDescription": "Có thể xem phiên trực tiếp nhưng không thể gõ chữ hoặc tương tác.", + "readWrite": "Đọc-viết", + "readWriteDescription": "Có thể nhập liệu và tương tác với phiên làm việc giống như chủ sở hữu." + }, + "expiryLabel": "Liên kết hết hạn", + "createLinkButton": "Tạo liên kết", + "createShareButton": "Chia sẻ với người dùng", + "searchUsersPlaceholder": "Tìm kiếm người dùng...", + "noUsersFound": "Không tìm thấy người dùng nào", + "linkCreated": "Đã tạo liên kết chia sẻ", + "linkCopied": "Liên kết đã được sao chép vào clipboard", + "copyLink": "Sao chép liên kết", + "shareCreated": "Phiên họp đã được chia sẻ. Nó sẽ xuất hiện trong tab Kết nối của họ.", + "shareFailed": "Không thể tạo chia sẻ", + "userLacksHostAccess": "Người dùng đó chưa có quyền truy cập vào máy chủ này. Hãy chia sẻ thông tin máy chủ với họ trước, hoặc sử dụng liên kết thay thế.", + "activeShares": "Cổ phiếu tích cực", + "noActiveShares": "Hiện không có cổ phiếu nào đang được giao dịch trong phiên này.", + "revoke": "Thu hồi", + "revokeConfirmTitle": "Thu hồi cổ phần này?", + "revokeConfirmDescription": "Bất kỳ ai sử dụng thư mục chia sẻ này sẽ mất quyền truy cập ngay lập tức.", + "revoked": "Cổ phần đã bị thu hồi", + "revokeFailed": "Không thể thu hồi cổ phần", + "joinCount": "{{count}} tham gia", + "joinCount_other": "{{count}} tham gia", + "expiresAt": "Hết hạn {{date}}", + "linkShareBadge": "Liên kết", + "userShareBadge": "Người dùng: {{username}}", + "loadSharesFailed": "Không thể tải các chia sẻ đang hoạt động" }, "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", + "failedToConnect": "Không lấy được mã token kết nối", "hostNotFound": "Không tìm thấy máy chủ", - "noHostSelected": "Chưa chọn máy chủ nào", + "noHostSelected": "Chưa chọn máy chủ", "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ị.", + "guacdUnavailable": "Dịch vụ máy tính từ xa (guacd) 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 trong cài đặt quản trị.", + "credentialPromptTitle": "Nhập thông tin đăng nhập RDP", + "credentialPromptDescription": "Máy chủ này được thiết lập để yêu cầu thông tin đăng nhập khi kết nối. Thông tin này chỉ được sử dụng cho phiên này và không được lưu lại.", + "connect": "Kết nối", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Tổ hợp phím Win+L (Khóa màn hình)", + "winL": "Win+L (Khóa màn hình)", "winKey": "Phím Windows", - "ctrl": "Điều khiển", + "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}} (đang giữ - nhấn để nhả)", + "stickyInactive": "{{key}} (nhấn để giữ)", + "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", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "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" + "dragHandle": "Kéo để di chuyển", + "switchToTrackpad": "Chuyển sang chế độ bàn di chuột (kéo để di chuyển con trỏ, chạm để nhấp chuột)", + "switchToTouch": "Chuyển sang chế độ cảm ứng (chạm trực tiếp vào vị trí bạn muốn nhấp chuột)" } }, "terminal": { - "connect": "Kết nối với máy chủ", - "clear": "Thông thoáng", + "connect": "Kết nối đến máy chủ", + "clear": "Xóa", "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.", - "passwordPromptFillTitle": "Nhập mật khẩu đã lưu vào ô này?", + "clipboardWriteFailed": "Sao chép vào clipboard thất bại. Đảm bảo trang được phục vụ qua HTTPS hoặc localhost.", + "clipboardReadFailed": "Đọc từ clipboard thất bại. Đảm bảo quyền truy cập clipboard đã được cấp.", + "clipboardHttpWarning": "Dán yêu cầu HTTPS. Sử dụng Ctrl+Shift+V hoặc phục vụ Termix qua HTTPS.", + "passwordPromptFillTitle": "Điền mật khẩu đã lưu vào dòng nhắc này?", "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", + "noHostSelected": "Chưa chọn máy chủ", "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ế.", + "tmuxSessionCreated": "Đã tạo phiên tmux: {{name}}", + "tmuxSessionAttached": "Đã gắn vào phiên tmux: {{name}}", + "tmuxUnavailable": "tmux chưa được cài đặt trên máy chủ từ xa, chuyển sang shell tiêu chuẩn", "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.", - "tmuxWindows": "Windows", + "tmuxSessionPickerDesc": "Các phiên tmux hiện có trên máy chủ này. Chọn một phiên để gắn lại hoặc tạo phiên mới.", + "tmuxWindows": "Cửa sổ", "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.", + "tmuxAttached": "Các máy khách đã gắn kết", + "tmuxAttachedCount": "{{count}} đã gắn kết", + "tmuxLastActivity": "Hoạt động cuối", + "tmuxTimeJustNow": "vừa xong", + "tmuxTimeMinutes": "{{count}} phút trước", + "tmuxTimeHours": "{{count}} giờ trước", + "tmuxTimeDays": "{{count}} ngày trước", + "tmuxCreateNew": "Tạo phiên mới", + "tmuxCopyHint": "Điều chỉnh lựa chọn và nhấn Enter để sao chép vào clipboard", + "tmuxDetach": "Ngắt khỏi phiên tmux", + "tmuxDetached": "Đã ngắt khỏi phiên tmux", + "maxReconnectAttemptsReached": "Đã đạt số lần 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}}", + "connectionTimeout": "Hết thời gian kết nối", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{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", + "mfaPromptRequired": "Yêu cầu xác thực", + "mfaPushRequired": "Yêu cầu xác thực đẩy", + "mfaMenuPlaceholder": "Nhập câu trả lời của bạn", + "mfaWaitingApproval": "Đang chờ phê duyệt trên thiết bị của bạn...", + "mfaSendRequest": "Gửi yêu cầu", + "warpgateAuthRequired": "Yêu cầu 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.", + "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.", + "opksshAuthDescription": "Hoàn tất xác thực trên trình duyệt để tiếp tục. Phiên làm việc 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.", + "opksshTimeout": "Xác thực đã hết thời gian chờ. Vui lòng thử lại.", + "opksshAuthFailed": "Xác thực thất bại. Vui lòng kiểm tra thông tin đăng nhập và thử lại.", "opksshSignInWith": "Đăng nhập bằng {{provider}}", - "vaultAuthTitle": "Cần đăng nhập vào Vault.", - "vaultAuthDescription": "Một cửa sổ đã mở để đăng nhập vào HashiCorp Vault. Hãy hoàn tất quá trình đăng nhập; kết nối này sẽ tự động được duy trì.", - "vaultAuthFailed": "Xác thực Vault không thành công. Vui lòng thử lại.", + "vaultAuthTitle": "Yêu cầu đăng nhập Vault", + "vaultAuthDescription": "Một cửa sổ đã mở để đăng nhập vào HashiCorp Vault. Hoàn tất đăng nhập ở đó; kết nối này sẽ tự động tiếp tục.", + "vaultAuthFailed": "Xác thực Vault thất bại. Vui lòng thử lại.", "vaultReopen": "Mở lại cửa sổ đăng nhập", "sudoPasswordPopupTitle": "Nhập mật khẩu?", "linkDialogTitle": "Mở liên kết", "linkDialogOpen": "Mở", "linkDialogCopy": "Sao chép", - "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ủ.", + "websocketAbnormalClose": "Kết nối đóng đột ngột. Có thể do sự cố cấu hình reverse proxy 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.", + "connectionLogEmpty": "Chưa có nhật ký kết nối", "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...", + "connectionLogCopied": "Đã sao chép nhật ký kết nối vào clipboard", + "connectionLogCopyFailed": "Sao chép nhật ký vào clipboard thất bại", + "connectionRejected": "Kết nối bị máy chủ từ chối. Vui lòng kiểm tra xác thực và cấu hình mạng.", + "hostKeyRejected": "Xác minh khóa máy chủ SSH bị từ chối. Đã hủy kết nối.", + "sessionTakenOver": "Phiên đã được mở trong tab khác. Đang kết nối lại...", "split": { "splitTab": "Chia tab", "addToSplit": "Thêm vào phần chia", - "removeFromSplit": "Xóa khỏi phần Chia" + "removeFromSplit": "Xóa khỏi phần chia" } }, "fileManager": { - "noHostSelected": "Chưa chọn máy chủ nào", + "noHostSelected": "Chưa chọn máy chủ", "initializingEditor": "Đang khởi tạo trình soạn thảo...", - "file": "Tài liệu", + "file": "Tệp", "folder": "Thư mục", - "uploadFile": "Tải lên tệp", + "uploadFile": "Tải tệp lên", "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ữ...", + "extractArchive": "Giải nén lưu trữ", + "extractingArchive": "Đang giải nén {{name}}...", + "archiveExtractedSuccessfully": "{{name}} đã được giải nén thành công", + "extractFailed": "Giải nén thất bại", + "compressFile": "Nén tập tin", + "compressFiles": "Nén tập tin", + "compressFilesDesc": "Nén {{count}} mục vào một tệp nén", + "archiveName": "Tên tệp lưu trữ", + "enterArchiveName": "Nhập tên tệp 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...", + "selectedFiles": "Tập tin đã chọn", + "andMoreFiles": "và {{count}} tập tin khác...", "compress": "Nén", - "compressingFiles": "Nén {{count}} mục thành {{name}}...", + "compressingFiles": "Đang nén {{count}} mục vào {{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", + "compressFailed": "Nén thất bại", + "edit": "Chỉnh sửa", "preview": "Xem trước", "previous": "Trước", - "next": "Kế tiếp", - "pageXOfY": "Trang {{current}} trên {{total}}", + "next": "Tiếp", + "pageXOfY": "Trang {{current}} / {{total}}", "zoomOut": "Thu nhỏ", "zoomIn": "Phóng to", - "newFile": "Tệp mới", + "newFile": "Tập tin 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", + "fileName": "Tên tập tin", "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", + "fileUploadedSuccessfully": "Tập tin \"{{name}}\" đã được tải lên thành công", + "failedToUploadFile": "Tải tập tin lên thất bại", + "fileDownloadedSuccessfully": "Tập tin \"{{name}}\" đã được tải xuống thành công", + "failedToDownloadFile": "Tải tập tin xuống thất bại", + "fileCreatedSuccessfully": "Tập tin \"{{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", + "operationFailed": "Thao tác {{operation}} thất bại đối với {{name}}: {{error}}", + "failedToResolveSymlink": "Không thể theo dõi symlink", + "itemsDeletedSuccessfully": "Đã xóa thành công {{count}} mục", "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", + "sudoPasswordRequired": "Yêu cầu mật khẩu quản trị", + "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 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...", + "searchFiles": "Tìm kiếm tệp...", "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.", + "selectHostToStart": "Chọn máy chủ để bắt đầu quản lý tệp", + "sshRequiredForFileManager": "Trình quản lý tệp yêu cầu SSH. Máy chủ này không bật SSH.", + "failedToConnect": "Kết nối SSH thất bại", "failedToLoadDirectory": "Không thể tải thư mục", - "noSSHConnection": "Không có kết nối SSH nào khả dụng", + "noSSHConnection": "Không có kết nối SSH", "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}}", + "copyPaths": "Sao chép các đường dẫn", + "delete": "Xóa", + "properties": "Thuộc tính", + "refresh": "Làm mới", + "downloadFiles": "Tải {{count}} tệp xuống trình duyệt", + "copyFiles": "Sao chép {{count}} mục", + "cutFiles": "Cắt {{count}} mục", "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", + "filesCopiedToClipboard": "Đã sao chép {{count}} mục vào clipboard", + "filesCutToClipboard": "Đã cắt {{count}} mục vào clipboard", + "pathCopiedToClipboard": "Đã sao chép đường dẫn vào clipboard", + "pathsCopiedToClipboard": "Đã sao chép {{count}} đường dẫn vào clipboard", "failedToCopyPath": "Không thể sao chép đường dẫn vào clipboard", - "copyFolderLink": "Sao chép liên kết vào thư mục", - "copyCurrentFolderLink": "Sao chép liên kết vào thư mục hiện tại", - "folderLinkCopied": "Liên kết thư mục đã được sao chép vào clipboard", - "failedToCopyFolderLink": "Không thể sao chép liên kết thư mục.", + "copyFolderLink": "Sao chép liên kết đến thư mục", + "copyCurrentFolderLink": "Sao chép liên kết đến thư mục hiện tại", + "folderLinkCopied": "Đã sao chép liên kết thư mục vào clipboard", + "failedToCopyFolderLink": "Không thể sao chép liên kết thư mục", "movedItems": "Đã di chuyển {{count}} mục", "failedToDeleteItem": "Không thể xóa mục", - "itemRenamedSuccessfully": "{{type}} đã đổi tên thành công", + "itemRenamedSuccessfully": "Đã đổi tên {{type}} thành công", "failedToRenameItem": "Không thể đổi tên mục", "download": "Tải xuống", - "openExternalEditor": "Mở từ bên ngoài", - "chooseExternalEditor": "Chọn trình chỉnh sửa bên ngoài", - "externalEditorSelected": "Đã chọn trình chỉnh sửa bên ngoài", - "externalEditorDesktopOnly": "Trình chỉnh sửa bên ngoài chỉ khả dụng trên ứng dụng dành cho máy tính để bàn.", - "externalEditorOpened": "Đã mở bằng trình chỉnh sửa bên ngoài. Việc lưu ở đó sẽ tải các thay đổi trở lại máy chủ.", - "failedToOpenExternalEditor": "Không thể mở trình chỉnh sửa bên ngoài", - "failedToSelectExternalEditor": "Không thể chọn trình chỉnh sửa bên ngoài", - "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", + "openExternalEditor": "Mở bên ngoài", + "chooseExternalEditor": "Chọn trình soạn thảo bên ngoài", + "externalEditorSelected": "Đã chọn trình soạn thảo bên ngoài", + "externalEditorDesktopOnly": "Trình soạn thảo bên ngoài chỉ khả dụng trong ứng dụng desktop", + "externalEditorOpened": "Đã mở bằng trình soạn thảo bên ngoài. Lưu ở đó sẽ tải các thay đổi lên máy chủ.", + "failedToOpenExternalEditor": "Không thể mở trình soạn thảo bên ngoài", + "failedToSelectExternalEditor": "Không thể chọn trình soạn thảo bên ngoài", + "permissions": "Quyền", + "size": "Kích thước", + "modified": "Đã sửa", + "path": "Đường dẫn", + "confirmDelete": "Bạn có chắc chắn muốn xóa {{name}}?", + "permissionDenied": "Quyền bị từ chối", "serverError": "Lỗi máy chủ", - "fileSavedSuccessfully": "Tệp đã được lưu thành công", + "fileSavedSuccessfully": "Đã lưu tệp 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ủ.", + "confirmDeleteSingleItem": "Bạn có chắc chắn muốn xóa vĩnh viễn \"{{name}}\"?", + "confirmDeleteMultipleItems": "Bạn có chắc chắn muốn xóa vĩnh viễn {{count}} mục?", + "confirmDeleteMultipleItemsWithFolders": "Bạn có chắc chắn muốn xóa vĩnh viễn {{count}} mục? Điều này bao gồm các thư mục và nội dung của 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 của nó?", + "permanentDeleteWarning": "Hành động này không thể hoàn tá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", + "folderShortcuts": "Lối tắt thư mục", + "failedToReconnectSSH": "Không thể kết nối lại phiên SSH", + "openTerminalHere": "Mở terminal 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", + "openTerminalInFolder": "Mở terminal trong thư mục này", + "openTerminalInFileLocation": "Mở terminal 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", + "onlyRunExecutableFiles": "Chỉ chạy được tệp thực thi", + "directories": "Thư mục", + "removedFromRecentFiles": "Đã xóa \"{{name}}\" khỏi tệp gần đây", + "removeFailed": "Xóa thất bại", + "unpinnedSuccessfully": "Đã bỏ ghim \"{{name}}\" thành công", + "unpinFailed": "Bỏ ghim thất bại", "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", + "removeShortcutFailed": "Xóa lối tắt thất bại", + "clearedAllRecentFiles": "Đã xóa toàn bộ 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", + "clearAllRecentFiles": "Xóa toàn bộ tệp gần đây", + "unpinFile": "Bỏ ghim tệp", "removeShortcut": "Xóa lối tắt", - "pinFile": "Tệp ghim", + "pinFile": "Ghim tệp", "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ủ.", + "noUndoableActions": "Không có hành động hoàn tác được", + "undoCopySuccess": "Đã hoàn 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 tệp đã 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 di chuyển: Đã di chuyển {{count}} tệp về vị trí ban đầu", + "undoMoveFailedMove": "Hoàn tác thất bại: Không thể di chuyển tệp về vị trí cũ", + "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 xóa: 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...", + "find": "Tìm kiếm...", "replace": "Thay thế", - "downloadInstead": "Tải xuống thay vì", + "downloadInstead": "Tải xuống thay thế", "keyboardShortcuts": "Phím tắt", - "searchAndReplace": "Tìm kiếm và thay thế", + "searchAndReplace": "Tìm & 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", + "findNext": "Tìm tiếp", + "findPrevious": "Tìm trước", + "save": "Lưu", "selectAll": "Chọn tất cả", "undo": "Hoàn tác", "redo": "Làm lại", - "moveLineUp": "Di chuyển hàng", + "moveLineUp": "Di chuyển dòng lên", "moveLineDown": "Di chuyển dòng xuống", - "toggleComment": "Ẩn/Hiện bình luận", + "toggleComment": "Chuyển đổi chú thích", "autoComplete": "Tự động hoàn thành", - "imageLoadError": "Không thể tải hình ảnh", - "startTyping": "Bắt đầu gõ...", + "imageLoadError": "Lỗi tải ảnh", + "startTyping": "Bắt đầu nhập...", "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}}", + "fileIsEmpty": "Tệp trống", + "largeFileWarning": "Cảnh báo tệp lớn", + "largeFileWarningDesc": "Tệp này có kích thước {{size}}, có thể gây ra vấn đề hiệu suất khi mở dưới dạng văn bản.", + "fileNotFoundAndRemoved": "Tệp \"{{name}}\" không tìm thấy và đã bị xóa khỏi tệp gần đây/đã ghim", + "failedToLoadFile": "Lỗi 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", + "autoSaveFailed": "Tự động lưu không thành công", "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", + "moveOperationFailed": "Thao tác di chuyển không thành công", "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", + "comparingFiles": "Đang so sánh tệp: {{file1}} và {{file2}}", + "dragFailed": "Thao tác kéo không thành công", "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", + "fileUnpinnedSuccessfully": "Tệp \"{{name}}\" đã được bỏ 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", + "operationCompletedSuccessfully": "Đã {{operation}} thành công {{count}} mục", + "operationCompleted": "Đã {{operation}} {{count}} mục", + "downloadFileSuccess": "Đã tải xuống tệp {{name}} thành công", "downloadFileFailed": "Tải xuống thất bại", - "moveTo": "Di chuyển đến {{name}}", + "moveTo": "Di chuyển tới {{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)", + "dragOutsideToDownload": "Kéo ra ngoài cửa sổ để tải xuống ({{count}} tệp)", "newFolderDefault": "Thư mục mới", - "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Đã chuyển thành công {{count}} mục đến {{target}}", + "newFileDefault": "Tệp mới.txt", + "successfullyMovedItems": "Đã di chuyển thành công {{count}} mục tới {{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", - "decreaseFontSize": "Giảm kích thước phông chữ", - "increaseFontSize": "Tăng kích thước phông chữ", - "startWritingMarkdown": "Hãy bắt đầu viết nội dung bằng định dạng Markdown...", + "searchInFile": "Tìm trong tệp (Ctrl+F)", + "showKeyboardShortcuts": "Hiển thị phím tắt", + "decreaseFontSize": "Giảm cỡ chữ", + "increaseFontSize": "Tăng cỡ chữ", + "startWritingMarkdown": "Bắt đầu viết nội dung 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}}", + "sideBySide": "Song song", + "inline": "Trong dòng", + "fileComparison": "So sánh tệp: {{file1}} vs {{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}})", + "sshConnectionFailed": "Kết nối SSH thất bại. Vui lòng kiểm tra kết nối 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", + "connectedSuccessfully": "Đã kết nối thành công", + "totpVerificationFailed": "Xác minh TOTP thất bại", "warpgateVerificationFailed": "Xác thực Warpgate thất bại", "authenticationFailed": "Xác thực thất bại", - "incorrectPassphrase": "Mật khẩu không chính xác. Vui lòng thử lại.", + "incorrectPassphrase": "Cụm mật khẩu không đúng. Vui lòng thử lại.", "verificationCodePrompt": "Mã xác minh:", "changePermissions": "Thay đổi quyền", "currentPermissions": "Quyền hiện tại", - "owner": "Người sở hữu", + "owner": "Chủ sở hữu", "group": "Nhóm", - "others": "Người khác", + "others": "Khác", "read": "Đọc", - "write": "Viết", + "write": "Ghi", "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", + "permissionsChangedSuccessfully": "Thay đổi quyền thành công", + "failedToChangePermissions": "Không thể thay đổi quyền", "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": "Gốc", + "sortBySize": "Kích thước", + "ascending": "Tăng dần", + "descending": "Giảm dần", + "root": "Thư mục gốc", "new": "Mới", "sortBy": "Sắp xếp theo", - "items": "Mặt hàng", + "items": "Mục", "selected": "Đã chọn", - "editor": "Biên tập viên", - "octal": "bát phân", - "storage": "Kho", + "editor": "Soạn thảo", + "octal": "Bát phân", + "storage": "Lưu trữ", "disk": "Đĩa", - "used": "Đã sử dụng", + "used": "Đã dùng", "of": "của", "toggleSidebar": "Ẩn/Hiện thanh bên", "cannotLoadPdf": "Không thể tải PDF", @@ -1797,109 +1930,109 @@ "loadingPage": "Đang tải trang..." }, "transfer": { - "copyToHost": "Sao chép vào máy chủ…", + "copyToHost": "Sao chép đến 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ủ.", + "copyItemsToHost": "Sao chép {{count}} mục đến máy chủ…", + "moveItemsToHost": "Di chuyển {{count}} mục tới máy chủ…", + "noHostsConnected": "Không có máy chủ nào khác có Trình quản lý tệp khả dụng.", + "noHostsConnectedHint": "Thêm một máy chủ SSH khá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", + "recentDestinations": "Đích gần đây", + "collapseRecentDestinations": "Thu gọn đích gần đây", + "expandRecentDestinations": "Mở rộng đích gần đây", + "browseFolders": "Duyệt 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}}", + "transferring": "Đang truyền tải…", + "compressing": "Đang nén…", + "extracting": "Đang giải nén…", + "transferringItems": "Đang truyền tải {{current}}/{{total}} mục…", + "transferSuccess": "Truyền tải hoàn tất", + "transferError": "Truyền tải thất bại", + "transferPartial": "Truyền tải hoàn tất với {{count}} lỗi", + "transferPartialHint": "Không thể truyền tải: {{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.", + "destMustBeDirectory": "Đích phải là thư mục khi truyền tải 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.", + "browsePathWillBeCreated": "Thư mục này chưa tồn tại. Nó sẽ được tạo khi truyền tải 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ủ…", + "goUp": "Lên trên", + "copyFolderToHost": "Sao chép thư mục tới máy chủ…", + "moveFolderToHost": "Di chuyển thư mục tới 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.", + "hostDisconnected": "Chưa kết nối", + "hostAuthRequired": "Yêu cầu xác thực — mở Trình quản lý tệp trên máy chủ này trước", "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}}", + "metricsTitle": "Thời gian truyền tải", + "metricsPrepare": "Chuẩn bị đích: {{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}}", + "metricsTransfer": "Toàn trình: {{throughput}} ({{duration}})", + "metricsExtract": "Giải nén tại đích: {{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…", + "metricsTotal": "Tổng: {{duration}}", + "progressCompressing": "Đang nén trên máy nguồn…", + "progressExtracting": "Đang giải nén tại đích…", "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}})…", + "parallelSegmentsLabel": "Làn truyền song song", + "parallelSegmentsOption": "{{count}} làn", + "parallelSegmentsHint": "Các tệp lớn được chia thành các khối 256 MB. Nhiều làn sử dụng các kết nối riêng biệt (như khởi chạy nhiều lần truyền) để tăng tổng thông lượng.", + "progressTotalSpeed": "{{speed}} tổng ({{lanes}} làn)", + "progressTransferringItems": "Đang truyền tệp ({{current}}/{{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}} tệp", + "sourceNotDeletedPartial": "Giữ tệp nguồn (truyền một phần)", + "jumpHostLimitation": "Cả hai máy chủ phải có thể truy cập từ máy chủ Termix. Không hỗ trợ định tuyến trực tiếp giữa các máy.", + "cancel": "Hủy", + "methodLabel": "Phương thức truyền", + "methodAuto": "Tự động", + "methodTar": "Lưu trữ tar", + "methodItemSftp": "SFTP từng tệp", + "methodAutoHint": "Chọn tar hoặc SFTP từng tệp dựa trên số lượng tệp, kích thước và khả năng nén. Tệp đơn lẻ luôn dùng SFTP truyền luồng.", + "methodTarHint": "Nén tại nguồn, truyền một tệp lưu trữ, giải nén tại đích. Yêu cầu tar trên cả hai máy Unix.", + "methodItemSftpHint": "Truyền từng tệp riêng lẻ qua SFTP. Hoạt động trên mọi máy, bao gồm Windows.", + "methodPreviewLoading": "Đang tính toán phương thức truyền…", + "methodPreviewError": "Không thể xem trước phương thức truyền. Máy chủ vẫn sẽ chọn phương thức khi bạn bắt đầu.", + "methodPreviewWillUseTar": "Sẽ dùng: Lưu trữ tar", + "methodPreviewWillUseItemSftp": "Sẽ dùng: SFTP từng tệp", + "methodPreviewScanSummary": "{{fileCount}} tệp, tổng {{totalSize}} (đã quét trên máy nguồn).", + "methodItemSftpLimitation": "Mỗi tệp sử dụng cùng một luồng SFTP như bản sao tệp đơn lẻ, lần lượt từng tệp một. Tiến trình được gộp chung cho tất cả tệp, do đó thanh tiến trình di chuyển chậm khi có tệp lớn.", "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": "Bạn đã chọn SFTP từng tệp.", + "user_tar": "Bạn đã chọn dùng tar.", + "tar_unavailable": "Tar không có trên một hoặc cả hai máy — sẽ dùng SFTP từng tệp thay thế.", + "windows_host": "Có máy Windows — không dùng tar.", + "auto_multi_large": "Tự động: nhiều tệp bao gồm một tệp lớn ({{largestSize}}) có dữ liệu nén được — tar gộp thành một lần chuyển.", + "auto_single_large_in_archive": "Tự động: một tệp lớn ({{largestSize}}) trong tập này — SFTP từng tệp.", + "auto_many_incompressible": "Tự động: dữ liệu hầu như không nén được — SFTP từng tệp.", + "auto_many_files": "Tự động: nhiều tệp ({{fileCount}}) — tar giảm chi phí trên mỗi tệp.", + "auto_default": "Tự động: SFTP từng tệp cho tập này." }, - "progressCancel": "Hủy bỏ", + "progressCancel": "Hủy", "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", + "progressStalled": "Đình trệ", + "resumedHint": "Đã kết nối lại với một tác vụ chuyển đang hoạt động được bắt đầu từ cửa sổ khác.", + "transferCancelled": "Đã hủy chuyển", + "transferCancelledCopyHint": "Các tệp chưa hoàn chỉnh có thể còn ở đích.", + "transferCancelledMoveHint": "Các tệp chưa hoàn chỉnh có thể còn ở đích. Tệp nguồn chưa bị xóa.", + "cleanupDestFiles": "Dọn dẹp đích", + "cleanupDestFilesSuccess": "Đã xóa các tệp chưa hoàn chỉnh ở đích", + "cleanupDestFilesPartial": "Không thể xóa một số tệp chưa hoàn chỉnh", + "cleanupDestFilesNothing": "Không có gì để dọn dẹp ở đích", + "cleanupDestFilesError": "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." + "retryTransferError": "Thử lại thất bại", + "transferFailedRetryHint": "Dữ liệu chưa hoàn chỉnh được giữ ở đích. Thử lại sẽ tiếp tục khi kết nối được khôi phục." }, "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 các kết nối đường hầm trong Trình quản lý máy chủ để bắt đầu.", + "createFirstTunnelMessage": "Bạn chưa tạo đườ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...", @@ -1907,57 +2040,57 @@ "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", + "cancel": "Hủy", + "port": "Cổng", + "localPort": "Cổng cục bộ", "remotePort": "Cổng từ xa", - "currentHostPort": "Cổng máy chủ hiện tại", + "currentHostPort": "Cổng máy hiện tại", "endpointPort": "Cổng điểm cuối", - "bindIp": "Địa chỉ IP cục bộ", + "bindIp": "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", + "endpointSshHostPlaceholder": "Chọn máy chủ đã cấu hình", + "endpointSshHostRequired": "Chọn máy chủ SSH điểm cuối cho mỗi đường hầm máy khách.", + "attempt": "Lần thử {{current}} / {{max}}", + "nextRetryIn": "Thử lại 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.", + "noClientTunnels": "Chưa có đường hầm máy khách nào được cấu hình trên máy 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", + "clientAutoStartDesc": "Tự động khởi động khi mở ứng dụng máy khách và duy trì kết nối.", + "clientManualStartDesc": "Sử dụng nút Bắt đầu và Dừng trên dòng này. Termix sẽ không tự động mở.", + "clientRemoteServerNote": "Chuyển tiếp từ xa có thể yêu cầu AllowTcpForwarding và GatewayPorts trên máy chủ SSH điểm cuối. Cổng từ xa sẽ đóng khi máy này ngắt kết nối.", + "clientTunnelStarted": "Đường hầm máy khách đã 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", + "tunnelTestSucceeded": "Kiểm tra đường hầm thành công", + "tunnelTestFailed": "Kiểm tra đường hầm thất bại", + "localSaved": "Đã lưu đường hầm máy khách", + "localSaveError": "Lưu đường hầm máy khách cục bộ thất bại", + "invalidBindIp": "IP cục bộ phải là địa chỉ IPv4 hợp lệ.", + "invalidLocalTargetIp": "IP đích cục bộ phải là địa chỉ IPv4 hợp lệ.", + "invalidLocalPort": "Cổng cục bộ phải nằm trong khoảng 1 đến 65535.", + "invalidRemotePort": "Cổng từ xa phải nằm trong khoảng 1 đến 65535.", + "invalidLocalTargetPort": "Cổng đích cục bộ phải nằm trong khoảng 1 đến 65535.", + "invalidEndpointPort": "Cổng điểm cuối phải nằm trong khoảng 1 đến 65535.", + "duplicateAutoStartBind": "Chỉ một đường hầm máy khách tự động có thể sử dụng {{bind}}.", + "manualControlError": "Cập nhật trạng thái đường hầm thất bại.", + "active": "Hoạt động", "start": "Bắt đầu", - "stop": "Dừng lại", - "test": "Bài kiểm tra", + "stop": "Dừng", + "test": "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)", + "typeLocal": "Cục bộ (-L)", + "typeRemote": "Từ xa (-R)", + "typeDynamic": "Động (-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ộ.", + "typeServerRemoteDesc": "Điểm cuối trở về máy chủ hiện tại.", + "typeClientLocalDesc": "Máy tính cục bộ đến điểm cuối.", + "typeClientRemoteDesc": "Điểm cuối trở về 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", + "typeDynamicDesc": "Chuyển tiếp lưu lượng SOCKS5 CONNECT 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}}.", @@ -1966,24 +2099,24 @@ "forwardDescriptionClientDynamic": "SOCKS trên cổng cục bộ {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → TẤT QUA {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS 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", + "autoNameClientDynamic": "SOCKS {{localPort}} qua {{endpoint}}", + "route": "Tuyến:", + "lastStarted": "Lần bắt đầu cuối", + "lastTested": "Lần kiểm tra cuối", "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", + "local": "Cục bộ", + "remote": "Từ xa", "destination": "Điểm đến", - "host": "Chủ nhà", - "mode": "Cách thức", - "noHostSelected": "Chưa chọn máy chủ nào", + "host": "Máy chủ", + "mode": "Chế độ", + "noHostSelected": "Chưa chọn máy chủ", "working": "Đang làm việc..." }, "cardGrid": { @@ -1991,22 +2124,22 @@ "dragToResize": "Kéo để thay đổi kích thước", "changeWidth": "Thay đổi chiều rộng", "removeCard": "Xóa {{label}}", - "addCard": "Thêm vào", + "addCard": "Thêm", "columns": "Cột", - "empty": "Không có thiệp. Sử dụng chức năng \"Thêm\" bên dưới để đặt thiệp." + "empty": "Không có thẻ nào. Dùng Thêm bên dưới để đặt thẻ." }, "hostMetrics": { "cpu": "CPU", - "memory": "Ký ức", - "disk": "Đĩa", + "memory": "Bộ nhớ", + "disk": "Ổ đĩa", "network": "Mạng", "uptime": "Thời gian hoạt động", - "processes": "Quy trình", - "available": "Có sẵn", - "free": "Miễn phí", + "processes": "Tiến trình", + "available": "Khả dụng", + "free": "Trống", "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", + "connectionFailed": "Kết nối máy chủ thất bại", + "naCpus": "N/A CPU", "cpuCores_one": "{{count}} Lõi", "cpuCores_other": "{{count}} Lõi", "cpuUsage": "Mức sử dụng CPU", @@ -2014,165 +2147,165 @@ "diskUsage": "Mức sử dụng ổ đĩa", "temperature": "Nhiệt độ", "highestTemperature": "Nhiệt độ cao nhất", - "failedToFetchHostConfig": "Không thể tải cấu hình máy chủ", + "failedToFetchHostConfig": "Không tải được 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", + "cannotFetchMetrics": "Không thể tải số liệu từ máy chủ ngoại tuyến", + "totpFailed": "Xác minh TOTP thất bại", "noneAuthNotSupported": "Host Metrics không hỗ trợ kiểu xác thực 'none'.", - "noHostSelected": "Chưa chọn máy chủ nào", - "load": "Trọng tải", + "noHostSelected": "Chưa chọn máy chủ", + "load": "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", + "kernel": "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.", - "processesTotal": "tổng cộng", + "noProcessesFound": "Không tìm thấy tiến trình nào", + "processesTotal": "Tổng", "processesRunning": "đang chạy", "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", + "quickActionSuccess": "{{name}} đã hoàn tất thành công", "quickActionFailed": "{{name}} thất bại", - "quickActionError": "Không thể thực thi {{name}}", + "quickActionError": "Thực thi {{name}} thất bại", "ports": { - "title": "Cổng nghe", + "title": "Cổng đang lắng nghe", "protocol": "Giao thức", - "port": "Cảng", + "port": "Cổng", "address": "Địa chỉ", - "process": "Quá trình", - "search": "Tìm kiếm các cảng...", + "process": "Tiến trình", + "search": "Tìm kiếm cổng...", "allProtocols": "Tất cả", - "noData": "Không có dữ liệu cổng lắng nghe" + "noData": "Không có dữ liệu cổng đang lắng nghe" }, "firewall": { "title": "Tường lửa", "inactive": "Không hoạt động", "policy": "Chính sách", "rules": "quy tắc", - "noRules": "Không có quy tắc nào trong chuỗi này.", - "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", + "noRules": "Không có quy tắc nào trong chuỗi này", + "noData": "Không có dữ liệu tường lửa", + "action": "Hành động", + "protocol": "Giao thức", + "port": "Cổng", "source": "Nguồn", - "anywhere": "Bất cứ nơi nào", + "anywhere": "Mọi nơi", "chains": "chuỗi" }, "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", + "swap": "Swap", + "architecture": "Kiến trúc", + "refresh": "Làm mới", "retry": "Thử lại", "customize": "Tùy chỉnh bố cục", - "reset": "Cài lại", - "tabLive": "Sống", - "editModeInstructions": "Kéo các thẻ để sắp xếp lại, kéo cạnh dưới để thay đổi kích thước và sử dụng nút chiều rộng để thay đổi chiều dài của thẻ. Thêm hoặc xóa thẻ bên dưới.", + "reset": "Đặt lại", + "tabLive": "Trực tiếp", + "editModeInstructions": "Kéo thẻ để sắp xếp lại, kéo cạnh dưới để thay đổi kích thước, và dùng nút chiều rộng để thay đổi độ rộng thẻ. Thêm hoặc xóa thẻ bên dưới.", "managers": { "services": "Dịch vụ", - "processInspector": "Thanh tra quy trình", + "processInspector": "Trình kiểm tra tiến trình", "logViewer": "Trình xem nhật ký", - "cron": "Công việc định kỳ (Cron Jobs)", - "packages": "Gói hàng", + "cron": "Cron Jobs", + "packages": "Gói", "ssl": "Chứng chỉ SSL", "firewall": "Tường lửa", - "users": "Người dùng & Quyền hạn", - "healthCheck": "Khám sức khỏe", - "diskBreakdown": "Sự cố đĩa", + "users": "Người dùng & Quyền", + "healthCheck": "Kiểm tra sức khỏe", + "diskBreakdown": "Phân tích ổ đĩa", "systemdTimers": "Bộ hẹn giờ", - "topMemory": "Xếp hạng cao nhất theo Memory", + "topMemory": "Top theo bộ nhớ", "noData": "Không có dữ liệu", - "sudoHint": "Đặt mật khẩu sudo cho máy chủ này trong trình chỉnh sửa máy chủ để kích hoạt các thao tác có quyền quản trị.", + "sudoHint": "Đặt mật khẩu sudo cho máy chủ này trong trình chỉnh sửa máy chủ để kích hoạt các hành động đặc quyền.", "filter": "Lọc...", - "start": "Bắt đầu", - "stop": "Dừng lại", + "start": "Khởi động", + "stop": "Dừng", "restart": "Khởi động lại", - "actionDone": "{{name}} đã cập nhật", + "actionDone": "{{name}} được cập nhật", "actionFailed": "Hành động thất bại", - "signalSent": "Tín hiệu được gửi đến PID {{pid}}", - "killHint": "Nhấp chuột: chấm dứt (SIGTERM). Nhấp chuột phải: buộc dừng (SIGKILL).", + "signalSent": "Đã gửi tín hiệu đến PID {{pid}}", + "killHint": "Nhấp: kết thúc (SIGTERM). Nhấp chuột phải: buộc kết thúc (SIGKILL).", "working": "Đang làm việc...", "update": "Cập nhật", "upgradeAll": "Nâng cấp tất cả", - "allUpToDate": "Mọi thứ đều được cập nhật.", - "save": "Cứu", - "command": "Yêu cầu", + "allUpToDate": "Tất cả đều mới nhất", + "save": "Lưu", + "command": "Lệnh", "enabled": "Đã bật", - "cronSaved": "Crontab đã được cập nhật", - "clients": "Khách hàng", + "cronSaved": "Đã cập nhật crontab", + "clients": "Máy khách", "dryRun": "Chạy thử", "renew": "Gia hạn", - "noAcmeClient": "Không tìm thấy ứng dụng khách ACME (certbot hoặc acme.sh) trên máy chủ này.", - "addInputRule": "Thêm quy tắc ĐẦU VÀO", - "firewallWarning": "Các thay đổi chỉ có hiệu lực trong thời gian chạy cho đến khi được lưu lại. Hãy cẩn thận để không tự khóa quyền truy cập của mình.", - "ruleApplied": "Quy tắc được áp dụng", - "invalidPort": "Nhập số cổng hợp lệ (1-65535)", + "noAcmeClient": "Không tìm thấy máy khách ACME (certbot hoặc acme.sh) trên máy này.", + "addInputRule": "Thêm quy tắc INPUT", + "firewallWarning": "Thay đổi chỉ tạm thời, chưa được lưu vĩnh viễn. Cẩn thận kẻo tự khóa mình ra ngoài.", + "ruleApplied": "Quy tắc đã được áp dụng", + "invalidPort": "Nhập cổng hợp lệ (1-65535)", "newUsername": "Tên người dùng mới", - "addUser": "Thêm vào", + "addUser": "Thêm", "deleteUser": "Xóa người dùng", - "noHealthChecks": "Chưa có cấu hình kiểm tra sức khỏe nào được thiết lập.", - "follow": "Theo", - "noLogData": "Không có đầu ra nhật ký.", - "tree": "Cây", - "enableDisable": "Bật/tắt khi khởi động", + "noHealthChecks": "Chưa có kiểm tra trạng thái nào được cấu hình.", + "follow": "Theo dõi", + "noLogData": "Không có dữ liệu nhật ký.", + "tree": "Dạng cây", + "enableDisable": "Bật / tắt khi khởi động", "grantSudo": "Cấp quyền sudo", "revokeSudo": "Thu hồi quyền sudo", - "sslIssueCert": "Cấp giấy chứng nhận", - "sslExpired": "Hết hạn", + "sslIssueCert": "Cấp chứng chỉ", + "sslExpired": "Đã hết hạn", "sslInDays": "trong {{days}}d", "sslNeedDomain": "Nhập ít nhất một tên miền", - "sslIssued": "Giấy chứng nhận đã được cấp", + "sslIssued": "Đã cấp chứng chỉ", "sslDomainsPlaceholder": "example.com, www.example.com", "sslHttpStandalone": "HTTP (độc lập)", "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "Nhà cung cấp DNS (ví dụ: Cloudflare)", - "sslIssueHint": "Thông tin đăng nhập của nhà cung cấp DNS phải đã được cấu hình trên máy chủ.", + "sslDnsProvider": "Nhà cung cấp DNS (ví dụ: cloudflare)", + "sslIssueHint": "Thông tin xác thực nhà cung cấp DNS phải được cấu hình trên máy chủ.", "sslRevoke": "Thu hồi chứng chỉ", "sslRevoked": "Chứng chỉ đã bị thu hồi", - "sslRevokeConfirm": "Thu hồi và xóa chứng chỉ \"{{name}}\"? Thao tác này không thể hoàn tác.", + "sslRevokeConfirm": "Thu hồi và xóa chứng chỉ \"{{name}}\"? Hành động này không thể hoàn tác.", "healthRun": "Chạy", "healthName": "Tên", "healthTarget": "Máy chủ / địa chỉ", "healthAddCheck": "Thêm kiểm tra", - "healthSaved": "Các lần kiểm tra sức khỏe đã được lưu", - "healthMissingFields": "Mỗi lần kiểm tra cần có tên và mục tiêu.", - "logFile": "Tài liệu", + "healthSaved": "Đã lưu kiểm tra sức khỏe", + "healthMissingFields": "Mỗi kiểm tra cần có tên và mục tiêu", + "logFile": "Tệp", "logUnit": "Đơn vị", - "logCustomPath": "Đường dẫn tùy chỉnh trong thư mục /var/log (tùy chọn)", - "logGrep": "Các đường lọc...", - "firewallPersist": "Các quy tắc bền vững", - "firewallPersisted": "Các quy tắc tường lửa vẫn được lưu giữ", + "logCustomPath": "Đường dẫn tùy chỉnh trong /var/log (tùy chọn)", + "logGrep": "Lọc dòng...", + "firewallPersist": "Lưu quy tắc", + "firewallPersisted": "Đã lưu quy tắc tường lửa", "wireguard": "WireGuard", - "tailscale": "Cân đuôi", - "wgNotInstalled": "WireGuard chưa được cài đặt trên máy chủ này.", - "wgNoInterfaces": "Không có giao diện WireGuard nào được cấu hình.", - "wgInterfaceUp": "Nuôi dưỡng", - "wgInterfaceDown": "Hạ xuống", - "wgBringingUp": "Đưa {{name}} lên...", - "wgBringingDown": "Đang hạ gục {{name}}...", - "wgInterfaceUpDone": "{{name}} đã được tải lên", - "wgInterfaceDownDone": "{{name}} đang bị lỗi", - "wgListenPort": "Cảng", + "tailscale": "Tailscale", + "wgNotInstalled": "WireGuard chưa được cài đặt trên máy chủ này", + "wgNoInterfaces": "Chưa cấu hình giao diện WireGuard nào", + "wgInterfaceUp": "Khởi động", + "wgInterfaceDown": "Dừng", + "wgBringingUp": "Đang khởi động {{name}}...", + "wgBringingDown": "Đang dừng {{name}}...", + "wgInterfaceUpDone": "{{name}} đã hoạt động", + "wgInterfaceDownDone": "{{name}} đã dừng", + "wgListenPort": "Cổng", "wgPublicKey": "Khóa công khai", "wgEndpoint": "Điểm cuối", - "wgAllowedIPs": "Địa chỉ IP được phép", - "wgLastHandshake": "cái bắt tay cuối cùng", - "wgHandshakeNever": "Không bao giờ", - "wgTransfer": "Chuyển khoản", - "tsNotInstalled": "Tailscale chưa được cài đặt trên máy chủ này.", + "wgAllowedIPs": "IP được phép", + "wgLastHandshake": "Lần bắt tay cuối", + "wgHandshakeNever": "Chưa bao giờ", + "wgTransfer": "Truyền tải", + "tsNotInstalled": "Tailscale chưa được cài đặt trên máy chủ này", "tsRunning": "Đang chạy", - "tsStopped": "Đã dừng lại", + "tsStopped": "Đã dừng", "tsEnable": "Kết nối", "tsDisable": "Ngắt kết nối", - "tsEnabling": "Đang kết nối với Tailscale...", + "tsEnabling": "Đang kết nối đến Tailscale...", "tsDisabling": "Đang ngắt kết nối khỏi Tailscale...", - "tsEnabled": "Kết nối quy mô đuôi", - "tsDisabled": "Cân đuôi bị ngắt kết nối", - "tsIPs": "IP quy mô nhỏ", - "tsPeers": "Đồng nghiệp", + "tsEnabled": "Đã kết nối Tailscale", + "tsDisabled": "Đã ngắt kết nối Tailscale", + "tsIPs": "IP Tailscale", + "tsPeers": "Các máy ngang hàng", "tsOnline": "Trực tuyến", "tsOffline": "Ngoại tuyến", "tsExitNode": "Nút thoát", @@ -2182,36 +2315,36 @@ }, "auth": { "tagline": "Quản lý SSH và máy tính từ xa tự lưu trữ", - "loginTitle": "Chào mừng trở lại!", + "loginTitle": "Chào mừng trở lại", "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?", + "rememberMe": "Nhớ thiết bị trong 30 ngày (bao gồm TOTP)", + "noAccount": "Chưa có tài khoản?", + "hasAccount": "Đã có tài khoản?", "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", + "backupCode": "Hoặc 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", + "sshAuthenticationFailed": "Xác thực thất bại", "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.", + "sshNoKeyboardInteractiveDescription": "Máy chủ không hỗ trợ xác thực tương tác bàn phím. Vui lòng cung cấp mật khẩu hoặc khóa SSH.", + "sshAuthFailedDescription": "Thông tin đăng nhập không chính xác. Vui lòng thử lại với thông tin hợp lệ.", + "sshTimeoutDescription": "Quá trình xác thực đã hết thời gian. Vui lòng thử lại.", + "sshProvideCredentialsDescription": "Vui lòng cung cấp thông tin SSH để kết nối đến 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", + "sshKeyPasswordDescription": "Nếu khóa SSH của bạn được mã hóa, hãy nhập cụm mật khẩu tại đây.", + "passphraseRequired": "Yêu cầu cụm mật khẩu", + "passphraseRequiredDescription": "Khóa SSH đã được mã hóa. Vui lòng nhập cụm mật khẩu để mở khóa.", + "back": "Quay lại", "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.", + "firstUserMessage": "Bạn là người dùng đầu tiên và sẽ được đặt làm quản trị viên. Bạn có thể xem cài đặt quản trị trong menu thả xuống người dùng ở thanh bên. Nếu bạn cho rằng đây là nhầm lẫn, hãy kiểm tra nhật ký Docker hoặc tạo một issue 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.", + "loginWithExternal": "Đăng nhập bằng 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", + "externalNotSupportedInElectron": "Xác thực bên ngoài chưa được hỗ trợ trong ứng dụng Electron. Vui lòng sử dụng phiên bản web để đăng nhập OIDC.", "loginWithProvider": "Đăng nhập bằng {{name}}", "orContinueWith": "hoặc tiếp tục với", "ldapUsername": "Tên người dùng LDAP", @@ -2220,7 +2353,7 @@ "ldapLoginFailed": "Đăng nhập LDAP thất bại", "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.", + "resetCodeDesc": "Nhập tên người dùng của bạn để nhận mã đặt lại mật khẩu. Mã sẽ được ghi trong nhật ký 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:", @@ -2228,85 +2361,86 @@ "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", + "desktopApp": "Ứng dụng máy tính", + "loggingInToDesktopApp": "Đang đăng nhập vào ứng dụng máy tính", "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.", - "passwordLoginDisabledDesc": "Chức năng đăng nhập bằng mật khẩu đã bị vô hiệu hóa. Vui lòng sử dụng mã xác thực hoặc nhà cung cấp xác thực bên ngoài.", - "signInWithPasskey": "Đăng nhập bằng mật khẩu", - "passkeyLoginFailed": "Đăng nhập bằng mật khẩu thất bại", - "attemptsRemaining": "{{count}} lần thử còn lại", - "confirmResetDataWipe": "Tài khoản này chưa đăng nhập kể từ khi nâng cấp mã hóa, vì vậy dữ liệu đã lưu trữ không thể khôi phục nếu không có mật khẩu cũ. Việc đặt lại sẽ xóa vĩnh viễn các máy chủ, thông tin đăng nhập và đoạn mã của tài khoản này. Bạn có muốn tiếp tục không?" + "dataLossWarning": "Đặ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 mã hóa khác. Hành động này không thể hoàn tác. Chỉ sử dụng khi bạn đã quên mật khẩu và chưa đăng nhập.", + "authenticationDisabled": "Xác thực bị vô hiệu", + "authenticationDisabledDesc": "Tất cả các phương thức xác thực đang bị vô hiệu. Vui lòng liên hệ quản trị viên của bạn.", + "passwordLoginDisabledDesc": "Đăng nhập bằng mật khẩu bị vô hiệu. Hãy sử dụng khóa truy cập hoặc nhà cung cấp xác thực bên ngoài.", + "signInWithPasskey": "Đăng nhập bằng khóa truy cập", + "passkeyLoginFailed": "Đăng nhập bằng passkey thất bại", + "attemptsRemaining": "Còn {{count}} lần thử", + "confirmResetDataWipe": "Tài khoản này chưa đăng nhập kể từ khi nâng cấp mã hóa, do đó dữ liệu đã lưu không thể khôi phục nếu không có mật khẩu cũ. Việc đặt lại sẽ xóa vĩnh viễn các máy chủ, thông tin xác thực và đoạn mã. Tiếp tục?" }, "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 đó", + "firstConnectionTitle": "Lần đầu kết nối đến máy chủ này", + "firstConnectionDescription": "Không thể xác thực danh tính của máy chủ này. Xác minh dấu vân tay khớp với mong đợi.", + "keyChangedDescription": "Khóa máy chủ cho máy chủ này đã thay đổi kể từ lần kết nối trước. Điều này có thể là dấu hiệu của vấn đề bảo mật.", + "previousKey": "Khóa 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" + "verifyInstructions": "Nếu bạn tin tưởng máy chủ này, nhấn 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 sau.", + "securityWarning": "Cảnh báo bảo mật", + "acceptAndContinue": "Chấp nhận & Tiếp tục", + "acceptNewKey": "Chấp nhận Khóa Mới & Tiếp tục" }, "errors": { - "databaseConnection": "Không thể kết nối với cơ sở dữ liệu", + "databaseConnection": "Không thể kết nối tớ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.", + "failedPasswordReset": "Không thể bắt đầu đặ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 đặ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.", + "failedOidcLogin": "Không thể bắt đầu đăng nhập OIDC", + "silentSigninOidcUnavailable": "Đã yêu cầu đăng nhập tự động, như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ợ.", + "invalidAuthUrl": "URL ủy quyền không hợp lệ nhận được từ máy chủ", "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ủ" + "passwordLoginDisabled": "Đă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 làm việc đã hết hạn - vui lòng đăng nhập lại", + "totpRateLimited": "Giới hạn tốc độ: Quá nhiều lần thử xác thực TOTP. Vui lòng thử lại sau.", + "totpRateLimitedWithTime": "Giới hạn tốc độ: 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": "Giới hạn tốc độ: Quá nhiều lần thử xác thực. Vui lòng thử lại sau.", + "resetCodeRateLimitedWithTime": "Hạn chế tốc độ: Quá nhiều lần xác minh. Vui lòng chờ {{time}} giây trước khi thử lại.", + "authTokenSaveFailed": "Lưu token xác thực thất bại", + "failedToLoadServer": "Tải máy chủ thất bại", + "remoteServerRequired": "Cần có máy chủ từ xa. Kết nối máy chủ từ xa trong Cài đặt để sử dụng loại kết nối này." }, "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.", + "registrationDisabled": "Đăng ký tài khoản mới hiện bị quản trị viên vô hiệu hóa. Vui lòng đăng nhập hoặc liên hệ 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ệ quản trị viên.", + "databaseConnectionFailed": "Kết nối máy chủ cơ sở dữ liệu thất bại", + "resetCodeSent": "Mã đặt lại đã được gửi đến nhật ký Docker", + "codeVerified": "Xác minh mã 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" }, "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.", + "c2sTunnelConfigDesc": "Đường hầm máy tính cục bộ trỏ đến các máy chủ SSH đã 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 máy khách này thành cài đặt sẵn trên máy chủ có tên, hoặc tải cài đặt sẵn trở lại máy khách này.", + "c2sTunnelPresetsUnavailable": "Cài đặt sẵn đường hầm máy khách chỉ khả dụng trên ứng dụng máy tính.", "c2sPresetName": "Tên cài đặt sẵn", - "c2sPresetNamePlaceholder": "Tên cài đặt sẵn của khách hàng", + "c2sPresetNamePlaceholder": "Tên cài đặt sẵn máy khách", "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", + "c2sNoPresetSelected": "Chưa chọn cài đặt sẵn", + "c2sNoPresets": "Chưa lưu cài đặt sẵn nào", + "c2sLoadPreset": "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 này.", + "c2sPresetSyncNote": "Cài đặt sẵn là ảnh chụp tức thời; tải một cài đặt sẽ thay thế danh sách đường hầm máy khách cục bộ trên máy này.", + "c2sPresetSaved": "Đã lưu cài đặt sẵn đường hầm máy khách", + "c2sPresetLoaded": "Đã tải cài đặt sẵn đường hầm máy khách cục bộ", + "c2sPresetRenamed": "Đã đổi tên 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." + "c2sPresetLoadError": "Tải cài đặt sẵn đường hầm máy khách thất bại" }, "placeholders": { "maxRetries": "3", @@ -2314,9 +2448,9 @@ "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)", + "localListenerHost": "127.0.0.1 (lắng nghe cục bộ)", + "localTargetHost": "127.0.0.1 (đích trên máy này)", + "socksListenerHost": "127.0.0.1 (Trình nghe SOCKS)", "enterPassword": "Nhập mật khẩu của bạn", "defaultPort": "22", "defaultEndpointPort": "224" @@ -2325,106 +2459,106 @@ "title": "Bảng điều khiển", "loading": "Đang tải bảng điều khiển...", "github": "GitHub", - "support": "Ủng hộ", + "support": "Hỗ trợ", "discord": "Discord", - "docs": "Bác sĩ", - "donate": "Quyên tặng", - "serverOverview": "Tổng quan về máy chủ", + "docs": "Tài liệu", + "donate": "Ủng hộ", + "serverOverview": "Tổng quan 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", + "updateAvailable": "Có bản cập nhật", + "beta": "Beta", "uptime": "Thời gian hoạt động", "database": "Cơ sở dữ liệu", - "healthy": "Khỏe mạnh", + "healthy": "Ổn đị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ỉ", + "totalCredentials": "Tổng số thông tin xác thực", "recentActivity": "Hoạt động gần đây", - "reset": "Cài lại", + "reset": "Đặt 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", + "quickActions": "Hành động nhanh", "addHost": "Thêm máy chủ", - "addCredential": "Thêm thông tin đăng nhập", + "addCredential": "Thêm thông tin xác thực", "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", + "noServerData": "Không có dữ liệu máy chủ", "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", + "ram": "RAM", + "customizeLayout": "Tùy chỉnh Bảng điều khiển", + "dashboardSettings": "Cài đặt Bảng điều khiển", + "enableDisableCards": "Bật/Tắt Thẻ", + "resetLayout": "Đặt lại về mặc định", + "serverOverviewCard": "Tổng quan Máy chủ", + "recentActivityCard": "Hoạt động Gần đây", + "networkGraphCard": "Biểu đồ Mạng", + "networkGraph": "Biểu đồ Mạng", "quickActionsCard": "Thao tác nhanh", "serverStatsCard": "Thống kê máy chủ", - "panelMain": "Chủ yếu", + "panelMain": "Chính", "panelSide": "Bên", - "justNow": "vừa nãy", - "serviceLinks": "Liên kết dịch vụ", - "homepagePreview": "Xem trước trang chủ" + "justNow": "vừa xong", + "serviceLinks": "Liên kết Dịch vụ", + "homepagePreview": "Xem trước Trang chủ" }, "donation": { - "title": "Bạn thấy Termix thế nào?", - "body": "Termix là phần mềm mã nguồn mở miễn phí, được xây dựng và duy trì bởi một nhóm hai người trong thời gian rảnh rỗi. Nếu nó thay thế công cụ thương mại mà bạn lẽ ra phải trả tiền, khoản quyên góp của bạn sẽ giúp trang trải chi phí lưu trữ và duy trì quá trình phát triển. Hiện tại, chúng tôi chỉ chấp nhận quyên góp bằng tiền điện tử.", - "milestones": "Các khoản quyên góp cũng giúp tài trợ cho thời gian nghiên cứu và học hỏi những gì cần thiết để xây dựng các tính năng như hỗ trợ SAML và Kubernetes. Xem tiến độ trên trang quyên góp.", - "cta": "Quyên tặng", - "dismiss": "Có lẽ sau này" + "title": "Bạn thích Termix chứ?", + "body": "Termix là phần mềm miễn phí và mã nguồn mở, được xây dựng và duy trì bởi một nhóm hai người trong thời gian rảnh. Nếu nó đã thay thế một công cụ thương mại mà bạn đáng lẽ phải trả tiền, một khoản quyên góp sẽ giúp trang trải chi phí hosting và duy trì phát triển. Hiện tại, quyên góp chỉ chấp nhận tiền mã hóa.", + "milestones": "Quyên góp cũng giúp tài trợ thời gian nghiên cứu và học hỏi những gì cần thiết để xây dựng các tính năng như hỗ trợ SAML và Kubernetes. Xem tiến độ trên trang quyên góp.", + "cta": "Quyên góp", + "dismiss": "Để sau" }, "dashboardTab": { "stable": "ỔN ĐỊNH", - "hostsOnline": "Hosts Online", - "activeTunnels": "Đường hầm đang hoạt động", + "hostsOnline": "Máy chủ Trực tuyến", + "activeTunnels": "Đường hầm 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", + "hostStatus": "Trạng thái Máy chủ", + "noHostsConfigured": "Chưa cấu hình máy chủ nào", "online": "TRỰC TUYẾN", "offline": "NGOẠI TUYẾN", - "checking": "KIỂM TRA", + "checking": "ĐANG KIỂM TRA", "onlineLower": "Trực tuyến", - "nodes": "{{count}} nút", - "add": "Thêm vào:", + "nodes": "{{count}} node", + "add": "Thêm:", "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", + "editModeInstructions": "Kéo thẻ để sắp xếp lại · Kéo dải 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 · Kéo vào thùng rác để xóa", "empty": "Trống", - "clear": "Thông thoáng", + "clear": "Xóa", "serviceLinksTitle": "Liên kết dịch vụ", - "serviceLinksEmpty": "Chưa có liên kết dịch vụ nào.", + "serviceLinksEmpty": "Chưa có liên kết dịch vụ nào", "serviceLinksAddLabel": "Nhãn", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "Thêm vào", + "serviceLinksAdd": "Thêm", "serviceLinksLabelPlaceholder": "Dịch vụ của tôi", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", "serviceLinksInvalidUrl": "Nhập địa chỉ web hợp lệ", - "serviceLinksAddFailed": "Không thể thêm liên kết dịch vụ.", - "disk": "Đĩa", + "serviceLinksAddFailed": "Thêm liên kết dịch vụ thất bại", + "disk": "Ổ đĩa", "viewServerDetails": "Xem chi tiết máy chủ" }, "sessionLogs": { "title": "Nhật ký phiên", "noLogs": "Chưa có nhật ký phiên nào", - "noLogsDesc": "Bật tính năng ghi nhật ký phiên trên máy chủ để bắt đầu ghi lại.", - "duration": "Khoảng thời gian", + "noLogsDesc": "Bật ghi nhật ký phiên trên máy chủ để bắt đầu ghi", + "duration": "Thời lượng", "viewLog": "Xem nhật ký", "downloadLog": "Tải xuống", - "deleteLog": "Xóa bỏ", + "deleteLog": "Xóa", "confirmDelete": "Xóa nhật ký phiên này?", - "confirmDeleteDesc": "Hành động này không thể đảo ngược.", + "confirmDeleteDesc": "Hành động này không thể hoàn tác.", "copyContent": "Sao chép", "copied": "Đã sao chép!", - "loadError": "Không thể tải nhật ký phiên.", - "deleteError": "Không thể xóa nhật ký phiên.", + "loadError": "Tải nhật ký phiên thất bại", + "deleteError": "Xóa nhật ký phiên thất bại", "filterByHost": "Lọc theo máy chủ..." }, "networkGraph": { @@ -2433,43 +2567,43 @@ "addLink": "Thêm liên kết", "zoomIn": "Phóng to", "zoomOut": "Thu nhỏ", - "resetView": "Đặt lại chế độ xem", + "resetView": "Đặt lại khung nhìn", "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", + "parentGroup": "Nhóm cha", + "noGroup": "Không 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", + "target": "Đích", + "moveToGroup": "Di chuyển vào nhóm", "selectGroup": "Chọn nhóm...", "addConnection": "Thêm kết nối", - "hostDetails": "Thông tin chủ nhà", + "hostDetails": "Chi tiết máy chủ", "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", + "editGroup": "Sửa nhóm", + "delete": "Xóa", + "add": "Thêm", + "create": "Tạo", "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...", + "selectTargetPlaceholder": "Chọn đích...", "invalidFile": "Tệp không hợp lệ", - "hostAlreadyExists": "Máy chủ đã có trong sơ đồ mạng.", + "hostAlreadyExists": "Máy chủ đã có trong cấu trúc liên kết", "connectionExists": "Kết nối đã tồn tại", - "unknown": "Không rõ", + "unknown": "Không xác định", "name": "Tên", - "ip": "Địa chỉ IP", + "ip": "IP", "status": "Trạng thái", - "failedToAddNode": "Thêm nút không thành công", + "failedToAddNode": "Không thể thêm nút", "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", + "terminal": "Terminal", + "fileManager": "Trình quản lý file", "tunnel": "Đường hầm", "docker": "Docker", "serverStats": "Số liệu máy chủ", @@ -2477,110 +2611,110 @@ "noNodes": "Chưa có nút nào" }, "docker": { - "notEnabled": "Docker chưa được kích hoạt trên máy chủ này.", - "validating": "Đang xác thực Docker...", + "notEnabled": "Docker chưa được kích hoạt cho máy chủ này", + "validating": "Đang kiểm tra Docker...", "connecting": "Đang kết nối...", "error": "Lỗi", "version": "Docker {{version}}", - "connectionFailed": "Không thể kết nối với Docker.", + "connectionFailed": "Kết nối Docker thất bại", "containerStarted": "Container {{name}} đã khởi động", - "failedToStartContainer": "Không thể khởi động container {{name}}", + "failedToStartContainer": "Khởi động container {{name}} thất bại", "containerStopped": "Container {{name}} đã dừng", - "failedToStopContainer": "Không thể dừng container {{name}}", + "failedToStopContainer": "Dừng container {{name}} thất bại", "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", - "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}}", + "failedToRestartContainer": "Khởi động lại container {{name}} thất bại", + "containerPaused": "Container {{name}} đã tạm dừng", + "containerUnpaused": "Container {{name}} đã tiếp tục", + "failedToTogglePauseContainer": "Chuyển đổi trạng thái tạm dừng cho container {{name}} thất bại", "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", + "failedToRemoveContainer": "Xóa container {{name}} thất bại", + "image": "Image", + "ports": "Cổng", + "noPorts": "Không có cổng nào", + "start": "Khởi động", + "confirmRemoveContainer": "Bạn có chắc chắn muốn xóa container '{{name}}' không? Hành động này không thể hoàn tác.", + "runningContainerWarning": "Cảnh báo: Container này đang chạy. Xóa nó sẽ dừng container trước.", + "loadingContainers": "Đang tải danh sách container...", + "manager": "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", + "refresh": "Làm mới", "download": "Tải xuống", - "clear": "Thông thoáng", - "logsDownloaded": "Nhật ký đã được tải xuống thành công", + "clear": "Xóa", + "logsDownloaded": "Đã tải xuống nhật ký 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.", + "noLogsMatching": "Không có nhật ký nào khớp với \"{{query}}\"", + "noLogsAvailable": "Không có nhật ký khả dụng", + "noContainersFound": "Không tìm thấy container", + "noContainersFoundHint": "Không có container Docker nào 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", + "stateRestarting": "Đang khởi động lại", + "noContainersMatchFilters": "Không có container nào khớp 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", + "failedToFetchStats": "Không thể lấy số liệu thống kê container", "containerNotRunning": "Container không chạy", - "startContainerToViewStats": "Khởi động container để xem số liệu thống kê.", + "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ớ", + "noStatsAvailable": "Không có số liệu thống kê", + "cpuUsage": "Sử dụng CPU", + "current": "Hiện tại", + "memoryUsage": "Sử dụng bộ nhớ", "networkIo": "I/O mạng", - "input": "Đầu vào", + "input": "Nhập", "output": "Đầu ra", - "blockIo": "Nhập/Xuất khối", + "blockIo": "I/O khối", "read": "Đọc", - "write": "Viết", - "pids": "PID", + "write": "Ghi", + "pids": "PIDs", "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.", + "id": "ID", + "state": "Trạng thái", + "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.", + "totpVerificationFailed": "Xác minh TOTP thất bại. 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}}", + "connectedTo": "Đã kết nối đến {{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", + "failedToConnect": "Không thể kết nối đến container", "console": "Bảng điều khiển", - "selectShell": "Chọn vỏ", + "selectShell": "Chọn shell", "bash": "Bash", "sh": "sh", - "ash": "tro", + "ash": "ash", "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}}...", + "notConnected": "Chưa kết nối", + "clickToConnect": "Nhấn Kết nối để bắt đầu phiên shell", + "connectingTo": "Đang kết nối đến {{containerName}}...", "containerNotFound": "Không tìm thấy container", - "backToList": "Trở lại danh sách", + "backToList": "Quay 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." + "startContainerToAccess": "Khởi động container để truy cập bảng điều khiển" }, "admin": { - "sectionGeneral": "Tổng quan", + "sectionGeneral": "Chung", "sectionOidc": "OIDC", "sectionSso": "Nhà cung cấp SSO", "ssoAddProvider": "Thêm nhà cung cấp", "ssoDocsLink": "Xem tài liệu", "ssoProviderDocsLink": "Xem tài liệu", - "ssoNoProviders": "Chưa có nhà cung cấp SSO nào được cấu hình.", + "ssoNoProviders": "Chưa có nhà cung cấp SSO.", "ssoProviderName": "Tên hiển thị", "ssoProviderType": "Loại nhà cung cấp", "ssoDeleteProvider": "Xóa nhà cung cấp", @@ -2590,622 +2724,684 @@ "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", "ssoEnabled": "Đã bật", - "ssoDisabled": "Tàn tật", + "ssoDisabled": "Đã tắt", "ssoSaveProvider": "Lưu nhà cung cấp", "ssoTestConnection": "Kiểm tra kết nối", "ssoEditProvider": "Chỉnh sửa nhà cung cấp", "ldapHost": "Máy chủ LDAP", - "ldapPort": "Cảng", - "ldapUseTls": "Sử dụng TLS (LDAPS)", + "ldapPort": "Cổng", + "ldapUseTls": "Dùng TLS (LDAPS)", "ldapBindDn": "Bind DN", - "ldapBindPassword": "Liên kết mật khẩu", + "ldapBindPassword": "Mật khẩu Bind", "ldapUserSearchBase": "Cơ sở tìm kiếm người dùng", "ldapUserSearchFilter": "Bộ lọc tìm kiếm người dùng", "ldapUsernameAttr": "Thuộc tính tên người dùng", "ldapDisplayNameAttr": "Thuộc tính tên hiển thị", - "ldapGroupSearchBase": "Tìm kiếm nhóm cơ sở", + "ldapGroupSearchBase": "Cơ sở tìm kiếm nhóm", "ldapAdminGroup": "Nhóm quản trị", "ldapAllowedUsers": "Người dùng được phép", "sectionUsers": "Người dùng", - "sectionSessions": "Phiên họp", + "sectionSessions": "Phiên", "sectionRoles": "Vai trò", "sectionDatabase": "Cơ sở dữ liệu", "sectionApiKeys": "Khóa API", - "sectionAuditLog": "Nhật ký kiểm toán", + "sectionAuditLog": "Nhật ký kiểm tra", "sectionSsl": "SSL / Let's Encrypt", - "sslDescription": "Tự động cấp và gia hạn chứng chỉ SSL đáng tin cậy từ Let's Encrypt. Yêu cầu tên miền công cộng và cổng 80 hoặc quyền truy cập DNS.", + "sslDescription": "Tự động cấp và gia hạn chứng chỉ SSL tin cậy từ Let's Encrypt. Yêu cầu tên miền công khai và quyền truy cập cổng 80 hoặc DNS.", "sslDocsLink": "Xem tài liệu SSL", - "sslDomain": "Lãnh địa", + "sslDomain": "Tên miền", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "Tên miền công cộng cho chứng chỉ.", - "sslEmail": "E-mail", + "sslDomainDesc": "Tên miền công khai để cấp chứng chỉ.", + "sslEmail": "Email", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Địa chỉ email liên hệ để nhận thông báo và quản lý tài khoản Let's Encrypt.", + "sslEmailDesc": "Email liên hệ để nhận thông báo và tài khoản từ Let's Encrypt.", "sslChallengeType": "Loại thử thách", - "sslChallengeTypeDesc": "Làm thế nào để chứng minh quyền sở hữu tên miền với Let's Encrypt.", - "sslChallengeHttp": "HTTP (webroot) - yêu cầu cổng 80 có thể truy cập được từ internet", - "sslChallengeDns": "DNS (Cloudflare) - yêu cầu mã thông báo API của Cloudflare.", - "sslCloudflareToken": "Mã thông báo API Cloudflare", - "sslCloudflareTokenPlaceholder": "Nhập mã thông báo...", - "sslCloudflareTokenDesc": "Token có phạm vi quyền Zone:DNS:Edit dành cho tên miền của bạn.", + "sslChallengeTypeDesc": "Cách chứng minh quyền sở hữu tên miền với Let's Encrypt.", + "sslChallengeHttp": "HTTP (webroot) - yêu cầu cổng 80 có thể truy cập từ internet", + "sslChallengeDns": "DNS (Cloudflare) - yêu cầu Token API Cloudflare", + "sslCloudflareToken": "Token API Cloudflare", + "sslCloudflareTokenPlaceholder": "Nhập token...", + "sslCloudflareTokenDesc": "Token có phạm vi với quyền Zone:DNS:Edit cho tên miền của bạn.", "sslCertStatus": "Trạng thái chứng chỉ", "sslCertStatusNone": "Không có chứng chỉ", - "sslCertStatusValid": "Có hiệu lực", + "sslCertStatusValid": "Hợp lệ", "sslCertStatusExpiring": "Sắp hết hạn", - "sslCertStatusExpired": "Hết hạn", + "sslCertStatusExpired": "Đã hết hạn", "sslCertExpiresAt": "Hết hạn {{date}}", - "sslLastIssued": "Lần phát hành cuối cùng {{date}}", - "sslRequestCert": "Cấp/Gia hạn Giấy chứng nhận", - "sslRequestCertLoading": "Đang yêu cầu cấp giấy chứng nhận...", - "sslRequestCertSuccess": "Chứng chỉ đã được cấp và cài đặt thành công.", - "sslRequestCertFailed": "Yêu cầu cấp chứng chỉ không thành công", + "sslLastIssued": "Cấp lần cuối {{date}}", + "sslRequestCert": "Cấp / Gia hạn chứng chỉ", + "sslRequestCertLoading": "Đang yêu cầu chứng chỉ...", + "sslRequestCertSuccess": "Chứng chỉ đã được cấp và cài đặt thành công", + "sslRequestCertFailed": "Yêu cầu chứng chỉ thất bại", "sslSave": "Lưu cài đặt", - "sslSaved": "Cài đặt SSL đã được lưu", - "sslSaveFailed": "Không thể lưu cài đặt SSL.", - "sslRequiresDomain": "Tên miền và email là bắt buộc", - "sslInfoNote": "Sau khi cấp chứng chỉ, hãy bật SSL trong biến môi trường của bạn (ENABLE_SSL=true) và khởi động lại Termix.", - "auditLogTotal": "{{total}} tổng số mục", - "auditLogEmpty": "Không tìm thấy mục nhật ký kiểm toán nào.", + "sslSaved": "Đã lưu cài đặt SSL", + "sslSaveFailed": "Không thể lưu cài đặt SSL", + "sslRequiresDomain": "Cần có tên miền và email", + "sslInfoNote": "Sau khi cấp chứng chỉ, hãy bật SSL trong biến môi trường (ENABLE_SSL=true) và khởi động lại Termix.", + "sslManualOption": "Hướng dẫn thủ công (tải lên chứng chỉ)", + "sslManualCert": "Chứng chỉ (PEM)", + "sslManualCertPlaceholder": "-----BẮT ĐẦU GIẤY CHỨNG NHẬN-----", + "sslManualKey": "Khóa riêng tư (PEM)", + "sslManualKeyPlaceholder": "-----BEGIN PRIVATE KEY-----", + "sslManualDesc": "Hãy dán chứng chỉ và khóa riêng hiện có của bạn, bao gồm cả chuỗi chứng chỉ đầy đủ nếu CA yêu cầu.", + "sslManualUpload": "Tải lên và cài đặt chứng chỉ", + "sslManualUploadLoading": "Đang tải chứng chỉ lên...", + "sslManualUploadSuccess": "Chứng chỉ đã được tải lên và cài đặt thành công.", + "sslManualUploadFailed": "Tải lên chứng chỉ không thành công", + "sslManualRequiresFields": "Cần có chứng chỉ và khóa riêng.", + "auditLogTotal": "Tổng số {{total}} mục", + "auditLogEmpty": "Không tìm thấy mục nhật ký kiểm tra", "auditLogSuccess": "Thành công", "auditLogFailed": "Thất bại", "auditLogClearFilters": "Xóa bộ lọc", - "auditLogPage": "Trang {{page}} trên {{totalPages}} (tổng cộng{{total}} trang)", - "auditLogIp": "Địa chỉ IP", + "auditLogPage": "Trang {{page}} / {{totalPages}} (tổng {{total}})", + "auditLogIp": "IP", "auditLogResourceId": "ID tài nguyên", "auditLogFilterUser": "Người dùng", - "auditLogFilterAction": "Hoạt động", + "auditLogFilterAction": "Hành động", "auditLogFilterResourceType": "Loại tài nguyên", "auditLogFilterStatus": "Trạng thái", "auditLogFilterFrom": "Từ", - "auditLogFilterTo": "ĐẾN", + "auditLogFilterTo": "Đến", "auditLogFilterAll": "Tất cả", "allowRegistration": "Cho phép đăng ký người dùng", - "allowRegistrationDesc": "Cho phép người dùng mới tự đăng ký bằng tên người dùng và mật khẩu.", + "allowRegistrationDesc": "Cho phép người dùng mới tự đăng ký bằng tên người dùng và mật khẩu", "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/SSO khi đăng nhập lần đầu (độc lập với tùy chọn đăng ký)", - "oidcSilentLoginDefault": "Đăng nhập OIDC im lặng theo mặc định", - "oidcSilentLoginDefaultDesc": "Tự động chuyển hướng đến trang đăng nhập OIDC mỗi khi truy cập, bỏ qua hoàn toàn biểu mẫu đăng nhập.", + "oidcAutoProvisionDesc": "Tự động tạo tài khoản cho người dùng OIDC/SSO khi đăng nhập lần đầu (độc lập với nút bật đăng ký)", + "oidcSilentLoginDefault": "Đăng nhập OIDC tự động mặc định", + "oidcSilentLoginDefaultDesc": "Tự động chuyển hướng đến đăng nhập OIDC mỗi lần truy cập, bỏ qua hoàn toàn biểu mẫu đăng nhập", "allowPasswordReset": "Cho phép đặt lại mật khẩu", - "allowPasswordResetDesc": "Đặt lại mã thông qua nhật ký Docker", + "allowPasswordResetDesc": "Mã đặt lại qua nhật ký Docker", "commandHistoryEnabled": "Lịch sử lệnh", - "commandHistoryEnabledDesc": "Cho phép ghi lại lịch sử lệnh. Khi tùy chọn này bị vô hiệu hóa, lịch sử sẽ không được lưu bất kể cài đặt trên từng máy chủ.", - "updateCommandHistoryFailed": "Không thể cập nhật cài đặt lịch sử lệnh.", - "sessionTimeout": "Hết thời gian phiên", + "commandHistoryEnabledDesc": "Cho phép ghi lịch sử lệnh. Khi tắt, lịch sử sẽ không được lưu bất kể cài đặt từng máy chủ.", + "updateCommandHistoryFailed": "Không thể cập nhật cài đặt lịch sử lệnh", + "analyticsEnabled": "Chia sẻ số liệu thống kê sử dụng ẩn danh", + "analyticsEnabledDesc": "Gửi báo cáo hàng ngày ẩn danh về số lượng người dùng, máy chủ và mức sử dụng tính năng để giúp cải thiện Termix. Không có dữ liệu cá nhân hoặc chi tiết kết nối nào được tiết lộ.", + "updateAnalyticsFailed": "Không thể cập nhật cài đặt phân tích.", + "sessionSharingGloballyEnabled": "Cho phép chia sẻ phiên", + "sessionSharingGloballyEnabledDesc": "Cho phép chia sẻ các phiên terminal trực tiếp, RDP, VNC và Telnet trên toàn bộ máy chủ. Khi bị vô hiệu hóa, tùy chọn này sẽ ghi đè lên mọi tùy chọn chia sẻ trên từng máy chủ riêng lẻ.", + "updateSessionSharingFailed": "Không thể cập nhật cài đặt chia sẻ phiên.", + "sessionTimeout": "Thời gian chờ 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", + "sessionTimeoutRange": "Tối thiểu 1h · Tối đa 720h", + "monitoringDefaults": "Mặc định giám sát", "statusCheck": "Kiểm tra trạng thái", "metrics": "Số liệu", "sec": "giây", - "logLevel": "Mức độ ghi nhật ký", + "logLevel": "Mức nhật ký", "enableGuacamole": "Bật Guacamole", - "enableGuacamoleDesc": "Máy tính từ xa RDP/VNC", + "enableGuacamoleDesc": "Kết nối RDP/VNC từ xa", "enableGuacamoleDocsLink": "Xem tài liệu", "guacdUrl": "URL guacd", "tailscaleApiKey": "Khóa API Tailscale", - "tailscaleApiKeyDescription": "Được sử dụng để phát hiện thiết bị trong trình chỉnh sửa máy chủ. Tạo khóa tại tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDescription": "Dùng để khám phá thiết bị trong trình chỉnh sửa máy chủ. Tạo khóa tại tailscale.com/admin/settings/keys.", "tailscaleApiKeyDocsLink": "Xem tài liệu", - "oidcDescription": "Cấu hình OpenID Connect cho SSO. Các trường được đánh dấu * là bắt buộc.", + "oidcDescription": "Cấu hình OpenID Connect cho SSO. Các trường đánh dấu * là bắt buộc.", "oidcDocsLink": "Xem tài liệu", - "oidcClientId": "Mã khách hàng", - "oidcClientSecret": "Bí mật khách hàng", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", "oidcAuthUrl": "URL ủy quyền", - "oidcIssuerUrl": "URL của tổ chức phát hành", - "oidcTokenUrl": "URL mã thông báo", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", "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", + "oidcUserinfoUrl": "Ghi đè URL Userinfo", "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ả.", - "oidcAdminGroup": "Nhóm quản trị", - "oidcAdminGroupDesc": "Người dùng trong nhóm này được cấp quyền quản trị. Để trống để tắt tính năng đồng bộ nhóm.", - "oidcGroupClaim": "Yêu cầu bồi thường tập thể", - "oidcGroupClaimDesc": "Tùy chọn. Đường dẫn yêu cầu chứa các nhóm của người dùng. Mặc định là nhóm, vai trò, sau đó là nhóm. Sử dụng tùy chọn này cho các nhà cung cấp có yêu cầu tùy chỉnh (ví dụ: Zitadel).", + "oidcAllowedUsersDesc": "Mỗi email một dòng. Để trống để cho phép tất cả.", + "oidcAdminGroup": "Nhóm Admin", + "oidcAdminGroupDesc": "Người dùng trong nhóm này được cấp quyền admin. Để trống để tắt đồng bộ nhóm.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Tùy chọn. Đường dẫn claim chứa các nhóm của người dùng. Mặc định là groups, roles, rồi group. Sử dụng cho các nhà cung cấp có claim tùy chỉnh (ví dụ: Zitadel).", "oidcCaCert": "Chứng chỉ CA tùy chỉnh", - "oidcCaCertDesc": "Tùy chọn. Chứng chỉ CA được mã hóa PEM dành cho các nhà cung cấp OIDC sử dụng CA riêng hoặc tự ký. Để trống để sử dụng kho lưu trữ chứng chỉ tin cậy của hệ thống.", - "removeOidc": "Di dời", + "oidcCaCertDesc": "Tùy chọn. Chứng chỉ CA mã hóa PEM cho các nhà cung cấp OIDC sử dụng CA riêng hoặc tự ký. Để trống để sử dụng kho tin cậy hệ thống.", + "removeOidc": "Xóa", "usersCount": "{{count}} người dùng", - "createUser": "Tạo nên", + "createUser": "Tạo", "newRole": "Vai trò mới", "roleName": "Tên", "roleDisplayName": "Tên hiển thị", - "roleDescription": "Sự miêu tả", + "roleDescription": "Mô tả", "rolesCount": "{{count}} vai trò", - "createRole": "Tạo nên", + "createRole": "Tạo", "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...", + "exportDatabaseDesc": "Tải về bản sao lưu tất cả máy chủ, thông tin đăng nhập và cài đặt", + "export": "Xuất", + "exporting": "Đang xuất...", "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", + "changeFile": "Đổi tệp", + "import": "Nhập", + "importing": "Đang nhập...", + "apiKeysCount": "{{count}} khóa", "apiKeysDocsLink": "Xem tài liệu", "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.", + "apiKeyCreatedWarning": "Khóa đã tạo - sao chép ngay, sẽ không hiển thị lại.", "apiKeyName": "Tên", "apiKeyUser": "Người dùng", "apiKeySelectUser": "Chọn người dùng...", - "apiKeyExpiresAt": "Hết hạn vào lúc", + "apiKeyExpiresAt": "Hết hạn 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", "authTypeOidc": "OIDC", - "authTypeLocal": "Địa phương", + "authTypeLocal": "Cục bộ", "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", + "customBadge": "TÙY CHỈNH", "youBadge": "BẠN", "sessionsActive": "{{count}} đang hoạt động", - "sessionActive": "Đang hoạt động: {{time}}", - "sessionExpires": "Exp: {{time}}", + "sessionActive": "Hoạt động: {{time}}", + "sessionExpires": "Hết hạn: {{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", + "revokeAllSessionsSuccess": "Đã thu hồi tất cả phiên của người dùng", + "revokeAllSessionsFailed": "Không thể thu hồi phiên", + "revokeSessionFailed": "Không thể thu hồi phiên", "addRole": "Thêm vai trò", - "noCustomRoles": "Không có vai trò tùy chỉnh nào được định nghĩa.", + "noCustomRoles": "Chưa định nghĩa vai trò tùy chỉnh", "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ị.", + "userAdminAccessDesc": "Toàn quyền truy cập tất cả cài đặt 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ị.", + "revokeAllUserSessions": "Thu hồi tất cả phiên", + "revokeAllUserSessionsDesc": "Buộc đăng nhập lại trên tất 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", + "deleteUserFailed": "Không thể xóa người dùng", + "deleteUserSuccess": "Đã xóa người dùng \"{{username}}\"", + "deleteRoleSuccess": "Đã xóa vai trò \"{{name}}\"", + "revokeKeySuccess": "Đã thu hồi khóa \"{{name}}\"", + "revokeKeyFailed": "Không thể thu hồi khóa", + "copiedToClipboard": "Đã sao chép vào bộ nhớ tạm", + "done": "Hoàn tất", "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", + "createUserUsername": "Tên đăng nhập", "createUserPassword": "Mật khẩu", "createUserPasswordHint": "Tối thiểu 6 ký tự.", - "createUserEnterUsername": "Nhập tên người dùng", + "createUserEnterUsername": "Nhập tên đăng nhập", "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", + "editUserDesc": "Chỉnh sửa vai trò, trạng thái quản trị, phiên và cài đặt tài khoản.", + "editUserUsername": "Tên đăng nhập", "editUserAuthType": "Loại xác thực", - "editUserAdminStatus": "Trạng thái quản trị viên", + "editUserAdminStatus": "Trạng thái quản trị", "editUserUserId": "ID người dùng", "linkAccountTitle": "Liên kết tài khoản", - "linkAccountDesc": "Hợp nhất tài khoản OIDC {{username}} với tài khoản cục bộ hiện có.", - "linkAccountDescLocal": "Liên kết tài khoản cục bộ {{username}} với tài khoản chỉ dành cho OIDC 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 tài khoản cục bộ", - "linkAccountTargetPlaceholder": "Nhập tên người dùng tài khoản cục bộ cần liên kết.", - "linkAccountOidcUsername": "Tên người dùng tài khoản OIDC", - "linkAccountOidcPlaceholder": "Nhập tên người dùng tài khoản chỉ dành cho OIDC để hợp nhất.", - "linkAccountOidcNotFound": "Không tìm thấy tài khoản chỉ dành cho OIDC nào có tên người dùng đó.", + "linkAccountDesc": "Hợp nhất tài khoản OIDC {{username}} với một tài khoản cục bộ hiện có.", + "linkAccountDescLocal": "Liên kết tài khoản cục bộ {{username}} với một tài khoản chỉ dùng OIDC hiện có.", + "linkAccountWarningTitle": "Hành động này sẽ:", + "linkAccountEffect1": "Xóa tài khoản chỉ dùng OIDC", + "linkAccountEffect2": "Thêm đăng nhập OIDC vào tài khoản đích", + "linkAccountEffect3": "Cho phép cả đăng nhập OIDC và mật khẩu", + "linkAccountTargetUsername": "Tên đăng nhập tài khoản cục bộ", + "linkAccountTargetPlaceholder": "Nhập tên đăng nhập tài khoản cục bộ để liên kết", + "linkAccountOidcUsername": "Tên đăng nhập tài khoản OIDC", + "linkAccountOidcPlaceholder": "Nhập tên đăng nhập tài khoản chỉ dùng OIDC để hợp nhất vào", + "linkAccountOidcNotFound": "Không tìm thấy tài khoản chỉ dùng OIDC nào với tên đăng nhập đó", "linkAccounts": "Liên kết tài khoản", - "linkAccountSuccess": "Tài khoản đã được liên kết thành công", - "linkAccountFailed": "Không thể liên kết tài khoản", + "linkAccountSuccess": "Liên kết tài khoản thành công", + "linkAccountFailed": "Liên kết tài khoản thất bại", "linkAccountInProgress": "Đang liên kết...", "unlinkAccountTitle": "Hủy liên kết OIDC", - "unlinkAccountDesc": "Xóa xác thực OIDC khỏi {{username}}. Họ sẽ chỉ có thể đăng nhập bằng mật khẩu của mình.", - "unlinkAccountWarning": "Thao tác này sẽ xóa quyền đăng nhập OIDC khỏi tài khoản này. Người dùng cần phải thiết lập mật khẩu để tiếp tục đăng nhập.", + "unlinkAccountDesc": "Xóa xác thực OIDC khỏi {{username}}. Người dùng sẽ chỉ có thể đăng nhập bằng mật khẩu.", + "unlinkAccountWarning": "Hành động này sẽ xóa đăng nhập OIDC khỏi tài khoản này. Người dùng phải thiết lập mật khẩu để tiếp tục đăng nhập.", "unlinkAccount": "Hủy liên kết OIDC", "unlinkAccountInProgress": "Đang hủy liên kết...", - "unlinkAccountSuccess": "OIDC đã được ngắt kết nối thành công.", - "unlinkAccountFailed": "Không thể hủy liên kết OIDC", + "unlinkAccountSuccess": "Hủy liên kết OIDC thành công", + "unlinkAccountFailed": "Hủy liên kết OIDC thất bại", "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.", - "cannotDisablePasswordLoginWithTotp": "Không thể vô hiệu hóa đăng nhập bằng mật khẩu khi xác thực hai yếu tố (2FA) đang được bật cho một hoặc nhiều người dùng. Hãy tắt 2FA trước.", - "updateOidcAutoProvisionFailed": "Không thể cập nhật cài đặt tự động cấp phép OIDC.", - "updateOidcSilentLoginDefaultFailed": "Không thể cập nhật cài đặt đăng nhập OIDC im lặng.", - "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ờ.", + "updateRegistrationFailed": "Cập nhật cài đặt đăng ký thất bại", + "updatePasswordLoginFailed": "Cập nhật cài đặt đăng nhập bằng mật khẩu thất bại", + "cannotDisablePasswordLoginWithTotp": "Không thể vô hiệu hóa đăng nhập bằng mật khẩu khi 2FA đang được bật cho một hoặc nhiều người dùng. Hãy tắt 2FA trước.", + "updateOidcAutoProvisionFailed": "Cập nhật cài đặt tự động cấp quyền OIDC thất bại", + "updateOidcSilentLoginDefaultFailed": "Cập nhật cài đặt đăng nhập OIDC im lặng thất bại", + "updatePasswordResetFailed": "Cập nhật cài đặt đặt lại mật khẩu thất bại", + "sessionTimeoutRange2": "Thời gian chờ phiên phải 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.", + "sessionTimeoutSaveFailed": "Lưu thời gian chờ phiên thất bại", "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.", - "metricsHistoryRetention": "Lưu trữ lịch sử số liệu", - "metricsHistoryRetentionRange": "Từ 1 đến 90 ngày", + "monitoringSaveFailed": "Lưu cài đặt giám sát thất bại", + "metricsHistoryRetention": "Thời gian lưu trữ lịch sử số liệu", + "metricsHistoryRetentionRange": "1 đến 90 ngày", "days": "ngày", - "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.", - "tailscaleSettingsSaved": "Cài đặt quy mô đuôi đã được lưu", - "tailscaleSettingsSaveFailed": "Không thể lưu cài đặt Tailscale.", - "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", + "guacamoleSaved": "Cài đặt Guacamole đã được lưu", + "guacamoleSaveFailed": "Lưu cài đặt Guacamole thất bại", + "guacamoleUpdateFailed": "Cập nhật cài đặt Guacamole thất bại", + "tailscaleSettingsSaved": "Cài đặt Tailscale đã được lưu", + "tailscaleSettingsSaveFailed": "Lưu cài đặt Tailscale thất bại", + "logLevelUpdateFailed": "Cập nhật mức ghi nhật ký thất bại", + "oidcSaved": "Cấu hình OIDC đã được lưu", + "oidcSaveFailed": "Lưu cấu hình OIDC thất bại", + "oidcRemoved": "Cấu hình OIDC đã được gỡ bỏ", + "oidcRemoveFailed": "Gỡ bỏ cấu hình OIDC thất bại", + "createUserRequired": "Tên đăng nhập 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}}\" đã được tạo", + "createUserFailed": "Tạo người dùng thất bại", + "updateAdminStatusFailed": "Cập nhật trạng thái quản trị viên thất bại", + "allSessionsRevoked": "Tất cả các phiên đã bị thu hồi", + "revokeSessionsFailed": "Thu hồi phiên thất bại", "manageUserData": "Quản lý dữ liệu người dùng", - "backToUsers": "Trở lại trang người dùng", + "backToUsers": "Quay lại danh sách người dùng", "manageTabAccount": "Tài khoản", - "manageTabHosts": "Người dẫn chương trình", + "manageTabHosts": "Máy chủ", "manageTabCredentials": "Thông tin xác thực", - "manageTabSnippets": "Những đoạn trích", - "manageTabSessions": "Phiên họp", - "manageTabDanger": "Sự nguy hiểm", + "manageTabSnippets": "Đoạn mã", + "manageTabSessions": "Phiên", + "manageTabDanger": "Nguy hiểm", "manageEditorBack": "Quay lại dữ liệu của {{username}}", "dataLockedBadge": "ĐÃ KHÓA", - "dataLockedNotice": "Dữ liệu của {{username}}sẽ bị khóa cho đến khi họ đăng nhập lần tiếp theo. Máy chủ, thông tin đăng nhập và đoạn mã của họ không thể được xem hoặc chỉnh sửa cho đến lúc đó.", + "dataLockedNotice": "Dữ liệu của {{username}} vẫn bị khóa cho đến khi họ đăng nhập lần tiếp theo. Máy chủ, thông tin đăng nhập và đoạn mã của họ không thể xem hoặc chỉnh sửa cho đến lúc đó.", "resetPasswordTitle": "Đặt lại mật khẩu", - "resetPasswordOidcOnly": "Người dùng này đăng nhập thông qua nhà cung cấp bên ngoài và không có mật khẩu.", + "resetPasswordOidcOnly": "Người dùng này đăng nhập qua nhà cung cấp bên ngoài và không có mật khẩu.", "resetPasswordPlaceholder": "Mật khẩu mới", - "resetPasswordBtn": "Cài lại", - "resetPasswordWorking": "Đang thiết lập lại...", - "resetPasswordSuccess": "Đặt lại mật khẩu", - "resetPasswordSuccessWiped": "Đã đặt lại mật khẩu. Dữ liệu được mã hóa của người dùng đã bị xóa.", - "resetPasswordFailed": "Không thể đặt lại mật khẩu.", - "resetPasswordConfirmWipe": "{{username}} chưa đăng nhập kể từ khi nâng cấp mã hóa, vì vậy dữ liệu của họ không thể khôi phục. Việc đặt lại ngay bây giờ sẽ xóa máy chủ, thông tin đăng nhập và đoạn mã của họ. Tiếp tục?", + "resetPasswordBtn": "Đặt lại", + "resetPasswordWorking": "Đang đặt lại...", + "resetPasswordSuccess": "Đã đặt lại mật khẩu", + "resetPasswordSuccessWiped": "Đã đặt lại mật khẩu. Dữ liệu mã hóa của người dùng đã bị xóa.", + "resetPasswordFailed": "Không thể đặt lại mật khẩu", + "resetPasswordConfirmWipe": "{{username}} chưa đăng nhập kể từ khi nâng cấp mã hóa, do đó dữ liệu của họ không thể khôi phục. Đặt lại ngay bây giờ sẽ xóa máy chủ, thông tin đăng nhập và đoạn mã của họ. Tiếp tục?", "totpSectionTitle": "Xác thực hai yếu tố", - "totpStatusEnabled": "TOTP đã được bật cho người dùng này.", - "totpStatusDisabled": "TOTP chưa được bật cho người dùng này.", + "totpStatusEnabled": "TOTP đã được bật cho người dùng này", + "totpStatusDisabled": "TOTP chưa được bật cho người dùng này", "disableTotp": "Vô hiệu hóa", - "disableTotpConfirm": "Vô hiệu hóa xác thực hai yếu tố cho {{username}}? Họ sẽ có thể đăng nhập chỉ bằng mật khẩu của mình.", - "totpDisabledSuccess": "TOTP bị vô hiệu hóa", - "totpDisableFailed": "Không thể vô hiệu hóa TOTP.", + "disableTotpConfirm": "Vô hiệu hóa xác thực hai yếu tố cho {{username}}? Họ sẽ có thể đăng nhập chỉ bằng mật khẩu.", + "totpDisabledSuccess": "Đã vô hiệu hóa TOTP", + "totpDisableFailed": "Không thể vô hiệu hóa TOTP", "manageApiKeys": "Khóa API", "apiKeyNamePlaceholder": "Tên khóa", - "apiKeyCopyNotice": "Hãy sao chép khóa này ngay bây giờ, nó sẽ không hiển thị lại nữa.", + "apiKeyCopyNotice": "Sao chép khóa này ngay, sẽ không hiển thị lại.", "noApiKeysForUser": "Không có khóa API", - "apiKeyDeleteFailed": "Không thể xóa khóa API.", + "apiKeyDeleteFailed": "Không thể xóa khóa API", "exportUserData": "Xuất dữ liệu", - "exportUserDataDesc": "Tải xuống dữ liệu về máy chủ, thông tin đăng nhập và trình quản lý tập tin của người dùng này dưới dạng JSON. Thông tin bí mật đã được giải mã.", - "exportUserDataSuccess": "Dữ liệu người dùng đã xuất", - "exportUserDataFailed": "Không thể xuất dữ liệu người dùng.", + "exportUserDataDesc": "Tải xuống máy chủ, thông tin đăng nhập và dữ liệu quản lý tệp của người dùng này dưới dạng JSON. Các bí mật được giải mã.", + "exportUserDataSuccess": "Đã xuất dữ liệu người dùng", + "exportUserDataFailed": "Không thể xuất dữ liệu người dùng", "hostsCount": "{{count}} máy chủ", "addHostForUser": "Thêm máy chủ", - "noHostsForUser": "Người dùng này không có máy chủ", + "noHostsForUser": "Người dùng này không có máy chủ nào", "connectToHost": "Kết nối", "deleteHostConfirm": "Xóa máy chủ \"{{name}}\" thuộc về {{username}}?", - "hostDeletedSuccess": "Máy chủ đã bị xóa", + "hostDeletedSuccess": "Đã xóa máy chủ", "hostDeleteFailed": "Không thể xóa máy chủ", - "credentialsCount": "{{count}} thông tin xác thực", + "credentialsCount": "{{count}} thông tin đăng nhập", "addCredentialForUser": "Thêm thông tin đăng nhập", - "noCredentialsForUser": "Người dùng này không có thông tin đăng nhập.", - "deleteCredentialConfirm": "Xóa thông tin xác thực \"{{name}}\" thuộc về {{username}}?", - "credentialDeletedSuccess": "Thông tin đăng nhập đã bị xóa", - "credentialDeleteFailed": "Không thể xóa thông tin đăng nhập", - "snippetsCount": "{{count}} đoạn trích", + "noCredentialsForUser": "Người dùng này không có thông tin xác thực", + "deleteCredentialConfirm": "Xóa thông tin xác thực \"{{name}}\" của {{username}}?", + "credentialDeletedSuccess": "Đã xóa thông tin xác thực", + "credentialDeleteFailed": "Xóa thông tin xác thực thất bại", + "snippetsCount": "{{count}} đoạn mã", "addSnippetForUser": "Thêm đoạn mã", - "noSnippetsForUser": "Người dùng này không có đoạn trích nào.", - "snippetRequiredFields": "Tên và nội dung đoạn mã là bắt buộc", - "snippetNamePlaceholder": "Tên đoạn trích", + "noSnippetsForUser": "Người dùng này không có đoạn mã nào", + "snippetRequiredFields": "Cần tên và nội dung đoạn mã", + "snippetNamePlaceholder": "Tên đoạn mã", "snippetContentPlaceholder": "Nội dung lệnh", "snippetFolderPlaceholder": "Thư mục (tùy chọn)", - "snippetSaved": "Đoạn mã đã được lưu", - "snippetSaveFailed": "Không thể lưu đoạn mã", - "deleteSnippetConfirm": "Xóa đoạn mã \"{{name}}\" thuộc về {{username}}?", - "snippetDeletedSuccess": "Đoạn văn đã bị xóa", - "snippetDeleteFailed": "Không thể xóa đoạn mã", - "noSessionsForUser": "Không có phiên hoạt động nào", - "deleteUserDangerDesc": "Xóa vĩnh viễn {{username}} và tất cả dữ liệu của chúng (máy chủ, thông tin đăng nhập, đoạn trích, lịch sử). Thao tác này không thể hoàn tác.", - "deleteUserConfirm": "Xóa vĩnh viễn {{username}} và tất cả dữ liệu của chúng?", - "deleteUserAdminBlocked": "Hãy gỡ bỏ quyền quản trị trước khi xóa người dùng này.", - "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.", + "snippetSaved": "Đã lưu đoạn mã", + "snippetSaveFailed": "Lưu đoạn mã thất bại", + "deleteSnippetConfirm": "Xóa đoạn mã \"{{name}}\" của {{username}}?", + "snippetDeletedSuccess": "Đã xóa đoạn mã", + "snippetDeleteFailed": "Xóa đoạn mã thất bại", + "noSessionsForUser": "Không có phiên làm việc đang hoạt động", + "deleteUserDangerDesc": "Xóa vĩnh viễn {{username}} và toàn bộ dữ liệu (máy chủ, thông tin xác thực, đoạn mã, lịch sử). Không thể hoàn tác.", + "deleteUserConfirm": "Xóa vĩnh viễn {{username}} và toàn bộ dữ liệu?", + "deleteUserAdminBlocked": "Hãy bỏ quyền quản trị trước khi xóa người dùng này.", + "createRoleRequired": "Cần tên và tên hiển thị", + "createRoleSuccess": "Đã tạo vai trò \"{{name}}\"", + "createRoleFailed": "Tạo vai trò thất bại", + "apiKeyNameRequired": "Cần tên khóa", + "apiKeyUserRequired": "Cần ID người dùng", + "apiKeyCreatedSuccess": "Đã tạo khóa API \"{{name}}\"", + "apiKeyCreateFailed": "Tạo khóa API thất bại", + "exportSuccess": "Đã xuất cơ sở dữ liệu 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", - "sectionHostDefaults": "Cài đặt mặc định của máy chủ", - "hostDefaultsDesc": "Các thiết lập được áp dụng tự động khi tạo máy chủ mới. Tuy nhiên, người dùng vẫn có thể ghi đè lên các thiết lập này.", + "importCompleted": "Đã nhập xong: đã nhập {{total}} mục, bỏ qua {{skipped}}", + "importFailed": "Nhập thất bại: {{error}}", + "importError": "Nhập cơ sở dữ liệu thất bại", + "sectionHostDefaults": "Mặc định máy chủ", + "hostDefaultsDesc": "Cài đặt được áp dụng tự động khi tạo máy chủ mới. Các máy chủ riêng lẻ vẫn có thể ghi đè.", "hostDefaultsSocks5": "Proxy SOCKS5", "hostDefaultsUseSocks5": "Bật Proxy SOCKS5", - "hostDefaultsUseSocks5Desc": "Cài đặt sẵn proxy SOCKS5 trên tất cả các máy chủ mới.", - "hostDefaultsSocks5Host": "Máy chủ/Cổng Proxy", - "hostDefaultsSocks5Username": "Tên người dùng ủy quyền", - "hostDefaultsSocks5Password": "Mật khẩu Proxy", + "hostDefaultsUseSocks5Desc": "Điền sẵn proxy SOCKS5 cho tất cả máy chủ mới", + "hostDefaultsSocks5Host": "Máy chủ / Cổng proxy", + "hostDefaultsSocks5Username": "Tên đăng nhập proxy", + "hostDefaultsSocks5Password": "Mật khẩu proxy", "hostDefaultsMetrics": "Số liệu máy chủ", - "hostDefaultsMetricsEnabled": "Kích hoạt số liệu", - "hostDefaultsMetricsEnabledDesc": "Thu thập số liệu thống kê về CPU, bộ nhớ và các thông số khác trên các máy chủ mới theo mặc định.", + "hostDefaultsMetricsEnabled": "Bật số liệu", + "hostDefaultsMetricsEnabledDesc": "Mặc định thu thập CPU, bộ nhớ và các số liệu khác trên máy chủ mới", "hostDefaultsStatusCheckEnabled": "Bật kiểm tra trạng thái", - "hostDefaultsStatusCheckEnabledDesc": "Theo mặc định, hãy kiểm tra trạng thái trực tuyến/ngoại tuyến của các máy chủ mới.", - "hostDefaultsTerminal": "Phần cuối", - "hostDefaultsSessionLogging": "Ghi nhật ký phiên", - "hostDefaultsSessionLoggingDesc": "Ghi lại các phiên làm việc trên thiết bị đầu cuối trên các máy chủ mới theo mặc định.", + "hostDefaultsStatusCheckEnabledDesc": "Mặc định kiểm tra trạng thái trực tuyến/ngoại tuyến trên máy chủ mới", + "hostDefaultsTerminal": "Terminal", + "hostDefaultsSessionLogging": "Ghi phiên", + "hostDefaultsSessionLoggingDesc": "Mặc định ghi lại phiên terminal trên máy chủ mới", "hostDefaultsCommandHistory": "Lịch sử lệnh", - "hostDefaultsCommandHistoryDesc": "Theo dõi lịch sử lệnh trên các máy chủ mới theo mặc định", - "hostDefaultsSaved": "Cài đặt mặc định của máy chủ đã được lưu", - "hostDefaultsSaveFailed": "Không thể lưu cài đặt mặc định của máy chủ.", + "hostDefaultsCommandHistoryDesc": "Mặc định theo dõi lịch sử lệnh trên máy chủ mới", + "hostDefaultsSaved": "Đã lưu mặc định máy chủ", + "hostDefaultsSaveFailed": "Không lưu được mặc định máy chủ", "rolePermissions": { - "count": "{{count}} quyền hạn", + "count": "{{count}} quyền", "editAction": "Chỉnh sửa quyền", - "loadError": "Không thể tải danh mục quyền.", - "saved": "Quyền hạn vai trò đã được lưu", - "saveError": "Không thể lưu quyền quản trị", - "save": "Cứu", + "loadError": "Không tải được danh mục quyền", + "saved": "Đã lưu quyền vai trò", + "saveError": "Không lưu được quyền vai trò", + "save": "Lưu", "saving": "Đang lưu..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Chủ nhà", - "hostPlaceholder": "192.168.1.1 hoặc example.com", - "portLabel": "Cảng", + "hostLabel": "Máy chủ", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Cổng", "portPlaceholder": "22", - "usernameLabel": "Tên người dùng", - "usernamePlaceholder": "tên người dùng", + "usernameLabel": "Tên đăng nhập", + "usernamePlaceholder": "tên đăng nhập", "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ỉ", + "privateKeyLabel": "Khóa riêng", + "privateKeyPlaceholder": "Dán khóa riêng...", + "credentialLabel": "Thông tin đăng nhập", "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" + "connectToTerminal": "Kết nối đến Terminal", + "connectToFiles": "Kết nối đến 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...", + "noTerminalSelected": "Chưa chọn terminal", + "noTerminalSelectedHint": "Mở một tab terminal SSH để xem lịch sử lệnh", + "searchPlaceholder": "Tìm kiếm lịch sử...", "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": "Kích hoạt chức năng này trong cài đặt thiết bị đầu cuối của máy chủ." + "noHistoryEntries": "Không có mục lịch sử", + "trackingDisabled": "Tính năng ghi lịch sử đã bị tắt", + "trackingDisabledHint": "Bật tính năng này trong cài đặt terminal của máy chủ." }, "sshTools": { - "keyRecordingTitle": "Ghi âm chính", - "recordToTerminals": "Ghi vào thiết bị đầu cuối", + "keyRecordingTitle": "Ghi phím", + "recordToTerminals": "Ghi vào các terminal", "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...", + "selectNone": "Không chọn", + "noTerminalTabsOpen": "Không có tab terminal nào mở", + "selectTerminalsAbove": "Chọn các terminal ở trên", + "broadcastInputPlaceholder": "Nhập vào đây để phát tán thao tác phím...", "fillPassword": "Điền mật khẩu", - "fillPasswordSuccess": "Đã nhập mật khẩu vào {{count}} thiết bị đầu cuối", - "fillPasswordMissing": "Không có mật khẩu nào được lưu cho {{count}} thiết bị đầu cuối đã chọn", - "stopRecording": "Dừng ghi âm", - "startRecording": "Bắt đầu ghi âm", + "fillPasswordSuccess": "Đã điền mật khẩu vào {{count}} terminal", + "fillPasswordMissing": "Không có mật khẩu đã lưu cho {{count}} terminal đã chọn", + "stopRecording": "Dừng ghi", + "startRecording": "Bắt đầu ghi", "settingsTitle": "Cài đặt", - "enableRightClickCopyPaste": "Cho phép sao chép/dán bằng chuột phải." + "enableRightClickCopyPaste": "Bật sao chép/dán bằng chuột phải" }, "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", + "layoutTitle": "Bố cục", + "selectLayoutAbove": "Chọn bố cục bên trên", + "selectLayoutHint": "Chọn số lượng ô hiển thị", + "panesTitle": "Các ô", "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", + "dragTabsHint": "Kéo tab vào các ô phía trên, hoặc dùng Gán nhanh", + "dropHere": "Thả vào đâ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", - "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", + "clearSplitScreen": "Xóa chia màn hình", + "quickAssign": "Gán nhanh", + "alreadyAssigned": "Khung {{index}}", + "splitTab": "Tách tab", + "addToSplit": "Thêm vào chia màn hình", + "removeFromSplit": "Xóa khỏi chia màn hình", + "assignToPane": "Gán vào khung", "hotkeysTitle": "Phím tắt", - "hotkeysSplitRight": "Bật/tắt chế độ chia 2 chiều", - "hotkeysSplitBelow": "Bật/tắt chế độ chia 3 chiều", - "hotkeysNavigatePane": "Điều hướng các ngăn", + "hotkeysSplitRight": "Bật/tắt chia đôi", + "hotkeysSplitBelow": "Bật/tắt chia ba", + "hotkeysNavigatePane": "Điều hướng khung", "hotkeysNextTab": "Tab tiếp theo", "hotkeysPrevTab": "Tab trước" }, "snippets": { - "title": "Những đoạn trích", + "title": "Đoạn mã", "createSnippetTitle": "Tạo đoạn mã", - "createSnippetDescription": "Tạo một đoạn mã lệnh mới để thực thi nhanh.", + "createSnippetDescription": "Tạo đ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ả", + "descriptionLabel": "Mô tả", "descriptionPlaceholder": "Mô tả tùy chọn", - "optional": "Không bắt buộc", + "optional": "Tùy chọn", "folderLabel": "Thư mục", - "noFolder": "Không có thư mục (Chưa được phân loại)", - "commandLabel": "Yêu cầu", + "noFolder": "Không thư mục (Chưa phân loại)", + "commandLabel": "Lệnh", "commandPlaceholder": "Ví dụ: sudo systemctl restart nginx", - "cancel": "Hủy bỏ", + "cancel": "Hủy", "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.", + "createFolderDescription": "Sắp xếp đoạn mã 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", + "folderNamePlaceholder": "Ví dụ: Lệnh hệ thống, Docker Scripts", "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", + "targetTerminals": "Terminal đích", "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", + "selectNone": "Không chọn", + "noTerminalTabsOpen": "Không có tab terminal nào mở", + "searchPlaceholder": "Tìm kiếm snippet...", + "newSnippet": "Snippet 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", + "noSnippetsInFolder": "Không có snippet nào trong thư mục này", + "uncategorized": "Chưa phân loại", + "editSnippetTitle": "Chỉnh sửa snippet", + "editSnippetDescription": "Cập nhật snippet 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", + "createSuccess": "Đã tạo snippet thành công", + "createFailed": "Tạo snippet thất bại", + "updateSuccess": "Đã cập nhật snippet thành công", + "updateFailed": "Cập nhật snippet thất bại", + "deleteFailed": "Xóa snippet thất bại", + "folderCreateSuccess": "Đã tạo thư mục thành công", + "folderCreateFailed": "Tạo thư mục thất bại", "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.", + "editFolderDescription": "Đổi tên hoặc thay đổi hình thức 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.", + "folderDeleteSuccess": "Đã xóa thư mục \"{{name}}\"", + "folderDeleteFailed": "Xóa thư mục thất bại", + "folderEditSuccess": "Đã cập nhật thư mục thành công", + "folderEditFailed": "Cập nhật thư mục thất bại", "confirmRunMessage": "Chạy \"{{name}}\"?", "confirmRunButton": "Chạy", - "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "runSuccess": "Đã chạy \"{{name}}\" trên {{count}} terminal", "copySuccess": "Đã sao chép \"{{name}}\" vào clipboard", - "shareTitle": "Chia sẻ đoạn trích", + "shareTitle": "Chia sẻ snippet", "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ẻ", + "selectRole": "Chọn vai trò...", + "shareSuccess": "Đã chia sẻ đoạn mã thành công", + "shareFailed": "Chia sẻ đoạn mã thất bại", + "revokeSuccess": "Đã thu hồi quyền truy cập", + "revokeFailed": "Thu hồi quyền truy cập thất bại", + "currentAccess": "Quyền truy cập hiện tại", + "shareLoadError": "Tải dữ liệu chia sẻ thất bại", "loading": "Đang tải...", "close": "Đóng", - "reorderFailed": "Không thể lưu thứ tự đoạn mã.", - "importExport": "Nhập khẩu / Xuất khẩu", + "reorderFailed": "Lưu thứ tự đoạn mã thất bại", + "importExport": "Nhập / Xuất", "exportBtn": "Xuất JSON", "importBtn": "Nhập JSON", - "exportSuccess": "Các đoạn mã đã được xuất khẩu thành công.", - "exportFailed": "Không thể xuất đoạn mã.", + "exportSuccess": "Đã xuất đoạn mã thành công", + "exportFailed": "Xuất đoạn mã thất bại", "importTitle": "Nhập đoạn mã", - "importDescription": "Nhập các đoạn mã và thư mục từ tệp JSON được xuất bởi Termix.", - "importDropOrClick": "Kéo thả tệp JSON vào đây hoặc nhấp vào đây để duyệt.", + "importDescription": "Nhập đoạn mã và thư mục từ tệp JSON được Termix xuất ra", + "importDropOrClick": "Kéo thả tệp JSON vào đây hoặc nhấn để duyệt", "importSelectedFile": "Đã chọn: {{name}}", - "importOverwrite": "Ghi đè lên các đoạn mã hiện có có cùng tên và thư mục.", - "importStartBtn": "Nhập khẩu", - "importSuccess": "Quá trình nhập hoàn tất: {{snippets}} đoạn mã đã được thêm, {{updated}} đã được cập nhật, {{skipped}} đã bị bỏ qua, {{folders}} thư mục đã được thêm", - "importFailed": "Không thể nhập đoạn mã.", - "importInvalidFile": "Tệp không hợp lệ: cần một đối tượng JSON chứa các đoạn mã hoặc mảng thư mục.", - "targetHostsLabel": "Máy chủ mục tiêu", - "targetHostsHint": "Chỉ định các máy chủ để chạy đoạn mã này trực tiếp mà không cần mở cửa sổ dòng lệnh.", - "noHostsAvailable": "Không có máy chủ nào được cấu hình", + "importOverwrite": "Ghi đè đoạn mã hiện có cùng tên và thư mục", + "importStartBtn": "Nhập", + "importSuccess": "Nhập xong: đã thêm {{snippets}} đoạn mã, cập nhật {{updated}}, bỏ qua {{skipped}}, thêm {{folders}} thư mục", + "importFailed": "Nhập đoạn mã thất bại", + "importInvalidFile": "Tệp không hợp lệ: cần một đối tượng JSON với mảng đoạn mã hoặc thư mục", + "targetHostsLabel": "Máy chủ đích", + "targetHostsHint": "Gán máy chủ để chạy đoạn mã này trực tiếp mà không cần mở terminal.", + "noHostsAvailable": "Chưa cấu hình máy chủ nào", "clearTargetHosts": "Xóa tất cả", - "hasTargetHosts": "Đã nhắm mục tiêu máy chủ", - "runOnTargets": "Chạy theo mục tiêu", - "directRunSuccess": "Ran \"{{name}}\" trên {{count}} máy chủ", - "directRunPartialFail": "\"{{name}}\" bị lỗi trên một hoặc nhiều máy chủ", + "hasTargetHosts": "Có máy chủ đích", + "runOnTargets": "Chạy trên máy đích", + "directRunSuccess": "Đã chạy \"{{name}}\" trên {{count}} máy chủ", + "directRunPartialFail": "\"{{name}}\" chạy thất bại trên một hoặc nhiều máy chủ", "executionResultTitle": "Kết quả thực thi: {{name}}", - "executionResultDescription": "Kết quả thu được khi chạy đoạn mã trên từng máy chủ mục tiêu.", + "executionResultDescription": "Kết quả từ việc chạy đoạn mã trên từng máy đích.", "executionSuccess": "Thành công", "executionFailed": "Thất bại" }, + "keybindings": { + "title": "Phím tắt", + "description": "Tùy chỉnh các phím tắt sao chép, dán và điều khiển trong terminal, hoặc gán phím để gửi văn bản hoặc chạy một đoạn mã.", + "defaultsHeading": "Phím tắt tích hợp sẵn", + "customHeading": "Phím tắt tùy chỉnh", + "addBinding": "Thêm lối tắt", + "addBindingTitle": "Thêm lối tắt", + "editBindingTitle": "Chỉnh sửa phím tắt", + "loading": "Đang tải...", + "noCustomBindings": "Chưa có phím tắt tùy chỉnh nào.", + "defaultBadge": "Mặc định", + "customizedBadge": "Tùy chỉnh", + "customize": "Tùy chỉnh", + "resetToDefault": "Khôi phục về mặc định", + "resetAllToDefaults": "Khôi phục tất cả về cài đặt mặc định", + "close": "Đóng", + "cancel": "Hủy bỏ", + "saveBinding": "Lưu lối tắt", + "saveError": "Không thể lưu các phím tắt", + "pressKeysToRecord": "Tổ hợp phím", + "pressKeysPlaceholder": "Nhấp chuột và nhấn phím", + "recording": "Nhấn các phím...", + "comboRequiredError": "Trước tiên, hãy nhấn tổ hợp phím.", + "textRequiredError": "Nhập văn bản cần gửi", + "controlCodeRequiredError": "Nhập một chữ cái duy nhất cho mã điều khiển.", + "snippetRequiredError": "Chọn một đoạn trích", + "conflictWarning": "Sự kết hợp này đã được liên kết với {{combo}}. Lưu lại sẽ khiến cả hai đều bốc cháy.", + "actionLabel": "Hoạt động", + "actionCopy": "Sao chép lựa chọn", + "actionPaste": "Dán", + "actionSendControlCode": "Gửi tín hiệu Ctrl+chữ cái", + "actionSendText": "Gửi tin nhắn", + "actionRunSnippet": "Chạy đoạn mã hiện có", + "controlCodeLabel": "Thư", + "textLabel": "Văn bản để gửi", + "appendEnterLabel": "Nhấn Enter sau khi gửi", + "snippetLabel": "Đoạn trích", + "selectSnippetPlaceholder": "Chọn một đoạn trích", + "orphanedSnippetWarning": "Không tìm thấy đoạn mã.", + "clipboardPermissionNote": "Một số trình duyệt có thể yêu cầu quyền truy cập clipboard khi sử dụng lần đầu." + }, "userProfile": { - "donateTitle": "Hãy giữ cho Termix luôn sống sót", - "donateDescription": "Termix là phần mềm miễn phí, tự lưu trữ và được xây dựng bởi một vài người trong thời gian rảnh rỗi. Nếu nó giúp bạn tiết kiệm thời gian hoặc tiền bạc, một khoản quyên góp bằng tiền điện tử sẽ giúp duy trì hoạt động của nó.", - "donateMilestones": "Các khoản quyên góp giúp tài trợ cho thời gian nghiên cứu và học hỏi những gì cần thiết để xây dựng hỗ trợ SAML, Kubernetes và Agent. Hãy theo dõi tiến độ và quyên góp.", - "donateButton": "Quyên góp tiền điện tử", + "donateTitle": "Duy trì Termix", + "donateDescription": "Termix là phần mềm miễn phí, tự lưu trữ, và được xây dựng bởi một vài người trong thời gian rảnh. Nếu nó giúp bạn tiết kiệm thời gian hoặc tiền bạc, một khoản quyên góp tiền mã hóa sẽ giúp duy trì dự án.", + "donateMilestones": "Các khoản quyên góp giúp tài trợ thời gian nghiên cứu và học hỏi để xây dựng hỗ trợ SAML, Kubernetes và Agent. Xem tiến độ và quyên góp.", + "donateButton": "Quyên góp tiền mã hóa", "storageModeLocal": "Trình duyệt", "storageModeCloud": "Cơ sở dữ liệu", - "storageModeDescription": "Trình duyệt chỉ lưu trữ cài đặt trong trình duyệt này. Cơ sở dữ liệu được đồng bộ hóa với máy chủ và tải trên mọi thiết bị.", - "resetToDefaults": "Khôi phục về cài đặt mặc định", - "resetToDefaultsSuccess": "Các cài đặt đã được đặt lại về mặc định.", + "storageModeDescription": "Trình duyệt chỉ lưu cài đặt trong trình duyệt này. Cơ sở dữ liệu đồng bộ với máy chủ và tải trên mọi thiết bị.", + "resetToDefaults": "Đặt lại mặc định", + "resetToDefaultsSuccess": "Đã đặt lại cài đặt về mặc định.", "storageModeSwitch": "Lưu trữ tùy chọn", "sectionAccount": "Tài khoản", - "sectionAppearance": "Vẻ bề ngoài", - "sectionSecurity": "Bảo vệ", + "sectionAppearance": "Giao diện", + "sectionSecurity": "Bảo mật", "sectionApiKeys": "Khóa API", + "sectionData": "Dữ liệu", "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", + "authMethodLocal": "Cục bộ", + "twoFaLabel": "2FA", + "twoFaOn": "Bật", "twoFaOff": "Tắt", "versionLabel": "Phiên bản", "betaProgramTitle": "Chương trình Beta", - "betaProgramDescription": "Hãy thử nghiệm các tính năng sắp ra mắt sớm với thẻ Docker :beta được cập nhật hàng tuần. Phiên bản này chưa ổn định, không dành cho môi trường sản xuất.", - "betaProgramFeedback": "Phát hiện lỗi? Báo cáo tại đây.", + "betaProgramDescription": "Dùng thử sớm các tính năng sắp ra mắt với thẻ Docker :beta cập nhật hàng tuần. Chưa ổn định, không dùng cho môi trường thực tế.", + "betaProgramFeedback": "Tìm thấy lỗi? Báo cáo tại đây.", "betaProgramLearnMore": "Tìm hiểu thêm", "deleteAccount": "Xóa tài khoản", "deleteAccountDescription": "Xóa vĩnh viễn tài khoản của bạn", - "changeServerDescription": "Chuyển sang máy chủ phụ trợ Termix khác", - "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", + "changeServerDescription": "Chuyển sang máy chủ backend Termix khác", + "deleteButton": "Xóa", + "deleteAccountPermanent": "Hành động này là vĩnh viễn và không thể hoàn tác.", + "deleteAccountWarning": "Tất cả phiên, máy chủ, thông tin xác thực và cài đặt sẽ bị xóa vĩnh viễn.", + "confirmPasswordDeletePlaceholder": "Nhập mật khẩu để xác nhận", "languageLabel": "Ngôn ngữ", - "themeLabel": "Chủ đề", - "fontSizeLabel": "Kích thước phông chữ", + "themeLabel": "Giao diện", + "fontSizeLabel": "Cỡ chữ", "accentColorLabel": "Màu nhấn", - "settingsTerminal": "Phần cuối", + "settingsTerminal": "Terminal", "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õ", - "terminalLinkBehavior": "Nhấp chuột vào liên kết thiết bị đầu cuối", - "terminalLinkBehaviorDesc": "Hành vi mặc định khi nhấp vào các liên kết trong cửa sổ terminal", + "commandAutocompleteDesc": "Hiển thị gợi ý tự động khi nhập", + "keyboardShortcuts": "Phím tắt", + "keyboardShortcutsDescription": "Tùy chỉnh các phím tắt sao chép, dán và lệnh trong terminal.", + "manageShortcuts": "Quản lý", + "terminalLinkBehavior": "Hành vi khi nhấp liên kết trong Terminal", + "terminalLinkBehaviorDesc": "Hành vi mặc định khi nhấp vào liên kết trong terminal", "historyTracking": "Theo dõi lịch sử", - "historyTrackingDesc": "Theo dõi lệnh đầu cuối", + "historyTrackingDesc": "Theo dõi các lệnh đã nhập", "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.", + "reopenTabsOnLoginDesc": "Khôi phục các tab đang mở khi đăng nhập hoặc tải lại trang, kể cả từ 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.", + "confirmTabCloseDesc": "Hỏi trước khi đóng tab terminal", "settingsSidebar": "Thanh bên", - "showHostTags": "Thẻ người dẫn chương trình", + "showHostTags": "Hiển thị thẻ máy chủ", "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.", + "hostTrayOnClick": "Nhấp để mở rộng hành động máy chủ", + "hostTrayOnClickDesc": "Luôn hiển thị nút kết nối; nhấp để mở rộng tùy chọn quản lý thay vì di chuột", "compactHostView": "Chế độ xem máy chủ thu gọn", - "compactHostViewDesc": "Thu gọn mỗi máy chủ thành một dòng duy nhất chỉ hiển thị tên và địa chỉ của nó.", - "statusColors": "Màu sắc trạng thái thực tế", - "statusColorsDesc": "Hãy sử dụng màu xanh lá cây/đỏ để thể hiện trạng thái trực tuyến/ngoại tuyến thay vì màu nhấn.", - "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.", + "compactHostViewDesc": "Thu gọn mỗi máy chủ thành một dòng chỉ hiển thị tên và địa chỉ", + "statusColors": "Màu trạng thái thực", + "statusColorsDesc": "Dùng màu xanh/đỏ cho trạng thái trực tuyến/ngoại tuyến thay vì màu nhấn", + "pinAppRail": "Ghim thanh ứng dụng", + "pinAppRailDesc": "Giữ thanh ứng dụng bên trái luôn mở rộng thay vì chỉ mở khi di chuột", "expandAppRailOnHover": "Mở rộng thanh ứng dụng khi di chuột", - "expandAppRailOnHoverDesc": "Cho phép thanh bên trái của ứng dụng mở rộng khi con trỏ chuột di chuyển qua đó.", + "expandAppRailOnHoverDesc": "Cho phép thanh ứng dụng bên trái mở rộng khi con trỏ di chuyển qua", "settingsNavigation": "Điều hướng", - "navigationTabsDesc": "Chọn các tab sẽ hiển thị trên thanh ứng dụng.", - "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", + "navigationTabsDesc": "Chọn các tab sẽ hiển thị trên thanh bên", + "settingsSnippets": "Đoạn mã", + "foldersCollapsed": "Thu gọn thư mục", + "foldersCollapsedDesc": "Mặc định thu gọn thư mục", "confirmExecution": "Xác nhận thực thi", - "confirmExecutionDesc": "Xác nhận trước khi chạy các đoạn mã.", + "confirmExecutionDesc": "Xác nhận trước khi chạy đ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 biện pháp bảo mật đăng nhập bổ sung", - "disable": "Vô hiệu hóa", - "enable": "Cho phép", + "disableUpdateChecksDesc": "Ngừng kiểm tra cập nhật", + "totpAuthenticator": "Trình xác thực TOTP", + "totpEnabled": "2FA đã được bật", + "totpDisabled": "Thêm bảo mật đăng nhập bổ sung", + "disable": "Tắt", + "enable": "Bật", "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ố.", + "totpInstructions": "Quét mã QR hoặc nhập mã bí mật vào ứng dụng xác thực, sau đó nhập mã 6 chữ số", "totpCodePlaceholder": "000000", "verify": "Xác minh", - "changePassword": "Thay đổi mật khẩu", + "changePassword": "Đổ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", @@ -3216,257 +3412,274 @@ "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", + "apiKeyNamePlaceholder": "ví dụ: CI Pipeline", "expiryDateLabel": "Ngày hết hạn", - "optional": "không bắt buộc", - "cancel": "Hủy bỏ", + "optional": "tùy chọn", + "cancel": "Hủy", "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", + "apiKeyCount": "{{count}} khóa", + "newKey": "Tạo khóa mới", + "noApiKeys": "Chưa có khóa API nào.", + "apiKeyActive": "Đang hoạt động", + "apiKeyUsageHint": "Thêm khóa của bạn vào", "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.", + "apiKeyPermissionsHint": "Khóa kế thừa quyền của người dùng tạo.", + "exportData": "Xuất dữ liệu của tôi", + "exportDataDesc": "Tải xuống bản sao lưu máy chủ, thông tin đăng nhập và cài đặt của bạn để chuyển sang thiết bị khác.", + "export": "Xuất khẩu", + "exporting": "Đang xuất khẩu...", + "importData": "Nhập dữ liệu của tôi", + "importDataDesc": "Khôi phục máy chủ, thông tin đăng nhập và cài đặt của bạn từ tệp sao lưu .sqlite.", + "importDataSelected": "Đã chọn: {{name}}", + "selectFile": "Chọn tệp", + "changeFile": "Thay đổi", + "import": "Nhập khẩu", + "importing": "Đang nhập khẩu...", + "exportSuccess": "Dữ liệu đã được xuất thành công.", + "exportFailed": "Xuất 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}}", "roleUser": "Người dùng", "authMethodDual": "Xác thực kép", "authMethodOidc": "OIDC", - "totpSetupFailed": "Không thể bắt đầu thiết lập TOTP.", + "totpSetupFailed": "Không khởi động được cài đặt 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.", + "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).", + "totpDisabledSuccess": "Đã tắt xác thực hai yếu tố", + "totpDisableFailed": "Không tắt được 2FA", + "totpDisableTitle": "Tắ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).", + "totpDisableConfirm": "Tắ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", - "passkeys": "Mã truy cập", - "passkeysDesc": "Sử dụng thông tin xác thực WebAuthn/FIDO2 để đăng nhập mà không cần mật khẩu.", - "passkeyName": "Tên mật khẩu", + "totpDownloadBackup": "Tải mã dự phòng", + "passkeys": "Passkeys", + "passkeysDesc": "Sử dụng thông tin đăng nhập WebAuthn/FIDO2 để đăng nhập không cần mật khẩu", + "passkeyName": "Tên passkey", "passkeyUvPreferred": "Ưu tiên", - "passkeyUvRequired": "Yêu cầu", - "passkeyUvDiscouraged": "Chán nản", - "addPasskey": "Thêm mật khẩu", - "noPasskeys": "Chưa có mật khẩu nào được đăng ký.", - "passkeyAdded": "Đã thêm mật khẩu", - "passkeyAddFailed": "Không thể thêm mật khẩu.", - "passkeyDeleted": "Mật khẩu đã bị xóa", - "passkeyDeleteFailed": "Không thể xóa mật khẩu.", + "passkeyUvRequired": "Bắt buộc", + "passkeyUvDiscouraged": "Không khuyến khích", + "addPasskey": "Thêm Passkey", + "noPasskeys": "Chưa đăng ký passkey nào.", + "passkeyAdded": "Đã thêm Passkey", + "passkeyAddFailed": "Không thêm được Passkey", + "passkeyDeleted": "Đã xóa Passkey", + "passkeyDeleteFailed": "Xóa passkey thất bại", "done": "Xong", - "secretCopied": "Bí mật đã được sao chép vào clipboard", + "secretCopied": "Đã sao chép mã bí mật 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", + "apiKeyCreated": "Đã tạo khóa API \"{{name}}\"", + "apiKeyCreateFailed": "Tạo khóa API thất bại", "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.", + "apiKeyRevoked": "Đã thu hồi khóa API \"{{name}}\"", + "apiKeyRevokeFailed": "Thu hồi khóa API thất bại", + "passwordFieldsRequired": "Cần nhập 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", + "passwordTooShort": "Mật khẩu phải có ít nhất 6 ký tự", + "passwordUpdated": "Cập nhật mật khẩu thành công", + "passwordUpdateFailed": "Cập nhật mật khẩu thất bại", + "deletePasswordRequired": "Cần mật khẩu để xóa tài khoản", + "deleteFailed": "Xóa tài khoản thất bại", "deleting": "Đang xóa...", - "colorPickerTooltip": "Mở công cụ chọn màu", + "colorPickerTooltip": "Mở bảng chọn màu", "themeSystem": "Hệ thống", - "themeLight": "Ánh sáng", - "themeDark": "Tối tăm", + "themeLight": "Sáng", + "themeDark": "Tối", "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": "Không thể tải máy chủ", - "failedToLoad": "Không thể tải các phiên tmux.", - "tmuxUnavailable": "tmux chưa được cài đặt trên máy chủ này.", - "noSessions": "Không có phiên tmux nào trên máy chủ này.", - "noHostSelected": "Chưa chọn máy chủ nào", - "attached": "Đính kèm", - "detached": "Tách rời", - "attach": "Gắn", - "editTags": "Chỉnh sửa thẻ", - "tagsHint": "Các thẻ được phân tách bằng dấu phẩy (ví dụ: YOLO, lab, training)", - "tagsSaved": "Thẻ đã được lưu", - "tagsSaveFailed": "Không thể lưu thẻ", - "searchPlaceholder": "Kết quả tìm kiếm trên nhiều phiên...", - "searchResults": "{{count}} khớp", + "title": "Giám sát Tmux", + "failedToLoadHosts": "Tải danh sách máy chủ thất bại", + "failedToLoad": "Tải phiên tmux thất bại", + "tmuxUnavailable": "tmux chưa được cài đặt trên máy chủ này", + "noSessions": "Không có phiên tmux nào trên máy chủ này", + "noHostSelected": "Chưa chọn máy chủ", + "attached": "Đã đính kèm", + "detached": "Đã tách rời", + "attach": "Đính kèm", + "editTags": "Sửa nhãn", + "tagsHint": "Nhãn phân cách bằng dấu phẩy (ví dụ: YOLO, lab, training)", + "tagsSaved": "Nhãn đã lưu", + "tagsSaveFailed": "Lưu nhãn thất bại", + "searchPlaceholder": "Tìm kiếm đầu ra qua các phiên...", + "searchResults": "{{count}} kết quả khớp", "searchFailed": "Tìm kiếm thất bại", - "selectPaneHint": "Chọn một khung để xem trước kết quả.", + "selectPaneHint": "Chọn một pane để xem trước đầu ra", "closePreview": "Đóng xem trước", - "newSession": "Phiên họp mới", + "newSession": "Phiên mới", "newSessionHint": "Tên phiên (chữ cái, chữ số, _ @ % + = -)", - "newSessionPlaceholder": "phiên của tôi", - "create": "Tạo nên", - "sessionCreated": "Phiên \"{{name}}\" đã được tạo", - "sessionCreateFailed": "Không thể tạo phiên", - "splitRight": "Chia rẽ bên phải", - "splitDown": "Chia nhỏ", - "splitFailed": "Không thể chia khung hình", + "newSessionPlaceholder": "my-session", + "create": "Tạo", + "sessionCreated": "Đã tạo phiên \"{{name}}\"", + "sessionCreateFailed": "Tạo phiên thất bại", + "splitRight": "Chia sang phải", + "splitDown": "Chia xuống dưới", + "splitFailed": "Chia pane thất bại", "newWindow": "Cửa sổ mới", - "windowCreateFailed": "Không thể tạo cửa sổ", + "windowCreateFailed": "Tạo cửa sổ thất bại", "attachSessionTooltip": "Đính kèm vào {{session}}", - "refresh": "Làm cho khỏe lại", - "refreshFailed": "Không thể làm mới phiên", + "refresh": "Làm mới", + "refreshFailed": "Làm mới phiên thất bại", "collapseAll": "Thu gọn tất cả", "expandAll": "Mở rộng tất cả", - "moreActions": "Thêm hành động", + "moreActions": "Thao tác khác", "sessionStats": "Thống kê phiên", "renameSessionTitle": "Đổi tên phiên \"{{name}}\"", "rename": "Đổi tên", - "sessionRenamed": "Phiên được đổi tên thành \"{{name}}\"", - "sessionRenameFailed": "Không thể đổi tên phiên", - "editTagsTitle": "Chỉnh sửa thẻ cho \"{{name}}\"", - "killPane": "bảng điều khiển tắt", - "resizeTree": "Kéo để thay đổi kích thước — nhấp đúp để đặt lại về kích thước ban đầu.", - "reattach": "Đính kèm lại (khắc phục lỗi hiển thị)", - "killWindow": "Cửa sổ tiêu diệt", - "killWindowTitle": "Cửa sổ tiêu diệt {{index}} của \"{{session}}\"?", - "killWindowBody": "Mọi khung cửa sổ và tiến trình trong cửa sổ này sẽ bị chấm dứt. Việc đóng cửa sổ cuối cùng sẽ kết thúc phiên làm việc.", - "windowKillFailed": "Không thể đóng cửa sổ", + "sessionRenamed": "Đã đổi tên phiên thành \"{{name}}\"", + "sessionRenameFailed": "Đổi tên phiên thất bại", + "editTagsTitle": "Sửa nhãn cho \"{{name}}\"", + "killPane": "Hủy pane", + "resizeTree": "Kéo để thay đổi kích thước — nhấn đúp để đặt lại", + "reattach": "Gắn lại (sửa hiển thị bị rối)", + "killWindow": "Hủy cửa sổ", + "killWindowTitle": "Hủy cửa sổ {{index}} của \"{{session}}\"?", + "killWindowBody": "Mọi pane và tiến trình trong cửa sổ này sẽ bị kết thúc. Hủy cửa sổ cuối cùng sẽ kết thúc phiên.", + "windowKillFailed": "Không thể hủy cửa sổ", "statusActivity": "Hoạt động", - "statusWindows": "Windows", - "statusPanes": "cửa sổ", + "statusWindows": "Cửa sổ", + "statusPanes": "Pane", "statusTags": "Thẻ", - "killPaneTitle": "Kill pane {{id}}?", - "killPaneBody": "Tiến trình đang chạy trong khung này sẽ bị chấm dứt. Việc đóng khung cuối cùng sẽ đóng cửa sổ của nó.", - "paneKillFailed": "Không thể đóng cửa sổ", - "killSessionTitle": "Kết thúc phiên \"{{name}}\"?", - "killSessionBody": "Tất cả các cửa sổ và tiến trình đang chạy trong phiên này sẽ bị đóng lại. Thao tác này không thể hoàn tác.", - "kill": "Giết", + "killPaneTitle": "Hủy pane {{id}}?", + "killPaneBody": "Tiến trình đang chạy trong pane này sẽ bị kết thúc. Hủy pane cuối cùng sẽ đóng cửa sổ của nó.", + "paneKillFailed": "Không thể hủy pane", + "killSessionTitle": "Hủy phiên \"{{name}}\"?", + "killSessionBody": "Tất cả cửa sổ và tiến trình đang chạy trong phiên này sẽ bị kết thúc. Hành động này không thể hoàn tác.", + "kill": "Hủy", "sessionKilled": "Phiên \"{{name}}\" đã bị hủy", - "sessionKillFailed": "Không thể kết thúc phiên", - "hostUnreachable": "Không thể kết nối với máy chủ. Hãy kiểm tra xem máy chủ có đang trực tuyến và có thể truy cập được không.", - "noServer": "Hiện không có máy chủ tmux nào đang chạy trên máy chủ này.", - "searchTruncated": "Kết quả một phần — tìm kiếm bao gồm {{lines}} dòng cuối cùng của mỗi khung và tối đa {{panes}} khung.", + "sessionKillFailed": "Không thể hủy phiên", + "hostUnreachable": "Không thể kết nối đến máy chủ. Kiểm tra xem máy chủ có trực tuyến và có thể truy cập được không.", + "noServer": "Không có máy chủ tmux nào đang chạy trên máy chủ này.", + "searchTruncated": "Kết quả bị cắt bớt — tìm kiếm bao gồm {{lines}} dòng cuối của mỗi pane và tối đa {{panes}} pane.", "closeSearchResults": "Đóng kết quả tìm kiếm", "retry": "Thử lại", - "noHosts": "Không có máy chủ SSH nào khả dụng", - "noHostsHint": "Kích hoạt tùy chọn Tmux Monitor trên máy chủ SSH (Host Manager → tab Terminal) để theo dõi các phiên tmux của nó.", + "noHosts": "Không có máy chủ SSH nào", + "noHostsHint": "Bật tùy chọn Tmux Monitor trên máy chủ SSH (Host Manager → tab Terminal) để giám sát phiên tmux của nó.", "tmuxInstallHint": "Cài đặt nó trên máy chủ bằng lệnh:", - "attachTooltip": "Mở cửa sổ dòng lệnh đến {{host}}", - "attachTooltipPane": "Mở cửa sổ dòng lệnh đến {{host}} — phiên tmux {{session}}", - "timeJustNow": "vừa nãy", - "timeMinutes": "{{count}}m trước", - "timeHours": "{{count}}giờ trước", - "timeDays": "{{count}}ngày trước" + "attachTooltip": "Mở terminal đến {{host}}", + "attachTooltipPane": "Mở terminal đến {{host}} — phiên tmux {{session}}", + "timeJustNow": "vừa xong", + "timeMinutes": "{{count}} phút trước", + "timeHours": "{{count}} giờ trước", + "timeDays": "{{count}} ngày trước" }, "mobileKeyboard": { - "shift": "Sự thay đổi", - "ctrl": "Điều khiển", - "esc": "Thoát", + "shift": "Shift", + "ctrl": "Ctrl", + "esc": "Esc", "tab": "Tab", "backTab": "⇥", "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", - "home": "Trang chủ", - "end": "Kết thúc", + "arrowLeft": "Mũi tên trái", + "arrowRight": "Mũi tên phải", + "home": "Home", + "end": "End", "pageUp": "PgUp", "pageDown": "PgDn", "delete": "Del", + "paste": "Dán", "editQuickKeys": "Chỉnh sửa phím tắt", "quickKeysTitle": "Phím tắt", "quickKeysDesc": "Nhấn × để xóa. Hỗ trợ tối đa 8 ký tự.", - "quickKeyPlaceholder": "ví dụ sudo ", - "addQuickKey": "Thêm vào", - "removeQuickKey": "Di dời", - "resetDefaults": "Khôi phục về cài đặt mặc định", + "quickKeyPlaceholder": "vd: sudo ", + "addQuickKey": "Thêm", + "removeQuickKey": "Xóa", + "resetDefaults": "Đặt lại mặc định", "done": "Xong" }, "serial": { - "title": "Số nối tiếp", - "portLabel": "Cảng", - "portPlaceholder": "/dev/ttyUSB0 hoặc COM3", + "title": "Cổng nối tiếp", + "portLabel": "Cổng", + "portPlaceholder": "/dev/ttyUSB0 or COM3", "baudRateLabel": "Tốc độ Baud", "dataBitsLabel": "Dữ liệu", - "stopBitsLabel": "Dừng lại", - "parityLabel": "Bình đẳng", - "parityNone": "Không có", - "parityEven": "Thậm chí", - "parityOdd": "Số lẻ", - "connect": "Kết nối với cổng nối tiếp", + "stopBitsLabel": "Bit dừng", + "parityLabel": "Kiểm tra chẵn lẻ", + "parityNone": "Không", + "parityEven": "Chẵn", + "parityOdd": "Lẻ", + "connect": "Kết nối cổng nối tiếp", "disconnect": "Ngắt kết nối", "refreshPorts": "Làm mới cổng", - "connected": "Đã kết nối với {{path}} ở tốc độ {{baud}} baud", - "disconnected": "Cổng nối tiếp đã bị ngắt kết nối", + "connected": "Đã kết nối tới {{path}} với tốc độ {{baud}} baud", + "disconnected": "Đã ngắt kết nối cổng nối tiếp", "connectionError": "Không thể mở cổng nối tiếp", "wsError": "Lỗi WebSocket", - "errorNoServerUrl": "Chưa cấu hình URL máy chủ.", - "notSupportedTitle": "Số sê-ri không được hỗ trợ", - "notSupported": "Kết nối nối tiếp yêu cầu trình duyệt hỗ trợ Web Serial API (Chrome, Edge hoặc Firefox 151 trở lên), hoặc ứng dụng Termix dành cho máy tính để bàn.", + "errorNoServerUrl": "Chưa cấu hình URL máy chủ", + "notSupportedTitle": "Không hỗ trợ Serial", + "notSupported": "Kết nối nối tiếp yêu cầu trình duyệt hỗ trợ Web Serial API (Chrome, Edge, hoặc Firefox 151+), hoặc ứng dụng Termix desktop.", "hideHint": "Bạn có thể ẩn tab Serial trong Hồ sơ người dùng > Giao diện > Thanh bên > Điều hướng.", - "browserPickerHint": "Nhấp vào Kết nối và trình duyệt của bạn sẽ mở trình chọn cổng để chọn thiết bị." + "browserPickerHint": "Nhấn Kết nối và trình duyệt sẽ mở bộ chọn cổng để chọn thiết bị." }, "metricsHistory": { - "title": "Lịch sử số liệu", + "title": "Lịch sử chỉ số", "historySuffix": "Lịch sử", "cpuMemoryDisk": "CPU / Bộ nhớ / Ổ đĩa", "network": "Mạng", "download": "RX", "upload": "TX", - "noData": "Không có dữ liệu lịch sử nào cho khoảng thời gian này.", - "custom": "Phong tục", - "to": "ĐẾN", + "noData": "Không có dữ liệu lịch sử cho khoảng thời gian này.", + "custom": "Tùy chỉnh", + "to": "đến", "apply": "Áp dụng", "viewHistory": "Xem lịch sử" }, "alerts": { - "tabFirings": "Thông báo", + "tabFirings": "Cảnh báo", "tabRules": "Quy tắc", "tabChannels": "Kênh", - "noFirings": "Không có cảnh báo nào chưa được xác nhận.", - "noRules": "Không có quy tắc cảnh báo nào được cấu hình.", - "noChannels": "Không có kênh thông báo nào được cấu hình.", - "rulesDesc": "Các quy tắc cảnh báo kích hoạt thông báo", + "noFirings": "Không có cảnh báo chưa xác nhận", + "noRules": "Chưa cấu hình quy tắc cảnh báo", + "noChannels": "Chưa cấu hình kênh thông báo", + "rulesDesc": "Quy tắc cảnh báo kích hoạt thông báo", "channelsDesc": "Nơi gửi thông báo cảnh báo", - "acknowledge": "Thừa nhận", - "ackAll": "Ack All", - "allAcknowledged": "Tất cả các cảnh báo đã được xác nhận.", - "ackFailed": "Không xác nhận được cảnh báo", - "ackAllFailed": "Không phản hồi tất cả các cảnh báo", - "showAcknowledged": "Hiển thị tất cả", - "hideAcknowledged": "Ẩn Acked", + "acknowledge": "Xác nhận", + "ackAll": "Xác nhận tất cả", + "allAcknowledged": "Tất cả cảnh báo đã được xác nhận", + "ackFailed": "Không thể xác nhận cảnh báo", + "ackAllFailed": "Không thể xác nhận tất cả cảnh báo", + "showAcknowledged": "Hiện tất cả", + "hideAcknowledged": "Ẩn đã xác nhận", "addChannel": "Thêm kênh", - "editChannel": "Chỉnh sửa kênh", + "editChannel": "Sửa kênh", "channelName": "Tên", - "channelType": "Kiểu", + "channelType": "Loại", "channelNameRequired": "Tên là bắt buộc", "webhookUrl": "URL", - "webhookUrlRequired": "URL webhook là bắt buộc", - "webhookDesc": "Gửi dữ liệu JSON qua phương thức POST tới URL này mỗi khi có cảnh báo được kích hoạt.", + "webhookUrlRequired": "Webhook URL là bắt buộc", + "webhookDesc": "Gửi tải trọng JSON qua POST đến URL này mỗi khi cảnh báo kích hoạt", "ntfyServer": "URL máy chủ", - "ntfyTopic": "Đề tài", - "ntfyTopicRequired": "Yêu cầu về chủ đề", + "ntfyTopic": "Chủ đề", + "ntfyTopicRequired": "Chủ đề là bắt buộc", "ntfyToken": "Mã truy cập (tùy chọn)", - "channelSaveFailed": "Không thể lưu kênh", - "test": "Bài kiểm tra", - "testSent": "Thông báo kiểm tra đã được gửi", - "testFailed": "Thông báo kiểm tra thất bại", + "channelSaveFailed": "Lỗi khi lưu kênh", + "test": "Kiểm tra", + "testSent": "Đã gửi thông báo kiểm tra", + "testFailed": "Gửi thông báo kiểm tra thất bại", "addRule": "Thêm quy tắc cảnh báo", - "editRule": "Chỉnh sửa quy tắc cảnh báo", + "editRule": "Sửa quy tắc cảnh báo", "ruleName": "Tên quy tắc", "ruleNameRequired": "Tên là bắt buộc", - "triggerType": "Cò súng", + "triggerType": "Kích hoạt", "thresholdValue": "Ngưỡng (%)", - "durationSeconds": "Thời gian (giây, 0 = bắn ngay lập tức)", - "cooldownMinutes": "Thời gian hồi chiêu (phút)", + "durationSeconds": "Thời gian (giây, 0 = kích hoạt ngay)", + "cooldownMinutes": "Thời gian chờ (phút)", "channels": "Kênh thông báo", - "noChannelsHint": "Trước tiên, hãy thêm kênh trong tab Kênh.", - "ruleSaveFailed": "Không thể lưu quy tắc" + "noChannelsHint": "Vui lòng thêm kênh trong tab Kênh trước", + "ruleSaveFailed": "Lỗi khi lưu quy tắc" } } diff --git a/src/ui/locales/translated/zh_CN.json b/src/ui/locales/translated/zh_CN.json index f56e1a15..938fa4ef 100644 --- a/src/ui/locales/translated/zh_CN.json +++ b/src/ui/locales/translated/zh_CN.json @@ -1,72 +1,72 @@ { "termixId": { "title": "Termix ID", - "loadFailed": "加载 Termix ID 失败", - "claimTitle": "领取您的 Termix ID", - "claimIntro": "选择一个唯一的用户名。您的 SSH 公钥将发布到一个公共 URL,您可以将其添加到任何服务器的 authorized_keys 文件中。", - "handleLabel": "处理", - "handlePlaceholder": "爱丽丝", - "checking": "检查…", - "available": "可用的", - "taken": "已经采取", - "invalidHandle": "仅小写字母、数字、连字符“-”和下划线“_”。", + "loadFailed": "Termix ID 加载失败", + "claimTitle": "认领您的 Termix ID", + "claimIntro": "选择一个唯一的用户名。您的 SSH 公钥将发布在一个公开 URL 上,您可以将该 URL 添加到任意服务器的 authorized_keys 文件中。", + "handleLabel": "用户名", + "handlePlaceholder": "alice", + "checking": "检查中…", + "available": "可用", + "taken": "已被占用", + "invalidHandle": "仅允许小写字母、数字、- 和 _", "descriptionLabel": "描述(可选)", - "descriptionPlaceholder": "工作笔记本电脑和手机钥匙", + "descriptionPlaceholder": "工作笔记本和手机密钥", "create": "创建 Termix ID", "created": "Termix ID 已创建", "createFailed": "创建 Termix ID 失败", - "deleteConfirm": "删除您的 Termix ID 和所有已发布的密钥?您配置的服务器将保留这些密钥,直到您手动删除它们。", + "deleteConfirm": "删除您的 Termix ID 和所有已发布的密钥?您配置的服务器将保留密钥,直到您手动移除。", "deleted": "Termix ID 已删除", "deleteFailed": "删除 Termix ID 失败", "copyFailed": "复制失败", - "resolverUrlLabel": "公共解析器 URL", + "resolverUrlLabel": "公开解析器 URL", "provisionLabel": "配置服务器", "publishTitle": "发布公钥", - "generate": "产生", + "generate": "生成", "generateTooltip": "生成 Ed25519 密钥对 — 发布公钥并将私钥下载到您的设备", "saveToVault": "保存到凭据", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "labelPlaceholder": "标签(可选)", "add": "添加", - "importFromCredential": "或者从已存储的凭据导入", - "keyPublished": "关键信息已发布", + "importFromCredential": "或从已存储的凭据导入", + "keyPublished": "密钥已发布", "addKeyFailed": "添加密钥失败", - "generatedSaved": "密钥对已生成并保存到您的凭证库。私钥也已下载。", - "generatedOnly": "密钥对已生成——私钥已下载(仅显示一次)。", + "generatedSaved": "密钥对已生成并保存到您的凭据库。私钥也已下载。", + "generatedOnly": "密钥对已生成 — 私钥已下载(仅显示一次)。", "generateFailed": "密钥生成失败", - "imported": "从凭证导入的密钥", + "imported": "密钥已从凭据导入", "importFailed": "导入密钥失败", - "noKeys": "尚未发布任何公钥。", - "keysTitle": "已发布密钥", + "noKeys": "尚未发布公钥。", + "keysTitle": "已发布的密钥", "published": "已发布", - "hidden": "隐", - "keyRemoved": "钥匙已移除", + "hidden": "隐藏", + "keyRemoved": "密钥已移除", "removeKeyFailed": "移除密钥失败", - "updateKeyFailed": "密钥更新失败", - "fromVault": "来自凭证库", + "updateKeyFailed": "更新密钥失败", + "fromVault": "来自凭据 Vault", "linkedToTermixId": "通过 Termix ID 发布", - "selectCredential": "选择凭证...", + "selectCredential": "选择凭据...", "import": "导入", "caTitle": "证书颁发机构", - "caIntro": "在服务器上信任此 CA,它将接受您签署的任何证书。轮换证书可以一次性撤销所有证书;证书也会自动过期。", + "caIntro": "在服务器上信任此 CA,它将接受您签名的任何证书。轮换可一次性吊销所有证书;证书也会自动过期。", "caEnable": "启用 CA", - "caEnabled": "CA已启用", + "caEnabled": "CA 已启用", "caCreateFailed": "启用 CA 失败", - "caPublicKeyLabel": "CA公钥", - "caTrustLabel": "信任服务器(以 root 用户身份运行)", - "caRotate": "旋转", - "caRotateConfirm": "更换证书颁发机构?该机构签发的所有证书将不再被接受,必须重新签发。", - "caRotated": "CA 轮换——之前的证书已被撤销", - "caRotateFailed": "CA旋转失败", + "caPublicKeyLabel": "CA 公钥", + "caTrustLabel": "在服务器上信任(以 root 身份运行)", + "caRotate": "轮换", + "caRotateConfirm": "轮换 CA?其已签名的所有证书将停止被接受,必须重新签发。", + "caRotated": "CA 已轮换 — 先前证书已吊销", + "caRotateFailed": "轮换 CA 失败", "caDelete": "移除 CA", "caDeleteConfirm": "移除证书颁发机构?", - "caDeleted": "CA移除", + "caDeleted": "CA 已移除", "caDeleteFailed": "移除 CA 失败", "caValidityLabel": "证书有效期(天)", "issueCert": "证书", - "issueCertTooltip": "为该密钥颁发由您的 CA 签名的 SSH 证书。", + "issueCertTooltip": "为此密钥签发一个 SSH 证书,由您的 CA 签名", "certIssued": "证书已签发并下载", - "certIssueFailed": "颁发证书失败" + "certIssueFailed": "签发证书失败" }, "credentials": { "folders": "文件夹", @@ -76,71 +76,71 @@ "sshPrivateKey": "SSH 私钥", "upload": "上传", "keyPassword": "密钥密码", - "sshKey": "SSH密钥", + "sshKey": "SSH 密钥", "uploadPrivateKeyFile": "上传私钥文件", "searchCredentials": "搜索凭据...", "addCredential": "添加凭据", - "caCertificate": "CA 证书(-cert.pub)", - "caCertificateDescription": "可选:上传或粘贴 CA 签名的证书文件(例如 id_ed25519-cert.pub)。如果您的 SSH 服务器使用基于证书的授权,则此步骤为必需。", + "caCertificate": "CA 证书 (-cert.pub)", + "caCertificateDescription": "可选:上传或粘贴 CA 签名的证书文件(例如 id_ed25519-cert.pub)。当您的 SSH 服务器使用基于证书的授权时需要。", "uploadCertFile": "上传 -cert.pub 文件", "clearCert": "清除", "certLoaded": "证书已加载", - "certPublicKeyLabel": "CA证书", + "certPublicKeyLabel": "CA 证书", "certTypeLabel": "证书类型", - "pasteOrUploadCert": "粘贴或上传 -cert.pub 证书...", - "hasCaCert": "拥有CA证书", - "noCaCert": "无加州证书", - "noPublicKeyAvailable": "没有可用的公钥。请先打开凭证编辑器。", + "pasteOrUploadCert": "粘贴或上传一个 -cert.pub 证书...", + "hasCaCert": "已有 CA 证书", + "noCaCert": "无 CA 证书", + "noPublicKeyAvailable": "没有公钥可用。请先打开凭据编辑器。", "deployCommandCopied": "部署命令已复制", - "sortCredentials": "排序凭证", - "sortDefault": "默认订单", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", + "sortCredentials": "排序凭据", + "sortDefault": "默认顺序", + "sortNameAsc": "名称(A → Z)", + "sortNameDesc": "名称(Z → A)", "sortUsernameAsc": "用户名(A → Z)", "sortUsernameDesc": "用户名(Z → A)", - "filterCredentials": "筛选凭证", - "filterClearAll": "清除筛选条件", + "filterCredentials": "筛选凭据", + "filterClearAll": "清除筛选", "filterTypeGroup": "类型", "filterTypePassword": "密码", - "filterTypeKey": "SSH密钥", + "filterTypeKey": "SSH 密钥", "filterTagsGroup": "标签" }, "homepage": { - "title": "首页", + "title": "主页", "addWidget": "添加小部件", "editWidget": "编辑小部件", "deleteWidget": "删除", "widgetTypes": "小部件类型", "serviceLink": "服务链接", "folder": "文件夹", - "clock": "钟", + "clock": "时钟", "notes": "笔记", "hostStatus": "主机状态", "bookmarkList": "书签", "zoomIn": "放大", "zoomOut": "缩小", "resetView": "重置视图", - "lockLayout": "锁具布局", + "lockLayout": "锁定布局", "unlockLayout": "解锁布局", - "noWidgets": "右键单击或单击“+”添加您的第一个小部件", + "noWidgets": "右键单击或点击 + 来添加第一个小部件", "openFullView": "打开完整视图", "previewTitle": "首页预览", "cancel": "取消", "save": "保存", "title_label": "标题", - "widgetTitlePlaceholder": "小部件标题(可选)", + "widgetTitlePlaceholder": "小组件标题(可选)", "url": "URL", - "imageUrl": "自定义图像 URL", - "imageUrlHint": "留空则自动使用网站图标", + "imageUrl": "自定义图片 URL", + "imageUrlHint": "留空将自动使用网站图标", "showImage": "显示图片", "description": "描述", "color": "颜色", "icon": "图标", "expanded": "默认展开", "timezone": "时区", - "showSeconds": "显示秒数", - "format12h": "12小时", - "format24h": "24小时", + "showSeconds": "显示秒", + "format12h": "12 小时制", + "format24h": "24 小时制", "content": "内容", "backgroundColor": "背景颜色", "host": "主机", @@ -153,70 +153,70 @@ "categoryLinks": "链接", "categoryInfo": "信息", "categorySystem": "系统", - "widgetServiceLinkDesc": "指向服务 URL 的可点击图块", - "widgetFolderDesc": "用于对相关组件进行分组的容器", - "widgetClockDesc": "可配置时区的实时时钟", - "widgetNotesDesc": "Markdown 笔记小部件", - "widgetHostStatusDesc": "显示 SSH 主机的实时 CPU、内存和磁盘使用情况", + "widgetServiceLinkDesc": "可点击的磁贴,链接到服务 URL", + "widgetFolderDesc": "将相关小组件分组的容器", + "widgetClockDesc": "带有可配置时区的实时时钟", + "widgetNotesDesc": "Markdown 笔记小组件", + "widgetHostStatusDesc": "显示 SSH 主机的实时 CPU、内存和磁盘", "widgetBookmarkListDesc": "快速链接列表", "copyLink": "复制链接", "linkCopied": "链接已复制!", - "location": "地点", + "location": "位置", "temperatureUnit": "温度单位", - "showForecast": "显示3天天气预报", + "showForecast": "显示三日天气预报", "scrolling": "允许滚动", - "feedUrl": "Feed URL", - "maxItems": "最大物品数", + "feedUrl": "订阅源 URL", + "maxItems": "最大条目数", "showDescription": "显示描述", "widgetWeatherName": "天气", - "widgetWeatherDesc": "任何地点的实时天气", + "widgetWeatherDesc": "实时天气信息", "widgetIframeName": "iFrame 嵌入", - "widgetIframeDesc": "在 iframe 中嵌入任何 URL", - "widgetRssName": "RSS订阅", - "widgetRssDesc": "显示来自 RSS 或 Atom 源的项目", - "dragToFolder": "将控件拖到此处或使用“+”添加", + "widgetIframeDesc": "在 iframe 中嵌入任意 URL", + "widgetRssName": "RSS 订阅源", + "widgetRssDesc": "显示 RSS 或 Atom 订阅源中的条目", + "dragToFolder": "拖动小组件至此或点击 + 添加", "showDisk": "显示磁盘使用情况", - "addToFolder": "将小部件添加到文件夹", + "addToFolder": "添加小组件到文件夹", "noHostSelected": "未选择主机", "metricsNotAvailable": "指标不可用", "selectHost": "选择主机...", - "displayedMetrics": "显示的指标", - "metricCpu": "中央处理器", - "metricMemory": "记忆", + "displayedMetrics": "显示指标", + "metricCpu": "CPU", + "metricMemory": "内存", "metricDisk": "磁盘", - "metricUptime": "正常运行时间", + "metricUptime": "运行时间", "metricSystem": "系统", "metricOs": "操作系统", - "metricKernel": "核心", + "metricKernel": "内核", "metricHostname": "主机名", "metricNetwork": "网络", - "metricProcesses": "流程", - "metricProcessesTotal": "全部的", - "metricProcessesRunning": "跑步", - "categoryMonitoring": "监测", + "metricProcesses": "进程", + "metricProcessesTotal": "总数", + "metricProcessesRunning": "运行中", + "categoryMonitoring": "监控", "loading": "加载中...", "noData": "暂无数据", "allClear": "一切正常", - "acknowledgeAlert": "承认", - "noPingUrls": "未配置任何 URL。", + "acknowledgeAlert": "确认告警", + "noPingUrls": "未配置 URL", "pingLabel": "标签", - "addPingUrl": "添加网址", + "addPingUrl": "添加 URL", "showLatency": "显示延迟", "refreshInterval": "刷新间隔", "seconds": "秒", - "filterActivityTypes": "筛选类型", - "filterTypesHint": "留空则显示全部", + "filterActivityTypes": "过滤类型", + "filterTypesHint": "留空显示全部", "showTimestamp": "显示时间戳", - "uptimeUnavailable": "正常运行时间不可用", - "uptimeLabel": "正常运行时间", + "uptimeUnavailable": "运行时间不可用", + "uptimeLabel": "运行时间", "overviewVersion": "版本", - "overviewUpdate": "最新", - "overviewUpdateAvailable": "更新可用", + "overviewUpdate": "已是最新", + "overviewUpdateAvailable": "有可用更新", "overviewDatabase": "数据库", - "overviewUptime": "正常运行时间", - "noHosts": "未配置任何主机", + "overviewUptime": "运行时间", + "noHosts": "未配置主机", "hostGridHosts": "主机", - "hostGridAllHint": "留空则显示所有主机。", + "hostGridAllHint": "留空显示所有主机", "columns": "列", "showIp": "显示 IP 地址", "connectionType": "连接类型", @@ -232,100 +232,100 @@ "widgetMetricsChartName": "指标图表", "widgetMetricsChartDesc": "主机的历史 CPU、内存、磁盘或网络图表", "widgetHostGridName": "主机网格", - "widgetHostGridDesc": "SSH主机状态网格视图", - "widgetAlertFeedName": "警报信息流", - "widgetAlertFeedDesc": "实时警报触发并确认支持", + "widgetHostGridDesc": "SSH 主机状态的网格视图", + "widgetAlertFeedName": "告警信息流", + "widgetAlertFeedDesc": "实时告警触发信息,支持确认操作", "widgetPingStatusName": "Ping 状态", - "widgetPingStatusDesc": "对一个或多个 URL 进行 HTTP ping 状态查询", + "widgetPingStatusDesc": "一个或多个 URL 的 HTTP ping 状态", "widgetRecentActivityName": "近期活动", - "widgetRecentActivityDesc": "可滚动查看近期 Termix 活动信息流", - "widgetTermixUptimeName": "Termix 正常运行时间", + "widgetRecentActivityDesc": "可滚动的近期 Termix 活动信息流", + "widgetTermixUptimeName": "Termix 运行时间", "widgetTermixUptimeDesc": "Termix 服务器的实时运行时间计数器", - "widgetSystemOverviewName": "系统概述", - "widgetSystemOverviewDesc": "Termix 版本、数据库健康状况和正常运行时间一览", - "widgetSshQuickConnectName": "SSH快速连接", - "widgetSshQuickConnectDesc": "一键打开 SSH 会话", + "widgetSystemOverviewName": "系统概览", + "widgetSystemOverviewDesc": "一目了然地查看 Termix 版本、数据库运行状况和运行时间", + "widgetSshQuickConnectName": "SSH 快速连接", + "widgetSshQuickConnectDesc": "一键打开 SSH 会话的按钮", "widgetDockerActivityName": "Docker 活动", - "widgetDockerActivityDesc": "近期所有主机上的 Docker 容器事件", + "widgetDockerActivityDesc": "所有主机上最近的 Docker 容器事件", "widgetCalendarName": "日历", - "widgetCalendarDesc": "月历,今日日期已高亮显示。", + "widgetCalendarDesc": "月历,高亮显示今天", "widgetCountdownName": "倒计时", - "widgetCountdownDesc": "目标日期倒计时", + "widgetCountdownDesc": "倒计时至目标日期", "widgetSearchBarName": "搜索栏", "widgetSearchBarDesc": "快速网页搜索小部件", - "widgetTextBannerName": "文字横幅", - "widgetTextBannerDesc": "画布的醒目标签或章节标题。", + "widgetTextBannerName": "文本横幅", + "widgetTextBannerDesc": "用于画布的粗体标签或分区标题", "widgetImageWidgetName": "图像", - "widgetImageWidgetDesc": "显示来自 URL 的图片", + "widgetImageWidgetDesc": "显示来自 URL 的图像", "widgetMarkdownNotesName": "Markdown 笔记", - "widgetMarkdownNotesDesc": "带有内联 Markdown 渲染的富文本", + "widgetMarkdownNotesDesc": "支持内联 Markdown 渲染的富文本笔记", "widgetCustomApiName": "自定义 API", "widgetCustomApiDesc": "从任何 JSON API 获取并显示数据", "widgetServiceGridName": "服务网格", "widgetServiceGridDesc": "可配置的服务磁贴链接网格", - "widgetDashboardLinksName": "仪表盘链接", - "widgetDashboardLinksDesc": "从控制面板显示您配置的服务链接。", + "widgetDashboardLinksName": "仪表板链接", + "widgetDashboardLinksDesc": "显示您在仪表板中配置的服务链接", "widgetSearchLinksName": "搜索快捷方式", - "widgetSearchLinksDesc": "带有内联输入的快速搜索快捷按钮", + "widgetSearchLinksDesc": "带内联输入的快速搜索快捷按钮", "widgetLinkTreeName": "链接树", - "widgetLinkTreeDesc": "带有标题的链接分组", - "calMon": "星期一", - "calTue": "星期二", - "calWed": "星期三", - "calThu": "星期四", - "calFri": "星期五", - "calSat": "星期六", - "calSun": "星期日", - "startOnMonday": "周一开始新的一周", - "countdownNoDate": "未设定目标日期", - "countdownPast": "事件已结束", + "widgetLinkTreeDesc": "带标题的分组链接部分", + "calMon": "一", + "calTue": "二", + "calWed": "三", + "calThu": "四", + "calFri": "五", + "calSat": "六", + "calSun": "日", + "startOnMonday": "从周一开始", + "countdownNoDate": "未设置目标日期", + "countdownPast": "事件已过", "countdownDays": "天", "countdownHours": "小时", - "countdownMinutes": "分", + "countdownMinutes": "分钟", "countdownSeconds": "秒", "countdownLabel": "标签", "countdownLabelPlaceholder": "例如:发布日", - "countdownShowDays": "显示天", + "countdownShowDays": "显示天数", "countdownShowHours": "显示小时", - "targetDate": "显示日期", + "targetDate": "目标日期", "searchEngine": "搜索引擎", - "customSearchUrl": "自定义搜索网址", + "customSearchUrl": "自定义搜索 URL", "searchPlaceholder": "搜索...", - "searchGo": "去", + "searchGo": "搜索", "searchPlaceholderLabel": "占位符文本", - "searchPlaceholderHint": "搜索框内显示的文本", + "searchPlaceholderHint": "搜索输入框中显示的文本", "openInNewTab": "在新标签页中打开", - "searchQueryPlaceholder": "请输入查询内容...", + "searchQueryPlaceholder": "输入查询...", "noSearchShortcuts": "未配置快捷方式", "addSearchShortcut": "添加快捷方式", "fontSize": "字体大小", "textAlign": "文本对齐", "fontWeight": "字体粗细", - "clearColor": "透明色", - "imageFit": "图像拟合", - "imageLinkUrl": "链接网址", - "noImage": "未设置图像 URL", - "altText": "替代文字", - "altTextPlaceholder": "描述这张图片", + "clearColor": "清除颜色", + "imageFit": "图片适应", + "imageLinkUrl": "链接 URL", + "noImage": "未设置图片 URL", + "altText": "替代文本", + "altTextPlaceholder": "描述图片", "renderMarkdown": "渲染 Markdown", "displayMode": "显示模式", "displayField": "显示字段", "jsonPath": "JSON 路径", "customApiLabel": "标签", "customApiLabelPlaceholder": "例如:温度", - "customApiUnit": "单元", + "customApiUnit": "单位", "customApiNoUrl": "未配置 API URL", "customApiError": "获取失败", "customApiNotArray": "响应不是数组", "addService": "添加服务", "showLabels": "显示标签", "iconSize": "图标大小", - "noDashboardLinks": "未配置任何仪表板链接", + "noDashboardLinks": "未配置仪表板链接", "noLimit": "无限制", - "sectionHeading": "章节标题", - "addSection": "添加部分", + "sectionHeading": "分区标题", + "addSection": "添加分区", "compactMode": "紧凑模式", - "showDetailed": "显示详细信息", + "showDetailed": "显示详情", "selectHosts": "选择主机", "allHosts": "所有主机", "listLayout": "列表", @@ -333,72 +333,111 @@ "terminal": "终端", "files": "文件", "docker": "Docker", - "range15m": "15分钟", - "range1h": "1小时", - "range6h": "6小时", - "range24h": "24小时", + "range15m": "15 分钟", + "range1h": "1 小时", + "range6h": "6 小时", + "range24h": "24 小时", "metricNetRx": "网络下载", "metricNetTx": "网络上传", - "severityFilter": "严重性过滤器", + "severityFilter": "告警级别筛选", "filterAll": "全部", "accentColor": "强调色", - "widgetSshTerminalName": "SSH终端", + "widgetSshTerminalName": "SSH 终端", "widgetSshTerminalDesc": "连接到已配置主机的内联 SSH 终端", "sshTerminalNoHost": "未配置主机", "sshTerminalConnect": "连接", - "sshTerminalAutoConnect": "负载自动连接", + "sshTerminalAutoConnect": "加载时自动连接", "widgetQuickConnectName": "快速连接", - "widgetQuickConnectDesc": "一键启动按钮,支持所有主机上的任何连接类型", + "widgetQuickConnectDesc": "一键启动按钮,适用于主机上的任何连接类型", "connectionTypes": "连接类型", "connType_terminal": "终端", "connType_files": "文件管理器", "connType_docker": "Docker", "connType_tunnel": "隧道", "connType_host-metrics": "主机指标", - "connType_rdp": "远程桌面协议", + "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", "widgetFileManagerName": "文件管理器", - "widgetFileManagerDesc": "为已配置主机提供的嵌入式 SFTP 文件管理器", + "widgetFileManagerDesc": "用于已配置主机的嵌入式 SFTP 文件管理器", "widgetDockerName": "Docker 管理器", - "widgetDockerDesc": "为已配置主机提供的嵌入式 Docker 容器管理器", + "widgetDockerDesc": "用于已配置主机的嵌入式 Docker 容器管理器", "widgetTunnelName": "隧道管理器", - "widgetTunnelDesc": "为已配置主机提供的嵌入式 SSH 隧道管理器", + "widgetTunnelDesc": "用于已配置主机的嵌入式 SSH 隧道管理器", "widgetNoHostSelected": "未配置主机" }, "serverConfig": { "title": "服务器配置", - "description": "配置 Termix 服务器 URL 以连接到您的后端服务", + "description": "配置 Termix 服务器 URL 以连接到后端服务", "serverUrl": "服务器 URL", - "enterServerUrl": "请输入服务器网址", - "saveFailed": "配置保存失败", - "saveError": "保存配置时出错", - "saving": "保存...", + "enterServerUrl": "请输入服务器 URL", + "saveFailed": "保存配置失败", + "saveError": "保存配置出错", + "saving": "保存中...", "saveConfig": "保存配置", - "helpText": "输入您的 Termix 服务器运行所在的 URL(例如,http://localhost:30001 或 https://your-server.com)", - "changeServer": "变更服务器", - "mustIncludeProtocol": "服务器 URL 必须以 http:// 或 https:// 开头。", + "helpText": "输入 Termix 服务器运行的 URL(例如 http://localhost:30001 或 https://your-server.com)", + "changeServer": "更改服务器", + "mustIncludeProtocol": "服务器 URL 必须以 http:// 或 https:// 开头", "allowInvalidCertificate": "允许无效证书", - "allowInvalidCertificateDesc": "仅适用于具有自签名证书或 IP 地址证书的受信任的自托管服务器。", + "allowInvalidCertificateDesc": "仅用于可信的自托管服务器,证书为自签名或 IP 地址证书。", "useEmbedded": "使用本地服务器", - "embeddedDesc": "使用内置的本地服务器运行Termix(无需远程服务器)", + "embeddedDesc": "使用内置本地服务器运行 Termix(无需远程服务器)", "embeddedConnecting": "正在连接本地服务器...", - "embeddedNotReady": "本地服务器尚未准备就绪,请稍等片刻后再试。", + "embeddedNotReady": "本地服务器尚未就绪,请稍候再试。", "localServer": "本地服务器", "savedServers": "已保存的服务器", - "noSavedServers": "没有已保存的服务器", - "removeServer": "清除" + "noSavedServers": "无已保存的服务器", + "removeServer": "移除" + }, + "migrationNotice": { + "title": "Termix Desktop 现在可以独立运行", + "body1": "此应用以前直接连接到您的 Termix 服务器。现在已重新设计,可完全独立运行,将主机和凭据存储在本地,因此即使离线也能正常工作。与自托管 Termix 服务器的双向同步现在是可选的。", + "body2": "您现有的主机和凭据仍然在 {{url}}上。启用远程同步并重新连接到该服务器,即可恢复它们并保持两者同步。", + "dismiss": "现在不要", + "setUpSync": "设置远程同步" + }, + "remoteSync": { + "title": "远程同步", + "description": "您可以选择将此桌面应用程序连接到自托管的 Termix 服务器,以便在不同设备间同步主机名、凭据和代码片段。无论是否连接,该应用程序始终可以完全离线运行。", + "notConnected": "未连接", + "connected": "已连接", + "connectedTo": "已连接至 {{url}}", + "lastSynced": "上次同步时间 {{time}}", + "neverSynced": "从未同步", + "syncError": "同步错误: {{message}}", + "needsReauth": "登录已过期", + "connectButton": "连接到服务器", + "disconnectButton": "断开", + "syncNowButton": "立即同步", + "syncing": "正在同步...", + "serverUrl": "服务器 URL", + "enterServerUrl": "请输入服务器网址", + "mustIncludeProtocol": "服务器 URL 必须以 http:// 或 https:// 开头。", + "connectionTestFailed": "无法通过该 URL 连接到 Termix 服务器", + "allowInvalidCertificate": "允许无效证书", + "allowInvalidCertificateDesc": "仅适用于具有自签名证书或 IP 地址证书的受信任的自托管服务器。", + "savedServers": "已保存的服务器", + "removeServer": "消除", + "continueButton": "继续", + "cancelButton": "取消", + "signInTitle": "登录到 {{url}}", + "originTitle": "连接源", + "originDescription": "选择 SSH 连接的默认发起位置。此设置可针对每个主机进行单独设置。", + "originLocal": "此设备(本地网络)", + "originRemote": "远程服务器", + "bannerReconnect": "重新连接", + "bannerMessage": "远程同步需要重新验证" }, "versionCheck": { "error": "版本检查错误", "checkFailed": "检查更新失败", - "upToDate": "应用已更新至最新版本", + "upToDate": "应用已是最新", "currentVersion": "您正在运行版本 {{version}}", - "updateAvailable": "更新可用", - "newVersionAvailable": "新版本可用!您正在运行 {{current}},但 {{latest}} 可用。", + "updateAvailable": "有可用更新", + "newVersionAvailable": "新版本可用!您当前版本为 {{current}},最新版本为 {{latest}}。", "betaVersion": "测试版", - "betaVersionDesc": "您正在运行 {{current}},它比最新的稳定版本 {{latest}} 更新。", - "releasedOn": "发布日期: {{date}}", + "betaVersionDesc": "您正在运行 {{current}},比最新稳定版 {{latest}} 更新。", + "releasedOn": "发布于 {{date}}", "downloadUpdate": "下载更新", "checking": "正在检查更新...", "checkUpdates": "检查更新", @@ -417,21 +456,21 @@ "error": "错误", "warning": "警告", "unsavedChanges": "未保存的更改", - "dismiss": "解雇", + "dismiss": "忽略", "loading": "加载中...", - "optional": "选修的", + "optional": "可选", "connect": "连接", "copied": "已复制", - "connecting": "正在连接...", + "connecting": "连接中...", "updateAvailable": "更新可用", "appName": "Termix", "openInNewTab": "在新标签页中打开", - "noReleases": "无发布", + "noReleases": "无版本", "updatesAndReleases": "更新与发布", - "newVersionAvailable": "新版本({{version}})可用。", + "newVersionAvailable": "新版本 ({{version}}) 可用。", "failedToFetchUpdateInfo": "获取更新信息失败", "preRelease": "预发布", - "noReleasesFound": "未找到任何版本。", + "noReleasesFound": "未找到发布版本。", "cancel": "取消", "username": "用户名", "login": "登录", @@ -441,7 +480,7 @@ "confirmPassword": "确认密码", "back": "返回", "save": "保存", - "saving": "保存...", + "saving": "保存中...", "delete": "删除", "rename": "重命名", "edit": "编辑", @@ -450,92 +489,92 @@ "no": "否", "or": "或", "next": "下一步", - "previous": "上一步", + "previous": "上一个", "refresh": "刷新", "language": "语言", - "checking": "检查...", + "checking": "检查中...", "checkingDatabase": "正在检查数据库连接...", "checkingAuthentication": "正在检查身份验证...", "backendReconnected": "服务器连接已恢复", - "connectionDegraded": "服务器连接丢失,正在恢复…", + "connectionDegraded": "服务器连接丢失,正在恢复中...", "reload": "重新加载", - "remove": "消除", - "create": "创造", + "remove": "移除", + "create": "创建", "update": "更新", "copy": "复制", "copyFailed": "复制到剪贴板失败", "maximize": "最大化", "restore": "恢复", - "of": "的", + "of": "共", "saved": "已保存", "deleted": "已删除", "deleteFailed": "删除失败", "saveFailed": "保存失败", - "required": "必需的" + "required": "必填" }, "nav": { - "home": "主页", + "home": "首页", "terminal": "终端", "docker": "Docker", "tunnels": "隧道", "fileManager": "文件管理器", "serverStats": "主机指标", "hostMetrics": "主机指标", - "admin": "行政", + "admin": "管理", "termixId": "ID", - "userProfile": "用户个人资料", + "userProfile": "用户资料", "splitScreen": "分屏", - "confirmClose": "关闭此会话?", + "confirmClose": "关闭当前会话?", "close": "关闭", "cancel": "取消", "sshManager": "SSH 管理器", "cannotSplitTab": "无法拆分此标签页", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "复制密码", - "copySudoPassword": "复制 sudo 密码", + "copySudoPassword": "复制 Sudo 密码", "passwordCopied": "密码已复制到剪贴板", - "noPasswordAvailable": "没有可用密码", + "noPasswordAvailable": "没有可用的密码", "failedToCopyPassword": "复制密码失败", "refreshTab": "刷新连接", - "renameTab": "重命名选项卡", + "renameTab": "重命名标签页", "openFileManager": "打开文件管理器", - "dashboard": "仪表板", - "networkGraph": "网络图", + "dashboard": "仪表盘", + "networkGraph": "网络拓扑图", "tmuxMonitor": "Tmux 监视器", "homepage": "首页", "quickConnect": "快速连接", "sshTools": "SSH 工具", - "history": "历史", + "history": "历史记录", "sessionLogs": "会话日志", - "hosts": "主持人", - "snippets": "片段", + "hosts": "主机", + "snippets": "代码片段", "hostManager": "主机管理器", - "credentials": "证书", + "credentials": "凭据", "connections": "连接", - "alerts": "警报", - "serial": "序列号", - "roleAdministrator": "行政人员", + "alerts": "告警", + "serial": "串口", + "roleAdministrator": "管理员", "roleUser": "用户" }, "hosts": { - "hosts": "主持人", - "noHosts": "无 SSH 主机", + "hosts": "主机", + "noHosts": "没有 SSH 主机", "retry": "重试", "refresh": "刷新", - "optional": "选修的", + "optional": "可选", "downloadSample": "下载示例", - "failedToDeleteHost": "删除失败 {{name}}", - "importSkipExisting": "导入(跳过现有)", + "failedToDeleteHost": "删除 {{name}} 失败", + "importSkipExisting": "导入(跳过已有)", "importSSHConfig": "从 SSH 配置导入", "connectionDetails": "连接详情", "ssh": "SSH", "telnet": "Telnet", "remoteDesktop": "远程桌面", - "port": "港口", + "port": "端口", "username": "用户名", "folder": "文件夹", "tags": "标签", - "pin": "别针", + "pin": "固定", "addHost": "添加主机", "editHost": "编辑主机", "cloneHost": "克隆主机", @@ -544,20 +583,20 @@ "enableFileManager": "启用文件管理器", "enableDocker": "启用 Docker", "defaultPath": "默认路径", - "connection": "联系", + "connection": "连接", "upload": "上传", - "authentication": "验证", + "authentication": "认证", "password": "密码", - "key": "钥匙", + "key": "密钥", "credential": "凭据", - "none": "没有任何", + "none": "无", "sshPrivateKey": "SSH 私钥", - "keyType": "关键类型", + "keyType": "密钥类型", "uploadFile": "上传文件", - "tabGeneral": "一般的", + "tabGeneral": "常规", "tabSsh": "SSH", "tabTerminal": "终端", - "tabRdp": "远程桌面协议", + "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "隧道", "tabDocker": "Docker", @@ -565,50 +604,51 @@ "tabStats": "主机指标", "tabHostMetrics": "主机指标", "tabTelnet": "Telnet", - "tabSharing": "分享", - "tabAuthentication": "验证", + "tabSharing": "共享", + "tabAuthentication": "认证", "terminal": "终端", "tunnel": "隧道", "fileManager": "文件管理器", "serverStats": "主机指标", - "status": "地位", - "folderRenamed": "文件夹“{{oldName}}”已成功重命名为“{{newName}}”。", + "status": "状态", + "folderRenamed": "文件夹 \"{{oldName}}\" 已成功重命名为 \"{{newName}}\"", "failedToRenameFolder": "重命名文件夹失败", - "movedToFolder": "已将 {{count}} 主机移动到“{{folder}}”", + "movedToFolder": "已将 {{count}} 台主机移至 \"{{folder}}\"", "editHostTooltip": "编辑主机", "statusChecks": "状态检查", - "metricsCollection": "指标收集", - "metricsInterval": "指标收集间隔", - "metricsIntervalDesc": "服务器统计信息收集频率(5秒-1小时)", + "metricsCollection": "指标采集", + "metricsInterval": "指标采集间隔", + "metricsIntervalDesc": "采集服务器统计信息的频率(5 秒 - 1 小时)", "behavior": "行为", "themePreview": "主题预览", "theme": "主题", - "fontFamily": "字体系列", + "fontFamily": "字体族", "fontSize": "字体大小", "letterSpacing": "字母间距", "lineHeight": "行高", "cursorStyle": "光标样式", "cursorBlink": "光标闪烁", "scrollbackBuffer": "回滚缓冲区", - "bellStyle": "贝尔风格", - "rightClickSelectsWord": "右键单击选择 Word", - "fastScrollModifier": "快速滚动修饰符", + "bellStyle": "响铃样式", + "rightClickSelectsWord": "右键单击选择单词", + "fastScrollModifier": "快速滚动修饰键", "fastScrollSensitivity": "快速滚动灵敏度", - "sshAgentForwarding": "SSH代理转发", + "sshAgentForwarding": "SSH 代理转发", "backspaceMode": "退格模式", - "startupSnippet": "启动片段", - "selectSnippet": "选择片段", - "forceKeyboardInteractive": "强制键盘交互", + "startupSnippet": "启动代码片段", + "selectSnippet": "选择代码片段", + "forceKeyboardInteractive": "强制键盘交互认证", "overrideCredentialUsername": "覆盖凭据用户名", - "overrideCredentialUsernameDesc": "请使用上面指定的用户名,而不是凭据中的用户名。", - "oidcUsernameHint": "使用 $oidc.preferred_username 替换您的 OIDC 登录名。", - "jumpHostChain": "跳转宿主链", - "portKnocking": "敲击", + "overrideCredentialUsernameDesc": "使用上方指定的用户名代替凭据中的用户名", + "oidcUsernameHint": "使用 $oidc.preferred_username 替代您的 OIDC 登录名。", + "tailscaleUsernameHint": "这必须是您在 Tailscale 身份中被授予的 Unix 用户,但不一定是 root 用户。", + "jumpHostChain": "跳板主机链", + "portKnocking": "端口敲门", "addKnock": "添加端口", "addProxyNode": "添加节点", "proxyNode": "代理节点", "proxyType": "代理类型", - "quickActions": "快速操作", + "quickActions": "快捷操作", "sudoPasswordAutoFill": "Sudo 密码自动填充", "sudoPassword": "Sudo 密码", "keepaliveInterval": "保持连接间隔(毫秒)", @@ -621,35 +661,40 @@ "copyRemoteDesktopUrl": "复制远程桌面 URL", "failedToConnect": "连接控制台失败", "connect": "连接", - "disconnect": "断开", - "start": "开始", + "disconnect": "断开连接", + "start": "启动", "enableStatusCheck": "启用状态检查", "enableMetrics": "启用指标", "bulkUpdateFailed": "批量更新失败", "selectAll": "全选", - "deselectAll": "取消选择所有", + "deselectAll": "取消全选", "protocols": "协议", - "secureShell": "安全外壳", + "secureShell": "SSH", "virtualNetwork": "虚拟网络", - "unencryptedShell": "未加密外壳", - "addressIp": "地址/IP", + "unencryptedShell": "未加密 Shell", + "addressIp": "地址 / IP", "friendlyName": "友好名称", - "macAddress": "MAC地址", + "macAddress": "MAC 地址", "wolBroadcastAddress": "WoL 广播地址", - "wolBroadcastAddressDesc": "可选的定向广播,适用于 Docker/路由网络(例如 192.168.1.255)。留空则使用 255.255.255.255。", - "folderAndAdvanced": "文件夹和高级功能", - "privateNotes": "私人笔记", - "privateNotesPlaceholder": "关于此服务器的详细信息……", + "wolBroadcastAddressDesc": "可选的定向广播,用于 Docker/路由网络(例如 192.168.1.255)。留空则使用 255.255.255.255。", + "folderAndAdvanced": "文件夹与高级设置", + "privateNotes": "私密备注", + "privateNotesPlaceholder": "有关此服务器的详细信息...", "pinToTop": "置顶", - "pinToTopDesc": "始终将此主机显示在列表顶部", - "portKnockingSequence": "端口敲击顺序", - "addKnockBtn": "添加敲击", + "pinToTopDesc": "始终在列表顶部显示此主机", + "portKnockingSequence": "端口敲门序列", + "addKnockBtn": "添加敲门", "noPortKnocking": "未配置端口敲门。", - "knockPort": "敲击港", + "knockPort": "敲门端口", "protocol": "协议", - "delayAfterMs": "延迟时间(毫秒)", + "delayAfterMs": "延迟(毫秒)", "useSocks5Proxy": "使用 SOCKS5 代理", "useSocks5ProxyDesc": "通过代理服务器路由连接", + "connectionOrigin": "连接源", + "connectionOriginDesc": "此主机 SSH 连接的来源位置。会覆盖桌面应用程序的全局默认值。", + "connectionOriginDefault": "使用默认值", + "connectionOriginLocal": "此设备(本地网络)", + "connectionOriginRemote": "远程服务器", "proxyHost": "代理主机", "proxyPort": "代理端口", "proxyUsername": "代理用户名", @@ -657,160 +702,170 @@ "proxySingleMode": "单代理", "proxyChainMode": "代理链", "you": "你", - "jumpHostChainLabel": "跳转宿主链", - "addJumpBtn": "添加跳转", - "noJumpHosts": "未配置跳转主机。", + "jumpHostChainLabel": "跳板主机链", + "addJumpBtn": "添加跳板", + "noJumpHosts": "未配置跳板主机。", "selectAServer": "选择服务器...", - "sshPort": "SSH端口", - "authMethod": "身份验证方法", - "storedCredential": "已存储凭证", - "selectACredential": "选择凭证...", + "sshPort": "SSH 端口", + "authMethod": "认证方式", + "storedCredential": "已存储凭据", + "selectACredential": "选择凭据...", "vaultProfile": "Vault 签名者配置文件", "selectAVaultProfile": "选择 Vault 配置文件...", - "vaultProfileHint": "设置来自共享配置文件;连接后,您将通过 OIDC 登录 Vault。不会存储任何密钥。", - "vaultNewProfile": "新个人资料", + "vaultProfileHint": "设置来自共享配置文件;连接时将使用 OIDC 登录 Vault。不会存储任何机密。", + "vaultNewProfile": "新建配置文件", "vaultManageProfiles": "管理 Vault 配置文件", - "vaultAddrLabel": "保险库地址", + "vaultAddrLabel": "Vault 地址", "vaultNamespaceLabel": "命名空间", - "vaultOidcMountLabel": "OIDC 身份验证挂载", + "vaultOidcMountLabel": "OIDC 认证挂载点", "vaultOidcRoleLabel": "OIDC 角色", - "vaultSshMountLabel": "SSH 密钥挂载", - "vaultSshRoleLabel": "SSH签名者角色", - "vaultValidPrincipalsLabel": "有效校长", + "vaultSshMountLabel": "SSH 机密挂载点", + "vaultSshRoleLabel": "SSH 签名者角色", + "vaultValidPrincipalsLabel": "有效主体", "vaultKeyTypeLabel": "临时密钥类型", "vaultSharedLabel": "与所有用户共享", - "vaultCreateProfile": "创建个人资料", - "vaultProfileCreated": "已创建 Vault 配置文件", + "vaultCreateProfile": "创建配置文件", + "vaultProfileCreated": "Vault 配置文件已创建", "vaultProfileSaved": "Vault 配置文件已保存", - "vaultProfileDeleted": "Vault 个人资料已删除", + "vaultProfileDeleted": "Vault 配置文件已删除", "vaultProfileSaveFailed": "保存 Vault 配置文件失败", "vaultProfileDeleteFailed": "删除 Vault 配置文件失败", - "vaultSaveProfile": "保存个人资料", - "vaultProfileValidationError": "姓名、Vault 地址和 SSH 签名者角色是必需的。", - "vaultNoProfiles": "目前还没有 Vault 配置文件。", + "vaultSaveProfile": "保存配置文件", + "vaultProfileValidationError": "名称、Vault 地址和 SSH 签名者角色为必填项", + "vaultNoProfiles": "暂无 Vault 配置文件。", "vaultSharedBadge": "共享", - "keyTypeLabel": "关键类型", + "keyTypeLabel": "密钥类型", "keyTypeAuto": "自动检测", "keyPasteTab": "粘贴", "keyUploadTab": "上传", "keyFileLoaded": "密钥文件已加载", - "keyUploadClick": "点击上传 .pem / .key / .ppk 文件", + "keyUploadClick": "点击上传 .pem / .key / .ppk", "clearKey": "清除密钥", - "keySaved": "SSH密钥已保存", - "keyReplaceNotice": "请在下方粘贴新密钥以替换它", - "keyPassphraseSaved": "密码已保存,输入即可更改", - "replaceKey": "更换钥匙", + "keySaved": "SSH 密钥已保存", + "keyReplaceNotice": "在下方粘贴新密钥以替换", + "keyPassphraseSaved": "密码短语已保存,输入以更改", + "replaceKey": "替换密钥", "docsLink": "查看文档", "opksshLabel": "OPKSSH", - "opksshDesc": "使用您的身份提供商而非密码或密钥登录此主机。需要在服务器上设置 OPKSSH。", - "warpgateLabel": "传送门", - "warpgateDesc": "此主机通过 Warpgate SSH 代理连接。Termix 将在身份验证后自动处理基于浏览器的审批流程。", - "agentLabel": "SSH代理", - "agentDesc": "使用运行在 Termix 服务器上的 SSH 代理(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)进行身份验证。该代理必须运行在 Termix 所在的机器上。", + "opksshDesc": "使用身份提供商登录此主机,而非密码或密钥。需要在服务器上设置 OPKSSH。", + "warpgateLabel": "Warpgate 网关", + "warpgateDesc": "此主机通过 Warpgate SSH 代理连接。Termix 将在认证后自动处理基于浏览器的审批流程。", + "agentLabel": "SSH 代理", + "agentDesc": "使用运行在 Termix 服务器上的 SSH 代理进行认证(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)。代理必须在托管 Termix 的机器上运行。", "agentSocketPathLabel": "代理套接字路径", - "agentSocketPathPlaceholder": "留空则使用 SSH_AUTH_SOCK", + "agentSocketPathPlaceholder": "留空以使用 SSH_AUTH_SOCK", "agentSocketPathHint": "留空以从 SSH_AUTH_SOCK 环境变量自动检测,或输入自定义套接字路径(例如 /run/user/1000/gnupg/S.gpg-agent.ssh)。", "tailscaleDeviceSelect": "选择 Tailscale 设备", - "tailscaleDeviceSelectPlaceholder": "选择设备……", - "tailscaleNoApiKey": "未配置 Tailscale API 密钥。请在管理设置中添加密钥以启用设备发现功能。", + "tailscaleDeviceSelectPlaceholder": "选择设备...", + "tailscaleNoApiKey": "未配置 Tailscale API 密钥。请在管理设置中添加一个以启用设备发现。", "tailscaleDocsLink": "查看文档", - "tailscaleLoadingDevices": "正在加载设备……", - "tailscaleNoDevices": "您的尾网中未发现任何设备。", - "tailscaleDeviceAutoFill": "选择设备后,主机 IP 地址将自动填充。", + "tailscaleLoadingDevices": "正在加载设备...", + "tailscaleNoDevices": "在您的 tailnet 中未找到设备。", + "tailscaleDeviceAutoFill": "选择设备将自动填充主机 IP 地址。", "forceKeyboardInteractiveLabel": "强制键盘交互", - "forceKeyboardInteractiveShortDesc": "即使钥匙在手,也强制手动输入密码。", - "allowLegacyAlgorithmsLabel": "允许使用旧版算法", - "allowLegacyAlgorithmsDesc": "为无法升级的旧设备连接启用已弃用的 SSH 算法(ssh-dss、ssh-rsa、diffie-hellman-group1-sha1、hmac-md5、3des-cbc)。", - "insecure": "缺乏安全感", + "forceKeyboardInteractiveShortDesc": "即使存在密钥也强制手动输入密码", + "allowLegacyAlgorithmsLabel": "允许旧版算法", + "allowLegacyAlgorithmsDesc": "启用已弃用的 SSH 算法(ssh-dss、ssh-rsa、diffie-hellman-group1-sha1、hmac-md5、3des-cbc),以便连接无法升级的旧设备。", + "insecure": "不安全", "terminalAppearance": "终端外观", "colorTheme": "颜色主题", - "fontFamilyLabel": "字体系列", + "fontFamilyLabel": "字体", "fontSizeLabel": "字体大小", "cursorStyleLabel": "光标样式", - "letterSpacingPx": "字母间距(像素)", + "letterSpacingPx": "字母间距 (px)", "lineHeightLabel": "行高", - "bellStyleLabel": "贝尔风格", + "bellStyleLabel": "响铃样式", "backspaceModeLabel": "退格模式", "cursorBlinking": "光标闪烁", - "cursorBlinkingDesc": "启用终端光标闪烁动画", - "rightClickSelectsWordLabel": "右键单击选择 Word", - "rightClickSelectsWordShortDesc": "右键单击,选择光标下的单词。", - "backgroundImageLabel": "背景图片网址", + "cursorBlinkingDesc": "为终端光标启用闪烁动画", + "rightClickSelectsWordLabel": "右键选择单词", + "rightClickSelectsWordShortDesc": "右键点击时选中光标下的单词", + "backgroundImageLabel": "背景图片 URL", "backgroundImageDesc": "终端背景图片的可选 URL", - "backgroundImageOpacityLabel": "背景图像不透明度", + "backgroundImageOpacityLabel": "背景图片不透明度", "customThemeColors": "自定义颜色", - "customThemeBackground": "背景", - "customThemeForeground": "前景", - "customThemeCursor": "光标", - "customThemeCursorAccent": "光标重音", - "customThemeSelection": "选拔", - "customThemeAnsiColors": "ANSI颜色", - "customThemeBlack": "黑色的", - "customThemeRed": "红色的", - "customThemeGreen": "绿色的", - "customThemeYellow": "黄色的", - "customThemeBlue": "蓝色的", + "customThemeBackground": "背景色", + "customThemeForeground": "前景色", + "customThemeCursor": "光标颜色", + "customThemeCursorAccent": "光标强调色", + "customThemeSelection": "选中色", + "customThemeAnsiColors": "ANSI 颜色", + "customThemeBlack": "黑色", + "customThemeRed": "红色", + "customThemeGreen": "绿色", + "customThemeYellow": "黄色", + "customThemeBlue": "蓝色", "customThemeMagenta": "品红", "customThemeCyan": "青色", - "customThemeWhite": "白色的", + "customThemeWhite": "白色", "customThemeBrightBlack": "亮黑色", "customThemeBrightRed": "亮红色", "customThemeBrightGreen": "亮绿色", "customThemeBrightYellow": "亮黄色", "customThemeBrightBlue": "亮蓝色", - "customThemeBrightMagenta": "亮洋红色", + "customThemeBrightMagenta": "亮品红", "customThemeBrightCyan": "亮青色", "customThemeBrightWhite": "亮白色", - "customThemeResetTooltip": "恢复默认设置", + "customThemeResetTooltip": "重置为默认值", + "savedThemesLabel": "已保存的主题", + "saveAsGlobalTheme": "另存为全局主题", + "saveGlobalThemeNamePrompt": "请为此主题输入名称", + "saveGlobalThemeSuccess": "主题已保存", + "saveGlobalThemeError": "主题保存失败", + "applyGlobalThemeTooltip": "应用此主题", + "deleteGlobalThemeTooltip": "删除此主题", + "noSavedThemes": "尚未保存任何主题", "syntaxHighlightingLabel": "语法高亮", - "syntaxHighlightingDesc": "对终端输出(错误信息、路径、IP地址、时间戳)进行着色", - "syntaxHighlightingCategories": "重点类别", + "syntaxHighlightingDesc": "对终端输出着色(错误、路径、IP、时间戳)", + "syntaxHighlightingCategories": "高亮类别", "syntaxHighlightingCategoriesDesc": "选择要着色的内容类型", "syntaxCategoryLogLevels": "日志级别", - "syntaxCategoryLogLevelsDesc": "错误、警告、信息、调试、致命", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", "syntaxCategoryPaths": "文件路径", - "syntaxCategoryPathsDesc": "/usr/share/doc,~/file.txt", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", "syntaxCategoryTimestamps": "时间戳", - "syntaxCategoryTimestampsDesc": "[12:34:56],2024-01-15", - "syntaxCategoryIpAddresses": "IP地址", - "syntaxCategoryIpAddressesDesc": "192.168.1.1,10.0.0.1:8080", - "syntaxCategoryUrls": "网址", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP 地址", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "标签数字", - "syntaxCategoryNumbersDesc": "端口 8080,退出代码 1,状态码 404", - "behaviorAndAdvanced": "行为与高级", + "syntaxCategoryNumbers": "标记数字", + "syntaxCategoryNumbersDesc": "端口 8080, 退出 1, 状态 404", + "behaviorAndAdvanced": "行为与高级设置", "scrollbackBufferLabel": "回滚缓冲区", - "scrollbackMaxLines": "历史记录中保留的最大行数", - "sshAgentForwardingLabel": "SSH代理转发", - "sshAgentForwardingShortDesc": "将您的本地 SSH 密钥传递给此主机", + "scrollbackMaxLines": "保留在历史记录中的最大行数", + "sshAgentForwardingLabel": "SSH 代理转发", + "sshAgentForwardingShortDesc": "将本地 SSH 密钥传递给此主机", "useSSHTitleLabel": "使用 SSH 窗口标题", - "useSSHTitleDesc": "将标签页标题更新为 shell 窗口标题,而不是主机名。", + "useSSHTitleDesc": "使用 shell 的窗口标题更新标签页标题,而非主机名", "enableAutoMosh": "启用自动 Mosh", - "enableAutoMoshDesc": "如果可用,优先选择 Mosh 而不是 SSH。", + "enableAutoMoshDesc": "若可用,优先使用 Mosh 而非 SSH", "enableAutoTmux": "启用自动 Tmux", - "enableAutoTmuxDesc": "自动启动或连接到 tmux 会话", - "enableSessionLogging": "会话日志", - "enableSessionLoggingDesc": "记录终端会话输出以供后续查看", - "enableCommandHistory": "命令历史记录", - "enableCommandHistoryDesc": "记录此终端中运行的命令,以便进行历史记录和自动补全。", + "enableAutoTmuxDesc": "自动启动或附加到 tmux 会话", + "enableSessionLogging": "会话日志记录", + "enableSessionLoggingDesc": "记录终端会话输出以供日后查看", + "allowSessionSharing": "允许会话共享", + "allowSessionSharingDesc": "允许通过链接或其他方式分享此主机上的实时会话。", + "enableCommandHistory": "命令历史", + "enableCommandHistoryDesc": "记录此终端中运行的命令,用于历史记录和自动补全", "linkClickBehaviorLabel": "链接点击行为", - "linkClickBehaviorDesc": "控制点击终端中的链接时发生的情况。使用“默认”可遵循应用程序级别的设置。", + "linkClickBehaviorDesc": "控制点击终端中链接时的行为。选择“默认”以遵循应用级设置。", "linkClickBehaviorDefault": "默认(遵循应用设置)", "linkClickBehaviorConfirm": "显示弹出窗口以打开或复制", "linkClickBehaviorDirect": "直接打开", "sudoPasswordAutoFillLabel": "Sudo 密码自动填充", "sudoPasswordAutoFillShortDesc": "提示时自动提供 sudo 密码", - "sudoPasswordAutoFillDesc": "存储 sudo 密码,以便主机指标、终端提示符和其他程序可以自动运行特权命令。", + "sudoPasswordAutoFillDesc": "存储 sudo 密码,以便主机指标、终端提示和其他操作可以自动运行特权命令。", "sudoPasswordLabel": "Sudo 密码", "environmentVariablesLabel": "环境变量", "addVariableBtn": "添加变量", - "noEnvVars": "未配置任何环境变量。", - "fastScrollModifierLabel": "快速滚动修饰符", + "noEnvVars": "未配置环境变量。", + "fastScrollModifierLabel": "快速滚动修饰键", "fastScrollSensitivityLabel": "快速滚动灵敏度", - "moshCommandLabel": "Mosh Command", - "startupSnippetLabel": "启动片段", - "keepaliveIntervalLabel": "保持连接间隔(秒)", - "maxKeepaliveMisses": "最大保命失误", + "moshCommandLabel": "Mosh 命令", + "startupSnippetLabel": "启动代码片段", + "keepaliveIntervalLabel": "保活间隔(秒)", + "maxKeepaliveMisses": "最大保活失败次数", "tunnelSettings": "隧道设置", "enableTunneling": "启用隧道", "enableTunnelingDesc": "为此主机启用 SSH 隧道功能", @@ -819,248 +874,255 @@ "noTunnelsConfigured": "未配置隧道。", "tunnelLabel": "隧道 {{number}}", "tunnelType": "隧道类型", - "tunnelModeLocalDesc": "将本地端口转发到远程服务器(或可从远程服务器访问的主机)上的端口。", - "tunnelModeRemoteDesc": "将远程服务器上的端口转发到您计算机上的本地端口。", + "tunnelModeLocalDesc": "将本地端口转发到远程服务器(或可从其访问的主机)上的端口。", + "tunnelModeRemoteDesc": "将远程服务器上的端口转发回您机器上的本地端口。", "tunnelModeDynamicDesc": "在本地端口上创建 SOCKS5 代理,用于动态端口转发。", "sameHost": "此主机(直接隧道)", "endpointHost": "端点主机", - "endpointHostPlaceholder": "同一主机、SSH 主机或可访问的主机/IP", + "endpointHostPlaceholder": "相同主机、SSH 主机或可访问的主机/IP", "endpointPort": "端点端口", "bindHost": "绑定主机", "sourcePort": "源端口", "maxRetries": "最大重试次数", "retryIntervalS": "重试间隔(秒)", "autoStartLabel": "自动启动", - "autoStartDesc": "主机加载完毕后自动连接此隧道", - "tunnelConnecting": "隧道连接……", - "tunnelDisconnected": "隧道断开", + "autoStartDesc": "主机加载时自动连接此隧道", + "tunnelConnecting": "隧道连接中...", + "tunnelDisconnected": "隧道已断开连接", "failedToConnectTunnel": "连接失败", "failedToDisconnectTunnel": "断开连接失败", "dockerIntegration": "Docker 集成", "enableDockerMonitor": "启用 Docker", "enableDockerMonitorDesc": "通过 Docker 监控和管理此主机上的容器", "containerRuntime": "容器运行时", - "containerRuntimeDesc": "选择用于此主机上容器管理的 CLI", + "containerRuntimeDesc": "选择此主机上用于容器管理的 CLI", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "启用 Tmux 监视器", - "enableTmuxMonitorDesc": "在 Tmux 监视器中显示此主机,并将其 tmux 操作添加到侧边栏。", - "tabProxmox": "普罗克斯莫克斯", + "enableTmuxMonitor": "启用 Tmux 监控", + "enableTmuxMonitorDesc": "在 Tmux 监控中显示此主机,并将其 tmux 操作添加到侧边栏", + "tabProxmox": "Proxmox", "proxmoxIntegration": "Proxmox 集成", "enableProxmox": "启用 Proxmox", - "enableProxmoxDesc": "将此主机标记为 Proxmox 节点。启用后,即可直接从此主机发现和导入虚拟机。", - "proxmoxDefaultAuthType": "默认身份验证类型", - "proxmoxDefaultAuthTypeDesc": "应用于导入的访客主机的身份验证方法。选择与您连接访客的方式相匹配的身份验证类型。", + "enableProxmoxDesc": "将此主机标记为 Proxmox 节点。启用从此主机直接进行客户机发现和导入。", + "proxmoxDefaultAuthType": "默认认证类型", + "proxmoxDefaultAuthTypeDesc": "应用于导入的客户机主机的认证方法。选择与您连接客户机的方式相匹配的认证类型。", "authTypePassword": "密码", - "authTypeKey": "SSH密钥", + "authTypeKey": "SSH 密钥", "authTypeCredential": "凭据", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "没有任何", + "authTypeNone": "无", "proxmoxDefaultCredential": "默认凭据", - "proxmoxDefaultCredentialDesc": "当身份验证类型设置为“凭据”时,导入的访客主机使用的凭据。", - "proxmoxWindowsDetection": "Windows/RDP 检测", - "proxmoxWindowsDetectionDesc": "以逗号分隔的名称模式,触发 RDP 而不是 SSH(不区分大小写)", + "proxmoxDefaultCredentialDesc": "当认证类型设置为凭据时,用于导入的客户主机的凭据。", + "proxmoxWindowsDetection": "Windows / RDP 检测", + "proxmoxWindowsDetectionDesc": "逗号分隔的名称模式,匹配时触发 RDP 而非 SSH(不区分大小写)", "proxmoxDockerDetection": "Docker 检测", - "proxmoxDockerDetectionDesc": "逗号分隔的名称模式使 Docker 能够匹配虚拟机", - "proxmoxPreferredRanges": "首选 IP 地址范围", - "proxmoxPreferredRangesDesc": "当访客拥有多个接口时,用于选择 IP 地址的优先级前缀以逗号分隔", - "proxmoxAutoSync": "自动同步访客", - "proxmoxAutoSyncDesc": "定期发现此 Proxmox 节点,并在会话解锁期间创建或更新导入的客户主机。", + "proxmoxDockerDetectionDesc": "逗号分隔的名称模式,为匹配的客户主机启用 Docker", + "proxmoxPreferredRanges": "首选 IP 范围", + "proxmoxPreferredRangesDesc": "逗号分隔的前缀,按优先级顺序用于当客户主机有多个网络接口时的 IP 选择", + "proxmoxAutoSync": "自动同步客户主机", + "proxmoxAutoSyncDesc": "在会话未锁定时定期发现此 Proxmox 节点,并创建或更新导入的客户主机。", "proxmoxSyncInterval": "同步间隔(分钟)", - "proxmoxSyncIntervalDesc": "最短 5 分钟。调度程序会跳过已锁定的用户会话。", - "proxmoxMarkMissing": "马克缺席的客人", - "proxmoxMarkMissingDesc": "如果之前导入的虚拟机消失而不是被删除,则添加 proxmox-missing 标签。", + "proxmoxSyncIntervalDesc": "最小 5 分钟。调度器会跳过已锁定的用户会话。", + "proxmoxMarkMissing": "标记缺失的客户主机", + "proxmoxMarkMissingDesc": "当之前导入的客户主机消失时,添加 proxmox-missing 标签,而非删除它。", "proxmoxLastSync": "上次同步", "proxmoxLastSyncNever": "尚未同步", "proxmoxLastSyncNoResult": "无结果详情", - "proxmoxLastSyncSummary": "{{created}} 已创建, {{updated}} 已更新, {{markedMissing}} 缺失, {{skipped}} 已跳过", + "proxmoxLastSyncSummary": "已创建 {{created}},已更新 {{updated}},已标记缺失 {{markedMissing}},已跳过 {{skipped}}", "proxmoxLastSyncStatus": { "success": "成功", - "error": "失败的", - "pending": "待办的" + "error": "失败", + "pending": "待处理" }, - "proxmoxDiscoverAction": "发现并导入 Proxmox 访客", + "proxmoxDiscoverAction": "发现并导入 Proxmox 客户主机", "proxmoxImportTitle": "从 Proxmox 导入", "proxmoxSelectHost": "选择 Proxmox 主机…", "proxmoxDiscover": "发现", - "proxmoxDiscovering": "发现…", - "proxmoxDiscoverGuests": "探索宾客", - "proxmoxGuestsSelected_one": "{{count}} 访客 — {{selected}} 已选", - "proxmoxGuestsSelected_other": "{{count}} 宾客 — {{selected}} 已选", + "proxmoxDiscovering": "正在发现…", + "proxmoxDiscoverGuests": "发现客户主机", + "proxmoxGuestsSelected_one": "{{count}} 个客户主机 — 已选择 {{selected}} 个", + "proxmoxGuestsSelected_other": "{{count}} 个客户主机 — 已选择 {{selected}} 个", "proxmoxSelectAll": "全选", - "proxmoxDeselectAll": "取消选择所有", - "proxmoxNoGuests": "此Proxmox节点上未发现虚拟机。", - "proxmoxImportButton_one": "导入 {{count}} 主机", - "proxmoxImportButton_other": "导入 {{count}} 主机", - "proxmoxResultImported": "{{count}} 导入", - "proxmoxResultUpdated": "{{count}} 已更新", - "proxmoxResultFailed": "{{count}} 失败", - "proxmoxImportComplete": "Proxmox 导入完成: {{summary}}", + "proxmoxDeselectAll": "取消全选", + "proxmoxNoGuests": "此 Proxmox 节点上未找到客户主机。", + "proxmoxImportButton_one": "导入 {{count}} 个主机", + "proxmoxImportButton_other": "导入 {{count}} 个主机", + "proxmoxResultImported": "已导入 {{count}} 个", + "proxmoxResultUpdated": "已更新 {{count}} 个", + "proxmoxResultFailed": "已失败 {{count}} 个", + "proxmoxResultSkippedNoIp": "{{count}} 已跳过(未找到 IP 地址)", + "proxmoxImportComplete": "Proxmox 导入完成:{{summary}}", "proxmoxDiscoveryFailed": "发现失败", "proxmoxImportFailed": "导入失败", "enableFileManagerMonitor": "启用文件管理器", "enableFileManagerMonitorDesc": "通过 SFTP 浏览和管理此主机上的文件", - "scpLegacyLabel": "SCP 遗产模式", - "scpLegacyDesc": "对于不支持 SFTP 子系统的服务器(例如嵌入式或最小 SSH 服务器),请使用传统文件传输方式。", + "scpLegacyLabel": "SCP 传统模式", + "scpLegacyDesc": "对不支持 SFTP 子系统的服务器使用传统文件传输(例如嵌入式或最小化 SSH 服务器)。", "defaultPathLabel": "默认路径", - "fileManagerPathHint": "当此主机上的文件管理器启动时要打开的目录。", + "fileManagerPathHint": "为此主机启动文件管理器时要打开的目录。", "statusChecksLabel": "状态检查", "enableStatusChecks": "启用状态检查", - "enableStatusChecksDesc": "定期 ping 此主机以验证其可用性", + "enableStatusChecksDesc": "定期 ping 此主机以验证可用性", "useGlobalInterval": "使用全局间隔", - "useGlobalIntervalDesc": "使用服务器范围的状态检查间隔进行覆盖", + "useGlobalIntervalDesc": "使用服务器范围的状态检查间隔覆盖", "checkIntervalS": "检查间隔(秒)", "checkIntervalDesc": "每次连接 ping 之间的秒数", "metricsCollectionLabel": "指标收集", "enableMetricsLabel": "启用指标", - "enableMetricsDesc": "从该主机收集 CPU、内存、磁盘和其他指标", + "enableMetricsDesc": "从此主机收集 CPU、RAM、磁盘和其他指标", "useGlobalMetrics": "使用全局间隔", - "useGlobalMetricsDesc": "使用服务器范围的指标间隔进行覆盖", + "useGlobalMetricsDesc": "使用服务器范围的指标间隔覆盖", "metricsIntervalS": "指标间隔(秒)", "metricsIntervalDesc2": "指标快照之间的秒数", "visibleWidgets": "可见小部件", - "widgetsMovedToHostMetrics": "现在可以直接在“主机指标”选项卡中添加、排列和调整卡片大小。打开此主机的“主机指标”,然后使用“自定义”来选择要显示的卡片。", + "widgetsMovedToHostMetrics": "卡片现在可在“主机指标”选项卡中直接添加、排列和调整大小。打开此主机的“主机指标”,使用“自定义”选择要显示的卡片。", "cpuUsageLabel": "CPU 使用率", - "cpuUsageDesc": "CPU 使用率、平均负载、迷你图", - "memoryLabel": "内存使用情况", - "memoryDesc": "内存使用情况、交换空间、缓存", - "storageLabel": "磁盘使用情况", + "cpuUsageDesc": "CPU 百分比、负载平均值、迷你图", + "memoryLabel": "内存使用率", + "memoryDesc": "RAM 使用、交换、缓存", + "storageLabel": "磁盘使用率", "storageDesc": "每个挂载点的磁盘使用情况", "networkLabel": "网络接口", "networkDesc": "接口列表和带宽", - "uptimeLabel": "正常运行时间", + "uptimeLabel": "运行时间", "uptimeDesc": "系统运行时间和启动时间", "systemInfoLabel": "系统信息", "systemInfoDesc": "操作系统、内核、主机名、架构", "recentLoginsLabel": "最近登录", - "recentLoginsDesc": "登录成功和失败事件", - "topProcessesLabel": "顶级流程", - "topProcessesDesc": "PID、CPU%、内存%、命令", + "recentLoginsDesc": "成功和失败的登录事件", + "topProcessesLabel": "顶级进程", + "topProcessesDesc": "PID、CPU%、MEM%、命令", "listeningPortsLabel": "监听端口", - "listeningPortsDesc": "打开带有进程和状态的端口", + "listeningPortsDesc": "带进程和状态的开放端口", "firewallLabel": "防火墙", "firewallDesc": "防火墙、AppArmor、SELinux 状态", "quickActionsLabel": "快速操作", - "quickActionsToolbar": "快速操作会以按钮的形式显示在主机指标工具栏中,只需单击一下即可执行命令。", - "noQuickActions": "目前暂无快速行动。", + "quickActionsToolbar": "快速操作以按钮形式显示在主机指标工具栏中,以便一键执行命令。", + "noQuickActions": "尚无快速操作。", "buttonLabel": "按钮标签", - "selectSnippetPlaceholder": "选择片段……", + "selectSnippetPlaceholder": "选择代码片段...", "addActionBtn": "添加操作", "hostSharedSuccessfully": "主机共享成功", "failedToShareHost": "共享主机失败", "accessRevoked": "访问权限已撤销", "failedToRevokeAccess": "撤销访问权限失败", "cancelBtn": "取消", - "savingBtn": "保存...", + "savingBtn": "保存中...", "addHostBtn": "添加主机", "hostUpdated": "主机已更新", "hostCreated": "主机已创建", "failedToSave": "保存主机失败", - "credentialUpdated": "凭证已更新", - "credentialCreated": "已创建凭证", + "credentialUpdated": "凭据已更新", + "credentialCreated": "凭据已创建", "failedToSaveCredential": "保存凭据失败", - "credentialNameRequired": "请输入凭证名称", - "credentialAuthRequired": "添加密码、SSH密钥或两者都添加。", - "backToHosts": "返回主持人", - "backToCredentials": "返回凭证", - "pinned": "置顶", + "credentialNameRequired": "请输入凭据名称", + "credentialAuthRequired": "添加密码、SSH 密钥或两者", + "createCredentialFromHostBtn": "创建凭证", + "createCredentialFromHostTitle": "从主机创建凭据", + "createCredentialFromHostDesc": "创建一个可重用、可共享的凭据条目,其中预先填充了此主机的当前用户名、密码和/或 SSH 密钥。", + "backToHosts": "返回主机列表", + "backToCredentials": "返回凭据列表", + "pinned": "已固定", "noHostsFound": "未找到主机", - "tryDifferentTerm": "换个说法", - "addFirstHost": "添加您的第一个主机即可开始", + "tryDifferentTerm": "尝试其他搜索词", + "addFirstHost": "添加第一个主机以开始使用", "noCredentialsFound": "未找到凭据", "addCredentialBtn": "添加凭据", - "updateCredentialBtn": "更新凭证", - "features": "特征", + "updateCredentialBtn": "更新凭据", + "features": "功能", "noFolder": "(无文件夹)", "deleteSelected": "删除", - "exitSelection": "出口选择", + "exitSelection": "退出选择", "importSkip": "导入(跳过现有)", "importOverwrite": "导入(覆盖)", - "collapseBtn": "坍塌", - "importExportBtn": "进出口", + "collapseBtn": "折叠", + "importExportBtn": "导入 / 导出", "hostStatusesRefreshed": "主机状态已刷新", - "failedToRefreshHosts": "刷新主机失败", - "movedHostTo": "将 {{host}} 移动到 \"{{folder}}\"", - "failedToMoveHost": "迁移主机失败", - "folderRenamedTo": "文件夹已重命名为“{{name}}”", - "deletedFolder": "已删除文件夹“{{name}}”", - "failedToDeleteFolder": "删除文件夹失败", - "deleteAllInFolder": "删除“{{name}}”中的所有主机?此操作无法撤销。", - "folderPickerPlaceholder": "没有文件夹", - "folderPickerSearch": "搜索或创建(使用 / 表示子文件夹)……", - "folderPickerNone": "没有文件夹", - "folderPickerCreate": "创建“{{path}}”", - "folderPickerEmpty": "没有匹配的文件夹", + "failedToRefreshHosts": "无法刷新主机", + "movedHostTo": "已将 {{host}} 移至 \"{{folder}}\"", + "failedToMoveHost": "无法移动主机", + "folderRenamedTo": "已将文件夹重命名为 \"{{name}}\"", + "deletedFolder": "已删除文件夹 \"{{name}}\"", + "failedToDeleteFolder": "无法删除文件夹", + "deleteAllInFolder": "删除 \"{{name}}\" 中的所有主机?此操作不可撤销。", + "folderPickerPlaceholder": "无文件夹", + "folderPickerSearch": "搜索或创建(使用 / 创建子文件夹)...", + "folderPickerNone": "无文件夹", + "folderPickerCreate": "创建 \"{{path}}\"", + "folderPickerEmpty": "无匹配的文件夹", "newFolder": "新建文件夹", "createFolderTitle": "创建文件夹", "editFolderTitle": "编辑文件夹", - "folderDialogDescription": "选择名称、颜色和图标。使用 / 分隔符嵌套文件夹。", + "folderDialogDescription": "选择名称、颜色和图标。使用 / 来嵌套文件夹。", "folderNameLabel": "文件夹名称", - "folderNamePlaceholder": "例如:生产/网站", - "folderNestingHint": "使用 / 分隔层级并创建嵌套文件夹。", + "folderNamePlaceholder": "例如:生产环境/Web", + "folderNestingHint": "使用 / 分隔层级以创建嵌套文件夹。", "folderColor": "颜色", "folderIcon": "图标", + "folderCredential": "凭据", + "folderCredentialNone": "未分配凭证", + "folderCredentialHint": "此文件夹中使用“存储凭据”身份验证但未选择自身凭据的主机将继承此凭据。", "folderPreview": "预览", "folderNameFallback": "未命名文件夹", "createFolderButton": "创建文件夹", "saveFolderButton": "保存文件夹", "cancel": "取消", - "iconSearchPlaceholder": "搜索图标……", + "iconSearchPlaceholder": "搜索图标...", "openAllSessions": "打开所有会话", "editFolder": "编辑文件夹", "deleteFolder": "删除文件夹", "folderSaved": "文件夹已保存", - "failedToSaveFolder": "保存文件夹失败", - "folderDeleted": "已删除文件夹“{{name}}”", - "deleteFolderConfirm": "删除“{{name}}”及其 {{count}} 主机?此操作无法撤销。", - "failedToMoveHosts": "主机迁移失败", + "failedToSaveFolder": "无法保存文件夹", + "folderDeleted": "已删除文件夹 \"{{name}}\"", + "deleteFolderConfirm": "删除 \"{{name}}\" 及其 {{count}} 个主机?此操作不可撤销。", + "failedToMoveHosts": "无法移动主机", "expandAll": "展开所有文件夹", "collapseAll": "折叠所有文件夹", - "moreActions": "更多的", - "groupBy": "按分组", + "moreActions": "更多", + "groupBy": "分组", "GroupByFolder": "文件夹", "GroupByTag": "标签", - "GroupByStatus": "地位", + "GroupByStatus": "状态", "GroupByProtocol": "协议", - "GroupByAuth": "身份验证类型", + "GroupByAuth": "认证类型", "groupUngrouped": "未分组", "deletedHost": "已删除 {{name}}", "copiedToClipboard": "已复制到剪贴板", - "terminalUrlCopied": "已复制终端 URL", + "terminalUrlCopied": "终端 URL 已复制", "fileManagerUrlCopied": "文件管理器 URL 已复制", "tunnelUrlCopied": "隧道 URL 已复制", "dockerUrlCopied": "Docker URL 已复制", - "hostMetricsUrlCopied": "主机指标 URL 已复制", + "hostMetricsUrlCopied": "主机监控 URL 已复制", "tmuxMonitorUrlCopied": "Tmux 监视器 URL 已复制", - "rdpUrlCopied": "复制的 RDP URL", + "rdpUrlCopied": "RDP URL 已复制", "vncUrlCopied": "VNC URL 已复制", "telnetUrlCopied": "Telnet URL 已复制", - "remoteDesktopUrlCopied": "已复制远程桌面 URL", + "remoteDesktopUrlCopied": "远程桌面 URL 已复制", "expandActions": "展开操作", - "collapseActions": "坍塌动作", - "wakeOnLanAction": "局域网唤醒", - "wakeOnLanSuccess": "魔法数据包已发送至 {{name}}", + "collapseActions": "收起操作", + "wakeOnLanAction": "网络唤醒", + "wakeOnLanSuccess": "已向 {{name}} 发送魔术包", "wakeOnLanError": "发送魔术包失败", "cloneHostAction": "克隆主机", - "copyAddress": "副本地址", + "copyAddress": "复制地址", "copyLink": "复制链接", "copyTerminalUrlAction": "复制终端 URL", "copyFileManagerUrlAction": "复制文件管理器 URL", "copyTunnelUrlAction": "复制隧道 URL", "copyDockerUrlAction": "复制 Docker URL", - "copyHostMetricsUrlAction": "复制主机指标 URL", + "copyHostMetricsUrlAction": "复制主机监控 URL", "copyTmuxMonitorUrlAction": "复制 Tmux 监视器 URL", "copyRdpUrlAction": "复制 RDP URL", "copyVncUrlAction": "复制 VNC URL", "copyTelnetUrlAction": "复制 Telnet URL", "copyRemoteDesktopUrlAction": "复制远程桌面 URL", - "deleteCredentialConfirm": "删除凭据“{{name}}”?", + "deleteCredentialConfirm": "删除凭据 \"{{name}}\"?", "deletedCredential": "已删除 {{name}}", "deploySSHKeyTitle": "部署 SSH 密钥", - "deployingBtn": "正在部署……", + "deployingBtn": "部署中...", "deployBtn": "部署", - "failedToDeployKey": "密钥部署失败", - "deleteHostsConfirm": "删除 {{count}} 主机{{plural}}?此操作无法撤销。", + "failedToDeployKey": "部署密钥失败", + "deleteHostsConfirm": "删除 {{count}} 台主机{{plural}}?此操作无法撤销。", "movedToRoot": "已移至根目录", "enableTerminalFeature": "启用终端", "disableTerminalFeature": "禁用终端", @@ -1072,118 +1134,122 @@ "disableDockerFeature": "禁用 Docker", "enableProxmoxFeature": "启用 Proxmox", "disableProxmoxFeature": "禁用 Proxmox", - "addTagsPlaceholder": "添加标签……", - "authDetails": "身份验证详情", + "addTagsPlaceholder": "添加标签...", + "authDetails": "认证详情", "credType": "类型", - "generateKeyPairDesc": "生成新的密钥对,私钥和公钥将自动填充。", - "generatingKey": "正在生成...", + "generateKeyPairDesc": "生成新的密钥对,私钥和公钥将自动填写。", + "generatingKey": "生成中...", "generateLabel": "生成 {{label}}", "uploadFileBtn": "上传文件", - "keyPassphraseOptional": "密钥口令(可选)", + "keyPassphraseOptional": "密钥密码(可选)", "sshPublicKeyOptional": "SSH 公钥(可选)", - "publicKeyGenerated": "生成的公钥", - "failedToGeneratePublicKey": "无法导出公钥", + "publicKeyGenerated": "公钥已生成", + "failedToGeneratePublicKey": "未能生成公钥", "publicKeyCopied": "公钥已复制", - "keyPairGenerated": "{{label}} key pair generated", - "failedToGenerateKeyPair": "密钥对生成失败", + "keyPairGenerated": "{{label}} 密钥对已生成", + "failedToGenerateKeyPair": "未能生成密钥对", "searchHostsPlaceholder": "搜索主机、地址、标签…", - "searchCredentialsPlaceholder": "搜索凭证…", + "searchCredentialsPlaceholder": "搜索凭据…", "refreshBtn": "刷新", - "addTag": "添加标签……", + "addTag": "添加标签...", "deleteConfirmBtn": "删除", "tunnelRequirementsText": "SSH 服务器必须在 /etc/ssh/sshd_config 中设置 GatewayPorts yes、AllowTcpForwarding yes 和 PermitRootLogin yes。", - "deleteHostConfirm": "删除“{{name}}”?", - "enableAtLeastOneProtocol": "启用上述至少一种协议以配置身份验证和连接设置。", - "keyPassphrase": "密钥口令", + "deleteHostConfirm": "删除 \"{{name}}\"?", + "enableAtLeastOneProtocol": "请至少启用上述一种协议以配置认证和连接设置。", + "keyPassphrase": "密钥密码", "connectBtn": "连接", "disconnectBtn": "断开", "basicInformation": "基本信息", - "authDetailsSection": "身份验证详情", + "authDetailsSection": "认证详情", "credTypeLabel": "类型", - "hostsTab": "主持人", - "credentialsTab": "证书", - "selectMultiple": "选择多个", + "hostsTab": "主机", + "credentialsTab": "凭据", + "selectMultiple": "多选", "selectHosts": "选择主机", - "connectionLabel": "联系", - "authenticationLabel": "验证", + "connectionLabel": "连接", + "authenticationLabel": "认证", "generateKeyPairTitle": "生成密钥对", - "generateKeyPairDescription": "生成新的密钥对,私钥和公钥将自动填充。", + "generateKeyPairDescription": "生成新的密钥对,私钥和公钥将自动填入。", "generateFromPrivateKey": "从私钥生成", "refreshBtn2": "刷新", - "exitSelectionTitle": "出口选择", - "exportAll": "全部导出", + "exitSelectionTitle": "退出选择", + "exportAll": "导出全部", "exportForSharing": "导出共享", "addHostBtn2": "添加主机", "addCredentialBtn2": "添加凭据", "checkingHostStatuses": "正在检查主机状态...", - "pinnedSection": "置顶", - "hostsExported": "主机导出成功", - "hostsShareExported": "共享主机已成功导出", + "pinnedSection": "已置顶", + "hostsExported": "主机已成功导出", + "hostsShareExported": "可共享的主机已成功导出", "exportFailed": "导出主机失败", - "sampleDownloaded": "下载示例文件", + "sampleDownloaded": "示例文件已下载", "failedToDeleteCredential2": "删除凭据失败", - "noFolderOption": "(无文件夹)", - "nSelected": "{{count}} 已选择", - "featuresMenu": "特征", + "noFolderOption": "(无文件夹)", + "nSelected": "已选择 {{count}} 项", + "featuresMenu": "功能", "moveMenu": "移动", - "connectSelected": "连接", + "connectSelected": "连接所选", "cancelSelection": "取消", - "deployDialogDesc": "将 {{name}} 部署到主机的 authorized_keys 中。", + "deployDialogDesc": "将 {{name}} 部署到主机的 authorized_keys 文件中。", "targetHostLabel": "目标主机", "selectHostOption": "选择主机...", - "keyDeployedSuccess": "密钥已成功部署", - "failedToDeployKey2": "密钥部署失败", - "deletedCount": "已删除 {{count}} 主机", - "failedToDeleteCount": "删除 {{count}} 主机失败", - "duplicatedHost": "重复的“{{name}}”", + "keyDeployedSuccess": "密钥部署成功", + "failedToDeployKey2": "部署密钥失败", + "deletedCount": "已删除 {{count}} 个主机", + "failedToDeleteCount": "删除 {{count}} 个主机失败", + "duplicatedHost": "已复制 \"{{name}}\"", "failedToDuplicateHost": "复制主机失败", - "updatedCount": "已更新 {{count}} 主机", + "updatedCount": "已更新 {{count}} 台主机", "friendlyNameLabel": "友好名称", "descriptionLabel": "描述", "loadingHost": "正在加载主机...", "loadingHosts": "正在加载主机...", "loadingCredentials": "正在加载凭据...", - "noHostsYet": "目前还没有主持人", - "noHostsMatchSearch": "没有符合您搜索条件的主机", + "noHostsYet": "暂无主机", + "noHostsMatchSearch": "没有匹配搜索的主机", "hostNotFound": "未找到主机", "searchHosts": "搜索主机...", "sortHosts": "排序主机", - "sortDefault": "默认订单", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", - "sortIpAsc": "IP地址(升序)", - "sortIpDesc": "IP地址(描述)", + "sortDefault": "默认顺序", + "sortNameAsc": "名称 (A → Z)", + "sortNameDesc": "名称 (Z → A)", + "sortIpAsc": "IP 地址 (升序)", + "sortIpDesc": "IP 地址 (降序)", "sortOnlineFirst": "在线优先", "sortOfflineFirst": "离线优先", - "sortPinnedFirst": "置顶", - "filterHosts": "过滤主机", + "sortPinnedFirst": "已固定优先", + "filterHosts": "筛选主机", "filterClearAll": "清除筛选条件", - "filterStatusGroup": "地位", - "filterOnline": "在线的", + "filterStatusGroup": "状态", + "filterOnline": "在线", "filterOffline": "离线", - "filterPinned": "置顶", - "filterAuthGroup": "身份验证类型", + "filterPinned": "已固定", + "filterAuthGroup": "认证类型", "filterAuthPassword": "密码", - "filterAuthKey": "SSH密钥", + "filterAuthKey": "SSH 密钥", "filterAuthCredential": "凭据", - "filterAuthNone": "没有任何", + "filterAuthNone": "无", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "协议", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "远程桌面协议", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特征", + "filterFeaturesGroup": "功能", "filterFeatureTerminal": "终端", "filterFeatureFileManager": "文件管理器", "filterFeatureTunnel": "隧道", "filterFeatureDocker": "Docker", "filterTagsGroup": "标签", "shareHost": "共享主机", - "shareHostTitle": "分享: {{name}}", + "shareHostTitle": "共享:{{name}}", + "shareFolder": "共享文件夹", + "shareFolderTitle": "共享文件夹: {{name}}", + "folderSharedSuccessfully": "文件夹中的共享主机 {{count}}", + "failedToShareFolder": "共享文件夹失败", "sharing": { - "loadError": "分享数据加载失败,请重试。", - "shareWithSection": "分享", + "loadError": "加载共享数据失败,请重试。", + "shareWithSection": "共享给", "usersTab": "用户", "rolesTab": "角色", "searchPlaceholder": "搜索用户或角色...", @@ -1192,91 +1258,95 @@ "levels": { "connect": { "label": "连接", - "description": "仅允许打开会话:终端、远程桌面、文件管理器、隧道和 Docker。无权访问主机配置。" + "description": "仅打开会话:终端、远程桌面、文件管理器、隧道和 Docker。无权访问主机配置。" }, "view": { - "label": "看法", - "description": "连接后,即可查看主机配置。密钥永远不会显示。" + "label": "查看", + "description": "连接,并查看主机配置。凭据信息从不显示。" }, "edit": { "label": "编辑", - "description": "查看并修改主机。密钥可以替换但永远无法读取;凭证分配仅限所有者操作。" + "description": "查看,并修改主机。凭据可替换但不可读取;凭据分配仅限所有者。" }, "manage": { "label": "管理", - "description": "编辑、与他人共享主机、更改权限级别和撤销访问权限。" + "description": "编辑,并与其他用户共享主机、更改权限级别及撤销访问。" } }, - "expiryLabel": "访问权限到期", + "expiryLabel": "访问有效期", "expiry": { - "never": "绝不", - "oneHour": "1小时", - "oneDay": "24小时", - "sevenDays": "7天", - "thirtyDays": "30天", - "custom": "风俗" + "never": "永不过期", + "oneHour": "1 小时", + "oneDay": "24 小时", + "sevenDays": "7 天", + "thirtyDays": "30 天", + "custom": "自定义" }, - "customHoursPlaceholder": "访问权限到期时间(剩余时间)", - "shareButton": "分享", - "shareWithCount": "分享({{count}})", + "customHoursPlaceholder": "过期前的小时数", + "shareButton": "共享", + "shareWithCount": "共享({{count}})", "currentAccess": "当前访问权限", "noAccessEntries": "此主机尚未共享", - "grantedBy": "由……授予", - "expires": "过期", - "expired": "已到期", - "never": "绝不", + "folderShareSummary": "此文件夹中的 {{shared}} 主机(共 {{total}} )共享", + "grantedBy": "授权者", + "expires": "过期时间", + "expired": "已过期", + "never": "永不过期", "revoke": "撤销", - "accessUpdated": "访问已更新", + "accessUpdated": "访问权限已更新", "accessUpdateFailed": "更新访问权限失败", - "sharedBadge": "共享", + "sharedBadge": "已共享", "sharedBadgeTooltip": "由 {{owner}} 共享({{level}} 访问权限)", - "viewOnlyBanner": "此主机由 {{owner}} 与您共享,您拥有查看权限。配置信息为只读。", - "sharedEditBanner": "此主机由 {{owner}} 与您共享,并授予您编辑权限。更改将应用于实际主机;身份验证引用只能由所有者更改。", - "ownerOnlyControl": "只有房东才能更改此设置" + "viewOnlyBanner": "此主机由 {{owner}} 与您共享,仅限查看权限。配置为只读。", + "sharedEditBanner": "此主机由 {{owner}} 与您共享,具有编辑权限。更改将应用于真实主机;凭据引用只能由所有者更改。", + "ownerOnlyControl": "仅主机所有者可更改此项" }, "guac": { - "connection": "联系", - "authentication": "验证", - "storedCredential": "已存储凭证", - "noCredential": "无凭证(直接凭证如下)", - "authMethod": "身份验证方法", - "authTypeDirect": "直接的", + "connection": "连接", + "authentication": "认证", + "storedCredential": "已存储凭据", + "noCredential": "无凭据(下方直接填写凭据)", + "authMethod": "认证方法", + "authTypeDirect": "直接", "authTypeCredential": "凭据", - "selectCredential": "选择凭证...", + "authTypeNone": "没有任何", + "authTypeNoneDesc": "不会存储任何凭据。每次连接时,系统都会提示您输入用户名和密码;这些信息不会被保存。", + "selectCredential": "选择凭据...", "connectionSettings": "连接设置", "displaySettings": "显示设置", "audioSettings": "音频设置", - "rdpPerformance": "RDP性能", + "rdpPerformance": "RDP 性能", "deviceRedirection": "设备重定向", - "session": "会议", + "session": "会话", "gateway": "网关", "remoteApp": "RemoteApp", "clipboard": "剪贴板", "sessionRecording": "会话录制", - "wakeOnLan": "局域网唤醒", + "wakeOnLan": "网络唤醒", "vncSettings": "VNC 设置", "terminalSettings": "终端设置", - "rdpPort": "RDP端口", + "rdpPort": "RDP 端口", "username": "用户名", "password": "密码", - "domain": "领域", + "passwordSaved": "密码已保存,输入即可更改", + "domain": "域", "securityMode": "安全模式", - "colorDepth": "色彩深度", + "colorDepth": "颜色深度", "width": "宽度", "height": "高度", - "dpi": "干粉", + "dpi": "DPI", "resizeMethod": "调整大小方法", - "clientName": "客户名称", - "initialProgram": "初始计划", + "clientName": "客户端名称", + "initialProgram": "初始程序", "serverLayout": "服务器布局", "timezone": "时区", - "loadBalanceInfo": "负载均衡信息/Cookie", - "loadBalanceInfoDesc": "用于 RDS 服务器场负载均衡的 RD 连接代理 cookie(例如 tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "负载均衡信息 / Cookie", + "loadBalanceInfoDesc": "RD 连接代理 Cookie,用于 RDS 场负载均衡(例如 tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacd 代理", "guacdHostname": "guacd 主机", "guacdHostnamePlaceholder": "全局默认", - "guacdPort": "瓜达克港", - "guacdProxyDesc": "覆盖此连接的全局 guacd 实例。留空则使用全局配置的 guacd。", + "guacdPort": "guacd 端口", + "guacdProxyDesc": "为此连接覆盖全局 guacd 实例。留空则使用全局配置的 guacd。", "gatewayHostname": "网关主机名", "gatewayPort": "网关端口", "gatewayUsername": "网关用户名", @@ -1284,26 +1354,26 @@ "gatewayDomain": "网关域", "remoteAppProgram": "RemoteApp 程序", "workingDirectory": "工作目录", - "arguments": "论点", + "arguments": "参数", "normalizeLineEndings": "规范化行尾", "recordingPath": "录制路径", - "recordingName": "录音名称", - "macAddress": "MAC地址", + "recordingName": "录制名称", + "macAddress": "MAC 地址", "broadcastAddress": "广播地址", - "udpPort": "UDP端口", + "udpPort": "UDP 端口", "waitTimeS": "等待时间(秒)", "driveName": "驱动器名称", - "drivePath": "驱动路径", + "drivePath": "驱动器路径", "ignoreCertificate": "忽略证书", "ignoreCertificateDesc": "允许连接到具有自签名证书的主机", - "forceLossless": "无损力", - "forceLosslessDesc": "强制无损图像编码(更高质量,更高带宽)", - "disableAudio": "关闭音频", - "disableAudioDesc": "将远程会话中的所有音频静音", + "forceLossless": "强制无损", + "forceLosslessDesc": "强制无损图像编码(质量更高,占用更多带宽)", + "disableAudio": "禁用音频", + "disableAudioDesc": "将远程会话的所有音频静音", "enableAudioInput": "启用音频输入(麦克风)", - "enableAudioInputDesc": "把本地麦克风转发到远程会话", - "wallpaper": "墙纸", - "wallpaperDesc": "显示桌面壁纸(禁用可提升性能)", + "enableAudioInputDesc": "将本地麦克风转发到远程会话", + "wallpaper": "壁纸", + "wallpaperDesc": "显示桌面壁纸(禁用可提高性能)", "theming": "主题", "themingDesc": "启用视觉主题和样式", "fontSmoothing": "字体平滑", @@ -1313,92 +1383,92 @@ "desktopComposition": "桌面合成", "desktopCompositionDesc": "启用 Aero 玻璃效果", "menuAnimations": "菜单动画", - "menuAnimationsDesc": "启用菜单淡入淡出和滑动动画", + "menuAnimationsDesc": "启用菜单淡入淡出与滑动动画", "disableBitmapCaching": "禁用位图缓存", - "disableBitmapCachingDesc": "关闭位图缓存(可能有助于解决故障)", - "disableOffscreenCaching": "禁用屏幕外缓存", - "disableOffscreenCachingDesc": "关闭屏幕外缓存", + "disableBitmapCachingDesc": "关闭位图缓存(可能有助于解决显示故障)", + "disableOffscreenCaching": "禁用离屏缓存", + "disableOffscreenCachingDesc": "关闭离屏缓存", "disableGlyphCaching": "禁用字形缓存", "disableGlyphCachingDesc": "关闭字形缓存", - "enableGfx": "启用图形", - "enableGfxDesc": "使用 RemoteFX 图形管线", + "enableGfx": "启用 GFX", + "enableGfxDesc": "使用 RemoteFX 图形管道", "enablePrinting": "启用打印", "enablePrintingDesc": "将本地打印机重定向到远程会话", "enableDriveRedirection": "启用驱动器重定向", "enableDriveRedirectionDesc": "将本地文件夹映射为远程会话中的驱动器", "createDrivePath": "创建驱动器路径", - "createDrivePathDesc": "如果文件夹不存在,则自动创建该文件夹。", + "createDrivePathDesc": "如果文件夹不存在,则自动创建", "disableDownload": "禁用下载", - "disableDownloadDesc": "阻止从远程会话下载文件", + "disableDownloadDesc": "禁止从远程会话下载文件", "disableUpload": "禁用上传", - "disableUploadDesc": "阻止向远程会话上传文件", - "enableTouch": "启用触摸功能", - "enableTouchDesc": "启用触摸输入转发", + "disableUploadDesc": "禁止上传文件到远程会话", + "enableTouch": "启用触控", + "enableTouchDesc": "启用触控输入转发", "consoleSession": "控制台会话", - "consoleSessionDesc": "连接到控制台(会话 0),而不是新建会话。", + "consoleSessionDesc": "连接到控制台(会话 0)而不是新会话", "sendWolPacket": "发送 WOL 数据包", - "sendWolPacketDesc": "连接前发送唤醒包以唤醒此主机", + "sendWolPacketDesc": "在连接前发送魔术包以唤醒此主机", "disableCopy": "禁用复制", - "disableCopyDesc": "阻止从远程会话复制文本", + "disableCopyDesc": "禁止从远程会话复制文本", "disablePaste": "禁用粘贴", - "disablePasteDesc": "阻止将文本粘贴到远程会话中", - "createPathIfMissing": "如果路径缺失,则创建路径。", + "disablePasteDesc": "禁止粘贴文本到远程会话", + "createPathIfMissing": "如果路径不存在则创建", "createPathIfMissingDesc": "自动创建录制目录", "excludeOutput": "排除输出", - "excludeOutputDesc": "不要录制屏幕输出(仅录制元数据)", + "excludeOutputDesc": "不录制屏幕输出(仅元数据)", "excludeMouse": "排除鼠标", - "excludeMouseDesc": "请勿记录鼠标移动", - "includeKeystrokes": "包含按键操作", - "includeKeystrokesDesc": "除了屏幕输出外,还要记录原始按键操作。", - "vncPort": "VNC端口", - "vncPassword": "VNC密码", - "vncUsernameOptional": "用户名(可选)", - "vncLeaveBlank": "如果不需要,请留空。", + "excludeMouseDesc": "不录制鼠标移动", + "includeKeystrokes": "包含击键", + "includeKeystrokesDesc": "除了屏幕输出外,还录制原始击键", + "vncPort": "VNC 端口", + "vncPassword": "VNC 密码", + "vncUsernameOptional": "用户名 (可选)", + "vncLeaveBlank": "如不需要请留空", "cursorMode": "光标模式", "swapRedBlue": "交换红/蓝", - "swapRedBlueDesc": "交换红色和蓝色通道(修复一些颜色问题)", + "swapRedBlueDesc": "交换红蓝色彩通道 (可修复某些色彩问题)", "readOnly": "只读", - "readOnlyDesc": "无需发送任何输入即可查看远程屏幕", + "readOnlyDesc": "仅查看远程屏幕,不发送任何输入", "telnetPort": "Telnet 端口", "terminalType": "终端类型", "fontName": "字体名称", "fontSize": "字体大小", "colorScheme": "配色方案", "backspaceKey": "退格键", - "saveHostFirst": "先保存主机。", - "sharingOptionsAfterSave": "保存主机后即可使用分享选项。", + "saveHostFirst": "请先保存主机。", + "sharingOptionsAfterSave": "保存主机后,共享选项可用。", "permissionLevel": "权限级别", "typeHeader": "类型", "targetHeader": "目标", - "permissionHeader": "允许", + "permissionHeader": "权限", "cancelBtn": "取消", - "savingBtn": "保存...", + "savingBtn": "保存中...", "updateHostBtn": "更新主机", "addHostBtn": "添加主机" } }, "commandPalette": { - "searchPlaceholder": "搜索主机、命令或设置……", + "searchPlaceholder": "搜索主机、命令或设置...", "quickActions": "快速操作", "hostManager": "主机管理器", "hostManagerDesc": "管理、添加或编辑主机", "addNewHost": "添加新主机", - "addNewHostDesc": "注册新主机", - "adminSettings": "管理员设置", - "adminSettingsDesc": "配置系统首选项和用户", - "userProfile": "用户个人资料", - "userProfileDesc": "管理您的帐户和偏好设置", + "addNewHostDesc": "注册新的主机", + "adminSettings": "管理设置", + "adminSettingsDesc": "配置系统偏好和用户", + "userProfile": "用户资料", + "userProfileDesc": "管理您的账户和偏好", "addCredential": "添加凭据", "addCredentialDesc": "存储 SSH 密钥或密码", - "tmuxMonitor": "Tmux 监视器", + "tmuxMonitor": "Tmux 监控器", "tmuxMonitorDesc": "监控主机上的 tmux 会话", - "recentActivity": "近期活动", - "serversAndHosts": "服务器和主机", - "noHostsFound": "未找到与“{{search}}”匹配的主机", + "recentActivity": "最近活动", + "serversAndHosts": "服务器与主机", + "noHostsFound": "未找到匹配 \"{{search}}\" 的主机", "links": "链接", "navigate": "导航", "select": "选择", - "toggleWith": "切换" + "toggleWith": "切换:使用" }, "splitScreen": { "paneEmpty": "窗格 {{index}} - 空", @@ -1406,25 +1476,78 @@ "focusedPane": "活动窗格" }, "connections": { - "noConnections": "没有连接", - "noConnectionsDesc": "打开终端、文件管理器或远程桌面即可在此处查看连接。", + "noConnections": "无连接", + "noConnectionsDesc": "打开终端、文件管理器或远程桌面以在此查看连接", "connectedFor": "已连接 {{duration}}", "connected": "已连接", - "disconnected": "断开连接", + "disconnected": "已断开连接", "closeTab": "关闭标签页", - "closeConnection": "紧密联系", + "closeConnection": "关闭连接", "forgetTab": "忘记", - "removeBackground": "消除", + "removeBackground": "移除", "reconnect": "重新连接", - "reopenTab": "重新开放", + "reopenTab": "重新打开", "sectionOpen": "打开", - "sectionBackground": "背景", - "backgroundDesc": "断开连接后,会话仍会保持 30 分钟,并且可以重新连接。", - "persisted": "在后台持续运行", - "expiresIn": "有效期至 {{duration}}", - "search": "搜索关联...", - "noSearchResults": "没有找到符合您搜索条件的结果。", - "rename": "重命名会话" + "sectionBackground": "后台", + "backgroundDesc": "会话在断开连接后持续 30 分钟,可以重新连接", + "persisted": "已在后台保持连接", + "expiresIn": "将在 {{duration}} 后到期", + "search": "搜索连接...", + "noSearchResults": "没有连接匹配您的搜索", + "rename": "重命名会话", + "sectionSharedWithMe": "与我分享", + "sharedBy": "由 {{username}} 分享", + "join": "加入", + "sharedSessionLabel": "{{hostName}} (共享)" + }, + "sessionSharing": { + "guestView": { + "loading": "正在连接到共享会话...", + "linkInvalid": "此分享链接无效、已过期或已被撤销。", + "rateLimited": "尝试次数过多,请稍后再试。", + "sessionEnded": "本次会议已结束", + "readOnlyBadge": "仅查看" + }, + "modalTitle": "分享会话", + "shareButton": "分享", + "notReadyToShare": "会话尚未准备好共享", + "modeTab": { + "link": "关联", + "user": "用户" + }, + "linkModeDescription": "任何人只要拥有此链接即可加入,无需注册账号。", + "userModeDescription": "将主机共享给已拥有此主机访问权限的特定用户。如果他们还没有访问权限,请先将主机共享给他们,或者使用链接代替。共享后,会话将显示在他们的“连接”选项卡中。", + "permissionLevel": { + "label": "权限级别", + "readOnly": "只读", + "readOnlyDescription": "可以观看直播,但无法打字或互动。", + "readWrite": "读写", + "readWriteDescription": "可以像会话所有者一样进行输入和互动。" + }, + "expiryLabel": "链接过期", + "createLinkButton": "创建链接", + "createShareButton": "与用户分享", + "searchUsersPlaceholder": "搜索用户...", + "noUsersFound": "未找到用户", + "linkCreated": "已创建分享链接", + "linkCopied": "链接已复制到剪贴板", + "copyLink": "复制链接", + "shareCreated": "会话已共享。它将出现在对方的“连接”选项卡中。", + "shareFailed": "创建共享失败", + "userLacksHostAccess": "该用户目前没有访问此主机的权限。请先与其共享主机,或使用链接进行共享。", + "activeShares": "活跃股", + "noActiveShares": "本会话无活跃份额", + "revoke": "撤销", + "revokeConfirmTitle": "撤销此份额?", + "revokeConfirmDescription": "任何使用此共享文件夹的用户都将立即失去访问权限。", + "revoked": "股份被撤销", + "revokeFailed": "撤销股份失败", + "joinCount": "{{count}} 加入", + "joinCount_other": "{{count}} 加入", + "expiresAt": "过期时间 {{date}}", + "linkShareBadge": "关联", + "userShareBadge": "用户: {{username}}", + "loadSharesFailed": "无法加载活跃份额" }, "guacamole": { "connecting": "正在连接到 {{type}} 会话...", @@ -1435,33 +1558,38 @@ "noHostSelected": "未选择主机", "reconnect": "重新连接", "retry": "重试", - "guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确保 guacd 正在运行、可访问,并且在管理设置中已正确配置。", + "guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确保 guacd 正在运行并可访问,且在管理设置中正确配置。", + "credentialPromptTitle": "输入 RDP 凭据", + "credentialPromptDescription": "此主机设置为在连接时提示输入凭据。这些凭据仅用于当前会话,不会被保存。", + "connect": "连接", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L(锁屏)", + "winL": "Win+L (锁定屏幕)", "winKey": "Windows 键", "ctrl": "Ctrl", - "alt": "另类", - "shift": "转移", - "win": "赢", - "stickyActive": "{{key}} (已锁定 - 单击释放)", - "stickyInactive": "{{key}} (点击锁定)", - "esc": "逃脱", - "tab": "标签页", - "home": "家", - "end": "结尾", - "pageUp": "上一页", - "pageDown": "向下翻页", - "arrowUp": "向上箭头", - "arrowDown": "向下箭头", - "arrowLeft": "左箭头", - "arrowRight": "向右箭头", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}}(已锁定 - 点击释放)", + "stickyInactive": "{{key}}(点击锁定)", + "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": "功能键", "reconnect": "重新连接会话", "collapse": "折叠工具栏", "expand": "展开工具栏", - "dragHandle": "拖动以重新定位" + "dragHandle": "拖动以重新定位", + "switchToTrackpad": "切换到触控板模式(拖动移动光标,点击点击)", + "switchToTouch": "切换到触摸模式(直接点击你想点击的地方)" } }, "terminal": { @@ -1471,76 +1599,81 @@ "reconnect": "重新连接", "connectionLost": "连接丢失", "connected": "已连接", - "clipboardWriteFailed": "复制到剪贴板失败。请确保页面是通过 HTTPS 或本地主机访问的。", - "clipboardReadFailed": "读取剪贴板内容失败。请确保已授予剪贴板权限。", - "clipboardHttpWarning": "粘贴操作需要 HTTPS。请使用 Ctrl+Shift+V 或通过 HTTPS 提供 Termix 服务。", - "passwordPromptFillTitle": "请将已保存的密码填入此提示框?", + "clipboardWriteFailed": "复制到剪贴板失败。请确保页面通过 HTTPS 或 localhost 提供服务。", + "clipboardReadFailed": "从剪贴板读取失败。请确保已授予剪贴板权限。", + "clipboardHttpWarning": "粘贴需要 HTTPS。请使用 Ctrl+Shift+V 或通过 HTTPS 提供 Termix。", + "passwordPromptFillTitle": "将保存的密码填入此提示?", "unknownError": "发生未知错误", "websocketError": "WebSocket 连接错误", - "connecting": "正在连接...", + "connecting": "正在连接…", "noHostSelected": "未选择主机", - "reconnecting": "正在重新连接... ({{attempt}}/{{max}})", - "reconnected": "已成功重新连接", - "tmuxSessionCreated": "tmux 会话已创建: {{name}}", - "tmuxSessionAttached": "tmux 会话已连接: {{name}}", + "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 会话中分离", + "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": "需要双因素身份验证", + "totpRequired": "需要双因素认证", "totpCodeLabel": "验证码", - "totpVerify": "核实", - "warpgateAuthRequired": "需要传送门认证", + "totpVerify": "验证", + "mfaPromptRequired": "需要身份验证", + "mfaPushRequired": "需要推送身份验证", + "mfaMenuPlaceholder": "请输入您的回复", + "mfaWaitingApproval": "正在等待设备审核通过……", + "mfaSendRequest": "发送请求", + "warpgateAuthRequired": "需要 Warpgate 认证", "warpgateSecurityKey": "安全密钥", - "warpgateAuthUrl": "身份验证 URL", + "warpgateAuthUrl": "认证 URL", "warpgateOpenBrowser": "在浏览器中打开", - "warpgateContinue": "我已经完成身份验证", - "opksshAuthRequired": "需要 OPKSSH 身份验证", - "opksshAuthDescription": "请在浏览器中完成身份验证以继续。此会话有效期为 24 小时。", - "opksshOpenBrowser": "打开浏览器进行身份验证", - "opksshWaitingForAuth": "正在等待浏览器身份验证...", - "opksshAuthenticating": "正在处理身份验证...", - "opksshTimeout": "身份验证超时,请重试。", - "opksshAuthFailed": "身份验证失败。请检查您的凭据并重试。", + "warpgateContinue": "我已完成认证", + "opksshAuthRequired": "需要 OPKSSH 认证", + "opksshAuthDescription": "在浏览器中完成认证以继续。此会话将在 24 小时内有效。", + "opksshOpenBrowser": "打开浏览器进行认证", + "opksshWaitingForAuth": "等待浏览器认证...", + "opksshAuthenticating": "正在处理认证...", + "opksshTimeout": "认证超时,请重试。", + "opksshAuthFailed": "认证失败,请检查凭据并重试。", "opksshSignInWith": "使用 {{provider}} 登录", - "vaultAuthTitle": "需要登录 Vault。", - "vaultAuthDescription": "已打开一个窗口,供您登录 HashiCorp Vault。请在那里完成登录;此连接将自动继续。", - "vaultAuthFailed": "Vault身份验证失败,请重试。", + "vaultAuthTitle": "需要 Vault 登录", + "vaultAuthDescription": "已打开窗口以登录 HashiCorp Vault。在该处完成登录;此连接将自动继续。", + "vaultAuthFailed": "Vault 认证失败,请重试。", "vaultReopen": "重新打开登录窗口", "sudoPasswordPopupTitle": "输入密码?", "linkDialogTitle": "打开链接", "linkDialogOpen": "打开", "linkDialogCopy": "复制", - "websocketAbnormalClose": "连接意外关闭。这可能是由于反向代理或 SSL 配置问题导致的。请检查服务器日志。", + "websocketAbnormalClose": "连接意外关闭。这可能是因为反向代理或SSL配置问题。请检查服务器日志。", "connectionLogTitle": "连接日志", - "connectionLogCopy": "将日志复制到剪贴板", + "connectionLogCopy": "复制日志到剪贴板", "connectionLogEmpty": "暂无连接日志", - "connectionLogWaiting": "正在等待连接日志...", + "connectionLogWaiting": "等待连接日志...", "connectionLogCopied": "连接日志已复制到剪贴板", - "connectionLogCopyFailed": "无法将日志复制到剪贴板", - "connectionRejected": "服务器拒绝连接。请检查您的身份验证和网络配置。", - "hostKeyRejected": "SSH主机密钥验证失败。连接已取消。", - "sessionTakenOver": "会话已在另一个标签页中打开。正在重新连接……", + "connectionLogCopyFailed": "复制日志到剪贴板失败", + "connectionRejected": "服务器拒绝了连接。请检查您的身份验证和网络配置。", + "hostKeyRejected": "SSH主机密钥验证被拒绝。连接已取消。", + "sessionTakenOver": "会话已在另一个标签页中打开。正在重新连接...", "split": { - "splitTab": "分页标签", + "splitTab": "拆分标签页", "addToSplit": "添加到拆分", "removeFromSplit": "从拆分中移除" } @@ -1552,374 +1685,374 @@ "folder": "文件夹", "uploadFile": "上传文件", "downloadFile": "下载", - "extractArchive": "提取存档", - "extractingArchive": "正在提取 {{name}}...", - "archiveExtractedSuccessfully": "{{name}} 提取成功", - "extractFailed": "提取失败", + "extractArchive": "解压归档文件", + "extractingArchive": "正在解压 {{name}}...", + "archiveExtractedSuccessfully": "{{name}} 解压成功", + "extractFailed": "解压失败", "compressFile": "压缩文件", "compressFiles": "压缩文件", - "compressFilesDesc": "将 {{count}} 个项目压缩到一个存档中", - "archiveName": "存档名称", - "enterArchiveName": "请输入存档名称...", + "compressFilesDesc": "将 {{count}} 个项目压缩成一个归档文件", + "archiveName": "归档名称", + "enterArchiveName": "输入归档名称...", "compressionFormat": "压缩格式", - "selectedFiles": "选定文件", - "andMoreFiles": "and {{count}} more...", + "selectedFiles": "已选文件", + "andMoreFiles": "还有 {{count}} 个...", "compress": "压缩", - "compressingFiles": "将 {{count}} 个项目压缩到 {{name}}...", + "compressingFiles": "正在将 {{count}} 个项目压缩成 {{name}}...", "filesCompressedSuccessfully": "{{name}} 创建成功", "compressFailed": "压缩失败", "edit": "编辑", "preview": "预览", - "previous": "以前的", + "previous": "上一个", "next": "下一个", "pageXOfY": "第 {{current}} 页,共 {{total}} 页", "zoomOut": "缩小", "zoomIn": "放大", - "newFile": "新文件", + "newFile": "新建文件", "newFolder": "新建文件夹", "rename": "重命名", - "uploading": "正在上传...", + "uploading": "上传中...", "uploadingFile": "正在上传 {{name}}...", "fileName": "文件名", "folderName": "文件夹名称", - "fileUploadedSuccessfully": "文件“{{name}}”已成功上传", - "failedToUploadFile": "文件上传失败", - "fileDownloadedSuccessfully": "文件“{{name}}”已成功下载", - "failedToDownloadFile": "文件下载失败", - "fileCreatedSuccessfully": "文件“{{name}}”已成功创建", - "folderCreatedSuccessfully": "文件夹“{{name}}”已成功创建", + "fileUploadedSuccessfully": "文件 \"{{name}}\" 上传成功", + "failedToUploadFile": "上传文件失败", + "fileDownloadedSuccessfully": "文件 \"{{name}}\" 下载成功", + "failedToDownloadFile": "下载文件失败", + "fileCreatedSuccessfully": "文件 \"{{name}}\" 创建成功", + "folderCreatedSuccessfully": "文件夹 \"{{name}}\" 创建成功", "failedToCreateItem": "创建项目失败", - "operationFailed": "{{operation}} 操作对 {{name}}: {{error}} 失败", + "operationFailed": "对 {{name}} 的 {{operation}} 操作失败:{{error}}", "failedToResolveSymlink": "解析符号链接失败", - "itemsDeletedSuccessfully": "{{count}} 项目已成功删除", + "itemsDeletedSuccessfully": "{{count}} 个项目删除成功", "failedToDeleteItems": "删除项目失败", "sudoPasswordRequired": "需要管理员密码", - "enterSudoPassword": "请输入 sudo 密码以继续此操作", + "enterSudoPassword": "输入 sudo 密码以继续此操作", "sudoPassword": "Sudo 密码", - "sudoOperationFailed": "sudo 操作失败", - "sudoAuthFailed": "sudo 身份验证失败", - "dragFilesToUpload": "文件拖放到这里即可上传", - "emptyFolder": "此文件夹为空。", + "sudoOperationFailed": "Sudo 操作失败", + "sudoAuthFailed": "Sudo 认证失败", + "dragFilesToUpload": "将文件拖放到此处以上传", + "emptyFolder": "此文件夹为空", "searchFiles": "搜索文件...", "upload": "上传", - "selectHostToStart": "选择主机以启动文件管理", - "sshRequiredForFileManager": "文件管理器需要 SSH 连接。此主机未启用 SSH 连接。", - "failedToConnect": "SSH连接失败。", + "selectHostToStart": "选择主机以开始文件管理", + "sshRequiredForFileManager": "文件管理器需要 SSH。此主机未启用 SSH。", + "failedToConnect": "连接 SSH 失败", "failedToLoadDirectory": "加载目录失败", "noSSHConnection": "没有可用的 SSH 连接", "copy": "复制", - "cut": "切", + "cut": "剪切", "paste": "粘贴", "copyPath": "复制路径", "copyPaths": "复制路径", "delete": "删除", - "properties": "特性", + "properties": "属性", "refresh": "刷新", - "downloadFiles": "下载 {{count}} 文件到浏览器", - "copyFiles": "复制 {{count}} 项", - "cutFiles": "剪切 {{count}} 项", - "deleteFiles": "删除 {{count}} 项", - "filesCopiedToClipboard": "{{count}} 项已复制到剪贴板", - "filesCutToClipboard": "{{count}} 项已复制到剪贴板", + "downloadFiles": "将 {{count}} 个文件下载到浏览器", + "copyFiles": "复制 {{count}} 个项目", + "cutFiles": "剪切 {{count}} 个项目", + "deleteFiles": "删除 {{count}} 个项目", + "filesCopiedToClipboard": "已将 {{count}} 个项目复制到剪贴板", + "filesCutToClipboard": "已将 {{count}} 个项目剪切到剪贴板", "pathCopiedToClipboard": "路径已复制到剪贴板", - "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "pathsCopiedToClipboard": "已将 {{count}} 条路径复制到剪贴板", "failedToCopyPath": "无法将路径复制到剪贴板", "copyFolderLink": "复制文件夹链接", - "copyCurrentFolderLink": "复制当前文件夹的链接", + "copyCurrentFolderLink": "复制当前文件夹链接", "folderLinkCopied": "文件夹链接已复制到剪贴板", - "failedToCopyFolderLink": "复制文件夹链接失败", + "failedToCopyFolderLink": "无法复制文件夹链接", "movedItems": "已移动 {{count}} 个项目", "failedToDeleteItem": "删除项目失败", - "itemRenamedSuccessfully": "{{type}} 已成功重命名", + "itemRenamedSuccessfully": "{{type}} 重命名成功", "failedToRenameItem": "重命名项目失败", "download": "下载", - "openExternalEditor": "向外开放", + "openExternalEditor": "在外部打开", "chooseExternalEditor": "选择外部编辑器", - "externalEditorSelected": "外部编辑已选定", - "externalEditorDesktopOnly": "外部编辑器仅在桌面应用程序中可用", - "externalEditorOpened": "在外部编辑器中打开。保存后,更改将上传回服务器。", + "externalEditorSelected": "已选择外部编辑器", + "externalEditorDesktopOnly": "外部编辑器仅在桌面应用中可用", + "externalEditorOpened": "已在外部编辑器中打开。在其中保存文件会将更改上传回服务器。", "failedToOpenExternalEditor": "无法打开外部编辑器", - "failedToSelectExternalEditor": "选择外部编辑器失败", + "failedToSelectExternalEditor": "无法选择外部编辑器", "permissions": "权限", - "size": "尺寸", - "modified": "修改的", - "path": "小路", - "confirmDelete": "您确定要删除 {{name}} 吗?", - "permissionDenied": "没有权限", + "size": "大小", + "modified": "修改时间", + "path": "路径", + "confirmDelete": "确定要删除 {{name}} 吗?", + "permissionDenied": "权限被拒绝", "serverError": "服务器错误", "fileSavedSuccessfully": "文件已成功保存", - "failedToSaveFile": "文件保存失败", - "confirmDeleteSingleItem": "您确定要永久删除“{{name}}”吗?", - "confirmDeleteMultipleItems": "您确定要永久删除 {{count}} 项吗?", - "confirmDeleteMultipleItemsWithFolders": "您确定要永久删除 {{count}} 项目吗?这包括文件夹及其内容。", - "confirmDeleteFolder": "您确定要永久删除文件夹“{{name}}”及其所有内容吗?", - "permanentDeleteWarning": "此操作无法撤销。物品将从服务器永久删除。", - "recent": "最近的", - "pinned": "置顶", + "failedToSaveFile": "保存文件失败", + "confirmDeleteSingleItem": "确定要永久删除“{{name}}”吗?", + "confirmDeleteMultipleItems": "确定要永久删除{{count}}个项目吗?", + "confirmDeleteMultipleItemsWithFolders": "确定要永久删除{{count}}个项目吗?这包括文件夹及其内容。", + "confirmDeleteFolder": "确定要永久删除文件夹“{{name}}”及其所有内容吗?", + "permanentDeleteWarning": "此操作无法撤销。项目将从服务器永久删除。", + "recent": "最近", + "pinned": "已固定", "folderShortcuts": "文件夹快捷方式", - "failedToReconnectSSH": "SSH 会话重新连接失败", + "failedToReconnectSSH": "重新连接SSH会话失败", "openTerminalHere": "在此处打开终端", - "run": "跑步", + "run": "运行", "openTerminalInFolder": "在此文件夹中打开终端", - "openTerminalInFileLocation": "打开终端,指向文件位置", - "runningFile": "运行中 - {{file}}", + "openTerminalInFileLocation": "在文件位置打开终端", + "runningFile": "正在运行 - {{file}}", "onlyRunExecutableFiles": "只能运行可执行文件", "directories": "目录", - "removedFromRecentFiles": "从最近文件中移除“{{name}}”", + "removedFromRecentFiles": "已从最近文件中移除“{{name}}”", "removeFailed": "移除失败", - "unpinnedSuccessfully": "已成功取消置顶“{{name}}”", - "unpinFailed": "解除锁定失败", + "unpinnedSuccessfully": "已成功取消固定“{{name}}”", + "unpinFailed": "取消固定失败", "removedShortcut": "已移除快捷方式“{{name}}”", "removeShortcutFailed": "移除快捷方式失败", - "clearedAllRecentFiles": "已清除所有最近文件", - "clearFailed": "清除失败", - "removeFromRecentFiles": "从最近文件中删除", - "clearAllRecentFiles": "清除所有最近文件", - "unpinFile": "解压文件", + "clearedAllRecentFiles": "已清空所有最近文件", + "clearFailed": "清空失败", + "removeFromRecentFiles": "从最近文件中移除", + "clearAllRecentFiles": "清空所有最近文件", + "unpinFile": "取消固定文件", "removeShortcut": "移除快捷方式", - "pinFile": "针文件", + "pinFile": "固定文件", "addToShortcuts": "添加到快捷方式", "pasteFailed": "粘贴失败", - "noUndoableActions": "没有可撤销的操作", - "undoCopySuccess": "撤销复制操作:已删除 {{count}} 个已复制的文件", + "noUndoableActions": "无可撤销的操作", + "undoCopySuccess": "已撤销复制操作:已删除{{count}}个已复制的文件", "undoCopyFailedDelete": "撤销失败:无法删除任何已复制的文件", "undoCopyFailedNoInfo": "撤销失败:找不到已复制的文件信息", - "undoMoveSuccess": "撤销移动操作:已将 {{count}} 个文件移回原始位置", - "undoMoveFailedMove": "撤销失败:无法将任何文件移回。", - "undoMoveFailedNoInfo": "撤销失败:找不到已移动的文件信息", - "undoDeleteNotSupported": "删除操作无法撤销:文件已从服务器永久删除。", + "undoMoveSuccess": "已撤销移动操作:已将{{count}}个文件移回原始位置", + "undoMoveFailedMove": "撤销失败:无法将任何文件移回", + "undoMoveFailedNoInfo": "撤销失败:找不到移动的文件信息", + "undoDeleteNotSupported": "删除操作无法撤销:文件已从服务器永久删除", "undoTypeNotSupported": "不支持的撤销操作类型", "undoOperationFailed": "撤销操作失败", "unknownError": "未知错误", "confirm": "确认", - "find": "寻找...", - "replace": "代替", - "downloadInstead": "下载", + "find": "查找...", + "replace": "替换", + "downloadInstead": "改为下载", "keyboardShortcuts": "键盘快捷键", - "searchAndReplace": "搜索和替换", + "searchAndReplace": "查找和替换", "editing": "编辑", "search": "搜索", "findNext": "查找下一个", "findPrevious": "查找上一个", - "save": "节省", + "save": "保存", "selectAll": "全选", "undo": "撤销", "redo": "重做", - "moveLineUp": "移动阵容", - "moveLineDown": "向下移动线路", - "toggleComment": "切换评论", - "autoComplete": "自动完成", - "imageLoadError": "图片加载失败", - "startTyping": "开始输入……", - "unknownSize": "尺寸未知", + "moveLineUp": "上移一行", + "moveLineDown": "下移一行", + "toggleComment": "切换注释", + "autoComplete": "自动补全", + "imageLoadError": "加载图像失败", + "startTyping": "开始键入...", + "unknownSize": "未知大小", "fileIsEmpty": "文件为空", "largeFileWarning": "大文件警告", - "largeFileWarningDesc": "此文件大小为 {{size}} ,以文本格式打开时可能会导致性能问题。", - "fileNotFoundAndRemoved": "文件“{{name}}”未找到,已从最近/置顶文件中移除。", - "failedToLoadFile": "文件加载失败: {{error}}", - "serverErrorOccurred": "服务器出错,请稍后再试。", + "largeFileWarningDesc": "该文件大小为 {{size}},以文本方式打开可能会导致性能问题。", + "fileNotFoundAndRemoved": "文件 \"{{name}}\" 未找到,已从最近/固定文件中移除", + "failedToLoadFile": "加载文件失败:{{error}}", + "serverErrorOccurred": "服务器发生错误,请稍后重试。", "autoSaveFailed": "自动保存失败", - "fileAutoSaved": "文件自动保存", - "moveFileFailed": "移动失败 {{name}}", + "fileAutoSaved": "文件已自动保存", + "moveFileFailed": "移动 {{name}} 失败", "moveOperationFailed": "移动操作失败", "canOnlyCompareFiles": "只能比较两个文件", - "comparingFiles": "正在比较文件: {{file1}} 和 {{file2}}", - "dragFailed": "拖拽操作失败", - "filePinnedSuccessfully": "文件“{{name}}”已成功固定", - "pinFileFailed": "文件固定失败", - "fileUnpinnedSuccessfully": "文件“{{name}}”已成功取消固定", + "comparingFiles": "正在比较文件:{{file1}} 和 {{file2}}", + "dragFailed": "拖动操作失败", + "filePinnedSuccessfully": "文件 \"{{name}}\" 固定成功", + "pinFileFailed": "固定文件失败", + "fileUnpinnedSuccessfully": "文件 \"{{name}}\" 取消固定成功", "unpinFileFailed": "取消固定文件失败", - "shortcutAddedSuccessfully": "文件夹快捷方式“{{name}}”已成功添加", + "shortcutAddedSuccessfully": "文件夹快捷方式 \"{{name}}\" 添加成功", "addShortcutFailed": "添加快捷方式失败", - "operationCompletedSuccessfully": "{{operation}} {{count}} 个项目已成功", - "operationCompleted": "{{operation}} {{count}} 项目", + "operationCompletedSuccessfully": "成功{{operation}}了{{count}}个项目", + "operationCompleted": "{{operation}}了{{count}}个项目", "downloadFileSuccess": "文件 {{name}} 下载成功", "downloadFileFailed": "下载失败", - "moveTo": "移至 {{name}}", - "diffCompareWith": "与 {{name}} 的差异比较", - "dragOutsideToDownload": "向窗口外拖动以下载({{count}} 文件)", + "moveTo": "移动到 {{name}}", + "diffCompareWith": "与 {{name}} 对比差异", + "dragOutsideToDownload": "拖到窗口外以下载 ({{count}} 个文件)", "newFolderDefault": "新建文件夹", - "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "已成功将 {{count}} 个项目移动到 {{target}}", + "newFileDefault": "新建文件.txt", + "successfullyMovedItems": "成功移动 {{count}} 个项目到 {{target}}", "move": "移动", - "searchInFile": "在文件中搜索(Ctrl+F)", + "searchInFile": "在文件中搜索 (Ctrl+F)", "showKeyboardShortcuts": "显示键盘快捷键", - "decreaseFontSize": "减小字体大小", - "increaseFontSize": "增大字体大小", - "startWritingMarkdown": "开始编写你的 Markdown 内容……", - "loadingFileComparison": "正在加载文件比较...", + "decreaseFontSize": "缩小字体", + "increaseFontSize": "放大字体", + "startWritingMarkdown": "开始编写Markdown内容...", + "loadingFileComparison": "加载文件比较中...", "reload": "重新加载", "compare": "比较", "sideBySide": "并排", - "inline": "排队", - "fileComparison": "文件比较: {{file1}} vs {{file2}}", - "fileTooLarge": "文件过大: {{error}}", - "sshConnectionFailed": "SSH 连接失败。请检查您与 {{name}} ({{ip}}:{{port}} ) 的连接。", - "loadFileFailed": "文件加载失败: {{error}}", - "connecting": "正在连接...", + "inline": "内联", + "fileComparison": "文件比较: {{file1}} 对比 {{file2}}", + "fileTooLarge": "文件过大: {{error}}", + "sshConnectionFailed": "SSH 连接失败。请检查您到 {{name}} ({{ip}}:{{port}}) 的连接", + "loadFileFailed": "加载文件失败: {{error}}", + "connecting": "连接中...", "connectedSuccessfully": "连接成功", - "totpVerificationFailed": "TOTP验证失败", - "warpgateVerificationFailed": "Warpgate 身份验证失败", - "authenticationFailed": "身份验证失败", - "incorrectPassphrase": "密码错误,请重试。", - "verificationCodePrompt": "验证码:", + "totpVerificationFailed": "TOTP 验证失败", + "warpgateVerificationFailed": "Warpgate 认证失败", + "authenticationFailed": "认证失败", + "incorrectPassphrase": "密码错误。请重试。", + "verificationCodePrompt": "验证码:", "changePermissions": "更改权限", "currentPermissions": "当前权限", "owner": "所有者", - "group": "团体", - "others": "其他的", - "read": "读", - "write": "写", + "group": "组", + "others": "其他", + "read": "读取", + "write": "写入", "execute": "执行", - "permissionsChangedSuccessfully": "权限已成功更改", - "failedToChangePermissions": "更改权限失败", - "name": "姓名", - "sortByName": "姓名", + "permissionsChangedSuccessfully": "权限修改成功", + "failedToChangePermissions": "权限修改失败", + "name": "名称", + "sortByName": "名称", "sortByDate": "修改日期", - "sortBySize": "尺寸", - "ascending": "上升", - "descending": "下降", - "root": "根", - "new": "新的", + "sortBySize": "大小", + "ascending": "升序", + "descending": "降序", + "root": "根目录", + "new": "新建", "sortBy": "排序方式", "items": "项目", - "selected": "已选", - "editor": "编辑", + "selected": "已选择", + "editor": "编辑器", "octal": "八进制", - "storage": "贮存", + "storage": "存储", "disk": "磁盘", - "used": "用过的", - "of": "的", + "used": "已用", + "of": "/", "toggleSidebar": "切换侧边栏", - "cannotLoadPdf": "无法加载PDF", - "pdfLoadError": "加载此PDF文件时出错。", - "loadingPdf": "正在加载PDF文件...", - "loadingPage": "页面正在加载..." + "cannotLoadPdf": "无法加载 PDF", + "pdfLoadError": "加载此 PDF 文件时出错。", + "loadingPdf": "正在加载 PDF...", + "loadingPage": "正在加载页面..." }, "transfer": { "copyToHost": "复制到主机…", - "moveToHost": "移至主机…", - "copyItemsToHost": "将 {{count}} 项复制到主机…", - "moveItemsToHost": "将 {{count}} 个物品移至主机…", - "noHostsConnected": "没有其他文件管理器主机可用。", - "noHostsConnectedHint": "在主机管理器中添加另一台启用了文件管理器的 SSH 主机。", + "moveToHost": "移动到主机…", + "copyItemsToHost": "复制 {{count}} 个项目到主机…", + "moveItemsToHost": "移动 {{count}} 个项目到主机…", + "noHostsConnected": "没有其他可用的文件管理器主机。", + "noHostsConnectedHint": "在主机管理器中添加启用文件管理器的其他 SSH 主机。", "selectDestinationHost": "选择目标主机", "destinationPath": "目标路径", - "recentDestinations": "近期目的地", - "collapseRecentDestinations": "收起最近的目的地", - "expandRecentDestinations": "展开最近目的地", + "recentDestinations": "最近目标", + "collapseRecentDestinations": "收起最近目标", + "expandRecentDestinations": "展开最近目标", "browseFolders": "浏览目标文件夹", "browseDestination": "浏览或输入路径", "confirmCopy": "复制", "confirmMove": "移动", - "transferring": "转移…", - "compressing": "正在压缩…", - "extracting": "提取…", - "transferringItems": "转移 {{current}} 个物品,共 {{total}} 个物品,共… 个物品。", - "transferSuccess": "转账完成", - "transferError": "转账失败", - "transferPartial": "转账完成,但出现 {{count}} 个错误", - "transferPartialHint": "转账失败: {{paths}}", - "itemsSummary": "{{count}} 项目", - "destMustBeDirectory": "对于多项传输,目标位置必须是目录。", + "transferring": "传输中…", + "compressing": "压缩中…", + "extracting": "解压中…", + "transferringItems": "正在传输 {{current}}/{{total}} 个项目…", + "transferSuccess": "传输完成", + "transferError": "传输失败", + "transferPartial": "传输完成,但有 {{count}} 个错误", + "transferPartialHint": "无法传输:{{paths}}", + "itemsSummary": "{{count}} 个项目", + "destMustBeDirectory": "对于多项目传输,目标必须是一个目录", "selectThisFolder": "选择此文件夹", - "browsePathWillBeCreated": "此文件夹尚不存在。传输开始时将创建此文件夹。", + "browsePathWillBeCreated": "此文件夹尚不存在,将在传输开始时创建。", "browsePathError": "无法在目标主机上打开此路径。", - "goUp": "上", - "copyFolderToHost": "将文件夹复制到主机…", - "moveFolderToHost": "将文件夹移动到主机…", - "hostReady": "准备好", - "hostConnecting": "正在连接…", + "goUp": "向上", + "copyFolderToHost": "复制文件夹到主机…", + "moveFolderToHost": "移动文件夹到主机…", + "hostReady": "就绪", + "hostConnecting": "连接中…", "hostDisconnected": "未连接", - "hostAuthRequired": "需要身份验证——请先在此主机上打开文件管理器。", + "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": "正在从目标位置… 提取", + "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}})…", + "progressReconnecting": "重新连接中…", + "parallelSegmentsLabel": "并行传输通道", + "parallelSegmentsOption": "{{count}} 个通道", + "parallelSegmentsHint": "大文件会被拆分为 256 MB 的块。多个通道使用独立连接(如同同时进行多个传输),以获得更高的总吞吐量。", + "progressTotalSpeed": "{{speed}} 总计({{lanes}} 通道)", + "progressTransferringItems": "正在传输文件({{current}}/{{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} 文件", - "sourceNotDeletedPartial": "保留源文件(部分传输)", - "jumpHostLimitation": "两台主机都必须能从Termix服务器访问。不支持主机之间的直接路由。", + "progressItems": "{{current}} / {{total}} 个文件", + "sourceNotDeletedPartial": "源文件已保留(部分传输)", + "jumpHostLimitation": "两个主机都必须能从 Termix 服务器访问。不支持直接的主机到主机路由。", "cancel": "取消", - "methodLabel": "转移方法", - "methodAuto": "汽车", + "methodLabel": "传输方式", + "methodAuto": "自动", "methodTar": "Tar 归档", - "methodItemSftp": "按文件 SFTP", - "methodAutoHint": "根据文件数量、大小和压缩率选择 tar 协议或按文件 SFTP 协议。单个文件始终使用流式 SFTP 协议。", - "methodTarHint": "在源端压缩,传输单个压缩包,在目标端解压。两个 Unix 主机都需要 tar 命令。", - "methodItemSftpHint": "通过 SFTP 逐个传输文件。支持所有主机,包括 Windows 系统。", - "methodPreviewLoading": "计算转移方法…", - "methodPreviewError": "无法预览传输方式。服务器将在您启动时选择一种传输方式。", + "methodItemSftp": "逐文件 SFTP", + "methodAutoHint": "根据文件数量、大小和可压缩性选择 tar 或逐文件 SFTP。单个文件始终使用流式 SFTP。", + "methodTarHint": "在源端压缩,传输单个归档文件,在目标端解压。要求两个 Unix 主机都有 tar。", + "methodItemSftpHint": "通过 SFTP 逐个传输文件。适用于包括 Windows 在内的所有主机。", + "methodPreviewLoading": "正在计算传输方式…", + "methodPreviewError": "无法预览传输方式。启动时服务器仍会选择方式。", "methodPreviewWillUseTar": "将使用:Tar 归档", - "methodPreviewWillUseItemSftp": "将使用:按文件 SFTP", - "methodPreviewScanSummary": "{{fileCount}} 个文件, {{totalSize}} 总计(在源主机上扫描)。", - "methodItemSftpLimitation": "每个文件都使用同一个 SFTP 流,以单文件复制的方式依次进行。所有文件的进度是合并计算的,因此在处理大文件时进度条移动较慢。", + "methodPreviewWillUseItemSftp": "将使用:逐文件 SFTP", + "methodPreviewScanSummary": "{{fileCount}} 个文件,总计 {{totalSize}}(已在源主机上扫描)。", + "methodItemSftpLimitation": "每个文件使用相同的 SFTP 流(与单文件拷贝一样),一个接一个传输。所有文件的进度合并显示,因此大文件传输时进度条移动缓慢。", "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": "您选择了逐文件 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。" }, "progressCancel": "取消", - "progressCancelling": "取消…", - "progressStalled": "停滞", - "resumedHint": "已重新连接到在另一个窗口中启动的活动传输。", - "transferCancelled": "转账已取消", - "transferCancelledCopyHint": "目标位置可能仍会保留部分文件。", - "transferCancelledMoveHint": "目标位置可能仍保留部分文件。源文件未被删除。", - "cleanupDestFiles": "清理目的地", + "progressCancelling": "正在取消…", + "progressStalled": "已停滞", + "resumedHint": "已重新连接到在另一个窗口中启动的进行中的传输。", + "transferCancelled": "传输已取消", + "transferCancelledCopyHint": "目标位置可能残留部分文件。", + "transferCancelledMoveHint": "目标位置可能残留部分文件。源文件未被删除。", + "cleanupDestFiles": "清理目标位置", "cleanupDestFilesSuccess": "已从目标位置删除部分文件", - "cleanupDestFilesPartial": "部分文件无法删除。", - "cleanupDestFilesNothing": "目的地无需清理", + "cleanupDestFilesPartial": "部分文件无法删除", + "cleanupDestFilesNothing": "目标位置无需清理", "cleanupDestFilesError": "清理失败", "retryTransfer": "重试", "retryTransferError": "重试失败", - "transferFailedRetryHint": "目标端保留了部分数据。连接恢复后,系统将恢复重试。" + "transferFailedRetryHint": "部分数据已保留在目标位置。连接恢复后重试将继续。" }, "tunnels": { - "noSshTunnels": "没有 SSH 隧道", - "createFirstTunnelMessage": "您尚未创建任何 SSH 隧道。请在主机管理器中配置隧道连接以开始使用。", + "noSshTunnels": "无 SSH 隧道", + "createFirstTunnelMessage": "您尚未创建任何 SSH 隧道。在主机管理器中配置隧道连接以开始使用。", "connected": "已连接", - "disconnected": "断开连接", - "connecting": "正在连接...", + "disconnected": "已断开", + "connecting": "连接中...", "error": "错误", - "canceling": "正在取消……", + "canceling": "取消中...", "connect": "连接", "disconnect": "断开", "cancel": "取消", - "port": "港口", - "localPort": "本地港口", + "port": "端口", + "localPort": "本地端口", "remotePort": "远程端口", "currentHostPort": "当前主机端口", "endpointPort": "端点端口", - "bindIp": "本地IP", + "bindIp": "本地 IP", "endpointSshConfig": "端点 SSH 配置", "endpointSshHost": "端点 SSH 主机", "endpointSshHostPlaceholder": "选择已配置的主机", - "endpointSshHostRequired": "为每个客户端隧道选择一个 SSH 主机端点。", - "attempt": "尝试 {{current}} 的 {{max}}", - "nextRetryIn": "下次重试时间为 {{seconds}} 秒", + "endpointSshHostRequired": "为每个客户端隧道选择一个端点 SSH 主机。", + "attempt": "尝试 {{current}}/{{max}}", + "nextRetryIn": "{{seconds}} 秒后重试", "clientTunnels": "客户端隧道", "clientTunnel": "客户端隧道", "addClientTunnel": "添加客户端隧道", @@ -1927,329 +2060,329 @@ "tunnelName": "隧道名称", "remoteHost": "远程主机", "autoStart": "自动启动", - "clientAutoStartDesc": "当此桌面客户端打开时开始,并保持连接。", - "clientManualStartDesc": "使用此行中的“开始”和“停止”按钮。Termix 不会自动打开它。", - "clientRemoteServerNote": "远程转发可能需要在终端 SSH 服务器上启用 AllowTcpForwarding 和 GatewayPorts 选项。当此桌面断开连接时,远程端口将关闭。", + "clientAutoStartDesc": "在此桌面客户端打开时启动并保持连接。", + "clientManualStartDesc": "从此行使用启动和停止。Termix 不会自动启动它。", + "clientRemoteServerNote": "远程转发可能需要在端点 SSH 服务器上设置 AllowTcpForwarding 和 GatewayPorts。此桌面断开连接时远程端口将关闭。", "clientTunnelStarted": "客户端隧道已启动", "clientTunnelStopped": "客户端隧道已停止", - "tunnelTestSucceeded": "隧道试验成功", + "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": "开始", + "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": "返回本地计算机的端点。", + "typeLocal": "本地 (-L)", + "typeRemote": "远程 (-R)", + "typeDynamic": "动态 (-D)", + "typeServerLocalDesc": "当前主机至端点。", + "typeServerRemoteDesc": "端点回连至当前主机。", + "typeClientLocalDesc": "本地计算机至端点。", + "typeClientRemoteDesc": "端点回连至本地计算机。", "typeClientDynamicDesc": "本地计算机上的 SOCKS。", - "typeDynamicDesc": "通过 SSH 转发 SOCKS5 CONNECT 流量。", + "typeDynamicDesc": "通过 SSH 转发 SOCKS5 CONNECT 流量", "forwardDescriptionServerLocal": "当前主机 {{sourcePort}} → 端点 {{endpointPort}}。", "forwardDescriptionServerRemote": "端点 {{endpointPort}} → 当前主机 {{sourcePort}}。", "forwardDescriptionServerDynamic": "当前主机上的 SOCKS {{sourcePort}}。", "forwardDescriptionClientLocal": "本地 {{sourcePort}} → 远程 {{endpointPort}}。", "forwardDescriptionClientRemote": "远程 {{sourcePort}} → 本地 {{endpointPort}}。", - "forwardDescriptionClientDynamic": "本地端口上的 SOCKS {{sourcePort}}。", + "forwardDescriptionClientDynamic": "本地端口 {{sourcePort}} 上的 SOCKS。", "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": "上次测试", + "autoNameClientDynamic": "SOCKS {{localPort}} 通过 {{endpoint}}", + "route": "路由:", + "lastStarted": "最后启动", + "lastTested": "最后测试", "lastError": "最后错误", "maxRetries": "最大重试次数", - "maxRetriesDescription": "最大重试次数。", + "maxRetriesDescription": "最大重试尝试次数。", "retryInterval": "重试间隔(秒)", - "retryIntervalDescription": "重试之间需要等待一段时间。", - "local": "当地的", - "remote": "偏僻的", - "destination": "目的地", - "host": "主持人", + "retryIntervalDescription": "重试间隔等待时间。", + "local": "本地", + "remote": "远程", + "destination": "目标", + "host": "主机", "mode": "模式", "noHostSelected": "未选择主机", - "working": "在职的..." + "working": "处理中..." }, "cardGrid": { - "dragToMove": "拖动移动", - "dragToResize": "拖动调整大小", - "changeWidth": "改变宽度", - "removeCard": "移除 {{label}}", + "dragToMove": "拖动以移动", + "dragToResize": "拖动以调整大小", + "changeWidth": "更改宽度", + "removeCard": "移除{{label}}", "addCard": "添加", - "columns": "列", - "empty": "暂无卡牌。请使用下方的“添加”按钮放置卡牌。" + "columns": "列数", + "empty": "无卡片。请使用下方添加按钮放置卡片。" }, "hostMetrics": { - "cpu": "中央处理器", - "memory": "记忆", + "cpu": "CPU", + "memory": "内存", "disk": "磁盘", "network": "网络", - "uptime": "正常运行时间", - "processes": "流程", - "available": "可用的", - "free": "自由的", - "connecting": "正在连接...", + "uptime": "运行时间", + "processes": "进程", + "available": "可用", + "free": "空闲", + "connecting": "连接中...", "connectionFailed": "连接服务器失败", "naCpus": "N/A CPU", "cpuCores_one": "{{count}} 核心", "cpuCores_other": "{{count}} 核心", "cpuUsage": "CPU 使用率", - "memoryUsage": "内存使用情况", - "diskUsage": "磁盘使用情况", + "memoryUsage": "内存使用率", + "diskUsage": "磁盘使用率", "temperature": "温度", "highestTemperature": "最高温度", "failedToFetchHostConfig": "获取主机配置失败", "serverOffline": "服务器离线", "cannotFetchMetrics": "无法从离线服务器获取指标", - "totpFailed": "TOTP验证失败", - "noneAuthNotSupported": "主机指标不支持“无”身份验证类型。", + "totpFailed": "TOTP 验证失败", + "noneAuthNotSupported": "主机指标不支持 'none' 认证类型。", "noHostSelected": "未选择主机", - "load": "加载", + "load": "负载", "systemInfo": "系统信息", "hostname": "主机名", "operatingSystem": "操作系统", - "kernel": "核心", + "kernel": "内核", "seconds": "秒", "networkInterfaces": "网络接口", "noInterfacesFound": "未找到网络接口", - "noProcessesFound": "未找到任何进程", - "processesTotal": "全部的", - "processesRunning": "跑步", - "loginStats": "SSH 登录统计信息", - "noRecentLoginData": "没有最近的登录数据", + "noProcessesFound": "未找到进程", + "processesTotal": "总计", + "processesRunning": "运行中", + "loginStats": "SSH 登录统计", + "noRecentLoginData": "无最近登录数据", "executingQuickAction": "正在执行 {{name}}...", - "quickActionSuccess": "{{name}} 已成功完成", - "quickActionFailed": "{{name}} 失败", + "quickActionSuccess": "{{name}} 执行成功", + "quickActionFailed": "{{name}} 执行失败", "quickActionError": "执行 {{name}} 失败", "ports": { "title": "监听端口", "protocol": "协议", - "port": "港口", + "port": "端口", "address": "地址", - "process": "过程", + "process": "进程", "search": "搜索端口...", "allProtocols": "全部", "noData": "无监听端口数据" }, "firewall": { "title": "防火墙", - "inactive": "非活跃状态", - "policy": "政策", + "inactive": "未激活", + "policy": "策略", "rules": "规则", - "noRules": "这条链没有规则", - "noData": "没有可用的防火墙数据", - "action": "行动", - "protocol": "原型", - "port": "港口", + "noRules": "该链中无规则", + "noData": "无可用防火墙数据", + "action": "操作", + "protocol": "协议", + "port": "端口", "source": "来源", - "anywhere": "任何地方", - "chains": "链条" + "anywhere": "任意", + "chains": "链" }, "loadAvg": "平均负载", - "swap": "交换", - "architecture": "建筑学", + "swap": "交换空间", + "architecture": "架构", "refresh": "刷新", "retry": "重试", "customize": "自定义布局", "reset": "重置", - "tabLive": "居住", - "editModeInstructions": "拖动卡片即可重新排列,拖动底部边缘可调整大小,使用宽度按钮可更改卡片的跨度。在下方添加或删除卡片。", + "tabLive": "实时", + "editModeInstructions": "拖动卡片可重新排序,拖动底部边缘可调整大小,使用宽度按钮可更改卡片的跨度。在下方添加或移除卡片。", "managers": { "services": "服务", - "processInspector": "过程检验员", + "processInspector": "进程查看器", "logViewer": "日志查看器", "cron": "定时任务", - "packages": "包裹", + "packages": "软件包", "ssl": "SSL证书", "firewall": "防火墙", "users": "用户与权限", "healthCheck": "健康检查", - "diskBreakdown": "磁盘故障", - "systemdTimers": "定时器", - "topMemory": "记忆顶部", + "diskBreakdown": "磁盘详情", + "systemdTimers": "计时器", + "topMemory": "按内存占用排行", "noData": "无数据", - "sudoHint": "在主机编辑器中为此主机设置 sudo 密码,以启用特权操作。", + "sudoHint": "在主机编辑器中为此主机设置 sudo 密码以启用特权操作。", "filter": "筛选...", - "start": "开始", + "start": "启动", "stop": "停止", "restart": "重启", "actionDone": "{{name}} 已更新", "actionFailed": "操作失败", - "signalSent": "向 PID {{pid}} 发送信号", - "killHint": "单击:终止(SIGTERM)。右键单击:强制终止(SIGKILL)。", - "working": "在职的...", + "signalSent": "信号已发送至 PID {{pid}}", + "killHint": "单击:终止 (SIGTERM)。右键单击:强制终止 (SIGKILL)。", + "working": "处理中...", "update": "更新", "upgradeAll": "全部升级", - "allUpToDate": "一切都是最新的", - "save": "节省", + "allUpToDate": "所有内容均为最新", + "save": "保存", "command": "命令", "enabled": "已启用", - "cronSaved": "Crontab 已更新", - "clients": "客户", + "cronSaved": "定时任务已更新", + "clients": "客户端", "dryRun": "试运行", - "renew": "更新", - "noAcmeClient": "在此主机上未找到 ACME 客户端(certbot 或 acme.sh)。", - "addInputRule": "添加输入规则", - "firewallWarning": "更改仅在运行时生效,直到持久化为止。请注意不要将自己锁定在外部。", - "ruleApplied": "适用规则", - "invalidPort": "请输入有效端口号(1-65535)", + "renew": "续期", + "noAcmeClient": "此主机上未找到 ACME 客户端 (certbot 或 acme.sh)。", + "addInputRule": "添加 INPUT 规则", + "firewallWarning": "更改仅在运行时有效,需持久化保存。小心不要将自己锁定在外。", + "ruleApplied": "规则已应用", + "invalidPort": "请输入有效端口 (1-65535)", "newUsername": "新用户名", "addUser": "添加", "deleteUser": "删除用户", - "noHealthChecks": "尚未配置任何健康检查。", - "follow": "跟随", + "noHealthChecks": "尚未配置健康检查。", + "follow": "跟踪", "noLogData": "无日志输出。", - "tree": "树", - "enableDisable": "启动时启用/禁用", + "tree": "树形", + "enableDisable": "启用/禁用开机自启", "grantSudo": "授予 sudo", "revokeSudo": "撤销 sudo", "sslIssueCert": "签发证书", - "sslExpired": "已到期", - "sslInDays": "在 {{days}}d", - "sslNeedDomain": "请至少输入一个域名", - "sslIssued": "签发证书", - "sslDomainsPlaceholder": "example.com,www.example.com", - "sslHttpStandalone": "HTTP(独立)", - "sslHttpWebroot": "HTTP(webroot)", + "sslExpired": "已过期", + "sslInDays": "{{days}} 天后", + "sslNeedDomain": "至少输入一个域名", + "sslIssued": "证书已签发", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (独立模式)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNS 提供商(例如 Cloudflare)", - "sslIssueHint": "主机上必须已配置 DNS 提供商凭据。", - "sslRevoke": "撤销证书", - "sslRevoked": "证书已撤销", - "sslRevokeConfirm": "撤销并删除证书“{{name}}”?此操作无法撤销。", - "healthRun": "跑步", - "healthName": "姓名", - "healthTarget": "主机/地址", + "sslDnsProvider": "DNS 提供商 (如 cloudflare)", + "sslIssueHint": "DNS 提供商凭据必须已在主机上配置。", + "sslRevoke": "吊销证书", + "sslRevoked": "证书已吊销", + "sslRevokeConfirm": "吊销并删除证书 \"{{name}}\"?此操作无法撤销。", + "healthRun": "运行", + "healthName": "名称", + "healthTarget": "主机 / 地址", "healthAddCheck": "添加检查", "healthSaved": "健康检查已保存", - "healthMissingFields": "每项检查都需要名称和目标", + "healthMissingFields": "每个检查都需要名称和目标", "logFile": "文件", "logUnit": "单元", - "logCustomPath": "/var/log 下的自定义路径(可选)", - "logGrep": "过滤线……", + "logCustomPath": "自定义路径 (位于 /var/log 下,可选)", + "logGrep": "过滤行...", "firewallPersist": "持久化规则", - "firewallPersisted": "防火墙规则保留", + "firewallPersisted": "防火墙规则已持久化", "wireguard": "WireGuard", - "tailscale": "尾鳞", - "wgNotInstalled": "此主机上未安装 WireGuard", + "tailscale": "Tailscale", + "wgNotInstalled": "此主机未安装 WireGuard", "wgNoInterfaces": "未配置 WireGuard 接口", - "wgInterfaceUp": "提起", - "wgInterfaceDown": "击倒", - "wgBringingUp": "提起 {{name}}...", - "wgBringingDown": "把 {{name}} 拉下来……", - "wgInterfaceUpDone": "{{name}} 已开启", - "wgInterfaceDownDone": "{{name}} 已关闭", - "wgListenPort": "港口", + "wgInterfaceUp": "启用", + "wgInterfaceDown": "停用", + "wgBringingUp": "正在启用 {{name}}...", + "wgBringingDown": "正在停用 {{name}}...", + "wgInterfaceUpDone": "{{name}} 已启用", + "wgInterfaceDownDone": "{{name}} 已停用", + "wgListenPort": "端口", "wgPublicKey": "公钥", - "wgEndpoint": "端点", - "wgAllowedIPs": "允许的IP地址", - "wgLastHandshake": "最后一次握手", - "wgHandshakeNever": "绝不", - "wgTransfer": "转移", - "tsNotInstalled": "此主机上未安装 Tailscale", - "tsRunning": "跑步", - "tsStopped": "停止", + "wgEndpoint": "对端", + "wgAllowedIPs": "允许的 IP", + "wgLastHandshake": "上次握手", + "wgHandshakeNever": "从未", + "wgTransfer": "传输", + "tsNotInstalled": "此主机未安装 Tailscale", + "tsRunning": "运行中", + "tsStopped": "已停止", "tsEnable": "连接", - "tsDisable": "断开", - "tsEnabling": "正在连接到Tailscale...", - "tsDisabling": "正在断开与Tailscale的连接……", - "tsEnabled": "Tailscale 连接", + "tsDisable": "断开连接", + "tsEnabling": "正在连接到 Tailscale...", + "tsDisabling": "正在断开 Tailscale 连接...", + "tsEnabled": "Tailscale 已连接", "tsDisabled": "Tailscale 已断开连接", - "tsIPs": "尾规模 IP", - "tsPeers": "同龄人", - "tsOnline": "在线的", + "tsIPs": "Tailscale IP", + "tsPeers": "节点", + "tsOnline": "在线", "tsOffline": "离线", "tsExitNode": "出口节点", - "tsExitNodeActive": "出口节点已激活", + "tsExitNodeActive": "出口节点活跃", "tsHostname": "主机名" } }, "auth": { - "tagline": "自托管 SSH 和远程桌面管理", + "tagline": "自托管 SSH 及远程桌面管理", "loginTitle": "欢迎回来", "registerTitle": "创建账户", "forgotPassword": "忘记密码?", - "rememberMe": "记住设备30天(包含TOTP)", - "noAccount": "还没有账号?", + "rememberMe": "记住此设备30天(含 TOTP)", + "noAccount": "没有账号?", "hasAccount": "已有账号?", - "twoFactorAuth": "双因素身份验证", - "enterCode": "请输入验证码", - "backupCode": "或者使用备用代码", - "verifyCode": "验证码", - "redirectingToApp": "正在重定向到应用程序...", - "sshAuthenticationRequired": "需要 SSH 身份验证", - "sshNoKeyboardInteractive": "键盘交互式身份验证不可用", + "twoFactorAuth": "双因素认证", + "enterCode": "输入验证码", + "backupCode": "或使用备用验证码", + "verifyCode": "验证", + "redirectingToApp": "正在重定向到应用...", + "sshAuthenticationRequired": "需要SSH身份验证", + "sshNoKeyboardInteractive": "键盘交互式验证不可用", "sshAuthenticationFailed": "身份验证失败", "sshAuthenticationTimeout": "身份验证超时", - "sshNoKeyboardInteractiveDescription": "服务器不支持键盘交互式身份验证。请提供您的密码或SSH密钥。", - "sshAuthFailedDescription": "您提供的凭据不正确。请使用有效的凭据重试。", - "sshTimeoutDescription": "身份验证尝试超时,请重试。", - "sshProvideCredentialsDescription": "请提供您的 SSH 凭据以连接到此服务器。", - "sshPasswordDescription": "请输入此SSH连接的密码。", - "sshKeyPasswordDescription": "如果您的 SSH 密钥已加密,请在此处输入密码。", - "passphraseRequired": "需要密码", - "passphraseRequiredDescription": "SSH密钥已加密。请输入密码短语进行解锁。", - "back": "后退", + "sshNoKeyboardInteractiveDescription": "服务器不支持键盘交互式验证。请提供密码或SSH密钥。", + "sshAuthFailedDescription": "提供的凭据不正确。请使用有效凭据重试。", + "sshTimeoutDescription": "身份验证尝试超时。请重试。", + "sshProvideCredentialsDescription": "请提供连接此主机的SSH凭据。", + "sshPasswordDescription": "输入SSH连接的密码。", + "sshKeyPasswordDescription": "如果SSH密钥已加密,请在此输入密码短语。", + "passphraseRequired": "需要密码短语", + "passphraseRequiredDescription": "SSH密钥已加密,请输入密码短语解锁。", + "back": "返回", "firstUser": "首位用户", - "firstUserMessage": "您是第一个用户,将被授予管理员权限。您可以在侧边栏用户下拉菜单中查看管理员设置。如果您认为这是一个错误,请查看 Docker 日志或在 GitHub 上创建一个 issue。", - "external": "外部的", + "firstUserMessage": "您是首位用户,将被设为管理员。您可以在侧边栏用户下拉菜单中查看管理员设置。如果您认为这是个错误,请检查Docker日志,或在GitHub上创建问题。", + "external": "外部", "loginWithExternal": "使用外部提供商登录", "loginWithExternalDesc": "使用您配置的外部身份提供商登录", - "externalNotSupportedInElectron": "Electron 应用目前尚不支持外部身份验证。请使用网页版进行 OIDC 登录。", - "loginWithProvider": "使用 {{name}} 登录", - "orContinueWith": "或继续", + "externalNotSupportedInElectron": "Electron应用尚不支持外部身份验证。请使用Web版本进行OIDC登录。", + "loginWithProvider": "使用{{name}}登录", + "orContinueWith": "或继续使用", "ldapUsername": "LDAP 用户名", "ldapPassword": "LDAP 密码", - "ldapSignIn": "登入", - "ldapLoginFailed": "LDAP 登录失败", + "ldapSignIn": "登录", + "ldapLoginFailed": "LDAP登录失败", "resetPasswordButton": "重置密码", - "sendResetCode": "发送重置代码", - "resetCodeDesc": "请输入您的用户名以获取密码重置代码。该代码将记录在 Docker 容器日志中。", - "resetCode": "重置代码", - "verifyCodeButton": "验证码", - "enterResetCode": "请输入 Docker 容器日志中该用户的 6 位代码:", + "sendResetCode": "发送重置验证码", + "resetCodeDesc": "输入用户名以接收密码重置验证码。验证码将记录在Docker容器日志中。", + "resetCode": "重置验证码", + "verifyCodeButton": "验证", + "enterResetCode": "输入Docker容器日志中该用户的6位数验证码:", "newPassword": "新密码", "confirmNewPassword": "确认密码", "enterNewPassword": "请输入用户的新密码:", - "signUp": "报名", - "desktopApp": "桌面应用程序", - "loggingInToDesktopApp": "登录桌面应用程序", - "loadingServer": "服务器正在加载...", - "dataLossWarning": "使用此方法重置密码将删除您所有已保存的 SSH 主机、凭据和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此方法。", + "signUp": "注册", + "desktopApp": "桌面应用", + "loggingInToDesktopApp": "正在登录桌面应用", + "loadingServer": "正在加载服务器...", + "dataLossWarning": "以此方式重置密码将删除所有保存的 SSH 主机、凭据和其他加密数据。此操作不可撤销。仅在忘记密码且未登录时使用。", "authenticationDisabled": "身份验证已禁用", - "authenticationDisabledDesc": "所有身份验证方式目前均已禁用。请联系您的管理员。", - "passwordLoginDisabledDesc": "密码登录已禁用。请使用密码密钥或外部身份验证提供商。", - "signInWithPasskey": "使用密码登录", - "passkeyLoginFailed": "密码登录失败", - "attemptsRemaining": "{{count}} 剩余尝试次数", - "confirmResetDataWipe": "此帐户自加密升级后未登录,因此没有旧密码将无法恢复其存储的数据。重置将永久删除其主机名、凭据和代码片段。是否继续?" + "authenticationDisabledDesc": "当前所有身份验证方法均已禁用。请联系管理员。", + "passwordLoginDisabledDesc": "密码登录已禁用。请使用通行密钥或外部身份验证提供程序。", + "signInWithPasskey": "使用通行密钥登录", + "passkeyLoginFailed": "通行密钥登录失败", + "attemptsRemaining": "剩余 {{count}} 次尝试", + "confirmResetDataWipe": "自加密升级以来,此帐户未登录,因此没有旧密码无法恢复其存储的数据。重置将永久删除其主机、凭据和代码片段。是否继续?" }, "hostKey": { "verifyNewHost": "验证 SSH 主机密钥", - "keyChangedWarning": "SSH主机密钥已更改", + "keyChangedWarning": "SSH 主机密钥已更改", "firstConnectionTitle": "首次连接到此主机", - "firstConnectionDescription": "无法确认此主机的真实性。请验证指纹是否符合预期。", - "keyChangedDescription": "此服务器的主机密钥自您上次连接以来已更改。这可能表明存在安全问题。", - "previousKey": "上一个密钥", + "firstConnectionDescription": "无法建立此主机的真实性。请验证指纹是否与您预期的一致。", + "keyChangedDescription": "此服务器的主机密钥自上次连接以来已更改。这可能表示存在安全问题。", + "previousKey": "先前的密钥", "newFingerprint": "新指纹", "fingerprint": "指纹", - "verifyInstructions": "如果您信任此主机,请单击“接受”继续并保存此指纹以便将来连接。", + "verifyInstructions": "如果您信任此主机,请点击“接受”以继续,并保存此指纹以供将来连接。", "securityWarning": "安全警告", "acceptAndContinue": "接受并继续", "acceptNewKey": "接受新密钥并继续" @@ -2258,146 +2391,147 @@ "databaseConnection": "无法连接到数据库", "unknownError": "未知错误", "loginFailed": "登录失败", - "failedPasswordReset": "密码重置失败", - "failedVerifyCode": "重置代码验证失败", - "failedCompleteReset": "密码重置失败", + "failedPasswordReset": "无法启动密码重置", + "failedVerifyCode": "验证重置代码失败", + "failedCompleteReset": "无法完成密码重置", "invalidTotpCode": "无效的 TOTP 代码", - "failedOidcLogin": "OIDC 登录启动失败", - "silentSigninOidcUnavailable": "已请求静默登录,但 OIDC 登录不可用。", - "failedUserInfo": "登录后获取用户信息失败", + "failedOidcLogin": "无法启动 OIDC 登录", + "silentSigninOidcUnavailable": "请求了静默登录,但 OIDC 登录不可用。", + "failedUserInfo": "登录后无法获取用户信息", "oidcAuthFailed": "OIDC 身份验证失败", - "invalidAuthUrl": "从后端收到的授权 URL 无效", - "requiredField": "此字段是必需的", + "invalidAuthUrl": "从后端接收到无效的授权 URL", + "requiredField": "此字段为必填项", "minLength": "最小长度为 {{min}}", "passwordMismatch": "密码不匹配", - "passwordLoginDisabled": "目前已禁用用户名/密码登录。", + "passwordLoginDisabled": "用户名/密码登录当前已禁用", "sessionExpired": "会话已过期 - 请重新登录", - "totpRateLimited": "频率限制:TOTP验证尝试次数过多。请稍后再试。", - "totpRateLimitedWithTime": "速率限制:TOTP 验证尝试次数过多。请稍等 {{time}} 秒后再试。", - "resetCodeRateLimited": "验证次数过多,请稍后再试。", - "resetCodeRateLimitedWithTime": "验证次数过多,请稍等 {{time}} 秒后再试。", - "authTokenSaveFailed": "保存身份验证令牌失败", - "failedToLoadServer": "服务器加载失败" + "totpRateLimited": "频率受限:TOTP 验证尝试次数过多,请稍后再试。", + "totpRateLimitedWithTime": "频率受限:TOTP 验证尝试次数过多,请等待 {{time}} 秒后再试。", + "resetCodeRateLimited": "频率受限:验证尝试次数过多,请稍后再试。", + "resetCodeRateLimitedWithTime": "频率受限:验证尝试次数过多,请等待 {{time}} 秒后再试。", + "authTokenSaveFailed": "保存认证令牌失败", + "failedToLoadServer": "加载服务器失败", + "remoteServerRequired": "需要远程服务器。请在“设置”中连接远程服务器以使用此连接类型。" }, "messages": { - "registrationDisabled": "管理员已禁用新账号注册功能。请登录或联系管理员。", - "userNotAllowed": "您的账户未获得注册授权。请联系管理员。", + "registrationDisabled": "新账户注册已被管理员禁用。请登录或联系管理员。", + "userNotAllowed": "您的账户无权注册。请联系管理员。", "databaseConnectionFailed": "连接数据库服务器失败", "resetCodeSent": "重置代码已发送到 Docker 日志", - "codeVerified": "代码验证成功", + "codeVerified": "验证码验证成功", "passwordResetSuccess": "密码重置成功", "loginSuccess": "登录成功", "registrationSuccess": "注册成功" }, "profile": { - "c2sTunnelConfigDesc": "针对已配置 SSH 主机的本地桌面隧道。", + "c2sTunnelConfigDesc": "本地桌面隧道目标为已配置的 SSH 主机。", "c2sTunnelPresets": "客户端隧道预设", - "c2sTunnelPresetsDesc": "将此桌面客户端的本地隧道列表保存为命名服务器预设,或将预设加载回此客户端。", + "c2sTunnelPresetsDesc": "将本桌面客户端的本地隧道列表保存为命名服务器预设,或将预设加载回此客户端。", "c2sTunnelPresetsUnavailable": "客户端隧道预设仅在桌面客户端中可用。", "c2sPresetName": "预设名称", - "c2sPresetNamePlaceholder": "客户预设名称", - "c2sPresetToLoad": "预设加载", - "c2sNoPresetSelected": "未选择预设值", - "c2sNoPresets": "未保存任何预设。", + "c2sPresetNamePlaceholder": "客户端预设名称", + "c2sPresetToLoad": "要加载的预设", + "c2sNoPresetSelected": "未选择预设", + "c2sNoPresets": "无保存的预设", "c2sLoadPreset": "加载", - "c2sCurrentLocalConfig": "{{count}} 此桌面上配置了本地客户端隧道。", - "c2sPresetSyncNote": "预设是明确的快照;加载一个预设会替换此桌面客户端的本地客户端隧道列表。", + "c2sCurrentLocalConfig": "此桌面上已配置 {{count}} 条本地客户端隧道。", + "c2sPresetSyncNote": "预设是显式快照;加载预设将替换此桌面客户端的本地客户端隧道列表。", "c2sPresetSaved": "客户端隧道预设已保存", - "c2sPresetLoaded": "客户端隧道预设已本地加载", + "c2sPresetLoaded": "客户端隧道预设已在本地加载", "c2sPresetRenamed": "客户端隧道预设已重命名", "c2sPresetDeleted": "客户端隧道预设已删除", - "c2sPresetLoadError": "客户端隧道预设加载失败" + "c2sPresetLoadError": "加载客户端隧道预设失败" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "语言", "keyPassword": "密钥密码", - "pastePrivateKey": "把你的私钥粘贴到这里……", - "localListenerHost": "127.0.0.1(本地收听)", - "localTargetHost": "127.0.0.1(此计算机上的目标)", + "pastePrivateKey": "在此粘贴您的私钥...", + "localListenerHost": "127.0.0.1 (本地监听)", + "localTargetHost": "127.0.0.1 (此计算机上的目标)", "socksListenerHost": "127.0.0.1(SOCKS 监听器)", - "enterPassword": "请输入您的密码", + "enterPassword": "输入您的密码", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "仪表板", + "title": "仪表盘", "loading": "正在加载仪表盘...", "github": "GitHub", "support": "支持", "discord": "Discord", "docs": "文档", - "donate": "捐", + "donate": "捐赠", "serverOverview": "服务器概览", "version": "版本", - "upToDate": "最新", - "updateAvailable": "更新可用", + "upToDate": "已是最新", + "updateAvailable": "有可用更新", "beta": "Beta", - "uptime": "正常运行时间", + "uptime": "运行时间", "database": "数据库", - "healthy": "健康", + "healthy": "运行正常", "error": "错误", - "totalHosts": "主持人总数", - "totalTunnels": "全隧道", - "totalCredentials": "证书总数", - "recentActivity": "近期活动", + "totalHosts": "主机总数", + "totalTunnels": "隧道总数", + "totalCredentials": "凭据总数", + "recentActivity": "最近活动", "reset": "重置", - "loadingRecentActivity": "正在加载最近的活动...", - "noRecentActivity": "近期无活动", + "loadingRecentActivity": "正在加载最近活动...", + "noRecentActivity": "无最近活动", "quickActions": "快速操作", "addHost": "添加主机", "addCredential": "添加凭据", "adminSettings": "管理员设置", - "userProfile": "用户个人资料", - "serverStats": "服务器统计信息", - "loadingServerStats": "正在加载服务器统计信息……", - "noServerData": "服务器数据不可用", - "cpu": "中央处理器", + "userProfile": "用户资料", + "serverStats": "服务器统计", + "loadingServerStats": "正在加载服务器统计...", + "noServerData": "无服务器数据可用", + "cpu": "CPU", "ram": "内存", "customizeLayout": "自定义仪表盘", "dashboardSettings": "仪表盘设置", "enableDisableCards": "启用/禁用卡片", - "resetLayout": "重置为默认值", + "resetLayout": "重置为默认", "serverOverviewCard": "服务器概览", - "recentActivityCard": "近期活动", - "networkGraphCard": "网络图", - "networkGraph": "网络图", + "recentActivityCard": "最近活动", + "networkGraphCard": "网络图表", + "networkGraph": "网络图表", "quickActionsCard": "快速操作", - "serverStatsCard": "服务器统计信息", - "panelMain": "主要的", - "panelSide": "边", - "justNow": "现在", + "serverStatsCard": "服务器统计", + "panelMain": "主面板", + "panelSide": "侧面板", + "justNow": "刚刚", "serviceLinks": "服务链接", - "homepagePreview": "首页预览" + "homepagePreview": "主页预览" }, "donation": { - "title": "喜欢Termix吗?", - "body": "Termix 是一个免费开源的软件,由我们两人团队利用业余时间开发和维护。如果您用它替代了原本需要付费的商业工具,您的捐赠将有助于支付托管费用并维持软件的持续开发。目前,我们仅接受加密货币捐赠。", - "milestones": "捐款还能帮助我们投入时间进行研究和学习,以开发诸如 SAML 和 Kubernetes 支持等功能。请在捐款页面查看项目进展。", - "cta": "捐", - "dismiss": "也许以后" + "title": "喜欢 Termix 吗?", + "body": "Termix 是一款免费开源工具,由两人团队业余时间构建和维护。如果它替代了您原本需要付费的商业工具,捐赠可以帮助覆盖托管成本并保持开发持续。目前,捐赠仅支持加密货币。", + "milestones": "捐赠也有助于投入时间研究和学习构建功能所需的知识,例如 SAML 和 Kubernetes 支持。可在捐赠页面查看进展。", + "cta": "捐赠", + "dismiss": "稍后再说" }, "dashboardTab": { - "stable": "稳定的", - "hostsOnline": "在线主持人", + "stable": "稳定", + "hostsOnline": "在线主机", "activeTunnels": "活跃隧道", "registerNewServer": "注册新服务器", "storeSshKeysOrPasswords": "存储 SSH 密钥或密码", "manageUsersAndRoles": "管理用户和角色", - "manageYourAccount": "管理您的帐户", + "manageYourAccount": "管理您的账户", "hostStatus": "主机状态", - "noHostsConfigured": "未配置任何主机", - "online": "在线的", + "noHostsConfigured": "未配置主机", + "online": "在线", "offline": "离线", - "checking": "检查", - "onlineLower": "在线的", - "nodes": "{{count}} 节点", + "checking": "检查中", + "onlineLower": "在线", + "nodes": "{{count}} 个节点", "add": "添加:", "commandPalette": "命令面板", - "done": "完毕", - "editModeInstructions": "拖动卡片重新排序 · 拖动列分隔线调整列宽 · 拖动卡片底部边缘调整卡片高度 · 点击垃圾桶图标删除卡片", - "empty": "空的", + "done": "完成", + "editModeInstructions": "拖拽卡片重新排序 · 拖拽列分隔线调整列宽 · 拖拽卡片底部边缘调整高度 · 移至垃圾桶移除", + "empty": "空", "clear": "清除", "serviceLinksTitle": "服务链接", "serviceLinksEmpty": "暂无服务链接", @@ -2406,7 +2540,7 @@ "serviceLinksAdd": "添加", "serviceLinksLabelPlaceholder": "我的服务", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "请输入有效的网址", + "serviceLinksInvalidUrl": "输入有效的网址", "serviceLinksAddFailed": "添加服务链接失败", "disk": "磁盘", "viewServerDetails": "查看服务器详情" @@ -2414,13 +2548,13 @@ "sessionLogs": { "title": "会话日志", "noLogs": "暂无会话日志", - "noLogsDesc": "启用主机上的会话日志记录以开始录制", - "duration": "期间", + "noLogsDesc": "在主机上启用会话日志以开始记录", + "duration": "持续时间", "viewLog": "查看日志", "downloadLog": "下载", "deleteLog": "删除", "confirmDelete": "删除此会话日志?", - "confirmDeleteDesc": "此操作无法撤销。", + "confirmDeleteDesc": "此操作无法撤消。", "copyContent": "复制", "copied": "已复制!", "loadError": "加载会话日志失败", @@ -2429,41 +2563,41 @@ }, "networkGraph": { "addHost": "添加主机", - "addGroup": "添加组", + "addGroup": "添加分组", "addLink": "添加链接", "zoomIn": "放大", "zoomOut": "缩小", "resetView": "重置视图", "selectHost": "选择主机", - "chooseHost": "选择房东……", - "parentGroup": "母集团", - "noGroup": "无组", - "groupName": "组名", + "chooseHost": "选择主机...", + "parentGroup": "父分组", + "noGroup": "无分组", + "groupName": "分组名称", "color": "颜色", - "source": "来源", + "source": "源", "target": "目标", - "moveToGroup": "移至群组", - "selectGroup": "选择组...", + "moveToGroup": "移至分组", + "selectGroup": "选择分组...", "addConnection": "添加连接", "hostDetails": "主机详情", - "removeFromGroup": "从群组中移除", + "removeFromGroup": "移出群组", "addHostHere": "在此添加主机", - "editGroup": "编辑组", + "editGroup": "编辑群组", "delete": "删除", "add": "添加", - "create": "创造", + "create": "创建", "move": "移动", "connect": "连接", - "createGroup": "创建组", - "selectSourcePlaceholder": "选择来源...", + "createGroup": "创建群组", + "selectSourcePlaceholder": "选择源...", "selectTargetPlaceholder": "选择目标...", "invalidFile": "无效文件", - "hostAlreadyExists": "主机已在拓扑结构中", + "hostAlreadyExists": "主机已在拓扑中", "connectionExists": "连接已存在", "unknown": "未知", - "name": "姓名", + "name": "名称", "ip": "IP", - "status": "地位", + "status": "状态", "failedToAddNode": "添加节点失败", "sourceDifferentFromTarget": "源和目标必须不同", "exportJSON": "导出 JSON", @@ -2474,593 +2608,610 @@ "docker": "Docker", "serverStats": "主机指标", "hostMetrics": "主机指标", - "noNodes": "暂无节点" + "noNodes": "尚无节点" }, "docker": { "notEnabled": "此主机未启用 Docker", - "validating": "正在验证 Docker...", - "connecting": "正在连接...", + "validating": "验证 Docker 中...", + "connecting": "连接中...", "error": "错误", "version": "Docker {{version}}", "connectionFailed": "连接 Docker 失败", "containerStarted": "容器 {{name}} 已启动", - "failedToStartContainer": "启动容器失败 {{name}}", + "failedToStartContainer": "启动容器 {{name}} 失败", "containerStopped": "容器 {{name}} 已停止", "failedToStopContainer": "停止容器 {{name}} 失败", "containerRestarted": "容器 {{name}} 已重启", - "failedToRestartContainer": "重启容器失败 {{name}}", + "failedToRestartContainer": "重启容器 {{name}} 失败", "containerPaused": "容器 {{name}} 已暂停", - "containerUnpaused": "容器 {{name}} 已解除暂停", - "failedToTogglePauseContainer": "切换容器 {{name}} 的暂停状态失败", + "containerUnpaused": "容器 {{name}} 已恢复", + "failedToTogglePauseContainer": "切换容器 {{name}} 暂停状态失败", "containerRemoved": "容器 {{name}} 已移除", "failedToRemoveContainer": "移除容器 {{name}} 失败", - "image": "图像", - "ports": "港口", + "image": "镜像", + "ports": "端口", "noPorts": "无端口", - "start": "开始", - "confirmRemoveContainer": "您确定要删除容器“{{name}}”吗?此操作无法撤销。", - "runningContainerWarning": "警告:此容器当前正在运行。移除此容器会先停止其运行。", - "loadingContainers": "正在装载集装箱……", + "start": "启动", + "confirmRemoveContainer": "确定要移除容器 '{{name}}' 吗?此操作无法撤销。", + "runningContainerWarning": "警告:此容器正在运行。移除操作将先停止容器。", + "loadingContainers": "正在加载容器...", "manager": "Docker 管理器", "autoRefresh": "自动刷新", "timestamps": "时间戳", - "lines": "线条", - "filterLogs": "过滤日志...", + "lines": "行数", + "filterLogs": "筛选日志...", "refresh": "刷新", "download": "下载", "clear": "清除", - "logsDownloaded": "日志已成功下载", - "last50": "最后50", - "last100": "最近100", - "last500": "最近500", - "last1000": "最近的1000", - "allLogs": "所有日志", - "noLogsMatching": "没有与“{{query}}”匹配的日志", - "noLogsAvailable": "没有可用日志", + "logsDownloaded": "日志下载成功", + "last50": "最近 50 条", + "last100": "最近 100 条", + "last500": "最近 500 条", + "last1000": "最近 1000 条", + "allLogs": "全部日志", + "noLogsMatching": "未找到匹配 \"{{query}}\" 的日志", + "noLogsAvailable": "暂无日志", "noContainersFound": "未找到容器", "noContainersFoundHint": "此主机上没有可用的 Docker 容器", - "searchPlaceholder": "搜索容器……", + "searchPlaceholder": "搜索容器...", "allStatuses": "所有状态", - "stateRunning": "跑步", - "statePaused": "暂停", + "stateRunning": "运行中", + "statePaused": "已暂停", "stateExited": "已退出", - "stateRestarting": "重新启动", - "noContainersMatchFilters": "没有容器符合您的筛选条件", - "noContainersMatchFiltersHint": "尝试调整搜索或筛选条件", + "stateRestarting": "重启中", + "noContainersMatchFilters": "没有容器符合筛选条件", + "noContainersMatchFiltersHint": "请尝试调整搜索或筛选条件", "failedToFetchStats": "获取容器统计信息失败", "containerNotRunning": "容器未运行", "startContainerToViewStats": "启动容器以查看统计信息", - "loadingStats": "正在加载统计信息……", + "loadingStats": "加载统计信息...", "errorLoadingStats": "加载统计信息时出错", - "noStatsAvailable": "暂无统计数据", + "noStatsAvailable": "无可用统计信息", "cpuUsage": "CPU 使用率", - "current": "当前的", - "memoryUsage": "内存使用情况", + "current": "当前", + "memoryUsage": "内存使用量", "networkIo": "网络 I/O", "input": "输入", "output": "输出", "blockIo": "块 I/O", - "read": "读", - "write": "写", - "pids": "PID", + "read": "读取", + "write": "写入", + "pids": "PIDs", "containerInformation": "容器信息", - "name": "姓名", + "name": "名称", "id": "ID", "state": "状态", - "containerMustBeRunning": "容器必须正在运行才能访问控制台", - "verificationCodePrompt": "请输入验证码", - "totpVerificationFailed": "TOTP验证失败,请重试。", - "warpgateVerificationFailed": "Warpgate身份验证失败,请重试。", - "connectedTo": "已连接至 {{containerName}}", - "disconnected": "断开连接", + "containerMustBeRunning": "访问控制台需要容器处于运行状态", + "verificationCodePrompt": "输入验证码", + "totpVerificationFailed": "TOTP 验证失败,请重试。", + "warpgateVerificationFailed": "Warpgate 认证失败,请重试。", + "connectedTo": "已连接到 {{containerName}}", + "disconnected": "已断开连接", "consoleError": "控制台错误", - "errorMessage": "错误: {{message}}", - "failedToConnect": "连接容器失败", - "console": "安慰", - "selectShell": "选择外壳", - "bash": "巴什", - "sh": "什", - "ash": "灰", + "errorMessage": "错误:{{message}}", + "failedToConnect": "连接到容器失败", + "console": "控制台", + "selectShell": "选择 shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", "connect": "连接", - "disconnect": "断开", + "disconnect": "断开连接", "notConnected": "未连接", - "clickToConnect": "单击“连接”以启动 shell 会话", + "clickToConnect": "点击“连接”启动 shell 会话", "connectingTo": "正在连接到 {{containerName}}...", "containerNotFound": "未找到容器", "backToList": "返回列表", "logs": "日志", - "stats": "统计数据", - "consoleTab": "安慰", + "stats": "统计", + "consoleTab": "控制台", "startContainerToAccess": "启动容器以访问控制台" }, "admin": { - "sectionGeneral": "一般的", + "sectionGeneral": "通用", "sectionOidc": "OIDC", - "sectionSso": "单点登录提供商", + "sectionSso": "SSO 提供商", "ssoAddProvider": "添加提供商", "ssoDocsLink": "查看文档", "ssoProviderDocsLink": "查看文档", - "ssoNoProviders": "未配置单点登录 (SSO) 提供商。", + "ssoNoProviders": "未配置 SSO 提供商。", "ssoProviderName": "显示名称", - "ssoProviderType": "提供者类型", + "ssoProviderType": "提供商类型", "ssoDeleteProvider": "删除提供商", "ssoDeleteConfirm": "删除此提供商?关联用户将无法登录。", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", - "ssoTypeGoogle": "谷歌", + "ssoTypeGoogle": "Google", "ssoEnabled": "已启用", "ssoDisabled": "已禁用", - "ssoSaveProvider": "储蓄提供者", + "ssoSaveProvider": "保存提供商", "ssoTestConnection": "测试连接", - "ssoEditProvider": "编辑提供者", + "ssoEditProvider": "编辑提供商", "ldapHost": "LDAP 主机", - "ldapPort": "港口", - "ldapUseTls": "使用 TLS(LDAPS)", - "ldapBindDn": "绑定DN", + "ldapPort": "端口", + "ldapUseTls": "使用 TLS (LDAPS)", + "ldapBindDn": "绑定 DN", "ldapBindPassword": "绑定密码", - "ldapUserSearchBase": "用户搜索库", - "ldapUserSearchFilter": "用户搜索筛选器", + "ldapUserSearchBase": "用户搜索基准", + "ldapUserSearchFilter": "用户搜索过滤器", "ldapUsernameAttr": "用户名属性", "ldapDisplayNameAttr": "显示名称属性", - "ldapGroupSearchBase": "群组搜索库", - "ldapAdminGroup": "管理员组", + "ldapGroupSearchBase": "群组搜索基准", + "ldapAdminGroup": "管理员群组", "ldapAllowedUsers": "允许的用户", "sectionUsers": "用户", - "sectionSessions": "会议", + "sectionSessions": "会话", "sectionRoles": "角色", "sectionDatabase": "数据库", - "sectionApiKeys": "API密钥", + "sectionApiKeys": "API 密钥", "sectionAuditLog": "审计日志", - "sectionSsl": "SSL/Let's Encrypt", - "sslDescription": "自动颁发和续订来自 Let's Encrypt 的可信 SSL 证书。需要公共域名和 80 端口或 DNS 访问权限。", + "sectionSsl": "SSL / Let's Encrypt", + "sslDescription": "自动从 Let's Encrypt 签发并续期受信任的 SSL 证书。需要公共域名以及端口 80 或 DNS 访问权限。", "sslDocsLink": "查看 SSL 文档", - "sslDomain": "领域", + "sslDomain": "域名", "sslDomainPlaceholder": "termix.example.com", "sslDomainDesc": "证书的公共域名。", - "sslEmail": "电子邮件", + "sslEmail": "邮箱", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encrypt 通知和帐户的联系邮箱。", - "sslChallengeType": "挑战类型", + "sslEmailDesc": "用于 Let's Encrypt 通知和账户的联系邮箱。", + "sslChallengeType": "验证类型", "sslChallengeTypeDesc": "如何向 Let's Encrypt 证明域名所有权。", - "sslChallengeHttp": "HTTP(webroot)——需要可通过互联网访问的 80 端口", - "sslChallengeDns": "DNS(Cloudflare)- 需要 Cloudflare API 令牌", + "sslChallengeHttp": "HTTP (webroot) - 需要从互联网可访问端口 80", + "sslChallengeDns": "DNS (Cloudflare) - 需要 Cloudflare API 令牌", "sslCloudflareToken": "Cloudflare API 令牌", - "sslCloudflareTokenPlaceholder": "请输入令牌...", - "sslCloudflareTokenDesc": "具有针对您的域的 Zone:DNS:Edit 权限的作用域令牌。", + "sslCloudflareTokenPlaceholder": "输入令牌...", + "sslCloudflareTokenDesc": "具有您域名的 Zone:DNS:Edit 权限的限定作用域令牌。", "sslCertStatus": "证书状态", "sslCertStatusNone": "无证书", - "sslCertStatusValid": "有效的", - "sslCertStatusExpiring": "即将到期", - "sslCertStatusExpired": "已到期", - "sslCertExpiresAt": "过期时间 {{date}}", - "sslLastIssued": "最后发布日期 {{date}}", - "sslRequestCert": "签发/续签证书", - "sslRequestCertLoading": "正在申请证书...", - "sslRequestCertSuccess": "证书已成功颁发并安装", + "sslCertStatusValid": "有效", + "sslCertStatusExpiring": "即将过期", + "sslCertStatusExpired": "已过期", + "sslCertExpiresAt": "过期于 {{date}}", + "sslLastIssued": "上次签发于 {{date}}", + "sslRequestCert": "签发 / 续期证书", + "sslRequestCertLoading": "正在请求证书...", + "sslRequestCertSuccess": "证书签发并安装成功", "sslRequestCertFailed": "证书请求失败", "sslSave": "保存设置", "sslSaved": "SSL 设置已保存", - "sslSaveFailed": "SSL 设置保存失败", - "sslRequiresDomain": "域名和电子邮件地址是必需的", - "sslInfoNote": "颁发证书后,请在环境变量中启用 SSL (ENABLE_SSL=true) 并重启 Termix。", - "auditLogTotal": "{{total}} 总条目数", + "sslSaveFailed": "保存 SSL 设置失败", + "sslRequiresDomain": "域名和邮箱为必填项", + "sslInfoNote": "签发证书后,请在环境变量中启用 SSL (ENABLE_SSL=true) 并重启 Termix。", + "sslManualOption": "手动(上传证书)", + "sslManualCert": "证书(PEM)", + "sslManualCertPlaceholder": "-----开始证书-----", + "sslManualKey": "私钥(PEM)", + "sslManualKeyPlaceholder": "-----私钥开始-----", + "sslManualDesc": "请粘贴您现有的证书和私钥,如果您的 CA 要求,请提供完整的证书链。", + "sslManualUpload": "上传并安装证书", + "sslManualUploadLoading": "正在上传证书……", + "sslManualUploadSuccess": "证书已成功上传并安装", + "sslManualUploadFailed": "证书上传失败", + "sslManualRequiresFields": "需要证书和私钥。", + "auditLogTotal": "共 {{total}} 条记录", "auditLogEmpty": "未找到审计日志条目", "auditLogSuccess": "成功", - "auditLogFailed": "失败的", - "auditLogClearFilters": "清除筛选条件", - "auditLogPage": "第 {{page}} 页,共 {{totalPages}} 页(总计{{total}} 页)", + "auditLogFailed": "失败", + "auditLogClearFilters": "清除筛选", + "auditLogPage": "第 {{page}} / {{totalPages}} 页 (共 {{total}} 条)", "auditLogIp": "IP", "auditLogResourceId": "资源 ID", "auditLogFilterUser": "用户", - "auditLogFilterAction": "行动", + "auditLogFilterAction": "操作", "auditLogFilterResourceType": "资源类型", - "auditLogFilterStatus": "地位", - "auditLogFilterFrom": "从", - "auditLogFilterTo": "到", + "auditLogFilterStatus": "状态", + "auditLogFilterFrom": "起始", + "auditLogFilterTo": "结束", "auditLogFilterAll": "全部", "allowRegistration": "允许用户注册", "allowRegistrationDesc": "允许新用户使用用户名和密码自行注册", "allowPasswordLogin": "允许密码登录", "allowPasswordLoginDesc": "用户名/密码登录", - "oidcAutoProvision": "OIDC自动配置", - "oidcAutoProvisionDesc": "首次登录时自动为 OIDC/SSO 用户创建帐户(与注册开关无关)", - "oidcSilentLoginDefault": "默认启用静默 OIDC 登录", - "oidcSilentLoginDefaultDesc": "每次访问时自动重定向到 OIDC 登录页面,完全跳过登录表单。", - "allowPasswordReset": "允许重置密码", - "allowPasswordResetDesc": "通过 Docker 日志重置代码", - "commandHistoryEnabled": "命令历史记录", - "commandHistoryEnabledDesc": "允许记录命令历史记录。禁用此功能后,无论主机设置如何,都不会保存历史记录。", - "updateCommandHistoryFailed": "更新命令历史记录设置失败", + "oidcAutoProvision": "OIDC 自动配置", + "oidcAutoProvisionDesc": "首次登录时自动为 OIDC/SSO 用户创建账户(与注册开关无关)", + "oidcSilentLoginDefault": "默认静默 OIDC 登录", + "oidcSilentLoginDefaultDesc": "每次访问时自动重定向到 OIDC 登录,完全跳过登录表单", + "allowPasswordReset": "允许密码重置", + "allowPasswordResetDesc": "通过 Docker 日志获取重置码", + "commandHistoryEnabled": "命令历史", + "commandHistoryEnabledDesc": "允许记录命令历史。禁用后,无论各主机设置如何,均不保存历史记录。", + "updateCommandHistoryFailed": "更新命令历史设置失败", + "analyticsEnabled": "分享匿名使用统计数据", + "analyticsEnabledDesc": "每日匿名发送用户、主机和功能使用情况统计数据,以帮助改进 Termix。绝不包含任何个人数据或连接详情。", + "updateAnalyticsFailed": "分析设置更新失败", + "sessionSharingGloballyEnabled": "允许会话共享", + "sessionSharingGloballyEnabledDesc": "允许在整个实例范围内共享实时终端、RDP、VNC 和 Telnet 会话。禁用此功能将覆盖所有主机级别的共享设置。", + "updateSessionSharingFailed": "会话共享设置更新失败", "sessionTimeout": "会话超时", "hours": "小时", "sessionTimeoutRange": "最短 1 小时 · 最长 720 小时", - "monitoringDefaults": "监控默认设置", + "monitoringDefaults": "监控默认值", "statusCheck": "状态检查", "metrics": "指标", "sec": "秒", "logLevel": "日志级别", - "enableGuacamole": "启用鳄梨酱", - "enableGuacamoleDesc": "RDP/VNC远程桌面", + "enableGuacamole": "启用 Guacamole", + "enableGuacamoleDesc": "RDP/VNC 远程桌面", "enableGuacamoleDocsLink": "查看文档", "guacdUrl": "guacd URL", "tailscaleApiKey": "Tailscale API 密钥", "tailscaleApiKeyDescription": "用于主机编辑器中的设备发现。在 tailscale.com/admin/settings/keys 生成密钥。", "tailscaleApiKeyDocsLink": "查看文档", - "oidcDescription": "配置 OpenID Connect 以实现单点登录。标有 * 的字段为必填项。", + "oidcDescription": "为 SSO 配置 OpenID Connect。带 * 的字段为必填项。", "oidcDocsLink": "查看文档", - "oidcClientId": "客户ID", - "oidcClientSecret": "客户机密", + "oidcClientId": "客户端 ID", + "oidcClientSecret": "客户端密钥", "oidcAuthUrl": "授权 URL", - "oidcIssuerUrl": "发行者 URL", + "oidcIssuerUrl": "颁发者 URL", "oidcTokenUrl": "令牌 URL", "oidcUserIdentifier": "用户标识符路径", "oidcDisplayName": "显示名称路径", - "oidcScopes": "瞄准镜", - "oidcUserinfoUrl": "覆盖用户信息 URL", + "oidcScopes": "作用域", + "oidcUserinfoUrl": "覆盖 Userinfo URL", "oidcAllowedUsers": "允许的用户", - "oidcAllowedUsersDesc": "每行一个电子邮件地址。留空则允许所有电子邮件地址。", + "oidcAllowedUsersDesc": "每行一个邮箱。留空表示允许所有用户。", "oidcAdminGroup": "管理员组", - "oidcAdminGroupDesc": "此群组中的用户拥有管理员权限。留空则禁用群组同步。", - "oidcGroupClaim": "团体索赔", - "oidcGroupClaimDesc": "可选。包含用户组的声明路径。默认顺序为:组、角色、组。用于具有自定义声明的提供商(例如 Zitadel)。", + "oidcAdminGroupDesc": "此组的用户将被授予管理员权限。留空可禁用组同步。", + "oidcGroupClaim": "组声明", + "oidcGroupClaimDesc": "可选。包含用户组的声明路径。默认依次使用 groups、roles、group。用于具有自定义声明的提供商(如 Zitadel)。", "oidcCaCert": "自定义 CA 证书", - "oidcCaCertDesc": "可选。适用于使用私有或自签名 CA 的 OIDC 提供商的 PEM 编码 CA 证书。留空则使用系统信任存储。", - "removeOidc": "消除", - "usersCount": "{{count}} 用户", - "createUser": "创造", + "oidcCaCertDesc": "可选。PEM 编码的 CA 证书,用于使用私有或自签名 CA 的 OIDC 提供商。留空则使用系统信任存储。", + "removeOidc": "移除", + "usersCount": "{{count}} 个用户", + "createUser": "创建", "newRole": "新角色", - "roleName": "姓名", + "roleName": "名称", "roleDisplayName": "显示名称", "roleDescription": "描述", - "rolesCount": "{{count}} 角色", - "createRole": "创造", - "creating": "正在创建……", + "rolesCount": "{{count}} 个角色", + "createRole": "创建", + "creating": "正在创建...", "exportDatabase": "导出数据库", - "exportDatabaseDesc": "下载所有主机、凭据和设置的备份", - "export": "出口", - "exporting": "出口...", + "exportDatabaseDesc": "下载包含所有主机、凭据和设置的备份", + "export": "导出", + "exporting": "正在导出...", "importDatabase": "导入数据库", "importDatabaseDesc": "从 .sqlite 备份文件恢复", - "importDatabaseSelected": "已选择: {{name}}", + "importDatabaseSelected": "已选择:{{name}}", "selectFile": "选择文件", - "changeFile": "改变", - "import": "进口", - "importing": "输入...", - "apiKeysCount": "{{count}} 键", + "changeFile": "更改", + "import": "导入", + "importing": "正在导入...", + "apiKeysCount": "{{count}} 个密钥", "apiKeysDocsLink": "查看文档", - "newApiKey": "新 API 密钥", - "apiKeyCreatedWarning": "密钥已创建 - 立即复制,之后不会再显示。", - "apiKeyName": "姓名", + "newApiKey": "新建 API 密钥", + "apiKeyCreatedWarning": "密钥已创建 - 请立即复制,密钥将不再显示。", + "apiKeyName": "名称", "apiKeyUser": "用户", "apiKeySelectUser": "选择用户...", - "apiKeyExpiresAt": "到期时间", + "apiKeyExpiresAt": "过期时间", "createKey": "创建密钥", - "apiKeyNoExpiry": "无有效期", - "revokedBadge": "撤销", + "apiKeyNoExpiry": "无过期", + "revokedBadge": "已吊销", "authTypeDual": "双重认证", "authTypeOidc": "OIDC", - "authTypeLocal": "当地的", - "adminStatusAdministrator": "行政人员", + "authTypeLocal": "本地", + "adminStatusAdministrator": "管理员", "adminStatusRegularUser": "普通用户", - "adminBadge": "行政", + "adminBadge": "管理员", "systemBadge": "系统", - "customBadge": "风俗", - "youBadge": "你", - "sessionsActive": "{{count}} 活跃", - "sessionActive": "当前状态: {{time}}", - "sessionExpires": "Exp: {{time}}", + "customBadge": "自定义", + "youBadge": "我", + "sessionsActive": "{{count}} 个活跃", + "sessionActive": "活跃: {{time}}", + "sessionExpires": "过期: {{time}}", "revokeAll": "全部", - "revokeAllSessionsSuccess": "该用户的所有会话均已撤销。", - "revokeAllSessionsFailed": "撤销会话失败", - "revokeSessionFailed": "撤销会话失败", + "revokeAllSessionsSuccess": "用户的所有会话已吊销", + "revokeAllSessionsFailed": "吊销会话失败", + "revokeSessionFailed": "吊销会话失败", "addRole": "添加角色", "noCustomRoles": "未定义自定义角色", "removeRoleFailed": "移除角色失败", - "assignRoleFailed": "角色分配失败", + "assignRoleFailed": "分配角色失败", "deleteRoleFailed": "删除角色失败", - "userAdminAccess": "行政人员", - "userAdminAccessDesc": "完全访问所有管理员设置", + "userAdminAccess": "管理员", + "userAdminAccessDesc": "完全访问所有管理设置", "userRoles": "角色", - "revokeAllUserSessions": "撤销所有会话", - "revokeAllUserSessionsDesc": "强制所有设备重新登录", - "revoke": "撤销", + "revokeAllUserSessions": "吊销所有会话", + "revokeAllUserSessionsDesc": "强制在所有设备上重新登录", + "revoke": "吊销", "deleteUserWarning": "删除此用户是永久性的。", "deleteUser": "删除 {{username}}", - "deleting": "正在删除……", + "deleting": "删除中...", "deleteUserFailed": "删除用户失败", - "deleteUserSuccess": "用户“{{username}}”已删除", - "deleteRoleSuccess": "角色“{{name}}”已删除", - "revokeKeySuccess": "密钥“{{name}}”已撤销", - "revokeKeyFailed": "撤销密钥失败", + "deleteUserSuccess": "用户 \"{{username}}\" 已删除", + "deleteRoleSuccess": "角色 \"{{name}}\" 已删除", + "revokeKeySuccess": "密钥 \"{{name}}\" 已吊销", + "revokeKeyFailed": "吊销密钥失败", "copiedToClipboard": "已复制到剪贴板", - "done": "完毕", + "done": "完成", "createUserTitle": "创建用户", - "createUserDesc": "创建一个新的本地帐户。", + "createUserDesc": "创建一个新的本地账号。", "createUserUsername": "用户名", "createUserPassword": "密码", "createUserPasswordHint": "至少6个字符。", - "createUserEnterUsername": "请输入用户名", + "createUserEnterUsername": "输入用户名", "createUserEnterPassword": "输入密码", "createUserSubmit": "创建用户", - "editUserTitle": "管理用户: {{username}}", - "editUserDesc": "编辑角色、管理员状态、会话和帐户设置。", + "editUserTitle": "管理用户:{{username}}", + "editUserDesc": "编辑角色、管理员状态、会话和账号设置。", "editUserUsername": "用户名", - "editUserAuthType": "身份验证类型", + "editUserAuthType": "认证类型", "editUserAdminStatus": "管理员状态", - "editUserUserId": "用户身份", - "linkAccountTitle": "关联账户", - "linkAccountDesc": "将 OIDC 帐户 {{username}} 与现有本地帐户合并。", - "linkAccountDescLocal": "将本地帐户 {{username}} 与现有的仅限 OIDC 的帐户关联。", + "editUserUserId": "用户ID", + "linkAccountTitle": "链接账号", + "linkAccountDesc": "将OIDC账号{{username}}与现有的本地账号合并。", + "linkAccountDescLocal": "将本地账号{{username}}与现有的仅OIDC账号链接。", "linkAccountWarningTitle": "这将:", - "linkAccountEffect1": "删除仅包含 OIDC 的帐户", - "linkAccountEffect2": "将 OIDC 登录信息添加到目标帐户", - "linkAccountEffect3": "允许同时使用 OIDC 和密码登录", - "linkAccountTargetUsername": "本地帐户用户名", - "linkAccountTargetPlaceholder": "输入要关联的本地帐户用户名", - "linkAccountOidcUsername": "OIDC账户用户名", - "linkAccountOidcPlaceholder": "输入仅用于 OIDC 的帐户用户名以进行合并", - "linkAccountOidcNotFound": "未找到使用该用户名的仅限 OIDC 帐户", - "linkAccounts": "关联账户", - "linkAccountSuccess": "账户已成功关联", - "linkAccountFailed": "账户关联失败", - "linkAccountInProgress": "正在连接……", - "unlinkAccountTitle": "解除 OIDC 链接", - "unlinkAccountDesc": "从 {{username}}中移除OIDC身份验证。他们将只能使用密码登录。", - "unlinkAccountWarning": "这将从该帐户中移除 OIDC 登录权限。用户必须设置密码才能继续登录。", - "unlinkAccount": "解除 OIDC 链接", - "unlinkAccountInProgress": "正在取消链接...", - "unlinkAccountSuccess": "OIDC已成功解除关联", - "unlinkAccountFailed": "取消链接 OIDC 失败", - "saving": "保存...", - "updateRegistrationFailed": "注册设置更新失败", + "linkAccountEffect1": "删除仅OIDC账号", + "linkAccountEffect2": "为目标账号添加OIDC登录", + "linkAccountEffect3": "允许OIDC和密码两种登录方式", + "linkAccountTargetUsername": "本地账号用户名", + "linkAccountTargetPlaceholder": "输入要链接的本地账号用户名", + "linkAccountOidcUsername": "OIDC账号用户名", + "linkAccountOidcPlaceholder": "输入要合并的仅OIDC账号用户名", + "linkAccountOidcNotFound": "未找到使用该用户名的仅OIDC账号", + "linkAccounts": "链接账号", + "linkAccountSuccess": "账号已成功链接", + "linkAccountFailed": "链接账号失败", + "linkAccountInProgress": "正在链接...", + "unlinkAccountTitle": "取消OIDC关联", + "unlinkAccountDesc": "从{{username}}移除OIDC认证。该用户将只能使用密码登录。", + "unlinkAccountWarning": "这将从此账号移除OIDC登录。用户必须设置密码才能继续登录。", + "unlinkAccount": "取消OIDC关联", + "unlinkAccountInProgress": "正在取消关联...", + "unlinkAccountSuccess": "OIDC 取消关联成功", + "unlinkAccountFailed": "取消关联 OIDC 失败", + "saving": "保存中...", + "updateRegistrationFailed": "更新注册设置失败", "updatePasswordLoginFailed": "更新密码登录设置失败", - "cannotDisablePasswordLoginWithTotp": "如果一个或多个用户启用了双因素身份验证 (2FA),则无法禁用密码登录。请先禁用双因素身份验证。", + "cannotDisablePasswordLoginWithTotp": "一个或多个用户启用了 2FA 时不能禁用密码登录。请先禁用 2FA。", "updateOidcAutoProvisionFailed": "更新 OIDC 自动配置设置失败", "updateOidcSilentLoginDefaultFailed": "更新静默 OIDC 登录设置失败", - "updatePasswordResetFailed": "密码重置设置更新失败", - "sessionTimeoutRange2": "会话超时时间必须介于 1 小时到 720 小时之间。", + "updatePasswordResetFailed": "更新密码重置设置失败", + "sessionTimeoutRange2": "会话超时必须在 1 到 720 小时之间", "sessionTimeoutSaved": "会话超时已保存", "sessionTimeoutSaveFailed": "保存会话超时失败", - "monitoringIntervalInvalid": "无效的区间值", - "monitoringSaved": "已保存的监控设置", + "monitoringIntervalInvalid": "无效的间隔值", + "monitoringSaved": "监控设置已保存", "monitoringSaveFailed": "保存监控设置失败", - "metricsHistoryRetention": "指标历史记录保留", - "metricsHistoryRetentionRange": "1至90天", + "metricsHistoryRetention": "指标历史保留", + "metricsHistoryRetentionRange": "1 到 90 天", "days": "天", - "guacamoleSaved": "鳄梨酱设置已保存", + "guacamoleSaved": "Guacamole 设置已保存", "guacamoleSaveFailed": "保存 Guacamole 设置失败", "guacamoleUpdateFailed": "更新 Guacamole 设置失败", - "tailscaleSettingsSaved": "尾尺度设置已保存", + "tailscaleSettingsSaved": "Tailscale 设置已保存", "tailscaleSettingsSaveFailed": "保存 Tailscale 设置失败", "logLevelUpdateFailed": "更新日志级别失败", "oidcSaved": "OIDC 配置已保存", "oidcSaveFailed": "保存 OIDC 配置失败", - "oidcRemoved": "已移除 OIDC 配置", + "oidcRemoved": "OIDC 配置已移除", "oidcRemoveFailed": "移除 OIDC 配置失败", - "createUserRequired": "需要用户名和密码。", - "createUserPasswordTooShort": "密码长度必须至少为 6 个字符", - "createUserSuccess": "用户“{{username}}”创建", + "createUserRequired": "需要用户名和密码", + "createUserPasswordTooShort": "密码长度至少需要 6 个字符", + "createUserSuccess": "用户 \"{{username}}\" 已创建", "createUserFailed": "创建用户失败", "updateAdminStatusFailed": "更新管理员状态失败", - "allSessionsRevoked": "所有会话均被撤销", + "allSessionsRevoked": "所有会话已撤销", "revokeSessionsFailed": "撤销会话失败", "manageUserData": "管理用户数据", - "backToUsers": "返回用户", - "manageTabAccount": "帐户", - "manageTabHosts": "主持人", - "manageTabCredentials": "证书", - "manageTabSnippets": "片段", - "manageTabSessions": "会议", + "backToUsers": "返回用户列表", + "manageTabAccount": "账户", + "manageTabHosts": "主机", + "manageTabCredentials": "凭据", + "manageTabSnippets": "代码片段", + "manageTabSessions": "会话", "manageTabDanger": "危险", - "manageEditorBack": "返回 {{username}}的数据", + "manageEditorBack": "返回 {{username}} 的数据", "dataLockedBadge": "已锁定", - "dataLockedNotice": "用户 {{username}}的数据将被锁定,直到他们下次登录。在此之前,他们的主机名、凭据和代码片段将无法查看或编辑。", + "dataLockedNotice": "{{username}} 的数据将保持锁定状态,直到其下次登录。在此之前,无法查看或编辑其主机、凭据和代码片段。", "resetPasswordTitle": "重置密码", - "resetPasswordOidcOnly": "该用户通过外部服务提供商登录,没有密码。", + "resetPasswordOidcOnly": "此用户通过外部提供商登录,没有密码。", "resetPasswordPlaceholder": "新密码", "resetPasswordBtn": "重置", - "resetPasswordWorking": "正在重置……", - "resetPasswordSuccess": "密码重置", - "resetPasswordSuccessWiped": "密码已重置。用户的加密数据已被清除。", - "resetPasswordFailed": "重置密码失败", - "resetPasswordConfirmWipe": "自加密升级以来,{{username}} 一直未登录,因此其数据无法恢复。立即重置将删除其主机、凭据和代码片段。是否继续?", - "totpSectionTitle": "双因素身份验证", + "resetPasswordWorking": "正在重置...", + "resetPasswordSuccess": "密码已重置", + "resetPasswordSuccessWiped": "密码已重置。用户的加密数据已被擦除。", + "resetPasswordFailed": "密码重置失败", + "resetPasswordConfirmWipe": "{{username}} 自加密升级后尚未登录,因此其数据无法恢复。现在重置将删除其主机、凭据和代码片段。是否继续?", + "totpSectionTitle": "双因素认证", "totpStatusEnabled": "此用户已启用 TOTP", - "totpStatusDisabled": "此用户未启用 TOTP。", + "totpStatusDisabled": "此用户未启用 TOTP", "disableTotp": "禁用", - "disableTotpConfirm": "是否禁用 {{username}}的双因素身份验证?他们将能够仅使用密码登录。", + "disableTotpConfirm": "为 {{username}} 禁用双因素认证?他们将只能使用密码登录。", "totpDisabledSuccess": "TOTP 已禁用", - "totpDisableFailed": "禁用 TOTP 失败", - "manageApiKeys": "API密钥", - "apiKeyNamePlaceholder": "键名", - "apiKeyCopyNotice": "立即复制此密钥,它不会再次显示。", - "noApiKeysForUser": "没有 API 密钥", + "totpDisableFailed": "TOTP 禁用失败", + "manageApiKeys": "API 密钥", + "apiKeyNamePlaceholder": "密钥名称", + "apiKeyCopyNotice": "立即复制此密钥,之后将不再显示。", + "noApiKeysForUser": "无 API 密钥", "apiKeyDeleteFailed": "删除 API 密钥失败", "exportUserData": "数据导出", - "exportUserDataDesc": "以 JSON 格式下载此用户的 hosts 文件、凭据和文件管理器数据。密钥已解密。", - "exportUserDataSuccess": "用户数据导出", - "exportUserDataFailed": "用户数据导出失败", - "hostsCount": "{{count}} 主机", + "exportUserDataDesc": "以 JSON 格式下载此用户的主机、凭据和文件管理器数据。机密数据已解密。", + "exportUserDataSuccess": "用户数据已导出", + "exportUserDataFailed": "导出用户数据失败", + "hostsCount": "{{count}} 个主机", "addHostForUser": "添加主机", - "noHostsForUser": "该用户没有主机。", + "noHostsForUser": "此用户没有主机", "connectToHost": "连接", - "deleteHostConfirm": "删除属于 {{username}} 的主机“{{name}}”?", + "deleteHostConfirm": "删除属于 {{username}} 的主机 \"{{name}}\"?", "hostDeletedSuccess": "主机已删除", "hostDeleteFailed": "删除主机失败", - "credentialsCount": "{{count}} 凭证", + "credentialsCount": "{{count}} 个凭据", "addCredentialForUser": "添加凭据", - "noCredentialsForUser": "此用户没有凭据", - "deleteCredentialConfirm": "删除属于 {{username}} 的凭据“{{name}}”?", - "credentialDeletedSuccess": "凭证已删除", + "noCredentialsForUser": "该用户没有凭据", + "deleteCredentialConfirm": "删除属于 {{username}} 的凭据 \"{{name}}\"?", + "credentialDeletedSuccess": "凭据已删除", "credentialDeleteFailed": "删除凭据失败", - "snippetsCount": "{{count}} 代码片段", + "snippetsCount": "{{count}} 个代码片段", "addSnippetForUser": "添加代码片段", - "noSnippetsForUser": "该用户没有任何代码片段。", - "snippetRequiredFields": "代码片段名称和内容均为必填项。", + "noSnippetsForUser": "该用户没有代码片段", + "snippetRequiredFields": "代码片段名称和内容为必填项", "snippetNamePlaceholder": "代码片段名称", "snippetContentPlaceholder": "命令内容", "snippetFolderPlaceholder": "文件夹(可选)", "snippetSaved": "代码片段已保存", "snippetSaveFailed": "保存代码片段失败", - "deleteSnippetConfirm": "删除属于 {{username}} 的片段“{{name}}”?", - "snippetDeletedSuccess": "片段已删除", + "deleteSnippetConfirm": "删除属于 {{username}} 的代码片段 \"{{name}}\"?", + "snippetDeletedSuccess": "代码片段已删除", "snippetDeleteFailed": "删除代码片段失败", "noSessionsForUser": "没有活动会话", - "deleteUserDangerDesc": "永久删除 {{username}} 及其所有数据(主机、凭据、代码片段、历史记录)。此操作无法撤销。", + "deleteUserDangerDesc": "永久删除 {{username}} 及其所有数据(主机、凭据、代码片段、历史记录)。此操作不可撤销。", "deleteUserConfirm": "永久删除 {{username}} 及其所有数据?", - "deleteUserAdminBlocked": "删除此用户前,请先移除其管理员权限。", - "createRoleRequired": "姓名和显示名称是必填项。", - "createRoleSuccess": "角色“{{name}}”已创建", + "deleteUserAdminBlocked": "删除此用户前请先移除管理员状态。", + "createRoleRequired": "名称和显示名称为必填项", + "createRoleSuccess": "角色 \"{{name}}\" 已创建", "createRoleFailed": "创建角色失败", "apiKeyNameRequired": "密钥名称为必填项", - "apiKeyUserRequired": "需要用户 ID", - "apiKeyCreatedSuccess": "API 密钥“{{name}}”已创建", + "apiKeyUserRequired": "用户ID为必填项", + "apiKeyCreatedSuccess": "API 密钥 \"{{name}}\" 已创建", "apiKeyCreateFailed": "创建 API 密钥失败", - "exportSuccess": "数据库已成功导出", + "exportSuccess": "数据库导出成功", "exportFailed": "数据库导出失败", - "importSelectFile": "请先选择文件。", + "importSelectFile": "请先选择文件", "importCompleted": "导入完成:已导入 {{total}} 项,跳过 {{skipped}} 项", - "importFailed": "导入失败: {{error}}", + "importFailed": "导入失败:{{error}}", "importError": "数据库导入失败", - "sectionHostDefaults": "主机默认值", - "hostDefaultsDesc": "创建新主机时会自动应用这些设置。单个主机仍然可以覆盖这些设置。", - "hostDefaultsSocks5": "SOCKS5代理", + "sectionHostDefaults": "主机默认设置", + "hostDefaultsDesc": "创建新主机时自动应用的设置。各主机仍可覆盖这些设置。", + "hostDefaultsSocks5": "SOCKS5 代理", "hostDefaultsUseSocks5": "启用 SOCKS5 代理", - "hostDefaultsUseSocks5Desc": "在所有新主机上预先填充 SOCKS5 代理", - "hostDefaultsSocks5Host": "代理主机/端口", + "hostDefaultsUseSocks5Desc": "在所有新主机上预填 SOCKS5 代理", + "hostDefaultsSocks5Host": "代理主机 / 端口", "hostDefaultsSocks5Username": "代理用户名", "hostDefaultsSocks5Password": "代理密码", "hostDefaultsMetrics": "主机指标", "hostDefaultsMetricsEnabled": "启用指标", - "hostDefaultsMetricsEnabledDesc": "默认情况下收集新主机的 CPU、内存和其他统计信息", + "hostDefaultsMetricsEnabledDesc": "默认情况下,在新主机上收集 CPU、内存及其他统计数据", "hostDefaultsStatusCheckEnabled": "启用状态检查", - "hostDefaultsStatusCheckEnabledDesc": "默认情况下,对新主机进行在线/离线状态轮询", + "hostDefaultsStatusCheckEnabledDesc": "默认轮询新主机的在线/离线状态", "hostDefaultsTerminal": "终端", - "hostDefaultsSessionLogging": "会话日志", - "hostDefaultsSessionLoggingDesc": "默认情况下记录新主机上的终端会话", - "hostDefaultsCommandHistory": "命令历史记录", - "hostDefaultsCommandHistoryDesc": "默认情况下跟踪新主机上的命令历史记录", + "hostDefaultsSessionLogging": "会话记录", + "hostDefaultsSessionLoggingDesc": "默认记录新主机的终端会话", + "hostDefaultsCommandHistory": "命令历史", + "hostDefaultsCommandHistoryDesc": "默认跟踪新主机的命令历史", "hostDefaultsSaved": "主机默认设置已保存", "hostDefaultsSaveFailed": "保存主机默认设置失败", "rolePermissions": { - "count": "{{count}} 权限", + "count": "{{count}} 个权限", "editAction": "编辑权限", "loadError": "加载权限目录失败", "saved": "角色权限已保存", "saveError": "保存角色权限失败", - "save": "节省", - "saving": "保存..." + "save": "保存", + "saving": "保存中..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "主持人", - "hostPlaceholder": "192.168.1.1 或 example.com", - "portLabel": "港口", + "hostLabel": "主机", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "端口", "portPlaceholder": "22", "usernameLabel": "用户名", - "usernamePlaceholder": "用户名", - "authLabel": "身份验证", + "usernamePlaceholder": "username", + "authLabel": "认证", "passwordLabel": "密码", - "passwordPlaceholder": "密码", + "passwordPlaceholder": "password", "privateKeyLabel": "私钥", - "privateKeyPlaceholder": "粘贴私钥……", + "privateKeyPlaceholder": "粘贴私钥...", "credentialLabel": "凭据", - "credentialPlaceholder": "选择已保存的凭据", + "credentialPlaceholder": "选择一个已保存的凭据", "connectToTerminal": "连接到终端", - "connectToFiles": "连接到文件" + "connectToFiles": "连接到文件管理器" }, "history": { "noTerminalSelected": "未选择终端", - "noTerminalSelectedHint": "打开 SSH 终端标签页以查看其命令历史记录", - "searchPlaceholder": "搜索历史……", + "noTerminalSelectedHint": "打开一个 SSH 终端标签页以查看其命令历史", + "searchPlaceholder": "搜索历史...", "clearAll": "全部清除", "noHistoryEntries": "无历史记录", - "trackingDisabled": "历史记录跟踪已禁用", - "trackingDisabledHint": "在主机终端设置中启用它。" + "trackingDisabled": "历史跟踪已禁用", + "trackingDisabledHint": "在主机终端设置中启用。" }, "sshTools": { - "keyRecordingTitle": "关键录音", - "recordToTerminals": "记录到终端", - "selectAll": "全部", - "selectNone": "没有任何", - "noTerminalTabsOpen": "没有打开任何终端标签页。", + "keyRecordingTitle": "按键录制", + "recordToTerminals": "录制到终端", + "selectAll": "全选", + "selectNone": "全不选", + "noTerminalTabsOpen": "无打开的终端标签页", "selectTerminalsAbove": "选择上方终端", - "broadcastInputPlaceholder": "在此输入以广播按键操作……", - "fillPassword": "填写密码", - "fillPasswordSuccess": "已将密码输入到 {{count}} 终端中", - "fillPasswordMissing": "{{count}} 所选终端未保存密码", + "broadcastInputPlaceholder": "在此输入以广播按键...", + "fillPassword": "填充密码", + "fillPasswordSuccess": "已将密码填充到 {{count}} 个终端", + "fillPasswordMissing": "{{count}} 个所选终端无保存的密码", "stopRecording": "停止录制", "startRecording": "开始录制", "settingsTitle": "设置", - "enableRightClickCopyPaste": "启用右键单击复制/粘贴" + "enableRightClickCopyPaste": "启用右键复制/粘贴" }, "splitScreen": { "layoutTitle": "布局", - "selectLayoutAbove": "请选择上方布局。", - "selectLayoutHint": "选择要显示的窗格数量", + "selectLayoutAbove": "选择上方布局", + "selectLayoutHint": "选择要显示的窗格数", "panesTitle": "窗格", - "openTabsTitle": "打开标签页", - "dragTabsHint": "将标签拖入上方窗格,或使用快速分配功能。", - "dropHere": "落在这里", - "emptyPane": "空的", + "openTabsTitle": "打开的标签页", + "dragTabsHint": "将标签页拖入上方窗格,或使用快速分配", + "dropHere": "拖放至此", + "emptyPane": "空", "dashboard": "仪表板", - "clearSplitScreen": "清晰的分屏", + "clearSplitScreen": "清除分屏", "quickAssign": "快速分配", "alreadyAssigned": "窗格 {{index}}", - "splitTab": "分页标签", - "addToSplit": "添加到拆分", - "removeFromSplit": "从拆分中移除", + "splitTab": "拆分标签页", + "addToSplit": "添加到分屏", + "removeFromSplit": "从分屏移除", "assignToPane": "分配到窗格", "hotkeysTitle": "键盘快捷键", - "hotkeysSplitRight": "切换双向分割", - "hotkeysSplitBelow": "切换三路分割", + "hotkeysSplitRight": "切换双窗格分屏", + "hotkeysSplitBelow": "切换三窗格分屏", "hotkeysNavigatePane": "导航窗格", "hotkeysNextTab": "下一个标签页", "hotkeysPrevTab": "上一个标签页" }, "snippets": { - "title": "片段", + "title": "代码片段", "createSnippetTitle": "创建代码片段", - "createSnippetDescription": "创建一个新的命令片段以便快速执行", - "nameLabel": "姓名", - "namePlaceholder": "例如,重启 Nginx", + "createSnippetDescription": "创建新的命令代码片段以便快速执行", + "nameLabel": "名称", + "namePlaceholder": "例如:重启 Nginx", "descriptionLabel": "描述", "descriptionPlaceholder": "可选描述", - "optional": "选修的", + "optional": "可选", "folderLabel": "文件夹", "noFolder": "无文件夹(未分类)", "commandLabel": "命令", - "commandPlaceholder": "例如,sudo systemctl restart nginx", + "commandPlaceholder": "例如:sudo systemctl restart nginx", "cancel": "取消", "createSnippetButton": "创建代码片段", "createFolderTitle": "创建文件夹", - "createFolderDescription": "将你的代码片段整理到文件夹中", + "createFolderDescription": "将代码片段整理到文件夹中", "folderNameLabel": "文件夹名称", - "folderNamePlaceholder": "例如:系统命令、Docker脚本", + "folderNamePlaceholder": "例如:系统命令、Docker 脚本", "folderColorLabel": "文件夹颜色", "folderIconLabel": "文件夹图标", "previewLabel": "预览", "folderNameFallback": "文件夹名称", "createFolderButton": "创建文件夹", "targetTerminals": "目标终端", - "selectAll": "全部", - "selectNone": "没有任何", - "noTerminalTabsOpen": "没有打开任何终端标签页。", - "searchPlaceholder": "搜索摘要……", - "newSnippet": "新代码片段", + "selectAll": "全选", + "selectNone": "取消全选", + "noTerminalTabsOpen": "没有打开的终端标签页", + "searchPlaceholder": "搜索代码片段...", + "newSnippet": "新建代码片段", "newFolder": "新建文件夹", - "run": "跑步", + "run": "运行", "noSnippetsInFolder": "此文件夹中没有代码片段", "uncategorized": "未分类", - "editSnippetTitle": "编辑片段", - "editSnippetDescription": "更新此命令片段", + "editSnippetTitle": "编辑代码片段", + "editSnippetDescription": "更新此命令代码片段", "saveSnippetButton": "保存更改", "createSuccess": "代码片段创建成功", "createFailed": "创建代码片段失败", - "updateSuccess": "代码片段已成功更新", + "updateSuccess": "代码片段更新成功", "updateFailed": "更新代码片段失败", "deleteFailed": "删除代码片段失败", "folderCreateSuccess": "文件夹创建成功", @@ -3072,140 +3223,185 @@ "deleteFolder": "删除文件夹", "folderDeleteSuccess": "文件夹“{{name}}”已删除", "folderDeleteFailed": "删除文件夹失败", - "folderEditSuccess": "文件夹已成功更新", + "folderEditSuccess": "文件夹更新成功", "folderEditFailed": "更新文件夹失败", "confirmRunMessage": "运行“{{name}}”?", - "confirmRunButton": "跑步", - "runSuccess": "在 {{count}} 终端中运行“{{name}}”", + "confirmRunButton": "运行", + "runSuccess": "已在 {{count}} 个终端中运行“{{name}}”", "copySuccess": "已将“{{name}}”复制到剪贴板", - "shareTitle": "分享片段", + "shareTitle": "共享代码片段", "shareUser": "用户", "shareRole": "角色", - "selectUser": "选择用户...", - "selectRole": "选择一个角色……", - "shareSuccess": "代码片段已成功分享", - "shareFailed": "分享片段失败", + "selectUser": "选择一个用户...", + "selectRole": "选择一个角色...", + "shareSuccess": "代码片段共享成功", + "shareFailed": "共享代码片段失败", "revokeSuccess": "访问权限已撤销", "revokeFailed": "撤销访问权限失败", "currentAccess": "当前访问权限", - "shareLoadError": "共享数据加载失败", + "shareLoadError": "加载共享数据失败", "loading": "加载中...", "close": "关闭", "reorderFailed": "保存代码片段顺序失败", - "importExport": "进出口", + "importExport": "导入 / 导出", "exportBtn": "导出 JSON", "importBtn": "导入 JSON", - "exportSuccess": "代码片段已成功导出", + "exportSuccess": "代码片段导出成功", "exportFailed": "导出代码片段失败", "importTitle": "导入代码片段", - "importDescription": "从 Termix 导出的 JSON 文件中导入代码片段和文件夹。", - "importDropOrClick": "将 JSON 文件拖放到此处,或点击浏览。", - "importSelectedFile": "已选择: {{name}}", - "importOverwrite": "覆盖同名且位于同一文件夹的现有代码片段", - "importStartBtn": "进口", - "importSuccess": "导入完成:已添加 {{snippets}} 个代码片段,已更新 {{updated}} 个代码片段,已跳过 {{skipped}} 个代码片段,已添加 {{folders}} 个文件夹", + "importDescription": "从 Termix 导出的 JSON 文件导入代码片段和文件夹", + "importDropOrClick": "将 JSON 文件拖放到此处或点击浏览", + "importSelectedFile": "已选择:{{name}}", + "importOverwrite": "覆盖具有相同名称和文件夹的现有代码片段", + "importStartBtn": "导入", + "importSuccess": "导入完成:添加了 {{snippets}} 个代码片段,更新了 {{updated}} 个,跳过了 {{skipped}} 个,添加了 {{folders}} 个文件夹", "importFailed": "导入代码片段失败", - "importInvalidFile": "文件无效:预期为包含代码片段或文件夹数组的 JSON 对象。", - "targetHostsLabel": "目标宿主", - "targetHostsHint": "指定主机直接运行此代码片段,无需打开终端。", + "importInvalidFile": "无效文件:应为包含 snippets 或 folders 数组的 JSON 对象", + "targetHostsLabel": "目标主机", + "targetHostsHint": "分配主机,以便无需打开终端即可直接运行此代码片段。", "noHostsAvailable": "未配置任何主机", - "clearTargetHosts": "全部清除", - "hasTargetHosts": "有目标主机", - "runOnTargets": "目标运行", - "directRunSuccess": "在 {{count}} 主机上运行“{{name}}”", - "directRunPartialFail": "“{{name}}”在一个或多个主机上失败", - "executionResultTitle": "执行结果: {{name}}", - "executionResultDescription": "在每个目标主机上运行代码片段的输出结果。", + "clearTargetHosts": "清除全部", + "hasTargetHosts": "已分配目标主机", + "runOnTargets": "在目标上运行", + "directRunSuccess": "已在 {{count}} 台主机上运行 \"{{name}}\"", + "directRunPartialFail": "\"{{name}}\" 在一台或多台主机上运行失败", + "executionResultTitle": "执行结果:{{name}}", + "executionResultDescription": "在每台目标主机上运行代码片段的输出。", "executionSuccess": "成功", - "executionFailed": "失败的" + "executionFailed": "失败" + }, + "keybindings": { + "title": "键盘快捷键", + "description": "自定义终端复制、粘贴和控制快捷键,或绑定按键以发送文本或运行代码片段。", + "defaultsHeading": "内置快捷方式", + "customHeading": "自定义快捷方式", + "addBinding": "添加快捷方式", + "addBindingTitle": "添加快捷方式", + "editBindingTitle": "编辑快捷方式", + "loading": "加载中...", + "noCustomBindings": "暂无自定义快捷键", + "defaultBadge": "默认", + "customizedBadge": "定制", + "customize": "定制", + "resetToDefault": "重置为默认值", + "resetAllToDefaults": "所有设置重置为默认值", + "close": "关闭", + "cancel": "取消", + "saveBinding": "保存快捷方式", + "saveError": "保存键盘快捷键失败", + "pressKeysToRecord": "关键组合", + "pressKeysPlaceholder": "点击并按下按键", + "recording": "按下按键……", + "comboRequiredError": "先按下组合键", + "textRequiredError": "请输入要发送的文本", + "controlCodeRequiredError": "请输入单个字母作为控制码", + "snippetRequiredError": "选择一段文字", + "conflictWarning": "此组合已绑定到 {{combo}}。保存后两者都会生效。", + "actionLabel": "行动", + "actionCopy": "复制选择", + "actionPaste": "粘贴", + "actionSendControlCode": "发送 Ctrl+字母信号", + "actionSendText": "发送短信", + "actionRunSnippet": "运行现有代码片段", + "controlCodeLabel": "信", + "textLabel": "发送的文本", + "appendEnterLabel": "发送后按回车键", + "snippetLabel": "片段", + "selectSnippetPlaceholder": "选择一段文字", + "orphanedSnippetWarning": "未找到代码片段", + "clipboardPermissionNote": "某些浏览器在首次使用时可能会提示授予剪贴板权限。" }, "userProfile": { - "donateTitle": "让Termix活下去", - "donateDescription": "Termix 是免费的,由几位业余人士自行托管和开发。如果它为您节省了时间或金钱,欢迎您捐赠加密货币以支持其持续运营。", - "donateMilestones": "捐款将用于资助我们研究和学习构建 SAML、Kubernetes 和 Agent 支持所需的知识。查看进展并捐款。", + "donateTitle": "让 Termix 持续运转", + "donateDescription": "Termix 是免费、自托管的,仅由少数几人用业余时间构建。若它为您节省了时间或金钱,请通过加密货币捐赠助力其持续发展。", + "donateMilestones": "捐赠有助于资助研究和学习构建 SAML、Kubernetes 及 Agent 支持所需的时间。查看进展并捐赠。", "donateButton": "捐赠加密货币", "storageModeLocal": "浏览器", "storageModeCloud": "数据库", - "storageModeDescription": "浏览器仅将设置存储在此浏览器中。数据库会同步到服务器,并可在任何设备上加载。", - "resetToDefaults": "恢复默认设置", - "resetToDefaultsSuccess": "设置将重置为默认值。", - "storageModeSwitch": "偏好存储", - "sectionAccount": "帐户", - "sectionAppearance": "外貌", + "storageModeDescription": "浏览器模式仅将设置存储在当前浏览器中。数据库模式则同步至服务器,并在任意设备上加载。", + "resetToDefaults": "重置为默认值", + "resetToDefaultsSuccess": "设置已重置为默认值。", + "storageModeSwitch": "偏好设置存储", + "sectionAccount": "账户", + "sectionAppearance": "外观", "sectionSecurity": "安全", - "sectionApiKeys": "API密钥", - "sectionC2sTunnels": "C2S隧道", + "sectionApiKeys": "API 密钥", + "sectionData": "数据", + "sectionC2sTunnels": "C2S 隧道", "usernameLabel": "用户名", "roleLabel": "角色", - "roleAdministrator": "行政人员", - "authMethodLabel": "身份验证方法", - "authMethodLocal": "当地的", - "twoFaLabel": "双因素认证", - "twoFaOn": "在", - "twoFaOff": "离开", + "roleAdministrator": "管理员", + "authMethodLabel": "认证方式", + "authMethodLocal": "本地", + "twoFaLabel": "双重认证", + "twoFaOn": "开启", + "twoFaOff": "关闭", "versionLabel": "版本", - "betaProgramTitle": "测试计划", - "betaProgramDescription": "使用每周滚动更新的 :beta Docker 标签,即可提前体验即将推出的功能。此版本不稳定,不适用于生产环境。", - "betaProgramFeedback": "发现漏洞?请在此处报告。", + "betaProgramTitle": "Beta 计划", + "betaProgramDescription": "使用每周滚动更新的 :beta Docker 标签提前体验新功能。不稳定,请勿用于生产环境。", + "betaProgramFeedback": "发现问题?在此报告。", "betaProgramLearnMore": "了解更多", - "deleteAccount": "删除帐户", - "deleteAccountDescription": "永久删除您的帐户", + "deleteAccount": "删除账户", + "deleteAccountDescription": "永久删除您的账户", "changeServerDescription": "切换到其他 Termix 后端服务器", "deleteButton": "删除", - "deleteAccountPermanent": "此操作不可逆转,且无法撤销。", - "deleteAccountWarning": "所有会话、主机、凭据和设置都将被永久删除。", - "confirmPasswordDeletePlaceholder": "请输入密码以确认", + "deleteAccountPermanent": "此操作是永久性的,无法撤销。", + "deleteAccountWarning": "所有会话、主机、凭据和设置将被永久删除。", + "confirmPasswordDeletePlaceholder": "输入密码以确认", "languageLabel": "语言", "themeLabel": "主题", "fontSizeLabel": "字体大小", "accentColorLabel": "强调色", "settingsTerminal": "终端", - "commandAutocomplete": "命令自动完成", - "commandAutocompleteDesc": "输入时显示自动完成功能", + "commandAutocomplete": "命令自动补全", + "commandAutocompleteDesc": "输入时显示自动补全", + "keyboardShortcuts": "键盘快捷键", + "keyboardShortcutsDescription": "自定义终端复制、粘贴和命令快捷键", + "manageShortcuts": "管理", "terminalLinkBehavior": "终端链接点击", - "terminalLinkBehaviorDesc": "点击终端中的链接时的默认行为", - "historyTracking": "历史跟踪", + "terminalLinkBehaviorDesc": "点击终端内链接时的默认行为", + "historyTracking": "历史记录跟踪", "historyTrackingDesc": "跟踪终端命令", "commandPalette": "命令面板", "commandPaletteDesc": "启用键盘快捷键", - "reopenTabsOnLogin": "登录后重新打开标签页", - "reopenTabsOnLoginDesc": "即使从其他设备登录或刷新页面,也能恢复您打开的标签页。", - "confirmTabClose": "确认关闭标签页", - "confirmTabCloseDesc": "关闭终端标签页前请先询问。", + "reopenTabsOnLogin": "登录时重新打开标签页", + "reopenTabsOnLoginDesc": "登录或刷新页面时恢复已打开的标签页,即使是从其他设备登录", + "confirmTabClose": "关闭标签页确认", + "confirmTabCloseDesc": "关闭终端标签页前询问", "settingsSidebar": "侧边栏", - "showHostTags": "节目主持人标签", + "showHostTags": "显示主机标签", "showHostTagsDesc": "在主机列表中显示标签", "hostTrayOnClick": "点击展开主机操作", - "hostTrayOnClickDesc": "始终显示连接按钮;点击展开管理选项,而不是悬停。", - "compactHostView": "紧凑型主机视图", - "compactHostViewDesc": "将每个主机折叠成一行,仅显示其名称和地址", + "hostTrayOnClickDesc": "始终显示连接按钮;点击展开管理选项而非悬停", + "compactHostView": "紧凑主机视图", + "compactHostViewDesc": "将每个主机折叠为一行,仅显示名称和地址", "statusColors": "真实状态颜色", - "statusColorsDesc": "使用绿色/红色表示在线/离线状态,而不是使用强调色。", - "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "保持左侧边栏应用导轨始终展开,而不是鼠标悬停时才展开。", - "expandAppRailOnHover": "鼠标悬停时展开应用轨道", - "expandAppRailOnHoverDesc": "允许鼠标指针移动到左侧边栏应用栏上方时展开。", + "statusColorsDesc": "使用绿色/红色表示在线/离线状态,而非强调色", + "pinAppRail": "固定应用边栏", + "pinAppRailDesc": "保持左侧应用边栏始终展开,而非悬停展开", + "expandAppRailOnHover": "悬停展开应用边栏", + "expandAppRailOnHoverDesc": "允许鼠标悬停时展开左侧应用边栏", "settingsNavigation": "导航", - "navigationTabsDesc": "选择在应用侧边栏中显示的选项卡", - "settingsSnippets": "片段", - "foldersCollapsed": "文件夹已折叠", + "navigationTabsDesc": "选择在应用导航栏中显示的标签页", + "settingsSnippets": "代码片段", + "foldersCollapsed": "折叠文件夹", "foldersCollapsedDesc": "默认折叠文件夹", "confirmExecution": "确认执行", - "confirmExecutionDesc": "运行代码片段前请确认", + "confirmExecutionDesc": "运行代码片段前确认", "settingsUpdates": "更新", "disableUpdateChecks": "禁用更新检查", "disableUpdateChecksDesc": "停止检查更新", - "totpAuthenticator": "TOTP 验证器", - "totpEnabled": "已启用双因素身份验证", - "totpDisabled": "增加额外的登录安全性", + "totpAuthenticator": "TOTP 身份验证器", + "totpEnabled": "双因素认证已启用", + "totpDisabled": "添加额外的登录安全", "disable": "禁用", - "enable": "使能够", + "enable": "启用", "setupTotp": "设置 TOTP", "qrCode": "二维码", - "totpInstructions": "扫描二维码或在身份验证器应用中输入密钥,然后输入 6 位数验证码", + "totpInstructions": "扫描二维码或在您的身份验证器应用中输入密钥,然后输入6位数字代码", "totpCodePlaceholder": "000000", - "verify": "核实", - "changePassword": "更改密码", + "verify": "验证", + "changePassword": "修改密码", "currentPasswordLabel": "当前密码", "currentPasswordPlaceholder": "当前密码", "newPasswordLabel": "新密码", @@ -3214,259 +3410,276 @@ "confirmPasswordPlaceholder": "确认新密码", "updatePassword": "更新密码", "createApiKeyTitle": "创建 API 密钥", - "createApiKeyDescription": "生成用于程序化访问的新 API 密钥。", - "apiKeyNameLabel": "姓名", - "apiKeyNamePlaceholder": "例如 CI 流水线", - "expiryDateLabel": "到期日", - "optional": "选修的", + "createApiKeyDescription": "生成用于编程访问的新 API 密钥。", + "apiKeyNameLabel": "名称", + "apiKeyNamePlaceholder": "例如:CI 流水线", + "expiryDateLabel": "到期日期", + "optional": "可选", "cancel": "取消", "createKey": "创建密钥", - "apiKeyCount": "{{count}} 键", - "newKey": "新钥匙", - "noApiKeys": "暂无API密钥。", - "apiKeyActive": "积极的", - "apiKeyUsageHint": "请将钥匙放在里面。", - "apiKeyUsageHintHeader": "标题。", + "apiKeyCount": "{{count}} 个密钥", + "newKey": "新建密钥", + "noApiKeys": "尚无 API 密钥。", + "apiKeyActive": "活跃", + "apiKeyUsageHint": "将您的密钥包含在", + "apiKeyUsageHintHeader": "标头。", "apiKeyPermissionsHint": "密钥继承创建用户的权限。", + "exportData": "导出我的数据", + "exportDataDesc": "下载主机、凭据和设置的备份,以便传输到另一台设备。", + "export": "出口", + "exporting": "出口...", + "importData": "导入我的数据", + "importDataDesc": "从 .sqlite 备份文件恢复您的主机、凭据和设置", + "importDataSelected": "已选择: {{name}}", + "selectFile": "选择文件", + "changeFile": "改变", + "import": "进口", + "importing": "输入...", + "exportSuccess": "数据已成功导出", + "exportFailed": "数据导出失败", + "importSelectFile": "请先选择文件。", + "importCompleted": "导入完成:已导入 {{total}} 项,跳过 {{skipped}} 项", + "importFailed": "导入失败: {{error}}", "roleUser": "用户", "authMethodDual": "双重认证", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP 设置启动失败", - "totpEnter6Digits": "请输入6位代码", - "totpEnabledSuccess": "已启用双因素身份验证", - "totpInvalidCode": "验证码无效,请重试。", - "totpDisableInputRequired": "请输入您的 TOTP 验证码或密码", - "totpDisabledSuccess": "双因素身份验证已禁用", - "totpDisableFailed": "禁用双重验证失败", - "totpDisableTitle": "禁用双因素身份验证", + "totpSetupFailed": "启动 TOTP 设置失败", + "totpEnter6Digits": "输入 6 位验证码", + "totpEnabledSuccess": "双因素认证已启用", + "totpInvalidCode": "验证码无效,请重试", + "totpDisableInputRequired": "输入您的 TOTP 验证码或密码", + "totpDisabledSuccess": "双因素认证已禁用", + "totpDisableFailed": "禁用 2FA 失败", + "totpDisableTitle": "禁用 2FA", "totpDisablePlaceholder": "输入 TOTP 验证码或密码", - "totpDisableConfirm": "禁用双因素身份验证", + "totpDisableConfirm": "禁用 2FA", "totpContinueVerify": "继续验证", - "totpVerifyTitle": "验证码", - "totpBackupTitle": "备用代码", - "totpDownloadBackup": "下载备份代码", + "totpVerifyTitle": "验证验证码", + "totpBackupTitle": "备份验证码", + "totpDownloadBackup": "下载备份验证码", "passkeys": "通行密钥", - "passkeysDesc": "使用 WebAuthn/FIDO2 凭据无需密码登录", - "passkeyName": "密码名称", + "passkeysDesc": "使用 WebAuthn/FIDO2 凭据进行无密码登录", + "passkeyName": "通行密钥名称", "passkeyUvPreferred": "首选", - "passkeyUvRequired": "必需的", - "passkeyUvDiscouraged": "灰心", - "addPasskey": "添加密码", - "noPasskeys": "未注册任何通行密钥。", - "passkeyAdded": "已添加通行密钥", - "passkeyAddFailed": "添加密码失败", - "passkeyDeleted": "密码已删除", - "passkeyDeleteFailed": "删除密码失败", - "done": "完毕", - "secretCopied": "秘密已复制到剪贴板", + "passkeyUvRequired": "必需", + "passkeyUvDiscouraged": "不鼓励", + "addPasskey": "添加通行密钥", + "noPasskeys": "未注册通行密钥。", + "passkeyAdded": "通行密钥已添加", + "passkeyAddFailed": "添加通行密钥失败", + "passkeyDeleted": "通行密钥已删除", + "passkeyDeleteFailed": "删除通行密钥失败", + "done": "完成", + "secretCopied": "密钥已复制到剪贴板", "apiKeyNameRequired": "密钥名称为必填项", - "apiKeyCreated": "API 密钥“{{name}}”已创建", + "apiKeyCreated": "API 密钥 \"{{name}}\" 已创建", "apiKeyCreateFailed": "创建 API 密钥失败", "apiKeyUser": "用户", - "apiKeyExpires": "过期", - "apiKeyRevoked": "API密钥“{{name}}”已撤销", + "apiKeyExpires": "过期时间", + "apiKeyRevoked": "API 密钥 \"{{name}}\" 已撤销", "apiKeyRevokeFailed": "撤销 API 密钥失败", - "passwordFieldsRequired": "需要当前密码和新密码。", + "passwordFieldsRequired": "需要提供当前密码和新密码", "passwordMismatch": "密码不匹配", - "passwordTooShort": "密码长度必须至少为 6 个字符", - "passwordUpdated": "密码已成功更新", + "passwordTooShort": "密码长度至少为6个字符", + "passwordUpdated": "密码更新成功", "passwordUpdateFailed": "密码更新失败", - "deletePasswordRequired": "删除帐户需要密码。", - "deleteFailed": "删除帐户失败", - "deleting": "正在删除……", + "deletePasswordRequired": "删除账户需要提供密码", + "deleteFailed": "账户删除失败", + "deleting": "删除中...", "colorPickerTooltip": "打开颜色选择器", "themeSystem": "系统", - "themeLight": "光", - "themeDark": "黑暗的", - "themeDracula": "德古拉", - "themeCatppuccin": "猫布奇宁", - "themeNord": "诺德", - "themeSolarized": "太阳能化", - "themeTokyoNight": "东京之夜", - "themeOneDark": "黑暗", + "themeLight": "浅色", + "themeDark": "深色", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { "title": "Tmux 监视器", - "failedToLoadHosts": "主机加载失败", - "failedToLoad": "加载 tmux 会话失败", - "tmuxUnavailable": "此主机上未安装 tmux", - "noSessions": "此主机上没有 tmux 会话", + "failedToLoadHosts": "加载主机失败", + "failedToLoad": "加载tmux会话失败", + "tmuxUnavailable": "此主机未安装tmux", + "noSessions": "此主机上没有tmux会话", "noHostSelected": "未选择主机", - "attached": "随附的", - "detached": "分离", - "attach": "附", + "attached": "已连接", + "detached": "已断开", + "attach": "连接", "editTags": "编辑标签", - "tagsHint": "逗号分隔的标签(例如 YOLO、lab、training)", + "tagsHint": "逗号分隔的标签(例如:YOLO, lab, training)", "tagsSaved": "标签已保存", "tagsSaveFailed": "标签保存失败", - "searchPlaceholder": "跨会话的搜索结果...", - "searchResults": "{{count}} 匹配", + "searchPlaceholder": "跨会话搜索输出...", + "searchResults": "{{count}} 个匹配", "searchFailed": "搜索失败", "selectPaneHint": "选择一个窗格以预览其输出", "closePreview": "关闭预览", - "newSession": "新会议", + "newSession": "新建会话", "newSessionHint": "会话名称(字母、数字、_ @ % + = -)", - "newSessionPlaceholder": "我的会话", - "create": "创造", - "sessionCreated": "会话“{{name}}”已创建", + "newSessionPlaceholder": "my-session", + "create": "创建", + "sessionCreated": "会话 \"{{name}}\" 已创建", "sessionCreateFailed": "创建会话失败", - "splitRight": "向右分裂", - "splitDown": "分解", + "splitRight": "向右拆分", + "splitDown": "向下拆分", "splitFailed": "拆分窗格失败", - "newWindow": "新窗口", + "newWindow": "新建窗口", "windowCreateFailed": "创建窗口失败", "attachSessionTooltip": "附加到 {{session}}", "refresh": "刷新", "refreshFailed": "刷新会话失败", - "collapseAll": "全部收起", + "collapseAll": "全部折叠", "expandAll": "全部展开", "moreActions": "更多操作", "sessionStats": "会话统计", - "renameSessionTitle": "重命名会话“{{name}}”", + "renameSessionTitle": "重命名会话 \"{{name}}\"", "rename": "重命名", - "sessionRenamed": "会话已重命名为“{{name}}”", - "sessionRenameFailed": "会话重命名失败", - "editTagsTitle": "编辑“{{name}}”的标签", - "killPane": "杀戮面板", + "sessionRenamed": "会话已重命名为 \"{{name}}\"", + "sessionRenameFailed": "重命名会话失败", + "editTagsTitle": "编辑 \"{{name}}\" 的标签", + "killPane": "终止窗格", "resizeTree": "拖动调整大小 — 双击重置", - "reattach": "重新连接(修复显示混乱的问题)", - "killWindow": "杀戮窗口", - "killWindowTitle": "终止窗口 {{index}} 的“{{session}}”?", - "killWindowBody": "此窗口中的所有窗格和进程都将被终止。终止最后一个窗口将结束会话。", - "windowKillFailed": "无法关闭窗口", + "reattach": "重新附加(修复显示错乱)", + "killWindow": "终止窗口", + "killWindowTitle": "终止会话 \"{{session}}\" 的窗口 {{index}}?", + "killWindowBody": "该窗口中的所有窗格和进程都将被终止。终止最后一个窗口将结束会话。", + "windowKillFailed": "终止窗口失败", "statusActivity": "活动", - "statusWindows": "视窗", + "statusWindows": "窗口", "statusPanes": "窗格", "statusTags": "标签", "killPaneTitle": "终止窗格 {{id}}?", - "killPaneBody": "此窗格中正在运行的进程将被终止。关闭最后一个窗格会将其窗口关闭。", - "paneKillFailed": "无法终止窗格", - "killSessionTitle": "终止会话“{{name}}”?", - "killSessionBody": "此会话中的所有窗口和正在运行的进程都将被终止。此操作无法撤销。", - "kill": "杀", - "sessionKilled": "会话“{{name}}”已终止", + "killPaneBody": "该窗格中运行的进程将被终止。终止最后一个窗格将关闭其窗口。", + "paneKillFailed": "终止窗格失败", + "killSessionTitle": "终止会话 \"{{name}}\"?", + "killSessionBody": "该会话中的所有窗口和运行中的进程都将被终止。此操作无法撤销。", + "kill": "终止", + "sessionKilled": "会话 \"{{name}}\" 已终止", "sessionKillFailed": "终止会话失败", "hostUnreachable": "无法连接到主机。请检查主机是否在线且可访问。", "noServer": "此主机上未运行 tmux 服务器。", - "searchTruncated": "部分结果——搜索涵盖每个窗格的最后 {{lines}} 行,最多涵盖 {{panes}} 个窗格。", + "searchTruncated": "部分结果 — 搜索覆盖每个窗格的最近 {{lines}} 行,最多 {{panes}} 个窗格。", "closeSearchResults": "关闭搜索结果", "retry": "重试", - "noHosts": "没有可用的 SSH 主机。", - "noHostsHint": "在 SSH 主机上启用 Tmux 监视器选项(主机管理器 → 终端选项卡)以监视其 tmux 会话。", - "tmuxInstallHint": "使用以下命令将其安装到主机上:", - "attachTooltip": "打开终端并输入 {{host}}", - "attachTooltipPane": "打开终端到 {{host}} — tmux 会话 {{session}}", - "timeJustNow": "现在", - "timeMinutes": "{{count}}几分钟前", - "timeHours": "{{count}}小时前", - "timeDays": "{{count}}天前" + "noHosts": "没有可用的 SSH 主机", + "noHostsHint": "在 SSH 主机上启用 Tmux Monitor 选项(主机管理器 → 终端标签页)以监控其 tmux 会话。", + "tmuxInstallHint": "使用以下命令在主机上安装:", + "attachTooltip": "打开到 {{host}} 的终端", + "attachTooltipPane": "打开到 {{host}} 的终端 — tmux 会话 {{session}}", + "timeJustNow": "刚刚", + "timeMinutes": "{{count}} 分钟前", + "timeHours": "{{count}} 小时前", + "timeDays": "{{count}} 天前" }, "mobileKeyboard": { - "shift": "转移", + "shift": "Shift", "ctrl": "Ctrl", - "esc": "Esc键", - "tab": "标签页", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "向上箭头", - "arrowDown": "向下箭头", - "arrowLeft": "左箭头", - "arrowRight": "向右箭头", - "home": "家", - "end": "结尾", - "pageUp": "翻页", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "home": "Home", + "end": "End", + "pageUp": "PgUp", "pageDown": "PgDn", - "delete": "德尔", - "editQuickKeys": "编辑快捷键", - "quickKeysTitle": "快捷键", + "delete": "Del", + "paste": "粘贴", + "editQuickKeys": "编辑快速键", + "quickKeysTitle": "快速键", "quickKeysDesc": "点击 × 删除。最多支持 8 个字符。", "quickKeyPlaceholder": "例如 sudo ", "addQuickKey": "添加", - "removeQuickKey": "消除", - "resetDefaults": "恢复默认设置", - "done": "完毕" + "removeQuickKey": "删除", + "resetDefaults": "重置为默认值", + "done": "完成" }, "serial": { - "title": "序列号", - "portLabel": "港口", + "title": "串口", + "portLabel": "端口", "portPlaceholder": "/dev/ttyUSB0 或 COM3", "baudRateLabel": "波特率", "dataBitsLabel": "数据", - "stopBitsLabel": "停止", - "parityLabel": "平价", - "parityNone": "没有任何", - "parityEven": "甚至", - "parityOdd": "奇怪的", - "connect": "连接到串口", + "stopBitsLabel": "停止位", + "parityLabel": "奇偶校验", + "parityNone": "无", + "parityEven": "偶校验", + "parityOdd": "奇校验", + "connect": "连接串口", "disconnect": "断开", "refreshPorts": "刷新端口", - "connected": "已连接至 {{path}} ,波特率为 {{baud}}", - "disconnected": "串口已断开连接", - "connectionError": "无法打开串口", + "connected": "已连接到 {{path}},波特率 {{baud}}", + "disconnected": "串口已断开", + "connectionError": "打开串口失败", "wsError": "WebSocket 错误", "errorNoServerUrl": "未配置服务器 URL", "notSupportedTitle": "不支持串口", - "notSupported": "串行连接需要支持 Web Serial API 的浏览器(Chrome、Edge 或 Firefox 151+),或者 Termix 桌面应用程序。", - "hideHint": "您可以在“用户配置文件”>“外观”>“侧边栏”>“导航”下隐藏“序列号”选项卡。", - "browserPickerHint": "点击“连接”,浏览器将打开端口选择器,供您选择设备。" + "notSupported": "串口连接需要支持 Web Serial API 的浏览器(Chrome、Edge 或 Firefox 151+),或者 Termix 桌面应用。", + "hideHint": "您可以在用户资料 > 外观 > 侧边栏 > 导航中隐藏串口标签页。", + "browserPickerHint": "点击连接,您的浏览器将打开端口选择器以选择设备。" }, "metricsHistory": { - "title": "指标历史记录", + "title": "指标历史", "historySuffix": "历史", - "cpuMemoryDisk": "CPU/内存/磁盘", + "cpuMemoryDisk": "CPU / 内存 / 磁盘", "network": "网络", "download": "RX", - "upload": "德克萨斯州", - "noData": "此时间段内没有历史数据可供参考。", - "custom": "风俗", - "to": "到", - "apply": "申请", - "viewHistory": "查看历史记录" + "upload": "TX", + "noData": "此时间范围内没有可用的历史数据。", + "custom": "自定义", + "to": "至", + "apply": "应用", + "viewHistory": "查看历史" }, "alerts": { - "tabFirings": "警报", + "tabFirings": "告警", "tabRules": "规则", - "tabChannels": "频道", - "noFirings": "没有未确认的警报", - "noRules": "未配置任何告警规则", - "noChannels": "未配置任何通知渠道", - "rulesDesc": "警报规则触发通知", - "channelsDesc": "警报通知发送至何处", - "acknowledge": "承认", + "tabChannels": "通道", + "noFirings": "没有未确认告警", + "noRules": "未配置告警规则", + "noChannels": "未配置通知渠道", + "rulesDesc": "告警规则触发通知", + "channelsDesc": "告警通知的发送渠道", + "acknowledge": "确认告警", "ackAll": "全部确认", - "allAcknowledged": "所有警报均已确认", - "ackFailed": "未能确认警报", - "ackAllFailed": "未能确认所有警报", + "allAcknowledged": "所有告警已确认", + "ackFailed": "确认告警失败", + "ackAllFailed": "确认所有告警失败", "showAcknowledged": "显示全部", - "hideAcknowledged": "隐藏", - "addChannel": "添加频道", - "editChannel": "编辑频道", - "channelName": "姓名", + "hideAcknowledged": "隐藏已确认", + "addChannel": "添加渠道", + "editChannel": "编辑渠道", + "channelName": "名称", "channelType": "类型", - "channelNameRequired": "姓名(必填)", + "channelNameRequired": "名称为必填项", "webhookUrl": "URL", - "webhookUrlRequired": "需要提供 Webhook URL", - "webhookDesc": "每次触发警报时,将 JSON 有效负载 POST 到此 URL", + "webhookUrlRequired": "Webhook URL 为必填项", + "webhookDesc": "每次告警触发时向该 URL 发送 POST JSON 数据", "ntfyServer": "服务器 URL", - "ntfyTopic": "话题", - "ntfyTopicRequired": "主题是必需的", - "ntfyToken": "访问令牌(可选)", - "channelSaveFailed": "频道保存失败", + "ntfyTopic": "主题", + "ntfyTopicRequired": "主题为必填项", + "ntfyToken": "访问令牌(选填)", + "channelSaveFailed": "保存渠道失败", "test": "测试", - "testSent": "已发送测试通知", + "testSent": "测试通知已发送", "testFailed": "测试通知失败", - "addRule": "添加警报规则", - "editRule": "编辑提醒规则", + "addRule": "添加告警规则", + "editRule": "编辑告警规则", "ruleName": "规则名称", - "ruleNameRequired": "姓名(必填)", - "triggerType": "扳机", - "thresholdValue": "临界点 (%)", - "durationSeconds": "持续时间(秒,0 = 立即发射)", + "ruleNameRequired": "名称为必填项", + "triggerType": "触发条件", + "thresholdValue": "阈值(%)", + "durationSeconds": "持续时间(秒,0 表示立即触发)", "cooldownMinutes": "冷却时间(分钟)", "channels": "通知渠道", - "noChannelsHint": "首先在“频道”选项卡中添加频道。", - "ruleSaveFailed": "规则保存失败" + "noChannelsHint": "请先在渠道标签页中添加渠道", + "ruleSaveFailed": "保存规则失败" } } diff --git a/src/ui/locales/translated/zh_TW.json b/src/ui/locales/translated/zh_TW.json index 15bd7d8e..4bde145c 100644 --- a/src/ui/locales/translated/zh_TW.json +++ b/src/ui/locales/translated/zh_TW.json @@ -1,137 +1,137 @@ { "termixId": { "title": "Termix ID", - "loadFailed": "載入 Termix ID 失敗", - "claimTitle": "領取您的 Termix ID", - "claimIntro": "選擇一個唯一的用戶名。您的 SSH 公鑰將發佈到一個公共 URL,您可以將其新增至任何伺服器的 authorized_keys 檔案中。", - "handleLabel": "處理", - "handlePlaceholder": "愛麗絲", - "checking": "檢查…", - "available": "可用的", - "taken": "已經採取", - "invalidHandle": "僅小寫字母、數字、連字號和底線。", - "descriptionLabel": "描述(可選)", - "descriptionPlaceholder": "工作筆記型電腦和手機鑰匙", + "loadFailed": "無法載入 Termix ID", + "claimTitle": "註冊您的 Termix ID", + "claimIntro": "選擇一個唯一的使用者代號。您的 SSH 公開金鑰將發佈在一個公開 URL,您可以將其加入任何伺服器的 authorized_keys 檔案中。", + "handleLabel": "代號", + "handlePlaceholder": "alice", + "checking": "檢查中…", + "available": "可用", + "taken": "已被使用", + "invalidHandle": "僅限小寫字母、數字、- 和 _", + "descriptionLabel": "描述(選填)", + "descriptionPlaceholder": "工作筆電和手機金鑰", "create": "建立 Termix ID", "created": "Termix ID 已建立", - "createFailed": "建立 Termix ID 失敗", - "deleteConfirm": "刪除您的 Termix ID 和所有已發佈的金鑰?您配置的伺服器將保留這些金鑰,直到您手動刪除它們。", + "createFailed": "無法建立 Termix ID", + "deleteConfirm": "刪除您的 Termix ID 和所有已發佈的金鑰?您已佈建的伺服器將保留這些金鑰,直到您手動移除為止。", "deleted": "Termix ID 已刪除", - "deleteFailed": "刪除 Termix ID 失敗", + "deleteFailed": "無法刪除 Termix ID", "copyFailed": "複製失敗", - "resolverUrlLabel": "公共解析器 URL", - "provisionLabel": "設定伺服器", - "publishTitle": "發佈公鑰", + "resolverUrlLabel": "公開解析器 URL", + "provisionLabel": "佈建伺服器", + "publishTitle": "發佈公開金鑰", "generate": "產生", - "generateTooltip": "產生 Ed25519 金鑰對 — 發佈公鑰並將私鑰下載到您的設備", - "saveToVault": "儲存到憑證", + "generateTooltip": "產生一組 Ed25519 金鑰對 — 發佈公開金鑰並將私密金鑰下載到您的裝置", + "saveToVault": "儲存至憑證庫", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", - "labelPlaceholder": "標籤(可選)", - "add": "添加", + "labelPlaceholder": "標籤(選填)", + "add": "新增", "importFromCredential": "或從已儲存的憑證匯入", - "keyPublished": "關鍵訊息已發布", - "addKeyFailed": "新增密鑰失敗", - "generatedSaved": "密鑰對已產生並儲存到您的憑證庫。私鑰也已下載。", - "generatedOnly": "金鑰對已產生-私鑰已下載(僅顯示一次)。", - "generateFailed": "密鑰產生失敗", - "imported": "從憑證導入的金鑰", - "importFailed": "導入金鑰失敗", - "noKeys": "尚未發布任何公鑰。", - "keysTitle": "已發布密鑰", + "keyPublished": "金鑰已發佈", + "addKeyFailed": "無法新增金鑰", + "generatedSaved": "金鑰對已產生並儲存至您的憑證庫。私密金鑰也已下載。", + "generatedOnly": "金鑰對已產生 — 私密金鑰已下載(僅顯示一次)。", + "generateFailed": "無法產生金鑰", + "imported": "已從憑證匯入金鑰", + "importFailed": "匯入金鑰失敗", + "noKeys": "尚未發布任何公開金鑰。", + "keysTitle": "已發布的金鑰", "published": "已發布", - "hidden": "隱", - "keyRemoved": "鑰匙已移除", - "removeKeyFailed": "移除密鑰失敗", - "updateKeyFailed": "密鑰更新失敗", + "hidden": "已隱藏", + "keyRemoved": "金鑰已移除", + "removeKeyFailed": "移除金鑰失敗", + "updateKeyFailed": "更新金鑰失敗", "fromVault": "來自憑證庫", "linkedToTermixId": "透過 Termix ID 發布", - "selectCredential": "選擇憑證...", - "import": "進口", - "caTitle": "證書頒發機構", - "caIntro": "在伺服器上信任此 CA,它將接受您簽署的任何憑證。輪換證書可以一次撤銷所有證書;證書也會自動過期。", + "selectCredential": "選擇一個憑證...", + "import": "匯入", + "caTitle": "憑證授權中心", + "caIntro": "在伺服器上信任此 CA,即可接受您簽署的任何憑證。輪替可一次性撤銷所有憑證;憑證也會自行過期。", "caEnable": "啟用 CA", - "caEnabled": "CA已啟用", + "caEnabled": "CA 已啟用", "caCreateFailed": "啟用 CA 失敗", - "caPublicKeyLabel": "CA公鑰", - "caTrustLabel": "信任伺服器(以 root 使用者身分執行)", - "caRotate": "旋轉", - "caRotateConfirm": "更換證書頒發機構?該機構簽發的所有證書將不再被接受,必須重新簽發。", - "caRotated": "CA 輪替-先前的憑證已撤銷", - "caRotateFailed": "CA旋轉失敗", + "caPublicKeyLabel": "CA 公開金鑰", + "caTrustLabel": "在伺服器上信任(以 root 執行)", + "caRotate": "輪替", + "caRotateConfirm": "輪替 CA?所有由其簽署的憑證將不再被接受,必須重新簽發。", + "caRotated": "CA 已輪替 — 之前的憑證已撤銷", + "caRotateFailed": "輪替 CA 失敗", "caDelete": "移除 CA", - "caDeleteConfirm": "移除證書頒發機構?", - "caDeleted": "CA移除", + "caDeleteConfirm": "移除憑證授權中心?", + "caDeleted": "CA 已移除", "caDeleteFailed": "移除 CA 失敗", - "caValidityLabel": "證書有效期限(天)", - "issueCert": "證書", - "issueCertTooltip": "為該金鑰頒發由您的 CA 簽署的 SSH 憑證。", - "certIssued": "證書已簽發並下載", - "certIssueFailed": "頒發證書失敗" + "caValidityLabel": "憑證有效期(天)", + "issueCert": "憑證", + "issueCertTooltip": "為此金鑰簽發 SSH 憑證,由您的 CA 簽署", + "certIssued": "憑證已簽發並下載", + "certIssueFailed": "簽發憑證失敗" }, "credentials": { "folders": "資料夾", "folder": "資料夾", "password": "密碼", - "key": "鑰匙", - "sshPrivateKey": "SSH 私鑰", + "key": "金鑰", + "sshPrivateKey": "SSH 私密金鑰", "upload": "上傳", - "keyPassword": "密鑰密碼", - "sshKey": "SSH金鑰", - "uploadPrivateKeyFile": "上傳私鑰文件", + "keyPassword": "金鑰密碼", + "sshKey": "SSH 金鑰", + "uploadPrivateKeyFile": "上傳私密金鑰檔案", "searchCredentials": "搜尋憑證...", "addCredential": "新增憑證", - "caCertificate": "CA憑證(-cert.pub)", - "caCertificateDescription": "可選:上傳或貼上 CA 簽署的憑證檔案(例如 id_ed25519-cert.pub)。如果您的 SSH 伺服器使用基於憑證的授權,則此步驟為必要。", - "uploadCertFile": "上傳 -cert.pub 文件", + "caCertificate": "CA 憑證 (-cert.pub)", + "caCertificateDescription": "可選:上傳或貼上 CA 簽署的憑證檔案(例如 id_ed25519-cert.pub)。當您的 SSH 伺服器使用憑證式驗證時需要。", + "uploadCertFile": "上傳 -cert.pub 檔案", "clearCert": "清除", - "certLoaded": "證書已載入", - "certPublicKeyLabel": "CA憑證", - "certTypeLabel": "證書類型", + "certLoaded": "憑證已載入", + "certPublicKeyLabel": "CA 憑證", + "certTypeLabel": "憑證類型", "pasteOrUploadCert": "貼上或上傳 -cert.pub 憑證...", - "hasCaCert": "擁有CA證書", - "noCaCert": "無加州證書", - "noPublicKeyAvailable": "沒有可用的公鑰。請先開啟憑證編輯器。", + "hasCaCert": "含有 CA 憑證", + "noCaCert": "無 CA 憑證", + "noPublicKeyAvailable": "沒有可用的公開金鑰。請先開啟憑證編輯器。", "deployCommandCopied": "部署命令已複製", "sortCredentials": "排序憑證", - "sortDefault": "預設訂單", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", - "sortUsernameAsc": "使用者名稱(A → Z)", - "sortUsernameDesc": "使用者名稱(Z → A)", + "sortDefault": "預設順序", + "sortNameAsc": "名稱 (A → Z)", + "sortNameDesc": "名稱 (Z → A)", + "sortUsernameAsc": "使用者名稱 (A → Z)", + "sortUsernameDesc": "使用者名稱 (Z → A)", "filterCredentials": "篩選憑證", - "filterClearAll": "清除篩選條件", + "filterClearAll": "清除篩選", "filterTypeGroup": "類型", "filterTypePassword": "密碼", - "filterTypeKey": "SSH金鑰", + "filterTypeKey": "SSH 金鑰", "filterTagsGroup": "標籤" }, "homepage": { "title": "首頁", - "addWidget": "添加小部件", + "addWidget": "新增小工具", "editWidget": "編輯小工具", "deleteWidget": "刪除", - "widgetTypes": "小部件類型", + "widgetTypes": "小工具類型", "serviceLink": "服務連結", "folder": "資料夾", - "clock": "鐘", + "clock": "時鐘", "notes": "筆記", "hostStatus": "主機狀態", "bookmarkList": "書籤", "zoomIn": "放大", "zoomOut": "縮小", - "resetView": "重置視圖", - "lockLayout": "鎖具佈局", + "resetView": "重設檢視", + "lockLayout": "鎖定佈局", "unlockLayout": "解鎖佈局", - "noWidgets": "右鍵單擊或單擊“+”添加您的第一個小部件", - "openFullView": "開啟完整視圖", + "noWidgets": "按右鍵或點擊 + 來新增您的第一個小工具", + "openFullView": "開啟完整檢視", "previewTitle": "首頁預覽", "cancel": "取消", - "save": "節省", + "save": "儲存", "title_label": "標題", - "widgetTitlePlaceholder": "小部件標題(可選)", + "widgetTitlePlaceholder": "小工具標題(選填)", "url": "URL", - "imageUrl": "自訂圖像 URL", - "imageUrlHint": "留空則自動使用網站圖標", + "imageUrl": "自訂圖片 URL", + "imageUrlHint": "留空以自動使用網站圖標", "showImage": "顯示圖片", "description": "描述", "color": "顏色", @@ -139,266 +139,305 @@ "expanded": "預設展開", "timezone": "時區", "showSeconds": "顯示秒數", - "format12h": "12小時", - "format24h": "24小時", + "format12h": "12 小時制", + "format24h": "24 小時制", "content": "內容", "backgroundColor": "背景顏色", - "host": "主持人", - "showMetrics": "顯示指標", + "host": "主機", + "showMetrics": "顯示效能指標", "links": "連結", - "addLink": "添加連結", + "addLink": "新增連結", "linkLabel": "標籤", "linkUrl": "URL", - "removeLink": "消除", + "removeLink": "移除", "categoryLinks": "連結", "categoryInfo": "資訊", "categorySystem": "系統", - "widgetServiceLinkDesc": "指向服務 URL 的可點擊圖塊", - "widgetFolderDesc": "用於對相關組件進行分組的容器", - "widgetClockDesc": "可設定時區的即時時鐘", - "widgetNotesDesc": "Markdown 筆記小工具", - "widgetHostStatusDesc": "顯示 SSH 主機的即時 CPU、記憶體和磁碟使用情況", - "widgetBookmarkListDesc": "快速連結列表", + "widgetServiceLinkDesc": "可點擊的方塊,連結至服務 URL", + "widgetFolderDesc": "用於將相關小工具分組的容器", + "widgetClockDesc": "具有可設定時區的即時時鐘", + "widgetNotesDesc": "一個 Markdown 筆記小工具", + "widgetHostStatusDesc": "顯示 SSH 主機的即時 CPU、記憶體與磁碟使用狀況", + "widgetBookmarkListDesc": "快速連結清單", "copyLink": "複製連結", "linkCopied": "連結已複製!", - "location": "地點", + "location": "位置", "temperatureUnit": "溫度單位", - "showForecast": "顯示3天天氣預報", - "scrolling": "允許滾動", - "feedUrl": "Feed URL", - "maxItems": "最大物品數", + "showForecast": "顯示 3 天天氣預報", + "scrolling": "允許捲動", + "feedUrl": "摘要來源 URL", + "maxItems": "最大項目數", "showDescription": "顯示描述", "widgetWeatherName": "天氣", "widgetWeatherDesc": "任何地點的即時天氣", "widgetIframeName": "iFrame 嵌入", - "widgetIframeDesc": "在 iframe 中嵌入任何 URL", - "widgetRssName": "RSS訂閱", - "widgetRssDesc": "顯示來自 RSS 或 Atom 來源的項目", - "dragToFolder": "將控制項拖曳到此處或使用“+”新增", - "showDisk": "顯示磁碟使用情況", - "addToFolder": "將小部件新增至資料夾", - "noHostSelected": "未選擇主機", - "metricsNotAvailable": "指標不可用", + "widgetIframeDesc": "在 iframe 中嵌入任何 URL 的內容", + "widgetRssName": "RSS 摘要", + "widgetRssDesc": "顯示來自 RSS 或 Atom 摘要的項目", + "dragToFolder": "拖曳小工具至此處,或按 + 新增", + "showDisk": "顯示磁碟使用量", + "addToFolder": "新增小工具至資料夾", + "noHostSelected": "未選取主機", + "metricsNotAvailable": "效能指標無法使用", "selectHost": "選擇主機...", "displayedMetrics": "顯示的指標", - "metricCpu": "中央處理器", - "metricMemory": "記憶", + "metricCpu": "CPU", + "metricMemory": "記憶體", "metricDisk": "磁碟", - "metricUptime": "正常運作時間", + "metricUptime": "運作時間", "metricSystem": "系統", "metricOs": "作業系統", "metricKernel": "核心", "metricHostname": "主機名稱", "metricNetwork": "網路", - "metricProcesses": "流程", - "metricProcessesTotal": "全部的", - "metricProcessesRunning": "跑步", - "categoryMonitoring": "監測", + "metricProcesses": "程序", + "metricProcessesTotal": "總數", + "metricProcessesRunning": "執行中", + "categoryMonitoring": "監控", "loading": "載入中...", - "noData": "暫無數據", + "noData": "無可用資料", "allClear": "一切正常", - "acknowledgeAlert": "承認", - "noPingUrls": "未配置任何 URL。", + "acknowledgeAlert": "確認", + "noPingUrls": "未設定 URL", "pingLabel": "標籤", - "addPingUrl": "新增網址", + "addPingUrl": "新增 URL", "showLatency": "顯示延遲", - "refreshInterval": "刷新間隔", + "refreshInterval": "重新整理間隔", "seconds": "秒", "filterActivityTypes": "篩選類型", - "filterTypesHint": "留空則顯示全部", - "showTimestamp": "顯示時間戳", - "uptimeUnavailable": "正常運轉時間不可用", - "uptimeLabel": "正常運作時間", + "filterTypesHint": "留空以顯示全部", + "showTimestamp": "顯示時間戳記", + "uptimeUnavailable": "運作時間無法取得", + "uptimeLabel": "運作時間", "overviewVersion": "版本", - "overviewUpdate": "最新", - "overviewUpdateAvailable": "更新可用", + "overviewUpdate": "最新版本", + "overviewUpdateAvailable": "有可用更新", "overviewDatabase": "資料庫", - "overviewUptime": "正常運作時間", - "noHosts": "未配置任何主機", - "hostGridHosts": "主持人", - "hostGridAllHint": "留空則顯示所有主機。", - "columns": "列", + "overviewUptime": "運作時間", + "noHosts": "未設定任何主機", + "hostGridHosts": "主機", + "hostGridAllHint": "留空以顯示所有主機", + "columns": "欄", "showIp": "顯示 IP 位址", - "connectionType": "連接類型", - "layout": "佈局", + "connectionType": "連線類型", + "layout": "版面配置", "showStatus": "顯示狀態", - "showHostName": "節目主持人姓名", + "showHostName": "顯示主機名稱", "noDockerActivity": "無 Docker 活動", "noActivity": "無活動", "showAcknowledged": "顯示已確認", - "showCurrentValue": "顯示當前值", + "showCurrentValue": "顯示目前值", "chartMetric": "指標", "metricRange": "範圍", "widgetMetricsChartName": "指標圖表", "widgetMetricsChartDesc": "主機的歷史 CPU、記憶體、磁碟或網路圖表", - "widgetHostGridName": "主機網格", - "widgetHostGridDesc": "SSH主機狀態網格視圖", - "widgetAlertFeedName": "警報訊息流", - "widgetAlertFeedDesc": "即時警報觸發並確認支持", + "widgetHostGridName": "主機方格", + "widgetHostGridDesc": "SSH 主機狀態的方格檢視", + "widgetAlertFeedName": "警報動態", + "widgetAlertFeedDesc": "即時警報觸發並支援確認", "widgetPingStatusName": "Ping 狀態", - "widgetPingStatusDesc": "對一個或多個 URL 進行 HTTP ping 狀態查詢", + "widgetPingStatusDesc": "一個或多個 URL 的 HTTP ping 狀態", "widgetRecentActivityName": "近期活動", - "widgetRecentActivityDesc": "可捲動查看近期 Termix 活動資訊流", - "widgetTermixUptimeName": "Termix 正常運作時間", - "widgetTermixUptimeDesc": "Termix 伺服器的即時運行時間計數器", - "widgetSystemOverviewName": "系統概述", - "widgetSystemOverviewDesc": "Termix 版本、資料庫健康狀況和正常運作時間一覽", - "widgetSshQuickConnectName": "SSH快速連接", - "widgetSshQuickConnectDesc": "一鍵開啟 SSH 會話", + "widgetRecentActivityDesc": "可捲動的近期 Termix 活動動態", + "widgetTermixUptimeName": "Termix 運作時間", + "widgetTermixUptimeDesc": "Termix 伺服器的即時運作時間計數器", + "widgetSystemOverviewName": "系統概覽", + "widgetSystemOverviewDesc": "一目了然 Termix 版本、資料庫健康狀態與運作時間", + "widgetSshQuickConnectName": "SSH 快速連線", + "widgetSshQuickConnectDesc": "一鍵按鈕開啟 SSH 工作階段", "widgetDockerActivityName": "Docker 活動", - "widgetDockerActivityDesc": "近期所有主機上的 Docker 容器事件", - "widgetCalendarName": "日曆", - "widgetCalendarDesc": "月曆,今日日期已高亮顯示。", + "widgetDockerActivityDesc": "所有主機上的近期 Docker 容器事件", + "widgetCalendarName": "行事曆", + "widgetCalendarDesc": "標示今日的月曆", "widgetCountdownName": "倒數計時", - "widgetCountdownDesc": "目標日期倒數計時", - "widgetSearchBarName": "搜尋欄", + "widgetCountdownDesc": "倒數至目標日期的計時器", + "widgetSearchBarName": "搜尋列", "widgetSearchBarDesc": "快速網頁搜尋小工具", "widgetTextBannerName": "文字橫幅", - "widgetTextBannerDesc": "畫布的醒目標籤或章節標題。", - "widgetImageWidgetName": "影像", + "widgetTextBannerDesc": "用於畫布的粗體標籤或區段標題", + "widgetImageWidgetName": "圖片", "widgetImageWidgetDesc": "顯示來自 URL 的圖片", "widgetMarkdownNotesName": "Markdown 筆記", - "widgetMarkdownNotesDesc": "帶有內聯 Markdown 渲染的富文本", + "widgetMarkdownNotesDesc": "具有內嵌 Markdown 渲染的豐富筆記", "widgetCustomApiName": "自訂 API", - "widgetCustomApiDesc": "從任何 JSON API 取得並顯示數據", + "widgetCustomApiDesc": "從任何 JSON API 擷取並顯示資料", "widgetServiceGridName": "服務網格", - "widgetServiceGridDesc": "可設定的服務磁貼連結網格", - "widgetDashboardLinksName": "儀錶板連結", - "widgetDashboardLinksDesc": "從控制台顯示您已配置的服務連結。", - "widgetSearchLinksName": "搜尋快捷方式", - "widgetSearchLinksDesc": "帶有內聯輸入的快速搜尋快捷按鈕", + "widgetServiceGridDesc": "可設定的服務磚塊連結網格", + "widgetDashboardLinksName": "儀表板連結", + "widgetDashboardLinksDesc": "顯示來自儀表板的已設定服務連結", + "widgetSearchLinksName": "搜尋捷徑", + "widgetSearchLinksDesc": "具有內嵌輸入的快速搜尋捷徑按鈕", "widgetLinkTreeName": "連結樹", - "widgetLinkTreeDesc": "帶有標題的連結分組", - "calMon": "莫", - "calTue": "圖", - "calWed": "我們", - "calThu": "釷", - "calFri": "法蘭西斯", - "calSat": "薩", - "calSun": "蘇", - "startOnMonday": "週一開始新的一周", + "widgetLinkTreeDesc": "附有標題的連結群組", + "calMon": "週一", + "calTue": "週二", + "calWed": "週三", + "calThu": "週四", + "calFri": "週五", + "calSat": "週六", + "calSun": "週日", + "startOnMonday": "從週一開始", "countdownNoDate": "未設定目標日期", - "countdownPast": "事件已結束", + "countdownPast": "目標日期已過", "countdownDays": "天", "countdownHours": "小時", - "countdownMinutes": "最小", + "countdownMinutes": "分鐘", "countdownSeconds": "秒", "countdownLabel": "標籤", - "countdownLabelPlaceholder": "例如:發布日", - "countdownShowDays": "演出日", - "countdownShowHours": "演出時間", + "countdownLabelPlaceholder": "例如:發佈日", + "countdownShowDays": "顯示天數", + "countdownShowHours": "顯示小時", "targetDate": "目標日期", "searchEngine": "搜尋引擎", - "customSearchUrl": "自訂搜尋網址", + "customSearchUrl": "自訂搜尋 URL", "searchPlaceholder": "搜尋...", - "searchGo": "去", - "searchPlaceholderLabel": "佔位符文字", - "searchPlaceholderHint": "搜尋框內顯示的文字", + "searchGo": "搜尋", + "searchPlaceholderLabel": "佔位文字", + "searchPlaceholderHint": "顯示在搜尋輸入框內的文字", "openInNewTab": "在新分頁中開啟", - "searchQueryPlaceholder": "請輸入查詢內容...", - "noSearchShortcuts": "未配置快捷方式", - "addSearchShortcut": "新增快捷方式", - "fontSize": "字體大小", + "searchQueryPlaceholder": "輸入查詢...", + "noSearchShortcuts": "尚未設定捷徑", + "addSearchShortcut": "新增捷徑", + "fontSize": "字型大小", "textAlign": "文字對齊", - "fontWeight": "字體粗細", - "clearColor": "透明色", - "imageFit": "影像擬合", - "imageLinkUrl": "連結網址", - "noImage": "未設定圖像 URL", + "fontWeight": "字型粗細", + "clearColor": "清除顏色", + "imageFit": "圖片填滿方式", + "imageLinkUrl": "連結 URL", + "noImage": "未設定圖片 URL", "altText": "替代文字", - "altTextPlaceholder": "描述這張圖片", - "renderMarkdown": "渲染 Markdown", + "altTextPlaceholder": "描述圖片", + "renderMarkdown": "轉譯 Markdown", "displayMode": "顯示模式", "displayField": "顯示欄位", "jsonPath": "JSON 路徑", "customApiLabel": "標籤", - "customApiLabelPlaceholder": "例如溫度", - "customApiUnit": "單元", - "customApiNoUrl": "未配置 API URL", - "customApiError": "獲取失敗", - "customApiNotArray": "響應不是數組", + "customApiLabelPlaceholder": "例如:溫度", + "customApiUnit": "單位", + "customApiNoUrl": "未設定 API URL", + "customApiError": "擷取失敗", + "customApiNotArray": "回應非陣列", "addService": "新增服務", "showLabels": "顯示標籤", "iconSize": "圖示大小", - "noDashboardLinks": "未配置任何儀表板鏈接", + "noDashboardLinks": "未設定儀表板連結", "noLimit": "無限制", - "sectionHeading": "章節標題", - "addSection": "添加部分", - "compactMode": "緊湊模式", - "showDetailed": "顯示詳細信息", - "selectHosts": "選擇主機", - "allHosts": "所有主持人", + "sectionHeading": "區段標題", + "addSection": "新增區段", + "compactMode": "精簡模式", + "showDetailed": "顯示詳細資訊", + "selectHosts": "選取主機", + "allHosts": "所有主機", "listLayout": "清單", - "gridLayout": "網格", - "terminal": "終端", - "files": "文件", + "gridLayout": "格線", + "terminal": "終端機", + "files": "檔案", "docker": "Docker", - "range15m": "15分鐘", - "range1h": "1小時", - "range6h": "6小時", - "range24h": "24小時", - "metricNetRx": "淨下載", - "metricNetTx": "淨上傳", - "severityFilter": "嚴重性過濾器", + "range15m": "15 分鐘", + "range1h": "1 小時", + "range6h": "6 小時", + "range24h": "24 小時", + "metricNetRx": "網路下載", + "metricNetTx": "網路上傳", + "severityFilter": "嚴重性篩選器", "filterAll": "全部", - "accentColor": "強調色", - "widgetSshTerminalName": "SSH終端", - "widgetSshTerminalDesc": "連接到已設定主機的內聯 SSH 終端", - "sshTerminalNoHost": "未配置主機", - "sshTerminalConnect": "連接", - "sshTerminalAutoConnect": "負載自動連接", - "widgetQuickConnectName": "快速連接", - "widgetQuickConnectDesc": "一鍵啟動按鈕,支援所有主機上的任何連線類型", - "connectionTypes": "連接類型", - "connType_terminal": "終端", - "connType_files": "文件管理器", + "accentColor": "輔色", + "widgetSshTerminalName": "SSH 終端機", + "widgetSshTerminalDesc": "連線至已設定主機的內嵌 SSH 終端機", + "sshTerminalNoHost": "未設定主機", + "sshTerminalConnect": "連線", + "sshTerminalAutoConnect": "載入時自動連線", + "widgetQuickConnectName": "快速連線", + "widgetQuickConnectDesc": "用於跨主機任何連線類型的一鍵啟動按鈕", + "connectionTypes": "連線類型", + "connType_terminal": "終端機", + "connType_files": "檔案管理員", "connType_docker": "Docker", - "connType_tunnel": "隧道", + "connType_tunnel": "通道", "connType_host-metrics": "主機指標", - "connType_rdp": "遠端桌面協定", + "connType_rdp": "RDP", "connType_vnc": "VNC", "connType_telnet": "Telnet", - "widgetFileManagerName": "文件管理器", - "widgetFileManagerDesc": "為已配置主機提供的嵌入式 SFTP 檔案管理器", - "widgetDockerName": "Docker 管理器", - "widgetDockerDesc": "為已配置主機提供的嵌入式 Docker 容器管理器", - "widgetTunnelName": "隧道經理", - "widgetTunnelDesc": "為已設定主機提供的嵌入式 SSH 隧道管理器", - "widgetNoHostSelected": "未配置主機" + "widgetFileManagerName": "檔案管理員", + "widgetFileManagerDesc": "用於已設定主機的內嵌 SFTP 檔案管理員", + "widgetDockerName": "Docker 管理員", + "widgetDockerDesc": "用於已設定主機的內嵌 Docker 容器管理員", + "widgetTunnelName": "通道管理員", + "widgetTunnelDesc": "用於已設定主機的內嵌 SSH 通道管理員", + "widgetNoHostSelected": "未設定主機" }, "serverConfig": { - "title": "伺服器配置", - "description": "設定 Termix 伺服器 URL 以連接到您的後端服務", + "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": "本機伺服器", + "savedServers": "已儲存的伺服器", + "noSavedServers": "無已儲存的伺服器", + "removeServer": "移除" + }, + "migrationNotice": { + "title": "Termix Desktop 現在可以獨立運行", + "body1": "此應用程式以前直接連接到您的 Termix 伺服器。現在已重新設計,可完全獨立運行,將主機和憑證儲存在本地,因此即使離線也能正常工作。與自託管 Termix 伺服器的雙向同步現在是可選的。", + "body2": "您現有的主機和憑證仍在 {{url}}上。啟用遠端同步並重新連接到該伺服器,即可恢復它們並保持兩者同步。", + "dismiss": "現在不要", + "setUpSync": "設定遠端同步" + }, + "remoteSync": { + "title": "遠端同步", + "description": "您可以選擇將此桌面應用程式連接到自架的 Termix 伺服器,以便在不同裝置間同步主機名稱、憑證和程式碼片段。無論是否連接,該應用程式始終可以完全離線運行。", + "notConnected": "未連接", + "connected": "已連接", + "connectedTo": "已連接至 {{url}}", + "lastSynced": "上次同步時間 {{time}}", + "neverSynced": "從未同步", + "syncError": "同步錯誤: {{message}}", + "needsReauth": "登入已過期", + "connectButton": "連接到伺服器", + "disconnectButton": "斷開", + "syncNowButton": "立即同步", + "syncing": "正在同步...", "serverUrl": "伺服器 URL", "enterServerUrl": "請輸入伺服器網址", - "saveFailed": "配置保存失敗", - "saveError": "儲存配置時發生錯誤", - "saving": "儲存...", - "saveConfig": "儲存配置", - "helpText": "輸入您的 Termix 伺服器運作所在的 URL(例如,http://localhost:30001 或 https://your-server.com)", - "changeServer": "變更伺服器", "mustIncludeProtocol": "伺服器 URL 必須以 http:// 或 https:// 開頭。", + "connectionTestFailed": "無法透過該 URL 連線到 Termix 伺服器", "allowInvalidCertificate": "允許無效證書", "allowInvalidCertificateDesc": "僅適用於具有自簽名憑證或 IP 位址憑證的受信任的自託管伺服器。", - "useEmbedded": "使用本地伺服器", - "embeddedDesc": "使用內建的本機伺服器運行Termix(無需遠端伺服器)", - "embeddedConnecting": "正在連接本地伺服器...", - "embeddedNotReady": "本地伺服器尚未準備就緒,請稍等片刻後再試。", - "localServer": "本地伺服器", "savedServers": "已儲存的伺服器", - "noSavedServers": "沒有已儲存的伺服器", - "removeServer": "消除" + "removeServer": "消除", + "continueButton": "繼續", + "cancelButton": "取消", + "signInTitle": "登入 {{url}}", + "originTitle": "連接來源", + "originDescription": "選擇 SSH 連線的預設發起位置。此設定可針對每個主機進行單獨設定。", + "originLocal": "此設備(本地網路)", + "originRemote": "遠端伺服器", + "bannerReconnect": "重新連接", + "bannerMessage": "遠端同步需要重新驗證" }, "versionCheck": { "error": "版本檢查錯誤", "checkFailed": "檢查更新失敗", - "upToDate": "應用程式已更新至最新版本", + "upToDate": "應用程式已是最新版本", "currentVersion": "您正在執行版本 {{version}}", - "updateAvailable": "更新可用", - "newVersionAvailable": "新版本可用!您正在執行 {{current}},但 {{latest}} 可用。", - "betaVersion": "測試版", - "betaVersionDesc": "您正在運行 {{current}},它比最新的穩定版本 {{latest}} 更新。", - "releasedOn": "發佈日期: {{date}}", + "updateAvailable": "有可用更新", + "newVersionAvailable": "有新版本可用!您正在執行 {{current}},但 {{latest}} 已可用。", + "betaVersion": "Beta 版本", + "betaVersionDesc": "您正在執行 {{current}},此版本比最新的穩定版本 {{latest}} 還新。", + "releasedOn": "發佈於 {{date}}", "downloadUpdate": "下載更新", "checking": "正在檢查更新...", "checkUpdates": "檢查更新", @@ -408,648 +447,671 @@ "common": { "close": "關閉", "minimize": "最小化", - "online": "在線的", + "online": "線上", "offline": "離線", "unknown": "未知", "continue": "繼續", "maintenance": "維護", - "degraded": "降級", + "degraded": "已降級", "error": "錯誤", "warning": "警告", - "unsavedChanges": "未儲存的更改", - "dismiss": "解僱", + "unsavedChanges": "未儲存的變更", + "dismiss": "忽略", "loading": "載入中...", - "optional": "選修的", - "connect": "連接", + "optional": "選用", + "connect": "連線", "copied": "已複製", - "connecting": "正在連接...", - "updateAvailable": "更新可用", - "appName": "特米克斯", + "connecting": "連線中...", + "updateAvailable": "有可用更新", + "appName": "Termix", "openInNewTab": "在新分頁中開啟", - "noReleases": "無發布", - "updatesAndReleases": "更新與發布", - "newVersionAvailable": "新版本({{version}})可用。", - "failedToFetchUpdateInfo": "取得更新資訊失敗", - "preRelease": "預發布", - "noReleasesFound": "未找到任何版本。", + "noReleases": "無發佈版本", + "updatesAndReleases": "更新與發佈", + "newVersionAvailable": "有新版本 ({{version}}) 可用。", + "failedToFetchUpdateInfo": "無法取得更新資訊", + "preRelease": "預先發行版", + "noReleasesFound": "找不到任何發行版。", "cancel": "取消", "username": "使用者名稱", "login": "登入", - "logout": "註銷", - "register": "登記", + "logout": "登出", + "register": "註冊", "password": "密碼", "confirmPassword": "確認密碼", - "back": "後退", - "save": "節省", - "saving": "儲存...", + "back": "返回", + "save": "儲存", + "saving": "儲存中...", "delete": "刪除", "rename": "重新命名", "edit": "編輯", - "add": "添加", + "add": "新增", "confirm": "確認", - "no": "不", - "or": "或者", - "next": "下一個", - "previous": "以前的", + "no": "否", + "or": "或", + "next": "下一步", + "previous": "上一步", "refresh": "重新整理", "language": "語言", - "checking": "檢查...", + "checking": "檢查中...", "checkingDatabase": "正在檢查資料庫連線...", - "checkingAuthentication": "正在檢查身份驗證...", + "checkingAuthentication": "正在檢查驗證...", "backendReconnected": "伺服器連線已恢復", - "connectionDegraded": "伺服器連線遺失,正在恢復…", + "connectionDegraded": "伺服器連線中斷,正在恢復…", "reload": "重新載入", - "remove": "消除", - "create": "創造", + "remove": "移除", + "create": "建立", "update": "更新", "copy": "複製", "copyFailed": "複製到剪貼簿失敗", "maximize": "最大化", - "restore": "恢復", - "of": "的", + "restore": "還原", + "of": "/", "saved": "已儲存", "deleted": "已刪除", "deleteFailed": "刪除失敗", - "saveFailed": "保存失敗", - "required": "必需的" + "saveFailed": "儲存失敗", + "required": "必填" }, "nav": { - "home": "家", - "terminal": "終端", + "home": "首頁", + "terminal": "終端機", "docker": "Docker", "tunnels": "隧道", - "fileManager": "文件管理器", + "fileManager": "檔案管理員", "serverStats": "主機指標", "hostMetrics": "主機指標", - "admin": "行政", + "admin": "管理", "termixId": "ID", - "userProfile": "用戶個人資料", + "userProfile": "使用者設定", "splitScreen": "分割畫面", - "confirmClose": "關閉此會話?", + "confirmClose": "關閉此使用中的工作階段?", "close": "關閉", "cancel": "取消", - "sshManager": "SSH 管理器", - "cannotSplitTab": "無法拆分此標籤頁", + "sshManager": "SSH 管理員", + "cannotSplitTab": "無法分割此分頁", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", "copyPassword": "複製密碼", - "copySudoPassword": "複製 sudo 密碼", + "copySudoPassword": "複製 Sudo 密碼", "passwordCopied": "密碼已複製到剪貼簿", - "noPasswordAvailable": "沒有可用密碼", + "noPasswordAvailable": "無可用的密碼", "failedToCopyPassword": "複製密碼失敗", - "refreshTab": "刷新連接", - "renameTab": "重新命名選項卡", - "openFileManager": "開啟文件管理器", + "refreshTab": "重新整理連線", + "renameTab": "重新命名分頁", + "openFileManager": "開啟檔案管理員", "dashboard": "儀表板", - "networkGraph": "網路圖", - "tmuxMonitor": "Tmux 監視器", + "networkGraph": "網路圖表", + "tmuxMonitor": "Tmux 監控器", "homepage": "首頁", - "quickConnect": "快速連接", + "quickConnect": "快速連線", "sshTools": "SSH 工具", - "history": "歷史", - "sessionLogs": "會話日誌", - "hosts": "主持人", - "snippets": "片段", - "hostManager": "主機管理器", - "credentials": "證書", - "connections": "連接", - "alerts": "警報", - "serial": "序號", - "roleAdministrator": "行政人員", + "history": "歷史記錄", + "sessionLogs": "工作階段記錄", + "hosts": "主機", + "snippets": "程式碼片段", + "hostManager": "主機管理員", + "credentials": "憑證", + "connections": "連線", + "alerts": "警示", + "serial": "序列", + "roleAdministrator": "管理員", "roleUser": "使用者" }, "hosts": { - "hosts": "主持人", - "noHosts": "無 SSH 主機", + "hosts": "主機", + "noHosts": "尚無 SSH 主機", "retry": "重試", "refresh": "重新整理", - "optional": "選修的", - "downloadSample": "下載範例", - "failedToDeleteHost": "刪除失敗 {{name}}", - "importSkipExisting": "導入(跳過現有)", - "importSSHConfig": "從 SSH 配置導入", - "connectionDetails": "連線詳情", + "optional": "選用", + "downloadSample": "下載範本", + "failedToDeleteHost": "無法刪除 {{name}}", + "importSkipExisting": "匯入(略過現有項目)", + "importSSHConfig": "從 SSH 設定匯入", + "connectionDetails": "連線詳細資料", "ssh": "SSH", "telnet": "Telnet", "remoteDesktop": "遠端桌面", - "port": "港口", + "port": "連接埠", "username": "使用者名稱", "folder": "資料夾", "tags": "標籤", - "pin": "別針", + "pin": "釘選", "addHost": "新增主機", "editHost": "編輯主機", "cloneHost": "複製主機", - "enableTerminal": "啟用終端", - "enableTunnel": "啟用隧道", - "enableFileManager": "啟用文件管理器", + "enableTerminal": "啟用終端機", + "enableTunnel": "啟用通道", + "enableFileManager": "啟用檔案管理員", "enableDocker": "啟用 Docker", "defaultPath": "預設路徑", - "connection": "聯繫", + "connection": "連線", "upload": "上傳", "authentication": "驗證", "password": "密碼", - "key": "鑰匙", + "key": "金鑰", "credential": "憑證", - "none": "沒有任何", - "sshPrivateKey": "SSH 私鑰", - "keyType": "關鍵類型", - "uploadFile": "上傳文件", - "tabGeneral": "一般的", + "none": "無", + "sshPrivateKey": "SSH 私密金鑰", + "keyType": "金鑰類型", + "uploadFile": "上傳檔案", + "tabGeneral": "一般", "tabSsh": "SSH", - "tabTerminal": "終端", - "tabRdp": "遠端桌面協定", + "tabTerminal": "終端機", + "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "隧道", + "tabTunnels": "通道", "tabDocker": "Docker", - "tabFiles": "文件", + "tabFiles": "檔案", "tabStats": "主機指標", "tabHostMetrics": "主機指標", "tabTelnet": "Telnet", "tabSharing": "分享", "tabAuthentication": "驗證", - "terminal": "終端", - "tunnel": "隧道", - "fileManager": "文件管理器", + "terminal": "終端機", + "tunnel": "通道", + "fileManager": "檔案管理員", "serverStats": "主機指標", - "status": "地位", - "folderRenamed": "資料夾「{{oldName}}」已成功重新命名為「{{newName}}」。", + "status": "狀態", + "folderRenamed": "資料夾「{{oldName}}」已成功重新命名為「{{newName}}」", "failedToRenameFolder": "重新命名資料夾失敗", - "movedToFolder": "已將 {{count}} 主機移至“{{folder}}”", + "movedToFolder": "已將 {{count}} 台主機移至 \"{{folder}}\"", "editHostTooltip": "編輯主機", "statusChecks": "狀態檢查", "metricsCollection": "指標收集", "metricsInterval": "指標收集間隔", - "metricsIntervalDesc": "伺服器統計資訊收集頻率(5秒-1小時)", + "metricsIntervalDesc": "收集伺服器統計資料的頻率 (5秒 - 1小時)", "behavior": "行為", "themePreview": "主題預覽", "theme": "主題", - "fontFamily": "字體系列", - "fontSize": "字體大小", + "fontFamily": "字型系列", + "fontSize": "字型大小", "letterSpacing": "字母間距", "lineHeight": "行高", - "cursorStyle": "遊標樣式", - "cursorBlink": "遊標閃爍", - "scrollbackBuffer": "回滾緩衝區", - "bellStyle": "貝爾風格", - "rightClickSelectsWord": "右鍵選擇 Word", - "fastScrollModifier": "快速滾動修飾符", - "fastScrollSensitivity": "快速滾動靈敏度", - "sshAgentForwarding": "SSH代理轉發", - "backspaceMode": "退格模式", - "startupSnippet": "啟動片段", - "selectSnippet": "選擇片段", - "forceKeyboardInteractive": "強制鍵盤交互", - "overrideCredentialUsername": "覆蓋憑證使用者名稱", - "overrideCredentialUsernameDesc": "請使用上面指定的用戶名,而不是憑證中的用戶名。", - "oidcUsernameHint": "使用 $oidc.preferred_username 取代您的 OIDC 登入名稱。", - "jumpHostChain": "跳轉宿主鏈", - "portKnocking": "敲擊", + "cursorStyle": "游標樣式", + "cursorBlink": "游標閃爍", + "scrollbackBuffer": "回捲緩衝區", + "bellStyle": "鈴聲樣式", + "rightClickSelectsWord": "按右鍵選取單字", + "fastScrollModifier": "快速捲動修飾鍵", + "fastScrollSensitivity": "快速捲動靈敏度", + "sshAgentForwarding": "SSH 代理轉發", + "backspaceMode": "退格鍵模式", + "startupSnippet": "啟動程式碼片段", + "selectSnippet": "選取片段", + "forceKeyboardInteractive": "強制鍵盤互動式驗證", + "overrideCredentialUsername": "覆寫憑證使用者名稱", + "overrideCredentialUsernameDesc": "使用上方指定的使用者名稱,而非憑證中的使用者名稱", + "oidcUsernameHint": "使用 $oidc.preferred_username 來替換您的 OIDC 登入名稱。", + "tailscaleUsernameHint": "這必須是您在 Tailscale 身分中被授予的 Unix 用戶,但不一定是 root 用戶。", + "jumpHostChain": "跳板主機鏈", + "portKnocking": "連接埠敲門", "addKnock": "新增連接埠", "addProxyNode": "新增節點", "proxyNode": "代理節點", "proxyType": "代理類型", - "quickActions": "快速操作", - "sudoPasswordAutoFill": "Sudo 密碼自動填充", + "quickActions": "快速動作", + "sudoPasswordAutoFill": "Sudo 密碼自動填寫", "sudoPassword": "Sudo 密碼", - "keepaliveInterval": "保持連線間隔(毫秒)", + "keepaliveInterval": "保持連線間隔 (毫秒)", "moshCommand": "MOSH 指令", "environmentVariables": "環境變數", "addVariable": "新增變數", "docker": "Docker", - "copyTerminalUrl": "複製終端 URL", - "copyFileManagerUrl": "複製文件管理器 URL", - "copyRemoteDesktopUrl": "複製遠端桌面 URL", - "failedToConnect": "連線控制台失敗", - "connect": "連接", - "disconnect": "斷開", - "start": "開始", + "copyTerminalUrl": "複製終端機網址", + "copyFileManagerUrl": "複製檔案管理員網址", + "copyRemoteDesktopUrl": "複製遠端桌面網址", + "failedToConnect": "無法連線至主控台", + "connect": "連線", + "disconnect": "中斷連線", + "start": "啟動", "enableStatusCheck": "啟用狀態檢查", "enableMetrics": "啟用指標", - "bulkUpdateFailed": "批量更新失敗", + "bulkUpdateFailed": "批次更新失敗", "selectAll": "全選", - "deselectAll": "取消選擇所有", - "protocols": "協定", - "secureShell": "安全外殼", + "deselectAll": "取消全選", + "protocols": "通訊協定", + "secureShell": "安全殼層", "virtualNetwork": "虛擬網路", - "unencryptedShell": "未加密外殼", - "addressIp": "位址/IP", - "friendlyName": "友善名稱", - "macAddress": "MAC位址", + "unencryptedShell": "未加密殼層", + "addressIp": "位址 / IP", + "friendlyName": "易記名稱", + "macAddress": "MAC 位址", "wolBroadcastAddress": "WoL 廣播位址", - "wolBroadcastAddressDesc": "可選的定向廣播,適用於 Docker/路由網路(例如 192.168.1.255)。留空則使用 255.255.255.255。", - "folderAndAdvanced": "資料夾和進階功能", + "wolBroadcastAddressDesc": "選用的定向廣播,用於 Docker/路由網路 (例如 192.168.1.255)。留空則使用 255.255.255.255。", + "folderAndAdvanced": "資料夾與進階", "privateNotes": "私人筆記", - "privateNotesPlaceholder": "關於此伺服器的詳細資訊…", + "privateNotesPlaceholder": "此伺服器的詳細資訊...", "pinToTop": "置頂", - "pinToTopDesc": "始終將此主機顯示在清單頂部", - "portKnockingSequence": "連接埠敲擊順序", - "addKnockBtn": "新增敲擊", - "noPortKnocking": "未配置連接埠敲門。", - "knockPort": "敲擊港", - "protocol": "協定", - "delayAfterMs": "延遲時間(毫秒)", + "pinToTopDesc": "永遠在清單頂端顯示此主機", + "portKnockingSequence": "連接埠敲門序列", + "addKnockBtn": "新增敲門", + "noPortKnocking": "未設定連接埠敲門。", + "knockPort": "敲門連接埠", + "protocol": "通訊協定", + "delayAfterMs": "延遲 (毫秒)", "useSocks5Proxy": "使用 SOCKS5 代理", - "useSocks5ProxyDesc": "透過代理伺服器路由連接", + "useSocks5ProxyDesc": "透過代理伺服器路由連線", + "connectionOrigin": "連接來源", + "connectionOriginDesc": "此主機 SSH 連線的來源位置。會覆寫桌面應用程式的全域預設值。", + "connectionOriginDefault": "使用預設值", + "connectionOriginLocal": "此設備(本地網路)", + "connectionOriginRemote": "遠端伺服器", "proxyHost": "代理主機", - "proxyPort": "代理端口", - "proxyUsername": "代理用戶名", + "proxyPort": "代理連接埠", + "proxyUsername": "代理使用者名稱", "proxyPassword": "代理密碼", - "proxySingleMode": "單代理", + "proxySingleMode": "單一代理", "proxyChainMode": "代理鏈", - "you": "你", - "jumpHostChainLabel": "跳轉宿主鏈", - "addJumpBtn": "添加跳轉", - "noJumpHosts": "未配置跳轉主機。", - "selectAServer": "選擇伺服器...", - "sshPort": "SSH埠", - "authMethod": "身份驗證方法", - "storedCredential": "已儲存憑證", - "selectACredential": "選擇憑證...", + "you": "您", + "jumpHostChainLabel": "跳板主機鏈", + "addJumpBtn": "新增跳板", + "noJumpHosts": "未設定跳板主機。", + "selectAServer": "選取伺服器...", + "sshPort": "SSH 連接埠", + "authMethod": "驗證方式", + "storedCredential": "儲存的憑證", + "selectACredential": "選取憑證...", "vaultProfile": "Vault 簽署者設定檔", - "selectAVaultProfile": "選擇 Vault 設定檔...", - "vaultProfileHint": "設定來自共用設定檔;連線後,您將透過 OIDC 登入 Vault。不會儲存任何密鑰。", - "vaultNewProfile": "新個人資料", + "selectAVaultProfile": "選取 Vault 設定檔...", + "vaultProfileHint": "設定來自共用設定檔;連線時您將透過 OIDC 登入 Vault。不會儲存任何機密。", + "vaultNewProfile": "新增設定檔", "vaultManageProfiles": "管理 Vault 設定檔", - "vaultAddrLabel": "保險庫地址", + "vaultAddrLabel": "Vault 位址", "vaultNamespaceLabel": "命名空間", "vaultOidcMountLabel": "OIDC 驗證掛載", "vaultOidcRoleLabel": "OIDC 角色", - "vaultSshMountLabel": "SSH 金鑰掛載", - "vaultSshRoleLabel": "SSH簽名者角色", - "vaultValidPrincipalsLabel": "有效校長", - "vaultKeyTypeLabel": "臨時密鑰類型", - "vaultSharedLabel": "與所有用戶分享", - "vaultCreateProfile": "建立個人資料", - "vaultProfileCreated": "已建立 Vault 設定檔", + "vaultSshMountLabel": "SSH 機密掛載", + "vaultSshRoleLabel": "SSH 簽署者角色", + "vaultValidPrincipalsLabel": "有效主體", + "vaultKeyTypeLabel": "暫時金鑰類型", + "vaultSharedLabel": "與所有使用者共用", + "vaultCreateProfile": "建立設定檔", + "vaultProfileCreated": "Vault 設定檔已建立", "vaultProfileSaved": "Vault 設定檔已儲存", - "vaultProfileDeleted": "Vault 個人資料已刪除", - "vaultProfileSaveFailed": "儲存 Vault 設定檔失敗", - "vaultProfileDeleteFailed": "刪除 Vault 設定檔失敗", - "vaultSaveProfile": "儲存個人資料", - "vaultProfileValidationError": "姓名、Vault 地址和 SSH 簽署者角色是必要的。", - "vaultNoProfiles": "目前還沒有 Vault 設定檔。", - "vaultSharedBadge": "共享", - "keyTypeLabel": "關鍵類型", + "vaultProfileDeleted": "Vault 設定檔已刪除", + "vaultProfileSaveFailed": "無法儲存 Vault 設定檔", + "vaultProfileDeleteFailed": "無法刪除 Vault 設定檔", + "vaultSaveProfile": "儲存設定檔", + "vaultProfileValidationError": "名稱、Vault 位址和 SSH 簽署者角色為必填", + "vaultNoProfiles": "尚無 Vault 設定檔。", + "vaultSharedBadge": "已共用", + "keyTypeLabel": "金鑰類型", "keyTypeAuto": "自動偵測", "keyPasteTab": "貼上", "keyUploadTab": "上傳", - "keyFileLoaded": "密鑰檔案已加載", - "keyUploadClick": "點選上傳 .pem / .key / .ppk 文件", - "clearKey": "清除密鑰", - "keySaved": "SSH金鑰已儲存", - "keyReplaceNotice": "請在下方貼上新密鑰以替換它", - "keyPassphraseSaved": "密碼已儲存,輸入即可更改", - "replaceKey": "更換鑰匙", - "docsLink": "查看文件", + "keyFileLoaded": "金鑰檔案已載入", + "keyUploadClick": "點擊以上傳 .pem / .key / .ppk", + "clearKey": "清除金鑰", + "keySaved": "SSH 金鑰已儲存", + "keyReplaceNotice": "在下方貼上新金鑰以取代", + "keyPassphraseSaved": "密碼片語已儲存,輸入以變更", + "replaceKey": "取代金鑰", + "docsLink": "檢視文件", "opksshLabel": "OPKSSH", - "opksshDesc": "使用您的身分提供者而非密碼或金鑰登入此主機。需要在伺服器上設定 OPKSSH。", - "warpgateLabel": "傳送門", - "warpgateDesc": "此主機透過 Warpgate SSH 代理程式連線。 Termix 會在驗證後自動處理基於瀏覽器的核准流程。", - "agentLabel": "SSH代理", - "agentDesc": "使用運行在 Termix 伺服器上的 SSH 代理程式(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)進行身份驗證。該代理程式必須運行在 Termix 所在的機器上。", - "agentSocketPathLabel": "代理套接字路徑", - "agentSocketPathPlaceholder": "留空則使用 SSH_AUTH_SOCK", - "agentSocketPathHint": "留空以從 SSH_AUTH_SOCK 環境變數自動檢測,或輸入自訂套接字路徑(例如 /run/user/1000/gnupg/S.gpg-agent.ssh)。", - "tailscaleDeviceSelect": "選擇 Tailscale 設備", - "tailscaleDeviceSelectPlaceholder": "選擇設備…", - "tailscaleNoApiKey": "未配置 Tailscale API 金鑰。請在管理設定中新增密鑰以啟用裝置發現功能。", - "tailscaleDocsLink": "查看文件", - "tailscaleLoadingDevices": "正在加載設備…", - "tailscaleNoDevices": "您的尾網中未發現任何裝置。", - "tailscaleDeviceAutoFill": "選擇設備後,主機 IP 位址將自動填入。", - "forceKeyboardInteractiveLabel": "強制鍵盤交互", - "forceKeyboardInteractiveShortDesc": "即使鑰匙在手,也強製手動輸入密碼。", - "allowLegacyAlgorithmsLabel": "允許使用舊版演算法", - "allowLegacyAlgorithmsDesc": "為無法升級的舊設備連線啟用已棄用的 SSH 演算法(ssh-dss、ssh-rsa、diffie-hellman-group1-sha1、hmac-md5、3des-cbc)。", - "insecure": "缺乏安全感", - "terminalAppearance": "終端外觀", - "colorTheme": "顏色主題", - "fontFamilyLabel": "字體系列", - "fontSizeLabel": "字體大小", - "cursorStyleLabel": "遊標樣式", - "letterSpacingPx": "字母間距(像素)", + "opksshDesc": "使用您的識別提供者而非密碼或金鑰來登入此主機。需要在伺服器上設定 OPKSSH。", + "warpgateLabel": "Warpgate 閘道", + "warpgateDesc": "此主機透過 Warpgate SSH 代理連線。Termix 會在驗證後自動處理瀏覽器核准流程。", + "agentLabel": "SSH 代理", + "agentDesc": "使用在 Termix 伺服器上執行的 SSH 代理進行驗證(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)。代理必須在託管 Termix 的機器上執行。", + "agentSocketPathLabel": "代理 Socket 路徑", + "agentSocketPathPlaceholder": "留空以使用 SSH_AUTH_SOCK", + "agentSocketPathHint": "留空以從 SSH_AUTH_SOCK 環境變數自動偵測,或輸入自訂 socket 路徑(例如 /run/user/1000/gnupg/S.gpg-agent.ssh)。", + "tailscaleDeviceSelect": "選取 Tailscale 裝置", + "tailscaleDeviceSelectPlaceholder": "選取裝置...", + "tailscaleNoApiKey": "未設定 Tailscale API 金鑰。請在管理設定中新增一個以啟用裝置探索。", + "tailscaleDocsLink": "檢視文件", + "tailscaleLoadingDevices": "正在載入裝置...", + "tailscaleNoDevices": "在您的 tailnet 中找不到裝置。", + "tailscaleDeviceAutoFill": "選取裝置將自動填入主機 IP 位址。", + "forceKeyboardInteractiveLabel": "強制鍵盤互動", + "forceKeyboardInteractiveShortDesc": "即使存在金鑰仍強制手動輸入密碼", + "allowLegacyAlgorithmsLabel": "允許舊版演算法", + "allowLegacyAlgorithmsDesc": "為連線至無法升級的舊裝置啟用已棄用的 SSH 演算法 (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc)。", + "insecure": "不安全", + "terminalAppearance": "終端機外觀", + "colorTheme": "色彩主題", + "fontFamilyLabel": "字型系列", + "fontSizeLabel": "字型大小", + "cursorStyleLabel": "游標樣式", + "letterSpacingPx": "字元間距 (px)", "lineHeightLabel": "行高", - "bellStyleLabel": "貝爾風格", - "backspaceModeLabel": "退格模式", - "cursorBlinking": "遊標閃爍", - "cursorBlinkingDesc": "啟用終端遊標閃爍動畫", - "rightClickSelectsWordLabel": "右鍵選擇 Word", - "rightClickSelectsWordShortDesc": "右鍵單擊,選擇遊標下的單字。", - "backgroundImageLabel": "背景圖片網址", - "backgroundImageDesc": "終端機背景圖片的可選 URL", - "backgroundImageOpacityLabel": "背景圖像不透明度", - "customThemeColors": "自訂顏色", + "bellStyleLabel": "鈴聲樣式", + "backspaceModeLabel": "退格鍵模式", + "cursorBlinking": "游標閃爍", + "cursorBlinkingDesc": "啟用終端機游標的閃爍動畫", + "rightClickSelectsWordLabel": "右鍵選取單字", + "rightClickSelectsWordShortDesc": "右鍵點擊時選取游標下的單字", + "backgroundImageLabel": "背景圖片 URL", + "backgroundImageDesc": "選用的終端機背景圖片 URL", + "backgroundImageOpacityLabel": "背景圖片不透明度", + "customThemeColors": "自訂色彩", "customThemeBackground": "背景", "customThemeForeground": "前景", "customThemeCursor": "游標", - "customThemeCursorAccent": "遊標重音", - "customThemeSelection": "選拔", - "customThemeAnsiColors": "ANSI顏色", - "customThemeBlack": "黑色的", - "customThemeRed": "紅色的", - "customThemeGreen": "綠色的", - "customThemeYellow": "黃色的", - "customThemeBlue": "藍色的", - "customThemeMagenta": "品紅", + "customThemeCursorAccent": "游標強調色", + "customThemeSelection": "選取範圍", + "customThemeAnsiColors": "ANSI 色彩", + "customThemeBlack": "黑色", + "customThemeRed": "紅色", + "customThemeGreen": "綠色", + "customThemeYellow": "黃色", + "customThemeBlue": "藍色", + "customThemeMagenta": "洋紅色", "customThemeCyan": "青色", - "customThemeWhite": "白色的", + "customThemeWhite": "白色", "customThemeBrightBlack": "亮黑色", - "customThemeBrightRed": "鮮紅色", + "customThemeBrightRed": "亮紅色", "customThemeBrightGreen": "亮綠色", "customThemeBrightYellow": "亮黃色", "customThemeBrightBlue": "亮藍色", "customThemeBrightMagenta": "亮洋紅色", "customThemeBrightCyan": "亮青色", "customThemeBrightWhite": "亮白色", - "customThemeResetTooltip": "恢復預設設定", - "syntaxHighlightingLabel": "語法高亮", - "syntaxHighlightingDesc": "對終端輸出(錯誤訊息、路徑、IP位址、時間戳記)進行著色", - "syntaxHighlightingCategories": "重點類別", - "syntaxHighlightingCategoriesDesc": "選擇要著色的內容類型", - "syntaxCategoryLogLevels": "日誌等級", - "syntaxCategoryLogLevelsDesc": "錯誤、警告、訊息、調試、致命", - "syntaxCategoryPaths": "文件路徑", - "syntaxCategoryPathsDesc": "/usr/share/doc,~/file.txt", - "syntaxCategoryTimestamps": "時間戳", - "syntaxCategoryTimestampsDesc": "[12:34:56],2024-01-15", - "syntaxCategoryIpAddresses": "IP位址", - "syntaxCategoryIpAddressesDesc": "192.168.1.1,10.0.0.1:8080", - "syntaxCategoryUrls": "網址", + "customThemeResetTooltip": "重設為預設值", + "savedThemesLabel": "已儲存的主題", + "saveAsGlobalTheme": "另存為全域主題", + "saveGlobalThemeNamePrompt": "請為此主題輸入名稱", + "saveGlobalThemeSuccess": "主題已儲存", + "saveGlobalThemeError": "主題保存失敗", + "applyGlobalThemeTooltip": "應用此主題", + "deleteGlobalThemeTooltip": "刪除此主題", + "noSavedThemes": "尚未儲存任何主題", + "syntaxHighlightingLabel": "語法突顯", + "syntaxHighlightingDesc": "為終端機輸出上色(錯誤、路徑、IP、時間戳記)", + "syntaxHighlightingCategories": "突顯類別", + "syntaxHighlightingCategoriesDesc": "選擇要上色的內容類型", + "syntaxCategoryLogLevels": "日誌層級", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "檔案路徑", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "時間戳記", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP 位址", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URL", "syntaxCategoryUrlsDesc": "https://example.com", - "syntaxCategoryNumbers": "標籤數字", - "syntaxCategoryNumbersDesc": "連接埠 8080,退出代碼 1,狀態碼 404", - "behaviorAndAdvanced": "行為與高階", + "syntaxCategoryNumbers": "帶標籤的數字", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "行為與進階", "scrollbackBufferLabel": "回滾緩衝區", - "scrollbackMaxLines": "歷史記錄中保留的最大行數", - "sshAgentForwardingLabel": "SSH代理轉發", - "sshAgentForwardingShortDesc": "將您的本機 SSH 金鑰傳遞給此主機", + "scrollbackMaxLines": "歷史記錄保留的最大行數", + "sshAgentForwardingLabel": "SSH 代理轉發", + "sshAgentForwardingShortDesc": "將您的本機 SSH 金鑰傳遞至此主機", "useSSHTitleLabel": "使用 SSH 視窗標題", - "useSSHTitleDesc": "將標籤頁標題更新為 shell 視窗標題,而不是主機名稱。", + "useSSHTitleDesc": "從 shell 視窗標題更新頁籤標題,而非主機名稱", "enableAutoMosh": "啟用自動 Mosh", - "enableAutoMoshDesc": "如果可用,優先選擇 Mosh 而不是 SSH。", + "enableAutoMoshDesc": "如果可用,優先使用 Mosh 而非 SSH", "enableAutoTmux": "啟用自動 Tmux", - "enableAutoTmuxDesc": "自動啟動或連線到 tmux 會話", - "enableSessionLogging": "會話日誌", - "enableSessionLoggingDesc": "記錄終端會話輸出以供後續查看", + "enableAutoTmuxDesc": "自動啟動或附加至 tmux 會話", + "enableSessionLogging": "工作階段記錄", + "enableSessionLoggingDesc": "記錄終端機工作階段輸出以供日後檢視", + "allowSessionSharing": "允許會話共享", + "allowSessionSharingDesc": "允許透過連結或其他方式分享此主機上的即時會話。", "enableCommandHistory": "命令歷史記錄", - "enableCommandHistoryDesc": "記錄此終端機中執行的命令,以便進行歷史記錄和自動補全。", + "enableCommandHistoryDesc": "記錄在此終端機中執行的命令,用於歷史記錄與自動完成", "linkClickBehaviorLabel": "連結點擊行為", - "linkClickBehaviorDesc": "控制點擊終端中的連結時發生的情況。使用“預設”可遵循應用程式層級的設定。", + "linkClickBehaviorDesc": "控制在終端機中點擊連結時的行為。使用「預設」將遵循應用程式級別的設定。", "linkClickBehaviorDefault": "預設(遵循應用程式設定)", "linkClickBehaviorConfirm": "顯示彈出視窗以開啟或複製", - "linkClickBehaviorDirect": "直接打開", - "sudoPasswordAutoFillLabel": "Sudo 密碼自動填充", - "sudoPasswordAutoFillShortDesc": "提示時自動提供 sudo 密碼", - "sudoPasswordAutoFillDesc": "儲存 sudo 密碼,以便主機指標、終端提示符號和其他程式可以自動執行特權命令。", + "linkClickBehaviorDirect": "直接開啟", + "sudoPasswordAutoFillLabel": "Sudo 密碼自動填入", + "sudoPasswordAutoFillShortDesc": "在提示時自動提供 sudo 密碼", + "sudoPasswordAutoFillDesc": "儲存 sudo 密碼,以便主機指標、終端機提示及其他功能能自動執行特權命令。", "sudoPasswordLabel": "Sudo 密碼", "environmentVariablesLabel": "環境變數", "addVariableBtn": "新增變數", - "noEnvVars": "未配置任何環境變數。", - "fastScrollModifierLabel": "快速滾動修飾符", - "fastScrollSensitivityLabel": "快速滾動靈敏度", - "moshCommandLabel": "Mosh Command", - "startupSnippetLabel": "啟動片段", + "noEnvVars": "未設定任何環境變數。", + "fastScrollModifierLabel": "快速捲動修飾鍵", + "fastScrollSensitivityLabel": "快速捲動靈敏度", + "moshCommandLabel": "Mosh 命令", + "startupSnippetLabel": "啟動程式碼片段", "keepaliveIntervalLabel": "保持連線間隔(秒)", - "maxKeepaliveMisses": "最大保命失誤", - "tunnelSettings": "隧道設定", - "enableTunneling": "啟用隧道", - "enableTunnelingDesc": "為此主機啟用 SSH 隧道功能", - "serverTunnelsSection": "伺服器隧道", - "addTunnelBtn": "添加隧道", - "noTunnelsConfigured": "未配置隧道。", - "tunnelLabel": "隧道 {{number}}", - "tunnelType": "隧道類型", - "tunnelModeLocalDesc": "將本機連接埠轉送到遠端伺服器(或可從遠端伺服器存取的主機)上的連接埠。", - "tunnelModeRemoteDesc": "將遠端伺服器上的連接埠轉送到您電腦上的本機連接埠。", - "tunnelModeDynamicDesc": "在本機連接埠上建立 SOCKS5 代理,用於動態連接埠轉送。", - "sameHost": "此主機(直接隧道)", + "maxKeepaliveMisses": "最大保持連線遺失次數", + "tunnelSettings": "通道設定", + "enableTunneling": "啟用通道", + "enableTunnelingDesc": "為此主機啟用 SSH 通道功能", + "serverTunnelsSection": "伺服器通道", + "addTunnelBtn": "新增通道", + "noTunnelsConfigured": "未設定任何通道。", + "tunnelLabel": "通道 {{number}}", + "tunnelType": "通道類型", + "tunnelModeLocalDesc": "將本機連接埠轉送至遠端伺服器的連接埠(或可從其連線的主機)。", + "tunnelModeRemoteDesc": "將遠端伺服器的連接埠轉送回本機的連接埠。", + "tunnelModeDynamicDesc": "在本機連接埠上建立 SOCKS5 代理以進行動態連接埠轉送。", + "sameHost": "此主機(直接通道)", "endpointHost": "端點主機", - "endpointHostPlaceholder": "同一主機、SSH 主機或可存取的主機/IP", - "endpointPort": "端點埠", + "endpointHostPlaceholder": "同一主機、SSH 主機或可連線的主機/IP", + "endpointPort": "端點連接埠", "bindHost": "綁定主機", "sourcePort": "來源連接埠", "maxRetries": "最大重試次數", "retryIntervalS": "重試間隔(秒)", "autoStartLabel": "自動啟動", - "autoStartDesc": "主機載入完畢後自動連線此隧道", - "tunnelConnecting": "連接隧道…", - "tunnelDisconnected": "隧道斷開", + "autoStartDesc": "載入主機時自動連線此通道", + "tunnelConnecting": "通道連線中…", + "tunnelDisconnected": "通道已中斷連線", "failedToConnectTunnel": "連線失敗", - "failedToDisconnectTunnel": "斷開連線失敗", - "dockerIntegration": "Docker 集成", + "failedToDisconnectTunnel": "中斷連線失敗", + "dockerIntegration": "Docker 整合", "enableDockerMonitor": "啟用 Docker", - "enableDockerMonitorDesc": "透過 Docker 監控和管理此主機上的容器", - "containerRuntime": "容器運轉時", - "containerRuntimeDesc": "選擇用於此主機上容器管理的 CLI", + "enableDockerMonitorDesc": "透過 Docker 監控及管理此主機上的容器", + "containerRuntime": "容器執行環境", + "containerRuntimeDesc": "選擇用於此主機容器管理的 CLI", "containerRuntimeDocker": "Docker", "containerRuntimePodman": "Podman", - "enableTmuxMonitor": "啟用 Tmux 監視器", - "enableTmuxMonitorDesc": "在 Tmux 監視器中顯示此主機,並將其 tmux 操作新增至側邊欄。", - "tabProxmox": "普羅克斯莫克斯", - "proxmoxIntegration": "Proxmox 集成", + "enableTmuxMonitor": "啟用 Tmux 監控器", + "enableTmuxMonitorDesc": "在 Tmux 監控器中顯示此主機,並將其 tmux 操作新增至側邊欄", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox 整合", "enableProxmox": "啟用 Proxmox", - "enableProxmoxDesc": "將此主機標記為 Proxmox 節點。啟用後,即可直接從此主機發現並匯入虛擬機器。", - "proxmoxDefaultAuthType": "預設身份驗證類型", - "proxmoxDefaultAuthTypeDesc": "套用於匯入的訪客主機的身份驗證方法。選擇與您連接訪客的方式相符的身份驗證類型。", + "enableProxmoxDesc": "將此主機標記為 Proxmox 節點。啟用直接從此主機探索及匯入客體的功能。", + "proxmoxDefaultAuthType": "預設驗證類型", + "proxmoxDefaultAuthTypeDesc": "套用至匯入客體主機的驗證方法。選擇與您連線至客體的方式相符的驗證類型。", "authTypePassword": "密碼", - "authTypeKey": "SSH金鑰", + "authTypeKey": "SSH 金鑰", "authTypeCredential": "憑證", "authTypeOpkssh": "OPKSSH", - "authTypeNone": "沒有任何", + "authTypeNone": "無", "proxmoxDefaultCredential": "預設憑證", - "proxmoxDefaultCredentialDesc": "當身份驗證類型設定為「憑證」時,匯入的訪客主機使用的憑證。", - "proxmoxWindowsDetection": "Windows/RDP 檢測", - "proxmoxWindowsDetectionDesc": "以逗號分隔的名稱模式,觸發 RDP 而不是 SSH(不區分大小寫)", + "proxmoxDefaultCredentialDesc": "當驗證類型設為憑證時,用於已匯入之訪客主機的憑證。", + "proxmoxWindowsDetection": "Windows / RDP 偵測", + "proxmoxWindowsDetectionDesc": "逗號分隔的名稱模式,符合時將觸發 RDP 而非 SSH(不分大小寫)", "proxmoxDockerDetection": "Docker 偵測", - "proxmoxDockerDetectionDesc": "逗號分隔的名稱模式使 Docker 能夠匹配虛擬機", - "proxmoxPreferredRanges": "首選 IP 位址範圍", - "proxmoxPreferredRangesDesc": "當訪客擁有多個介面時,用於選擇 IP 位址的優先順序以逗號分隔", - "proxmoxAutoSync": "自動同步訪客", - "proxmoxAutoSyncDesc": "定期發現此 Proxmox 節點,並在會話解鎖期間建立或更新匯入的客戶主機。", + "proxmoxDockerDetectionDesc": "逗號分隔的名稱模式,符合時將為對應的訪客主機啟用 Docker", + "proxmoxPreferredRanges": "偏好的 IP 範圍", + "proxmoxPreferredRangesDesc": "當訪客主機有多個網路介面時,以此逗號分隔的前綴清單依優先順序選取 IP", + "proxmoxAutoSync": "自動同步訪客主機", + "proxmoxAutoSyncDesc": "在您的工作階段解鎖時,定期探索此 Proxmox 節點並建立或更新已匯入的訪客主機。", "proxmoxSyncInterval": "同步間隔(分鐘)", - "proxmoxSyncIntervalDesc": "最短 5 分鐘。調度程式會跳過已鎖定的使用者會話。", - "proxmoxMarkMissing": "馬克缺席的客人", - "proxmoxMarkMissingDesc": "如果先前匯入的虛擬機器消失而不是被刪除,則新增 proxmox-missing 標籤。", + "proxmoxSyncIntervalDesc": "最少 5 分鐘。排程器會略過鎖定的使用者工作階段。", + "proxmoxMarkMissing": "標記遺失的訪客主機", + "proxmoxMarkMissingDesc": "當先前匯入的訪客主機消失時,新增 proxmox-missing 標籤而非將其刪除。", "proxmoxLastSync": "上次同步", "proxmoxLastSyncNever": "尚未同步", - "proxmoxLastSyncNoResult": "無結果詳情", - "proxmoxLastSyncSummary": "{{created}} 已創建, {{updated}} 已更新, {{markedMissing}} 缺失, {{skipped}} 已跳過", + "proxmoxLastSyncNoResult": "無結果詳細資料", + "proxmoxLastSyncSummary": "建立 {{created}},更新 {{updated}},遺失 {{markedMissing}},略過 {{skipped}}", "proxmoxLastSyncStatus": { "success": "成功", - "error": "失敗的", - "pending": "待辦的" + "error": "失敗", + "pending": "待處理" }, - "proxmoxDiscoverAction": "發現並導入 Proxmox 訪客", - "proxmoxImportTitle": "從 Proxmox 導入", - "proxmoxSelectHost": "選擇 Proxmox 主機…", - "proxmoxDiscover": "發現", - "proxmoxDiscovering": "發現…", - "proxmoxDiscoverGuests": "探索賓客", - "proxmoxGuestsSelected_one": "{{count}} 訪客 — {{selected}} 已選", - "proxmoxGuestsSelected_other": "{{count}} 賓客 — {{selected}} 已選", + "proxmoxDiscoverAction": "探索並匯入 Proxmox 訪客主機", + "proxmoxImportTitle": "從 Proxmox 匯入", + "proxmoxSelectHost": "選取 Proxmox 主機…", + "proxmoxDiscover": "探索", + "proxmoxDiscovering": "探索中…", + "proxmoxDiscoverGuests": "探索訪客主機", + "proxmoxGuestsSelected_one": "{{count}} 個訪客主機 — 已選取 {{selected}}", + "proxmoxGuestsSelected_other": "{{count}} 個訪客主機 — 已選取 {{selected}}", "proxmoxSelectAll": "全選", - "proxmoxDeselectAll": "取消選擇所有", - "proxmoxNoGuests": "此Proxmox節點上未發現虛擬機器。", - "proxmoxImportButton_one": "導入 {{count}} 主機", - "proxmoxImportButton_other": "導入 {{count}} 主機", - "proxmoxResultImported": "{{count}} 導入", - "proxmoxResultUpdated": "{{count}} 已更新", - "proxmoxResultFailed": "{{count}} 失敗", - "proxmoxImportComplete": "Proxmox 匯入完成: {{summary}}", - "proxmoxDiscoveryFailed": "發現失敗", - "proxmoxImportFailed": "導入失敗", - "enableFileManagerMonitor": "啟用文件管理器", - "enableFileManagerMonitorDesc": "透過 SFTP 瀏覽和管理此主機上的文件", - "scpLegacyLabel": "SCP 遺產模式", - "scpLegacyDesc": "對於不支援 SFTP 子系統的伺服器(例如嵌入式或最小 SSH 伺服器),請使用傳統檔案傳輸方式。", + "proxmoxDeselectAll": "取消全選", + "proxmoxNoGuests": "此 Proxmox 節點上找不到任何訪客主機。", + "proxmoxImportButton_one": "匯入 {{count}} 台主機", + "proxmoxImportButton_other": "匯入 {{count}} 台主機", + "proxmoxResultImported": "已匯入 {{count}} 台", + "proxmoxResultUpdated": "已更新 {{count}} 台", + "proxmoxResultFailed": "失敗 {{count}} 台", + "proxmoxResultSkippedNoIp": "{{count}} 已跳過(找不到 IP 位址)", + "proxmoxImportComplete": "Proxmox 匯入完成:{{summary}}", + "proxmoxDiscoveryFailed": "探索失敗", + "proxmoxImportFailed": "匯入失敗", + "enableFileManagerMonitor": "啟用檔案管理員", + "enableFileManagerMonitorDesc": "透過 SFTP 瀏覽並管理此主機上的檔案", + "scpLegacyLabel": "SCP 舊版模式", + "scpLegacyDesc": "對不支援 SFTP 子系統的伺服器(例如嵌入式或精簡型 SSH 伺服器)使用舊版檔案傳輸。", "defaultPathLabel": "預設路徑", - "fileManagerPathHint": "當此主機上的檔案管理器啟動時要開啟的目錄。", + "fileManagerPathHint": "啟動此主機的檔案管理員時開啟的目錄。", "statusChecksLabel": "狀態檢查", "enableStatusChecks": "啟用狀態檢查", - "enableStatusChecksDesc": "定期 ping 此主機以驗證其可用性", + "enableStatusChecksDesc": "定期 ping 此主機以驗證可用性", "useGlobalInterval": "使用全域間隔", - "useGlobalIntervalDesc": "使用伺服器範圍的狀態檢查間隔進行覆蓋", + "useGlobalIntervalDesc": "以伺服器範圍的狀態檢查間隔覆寫", "checkIntervalS": "檢查間隔(秒)", - "checkIntervalDesc": "每次連接 ping 之間的秒數", + "checkIntervalDesc": "每次連線 ping 之間的秒數", "metricsCollectionLabel": "指標收集", "enableMetricsLabel": "啟用指標", - "enableMetricsDesc": "從該主機收集 CPU、記憶體、磁碟和其他指標", + "enableMetricsDesc": "從此主機收集 CPU、記憶體、磁碟和其他指標", "useGlobalMetrics": "使用全域間隔", - "useGlobalMetricsDesc": "使用伺服器範圍的指標間隔進行覆蓋", + "useGlobalMetricsDesc": "以伺服器範圍的指標間隔覆寫", "metricsIntervalS": "指標間隔(秒)", - "metricsIntervalDesc2": "指標快照之間的秒數", - "visibleWidgets": "可見小部件", - "widgetsMovedToHostMetrics": "現在可以直接在「主機指標」標籤中新增、排列和調整卡片大小。開啟此主機的“主機指標”,然後使用“自訂”來選擇要顯示的卡片。", + "metricsIntervalDesc2": "每次指標快照之間的秒數", + "visibleWidgets": "可見的小工具", + "widgetsMovedToHostMetrics": "現在卡片可直接在主機指標分頁中新增、排列和調整大小。開啟此主機的主機指標,然後使用自訂功能選擇要顯示的卡片。", "cpuUsageLabel": "CPU 使用率", - "cpuUsageDesc": "CPU 使用率、平均負載、迷你圖", - "memoryLabel": "記憶體使用情況", - "memoryDesc": "記憶體使用情況、交換空間、緩存", - "storageLabel": "磁碟使用情況", - "storageDesc": "每個掛載點的磁碟使用情況", + "cpuUsageDesc": "CPU 百分比、平均負載、迷你走勢圖", + "memoryLabel": "記憶體使用量", + "memoryDesc": "RAM 使用量、交換空間、快取", + "storageLabel": "磁碟使用量", + "storageDesc": "每個掛載點的磁碟使用量", "networkLabel": "網路介面", - "networkDesc": "介面列表和頻寬", - "uptimeLabel": "正常運作時間", - "uptimeDesc": "系統運作時間和啟動時間", + "networkDesc": "介面清單與頻寬", + "uptimeLabel": "運作時間", + "uptimeDesc": "系統運作時間與開機時間", "systemInfoLabel": "系統資訊", "systemInfoDesc": "作業系統、核心、主機名稱、架構", - "recentLoginsLabel": "最近登入", - "recentLoginsDesc": "登入成功和失敗事件", - "topProcessesLabel": "頂級流程", - "topProcessesDesc": "PID、CPU%、記憶體%、指令", - "listeningPortsLabel": "監聽埠", - "listeningPortsDesc": "打開帶有進程和狀態的端口", + "recentLoginsLabel": "近期登入", + "recentLoginsDesc": "成功與失敗的登入事件", + "topProcessesLabel": "高資源程序", + "topProcessesDesc": "PID、CPU%、MEM%、指令", + "listeningPortsLabel": "監聽連接埠", + "listeningPortsDesc": "開啟的連接埠及其程序與狀態", "firewallLabel": "防火牆", "firewallDesc": "防火牆、AppArmor、SELinux 狀態", - "quickActionsLabel": "快速操作", - "quickActionsToolbar": "快速操作會以按鈕的形式顯示在主機指標工具列中,只需按一下即可執行指令。", - "noQuickActions": "目前暫無快速行動。", + "quickActionsLabel": "快速動作", + "quickActionsToolbar": "快速動作會以按鈕形式顯示在主機計量工具列上,可一鍵執行指令。", + "noQuickActions": "尚無快速動作。", "buttonLabel": "按鈕標籤", - "selectSnippetPlaceholder": "選擇片段…", - "addActionBtn": "新增操作", - "hostSharedSuccessfully": "主機共享成功", - "failedToShareHost": "共享主機失敗", - "accessRevoked": "存取權限已撤銷", - "failedToRevokeAccess": "撤銷存取權限失敗", + "selectSnippetPlaceholder": "選取程式碼片段...", + "addActionBtn": "新增動作", + "hostSharedSuccessfully": "主機分享成功", + "failedToShareHost": "分享主機失敗", + "accessRevoked": "存取權已撤銷", + "failedToRevokeAccess": "撤銷存取權失敗", "cancelBtn": "取消", - "savingBtn": "儲存...", + "savingBtn": "儲存中...", "addHostBtn": "新增主機", "hostUpdated": "主機已更新", "hostCreated": "主機已建立", "failedToSave": "儲存主機失敗", "credentialUpdated": "憑證已更新", - "credentialCreated": "已建立憑證", + "credentialCreated": "憑證已建立", "failedToSaveCredential": "儲存憑證失敗", "credentialNameRequired": "請輸入憑證名稱", - "credentialAuthRequired": "新增密碼、SSH金鑰或兩者都新增。", - "backToHosts": "返回主持人", + "credentialAuthRequired": "請新增密碼、SSH 金鑰或兩者皆新增", + "createCredentialFromHostBtn": "創建憑證", + "createCredentialFromHostTitle": "從主機建立憑證", + "createCredentialFromHostDesc": "建立一個可重複使用、可共用的憑證條目,其中預先填入了此主機的目前使用者名稱、密碼和/或 SSH 金鑰。", + "backToHosts": "返回主機", "backToCredentials": "返回憑證", - "pinned": "置頂", - "noHostsFound": "未找到主機", - "tryDifferentTerm": "換個說法", - "addFirstHost": "新增您的第一個主機即可開始", - "noCredentialsFound": "未找到憑證", + "pinned": "已釘選", + "noHostsFound": "找不到主機", + "tryDifferentTerm": "請嘗試其他搜尋條件", + "addFirstHost": "新增第一台主機以開始使用", + "noCredentialsFound": "找不到憑證", "addCredentialBtn": "新增憑證", "updateCredentialBtn": "更新憑證", - "features": "特徵", + "features": "功能", "noFolder": "(無資料夾)", "deleteSelected": "刪除", - "exitSelection": "出口選擇", - "importSkip": "導入(跳過現有)", - "importOverwrite": "導入(覆蓋)", - "collapseBtn": "坍塌", - "importExportBtn": "進出口", - "hostStatusesRefreshed": "主機狀態已刷新", - "failedToRefreshHosts": "刷新主機失敗", - "movedHostTo": "將 {{host}} 移到 \"{{folder}}\"", - "failedToMoveHost": "主機遷移失敗", - "folderRenamedTo": "資料夾已重新命名為“{{name}}”", - "deletedFolder": "已刪除資料夾“{{name}}”", - "failedToDeleteFolder": "刪除資料夾失敗", - "deleteAllInFolder": "刪除「{{name}}」中的所有主機?此操作無法撤銷。", - "folderPickerPlaceholder": "沒有資料夾", - "folderPickerSearch": "搜尋或建立(使用 / 表示子資料夾)…", - "folderPickerNone": "沒有資料夾", - "folderPickerCreate": "創建“{{path}}”", - "folderPickerEmpty": "沒有匹配的資料夾", - "newFolder": "新建資料夾", + "exitSelection": "退出選取", + "importSkip": "匯入(跳過已存在項目)", + "importOverwrite": "匯入(覆寫)", + "collapseBtn": "摺疊", + "importExportBtn": "匯入/匯出", + "hostStatusesRefreshed": "主機狀態已重新整理", + "failedToRefreshHosts": "無法重新整理主機", + "movedHostTo": "已將 {{host}} 移至「{{folder}}」", + "failedToMoveHost": "無法移動主機", + "folderRenamedTo": "資料夾已重新命名為「{{name}}」", + "deletedFolder": "已刪除資料夾「{{name}}」", + "failedToDeleteFolder": "無法刪除資料夾", + "deleteAllInFolder": "刪除「{{name}}」中的所有主機?此操作無法復原。", + "folderPickerPlaceholder": "無資料夾", + "folderPickerSearch": "搜尋或建立(用 / 建立子資料夾)…", + "folderPickerNone": "無資料夾", + "folderPickerCreate": "建立「{{path}}」", + "folderPickerEmpty": "無相符資料夾", + "newFolder": "新資料夾", "createFolderTitle": "建立資料夾", "editFolderTitle": "編輯資料夾", - "folderDialogDescription": "選擇名稱、顏色和圖示。使用 / 分隔符號嵌套資料夾。", + "folderDialogDescription": "選擇名稱、色彩和圖示。使用 / 來巢狀資料夾。", "folderNameLabel": "資料夾名稱", - "folderNamePlaceholder": "例如:生產/網站", - "folderNestingHint": "使用 / 分隔層級並建立嵌套資料夾。", + "folderNamePlaceholder": "例如:Production/Web", + "folderNestingHint": "使用 / 來分隔層級並建立巢狀資料夾。", "folderColor": "顏色", "folderIcon": "圖示", + "folderCredential": "憑證", + "folderCredentialNone": "未分配憑證", + "folderCredentialHint": "此資料夾中使用「儲存憑證」驗證但未選擇自身憑證的主機將繼承此憑證。", "folderPreview": "預覽", "folderNameFallback": "未命名資料夾", "createFolderButton": "建立資料夾", "saveFolderButton": "儲存資料夾", "cancel": "取消", - "iconSearchPlaceholder": "搜尋圖示…", - "openAllSessions": "開啟所有會話", + "iconSearchPlaceholder": "搜尋圖示...", + "openAllSessions": "開啟所有連線", "editFolder": "編輯資料夾", "deleteFolder": "刪除資料夾", "folderSaved": "資料夾已儲存", "failedToSaveFolder": "儲存資料夾失敗", - "folderDeleted": "已刪除資料夾“{{name}}”", - "deleteFolderConfirm": "刪除「{{name}}」及其 {{count}} 主機?此操作無法撤銷。", - "failedToMoveHosts": "主機遷移失敗", + "folderDeleted": "已刪除資料夾「{{name}}」", + "deleteFolderConfirm": "刪除「{{name}}」及其 {{count}} 台主機?此操作無法復原。", + "failedToMoveHosts": "移動主機失敗", "expandAll": "展開所有資料夾", - "collapseAll": "折疊所有資料夾", - "moreActions": "更多的", - "groupBy": "按分組", + "collapseAll": "收合所有資料夾", + "moreActions": "更多", + "groupBy": "分組方式", "GroupByFolder": "資料夾", "GroupByTag": "標籤", - "GroupByStatus": "地位", - "GroupByProtocol": "協定", - "GroupByAuth": "身份驗證類型", + "GroupByStatus": "狀態", + "GroupByProtocol": "通訊協定", + "GroupByAuth": "驗證類型", "groupUngrouped": "未分組", "deletedHost": "已刪除 {{name}}", "copiedToClipboard": "已複製到剪貼簿", - "terminalUrlCopied": "已複製終端 URL", - "fileManagerUrlCopied": "文件管理器 URL 已複製", - "tunnelUrlCopied": "隧道 URL 已複製", + "terminalUrlCopied": "終端機 URL 已複製", + "fileManagerUrlCopied": "檔案管理員 URL 已複製", + "tunnelUrlCopied": "通道 URL 已複製", "dockerUrlCopied": "Docker URL 已複製", "hostMetricsUrlCopied": "主機指標 URL 已複製", - "tmuxMonitorUrlCopied": "Tmux 監視器 URL 已複製", - "rdpUrlCopied": "複製的 RDP URL", + "tmuxMonitorUrlCopied": "Tmux 監控 URL 已複製", + "rdpUrlCopied": "RDP URL 已複製", "vncUrlCopied": "VNC URL 已複製", "telnetUrlCopied": "Telnet URL 已複製", - "remoteDesktopUrlCopied": "已複製遠端桌面 URL", + "remoteDesktopUrlCopied": "遠端桌面 URL 已複製", "expandActions": "展開操作", - "collapseActions": "坍塌動作", + "collapseActions": "摺疊操作", "wakeOnLanAction": "區域網路喚醒", - "wakeOnLanSuccess": "魔法資料包已寄至 {{name}}", - "wakeOnLanError": "發送魔術包失敗", + "wakeOnLanSuccess": "已傳送魔術封包至 {{name}}", + "wakeOnLanError": "傳送魔術封包失敗", "cloneHostAction": "複製主機", - "copyAddress": "副本地址", + "copyAddress": "複製位址", "copyLink": "複製連結", - "copyTerminalUrlAction": "複製終端 URL", - "copyFileManagerUrlAction": "複製文件管理器 URL", - "copyTunnelUrlAction": "複製隧道 URL", + "copyTerminalUrlAction": "複製終端機 URL", + "copyFileManagerUrlAction": "複製檔案管理員 URL", + "copyTunnelUrlAction": "複製通道 URL", "copyDockerUrlAction": "複製 Docker URL", "copyHostMetricsUrlAction": "複製主機指標 URL", - "copyTmuxMonitorUrlAction": "複製 Tmux 監視器 URL", + "copyTmuxMonitorUrlAction": "複製 Tmux 監控 URL", "copyRdpUrlAction": "複製 RDP URL", "copyVncUrlAction": "複製 VNC URL", "copyTelnetUrlAction": "複製 Telnet URL", @@ -1057,2155 +1119,2289 @@ "deleteCredentialConfirm": "刪除憑證「{{name}}」?", "deletedCredential": "已刪除 {{name}}", "deploySSHKeyTitle": "部署 SSH 金鑰", - "deployingBtn": "正在部署…", + "deployingBtn": "部署中…", "deployBtn": "部署", - "failedToDeployKey": "金鑰部署失敗", - "deleteHostsConfirm": "刪除 {{count}} 主機{{plural}}?此操作無法撤銷。", - "movedToRoot": "已移至根目錄", - "enableTerminalFeature": "啟用終端", - "disableTerminalFeature": "禁用終端", - "enableFilesFeature": "啟用文件", + "failedToDeployKey": "部署金鑰失敗", + "deleteHostsConfirm": "刪除 {{count}} 個主機{{plural}}?此操作無法復原。", + "movedToRoot": "已移至根層級", + "enableTerminalFeature": "啟用終端機", + "disableTerminalFeature": "停用終端機", + "enableFilesFeature": "啟用檔案", "disableFilesFeature": "停用檔案", - "enableTunnelsFeature": "啟用隧道", - "disableTunnelsFeature": "禁用隧道", + "enableTunnelsFeature": "啟用通道", + "disableTunnelsFeature": "停用通道", "enableDockerFeature": "啟用 Docker", - "disableDockerFeature": "禁用 Docker", + "disableDockerFeature": "停用 Docker", "enableProxmoxFeature": "啟用 Proxmox", - "disableProxmoxFeature": "禁用 Proxmox", - "addTagsPlaceholder": "添加標籤…", - "authDetails": "身份驗證詳情", + "disableProxmoxFeature": "停用 Proxmox", + "addTagsPlaceholder": "新增標籤...", + "authDetails": "驗證詳細資訊", "credType": "類型", - "generateKeyPairDesc": "產生新的金鑰對,私鑰和公鑰將自動填入。", - "generatingKey": "正在生成...", - "generateLabel": "生成 {{label}}", - "uploadFileBtn": "上傳文件", - "keyPassphraseOptional": "密鑰口令(可選)", - "sshPublicKeyOptional": "SSH 公鑰(可選)", - "publicKeyGenerated": "產生的公鑰", - "failedToGeneratePublicKey": "無法匯出公鑰", - "publicKeyCopied": "公鑰已複製", - "keyPairGenerated": "{{label}} key pair generated", - "failedToGenerateKeyPair": "密鑰對產生失敗", + "generateKeyPairDesc": "產生新的金鑰對,系統將自動填入私密金鑰和公開金鑰。", + "generatingKey": "產生中...", + "generateLabel": "產生 {{label}}", + "uploadFileBtn": "上傳檔案", + "keyPassphraseOptional": "金鑰密碼短語(選填)", + "sshPublicKeyOptional": "SSH 公開金鑰(選填)", + "publicKeyGenerated": "公開金鑰已產生", + "failedToGeneratePublicKey": "無法衍生公開金鑰", + "publicKeyCopied": "公開金鑰已複製", + "keyPairGenerated": "{{label}} 金鑰對已產生", + "failedToGenerateKeyPair": "無法產生金鑰對", "searchHostsPlaceholder": "搜尋主機、位址、標籤…", "searchCredentialsPlaceholder": "搜尋憑證…", "refreshBtn": "重新整理", - "addTag": "添加標籤…", + "addTag": "新增標籤...", "deleteConfirmBtn": "刪除", - "tunnelRequirementsText": "SSH 伺服器必須在 /etc/ssh/sshd_config 中設定 GatewayPorts yes、AllowTcpForwarding yes 和 PermitRootLogin yes。", - "deleteHostConfirm": "刪除“{{name}}”?", - "enableAtLeastOneProtocol": "啟用上述至少一種協定以配置身份驗證和連線設定。", - "keyPassphrase": "密鑰口令", - "connectBtn": "連接", - "disconnectBtn": "斷開", - "basicInformation": "基本訊息", - "authDetailsSection": "身份驗證詳情", + "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": "聯繫", + "hostsTab": "主機", + "credentialsTab": "憑證", + "selectMultiple": "選取多個", + "selectHosts": "選取主機", + "connectionLabel": "連線", "authenticationLabel": "驗證", - "generateKeyPairTitle": "產生密鑰對", - "generateKeyPairDescription": "產生新的金鑰對,私鑰和公鑰將自動填入。", - "generateFromPrivateKey": "從私鑰生成", + "generateKeyPairTitle": "產生金鑰對", + "generateKeyPairDescription": "產生新的金鑰對,私密金鑰與公開金鑰將自動填入。", + "generateFromPrivateKey": "從私密金鑰產生", "refreshBtn2": "重新整理", - "exitSelectionTitle": "出口選擇", - "exportAll": "全部導出", - "exportForSharing": "匯出共享", + "exitSelectionTitle": "離開選取", + "exportAll": "全部匯出", + "exportForSharing": "匯出供分享", "addHostBtn2": "新增主機", "addCredentialBtn2": "新增憑證", "checkingHostStatuses": "正在檢查主機狀態...", - "pinnedSection": "置頂", - "hostsExported": "主機匯出成功", - "hostsShareExported": "共享主機已成功匯出", - "exportFailed": "匯出主機失敗", - "sampleDownloaded": "下載範例文件", - "failedToDeleteCredential2": "刪除憑證失敗", - "noFolderOption": "(無資料夾)", - "nSelected": "{{count}} 已選擇", - "featuresMenu": "特徵", + "pinnedSection": "已釘選", + "hostsExported": "主機已成功匯出", + "hostsShareExported": "可分享的主機已成功匯出", + "exportFailed": "主機匯出失敗", + "sampleDownloaded": "範例檔案已下載", + "failedToDeleteCredential2": "憑證刪除失敗", + "noFolderOption": "(無資料夾)", + "nSelected": "已選取 {{count}} 個", + "featuresMenu": "功能", "moveMenu": "移動", - "connectSelected": "連接", + "connectSelected": "連線", "cancelSelection": "取消", - "deployDialogDesc": "將 {{name}} 部署到主機的 authorized_keys 中。", + "deployDialogDesc": "部署 {{name}} 到主機的 authorized_keys。", "targetHostLabel": "目標主機", - "selectHostOption": "選擇主機...", + "selectHostOption": "選取主機...", "keyDeployedSuccess": "金鑰已成功部署", "failedToDeployKey2": "金鑰部署失敗", - "deletedCount": "已刪除 {{count}} 主機", - "failedToDeleteCount": "刪除 {{count}} 主機失敗", - "duplicatedHost": "重複的“{{name}}”", - "failedToDuplicateHost": "複製主機失敗", - "updatedCount": "已更新 {{count}} 主機", - "friendlyNameLabel": "友善名稱", + "deletedCount": "已刪除 {{count}} 台主機", + "failedToDeleteCount": "無法刪除 {{count}} 台主機", + "duplicatedHost": "重複 \"{{name}}\"", + "failedToDuplicateHost": "無法複製主機", + "updatedCount": "已更新 {{count}} 台主機", + "friendlyNameLabel": "易記名稱", "descriptionLabel": "描述", - "loadingHost": "正在加載主機...", - "loadingHosts": "正在加載主機...", - "loadingCredentials": "正在加載憑證...", - "noHostsYet": "目前還沒有主持人", - "noHostsMatchSearch": "沒有符合您搜尋條件的主機", - "hostNotFound": "未找到主機", + "loadingHost": "正在載入主機...", + "loadingHosts": "正在載入主機...", + "loadingCredentials": "正在載入憑證...", + "noHostsYet": "尚無主機", + "noHostsMatchSearch": "沒有符合搜尋的主機", + "hostNotFound": "找不到主機", "searchHosts": "搜尋主機...", "sortHosts": "排序主機", - "sortDefault": "預設訂單", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", - "sortIpAsc": "IP位址(升序)", - "sortIpDesc": "IP位址(說明)", + "sortDefault": "預設排序", + "sortNameAsc": "名稱 (A → Z)", + "sortNameDesc": "名稱 (Z → A)", + "sortIpAsc": "IP 位址 (遞增)", + "sortIpDesc": "IP 位址 (遞減)", "sortOnlineFirst": "線上優先", "sortOfflineFirst": "離線優先", - "sortPinnedFirst": "置頂", - "filterHosts": "過濾主機", - "filterClearAll": "清除篩選條件", - "filterStatusGroup": "地位", - "filterOnline": "在線的", + "sortPinnedFirst": "已釘選優先", + "filterHosts": "篩選主機", + "filterClearAll": "清除篩選", + "filterStatusGroup": "狀態", + "filterOnline": "線上", "filterOffline": "離線", - "filterPinned": "置頂", - "filterAuthGroup": "身份驗證類型", + "filterPinned": "已釘選", + "filterAuthGroup": "驗證類型", "filterAuthPassword": "密碼", - "filterAuthKey": "SSH金鑰", + "filterAuthKey": "SSH 金鑰", "filterAuthCredential": "憑證", - "filterAuthNone": "沒有任何", + "filterAuthNone": "無", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "協定", + "filterProtocolGroup": "通訊協定", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "遠端桌面協定", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特徵", - "filterFeatureTerminal": "終端", - "filterFeatureFileManager": "文件管理器", - "filterFeatureTunnel": "隧道", + "filterFeaturesGroup": "功能", + "filterFeatureTerminal": "終端機", + "filterFeatureFileManager": "檔案管理員", + "filterFeatureTunnel": "通道", "filterFeatureDocker": "Docker", "filterTagsGroup": "標籤", - "shareHost": "共享主機", - "shareHostTitle": "分享: {{name}}", + "shareHost": "分享主機", + "shareHostTitle": "分享:{{name}}", + "shareFolder": "共享資料夾", + "shareFolderTitle": "共用資料夾: {{name}}", + "folderSharedSuccessfully": "資料夾中的共用主機 {{count}}", + "failedToShareFolder": "共享資料夾失敗", "sharing": { - "loadError": "分享資料載入失敗,請重試。", - "shareWithSection": "分享", + "loadError": "載入分享資料失敗,請再試一次。", + "shareWithSection": "分享對象", "usersTab": "使用者", "rolesTab": "角色", "searchPlaceholder": "搜尋使用者或角色...", - "noMatches": "未找到匹配項", - "permissionLevelLabel": "權限等級", + "noMatches": "找不到符合項目", + "permissionLevelLabel": "權限層級", "levels": { "connect": { - "label": "連接", - "description": "僅允許開啟會話:終端機、遠端桌面、檔案管理器、隧道和 Docker。無權存取主機配置。" + "label": "連線", + "description": "僅開啟工作階段:終端機、遠端桌面、檔案管理員、通道和 Docker。無法存取主機設定。" }, "view": { - "label": "看法", - "description": "連線後,即可查看主機配置。密鑰永遠不會顯示。" + "label": "檢視", + "description": "連線,並可查看主機設定。機密資料絕不顯示。" }, "edit": { "label": "編輯", - "description": "查看並修改主機。金鑰可以替換但永遠無法讀取;憑證分配僅限擁有者操作。" + "description": "檢視,並可修改主機。可更換機密資料但無法讀取;憑證指派僅限擁有者。" }, "manage": { "label": "管理", - "description": "編輯、與他人共享主機、更改權限等級和撤銷存取權限。" + "description": "編輯,並可與他人分享主機、變更權限層級及撤銷存取權。" } }, - "expiryLabel": "存取權限到期", + "expiryLabel": "存取期限", "expiry": { - "never": "絕不", - "oneHour": "1小時", - "oneDay": "24小時", - "sevenDays": "7天", - "thirtyDays": "30天", - "custom": "風俗" + "never": "永不", + "oneHour": "1 小時", + "oneDay": "24 小時", + "sevenDays": "7 天", + "thirtyDays": "30 天", + "custom": "自訂" }, - "customHoursPlaceholder": "存取權限到期時間(剩餘時間)", + "customHoursPlaceholder": "存取到期前的小時數", "shareButton": "分享", - "shareWithCount": "分享({{count}})", + "shareWithCount": "分享 ({{count}})", "currentAccess": "目前存取權限", - "noAccessEntries": "此主機尚未共用", - "grantedBy": "由……授予", - "expires": "過期", + "noAccessEntries": "此主機尚未分享", + "folderShareSummary": "此資料夾中的 {{shared}} 主機(共 {{total}} )共用", + "grantedBy": "授予者", + "expires": "到期日", "expired": "已到期", - "never": "絕不", + "never": "永不", "revoke": "撤銷", - "accessUpdated": "訪問已更新", - "accessUpdateFailed": "更新存取權限失敗", - "sharedBadge": "共享", - "sharedBadgeTooltip": "由 {{owner}} 共用({{level}} 存取權限)", - "viewOnlyBanner": "此主機由 {{owner}} 與您分享,您擁有檢視權限。配置資訊為唯讀。", - "sharedEditBanner": "此主機由 {{owner}} 與您分享,並授予您編輯權限。更改將應用於實際主機;身份驗證引用只能由所有者更改。", - "ownerOnlyControl": "只有房東才能更改此設置" + "accessUpdated": "存取已更新", + "accessUpdateFailed": "更新存取失敗", + "sharedBadge": "已分享", + "sharedBadgeTooltip": "由 {{owner}} 分享({{level}} 存取權限)", + "viewOnlyBanner": "此主機由 {{owner}} 與您分享,僅供檢視。設定為唯讀。", + "sharedEditBanner": "此主機由 {{owner}} 與您分享,具編輯權限。變更會套用至實際主機;驗證設定僅能由擁有者變更。", + "ownerOnlyControl": "僅主機擁有者可變更此項目" }, "guac": { - "connection": "聯繫", + "connection": "連線", "authentication": "驗證", - "storedCredential": "已儲存憑證", - "noCredential": "無憑證(直接憑證如下)", - "authMethod": "身份驗證方法", - "authTypeDirect": "直接的", + "storedCredential": "已儲存的憑證", + "noCredential": "無憑證(下方直接輸入憑證)", + "authMethod": "驗證方法", + "authTypeDirect": "直接", "authTypeCredential": "憑證", - "selectCredential": "選擇憑證...", - "connectionSettings": "連接設定", + "authTypeNone": "沒有任何", + "authTypeNoneDesc": "不會儲存任何憑證。每次連線時,系統都會提示您輸入使用者名稱和密碼;這些資訊不會被儲存。", + "selectCredential": "選取憑證…", + "connectionSettings": "連線設定", "displaySettings": "顯示設定", "audioSettings": "音訊設定", - "rdpPerformance": "RDP性能", - "deviceRedirection": "裝置重定向", - "session": "會議", + "rdpPerformance": "RDP 效能", + "deviceRedirection": "裝置重新導向", + "session": "工作階段", "gateway": "閘道", "remoteApp": "RemoteApp", "clipboard": "剪貼簿", - "sessionRecording": "會話錄製", - "wakeOnLan": "區域網路喚醒", + "sessionRecording": "工作階段錄製", + "wakeOnLan": "Wake-on-LAN", "vncSettings": "VNC 設定", - "terminalSettings": "終端設定", - "rdpPort": "RDP埠", + "terminalSettings": "終端機設定", + "rdpPort": "RDP 連接埠", "username": "使用者名稱", "password": "密碼", - "domain": "領域", + "passwordSaved": "密碼已儲存,輸入即可更改", + "domain": "網域", "securityMode": "安全模式", "colorDepth": "色彩深度", "width": "寬度", "height": "高度", - "dpi": "乾粉", - "resizeMethod": "調整大小方法", - "clientName": "客戶名稱", - "initialProgram": "初始計劃", - "serverLayout": "伺服器佈局", + "dpi": "DPI", + "resizeMethod": "調整尺寸方法", + "clientName": "用戶端名稱", + "initialProgram": "初始程式", + "serverLayout": "伺服器鍵盤配置", "timezone": "時區", - "loadBalanceInfo": "負載平衡資訊/Cookie", - "loadBalanceInfoDesc": "用於 RDS 伺服器場負載平衡的 RD 連線代理 cookie(例如 tsv://MS Terminal Services Plugin.1.CollectionName)", + "loadBalanceInfo": "負載平衡資訊 / Cookie", + "loadBalanceInfoDesc": "用於 RDS 伺服器陣列負載平衡的 RD 連線代理 Cookie (例如 tsv://MS Terminal Services Plugin.1.CollectionName)", "guacdProxy": "guacd 代理", "guacdHostname": "guacd 主機", "guacdHostnamePlaceholder": "全域預設", - "guacdPort": "瓜達克港", - "guacdProxyDesc": "覆寫此連線的全域 guacd 實例。留空則使用全域配置的 guacd。", - "gatewayHostname": "網關主機名", - "gatewayPort": "網關連接埠", - "gatewayUsername": "網關用戶名", - "gatewayPassword": "網關密碼", - "gatewayDomain": "閘道", + "guacdPort": "guacd 連接埠", + "guacdProxyDesc": "覆寫此連線的全域 guacd 執行個體。留空以使用全域設定的 guacd。", + "gatewayHostname": "閘道主機名稱", + "gatewayPort": "閘道連接埠", + "gatewayUsername": "閘道使用者名稱", + "gatewayPassword": "閘道密碼", + "gatewayDomain": "閘道網域", "remoteAppProgram": "RemoteApp 程式", "workingDirectory": "工作目錄", - "arguments": "論點", - "normalizeLineEndings": "標準化行尾", - "recordingPath": "錄影路徑", - "recordingName": "錄音名稱", - "macAddress": "MAC位址", + "arguments": "引數", + "normalizeLineEndings": "正規化行尾符號", + "recordingPath": "錄製路徑", + "recordingName": "錄製名稱", + "macAddress": "MAC 位址", "broadcastAddress": "廣播位址", - "udpPort": "UDP埠", - "waitTimeS": "等待時間(秒)", + "udpPort": "UDP 連接埠", + "waitTimeS": "等待時間 (秒)", "driveName": "磁碟機名稱", - "drivePath": "驅動路徑", - "ignoreCertificate": "忽略證書", - "ignoreCertificateDesc": "允許連接到具有自簽名憑證的主機", - "forceLossless": "無損力", - "forceLosslessDesc": "強制無損影像編碼(更高品質,更高頻寬)", - "disableAudio": "關閉音訊", - "disableAudioDesc": "將遠端會話中的所有音訊靜音", + "drivePath": "磁碟機路徑", + "ignoreCertificate": "忽略憑證", + "ignoreCertificateDesc": "允許連線至使用自行簽署憑證的主機", + "forceLossless": "強制無損", + "forceLosslessDesc": "強制使用無損影像編碼(品質較高,頻寬需求較大)", + "disableAudio": "停用音訊", + "disableAudioDesc": "將遠端工作階段的音訊全部靜音", "enableAudioInput": "啟用音訊輸入(麥克風)", - "enableAudioInputDesc": "把本地麥克風轉送到遠端會話", - "wallpaper": "壁紙", - "wallpaperDesc": "顯示桌面桌布(停用可提升效能)", - "theming": "主題", - "themingDesc": "啟用視覺主題和樣式", - "fontSmoothing": "字體平滑", - "fontSmoothingDesc": "啟用 ClearType 字體渲染", + "enableAudioInputDesc": "將本機麥克風轉送至遠端工作階段", + "wallpaper": "桌面背景", + "wallpaperDesc": "顯示桌面背景(停用可提升效能)", + "theming": "佈景主題", + "themingDesc": "啟用視覺主題與樣式", + "fontSmoothing": "字型平滑處理", + "fontSmoothingDesc": "啟用 ClearType 字型轉譯", "fullWindowDrag": "全視窗拖曳", "fullWindowDragDesc": "拖曳時顯示視窗內容", "desktopComposition": "桌面合成", "desktopCompositionDesc": "啟用 Aero 玻璃效果", "menuAnimations": "選單動畫", - "menuAnimationsDesc": "啟用選單淡入淡出和滑動動畫", - "disableBitmapCaching": "禁用位圖緩存", - "disableBitmapCachingDesc": "關閉位圖快取(可能有助於解決故障)", - "disableOffscreenCaching": "禁用螢幕外緩存", - "disableOffscreenCachingDesc": "關閉螢幕外緩存", - "disableGlyphCaching": "禁用字形快取", - "disableGlyphCachingDesc": "關閉字形快取", - "enableGfx": "啟用圖形", + "menuAnimationsDesc": "啟用選單淡入淡出及滑動動畫", + "disableBitmapCaching": "停用點陣圖快取", + "disableBitmapCachingDesc": "關閉點陣圖快取(可能有助於解決顯示問題)", + "disableOffscreenCaching": "停用螢幕外快取", + "disableOffscreenCachingDesc": "關閉螢幕外快取", + "disableGlyphCaching": "停用字符快取", + "disableGlyphCachingDesc": "關閉字符快取", + "enableGfx": "啟用 GFX", "enableGfxDesc": "使用 RemoteFX 圖形管線", - "enablePrinting": "啟用列印", - "enablePrintingDesc": "將本機印表機重定向到遠端會話", - "enableDriveRedirection": "啟用驅動器重定向", - "enableDriveRedirectionDesc": "將本機資料夾對應為遠端會話中的磁碟機", - "createDrivePath": "建立驅動器路徑", - "createDrivePathDesc": "如果資料夾不存在,則自動建立該資料夾。", + "enablePrinting": "啟用列印功能", + "enablePrintingDesc": "將本機印表機重新導向至遠端工作階段", + "enableDriveRedirection": "啟用磁碟機重新導向", + "enableDriveRedirectionDesc": "在遠端工作階段中將本機資料夾對應為磁碟機", + "createDrivePath": "建立磁碟機路徑", + "createDrivePathDesc": "如果資料夾不存在則自動建立", "disableDownload": "停用下載", - "disableDownloadDesc": "阻止從遠端會話下載文件", - "disableUpload": "禁用上傳", - "disableUploadDesc": "阻止向遠端會話上傳文件", - "enableTouch": "啟用觸控功能", - "enableTouchDesc": "啟用觸控輸入轉發", - "consoleSession": "控制台會話", - "consoleSessionDesc": "連接到控制台(會話 0),而不是新建會話。", - "sendWolPacket": "發送 WOL 封包", - "sendWolPacketDesc": "連線前發送喚醒包以喚醒此主機", + "disableDownloadDesc": "防止從遠端工作階段下載檔案", + "disableUpload": "停用上傳", + "disableUploadDesc": "防止將檔案上傳至遠端工作階段", + "enableTouch": "啟用觸控", + "enableTouchDesc": "啟用觸控輸入轉送", + "consoleSession": "主控台工作階段", + "consoleSessionDesc": "連線至主控台 (工作階段 0) 而非新工作階段", + "sendWolPacket": "傳送 WOL 封包", + "sendWolPacketDesc": "在連線前傳送魔術封包以喚醒此主機", "disableCopy": "停用複製", - "disableCopyDesc": "阻止從遠端會話複製文本", + "disableCopyDesc": "防止從遠端工作階段複製文字", "disablePaste": "停用貼上", - "disablePasteDesc": "阻止將文字貼到遠端會話中", - "createPathIfMissing": "如果路徑缺失,則建立路徑。", + "disablePasteDesc": "防止將文字貼上至遠端工作階段", + "createPathIfMissing": "若路徑不存在則建立", "createPathIfMissingDesc": "自動建立錄製目錄", "excludeOutput": "排除輸出", - "excludeOutputDesc": "不要錄製螢幕輸出(僅錄製元資料)", + "excludeOutputDesc": "不錄製螢幕輸出 (僅中繼資料)", "excludeMouse": "排除滑鼠", - "excludeMouseDesc": "請勿記錄滑鼠移動", - "includeKeystrokes": "包含按鍵操作", - "includeKeystrokesDesc": "除了螢幕輸出外,還要記錄原始按鍵操作。", - "vncPort": "VNC埠", - "vncPassword": "VNC密碼", - "vncUsernameOptional": "使用者名稱(可選)", - "vncLeaveBlank": "如果不需要,請留空。", - "cursorMode": "遊標模式", + "excludeMouseDesc": "不錄製滑鼠移動", + "includeKeystrokes": "包含按鍵輸入", + "includeKeystrokesDesc": "除了螢幕輸出外,也錄製原始按鍵輸入", + "vncPort": "VNC 連接埠", + "vncPassword": "VNC 密碼", + "vncUsernameOptional": "使用者名稱 (選用)", + "vncLeaveBlank": "若不需要則留空", + "cursorMode": "游標模式", "swapRedBlue": "交換紅/藍", - "swapRedBlueDesc": "交換紅色和藍色通道(修復一些顏色問題)", - "readOnly": "只讀", - "readOnlyDesc": "無需發送任何輸入即可查看遠端螢幕", + "swapRedBlueDesc": "交換紅色與藍色色版 (修正某些色彩問題)", + "readOnly": "唯讀", + "readOnlyDesc": "僅檢視遠端螢幕,不傳送任何輸入", "telnetPort": "Telnet 連接埠", - "terminalType": "終端類型", - "fontName": "字體名稱", - "fontSize": "字體大小", - "colorScheme": "配色方案", + "terminalType": "終端機類型", + "fontName": "字型名稱", + "fontSize": "字型大小", + "colorScheme": "色彩配置", "backspaceKey": "退格鍵", - "saveHostFirst": "先保存主機。", - "sharingOptionsAfterSave": "儲存主機後即可使用分享選項。", + "saveHostFirst": "請先儲存主機。", + "sharingOptionsAfterSave": "主機儲存後即可使用分享選項。", "permissionLevel": "權限等級", "typeHeader": "類型", "targetHeader": "目標", - "permissionHeader": "允許", + "permissionHeader": "權限", "cancelBtn": "取消", - "savingBtn": "儲存...", + "savingBtn": "儲存中…", "updateHostBtn": "更新主機", "addHostBtn": "新增主機" } }, "commandPalette": { - "searchPlaceholder": "搜尋主機、指令或設定…", + "searchPlaceholder": "搜尋主機、命令或設定…", "quickActions": "快速操作", - "hostManager": "主機管理器", + "hostManager": "主機管理", "hostManagerDesc": "管理、新增或編輯主機", "addNewHost": "新增主機", "addNewHostDesc": "註冊新主機", "adminSettings": "管理員設定", - "adminSettingsDesc": "配置系統首選項和用戶", - "userProfile": "用戶個人資料", - "userProfileDesc": "管理您的帳戶和偏好設定", + "adminSettingsDesc": "設定系統偏好與使用者", + "userProfile": "使用者設定檔", + "userProfileDesc": "管理您的帳號與偏好設定", "addCredential": "新增憑證", "addCredentialDesc": "儲存 SSH 金鑰或密碼", "tmuxMonitor": "Tmux 監視器", - "tmuxMonitorDesc": "監控主機上的 tmux 會話", + "tmuxMonitorDesc": "監控您所有主機的 tmux 會話", "recentActivity": "近期活動", - "serversAndHosts": "伺服器和主機", - "noHostsFound": "找不到與「{{search}}」相符的主機", + "serversAndHosts": "伺服器與主機", + "noHostsFound": "找不到符合「{{search}}」的主機", "links": "連結", - "navigate": "導航", - "select": "選擇", - "toggleWith": "切換" + "navigate": "導覽", + "select": "選取", + "toggleWith": "切換方式" }, "splitScreen": { - "paneEmpty": "窗格 {{index}} - 空", - "noTabAssigned": "未分配標籤頁", - "focusedPane": "活動窗格" + "paneEmpty": "窗格 {{index}} - 空白", + "noTabAssigned": "未指派任何分頁", + "focusedPane": "作用中窗格" }, "connections": { - "noConnections": "沒有連接", - "noConnectionsDesc": "開啟終端機、檔案總管或遠端桌面即可在此處查看連線。", + "noConnections": "無連線", + "noConnectionsDesc": "開啟終端機、檔案管理員或遠端桌面,即可在此處查看連線", "connectedFor": "已連線 {{duration}}", - "connected": "已連接", - "disconnected": "斷開連接", - "closeTab": "關閉標籤頁", - "closeConnection": "緊密聯繫", + "connected": "已連線", + "disconnected": "已中斷連線", + "closeTab": "關閉分頁", + "closeConnection": "關閉連線", "forgetTab": "忘記", - "removeBackground": "消除", - "reconnect": "重新連接", - "reopenTab": "重新開放", - "sectionOpen": "打開", + "removeBackground": "移除", + "reconnect": "重新連線", + "reopenTab": "重新開啟", + "sectionOpen": "開啟", "sectionBackground": "背景", - "backgroundDesc": "斷開連線後,會話仍會保持 30 分鐘,並且可以重新連線。", - "persisted": "在背景持續運行", - "expiresIn": "有效期限至 {{duration}}", - "search": "搜尋關聯...", - "noSearchResults": "沒有找到與您的搜尋相符的結果。", - "rename": "重新命名會話" + "backgroundDesc": "工作階段在中斷連線後會保留 30 分鐘,且可重新連線。", + "persisted": "已在背景中保留", + "expiresIn": "將於 {{duration}} 後到期", + "search": "搜尋連線…", + "noSearchResults": "無符合搜尋條件的連線", + "rename": "重新命名工作階段", + "sectionSharedWithMe": "與我分享", + "sharedBy": "由 {{username}} 分享", + "join": "加入", + "sharedSessionLabel": "{{hostName}} (共享)" + }, + "sessionSharing": { + "guestView": { + "loading": "正在連接到共享會話...", + "linkInvalid": "此分享連結無效、已過期或已撤銷。", + "rateLimited": "嘗試次數過多,請稍後再試。", + "sessionEnded": "本次會議已結束", + "readOnlyBadge": "僅查看" + }, + "modalTitle": "分享會話", + "shareButton": "分享", + "notReadyToShare": "會話尚未準備好分享", + "modeTab": { + "link": "關聯", + "user": "使用者" + }, + "linkModeDescription": "任何人只要擁有此連結即可加入,無需註冊帳號。", + "userModeDescription": "將主機共用給已擁有此主機存取權限的特定使用者。如果他們還沒有存取權限,請先將主機分享給他們,或使用連結代替。共享後,會話將顯示在他們的“連接”選項卡中。", + "permissionLevel": { + "label": "權限等級", + "readOnly": "只讀", + "readOnlyDescription": "可以觀看直播,但無法打字或互動。", + "readWrite": "讀寫", + "readWriteDescription": "可以像會話擁有者一樣進行輸入和互動。" + }, + "expiryLabel": "連結過期", + "createLinkButton": "建立連結", + "createShareButton": "與用戶分享", + "searchUsersPlaceholder": "搜尋用戶...", + "noUsersFound": "未找到用戶", + "linkCreated": "已建立分享連結", + "linkCopied": "連結已複製到剪貼簿", + "copyLink": "複製連結", + "shareCreated": "會話已共用。它將出現在對方的「連線」標籤中。", + "shareFailed": "建立共享失敗", + "userLacksHostAccess": "該使用者目前沒有存取此主機的權限。請先與其共享主機,或使用連結進行共享。", + "activeShares": "活躍股", + "noActiveShares": "本會話無活躍份額", + "revoke": "撤銷", + "revokeConfirmTitle": "撤銷此份額?", + "revokeConfirmDescription": "任何使用此共用資料夾的使用者都將立即失去存取權限。", + "revoked": "股份撤銷", + "revokeFailed": "撤銷股份失敗", + "joinCount": "{{count}} 加入", + "joinCount_other": "{{count}} 加入", + "expiresAt": "過期時間 {{date}}", + "linkShareBadge": "關聯", + "userShareBadge": "使用者: {{username}}", + "loadSharesFailed": "無法加載活動份額" }, "guacamole": { - "connecting": "正在連接到 {{type}} 會話...", + "connecting": "正在連線至 {{type}} 工作階段…", "connectionError": "連線錯誤", "connectionFailed": "連線失敗", - "failedToConnect": "取得連線令牌失敗", - "hostNotFound": "未找到主機", - "noHostSelected": "未選擇主機", - "reconnect": "重新連接", + "failedToConnect": "無法取得連線權杖", + "hostNotFound": "找不到主機", + "noHostSelected": "未選取主機", + "reconnect": "重新連線", "retry": "重試", - "guacdUnavailable": "遠端桌面服務 (guacd) 不可用。請確保 guacd 正在運行、可訪問,並且在管理設定中已正確配置。", + "guacdUnavailable": "遠端桌面服務 (guacd) 無法使用。請確保 guacd 正在執行且可存取,並已在管理員設定中正確設定。", + "credentialPromptTitle": "輸入 RDP 憑證", + "credentialPromptDescription": "此主機設定為在連線時提示輸入憑證。這些憑證僅用於當前會話,不會被儲存。", + "connect": "連接", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L(鎖定畫面)", + "winL": "Win+L (鎖定螢幕)", "winKey": "Windows 鍵", "ctrl": "Ctrl", - "alt": "另類", - "shift": "轉移", - "win": "贏", - "stickyActive": "{{key}} (已鎖定 - 按一下釋放)", - "stickyInactive": "{{key}} (點選鎖定)", - "esc": "逃脫", - "tab": "標籤頁", - "home": "家", - "end": "結尾", - "pageUp": "上一頁", - "pageDown": "往下翻頁", - "arrowUp": "向上箭頭", - "arrowDown": "向下箭頭", - "arrowLeft": "左箭頭", - "arrowRight": "向右箭頭", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (已鎖定 - 點擊釋放)", + "stickyInactive": "{{key}} (點擊鎖定)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "向上鍵", + "arrowDown": "向下鍵", + "arrowLeft": "向左鍵", + "arrowRight": "向右鍵", "fnToggle": "功能鍵", - "reconnect": "重新連接會話", - "collapse": "折疊工具列", + "reconnect": "重新連線工作階段", + "collapse": "收合工具列", "expand": "展開工具列", - "dragHandle": "拖曳以重新定位" + "dragHandle": "拖曳以重新定位", + "switchToTrackpad": "切換到觸控板模式(拖曳移動遊標,點擊點擊)", + "switchToTouch": "切換到觸控模式(直接點擊你想點擊的地方)" } }, "terminal": { - "connect": "連接到主機", + "connect": "連線至主機", "clear": "清除", "paste": "貼上", - "reconnect": "重新連接", - "connectionLost": "連線遺失", - "connected": "已連接", - "clipboardWriteFailed": "複製到剪貼簿失敗。請確保頁面是透過 HTTPS 或本地主機存取的。", - "clipboardReadFailed": "讀取剪貼簿內容失敗。請確保已授予剪貼簿權限。", - "clipboardHttpWarning": "貼上操作需要 HTTPS。請使用 Ctrl+Shift+V 或透過 HTTPS 提供 Termix 服務。", - "passwordPromptFillTitle": "請將已儲存的密碼填入此提示框?", + "reconnect": "重新連線", + "connectionLost": "連線中斷", + "connected": "已連線", + "clipboardWriteFailed": "複製到剪貼簿失敗。請確認頁面是透過 HTTPS 或 localhost 提供。", + "clipboardReadFailed": "從剪貼簿讀取失敗。請確認已授予剪貼簿權限。", + "clipboardHttpWarning": "貼上需使用 HTTPS。請使用 Ctrl+Shift+V 或透過 HTTPS 提供 Termix 服務。", + "passwordPromptFillTitle": "將已儲存的密碼填入此提示?", "unknownError": "發生未知錯誤", "websocketError": "WebSocket 連線錯誤", - "connecting": "正在連接...", - "noHostSelected": "未選擇主機", - "reconnecting": "正在重新連線... ({{attempt}}/{{max}})", - "reconnected": "已成功重新連接", - "tmuxSessionCreated": "tmux 會話已建立: {{name}}", - "tmuxSessionAttached": "tmux 會話已連線: {{name}}", - "tmuxUnavailable": "遠端主機上未安裝 tmux,回退到標準 shell", - "tmuxSessionPickerTitle": "tmux 會話", - "tmuxSessionPickerDesc": "此主機上已存在 tmux 會話。請選擇一個會話重新連線或建立新會話。", + "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}} 已連接", + "tmuxWindowCount": "{{count}} 個視窗", + "tmuxAttached": "已附加的用戶端", + "tmuxAttachedCount": "{{count}} 個已附加", "tmuxLastActivity": "上次活動", - "tmuxTimeJustNow": "現在", - "tmuxTimeMinutes": "{{count}}幾分鐘前", - "tmuxTimeHours": "{{count}}小時前", - "tmuxTimeDays": "{{count}}天前", - "tmuxCreateNew": "開始新會話", - "tmuxCopyHint": "調整選取範圍並按 Enter 鍵複製到剪貼簿", - "tmuxDetach": "從 tmux 會話中分離", - "tmuxDetached": "已從 tmux 會話中分離", - "maxReconnectAttemptsReached": "已達最大重連嘗試次數", + "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": "需要雙重認證", + "terminalTitle": "終端機 - {{host}}", + "terminalWithPath": "終端機 - {{host}}:{{path}}", + "runTitle": "正在執行 {{command}} - {{host}}", + "totpRequired": "需要雙因素驗證", "totpCodeLabel": "驗證碼", - "totpVerify": "核實", - "warpgateAuthRequired": "需要傳送門認證", + "totpVerify": "驗證", + "mfaPromptRequired": "需要身份驗證", + "mfaPushRequired": "需要推送身份驗證", + "mfaMenuPlaceholder": "請輸入您的回复", + "mfaWaitingApproval": "正在等待設備審核通過…", + "mfaSendRequest": "發送請求", + "warpgateAuthRequired": "需要 Warpgate 驗證", "warpgateSecurityKey": "安全金鑰", - "warpgateAuthUrl": "身份驗證 URL", + "warpgateAuthUrl": "驗證 URL", "warpgateOpenBrowser": "在瀏覽器中開啟", - "warpgateContinue": "我已經完成身份驗證", + "warpgateContinue": "我已完成驗證", "opksshAuthRequired": "需要 OPKSSH 驗證", - "opksshAuthDescription": "請在瀏覽器中完成身份驗證以繼續。此會話有效期限為 24 小時。", - "opksshOpenBrowser": "開啟瀏覽器進行身份驗證", - "opksshWaitingForAuth": "正在等待瀏覽器驗證...", - "opksshAuthenticating": "正在處理身份驗證...", - "opksshTimeout": "身份驗證超時,請重試。", - "opksshAuthFailed": "身份驗證失敗。請檢查您的憑證並重試。", + "opksshAuthDescription": "請在瀏覽器中完成驗證以繼續。此工作階段將持續有效 24 小時。", + "opksshOpenBrowser": "開啟瀏覽器以進行驗證", + "opksshWaitingForAuth": "正在等待瀏覽器中的驗證…", + "opksshAuthenticating": "正在處理驗證…", + "opksshTimeout": "驗證逾時。請再試一次。", + "opksshAuthFailed": "驗證失敗。請檢查您的憑證並再試一次。", "opksshSignInWith": "使用 {{provider}} 登入", - "vaultAuthTitle": "需要登入 Vault。", - "vaultAuthDescription": "已開啟一個窗口,供您登入 HashiCorp Vault。請在那裡完成登入;此連線將自動繼續。", - "vaultAuthFailed": "Vault身份驗證失敗,請重試。", + "vaultAuthTitle": "需要 Vault 登入", + "vaultAuthDescription": "已開啟視窗以登入 HashiCorp Vault。在該處完成登入;此連線將自動繼續。", + "vaultAuthFailed": "Vault 驗證失敗。請再試一次。", "vaultReopen": "重新開啟登入視窗", "sudoPasswordPopupTitle": "輸入密碼?", - "linkDialogTitle": "打開連結", - "linkDialogOpen": "打開", + "linkDialogTitle": "開啟連結", + "linkDialogOpen": "開啟", "linkDialogCopy": "複製", - "websocketAbnormalClose": "連線意外關閉。這可能是由於反向代理或 SSL 配置問題導致的。請檢查伺服器日誌。", - "connectionLogTitle": "連線日誌", - "connectionLogCopy": "將日誌複製到剪貼簿", - "connectionLogEmpty": "暫無連線日誌", - "connectionLogWaiting": "正在等待連線日誌...", - "connectionLogCopied": "連線日誌已複製到剪貼簿", - "connectionLogCopyFailed": "無法將日誌複製到剪貼簿", - "connectionRejected": "伺服器拒絕連線。請檢查您的身份驗證和網路配置。", - "hostKeyRejected": "SSH主機金鑰驗證失敗。連線已取消。", - "sessionTakenOver": "會話已在另一個標籤頁中開啟。正在重新連線…", + "websocketAbnormalClose": "連線意外關閉。這可能是由於反向代理或 SSL 設定問題所導致。請檢查伺服器記錄。", + "connectionLogTitle": "連線記錄", + "connectionLogCopy": "複製記錄到剪貼簿", + "connectionLogEmpty": "尚無連線記錄", + "connectionLogWaiting": "正在等待連線記錄…", + "connectionLogCopied": "連線記錄已複製到剪貼簿", + "connectionLogCopyFailed": "無法將連線記錄複製到剪貼簿", + "connectionRejected": "連線被伺服器拒絕。請檢查您的驗證與網路設定。", + "hostKeyRejected": "SSH 主機金鑰驗證被拒絕。連線已取消。", + "sessionTakenOver": "工作階段已在另一個分頁中開啟。正在重新連線…", "split": { - "splitTab": "分頁標籤", - "addToSplit": "添加到拆分", + "splitTab": "分割分頁", + "addToSplit": "加入分割", "removeFromSplit": "從分割中移除" } }, "fileManager": { - "noHostSelected": "未選擇主機", - "initializingEditor": "正在初始化編輯器...", - "file": "文件", + "noHostSelected": "未選取主機", + "initializingEditor": "正在初始化編輯器…", + "file": "檔案", "folder": "資料夾", - "uploadFile": "上傳文件", + "uploadFile": "上傳檔案", "downloadFile": "下載", - "extractArchive": "提取存檔", - "extractingArchive": "正在提取 {{name}}...", - "archiveExtractedSuccessfully": "{{name}} 提取成功", - "extractFailed": "提取失敗", + "extractArchive": "解壓縮", + "extractingArchive": "正在解壓縮 {{name}}...", + "archiveExtractedSuccessfully": "{{name}} 解壓縮成功", + "extractFailed": "解壓縮失敗", "compressFile": "壓縮檔案", "compressFiles": "壓縮檔案", - "compressFilesDesc": "將 {{count}} 個項目壓縮到一個檔案中", - "archiveName": "存檔名稱", - "enterArchiveName": "請輸入存檔名稱...", + "compressFilesDesc": "將 {{count}} 個項目壓縮為一個壓縮檔", + "archiveName": "壓縮檔名稱", + "enterArchiveName": "輸入壓縮檔名稱...", "compressionFormat": "壓縮格式", - "selectedFiles": "選定文件", - "andMoreFiles": "and {{count}} more...", + "selectedFiles": "已選取的檔案", + "andMoreFiles": "還有 {{count}} 個...", "compress": "壓縮", - "compressingFiles": "將 {{count}} 個項目壓縮到 {{name}}...", - "filesCompressedSuccessfully": "{{name}} 創建成功", + "compressingFiles": "正在將 {{count}} 個項目壓縮為 {{name}}...", + "filesCompressedSuccessfully": "{{name}} 建立成功", "compressFailed": "壓縮失敗", "edit": "編輯", "preview": "預覽", - "previous": "以前的", + "previous": "上一個", "next": "下一個", "pageXOfY": "第 {{current}} 頁,共 {{total}} 頁", "zoomOut": "縮小", "zoomIn": "放大", - "newFile": "新文件", - "newFolder": "新建資料夾", + "newFile": "新增檔案", + "newFolder": "新增資料夾", "rename": "重新命名", - "uploading": "正在上傳...", + "uploading": "上傳中...", "uploadingFile": "正在上傳 {{name}}...", "fileName": "檔案名稱", "folderName": "資料夾名稱", - "fileUploadedSuccessfully": "檔案「{{name}}」已成功上傳", - "failedToUploadFile": "文件上傳失敗", - "fileDownloadedSuccessfully": "檔案「{{name}}」已成功下載", - "failedToDownloadFile": "文件下載失敗", - "fileCreatedSuccessfully": "檔案「{{name}}」已成功建立", - "folderCreatedSuccessfully": "資料夾「{{name}}」已成功創建", - "failedToCreateItem": "建立專案失敗", - "operationFailed": "{{operation}} 操作對 {{name}}: {{error}} 失敗", - "failedToResolveSymlink": "解析符號連結失敗", - "itemsDeletedSuccessfully": "{{count}} 項目已成功刪除", + "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": "搜尋文件...", + "sudoOperationFailed": "Sudo 操作失敗", + "sudoAuthFailed": "Sudo 驗證失敗", + "dragFilesToUpload": "拖放檔案至此以上傳", + "emptyFolder": "此資料夾為空", + "searchFiles": "搜尋檔案...", "upload": "上傳", - "selectHostToStart": "選擇主機以啟動檔案管理", - "sshRequiredForFileManager": "檔案管理器需要 SSH 連線。此主機未啟用 SSH 連線。", - "failedToConnect": "SSH連線失敗。", + "selectHostToStart": "選擇主機以開始檔案管理", + "sshRequiredForFileManager": "檔案管理員需要 SSH。此主機未啟用 SSH。", + "failedToConnect": "無法連線至 SSH", "failedToLoadDirectory": "載入目錄失敗", - "noSSHConnection": "沒有可用的 SSH 連接", + "noSSHConnection": "沒有可用的 SSH 連線", "copy": "複製", - "cut": "切", + "cut": "剪下", "paste": "貼上", "copyPath": "複製路徑", "copyPaths": "複製路徑", "delete": "刪除", - "properties": "特性", + "properties": "內容", "refresh": "重新整理", - "downloadFiles": "下載 {{count}} 檔案到瀏覽器", - "copyFiles": "複製 {{count}} 項", - "cutFiles": "剪力 {{count}} 項", - "deleteFiles": "刪除 {{count}} 項", - "filesCopiedToClipboard": "{{count}} 項目已複製到剪貼簿", - "filesCutToClipboard": "{{count}} 項目已複製到剪貼簿", + "downloadFiles": "下載 {{count}} 個檔案到瀏覽器", + "copyFiles": "複製 {{count}} 個項目", + "cutFiles": "剪下 {{count}} 個項目", + "deleteFiles": "刪除 {{count}} 個項目", + "filesCopiedToClipboard": "{{count}} 個項目已複製到剪貼簿", + "filesCutToClipboard": "{{count}} 個項目已剪下到剪貼簿", "pathCopiedToClipboard": "路徑已複製到剪貼簿", - "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", - "failedToCopyPath": "無法將路徑複製到剪貼簿", + "pathsCopiedToClipboard": "{{count}} 個路徑已複製到剪貼簿", + "failedToCopyPath": "無法複製路徑到剪貼簿", "copyFolderLink": "複製資料夾連結", - "copyCurrentFolderLink": "複製當前資料夾的連結", + "copyCurrentFolderLink": "複製目前資料夾連結", "folderLinkCopied": "資料夾連結已複製到剪貼簿", - "failedToCopyFolderLink": "複製資料夾連結失敗", + "failedToCopyFolderLink": "無法複製資料夾連結", "movedItems": "已移動 {{count}} 個項目", - "failedToDeleteItem": "刪除項目失敗", - "itemRenamedSuccessfully": "{{type}} 已成功重新命名", - "failedToRenameItem": "重新命名專案失敗", + "failedToDeleteItem": "無法刪除項目", + "itemRenamedSuccessfully": "{{type}} 重新命名成功", + "failedToRenameItem": "無法重新命名項目", "download": "下載", - "openExternalEditor": "向外開放", + "openExternalEditor": "以外部編輯器開啟", "chooseExternalEditor": "選擇外部編輯器", - "externalEditorSelected": "外部編輯已選定", - "externalEditorDesktopOnly": "外部編輯器僅在桌面應用程式中可用", - "externalEditorOpened": "在外部編輯器中開啟。儲存後,變更將上傳回伺服器。", + "externalEditorSelected": "已選擇外部編輯器", + "externalEditorDesktopOnly": "外部編輯器僅限桌面版應用程式使用", + "externalEditorOpened": "已於外部編輯器開啟。在此儲存將上傳變更回伺服器。", "failedToOpenExternalEditor": "無法開啟外部編輯器", - "failedToSelectExternalEditor": "選擇外部編輯器失敗", + "failedToSelectExternalEditor": "無法選擇外部編輯器", "permissions": "權限", - "size": "尺寸", - "modified": "修改的", - "path": "小路", - "confirmDelete": "您確定要刪除 {{name}} 嗎?", - "permissionDenied": "沒有權限", + "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": "只能運行可執行文件", + "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}}”", + "removedFromRecentFiles": "已從最近檔案中移除 \"{{name}}\"", "removeFailed": "移除失敗", - "unpinnedSuccessfully": "已成功取消置頂“{{name}}”", - "unpinFailed": "解除鎖定失敗", - "removedShortcut": "已移除捷徑“{{name}}”", - "removeShortcutFailed": "移除快捷方式失敗", - "clearedAllRecentFiles": "已清除所有最近文件", + "unpinnedSuccessfully": "已成功取消釘選 \"{{name}}\"", + "unpinFailed": "取消釘選失敗", + "removedShortcut": "已移除捷徑 \"{{name}}\"", + "removeShortcutFailed": "移除捷徑失敗", + "clearedAllRecentFiles": "已清除所有最近檔案", "clearFailed": "清除失敗", - "removeFromRecentFiles": "從最近文件中刪除", - "clearAllRecentFiles": "清除所有最近文件", - "unpinFile": "解壓縮文件", - "removeShortcut": "移除快捷方式", - "pinFile": "針文件", - "addToShortcuts": "新增到快捷方式", + "removeFromRecentFiles": "從最近檔案中移除", + "clearAllRecentFiles": "清除所有最近檔案", + "unpinFile": "取消釘選檔案", + "removeShortcut": "移除捷徑", + "pinFile": "釘選檔案", + "addToShortcuts": "加入捷徑", "pasteFailed": "貼上失敗", - "noUndoableActions": "沒有可撤銷的操作", - "undoCopySuccess": "撤銷複製操作:已刪除 {{count}} 個已複製的文件", - "undoCopyFailedDelete": "撤銷失敗:無法刪除任何已複製的文件", - "undoCopyFailedNoInfo": "撤銷失敗:找不到已複製的文件訊息", - "undoMoveSuccess": "撤銷移動操作:已將 {{count}} 個檔案移回原始位置", - "undoMoveFailedMove": "撤銷失敗:無法將任何檔案移回。", - "undoMoveFailedNoInfo": "撤銷失敗:找不到已移動的文件訊息", - "undoDeleteNotSupported": "刪除操作無法撤銷:檔案已從伺服器永久刪除。", - "undoTypeNotSupported": "不支援的撤銷操作類型", - "undoOperationFailed": "撤銷操作失敗", + "noUndoableActions": "沒有可復原的動作", + "undoCopySuccess": "已復原複製操作:已刪除 {{count}} 個複製的檔案", + "undoCopyFailedDelete": "復原失敗:無法刪除任何複製的檔案", + "undoCopyFailedNoInfo": "復原失敗:找不到複製檔案的資訊", + "undoMoveSuccess": "已復原移動操作:已將 {{count}} 個檔案移回原始位置", + "undoMoveFailedMove": "復原失敗:無法移回任何檔案", + "undoMoveFailedNoInfo": "復原失敗:找不到移動檔案的資訊", + "undoDeleteNotSupported": "刪除操作無法復原:檔案已從伺服器永久刪除", + "undoTypeNotSupported": "不支援的復原操作類型", + "undoOperationFailed": "復原操作失敗", "unknownError": "未知錯誤", "confirm": "確認", "find": "尋找...", - "replace": "代替", - "downloadInstead": "下載", - "keyboardShortcuts": "鍵盤快速鍵", - "searchAndReplace": "搜尋和替換", + "replace": "取代", + "downloadInstead": "改為下載", + "keyboardShortcuts": "鍵盤快捷鍵", + "searchAndReplace": "搜尋與取代", "editing": "編輯", "search": "搜尋", "findNext": "尋找下一個", "findPrevious": "尋找上一個", - "save": "節省", + "save": "儲存", "selectAll": "全選", - "undo": "撤銷", + "undo": "復原", "redo": "重做", - "moveLineUp": "移動陣容", - "moveLineDown": "向下移動線路", - "toggleComment": "切換評論", + "moveLineUp": "將行上移", + "moveLineDown": "將行下移", + "toggleComment": "切換註解", "autoComplete": "自動完成", - "imageLoadError": "圖片載入失敗", - "startTyping": "開始輸入…", - "unknownSize": "尺寸未知", - "fileIsEmpty": "文件為空", - "largeFileWarning": "大文件警告", - "largeFileWarningDesc": "此檔案大小為 {{size}} ,以文字格式開啟時可能會導致效能問題。", - "fileNotFoundAndRemoved": "檔案「{{name}}」未找到,已從最近/置頂檔案移除。", - "failedToLoadFile": "檔案載入失敗: {{error}}", - "serverErrorOccurred": "伺服器出錯,請稍後再試。", + "imageLoadError": "無法載入圖片", + "startTyping": "開始輸入...", + "unknownSize": "未知大小", + "fileIsEmpty": "檔案為空", + "largeFileWarning": "大檔案警告", + "largeFileWarningDesc": "此檔案大小為 {{size}},以文字模式開啟可能會影響效能。", + "fileNotFoundAndRemoved": "找不到檔案 \"{{name}}\",已從最近使用的/已釘選的檔案中移除", + "failedToLoadFile": "無法載入檔案:{{error}}", + "serverErrorOccurred": "伺服器發生錯誤,請稍後再試。", "autoSaveFailed": "自動儲存失敗", - "fileAutoSaved": "文件自動儲存", - "moveFileFailed": "移動失敗 {{name}}", + "fileAutoSaved": "檔案已自動儲存", + "moveFileFailed": "無法移動 {{name}}", "moveOperationFailed": "移動操作失敗", - "canOnlyCompareFiles": "只能比較兩個文件", - "comparingFiles": "正在比較文件: {{file1}} 和 {{file2}}", + "canOnlyCompareFiles": "只能比較兩個檔案", + "comparingFiles": "正在比較檔案:{{file1}} 與 {{file2}}", "dragFailed": "拖曳操作失敗", - "filePinnedSuccessfully": "文件「{{name}}」已成功固定", - "pinFileFailed": "文件固定失敗", - "fileUnpinnedSuccessfully": "文件「{{name}}」已成功取消固定", - "unpinFileFailed": "取消固定文件失敗", - "shortcutAddedSuccessfully": "資料夾捷徑「{{name}}」已成功新增", - "addShortcutFailed": "新增快捷方式失敗", - "operationCompletedSuccessfully": "{{operation}} {{count}} 個專案已成功", - "operationCompleted": "{{operation}} {{count}} 項目", - "downloadFileSuccess": "檔案 {{name}} 下載成功", + "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}}", + "moveTo": "移動至 {{name}}", + "diffCompareWith": "與 {{name}} 進行差異比對", + "dragOutsideToDownload": "拖曳至視窗外以下載 ({{count}} 個檔案)", + "newFolderDefault": "新資料夾", + "newFileDefault": "新檔案.txt", + "successfullyMovedItems": "已成功移動 {{count}} 個項目至 {{target}}", "move": "移動", - "searchInFile": "在文件中搜尋(Ctrl+F)", - "showKeyboardShortcuts": "顯示鍵盤快速鍵", - "decreaseFontSize": "減小字體大小", - "increaseFontSize": "增大字體大小", - "startWritingMarkdown": "開始寫你的 Markdown 內容…", - "loadingFileComparison": "正在載入檔案比較...", + "searchInFile": "在檔案中搜尋 (Ctrl+F)", + "showKeyboardShortcuts": "顯示鍵盤快捷鍵", + "decreaseFontSize": "縮小字型大小", + "increaseFontSize": "放大字型大小", + "startWritingMarkdown": "開始撰寫您的 Markdown 內容...", + "loadingFileComparison": "載入檔案比對中...", "reload": "重新載入", - "compare": "比較", + "compare": "比對", "sideBySide": "並排", - "inline": "排隊", - "fileComparison": "文件比較: {{file1}} vs {{file2}}", - "fileTooLarge": "文件過大: {{error}}", - "sshConnectionFailed": "SSH 連線失敗。請檢查您與 {{name}} ({{ip}}:{{port}} ) 的連接。", - "loadFileFailed": "檔案載入失敗: {{error}}", - "connecting": "正在連接...", - "connectedSuccessfully": "連線成功", - "totpVerificationFailed": "TOTP驗證失敗", - "warpgateVerificationFailed": "Warpgate 身份驗證失敗", - "authenticationFailed": "身份驗證失敗", - "incorrectPassphrase": "密碼錯誤,請重試。", + "inline": "內嵌", + "fileComparison": "檔案比對:{{file1}} vs {{file2}}", + "fileTooLarge": "檔案過大:{{error}}", + "sshConnectionFailed": "SSH 連線失敗。請檢查與 {{name}} ({{ip}}:{{port}}) 的連線", + "loadFileFailed": "載入檔案失敗:{{error}}", + "connecting": "連線中...", + "connectedSuccessfully": "已成功連線", + "totpVerificationFailed": "TOTP 驗證失敗", + "warpgateVerificationFailed": "Warpgate 驗證失敗", + "authenticationFailed": "驗證失敗", + "incorrectPassphrase": "密碼片語錯誤。請再試一次。", "verificationCodePrompt": "驗證碼:", - "changePermissions": "更改權限", + "changePermissions": "變更權限", "currentPermissions": "目前權限", "owner": "擁有者", - "group": "團體", - "others": "其他的", - "read": "讀", - "write": "寫", + "group": "群組", + "others": "其他人", + "read": "讀取", + "write": "寫入", "execute": "執行", - "permissionsChangedSuccessfully": "權限已成功更改", - "failedToChangePermissions": "更改權限失敗", - "name": "姓名", - "sortByName": "姓名", + "permissionsChangedSuccessfully": "權限已成功變更", + "failedToChangePermissions": "無法變更權限", + "name": "名稱", + "sortByName": "名稱", "sortByDate": "修改日期", - "sortBySize": "尺寸", - "ascending": "上升", - "descending": "下降", - "root": "根", - "new": "新的", - "sortBy": "排序方式", - "items": "專案", - "selected": "已選", - "editor": "編輯", + "sortBySize": "大小", + "ascending": "遞增", + "descending": "遞減", + "root": "根目錄", + "new": "新增", + "sortBy": "排序依據", + "items": "項目", + "selected": "已選取", + "editor": "編輯器", "octal": "八進位", - "storage": "貯存", + "storage": "儲存空間", "disk": "磁碟", - "used": "用過的", - "of": "的", + "used": "已使用", + "of": " / ", "toggleSidebar": "切換側邊欄", - "cannotLoadPdf": "無法載入PDF", - "pdfLoadError": "載入此PDF檔案時出錯。", - "loadingPdf": "正在載入PDF文件...", - "loadingPage": "頁面正在加載..." + "cannotLoadPdf": "無法載入 PDF", + "pdfLoadError": "載入此 PDF 檔案時發生錯誤。", + "loadingPdf": "正在載入 PDF...", + "loadingPage": "正在載入頁面..." }, "transfer": { "copyToHost": "複製到主機…", - "moveToHost": "移至主機…", - "copyItemsToHost": "將 {{count}} 項目複製到主機…", - "moveItemsToHost": "將 {{count}} 個物品移至主機…", - "noHostsConnected": "沒有其他檔案總管主機可用。", - "noHostsConnectedHint": "在主機管理員中新增另一台啟用了檔案管理器的 SSH 主機。", + "moveToHost": "移動到主機…", + "copyItemsToHost": "複製 {{count}} 個項目到主機…", + "moveItemsToHost": "將 {{count}} 個項目移至主機…", + "noHostsConnected": "無其他可用的檔案管理員主機。", + "noHostsConnectedHint": "在主機管理員中新增另一個已啟用檔案管理員的 SSH 主機。", "selectDestinationHost": "選擇目標主機", "destinationPath": "目標路徑", - "recentDestinations": "近期目的地", - "collapseRecentDestinations": "收起最近的目的地", - "expandRecentDestinations": "展開最近目的地", + "recentDestinations": "最近使用的目的地", + "collapseRecentDestinations": "收起最近使用的目的地", + "expandRecentDestinations": "展開最近使用的目的地", "browseFolders": "瀏覽目標資料夾", "browseDestination": "瀏覽或輸入路徑", "confirmCopy": "複製", "confirmMove": "移動", - "transferring": "轉移…", - "compressing": "正在壓縮…", - "extracting": "提取…", - "transferringItems": "轉移 {{current}} 個物品,共 {{total}} 個物品,共… 個物品。", - "transferSuccess": "轉帳完成", - "transferError": "轉帳失敗", - "transferPartial": "轉帳完成,但出現 {{count}} 錯誤", - "transferPartialHint": "轉帳失敗: {{paths}}", - "itemsSummary": "{{count}} 項目", - "destMustBeDirectory": "對於多項傳輸,目標位置必須是目錄。", + "transferring": "傳送中…", + "compressing": "壓縮中…", + "extracting": "解壓縮中…", + "transferringItems": "正在傳送 {{total}} 個項目中的第 {{current}} 個…", + "transferSuccess": "傳送完成", + "transferError": "傳送失敗", + "transferPartial": "傳送完成,有 {{count}} 個錯誤", + "transferPartialHint": "無法傳送:{{paths}}", + "itemsSummary": "{{count}} 個項目", + "destMustBeDirectory": "多項目傳送時,目標必須是資料夾", "selectThisFolder": "選擇此資料夾", - "browsePathWillBeCreated": "此資料夾尚不存在。傳輸開始時將建立此資料夾。", + "browsePathWillBeCreated": "此資料夾尚不存在。傳送開始時將會建立。", "browsePathError": "無法在目標主機上開啟此路徑。", - "goUp": "上", - "copyFolderToHost": "將資料夾複製到主機…", - "moveFolderToHost": "將資料夾移至主機…", - "hostReady": "準備好", - "hostConnecting": "正在連接…", - "hostDisconnected": "未連接", - "hostAuthRequired": "需要身份驗證-請先在此主機上開啟檔案總管。", + "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}})…", + "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}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} 文件", + "progressItems": "{{current}} / {{total}} 個檔案", "sourceNotDeletedPartial": "保留來源檔案(部分傳輸)", - "jumpHostLimitation": "兩台主機都必須能從Termix伺服器存取。不支援主機之間的直接路由。", + "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 流,以單一檔案複製的方式依序進行。所有檔案的進度是合併計算的,因此在處理大檔案時進度條移動較慢。", + "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 串流作為單一檔案複製。進度會合併所有檔案,因此處理大型檔案時進度條會緩慢移動。", "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": "您選擇了逐檔 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。" }, "progressCancel": "取消", - "progressCancelling": "取消…", + "progressCancelling": "正在取消…", "progressStalled": "停滯", - "resumedHint": "已重新連線到在另一個視窗中啟動的活動傳輸。", - "transferCancelled": "轉帳已取消", - "transferCancelledCopyHint": "目標位置可能仍保留部分檔案。", - "transferCancelledMoveHint": "目標位置可能仍保留部分檔案。來源檔案未被刪除。", + "resumedHint": "已重新連線至在另一個視窗中啟動的進行中傳輸。", + "transferCancelled": "傳輸已取消", + "transferCancelledCopyHint": "部分檔案可能仍留在目的地。", + "transferCancelledMoveHint": "部分檔案可能仍留在目的地,來源檔案未被移除。", "cleanupDestFiles": "清理目的地", - "cleanupDestFilesSuccess": "已從目標位置刪除部分文件", - "cleanupDestFilesPartial": "部分檔案無法刪除。", + "cleanupDestFilesSuccess": "已從目的地移除部分檔案", + "cleanupDestFilesPartial": "部分檔案無法移除", "cleanupDestFilesNothing": "目的地無需清理", "cleanupDestFilesError": "清理失敗", "retryTransfer": "重試", "retryTransferError": "重試失敗", - "transferFailedRetryHint": "目標端保留了部分資料。連線恢復後,系統將恢復重試。" + "transferFailedRetryHint": "部分資料已保留在目的地。重新連線後,重試將從斷點繼續。" }, "tunnels": { - "noSshTunnels": "沒有 SSH 隧道", - "createFirstTunnelMessage": "您尚未建立任何 SSH 隧道。請在主機管理員中設定隧道連線以開始使用。", - "connected": "已連接", - "disconnected": "斷開連接", - "connecting": "正在連接...", + "noSshTunnels": "無 SSH 通道", + "createFirstTunnelMessage": "您尚未建立任何 SSH 通道。請在主機管理員中設定通道連線以開始使用。", + "connected": "已連線", + "disconnected": "已離線", + "connecting": "連線中…", "error": "錯誤", "canceling": "正在取消…", - "connect": "連接", - "disconnect": "斷開", + "connect": "連線", + "disconnect": "中斷連線", "cancel": "取消", - "port": "港口", - "localPort": "本地港口", - "remotePort": "遠端埠", + "port": "連接埠", + "localPort": "本機連接埠", + "remotePort": "遠端連接埠", "currentHostPort": "目前主機連接埠", - "endpointPort": "端點埠", - "bindIp": "本地IP", - "endpointSshConfig": "端點 SSH 配置", + "endpointPort": "端點連接埠", + "bindIp": "本機 IP", + "endpointSshConfig": "端點 SSH 設定", "endpointSshHost": "端點 SSH 主機", - "endpointSshHostPlaceholder": "選擇已配置的主機", - "endpointSshHostRequired": "為每個客戶端隧道選擇一個 SSH 主機端點。", - "attempt": "試 {{current}} 的 {{max}}", - "nextRetryIn": "下次重試時間為 {{seconds}} 秒", - "clientTunnels": "用戶端隧道", - "clientTunnel": "用戶端隧道", - "addClientTunnel": "新增客戶端隧道", - "noClientTunnels": "此桌面未配置客戶端隧道。", - "tunnelName": "隧道名稱", + "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": "開始", + "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 流量。", + "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}}。", + "forwardDescriptionClientLocal": "本機 {{sourcePort}} → 遠端 {{endpointPort}}。", + "forwardDescriptionClientRemote": "遠端 {{sourcePort}} → 本機 {{endpointPort}}。", + "forwardDescriptionClientDynamic": "本機連接埠 {{sourcePort}} 上的 SOCKS。", "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": "上次開始", + "autoNameClientLocal": "本機 {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → 本機 {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} 透過 {{endpoint}}", + "route": "路由:", + "lastStarted": "上次啟動", "lastTested": "上次測試", - "lastError": "最後錯誤", + "lastError": "上次錯誤", "maxRetries": "最大重試次數", "maxRetriesDescription": "最大重試次數。", "retryInterval": "重試間隔(秒)", - "retryIntervalDescription": "重試之間需要等待一段時間。", - "local": "當地的", - "remote": "偏僻的", + "retryIntervalDescription": "每次重試之間的等待時間。", + "local": "本機", + "remote": "遠端", "destination": "目的地", - "host": "主持人", + "host": "主機", "mode": "模式", - "noHostSelected": "未選擇主機", - "working": "在職的..." + "noHostSelected": "未選取主機", + "working": "處理中..." }, "cardGrid": { - "dragToMove": "拖曳移動", - "dragToResize": "拖曳調整大小", - "changeWidth": "改變寬度", + "dragToMove": "拖曳以移動", + "dragToResize": "拖曳以調整大小", + "changeWidth": "變更寬度", "removeCard": "移除 {{label}}", - "addCard": "添加", - "columns": "列", - "empty": "暫無卡牌。請使用下方的「新增」按鈕放置卡片。" + "addCard": "新增", + "columns": "欄", + "empty": "無卡片。使用下方的新增按鈕來放置卡片。" }, "hostMetrics": { - "cpu": "中央處理器", - "memory": "記憶", + "cpu": "CPU", + "memory": "記憶體", "disk": "磁碟", "network": "網路", - "uptime": "正常運作時間", - "processes": "流程", - "available": "可用的", - "free": "自由的", - "connecting": "正在連接...", - "connectionFailed": "連線伺服器失敗", + "uptime": "運作時間", + "processes": "程序", + "available": "可用", + "free": "可用", + "connecting": "連線中...", + "connectionFailed": "無法連線至伺服器", "naCpus": "N/A CPU", "cpuCores_one": "{{count}} 核心", "cpuCores_other": "{{count}} 核心", "cpuUsage": "CPU 使用率", - "memoryUsage": "記憶體使用情況", - "diskUsage": "磁碟使用情況", + "memoryUsage": "記憶體使用率", + "diskUsage": "磁碟使用率", "temperature": "溫度", "highestTemperature": "最高溫度", - "failedToFetchHostConfig": "取得主機配置失敗", + "failedToFetchHostConfig": "無法取得主機設定", "serverOffline": "伺服器離線", "cannotFetchMetrics": "無法從離線伺服器取得指標", - "totpFailed": "TOTP驗證失敗", - "noneAuthNotSupported": "主機指標不支援“無”身份驗證類型。", + "totpFailed": "TOTP 驗證失敗", + "noneAuthNotSupported": "主機指標不支援 'none' 驗證類型。", "noHostSelected": "未選擇主機", - "load": "載入", + "load": "負載", "systemInfo": "系統資訊", "hostname": "主機名稱", "operatingSystem": "作業系統", "kernel": "核心", "seconds": "秒", "networkInterfaces": "網路介面", - "noInterfacesFound": "未找到網路介面", - "noProcessesFound": "未找到任何進程", - "processesTotal": "全部的", - "processesRunning": "跑步", - "loginStats": "SSH 登入統計訊息", - "noRecentLoginData": "沒有最近的登入數據", + "noInterfacesFound": "找不到網路介面", + "noProcessesFound": "找不到程序", + "processesTotal": "總計", + "processesRunning": "執行中", + "loginStats": "SSH 登入統計資訊", + "noRecentLoginData": "沒有最近的登入資料", "executingQuickAction": "正在執行 {{name}}...", "quickActionSuccess": "{{name}} 已成功完成", "quickActionFailed": "{{name}} 失敗", - "quickActionError": "執行 {{name}} 失敗", + "quickActionError": "無法執行 {{name}}", "ports": { - "title": "監聽埠", - "protocol": "協定", - "port": "港口", - "address": "地址", - "process": "流程", + "title": "監聽中的連接埠", + "protocol": "通訊協定", + "port": "連接埠", + "address": "位址", + "process": "程序", "search": "搜尋連接埠...", "allProtocols": "全部", - "noData": "無監聽埠數據" + "noData": "無監聽連接埠資料" }, "firewall": { "title": "防火牆", - "inactive": "非活躍狀態", - "policy": "政策", + "inactive": "未啟用", + "policy": "原則", "rules": "規則", - "noRules": "這條鏈沒有規則", - "noData": "沒有可用的防火牆數據", - "action": "行動", - "protocol": "原型", - "port": "港口", + "noRules": "此鏈中無規則", + "noData": "無防火牆資料可用", + "action": "動作", + "protocol": "通訊協定", + "port": "連接埠", "source": "來源", - "anywhere": "任何地方", - "chains": "鏈條" + "anywhere": "任意位置", + "chains": "鏈" }, "loadAvg": "平均負載", - "swap": "交換", - "architecture": "建築學", + "swap": "交換空間", + "architecture": "架構", "refresh": "重新整理", "retry": "重試", - "customize": "自訂佈局", - "reset": "重置", - "tabLive": "居住", - "editModeInstructions": "拖曳卡片即可重新排列,拖曳底部邊緣可調整大小,使用寬度按鈕可變更卡片的跨度。在下方新增或刪除卡片。", + "customize": "自訂版面配置", + "reset": "重設", + "tabLive": "即時", + "editModeInstructions": "拖曳卡片以重新排列,拖曳底部邊緣以調整大小,並使用寬度按鈕變更卡片跨度。於下方新增或移除卡片。", "managers": { "services": "服務", - "processInspector": "過程檢驗員", + "processInspector": "程序檢查器", "logViewer": "日誌檢視器", - "cron": "定時任務", - "packages": "包裹", - "ssl": "SSL憑證", + "cron": "Cron 工作", + "packages": "套件", + "ssl": "SSL 憑證", "firewall": "防火牆", "users": "使用者與權限", "healthCheck": "健康檢查", - "diskBreakdown": "磁碟故障", - "systemdTimers": "定時器", - "topMemory": "記憶頂部", - "noData": "無數據", + "diskBreakdown": "磁碟明細", + "systemdTimers": "計時器", + "topMemory": "記憶體用量排行", + "noData": "無資料", "sudoHint": "在主機編輯器中為此主機設定 sudo 密碼,以啟用特權操作。", "filter": "篩選...", - "start": "開始", + "start": "啟動", "stop": "停止", - "restart": "重啟", + "restart": "重新啟動", "actionDone": "{{name}} 已更新", "actionFailed": "操作失敗", - "signalSent": "向 PID {{pid}} 發送訊號", - "killHint": "點選:終止(SIGTERM)。右鍵點選:強制終止(SIGKILL)。", - "working": "在職的...", + "signalSent": "訊號已傳送至 PID {{pid}}", + "killHint": "按一下:終止 (SIGTERM)。按右鍵:強制終止 (SIGKILL)。", + "working": "處理中...", "update": "更新", "upgradeAll": "全部升級", - "allUpToDate": "一切都是最新的", - "save": "節省", - "command": "命令", + "allUpToDate": "所有項目皆為最新", + "save": "儲存", + "command": "指令", "enabled": "已啟用", "cronSaved": "Crontab 已更新", - "clients": "客戶", - "dryRun": "試運行", - "renew": "更新", - "noAcmeClient": "在此主機上未找到 ACME 用戶端(certbot 或 acme.sh)。", - "addInputRule": "新增輸入規則", - "firewallWarning": "變更僅在運行時生效,直到持久化為止。請注意不要將自己鎖在外部。", - "ruleApplied": "適用規則", - "invalidPort": "請輸入有效連接埠號碼(1-65535)", - "newUsername": "新用戶名", - "addUser": "添加", - "deleteUser": "刪除用戶", - "noHealthChecks": "尚未配置任何健康檢查。", - "follow": "跟隨", + "clients": "客戶端", + "dryRun": "試執行", + "renew": "續期", + "noAcmeClient": "此主機上找不到 ACME 用戶端 (certbot 或 acme.sh)。", + "addInputRule": "新增 INPUT 規則", + "firewallWarning": "變更僅在執行階段有效,儲存後才會永久生效。小心切勿將自己鎖定在外。", + "ruleApplied": "規則已套用", + "invalidPort": "輸入有效的連接埠 (1-65535)", + "newUsername": "新使用者名稱", + "addUser": "新增", + "deleteUser": "刪除使用者", + "noHealthChecks": "尚未設定健康檢查。", + "follow": "追蹤", "noLogData": "無日誌輸出。", - "tree": "樹", - "enableDisable": "啟動時啟用/停用", - "grantSudo": "授予 sudo", - "revokeSudo": "撤銷 sudo", - "sslIssueCert": "簽發證書", - "sslExpired": "已到期", - "sslInDays": "在 {{days}}d", - "sslNeedDomain": "請至少輸入一個域名", - "sslIssued": "簽發證書", - "sslDomainsPlaceholder": "example.com,www.example.com", - "sslHttpStandalone": "HTTP(獨立)", - "sslHttpWebroot": "HTTP(webroot)", + "tree": "樹狀", + "enableDisable": "開機時啟用 / 停用", + "grantSudo": "授予 sudo 權限", + "revokeSudo": "撤銷 sudo 權限", + "sslIssueCert": "頒發憑證", + "sslExpired": "已過期", + "sslInDays": "{{days}} 天內", + "sslNeedDomain": "至少輸入一個網域", + "sslIssued": "憑證已頒發", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (獨立模式)", + "sslHttpWebroot": "HTTP (webroot)", "sslDns": "DNS", - "sslDnsProvider": "DNS 提供者(例如 Cloudflare)", - "sslIssueHint": "主機上必須已設定 DNS 提供者憑證。", - "sslRevoke": "撤銷證書", - "sslRevoked": "證書已撤銷", - "sslRevokeConfirm": "撤銷並刪除憑證「{{name}}」?此操作無法撤銷。", - "healthRun": "跑步", - "healthName": "姓名", - "healthTarget": "主機/位址", + "sslDnsProvider": "DNS 提供者 (例如 cloudflare)", + "sslIssueHint": "DNS 提供者的憑證必須已在主機上設定。", + "sslRevoke": "撤銷憑證", + "sslRevoked": "憑證已撤銷", + "sslRevokeConfirm": "撤銷並移除憑證「{{name}}」?此操作無法復原。", + "healthRun": "執行", + "healthName": "名稱", + "healthTarget": "主機 / 位址", "healthAddCheck": "新增檢查", - "healthSaved": "健康檢查已保存", - "healthMissingFields": "每項檢查都需要名稱和目標", - "logFile": "文件", + "healthSaved": "健康檢查已儲存", + "healthMissingFields": "每個檢查都需要名稱和目標", + "logFile": "檔案", "logUnit": "單元", - "logCustomPath": "/var/log 下的自訂路徑(可選)", - "logGrep": "過濾線…", - "firewallPersist": "持久化規則", - "firewallPersisted": "防火牆規則保留", + "logCustomPath": "/var/log 下的自訂路徑 (選用)", + "logGrep": "篩選行...", + "firewallPersist": "永久儲存規則", + "firewallPersisted": "防火牆規則已永久儲存", "wireguard": "WireGuard", - "tailscale": "尾鱗", + "tailscale": "Tailscale", "wgNotInstalled": "此主機上未安裝 WireGuard", - "wgNoInterfaces": "未配置 WireGuard 接口", - "wgInterfaceUp": "提起", - "wgInterfaceDown": "擊倒", - "wgBringingUp": "提起 {{name}}...", - "wgBringingDown": "把 {{name}} 拉下來…", - "wgInterfaceUpDone": "{{name}} 已開啟", - "wgInterfaceDownDone": "{{name}} 已關閉", - "wgListenPort": "港口", - "wgPublicKey": "公鑰", + "wgNoInterfaces": "未設定任何 WireGuard 介面", + "wgInterfaceUp": "啟用", + "wgInterfaceDown": "停用", + "wgBringingUp": "正在啟用 {{name}}...", + "wgBringingDown": "正在停用 {{name}}...", + "wgInterfaceUpDone": "{{name}} 已啟用", + "wgInterfaceDownDone": "{{name}} 已停用", + "wgListenPort": "連接埠", + "wgPublicKey": "公開金鑰", "wgEndpoint": "端點", - "wgAllowedIPs": "允許的IP位址", - "wgLastHandshake": "最後一次握手", - "wgHandshakeNever": "絕不", - "wgTransfer": "轉移", + "wgAllowedIPs": "允許的 IP", + "wgLastHandshake": "上次交握", + "wgHandshakeNever": "從未", + "wgTransfer": "傳輸", "tsNotInstalled": "此主機上未安裝 Tailscale", - "tsRunning": "跑步", - "tsStopped": "停止", - "tsEnable": "連接", - "tsDisable": "斷開", - "tsEnabling": "正在連接到Tailscale...", - "tsDisabling": "正在斷開與Tailscale的連接…", - "tsEnabled": "Tailscale 連接", - "tsDisabled": "Tailscale 已斷開連接", - "tsIPs": "尾規模 IP", - "tsPeers": "同齡人", - "tsOnline": "在線的", + "tsRunning": "執行中", + "tsStopped": "已停止", + "tsEnable": "連線", + "tsDisable": "中斷連線", + "tsEnabling": "正在連線至 Tailscale...", + "tsDisabling": "正在中斷 Tailscale 連線...", + "tsEnabled": "Tailscale 已連線", + "tsDisabled": "Tailscale 已中斷連線", + "tsIPs": "Tailscale IP", + "tsPeers": "對等節點", + "tsOnline": "線上", "tsOffline": "離線", "tsExitNode": "出口節點", - "tsExitNodeActive": "出口節點已激活", + "tsExitNodeActive": "出口節點作用中", "tsHostname": "主機名稱" } }, "auth": { - "tagline": "自託管 SSH 和遠端桌面管理", + "tagline": "自託管的 SSH 與遠端桌面管理", "loginTitle": "歡迎回來", - "registerTitle": "建立帳戶", + "registerTitle": "建立帳號", "forgotPassword": "忘記密碼?", - "rememberMe": "記住設備30天(包含TOTP)", - "noAccount": "還沒有帳號?", - "hasAccount": "已有帳號?", - "twoFactorAuth": "雙重身份驗證", - "enterCode": "請輸入驗證碼", - "backupCode": "或使用備用代碼", - "verifyCode": "驗證碼", - "redirectingToApp": "正在重定向到應用程式...", + "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": "外部的", + "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 登入。", + "loginWithExternalDesc": "使用您設定的外部識別提供者登入", + "externalNotSupportedInElectron": "Electron 應用程式尚不支援外部驗證。請使用網頁版進行 OIDC 登入。", "loginWithProvider": "使用 {{name}} 登入", - "orContinueWith": "或繼續", + "orContinueWith": "或使用以下方式繼續", "ldapUsername": "LDAP 使用者名稱", "ldapPassword": "LDAP 密碼", "ldapSignIn": "登入", "ldapLoginFailed": "LDAP 登入失敗", "resetPasswordButton": "重設密碼", - "sendResetCode": "發送重置代碼", - "resetCodeDesc": "請輸入您的使用者名稱以取得密碼重設代碼。該程式碼將記錄在 Docker 容器日誌中。", - "resetCode": "重置程式碼", - "verifyCodeButton": "驗證碼", - "enterResetCode": "請輸入 Docker 容器日誌中該使用者的 6 位元代碼:", + "sendResetCode": "傳送重設代碼", + "resetCodeDesc": "輸入您的使用者名稱以接收密碼重設代碼。代碼將記錄在 Docker 容器記錄中。", + "resetCode": "重設代碼", + "verifyCodeButton": "驗證代碼", + "enterResetCode": "輸入來自 Docker 容器記錄的 6 位數代碼,使用者:", "newPassword": "新密碼", "confirmNewPassword": "確認密碼", - "enterNewPassword": "請輸入使用者的新密碼:", - "signUp": "報名", + "enterNewPassword": "為使用者輸入新的密碼:", + "signUp": "註冊", "desktopApp": "桌面應用程式", - "loggingInToDesktopApp": "登入桌面應用程式", - "loadingServer": "伺服器正在載入...", - "dataLossWarning": "使用此方法重設密碼將刪除您所有已儲存的 SSH 主機、憑證和其他加密資料。此操作無法撤銷。僅當您忘記密碼且未登入時才使用此方法。", - "authenticationDisabled": "身份驗證已停用", - "authenticationDisabledDesc": "所有身份驗證方式目前均已停用。請聯絡您的管理員。", - "passwordLoginDisabledDesc": "密碼登入已停用。請使用密碼金鑰或外部身份驗證提供者。", - "signInWithPasskey": "使用密碼登入", - "passkeyLoginFailed": "密碼登入失敗", - "attemptsRemaining": "{{count}} 剩餘嘗試次數", - "confirmResetDataWipe": "此帳戶自加密升級後未登錄,因此沒有舊密碼將無法恢復其儲存的資料。重置將永久刪除其主機名稱、憑證和程式碼片段。是否繼續?" + "loggingInToDesktopApp": "正在登入桌面應用程式", + "loadingServer": "正在載入伺服器...", + "dataLossWarning": "以此方式重設密碼將會刪除所有已儲存的 SSH 主機、憑證及其他加密資料。此動作無法復原。僅在您忘記密碼且未登入時使用此功能。", + "authenticationDisabled": "驗證已停用", + "authenticationDisabledDesc": "目前所有驗證方法均已停用。請聯絡您的管理員。", + "passwordLoginDisabledDesc": "密碼登入已停用。請使用通行密鑰或外部驗證提供者。", + "signInWithPasskey": "使用通行密鑰登入", + "passkeyLoginFailed": "Passkey 登入失敗", + "attemptsRemaining": "剩餘 {{count}} 次嘗試", + "confirmResetDataWipe": "此帳戶自加密升級後尚未登入,因此若無舊密碼,其儲存的資料將無法復原。重設將會永久刪除其主機、憑證與程式碼片段。是否繼續?" }, "hostKey": { "verifyNewHost": "驗證 SSH 主機金鑰", - "keyChangedWarning": "SSH主機金鑰已更改", - "firstConnectionTitle": "首次連線到此主機", - "firstConnectionDescription": "無法確認此主機的真實性。請驗證指紋是否符合預期。", - "keyChangedDescription": "此伺服器的主機金鑰自您上次連線以來已變更。這可能表示存在安全問題。", - "previousKey": "上一個密鑰", + "keyChangedWarning": "SSH 主機金鑰已變更", + "firstConnectionTitle": "首次連線至此主機", + "firstConnectionDescription": "無法確認此主機的真實性。請驗證指紋是否符合您的預期。", + "keyChangedDescription": "此伺服器的主機金鑰自您上次連線後已變更。這可能表示有安全性問題。", + "previousKey": "先前的金鑰", "newFingerprint": "新指紋", "fingerprint": "指紋", - "verifyInstructions": "如果您信任此主機,請按一下「接受」繼續並儲存此指紋以便將來連線。", - "securityWarning": "安全警告", + "verifyInstructions": "如果您信任此主機,請按一下「接受」以繼續,並儲存此指紋供未來連線使用。", + "securityWarning": "安全性警告", "acceptAndContinue": "接受並繼續", - "acceptNewKey": "接受新密鑰並繼續" + "acceptNewKey": "接受新金鑰並繼續" }, "errors": { - "databaseConnection": "無法連線到資料庫", + "databaseConnection": "無法連線至資料庫", "unknownError": "未知錯誤", "loginFailed": "登入失敗", - "failedPasswordReset": "密碼重置失敗", - "failedVerifyCode": "重置程式碼驗證失敗", - "failedCompleteReset": "密碼重置失敗", - "invalidTotpCode": "無效的 TOTP 程式碼", - "failedOidcLogin": "OIDC 登入啟動失敗", - "silentSigninOidcUnavailable": "已請求靜默登錄,但 OIDC 登入不可用。", - "failedUserInfo": "登入後取得使用者資訊失敗", + "failedPasswordReset": "無法啟動密碼重設", + "failedVerifyCode": "驗證重設碼失敗", + "failedCompleteReset": "無法完成密碼重設", + "invalidTotpCode": "無效的 TOTP 驗證碼", + "failedOidcLogin": "無法啟動 OIDC 登入", + "silentSigninOidcUnavailable": "已要求無訊息登入,但 OIDC 登入無法使用。", + "failedUserInfo": "登入後無法取得使用者資訊", "oidcAuthFailed": "OIDC 驗證失敗", - "invalidAuthUrl": "從後端收到的授權 URL 無效", - "requiredField": "此欄位是必需的", + "invalidAuthUrl": "從後端接收到無效的授權 URL", + "requiredField": "此欄位為必填", "minLength": "最小長度為 {{min}}", - "passwordMismatch": "密碼不匹配", - "passwordLoginDisabled": "目前已停用使用者名稱/密碼登入。", - "sessionExpired": "會話已過期 - 請重新登錄", - "totpRateLimited": "頻率限制:TOTP驗證嘗試次數過多。請稍後再試。", - "totpRateLimitedWithTime": "速率限制:TOTP 驗證嘗試次數過多。請稍候 {{time}} 秒後再試。", - "resetCodeRateLimited": "驗證次數過多,請稍後再試。", - "resetCodeRateLimitedWithTime": "驗證次數過多,請稍等 {{time}} 秒後再試。", - "authTokenSaveFailed": "保存身份驗證令牌失敗", - "failedToLoadServer": "伺服器載入失敗" + "passwordMismatch": "密碼不相符", + "passwordLoginDisabled": "使用者名稱/密碼登入目前已停用", + "sessionExpired": "工作階段已過期 - 請重新登入", + "totpRateLimited": "速率限制:過多次 TOTP 驗證嘗試。請稍後再試。", + "totpRateLimitedWithTime": "速率限制:過多次 TOTP 驗證嘗試。請等待 {{time}} 秒後再試。", + "resetCodeRateLimited": "速率限制:過多次驗證嘗試。請稍後再試。", + "resetCodeRateLimitedWithTime": "速率限制:驗證嘗試次數過多。請等待 {{time}} 秒後再試。", + "authTokenSaveFailed": "無法儲存驗證權杖", + "failedToLoadServer": "無法載入伺服器", + "remoteServerRequired": "需要遠端伺服器。請在「設定」中連接遠端伺服器以使用此連線類型。" }, "messages": { - "registrationDisabled": "管理員已停用新帳號註冊功能。請登入或聯絡管理員。", - "userNotAllowed": "您的帳戶未獲得註冊授權。請聯絡管理員。", - "databaseConnectionFailed": "連線資料庫伺服器失敗", - "resetCodeSent": "重置代碼已傳送至 Docker 日誌", - "codeVerified": "程式碼驗證成功", - "passwordResetSuccess": "密碼重置成功", + "registrationDisabled": "管理員目前已停用新帳戶註冊。請登入或聯絡管理員。", + "userNotAllowed": "您的帳戶無權註冊。請聯絡管理員。", + "databaseConnectionFailed": "無法連線至資料庫伺服器", + "resetCodeSent": "重設代碼已傳送至 Docker 日誌", + "codeVerified": "代碼驗證成功", + "passwordResetSuccess": "密碼重設成功", "loginSuccess": "登入成功", "registrationSuccess": "註冊成功" }, "profile": { - "c2sTunnelConfigDesc": "針對已設定 SSH 主機的本機桌面隧道。", - "c2sTunnelPresets": "客戶端隧道預設", - "c2sTunnelPresetsDesc": "將此桌面用戶端的本機隧道清單儲存為命名伺服器預設,或將預設載入回此用戶端。", - "c2sTunnelPresetsUnavailable": "客戶端隧道預設僅在桌面用戶端中可用。", - "c2sPresetName": "預設名稱", - "c2sPresetNamePlaceholder": "客戶預設名稱", - "c2sPresetToLoad": "預設載入", - "c2sNoPresetSelected": "未選擇預設值", - "c2sNoPresets": "未儲存任何預設。", + "c2sTunnelConfigDesc": "本機桌面通道,目標為已設定的 SSH 主機。", + "c2sTunnelPresets": "用戶端通道預設組", + "c2sTunnelPresetsDesc": "將此桌面用戶端的本機通道清單儲存為命名的伺服器預設組,或將預設組載回此用戶端。", + "c2sTunnelPresetsUnavailable": "用戶端通道預設組僅在桌面用戶端中可用。", + "c2sPresetName": "預設組名稱", + "c2sPresetNamePlaceholder": "用戶端預設組名稱", + "c2sPresetToLoad": "要載入的預設組", + "c2sNoPresetSelected": "未選取預設組", + "c2sNoPresets": "無儲存的預設組", "c2sLoadPreset": "載入", - "c2sCurrentLocalConfig": "{{count}} 此桌面上配置了本機用戶端隧道。", - "c2sPresetSyncNote": "預設是明確的快照;載入一個預設會取代此桌面用戶端的本機用戶端隧道清單。", - "c2sPresetSaved": "客戶端隧道預設已儲存", - "c2sPresetLoaded": "客戶端隧道預設已本地加載", - "c2sPresetRenamed": "用戶端隧道預設已重新命名", - "c2sPresetDeleted": "客戶端隧道預設已刪除", - "c2sPresetLoadError": "客戶端隧道預設載入失敗" + "c2sCurrentLocalConfig": "此桌面上設定了 {{count}} 個本機用戶端通道。", + "c2sPresetSyncNote": "預設組是明確的快照;載入預設組將會取代此桌面用戶端的本機用戶端通道清單。", + "c2sPresetSaved": "用戶端通道預設組已儲存", + "c2sPresetLoaded": "用戶端通道預設組已在本機載入", + "c2sPresetRenamed": "用戶端通道預設組已重新命名", + "c2sPresetDeleted": "用戶端通道預設組已刪除", + "c2sPresetLoadError": "無法載入用戶端通道預設組" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", "language": "語言", - "keyPassword": "密鑰密碼", - "pastePrivateKey": "把你的私鑰貼到這裡…", - "localListenerHost": "127.0.0.1(本地收聽)", - "localTargetHost": "127.0.0.1(此計算機上的目標)", + "keyPassword": "金鑰密碼", + "pastePrivateKey": "在此貼上您的私密金鑰...", + "localListenerHost": "127.0.0.1 (本機監聽)", + "localTargetHost": "127.0.0.1 (此電腦上的目標)", "socksListenerHost": "127.0.0.1(SOCKS 監聽器)", - "enterPassword": "請輸入您的密碼", + "enterPassword": "輸入您的密碼", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "儀表板", - "loading": "正在加載儀錶板...", + "loading": "正在載入儀表板...", "github": "GitHub", "support": "支援", "discord": "Discord", "docs": "文件", - "donate": "捐", + "donate": "贊助", "serverOverview": "伺服器概覽", "version": "版本", - "upToDate": "最新", - "updateAvailable": "更新可用", + "upToDate": "已是最新", + "updateAvailable": "有可用更新", "beta": "Beta", - "uptime": "正常運作時間", + "uptime": "運行時間", "database": "資料庫", - "healthy": "健康", + "healthy": "正常", "error": "錯誤", - "totalHosts": "主持人總數", - "totalTunnels": "全隧道", - "totalCredentials": "證書總數", + "totalHosts": "主機總數", + "totalTunnels": "隧道總數", + "totalCredentials": "憑證總數", "recentActivity": "近期活動", - "reset": "重置", - "loadingRecentActivity": "正在加載最近的活動...", - "noRecentActivity": "近期無活動", + "reset": "重設", + "loadingRecentActivity": "正在載入近期活動...", + "noRecentActivity": "無近期活動", "quickActions": "快速操作", "addHost": "新增主機", "addCredential": "新增憑證", "adminSettings": "管理員設定", - "userProfile": "用戶個人資料", - "serverStats": "伺服器統計訊息", - "loadingServerStats": "正在載入伺服器統計資料…", - "noServerData": "伺服器資料不可用", - "cpu": "中央處理器", - "ram": "記憶體", - "customizeLayout": "自訂儀錶板", + "userProfile": "使用者資料", + "serverStats": "伺服器統計", + "loadingServerStats": "正在載入伺服器統計...", + "noServerData": "沒有可用的伺服器資料", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "自訂儀表板", "dashboardSettings": "儀表板設定", "enableDisableCards": "啟用/停用卡片", "resetLayout": "重設為預設值", "serverOverviewCard": "伺服器概覽", "recentActivityCard": "近期活動", - "networkGraphCard": "網路圖", - "networkGraph": "網路圖", + "networkGraphCard": "網路圖表", + "networkGraph": "網路圖表", "quickActionsCard": "快速操作", - "serverStatsCard": "伺服器統計訊息", - "panelMain": "主要的", - "panelSide": "邊", - "justNow": "現在", + "serverStatsCard": "伺服器統計", + "panelMain": "主要", + "panelSide": "側邊", + "justNow": "剛剛", "serviceLinks": "服務連結", "homepagePreview": "首頁預覽" }, "donation": { - "title": "喜歡Termix嗎?", - "body": "Termix 是一個免費開源的軟體,由我們兩人團隊利用業餘時間開發和維護。如果您用它取代了原本需要付費的商業工具,您的捐贈將有助於支付託管費用並維持軟體的持續開發。目前,我們僅接受加密貨幣捐贈。", - "milestones": "捐款也能幫助我們投入時間進行研究和學習,以發展諸如 SAML 和 Kubernetes 支援等功能。請在捐款頁面查看專案進度。", - "cta": "捐", - "dismiss": "也許以後" + "title": "喜歡 Termix 嗎?", + "body": "Termix 是免費開源軟體,由兩人團隊利用業餘時間開發和維護。如果它取代了你原本需付費的商業工具,捐款有助於支付託管成本並維持開發。目前僅接受加密貨幣捐款。", + "milestones": "捐款也有助於資助研究學習所需的時間,以開發如 SAML 與 Kubernetes 支援等功能。請在捐款頁面查看進度。", + "cta": "捐款", + "dismiss": "稍後再說" }, "dashboardTab": { - "stable": "穩定的", - "hostsOnline": "線上主持人", - "activeTunnels": "活躍隧道", + "stable": "穩定", + "hostsOnline": "主機在線數", + "activeTunnels": "活動通道", "registerNewServer": "註冊新伺服器", "storeSshKeysOrPasswords": "儲存 SSH 金鑰或密碼", - "manageUsersAndRoles": "管理使用者和角色", - "manageYourAccount": "管理您的帳戶", + "manageUsersAndRoles": "管理使用者與角色", + "manageYourAccount": "管理你的帳戶", "hostStatus": "主機狀態", - "noHostsConfigured": "未配置任何主機", - "online": "在線的", + "noHostsConfigured": "未設定主機", + "online": "在線", "offline": "離線", - "checking": "檢查", - "onlineLower": "在線的", - "nodes": "{{count}} 節點", - "add": "添加:", + "checking": "檢查中", + "onlineLower": "在線", + "nodes": "{{count}} 個節點", + "add": "新增:", "commandPalette": "命令面板", - "done": "完畢", - "editModeInstructions": "拖曳卡片重新排序 · 拖曳列分隔線調整列寬 · 拖曳卡片底部邊緣調整卡片高度 · 點選垃圾桶圖示刪除卡片", - "empty": "空的", + "done": "完成", + "editModeInstructions": "拖曳卡片以重新排序 · 拖曳欄分隔線以調整欄寬 · 拖曳卡片底部邊緣以調整高度 · 拖曳至垃圾桶以移除", + "empty": "空", "clear": "清除", "serviceLinksTitle": "服務連結", - "serviceLinksEmpty": "暫無服務連結", + "serviceLinksEmpty": "尚無服務連結", "serviceLinksAddLabel": "標籤", "serviceLinksAddUrl": "URL", - "serviceLinksAdd": "添加", + "serviceLinksAdd": "新增", "serviceLinksLabelPlaceholder": "我的服務", "serviceLinksUrlPlaceholder": "http://192.168.1.10:8080", - "serviceLinksInvalidUrl": "請輸入有效的網址", + "serviceLinksInvalidUrl": "輸入有效的網址", "serviceLinksAddFailed": "新增服務連結失敗", "disk": "磁碟", - "viewServerDetails": "查看伺服器詳情" + "viewServerDetails": "檢視伺服器詳細資訊" }, "sessionLogs": { - "title": "會話日誌", - "noLogs": "暫無會話日誌", - "noLogsDesc": "啟用主機上的會話日誌記錄以開始錄製", - "duration": "期間", - "viewLog": "查看日誌", + "title": "工作階段記錄", + "noLogs": "尚無任何工作階段記錄", + "noLogsDesc": "在主機上啟用工作階段記錄以開始錄製", + "duration": "持續時間", + "viewLog": "檢視記錄", "downloadLog": "下載", "deleteLog": "刪除", - "confirmDelete": "刪除此會話日誌?", - "confirmDeleteDesc": "此操作無法撤銷。", + "confirmDelete": "刪除此工作階段記錄?", + "confirmDeleteDesc": "此動作無法復原。", "copyContent": "複製", "copied": "已複製!", - "loadError": "會話日誌載入失敗", - "deleteError": "刪除會話日誌失敗", - "filterByHost": "按主機篩選..." + "loadError": "載入工作階段記錄失敗", + "deleteError": "刪除工作階段記錄失敗", + "filterByHost": "依主機篩選..." }, "networkGraph": { "addHost": "新增主機", "addGroup": "新增群組", - "addLink": "添加連結", + "addLink": "新增連結", "zoomIn": "放大", "zoomOut": "縮小", - "resetView": "重置視圖", - "selectHost": "選擇主機", - "chooseHost": "選擇房東…", - "parentGroup": "母集團", - "noGroup": "無組", - "groupName": "組名", + "resetView": "重置檢視", + "selectHost": "選取主機", + "chooseHost": "選擇主機...", + "parentGroup": "父群組", + "noGroup": "無群組", + "groupName": "群組名稱", "color": "顏色", "source": "來源", "target": "目標", "moveToGroup": "移至群組", - "selectGroup": "選擇群組...", - "addConnection": "新增連接", - "hostDetails": "主機詳情", - "removeFromGroup": "從群組移除", + "selectGroup": "選取群組...", + "addConnection": "新增連線", + "hostDetails": "主機詳細資料", + "removeFromGroup": "從群組中移除", "addHostHere": "在此新增主機", "editGroup": "編輯群組", "delete": "刪除", - "add": "添加", - "create": "創造", + "add": "新增", + "create": "建立", "move": "移動", - "connect": "連接", + "connect": "連線", "createGroup": "建立群組", - "selectSourcePlaceholder": "選擇來源...", - "selectTargetPlaceholder": "選擇目標...", - "invalidFile": "無效文件", - "hostAlreadyExists": "主機已在拓樸結構中", + "selectSourcePlaceholder": "選取來源...", + "selectTargetPlaceholder": "選取目標...", + "invalidFile": "無效的檔案", + "hostAlreadyExists": "主機已存在於拓撲中", "connectionExists": "連線已存在", "unknown": "未知", - "name": "姓名", + "name": "名稱", "ip": "IP", - "status": "地位", - "failedToAddNode": "新增節點失敗", - "sourceDifferentFromTarget": "源和目標必須不同", - "exportJSON": "導出 JSON", - "importJSON": "導入 JSON", - "terminal": "終端", - "fileManager": "文件管理器", + "status": "狀態", + "failedToAddNode": "無法新增節點", + "sourceDifferentFromTarget": "來源與目標必須不同", + "exportJSON": "匯出 JSON", + "importJSON": "匯入 JSON", + "terminal": "終端機", + "fileManager": "檔案管理員", "tunnel": "隧道", "docker": "Docker", "serverStats": "主機指標", "hostMetrics": "主機指標", - "noNodes": "暫無節點" + "noNodes": "尚無節點" }, "docker": { "notEnabled": "此主機未啟用 Docker", "validating": "正在驗證 Docker...", - "connecting": "正在連接...", + "connecting": "連線中...", "error": "錯誤", "version": "Docker {{version}}", - "connectionFailed": "連線 Docker 失敗", + "connectionFailed": "無法連線至 Docker", "containerStarted": "容器 {{name}} 已啟動", - "failedToStartContainer": "啟動容器失敗 {{name}}", + "failedToStartContainer": "無法啟動容器 {{name}}", "containerStopped": "容器 {{name}} 已停止", - "failedToStopContainer": "停止容器 {{name}} 失敗", - "containerRestarted": "容器 {{name}} 已重啟", - "failedToRestartContainer": "重啟容器失敗 {{name}}", + "failedToStopContainer": "無法停止容器 {{name}}", + "containerRestarted": "容器 {{name}} 已重新啟動", + "failedToRestartContainer": "無法重新啟動容器 {{name}}", "containerPaused": "容器 {{name}} 已暫停", - "containerUnpaused": "容器 {{name}} 已解除暫停", - "failedToTogglePauseContainer": "切換容器 {{name}} 的暫停狀態失敗", + "containerUnpaused": "容器 {{name}} 已取消暫停", + "failedToTogglePauseContainer": "無法切換容器 {{name}} 的暫停狀態", "containerRemoved": "容器 {{name}} 已移除", - "failedToRemoveContainer": "移除容器 {{name}} 失敗", - "image": "影像", - "ports": "港口", + "failedToRemoveContainer": "無法移除容器 {{name}}", + "image": "映像", + "ports": "連接埠", "noPorts": "無連接埠", - "start": "開始", - "confirmRemoveContainer": "您確定要刪除容器「{{name}}」嗎?此操作無法撤銷。", - "runningContainerWarning": "警告:此容器目前正在運行。移除此容器會先停止其運作。", - "loadingContainers": "正在裝載貨櫃…", - "manager": "Docker 管理器", + "start": "啟動", + "confirmRemoveContainer": "確定要移除容器 '{{name}}' 嗎?此動作無法復原。", + "runningContainerWarning": "警告:此容器正在執行中。移除時會先將其停止。", + "loadingContainers": "正在載入容器...", + "manager": "Docker 管理", "autoRefresh": "自動重新整理", - "timestamps": "時間戳", - "lines": "線條", - "filterLogs": "過濾日誌...", + "timestamps": "時間戳記", + "lines": "行", + "filterLogs": "篩選日誌...", "refresh": "重新整理", "download": "下載", "clear": "清除", - "logsDownloaded": "日誌已成功下載", - "last50": "最後50", - "last100": "最近100", - "last500": "最近500", - "last1000": "最近的1000", + "logsDownloaded": "日誌下載成功", + "last50": "最後 50 筆", + "last100": "最後 100 筆", + "last500": "最後 500 筆", + "last1000": "最後 1000 筆", "allLogs": "所有日誌", - "noLogsMatching": "沒有與「{{query}}」相符的日誌", - "noLogsAvailable": "沒有可用日誌", - "noContainersFound": "未找到容器", + "noLogsMatching": "沒有符合 \"{{query}}\" 的日誌", + "noLogsAvailable": "無可用日誌", + "noContainersFound": "找不到容器", "noContainersFoundHint": "此主機上沒有可用的 Docker 容器", - "searchPlaceholder": "搜尋容器…", + "searchPlaceholder": "搜尋容器...", "allStatuses": "所有狀態", - "stateRunning": "跑步", - "statePaused": "暫停", + "stateRunning": "執行中", + "statePaused": "已暫停", "stateExited": "已退出", - "stateRestarting": "重新啟動", - "noContainersMatchFilters": "沒有容器符合您的篩選條件", - "noContainersMatchFiltersHint": "嘗試調整搜尋或篩選條件", - "failedToFetchStats": "取得容器統計資料失敗", - "containerNotRunning": "容器未運行", - "startContainerToViewStats": "啟動容器以查看統計信息", - "loadingStats": "正在載入統計資料…", - "errorLoadingStats": "載入統計資料時出錯", - "noStatsAvailable": "暫無統計數據", + "stateRestarting": "正在重新啟動", + "noContainersMatchFilters": "沒有符合篩選條件的容器", + "noContainersMatchFiltersHint": "請嘗試調整搜尋或篩選條件", + "failedToFetchStats": "無法取得容器統計資料", + "containerNotRunning": "容器未執行", + "startContainerToViewStats": "啟動容器以檢視統計資料", + "loadingStats": "正在載入統計資料...", + "errorLoadingStats": "載入統計資料時發生錯誤", + "noStatsAvailable": "無可用統計資料", "cpuUsage": "CPU 使用率", - "current": "目前的", - "memoryUsage": "記憶體使用情況", + "current": "目前", + "memoryUsage": "記憶體使用量", "networkIo": "網路 I/O", "input": "輸入", "output": "輸出", - "blockIo": "塊 I/O", - "read": "讀", - "write": "寫", + "blockIo": "區塊 I/O", + "read": "讀取", + "write": "寫入", "pids": "PID", "containerInformation": "容器資訊", - "name": "姓名", + "name": "名稱", "id": "ID", "state": "狀態", - "containerMustBeRunning": "容器必須正在運行才能存取控制台", + "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": "未找到容器", + "totpVerificationFailed": "TOTP 驗證失敗,請再試一次。", + "warpgateVerificationFailed": "Warpgate 驗證失敗,請再試一次。", + "connectedTo": "已連線至 {{containerName}}", + "disconnected": "已中斷連線", + "consoleError": "主控台錯誤", + "errorMessage": "錯誤:{{message}}", + "failedToConnect": "無法連線至容器", + "console": "主控台", + "selectShell": "選取 shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "連線", + "disconnect": "中斷連線", + "notConnected": "未連線", + "clickToConnect": "點擊連線以啟動 shell 工作階段", + "connectingTo": "正在連線至 {{containerName}}...", + "containerNotFound": "找不到容器", "backToList": "返回列表", - "logs": "紀錄", - "stats": "統計數據", - "consoleTab": "安慰", - "startContainerToAccess": "啟動容器以存取控制台" + "logs": "記錄", + "stats": "統計資料", + "consoleTab": "主控台", + "startContainerToAccess": "啟動容器以存取主控台" }, "admin": { - "sectionGeneral": "一般的", + "sectionGeneral": "一般", "sectionOidc": "OIDC", - "sectionSso": "單一登入提供者", + "sectionSso": "SSO 提供者", "ssoAddProvider": "新增提供者", - "ssoDocsLink": "查看文件", - "ssoProviderDocsLink": "查看文件", - "ssoNoProviders": "未設定單一登入 (SSO) 提供者。", + "ssoDocsLink": "檢視文件", + "ssoProviderDocsLink": "檢視文件", + "ssoNoProviders": "尚未設定 SSO 提供者。", "ssoProviderName": "顯示名稱", "ssoProviderType": "提供者類型", "ssoDeleteProvider": "刪除提供者", - "ssoDeleteConfirm": "刪除此提供者?關聯用戶將無法登入。", + "ssoDeleteConfirm": "刪除此提供者?相關使用者將無法登入。", "ssoTypeOidc": "OIDC", "ssoTypeLdap": "LDAP", "ssoTypeGithub": "GitHub", "ssoTypeGoogle": "Google", "ssoEnabled": "已啟用", "ssoDisabled": "已停用", - "ssoSaveProvider": "儲蓄提供者", - "ssoTestConnection": "測試連接", + "ssoSaveProvider": "儲存提供者", + "ssoTestConnection": "測試連線", "ssoEditProvider": "編輯提供者", "ldapHost": "LDAP 主機", - "ldapPort": "港口", - "ldapUseTls": "使用 TLS(LDAPS)", - "ldapBindDn": "綁定DN", + "ldapPort": "連接埠", + "ldapUseTls": "使用 TLS (LDAPS)", + "ldapBindDn": "綁定 DN", "ldapBindPassword": "綁定密碼", - "ldapUserSearchBase": "用戶搜尋庫", + "ldapUserSearchBase": "使用者搜尋基底", "ldapUserSearchFilter": "使用者搜尋篩選器", "ldapUsernameAttr": "使用者名稱屬性", "ldapDisplayNameAttr": "顯示名稱屬性", - "ldapGroupSearchBase": "群組搜尋庫", + "ldapGroupSearchBase": "群組搜尋基底", "ldapAdminGroup": "管理員群組", - "ldapAllowedUsers": "允許的用戶", + "ldapAllowedUsers": "允許的使用者", "sectionUsers": "使用者", - "sectionSessions": "會議", + "sectionSessions": "工作階段", "sectionRoles": "角色", "sectionDatabase": "資料庫", - "sectionApiKeys": "API金鑰", - "sectionAuditLog": "審計日誌", - "sectionSsl": "SSL/Let's Encrypt", - "sslDescription": "自動發行和續訂來自 Let's Encrypt 的可信任 SSL 憑證。需要公共網域名稱和 80 連接埠或 DNS 存取權限。", - "sslDocsLink": "查看 SSL 文檔", - "sslDomain": "領域", + "sectionApiKeys": "API 金鑰", + "sectionAuditLog": "稽核記錄", + "sectionSsl": "SSL / Let's Encrypt", + "sslDescription": "自動從 Let's Encrypt 簽發並更新受信任的 SSL 憑證。需要公開網域及通訊埠 80 或 DNS 存取權。", + "sslDocsLink": "檢視 SSL 文件", + "sslDomain": "網域", "sslDomainPlaceholder": "termix.example.com", - "sslDomainDesc": "憑證的公共網域。", + "sslDomainDesc": "憑證使用的公開網域名稱。", "sslEmail": "電子郵件", "sslEmailPlaceholder": "admin@example.com", - "sslEmailDesc": "Let's Encrypt 通知和帳戶的聯絡信箱。", - "sslChallengeType": "挑戰類型", + "sslEmailDesc": "用於 Let's Encrypt 通知及帳戶的聯絡電子郵件。", + "sslChallengeType": "驗證類型", "sslChallengeTypeDesc": "如何向 Let's Encrypt 證明網域所有權。", - "sslChallengeHttp": "HTTP(webroot)-需要可透過網際網路存取的 80 端口", - "sslChallengeDns": "DNS(Cloudflare)- 需要 Cloudflare API 令牌", - "sslCloudflareToken": "Cloudflare API 令牌", - "sslCloudflareTokenPlaceholder": "請輸入令牌...", - "sslCloudflareTokenDesc": "具有針對您的網域的 Zone:DNS:Edit 權限的作用域令牌。", - "sslCertStatus": "證書狀態", + "sslChallengeHttp": "HTTP (webroot) - 需要可從網際網路存取的連接埠 80", + "sslChallengeDns": "DNS (Cloudflare) - 需要 Cloudflare API 權杖", + "sslCloudflareToken": "Cloudflare API 權杖", + "sslCloudflareTokenPlaceholder": "輸入權杖...", + "sslCloudflareTokenDesc": "具有您網域 Zone:DNS:Edit 權限的限定範圍權杖。", + "sslCertStatus": "憑證狀態", "sslCertStatusNone": "無憑證", - "sslCertStatusValid": "有效的", + "sslCertStatusValid": "有效", "sslCertStatusExpiring": "即將到期", - "sslCertStatusExpired": "已到期", - "sslCertExpiresAt": "過期時間 {{date}}", - "sslLastIssued": "最後發佈日期 {{date}}", - "sslRequestCert": "簽發/續簽證書", - "sslRequestCertLoading": "正在申請證書...", - "sslRequestCertSuccess": "證書已成功頒發並安裝", - "sslRequestCertFailed": "證書請求失敗", + "sslCertStatusExpired": "已過期", + "sslCertExpiresAt": "到期日 {{date}}", + "sslLastIssued": "上次簽發日期 {{date}}", + "sslRequestCert": "簽發 / 續期憑證", + "sslRequestCertLoading": "請求憑證中...", + "sslRequestCertSuccess": "憑證已成功簽發並安裝", + "sslRequestCertFailed": "憑證請求失敗", "sslSave": "儲存設定", "sslSaved": "SSL 設定已儲存", - "sslSaveFailed": "SSL 設定儲存失敗", - "sslRequiresDomain": "網域名稱和電子郵件地址是必需的", - "sslInfoNote": "頒發憑證後,請在環境變數中啟用 SSL (ENABLE_SSL=true) 並重新啟動 Termix。", - "auditLogTotal": "{{total}} 總條目數", - "auditLogEmpty": "未找到稽核日誌條目", + "sslSaveFailed": "儲存 SSL 設定失敗", + "sslRequiresDomain": "網域和電子郵件為必填", + "sslInfoNote": "簽發憑證後,請在您的環境變數中啟用 SSL (ENABLE_SSL=true) 並重新啟動 Termix。", + "sslManualOption": "手動(上傳證書)", + "sslManualCert": "證書(PEM)", + "sslManualCertPlaceholder": "-----開始憑證-----", + "sslManualKey": "私鑰(PEM)", + "sslManualKeyPlaceholder": "-----私鑰開始-----", + "sslManualDesc": "請貼上您現有的憑證和私鑰,如果您的 CA 要求,請提供完整的憑證鏈。", + "sslManualUpload": "上傳並安裝證書", + "sslManualUploadLoading": "正在上傳憑證…", + "sslManualUploadSuccess": "證書已成功上傳並安裝", + "sslManualUploadFailed": "證書上傳失敗", + "sslManualRequiresFields": "需要證書和私鑰。", + "auditLogTotal": "總共 {{total}} 筆記錄", + "auditLogEmpty": "查無稽核記錄", "auditLogSuccess": "成功", - "auditLogFailed": "失敗的", + "auditLogFailed": "失敗", "auditLogClearFilters": "清除篩選條件", - "auditLogPage": "第 {{page}} 頁,共 {{totalPages}} 頁(總計{{total}} 頁)", + "auditLogPage": "第 {{page}} 頁,共 {{totalPages}} 頁 (共 {{total}} 筆)", "auditLogIp": "IP", "auditLogResourceId": "資源 ID", "auditLogFilterUser": "使用者", - "auditLogFilterAction": "行動", + "auditLogFilterAction": "動作", "auditLogFilterResourceType": "資源類型", - "auditLogFilterStatus": "地位", - "auditLogFilterFrom": "從", - "auditLogFilterTo": "到", + "auditLogFilterStatus": "狀態", + "auditLogFilterFrom": "開始時間", + "auditLogFilterTo": "結束時間", "auditLogFilterAll": "全部", - "allowRegistration": "允許用戶註冊", - "allowRegistrationDesc": "允許新用戶使用使用者名稱和密碼自行註冊", + "allowRegistration": "允許使用者註冊", + "allowRegistrationDesc": "允許新使用者使用使用者名稱和密碼自行註冊", "allowPasswordLogin": "允許密碼登入", "allowPasswordLoginDesc": "使用者名稱/密碼登入", - "oidcAutoProvision": "OIDC自動配置", - "oidcAutoProvisionDesc": "首次登入時自動為 OIDC/SSO 使用者建立帳戶(與註冊開關無關)", - "oidcSilentLoginDefault": "預設啟用靜默 OIDC 登入", - "oidcSilentLoginDefaultDesc": "每次造訪時自動重定向到 OIDC 登入頁面,完全跳過登入表單。", - "allowPasswordReset": "允許重置密碼", - "allowPasswordResetDesc": "透過 Docker 日誌重置程式碼", - "commandHistoryEnabled": "命令歷史記錄", - "commandHistoryEnabledDesc": "允許記錄命令歷史記錄。停用此功能後,無論主機設定為何,都不會儲存歷史記錄。", - "updateCommandHistoryFailed": "更新命令歷史記錄設定失敗", - "sessionTimeout": "會話逾時", + "oidcAutoProvision": "OIDC 自動佈建", + "oidcAutoProvisionDesc": "首次登入時自動為 OIDC/SSO 使用者建立帳號 (與註冊開關無關)", + "oidcSilentLoginDefault": "預設靜默 OIDC 登入", + "oidcSilentLoginDefaultDesc": "每次造訪時自動重新導向至 OIDC 登入,完全略過登入表單", + "allowPasswordReset": "允許密碼重設", + "allowPasswordResetDesc": "透過 Docker 日誌取得重設代碼", + "commandHistoryEnabled": "指令歷史記錄", + "commandHistoryEnabledDesc": "允許記錄指令歷史記錄。停用時,無論各主機設定如何,都不會儲存歷史記錄。", + "updateCommandHistoryFailed": "無法更新指令歷史記錄設定", + "analyticsEnabled": "分享匿名使用統計數據", + "analyticsEnabledDesc": "每日匿名傳送使用者、主機和功能使用統計數據,以協助改善 Termix。絕不包含任何個人資料或連結詳情。", + "updateAnalyticsFailed": "分析設定更新失敗", + "sessionSharingGloballyEnabled": "允許會話共享", + "sessionSharingGloballyEnabledDesc": "允許在整個執行個體範圍內共用即時終端、RDP、VNC 和 Telnet 會話。停用此功能將覆蓋所有主機層級的共用設定。", + "updateSessionSharingFailed": "會話共享設定更新失敗", + "sessionTimeout": "工作階段逾時", "hours": "小時", - "sessionTimeoutRange": "最短 1 小時 · 最長 720 小時", - "monitoringDefaults": "監控預設設定", + "sessionTimeoutRange": "最短 1 小時,最長 720 小時", + "monitoringDefaults": "監控預設值", "statusCheck": "狀態檢查", "metrics": "指標", "sec": "秒", "logLevel": "日誌等級", - "enableGuacamole": "啟用酪梨醬", - "enableGuacamoleDesc": "RDP/VNC遠端桌面", - "enableGuacamoleDocsLink": "查看文件", + "enableGuacamole": "啟用 Guacamole", + "enableGuacamoleDesc": "RDP/VNC 遠端桌面", + "enableGuacamoleDocsLink": "檢視文件", "guacdUrl": "guacd URL", "tailscaleApiKey": "Tailscale API 金鑰", - "tailscaleApiKeyDescription": "用於主機編輯器中的設備發現。在 tailscale.com/admin/settings/keys 產生金鑰。", - "tailscaleApiKeyDocsLink": "查看文件", - "oidcDescription": "配置 OpenID Connect 以實現單一登入。標示 * 的欄位為必填項。", - "oidcDocsLink": "查看文件", - "oidcClientId": "客戶ID", - "oidcClientSecret": "客戶機密", + "tailscaleApiKeyDescription": "用於主機編輯器中的裝置探索。在 tailscale.com/admin/settings/keys 產生金鑰。", + "tailscaleApiKeyDocsLink": "檢視文件", + "oidcDescription": "設定 OpenID Connect 以進行單一登入 (SSO)。標記 * 的欄位為必填。", + "oidcDocsLink": "檢視文件", + "oidcClientId": "用戶端 ID", + "oidcClientSecret": "用戶端密鑰", "oidcAuthUrl": "授權 URL", "oidcIssuerUrl": "發行者 URL", - "oidcTokenUrl": "令牌 URL", - "oidcUserIdentifier": "使用者標識符路徑", + "oidcTokenUrl": "權杖 URL", + "oidcUserIdentifier": "使用者識別碼路徑", "oidcDisplayName": "顯示名稱路徑", - "oidcScopes": "瞄準鏡", - "oidcUserinfoUrl": "覆蓋使用者資訊 URL", - "oidcAllowedUsers": "允許的用戶", - "oidcAllowedUsersDesc": "每行一個電子郵件地址。留空則允許所有電子郵件地址。", + "oidcScopes": "範圍", + "oidcUserinfoUrl": "覆寫使用者資訊 URL", + "oidcAllowedUsers": "允許的使用者", + "oidcAllowedUsersDesc": "每行一個電子郵件。留空以允許全部。", "oidcAdminGroup": "管理員群組", - "oidcAdminGroupDesc": "此群組中的使用者擁有管理員權限。留空則可停用群組同步。", - "oidcGroupClaim": "團體索賠", - "oidcGroupClaimDesc": "可選。包含使用者群組的聲明路徑。預設順序為:組、角色、組。用於具有自訂聲明的提供者(例如 Zitadel)。", + "oidcAdminGroupDesc": "此群組中的使用者將被授予管理員權限。留空以停用群組同步。", + "oidcGroupClaim": "群組宣告", + "oidcGroupClaimDesc": "選用。包含使用者群組的宣告路徑。預設為 groups、roles、group。用於自訂宣告的提供者(例如 Zitadel)。", "oidcCaCert": "自訂 CA 憑證", - "oidcCaCertDesc": "可選。適用於使用私人或自簽名 CA 的 OIDC 提供者的 PEM 編碼 CA 憑證。留空則使用系統信任儲存。", - "removeOidc": "消除", - "usersCount": "{{count}} 用戶", - "createUser": "創造", - "newRole": "新角色", - "roleName": "姓名", + "oidcCaCertDesc": "選用。PEM 編碼的 CA 憑證,用於使用私有或自簽 CA 的 OIDC 提供者。留空以使用系統信任儲存區。", + "removeOidc": "移除", + "usersCount": "{{count}} 位使用者", + "createUser": "建立", + "newRole": "新增角色", + "roleName": "名稱", "roleDisplayName": "顯示名稱", "roleDescription": "描述", - "rolesCount": "{{count}} 角色", - "createRole": "創造", - "creating": "正在創建…", + "rolesCount": "{{count}} 個角色", + "createRole": "建立", + "creating": "建立中...", "exportDatabase": "匯出資料庫", "exportDatabaseDesc": "下載所有主機、憑證和設定的備份", - "export": "出口", - "exporting": "出口...", - "importDatabase": "導入資料庫", - "importDatabaseDesc": "從 .sqlite 備份檔案恢復", - "importDatabaseSelected": "已選擇: {{name}}", - "selectFile": "選擇文件", - "changeFile": "改變", - "import": "進口", - "importing": "輸入...", - "apiKeysCount": "{{count}} 鍵", - "apiKeysDocsLink": "查看文件", - "newApiKey": "新 API 金鑰", - "apiKeyCreatedWarning": "密鑰已建立 - 立即複製,之後不會再顯示。", - "apiKeyName": "姓名", + "export": "匯出", + "exporting": "匯出中...", + "importDatabase": "匯入資料庫", + "importDatabaseDesc": "從 .sqlite 備份檔案還原", + "importDatabaseSelected": "已選取:{{name}}", + "selectFile": "選取檔案", + "changeFile": "變更", + "import": "匯入", + "importing": "匯入中...", + "apiKeysCount": "{{count}} 個金鑰", + "apiKeysDocsLink": "檢視文件", + "newApiKey": "新增 API 金鑰", + "apiKeyCreatedWarning": "金鑰已建立 - 請立即複製,之後不會再顯示。", + "apiKeyName": "名稱", "apiKeyUser": "使用者", - "apiKeySelectUser": "選擇用戶...", - "apiKeyExpiresAt": "到期時間", - "createKey": "建立密鑰", - "apiKeyNoExpiry": "無有效期限", - "revokedBadge": "撤銷", - "authTypeDual": "雙重認證", + "apiKeySelectUser": "選取使用者...", + "apiKeyExpiresAt": "到期日", + "createKey": "建立金鑰", + "apiKeyNoExpiry": "無到期日", + "revokedBadge": "已撤銷", + "authTypeDual": "雙重驗證", "authTypeOidc": "OIDC", - "authTypeLocal": "當地的", - "adminStatusAdministrator": "行政人員", - "adminStatusRegularUser": "普通用戶", - "adminBadge": "行政", + "authTypeLocal": "本機", + "adminStatusAdministrator": "管理員", + "adminStatusRegularUser": "一般使用者", + "adminBadge": "管理員", "systemBadge": "系統", - "customBadge": "風俗", - "youBadge": "你", - "sessionsActive": "{{count}} 活躍", - "sessionActive": "目前狀態: {{time}}", - "sessionExpires": "Exp: {{time}}", + "customBadge": "自訂", + "youBadge": "您", + "sessionsActive": "{{count}} 個使用中", + "sessionActive": "使用中:{{time}}", + "sessionExpires": "到期:{{time}}", "revokeAll": "全部", - "revokeAllSessionsSuccess": "該使用者的所有會話均已撤銷。", - "revokeAllSessionsFailed": "撤銷會話失敗", - "revokeSessionFailed": "撤銷會話失敗", - "addRole": "添加角色", - "noCustomRoles": "未訂自訂角色", + "revokeAllSessionsSuccess": "已撤銷使用者的所有工作階段", + "revokeAllSessionsFailed": "撤銷工作階段失敗", + "revokeSessionFailed": "撤銷工作階段失敗", + "addRole": "新增角色", + "noCustomRoles": "未定義任何自訂角色", "removeRoleFailed": "移除角色失敗", - "assignRoleFailed": "角色分配失敗", + "assignRoleFailed": "指派角色失敗", "deleteRoleFailed": "刪除角色失敗", - "userAdminAccess": "行政人員", - "userAdminAccessDesc": "完全訪問所有管理員設置", + "userAdminAccess": "管理員", + "userAdminAccessDesc": "完整存取所有管理員設定", "userRoles": "角色", - "revokeAllUserSessions": "撤銷所有會話", + "revokeAllUserSessions": "撤銷所有工作階段", "revokeAllUserSessionsDesc": "強制所有裝置重新登入", "revoke": "撤銷", - "deleteUserWarning": "刪除此用戶是永久性的。", + "deleteUserWarning": "永久刪除此使用者。", "deleteUser": "刪除 {{username}}", - "deleting": "正在刪除…", - "deleteUserFailed": "刪除用戶失敗", - "deleteUserSuccess": "使用者「{{username}}」已刪除", - "deleteRoleSuccess": "角色「{{name}}」已刪除", - "revokeKeySuccess": "金鑰「{{name}}」已撤銷", + "deleting": "正在刪除...", + "deleteUserFailed": "刪除使用者失敗", + "deleteUserSuccess": "已刪除使用者「{{username}}」", + "deleteRoleSuccess": "已刪除角色「{{name}}」", + "revokeKeySuccess": "已撤銷金鑰「{{name}}」", "revokeKeyFailed": "撤銷金鑰失敗", "copiedToClipboard": "已複製到剪貼簿", - "done": "完畢", - "createUserTitle": "創建用戶", - "createUserDesc": "建立一個新的本機帳戶。", + "done": "完成", + "createUserTitle": "建立使用者", + "createUserDesc": "建立新的本機帳戶。", "createUserUsername": "使用者名稱", "createUserPassword": "密碼", - "createUserPasswordHint": "至少6個字元。", - "createUserEnterUsername": "請輸入使用者名稱", + "createUserPasswordHint": "最少 6 個字元。", + "createUserEnterUsername": "輸入使用者名稱", "createUserEnterPassword": "輸入密碼", - "createUserSubmit": "創建用戶", - "editUserTitle": "管理用戶: {{username}}", - "editUserDesc": "編輯角色、管理員狀態、會話和帳戶設定。", + "createUserSubmit": "建立使用者", + "editUserTitle": "管理使用者:{{username}}", + "editUserDesc": "編輯角色、管理員狀態、工作階段及帳戶設定。", "editUserUsername": "使用者名稱", - "editUserAuthType": "身份驗證類型", + "editUserAuthType": "驗證類型", "editUserAdminStatus": "管理員狀態", - "editUserUserId": "使用者身分", - "linkAccountTitle": "關聯帳戶", - "linkAccountDesc": "將 OIDC 帳戶 {{username}} 與現有本地帳戶合併。", - "linkAccountDescLocal": "將本地帳戶 {{username}} 與現有的僅限 OIDC 的帳戶關聯。", - "linkAccountWarningTitle": "這將:", - "linkAccountEffect1": "刪除僅包含 OIDC 的帳戶", - "linkAccountEffect2": "將 OIDC 登入資訊新增至目標帳戶", - "linkAccountEffect3": "允許同時使用 OIDC 和密碼登入", - "linkAccountTargetUsername": "本機帳戶使用者名稱", - "linkAccountTargetPlaceholder": "輸入要關聯的本機帳戶使用者名稱", - "linkAccountOidcUsername": "OIDC帳戶用戶名", - "linkAccountOidcPlaceholder": "輸入僅用於 OIDC 的帳戶使用者名稱以進行合併", - "linkAccountOidcNotFound": "未找到使用該使用者名稱的僅限 OIDC 帳戶", - "linkAccounts": "關聯帳戶", - "linkAccountSuccess": "帳戶已成功關聯", - "linkAccountFailed": "帳戶關聯失敗", - "linkAccountInProgress": "正在連接…", - "unlinkAccountTitle": "解除 OIDC 鏈接", - "unlinkAccountDesc": "從 {{username}}移除OIDC驗證。他們將只能使用密碼登入。", - "unlinkAccountWarning": "這將從該帳戶中移除 OIDC 登入權限。用戶必須設定密碼才能繼續登入。", - "unlinkAccount": "解除 OIDC 鏈接", - "unlinkAccountInProgress": "正在取消連結...", - "unlinkAccountSuccess": "OIDC已成功解除關聯", - "unlinkAccountFailed": "取消連結 OIDC 失敗", - "saving": "儲存...", - "updateRegistrationFailed": "註冊設定更新失敗", + "editUserUserId": "使用者 ID", + "linkAccountTitle": "連結帳號", + "linkAccountDesc": "將 OIDC 帳號 {{username}} 合併到現有的本機帳號。", + "linkAccountDescLocal": "將本機帳號 {{username}} 連結到現有的僅 OIDC 帳號。", + "linkAccountWarningTitle": "此操作將會:", + "linkAccountEffect1": "刪除僅 OIDC 帳號", + "linkAccountEffect2": "將 OIDC 登入新增至目標帳號", + "linkAccountEffect3": "允許 OIDC 和密碼登入", + "linkAccountTargetUsername": "本機帳號使用者名稱", + "linkAccountTargetPlaceholder": "輸入要連結到的本機帳號使用者名稱", + "linkAccountOidcUsername": "OIDC 帳號使用者名稱", + "linkAccountOidcPlaceholder": "輸入要合併的僅 OIDC 帳號使用者名稱", + "linkAccountOidcNotFound": "找不到具有該使用者名稱的僅 OIDC 帳號", + "linkAccounts": "連結帳號", + "linkAccountSuccess": "帳號連結成功", + "linkAccountFailed": "帳號連結失敗", + "linkAccountInProgress": "連結中...", + "unlinkAccountTitle": "解除連結 OIDC", + "unlinkAccountDesc": "從 {{username}} 移除 OIDC 驗證。他們將只能使用密碼登入。", + "unlinkAccountWarning": "這將從此帳號移除 OIDC 登入。使用者必須已設定密碼才能繼續登入。", + "unlinkAccount": "解除連結 OIDC", + "unlinkAccountInProgress": "解除連結中...", + "unlinkAccountSuccess": "OIDC 解除連結成功", + "unlinkAccountFailed": "OIDC 解除連結失敗", + "saving": "儲存中...", + "updateRegistrationFailed": "更新註冊設定失敗", "updatePasswordLoginFailed": "更新密碼登入設定失敗", - "cannotDisablePasswordLoginWithTotp": "如果一個或多個使用者啟用了雙重認證 (2FA),則無法停用密碼登入。請先停用雙重認證。", - "updateOidcAutoProvisionFailed": "更新 OIDC 自動配置設定失敗", - "updateOidcSilentLoginDefaultFailed": "更新靜默 OIDC 登入設定失敗", - "updatePasswordResetFailed": "密碼重置設定更新失敗", - "sessionTimeoutRange2": "會話逾時時間必須介於 1 小時到 720 小時之間。", - "sessionTimeoutSaved": "會話逾時已儲存", - "sessionTimeoutSaveFailed": "保存會話逾時失敗", - "monitoringIntervalInvalid": "無效的區間值", - "monitoringSaved": "已儲存的監控設定", - "monitoringSaveFailed": "儲存監控設定失敗", - "metricsHistoryRetention": "指標歷史記錄保留", - "metricsHistoryRetentionRange": "1至90天", + "cannotDisablePasswordLoginWithTotp": "當一或多個使用者啟用雙重驗證 (2FA) 時,無法停用密碼登入。請先停用 2FA。", + "updateOidcAutoProvisionFailed": "更新 OIDC 自動佈建設定失敗", + "updateOidcSilentLoginDefaultFailed": "更新無提示 OIDC 登入設定失敗", + "updatePasswordResetFailed": "更新密碼重設設定失敗", + "sessionTimeoutRange2": "工作階段逾時必須介於 1 到 720 小時之間", + "sessionTimeoutSaved": "工作階段逾時已儲存", + "sessionTimeoutSaveFailed": "無法儲存工作階段逾時", + "monitoringIntervalInvalid": "無效的間隔值", + "monitoringSaved": "監控設定已儲存", + "monitoringSaveFailed": "無法儲存監控設定", + "metricsHistoryRetention": "指標歷史保留", + "metricsHistoryRetentionRange": "1 至 90 天", "days": "天", - "guacamoleSaved": "酪梨醬設定已儲存", - "guacamoleSaveFailed": "儲存 Guacamole 設定失敗", - "guacamoleUpdateFailed": "更新 Guacamole 設定失敗", - "tailscaleSettingsSaved": "尾尺度設定已儲存", - "tailscaleSettingsSaveFailed": "儲存 Tailscale 設定失敗", - "logLevelUpdateFailed": "更新日誌等級失敗", - "oidcSaved": "OIDC 配置已儲存", - "oidcSaveFailed": "儲存 OIDC 配置失敗", - "oidcRemoved": "已移除 OIDC 配置", - "oidcRemoveFailed": "移除 OIDC 配置失敗", - "createUserRequired": "需要使用者名稱和密碼。", - "createUserPasswordTooShort": "密碼長度必須至少為 6 個字符", - "createUserSuccess": "使用者「{{username}}」創建", - "createUserFailed": "建立使用者失敗", - "updateAdminStatusFailed": "更新管理員狀態失敗", - "allSessionsRevoked": "所有會話均被撤銷", - "revokeSessionsFailed": "撤銷會話失敗", - "manageUserData": "管理用戶數據", - "backToUsers": "返回用戶", + "guacamoleSaved": "Guacamole 設定已儲存", + "guacamoleSaveFailed": "無法儲存 Guacamole 設定", + "guacamoleUpdateFailed": "無法更新 Guacamole 設定", + "tailscaleSettingsSaved": "Tailscale 設定已儲存", + "tailscaleSettingsSaveFailed": "無法儲存 Tailscale 設定", + "logLevelUpdateFailed": "無法更新記錄層級", + "oidcSaved": "OIDC 設定已儲存", + "oidcSaveFailed": "無法儲存 OIDC 設定", + "oidcRemoved": "OIDC 設定已移除", + "oidcRemoveFailed": "無法移除 OIDC 設定", + "createUserRequired": "需要使用者名稱與密碼", + "createUserPasswordTooShort": "密碼長度至少需要 6 個字元", + "createUserSuccess": "已建立使用者「{{username}}」", + "createUserFailed": "無法建立使用者", + "updateAdminStatusFailed": "無法更新管理員狀態", + "allSessionsRevoked": "已撤銷所有工作階段", + "revokeSessionsFailed": "無法撤銷工作階段", + "manageUserData": "管理使用者資料", + "backToUsers": "返回使用者", "manageTabAccount": "帳戶", - "manageTabHosts": "主持人", - "manageTabCredentials": "證書", - "manageTabSnippets": "片段", - "manageTabSessions": "會議", + "manageTabHosts": "主機", + "manageTabCredentials": "憑證", + "manageTabSnippets": "程式碼片段", + "manageTabSessions": "工作階段", "manageTabDanger": "危險", - "manageEditorBack": "傳回 {{username}}的數據", + "manageEditorBack": "返回 {{username}} 的資料", "dataLockedBadge": "已鎖定", - "dataLockedNotice": "使用者 {{username}}的資料將被鎖定,直到他們下次登入。在此之前,他們的主機名稱、憑證和程式碼片段將無法查看或編輯。", + "dataLockedNotice": "{{username}} 的資料將保持鎖定,直到他們下次登入為止。在此之前無法檢視或編輯其主機、憑證和程式碼片段。", "resetPasswordTitle": "重設密碼", - "resetPasswordOidcOnly": "該用戶透過外部服務提供者登錄,沒有密碼。", + "resetPasswordOidcOnly": "此使用者透過外部提供者登入,沒有密碼。", "resetPasswordPlaceholder": "新密碼", - "resetPasswordBtn": "重置", - "resetPasswordWorking": "正在重置…", - "resetPasswordSuccess": "密碼重設", - "resetPasswordSuccessWiped": "密碼已重設。用戶的加密資料已清除。", - "resetPasswordFailed": "重置密碼失敗", - "resetPasswordConfirmWipe": "自加密升級以來,{{username}} 一直未登錄,因此其資料無法恢復。立即重置將刪除其主機、憑證和程式碼片段。是否繼續?", - "totpSectionTitle": "雙重身份驗證", + "resetPasswordBtn": "重設", + "resetPasswordWorking": "重設中...", + "resetPasswordSuccess": "密碼已重設", + "resetPasswordSuccessWiped": "密碼已重設。使用者的加密資料已清除。", + "resetPasswordFailed": "無法重設密碼", + "resetPasswordConfirmWipe": "{{username}} 自加密升級後尚未登入,因此其資料無法復原。現在重設將刪除其主機、憑證和程式碼片段。要繼續嗎?", + "totpSectionTitle": "雙因素驗證", "totpStatusEnabled": "此使用者已啟用 TOTP", - "totpStatusDisabled": "此使用者未啟用 TOTP。", + "totpStatusDisabled": "此使用者未啟用 TOTP", "disableTotp": "停用", - "disableTotpConfirm": "是否停用 {{username}}的雙重認證?他們將能夠僅使用密碼登入。", + "disableTotpConfirm": "確定要停用 {{username}} 的雙因素驗證嗎?停用後該使用者將只能使用密碼登入。", "totpDisabledSuccess": "TOTP 已停用", - "totpDisableFailed": "停用 TOTP 失敗", - "manageApiKeys": "API金鑰", - "apiKeyNamePlaceholder": "鍵名", - "apiKeyCopyNotice": "立即複製此密鑰,它不會再次顯示。", - "noApiKeysForUser": "沒有 API 金鑰", - "apiKeyDeleteFailed": "刪除 API 金鑰失敗", - "exportUserData": "數據導出", - "exportUserDataDesc": "以 JSON 格式下載此使用者的 hosts 檔案、憑證和檔案管理器資料。密鑰已解密。", - "exportUserDataSuccess": "用戶資料匯出", - "exportUserDataFailed": "用戶資料匯出失敗", - "hostsCount": "{{count}} 主機", + "totpDisableFailed": "無法停用 TOTP", + "manageApiKeys": "API 金鑰", + "apiKeyNamePlaceholder": "金鑰名稱", + "apiKeyCopyNotice": "立即複製此金鑰,之後將不會再顯示。", + "noApiKeysForUser": "無 API 金鑰", + "apiKeyDeleteFailed": "無法刪除 API 金鑰", + "exportUserData": "資料匯出", + "exportUserDataDesc": "將此使用者的主機、憑證和檔案管理員資料下載為 JSON。機密資料已解密。", + "exportUserDataSuccess": "使用者資料已匯出", + "exportUserDataFailed": "無法匯出使用者資料", + "hostsCount": "{{count}} 台主機", "addHostForUser": "新增主機", - "noHostsForUser": "該用戶沒有主機。", - "connectToHost": "連接", - "deleteHostConfirm": "刪除屬於 {{username}} 的主機「{{name}}」?", + "noHostsForUser": "此使用者沒有主機", + "connectToHost": "連線", + "deleteHostConfirm": "確定要刪除 {{username}} 的主機 \"{{name}}\" 嗎?", "hostDeletedSuccess": "主機已刪除", - "hostDeleteFailed": "刪除主機失敗", - "credentialsCount": "{{count}} 憑證", + "hostDeleteFailed": "無法刪除主機", + "credentialsCount": "{{count}} 個憑證", "addCredentialForUser": "新增憑證", - "noCredentialsForUser": "此用戶沒有憑證", + "noCredentialsForUser": "此使用者沒有任何憑證", "deleteCredentialConfirm": "刪除屬於 {{username}} 的憑證「{{name}}」?", "credentialDeletedSuccess": "憑證已刪除", - "credentialDeleteFailed": "刪除憑證失敗", - "snippetsCount": "{{count}} 程式碼片段", - "addSnippetForUser": "新增程式碼片段", - "noSnippetsForUser": "該用戶沒有任何程式碼片段。", - "snippetRequiredFields": "程式碼片段名稱和內容均為必填項。", - "snippetNamePlaceholder": "程式碼片段名稱", - "snippetContentPlaceholder": "命令內容", + "credentialDeleteFailed": "無法刪除憑證", + "snippetsCount": "{{count}} 個片段", + "addSnippetForUser": "新增片段", + "noSnippetsForUser": "此使用者沒有任何片段", + "snippetRequiredFields": "片段名稱與內容為必填", + "snippetNamePlaceholder": "片段名稱", + "snippetContentPlaceholder": "指令內容", "snippetFolderPlaceholder": "資料夾(可選)", - "snippetSaved": "程式碼片段已儲存", - "snippetSaveFailed": "儲存程式碼片段失敗", + "snippetSaved": "片段已儲存", + "snippetSaveFailed": "無法儲存片段", "deleteSnippetConfirm": "刪除屬於 {{username}} 的片段「{{name}}」?", "snippetDeletedSuccess": "片段已刪除", - "snippetDeleteFailed": "刪除程式碼片段失敗", - "noSessionsForUser": "沒有活動會話", - "deleteUserDangerDesc": "永久刪除 {{username}} 及其所有資料(主機、憑證、程式碼片段、歷史記錄)。此操作無法撤銷。", + "snippetDeleteFailed": "無法刪除片段", + "noSessionsForUser": "無使用中的工作階段", + "deleteUserDangerDesc": "永久刪除 {{username}} 及其所有資料(主機、憑證、片段、歷史記錄)。此動作無法復原。", "deleteUserConfirm": "永久刪除 {{username}} 及其所有資料?", - "deleteUserAdminBlocked": "刪除此使用者前,請先移除其管理員權限。", - "createRoleRequired": "姓名和顯示名稱是必填項。", - "createRoleSuccess": "角色“{{name}}”已創建", - "createRoleFailed": "創建角色失敗", - "apiKeyNameRequired": "密鑰名稱為必填項", - "apiKeyUserRequired": "需要使用者 ID", + "deleteUserAdminBlocked": "刪除此使用者前請先移除管理員狀態。", + "createRoleRequired": "名稱與顯示名稱為必填", + "createRoleSuccess": "角色「{{name}}」已建立", + "createRoleFailed": "無法建立角色", + "apiKeyNameRequired": "金鑰名稱為必填", + "apiKeyUserRequired": "使用者 ID 為必填", "apiKeyCreatedSuccess": "API 金鑰「{{name}}」已建立", - "apiKeyCreateFailed": "建立 API 金鑰失敗", - "exportSuccess": "資料庫已成功匯出", + "apiKeyCreateFailed": "無法建立 API 金鑰", + "exportSuccess": "資料庫匯出成功", "exportFailed": "資料庫匯出失敗", - "importSelectFile": "請先選擇文件。", - "importCompleted": "匯入完成:已匯入 {{total}} 項,跳過 {{skipped}} 項", - "importFailed": "導入失敗: {{error}}", - "importError": "資料庫導入失敗", + "importSelectFile": "請先選取檔案", + "importCompleted": "匯入完成:已匯入 {{total}} 個項目,跳過 {{skipped}} 個", + "importFailed": "匯入失敗:{{error}}", + "importError": "資料庫匯入失敗", "sectionHostDefaults": "主機預設值", - "hostDefaultsDesc": "建立新主機時會自動套用這些設定。單一主機仍然可以覆蓋這些設定。", - "hostDefaultsSocks5": "SOCKS5代理", + "hostDefaultsDesc": "建立新主機時自動套用的設定。個別主機仍可覆寫。", + "hostDefaultsSocks5": "SOCKS5 代理", "hostDefaultsUseSocks5": "啟用 SOCKS5 代理", "hostDefaultsUseSocks5Desc": "在所有新主機上預先填入 SOCKS5 代理", - "hostDefaultsSocks5Host": "代理主機/連接埠", - "hostDefaultsSocks5Username": "代理用戶名", + "hostDefaultsSocks5Host": "代理主機 / 連接埠", + "hostDefaultsSocks5Username": "代理使用者名稱", "hostDefaultsSocks5Password": "代理密碼", "hostDefaultsMetrics": "主機指標", "hostDefaultsMetricsEnabled": "啟用指標", - "hostDefaultsMetricsEnabledDesc": "預設情況下收集新主機的 CPU、記憶體和其他統計信息", + "hostDefaultsMetricsEnabledDesc": "預設在新主機上收集 CPU、記憶體及其他統計資料", "hostDefaultsStatusCheckEnabled": "啟用狀態檢查", - "hostDefaultsStatusCheckEnabledDesc": "預設情況下,對新主機進行線上/離線狀態輪詢", - "hostDefaultsTerminal": "終端", - "hostDefaultsSessionLogging": "會話日誌", - "hostDefaultsSessionLoggingDesc": "預設情況下記錄新主機上的終端機會話", - "hostDefaultsCommandHistory": "命令歷史記錄", - "hostDefaultsCommandHistoryDesc": "預設會追蹤新主機上的命令歷史記錄", - "hostDefaultsSaved": "主機預設設定已儲存", - "hostDefaultsSaveFailed": "儲存主機預設設定失敗", + "hostDefaultsStatusCheckEnabledDesc": "預設輪詢新主機的線上/離線狀態", + "hostDefaultsTerminal": "終端機", + "hostDefaultsSessionLogging": "工作階段記錄", + "hostDefaultsSessionLoggingDesc": "預設在新主機上記錄終端機工作階段", + "hostDefaultsCommandHistory": "指令歷程", + "hostDefaultsCommandHistoryDesc": "預設在新主機上追蹤指令歷程", + "hostDefaultsSaved": "主機預設值已儲存", + "hostDefaultsSaveFailed": "主機預設值儲存失敗", "rolePermissions": { - "count": "{{count}} 權限", + "count": "{{count}} 個權限", "editAction": "編輯權限", - "loadError": "載入權限目錄失敗", + "loadError": "無法載入權限目錄", "saved": "角色權限已儲存", - "saveError": "儲存角色權限失敗", - "save": "節省", - "saving": "儲存..." + "saveError": "無法儲存角色權限", + "save": "儲存", + "saving": "儲存中..." } }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "主持人", + "hostLabel": "主機", "hostPlaceholder": "192.168.1.1 或 example.com", - "portLabel": "港口", + "portLabel": "連接埠", "portPlaceholder": "22", "usernameLabel": "使用者名稱", "usernamePlaceholder": "使用者名稱", - "authLabel": "身份驗證", + "authLabel": "驗證", "passwordLabel": "密碼", "passwordPlaceholder": "密碼", - "privateKeyLabel": "私鑰", - "privateKeyPlaceholder": "貼上私鑰…", + "privateKeyLabel": "私密金鑰", + "privateKeyPlaceholder": "貼上私密金鑰...", "credentialLabel": "憑證", - "credentialPlaceholder": "選擇已儲存的憑證", - "connectToTerminal": "連接到終端", - "connectToFiles": "連接到文件" + "credentialPlaceholder": "選取已儲存的憑證", + "connectToTerminal": "連線至終端機", + "connectToFiles": "連線至檔案管理員" }, "history": { - "noTerminalSelected": "未選擇終端", - "noTerminalSelectedHint": "開啟 SSH 終端標籤頁以查看其命令歷史記錄", - "searchPlaceholder": "搜尋紀錄…", + "noTerminalSelected": "未選取終端機", + "noTerminalSelectedHint": "開啟 SSH 終端機分頁以檢視其命令歷史記錄", + "searchPlaceholder": "搜尋歷史記錄...", "clearAll": "全部清除", - "noHistoryEntries": "無歷史記錄", + "noHistoryEntries": "無歷史記錄項目", "trackingDisabled": "歷史記錄追蹤已停用", - "trackingDisabledHint": "在主機終端設定中啟用它。" + "trackingDisabledHint": "在主機的終端機設定中啟用。" }, "sshTools": { - "keyRecordingTitle": "關鍵錄音", - "recordToTerminals": "記錄到終端", + "keyRecordingTitle": "按鍵錄製", + "recordToTerminals": "錄製至終端機", "selectAll": "全部", - "selectNone": "沒有任何", - "noTerminalTabsOpen": "沒有打開任何終端標籤頁。", - "selectTerminalsAbove": "選擇上方終端", - "broadcastInputPlaceholder": "在此輸入以廣播按鍵操作…", - "fillPassword": "填寫密碼", - "fillPasswordSuccess": "已將密碼輸入 {{count}} 終端機中", - "fillPasswordMissing": "{{count}} 所選終端未儲存密碼", + "selectNone": "無", + "noTerminalTabsOpen": "無終端機分頁開啟", + "selectTerminalsAbove": "選取上方的終端機", + "broadcastInputPlaceholder": "在此輸入以廣播按鍵輸入...", + "fillPassword": "填入密碼", + "fillPasswordSuccess": "已將密碼填入 {{count}} 個終端機", + "fillPasswordMissing": "選取的 {{count}} 個終端機沒有已儲存的密碼", "stopRecording": "停止錄製", "startRecording": "開始錄製", "settingsTitle": "設定", "enableRightClickCopyPaste": "啟用右鍵複製/貼上" }, "splitScreen": { - "layoutTitle": "佈局", - "selectLayoutAbove": "請選擇上方佈局。", - "selectLayoutHint": "選擇要顯示的窗格數量", + "layoutTitle": "版面配置", + "selectLayoutAbove": "選取上方的版面配置", + "selectLayoutHint": "選擇要顯示多少窗格", "panesTitle": "窗格", - "openTabsTitle": "開啟標籤頁", - "dragTabsHint": "將標籤拖曳到上方窗格,或使用快速指派功能。", - "dropHere": "落在這裡", + "openTabsTitle": "開啟的分頁", + "dragTabsHint": "將分頁拖曳至上方的窗格,或使用快速指派", + "dropHere": "放置此處", "emptyPane": "空的", "dashboard": "儀表板", - "clearSplitScreen": "清晰的分割畫面", - "quickAssign": "快速分配", + "clearSplitScreen": "清除分割畫面", + "quickAssign": "快速指派", "alreadyAssigned": "窗格 {{index}}", - "splitTab": "分頁標籤", - "addToSplit": "添加到拆分", - "removeFromSplit": "從分割中移除", - "assignToPane": "指派到窗格", + "splitTab": "分割索引標籤", + "addToSplit": "加入至分割", + "removeFromSplit": "從分割移除", + "assignToPane": "指派至窗格", "hotkeysTitle": "鍵盤快速鍵", "hotkeysSplitRight": "切換雙向分割", - "hotkeysSplitBelow": "切換三路分割", - "hotkeysNavigatePane": "導覽窗格", - "hotkeysNextTab": "下一個標籤頁", - "hotkeysPrevTab": "上一個標籤頁" + "hotkeysSplitBelow": "切換三向分割", + "hotkeysNavigatePane": "瀏覽窗格", + "hotkeysNextTab": "下一個索引標籤", + "hotkeysPrevTab": "上一個索引標籤" }, "snippets": { - "title": "片段", - "createSnippetTitle": "建立程式碼片段", - "createSnippetDescription": "建立一個新的命令片段以便快速執行", - "nameLabel": "姓名", - "namePlaceholder": "例如,重啟 Nginx", - "descriptionLabel": "描述", - "descriptionPlaceholder": "選用描述", - "optional": "選修的", + "title": "指令片段", + "createSnippetTitle": "建立片段", + "createSnippetDescription": "建立新的指令片段以便快速執行", + "nameLabel": "名稱", + "namePlaceholder": "例如:重新啟動 Nginx", + "descriptionLabel": "說明", + "descriptionPlaceholder": "選用說明", + "optional": "選用", "folderLabel": "資料夾", "noFolder": "無資料夾(未分類)", - "commandLabel": "命令", - "commandPlaceholder": "例如,sudo systemctl restart nginx", + "commandLabel": "指令", + "commandPlaceholder": "例如:sudo systemctl restart nginx", "cancel": "取消", - "createSnippetButton": "建立程式碼片段", + "createSnippetButton": "建立片段", "createFolderTitle": "建立資料夾", - "createFolderDescription": "將你的程式碼片段整理到資料夾中", + "createFolderDescription": "將您的片段整理至資料夾", "folderNameLabel": "資料夾名稱", - "folderNamePlaceholder": "例如:系統指令、Docker腳本", + "folderNamePlaceholder": "例如:系統指令、Docker 指令碼", "folderColorLabel": "資料夾顏色", "folderIconLabel": "資料夾圖示", "previewLabel": "預覽", "folderNameFallback": "資料夾名稱", "createFolderButton": "建立資料夾", - "targetTerminals": "目標終端", - "selectAll": "全部", - "selectNone": "沒有任何", - "noTerminalTabsOpen": "沒有打開任何終端標籤頁。", - "searchPlaceholder": "搜尋摘要…", - "newSnippet": "新程式碼片段", - "newFolder": "新建資料夾", - "run": "跑步", - "noSnippetsInFolder": "此資料夾中沒有程式碼片段", + "targetTerminals": "目標終端機", + "selectAll": "全選", + "selectNone": "取消全選", + "noTerminalTabsOpen": "無終端機分頁開啟", + "searchPlaceholder": "搜尋指令片段...", + "newSnippet": "新增指令片段", + "newFolder": "新增資料夾", + "run": "執行", + "noSnippetsInFolder": "此資料夾中無指令片段", "uncategorized": "未分類", - "editSnippetTitle": "編輯片段", + "editSnippetTitle": "編輯指令片段", "editSnippetDescription": "更新此指令片段", "saveSnippetButton": "儲存變更", - "createSuccess": "程式碼片段創建成功", - "createFailed": "建立程式碼片段失敗", - "updateSuccess": "程式碼片段已成功更新", - "updateFailed": "更新程式碼片段失敗", - "deleteFailed": "刪除程式碼片段失敗", + "createSuccess": "指令片段建立成功", + "createFailed": "建立指令片段失敗", + "updateSuccess": "指令片段更新成功", + "updateFailed": "更新指令片段失敗", + "deleteFailed": "刪除指令片段失敗", "folderCreateSuccess": "資料夾建立成功", "folderCreateFailed": "建立資料夾失敗", "editFolderTitle": "編輯資料夾", - "editFolderDescription": "重新命名或更改此資料夾的外觀", + "editFolderDescription": "重新命名或變更此資料夾的外觀", "saveFolderButton": "儲存變更", "editFolder": "編輯資料夾", "deleteFolder": "刪除資料夾", - "folderDeleteSuccess": "資料夾「{{name}}」已刪除", + "folderDeleteSuccess": "資料夾 \"{{name}}\" 已刪除", "folderDeleteFailed": "刪除資料夾失敗", - "folderEditSuccess": "資料夾已成功更新", + "folderEditSuccess": "資料夾更新成功", "folderEditFailed": "更新資料夾失敗", - "confirmRunMessage": "運行“{{name}}”?", - "confirmRunButton": "跑步", - "runSuccess": "在 {{count}} 終端機中執行“{{name}}”", - "copySuccess": "已將「{{name}}」複製到剪貼簿", - "shareTitle": "分享片段", + "confirmRunMessage": "執行 \"{{name}}\"?", + "confirmRunButton": "執行", + "runSuccess": "已在 {{count}} 個終端機執行 \"{{name}}\"", + "copySuccess": "已將 \"{{name}}\" 複製到剪貼簿", + "shareTitle": "分享指令片段", "shareUser": "使用者", "shareRole": "角色", - "selectUser": "選擇用戶...", - "selectRole": "選擇一個角色…", - "shareSuccess": "程式碼片段已成功分享", + "selectUser": "選擇使用者...", + "selectRole": "選擇角色...", + "shareSuccess": "片段分享成功", "shareFailed": "分享片段失敗", - "revokeSuccess": "存取權限已撤銷", - "revokeFailed": "撤銷存取權限失敗", - "currentAccess": "目前存取權限", - "shareLoadError": "共享資料載入失敗", + "revokeSuccess": "已撤銷存取權限", + "revokeFailed": "撤銷存取失敗", + "currentAccess": "目前的存取權限", + "shareLoadError": "載入分享資料失敗", "loading": "載入中...", "close": "關閉", - "reorderFailed": "儲存程式碼片段順序失敗", - "importExport": "進出口", - "exportBtn": "導出 JSON", - "importBtn": "導入 JSON", - "exportSuccess": "程式碼片段已成功匯出", - "exportFailed": "匯出程式碼片段失敗", - "importTitle": "導入程式碼片段", - "importDescription": "從 Termix 匯出的 JSON 檔案中匯入程式碼片段和資料夾。", - "importDropOrClick": "將 JSON 檔案拖放到此處,或點擊瀏覽。", - "importSelectedFile": "已選擇: {{name}}", - "importOverwrite": "覆蓋同名且位於相同資料夾的現有程式碼片段", - "importStartBtn": "進口", - "importSuccess": "Import complete: {{snippets}} snippet(s) added, {{updated}} updated, {{skipped}} skipped, {{folders}} folder(s) added", - "importFailed": "導入程式碼片段失敗", - "importInvalidFile": "檔案無效:預期為包含程式碼片段或資料夾陣列的 JSON 物件。", - "targetHostsLabel": "目標宿主", - "targetHostsHint": "指定主機直接執行此程式碼片段,無需開啟終端機。", - "noHostsAvailable": "未配置任何主機", - "clearTargetHosts": "全部清除", + "reorderFailed": "儲存片段排序失敗", + "importExport": "匯入 / 匯出", + "exportBtn": "匯出 JSON", + "importBtn": "匯入 JSON", + "exportSuccess": "片段匯出成功", + "exportFailed": "匯出片段失敗", + "importTitle": "匯入片段", + "importDescription": "從 Termix 匯出的 JSON 檔案匯入片段和資料夾", + "importDropOrClick": "將 JSON 檔案拖放至此,或點擊瀏覽", + "importSelectedFile": "已選取:{{name}}", + "importOverwrite": "覆寫具有相同名稱和資料夾的現有片段", + "importStartBtn": "匯入", + "importSuccess": "匯入完成:已新增 {{snippets}} 個片段、{{updated}} 個已更新、{{skipped}} 個略過、{{folders}} 個資料夾", + "importFailed": "匯入片段失敗", + "importInvalidFile": "無效的檔案:需要包含片段或資料夾陣列的 JSON 物件", + "targetHostsLabel": "目標主機", + "targetHostsHint": "指定主機以直接執行此片段,無需開啟終端機。", + "noHostsAvailable": "未設定主機", + "clearTargetHosts": "清除全部", "hasTargetHosts": "有目標主機", - "runOnTargets": "目標運行", - "directRunSuccess": "在 {{count}} 主機上執行“{{name}}”", - "directRunPartialFail": "「{{name}}」在一個或多個主機上失敗", - "executionResultTitle": "執行結果: {{name}}", - "executionResultDescription": "在每個目標主機上執行程式碼片段的輸出結果。", + "runOnTargets": "在目標上執行", + "directRunSuccess": "已在 {{count}} 台主機上執行「{{name}}」", + "directRunPartialFail": "「{{name}}」在一或多台主機上執行失敗", + "executionResultTitle": "執行結果:{{name}}", + "executionResultDescription": "在每個目標主機上執行片段所產生的輸出。", "executionSuccess": "成功", - "executionFailed": "失敗的" + "executionFailed": "失敗" + }, + "keybindings": { + "title": "鍵盤快速鍵", + "description": "自訂終端複製、貼上和控制快捷鍵,或綁定按鍵以傳送文字或執行程式碼片段。", + "defaultsHeading": "內建快捷方式", + "customHeading": "自訂快捷方式", + "addBinding": "新增快捷方式", + "addBindingTitle": "新增快捷方式", + "editBindingTitle": "編輯快捷方式", + "loading": "載入中...", + "noCustomBindings": "暫無自訂快速鍵", + "defaultBadge": "預設", + "customizedBadge": "客製化", + "customize": "客製化", + "resetToDefault": "重設為預設值", + "resetAllToDefaults": "所有設定重置為預設值", + "close": "關閉", + "cancel": "取消", + "saveBinding": "儲存快捷方式", + "saveError": "儲存鍵盤快速鍵失敗", + "pressKeysToRecord": "關鍵組合", + "pressKeysPlaceholder": "點擊並按下按鍵", + "recording": "按下按鍵…", + "comboRequiredError": "先按下組合鍵", + "textRequiredError": "請輸入要發送的文字", + "controlCodeRequiredError": "請輸入一個字母作為控制碼", + "snippetRequiredError": "選擇一段文字", + "conflictWarning": "此組合已綁定至 {{combo}}。保存後兩者都會生效。", + "actionLabel": "行動", + "actionCopy": "複製選擇", + "actionPaste": "貼上", + "actionSendControlCode": "發送 Ctrl+字母訊號", + "actionSendText": "傳簡訊", + "actionRunSnippet": "運行現有程式碼片段", + "controlCodeLabel": "信", + "textLabel": "發送的文字", + "appendEnterLabel": "發送後按回車鍵", + "snippetLabel": "片段", + "selectSnippetPlaceholder": "選擇一段文字", + "orphanedSnippetWarning": "未找到程式碼片段", + "clipboardPermissionNote": "某些瀏覽器在首次使用時可能會提示授予剪貼簿權限。" }, "userProfile": { - "donateTitle": "讓Termix活下去", - "donateDescription": "Termix 是免費的,由幾位業餘人士自行託管和開發。如果它為您節省了時間或金錢,歡迎您捐贈加密貨幣以支持其持續運作。", - "donateMilestones": "捐款將用於資助我們研究和學習建立 SAML、Kubernetes 和 Agent 支援所需的知識。查看進度並捐款。", + "donateTitle": "讓 Termix 持續營運", + "donateDescription": "Termix 是免費、可自行託管的,且僅由少數人在業餘時間打造。如果它為您節省了時間或金錢,加密貨幣捐贈將有助於使其持續發展。", + "donateMilestones": "捐贈有助於資助研究和學習建置 SAML、Kubernetes 和 Agent 支援所需的時間。查看進度並進行捐贈。", "donateButton": "捐贈加密貨幣", "storageModeLocal": "瀏覽器", "storageModeCloud": "資料庫", - "storageModeDescription": "瀏覽器僅將設定儲存在此瀏覽器中。資料庫會同步到伺服器,並可在任何裝置上載入。", - "resetToDefaults": "恢復預設設定", - "resetToDefaultsSuccess": "設定將重設為預設值。", - "storageModeSwitch": "偏好存儲", - "sectionAccount": "帳戶", - "sectionAppearance": "外貌", - "sectionSecurity": "安全", - "sectionApiKeys": "API金鑰", - "sectionC2sTunnels": "C2S隧道", + "storageModeDescription": "瀏覽器僅將設定儲存在此瀏覽器。資料庫會同步至伺服器並在任何裝置上載入。", + "resetToDefaults": "重設為預設值", + "resetToDefaultsSuccess": "設定已重設為預設值。", + "storageModeSwitch": "偏好設定儲存", + "sectionAccount": "帳號", + "sectionAppearance": "外觀", + "sectionSecurity": "安全性", + "sectionApiKeys": "API 金鑰", + "sectionData": "數據", + "sectionC2sTunnels": "C2S 通道", "usernameLabel": "使用者名稱", "roleLabel": "角色", - "roleAdministrator": "行政人員", - "authMethodLabel": "身份驗證方法", - "authMethodLocal": "當地的", - "twoFaLabel": "雙因素認證", - "twoFaOn": "在", - "twoFaOff": "離開", + "roleAdministrator": "管理員", + "authMethodLabel": "驗證方法", + "authMethodLocal": "本地", + "twoFaLabel": "2FA", + "twoFaOn": "開啟", + "twoFaOff": "關閉", "versionLabel": "版本", - "betaProgramTitle": "測試計劃", - "betaProgramDescription": "使用每週滾動更新的 :beta Docker 標籤,即可提前體驗即將推出的功能。此版本不穩定,不適用於生產環境。", - "betaProgramFeedback": "發現漏洞?請在此處報告。", + "betaProgramTitle": "Beta 方案", + "betaProgramDescription": "使用每週滾動更新的 :beta Docker 標籤,搶先試用即將推出的功能。不穩定,不適用於生產環境。", + "betaProgramFeedback": "發現錯誤?請在此回報。", "betaProgramLearnMore": "了解更多", - "deleteAccount": "刪除帳戶", - "deleteAccountDescription": "永久刪除您的帳戶", - "changeServerDescription": "切換到其他 Termix 後端伺服器", + "deleteAccount": "刪除帳號", + "deleteAccountDescription": "永久刪除您的帳號", + "changeServerDescription": "切換到不同的 Termix 後端伺服器", "deleteButton": "刪除", - "deleteAccountPermanent": "此操作不可逆轉,且無法撤銷。", - "deleteAccountWarning": "所有會話、主機、憑證和設定都將永久刪除。", - "confirmPasswordDeletePlaceholder": "請輸入密碼以確認", + "deleteAccountPermanent": "此操作為永久且無法復原。", + "deleteAccountWarning": "所有工作階段、主機、憑證和設定將被永久刪除。", + "confirmPasswordDeletePlaceholder": "輸入密碼以確認", "languageLabel": "語言", "themeLabel": "主題", - "fontSizeLabel": "字體大小", + "fontSizeLabel": "字型大小", "accentColorLabel": "強調色", - "settingsTerminal": "終端", - "commandAutocomplete": "命令自動完成", - "commandAutocompleteDesc": "輸入時顯示自動完成功能", - "terminalLinkBehavior": "終端連結點擊", - "terminalLinkBehaviorDesc": "點擊終端中的連結時的預設行為", - "historyTracking": "歷史追蹤", - "historyTrackingDesc": "追蹤終端命令", - "commandPalette": "命令面板", - "commandPaletteDesc": "啟用鍵盤快速鍵", - "reopenTabsOnLogin": "登入後重新開啟標籤頁", - "reopenTabsOnLoginDesc": "即使從其他裝置登入或重新整理頁面,也能恢復您開啟的標籤頁。", - "confirmTabClose": "確認關閉標籤頁", - "confirmTabCloseDesc": "關閉終端標籤頁前請先詢問。", + "settingsTerminal": "終端機", + "commandAutocomplete": "指令自動完成", + "commandAutocompleteDesc": "輸入時顯示自動完成", + "keyboardShortcuts": "鍵盤快速鍵", + "keyboardShortcutsDescription": "自訂終端複製、貼上和命令快捷鍵", + "manageShortcuts": "管理", + "terminalLinkBehavior": "終端機連結點選", + "terminalLinkBehaviorDesc": "在終端機中點選連結時的預設行為", + "historyTracking": "歷史記錄追蹤", + "historyTrackingDesc": "追蹤終端機指令", + "commandPalette": "指令面板", + "commandPaletteDesc": "啟用鍵盤快捷鍵", + "reopenTabsOnLogin": "登入時重新開啟分頁", + "reopenTabsOnLoginDesc": "登入或重新整理頁面時復原開啟的分頁,即使是從另一部裝置", + "confirmTabClose": "確認關閉分頁", + "confirmTabCloseDesc": "關閉終端機分頁前先詢問", "settingsSidebar": "側邊欄", - "showHostTags": "節目主持人標籤", - "showHostTagsDesc": "在主機清單中顯示標籤", - "hostTrayOnClick": "點選展開主機操作", - "hostTrayOnClickDesc": "始終顯示連線按鈕;點擊展開管理選項,而不是懸停。", - "compactHostView": "緊湊型主機視圖", - "compactHostViewDesc": "將每個主機折疊成一行,僅顯示其名稱和位址", - "statusColors": "真實狀態顏色", - "statusColorsDesc": "使用綠色/紅色表示在線/離線狀態,而不是使用強調色。", - "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "保持左側邊欄應用導軌始終展開,而不是滑鼠懸停時才展開。", - "expandAppRailOnHover": "滑鼠懸停時展開應用軌道", - "expandAppRailOnHoverDesc": "允許滑鼠指標移動到左側邊欄應用程式欄上方時展開。", - "settingsNavigation": "導航", - "navigationTabsDesc": "選擇在應用程式側邊欄中顯示的選項卡", - "settingsSnippets": "片段", - "foldersCollapsed": "資料夾已折疊", - "foldersCollapsedDesc": "預設折疊資料夾", + "showHostTags": "顯示主機標籤", + "showHostTagsDesc": "在主機列表中顯示標籤", + "hostTrayOnClick": "點選以展開主機操作", + "hostTrayOnClickDesc": "總是顯示連線按鈕;以點選取代懸停來展開管理選項", + "compactHostView": "精簡主機檢視", + "compactHostViewDesc": "將每個主機摺疊為一行,僅顯示名稱和位址", + "statusColors": "真實狀態色彩", + "statusColorsDesc": "使用綠色/紅色代替強調色來表示線上/離線狀態", + "pinAppRail": "固定應用程式側欄", + "pinAppRailDesc": "保持左側應用程式側欄永遠展開,而不是懸停時才展開", + "expandAppRailOnHover": "懸停時展開應用程式側欄", + "expandAppRailOnHoverDesc": "允許左側應用程式側欄在指標移至其上方時展開", + "settingsNavigation": "導覽", + "navigationTabsDesc": "選擇要在應用程式導覽欄顯示的分頁", + "settingsSnippets": "程式碼片段", + "foldersCollapsed": "收合資料夾", + "foldersCollapsedDesc": "預設收合資料夾", "confirmExecution": "確認執行", - "confirmExecutionDesc": "運行程式碼片段前請確認", + "confirmExecutionDesc": "執行程式碼片段前確認", "settingsUpdates": "更新", - "disableUpdateChecks": "禁用更新檢查", + "disableUpdateChecks": "停用更新檢查", "disableUpdateChecksDesc": "停止檢查更新", "totpAuthenticator": "TOTP 驗證器", - "totpEnabled": "已啟用雙重認證", - "totpDisabled": "增加額外的登入安全性", + "totpEnabled": "雙因素驗證已啟用", + "totpDisabled": "新增額外的登入安全性", "disable": "停用", - "enable": "使能夠", + "enable": "啟用", "setupTotp": "設定 TOTP", - "qrCode": "QR 圖碼", - "totpInstructions": "掃描二維碼或在驗證器應用程式中輸入金鑰,然後輸入 6 位數驗證碼", + "qrCode": "QR 碼", + "totpInstructions": "掃描 QR 碼或在您的驗證器應用程式中輸入金鑰,然後輸入 6 位數驗證碼", "totpCodePlaceholder": "000000", - "verify": "核實", - "changePassword": "更改密碼", + "verify": "驗證", + "changePassword": "變更密碼", "currentPasswordLabel": "目前密碼", "currentPasswordPlaceholder": "目前密碼", "newPasswordLabel": "新密碼", @@ -3214,259 +3410,276 @@ "confirmPasswordPlaceholder": "確認新密碼", "updatePassword": "更新密碼", "createApiKeyTitle": "建立 API 金鑰", - "createApiKeyDescription": "產生用於程式化存取的新 API 金鑰。", - "apiKeyNameLabel": "姓名", - "apiKeyNamePlaceholder": "例如 CI 流水線", + "createApiKeyDescription": "產生新的 API 金鑰用於程式化存取", + "apiKeyNameLabel": "名稱", + "apiKeyNamePlaceholder": "例如:CI 管線", "expiryDateLabel": "到期日", - "optional": "選修的", + "optional": "選用", "cancel": "取消", - "createKey": "建立密鑰", - "apiKeyCount": "{{count}} 鍵", - "newKey": "新鑰匙", - "noApiKeys": "暫無API金鑰。", - "apiKeyActive": "積極的", - "apiKeyUsageHint": "請將鑰匙放在裡面。", - "apiKeyUsageHintHeader": "標題。", - "apiKeyPermissionsHint": "金鑰繼承建立使用者的權限。", + "createKey": "建立金鑰", + "apiKeyCount": "{{count}} 個 API 金鑰", + "newKey": "新增金鑰", + "noApiKeys": "尚無 API 金鑰。", + "apiKeyActive": "啟用中", + "apiKeyUsageHint": "將您的金鑰包含在", + "apiKeyUsageHintHeader": "標頭。", + "apiKeyPermissionsHint": "金鑰會繼承建立者的權限。", + "exportData": "匯出我的數據", + "exportDataDesc": "下載主機、憑證和設定的備份,以便傳輸到另一台裝置。", + "export": "出口", + "exporting": "出口...", + "importData": "導入我的數據", + "importDataDesc": "從 .sqlite 備份檔案還原您的主機、憑證和設置", + "importDataSelected": "已選擇: {{name}}", + "selectFile": "選擇文件", + "changeFile": "改變", + "import": "進口", + "importing": "輸入...", + "exportSuccess": "數據已成功匯出", + "exportFailed": "資料匯出失敗", + "importSelectFile": "請先選擇文件。", + "importCompleted": "匯入完成:已匯入 {{total}} 項,跳過 {{skipped}} 項", + "importFailed": "導入失敗: {{error}}", "roleUser": "使用者", - "authMethodDual": "雙重認證", + "authMethodDual": "雙重驗證", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP 設定啟動失敗", - "totpEnter6Digits": "請輸入6位代碼", - "totpEnabledSuccess": "已啟用雙重認證", - "totpInvalidCode": "驗證碼無效,請重試。", - "totpDisableInputRequired": "請輸入您的 TOTP 驗證碼或密碼", - "totpDisabledSuccess": "雙重認證已停用", - "totpDisableFailed": "禁用雙重驗證失敗", - "totpDisableTitle": "禁用雙重認證", + "totpSetupFailed": "無法啟動 TOTP 設定", + "totpEnter6Digits": "輸入 6 位數驗證碼", + "totpEnabledSuccess": "雙因素驗證已啟用", + "totpInvalidCode": "驗證碼無效,請再試一次", + "totpDisableInputRequired": "輸入您的 TOTP 驗證碼或密碼", + "totpDisabledSuccess": "雙因素驗證已停用", + "totpDisableFailed": "無法停用雙因素驗證", + "totpDisableTitle": "停用雙因素驗證", "totpDisablePlaceholder": "輸入 TOTP 驗證碼或密碼", - "totpDisableConfirm": "禁用雙重認證", + "totpDisableConfirm": "停用雙因素驗證", "totpContinueVerify": "繼續驗證", "totpVerifyTitle": "驗證碼", - "totpBackupTitle": "備用代碼", - "totpDownloadBackup": "下載備份代碼", + "totpBackupTitle": "備用驗證碼", + "totpDownloadBackup": "下載備用驗證碼", "passkeys": "通行金鑰", - "passkeysDesc": "使用 WebAuthn/FIDO2 憑證無需密碼登入", - "passkeyName": "密碼名稱", - "passkeyUvPreferred": "首選", - "passkeyUvRequired": "必需的", - "passkeyUvDiscouraged": "灰心", - "addPasskey": "新增密碼", - "noPasskeys": "未註冊任何通行金鑰。", - "passkeyAdded": "已新增通行金鑰", - "passkeyAddFailed": "新增密碼失敗", - "passkeyDeleted": "密碼已刪除", - "passkeyDeleteFailed": "刪除密碼失敗", - "done": "完畢", - "secretCopied": "秘密已複製到剪貼簿", - "apiKeyNameRequired": "密鑰名稱為必填項", + "passkeysDesc": "使用 WebAuthn/FIDO2 憑證無需密碼即可登入", + "passkeyName": "通行金鑰名稱", + "passkeyUvPreferred": "偏好", + "passkeyUvRequired": "需要", + "passkeyUvDiscouraged": "不鼓勵", + "addPasskey": "新增通行金鑰", + "noPasskeys": "尚未註冊通行金鑰。", + "passkeyAdded": "通行金鑰已新增", + "passkeyAddFailed": "無法新增通行金鑰", + "passkeyDeleted": "通行金鑰已刪除", + "passkeyDeleteFailed": "刪除通行密鑰失敗", + "done": "完成", + "secretCopied": "密鑰已複製到剪貼簿", + "apiKeyNameRequired": "必須提供金鑰名稱", "apiKeyCreated": "API 金鑰「{{name}}」已建立", "apiKeyCreateFailed": "建立 API 金鑰失敗", "apiKeyUser": "使用者", - "apiKeyExpires": "過期", - "apiKeyRevoked": "API金鑰「{{name}}」已撤銷", + "apiKeyExpires": "到期", + "apiKeyRevoked": "API 金鑰「{{name}}」已撤銷", "apiKeyRevokeFailed": "撤銷 API 金鑰失敗", - "passwordFieldsRequired": "需要當前密碼和新密碼。", - "passwordMismatch": "密碼不匹配", - "passwordTooShort": "密碼長度必須至少為 6 個字符", + "passwordFieldsRequired": "必須提供目前密碼和新密碼", + "passwordMismatch": "密碼不相符", + "passwordTooShort": "密碼至少需要 6 個字元", "passwordUpdated": "密碼已成功更新", - "passwordUpdateFailed": "密碼更新失敗", - "deletePasswordRequired": "刪除帳戶需要密碼。", - "deleteFailed": "刪除帳戶失敗", - "deleting": "正在刪除…", - "colorPickerTooltip": "打開顏色選擇器", + "passwordUpdateFailed": "更新密碼失敗", + "deletePasswordRequired": "需要密碼才能刪除您的帳號", + "deleteFailed": "刪除帳號失敗", + "deleting": "刪除中...", + "colorPickerTooltip": "開啟色彩選取器", "themeSystem": "系統", - "themeLight": "光", - "themeDark": "黑暗的", - "themeDracula": "德古拉", - "themeCatppuccin": "貓布奇寧", - "themeNord": "諾德", - "themeSolarized": "太陽能化", - "themeTokyoNight": "東京之夜", - "themeOneDark": "黑暗", + "themeLight": "淺色", + "themeDark": "深色", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } }, "tmuxMonitor": { "title": "Tmux 監視器", - "failedToLoadHosts": "主機載入失敗", - "failedToLoad": "載入 tmux 會話失敗", - "tmuxUnavailable": "此主機上未安裝 tmux", - "noSessions": "此主機上沒有 tmux 會話", - "noHostSelected": "未選擇主機", - "attached": "隨附的", - "detached": "分離", - "attach": "附", + "failedToLoadHosts": "載入主機失敗", + "failedToLoad": "載入 tmux 工作階段失敗", + "tmuxUnavailable": "此主機未安裝 tmux", + "noSessions": "此主機上沒有 tmux 工作階段", + "noHostSelected": "未選取主機", + "attached": "已附加", + "detached": "已分離", + "attach": "附加", "editTags": "編輯標籤", - "tagsHint": "逗號分隔的標籤(例如 YOLO、lab、training)", + "tagsHint": "以逗號分隔的標籤(例如:YOLO, lab, training)", "tagsSaved": "標籤已儲存", - "tagsSaveFailed": "標籤保存失敗", - "searchPlaceholder": "跨會話的搜尋結果...", - "searchResults": "{{count}} 匹配", + "tagsSaveFailed": "標籤儲存失敗", + "searchPlaceholder": "在所有會話中搜尋輸出...", + "searchResults": "{{count}} 個符合項", "searchFailed": "搜尋失敗", - "selectPaneHint": "選擇一個窗格以預覽其輸出", + "selectPaneHint": "選取一個窗格以預覽其輸出", "closePreview": "關閉預覽", - "newSession": "新會議", + "newSession": "新會話", "newSessionHint": "會話名稱(字母、數字、_ @ % + = -)", - "newSessionPlaceholder": "我的會話", - "create": "創造", + "newSessionPlaceholder": "my-session", + "create": "建立", "sessionCreated": "會話「{{name}}」已建立", "sessionCreateFailed": "建立會話失敗", - "splitRight": "向右分裂", - "splitDown": "分解", + "splitRight": "向右分割", + "splitDown": "向下分割", "splitFailed": "分割窗格失敗", "newWindow": "新視窗", "windowCreateFailed": "建立視窗失敗", - "attachSessionTooltip": "附加到 {{session}}", + "attachSessionTooltip": "附加至 {{session}}", "refresh": "重新整理", - "refreshFailed": "刷新會話失敗", - "collapseAll": "全部收起", + "refreshFailed": "重新整理會話失敗", + "collapseAll": "全部收合", "expandAll": "全部展開", - "moreActions": "更多操作", + "moreActions": "更多動作", "sessionStats": "會話統計", - "renameSessionTitle": "重新命名會話“{{name}}”", + "renameSessionTitle": "重新命名會話「{{name}}」", "rename": "重新命名", - "sessionRenamed": "會話已重新命名為“{{name}}”", - "sessionRenameFailed": "會話重命名失敗", + "sessionRenamed": "會話已重新命名為「{{name}}」", + "sessionRenameFailed": "重新命名會話失敗", "editTagsTitle": "編輯「{{name}}」的標籤", - "killPane": "殺戮面板", - "resizeTree": "拖曳調整大小 — 雙擊重置", - "reattach": "重新連線(修復顯示混亂的問題)", - "killWindow": "殺戮視窗", - "killWindowTitle": "終止視窗 {{index}} 的「{{session}}」?", - "killWindowBody": "此視窗中的所有窗格和進程都將終止。終止最後一個視窗將結束會話。", - "windowKillFailed": "無法關閉視窗", + "killPane": "終止窗格", + "resizeTree": "拖曳調整大小 — 雙擊重設", + "reattach": "重新附加(修復亂碼畫面)", + "killWindow": "終止視窗", + "killWindowTitle": "終止「{{session}}」的視窗 {{index}}?", + "killWindowBody": "此視窗中的所有窗格和程序將被終止。終止最後一個視窗會結束該工作階段。", + "windowKillFailed": "無法終止視窗", "statusActivity": "活動", "statusWindows": "視窗", "statusPanes": "窗格", "statusTags": "標籤", "killPaneTitle": "終止窗格 {{id}}?", - "killPaneBody": "此窗格中正在執行的進程將被終止。關閉最後一個窗格會將其視窗關閉。", + "killPaneBody": "在此窗格中執行的程序將被終止。終止最後一個窗格會關閉其視窗。", "paneKillFailed": "無法終止窗格", - "killSessionTitle": "終止會話「{{name}}」?", - "killSessionBody": "此會話中的所有視窗和正在運行的進程都將被終止。此操作無法撤銷。", - "kill": "殺", - "sessionKilled": "會話「{{name}}」終止", - "sessionKillFailed": "終止會話失敗", - "hostUnreachable": "無法連接到主機。請檢查主機是否在線且可存取。", - "noServer": "此主機上未執行 tmux 伺服器。", - "searchTruncated": "部分結果-搜尋涵蓋每個窗格的最後 {{lines}} 行,最多涵蓋 {{panes}} 個窗格。", + "killSessionTitle": "終止工作階段「{{name}}」?", + "killSessionBody": "此工作階段中的所有視窗和執行中的程序將被終止。此操作無法復原。", + "kill": "終止", + "sessionKilled": "工作階段「{{name}}」已終止", + "sessionKillFailed": "無法終止工作階段", + "hostUnreachable": "無法連線至主機。請檢查主機是否在線上且可連線。", + "noServer": "此主機上沒有正在執行的 tmux 伺服器。", + "searchTruncated": "部分結果 — 搜尋範圍涵蓋每個窗格的最後 {{lines}} 行,且最多 {{panes}} 個窗格。", "closeSearchResults": "關閉搜尋結果", "retry": "重試", - "noHosts": "沒有可用的 SSH 主機。", - "noHostsHint": "在 SSH 主機上啟用 Tmux 監視器選項(主機管理員 → 終端機選項卡)以監視其 tmux 會話。", - "tmuxInstallHint": "使用以下命令將其安裝到主機上:", - "attachTooltip": "打開終端機並輸入 {{host}}", - "attachTooltipPane": "開啟終端機到 {{host}} — tmux 會話 {{session}}", - "timeJustNow": "現在", - "timeMinutes": "{{count}}幾分鐘前", - "timeHours": "{{count}}小時前", - "timeDays": "{{count}}天前" + "noHosts": "沒有可用的 SSH 主機", + "noHostsHint": "在 SSH 主機上啟用 Tmux 監控選項(主機管理員 → 終端機頁籤),以監控其 tmux 工作階段。", + "tmuxInstallHint": "在主機上使用以下指令安裝:", + "attachTooltip": "開啟連至 {{host}} 的終端機", + "attachTooltipPane": "開啟連至 {{host}} 的終端機 — tmux 工作階段 {{session}}", + "timeJustNow": "剛剛", + "timeMinutes": "{{count}} 分鐘前", + "timeHours": "{{count}} 小時前", + "timeDays": "{{count}} 天前" }, "mobileKeyboard": { - "shift": "轉移", + "shift": "Shift", "ctrl": "Ctrl", - "esc": "Esc鍵", - "tab": "標籤頁", + "esc": "Esc", + "tab": "Tab", "backTab": "⇥", - "arrowUp": "向上箭頭", - "arrowDown": "向下箭頭", + "arrowUp": "上箭頭", + "arrowDown": "下箭頭", "arrowLeft": "左箭頭", - "arrowRight": "向右箭頭", - "home": "家", - "end": "結尾", - "pageUp": "翻頁", + "arrowRight": "右箭頭", + "home": "Home", + "end": "End", + "pageUp": "PgUp", "pageDown": "PgDn", - "delete": "德爾", - "editQuickKeys": "編輯快速鍵", - "quickKeysTitle": "快速鍵", - "quickKeysDesc": "點擊 × 刪除。最多支援 8 個字元。", - "quickKeyPlaceholder": "例如 sudo ", - "addQuickKey": "添加", - "removeQuickKey": "消除", - "resetDefaults": "恢復預設設定", - "done": "完畢" + "delete": "Del", + "paste": "貼上", + "editQuickKeys": "編輯快捷鍵", + "quickKeysTitle": "快捷鍵", + "quickKeysDesc": "點選 × 來移除。最多支援 8 個字元。", + "quickKeyPlaceholder": "例如:sudo ", + "addQuickKey": "新增", + "removeQuickKey": "移除", + "resetDefaults": "重設為預設值", + "done": "完成" }, "serial": { - "title": "序號", - "portLabel": "港口", - "portPlaceholder": "/dev/ttyUSB0 或 COM3", - "baudRateLabel": "波特率", - "dataBitsLabel": "數據", - "stopBitsLabel": "停止", - "parityLabel": "平價", - "parityNone": "沒有任何", - "parityEven": "甚至", - "parityOdd": "奇怪的", - "connect": "連接到串口", - "disconnect": "斷開", + "title": "序列", + "portLabel": "連接埠", + "portPlaceholder": "/dev/ttyUSB0 or COM3", + "baudRateLabel": "鮑率", + "dataBitsLabel": "資料位元", + "stopBitsLabel": "停止位元", + "parityLabel": "同位檢查", + "parityNone": "無", + "parityEven": "偶", + "parityOdd": "奇", + "connect": "連線至序列埠", + "disconnect": "中斷連線", "refreshPorts": "重新整理連接埠", - "connected": "已連接至 {{path}} ,波特率為 {{baud}}", - "disconnected": "串列埠已斷開連接", - "connectionError": "無法打開串口", + "connected": "已連線至 {{path}},鮑率 {{baud}}", + "disconnected": "序列埠已中斷連線", + "connectionError": "開啟序列埠失敗", "wsError": "WebSocket 錯誤", - "errorNoServerUrl": "未設定伺服器 URL", - "notSupportedTitle": "不支援串口", - "notSupported": "串行連接需要支援 Web Serial API 的瀏覽器(Chrome、Edge 或 Firefox 151+),或 Termix 桌面應用程式。", - "hideHint": "您可以在「使用者設定檔」>「外觀」>「側邊欄」>「導覽」下隱藏「序號」標籤。", - "browserPickerHint": "點擊“連接”,瀏覽器將開啟連接埠選擇器,供您選擇裝置。" + "errorNoServerUrl": "未設定伺服器網址", + "notSupportedTitle": "不支援序列埠", + "notSupported": "序列連線需要支援 Web Serial API 的瀏覽器 (Chrome、Edge 或 Firefox 151+) 或 Termix 桌面應用程式。", + "hideHint": "您可以在「使用者設定檔 > 外觀 > 側邊欄 > 導覽」中隱藏序列索引標籤。", + "browserPickerHint": "按一下連線,瀏覽器會開啟連接埠選擇器讓您選取裝置。" }, "metricsHistory": { "title": "指標歷史記錄", - "historySuffix": "歷史", - "cpuMemoryDisk": "CPU/記憶體/磁碟", + "historySuffix": "歷史記錄", + "cpuMemoryDisk": "CPU / 記憶體 / 磁碟", "network": "網路", "download": "RX", - "upload": "德州", - "noData": "此時間段內沒有歷史資料可供參考。", - "custom": "風俗", - "to": "到", - "apply": "申請", - "viewHistory": "查看歷史記錄" + "upload": "TX", + "noData": "此時間範圍內沒有可用的歷史資料。", + "custom": "自訂", + "to": "至", + "apply": "套用", + "viewHistory": "檢視歷史記錄" }, "alerts": { - "tabFirings": "警報", + "tabFirings": "警示", "tabRules": "規則", - "tabChannels": "頻道", - "noFirings": "沒有未確認的警報", - "noRules": "未配置任何告警規則", - "noChannels": "未配置任何通知管道", - "rulesDesc": "警報規則觸發通知", - "channelsDesc": "警報通知發送至何處", - "acknowledge": "承認", + "tabChannels": "通道", + "noFirings": "沒有未確認的警示", + "noRules": "未設定警示規則", + "noChannels": "未設定通知通道", + "rulesDesc": "警示規則會觸發通知", + "channelsDesc": "警示通知傳送的位置", + "acknowledge": "確認", "ackAll": "全部確認", - "allAcknowledged": "所有警報均已確認", - "ackFailed": "未能確認警報", - "ackAllFailed": "未能確認所有警報", + "allAcknowledged": "所有警示已確認", + "ackFailed": "確認警示失敗", + "ackAllFailed": "確認所有警示失敗", "showAcknowledged": "顯示全部", - "hideAcknowledged": "隱藏", - "addChannel": "新增頻道", - "editChannel": "編輯頻道", - "channelName": "姓名", + "hideAcknowledged": "隱藏已確認", + "addChannel": "新增通道", + "editChannel": "編輯通道", + "channelName": "名稱", "channelType": "類型", - "channelNameRequired": "姓名(必填)", + "channelNameRequired": "名稱為必填", "webhookUrl": "URL", - "webhookUrlRequired": "需要 Webhook URL", - "webhookDesc": "每次觸發警報時,將 JSON 有效負載 POST 到此 URL", + "webhookUrlRequired": "Webhook URL 為必填", + "webhookDesc": "每次警示觸發時,對此 URL 發送 POST JSON 酬載", "ntfyServer": "伺服器 URL", - "ntfyTopic": "話題", - "ntfyTopicRequired": "主題是必要的", - "ntfyToken": "訪問令牌(可選)", - "channelSaveFailed": "頻道保存失敗", + "ntfyTopic": "主題", + "ntfyTopicRequired": "主題為必填", + "ntfyToken": "存取權杖(選填)", + "channelSaveFailed": "無法儲存頻道", "test": "測試", - "testSent": "已發送測試通知", + "testSent": "已傳送測試通知", "testFailed": "測試通知失敗", - "addRule": "新增警報規則", - "editRule": "編輯提醒規則", + "addRule": "新增警示規則", + "editRule": "編輯警示規則", "ruleName": "規則名稱", - "ruleNameRequired": "姓名(必填)", - "triggerType": "扳機", - "thresholdValue": "臨界點 (%)", - "durationSeconds": "持續時間(秒,0 = 立即發射)", + "ruleNameRequired": "名稱為必填", + "triggerType": "觸發條件", + "thresholdValue": "閾值(%)", + "durationSeconds": "持續時間(秒,0 = 立即觸發)", "cooldownMinutes": "冷卻時間(分鐘)", "channels": "通知頻道", - "noChannelsHint": "首先在「頻道」標籤中新增頻道", - "ruleSaveFailed": "規則保存失敗" + "noChannelsHint": "請先於「頻道」分頁新增頻道", + "ruleSaveFailed": "無法儲存規則" } } diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 98bce891..0579404f 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -51,7 +51,6 @@ import { tunnelLogger, fileLogger, statsLogger, - systemLogger, dashboardLogger, type LogContext, } from "@/lib/frontend-logger"; @@ -646,14 +645,6 @@ function isDev(): boolean { } const apiHost = import.meta.env.VITE_API_HOST || "localhost"; -let configuredServerUrl: string | null = null; -let embeddedMode = false; - -export interface ServerConfig { - serverUrl: string; - lastUpdated: string; - allowInvalidCertificate?: boolean; -} interface AxiosRequestConfigExtended extends AxiosRequestConfig { startTime?: number; @@ -665,83 +656,6 @@ interface AxiosErrorExtended extends AxiosError { config?: AxiosRequestConfigExtended; } -export async function getServerConfig(): Promise { - if (!isElectron()) return null; - - try { - const result = await ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).electronAPI?.invoke("get-server-config"); - return result; - } catch (error) { - console.error("Failed to get server config:", error); - return null; - } -} - -export async function saveServerConfig(config: ServerConfig): Promise { - if (!isElectron()) return false; - - try { - const result = await ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).electronAPI?.invoke("save-server-config", config); - if (result?.success) { - configuredServerUrl = config.serverUrl; - ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).configuredServerUrl = configuredServerUrl; - updateApiInstances(); - return true; - } - return false; - } catch (error) { - console.error("Failed to save server config:", error); - return false; - } -} - -export function getConfiguredServerUrl(): string | null { - return configuredServerUrl; -} - -export async function testServerConnection( - serverUrl: string, -): Promise<{ success: boolean; error?: string }> { - if (!isElectron()) - return { success: false, error: "Not in Electron environment" }; - - try { - const result = await ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).electronAPI?.invoke("test-server-connection", serverUrl); - return result; - } catch (error) { - console.error("Failed to test server connection:", error); - return { success: false, error: "Connection test failed" }; - } -} - export async function checkElectronUpdate(): Promise<{ success: boolean; status?: "up_to_date" | "requires_update" | "beta"; @@ -767,7 +681,6 @@ export async function checkElectronUpdate(): Promise<{ typeof globalThis & { IS_ELECTRON?: boolean; electronAPI?: unknown; - configuredServerUrl?: string; } ).electronAPI?.invoke("check-electron-update"); return result; @@ -777,60 +690,16 @@ export async function checkElectronUpdate(): Promise<{ } } -export async function getEmbeddedServerStatus(): Promise<{ - running: boolean; - embedded: boolean; - dataDir: string | null; -} | null> { - if (!isElectron()) return null; - - try { - const result = await ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: { - invoke: (channel: string, ...args: unknown[]) => Promise; - }; - } - ).electronAPI?.invoke("get-embedded-server-status"); - return result as { - running: boolean; - embedded: boolean; - dataDir: string | null; - } | null; - } catch { - return null; - } -} - -export function isEmbeddedMode(): boolean { - return embeddedMode; -} - -export function setEmbeddedMode(value: boolean): void { - embeddedMode = value; - if (value) { - configuredServerUrl = null; - initializeApiInstances(); - } -} - function getApiUrl(path: string, defaultPort: number): string { const devMode = isDev(); const electronMode = isElectron(); if (electronMode) { - if (embeddedMode && !configuredServerUrl) { - return `http://localhost:${defaultPort}${path}`; - } - if (configuredServerUrl) { - const baseUrl = configuredServerUrl.replace(/\/$/, ""); - const url = `${baseUrl}${path}`; - return url; - } - console.warn("Electron mode but no server configured!"); - return "http://no-server-configured"; + // The desktop app always runs its embedded local backend as the + // source of truth. A configured remote sync server is a separate, + // narrow connection used only by the sync engine (see + // remote-sync-axios.ts), not by these shared instances. + return `http://localhost:${defaultPort}${path}`; } else if (devMode) { const protocol = window.location.protocol === "https:" ? "https" : "http"; const sslPort = protocol === "https" ? 8443 : defaultPort; @@ -841,6 +710,116 @@ function getApiUrl(path: string, defaultPort: number): string { } } +// ============================================================================ +// PER-HOST ORIGIN ROUTING (Electron desktop only) +// ============================================================================ +// +// hostApi/fileManagerApi/tunnelApi/statsApi above always point at the +// embedded local backend -- they're the shared, always-on instances. When a +// host's connection origin resolves to "remote" (see +// src/ui/lib/connection-origin.ts), the backend that actually holds that +// host's live SSH session is the connected remote server instead, so file +// manager, tunnel, and stats calls for that host must follow it there. +// +// These dynamically-baseURL'd instances resolve the remote server's URL and +// JWT fresh on every request (cheap, and correct even if the user +// connects/disconnects remote sync without an app reload). + +function createRemoteOriginApiInstance(path: string): AxiosInstance { + const instance = axios.create({ + headers: { "Content-Type": "application/json" }, + timeout: 30000, + }); + + instance.interceptors.request.use(async (config: AxiosRequestConfig) => { + const [remoteConfig, remoteJwt] = await Promise.all([ + window.electronAPI?.invoke?.("get-remote-sync-config") as Promise<{ + serverUrl?: string; + } | null>, + window.electronAPI?.invoke?.("get-remote-sync-jwt") as Promise< + string | null + >, + ]); + + const baseUrl = (remoteConfig?.serverUrl || "").replace(/\/$/, ""); + config.baseURL = baseUrl + ? `${baseUrl}${path}` + : "http://no-server-configured"; + + if (config.headers.set) { + config.headers.set("X-Electron-App", "true"); + if (remoteJwt) config.headers.set("Authorization", `Bearer ${remoteJwt}`); + } else { + config.headers["X-Electron-App"] = "true"; + if (remoteJwt) config.headers["Authorization"] = `Bearer ${remoteJwt}`; + } + + return config; + }); + + return instance; +} + +let remoteFileManagerApi: AxiosInstance | null = null; +let remoteTunnelApi: AxiosInstance | null = null; +let remoteStatsApi: AxiosInstance | null = null; + +export function getRemoteFileManagerApi(): AxiosInstance { + if (!remoteFileManagerApi) { + remoteFileManagerApi = createRemoteOriginApiInstance("/ssh/file_manager"); + } + return remoteFileManagerApi; +} + +export function getRemoteTunnelApi(): AxiosInstance { + if (!remoteTunnelApi) { + remoteTunnelApi = createRemoteOriginApiInstance("/ssh"); + } + return remoteTunnelApi; +} + +export function getRemoteStatsApi(): AxiosInstance { + if (!remoteStatsApi) { + remoteStatsApi = createRemoteOriginApiInstance(""); + } + return remoteStatsApi; +} + +// Maps a live SSH session (keyed by sessionId, which today is the host's +// numeric id as a string -- see ensureSSHSessionForHost) to the resolved +// origin it was connected through, so every subsequent file-manager call +// for that session reaches the backend that actually holds it. +const sessionOrigins = new Map(); + +export function setSessionOrigin( + sessionId: string, + origin: "local" | "remote", +): void { + sessionOrigins.set(sessionId, origin); +} + +export function clearSessionOrigin(sessionId: string): void { + sessionOrigins.delete(sessionId); +} + +export function getFileManagerApiForSession(sessionId: string): AxiosInstance { + return sessionOrigins.get(sessionId) === "remote" + ? getRemoteFileManagerApi() + : fileManagerApi; +} + +export function getTunnelApiForOrigin( + origin: "local" | "remote", +): AxiosInstance { + return origin === "remote" ? getRemoteTunnelApi() : tunnelApi; +} + +export function getStatsApiForOrigin( + origin: "local" | "remote", +): AxiosInstance { + return origin === "remote" ? getRemoteStatsApi() : statsApi; +} + function initializeApiInstances() { // Host Management API (port 30001) - supports SSH, RDP, VNC, Telnet hostApi = createApiInstance(getApiUrl("/host", 30001), "HOST"); @@ -921,43 +900,8 @@ export const appReadyPromise: Promise = new Promise((resolve) => { }); function initializeApp() { - if (isElectron()) { - Promise.all([getServerConfig(), getEmbeddedServerStatus()]) - .then(([config, status]) => { - if (status?.embedded && status?.running && !config?.serverUrl) { - embeddedMode = true; - } - if (config?.serverUrl) { - configuredServerUrl = config.serverUrl; - ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).configuredServerUrl = configuredServerUrl; - } else if (embeddedMode) { - // Embedded backend running, no remote server needed - } else { - console.warn("No server URL in config"); - } - initializeApiInstances(); - }) - .catch((error) => { - console.error( - "Failed to load server config, initializing with default:", - error, - ); - initializeApiInstances(); - }) - .finally(() => { - _resolveAppReady(); - }); - } else { - initializeApiInstances(); - _resolveAppReady(); - } + initializeApiInstances(); + _resolveAppReady(); } if (document.readyState === "loading") { @@ -966,29 +910,6 @@ if (document.readyState === "loading") { initializeApp(); } -function updateApiInstances() { - systemLogger.info("Updating API instances with new server configuration", { - operation: "api_instance_update", - configuredServerUrl, - }); - - initializeApiInstances(); - - ( - window as Window & - typeof globalThis & { - IS_ELECTRON?: boolean; - electronAPI?: unknown; - configuredServerUrl?: string; - } - ).configuredServerUrl = configuredServerUrl; - - systemLogger.success("All API instances updated successfully", { - operation: "api_instance_update_complete", - configuredServerUrl, - }); -} - // ============================================================================ // ERROR HANDLING // ============================================================================ @@ -1680,25 +1601,6 @@ export async function loginUser( rememberMe, }); - const isInIframe = - typeof window !== "undefined" && window.self !== window.top; - - if (isInIframe && isElectron() && response.data.success) { - try { - window.parent.postMessage( - { - type: "AUTH_SUCCESS", - source: "login_api", - platform: "desktop", - timestamp: Date.now(), - }, - window.location.origin, - ); - } catch (e) { - console.error("[main-axios] Error posting message to parent:", e); - } - } - if (response.data.token) { localStorage.setItem("jwt", response.data.token); } @@ -1856,6 +1758,67 @@ export async function getSetupRequired(): Promise<{ setup_required: boolean }> { } } +// Module-level (not per-component) so concurrent/duplicate mounts -- e.g. +// React StrictMode's intentional double-invoke of effects in dev, or any +// other double-call -- share one in-flight request instead of each minting +// its own session. Minting more than one session here is not just wasted +// work: the backend sets a fresh `jwt` cookie on every call, and since the +// auth middleware prefers the cookie over the Authorization header, a +// second mint silently invalidates whichever token the app already started +// using, surfacing as a spurious "session expired/revoked" on the very +// next request. +let desktopAutoSessionRequest: Promise | null = null; + +// A 403 from the auto-session endpoint means the backend positively +// evaluated the request and declined (not loopback, or not exactly one +// local user) -- that verdict won't change by retrying. Any other failure +// (connection refused, timeout, 5xx) most likely means the embedded +// backend process hasn't finished booting yet, which is routine on a cold +// first launch, so it's worth retrying rather than treated as final. +export type DesktopAutoSessionOutcome = + | { kind: "success"; data: AuthResponse } + | { kind: "declined" } + | { kind: "retry" }; + +/** + * Electron-only, non-iframed local login: exchanges the embedded backend's + * single auto-provisioned local user for a session without ever showing a + * login form. Only succeeds when exactly one user exists locally (a synced + * or otherwise multi-user install never satisfies this, and falls through + * to a normal login screen instead). + */ +export async function requestDesktopAutoSession(): Promise { + if (desktopAutoSessionRequest) return desktopAutoSessionRequest; + + desktopAutoSessionRequest = (async () => { + try { + const response = await authApi.post("/users/internal/auto-session"); + if (response.data?.token) { + localStorage.setItem("jwt", response.data.token); + } + if (response.data?.success) { + markUserAuthenticated(); + return { kind: "success" as const, data: response.data }; + } + return { kind: "declined" as const }; + } catch (err) { + const status = + (err as { status?: number; response?: { status?: number } })?.status ?? + (err as { response?: { status?: number } })?.response?.status; + if (status === 403) { + return { kind: "declined" as const }; + } + return { kind: "retry" as const }; + } + })(); + + try { + return await desktopAutoSessionRequest; + } finally { + desktopAutoSessionRequest = null; + } +} + export async function getUserCount(): Promise { try { const response = await authApi.get("/users/count"); @@ -2120,6 +2083,7 @@ export { assignRoleToUser, removeRoleFromUser, shareHost, + shareFolder, updateHostAccess, getHostAccess, revokeHostAccess, diff --git a/src/ui/settings/RemoteSyncPanel.tsx b/src/ui/settings/RemoteSyncPanel.tsx new file mode 100644 index 00000000..e8a16cba --- /dev/null +++ b/src/ui/settings/RemoteSyncPanel.tsx @@ -0,0 +1,301 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Server, RefreshCw, Loader2 } from "lucide-react"; +import { Button } from "@/components/button.tsx"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/dialog.tsx"; +import { RemoteSyncServerPicker } from "./RemoteSyncServerPicker.tsx"; +import { ElectronLoginForm } from "@/auth/ElectronLoginForm.tsx"; + +interface RemoteSyncConfig { + serverUrl: string; + connectedAt: string; + lastSyncedAt?: string | null; + lastSyncStatus?: "ok" | "error" | "never"; + lastSyncError?: string | null; +} + +interface RemoteSyncStatus { + connected: boolean; + syncing: boolean; + lastSyncedAt: string | null; + lastError: string | null; + needsReauth: boolean; +} + +type DesktopSettings = { defaultConnectionOrigin: "local" | "remote" }; + +interface RemoteSyncPanelProps { + initialServerUrl?: string; +} + +export function RemoteSyncPanel({ initialServerUrl }: RemoteSyncPanelProps) { + const { t } = useTranslation(); + const [config, setConfig] = useState(null); + const [status, setStatus] = useState(null); + const [desktopSettings, setDesktopSettings] = useState({ + defaultConnectionOrigin: "local", + }); + const [step, setStep] = useState<"idle" | "picker" | "login">("idle"); + const [pendingServerUrl, setPendingServerUrl] = useState(""); + const [syncingNow, setSyncingNow] = useState(false); + const consumedInitialServerUrl = useRef(false); + + const refresh = useCallback(async () => { + const [cfg, st, settings] = await Promise.all([ + window.electronAPI?.invoke?.( + "get-remote-sync-config", + ) as Promise, + window.electronAPI?.invoke?.( + "get-remote-sync-status", + ) as Promise, + window.electronAPI?.invoke?.( + "get-desktop-settings", + ) as Promise, + ]); + setConfig(cfg ?? null); + setStatus(st ?? null); + if (settings) setDesktopSettings(settings); + }, []); + + useEffect(() => { + refresh(); + const unsubscribe = window.electronAPI?.onRemoteSyncStatusChanged?.( + (nextStatus: RemoteSyncStatus) => setStatus(nextStatus), + ); + return () => unsubscribe?.(); + }, [refresh]); + + useEffect(() => { + if (consumedInitialServerUrl.current) return; + if (!initialServerUrl || config?.serverUrl) return; + consumedInitialServerUrl.current = true; + setPendingServerUrl(initialServerUrl); + setStep("picker"); + }, [initialServerUrl, config]); + + const handleConnectClick = () => setStep("picker"); + + const handleServerConfigured = (serverUrl: string) => { + setPendingServerUrl(serverUrl); + setStep("login"); + }; + + const handleAuthSuccess = async () => { + setStep("idle"); + await refresh(); + }; + + const handleDisconnect = async () => { + await window.electronAPI?.invoke?.("clear-remote-sync-config"); + await refresh(); + }; + + const handleSyncNow = async () => { + setSyncingNow(true); + try { + await window.electronAPI?.invoke?.("remote-sync-now"); + } finally { + setSyncingNow(false); + await refresh(); + } + }; + + const handleOriginChange = async (origin: "local" | "remote") => { + const next = { ...desktopSettings, defaultConnectionOrigin: origin }; + setDesktopSettings(next); + await window.electronAPI?.invoke?.("save-desktop-settings", next); + }; + + const isConnected = !!config?.serverUrl; + const connectedToText = config?.serverUrl + ? t("remoteSync.connectedTo", { url: config.serverUrl }) + : ""; + const connectionSubtitleText = status?.needsReauth + ? t("remoteSync.needsReauth") + : status?.lastError + ? t("remoteSync.syncError", { message: status.lastError }) + : status?.lastSyncedAt + ? t("remoteSync.lastSynced", { + time: new Date(status.lastSyncedAt).toLocaleString(), + }) + : t("remoteSync.neverSynced"); + + return ( +
+
+
+ +

{t("remoteSync.title")}

+
+

+ {t("remoteSync.description")} +

+ +
+
+ {isConnected ? ( + <> + + {connectedToText} + + + {connectionSubtitleText} + + + ) : ( + + {t("remoteSync.notConnected")} + + )} +
+ +
+ {isConnected ? ( + <> + {status?.needsReauth && ( + + )} + + + + ) : ( + + )} +
+
+
+ +
+

{t("remoteSync.originTitle")}

+

+ {t("remoteSync.originDescription")} +

+
+ + +
+
+ + !open && setStep("idle")} + > + + + + {t("remoteSync.title")} + + + setStep("idle")} + initialServerUrl={initialServerUrl} + /> + + + + !open && setStep("idle")} + > + e.preventDefault()} + onInteractOutside={(e) => e.preventDefault()} + > + + + {t("remoteSync.signInTitle", { url: pendingServerUrl })} + + +
+ setStep("picker")} + /> +
+
+
+
+ ); +} diff --git a/src/ui/settings/RemoteSyncServerPicker.tsx b/src/ui/settings/RemoteSyncServerPicker.tsx new file mode 100644 index 00000000..9cb0784e --- /dev/null +++ b/src/ui/settings/RemoteSyncServerPicker.tsx @@ -0,0 +1,277 @@ +import React, { useState, useEffect, useRef } from "react"; +import { Button } from "@/components/button.tsx"; +import { Input } from "@/components/input.tsx"; +import { Label } from "@/components/label.tsx"; +import { Alert, AlertTitle, AlertDescription } from "@/components/alert.tsx"; +import { Switch } from "@/components/switch.tsx"; +import { useTranslation } from "react-i18next"; +import { ChevronDown, X } from "lucide-react"; +import { toast } from "sonner"; + +const SAVED_URLS_KEY = "termix_saved_server_urls"; +const MAX_SAVED_URLS = 5; + +function getSavedUrls(): string[] { + try { + const raw = localStorage.getItem(SAVED_URLS_KEY); + if (!raw) return []; + return JSON.parse(raw); + } catch { + return []; + } +} + +function addSavedUrl(url: string) { + const urls = getSavedUrls().filter((u) => u !== url); + urls.unshift(url); + localStorage.setItem( + SAVED_URLS_KEY, + JSON.stringify(urls.slice(0, MAX_SAVED_URLS)), + ); +} + +function removeSavedUrl(url: string) { + const urls = getSavedUrls().filter((u) => u !== url); + localStorage.setItem(SAVED_URLS_KEY, JSON.stringify(urls)); +} + +interface RemoteSyncServerPickerProps { + onServerConfigured: (serverUrl: string) => void; + onCancel: () => void; + initialServerUrl?: string; +} + +export function RemoteSyncServerPicker({ + onServerConfigured, + onCancel, + initialServerUrl, +}: RemoteSyncServerPickerProps) { + const { t } = useTranslation(); + const [serverUrl, setServerUrl] = useState(initialServerUrl ?? ""); + const [allowInvalidCertificate, setAllowInvalidCertificate] = useState(false); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [savedUrls, setSavedUrls] = useState([]); + const [dropdownOpen, setDropdownOpen] = useState(false); + const dropdownRef = useRef(null); + + useEffect(() => { + setSavedUrls(getSavedUrls()); + }, []); + + useEffect(() => { + function handleClickOutside(e: MouseEvent) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(e.target as Node) + ) { + setDropdownOpen(false); + } + } + if (dropdownOpen) { + document.addEventListener("mousedown", handleClickOutside); + } + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [dropdownOpen]); + + const handleSaveConfig = async () => { + if (!serverUrl.trim()) { + setError(t("remoteSync.enterServerUrl")); + return; + } + + setLoading(true); + setError(null); + + try { + const normalizedUrl = serverUrl.trim(); + + if ( + !normalizedUrl.startsWith("http://") && + !normalizedUrl.startsWith("https://") + ) { + setError(t("remoteSync.mustIncludeProtocol")); + setLoading(false); + return; + } + + const testResult = (await window.electronAPI?.invoke?.( + "test-server-connection", + normalizedUrl, + )) as { success?: boolean; error?: string; warning?: string } | undefined; + + if (!testResult?.success) { + setError(testResult?.error || t("remoteSync.connectionTestFailed")); + setLoading(false); + return; + } + + if (testResult.warning) { + toast.warning(testResult.warning); + } + + const result = await window.electronAPI?.invoke?.( + "save-remote-sync-config", + { + serverUrl: normalizedUrl, + allowInvalidCertificate: + normalizedUrl.startsWith("https://") && allowInvalidCertificate, + connectedAt: new Date().toISOString(), + }, + ); + + if ((result as { success?: boolean })?.success) { + addSavedUrl(normalizedUrl); + setSavedUrls(getSavedUrls()); + onServerConfigured(normalizedUrl); + } else { + setError(t("serverConfig.saveFailed")); + } + } catch { + setError(t("serverConfig.saveError")); + } finally { + setLoading(false); + } + }; + + const handleUrlChange = (value: string) => { + setServerUrl(value); + setError(null); + }; + + return ( +
+
+

{t("remoteSync.title")}

+

+ {t("remoteSync.description")} +

+
+ +
+
+ +
+ handleUrlChange(e.target.value)} + disabled={loading} + className={savedUrls.length > 0 ? "pr-9" : ""} + onFocus={() => { + if (savedUrls.length > 0) setDropdownOpen(true); + }} + /> + {savedUrls.length > 0 && ( + + )} + {dropdownOpen && savedUrls.length > 0 && ( +
+

+ {t("remoteSync.savedServers")} +

+ {savedUrls.map((url) => ( +
+ + +
+ ))} +
+ )} +
+
+ + {serverUrl.trim().startsWith("https://") && ( +
+
+ +

+ {t("remoteSync.allowInvalidCertificateDesc")} +

+
+ +
+ )} + + {error && ( + + {t("common.error")} + {error} + + )} + +
+ + +
+
+
+ ); +} diff --git a/src/ui/shell/CommandPalette.tsx b/src/ui/shell/CommandPalette.tsx index 884025d0..3bb46f64 100644 --- a/src/ui/shell/CommandPalette.tsx +++ b/src/ui/shell/CommandPalette.tsx @@ -140,12 +140,15 @@ export function CommandPalette({ return () => window.removeEventListener("keydown", handleKeyDown, true); }, [isOpen, setIsOpen]); - const filteredHosts = hosts.filter( - (h) => - h.name.toLowerCase().includes(search.toLowerCase()) || - h.ip.toLowerCase().includes(search.toLowerCase()) || - h.username.toLowerCase().includes(search.toLowerCase()), - ); + const filteredHosts = hosts.filter((h) => { + const query = search.toLowerCase(); + return ( + h.name.toLowerCase().includes(query) || + h.ip.toLowerCase().includes(query) || + h.username.toLowerCase().includes(query) || + h.tags?.some((tag) => tag.toLowerCase().includes(query)) + ); + }); // Group hosts by folder; ungrouped hosts appear first under an implicit root group const groupedHosts: { folder: string | null; hosts: Host[] }[] = []; diff --git a/src/ui/shell/TabBar.tsx b/src/ui/shell/TabBar.tsx index f9553dc1..f7e2305b 100644 --- a/src/ui/shell/TabBar.tsx +++ b/src/ui/shell/TabBar.tsx @@ -19,6 +19,7 @@ import { Maximize2, Minimize2, FolderOpen, + Share2, } from "lucide-react"; import { tabIcon } from "@/shell/tabUtils"; import { isElectron } from "@/lib/electron"; @@ -42,6 +43,7 @@ export function TabBar({ onRemoveFromSplit, onRenameTab, onOpenFileManager, + onOpenShare, isAppFullscreen, onToggleAppFullscreen, }: { @@ -59,6 +61,7 @@ export function TabBar({ onRemoveFromSplit: (tabId: string) => void; onRenameTab?: (tabId: string, newLabel: string) => void; onOpenFileManager?: (tabId: string) => void; + onOpenShare?: (tabId: string) => void; isAppFullscreen: boolean; onToggleAppFullscreen: () => void; }) { @@ -352,6 +355,19 @@ export function TabBar({ )} + {CONNECTION_TAB_TYPES.includes(tab.type) && onOpenShare && ( + + )}