From 7eb08474ff2bbe29f3a44bd184b59f54d9bbe4ae Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 15:11:42 +0000 Subject: [PATCH 001/105] Add first i18n stuff --- crowdin.yml | 3 + messages/de-DE.json | 22 + messages/en-US.json | 154 + next.config.mjs | 10 +- package-lock.json | 2522 +++++++++++++---- package.json | 2 + .../settings/resources/ResourcesDataTable.tsx | 8 +- .../settings/resources/ResourcesTable.tsx | 40 +- .../[resourceId]/ResourceInfoBox.tsx | 20 +- .../resources/[resourceId]/layout.tsx | 14 +- .../settings/resources/create/page.tsx | 103 +- src/app/[orgId]/settings/resources/page.tsx | 7 +- .../share-links/ShareLinksDataTable.tsx | 8 +- .../settings/share-links/ShareLinksTable.tsx | 29 +- src/app/[orgId]/settings/share-links/page.tsx | 7 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 48 +- .../settings/sites/CreateSiteModal.tsx | 10 +- .../[orgId]/settings/sites/SitesDataTable.tsx | 8 +- .../settings/sites/SitesSplashCard.tsx | 13 +- src/app/[orgId]/settings/sites/SitesTable.tsx | 56 +- src/app/[orgId]/settings/sites/page.tsx | 7 +- src/app/auth/signup/SignupForm.tsx | 15 +- src/app/components/LicenseViolation.tsx | 14 +- src/app/components/OrganizationLanding.tsx | 18 +- src/app/components/SupporterMessage.tsx | 5 +- src/app/globals.css | 5 + src/app/invite/InviteStatusCard.tsx | 24 +- src/app/layout.tsx | 51 +- src/app/setup/page.tsx | 32 +- src/components/LocaleSwitcher.tsx | 24 + src/components/LocaleSwitcherSelect.tsx | 72 + src/components/ProfileIcon.tsx | 6 + src/i18n/config.ts | 4 + src/i18n/request.ts | 11 + src/services/locale.ts | 16 + 35 files changed, 2629 insertions(+), 759 deletions(-) create mode 100644 crowdin.yml create mode 100644 messages/de-DE.json create mode 100644 messages/en-US.json create mode 100644 src/components/LocaleSwitcher.tsx create mode 100644 src/components/LocaleSwitcherSelect.tsx create mode 100644 src/i18n/config.ts create mode 100644 src/i18n/request.ts create mode 100644 src/services/locale.ts diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..6787087e --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /messages/en-US.json + translation: /messages/%locale%.json \ No newline at end of file diff --git a/messages/de-DE.json b/messages/de-DE.json new file mode 100644 index 00000000..6074e8e6 --- /dev/null +++ b/messages/de-DE.json @@ -0,0 +1,22 @@ +{ + "locales": { + "label": "Sprache", + "en-US": "Englisch", + "de-DE": "Deutsch" + }, + "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", + "setupNewOrg": "Neue Organisation", + "setupCreateOrg": "Organisation erstellen", + "setupCreateSite": "Seite erstellen", + "setupCreateResources": "Ressource erstellen", + "setupOrgName": "Organisation's Name", + "setupDisplayName": "Dies ist der Anzeigename für Ihre Organisation.", + "setupOrgId": "Organisations-ID", + "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", + "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", + "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", + "welcome": "Willkommen zu Pangolin", + "componentsCreateOrg": "Erstelle eine Organisation", + "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}." +} \ No newline at end of file diff --git a/messages/en-US.json b/messages/en-US.json new file mode 100644 index 00000000..1c9468dc --- /dev/null +++ b/messages/en-US.json @@ -0,0 +1,154 @@ +{ + "locales": { + "label": "Language", + "en-US": "English", + "de-DE": "German" + }, + "setupCreate": "Create your organization, site, and resources", + "setupNewOrg": "New Organization", + "setupCreateOrg": "Create Organization", + "setupCreateSite": "Create Site", + "setupCreateResources": "Create Resources", + "setupOrgName": "Organization Name", + "setupDisplayName": "This is the display name for your organization.", + "setupOrgId": "Organization ID", + "setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.", + "setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.", + "componentsErrorNoMemberCreate": "You are not currently a member of any organizations. Create an organization to get started.", + "componentsErrorNoMember": "You are not currently a member of any organizations.", + "welcome": "Welcome to Pangolin", + "componentsCreateOrg": "Create an Organization", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", + "dismiss": "Dismiss", + "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", + "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", + "inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.", + "inviteErrorUser": "We're sorry, but it looks like the invite you're trying to access is not for this user.", + "inviteLoginUser": "Please make sure you're logged in as the correct user.", + "inviteErrorNoUser": "We're sorry, but it looks like the invite you're trying to access is not for a user that exists.", + "inviteCreateUser": "Please create an account first.", + "goHome": "Go Home", + "inviteLogInOtherUser": "Log In as a Different User", + "createAnAccount": "Create an Account", + "inviteNotAccepted": "Invite Not Accepted", + "authCreateAccount": "Create an account to get started", + "email": "Email", + "password": "Password", + "confirmPassword": "Confirm Password", + "createAccount": "Create Account", + "viewSettings": "View settings", + "delete": "Delete", + "name": "Name", + "online": "Online", + "offline": "Offline", + "site": "Site", + "dataIn": "Data In", + "dataOut": "Data Out", + "connectionType": "Connection Type", + "local": "Local", + "edit": "Edit", + "siteConfirmDelete": "Confirm Delete Site", + "siteDelete": "Delete Site", + "siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.", + "siteMessageConfirm": "To confirm, please type the name of the site below.", + "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", + "siteManageSites": "Manage Sites", + "siteDescription": "Allow connectivity to your network through secure tunnels", + "siteCreate": "Create Site", + "siteCreateDescription": "Create a new site to start connecting your resources", + "close": "Close", + "siteNameMin": "Name must be at least 2 characters.", + "siteNameMax": "Name must not be longer than 30 characters.", + "siteErrorCreate": "Error creating site", + "siteErrorCreateKeyPair": "Key pair or site defaults not found", + "siteErrorCreateDefaults": "Site defaults not found", + "siteNameDescription": "This is the display name for the site.", + "method": "Method", + "siteMethodDescription": "This is how you will expose connections.", + "siteLearnNewt": "Learn how to install Newt on your system", + "siteSeeConfigOnce": "You will only be able to see the configuration once.", + "siteLoadWGConfig": "Loading WireGuard configuration...", + "siteDocker": "Expand for Docker Deployment Details", + "toggle": "Toggle", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "Local sites do not tunnel, learn more", + "siteConfirmCopy": "I have copied the config", + "searchSites": "Search sites...", + "siteAdd": "Add Site", + "recommended": "Recommended", + "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", + "siteRunsInDocker": "Runs in Docker", + "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", + "siteErrorDelete": "Error deleting site", + "shareTitle": "Manage Share Links", + "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", + "shareSearch": "Search share links...", + "shareCreate": "Create Share Link", + "shareErrorDelete": "Failed to delete link", + "shareErrorDeleteMessage": "An error occurred deleting link", + "shareDeleted": "Link deleted", + "shareDeletedDesciption": "The link has been deleted", + "openMenu": "Open menu", + "resource": "Resource", + "title": "Title", + "created": "Created", + "expires": "Expires", + "never": "Never", + "shareErrorSelectResource": "Please select a resource", + "resourceTitle": "Manage Resources", + "resourceDescription": "Create secure proxies to your private applications", + "resourceSearch": "Search resources...", + "resourceAdd": "Add Resource", + "resourceErrorDelte": "Error deleting resource", + "authentication": "Authentication", + "protected": "Protected", + "notProtected": "Not Protected", + "resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.", + "resourceMessageConfirm": "To confirm, please type the name of the resource below.", + "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", + "resourceHTTP": "HTTPS Resource", + "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", + "resourceRaw": "Raw TCP/UDP Resource", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", + "resourceCreate": "Create Resource", + "resourceCreateDescription": "Follow the steps below to create a new resource", + "resourceSeeAll": "See All Resources", + "resourceInfo": "Resource Information", + "resourceNameDescription": "This is the display name for the resource.", + "siteSelect": "Select site", + "siteSearch": "Search site", + "siteNotFound": "No site found.", + "siteSelectionDescription": "This site will provide connectivity to the resource.", + "resourceType": "Resource Type", + "resourceTypeDescription": "Determine how you want to access your resource", + "resourceHTTPSSettings": "HTTPS Settings", + "resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS", + "domainType": "Domain Type", + "subdomain": "Subdomain", + "baseDomain": "Base Domain", + "subdomnainDescription": "The subdomain where your resource will be accessible.", + "resourceRawSettings": "TCP/UDP Settings", + "resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Port Number", + "resourcePortNumberDescription": "The external port number to proxy requests.", + "cancle": "Cancle", + "resourceConfig": "Configuration Snippets", + "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", + "resourceAddEntrypoints": "Traefik: Add Entrypoints", + "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", + "resourceLearnRaw": "Learn how to configure TCP/UDP resources", + "resourceBack": "Back to Resources", + "resourceGoTo": "Go to Resource", + "visibility": "Visibility", + "enabled": "Enabled", + "disabled": "Disabled", + "general": "General", + "proxy": "Proxy", + "rules": "Rules", + "resourceSettingDescription": "Configure the settings on your resource", + "resourceSetting": "{resourceName} Settings" +} \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index fce5b1fa..e8856db1 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,9 +1,13 @@ +import createNextIntlPlugin from 'next-intl/plugin'; + +const withNextIntl = createNextIntlPlugin(); + /** @type {import('next').NextConfig} */ const nextConfig = { eslint: { - ignoreDuringBuilds: true, + ignoreDuringBuilds: true }, - output: "standalone" + output: 'standalone' }; -export default nextConfig; +export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index c6da9176..747d5c7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.0", + "@heroicons/react": "^2.2.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "2.0.2", "@oslojs/crypto": "1.0.1", @@ -63,6 +64,7 @@ "lucide-react": "0.469.0", "moment": "2.30.1", "next": "15.2.4", + "next-intl": "^4.1.0", "next-themes": "0.4.4", "node-cache": "5.1.2", "node-fetch": "3.3.2", @@ -146,29 +148,29 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -178,13 +180,13 @@ } }, "node_modules/@babel/generator/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -194,9 +196,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -204,9 +206,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -227,28 +229,28 @@ } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz", + "integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -277,13 +279,13 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -303,14 +305,14 @@ } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -369,9 +371,9 @@ "license": "Apache-2.0" }, "node_modules/@ecies/ciphers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.2.tgz", - "integrity": "sha512-ylfGR7PyTd+Rm2PqQowG08BCKA22QuX8NzrL+LxAAvazN10DMwdJ2fWwAzRj05FI/M8vNFGm3cv9Wq/GFWCBLg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.3.tgz", + "integrity": "sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==", "dev": true, "license": "MIT", "engines": { @@ -383,11 +385,31 @@ "@noble/ciphers": "^1.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", + "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", - "dev": true, + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", + "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", "license": "MIT", "optional": true, "dependencies": { @@ -1272,9 +1294,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -1311,12 +1333,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1337,9 +1359,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -1369,21 +1391,21 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.13.0", "levn": "^0.4.1" }, "engines": { @@ -1391,9 +1413,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1403,21 +1425,21 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, @@ -1440,6 +1462,75 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", + "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, "node_modules/@hookform/resolvers": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", @@ -1498,9 +1589,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -1532,6 +1623,28 @@ "@img/sharp-libvips-darwin-arm64": "1.0.4" } }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", @@ -1548,6 +1661,307 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1618,6 +2032,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz", + "integrity": "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@tybys/wasm-util": "^0.9.0" + } + }, "node_modules/@next/env": { "version": "15.2.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", @@ -1762,9 +2188,9 @@ } }, "node_modules/@noble/ciphers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz", - "integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "dev": true, "license": "MIT", "engines": { @@ -1775,13 +2201,13 @@ } }, "node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.0.tgz", + "integrity": "sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==", "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.7.1" + "@noble/hashes": "1.8.0" }, "engines": { "node": "^14.21.3 || >=16" @@ -1791,9 +2217,9 @@ } }, "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, "license": "MIT", "engines": { @@ -1828,6 +2254,38 @@ "@node-rs/argon2-win32-x64-msvc": "2.0.2" } }, + "node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-2.0.2.tgz", + "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-android-arm64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-2.0.2.tgz", + "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@node-rs/argon2-darwin-arm64": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-2.0.2.tgz", @@ -1844,6 +2302,182 @@ "node": ">= 10" } }, + "node_modules/@node-rs/argon2-darwin-x64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-2.0.2.tgz", + "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-freebsd-x64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-2.0.2.tgz", + "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-2.0.2.tgz", + "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-2.0.2.tgz", + "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-2.0.2.tgz", + "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", + "integrity": "sha512-ZM3jrHuJ0dKOhvA80gKJqBpBRmTJTFSo2+xVZR+phQcbAKRlDMSZMFDiKbSTnctkfwNFtjgDdh5g1vaEV04AvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-2.0.2.tgz", + "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-2.0.2.tgz", + "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-2.0.2.tgz", + "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-2.0.2.tgz", + "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-2.0.2.tgz", + "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@node-rs/bcrypt": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", @@ -1873,6 +2507,38 @@ "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, + "node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", + "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@node-rs/bcrypt-darwin-arm64": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", @@ -1889,6 +2555,215 @@ "node": ">= 10" } }, + "node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", + "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3126,6 +4001,24 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, + "node_modules/@react-email/components/node_modules/@react-email/render": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", + "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", + "license": "MIT", + "dependencies": { + "html-to-text": "9.0.5", + "prettier": "3.5.3", + "react-promise-suspense": "0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, "node_modules/@react-email/container": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", @@ -3247,14 +4140,14 @@ } }, "node_modules/@react-email/render": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", - "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.0.tgz", + "integrity": "sha512-X4CsHvXi5X7kTn5NgXNGg8Y5U1VtVJmlpNLlTc2E8RVHKFS3bpr+o/ZXhEPN4yRkdY+ZYN5eqVTV922Hujqsxw==", "license": "MIT", "dependencies": { - "html-to-text": "9.0.5", - "prettier": "3.5.3", - "react-promise-suspense": "0.3.4" + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" }, "engines": { "node": ">=18.0.0" @@ -3319,9 +4212,9 @@ "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz", - "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", "license": "MIT" }, "node_modules/@scarf/scarf": { @@ -3331,6 +4224,12 @@ "hasInstallScript": true, "license": "Apache-2.0" }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", + "license": "MIT" + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", @@ -3379,46 +4278,46 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", - "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.5.tgz", + "integrity": "sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==", "dev": true, "license": "MIT", "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", - "tailwindcss": "4.1.4" + "tailwindcss": "4.1.5" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", - "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.5.tgz", + "integrity": "sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-x64": "4.1.4", - "@tailwindcss/oxide-freebsd-x64": "4.1.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-x64-musl": "4.1.4", - "@tailwindcss/oxide-wasm32-wasi": "4.1.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + "@tailwindcss/oxide-android-arm64": "4.1.5", + "@tailwindcss/oxide-darwin-arm64": "4.1.5", + "@tailwindcss/oxide-darwin-x64": "4.1.5", + "@tailwindcss/oxide-freebsd-x64": "4.1.5", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.5", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.5", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.5", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.5", + "@tailwindcss/oxide-linux-x64-musl": "4.1.5", + "@tailwindcss/oxide-wasm32-wasi": "4.1.5", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.5", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.5" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", - "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.5.tgz", + "integrity": "sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==", "cpu": [ "arm64" ], @@ -3433,9 +4332,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", - "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.5.tgz", + "integrity": "sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==", "cpu": [ "arm64" ], @@ -3450,9 +4349,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", - "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.5.tgz", + "integrity": "sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==", "cpu": [ "x64" ], @@ -3467,9 +4366,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", - "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.5.tgz", + "integrity": "sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==", "cpu": [ "x64" ], @@ -3484,9 +4383,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", - "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.5.tgz", + "integrity": "sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==", "cpu": [ "arm" ], @@ -3501,9 +4400,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", - "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.5.tgz", + "integrity": "sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==", "cpu": [ "arm64" ], @@ -3518,9 +4417,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", - "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.5.tgz", + "integrity": "sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==", "cpu": [ "arm64" ], @@ -3535,9 +4434,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", - "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.5.tgz", + "integrity": "sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==", "cpu": [ "x64" ], @@ -3552,9 +4451,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", - "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.5.tgz", + "integrity": "sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==", "cpu": [ "x64" ], @@ -3569,9 +4468,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", - "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.5.tgz", + "integrity": "sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3587,10 +4486,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", - "@emnapi/wasi-threads": "^1.0.1", - "@napi-rs/wasm-runtime": "^0.2.8", + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -3599,9 +4498,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", - "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.5.tgz", + "integrity": "sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==", "cpu": [ "arm64" ], @@ -3616,9 +4515,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", - "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.5.tgz", + "integrity": "sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==", "cpu": [ "x64" ], @@ -3633,17 +4532,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.4.tgz", - "integrity": "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.5.tgz", + "integrity": "sha512-5lAC2/pzuyfhsFgk6I58HcNy6vPK3dV/PoPxSDuOTVbDvCddYHzHiJZZInGIY0venvzzfrTEUAXJFULAfFmObg==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.4", - "@tailwindcss/oxide": "4.1.4", + "@tailwindcss/node": "4.1.5", + "@tailwindcss/oxide": "4.1.5", "postcss": "^8.4.41", - "tailwindcss": "4.1.4" + "tailwindcss": "4.1.5" } }, "node_modules/@tanstack/react-table": { @@ -3679,6 +4578,16 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/better-sqlite3": { "version": "7.6.12", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", @@ -3738,9 +4647,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, "node_modules/@types/express": { @@ -3757,9 +4666,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.5.tgz", - "integrity": "sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -3828,13 +4737,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/nodemailer": { @@ -3956,20 +4865,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz", + "integrity": "sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.31.1", + "@typescript-eslint/type-utils": "8.31.1", + "@typescript-eslint/utils": "8.31.1", + "@typescript-eslint/visitor-keys": "8.31.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3981,19 +4890,19 @@ "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.1.tgz", + "integrity": "sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.31.1", + "@typescript-eslint/types": "8.31.1", + "@typescript-eslint/typescript-estree": "8.31.1", + "@typescript-eslint/visitor-keys": "8.31.1", "debug": "^4.3.4" }, "engines": { @@ -4005,17 +4914,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz", + "integrity": "sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.31.1", + "@typescript-eslint/visitor-keys": "8.31.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4026,15 +4935,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz", + "integrity": "sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.31.1", + "@typescript-eslint/utils": "8.31.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4045,13 +4954,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.1.tgz", + "integrity": "sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4062,19 +4971,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz", + "integrity": "sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.31.1", + "@typescript-eslint/visitor-keys": "8.31.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4084,7 +4993,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -4140,15 +5049,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.1.tgz", + "integrity": "sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.31.1", + "@typescript-eslint/types": "8.31.1", + "@typescript-eslint/typescript-estree": "8.31.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4159,16 +5068,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.31.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz", + "integrity": "sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.31.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4179,6 +5088,230 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz", + "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz", + "integrity": "sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz", + "integrity": "sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz", + "integrity": "sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz", + "integrity": "sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz", + "integrity": "sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz", + "integrity": "sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz", + "integrity": "sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz", + "integrity": "sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz", + "integrity": "sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz", + "integrity": "sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz", + "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz", + "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz", + "integrity": "sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.9" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz", + "integrity": "sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz", + "integrity": "sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz", + "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4193,9 +5326,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4406,17 +5539,18 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4541,9 +5675,9 @@ } }, "node_modules/axe-core": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", - "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", "license": "MPL-2.0", "engines": { "node": ">=4" @@ -4786,9 +5920,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4799,13 +5933,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -4824,9 +5958,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "version": "1.0.30001716", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz", + "integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==", "funding": [ { "type": "opencollective", @@ -5014,10 +6148,9 @@ } }, "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "license": "MIT", "engines": { "node": ">=0.8" @@ -5381,6 +6514,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "license": "MIT" + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -5433,6 +6572,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5496,9 +6645,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -5591,9 +6740,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -6205,9 +7354,9 @@ } }, "node_modules/eciesjs": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.13.tgz", - "integrity": "sha512-zBdtR4K+wbj10bWPpIOF9DW+eFYQu8miU5ypunh0t4Bvt83ZPlEWgT5Dq/0G6uwEXumZKjfb5BZxYUZQ2Hzn/Q==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.14.tgz", + "integrity": "sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==", "dev": true, "license": "MIT", "dependencies": { @@ -6343,6 +7492,7 @@ "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6515,12 +7665,15 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -6745,25 +7898,24 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz", - "integrity": "sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.7", - "enhanced-resolve": "^5.15.0", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3", - "stable-hash": "^0.0.4" + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -6779,34 +7931,6 @@ } } }, - "node_modules/eslint-import-resolver-typescript/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/eslint-module-utils": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", @@ -6914,9 +8038,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "license": "MIT", "dependencies": { "array-includes": "^3.1.8", @@ -6929,7 +8053,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -6946,9 +8070,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", - "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "license": "MIT", "engines": { "node": ">=10" @@ -6984,9 +8108,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -7244,19 +8368,18 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", - "dev": true, + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -7398,9 +8521,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "license": "ISC" }, "node_modules/fn.name": { @@ -7430,9 +8553,9 @@ } }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -7445,12 +8568,12 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -7473,13 +8596,14 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -7522,6 +8646,13 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "license": "Unlicense", + "optional": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -7576,9 +8707,9 @@ } }, "node_modules/gel": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gel/-/gel-2.0.2.tgz", - "integrity": "sha512-XTKpfNR9HZOw+k0Bl04nETZjuP5pypVAXsZADSdwr3EtyygTTe1RqvftU2FjGu7Tp9e576a9b/iIOxWrRBxMiQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gel/-/gel-2.1.0.tgz", + "integrity": "sha512-HCeRqInCt6BjbMmeghJ6BKeYwOj7WJT5Db6IWWAA3IMUUa7or7zJfTUEkUWCxiOtoXnwnm96sFK9Fr47Yh2hOA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7607,17 +8738,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -7824,6 +8955,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -8039,9 +9171,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -8099,6 +9231,18 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8179,12 +9323,12 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -8195,12 +9339,24 @@ } }, "node_modules/is-bun-module": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", - "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/is-callable": { @@ -8504,12 +9660,12 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8568,9 +9724,9 @@ } }, "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8650,6 +9806,18 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -9124,9 +10292,9 @@ } }, "node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "license": "ISC", "engines": { "node": "20 || >=22" @@ -9183,6 +10351,29 @@ "node": ">= 0.6" } }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", + "optional": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memfs-browser": { + "version": "3.5.10302", + "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", + "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", + "license": "Unlicense", + "optional": true, + "dependencies": { + "memfs": "3.5.3" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -9372,9 +10563,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -9395,6 +10586,21 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/napi-postinstall": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.3.tgz", + "integrity": "sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==", + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9464,6 +10670,42 @@ } } }, + "node_modules/next-intl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.1.0.tgz", + "integrity": "sha512-JNJRjc7sdnfUxhZmGcvzDszZ60tQKrygV/VLsgzXhnJDxQPn1cN2rVpc53adA1SvBJwPK2O6Sc6b4gYSILjCzw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/intl-localematcher": "^0.5.4", + "negotiator": "^1.0.0", + "use-intl": "^4.1.0" + }, + "peerDependencies": { + "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/next-intl/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-themes": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz", @@ -9503,9 +10745,9 @@ } }, "node_modules/node-abi": { - "version": "3.73.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.73.0.tgz", - "integrity": "sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==", + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -9526,19 +10768,11 @@ "node": ">= 8.0.0" } }, - "node_modules/node-cache/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -9592,9 +10826,9 @@ } }, "node_modules/npm": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.2.0.tgz", - "integrity": "sha512-PcnFC6gTo9VDkxVaQ1/mZAS3JoWrDjAI+a6e2NgfYQSGDwftJlbdV0jBMi2V8xQPqbGcWaa7p3UP0SKF+Bhm2g==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.3.0.tgz", + "integrity": "sha512-luthFIP0nFX3+nTfYbWI3p4hP4CiVnKOZ5jdxnF2x7B+Shz8feiSJCLLzgJUNxQ2cDdTaVUiH6RRsMT++vIMZg==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -9673,20 +10907,20 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.0.1", - "@npmcli/config": "^10.1.0", + "@npmcli/arborist": "^9.0.2", + "@npmcli/config": "^10.2.0", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^4.0.2", "@npmcli/package-json": "^6.1.1", "@npmcli/promise-spawn": "^8.0.2", "@npmcli/redact": "^3.1.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/run-script": "^9.1.0", "@sigstore/tuf": "^3.0.0", "abbrev": "^3.0.0", "archy": "~1.0.0", "cacache": "^19.0.1", "chalk": "^5.4.1", - "ci-info": "^4.1.0", + "ci-info": "^4.2.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", @@ -9698,11 +10932,11 @@ "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^10.0.0", - "libnpmdiff": "^8.0.1", - "libnpmexec": "^10.1.0", - "libnpmfund": "^7.0.1", + "libnpmdiff": "^8.0.2", + "libnpmexec": "^10.1.1", + "libnpmfund": "^7.0.2", "libnpmorg": "^8.0.0", - "libnpmpack": "^9.0.1", + "libnpmpack": "^9.0.2", "libnpmpublish": "^11.0.0", "libnpmsearch": "^9.0.0", "libnpmteam": "^8.0.0", @@ -9712,7 +10946,7 @@ "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^11.1.0", + "node-gyp": "^11.2.0", "nopt": "^8.1.0", "normalize-package-data": "^7.0.0", "npm-audit-report": "^6.0.0", @@ -9854,7 +11088,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -9901,7 +11135,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.1.0", + "version": "10.2.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -10055,7 +11289,7 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "9.0.2", + "version": "9.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -10282,12 +11516,11 @@ } }, "node_modules/npm/node_modules/cacache/node_modules/minizlib": { - "version": "3.0.1", + "version": "3.0.2", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^7.0.4", - "rimraf": "^5.0.5" + "minipass": "^7.1.2" }, "engines": { "node": ">= 18" @@ -10351,7 +11584,7 @@ } }, "node_modules/npm/node_modules/ci-info": { - "version": "4.1.0", + "version": "4.2.0", "funding": [ { "type": "github", @@ -10782,11 +12015,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.0.2", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", "diff": "^7.0.0", @@ -10800,11 +12033,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.0", + "version": "10.1.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.0.2", "@npmcli/package-json": "^6.1.1", "@npmcli/run-script": "^9.0.1", "ci-info": "^4.0.0", @@ -10821,11 +12054,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1" + "@npmcli/arborist": "^9.0.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -10844,11 +12077,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.0.2", "@npmcli/run-script": "^9.0.1", "npm-package-arg": "^12.0.0", "pacote": "^21.0.0" @@ -10981,7 +12214,7 @@ } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "4.0.0", + "version": "4.0.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -10997,12 +12230,11 @@ } }, "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { - "version": "3.0.1", + "version": "3.0.2", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^7.0.4", - "rimraf": "^5.0.5" + "minipass": "^7.1.2" }, "engines": { "node": ">= 18" @@ -11122,19 +12354,19 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "11.1.0", + "version": "11.2.0", "inBundle": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", + "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { @@ -11153,12 +12385,11 @@ } }, "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { - "version": "3.0.1", + "version": "3.0.2", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^7.0.4", - "rimraf": "^5.0.5" + "minipass": "^7.1.2" }, "engines": { "node": ">= 18" @@ -11337,12 +12568,11 @@ } }, "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { - "version": "3.0.1", + "version": "3.0.2", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^7.0.4", - "rimraf": "^5.0.5" + "minipass": "^7.1.2" }, "engines": { "node": ">= 18" @@ -11551,20 +12781,6 @@ "node": ">= 4" } }, - "node_modules/npm/node_modules/rimraf": { - "version": "5.0.10", - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", "inBundle": true, @@ -11833,6 +13049,45 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.12", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", "inBundle": true, @@ -12066,9 +13321,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -12117,14 +13372,15 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -12319,6 +13575,26 @@ "@node-rs/bcrypt": "1.9.0" } }, + "node_modules/oslo/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/oslo/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/oslo/node_modules/@node-rs/argon2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", @@ -12344,6 +13620,38 @@ "@node-rs/argon2-win32-x64-msvc": "1.7.0" } }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", + "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", + "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", @@ -12360,6 +13668,195 @@ "node": ">= 10" } }, + "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", + "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", + "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -12522,7 +14019,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12545,18 +14041,18 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -13553,9 +15049,9 @@ } }, "node_modules/readdirp": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", - "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { @@ -13685,9 +15181,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -14317,9 +15813,9 @@ } }, "node_modules/stable-hash": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", - "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "license": "MIT" }, "node_modules/stack-trace": { @@ -14673,15 +16169,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", - "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.5.tgz", + "integrity": "sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==", "license": "MIT" }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -14721,6 +16218,22 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14752,9 +16265,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "license": "MIT", "engines": { "node": ">=18.12" @@ -14867,18 +16380,6 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -14927,9 +16428,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.8.tgz", - "integrity": "sha512-AxSnYRvyFnAiZCUndS3zQZhNfV/B77ZhJ+O7d3K6wfg/jKJY+yv6ahuyXwnyaYA9UdLqnpCwhTRv9pPTBnPR2g==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.9.tgz", + "integrity": "sha512-9O4k1at9pMQff9EAcCEuy1UNO43JmaPQvq+0lwza9Y0BQ6LB38NiMj+qHqjoQf40355MX+gs6wtlR6H9WsSXFg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -15035,9 +16536,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -15066,9 +16567,9 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "devOptional": true, "license": "MIT" }, @@ -15081,6 +16582,38 @@ "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", + "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/JounQin" + }, + "optionalDependencies": { + "@unrs/resolver-binding-darwin-arm64": "1.7.2", + "@unrs/resolver-binding-darwin-x64": "1.7.2", + "@unrs/resolver-binding-freebsd-x64": "1.7.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-musl": "1.7.2", + "@unrs/resolver-binding-wasm32-wasi": "1.7.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -15111,6 +16644,20 @@ } } }, + "node_modules/use-intl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.1.0.tgz", + "integrity": "sha512-mQvDYFvoGn+bm/PWvlQOtluKCknsQ5a9F1Cj0hMfBjMBVTwnOqLPd6srhjvVdEQEQFVyHM1PfyifKqKYb11M9Q==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "^2.2.0", + "@schummar/icu-type-parser": "1.21.5", + "intl-messageformat": "^10.5.14" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + } + }, "node_modules/use-sidecar": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", @@ -15134,9 +16681,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -15292,15 +16839,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, diff --git a/package.json b/package.json index f2ce2cd4..f3034ec4 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.0", + "@heroicons/react": "^2.2.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "2.0.2", "@oslojs/crypto": "1.0.1", @@ -74,6 +75,7 @@ "lucide-react": "0.469.0", "moment": "2.30.1", "next": "15.2.4", + "next-intl": "^4.1.0", "next-themes": "0.4.4", "node-cache": "5.1.2", "node-fetch": "3.3.2", diff --git a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx index a9db3e79..812c79e6 100644 --- a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -16,15 +17,18 @@ export function ResourcesDataTable({ data, createResource }: DataTableProps) { + + const t = useTranslations(); + return ( ); } diff --git a/src/app/[orgId]/settings/resources/ResourcesTable.tsx b/src/app/[orgId]/settings/resources/ResourcesTable.tsx index bfb4f08b..b80c4d50 100644 --- a/src/app/[orgId]/settings/resources/ResourcesTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesTable.tsx @@ -31,6 +31,7 @@ import CopyToClipboard from "@app/components/CopyToClipboard"; import { Switch } from "@app/components/ui/switch"; import { AxiosResponse } from "axios"; import { UpdateResourceResponse } from "@server/routers/resource"; +import { useTranslations } from 'next-intl'; export type ResourceRow = { id: number; @@ -53,6 +54,7 @@ type ResourcesTableProps = { export default function SitesTable({ resources, orgId }: ResourcesTableProps) { const router = useRouter(); + const t = useTranslations(); const api = createApiClient(useEnvContext()); @@ -63,11 +65,11 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { const deleteResource = (resourceId: number) => { api.delete(`/resource/${resourceId}`) .catch((e) => { - console.error("Error deleting resource", e); + console.error(t('resourceErrorDelte'), e); toast({ variant: "destructive", - title: "Error deleting resource", - description: formatAxiosError(e, "Error deleting resource") + title: t('resourceErrorDelte'), + description: formatAxiosError(e, t('resourceErrorDelte')) }); }) .then(() => { @@ -108,7 +110,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { @@ -118,7 +120,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { href={`/${resourceRow.orgId}/settings/resources/${resourceRow.id}`} > - View settings + {t('viewSettings')} - Delete + {t('delete')} @@ -144,7 +146,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -160,7 +162,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Site + {t('site')} ); @@ -219,7 +221,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Authentication + {t('authentication')} ); @@ -231,12 +233,12 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { {resourceRow.authState === "protected" ? ( - Protected + {t('protected')} ) : resourceRow.authState === "not_protected" ? ( - Not Protected + {t('notProtected')} ) : ( - @@ -267,7 +269,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { href={`/${resourceRow.orgId}/settings/resources/${resourceRow.id}`} > @@ -289,23 +291,15 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { dialog={

- Are you sure you want to remove the resource{" "} - - {selectedResource?.name || - selectedResource?.id} - {" "} - from the organization? + {t('resourceQuestionRemove', {selectedResource: selectedResource?.name || selectedResource?.id})}

- Once removed, the resource will no longer be - accessible. All targets attached to the resource - will be removed. + {t('resourceMessageRemove')}

- To confirm, please type the name of the resource - below. + {t('resourceMessageConfirm')}

} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx index 86916755..788652f4 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx @@ -13,11 +13,13 @@ import { } from "@app/components/InfoSection"; import Link from "next/link"; import { Switch } from "@app/components/ui/switch"; +import { useTranslations } from 'next-intl'; type ResourceInfoBoxType = {}; export default function ResourceInfoBox({}: ResourceInfoBoxType) { const { resource, authInfo } = useResourceContext(); + const t = useTranslations(); let fullUrl = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`; @@ -25,7 +27,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { - Resource Information + {t('resourceInfo')} @@ -33,7 +35,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { <> - Authentication + {t('authentication')} {authInfo.password || @@ -42,12 +44,12 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { authInfo.whitelist ? (
- Protected + {t('protected')}
) : (
- Not Protected + {t('notProtected')}
)}
@@ -62,7 +64,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
- Site + {t('site')} {resource.siteName} @@ -71,7 +73,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { ) : ( <> - Protocol + {t('protocol')} {resource.protocol.toUpperCase()} @@ -79,7 +81,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { - Port + {t('port')} )} - Visibility + {t('visibility')} - {resource.enabled ? "Enabled" : "Disabled"} + {resource.enabled ? t('enabled') : t('disabled')}
diff --git a/src/app/[orgId]/settings/resources/[resourceId]/layout.tsx b/src/app/[orgId]/settings/resources/[resourceId]/layout.tsx index edb21303..99ac2919 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/layout.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/layout.tsx @@ -22,6 +22,7 @@ import { BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; import Link from "next/link"; +import { getTranslations } from 'next-intl/server'; interface ResourceLayoutProps { children: React.ReactNode; @@ -30,6 +31,7 @@ interface ResourceLayoutProps { export default async function ResourceLayout(props: ResourceLayoutProps) { const params = await props.params; + const t = await getTranslations(); const { children } = props; @@ -82,22 +84,22 @@ export default async function ResourceLayout(props: ResourceLayoutProps) { const navItems = [ { - title: "General", + title: t('general'), href: `/{orgId}/settings/resources/{resourceId}/general` }, { - title: "Proxy", + title: t('proxy'), href: `/{orgId}/settings/resources/{resourceId}/proxy` } ]; if (resource.http) { navItems.push({ - title: "Authentication", + title: t('authentication'), href: `/{orgId}/settings/resources/{resourceId}/authentication` }); navItems.push({ - title: "Rules", + title: t('rules'), href: `/{orgId}/settings/resources/{resourceId}/rules` }); } @@ -105,8 +107,8 @@ export default async function ResourceLayout(props: ResourceLayoutProps) { return ( <> diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index c1be6353..1b702e89 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -62,6 +62,7 @@ import { cn } from "@app/lib/cn"; import { SquareArrowOutUpRight } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import Link from "next/link"; +import { useTranslations } from 'next-intl'; const baseResourceFormSchema = z.object({ name: z.string().min(1).max(255), @@ -104,6 +105,7 @@ export default function Page() { const api = createApiClient({ env }); const { orgId } = useParams(); const router = useRouter(); + const t = useTranslations(); const [loadingPage, setLoadingPage] = useState(true); const [sites, setSites] = useState([]); @@ -117,15 +119,13 @@ export default function Page() { const resourceTypes: ReadonlyArray = [ { id: "http", - title: "HTTPS Resource", - description: - "Proxy requests to your app over HTTPS using a subdomain or base domain." + title: t('resourceHTTP'), + description: t('resourceHTTPDescription') }, { id: "raw", - title: "Raw TCP/UDP Resource", - description: - "Proxy requests to your app over TCP/UDP using a port number.", + title: t('resourceRaw'), + description: t('resourceRawDescription'), disabled: !env.flags.allowRawResources } ]; @@ -300,8 +300,8 @@ export default function Page() { <>
@@ -320,7 +320,7 @@ export default function Page() { - Resource Information + {t('resourceInfo')} @@ -336,7 +336,7 @@ export default function Page() { render={({ field }) => ( - Name + {t('name')} - This is the - display name for - the resource. + {t('resourceNameDescription')} )} @@ -359,7 +357,7 @@ export default function Page() { render={({ field }) => ( - Site + {t('site')} - + - No - site - found. + {t('siteNotFound')} {sites.map( @@ -437,10 +433,7 @@ export default function Page() { - This site will - provide - connectivity to - the resource. + {t('siteSelectionDescription')} )} @@ -454,11 +447,10 @@ export default function Page() { - Resource Type + {t('resourceType')} - Determine how you want to access your - resource + {t('resourceTypeDescription')} @@ -480,11 +472,10 @@ export default function Page() { - HTTPS Settings + {t('resourceHTTPSSettings')} - Configure how your resource will be - accessed over HTTPS + {t('resourceHTTPSSettingsDescription')} @@ -506,8 +497,7 @@ export default function Page() { }) => ( - Domain - Type + {t('domainType')} @@ -550,7 +539,7 @@ export default function Page() { ) && ( - Subdomain + {t('subdomain')}
@@ -629,10 +618,7 @@ export default function Page() {
- The subdomain - where your - resource will be - accessible. + {t('subdomnainDescription')}
)} @@ -650,8 +636,7 @@ export default function Page() { }) => ( - Base - Domain + {t('baseDomain')} - + @@ -759,7 +743,7 @@ export default function Page() { render={({ field }) => ( - Port Number + {t('resourcePortNumber')} - The external - port number - to proxy - requests. + {t('resourcePortNumberDescription')} )} @@ -810,7 +791,7 @@ export default function Page() { router.push(`/${orgId}/settings/resources`) } > - Cancel + {t('cancle')} @@ -836,17 +817,17 @@ export default function Page() { - Configuration Snippets + {t('resourceConfig')} - Copy and paste these configuration snippets to set up your TCP/UDP resource + {t('resourceConfigDescription')}

- Traefik: Add Entrypoints + {t('resourceAddEntrypoints')}

- Gerbil: Expose Ports in Docker Compose + {t('resourceExposePorts')}

- Learn how to configure TCP/UDP resources + {t('resourceLearnRaw')} @@ -890,7 +871,7 @@ export default function Page() { router.push(`/${orgId}/settings/resources`) } > - Back to Resources + {t('resourceBack')}
diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index 40f6296e..707ece63 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -9,6 +9,7 @@ import { cache } from "react"; import { GetOrgResponse } from "@server/routers/org"; import OrgProvider from "@app/providers/OrgProvider"; import ResourcesSplashCard from "./ResourcesSplashCard"; +import { getTranslations } from 'next-intl/server'; type ResourcesPageProps = { params: Promise<{ orgId: string }>; @@ -68,13 +69,15 @@ export default async function ResourcesPage(props: ResourcesPageProps) { }; }); + const t = await getTranslations(); + return ( <> {/* */} diff --git a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx index 35ab6d3d..9eb37743 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -16,15 +17,18 @@ export function ShareLinksDataTable({ data, createShareLink }: DataTableProps) { + + const t = useTranslations(); + return ( ); } diff --git a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx index 69c88cf7..767f15ad 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx @@ -33,6 +33,7 @@ import { ListAccessTokensResponse } from "@server/routers/accessToken"; import moment from "moment"; import CreateShareLinkForm from "./CreateShareLinkForm"; import { constructShareLink } from "@app/lib/shareLinks"; +import { useTranslations } from 'next-intl'; export type ShareLinkRow = { accessTokenId: string; @@ -54,6 +55,7 @@ export default function ShareLinksTable({ orgId }: ShareLinksTableProps) { const router = useRouter(); + const t = useTranslations(); const api = createApiClient(useEnvContext()); @@ -67,11 +69,8 @@ export default function ShareLinksTable({ async function deleteSharelink(id: string) { await api.delete(`/access-token/${id}`).catch((e) => { toast({ - title: "Failed to delete link", - description: formatAxiosError( - e, - "An error occurred deleting link" - ) + title: t('shareErrorDelete'), + description: formatAxiosError(e,t('shareErrorDeleteMessage')) }); }); @@ -79,8 +78,8 @@ export default function ShareLinksTable({ setRows(newRows); toast({ - title: "Link deleted", - description: "The link has been deleted" + title: t('shareDeleted'), + description: t('shareDeletedDesciption') }); } @@ -102,7 +101,7 @@ export default function ShareLinksTable({ className="h-8 w-8 p-0" > - Open menu + {t('openMenu')} @@ -116,7 +115,7 @@ export default function ShareLinksTable({ }} > @@ -136,7 +135,7 @@ export default function ShareLinksTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Resource + {t('resource')} ); @@ -164,7 +163,7 @@ export default function ShareLinksTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Title + {t('title')} ); @@ -243,7 +242,7 @@ export default function ShareLinksTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Created + {t('created')} ); @@ -263,7 +262,7 @@ export default function ShareLinksTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Expires + {t('expires')} ); @@ -273,7 +272,7 @@ export default function ShareLinksTable({ if (r.expiresAt) { return moment(r.expiresAt).format("lll"); } - return "Never"; + return t('never'); } }, { @@ -286,7 +285,7 @@ export default function ShareLinksTable({ deleteSharelink(row.original.accessTokenId) } > - Delete + {t('delete')}
) diff --git a/src/app/[orgId]/settings/share-links/page.tsx b/src/app/[orgId]/settings/share-links/page.tsx index 0bfa023d..14a0d9b6 100644 --- a/src/app/[orgId]/settings/share-links/page.tsx +++ b/src/app/[orgId]/settings/share-links/page.tsx @@ -9,6 +9,7 @@ import OrgProvider from "@app/providers/OrgProvider"; import { ListAccessTokensResponse } from "@server/routers/accessToken"; import ShareLinksTable, { ShareLinkRow } from "./ShareLinksTable"; import ShareableLinksSplash from "./ShareLinksSplash"; +import { getTranslations } from 'next-intl/server'; type ShareLinksPageProps = { params: Promise<{ orgId: string }>; @@ -51,13 +52,15 @@ export default async function ShareLinksPage(props: ShareLinksPageProps) { (token) => ({ ...token }) as ShareLinkRow ); + const t = await getTranslations(); + return ( <> {/* */} diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index c4da2336..74667af2 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -50,15 +50,18 @@ import { CollapsibleTrigger } from "@app/components/ui/collapsible"; import LoaderPlaceholder from "@app/components/PlaceHolderLoader"; +import { useTranslations } from 'next-intl'; + +const t = useTranslations(); const createSiteFormSchema = z.object({ name: z .string() .min(2, { - message: "Name must be at least 2 characters." + message: {t('siteNameMin')} }) .max(30, { - message: "Name must not be longer than 30 characters." + message: {t('siteNameMax')} }), method: z.enum(["wireguard", "newt", "local"]) }); @@ -169,8 +172,8 @@ export default function CreateSiteForm({ if (!keypair || !siteDefaults) { toast({ variant: "destructive", - title: "Error creating site", - description: "Key pair or site defaults not found" + title: {t('siteErrorCreate')}, + description: {t('siteErrorCreateKeyPair')} }); setLoading?.(false); setIsLoading(false); @@ -188,8 +191,8 @@ export default function CreateSiteForm({ if (!siteDefaults) { toast({ variant: "destructive", - title: "Error creating site", - description: "Site defaults not found" + title: {t('siteErrorCreate')}, + description: {t('siteErrorCreateDefaults')} }); setLoading?.(false); setIsLoading(false); @@ -212,7 +215,7 @@ export default function CreateSiteForm({ .catch((e) => { toast({ variant: "destructive", - title: "Error creating site", + title: {t('siteErrorCreate')}, description: formatAxiosError(e) }); }); @@ -285,13 +288,13 @@ PersistentKeepalive = 5` name="name" render={({ field }) => ( - Name + {t('name')} - This is the display name for the site. + {t('siteNameDescription')} )} @@ -301,7 +304,7 @@ PersistentKeepalive = 5` name="method" render={({ field }) => ( - Method + {t('method')} @@ -156,7 +159,7 @@ export default function SignupForm({ name="password" render={({ field }) => ( - Password + {t('password')} ( - Confirm Password + {t('confirmPassword')} - Create Account + {t('createAccount')} diff --git a/src/app/components/LicenseViolation.tsx b/src/app/components/LicenseViolation.tsx index 75d544d3..8fa38454 100644 --- a/src/app/components/LicenseViolation.tsx +++ b/src/app/components/LicenseViolation.tsx @@ -8,10 +8,12 @@ import { Button } from "@app/components/ui/button"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useState } from "react"; +import { useTranslations } from 'next-intl'; export default function LicenseViolation() { const { licenseStatus } = useLicenseStatusContext(); const [isDismissed, setIsDismissed] = useState(false); + const t = useTranslations(); if (!licenseStatus || isDismissed) return null; @@ -21,15 +23,14 @@ export default function LicenseViolation() {

- Invalid or expired license keys detected. Follow license - terms to continue using all features. + {t('componentsInvalidKey')}

@@ -46,17 +47,14 @@ export default function LicenseViolation() {

- License Violation: This server is using{" "} - {licenseStatus.usedSites} sites which exceeds its - licensed limit of {licenseStatus.maxSites} sites. Follow - license terms to continue using all features. + {t('componentsLicenseViolation', {usedSites: licenseStatus.usedSites, maxSites: licenseStatus.maxSites})}

diff --git a/src/app/components/OrganizationLanding.tsx b/src/app/components/OrganizationLanding.tsx index 58e765e6..1a3a4086 100644 --- a/src/app/components/OrganizationLanding.tsx +++ b/src/app/components/OrganizationLanding.tsx @@ -11,6 +11,8 @@ import { import { Button } from "@/components/ui/button"; import Link from "next/link"; import { ArrowRight, Plus } from "lucide-react"; +import { useTranslations } from 'next-intl'; + interface Organization { id: string; name: string; @@ -31,31 +33,31 @@ export default function OrganizationLanding({ setSelectedOrg(orgId); }; + const t = useTranslations(); + function getDescriptionText() { if (organizations.length === 0) { if (!disableCreateOrg) { - return "You are not currently a member of any organizations. Create an organization to get started."; + return t('componentsErrorNoMemberCreate'); } else { - return "You are not currently a member of any organizations."; + return t('componentsErrorNoMember'); } } - return `You're a member of ${organizations.length} ${ - organizations.length === 1 ? "organization" : "organizations" - }.`; + return t('componentsMember', {count: organizations.length}); } return ( - Welcome to Pangolin + {t('welcome')} {getDescriptionText()} {organizations.length === 0 ? ( disableCreateOrg ? (

- You are not currently a member of any organizations. + t('componentsErrorNoMember')

) : ( @@ -64,7 +66,7 @@ export default function OrganizationLanding({ size="lg" > - Create an Organization + {t('componentsCreateOrg')} ) diff --git a/src/app/components/SupporterMessage.tsx b/src/app/components/SupporterMessage.tsx index f21cd52c..d9869f7b 100644 --- a/src/app/components/SupporterMessage.tsx +++ b/src/app/components/SupporterMessage.tsx @@ -3,8 +3,11 @@ import React from "react"; import confetti from "canvas-confetti"; import { Star } from "lucide-react"; +import { useTranslations } from 'next-intl'; export default function SupporterMessage({ tier }: { tier: string }) { + const t = useTranslations(); + return (
- Thank you for supporting Pangolin as a {tier}! + {t('componentsSupporterMessage', {tier: tier})}
); diff --git a/src/app/globals.css b/src/app/globals.css index e2a6e31a..9b6c18bc 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -118,3 +118,8 @@ @apply bg-background text-foreground; } } + +p { + word-break: keep-all; + white-space: normal; +} \ No newline at end of file diff --git a/src/app/invite/InviteStatusCard.tsx b/src/app/invite/InviteStatusCard.tsx index 313bee66..1eee0174 100644 --- a/src/app/invite/InviteStatusCard.tsx +++ b/src/app/invite/InviteStatusCard.tsx @@ -12,6 +12,7 @@ import { import { useEnvContext } from "@app/hooks/useEnvContext"; import { XCircle } from "lucide-react"; import { useRouter } from "next/navigation"; +import { useTranslations } from 'next-intl'; type InviteStatusCardProps = { type: "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in"; @@ -23,8 +24,8 @@ export default function InviteStatusCard({ token, }: InviteStatusCardProps) { const router = useRouter(); - const api = createApiClient(useEnvContext()); + const t = useTranslations(); async function goToLogin() { await api.post("/auth/logout", {}); @@ -41,8 +42,7 @@ export default function InviteStatusCard({ return (

- We're sorry, but it looks like the invite you're trying - to access has not been accepted or is no longer valid. + {t('inviteErrorNotValid')}

  • The invite may have expired
  • @@ -55,11 +55,10 @@ export default function InviteStatusCard({ return (

    - We're sorry, but it looks like the invite you're trying - to access is not for this user. + {t('inviteErrorUser')}

    - Please make sure you're logged in as the correct user. + {t('inviteLoginUser')}

    ); @@ -67,11 +66,10 @@ export default function InviteStatusCard({ return (

    - We're sorry, but it looks like the invite you're trying - to access is not for a user that exists. + {t('inviteErrorNoUser')}

    - Please create an account first. + {t('inviteCreateUser')}

    ); @@ -86,15 +84,15 @@ export default function InviteStatusCard({ router.push("/"); }} > - Go Home + {t('goHome')} ); } else if (type === "wrong_user") { return ( - + ); } else if (type === "user_does_not_exist") { - return ; + return ; } } @@ -109,7 +107,7 @@ export default function InviteStatusCard({ />
*/} - Invite Not Accepted + {t('inviteNotAccepted')} {renderBody()} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e0089bc5..95db19d7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -12,6 +12,8 @@ import { IsSupporterKeyVisibleResponse } from "@server/routers/supporterKey"; import LicenseStatusProvider from "@app/providers/LicenseStatusProvider"; import { GetLicenseStatusResponse } from "@server/routers/license"; import LicenseViolation from "./components/LicenseViolation"; +import { NextIntlClientProvider } from 'next-intl'; +import { getLocale } from 'next-intl/server'; export const metadata: Metadata = { title: `Dashboard - Pangolin`, @@ -29,6 +31,7 @@ export default async function RootLayout({ children: React.ReactNode; }>) { const env = pullEnv(); + const locale = await getLocale(); let supporterData = { visible: true @@ -47,31 +50,33 @@ export default async function RootLayout({ const licenseStatus = licenseStatusRes.data.data; return ( - + - - - - - {/* Main content */} -
-
- - {children} + + + + + + {/* Main content */} +
+
+ + {children} +
-
- - - - - + + + + + + ); diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 5420748c..3ef5edf8 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -33,6 +33,7 @@ import { } from "@app/components/ui/form"; import { Alert, AlertDescription } from "@app/components/ui/alert"; import CreateSiteForm from "../[orgId]/settings/sites/CreateSiteForm"; +import { useTranslations } from 'next-intl'; type Step = "org" | "site" | "resources"; @@ -112,13 +113,15 @@ export default function StepperForm() { setLoading(false); } + const t = useTranslations(); + return ( <> - New Organization + {t('setupNewOrg')} - Create your organization, site, and resources + {t('setupCreate')} @@ -141,7 +144,7 @@ export default function StepperForm() { : "text-muted-foreground" }`} > - Create Org + {t('setupCreateOrg')}
@@ -161,7 +164,7 @@ export default function StepperForm() { : "text-muted-foreground" }`} > - Create Site + {t('setupCreateSite')}
@@ -181,7 +184,7 @@ export default function StepperForm() { : "text-muted-foreground" }`} > - Create Resources + {t('setupCreateResources')}
@@ -200,7 +203,7 @@ export default function StepperForm() { render={({ field }) => ( - Organization Name + {t('setupOrgName')} - This is the display name for - your organization. + {t('setupDisplayName')} )} @@ -240,7 +242,7 @@ export default function StepperForm() { render={({ field }) => ( - Organization ID + {t('setupOrgId')} - This is the unique - identifier for your - organization. This is - separate from the display - name. + {t('setupIdentifierMessage')} )} @@ -263,9 +261,7 @@ export default function StepperForm() { {orgIdTaken && ( - Organization ID is already - taken. Please choose a different - one. + {t('setupErrorIdentifier')} )} @@ -288,7 +284,7 @@ export default function StepperForm() { orgIdTaken } > - Create Organization + {t('setupCreateOrg')} diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx new file mode 100644 index 00000000..f03f3ace --- /dev/null +++ b/src/components/LocaleSwitcher.tsx @@ -0,0 +1,24 @@ +import { useLocale, useTranslations } from 'next-intl'; +import LocaleSwitcherSelect from './LocaleSwitcherSelect'; + +export default function LocaleSwitcher() { + const t = useTranslations('locales'); + const locale = useLocale(); + + return ( + + ); +} diff --git a/src/components/LocaleSwitcherSelect.tsx b/src/components/LocaleSwitcherSelect.tsx new file mode 100644 index 00000000..93fe1a43 --- /dev/null +++ b/src/components/LocaleSwitcherSelect.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { CheckIcon, LanguageIcon } from '@heroicons/react/24/solid'; +import * as Select from '@radix-ui/react-select'; +import clsx from 'clsx'; +import { useTransition } from 'react'; +import { Locale } from '@/i18n/config'; +import { setUserLocale } from '@/services/locale'; + +type Props = { + defaultValue: string; + items: Array<{value: string; label: string}>; + label: string; +}; + +export default function LocaleSwitcherSelect({ + defaultValue, + items, + label +}: Props) { + const [isPending, startTransition] = useTransition(); + + function onChange(value: string) { + const locale = value as Locale; + startTransition(() => { + setUserLocale(locale); + }); + } + + return ( +
+ + + + + + + + + + {items.map((item) => ( + +
+ {item.value === defaultValue && ( + + )} +
+ {item.label} +
+ ))} +
+ +
+
+
+
+ ); +} diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx index 55b939f0..443f9ac3 100644 --- a/src/components/ProfileIcon.tsx +++ b/src/components/ProfileIcon.tsx @@ -23,6 +23,8 @@ import Disable2FaForm from "./Disable2FaForm"; import Enable2FaForm from "./Enable2FaForm"; import SupporterStatus from "./SupporterStatus"; import { UserType } from "@server/types/UserTypes"; +import LocaleSwitcher from '@app/components/LocaleSwitcher'; + export default function ProfileIcon() { const { setTheme, theme } = useTheme(); @@ -157,6 +159,10 @@ export default function ProfileIcon() { ) )} + +
+ +
logout()}> {/* */} diff --git a/src/i18n/config.ts b/src/i18n/config.ts new file mode 100644 index 00000000..7e71f24b --- /dev/null +++ b/src/i18n/config.ts @@ -0,0 +1,4 @@ +export type Locale = (typeof locales)[number]; + +export const locales = ['en-US', 'de-DE'] as const; +export const defaultLocale: Locale = 'en-US'; \ No newline at end of file diff --git a/src/i18n/request.ts b/src/i18n/request.ts new file mode 100644 index 00000000..352cccc8 --- /dev/null +++ b/src/i18n/request.ts @@ -0,0 +1,11 @@ +import {getRequestConfig} from 'next-intl/server'; +import {getUserLocale} from '../services/locale'; + +export default getRequestConfig(async () => { + const locale = await getUserLocale(); + + return { + locale, + messages: (await import(`../../messages/${locale}.json`)).default + }; +}); \ No newline at end of file diff --git a/src/services/locale.ts b/src/services/locale.ts new file mode 100644 index 00000000..29051152 --- /dev/null +++ b/src/services/locale.ts @@ -0,0 +1,16 @@ +'use server'; + +import {cookies} from 'next/headers'; +import {Locale, defaultLocale} from '@/i18n/config'; + +// In this example the locale is read from a cookie. You could alternatively +// also read it from a database, backend service, or any other source. +const COOKIE_NAME = 'NEXT_LOCALE'; + +export async function getUserLocale() { + return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale; +} + +export async function setUserLocale(locale: Locale) { + (await cookies()).set(COOKIE_NAME, locale); +} From 059081ad8b240e32494764f04db986f5a90f62d4 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 16:23:08 +0000 Subject: [PATCH 002/105] more i18n --- messages/en-US.json | 45 +++++++++++++- .../invitations/InvitationsDataTable.tsx | 6 +- .../settings/access/invitations/page.tsx | 7 ++- .../settings/access/roles/RolesDataTable.tsx | 8 ++- .../settings/access/roles/RolesTable.tsx | 11 ++-- .../[orgId]/settings/access/roles/page.tsx | 6 +- .../settings/access/users/UsersDataTable.tsx | 8 ++- .../settings/access/users/UsersTable.tsx | 14 +++-- .../[orgId]/settings/access/users/page.tsx | 7 ++- src/app/[orgId]/settings/general/layout.tsx | 9 ++- src/app/[orgId]/settings/general/page.tsx | 62 ++++++++----------- .../resources/[resourceId]/rules/page.tsx | 1 + 12 files changed, 122 insertions(+), 62 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 1c9468dc..3eb137e9 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -10,7 +10,7 @@ "setupCreateSite": "Create Site", "setupCreateResources": "Create Resources", "setupOrgName": "Organization Name", - "setupDisplayName": "This is the display name for your organization.", + "orgDisplayName": "This is the display name of your organization.", "setupOrgId": "Organization ID", "setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.", "setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.", @@ -150,5 +150,46 @@ "proxy": "Proxy", "rules": "Rules", "resourceSettingDescription": "Configure the settings on your resource", - "resourceSetting": "{resourceName} Settings" + "resourceSetting": "{resourceName} Settings", + "alwaysAllow": "Always Allow", + "alwaysDeny": "Always Deny", + "orgSettingsDescription": "Configure your organization's general settings", + "orgGeneralSettings": "Organization Settings", + "orgGeneralSettingsDescription": "Manage your organization details and configuration", + "orgGeneralSave": "Save General Settings", + "orgDangerZone": "Danger Zone", + "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", + "orgDelete": "Delete Organization", + "orgDeleteConfirm": "Confirm Delete Organization", + "orgMessageRemove": "This action is irreversible and will delete all associated data.", + "orgMessageConfirm": "To confirm, please type the name of the organization below.", + "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", + "orgUpdated": "Organization updated", + "orgUpdatedDescription": "The organization has been updated.", + "orgErrorUpdate": "Failed to update organization", + "orgErrorUpdateMessage": "An error occurred while updating the organization.", + "orgErrorFetch": "Failed to fetch organizations", + "orgErrorFetchMessage": "An error occurred while listing your organizations", + "orgErrorDelete": "Failed to delete organization", + "orgErrorDeleteMessage": "An error occurred while deleting the organization.", + "orgDeleted": "Organization deleted", + "orgDeletedMessage": "The organization and its data has been deleted.", + "accessUsersManage": "Manage Users", + "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", + "accessUsersSearch": "Search users...", + "accessUserCreate": "Create User", + "accessUserRemove": "Remove User", + "username": "Username", + "identityProvider": "Identity Provider", + "role": "Role", + "accessRoleNameRequired": "Name is required", + "accessRolesManage": "Manage Roles", + "accessRolesDescription": "Configure roles to manage access to your organization", + "accessRolesSearch": "Search roles...", + "accessRolesAdd": "Add Role", + "accessRoleDelete": "Delete Role", + "description": "Description", + "inviteTitle": "Open Invitations", + "inviteDescription": "Manage your invitations to other users", + "inviteSearch": "Search invitations..." } \ No newline at end of file diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx index e2154b2d..ecce6913 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -14,12 +15,15 @@ export function InvitationsDataTable({ columns, data }: DataTableProps) { + + const t = useTranslations(); + return ( ); diff --git a/src/app/[orgId]/settings/access/invitations/page.tsx b/src/app/[orgId]/settings/access/invitations/page.tsx index 9c8b5e11..e7899282 100644 --- a/src/app/[orgId]/settings/access/invitations/page.tsx +++ b/src/app/[orgId]/settings/access/invitations/page.tsx @@ -9,6 +9,7 @@ import UserProvider from "@app/providers/UserProvider"; import { verifySession } from "@app/lib/auth/verifySession"; import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from 'next-intl/server'; type InvitationsPageProps = { params: Promise<{ orgId: string }>; @@ -71,11 +72,13 @@ export default async function InvitationsPage(props: InvitationsPageProps) { }; }); + const t = await getTranslations(); + return ( <> diff --git a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx index 93ddd1cc..ab381813 100644 --- a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -16,15 +17,18 @@ export function RolesDataTable({ data, createRole }: DataTableProps) { + + const t = useTranslations(); + return ( ); } diff --git a/src/app/[orgId]/settings/access/roles/RolesTable.tsx b/src/app/[orgId]/settings/access/roles/RolesTable.tsx index 7ebcfbce..8a3f3647 100644 --- a/src/app/[orgId]/settings/access/roles/RolesTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesTable.tsx @@ -19,6 +19,7 @@ import CreateRoleForm from "./CreateRoleForm"; import DeleteRoleForm from "./DeleteRoleForm"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from 'next-intl'; export type RoleRow = Role; @@ -38,6 +39,8 @@ export default function UsersTable({ roles: r }: RolesTableProps) { const { org } = useOrgContext(); + const t = useTranslations(); + const columns: ColumnDef[] = [ { id: "actions", @@ -58,7 +61,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) { className="h-8 w-8 p-0" > - Open menu + {t('openMenu')} @@ -71,7 +74,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) { }} > - Delete Role + {t('accessRoleDelete')} @@ -92,7 +95,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -100,7 +103,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) { }, { accessorKey: "description", - header: "Description" + header: t('description') } ]; diff --git a/src/app/[orgId]/settings/access/roles/page.tsx b/src/app/[orgId]/settings/access/roles/page.tsx index 16fefd7d..fed52c26 100644 --- a/src/app/[orgId]/settings/access/roles/page.tsx +++ b/src/app/[orgId]/settings/access/roles/page.tsx @@ -9,6 +9,7 @@ import RolesTable, { RoleRow } from "./RolesTable"; import { SidebarSettings } from "@app/components/SidebarSettings"; import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from 'next-intl/server'; type RolesPageProps = { params: Promise<{ orgId: string }>; @@ -62,12 +63,13 @@ export default async function RolesPage(props: RolesPageProps) { } const roleRows: RoleRow[] = roles; + const t = await getTranslations(); return ( <> diff --git a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx index 643d8641..93998a45 100644 --- a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx +++ b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -16,15 +17,18 @@ export function UsersDataTable({ data, inviteUser }: DataTableProps) { + + const t = useTranslations(); + return ( ); } diff --git a/src/app/[orgId]/settings/access/users/UsersTable.tsx b/src/app/[orgId]/settings/access/users/UsersTable.tsx index 8036cc84..eebf6d22 100644 --- a/src/app/[orgId]/settings/access/users/UsersTable.tsx +++ b/src/app/[orgId]/settings/access/users/UsersTable.tsx @@ -20,6 +20,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useUserContext } from "@app/hooks/useUserContext"; +import { useTranslations } from 'next-intl'; export type UserRow = { id: string; @@ -47,6 +48,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { const api = createApiClient(useEnvContext()); const { user, updateUser } = useUserContext(); const { org } = useOrgContext(); + const t = useTranslations(); const columns: ColumnDef[] = [ { @@ -68,7 +70,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { className="h-8 w-8 p-0" > - Open menu + {t('openMenu')} @@ -79,7 +81,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { className="block w-full" > - Manage User + {t('accessUsersManage')} {`${userRow.username}-${userRow.idpId}` !== @@ -95,7 +97,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { }} > - Remove User + {t('accessUserRemove')} )} @@ -118,7 +120,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Username + {t('username')} ); @@ -134,7 +136,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Identity Provider + {t('identityProvider')} ); @@ -150,7 +152,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Role + {t('role')} ); diff --git a/src/app/[orgId]/settings/access/users/page.tsx b/src/app/[orgId]/settings/access/users/page.tsx index f82cfdb0..11a2570d 100644 --- a/src/app/[orgId]/settings/access/users/page.tsx +++ b/src/app/[orgId]/settings/access/users/page.tsx @@ -10,6 +10,7 @@ import UserProvider from "@app/providers/UserProvider"; import { verifySession } from "@app/lib/auth/verifySession"; import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from 'next-intl/server'; type UsersPageProps = { params: Promise<{ orgId: string }>; @@ -83,11 +84,13 @@ export default async function UsersPage(props: UsersPageProps) { }; }); + const t = await getTranslations(); + return ( <> diff --git a/src/app/[orgId]/settings/general/layout.tsx b/src/app/[orgId]/settings/general/layout.tsx index a2d9cc0a..3fae9ce4 100644 --- a/src/app/[orgId]/settings/general/layout.tsx +++ b/src/app/[orgId]/settings/general/layout.tsx @@ -10,6 +10,7 @@ import { GetOrgUserResponse } from "@server/routers/user"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; +import { getTranslations } from 'next-intl/server'; type GeneralSettingsProps = { children: React.ReactNode; @@ -57,9 +58,11 @@ export default async function GeneralSettingsPage({ redirect(`/${orgId}`); } + const t = await getTranslations(); + const navItems = [ { - title: "General", + title: t('general'), href: `/{orgId}/settings/general`, }, ]; @@ -69,8 +72,8 @@ export default async function GeneralSettingsPage({ diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 9819be59..14a2885c 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -44,6 +44,7 @@ import { SettingsSectionFooter } from "@app/components/Settings"; import { useUserContext } from "@app/hooks/useUserContext"; +import { useTranslations } from 'next-intl'; const GeneralFormSchema = z.object({ name: z.string() @@ -79,8 +80,8 @@ export default function GeneralPage() { ); toast({ - title: "Organization deleted", - description: "The organization and its data has been deleted." + title: t('orgDeleted'), + description: t('orgDeletedMessage') }); if (res.status === 200) { @@ -90,11 +91,8 @@ export default function GeneralPage() { console.error(err); toast({ variant: "destructive", - title: "Failed to delete org", - description: formatAxiosError( - err, - "An error occurred while deleting the org." - ) + title: t('orgErrorDelete'), + description: formatAxiosError(err,t('orgErrorDeleteMessage')) }); } finally { setLoadingDelete(false); @@ -121,11 +119,8 @@ export default function GeneralPage() { console.error(err); toast({ variant: "destructive", - title: "Failed to fetch orgs", - description: formatAxiosError( - err, - "An error occurred while listing your orgs" - ) + title: t('orgErrorFetch'), + description: formatAxiosError(err,t('orgErrorFetchMessage')) }); } } @@ -138,8 +133,8 @@ export default function GeneralPage() { }) .then(() => { toast({ - title: "Organization updated", - description: "The organization has been updated." + title: t('orgUpdated'), + description: t('orgUpdatedDescription') }); router.refresh(); @@ -147,11 +142,8 @@ export default function GeneralPage() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to update org", - description: formatAxiosError( - e, - "An error occurred while updating the org." - ) + title: t('orgErrorUpdate'), + description: formatAxiosError(e,t('orgErrorUpdateMessage')) }); }) .finally(() => { @@ -159,6 +151,8 @@ export default function GeneralPage() { }); } + const t = useTranslations(); + return (

- Are you sure you want to delete the organization{" "} - {org?.org.name}? + {t('orgQuestionRemove', {selectedOrg: org?.org.name})}

- This action is irreversible and will delete all - associated data. + {t('orgMessageRemove')}

- To confirm, type the name of the organization below. + {t('orgMessageConfirm')}

} - buttonText="Confirm Delete Organization" + buttonText={t('orgDeleteConfirm')} onConfirm={deleteOrg} string={org?.org.name || ""} - title="Delete Organization" + title={t('orgDelete')} /> - Organization Settings + {t('orgGeneralSettings')} - Manage your organization details and configuration + {t('orgGeneralSettingsDescription')} @@ -210,14 +202,13 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - Name + {t('name')} - This is the display name of the - organization. + {t('orgDisplayName')} )} @@ -234,17 +225,16 @@ export default function GeneralPage() { loading={loadingSave} disabled={loadingSave} > - Save General Settings + {t('orgGeneralSave')} - Danger Zone + {t('orgDangerZone')} - Once you delete this org, there is no going back. Please - be certain. + {t('orgDangerZoneDescription')} @@ -256,7 +246,7 @@ export default function GeneralPage() { loading={loadingDelete} disabled={loadingDelete} > - Delete Organization Data + {t('orgDelete')} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 2a9fa00f..139b9926 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -1,4 +1,5 @@ "use client"; + import { useEffect, useState, use } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; From d460dd35c79abb63e78324b8b1098aea1325aca4 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 16:29:47 +0000 Subject: [PATCH 003/105] deleted: messages/de-DE.json --- messages/de-DE.json | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 messages/de-DE.json diff --git a/messages/de-DE.json b/messages/de-DE.json deleted file mode 100644 index 6074e8e6..00000000 --- a/messages/de-DE.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "locales": { - "label": "Sprache", - "en-US": "Englisch", - "de-DE": "Deutsch" - }, - "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", - "setupNewOrg": "Neue Organisation", - "setupCreateOrg": "Organisation erstellen", - "setupCreateSite": "Seite erstellen", - "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Organisation's Name", - "setupDisplayName": "Dies ist der Anzeigename für Ihre Organisation.", - "setupOrgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", - "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", - "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", - "welcome": "Willkommen zu Pangolin", - "componentsCreateOrg": "Erstelle eine Organisation", - "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}." -} \ No newline at end of file From 9d68c5666f19dbd44b3249720cce679c6ee268ef Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 16:35:43 +0000 Subject: [PATCH 004/105] remove language translation --- messages/de-DE.json | 22 ++++++++++++++++++++++ messages/en-US.json | 5 ----- src/components/LocaleSwitcher.tsx | 9 ++++----- 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 messages/de-DE.json diff --git a/messages/de-DE.json b/messages/de-DE.json new file mode 100644 index 00000000..6074e8e6 --- /dev/null +++ b/messages/de-DE.json @@ -0,0 +1,22 @@ +{ + "locales": { + "label": "Sprache", + "en-US": "Englisch", + "de-DE": "Deutsch" + }, + "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", + "setupNewOrg": "Neue Organisation", + "setupCreateOrg": "Organisation erstellen", + "setupCreateSite": "Seite erstellen", + "setupCreateResources": "Ressource erstellen", + "setupOrgName": "Organisation's Name", + "setupDisplayName": "Dies ist der Anzeigename für Ihre Organisation.", + "setupOrgId": "Organisations-ID", + "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", + "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", + "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", + "welcome": "Willkommen zu Pangolin", + "componentsCreateOrg": "Erstelle eine Organisation", + "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}." +} \ No newline at end of file diff --git a/messages/en-US.json b/messages/en-US.json index 3eb137e9..c392a417 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1,9 +1,4 @@ { - "locales": { - "label": "Language", - "en-US": "English", - "de-DE": "German" - }, "setupCreate": "Create your organization, site, and resources", "setupNewOrg": "New Organization", "setupCreateOrg": "Create Organization", diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index f03f3ace..d7869326 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -1,8 +1,7 @@ -import { useLocale, useTranslations } from 'next-intl'; +import { useLocale } from 'next-intl'; import LocaleSwitcherSelect from './LocaleSwitcherSelect'; export default function LocaleSwitcher() { - const t = useTranslations('locales'); const locale = useLocale(); return ( @@ -11,14 +10,14 @@ export default function LocaleSwitcher() { items={[ { value: 'en-US', - label: t('en-US') + label: 'Englisch' }, { value: 'de-DE', - label: t('de-DE') + label: 'German' } ]} - label={t('label')} + label='Language' /> ); } From 7f4135e0cfa6f095ee12ce1b9134975f98e958a6 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 18:58:14 +0200 Subject: [PATCH 005/105] New translations en-us.json (German) --- messages/de-DE.json | 182 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 7 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 6074e8e6..6bd81caa 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1,16 +1,11 @@ { - "locales": { - "label": "Sprache", - "en-US": "Englisch", - "de-DE": "Deutsch" - }, "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", "setupNewOrg": "Neue Organisation", "setupCreateOrg": "Organisation erstellen", "setupCreateSite": "Seite erstellen", "setupCreateResources": "Ressource erstellen", "setupOrgName": "Organisation's Name", - "setupDisplayName": "Dies ist der Anzeigename für Ihre Organisation.", + "orgDisplayName": "This is the display name of your organization.", "setupOrgId": "Organisations-ID", "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", @@ -18,5 +13,178 @@ "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", "welcome": "Willkommen zu Pangolin", "componentsCreateOrg": "Erstelle eine Organisation", - "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}." + "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", + "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", + "dismiss": "Dismiss", + "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", + "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", + "inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.", + "inviteErrorUser": "We're sorry, but it looks like the invite you're trying to access is not for this user.", + "inviteLoginUser": "Please make sure you're logged in as the correct user.", + "inviteErrorNoUser": "We're sorry, but it looks like the invite you're trying to access is not for a user that exists.", + "inviteCreateUser": "Please create an account first.", + "goHome": "Go Home", + "inviteLogInOtherUser": "Log In as a Different User", + "createAnAccount": "Create an Account", + "inviteNotAccepted": "Invite Not Accepted", + "authCreateAccount": "Create an account to get started", + "email": "Email", + "password": "Password", + "confirmPassword": "Confirm Password", + "createAccount": "Create Account", + "viewSettings": "View settings", + "delete": "Delete", + "name": "Name", + "online": "Online", + "offline": "Offline", + "site": "Site", + "dataIn": "Data In", + "dataOut": "Data Out", + "connectionType": "Connection Type", + "local": "Local", + "edit": "Edit", + "siteConfirmDelete": "Confirm Delete Site", + "siteDelete": "Delete Site", + "siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.", + "siteMessageConfirm": "To confirm, please type the name of the site below.", + "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", + "siteManageSites": "Manage Sites", + "siteDescription": "Allow connectivity to your network through secure tunnels", + "siteCreate": "Create Site", + "siteCreateDescription": "Create a new site to start connecting your resources", + "close": "Close", + "siteNameMin": "Name must be at least 2 characters.", + "siteNameMax": "Name must not be longer than 30 characters.", + "siteErrorCreate": "Error creating site", + "siteErrorCreateKeyPair": "Key pair or site defaults not found", + "siteErrorCreateDefaults": "Site defaults not found", + "siteNameDescription": "This is the display name for the site.", + "method": "Method", + "siteMethodDescription": "This is how you will expose connections.", + "siteLearnNewt": "Learn how to install Newt on your system", + "siteSeeConfigOnce": "You will only be able to see the configuration once.", + "siteLoadWGConfig": "Loading WireGuard configuration...", + "siteDocker": "Expand for Docker Deployment Details", + "toggle": "Toggle", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "Local sites do not tunnel, learn more", + "siteConfirmCopy": "I have copied the config", + "searchSites": "Search sites...", + "siteAdd": "Add Site", + "recommended": "Recommended", + "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", + "siteRunsInDocker": "Runs in Docker", + "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", + "siteErrorDelete": "Error deleting site", + "shareTitle": "Manage Share Links", + "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", + "shareSearch": "Search share links...", + "shareCreate": "Create Share Link", + "shareErrorDelete": "Failed to delete link", + "shareErrorDeleteMessage": "An error occurred deleting link", + "shareDeleted": "Link deleted", + "shareDeletedDesciption": "The link has been deleted", + "openMenu": "Open menu", + "resource": "Resource", + "title": "Title", + "created": "Created", + "expires": "Expires", + "never": "Never", + "shareErrorSelectResource": "Please select a resource", + "resourceTitle": "Manage Resources", + "resourceDescription": "Create secure proxies to your private applications", + "resourceSearch": "Search resources...", + "resourceAdd": "Add Resource", + "resourceErrorDelte": "Error deleting resource", + "authentication": "Authentication", + "protected": "Protected", + "notProtected": "Not Protected", + "resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.", + "resourceMessageConfirm": "To confirm, please type the name of the resource below.", + "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", + "resourceHTTP": "HTTPS Resource", + "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", + "resourceRaw": "Raw TCP/UDP Resource", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", + "resourceCreate": "Create Resource", + "resourceCreateDescription": "Follow the steps below to create a new resource", + "resourceSeeAll": "See All Resources", + "resourceInfo": "Resource Information", + "resourceNameDescription": "This is the display name for the resource.", + "siteSelect": "Select site", + "siteSearch": "Search site", + "siteNotFound": "No site found.", + "siteSelectionDescription": "This site will provide connectivity to the resource.", + "resourceType": "Resource Type", + "resourceTypeDescription": "Determine how you want to access your resource", + "resourceHTTPSSettings": "HTTPS Settings", + "resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS", + "domainType": "Domain Type", + "subdomain": "Subdomain", + "baseDomain": "Base Domain", + "subdomnainDescription": "The subdomain where your resource will be accessible.", + "resourceRawSettings": "TCP/UDP Settings", + "resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Port Number", + "resourcePortNumberDescription": "The external port number to proxy requests.", + "cancle": "Cancle", + "resourceConfig": "Configuration Snippets", + "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", + "resourceAddEntrypoints": "Traefik: Add Entrypoints", + "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", + "resourceLearnRaw": "Learn how to configure TCP/UDP resources", + "resourceBack": "Back to Resources", + "resourceGoTo": "Go to Resource", + "visibility": "Visibility", + "enabled": "Enabled", + "disabled": "Disabled", + "general": "General", + "proxy": "Proxy", + "rules": "Rules", + "resourceSettingDescription": "Configure the settings on your resource", + "resourceSetting": "{resourceName} Settings", + "alwaysAllow": "Always Allow", + "alwaysDeny": "Always Deny", + "orgSettingsDescription": "Configure your organization's general settings", + "orgGeneralSettings": "Organization Settings", + "orgGeneralSettingsDescription": "Manage your organization details and configuration", + "orgGeneralSave": "Save General Settings", + "orgDangerZone": "Danger Zone", + "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", + "orgDelete": "Delete Organization", + "orgDeleteConfirm": "Confirm Delete Organization", + "orgMessageRemove": "This action is irreversible and will delete all associated data.", + "orgMessageConfirm": "To confirm, please type the name of the organization below.", + "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", + "orgUpdated": "Organization updated", + "orgUpdatedDescription": "The organization has been updated.", + "orgErrorUpdate": "Failed to update organization", + "orgErrorUpdateMessage": "An error occurred while updating the organization.", + "orgErrorFetch": "Failed to fetch organizations", + "orgErrorFetchMessage": "An error occurred while listing your organizations", + "orgErrorDelete": "Failed to delete organization", + "orgErrorDeleteMessage": "An error occurred while deleting the organization.", + "orgDeleted": "Organization deleted", + "orgDeletedMessage": "The organization and its data has been deleted.", + "accessUsersManage": "Manage Users", + "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", + "accessUsersSearch": "Search users...", + "accessUserCreate": "Create User", + "accessUserRemove": "Remove User", + "username": "Username", + "identityProvider": "Identity Provider", + "role": "Role", + "accessRoleNameRequired": "Name is required", + "accessRolesManage": "Manage Roles", + "accessRolesDescription": "Configure roles to manage access to your organization", + "accessRolesSearch": "Search roles...", + "accessRolesAdd": "Add Role", + "accessRoleDelete": "Delete Role", + "description": "Description", + "inviteTitle": "Open Invitations", + "inviteDescription": "Manage your invitations to other users", + "inviteSearch": "Search invitations..." } \ No newline at end of file From 9e572685bada223bd46eed4204b431e72922a565 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 17:00:29 +0000 Subject: [PATCH 006/105] deleted: messages/de-DE.json --- messages/de-DE.json | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 messages/de-DE.json diff --git a/messages/de-DE.json b/messages/de-DE.json deleted file mode 100644 index 6074e8e6..00000000 --- a/messages/de-DE.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "locales": { - "label": "Sprache", - "en-US": "Englisch", - "de-DE": "Deutsch" - }, - "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", - "setupNewOrg": "Neue Organisation", - "setupCreateOrg": "Organisation erstellen", - "setupCreateSite": "Seite erstellen", - "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Organisation's Name", - "setupDisplayName": "Dies ist der Anzeigename für Ihre Organisation.", - "setupOrgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", - "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", - "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", - "welcome": "Willkommen zu Pangolin", - "componentsCreateOrg": "Erstelle eine Organisation", - "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}." -} \ No newline at end of file From 230c08e5412e630edf676ce36aa61fd53db68bf3 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:29 +0200 Subject: [PATCH 007/105] New translations en-us.json (German) --- messages/de-DE.json | 330 ++++++++++++++++++++++---------------------- 1 file changed, 165 insertions(+), 165 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 6bd81caa..04469f16 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -5,7 +5,7 @@ "setupCreateSite": "Seite erstellen", "setupCreateResources": "Ressource erstellen", "setupOrgName": "Organisation's Name", - "orgDisplayName": "This is the display name of your organization.", + "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", "setupOrgId": "Organisations-ID", "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", @@ -14,177 +14,177 @@ "welcome": "Willkommen zu Pangolin", "componentsCreateOrg": "Erstelle eine Organisation", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", - "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", - "dismiss": "Dismiss", - "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", - "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", - "inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.", - "inviteErrorUser": "We're sorry, but it looks like the invite you're trying to access is not for this user.", - "inviteLoginUser": "Please make sure you're logged in as the correct user.", - "inviteErrorNoUser": "We're sorry, but it looks like the invite you're trying to access is not for a user that exists.", - "inviteCreateUser": "Please create an account first.", - "goHome": "Go Home", - "inviteLogInOtherUser": "Log In as a Different User", - "createAnAccount": "Create an Account", - "inviteNotAccepted": "Invite Not Accepted", - "authCreateAccount": "Create an account to get started", - "email": "Email", - "password": "Password", - "confirmPassword": "Confirm Password", - "createAccount": "Create Account", - "viewSettings": "View settings", - "delete": "Delete", + "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "dismiss": "Verwerfen", + "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", + "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", + "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", + "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", + "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", + "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", + "goHome": "Nach Hause", + "inviteLogInOtherUser": "Als anderer Benutzer anmelden", + "createAnAccount": "Konto erstellen", + "inviteNotAccepted": "Einladung nicht angenommen", + "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", + "email": "E-Mail", + "password": "Passwort", + "confirmPassword": "Passwort bestätigen", + "createAccount": "Konto erstellen", + "viewSettings": "Einstellungen anzeigen", + "delete": "Löschen", "name": "Name", "online": "Online", "offline": "Offline", "site": "Site", - "dataIn": "Data In", - "dataOut": "Data Out", - "connectionType": "Connection Type", - "local": "Local", - "edit": "Edit", - "siteConfirmDelete": "Confirm Delete Site", - "siteDelete": "Delete Site", - "siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.", - "siteMessageConfirm": "To confirm, please type the name of the site below.", - "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", - "siteManageSites": "Manage Sites", - "siteDescription": "Allow connectivity to your network through secure tunnels", - "siteCreate": "Create Site", - "siteCreateDescription": "Create a new site to start connecting your resources", - "close": "Close", - "siteNameMin": "Name must be at least 2 characters.", - "siteNameMax": "Name must not be longer than 30 characters.", - "siteErrorCreate": "Error creating site", - "siteErrorCreateKeyPair": "Key pair or site defaults not found", - "siteErrorCreateDefaults": "Site defaults not found", - "siteNameDescription": "This is the display name for the site.", - "method": "Method", - "siteMethodDescription": "This is how you will expose connections.", - "siteLearnNewt": "Learn how to install Newt on your system", - "siteSeeConfigOnce": "You will only be able to see the configuration once.", - "siteLoadWGConfig": "Loading WireGuard configuration...", - "siteDocker": "Expand for Docker Deployment Details", - "toggle": "Toggle", - "dockerCompose": "Docker Compose", + "dataIn": "Daten in", + "dataOut": "Daten raus", + "connectionType": "Verbindungstyp", + "local": "Lokal", + "edit": "Bearbeiten", + "siteConfirmDelete": "Site löschen bestätigen", + "siteDelete": "Site löschen", + "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", + "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", + "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", + "siteManageSites": "Sites verwalten", + "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", + "siteCreate": "Site erstellen", + "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", + "close": "Schließen", + "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", + "siteNameMax": "Name darf nicht länger als 30 Zeichen sein.", + "siteErrorCreate": "Fehler beim Erstellen der Seite", + "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", + "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", + "siteNameDescription": "Dies ist der Anzeigename für die Website.", + "method": "Methode", + "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", + "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", + "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", + "siteLoadWGConfig": "Lade WireGuard Konfiguration...", + "siteDocker": "Erweitern für Docker-Details", + "toggle": "Umschalten", + "dockerCompose": "Docker komponieren", "dockerRun": "Docker Run", - "siteLearnLocal": "Local sites do not tunnel, learn more", - "siteConfirmCopy": "I have copied the config", - "searchSites": "Search sites...", - "siteAdd": "Add Site", - "recommended": "Recommended", - "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", - "siteRunsInDocker": "Runs in Docker", - "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", - "siteErrorDelete": "Error deleting site", - "shareTitle": "Manage Share Links", - "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", - "shareSearch": "Search share links...", - "shareCreate": "Create Share Link", - "shareErrorDelete": "Failed to delete link", - "shareErrorDeleteMessage": "An error occurred deleting link", - "shareDeleted": "Link deleted", - "shareDeletedDesciption": "The link has been deleted", - "openMenu": "Open menu", - "resource": "Resource", - "title": "Title", - "created": "Created", - "expires": "Expires", - "never": "Never", - "shareErrorSelectResource": "Please select a resource", - "resourceTitle": "Manage Resources", - "resourceDescription": "Create secure proxies to your private applications", - "resourceSearch": "Search resources...", - "resourceAdd": "Add Resource", - "resourceErrorDelte": "Error deleting resource", - "authentication": "Authentication", - "protected": "Protected", - "notProtected": "Not Protected", - "resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.", - "resourceMessageConfirm": "To confirm, please type the name of the resource below.", - "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", - "resourceHTTP": "HTTPS Resource", - "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", - "resourceRaw": "Raw TCP/UDP Resource", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", - "resourceCreate": "Create Resource", - "resourceCreateDescription": "Follow the steps below to create a new resource", - "resourceSeeAll": "See All Resources", - "resourceInfo": "Resource Information", - "resourceNameDescription": "This is the display name for the resource.", - "siteSelect": "Select site", - "siteSearch": "Search site", - "siteNotFound": "No site found.", - "siteSelectionDescription": "This site will provide connectivity to the resource.", - "resourceType": "Resource Type", - "resourceTypeDescription": "Determine how you want to access your resource", - "resourceHTTPSSettings": "HTTPS Settings", - "resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS", - "domainType": "Domain Type", + "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", + "siteConfirmCopy": "Ich habe die Konfiguration kopiert", + "searchSites": "Seiten suchen...", + "siteAdd": "Site hinzufügen", + "recommended": "Empfohlen", + "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", + "siteRunsInDocker": "Läuft im Docker", + "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", + "siteErrorDelete": "Fehler beim Löschen der Seite", + "shareTitle": "Links zum Teilen verwalten", + "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", + "shareSearch": "Freigabe-Links suchen...", + "shareCreate": "Link erstellen", + "shareErrorDelete": "Link konnte nicht gelöscht werden", + "shareErrorDeleteMessage": "Fehler beim Löschen des Links", + "shareDeleted": "Link gelöscht", + "shareDeletedDesciption": "Der Link wurde gelöscht", + "openMenu": "Menü öffnen", + "resource": "Ressource", + "title": "Titel", + "created": "Erstellt", + "expires": "Gültig bis", + "never": "Nie", + "shareErrorSelectResource": "Bitte wählen Sie eine Ressource", + "resourceTitle": "Ressourcen verwalten", + "resourceDescription": "Erstellen Sie sichere Proxies für Ihre privaten Anwendungen", + "resourceSearch": "Suche Ressourcen...", + "resourceAdd": "Ressource hinzufügen", + "resourceErrorDelte": "Fehler beim Löschen der Ressource", + "authentication": "Authentifizierung", + "protected": "Geschützt", + "notProtected": "Nicht geschützt", + "resourceMessageRemove": "Einmal entfernt, wird die Ressource nicht mehr zugänglich sein. Alle mit der Ressource verbundenen Ziele werden ebenfalls entfernt.", + "resourceMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Ressource unten ein.", + "resourceQuestionRemove": "Sind Sie sicher, dass Sie die Ressource {selectedResource} aus der Organisation entfernen möchten?", + "resourceHTTP": "HTTPS-Ressource", + "resourceHTTPDescription": "Proxy-Anfragen an Ihre App über HTTPS unter Verwendung einer Subdomain oder einer Basis-Domain.", + "resourceRaw": "Rohe TCP/UDP Ressource", + "resourceRawDescription": "Proxy-Anfragen an Ihre App über TCP/UDP mit einer Portnummer.", + "resourceCreate": "Ressource erstellen", + "resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen", + "resourceSeeAll": "Alle Ressourcen anzeigen", + "resourceInfo": "Ressourcen-Informationen", + "resourceNameDescription": "Dies ist der Anzeigename für die Ressource.", + "siteSelect": "Site auswählen", + "siteSearch": "Website durchsuchen", + "siteNotFound": "Keine Site gefunden.", + "siteSelectionDescription": "Diese Seite wird die Verbindung zu der Ressource herstellen.", + "resourceType": "Ressourcentyp", + "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", + "resourceHTTPSSettings": "HTTPS-Einstellungen", + "resourceHTTPSSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über HTTPS", + "domainType": "Domänentyp", "subdomain": "Subdomain", - "baseDomain": "Base Domain", - "subdomnainDescription": "The subdomain where your resource will be accessible.", - "resourceRawSettings": "TCP/UDP Settings", - "resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP", + "baseDomain": "Basisdomäne", + "subdomnainDescription": "Die Subdomäne, auf die Ihre Ressource zugegriffen werden soll.", + "resourceRawSettings": "TCP/UDP Einstellungen", + "resourceRawSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über TCP/UDP", "protocol": "Protocol", "protocolSelect": "Select a protocol", - "resourcePortNumber": "Port Number", - "resourcePortNumberDescription": "The external port number to proxy requests.", - "cancle": "Cancle", - "resourceConfig": "Configuration Snippets", - "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", - "resourceAddEntrypoints": "Traefik: Add Entrypoints", - "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", - "resourceLearnRaw": "Learn how to configure TCP/UDP resources", - "resourceBack": "Back to Resources", - "resourceGoTo": "Go to Resource", - "visibility": "Visibility", - "enabled": "Enabled", - "disabled": "Disabled", - "general": "General", + "resourcePortNumber": "Portnummer", + "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", + "cancle": "Abbrechen", + "resourceConfig": "Konfiguration Snippets", + "resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um Ihre TCP/UDP Ressource einzurichten", + "resourceAddEntrypoints": "Traefik: Einstiegspunkte hinzufügen", + "resourceExposePorts": "Gerbil: Ports im Docker Compose ausblenden", + "resourceLearnRaw": "Lernen Sie, wie Sie TCP/UDP Ressourcen konfigurieren", + "resourceBack": "Zurück zu den Ressourcen", + "resourceGoTo": "Zu Ressource gehen", + "visibility": "Sichtbarkeit", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "general": "Allgemein", "proxy": "Proxy", - "rules": "Rules", - "resourceSettingDescription": "Configure the settings on your resource", - "resourceSetting": "{resourceName} Settings", - "alwaysAllow": "Always Allow", - "alwaysDeny": "Always Deny", - "orgSettingsDescription": "Configure your organization's general settings", - "orgGeneralSettings": "Organization Settings", - "orgGeneralSettingsDescription": "Manage your organization details and configuration", - "orgGeneralSave": "Save General Settings", - "orgDangerZone": "Danger Zone", - "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", - "orgDelete": "Delete Organization", - "orgDeleteConfirm": "Confirm Delete Organization", - "orgMessageRemove": "This action is irreversible and will delete all associated data.", - "orgMessageConfirm": "To confirm, please type the name of the organization below.", - "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", - "orgUpdated": "Organization updated", - "orgUpdatedDescription": "The organization has been updated.", - "orgErrorUpdate": "Failed to update organization", - "orgErrorUpdateMessage": "An error occurred while updating the organization.", - "orgErrorFetch": "Failed to fetch organizations", - "orgErrorFetchMessage": "An error occurred while listing your organizations", - "orgErrorDelete": "Failed to delete organization", - "orgErrorDeleteMessage": "An error occurred while deleting the organization.", - "orgDeleted": "Organization deleted", - "orgDeletedMessage": "The organization and its data has been deleted.", - "accessUsersManage": "Manage Users", - "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", - "accessUsersSearch": "Search users...", - "accessUserCreate": "Create User", - "accessUserRemove": "Remove User", - "username": "Username", - "identityProvider": "Identity Provider", - "role": "Role", - "accessRoleNameRequired": "Name is required", - "accessRolesManage": "Manage Roles", - "accessRolesDescription": "Configure roles to manage access to your organization", - "accessRolesSearch": "Search roles...", - "accessRolesAdd": "Add Role", - "accessRoleDelete": "Delete Role", - "description": "Description", - "inviteTitle": "Open Invitations", - "inviteDescription": "Manage your invitations to other users", - "inviteSearch": "Search invitations..." + "rules": "Regeln", + "resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource", + "resourceSetting": "{resourceName} Einstellungen", + "alwaysAllow": "Immer erlauben", + "alwaysDeny": "Immer ablehnen", + "orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation", + "orgGeneralSettings": "Organisations-Einstellungen", + "orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten", + "orgGeneralSave": "Allgemeine Einstellungen speichern", + "orgDangerZone": "Gefahrenzone", + "orgDangerZoneDescription": "Sobald Sie diesen Org löschen, gibt es kein Zurück mehr. Bitte seien Sie vorsichtig.", + "orgDelete": "Organisation löschen", + "orgDeleteConfirm": "Organisation löschen bestätigen", + "orgMessageRemove": "Diese Aktion ist unwiderruflich und löscht alle zugehörigen Daten.", + "orgMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Organisation unten ein.", + "orgQuestionRemove": "Sind Sie sicher, dass Sie die Organisation {selectedOrg} entfernen möchten?", + "orgUpdated": "Organisation aktualisiert", + "orgUpdatedDescription": "Die Organisation wurde aktualisiert.", + "orgErrorUpdate": "Fehler beim Aktualisieren der Organisation", + "orgErrorUpdateMessage": "Beim Aktualisieren der Organisation ist ein Fehler aufgetreten.", + "orgErrorFetch": "Fehler beim Abrufen von Organisationen", + "orgErrorFetchMessage": "Beim Auflisten Ihrer Organisationen ist ein Fehler aufgetreten", + "orgErrorDelete": "Organisation konnte nicht gelöscht werden", + "orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.", + "orgDeleted": "Organisation gelöscht", + "orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.", + "accessUsersManage": "Benutzer verwalten", + "accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten", + "accessUsersSearch": "Benutzer suchen...", + "accessUserCreate": "Benutzer erstellen", + "accessUserRemove": "Benutzer entfernen", + "username": "Benutzername", + "identityProvider": "Identitätsanbieter", + "role": "Rolle", + "accessRoleNameRequired": "Name ist erforderlich", + "accessRolesManage": "Rollen verwalten", + "accessRolesDescription": "Konfigurieren Sie Rollen, um den Zugriff auf Ihre Organisation zu verwalten", + "accessRolesSearch": "Rollen suchen...", + "accessRolesAdd": "Rolle hinzufügen", + "accessRoleDelete": "Rolle löschen", + "description": "Beschreibung", + "inviteTitle": "Einladungen öffnen", + "inviteDescription": "Ihre Einladungen an andere Benutzer verwalten", + "inviteSearch": "Einladungen suchen..." } \ No newline at end of file From 576fda235767ec66890813b496c655e726766c6b Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:30 +0200 Subject: [PATCH 008/105] New translations en-us.json (French) --- messages/fr-FR.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/fr-FR.json diff --git a/messages/fr-FR.json b/messages/fr-FR.json new file mode 100644 index 00000000..71810b2a --- /dev/null +++ b/messages/fr-FR.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Créez votre organisation, votre site et vos ressources", + "setupNewOrg": "Nouvelle organisation", + "setupCreateOrg": "Créer une organisation", + "setupCreateSite": "Créer un site", + "setupCreateResources": "Créer des ressources", + "setupOrgName": "Nom de l'organisation", + "orgDisplayName": "Ceci est le nom d'affichage de votre organisation.", + "setupOrgId": "ID de l'organisation", + "setupIdentifierMessage": "Ceci est l'identifiant unique pour votre organisation. Il est séparé du nom affiché.", + "setupErrorIdentifier": "L'ID de l'organisation est déjà pris. Veuillez en choisir un autre.", + "componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.", + "componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.", + "welcome": "Bienvenue à Pangolin", + "componentsCreateOrg": "Créer une organisation", + "componentsMember": "Vous êtes membre de {count, plural, =0 {aucune organisation} =1 {Une organisation} other {# organisations}}.", + "componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.", + "dismiss": "Refuser", + "componentsLicenseViolation": "Violation de licence : Ce serveur utilise des sites {usedSites} qui dépassent la limite autorisée des sites {maxSites} . Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.", + "componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!", + "inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder n'ait pas été acceptée ou n'est plus valide.", + "inviteErrorUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour cet utilisateur.", + "inviteLoginUser": "Assurez-vous que vous êtes bien connecté en tant qu'utilisateur correct.", + "inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour un utilisateur qui existe.", + "inviteCreateUser": "Veuillez d'abord créer un compte.", + "goHome": "Retour à la maison", + "inviteLogInOtherUser": "Se connecter en tant qu'utilisateur différent", + "createAnAccount": "Créer un compte", + "inviteNotAccepted": "Invitation non acceptée", + "authCreateAccount": "Créez un compte pour commencer", + "email": "Courriel", + "password": "Mot de passe", + "confirmPassword": "Confirmer le mot de passe", + "createAccount": "Créer un compte", + "viewSettings": "Afficher les paramètres", + "delete": "Supprimez", + "name": "Nom", + "online": "En ligne", + "offline": "Hors ligne", + "site": "Site", + "dataIn": "Données dans", + "dataOut": "Données épuisées", + "connectionType": "Type de connexion", + "local": "Locale", + "edit": "Editer", + "siteConfirmDelete": "Confirmer la suppression du site", + "siteDelete": "Supprimer le site", + "siteMessageRemove": "Une fois supprimé, le site ne sera plus accessible. Toutes les ressources et cibles associées au site seront également supprimées.", + "siteMessageConfirm": "Pour confirmer, veuillez saisir le nom du site ci-dessous.", + "siteQuestionRemove": "Êtes-vous sûr de vouloir supprimer le site {selectedSite} de l'organisation ?", + "siteManageSites": "Gérer les sites", + "siteDescription": "Autoriser la connectivité à votre réseau via des tunnels sécurisés", + "siteCreate": "Créer un site", + "siteCreateDescription": "Créez un nouveau site pour commencer à connecter vos ressources", + "close": "Fermer", + "siteNameMin": "Le nom doit comporter au moins 2 caractères.", + "siteNameMax": "Le nom ne doit pas contenir plus de 30 caractères.", + "siteErrorCreate": "Erreur lors de la création du site", + "siteErrorCreateKeyPair": "Paire de clés ou site par défaut introuvable", + "siteErrorCreateDefaults": "Les valeurs par défaut du site sont introuvables", + "siteNameDescription": "Ceci est le nom d'affichage du site.", + "method": "Méthode", + "siteMethodDescription": "C'est ainsi que vous exposerez les connexions.", + "siteLearnNewt": "Apprenez à installer Newt sur votre système", + "siteSeeConfigOnce": "Vous ne pourrez voir la configuration qu'une seule fois.", + "siteLoadWGConfig": "Chargement de la configuration WireGuard...", + "siteDocker": "Développer les détails du déploiement Docker", + "toggle": "Activer/désactiver", + "dockerCompose": "Composition Docker", + "dockerRun": "Exécution Docker", + "siteLearnLocal": "Les sites locaux ne tunnel, en savoir plus", + "siteConfirmCopy": "J'ai copié la configuration", + "searchSites": "Rechercher des sites...", + "siteAdd": "Ajouter un site", + "recommended": "Recommandé", + "siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.", + "siteRunsInDocker": "Exécute dans Docker", + "siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows", + "siteErrorDelete": "Erreur lors de la suppression du site", + "shareTitle": "Gérer les liens de partage", + "shareDescription": "Créez des liens partageables pour accorder un accès temporaire ou permanent à vos ressources", + "shareSearch": "Rechercher des liens de partage...", + "shareCreate": "Créer un lien de partage", + "shareErrorDelete": "Impossible de supprimer le lien", + "shareErrorDeleteMessage": "Une erreur s'est produite lors de la suppression du lien", + "shareDeleted": "Lien supprimé", + "shareDeletedDesciption": "Le lien a été supprimé", + "openMenu": "Ouvrir le menu", + "resource": "Ressource", + "title": "Titre de la page", + "created": "Créé", + "expires": "Expire", + "never": "Jamais", + "shareErrorSelectResource": "Veuillez sélectionner une ressource", + "resourceTitle": "Gérer les ressources", + "resourceDescription": "Créez des proxy sécurisés pour vos applications privées", + "resourceSearch": "Rechercher des ressources...", + "resourceAdd": "Ajouter une ressource", + "resourceErrorDelte": "Erreur de suppression de la ressource", + "authentication": "Authentification", + "protected": "Protégé", + "notProtected": "Non Protégé", + "resourceMessageRemove": "Une fois supprimée, la ressource ne sera plus accessible. Toutes les cibles associées à la ressource seront également supprimées.", + "resourceMessageConfirm": "Pour confirmer, veuillez saisir le nom de la ressource ci-dessous.", + "resourceQuestionRemove": "Êtes-vous sûr de vouloir supprimer la ressource {selectedResource} de l'organisation ?", + "resourceHTTP": "Ressource HTTPS", + "resourceHTTPDescription": "Requêtes de proxy à votre application via HTTPS en utilisant un sous-domaine ou un domaine de base.", + "resourceRaw": "Ressource TCP/UDP brute", + "resourceRawDescription": "Demandes de proxy à votre application via TCP/UDP en utilisant un numéro de port.", + "resourceCreate": "Créer une ressource", + "resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource", + "resourceSeeAll": "Voir toutes les ressources", + "resourceInfo": "Informations sur la ressource", + "resourceNameDescription": "Ceci est le nom d'affichage de la ressource.", + "siteSelect": "Sélectionner un site", + "siteSearch": "Chercher un site", + "siteNotFound": "Aucun site trouvé.", + "siteSelectionDescription": "Ce site fournira la connectivité à la ressource.", + "resourceType": "Type de ressource", + "resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource", + "resourceHTTPSSettings": "Paramètres HTTPS", + "resourceHTTPSSettingsDescription": "Configurer comment votre ressource sera accédée via HTTPS", + "domainType": "Type de domaine", + "subdomain": "Sous-domaine", + "baseDomain": "Domaine de base", + "subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.", + "resourceRawSettings": "Paramètres TCP/UDP", + "resourceRawSettingsDescription": "Configurer comment votre ressource sera accédée via TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Numéro de port", + "resourcePortNumberDescription": "Le numéro de port externe pour les requêtes de proxy.", + "cancle": "Annuler", + "resourceConfig": "Snippets de configuration", + "resourceConfigDescription": "Copiez et collez ces modules de configuration pour configurer votre ressource TCP/UDP", + "resourceAddEntrypoints": "Traefik: Ajouter des points d’entrée", + "resourceExposePorts": "Gerbil: Exposer des ports dans Docker Compose", + "resourceLearnRaw": "Apprenez à configurer les ressources TCP/UDP", + "resourceBack": "Retour aux ressources", + "resourceGoTo": "Aller à la ressource", + "visibility": "Visibilité", + "enabled": "Activé", + "disabled": "Désactivé", + "general": "Généraux", + "proxy": "Proxy", + "rules": "Règles", + "resourceSettingDescription": "Configurer les paramètres de votre ressource", + "resourceSetting": "Réglages {resourceName}", + "alwaysAllow": "Toujours autoriser", + "alwaysDeny": "Toujours refuser", + "orgSettingsDescription": "Configurer les paramètres généraux de votre organisation", + "orgGeneralSettings": "Paramètres de l'organisation", + "orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation", + "orgGeneralSave": "Enregistrer les paramètres généraux", + "orgDangerZone": "Zone de danger", + "orgDangerZoneDescription": "Une fois que vous supprimez cette organisation, il n'y a pas de retour en arrière. Soyez certain.", + "orgDelete": "Supprimer l'organisation", + "orgDeleteConfirm": "Confirmer la suppression de l'organisation", + "orgMessageRemove": "Cette action est irréversible et supprimera toutes les données associées.", + "orgMessageConfirm": "Pour confirmer, veuillez saisir le nom de l'organisation ci-dessous.", + "orgQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'organisation {selectedOrg}?", + "orgUpdated": "Organisation mise à jour", + "orgUpdatedDescription": "L'organisation a été mise à jour.", + "orgErrorUpdate": "Échec de la mise à jour de l'organisation", + "orgErrorUpdateMessage": "Une erreur s'est produite lors de la mise à jour de l'organisation.", + "orgErrorFetch": "Impossible de récupérer les organisations", + "orgErrorFetchMessage": "Une erreur s'est produite lors de la liste de vos organisations", + "orgErrorDelete": "Échec de la suppression de l'organisation", + "orgErrorDeleteMessage": "Une erreur s'est produite lors de la suppression de l'organisation.", + "orgDeleted": "Organisation supprimée", + "orgDeletedMessage": "L'organisation et ses données ont été supprimées.", + "accessUsersManage": "Gérer les utilisateurs", + "accessUsersDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à votre organisation", + "accessUsersSearch": "Rechercher des utilisateurs...", + "accessUserCreate": "Créer un utilisateur", + "accessUserRemove": "Supprimer l'utilisateur", + "username": "Nom d'utilisateur", + "identityProvider": "Fournisseur d'identité", + "role": "Rôle", + "accessRoleNameRequired": "Le nom est requis", + "accessRolesManage": "Gérer les rôles", + "accessRolesDescription": "Configurer les rôles pour gérer l'accès à votre organisation", + "accessRolesSearch": "Rechercher des rôles...", + "accessRolesAdd": "Ajouter un rôle", + "accessRoleDelete": "Supprimer le rôle", + "description": "Libellé", + "inviteTitle": "Invitations ouvertes", + "inviteDescription": "Gérer vos invitations à d'autres utilisateurs", + "inviteSearch": "Rechercher des invitations..." +} \ No newline at end of file From 8a377d73fd325545ee2e1cbd5dfe39d53f385a93 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:31 +0200 Subject: [PATCH 009/105] New translations en-us.json (Italian) --- messages/it-IT.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/it-IT.json diff --git a/messages/it-IT.json b/messages/it-IT.json new file mode 100644 index 00000000..c4e8557c --- /dev/null +++ b/messages/it-IT.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Crea la tua organizzazione, sito e risorse", + "setupNewOrg": "Nuova Organizzazione", + "setupCreateOrg": "Crea Organizzazione", + "setupCreateSite": "Crea Sito", + "setupCreateResources": "Crea Risorse", + "setupOrgName": "Nome Dell'Organizzazione", + "orgDisplayName": "Questo è il nome visualizzato della tua organizzazione.", + "setupOrgId": "Id Organizzazione", + "setupIdentifierMessage": "Questo è l' identificatore univoco della tua organizzazione. Questo è separato dal nome del display.", + "setupErrorIdentifier": "L'ID dell'organizzazione è già utilizzato. Si prega di sceglierne uno diverso.", + "componentsErrorNoMemberCreate": "Al momento non sei un membro di nessuna organizzazione. Crea un'organizzazione per iniziare.", + "componentsErrorNoMember": "Attualmente non sei membro di nessuna organizzazione.", + "welcome": "Benvenuti a Pangolin", + "componentsCreateOrg": "Crea un'organizzazione", + "componentsMember": "Sei un membro di {count, plural, =0 {nessuna organizzazione} =1 {una organizzazione} other {# organizzazioni}}.", + "componentsInvalidKey": "Rilevata chiave di licenza non valida o scaduta. Segui i termini di licenza per continuare a utilizzare tutte le funzionalità.", + "dismiss": "Ignora", + "componentsLicenseViolation": "Violazione della licenza: Questo server sta usando i siti {usedSites} che superano il suo limite concesso in licenza per i siti {maxSites} . Segui i termini di licenza per continuare a usare tutte le funzionalità.", + "componentsSupporterMessage": "Grazie per aver supportato Pangolin come {tier}!", + "inviteErrorNotValid": "Siamo spiacenti, ma sembra che l'invito che stai cercando di accedere non sia stato accettato o non sia più valido.", + "inviteErrorUser": "Siamo spiacenti, ma sembra che l'invito che stai cercando di accedere non sia per questo utente.", + "inviteLoginUser": "Assicurati di aver effettuato l'accesso come utente corretto.", + "inviteErrorNoUser": "Siamo spiacenti, ma sembra che l'invito che stai cercando di accedere non sia per un utente che esiste.", + "inviteCreateUser": "Si prega di creare un account prima.", + "goHome": "Vai A Home", + "inviteLogInOtherUser": "Accedi come utente diverso", + "createAnAccount": "Crea un account", + "inviteNotAccepted": "Invito Non Accettato", + "authCreateAccount": "Crea un account per iniziare", + "email": "Email", + "password": "Password", + "confirmPassword": "Conferma Password", + "createAccount": "Crea Account", + "viewSettings": "Visualizza impostazioni", + "delete": "Elimina", + "name": "Nome", + "online": "Online", + "offline": "Offline", + "site": "Sito", + "dataIn": "Dati In", + "dataOut": "Dati Fuori", + "connectionType": "Tipo Di Connessione", + "local": "Locale", + "edit": "Modifica", + "siteConfirmDelete": "Conferma Eliminazione Sito", + "siteDelete": "Elimina Sito", + "siteMessageRemove": "Una volta rimosso, il sito non sarà più accessibile. Anche tutte le risorse e gli obiettivi associati al sito saranno rimossi.", + "siteMessageConfirm": "Per confermare, digita il nome del sito qui sotto.", + "siteQuestionRemove": "Sei sicuro di voler rimuovere il sito {selectedSite} dall'organizzazione?", + "siteManageSites": "Gestisci Siti", + "siteDescription": "Consenti la connettività alla rete attraverso tunnel sicuri", + "siteCreate": "Crea Sito", + "siteCreateDescription": "Crea un nuovo sito per iniziare a connettere le tue risorse", + "close": "Chiudi", + "siteNameMin": "Il nome deve contenere almeno 2 caratteri.", + "siteNameMax": "Il nome non deve essere più lungo di 30 caratteri.", + "siteErrorCreate": "Errore nella creazione del sito", + "siteErrorCreateKeyPair": "Coppia di chiavi o valori predefiniti del sito non trovati", + "siteErrorCreateDefaults": "Predefiniti del sito non trovati", + "siteNameDescription": "Questo è il nome visualizzato per il sito.", + "method": "Metodo", + "siteMethodDescription": "Questo è il modo in cui esporrete le connessioni.", + "siteLearnNewt": "Scopri come installare Newt sul tuo sistema", + "siteSeeConfigOnce": "Potrai vedere la configurazione solo una volta.", + "siteLoadWGConfig": "Caricamento configurazione WireGuard...", + "siteDocker": "Espandi per i dettagli di distribuzione Docker", + "toggle": "Attiva/disattiva", + "dockerCompose": "Composizione Docker", + "dockerRun": "Corsa Docker", + "siteLearnLocal": "I siti locali non tunnel, saperne di più", + "siteConfirmCopy": "Ho copiato la configurazione", + "searchSites": "Cerca siti...", + "siteAdd": "Aggiungi Sito", + "recommended": "Consigliato", + "siteNewtDescription": "Per la migliore esperienza utente, utilizzare Newt. Utilizza WireGuard sotto il cofano e ti permette di indirizzare le tue risorse private tramite il loro indirizzo LAN sulla tua rete privata dall'interno della dashboard Pangolin.", + "siteRunsInDocker": "Esegue nel Docker", + "siteRunsInShell": "Esegue in shell su macOS, Linux e Windows", + "siteErrorDelete": "Errore nell'eliminare il sito", + "shareTitle": "Gestisci Collegamenti Di Condivisione", + "shareDescription": "Crea link condivisibili per concedere un accesso temporaneo o permanente alle tue risorse", + "shareSearch": "Cerca link condivisi...", + "shareCreate": "Crea Link Di Condivisione", + "shareErrorDelete": "Impossibile eliminare il link", + "shareErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione del link", + "shareDeleted": "Link eliminato", + "shareDeletedDesciption": "Il link è stato eliminato", + "openMenu": "Apri menu", + "resource": "Risorsa", + "title": "Titolo", + "created": "Creato", + "expires": "Scade", + "never": "Mai", + "shareErrorSelectResource": "Seleziona una risorsa", + "resourceTitle": "Gestisci Risorse", + "resourceDescription": "Crea proxy sicuri per le tue applicazioni private", + "resourceSearch": "Cerca risorse...", + "resourceAdd": "Aggiungi Risorsa", + "resourceErrorDelte": "Errore nell'eliminare la risorsa", + "authentication": "Autenticazione", + "protected": "Protetto", + "notProtected": "Non Protetto", + "resourceMessageRemove": "Una volta rimossa, la risorsa non sarà più accessibile. Tutti gli obiettivi associati alla risorsa saranno rimossi.", + "resourceMessageConfirm": "Per confermare, digita il nome della risorsa qui sotto.", + "resourceQuestionRemove": "Sei sicuro di voler rimuovere la risorsa {selectedResource} dall'organizzazione?", + "resourceHTTP": "Risorsa HTTPS", + "resourceHTTPDescription": "Richieste proxy alla tua app tramite HTTPS utilizzando un sottodominio o un dominio di base.", + "resourceRaw": "Risorsa Raw TCP/UDP", + "resourceRawDescription": "Richieste proxy alla tua app tramite TCP/UDP utilizzando un numero di porta.", + "resourceCreate": "Crea Risorsa", + "resourceCreateDescription": "Segui i passaggi seguenti per creare una nuova risorsa", + "resourceSeeAll": "Vedi Tutte Le Risorse", + "resourceInfo": "Informazioni Risorsa", + "resourceNameDescription": "Questo è il nome visualizzato per la risorsa.", + "siteSelect": "Seleziona sito", + "siteSearch": "Cerca sito", + "siteNotFound": "Nessun sito trovato.", + "siteSelectionDescription": "Questo sito fornirà connettività alla risorsa.", + "resourceType": "Tipo Di Risorsa", + "resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa", + "resourceHTTPSSettings": "Impostazioni HTTPS", + "resourceHTTPSSettingsDescription": "Configura come sarà possibile accedere alla tua risorsa su HTTPS", + "domainType": "Tipo Di Dominio", + "subdomain": "Sottodominio", + "baseDomain": "Dominio Base", + "subdomnainDescription": "Il sottodominio in cui la tua risorsa sarà accessibile.", + "resourceRawSettings": "Impostazioni TCP/UDP", + "resourceRawSettingsDescription": "Configura come accedere alla tua risorsa tramite TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Numero Porta", + "resourcePortNumberDescription": "Il numero di porta esterna per le richieste di proxy.", + "cancle": "Annullare", + "resourceConfig": "Snippet Di Configurazione", + "resourceConfigDescription": "Copia e incolla questi snippet di configurazione per configurare la tua risorsa TCP/UDP", + "resourceAddEntrypoints": "Traefik: Aggiungi Ingresso", + "resourceExposePorts": "Gerbil: espone le porte in Docker componi", + "resourceLearnRaw": "Scopri come configurare le risorse TCP/UDP", + "resourceBack": "Torna alle risorse", + "resourceGoTo": "Vai alla Risorsa", + "visibility": "Visibilità", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "general": "Generale", + "proxy": "Proxy", + "rules": "Regole", + "resourceSettingDescription": "Configura le impostazioni sulla tua risorsa", + "resourceSetting": "Impostazioni {resourceName}", + "alwaysAllow": "Consenti Sempre", + "alwaysDeny": "Nega Sempre", + "orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione", + "orgGeneralSettings": "Impostazioni Organizzazione", + "orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione", + "orgGeneralSave": "Salva Impostazioni Generali", + "orgDangerZone": "Zona Pericolosa", + "orgDangerZoneDescription": "Una volta che si elimina questo org, non c'è ritorno. Si prega di essere certi.", + "orgDelete": "Elimina Organizzazione", + "orgDeleteConfirm": "Conferma Elimina Organizzazione", + "orgMessageRemove": "Questa azione è irreversibile e cancellerà tutti i dati associati.", + "orgMessageConfirm": "Per confermare, digita il nome dell'organizzazione qui sotto.", + "orgQuestionRemove": "Sei sicuro di voler rimuovere l'organizzazione {selectedOrg}?", + "orgUpdated": "Organizzazione aggiornata", + "orgUpdatedDescription": "L'organizzazione è stata aggiornata.", + "orgErrorUpdate": "Impossibile aggiornare l'organizzazione", + "orgErrorUpdateMessage": "Si è verificato un errore nell'aggiornamento dell'organizzazione.", + "orgErrorFetch": "Recupero delle organizzazioni non riuscito", + "orgErrorFetchMessage": "Si è verificato un errore durante l'elenco delle organizzazioni", + "orgErrorDelete": "Impossibile eliminare l'organizzazione", + "orgErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione dell'organizzazione.", + "orgDeleted": "Organizzazione eliminata", + "orgDeletedMessage": "L'organizzazione e i suoi dati sono stati eliminati.", + "accessUsersManage": "Gestisci Utenti", + "accessUsersDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso alla tua organizzazione", + "accessUsersSearch": "Cerca utenti...", + "accessUserCreate": "Crea Utente", + "accessUserRemove": "Rimuovi Utente", + "username": "Username", + "identityProvider": "Provider Di Identità", + "role": "Ruolo", + "accessRoleNameRequired": "Il nome è obbligatorio", + "accessRolesManage": "Gestisci Ruoli", + "accessRolesDescription": "Configura i ruoli per gestire l'accesso alla tua organizzazione", + "accessRolesSearch": "Ricerca ruoli...", + "accessRolesAdd": "Aggiungi Ruolo", + "accessRoleDelete": "Elimina Ruolo", + "description": "Descrizione", + "inviteTitle": "Inviti Aperti", + "inviteDescription": "Gestisci i tuoi inviti ad altri utenti", + "inviteSearch": "Cerca inviti..." +} \ No newline at end of file From 95fed840d41b77d811ac364fb534760df9f603f2 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:32 +0200 Subject: [PATCH 010/105] New translations en-us.json (Polish) --- messages/pl-PL.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/pl-PL.json diff --git a/messages/pl-PL.json b/messages/pl-PL.json new file mode 100644 index 00000000..9da108c2 --- /dev/null +++ b/messages/pl-PL.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Utwórz swoją organizację, witrynę i zasoby", + "setupNewOrg": "Nowa organizacja", + "setupCreateOrg": "Utwórz organizację", + "setupCreateSite": "Utwórz witrynę", + "setupCreateResources": "Utwórz Zasoby", + "setupOrgName": "Nazwa organizacji", + "orgDisplayName": "To jest wyświetlana nazwa Twojej organizacji.", + "setupOrgId": "Identyfikator organizacji", + "setupIdentifierMessage": "To jest unikalny identyfikator Twojej organizacji. Jest to oddzielone od nazwy wyświetlanej.", + "setupErrorIdentifier": "Identyfikator organizacji jest już zajęty. Wybierz inny.", + "componentsErrorNoMemberCreate": "Nie jesteś obecnie członkiem żadnej organizacji. Aby rozpocząć, utwórz organizację.", + "componentsErrorNoMember": "Nie jesteś obecnie członkiem żadnej organizacji.", + "welcome": "Witaj w Pangolinie", + "componentsCreateOrg": "Utwórz organizację", + "componentsMember": "Jesteś członkiem {count, plural, =0 {Żadna organizacja} =1 {Jedna organizacja} other {# organizacji}}.", + "componentsInvalidKey": "Wykryto nieprawidłowe lub wygasłe klucze licencyjne. Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.", + "dismiss": "Odrzuć", + "componentsLicenseViolation": "Naruszenie licencji: Ten serwer używa stron {usedSites} , które przekraczają limit licencyjny stron {maxSites} . Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.", + "componentsSupporterMessage": "Dziękujemy za wsparcie Pangolina jako {tier}!", + "inviteErrorNotValid": "Przykro nam, ale wygląda na to, że zaproszenie, do którego próbujesz uzyskać dostęp, nie zostało zaakceptowane lub jest już nieważne.", + "inviteErrorUser": "Przykro nam, ale wygląda na to, że zaproszenie, do którego próbujesz uzyskać dostęp, nie jest dla tego użytkownika.", + "inviteLoginUser": "Upewnij się, że jesteś zalogowany jako właściwy użytkownik.", + "inviteErrorNoUser": "Przykro nam, ale wygląda na to, że zaproszenie, do którego próbujesz uzyskać dostęp, nie jest dla użytkownika, który istnieje.", + "inviteCreateUser": "Proszę najpierw utworzyć konto.", + "goHome": "Przejdź do strony głównej", + "inviteLogInOtherUser": "Zaloguj się jako inny użytkownik", + "createAnAccount": "Utwórz konto", + "inviteNotAccepted": "Zaproszenie nie zaakceptowane", + "authCreateAccount": "Utwórz konto, aby rozpocząć", + "email": "E-mail", + "password": "Hasło", + "confirmPassword": "Potwierdź hasło", + "createAccount": "Utwórz konto", + "viewSettings": "Pokaż ustawienia", + "delete": "Usuń", + "name": "Nazwisko", + "online": "Dostępny", + "offline": "Offline", + "site": "Witryna", + "dataIn": "Dane w", + "dataOut": "Dane niedostępne", + "connectionType": "Typ połączenia", + "local": "Lokalny", + "edit": "Edytuj", + "siteConfirmDelete": "Potwierdź usunięcie witryny", + "siteDelete": "Usuń witrynę", + "siteMessageRemove": "Po usunięciu, witryna nie będzie już dostępna. Wszystkie zasoby i cele związane z witryną zostaną również usunięte.", + "siteMessageConfirm": "Aby potwierdzić, wpisz nazwę witryny poniżej.", + "siteQuestionRemove": "Czy na pewno chcesz usunąć stronę {selectedSite} z organizacji?", + "siteManageSites": "Zarządzaj stronami", + "siteDescription": "Zezwalaj na połączenie z siecią przez bezpieczne tunele", + "siteCreate": "Utwórz witrynę", + "siteCreateDescription": "Utwórz nową witrynę, aby rozpocząć łączenie zasobów", + "close": "Zamknij", + "siteNameMin": "Nazwa musi mieć co najmniej 2 znaki.", + "siteNameMax": "Nazwa nie może przekraczać 30 znaków.", + "siteErrorCreate": "Błąd podczas tworzenia witryny", + "siteErrorCreateKeyPair": "Nie znaleziono pary kluczy lub domyślnych ustawień witryny", + "siteErrorCreateDefaults": "Nie znaleziono domyślnych ustawień witryny", + "siteNameDescription": "To jest wyświetlana nazwa witryny.", + "method": "Metoda", + "siteMethodDescription": "W ten sposób ujawnisz połączenia.", + "siteLearnNewt": "Dowiedz się, jak zainstalować Newt w systemie", + "siteSeeConfigOnce": "Możesz zobaczyć konfigurację tylko raz.", + "siteLoadWGConfig": "Ładowanie konfiguracji WireGuard...", + "siteDocker": "Rozwiń o szczegóły wdrożenia dokera", + "toggle": "Przełącz", + "dockerCompose": "Kompozytor dokujący", + "dockerRun": "Uruchom Docker", + "siteLearnLocal": "Lokalne witryny nie tunelowają, dowiedz się więcej", + "siteConfirmCopy": "Skopiowałem konfigurację", + "searchSites": "Szukaj witryn...", + "siteAdd": "Dodaj witrynę", + "recommended": "Rekomendowane", + "siteNewtDescription": "Aby uzyskać najlepsze doświadczenia użytkownika, użyj Newt. Używa WireGuard pod zapleczem i pozwala na przekierowanie twoich prywatnych zasobów przez ich adres LAN w sieci prywatnej z panelu Pangolin.", + "siteRunsInDocker": "Uruchamia w Docke'u", + "siteRunsInShell": "Uruchamia w skorupce na macOS, Linux i Windows", + "siteErrorDelete": "Błąd podczas usuwania witryny", + "shareTitle": "Zarządzaj linkami udostępniania", + "shareDescription": "Utwórz linki, które można udostępnić, aby przyznać tymczasowy lub stały dostęp do Twoich zasobów", + "shareSearch": "Szukaj linków udostępnienia...", + "shareCreate": "Utwórz link udostępniania", + "shareErrorDelete": "Nie udało się usunąć linku", + "shareErrorDeleteMessage": "Wystąpił błąd podczas usuwania linku", + "shareDeleted": "Link usunięty", + "shareDeletedDesciption": "Link został usunięty", + "openMenu": "Otwórz menu", + "resource": "Zasoby", + "title": "Rozporządzenie Rady (EWG) nr 2658/87 z dnia 23 lipca 1987 r. w sprawie nomenklatury taryfowej i statystycznej oraz w sprawie Wspólnej Taryfy Celnej (Dz.U. L 256 z 7.9.1987, s. 1).", + "created": "Utworzono", + "expires": "Wygasa", + "never": "Nigdy", + "shareErrorSelectResource": "Wybierz zasób", + "resourceTitle": "Zarządzaj zasobami", + "resourceDescription": "Utwórz bezpieczne proxy do prywatnych aplikacji", + "resourceSearch": "Szukaj zasobów...", + "resourceAdd": "Dodaj zasób", + "resourceErrorDelte": "Błąd podczas usuwania zasobu", + "authentication": "Uwierzytelnianie", + "protected": "Chronione", + "notProtected": "Niechronione", + "resourceMessageRemove": "Po usunięciu, zasób nie będzie już dostępny. Wszystkie cele związane z zasobem zostaną również usunięte.", + "resourceMessageConfirm": "Aby potwierdzić, wpisz nazwę zasobu poniżej.", + "resourceQuestionRemove": "Czy na pewno chcesz usunąć zasób {selectedResource} z organizacji?", + "resourceHTTP": "Zasób HTTPS", + "resourceHTTPDescription": "Proxy do Twojej aplikacji przez HTTPS, przy użyciu poddomeny lub domeny bazowej.", + "resourceRaw": "Surowy zasób TCP/UDP", + "resourceRawDescription": "Proxy do aplikacji przez TCP/UDP przy użyciu numeru portu.", + "resourceCreate": "Utwórz zasób", + "resourceCreateDescription": "Wykonaj poniższe kroki, aby utworzyć nowy zasób", + "resourceSeeAll": "Zobacz wszystkie zasoby", + "resourceInfo": "Informacje o zasobach", + "resourceNameDescription": "To jest wyświetlana nazwa zasobu.", + "siteSelect": "Wybierz witrynę", + "siteSearch": "Szukaj witryny", + "siteNotFound": "Nie znaleziono witryny.", + "siteSelectionDescription": "Ta strona zapewni połączenie z zasobem.", + "resourceType": "Typ zasobu", + "resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu", + "resourceHTTPSSettings": "Ustawienia HTTPS", + "resourceHTTPSSettingsDescription": "Skonfiguruj jak twój zasób będzie dostępny przez HTTPS", + "domainType": "Typ domeny", + "subdomain": "Poddomena", + "baseDomain": "Bazowa domena", + "subdomnainDescription": "Poddomena, w której twój zasób będzie dostępny.", + "resourceRawSettings": "Ustawienia TCP/UDP", + "resourceRawSettingsDescription": "Skonfiguruj jak twój zasób będzie dostępny przez TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Numer portu", + "resourcePortNumberDescription": "Numer portu zewnętrznego do żądań proxy.", + "cancle": "Anuluj", + "resourceConfig": "Snippety konfiguracji", + "resourceConfigDescription": "Skopiuj i wklej te fragmenty konfiguracji, aby skonfigurować swój zasób TCP/UDP", + "resourceAddEntrypoints": "Traefik: Dodaj punkty wejścia", + "resourceExposePorts": "Gerbil: Podnieś porty w Komponencie Dockera", + "resourceLearnRaw": "Dowiedz się, jak skonfigurować zasoby TCP/UDP", + "resourceBack": "Powrót do zasobów", + "resourceGoTo": "Przejdź do zasobu", + "visibility": "Widoczność", + "enabled": "Włączone", + "disabled": "Wyłączone", + "general": "Ogólny", + "proxy": "Proxy", + "rules": "Regulamin", + "resourceSettingDescription": "Skonfiguruj ustawienia zasobu", + "resourceSetting": "Ustawienia {resourceName}", + "alwaysAllow": "Zawsze zezwalaj", + "alwaysDeny": "Zawsze odmawiaj", + "orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji", + "orgGeneralSettings": "Ustawienia organizacji", + "orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją", + "orgGeneralSave": "Zapisz ustawienia ogólne", + "orgDangerZone": "Strefa zagrożenia", + "orgDangerZoneDescription": "Po usunięciu tego organa nie ma odwrotu. Upewnij się.", + "orgDelete": "Usuń organizację", + "orgDeleteConfirm": "Potwierdź usunięcie organizacji", + "orgMessageRemove": "Ta akcja jest nieodwracalna i usunie wszystkie powiązane dane.", + "orgMessageConfirm": "Aby potwierdzić, wpisz nazwę organizacji poniżej.", + "orgQuestionRemove": "Czy na pewno chcesz usunąć organizację {selectedOrg}?", + "orgUpdated": "Organizacja zaktualizowana", + "orgUpdatedDescription": "Organizacja została zaktualizowana.", + "orgErrorUpdate": "Nie udało się zaktualizować organizacji", + "orgErrorUpdateMessage": "Wystąpił błąd podczas aktualizacji organizacji.", + "orgErrorFetch": "Nie udało się pobrać organizacji", + "orgErrorFetchMessage": "Wystąpił błąd podczas wyświetlania Twoich organizacji", + "orgErrorDelete": "Nie udało się usunąć organizacji", + "orgErrorDeleteMessage": "Wystąpił błąd podczas usuwania organizacji.", + "orgDeleted": "Organizacja usunięta", + "orgDeletedMessage": "Organizacja i jej dane zostały usunięte.", + "accessUsersManage": "Zarządzaj użytkownikami", + "accessUsersDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do Twojej organizacji", + "accessUsersSearch": "Szukaj użytkowników...", + "accessUserCreate": "Utwórz użytkownika", + "accessUserRemove": "Usuń użytkownika", + "username": "Nazwa użytkownika", + "identityProvider": "Dostawca tożsamości", + "role": "Rola", + "accessRoleNameRequired": "Nazwa jest wymagana", + "accessRolesManage": "Zarządzaj rolami", + "accessRolesDescription": "Skonfiguruj role do zarządzania dostępem do Twojej organizacji", + "accessRolesSearch": "Szukaj ról...", + "accessRolesAdd": "Dodaj rolę", + "accessRoleDelete": "Usuń rolę", + "description": "Opis", + "inviteTitle": "Otwórz zaproszenia", + "inviteDescription": "Zarządzaj zaproszeniami dla innych użytkowników", + "inviteSearch": "Szukaj zaproszeń..." +} \ No newline at end of file From 2543bf356c239675a58434a0bb8de6b731797b5b Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:33 +0200 Subject: [PATCH 011/105] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/pt-PT.json diff --git a/messages/pt-PT.json b/messages/pt-PT.json new file mode 100644 index 00000000..24d4f728 --- /dev/null +++ b/messages/pt-PT.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Crie sua organização, site e recursos", + "setupNewOrg": "Nova organização", + "setupCreateOrg": "Criar Organização", + "setupCreateSite": "Criar site", + "setupCreateResources": "Criar recursos", + "setupOrgName": "Nome Da Organização", + "orgDisplayName": "Este é o nome de exibição da sua organização.", + "setupOrgId": "ID da organização", + "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", + "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", + "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", + "welcome": "Bem-vindo ao Pangolin", + "componentsCreateOrg": "Criar uma organização", + "componentsMember": "Você é membro de {count, plural, =0 {Nenhuma organização} =1 {Uma organização} other {# organizações}}", + "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", + "dismiss": "Descartar", + "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", + "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", + "inviteLoginUser": "Verifique se você está logado como o usuário correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", + "inviteCreateUser": "Por favor, crie uma conta primeiro.", + "goHome": "Ir para casa", + "inviteLogInOtherUser": "Fazer login como um usuário diferente", + "createAnAccount": "Crie uma conta", + "inviteNotAccepted": "Convite não aceito", + "authCreateAccount": "Crie uma conta para começar", + "email": "e-mail", + "password": "Palavra-passe", + "confirmPassword": "Confirmar senha", + "createAccount": "Criar conta", + "viewSettings": "Visualizar configurações", + "delete": "excluir", + "name": "Nome:", + "online": "Disponível", + "offline": "Desconectado", + "site": "site", + "dataIn": "Dados em", + "dataOut": "Dados de saída", + "connectionType": "Tipo de conexão", + "local": "Localização", + "edit": "Alterar", + "siteConfirmDelete": "Confirmar exclusão do site", + "siteDelete": "Excluir site", + "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", + "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", + "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", + "siteManageSites": "Gerenciar sites", + "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", + "siteCreate": "Criar site", + "siteCreateDescription": "Crie um novo site para começar a conectar seus recursos", + "close": "FECHAR", + "siteNameMin": "O nome deve ter pelo menos 2 caracteres.", + "siteNameMax": "O nome não deve ter mais de 30 caracteres.", + "siteErrorCreate": "Erro ao criar site", + "siteErrorCreateKeyPair": "Par de chaves ou padrões do site não encontrados", + "siteErrorCreateDefaults": "Padrão do site não encontrado", + "siteNameDescription": "Este é o nome de exibição do site.", + "method": "Método", + "siteMethodDescription": "É assim que você irá expor as conexões.", + "siteLearnNewt": "Saiba como instalar o Newt no seu sistema", + "siteSeeConfigOnce": "Você só poderá ver a configuração uma vez.", + "siteLoadWGConfig": "Carregando configuração do WireGuarde...", + "siteDocker": "Expandir para detalhes da implantação Docker", + "toggle": "Alternador", + "dockerCompose": "Composição do Docker", + "dockerRun": "Execução do Docker", + "siteLearnLocal": "Os sites locais não são túneis, saiba mais", + "siteConfirmCopy": "Eu copiei a configuração", + "searchSites": "Procurar sites...", + "siteAdd": "Adicionar Site", + "recommended": "Recomendados", + "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteRunsInDocker": "Executa no Docker", + "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", + "siteErrorDelete": "Erro ao excluir site", + "shareTitle": "Gerenciar links de compartilhamento", + "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", + "shareSearch": "Pesquisar links de compartilhamento...", + "shareCreate": "Criar Link de Compartilhamento", + "shareErrorDelete": "Falha ao excluir o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", + "shareDeleted": "Link excluído", + "shareDeletedDesciption": "O link foi eliminado", + "openMenu": "Abrir menu", + "resource": "Recurso", + "title": "Título", + "created": "Criado", + "expires": "Expira", + "never": "nunca", + "shareErrorSelectResource": "Por favor, selecione um recurso", + "resourceTitle": "Gerenciar Recursos", + "resourceDescription": "Crie proxies seguros para seus aplicativos privados", + "resourceSearch": "Procurar recursos...", + "resourceAdd": "Adicionar Recurso", + "resourceErrorDelte": "Erro ao excluir recurso", + "authentication": "Autenticação", + "protected": "Protegido", + "notProtected": "Não Protegido", + "resourceMessageRemove": "Uma vez removido, o recurso não estará mais acessível. Todos os alvos associados ao recurso também serão removidos.", + "resourceMessageConfirm": "Para confirmar, por favor, digite o nome do recurso abaixo.", + "resourceQuestionRemove": "Tem certeza que deseja remover o recurso {selectedResource} da organização?", + "resourceHTTP": "Recurso HTTPS", + "resourceHTTPDescription": "O proxy solicita ao seu aplicativo via HTTPS usando um subdomínio ou domínio base.", + "resourceRaw": "Recurso TCP/UDP bruto", + "resourceRawDescription": "O proxy solicita ao seu aplicativo sobre TCP/UDP usando um número de porta.", + "resourceCreate": "Criar Recurso", + "resourceCreateDescription": "Siga os passos abaixo para criar um novo recurso", + "resourceSeeAll": "Ver todos os recursos", + "resourceInfo": "Informação do recurso", + "resourceNameDescription": "Este é o nome de exibição para o recurso.", + "siteSelect": "Selecionar site", + "siteSearch": "Procurar no site", + "siteNotFound": "Nenhum site encontrado.", + "siteSelectionDescription": "Este site fornecerá conectividade ao recurso.", + "resourceType": "Tipo de Recurso", + "resourceTypeDescription": "Determine como você deseja acessar seu recurso", + "resourceHTTPSSettings": "Configurações de HTTPS", + "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", + "domainType": "Tipo de domínio", + "subdomain": "Subdomínio", + "baseDomain": "Domínio Base", + "subdomnainDescription": "O subdomínio onde seu recurso estará acessível.", + "resourceRawSettings": "Configurações TCP/UDP", + "resourceRawSettingsDescription": "Configure como seu recurso será acessado sobre TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Número da Porta", + "resourcePortNumberDescription": "O número da porta externa para requisições de proxy.", + "cancle": "Cancelar", + "resourceConfig": "Snippets de Configuração", + "resourceConfigDescription": "Copie e cole estes snippets de configuração para configurar o seu recurso TCP/UDP", + "resourceAddEntrypoints": "Traefik: Adicionar pontos de entrada", + "resourceExposePorts": "Gerbil: Expor Portas no Docker Compose", + "resourceLearnRaw": "Aprenda como configurar os recursos TCP/UDP", + "resourceBack": "Voltar aos recursos", + "resourceGoTo": "Ir para o Recurso", + "visibility": "Visibilidade", + "enabled": "Ativado", + "disabled": "Desabilitado", + "general": "Gerais", + "proxy": "Proxy", + "rules": "Regras", + "resourceSettingDescription": "Configure as configurações do seu recurso", + "resourceSetting": "Configurações do {resourceName}", + "alwaysAllow": "Sempre permitir", + "alwaysDeny": "Sempre negar", + "orgSettingsDescription": "Configurar as configurações gerais da sua organização", + "orgGeneralSettings": "Configurações da organização", + "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", + "orgGeneralSave": "Salvar configurações gerais", + "orgDangerZone": "Zona de Perigo", + "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", + "orgDelete": "Excluir Organização", + "orgDeleteConfirm": "Confirmar exclusão da organização", + "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", + "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", + "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", + "orgUpdated": "Organização atualizada", + "orgUpdatedDescription": "A organização foi atualizada.", + "orgErrorUpdate": "Falha ao atualizar organização", + "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", + "orgErrorFetch": "Falha ao buscar organizações", + "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", + "orgErrorDelete": "Falha ao excluir organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", + "orgDeleted": "Organização excluída", + "orgDeletedMessage": "A organização e seus dados foram excluídos.", + "accessUsersManage": "Gerenciar Usuários", + "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", + "accessUsersSearch": "Procurar usuários...", + "accessUserCreate": "Criar Usuário", + "accessUserRemove": "Remover usuário", + "username": "Usuário:", + "identityProvider": "Provedor de Identidade", + "role": "Funções", + "accessRoleNameRequired": "O nome é obrigatório", + "accessRolesManage": "Gerenciar Funções", + "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", + "accessRolesSearch": "Pesquisar funções...", + "accessRolesAdd": "Adicionar função", + "accessRoleDelete": "Excluir Papel", + "description": "Descrição:", + "inviteTitle": "Convites Abertos", + "inviteDescription": "Gerencie seus convites para outros usuários", + "inviteSearch": "Procurar convites..." +} \ No newline at end of file From aca1cc0518e2226b599ed26f1cb5677fb771e798 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:15:33 +0200 Subject: [PATCH 012/105] New translations en-us.json (Turkish) --- messages/tr-TR.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/tr-TR.json diff --git a/messages/tr-TR.json b/messages/tr-TR.json new file mode 100644 index 00000000..c392a417 --- /dev/null +++ b/messages/tr-TR.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Create your organization, site, and resources", + "setupNewOrg": "New Organization", + "setupCreateOrg": "Create Organization", + "setupCreateSite": "Create Site", + "setupCreateResources": "Create Resources", + "setupOrgName": "Organization Name", + "orgDisplayName": "This is the display name of your organization.", + "setupOrgId": "Organization ID", + "setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.", + "setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.", + "componentsErrorNoMemberCreate": "You are not currently a member of any organizations. Create an organization to get started.", + "componentsErrorNoMember": "You are not currently a member of any organizations.", + "welcome": "Welcome to Pangolin", + "componentsCreateOrg": "Create an Organization", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", + "dismiss": "Dismiss", + "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", + "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", + "inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.", + "inviteErrorUser": "We're sorry, but it looks like the invite you're trying to access is not for this user.", + "inviteLoginUser": "Please make sure you're logged in as the correct user.", + "inviteErrorNoUser": "We're sorry, but it looks like the invite you're trying to access is not for a user that exists.", + "inviteCreateUser": "Please create an account first.", + "goHome": "Go Home", + "inviteLogInOtherUser": "Log In as a Different User", + "createAnAccount": "Create an Account", + "inviteNotAccepted": "Invite Not Accepted", + "authCreateAccount": "Create an account to get started", + "email": "Email", + "password": "Password", + "confirmPassword": "Confirm Password", + "createAccount": "Create Account", + "viewSettings": "View settings", + "delete": "Delete", + "name": "Name", + "online": "Online", + "offline": "Offline", + "site": "Site", + "dataIn": "Data In", + "dataOut": "Data Out", + "connectionType": "Connection Type", + "local": "Local", + "edit": "Edit", + "siteConfirmDelete": "Confirm Delete Site", + "siteDelete": "Delete Site", + "siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.", + "siteMessageConfirm": "To confirm, please type the name of the site below.", + "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", + "siteManageSites": "Manage Sites", + "siteDescription": "Allow connectivity to your network through secure tunnels", + "siteCreate": "Create Site", + "siteCreateDescription": "Create a new site to start connecting your resources", + "close": "Close", + "siteNameMin": "Name must be at least 2 characters.", + "siteNameMax": "Name must not be longer than 30 characters.", + "siteErrorCreate": "Error creating site", + "siteErrorCreateKeyPair": "Key pair or site defaults not found", + "siteErrorCreateDefaults": "Site defaults not found", + "siteNameDescription": "This is the display name for the site.", + "method": "Method", + "siteMethodDescription": "This is how you will expose connections.", + "siteLearnNewt": "Learn how to install Newt on your system", + "siteSeeConfigOnce": "You will only be able to see the configuration once.", + "siteLoadWGConfig": "Loading WireGuard configuration...", + "siteDocker": "Expand for Docker Deployment Details", + "toggle": "Toggle", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "Local sites do not tunnel, learn more", + "siteConfirmCopy": "I have copied the config", + "searchSites": "Search sites...", + "siteAdd": "Add Site", + "recommended": "Recommended", + "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", + "siteRunsInDocker": "Runs in Docker", + "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", + "siteErrorDelete": "Error deleting site", + "shareTitle": "Manage Share Links", + "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", + "shareSearch": "Search share links...", + "shareCreate": "Create Share Link", + "shareErrorDelete": "Failed to delete link", + "shareErrorDeleteMessage": "An error occurred deleting link", + "shareDeleted": "Link deleted", + "shareDeletedDesciption": "The link has been deleted", + "openMenu": "Open menu", + "resource": "Resource", + "title": "Title", + "created": "Created", + "expires": "Expires", + "never": "Never", + "shareErrorSelectResource": "Please select a resource", + "resourceTitle": "Manage Resources", + "resourceDescription": "Create secure proxies to your private applications", + "resourceSearch": "Search resources...", + "resourceAdd": "Add Resource", + "resourceErrorDelte": "Error deleting resource", + "authentication": "Authentication", + "protected": "Protected", + "notProtected": "Not Protected", + "resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.", + "resourceMessageConfirm": "To confirm, please type the name of the resource below.", + "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", + "resourceHTTP": "HTTPS Resource", + "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", + "resourceRaw": "Raw TCP/UDP Resource", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", + "resourceCreate": "Create Resource", + "resourceCreateDescription": "Follow the steps below to create a new resource", + "resourceSeeAll": "See All Resources", + "resourceInfo": "Resource Information", + "resourceNameDescription": "This is the display name for the resource.", + "siteSelect": "Select site", + "siteSearch": "Search site", + "siteNotFound": "No site found.", + "siteSelectionDescription": "This site will provide connectivity to the resource.", + "resourceType": "Resource Type", + "resourceTypeDescription": "Determine how you want to access your resource", + "resourceHTTPSSettings": "HTTPS Settings", + "resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS", + "domainType": "Domain Type", + "subdomain": "Subdomain", + "baseDomain": "Base Domain", + "subdomnainDescription": "The subdomain where your resource will be accessible.", + "resourceRawSettings": "TCP/UDP Settings", + "resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Port Number", + "resourcePortNumberDescription": "The external port number to proxy requests.", + "cancle": "Cancle", + "resourceConfig": "Configuration Snippets", + "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", + "resourceAddEntrypoints": "Traefik: Add Entrypoints", + "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", + "resourceLearnRaw": "Learn how to configure TCP/UDP resources", + "resourceBack": "Back to Resources", + "resourceGoTo": "Go to Resource", + "visibility": "Visibility", + "enabled": "Enabled", + "disabled": "Disabled", + "general": "General", + "proxy": "Proxy", + "rules": "Rules", + "resourceSettingDescription": "Configure the settings on your resource", + "resourceSetting": "{resourceName} Settings", + "alwaysAllow": "Always Allow", + "alwaysDeny": "Always Deny", + "orgSettingsDescription": "Configure your organization's general settings", + "orgGeneralSettings": "Organization Settings", + "orgGeneralSettingsDescription": "Manage your organization details and configuration", + "orgGeneralSave": "Save General Settings", + "orgDangerZone": "Danger Zone", + "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", + "orgDelete": "Delete Organization", + "orgDeleteConfirm": "Confirm Delete Organization", + "orgMessageRemove": "This action is irreversible and will delete all associated data.", + "orgMessageConfirm": "To confirm, please type the name of the organization below.", + "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", + "orgUpdated": "Organization updated", + "orgUpdatedDescription": "The organization has been updated.", + "orgErrorUpdate": "Failed to update organization", + "orgErrorUpdateMessage": "An error occurred while updating the organization.", + "orgErrorFetch": "Failed to fetch organizations", + "orgErrorFetchMessage": "An error occurred while listing your organizations", + "orgErrorDelete": "Failed to delete organization", + "orgErrorDeleteMessage": "An error occurred while deleting the organization.", + "orgDeleted": "Organization deleted", + "orgDeletedMessage": "The organization and its data has been deleted.", + "accessUsersManage": "Manage Users", + "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", + "accessUsersSearch": "Search users...", + "accessUserCreate": "Create User", + "accessUserRemove": "Remove User", + "username": "Username", + "identityProvider": "Identity Provider", + "role": "Role", + "accessRoleNameRequired": "Name is required", + "accessRolesManage": "Manage Roles", + "accessRolesDescription": "Configure roles to manage access to your organization", + "accessRolesSearch": "Search roles...", + "accessRolesAdd": "Add Role", + "accessRoleDelete": "Delete Role", + "description": "Description", + "inviteTitle": "Open Invitations", + "inviteDescription": "Manage your invitations to other users", + "inviteSearch": "Search invitations..." +} \ No newline at end of file From 3d4b9d48e36a856a7eec8e75db36a9022121492c Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sun, 4 May 2025 17:20:01 +0000 Subject: [PATCH 013/105] modified: src/components/LocaleSwitcher.tsx modified: src/i18n/config.ts --- src/components/LocaleSwitcher.tsx | 20 ++++++++++++++++++++ src/i18n/config.ts | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index d7869326..962b39bb 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -12,9 +12,29 @@ export default function LocaleSwitcher() { value: 'en-US', label: 'Englisch' }, + { + value: 'fr-FR', + label: 'French' + }, { value: 'de-DE', label: 'German' + }, + { + value: 'it-IT', + label: 'Italian' + }, + { + value: 'pl-PL', + label: 'Polish' + }, + { + value: 'pt-PT', + label: 'Portuguese' + }, + { + value: 'tr-TR', + label: 'Turkish' } ]} label='Language' diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 7e71f24b..030d7f71 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,4 +1,4 @@ export type Locale = (typeof locales)[number]; -export const locales = ['en-US', 'de-DE'] as const; +export const locales = ['en-US', 'fr-FR', 'de-DE', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR'] as const; export const defaultLocale: Locale = 'en-US'; \ No newline at end of file From 6e5391cb8fc274e8cbf29f3ad2395a956647b748 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:40:10 +0200 Subject: [PATCH 014/105] Delete messages/de-DE.json --- messages/de-DE.json | 190 -------------------------------------------- 1 file changed, 190 deletions(-) delete mode 100644 messages/de-DE.json diff --git a/messages/de-DE.json b/messages/de-DE.json deleted file mode 100644 index 04469f16..00000000 --- a/messages/de-DE.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", - "setupNewOrg": "Neue Organisation", - "setupCreateOrg": "Organisation erstellen", - "setupCreateSite": "Seite erstellen", - "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Organisation's Name", - "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", - "setupOrgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", - "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", - "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", - "welcome": "Willkommen zu Pangolin", - "componentsCreateOrg": "Erstelle eine Organisation", - "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", - "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", - "dismiss": "Verwerfen", - "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", - "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", - "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", - "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", - "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", - "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", - "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", - "goHome": "Nach Hause", - "inviteLogInOtherUser": "Als anderer Benutzer anmelden", - "createAnAccount": "Konto erstellen", - "inviteNotAccepted": "Einladung nicht angenommen", - "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", - "email": "E-Mail", - "password": "Passwort", - "confirmPassword": "Passwort bestätigen", - "createAccount": "Konto erstellen", - "viewSettings": "Einstellungen anzeigen", - "delete": "Löschen", - "name": "Name", - "online": "Online", - "offline": "Offline", - "site": "Site", - "dataIn": "Daten in", - "dataOut": "Daten raus", - "connectionType": "Verbindungstyp", - "local": "Lokal", - "edit": "Bearbeiten", - "siteConfirmDelete": "Site löschen bestätigen", - "siteDelete": "Site löschen", - "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", - "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", - "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", - "siteManageSites": "Sites verwalten", - "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", - "siteCreate": "Site erstellen", - "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", - "close": "Schließen", - "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", - "siteNameMax": "Name darf nicht länger als 30 Zeichen sein.", - "siteErrorCreate": "Fehler beim Erstellen der Seite", - "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", - "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", - "siteNameDescription": "Dies ist der Anzeigename für die Website.", - "method": "Methode", - "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", - "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", - "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", - "siteLoadWGConfig": "Lade WireGuard Konfiguration...", - "siteDocker": "Erweitern für Docker-Details", - "toggle": "Umschalten", - "dockerCompose": "Docker komponieren", - "dockerRun": "Docker Run", - "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", - "siteConfirmCopy": "Ich habe die Konfiguration kopiert", - "searchSites": "Seiten suchen...", - "siteAdd": "Site hinzufügen", - "recommended": "Empfohlen", - "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", - "siteRunsInDocker": "Läuft im Docker", - "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", - "siteErrorDelete": "Fehler beim Löschen der Seite", - "shareTitle": "Links zum Teilen verwalten", - "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", - "shareSearch": "Freigabe-Links suchen...", - "shareCreate": "Link erstellen", - "shareErrorDelete": "Link konnte nicht gelöscht werden", - "shareErrorDeleteMessage": "Fehler beim Löschen des Links", - "shareDeleted": "Link gelöscht", - "shareDeletedDesciption": "Der Link wurde gelöscht", - "openMenu": "Menü öffnen", - "resource": "Ressource", - "title": "Titel", - "created": "Erstellt", - "expires": "Gültig bis", - "never": "Nie", - "shareErrorSelectResource": "Bitte wählen Sie eine Ressource", - "resourceTitle": "Ressourcen verwalten", - "resourceDescription": "Erstellen Sie sichere Proxies für Ihre privaten Anwendungen", - "resourceSearch": "Suche Ressourcen...", - "resourceAdd": "Ressource hinzufügen", - "resourceErrorDelte": "Fehler beim Löschen der Ressource", - "authentication": "Authentifizierung", - "protected": "Geschützt", - "notProtected": "Nicht geschützt", - "resourceMessageRemove": "Einmal entfernt, wird die Ressource nicht mehr zugänglich sein. Alle mit der Ressource verbundenen Ziele werden ebenfalls entfernt.", - "resourceMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Ressource unten ein.", - "resourceQuestionRemove": "Sind Sie sicher, dass Sie die Ressource {selectedResource} aus der Organisation entfernen möchten?", - "resourceHTTP": "HTTPS-Ressource", - "resourceHTTPDescription": "Proxy-Anfragen an Ihre App über HTTPS unter Verwendung einer Subdomain oder einer Basis-Domain.", - "resourceRaw": "Rohe TCP/UDP Ressource", - "resourceRawDescription": "Proxy-Anfragen an Ihre App über TCP/UDP mit einer Portnummer.", - "resourceCreate": "Ressource erstellen", - "resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen", - "resourceSeeAll": "Alle Ressourcen anzeigen", - "resourceInfo": "Ressourcen-Informationen", - "resourceNameDescription": "Dies ist der Anzeigename für die Ressource.", - "siteSelect": "Site auswählen", - "siteSearch": "Website durchsuchen", - "siteNotFound": "Keine Site gefunden.", - "siteSelectionDescription": "Diese Seite wird die Verbindung zu der Ressource herstellen.", - "resourceType": "Ressourcentyp", - "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", - "resourceHTTPSSettings": "HTTPS-Einstellungen", - "resourceHTTPSSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über HTTPS", - "domainType": "Domänentyp", - "subdomain": "Subdomain", - "baseDomain": "Basisdomäne", - "subdomnainDescription": "Die Subdomäne, auf die Ihre Ressource zugegriffen werden soll.", - "resourceRawSettings": "TCP/UDP Einstellungen", - "resourceRawSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", - "resourcePortNumber": "Portnummer", - "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", - "cancle": "Abbrechen", - "resourceConfig": "Konfiguration Snippets", - "resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um Ihre TCP/UDP Ressource einzurichten", - "resourceAddEntrypoints": "Traefik: Einstiegspunkte hinzufügen", - "resourceExposePorts": "Gerbil: Ports im Docker Compose ausblenden", - "resourceLearnRaw": "Lernen Sie, wie Sie TCP/UDP Ressourcen konfigurieren", - "resourceBack": "Zurück zu den Ressourcen", - "resourceGoTo": "Zu Ressource gehen", - "visibility": "Sichtbarkeit", - "enabled": "Aktiviert", - "disabled": "Deaktiviert", - "general": "Allgemein", - "proxy": "Proxy", - "rules": "Regeln", - "resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource", - "resourceSetting": "{resourceName} Einstellungen", - "alwaysAllow": "Immer erlauben", - "alwaysDeny": "Immer ablehnen", - "orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation", - "orgGeneralSettings": "Organisations-Einstellungen", - "orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten", - "orgGeneralSave": "Allgemeine Einstellungen speichern", - "orgDangerZone": "Gefahrenzone", - "orgDangerZoneDescription": "Sobald Sie diesen Org löschen, gibt es kein Zurück mehr. Bitte seien Sie vorsichtig.", - "orgDelete": "Organisation löschen", - "orgDeleteConfirm": "Organisation löschen bestätigen", - "orgMessageRemove": "Diese Aktion ist unwiderruflich und löscht alle zugehörigen Daten.", - "orgMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Organisation unten ein.", - "orgQuestionRemove": "Sind Sie sicher, dass Sie die Organisation {selectedOrg} entfernen möchten?", - "orgUpdated": "Organisation aktualisiert", - "orgUpdatedDescription": "Die Organisation wurde aktualisiert.", - "orgErrorUpdate": "Fehler beim Aktualisieren der Organisation", - "orgErrorUpdateMessage": "Beim Aktualisieren der Organisation ist ein Fehler aufgetreten.", - "orgErrorFetch": "Fehler beim Abrufen von Organisationen", - "orgErrorFetchMessage": "Beim Auflisten Ihrer Organisationen ist ein Fehler aufgetreten", - "orgErrorDelete": "Organisation konnte nicht gelöscht werden", - "orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.", - "orgDeleted": "Organisation gelöscht", - "orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.", - "accessUsersManage": "Benutzer verwalten", - "accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten", - "accessUsersSearch": "Benutzer suchen...", - "accessUserCreate": "Benutzer erstellen", - "accessUserRemove": "Benutzer entfernen", - "username": "Benutzername", - "identityProvider": "Identitätsanbieter", - "role": "Rolle", - "accessRoleNameRequired": "Name ist erforderlich", - "accessRolesManage": "Rollen verwalten", - "accessRolesDescription": "Konfigurieren Sie Rollen, um den Zugriff auf Ihre Organisation zu verwalten", - "accessRolesSearch": "Rollen suchen...", - "accessRolesAdd": "Rolle hinzufügen", - "accessRoleDelete": "Rolle löschen", - "description": "Beschreibung", - "inviteTitle": "Einladungen öffnen", - "inviteDescription": "Ihre Einladungen an andere Benutzer verwalten", - "inviteSearch": "Einladungen suchen..." -} \ No newline at end of file From 29375385c0b38aafa1e33e9d2cf6b41bb0fc6316 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 4 May 2025 19:41:09 +0200 Subject: [PATCH 015/105] Create de-DE.json --- messages/de-DE.json | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 messages/de-DE.json diff --git a/messages/de-DE.json b/messages/de-DE.json new file mode 100644 index 00000000..79220877 --- /dev/null +++ b/messages/de-DE.json @@ -0,0 +1,190 @@ +{ + "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", + "setupNewOrg": "Neue Organisation", + "setupCreateOrg": "Organisation erstellen", + "setupCreateSite": "Seite erstellen", + "setupCreateResources": "Ressource erstellen", + "setupOrgName": "Organisation's Name", + "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", + "setupOrgId": "Organisations-ID", + "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", + "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", + "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", + "welcome": "Willkommen zu Pangolin", + "componentsCreateOrg": "Erstelle eine Organisation", + "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", + "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "dismiss": "Verwerfen", + "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", + "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", + "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", + "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", + "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", + "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", + "goHome": "Nach Hause", + "inviteLogInOtherUser": "Als anderer Benutzer anmelden", + "createAnAccount": "Konto erstellen", + "inviteNotAccepted": "Einladung nicht angenommen", + "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", + "email": "E-Mail", + "password": "Passwort", + "confirmPassword": "Passwort bestätigen", + "createAccount": "Konto erstellen", + "viewSettings": "Einstellungen anzeigen", + "delete": "Löschen", + "name": "Name", + "online": "Online", + "offline": "Offline", + "site": "Site", + "dataIn": "Daten in", + "dataOut": "Daten raus", + "connectionType": "Verbindungstyp", + "local": "Lokal", + "edit": "Bearbeiten", + "siteConfirmDelete": "Site löschen bestätigen", + "siteDelete": "Site löschen", + "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", + "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", + "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", + "siteManageSites": "Sites verwalten", + "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", + "siteCreate": "Site erstellen", + "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", + "close": "Schließen", + "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", + "siteNameMax": "Name darf nicht länger als 30 Zeichen sein.", + "siteErrorCreate": "Fehler beim Erstellen der Seite", + "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", + "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", + "siteNameDescription": "Dies ist der Anzeigename für die Website.", + "method": "Methode", + "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", + "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", + "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", + "siteLoadWGConfig": "Lade WireGuard Konfiguration...", + "siteDocker": "Erweitern für Docker-Details", + "toggle": "Umschalten", + "dockerCompose": "Docker komponieren", + "dockerRun": "Docker Run", + "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", + "siteConfirmCopy": "Ich habe die Konfiguration kopiert", + "searchSites": "Seiten suchen...", + "siteAdd": "Site hinzufügen", + "recommended": "Empfohlen", + "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", + "siteRunsInDocker": "Läuft im Docker", + "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", + "siteErrorDelete": "Fehler beim Löschen der Seite", + "shareTitle": "Links zum Teilen verwalten", + "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", + "shareSearch": "Freigabe-Links suchen...", + "shareCreate": "Link erstellen", + "shareErrorDelete": "Link konnte nicht gelöscht werden", + "shareErrorDeleteMessage": "Fehler beim Löschen des Links", + "shareDeleted": "Link gelöscht", + "shareDeletedDesciption": "Der Link wurde gelöscht", + "openMenu": "Menü öffnen", + "resource": "Ressource", + "title": "Titel", + "created": "Erstellt", + "expires": "Gültig bis", + "never": "Nie", + "shareErrorSelectResource": "Bitte wählen Sie eine Ressource", + "resourceTitle": "Ressourcen verwalten", + "resourceDescription": "Erstellen Sie sichere Proxies für Ihre privaten Anwendungen", + "resourceSearch": "Suche Ressourcen...", + "resourceAdd": "Ressource hinzufügen", + "resourceErrorDelte": "Fehler beim Löschen der Ressource", + "authentication": "Authentifizierung", + "protected": "Geschützt", + "notProtected": "Nicht geschützt", + "resourceMessageRemove": "Einmal entfernt, wird die Ressource nicht mehr zugänglich sein. Alle mit der Ressource verbundenen Ziele werden ebenfalls entfernt.", + "resourceMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Ressource unten ein.", + "resourceQuestionRemove": "Sind Sie sicher, dass Sie die Ressource {selectedResource} aus der Organisation entfernen möchten?", + "resourceHTTP": "HTTPS-Ressource", + "resourceHTTPDescription": "Proxy-Anfragen an Ihre App über HTTPS unter Verwendung einer Subdomain oder einer Basis-Domain.", + "resourceRaw": "Rohe TCP/UDP Ressource", + "resourceRawDescription": "Proxy-Anfragen an Ihre App über TCP/UDP mit einer Portnummer.", + "resourceCreate": "Ressource erstellen", + "resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen", + "resourceSeeAll": "Alle Ressourcen anzeigen", + "resourceInfo": "Ressourcen-Informationen", + "resourceNameDescription": "Dies ist der Anzeigename für die Ressource.", + "siteSelect": "Site auswählen", + "siteSearch": "Website durchsuchen", + "siteNotFound": "Keine Site gefunden.", + "siteSelectionDescription": "Diese Seite wird die Verbindung zu der Ressource herstellen.", + "resourceType": "Ressourcentyp", + "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", + "resourceHTTPSSettings": "HTTPS-Einstellungen", + "resourceHTTPSSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über HTTPS", + "domainType": "Domänentyp", + "subdomain": "Subdomain", + "baseDomain": "Basisdomäne", + "subdomnainDescription": "Die Subdomäne, auf die Ihre Ressource zugegriffen werden soll.", + "resourceRawSettings": "TCP/UDP Einstellungen", + "resourceRawSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Portnummer", + "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", + "cancle": "Abbrechen", + "resourceConfig": "Konfiguration Snippets", + "resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um Ihre TCP/UDP Ressource einzurichten", + "resourceAddEntrypoints": "Traefik: Einstiegspunkte hinzufügen", + "resourceExposePorts": "Gerbil: Ports im Docker Compose ausblenden", + "resourceLearnRaw": "Lernen Sie, wie Sie TCP/UDP Ressourcen konfigurieren", + "resourceBack": "Zurück zu den Ressourcen", + "resourceGoTo": "Zu Ressource gehen", + "visibility": "Sichtbarkeit", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "general": "Allgemein", + "proxy": "Proxy", + "rules": "Regeln", + "resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource", + "resourceSetting": "{resourceName} Einstellungen", + "alwaysAllow": "Immer erlauben", + "alwaysDeny": "Immer ablehnen", + "orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation", + "orgGeneralSettings": "Organisations-Einstellungen", + "orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten", + "orgGeneralSave": "Allgemeine Einstellungen speichern", + "orgDangerZone": "Gefahrenzone", + "orgDangerZoneDescription": "Sobald Sie diesen Org löschen, gibt es kein Zurück mehr. Bitte seien Sie vorsichtig.", + "orgDelete": "Organisation löschen", + "orgDeleteConfirm": "Organisation löschen bestätigen", + "orgMessageRemove": "Diese Aktion ist unwiderruflich und löscht alle zugehörigen Daten.", + "orgMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Organisation unten ein.", + "orgQuestionRemove": "Sind Sie sicher, dass Sie die Organisation {selectedOrg} entfernen möchten?", + "orgUpdated": "Organisation aktualisiert", + "orgUpdatedDescription": "Die Organisation wurde aktualisiert.", + "orgErrorUpdate": "Fehler beim Aktualisieren der Organisation", + "orgErrorUpdateMessage": "Beim Aktualisieren der Organisation ist ein Fehler aufgetreten.", + "orgErrorFetch": "Fehler beim Abrufen von Organisationen", + "orgErrorFetchMessage": "Beim Auflisten Ihrer Organisationen ist ein Fehler aufgetreten", + "orgErrorDelete": "Organisation konnte nicht gelöscht werden", + "orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.", + "orgDeleted": "Organisation gelöscht", + "orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.", + "accessUsersManage": "Benutzer verwalten", + "accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten", + "accessUsersSearch": "Benutzer suchen...", + "accessUserCreate": "Benutzer erstellen", + "accessUserRemove": "Benutzer entfernen", + "username": "Benutzername", + "identityProvider": "Identitätsanbieter", + "role": "Rolle", + "accessRoleNameRequired": "Name ist erforderlich", + "accessRolesManage": "Rollen verwalten", + "accessRolesDescription": "Konfigurieren Sie Rollen, um den Zugriff auf Ihre Organisation zu verwalten", + "accessRolesSearch": "Rollen suchen...", + "accessRolesAdd": "Rolle hinzufügen", + "accessRoleDelete": "Rolle löschen", + "description": "Beschreibung", + "inviteTitle": "Einladungen öffnen", + "inviteDescription": "Ihre Einladungen an andere Benutzer verwalten", + "inviteSearch": "Einladungen suchen..." +} From fa1997adc1814b4bf88358e5958c11897fb56495 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Mon, 5 May 2025 16:10:08 +0000 Subject: [PATCH 016/105] complete share link i18n --- messages/en-US.json | 47 +++++++++++++- src/app/[orgId]/settings/general/page.tsx | 2 +- .../settings/resources/ResourcesDataTable.tsx | 2 +- .../settings/share-links/AccessTokenUsage.tsx | 29 ++++----- .../share-links/CreateShareLinkForm.tsx | 64 ++++++++----------- .../settings/sites/[niceId]/SiteInfoCard.tsx | 14 ++-- .../settings/sites/[niceId]/general/page.tsx | 24 ++++--- .../settings/sites/[niceId]/layout.tsx | 9 ++- 8 files changed, 109 insertions(+), 82 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index c392a417..fd80fb83 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Runs in Docker", "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", "siteErrorDelete": "Error deleting site", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Manage Share Links", "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", "shareSearch": "Search share links...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "An error occurred deleting link", "shareDeleted": "Link deleted", "shareDeletedDesciption": "The link has been deleted", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Open menu", "resource": "Resource", "title": "Title", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Please select a resource", "resourceTitle": "Manage Resources", "resourceDescription": "Create secure proxies to your private applications", - "resourceSearch": "Search resources...", + "resourcesSearch": "Search resources...", "resourceAdd": "Add Resource", "resourceErrorDelte": "Error deleting resource", "authentication": "Authentication", @@ -142,6 +176,7 @@ "enabled": "Enabled", "disabled": "Disabled", "general": "General", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Rules", "resourceSettingDescription": "Configure the settings on your resource", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Configure your organization's general settings", "orgGeneralSettings": "Organization Settings", "orgGeneralSettingsDescription": "Manage your organization details and configuration", - "orgGeneralSave": "Save General Settings", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Danger Zone", "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", "orgDelete": "Delete Organization", @@ -186,5 +221,11 @@ "description": "Description", "inviteTitle": "Open Invitations", "inviteDescription": "Manage your invitations to other users", - "inviteSearch": "Search invitations..." + "inviteSearch": "Search invitations...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 14a2885c..967cc21a 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -225,7 +225,7 @@ export default function GeneralPage() { loading={loadingSave} disabled={loadingSave} > - {t('orgGeneralSave')} + {t('saveGeneralSettings')}
diff --git a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx index 812c79e6..13d31953 100644 --- a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx @@ -25,7 +25,7 @@ export function ResourcesDataTable({ columns={columns} data={data} title="Resources" - searchPlaceholder={t('resourceSearch')} + searchPlaceholder={t('resourcesSearch')} searchColumn="name" onAdd={createResource} addButtonText={t('resourceAdd')} diff --git a/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx index 5f44ca52..62c223e0 100644 --- a/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx +++ b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx @@ -15,6 +15,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { useEnvContext } from "@app/hooks/useEnvContext"; import CopyToClipboard from "@app/components/CopyToClipboard"; import CopyTextBox from "@app/components/CopyTextBox"; +import { useTranslations } from 'next-intl'; interface AccessTokenSectionProps { token: string; @@ -37,37 +38,37 @@ export default function AccessTokenSection({ setTimeout(() => setCopied(null), 2000); }; + const t = useTranslations(); + return ( <>

- Your access token can be passed in two ways: as a query - parameter or in the request headers. These must be passed - from the client on every request for authenticated access. + {t('shareTokenDescription')}

- Access Token - Usage Examples + {t('accessToken')} + {t('usageExamples')}
-
Token ID
+
{t('tokenId')}
-
Token
+
{t('token')}
-

Request Headers

+

{t('requestHeades')}

-

Query Parameter

+

{t('queryParameter')}

@@ -84,21 +85,17 @@ ${env.server.resourceAccessTokenHeadersToken}: ${token}`} - Important Note + {t('importantNote')} - For security reasons, using headers is recommended - over query parameters when possible, as query - parameters may be logged in server logs or browser - history. + {t('shareImportantDescription')}
- Keep your access token secure. Do not share it in publicly - accessible areas or client-side code. + {t('shareTokenSecurety')}
); diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index 871f0ca0..ff8ae948 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -66,6 +66,7 @@ import { CollapsibleTrigger } from "@app/components/ui/collapsible"; import AccessTokenSection from "./AccessTokenUsage"; +import { useTranslations } from 'next-intl'; type FormProps = { open: boolean; @@ -91,6 +92,7 @@ export default function CreateShareLinkForm({ const { env } = useEnvContext(); const api = createApiClient({ env }); + const t = useTranslations(); const [link, setLink] = useState(null); const [accessTokenId, setAccessTokenId] = useState(null); @@ -110,12 +112,12 @@ export default function CreateShareLinkForm({ >([]); const timeUnits = [ - { unit: "minutes", name: "Minutes" }, - { unit: "hours", name: "Hours" }, - { unit: "days", name: "Days" }, - { unit: "weeks", name: "Weeks" }, - { unit: "months", name: "Months" }, - { unit: "years", name: "Years" } + { unit: "minutes", name: t('minutes') }, + { unit: "hours", name: t('hours') }, + { unit: "days", name: t('days') }, + { unit: "weeks", name: t('weeks') }, + { unit: "months", name: t('months') }, + { unit: "years", name: t('years') } ]; const form = useForm>({ @@ -141,11 +143,8 @@ export default function CreateShareLinkForm({ console.error(e); toast({ variant: "destructive", - title: "Failed to fetch resources", - description: formatAxiosError( - e, - "An error occurred while fetching the resources" - ) + title: t('shareErrorFetchResource'), + description: formatAxiosError(e, t('shareErrorFetchResourceDescription')) }); }); @@ -208,11 +207,8 @@ export default function CreateShareLinkForm({ console.error(e); toast({ variant: "destructive", - title: "Failed to create share link", - description: formatAxiosError( - e, - "An error occurred while creating the share link" - ) + title: t('shareErrorCreate'), + description: formatAxiosError(e, t('shareErrorCreateDescription')) }); }); @@ -260,9 +256,9 @@ export default function CreateShareLinkForm({ > - Create Shareable Link + {t('shareCreate')} - Anyone with this link can access the resource + {t('shareCreateDescription')} @@ -280,7 +276,7 @@ export default function CreateShareLinkForm({ render={({ field }) => ( - Resource + {t('resource')} @@ -305,12 +301,10 @@ export default function CreateShareLinkForm({ - + - No - resources - found + {t('resourceNotFound')} {resources.map( @@ -366,7 +360,7 @@ export default function CreateShareLinkForm({ render={({ field }) => ( - Title (optional) + {t('shareTitleOptional')} @@ -378,7 +372,7 @@ export default function CreateShareLinkForm({
- Expire In + {t('expireIn')}
- Never expire + {t('neverExpire')}

- Expiration time is how long the - link will be usable and provide - access to the resource. After - this time, the link will no - longer work, and users who used - this link will lose access to - the resource. + {t('shareExpireDescription')}

@@ -475,12 +463,10 @@ export default function CreateShareLinkForm({ {link && (

- You will only be able to see this link - once. Make sure to copy it. + {t('shareSeeOnce')}

- Anyone with this link can access the - resource. Share it with care. + {t('shareAccessHint')}

@@ -506,7 +492,7 @@ export default function CreateShareLinkForm({ className="p-0 flex items-center justify-between w-full" >

- See Access Token Usage + {t('shareTokenUsage')}

@@ -549,7 +535,7 @@ export default function CreateShareLinkForm({ loading={loading} disabled={link !== null || loading} > - Create Link + {t('createLink')} diff --git a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx index ee4758be..63c7b2e8 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx @@ -9,11 +9,13 @@ import { InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; +import { useTranslations } from 'next-intl'; type SiteInfoCardProps = {}; export default function SiteInfoCard({}: SiteInfoCardProps) { const { site, updateSite } = useSiteContext(); + const t = useTranslations(); const getConnectionTypeString = (type: string) => { if (type === "newt") { @@ -21,7 +23,7 @@ export default function SiteInfoCard({}: SiteInfoCardProps) { } else if (type === "wireguard") { return "WireGuard"; } else if (type === "local") { - return "Local"; + return t('local'); } else { return "Unknown"; } @@ -30,23 +32,23 @@ export default function SiteInfoCard({}: SiteInfoCardProps) { return ( - Site Information + {t('siteInfo')} {(site.type == "newt" || site.type == "wireguard") && ( <> - Status + {t('status')} {site.online ? (
- Online + {t('online')}
) : (
- Offline + {t('offline')}
)}
@@ -54,7 +56,7 @@ export default function SiteInfoCard({}: SiteInfoCardProps) { )} - Connection Type + {t('connectionType')} {getConnectionTypeString(site.type)} diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index f107d960..1ed6ad91 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -31,6 +31,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useState } from "react"; +import { useTranslations } from 'next-intl'; const GeneralFormSchema = z.object({ name: z.string().nonempty("Name is required") @@ -46,6 +47,7 @@ export default function GeneralPage() { const [loading, setLoading] = useState(false); const router = useRouter(); + const t = useTranslations(); const form = useForm({ resolver: zodResolver(GeneralFormSchema), @@ -65,19 +67,16 @@ export default function GeneralPage() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to update site", - description: formatAxiosError( - e, - "An error occurred while updating the site." - ) + title: t('siteErrorUpdate'), + description: formatAxiosError(e,t('siteErrorUpdateDescription')) }); }); updateSite({ name: data.name }); toast({ - title: "Site updated", - description: "The site has been updated." + title: t('siteUpdated'), + description: t('siteUpdatedDescription') }); setLoading(false); @@ -90,10 +89,10 @@ export default function GeneralPage() { - General Settings + {t('generalSettings')} - Configure the general settings for this site + {t('siteGeneralDescription')} @@ -110,14 +109,13 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - Name + {t('name')} - This is the display name of the - site. + {t('siteNameDescription')} )} @@ -134,7 +132,7 @@ export default function GeneralPage() { loading={loading} disabled={loading} > - Save General Settings + {t('saveGeneralSettings')} diff --git a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx index 5bcc8af9..f5c98c31 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx @@ -16,6 +16,7 @@ import { BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; import SiteInfoCard from "./SiteInfoCard"; +import { getTranslations } from 'next-intl/server'; interface SettingsLayoutProps { children: React.ReactNode; @@ -38,9 +39,11 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { redirect(`/${params.orgId}/settings/sites`); } + const t = await getTranslations(); + const navItems = [ { - title: "General", + title: t('general'), href: "/{orgId}/settings/sites/{niceId}/general" } ]; @@ -48,8 +51,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <> From 5b44f3552d91d9c77b730e781f67b1f8855d4c93 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:40 +0200 Subject: [PATCH 017/105] New translations en-us.json (German) --- messages/de-DE.json | 49 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 79220877..e618647a 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Läuft im Docker", "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", "siteErrorDelete": "Fehler beim Löschen der Seite", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Links zum Teilen verwalten", "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", "shareSearch": "Freigabe-Links suchen...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "Fehler beim Löschen des Links", "shareDeleted": "Link gelöscht", "shareDeletedDesciption": "Der Link wurde gelöscht", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Menü öffnen", "resource": "Ressource", "title": "Titel", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Bitte wählen Sie eine Ressource", "resourceTitle": "Ressourcen verwalten", "resourceDescription": "Erstellen Sie sichere Proxies für Ihre privaten Anwendungen", - "resourceSearch": "Suche Ressourcen...", + "resourcesSearch": "Search resources...", "resourceAdd": "Ressource hinzufügen", "resourceErrorDelte": "Fehler beim Löschen der Ressource", "authentication": "Authentifizierung", @@ -142,6 +176,7 @@ "enabled": "Aktiviert", "disabled": "Deaktiviert", "general": "Allgemein", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Regeln", "resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation", "orgGeneralSettings": "Organisations-Einstellungen", "orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten", - "orgGeneralSave": "Allgemeine Einstellungen speichern", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Gefahrenzone", "orgDangerZoneDescription": "Sobald Sie diesen Org löschen, gibt es kein Zurück mehr. Bitte seien Sie vorsichtig.", "orgDelete": "Organisation löschen", @@ -186,5 +221,11 @@ "description": "Beschreibung", "inviteTitle": "Einladungen öffnen", "inviteDescription": "Ihre Einladungen an andere Benutzer verwalten", - "inviteSearch": "Einladungen suchen..." -} + "inviteSearch": "Einladungen suchen...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" +} \ No newline at end of file From d9aab7b3ff3ababae87c0658eca3f1edd032c33e Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:41 +0200 Subject: [PATCH 018/105] New translations en-us.json (French) --- messages/fr-FR.json | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 71810b2a..0a302d8e 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Exécute dans Docker", "siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows", "siteErrorDelete": "Erreur lors de la suppression du site", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Gérer les liens de partage", "shareDescription": "Créez des liens partageables pour accorder un accès temporaire ou permanent à vos ressources", "shareSearch": "Rechercher des liens de partage...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "Une erreur s'est produite lors de la suppression du lien", "shareDeleted": "Lien supprimé", "shareDeletedDesciption": "Le lien a été supprimé", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Ouvrir le menu", "resource": "Ressource", "title": "Titre de la page", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Veuillez sélectionner une ressource", "resourceTitle": "Gérer les ressources", "resourceDescription": "Créez des proxy sécurisés pour vos applications privées", - "resourceSearch": "Rechercher des ressources...", + "resourcesSearch": "Search resources...", "resourceAdd": "Ajouter une ressource", "resourceErrorDelte": "Erreur de suppression de la ressource", "authentication": "Authentification", @@ -142,6 +176,7 @@ "enabled": "Activé", "disabled": "Désactivé", "general": "Généraux", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Règles", "resourceSettingDescription": "Configurer les paramètres de votre ressource", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Configurer les paramètres généraux de votre organisation", "orgGeneralSettings": "Paramètres de l'organisation", "orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation", - "orgGeneralSave": "Enregistrer les paramètres généraux", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Zone de danger", "orgDangerZoneDescription": "Une fois que vous supprimez cette organisation, il n'y a pas de retour en arrière. Soyez certain.", "orgDelete": "Supprimer l'organisation", @@ -186,5 +221,11 @@ "description": "Libellé", "inviteTitle": "Invitations ouvertes", "inviteDescription": "Gérer vos invitations à d'autres utilisateurs", - "inviteSearch": "Rechercher des invitations..." + "inviteSearch": "Rechercher des invitations...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file From 6b8fa28308d1282055ad4a2e1cc425704d34ec4b Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:42 +0200 Subject: [PATCH 019/105] New translations en-us.json (Italian) --- messages/it-IT.json | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index c4e8557c..e026448b 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Esegue nel Docker", "siteRunsInShell": "Esegue in shell su macOS, Linux e Windows", "siteErrorDelete": "Errore nell'eliminare il sito", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Gestisci Collegamenti Di Condivisione", "shareDescription": "Crea link condivisibili per concedere un accesso temporaneo o permanente alle tue risorse", "shareSearch": "Cerca link condivisi...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione del link", "shareDeleted": "Link eliminato", "shareDeletedDesciption": "Il link è stato eliminato", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Apri menu", "resource": "Risorsa", "title": "Titolo", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Seleziona una risorsa", "resourceTitle": "Gestisci Risorse", "resourceDescription": "Crea proxy sicuri per le tue applicazioni private", - "resourceSearch": "Cerca risorse...", + "resourcesSearch": "Search resources...", "resourceAdd": "Aggiungi Risorsa", "resourceErrorDelte": "Errore nell'eliminare la risorsa", "authentication": "Autenticazione", @@ -142,6 +176,7 @@ "enabled": "Abilitato", "disabled": "Disabilitato", "general": "Generale", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Regole", "resourceSettingDescription": "Configura le impostazioni sulla tua risorsa", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione", "orgGeneralSettings": "Impostazioni Organizzazione", "orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione", - "orgGeneralSave": "Salva Impostazioni Generali", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Zona Pericolosa", "orgDangerZoneDescription": "Una volta che si elimina questo org, non c'è ritorno. Si prega di essere certi.", "orgDelete": "Elimina Organizzazione", @@ -186,5 +221,11 @@ "description": "Descrizione", "inviteTitle": "Inviti Aperti", "inviteDescription": "Gestisci i tuoi inviti ad altri utenti", - "inviteSearch": "Cerca inviti..." + "inviteSearch": "Cerca inviti...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file From dd24b4ad74e0ee846b9825a40239a6599fd328ae Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:43 +0200 Subject: [PATCH 020/105] New translations en-us.json (Polish) --- messages/pl-PL.json | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 9da108c2..52f09b03 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Uruchamia w Docke'u", "siteRunsInShell": "Uruchamia w skorupce na macOS, Linux i Windows", "siteErrorDelete": "Błąd podczas usuwania witryny", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Zarządzaj linkami udostępniania", "shareDescription": "Utwórz linki, które można udostępnić, aby przyznać tymczasowy lub stały dostęp do Twoich zasobów", "shareSearch": "Szukaj linków udostępnienia...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "Wystąpił błąd podczas usuwania linku", "shareDeleted": "Link usunięty", "shareDeletedDesciption": "Link został usunięty", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Otwórz menu", "resource": "Zasoby", "title": "Rozporządzenie Rady (EWG) nr 2658/87 z dnia 23 lipca 1987 r. w sprawie nomenklatury taryfowej i statystycznej oraz w sprawie Wspólnej Taryfy Celnej (Dz.U. L 256 z 7.9.1987, s. 1).", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Wybierz zasób", "resourceTitle": "Zarządzaj zasobami", "resourceDescription": "Utwórz bezpieczne proxy do prywatnych aplikacji", - "resourceSearch": "Szukaj zasobów...", + "resourcesSearch": "Search resources...", "resourceAdd": "Dodaj zasób", "resourceErrorDelte": "Błąd podczas usuwania zasobu", "authentication": "Uwierzytelnianie", @@ -142,6 +176,7 @@ "enabled": "Włączone", "disabled": "Wyłączone", "general": "Ogólny", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Regulamin", "resourceSettingDescription": "Skonfiguruj ustawienia zasobu", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji", "orgGeneralSettings": "Ustawienia organizacji", "orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją", - "orgGeneralSave": "Zapisz ustawienia ogólne", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Strefa zagrożenia", "orgDangerZoneDescription": "Po usunięciu tego organa nie ma odwrotu. Upewnij się.", "orgDelete": "Usuń organizację", @@ -186,5 +221,11 @@ "description": "Opis", "inviteTitle": "Otwórz zaproszenia", "inviteDescription": "Zarządzaj zaproszeniami dla innych użytkowników", - "inviteSearch": "Szukaj zaproszeń..." + "inviteSearch": "Szukaj zaproszeń...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file From 17789ef1a5504c8954fe6e41f923bf80060d9499 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:44 +0200 Subject: [PATCH 021/105] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 24d4f728..e4568442 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", "siteErrorDelete": "Erro ao excluir site", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Gerenciar links de compartilhamento", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", "shareDeleted": "Link excluído", "shareDeletedDesciption": "O link foi eliminado", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Abrir menu", "resource": "Recurso", "title": "Título", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Por favor, selecione um recurso", "resourceTitle": "Gerenciar Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", - "resourceSearch": "Procurar recursos...", + "resourcesSearch": "Search resources...", "resourceAdd": "Adicionar Recurso", "resourceErrorDelte": "Erro ao excluir recurso", "authentication": "Autenticação", @@ -142,6 +176,7 @@ "enabled": "Ativado", "disabled": "Desabilitado", "general": "Gerais", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Regras", "resourceSettingDescription": "Configure as configurações do seu recurso", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", - "orgGeneralSave": "Salvar configurações gerais", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", @@ -186,5 +221,11 @@ "description": "Descrição:", "inviteTitle": "Convites Abertos", "inviteDescription": "Gerencie seus convites para outros usuários", - "inviteSearch": "Procurar convites..." + "inviteSearch": "Procurar convites...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file From 55222450f38b652606acaf07219f178f13c52194 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 18:18:45 +0200 Subject: [PATCH 022/105] New translations en-us.json (Turkish) --- messages/tr-TR.json | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index c392a417..fd80fb83 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -77,6 +77,15 @@ "siteRunsInDocker": "Runs in Docker", "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", "siteErrorDelete": "Error deleting site", + "siteErrorUpdate": "Failed to update site", + "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteUpdated": "Site updated", + "siteUpdatedDescription": "The site has been updated.", + "siteGeneralDescription": "Configure the general settings for this site", + "siteSettingDescription": "Configure the settings on your site", + "siteSetting": "{siteName} Settings", + "siteInfo": "Site Information", + "status": "Status", "shareTitle": "Manage Share Links", "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", "shareSearch": "Search share links...", @@ -85,6 +94,31 @@ "shareErrorDeleteMessage": "An error occurred deleting link", "shareDeleted": "Link deleted", "shareDeletedDesciption": "The link has been deleted", + "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", + "accessToken": "Access Token", + "usageExamples": "Usage Examples", + "tokenId": "Token ID", + "requestHeades": "Request Headers", + "queryParameter": "Query Parameter", + "importantNote": "Important Note", + "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "token": "Token", + "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", + "shareErrorFetchResource": "Failed to fetch resources", + "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", + "shareErrorCreate": "Failed to create share link", + "shareErrorCreateDescription": "An error occurred while creating the share link", + "shareCreateDescription": "Anyone with this link can access the resource", + "shareTitleOptional": "Title (optional)", + "expireIn": "Expire In", + "neverExpire": "Never expire", + "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", + "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", + "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", + "shareTokenUsage": "See Access Token Usage", + "createLink": "Create Link", + "resourceNotFound": "No resources found", + "resourceSearch": "Search resources", "openMenu": "Open menu", "resource": "Resource", "title": "Title", @@ -94,7 +128,7 @@ "shareErrorSelectResource": "Please select a resource", "resourceTitle": "Manage Resources", "resourceDescription": "Create secure proxies to your private applications", - "resourceSearch": "Search resources...", + "resourcesSearch": "Search resources...", "resourceAdd": "Add Resource", "resourceErrorDelte": "Error deleting resource", "authentication": "Authentication", @@ -142,6 +176,7 @@ "enabled": "Enabled", "disabled": "Disabled", "general": "General", + "generalSettings": "General Settings", "proxy": "Proxy", "rules": "Rules", "resourceSettingDescription": "Configure the settings on your resource", @@ -151,7 +186,7 @@ "orgSettingsDescription": "Configure your organization's general settings", "orgGeneralSettings": "Organization Settings", "orgGeneralSettingsDescription": "Manage your organization details and configuration", - "orgGeneralSave": "Save General Settings", + "saveGeneralSettings": "Save General Settings", "orgDangerZone": "Danger Zone", "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", "orgDelete": "Delete Organization", @@ -186,5 +221,11 @@ "description": "Description", "inviteTitle": "Open Invitations", "inviteDescription": "Manage your invitations to other users", - "inviteSearch": "Search invitations..." + "inviteSearch": "Search invitations...", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "months": "Months", + "years": "Years" } \ No newline at end of file From a058f4acf33eb49654c77dad046f0088ca3f60db Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Mon, 5 May 2025 19:24:14 +0000 Subject: [PATCH 023/105] complete sites i18n --- messages/en-US.json | 22 ++++- .../settings/resources/create/page.tsx | 2 +- .../[orgId]/settings/sites/create/page.tsx | 85 ++++++++----------- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index fd80fb83..3d500de1 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -2,7 +2,6 @@ "setupCreate": "Create your organization, site, and resources", "setupNewOrg": "New Organization", "setupCreateOrg": "Create Organization", - "setupCreateSite": "Create Site", "setupCreateResources": "Create Resources", "setupOrgName": "Organization Name", "orgDisplayName": "This is the display name of your organization.", @@ -41,6 +40,7 @@ "dataIn": "Data In", "dataOut": "Data Out", "connectionType": "Connection Type", + "tunnelType": "Tunnel Type", "local": "Local", "edit": "Edit", "siteConfirmDelete": "Confirm Delete Site", @@ -51,6 +51,7 @@ "siteManageSites": "Manage Sites", "siteDescription": "Allow connectivity to your network through secure tunnels", "siteCreate": "Create Site", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Create a new site to start connecting your resources", "close": "Close", "siteNameMin": "Name must be at least 2 characters.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "I have copied the config", "searchSites": "Search sites...", "siteAdd": "Add Site", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Recommended", "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", "siteRunsInDocker": "Runs in Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Manage Share Links", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Port Number", "resourcePortNumberDescription": "The external port number to proxy requests.", - "cancle": "Cancle", + "cancel": "Cancel", "resourceConfig": "Configuration Snippets", "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", "resourceAddEntrypoints": "Traefik: Add Entrypoints", diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 1b702e89..22ebf1ff 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -791,7 +791,7 @@ export default function Page() { router.push(`/${orgId}/settings/resources`) } > - {t('cancle')} + {t('cancel')}
@@ -539,7 +539,7 @@ WantedBy=default.target` - Site Information + {t('siteInfo')} @@ -555,7 +555,7 @@ WantedBy=default.target` render={({ field }) => ( - Name + {t('name')} - This is the display - name for the site. + {t('siteNameDescription')} )} @@ -580,11 +579,10 @@ WantedBy=default.target` - Tunnel Type + {t('tunnelType')} - Determine how you want to connect to your - site + {t('siteTunnelDescription')} @@ -604,11 +602,10 @@ WantedBy=default.target` - Newt Credentials + {t('siteNewtCredentials')} - This is how Newt will authenticate - with the server + {t('siteNewtCredentialsDescription')} @@ -650,12 +647,10 @@ WantedBy=default.target` - Save Your Credentials + {t('siteCredentialsSave')} - You will only be able to see - this once. Make sure to copy it - to a secure place. + {t('siteCredentialsSaveDescription')} @@ -690,9 +685,7 @@ WantedBy=default.target` htmlFor="terms" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > - I have - copied the - config + {t('siteConfirmCopy')}
@@ -707,16 +700,16 @@ WantedBy=default.target` - Install Newt + {t('siteInstallNewt')} - Get Newt running on your system + {t('siteInstallNewtDescription')}

- Operating System + {t('operatingSystem')}

{platforms.map((os) => ( @@ -772,7 +765,7 @@ WantedBy=default.target`

- Commands + {t('commands')}

- WireGuard Configuration + {t('WgConfiguration')} - Use the following configuration to - connect to your network + {t('WgConfigurationDescription')} @@ -818,12 +810,10 @@ WantedBy=default.target` - Save Your Credentials + {t('siteCredentialsSave')} - You will only be able to see this - once. Make sure to copy it to a - secure place. + {t('siteCredentialsSaveDescription')} @@ -858,8 +848,7 @@ WantedBy=default.target` htmlFor="terms" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > - I have copied - the config + {t('siteConfirmCopy')}
@@ -881,7 +870,7 @@ WantedBy=default.target` router.push(`/${orgId}/settings/sites`); }} > - Cancel + {t('cancel')}
From 3bb4b44f1981dbc5eecf8e449bf7b4e6f36e4a9b Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:35 +0200 Subject: [PATCH 024/105] New translations en-us.json (German) --- messages/de-DE.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index e618647a..c1eb5824 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -2,7 +2,6 @@ "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", "setupNewOrg": "Neue Organisation", "setupCreateOrg": "Organisation erstellen", - "setupCreateSite": "Seite erstellen", "setupCreateResources": "Ressource erstellen", "setupOrgName": "Organisation's Name", "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", @@ -41,6 +40,7 @@ "dataIn": "Daten in", "dataOut": "Daten raus", "connectionType": "Verbindungstyp", + "tunnelType": "Tunnel Type", "local": "Lokal", "edit": "Bearbeiten", "siteConfirmDelete": "Site löschen bestätigen", @@ -51,6 +51,7 @@ "siteManageSites": "Sites verwalten", "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", "siteCreate": "Site erstellen", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", "close": "Schließen", "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "Ich habe die Konfiguration kopiert", "searchSites": "Seiten suchen...", "siteAdd": "Site hinzufügen", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Empfohlen", "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", "siteRunsInDocker": "Läuft im Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Links zum Teilen verwalten", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Portnummer", "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", - "cancle": "Abbrechen", + "cancel": "Cancel", "resourceConfig": "Konfiguration Snippets", "resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um Ihre TCP/UDP Ressource einzurichten", "resourceAddEntrypoints": "Traefik: Einstiegspunkte hinzufügen", From 08bd3cfd0b5f6a3105475a331c8c64d50c31beec Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:38 +0200 Subject: [PATCH 025/105] New translations en-us.json (French) --- messages/fr-FR.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 0a302d8e..86a9b924 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -2,7 +2,6 @@ "setupCreate": "Créez votre organisation, votre site et vos ressources", "setupNewOrg": "Nouvelle organisation", "setupCreateOrg": "Créer une organisation", - "setupCreateSite": "Créer un site", "setupCreateResources": "Créer des ressources", "setupOrgName": "Nom de l'organisation", "orgDisplayName": "Ceci est le nom d'affichage de votre organisation.", @@ -41,6 +40,7 @@ "dataIn": "Données dans", "dataOut": "Données épuisées", "connectionType": "Type de connexion", + "tunnelType": "Tunnel Type", "local": "Locale", "edit": "Editer", "siteConfirmDelete": "Confirmer la suppression du site", @@ -51,6 +51,7 @@ "siteManageSites": "Gérer les sites", "siteDescription": "Autoriser la connectivité à votre réseau via des tunnels sécurisés", "siteCreate": "Créer un site", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Créez un nouveau site pour commencer à connecter vos ressources", "close": "Fermer", "siteNameMin": "Le nom doit comporter au moins 2 caractères.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "J'ai copié la configuration", "searchSites": "Rechercher des sites...", "siteAdd": "Ajouter un site", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Recommandé", "siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.", "siteRunsInDocker": "Exécute dans Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Gérer les liens de partage", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numéro de port", "resourcePortNumberDescription": "Le numéro de port externe pour les requêtes de proxy.", - "cancle": "Annuler", + "cancel": "Cancel", "resourceConfig": "Snippets de configuration", "resourceConfigDescription": "Copiez et collez ces modules de configuration pour configurer votre ressource TCP/UDP", "resourceAddEntrypoints": "Traefik: Ajouter des points d’entrée", From 938cc31b8ad14c0078619c27b99705766720cb26 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:39 +0200 Subject: [PATCH 026/105] New translations en-us.json (Italian) --- messages/it-IT.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index e026448b..a14552a6 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -2,7 +2,6 @@ "setupCreate": "Crea la tua organizzazione, sito e risorse", "setupNewOrg": "Nuova Organizzazione", "setupCreateOrg": "Crea Organizzazione", - "setupCreateSite": "Crea Sito", "setupCreateResources": "Crea Risorse", "setupOrgName": "Nome Dell'Organizzazione", "orgDisplayName": "Questo è il nome visualizzato della tua organizzazione.", @@ -41,6 +40,7 @@ "dataIn": "Dati In", "dataOut": "Dati Fuori", "connectionType": "Tipo Di Connessione", + "tunnelType": "Tunnel Type", "local": "Locale", "edit": "Modifica", "siteConfirmDelete": "Conferma Eliminazione Sito", @@ -51,6 +51,7 @@ "siteManageSites": "Gestisci Siti", "siteDescription": "Consenti la connettività alla rete attraverso tunnel sicuri", "siteCreate": "Crea Sito", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Crea un nuovo sito per iniziare a connettere le tue risorse", "close": "Chiudi", "siteNameMin": "Il nome deve contenere almeno 2 caratteri.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "Ho copiato la configurazione", "searchSites": "Cerca siti...", "siteAdd": "Aggiungi Sito", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Consigliato", "siteNewtDescription": "Per la migliore esperienza utente, utilizzare Newt. Utilizza WireGuard sotto il cofano e ti permette di indirizzare le tue risorse private tramite il loro indirizzo LAN sulla tua rete privata dall'interno della dashboard Pangolin.", "siteRunsInDocker": "Esegue nel Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Gestisci Collegamenti Di Condivisione", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numero Porta", "resourcePortNumberDescription": "Il numero di porta esterna per le richieste di proxy.", - "cancle": "Annullare", + "cancel": "Cancel", "resourceConfig": "Snippet Di Configurazione", "resourceConfigDescription": "Copia e incolla questi snippet di configurazione per configurare la tua risorsa TCP/UDP", "resourceAddEntrypoints": "Traefik: Aggiungi Ingresso", From 8df01208e04966c96dfca539f99b3f3cb61048a6 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:40 +0200 Subject: [PATCH 027/105] New translations en-us.json (Polish) --- messages/pl-PL.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 52f09b03..ce312285 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -2,7 +2,6 @@ "setupCreate": "Utwórz swoją organizację, witrynę i zasoby", "setupNewOrg": "Nowa organizacja", "setupCreateOrg": "Utwórz organizację", - "setupCreateSite": "Utwórz witrynę", "setupCreateResources": "Utwórz Zasoby", "setupOrgName": "Nazwa organizacji", "orgDisplayName": "To jest wyświetlana nazwa Twojej organizacji.", @@ -41,6 +40,7 @@ "dataIn": "Dane w", "dataOut": "Dane niedostępne", "connectionType": "Typ połączenia", + "tunnelType": "Tunnel Type", "local": "Lokalny", "edit": "Edytuj", "siteConfirmDelete": "Potwierdź usunięcie witryny", @@ -51,6 +51,7 @@ "siteManageSites": "Zarządzaj stronami", "siteDescription": "Zezwalaj na połączenie z siecią przez bezpieczne tunele", "siteCreate": "Utwórz witrynę", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Utwórz nową witrynę, aby rozpocząć łączenie zasobów", "close": "Zamknij", "siteNameMin": "Nazwa musi mieć co najmniej 2 znaki.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "Skopiowałem konfigurację", "searchSites": "Szukaj witryn...", "siteAdd": "Dodaj witrynę", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Rekomendowane", "siteNewtDescription": "Aby uzyskać najlepsze doświadczenia użytkownika, użyj Newt. Używa WireGuard pod zapleczem i pozwala na przekierowanie twoich prywatnych zasobów przez ich adres LAN w sieci prywatnej z panelu Pangolin.", "siteRunsInDocker": "Uruchamia w Docke'u", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Zarządzaj linkami udostępniania", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numer portu", "resourcePortNumberDescription": "Numer portu zewnętrznego do żądań proxy.", - "cancle": "Anuluj", + "cancel": "Cancel", "resourceConfig": "Snippety konfiguracji", "resourceConfigDescription": "Skopiuj i wklej te fragmenty konfiguracji, aby skonfigurować swój zasób TCP/UDP", "resourceAddEntrypoints": "Traefik: Dodaj punkty wejścia", From 4e02a7712a33da65003d8ccdec16527cbadcb1e9 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:41 +0200 Subject: [PATCH 028/105] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index e4568442..c82ecfe2 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -2,7 +2,6 @@ "setupCreate": "Crie sua organização, site e recursos", "setupNewOrg": "Nova organização", "setupCreateOrg": "Criar Organização", - "setupCreateSite": "Criar site", "setupCreateResources": "Criar recursos", "setupOrgName": "Nome Da Organização", "orgDisplayName": "Este é o nome de exibição da sua organização.", @@ -41,6 +40,7 @@ "dataIn": "Dados em", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", + "tunnelType": "Tunnel Type", "local": "Localização", "edit": "Alterar", "siteConfirmDelete": "Confirmar exclusão do site", @@ -51,6 +51,7 @@ "siteManageSites": "Gerenciar sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Crie um novo site para começar a conectar seus recursos", "close": "FECHAR", "siteNameMin": "O nome deve ter pelo menos 2 caracteres.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "Eu copiei a configuração", "searchSites": "Procurar sites...", "siteAdd": "Adicionar Site", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Recomendados", "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Gerenciar links de compartilhamento", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Número da Porta", "resourcePortNumberDescription": "O número da porta externa para requisições de proxy.", - "cancle": "Cancelar", + "cancel": "Cancel", "resourceConfig": "Snippets de Configuração", "resourceConfigDescription": "Copie e cole estes snippets de configuração para configurar o seu recurso TCP/UDP", "resourceAddEntrypoints": "Traefik: Adicionar pontos de entrada", From bb0c1c839bc0d6413ddaf1d1d170a034d8dd1110 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:26:42 +0200 Subject: [PATCH 029/105] New translations en-us.json (Turkish) --- messages/tr-TR.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index fd80fb83..3d500de1 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -2,7 +2,6 @@ "setupCreate": "Create your organization, site, and resources", "setupNewOrg": "New Organization", "setupCreateOrg": "Create Organization", - "setupCreateSite": "Create Site", "setupCreateResources": "Create Resources", "setupOrgName": "Organization Name", "orgDisplayName": "This is the display name of your organization.", @@ -41,6 +40,7 @@ "dataIn": "Data In", "dataOut": "Data Out", "connectionType": "Connection Type", + "tunnelType": "Tunnel Type", "local": "Local", "edit": "Edit", "siteConfirmDelete": "Confirm Delete Site", @@ -51,6 +51,7 @@ "siteManageSites": "Manage Sites", "siteDescription": "Allow connectivity to your network through secure tunnels", "siteCreate": "Create Site", + "siteCreateDescription2": "Follow the steps below to create and connect a new site", "siteCreateDescription": "Create a new site to start connecting your resources", "close": "Close", "siteNameMin": "Name must be at least 2 characters.", @@ -72,6 +73,12 @@ "siteConfirmCopy": "I have copied the config", "searchSites": "Search sites...", "siteAdd": "Add Site", + "siteInstallNewt": "Install Newt", + "siteInstallNewtDescription": "Get Newt running on your system", + "WgConfiguration": "WireGuard Configuration", + "WgConfigurationDescription": "Use the following configuration to connect to your network", + "operatingSystem": "Operating System", + "commands": "Commands", "recommended": "Recommended", "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", "siteRunsInDocker": "Runs in Docker", @@ -84,6 +91,17 @@ "siteGeneralDescription": "Configure the general settings for this site", "siteSettingDescription": "Configure the settings on your site", "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "Newt Tunnel (Recommended)", + "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteLocalDescription": "Local resources only. No tunneling.", + "siteSeeAll": "See All Sites", + "siteTunnelDescription": "Determine how you want to connect to your site", + "siteNewtCredentials": "Newt Credentials", + "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", + "siteCredentialsSave": "Save Your Credentials", + "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", "siteInfo": "Site Information", "status": "Status", "shareTitle": "Manage Share Links", @@ -164,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Port Number", "resourcePortNumberDescription": "The external port number to proxy requests.", - "cancle": "Cancle", + "cancel": "Cancel", "resourceConfig": "Configuration Snippets", "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", "resourceAddEntrypoints": "Traefik: Add Entrypoints", From 0b235f985f441f9a3c58b762f18d5d222b06f198 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:33:37 +0200 Subject: [PATCH 030/105] New translations en-us.json (German) --- messages/de-DE.json | 120 ++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index c1eb5824..aeba3d65 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -40,7 +40,7 @@ "dataIn": "Daten in", "dataOut": "Daten raus", "connectionType": "Verbindungstyp", - "tunnelType": "Tunnel Type", + "tunnelType": "Tunneltyp", "local": "Lokal", "edit": "Bearbeiten", "siteConfirmDelete": "Site löschen bestätigen", @@ -51,7 +51,7 @@ "siteManageSites": "Sites verwalten", "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", "siteCreate": "Site erstellen", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", + "siteCreateDescription2": "Folgen Sie den Schritten unten, um eine neue Seite zu erstellen und zu verbinden", "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", "close": "Schließen", "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", @@ -73,36 +73,36 @@ "siteConfirmCopy": "Ich habe die Konfiguration kopiert", "searchSites": "Seiten suchen...", "siteAdd": "Site hinzufügen", - "siteInstallNewt": "Install Newt", - "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", + "siteInstallNewt": "Neustart installieren", + "siteInstallNewtDescription": "Lass Newt auf deinem System laufen", + "WgConfiguration": "WireGuard Konfiguration", + "WgConfigurationDescription": "Verwenden Sie folgende Konfiguration, um sich mit Ihrem Netzwerk zu verbinden", + "operatingSystem": "Betriebssystem", + "commands": "Befehle", "recommended": "Empfohlen", "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", "siteRunsInDocker": "Läuft im Docker", "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", "siteErrorDelete": "Fehler beim Löschen der Seite", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", - "siteUpdated": "Site updated", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", + "siteErrorUpdate": "Fehler beim Aktualisieren der Seite", + "siteErrorUpdateDescription": "Beim Aktualisieren der Seite ist ein Fehler aufgetreten.", + "siteUpdated": "Site aktualisiert", + "siteUpdatedDescription": "Die Seite wurde aktualisiert.", + "siteGeneralDescription": "Allgemeine Einstellungen für diese Seite konfigurieren", + "siteSettingDescription": "Konfigurieren Sie die Einstellungen auf Ihrer Seite", + "siteSetting": "{siteName} Einstellungen", + "siteNewtTunnel": "Newt-Tunnel (empfohlen)", + "siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in Ihr Netzwerk zu erstellen. Keine zusätzliche Einrichtung.", + "siteWg": "Einfacher WireGuard", + "siteWgDescription": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu errichten. Manuelle NAT-Setup erforderlich.", + "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", + "siteSeeAll": "Alle Seiten anzeigen", + "siteTunnelDescription": "Legen Sie fest, wie Sie sich mit Ihrer Website verbinden möchten", + "siteNewtCredentials": "Anmeldedaten neu", + "siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren", + "siteCredentialsSave": "Ihre Zugangsdaten speichern", + "siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.", + "siteInfo": "Site-Informationen", "status": "Status", "shareTitle": "Links zum Teilen verwalten", "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", @@ -112,31 +112,31 @@ "shareErrorDeleteMessage": "Fehler beim Löschen des Links", "shareDeleted": "Link gelöscht", "shareDeletedDesciption": "Der Link wurde gelöscht", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", + "shareTokenDescription": "Ihr Zugriffstoken kann auf zwei Arten übergeben werden: als Abfrageparameter oder in den Anfrage-Headern. Diese müssen vom Client auf jeder Anfrage für authentifizierten Zugriff weitergegeben werden.", + "accessToken": "Zugangs-Token", + "usageExamples": "Nutzungsbeispiele", "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "requestHeades": "Anfrage-Header", + "queryParameter": "Abfrageparameter", + "importantNote": "Wichtige Notiz", + "shareImportantDescription": "Aus Sicherheitsgründen wird die Verwendung von Headern über Abfrageparameter empfohlen, wenn möglich, da Abfrageparameter in Server-Logs oder Browserverlauf protokolliert werden können.", "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourceNotFound": "No resources found", - "resourceSearch": "Search resources", + "shareTokenSecurety": "Halten Sie Ihr Zugangs-Token sicher. Teilen Sie es nicht in öffentlich zugänglichen Bereichen oder Client-seitigem Code.", + "shareErrorFetchResource": "Fehler beim Abrufen der Ressourcen", + "shareErrorFetchResourceDescription": "Beim Abrufen der Ressourcen ist ein Fehler aufgetreten", + "shareErrorCreate": "Fehler beim Erstellen des Teilen-Links", + "shareErrorCreateDescription": "Beim Erstellen des Teilen-Links ist ein Fehler aufgetreten", + "shareCreateDescription": "Jeder mit diesem Link kann auf die Ressource zugreifen", + "shareTitleOptional": "Titel (optional)", + "expireIn": "Verfällt in", + "neverExpire": "Nie ablaufen", + "shareExpireDescription": "Ablaufzeit ist, wie lange der Link verwendet werden kann und bietet Zugriff auf die Ressource. Nach dieser Zeit wird der Link nicht mehr funktionieren und Benutzer, die diesen Link benutzt haben, verlieren den Zugriff auf die Ressource.", + "shareSeeOnce": "Sie können diese Linie nur sehen. Bitte kopieren Sie sie.", + "shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.", + "shareTokenUsage": "Zugriffstoken-Nutzung anzeigen", + "createLink": "Link erstellen", + "resourceNotFound": "Keine Ressourcen gefunden", + "resourceSearch": "Suche Ressourcen", "openMenu": "Menü öffnen", "resource": "Ressource", "title": "Titel", @@ -146,7 +146,7 @@ "shareErrorSelectResource": "Bitte wählen Sie eine Ressource", "resourceTitle": "Ressourcen verwalten", "resourceDescription": "Erstellen Sie sichere Proxies für Ihre privaten Anwendungen", - "resourcesSearch": "Search resources...", + "resourcesSearch": "Suche Ressourcen...", "resourceAdd": "Ressource hinzufügen", "resourceErrorDelte": "Fehler beim Löschen der Ressource", "authentication": "Authentifizierung", @@ -182,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Portnummer", "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", - "cancel": "Cancel", + "cancel": "Abbrechen", "resourceConfig": "Konfiguration Snippets", "resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um Ihre TCP/UDP Ressource einzurichten", "resourceAddEntrypoints": "Traefik: Einstiegspunkte hinzufügen", @@ -194,7 +194,7 @@ "enabled": "Aktiviert", "disabled": "Deaktiviert", "general": "Allgemein", - "generalSettings": "General Settings", + "generalSettings": "Allgemeine Einstellungen", "proxy": "Proxy", "rules": "Regeln", "resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource", @@ -204,7 +204,7 @@ "orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation", "orgGeneralSettings": "Organisations-Einstellungen", "orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten", - "saveGeneralSettings": "Save General Settings", + "saveGeneralSettings": "Allgemeine Einstellungen speichern", "orgDangerZone": "Gefahrenzone", "orgDangerZoneDescription": "Sobald Sie diesen Org löschen, gibt es kein Zurück mehr. Bitte seien Sie vorsichtig.", "orgDelete": "Organisation löschen", @@ -240,10 +240,10 @@ "inviteTitle": "Einladungen öffnen", "inviteDescription": "Ihre Einladungen an andere Benutzer verwalten", "inviteSearch": "Einladungen suchen...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years" + "minutes": "Minuten", + "hours": "Stunden", + "days": "Tage", + "weeks": "Wochen", + "months": "Monate", + "years": "Jahre" } \ No newline at end of file From f62f2e3b085dac03025033f1ba19d377d82c3d1e Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:33:38 +0200 Subject: [PATCH 031/105] New translations en-us.json (French) --- messages/fr-FR.json | 122 ++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 86a9b924..e11fa66f 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -40,7 +40,7 @@ "dataIn": "Données dans", "dataOut": "Données épuisées", "connectionType": "Type de connexion", - "tunnelType": "Tunnel Type", + "tunnelType": "Type de tunnel", "local": "Locale", "edit": "Editer", "siteConfirmDelete": "Confirmer la suppression du site", @@ -51,7 +51,7 @@ "siteManageSites": "Gérer les sites", "siteDescription": "Autoriser la connectivité à votre réseau via des tunnels sécurisés", "siteCreate": "Créer un site", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", + "siteCreateDescription2": "Suivez les étapes ci-dessous pour créer et connecter un nouveau site", "siteCreateDescription": "Créez un nouveau site pour commencer à connecter vos ressources", "close": "Fermer", "siteNameMin": "Le nom doit comporter au moins 2 caractères.", @@ -73,37 +73,37 @@ "siteConfirmCopy": "J'ai copié la configuration", "searchSites": "Rechercher des sites...", "siteAdd": "Ajouter un site", - "siteInstallNewt": "Install Newt", - "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", + "siteInstallNewt": "Installer Newt", + "siteInstallNewtDescription": "Faites fonctionner Newt sur votre système", + "WgConfiguration": "Configuration WireGuard", + "WgConfigurationDescription": "Utilisez la configuration suivante pour vous connecter à votre réseau", + "operatingSystem": "Système d'exploitation", + "commands": "Commandes", "recommended": "Recommandé", "siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.", "siteRunsInDocker": "Exécute dans Docker", "siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows", "siteErrorDelete": "Erreur lors de la suppression du site", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", - "siteUpdated": "Site updated", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", - "status": "Status", + "siteErrorUpdate": "Impossible de mettre à jour le site", + "siteErrorUpdateDescription": "Une erreur s'est produite lors de la mise à jour du site.", + "siteUpdated": "Site mis à jour", + "siteUpdatedDescription": "Le site a été mis à jour.", + "siteGeneralDescription": "Configurer les paramètres généraux de ce site", + "siteSettingDescription": "Configurer les paramètres de votre site", + "siteSetting": "Réglages {siteName}", + "siteNewtTunnel": "Tunnel Newt (Recommandé)", + "siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.", + "siteWg": "WireGuard basique", + "siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.", + "siteLocalDescription": "Ressources locales seulement. Pas de tunneling.", + "siteSeeAll": "Voir tous les sites", + "siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site", + "siteNewtCredentials": "Identifiants Newt", + "siteNewtCredentialsDescription": "C'est ainsi que Newt s'authentifiera avec le serveur", + "siteCredentialsSave": "Enregistrez vos identifiants", + "siteCredentialsSaveDescription": "Vous ne pourrez voir cela qu'une seule fois. Assurez-vous de le copier dans un endroit sécurisé.", + "siteInfo": "Informations sur le site", + "status": "Statut", "shareTitle": "Gérer les liens de partage", "shareDescription": "Créez des liens partageables pour accorder un accès temporaire ou permanent à vos ressources", "shareSearch": "Rechercher des liens de partage...", @@ -112,31 +112,31 @@ "shareErrorDeleteMessage": "Une erreur s'est produite lors de la suppression du lien", "shareDeleted": "Lien supprimé", "shareDeletedDesciption": "Le lien a été supprimé", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", + "shareTokenDescription": "Votre jeton d'accès peut être passé de deux façons : en tant que paramètre de requête ou dans les en-têtes de la requête. Elles doivent être transmises par le client à chaque demande d'accès authentifié.", + "accessToken": "Jeton d'accès", + "usageExamples": "Exemples d'utilisation", "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", - "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourceNotFound": "No resources found", - "resourceSearch": "Search resources", + "requestHeades": "En-têtes de la requête", + "queryParameter": "Paramètre de requête", + "importantNote": "Note importante", + "shareImportantDescription": "Pour des raisons de sécurité, l'utilisation des en-têtes est recommandée par rapport aux paramètres de la requête, dans la mesure du possible, car les paramètres de requête peuvent être enregistrés dans les journaux du serveur ou dans l'historique du navigateur.", + "token": "Jeton", + "shareTokenSecurety": "Gardez votre jeton d'accès sécurisé. Ne le partagez pas dans des zones accessibles au public ou dans du code côté client.", + "shareErrorFetchResource": "Impossible de récupérer les ressources", + "shareErrorFetchResourceDescription": "Une erreur est survenue lors de la récupération des ressources", + "shareErrorCreate": "Impossible de créer le lien de partage", + "shareErrorCreateDescription": "Une erreur s'est produite lors de la création du lien de partage", + "shareCreateDescription": "N'importe qui avec ce lien peut accéder à la ressource", + "shareTitleOptional": "Titre (facultatif)", + "expireIn": "Expire dans", + "neverExpire": "N'expire jamais", + "shareExpireDescription": "Le temps d'expiration est combien de temps le lien sera utilisable et fournira un accès à la ressource. Après cette période, le lien ne fonctionnera plus et les utilisateurs qui ont utilisé ce lien perdront l'accès à la ressource.", + "shareSeeOnce": "Vous ne pourrez voir ce lien. Assurez-vous de le copier.", + "shareAccessHint": "N'importe qui avec ce lien peut accéder à la ressource. Partagez-le avec soin.", + "shareTokenUsage": "Voir Utilisation du jeton d'accès", + "createLink": "Créer un lien", + "resourceNotFound": "Aucune ressource trouvée", + "resourceSearch": "Rechercher des ressources", "openMenu": "Ouvrir le menu", "resource": "Ressource", "title": "Titre de la page", @@ -146,7 +146,7 @@ "shareErrorSelectResource": "Veuillez sélectionner une ressource", "resourceTitle": "Gérer les ressources", "resourceDescription": "Créez des proxy sécurisés pour vos applications privées", - "resourcesSearch": "Search resources...", + "resourcesSearch": "Rechercher des ressources...", "resourceAdd": "Ajouter une ressource", "resourceErrorDelte": "Erreur de suppression de la ressource", "authentication": "Authentification", @@ -182,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numéro de port", "resourcePortNumberDescription": "Le numéro de port externe pour les requêtes de proxy.", - "cancel": "Cancel", + "cancel": "Abandonner", "resourceConfig": "Snippets de configuration", "resourceConfigDescription": "Copiez et collez ces modules de configuration pour configurer votre ressource TCP/UDP", "resourceAddEntrypoints": "Traefik: Ajouter des points d’entrée", @@ -194,7 +194,7 @@ "enabled": "Activé", "disabled": "Désactivé", "general": "Généraux", - "generalSettings": "General Settings", + "generalSettings": "Paramètres généraux", "proxy": "Proxy", "rules": "Règles", "resourceSettingDescription": "Configurer les paramètres de votre ressource", @@ -204,7 +204,7 @@ "orgSettingsDescription": "Configurer les paramètres généraux de votre organisation", "orgGeneralSettings": "Paramètres de l'organisation", "orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation", - "saveGeneralSettings": "Save General Settings", + "saveGeneralSettings": "Enregistrer les paramètres généraux", "orgDangerZone": "Zone de danger", "orgDangerZoneDescription": "Une fois que vous supprimez cette organisation, il n'y a pas de retour en arrière. Soyez certain.", "orgDelete": "Supprimer l'organisation", @@ -241,9 +241,9 @@ "inviteDescription": "Gérer vos invitations à d'autres utilisateurs", "inviteSearch": "Rechercher des invitations...", "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years" + "hours": "Heures", + "days": "Jours", + "weeks": "Semaines", + "months": "Mois", + "years": "Années" } \ No newline at end of file From dde2f45669971d7427d4498b7f248c788ade6df3 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:33:39 +0200 Subject: [PATCH 032/105] New translations en-us.json (Italian) --- messages/it-IT.json | 120 ++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index a14552a6..39483529 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -40,7 +40,7 @@ "dataIn": "Dati In", "dataOut": "Dati Fuori", "connectionType": "Tipo Di Connessione", - "tunnelType": "Tunnel Type", + "tunnelType": "Tipo Di Tunnel", "local": "Locale", "edit": "Modifica", "siteConfirmDelete": "Conferma Eliminazione Sito", @@ -51,7 +51,7 @@ "siteManageSites": "Gestisci Siti", "siteDescription": "Consenti la connettività alla rete attraverso tunnel sicuri", "siteCreate": "Crea Sito", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", + "siteCreateDescription2": "Segui i passaggi qui sotto per creare e collegare un nuovo sito", "siteCreateDescription": "Crea un nuovo sito per iniziare a connettere le tue risorse", "close": "Chiudi", "siteNameMin": "Il nome deve contenere almeno 2 caratteri.", @@ -73,37 +73,37 @@ "siteConfirmCopy": "Ho copiato la configurazione", "searchSites": "Cerca siti...", "siteAdd": "Aggiungi Sito", - "siteInstallNewt": "Install Newt", + "siteInstallNewt": "Installa Newt", "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", + "WgConfiguration": "Configurazione WireGuard", + "WgConfigurationDescription": "Usa la seguente configurazione per connetterti alla tua rete", + "operatingSystem": "Sistema Operativo", + "commands": "Comandi", "recommended": "Consigliato", "siteNewtDescription": "Per la migliore esperienza utente, utilizzare Newt. Utilizza WireGuard sotto il cofano e ti permette di indirizzare le tue risorse private tramite il loro indirizzo LAN sulla tua rete privata dall'interno della dashboard Pangolin.", "siteRunsInDocker": "Esegue nel Docker", "siteRunsInShell": "Esegue in shell su macOS, Linux e Windows", "siteErrorDelete": "Errore nell'eliminare il sito", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", - "siteUpdated": "Site updated", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", - "status": "Status", + "siteErrorUpdate": "Impossibile aggiornare il sito", + "siteErrorUpdateDescription": "Si è verificato un errore durante l'aggiornamento del sito.", + "siteUpdated": "Sito aggiornato", + "siteUpdatedDescription": "Il sito è stato aggiornato.", + "siteGeneralDescription": "Configura le impostazioni generali per questo sito", + "siteSettingDescription": "Configura le impostazioni sul tuo sito", + "siteSetting": "Impostazioni {siteName}", + "siteNewtTunnel": "Tunnel Newt (Consigliato)", + "siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint nella rete. Nessuna configurazione aggiuntiva.", + "siteWg": "WireGuard Base", + "siteWgDescription": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta.", + "siteLocalDescription": "Solo risorse locali. Nessun tunneling.", + "siteSeeAll": "Vedi Tutti I Siti", + "siteTunnelDescription": "Determina come vuoi connetterti al tuo sito", + "siteNewtCredentials": "Credenziali Newt", + "siteNewtCredentialsDescription": "Questo è come Newt si autenticerà con il server", + "siteCredentialsSave": "Salva Le Tue Credenziali", + "siteCredentialsSaveDescription": "Potrai vederlo solo una volta. Assicurati di copiarlo in un luogo sicuro.", + "siteInfo": "Informazioni Sito", + "status": "Stato", "shareTitle": "Gestisci Collegamenti Di Condivisione", "shareDescription": "Crea link condivisibili per concedere un accesso temporaneo o permanente alle tue risorse", "shareSearch": "Cerca link condivisi...", @@ -112,31 +112,31 @@ "shareErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione del link", "shareDeleted": "Link eliminato", "shareDeletedDesciption": "Il link è stato eliminato", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", + "shareTokenDescription": "Il token di accesso può essere passato in due modi: come parametro di interrogazione o nelle intestazioni della richiesta. Questi devono essere passati dal client su ogni richiesta di accesso autenticato.", + "accessToken": "Token Di Accesso", + "usageExamples": "Esempi Di Utilizzo", "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "requestHeades": "Richiedi Intestazioni", + "queryParameter": "Parametro Query", + "importantNote": "Nota Importante", + "shareImportantDescription": "Per motivi di sicurezza, si consiglia di utilizzare le intestazioni su parametri di query quando possibile, in quanto i parametri di query possono essere registrati in log server o cronologia browser.", "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourceNotFound": "No resources found", - "resourceSearch": "Search resources", + "shareTokenSecurety": "Mantieni sicuro il tuo token di accesso. Non condividerlo in aree accessibili al pubblico o codice lato client.", + "shareErrorFetchResource": "Recupero delle risorse non riuscito", + "shareErrorFetchResourceDescription": "Si è verificato un errore durante il recupero delle risorse", + "shareErrorCreate": "Impossibile creare il link di condivisione", + "shareErrorCreateDescription": "Si è verificato un errore durante la creazione del link di condivisione", + "shareCreateDescription": "Chiunque con questo link può accedere alla risorsa", + "shareTitleOptional": "Titolo (facoltativo)", + "expireIn": "Scadenza In", + "neverExpire": "Mai scadere", + "shareExpireDescription": "Il tempo di scadenza è per quanto tempo il link sarà utilizzabile e fornirà accesso alla risorsa. Dopo questo tempo, il link non funzionerà più e gli utenti che hanno utilizzato questo link perderanno l'accesso alla risorsa.", + "shareSeeOnce": "Potrai vedere solo questo linkonce. Assicurati di copiarlo.", + "shareAccessHint": "Chiunque abbia questo link può accedere alla risorsa. Condividilo con cura.", + "shareTokenUsage": "Vedi Utilizzo Token Di Accesso", + "createLink": "Crea Collegamento", + "resourceNotFound": "Nessuna risorsa trovata", + "resourceSearch": "Cerca risorse", "openMenu": "Apri menu", "resource": "Risorsa", "title": "Titolo", @@ -146,7 +146,7 @@ "shareErrorSelectResource": "Seleziona una risorsa", "resourceTitle": "Gestisci Risorse", "resourceDescription": "Crea proxy sicuri per le tue applicazioni private", - "resourcesSearch": "Search resources...", + "resourcesSearch": "Cerca risorse...", "resourceAdd": "Aggiungi Risorsa", "resourceErrorDelte": "Errore nell'eliminare la risorsa", "authentication": "Autenticazione", @@ -182,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numero Porta", "resourcePortNumberDescription": "Il numero di porta esterna per le richieste di proxy.", - "cancel": "Cancel", + "cancel": "Annulla", "resourceConfig": "Snippet Di Configurazione", "resourceConfigDescription": "Copia e incolla questi snippet di configurazione per configurare la tua risorsa TCP/UDP", "resourceAddEntrypoints": "Traefik: Aggiungi Ingresso", @@ -194,7 +194,7 @@ "enabled": "Abilitato", "disabled": "Disabilitato", "general": "Generale", - "generalSettings": "General Settings", + "generalSettings": "Impostazioni Generali", "proxy": "Proxy", "rules": "Regole", "resourceSettingDescription": "Configura le impostazioni sulla tua risorsa", @@ -204,7 +204,7 @@ "orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione", "orgGeneralSettings": "Impostazioni Organizzazione", "orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione", - "saveGeneralSettings": "Save General Settings", + "saveGeneralSettings": "Salva Impostazioni Generali", "orgDangerZone": "Zona Pericolosa", "orgDangerZoneDescription": "Una volta che si elimina questo org, non c'è ritorno. Si prega di essere certi.", "orgDelete": "Elimina Organizzazione", @@ -240,10 +240,10 @@ "inviteTitle": "Inviti Aperti", "inviteDescription": "Gestisci i tuoi inviti ad altri utenti", "inviteSearch": "Cerca inviti...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years" + "minutes": "Minuti", + "hours": "Ore", + "days": "Giorni", + "weeks": "Settimane", + "months": "Mesi", + "years": "Anni" } \ No newline at end of file From bc050097c3dec051254f045d2c134053a2f6d85c Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:33:41 +0200 Subject: [PATCH 033/105] New translations en-us.json (Polish) --- messages/pl-PL.json | 120 ++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index ce312285..8ca316e0 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -40,7 +40,7 @@ "dataIn": "Dane w", "dataOut": "Dane niedostępne", "connectionType": "Typ połączenia", - "tunnelType": "Tunnel Type", + "tunnelType": "Typ tunelu", "local": "Lokalny", "edit": "Edytuj", "siteConfirmDelete": "Potwierdź usunięcie witryny", @@ -51,7 +51,7 @@ "siteManageSites": "Zarządzaj stronami", "siteDescription": "Zezwalaj na połączenie z siecią przez bezpieczne tunele", "siteCreate": "Utwórz witrynę", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", + "siteCreateDescription2": "Wykonaj poniższe kroki, aby utworzyć i połączyć nową witrynę", "siteCreateDescription": "Utwórz nową witrynę, aby rozpocząć łączenie zasobów", "close": "Zamknij", "siteNameMin": "Nazwa musi mieć co najmniej 2 znaki.", @@ -73,36 +73,36 @@ "siteConfirmCopy": "Skopiowałem konfigurację", "searchSites": "Szukaj witryn...", "siteAdd": "Dodaj witrynę", - "siteInstallNewt": "Install Newt", - "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", + "siteInstallNewt": "Zainstaluj Newt", + "siteInstallNewtDescription": "Uruchom Newt w swoim systemie", + "WgConfiguration": "Konfiguracja WireGuard", + "WgConfigurationDescription": "Użyj następującej konfiguracji, aby połączyć się z siecią", + "operatingSystem": "System operacyjny", + "commands": "Polecenia", "recommended": "Rekomendowane", "siteNewtDescription": "Aby uzyskać najlepsze doświadczenia użytkownika, użyj Newt. Używa WireGuard pod zapleczem i pozwala na przekierowanie twoich prywatnych zasobów przez ich adres LAN w sieci prywatnej z panelu Pangolin.", "siteRunsInDocker": "Uruchamia w Docke'u", "siteRunsInShell": "Uruchamia w skorupce na macOS, Linux i Windows", "siteErrorDelete": "Błąd podczas usuwania witryny", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", - "siteUpdated": "Site updated", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", + "siteErrorUpdate": "Nie udało się zaktualizować witryny", + "siteErrorUpdateDescription": "Wystąpił błąd podczas aktualizacji witryny.", + "siteUpdated": "Strona zaktualizowana", + "siteUpdatedDescription": "Strona została zaktualizowana.", + "siteGeneralDescription": "Skonfiguruj ustawienia ogólne dla tej witryny", + "siteSettingDescription": "Skonfiguruj ustawienia na swojej stronie", + "siteSetting": "Ustawienia {siteName}", + "siteNewtTunnel": "Newt Tunnel (Zalecane)", + "siteNewtTunnelDescription": "Łatwiejszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.", + "siteWg": "Podstawowy WireGuard", + "siteWgDescription": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana jest ręczna konfiguracja NAT.", + "siteLocalDescription": "Tylko lokalne zasoby. Brak tunelu.", + "siteSeeAll": "Zobacz wszystkie witryny", + "siteTunnelDescription": "Określ jak chcesz połączyć się ze swoją stroną", + "siteNewtCredentials": "Aktualne dane logowania", + "siteNewtCredentialsDescription": "Oto jak Newt będzie uwierzytelniał się z serwerem", + "siteCredentialsSave": "Zapisz swoje poświadczenia", + "siteCredentialsSaveDescription": "Możesz to zobaczyć tylko raz. Upewnij się, że skopiuj je do bezpiecznego miejsca.", + "siteInfo": "Informacje o witrynie", "status": "Status", "shareTitle": "Zarządzaj linkami udostępniania", "shareDescription": "Utwórz linki, które można udostępnić, aby przyznać tymczasowy lub stały dostęp do Twoich zasobów", @@ -112,31 +112,31 @@ "shareErrorDeleteMessage": "Wystąpił błąd podczas usuwania linku", "shareDeleted": "Link usunięty", "shareDeletedDesciption": "Link został usunięty", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", + "shareTokenDescription": "Twój token dostępu może być przekazywany na dwa sposoby: jako parametr zapytania lub w nagłówkach żądania. Muszą być przekazywane z klienta na każde żądanie uwierzytelnionego dostępu.", + "accessToken": "Token dostępu", + "usageExamples": "Przykłady użycia", "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", + "requestHeades": "Nagłówki żądania", + "queryParameter": "Parametr zapytania", + "importantNote": "Ważna uwaga", + "shareImportantDescription": "Ze względów bezpieczeństwa zaleca się użycie nagłówków nad parametrami zapytania, jeśli to możliwe, ponieważ parametry zapytania mogą być zalogowane w dziennikach serwera lub historii przeglądarki.", "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourceNotFound": "No resources found", - "resourceSearch": "Search resources", + "shareTokenSecurety": "Chroń swój token dostępu. Nie udostępniaj go w publicznie dostępnych miejscach ani w kodzie po stronie klienta.", + "shareErrorFetchResource": "Nie udało się pobrać zasobów", + "shareErrorFetchResourceDescription": "Wystąpił błąd podczas pobierania zasobów", + "shareErrorCreate": "Nie udało się utworzyć linku udostępniania", + "shareErrorCreateDescription": "Wystąpił błąd podczas tworzenia linku udostępniania", + "shareCreateDescription": "Każdy z tym linkiem może uzyskać dostęp do zasobu", + "shareTitleOptional": "Tytuł (opcjonalnie)", + "expireIn": "Wygasa za", + "neverExpire": "Nigdy nie wygasa", + "shareExpireDescription": "Czas wygaśnięcia to jak długo link będzie mógł być użyty i zapewni dostęp do zasobu. Po tym czasie link nie będzie już działał, a użytkownicy, którzy użyli tego linku, utracą dostęp do zasobu.", + "shareSeeOnce": "Możesz zobaczyć tylko ten link. Upewnij się, że go skopiowało.", + "shareAccessHint": "Każdy z tym linkiem może uzyskać dostęp do zasobu. Podziel się nim ostrożnie.", + "shareTokenUsage": "Zobacz użycie tokenu dostępu", + "createLink": "Utwórz link", + "resourceNotFound": "Nie znaleziono zasobów", + "resourceSearch": "Szukaj zasobów", "openMenu": "Otwórz menu", "resource": "Zasoby", "title": "Rozporządzenie Rady (EWG) nr 2658/87 z dnia 23 lipca 1987 r. w sprawie nomenklatury taryfowej i statystycznej oraz w sprawie Wspólnej Taryfy Celnej (Dz.U. L 256 z 7.9.1987, s. 1).", @@ -146,7 +146,7 @@ "shareErrorSelectResource": "Wybierz zasób", "resourceTitle": "Zarządzaj zasobami", "resourceDescription": "Utwórz bezpieczne proxy do prywatnych aplikacji", - "resourcesSearch": "Search resources...", + "resourcesSearch": "Szukaj zasobów...", "resourceAdd": "Dodaj zasób", "resourceErrorDelte": "Błąd podczas usuwania zasobu", "authentication": "Uwierzytelnianie", @@ -182,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Numer portu", "resourcePortNumberDescription": "Numer portu zewnętrznego do żądań proxy.", - "cancel": "Cancel", + "cancel": "Anuluj", "resourceConfig": "Snippety konfiguracji", "resourceConfigDescription": "Skopiuj i wklej te fragmenty konfiguracji, aby skonfigurować swój zasób TCP/UDP", "resourceAddEntrypoints": "Traefik: Dodaj punkty wejścia", @@ -194,7 +194,7 @@ "enabled": "Włączone", "disabled": "Wyłączone", "general": "Ogólny", - "generalSettings": "General Settings", + "generalSettings": "Ustawienia ogólne", "proxy": "Proxy", "rules": "Regulamin", "resourceSettingDescription": "Skonfiguruj ustawienia zasobu", @@ -204,7 +204,7 @@ "orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji", "orgGeneralSettings": "Ustawienia organizacji", "orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją", - "saveGeneralSettings": "Save General Settings", + "saveGeneralSettings": "Zapisz ustawienia ogólne", "orgDangerZone": "Strefa zagrożenia", "orgDangerZoneDescription": "Po usunięciu tego organa nie ma odwrotu. Upewnij się.", "orgDelete": "Usuń organizację", @@ -240,10 +240,10 @@ "inviteTitle": "Otwórz zaproszenia", "inviteDescription": "Zarządzaj zaproszeniami dla innych użytkowników", "inviteSearch": "Szukaj zaproszeń...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years" + "minutes": "Protokoły", + "hours": "Godziny", + "days": "Dni", + "weeks": "Tygodnie", + "months": "Miesiące", + "years": "Lata" } \ No newline at end of file From e9e9478f6c0c05c95dd771b435e80a648f70b077 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 5 May 2025 21:33:43 +0200 Subject: [PATCH 034/105] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 124 ++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index c82ecfe2..6abfa1c2 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -40,7 +40,7 @@ "dataIn": "Dados em", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", - "tunnelType": "Tunnel Type", + "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", "siteConfirmDelete": "Confirmar exclusão do site", @@ -51,7 +51,7 @@ "siteManageSites": "Gerenciar sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", + "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", "siteCreateDescription": "Crie um novo site para começar a conectar seus recursos", "close": "FECHAR", "siteNameMin": "O nome deve ter pelo menos 2 caracteres.", @@ -73,37 +73,37 @@ "siteConfirmCopy": "Eu copiei a configuração", "searchSites": "Procurar sites...", "siteAdd": "Adicionar Site", - "siteInstallNewt": "Install Newt", - "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", + "siteInstallNewt": "Instalar Novo", + "siteInstallNewtDescription": "Novo item em execução no seu sistema", + "WgConfiguration": "Configuração do WireGuard", + "WgConfigurationDescription": "Use a seguinte configuração para conectar-se à sua rede", + "operatingSystem": "Sistema operacional", + "commands": "Comandos", "recommended": "Recomendados", "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", "siteErrorDelete": "Erro ao excluir site", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", - "siteUpdated": "Site updated", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", - "status": "Status", + "siteErrorUpdate": "Falha ao atualizar site", + "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", + "siteUpdated": "Site atualizado", + "siteUpdatedDescription": "O site foi atualizado.", + "siteGeneralDescription": "Configurar as configurações gerais para este site", + "siteSettingDescription": "Configure as configurações no seu site", + "siteSetting": "Configurações do {siteName}", + "siteNewtTunnel": "Novo túnel (recomendado)", + "siteNewtTunnelDescription": "A maneira mais fácil de criar um ponto de entrada na sua rede. Nenhuma configuração extra.", + "siteWg": "WireGuard Básico", + "siteWgDescription": "Use qualquer cliente do WireGuard para estabelecer um túnel. Configuração manual NAT é necessária.", + "siteLocalDescription": "Recursos locais apenas. Sem túneis.", + "siteSeeAll": "Ver todos os sites", + "siteTunnelDescription": "Determine como você deseja se conectar ao seu site", + "siteNewtCredentials": "Credenciais Novas", + "siteNewtCredentialsDescription": "É assim que o novo sistema se autenticará com o servidor", + "siteCredentialsSave": "Salve suas credenciais", + "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", + "siteInfo": "Informações do Site", + "status": "SItuação", "shareTitle": "Gerenciar links de compartilhamento", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", @@ -112,31 +112,31 @@ "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", "shareDeleted": "Link excluído", "shareDeletedDesciption": "O link foi eliminado", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", + "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", + "accessToken": "Token de acesso", + "usageExamples": "Exemplos de uso", "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", - "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourceNotFound": "No resources found", - "resourceSearch": "Search resources", + "requestHeades": "Cabeçalhos de solicitação", + "queryParameter": "Parâmetro de consulta", + "importantNote": "Nota importante", + "shareImportantDescription": "Por razões de segurança, o uso de cabeçalhos é recomendado através dos parâmetros de consulta quando possível, já que os parâmetros de consulta podem estar logados nos logs do servidor ou no histórico do navegador.", + "token": "Identificador", + "shareTokenSecurety": "Mantenha seu token de acesso seguro. Não o compartilhe em áreas de acesso público ou código do lado do cliente.", + "shareErrorFetchResource": "Falha ao buscar recursos", + "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", + "shareErrorCreate": "Falha ao criar link de compartilhamento", + "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", + "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", + "shareTitleOptional": "Título (opcional)", + "expireIn": "Expira em", + "neverExpire": "Nunca expirar", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", + "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", + "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", + "shareTokenUsage": "Ver Uso do Token de Acesso", + "createLink": "Criar Link", + "resourceNotFound": "Nenhum recurso encontrado", + "resourceSearch": "Recursos de pesquisa", "openMenu": "Abrir menu", "resource": "Recurso", "title": "Título", @@ -146,7 +146,7 @@ "shareErrorSelectResource": "Por favor, selecione um recurso", "resourceTitle": "Gerenciar Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", - "resourcesSearch": "Search resources...", + "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", "resourceErrorDelte": "Erro ao excluir recurso", "authentication": "Autenticação", @@ -182,7 +182,7 @@ "protocolSelect": "Select a protocol", "resourcePortNumber": "Número da Porta", "resourcePortNumberDescription": "O número da porta externa para requisições de proxy.", - "cancel": "Cancel", + "cancel": "cancelar", "resourceConfig": "Snippets de Configuração", "resourceConfigDescription": "Copie e cole estes snippets de configuração para configurar o seu recurso TCP/UDP", "resourceAddEntrypoints": "Traefik: Adicionar pontos de entrada", @@ -194,7 +194,7 @@ "enabled": "Ativado", "disabled": "Desabilitado", "general": "Gerais", - "generalSettings": "General Settings", + "generalSettings": "Configurações Gerais", "proxy": "Proxy", "rules": "Regras", "resourceSettingDescription": "Configure as configurações do seu recurso", @@ -204,7 +204,7 @@ "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Save General Settings", + "saveGeneralSettings": "Salvar configurações gerais", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", @@ -240,10 +240,10 @@ "inviteTitle": "Convites Abertos", "inviteDescription": "Gerencie seus convites para outros usuários", "inviteSearch": "Procurar convites...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years" + "minutes": "minutos", + "hours": "horas", + "days": "dias", + "weeks": "semanas", + "months": "Meses", + "years": "anos" } \ No newline at end of file From 87b95986c3dde1dce306472b70570cc85e21b8c6 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Mon, 5 May 2025 19:35:24 +0000 Subject: [PATCH 035/105] ... --- messages/en-US.json | 2 ++ src/app/[orgId]/settings/resources/ResourcesTable.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 3d500de1..68add635 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Learn how to configure TCP/UDP resources", "resourceBack": "Back to Resources", "resourceGoTo": "Go to Resource", + "resourceDelete": "Delete Resource", + "resourceDeleteConfirm": "Confirm Delete Resource", "visibility": "Visibility", "enabled": "Enabled", "disabled": "Disabled", diff --git a/src/app/[orgId]/settings/resources/ResourcesTable.tsx b/src/app/[orgId]/settings/resources/ResourcesTable.tsx index b80c4d50..4336287f 100644 --- a/src/app/[orgId]/settings/resources/ResourcesTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesTable.tsx @@ -303,10 +303,10 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {

} - buttonText="Confirm Delete Resource" + buttonText={t('resourceDeleteConfirm')} onConfirm={async () => deleteResource(selectedResource!.id)} string={selectedResource.name} - title="Delete Resource" + title={t('resourceDelete')} /> )} From b9c7c8c9664bcfcef31b7be37618bc43a26c0b74 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 06:33:43 +0300 Subject: [PATCH 036/105] api keys --- messages/de-DE.json | 42 ++++++++++++++- messages/en-US.json | 42 ++++++++++++++- messages/fr-FR.json | 42 ++++++++++++++- messages/it-IT.json | 42 ++++++++++++++- messages/pl-PL.json | 42 ++++++++++++++- messages/pt-PT.json | 42 ++++++++++++++- messages/tr-TR.json | 42 ++++++++++++++- .../settings/api-keys/OrgApiKeysDataTable.tsx | 10 ++-- .../settings/api-keys/OrgApiKeysTable.tsx | 29 +++++------ .../settings/api-keys/[apiKeyId]/layout.tsx | 5 +- .../api-keys/[apiKeyId]/permissions/page.tsx | 21 ++++---- .../[orgId]/settings/api-keys/create/page.tsx | 52 +++++++++---------- src/app/[orgId]/settings/api-keys/page.tsx | 6 ++- 13 files changed, 347 insertions(+), 70 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index aeba3d65..50173903 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -245,5 +245,43 @@ "days": "Tage", "weeks": "Wochen", "months": "Monate", - "years": "Jahre" -} \ No newline at end of file + "years": "Jahre", + "apiKeysTitle": "API-Schlüssel Information", + "apiKeysNameMin": "Name muss mindestens 2 Zeichen lang sein.", + "apiKeysNameMax": "Name darf nicht länger als 255 Zeichen sein.", + "apiKeysConfirmCopy2": "Sie müssen bestätigen, dass Sie den API-Schlüssel kopiert haben.", + "apiKeysErrorCreate": "Fehler beim Erstellen des API-Schlüssels", + "apiKeysErrorSetPermission": "Fehler beim Setzen der Berechtigungen", + "apiKeysCreate": "API-Schlüssel generieren", + "apiKeysCreateDescription": "Generieren Sie einen neuen API-Schlüssel für Ihre Organisation", + "apiKeysGeneralSettings": "Berechtigungen", + "apiKeysGeneralSettingsDescription": "Legen Sie fest, was dieser API-Schlüssel tun kann", + "apiKeysList": "Ihr API-Schlüssel", + "apiKeysSave": "Speichern Sie Ihren API-Schlüssel", + "apiKeysSaveDescription": "Sie können dies nur einmal sehen. Kopieren Sie es an einen sicheren Ort.", + "apiKeysInfo": "Ihr API-Schlüssel ist:", + "apiKeysConfirmCopy": "Ich habe den API-Schlüssel kopiert", + "generate": "Generieren", + "done": "Fertig", + "apiKeysSeeAll": "Alle API-Schlüssel anzeigen", + "apiKeysPermissionsErrorLoadingActions": "Fehler beim Laden der API-Schlüsselaktionen", + "apiKeysPermissionsErrorUpdate": "Fehler beim Setzen der Berechtigungen", + "apiKeysPermissionsUpdated": "Berechtigungen aktualisiert", + "apiKeysPermissionsUpdatedDescription": "Die Berechtigungen wurden aktualisiert.", + "apiKeysPermissionsGeneralSettings": "Berechtigungen", + "apiKeysPermissionsGeneralSettingsDescription": "Legen Sie fest, was dieser API-Schlüssel tun kann", + "apiKeysPermissionsSave": "Berechtigungen speichern", + "apiKeysPermissionsTitle": "Berechtigungen", + "apiKeys": "API-Schlüssel", + "searchApiKeys": "API-Schlüssel suchen...", + "apiKeysAdd": "API-Schlüssel generieren", + "apiKeysErrorDelete": "Fehler beim Löschen des API-Schlüssels", + "apiKeysErrorDeleteMessage": "Fehler beim Löschen des API-Schlüssels", + "apiKeysQuestionRemove": "Sind Sie sicher, dass Sie den API-Schlüssel {selectedApiKey} aus der Organisation entfernen möchten?", + "apiKeysMessageRemove": "Einmal entfernt, kann der API-Schlüssel nicht mehr verwendet werden.", + "apiKeysMessageConfirm": "Zur Bestätigung geben Sie bitte den Namen des API-Schlüssels unten ein.", + "apiKeysDeleteConfirm": "Löschen des API-Schlüssels bestätigen", + "apiKeysDelete": "API-Schlüssel löschen", + "apiKeysManage": "API-Schlüssel verwalten", + "apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet" +} diff --git a/messages/en-US.json b/messages/en-US.json index 68add635..562977b3 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -247,5 +247,43 @@ "days": "Days", "weeks": "Weeks", "months": "Months", - "years": "Years" -} \ No newline at end of file + "years": "Years", + "apiKeysTitle": "API Key Information", + "apiKeysNameMin": "Name must be at least 2 characters.", + "apiKeysNameMax": "Name must not be longer than 255 characters.", + "apiKeysConfirmCopy2": "You must confirm that you have copied the API key.", + "apiKeysErrorCreate": "Error creating API key", + "apiKeysErrorSetPermission": "Error setting permissions", + "apiKeysCreate": "Generate API Key", + "apiKeysCreateDescription": "Generate a new API key for your organization", + "apiKeysGeneralSettings": "Permissions", + "apiKeysGeneralSettingsDescription": "Determine what this API key can do", + "apiKeysList": "Your API Key", + "apiKeysSave": "Save Your API Key", + "apiKeysSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", + "apiKeysInfo": "Your API key is:", + "apiKeysConfirmCopy": "I have copied the API key", + "generate": "Generate", + "done": "Done", + "apiKeysSeeAll": "See All API Keys", + "apiKeysPermissionsErrorLoadingActions": "Error loading API key actions", + "apiKeysPermissionsErrorUpdate": "Error setting permissions", + "apiKeysPermissionsUpdated": "Permissions updated", + "apiKeysPermissionsUpdatedDescription": "The permissions have been updated.", + "apiKeysPermissionsGeneralSettings": "Permissions", + "apiKeysPermissionsGeneralSettingsDescription": "Determine what this API key can do", + "apiKeysPermissionsSave": "Save Permissions", + "apiKeysPermissionsTitle": "Permissions", + "apiKeys": "API Keys", + "searchApiKeys": "Search API keys...", + "apiKeysAdd": "Generate API Key", + "apiKeysErrorDelete": "Error deleting API key", + "apiKeysErrorDeleteMessage": "Error deleting API key", + "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", + "apiKeysMessageRemove": "Once removed, the API key will no longer be able to be used.", + "apiKeysMessageConfirm": "To confirm, please type the name of the API key below.", + "apiKeysDeleteConfirm": "Confirm Delete API Key", + "apiKeysDelete": "Delete API Key", + "apiKeysManage": "Manage API Keys", + "apiKeysDescription": "API keys are used to authenticate with the integration API" +} diff --git a/messages/fr-FR.json b/messages/fr-FR.json index e11fa66f..9aaf1654 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -245,5 +245,43 @@ "days": "Jours", "weeks": "Semaines", "months": "Mois", - "years": "Années" -} \ No newline at end of file + "years": "Années", + "apiKeysTitle": "Informations sur la clé API", + "apiKeysNameMin": "Le nom doit comporter au moins 2 caractères.", + "apiKeysNameMax": "Le nom ne doit pas dépasser 255 caractères.", + "apiKeysConfirmCopy2": "Vous devez confirmer que vous avez copié la clé API.", + "apiKeysErrorCreate": "Erreur lors de la création de la clé API", + "apiKeysErrorSetPermission": "Erreur lors de la définition des permissions", + "apiKeysCreate": "Générer une clé API", + "apiKeysCreateDescription": "Générer une nouvelle clé API pour votre organisation", + "apiKeysGeneralSettings": "Permissions", + "apiKeysGeneralSettingsDescription": "Déterminez ce que cette clé API peut faire", + "apiKeysList": "Votre clé API", + "apiKeysSave": "Enregistrer votre clé API", + "apiKeysSaveDescription": "Vous ne pourrez voir cela qu'une seule fois. Assurez-vous de la copier dans un endroit sécurisé.", + "apiKeysInfo": "Votre clé API est :", + "apiKeysConfirmCopy": "J'ai copié la clé API", + "generate": "Générer", + "done": "Terminé", + "apiKeysSeeAll": "Voir toutes les clés API", + "apiKeysPermissionsErrorLoadingActions": "Erreur lors du chargement des actions de la clé API", + "apiKeysPermissionsErrorUpdate": "Erreur lors de la définition des permissions", + "apiKeysPermissionsUpdated": "Permissions mises à jour", + "apiKeysPermissionsUpdatedDescription": "Les permissions ont été mises à jour.", + "apiKeysPermissionsGeneralSettings": "Permissions", + "apiKeysPermissionsGeneralSettingsDescription": "Déterminez ce que cette clé API peut faire", + "apiKeysPermissionsSave": "Enregistrer les permissions", + "apiKeysPermissionsTitle": "Permissions", + "apiKeys": "Clés API", + "searchApiKeys": "Rechercher des clés API...", + "apiKeysAdd": "Générer une clé API", + "apiKeysErrorDelete": "Erreur lors de la suppression de la clé API", + "apiKeysErrorDeleteMessage": "Erreur lors de la suppression de la clé API", + "apiKeysQuestionRemove": "Êtes-vous sûr de vouloir supprimer la clé API {selectedApiKey} de l'organisation ?", + "apiKeysMessageRemove": "Une fois supprimée, la clé API ne pourra plus être utilisée.", + "apiKeysMessageConfirm": "Pour confirmer, veuillez saisir le nom de la clé API ci-dessous.", + "apiKeysDeleteConfirm": "Confirmer la suppression de la clé API", + "apiKeysDelete": "Supprimer la clé API", + "apiKeysManage": "Gérer les clés API", + "apiKeysDescription": "Les clés API sont utilisées pour s'authentifier avec l'API d'intégration" +} diff --git a/messages/it-IT.json b/messages/it-IT.json index 39483529..3909a646 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -245,5 +245,43 @@ "days": "Giorni", "weeks": "Settimane", "months": "Mesi", - "years": "Anni" -} \ No newline at end of file + "years": "Anni", + "apiKeysTitle": "Informazioni Chiave API", + "apiKeysNameMin": "Il nome deve contenere almeno 2 caratteri.", + "apiKeysNameMax": "Il nome non deve essere più lungo di 255 caratteri.", + "apiKeysConfirmCopy2": "Devi confermare di aver copiato la chiave API.", + "apiKeysErrorCreate": "Errore nella creazione della chiave API", + "apiKeysErrorSetPermission": "Errore nell'impostazione dei permessi", + "apiKeysCreate": "Genera Chiave API", + "apiKeysCreateDescription": "Genera una nuova chiave API per la tua organizzazione", + "apiKeysGeneralSettings": "Permessi", + "apiKeysGeneralSettingsDescription": "Determina cosa può fare questa chiave API", + "apiKeysList": "La Tua Chiave API", + "apiKeysSave": "Salva La Tua Chiave API", + "apiKeysSaveDescription": "Potrai vederla solo una volta. Assicurati di copiarla in un luogo sicuro.", + "apiKeysInfo": "La tua chiave API è:", + "apiKeysConfirmCopy": "Ho copiato la chiave API", + "generate": "Genera", + "done": "Fatto", + "apiKeysSeeAll": "Vedi Tutte Le Chiavi API", + "apiKeysPermissionsErrorLoadingActions": "Errore nel caricamento delle azioni della chiave API", + "apiKeysPermissionsErrorUpdate": "Errore nell'impostazione dei permessi", + "apiKeysPermissionsUpdated": "Permessi aggiornati", + "apiKeysPermissionsUpdatedDescription": "I permessi sono stati aggiornati.", + "apiKeysPermissionsGeneralSettings": "Permessi", + "apiKeysPermissionsGeneralSettingsDescription": "Determina cosa può fare questa chiave API", + "apiKeysPermissionsSave": "Salva Permessi", + "apiKeysPermissionsTitle": "Permessi", + "apiKeys": "Chiavi API", + "searchApiKeys": "Cerca chiavi API...", + "apiKeysAdd": "Genera Chiave API", + "apiKeysErrorDelete": "Errore nell'eliminazione della chiave API", + "apiKeysErrorDeleteMessage": "Errore nell'eliminazione della chiave API", + "apiKeysQuestionRemove": "Sei sicuro di voler rimuovere la chiave API {selectedApiKey} dall'organizzazione?", + "apiKeysMessageRemove": "Una volta rimossa, la chiave API non potrà più essere utilizzata.", + "apiKeysMessageConfirm": "Per confermare, digita il nome della chiave API qui sotto.", + "apiKeysDeleteConfirm": "Conferma Eliminazione Chiave API", + "apiKeysDelete": "Elimina Chiave API", + "apiKeysManage": "Gestisci Chiavi API", + "apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione" +} diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 8ca316e0..313ca200 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -245,5 +245,43 @@ "days": "Dni", "weeks": "Tygodnie", "months": "Miesiące", - "years": "Lata" -} \ No newline at end of file + "years": "Lata", + "apiKeysTitle": "Informacje o kluczu API", + "apiKeysNameMin": "Nazwa musi mieć co najmniej 2 znaki.", + "apiKeysNameMax": "Nazwa nie może być dłuższa niż 255 znaków.", + "apiKeysConfirmCopy2": "Musisz potwierdzić, że skopiowałeś klucz API.", + "apiKeysErrorCreate": "Błąd podczas tworzenia klucza API", + "apiKeysErrorSetPermission": "Błąd podczas ustawiania uprawnień", + "apiKeysCreate": "Generuj klucz API", + "apiKeysCreateDescription": "Wygeneruj nowy klucz API dla swojej organizacji", + "apiKeysGeneralSettings": "Uprawnienia", + "apiKeysGeneralSettingsDescription": "Określ, co ten klucz API może zrobić", + "apiKeysList": "Twój klucz API", + "apiKeysSave": "Zapisz swój klucz API", + "apiKeysSaveDescription": "Będziesz mógł zobaczyć to tylko raz. Upewnij się, że skopiujesz go w bezpieczne miejsce.", + "apiKeysInfo": "Twój klucz API to:", + "apiKeysConfirmCopy": "Skopiowałem klucz API", + "generate": "Generuj", + "done": "Gotowe", + "apiKeysSeeAll": "Zobacz wszystkie klucze API", + "apiKeysPermissionsErrorLoadingActions": "Błąd podczas ładowania akcji klucza API", + "apiKeysPermissionsErrorUpdate": "Błąd podczas ustawiania uprawnień", + "apiKeysPermissionsUpdated": "Uprawnienia zaktualizowane", + "apiKeysPermissionsUpdatedDescription": "Uprawnienia zostały zaktualizowane.", + "apiKeysPermissionsGeneralSettings": "Uprawnienia", + "apiKeysPermissionsGeneralSettingsDescription": "Określ, co ten klucz API może zrobić", + "apiKeysPermissionsSave": "Zapisz uprawnienia", + "apiKeysPermissionsTitle": "Uprawnienia", + "apiKeys": "Klucze API", + "searchApiKeys": "Szukaj kluczy API...", + "apiKeysAdd": "Generuj klucz API", + "apiKeysErrorDelete": "Błąd podczas usuwania klucza API", + "apiKeysErrorDeleteMessage": "Błąd podczas usuwania klucza API", + "apiKeysQuestionRemove": "Czy na pewno chcesz usunąć klucz API {selectedApiKey} z organizacji?", + "apiKeysMessageRemove": "Po usunięciu klucz API nie będzie już mógł być używany.", + "apiKeysMessageConfirm": "Aby potwierdzić, wpisz nazwę klucza API poniżej.", + "apiKeysDeleteConfirm": "Potwierdź usunięcie klucza API", + "apiKeysDelete": "Usuń klucz API", + "apiKeysManage": "Zarządzaj kluczami API", + "apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji" +} diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 6abfa1c2..f45b0b05 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -245,5 +245,43 @@ "days": "dias", "weeks": "semanas", "months": "Meses", - "years": "anos" -} \ No newline at end of file + "years": "anos", + "apiKeysTitle": "Informações da Chave API", + "apiKeysNameMin": "O nome deve ter pelo menos 2 caracteres.", + "apiKeysNameMax": "O nome não deve ter mais de 255 caracteres.", + "apiKeysConfirmCopy2": "Você deve confirmar que copiou a chave API.", + "apiKeysErrorCreate": "Erro ao criar chave API", + "apiKeysErrorSetPermission": "Erro ao definir permissões", + "apiKeysCreate": "Gerar Chave API", + "apiKeysCreateDescription": "Gerar uma nova chave API para sua organização", + "apiKeysGeneralSettings": "Permissões", + "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", + "apiKeysList": "Sua Chave API", + "apiKeysSave": "Salvar Sua Chave API", + "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", + "apiKeysInfo": "Sua chave API é:", + "apiKeysConfirmCopy": "Eu copiei a chave API", + "generate": "Gerar", + "done": "Concluído", + "apiKeysSeeAll": "Ver Todas as Chaves API", + "apiKeysPermissionsErrorLoadingActions": "Erro ao carregar ações da chave API", + "apiKeysPermissionsErrorUpdate": "Erro ao definir permissões", + "apiKeysPermissionsUpdated": "Permissões atualizadas", + "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", + "apiKeysPermissionsGeneralSettings": "Permissões", + "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", + "apiKeysPermissionsSave": "Salvar Permissões", + "apiKeysPermissionsTitle": "Permissões", + "apiKeys": "Chaves API", + "searchApiKeys": "Pesquisar chaves API...", + "apiKeysAdd": "Gerar Chave API", + "apiKeysErrorDelete": "Erro ao excluir chave API", + "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", + "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", + "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", + "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", + "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", + "apiKeysDelete": "Excluir Chave API", + "apiKeysManage": "Gerenciar Chaves API", + "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração" +} diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 3d500de1..6f2c0735 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -245,5 +245,43 @@ "days": "Days", "weeks": "Weeks", "months": "Months", - "years": "Years" -} \ No newline at end of file + "years": "Years", + "apiKeysTitle": "API Key Information", + "apiKeysNameMin": "Name must be at least 2 characters.", + "apiKeysNameMax": "Name must not be longer than 255 characters.", + "apiKeysConfirmCopy2": "You must confirm that you have copied the API key.", + "apiKeysErrorCreate": "Error creating API key", + "apiKeysErrorSetPermission": "Error setting permissions", + "apiKeysCreate": "Generate API Key", + "apiKeysCreateDescription": "Generate a new API key for your organization", + "apiKeysGeneralSettings": "Permissions", + "apiKeysGeneralSettingsDescription": "Determine what this API key can do", + "apiKeysList": "Your API Key", + "apiKeysSave": "Save Your API Key", + "apiKeysSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", + "apiKeysInfo": "Your API key is:", + "apiKeysConfirmCopy": "I have copied the API key", + "generate": "Generate", + "done": "Done", + "apiKeysSeeAll": "See All API Keys", + "apiKeysPermissionsErrorLoadingActions": "Error loading API key actions", + "apiKeysPermissionsErrorUpdate": "Error setting permissions", + "apiKeysPermissionsUpdated": "Permissions updated", + "apiKeysPermissionsUpdatedDescription": "The permissions have been updated.", + "apiKeysPermissionsGeneralSettings": "Permissions", + "apiKeysPermissionsGeneralSettingsDescription": "Determine what this API key can do", + "apiKeysPermissionsSave": "Save Permissions", + "apiKeysPermissionsTitle": "Permissions", + "apiKeys": "API Keys", + "searchApiKeys": "Search API keys...", + "apiKeysAdd": "Generate API Key", + "apiKeysErrorDelete": "Error deleting API key", + "apiKeysErrorDeleteMessage": "Error deleting API key", + "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", + "apiKeysMessageRemove": "Once removed, the API key will no longer be able to be used.", + "apiKeysMessageConfirm": "To confirm, please type the name of the API key below.", + "apiKeysDeleteConfirm": "Confirm Delete API Key", + "apiKeysDelete": "Delete API Key", + "apiKeysManage": "Manage API Keys", + "apiKeysDescription": "API keys are used to authenticate with the integration API" +} diff --git a/src/app/[orgId]/settings/api-keys/OrgApiKeysDataTable.tsx b/src/app/[orgId]/settings/api-keys/OrgApiKeysDataTable.tsx index 69fe7176..a68f0e32 100644 --- a/src/app/[orgId]/settings/api-keys/OrgApiKeysDataTable.tsx +++ b/src/app/[orgId]/settings/api-keys/OrgApiKeysDataTable.tsx @@ -7,6 +7,7 @@ import { DataTable } from "@app/components/ui/data-table"; import { ColumnDef } from "@tanstack/react-table"; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; @@ -19,15 +20,18 @@ export function OrgApiKeysDataTable({ columns, data }: DataTableProps) { + + const t = useTranslations(); + return ( ); } diff --git a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx index 89e47842..b26bc622 100644 --- a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx +++ b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx @@ -24,6 +24,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import moment from "moment"; +import { useTranslations } from "next-intl"; export type OrgApiKeyRow = { id: string; @@ -49,14 +50,16 @@ export default function OrgApiKeysTable({ const api = createApiClient(useEnvContext()); + const t = useTranslations(); + const deleteSite = (apiKeyId: string) => { api.delete(`/org/${orgId}/api-key/${apiKeyId}`) .catch((e) => { - console.error("Error deleting API key", e); + console.error(t('apiKeysErrorDelete'), e); toast({ variant: "destructive", - title: "Error deleting API key", - description: formatAxiosError(e, "Error deleting API key") + title: t('apiKeysErrorDelete'), + description: formatAxiosError(e, t('apiKeysErrorDeleteMessage')) }); }) .then(() => { @@ -90,7 +93,7 @@ export default function OrgApiKeysTable({ setSelected(apiKeyROw); }} > - View settings + {t('viewSettings')} { @@ -115,7 +118,7 @@ export default function OrgApiKeysTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -145,7 +148,7 @@ export default function OrgApiKeysTable({
@@ -167,28 +170,24 @@ export default function OrgApiKeysTable({ dialog={

- Are you sure you want to remove the API key{" "} - {selected?.name || selected?.id} from the - organization? + {t('apiKeysQuestionRemove', {selectedApiKey: selected?.name || selected?.id})}

- Once removed, the API key will no longer be - able to be used. + {t('apiKeysMessageRemove')}

- To confirm, please type the name of the API key - below. + {t('apiKeysMessageConfirm')}

} - buttonText="Confirm Delete API Key" + buttonText={t('apiKeysDeleteConfirm')} onConfirm={async () => deleteSite(selected!.id)} string={selected.name} - title="Delete API Key" + title={t('apiKeysDelete')} /> )} diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx index a4c13c9a..79b28dfd 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx @@ -20,6 +20,7 @@ import { import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; +import { useTranslations } from "next-intl"; interface SettingsLayoutProps { children: React.ReactNode; @@ -29,6 +30,8 @@ interface SettingsLayoutProps { export default async function SettingsLayout(props: SettingsLayoutProps) { const params = await props.params; + const t = useTranslations(); + const { children } = props; let apiKey = null; @@ -45,7 +48,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems = [ { - title: "Permissions", + title: t('apiKeysPermissionsTitle'), href: "/{orgId}/settings/api-keys/{apiKeyId}/permissions" } ]; diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx index d1e6f518..ee0d4ba2 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx @@ -23,12 +23,15 @@ import { ListApiKeyActionsResponse } from "@server/routers/apiKeys"; import { AxiosResponse } from "axios"; import { useParams } from "next/navigation"; import { useEffect, useState } from "react"; +import { useTranslations } from "next-intl"; export default function Page() { const { env } = useEnvContext(); const api = createApiClient({ env }); const { orgId, apiKeyId } = useParams(); + const t = useTranslations(); + const [loadingPage, setLoadingPage] = useState(true); const [selectedPermissions, setSelectedPermissions] = useState< Record @@ -47,10 +50,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error loading API key actions", + title: t('apiKeysPermissionsErrorLoadingActions'), description: formatAxiosError( e, - "Error loading API key actions" + t('apiKeysPermissionsErrorLoadingActions') ) }); }); @@ -81,18 +84,18 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysPermissionsErrorUpdate'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysPermissionsErrorUpdate'), description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: "Permissions updated", - description: "The permissions have been updated." + title: t('apiKeysPermissionsUpdated'), + description: t('apiKeysPermissionsUpdatedDescription') }); } @@ -106,10 +109,10 @@ export default function Page() { - Permissions + {t('apiKeysPermissionsGeneralSettings')} - Determine what this API key can do + {t('apiKeysPermissionsGeneralSettingsDescription')} @@ -126,7 +129,7 @@ export default function Page() { loading={loadingSavePermissions} disabled={loadingSavePermissions} > - Save Permissions + {t('apiKeysPermissionsSave')} diff --git a/src/app/[orgId]/settings/api-keys/create/page.tsx b/src/app/[orgId]/settings/api-keys/create/page.tsx index 3ede2ac0..3d1ddacf 100644 --- a/src/app/[orgId]/settings/api-keys/create/page.tsx +++ b/src/app/[orgId]/settings/api-keys/create/page.tsx @@ -61,15 +61,18 @@ import moment from "moment"; import CopyCodeBox from "@server/emails/templates/components/CopyCodeBox"; import CopyTextBox from "@app/components/CopyTextBox"; import PermissionsSelectBox from "@app/components/PermissionsSelectBox"; +import { useTranslations } from "next-intl"; + +const t = useTranslations(); const createFormSchema = z.object({ name: z .string() .min(2, { - message: "Name must be at least 2 characters." + message: t('apiKeysNameMin') }) .max(255, { - message: "Name must not be longer than 255 characters." + message: t('apiKeysNameMax') }) }); @@ -84,7 +87,7 @@ const copiedFormSchema = z return data.copied; }, { - message: "You must confirm that you have copied the API key.", + message: t('apiKeysConfirmCopy2'), path: ["copied"] } ); @@ -132,7 +135,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error creating API key", + title: t('apiKeysErrorCreate'), description: formatAxiosError(e) }); }); @@ -153,10 +156,10 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysErrorSetPermission'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysErrorSetPermission'), description: formatAxiosError(e) }); }); @@ -195,8 +198,8 @@ export default function Page() { <>
@@ -216,7 +219,7 @@ export default function Page() { - API Key Information + {t('apiKeysTitle')} @@ -232,7 +235,7 @@ export default function Page() { render={({ field }) => ( - Name + {t('name')} - Permissions + {t('apiKeysGeneralSettings')} - Determine what this API key can do + {t('apiKeysGeneralSettingsDescription')} @@ -275,14 +278,14 @@ export default function Page() { - Your API Key + {t('apiKeysList')} - Name + {t('name')} - Created + {t('created')} {moment( @@ -305,17 +308,15 @@ export default function Page() { - Save Your API Key + {t('apiKeysSave')} - You will only be able to see this - once. Make sure to copy it to a - secure place. + {t('apiKeysSaveDescription')}

- Your API key is: + {t('apiKeysInfo')}

- I have copied - the API key + {t('apiKeysConfirmCopy')}
@@ -378,7 +378,7 @@ export default function Page() { router.push(`/${orgId}/settings/api-keys`); }} > - Cancel + {t('cancel')} )} {!apiKey && ( @@ -390,7 +390,7 @@ export default function Page() { form.handleSubmit(onSubmit)(); }} > - Generate + {t('generate')} )} @@ -401,7 +401,7 @@ export default function Page() { copiedForm.handleSubmit(onCopiedSubmit)(); }} > - Done + {t('done')} )}
diff --git a/src/app/[orgId]/settings/api-keys/page.tsx b/src/app/[orgId]/settings/api-keys/page.tsx index ef1e3dd1..4e62617e 100644 --- a/src/app/[orgId]/settings/api-keys/page.tsx +++ b/src/app/[orgId]/settings/api-keys/page.tsx @@ -9,6 +9,7 @@ import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import OrgApiKeysTable, { OrgApiKeyRow } from "./OrgApiKeysTable"; import { ListOrgApiKeysResponse } from "@server/routers/apiKeys"; +import { useTranslations } from "next-intl"; type ApiKeyPageProps = { params: Promise<{ orgId: string }>; @@ -18,6 +19,7 @@ export const dynamic = "force-dynamic"; export default async function ApiKeysPage(props: ApiKeyPageProps) { const params = await props.params; + const t = useTranslations(); let apiKeys: ListOrgApiKeysResponse["apiKeys"] = []; try { const res = await internal.get>( @@ -39,8 +41,8 @@ export default async function ApiKeysPage(props: ApiKeyPageProps) { return ( <> From 4dd9f4736d9507ff854bebb79bab56e1ed202bb3 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 6 May 2025 06:49:47 +0000 Subject: [PATCH 037/105] add admin/user i18n --- messages/en-US.json | 14 ++++++++- src/app/admin/users/AdminUsersDataTable.tsx | 8 +++-- src/app/admin/users/AdminUsersTable.tsx | 35 +++++++++------------ src/app/admin/users/page.tsx | 11 ++++--- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 562977b3..411fbb12 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -285,5 +285,17 @@ "apiKeysDeleteConfirm": "Confirm Delete API Key", "apiKeysDelete": "Delete API Key", "apiKeysManage": "Manage API Keys", - "apiKeysDescription": "API keys are used to authenticate with the integration API" + "apiKeysDescription": "API keys are used to authenticate with the integration API", + "userTitle": "Manage All Users", + "userDescription": "View and manage all users in the system", + "userAbount": "About User Management", + "userAbountDescription": "This table displays all root user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their root user object - they will remain in the system. To completely remove a user from the system, you must delete their root user object using the delete action in this table.", + "userServer": "Server Users", + "userSearch": "Search server users...", + "userErrorDelete": "Error deleting user", + "userDeleteConfirm": "Confirm Delete User", + "userDeleteServer": "Delete User from Server", + "userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.", + "userMessageConfirm": "To confirm, please type the name of the user below.", + "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?" } diff --git a/src/app/admin/users/AdminUsersDataTable.tsx b/src/app/admin/users/AdminUsersDataTable.tsx index 7532a8cc..3a1e85cf 100644 --- a/src/app/admin/users/AdminUsersDataTable.tsx +++ b/src/app/admin/users/AdminUsersDataTable.tsx @@ -4,6 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; @@ -14,12 +15,15 @@ export function UsersDataTable({ columns, data }: DataTableProps) { + + const t = useTranslations(); + return ( ); diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index 68ad2790..f8cc7506 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -11,6 +11,7 @@ import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from 'next-intl'; export type GlobalUserRow = { id: string; @@ -29,6 +30,7 @@ type Props = { export default function UsersTable({ users }: Props) { const router = useRouter(); + const t = useTranslations(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selected, setSelected] = useState(null); @@ -42,8 +44,8 @@ export default function UsersTable({ users }: Props) { console.error("Error deleting user", e); toast({ variant: "destructive", - title: "Error deleting user", - description: formatAxiosError(e, "Error deleting user") + title: t('userErrorDelete'), + description: formatAxiosError(e, t('userErrorDelete')) }); }) .then(() => { @@ -82,7 +84,7 @@ export default function UsersTable({ users }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Username + {t('username')} ); @@ -98,7 +100,7 @@ export default function UsersTable({ users }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Email + {t('email')} ); @@ -114,7 +116,7 @@ export default function UsersTable({ users }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -130,7 +132,7 @@ export default function UsersTable({ users }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Identity Provider + {t('identityProvider')} ); @@ -151,7 +153,7 @@ export default function UsersTable({ users }: Props) { setIsDeleteModalOpen(true); }} > - Delete + {t('delete')}
@@ -172,35 +174,26 @@ export default function UsersTable({ users }: Props) { dialog={

- Are you sure you want to permanently delete{" "} - - {selected?.email || - selected?.name || - selected?.username} - {" "} - from the server? + {t('userQuestionRemove', {selectedUser: selected?.email || selected?.name || selected?.username})}

- The user will be removed from all - organizations and be completely removed from - the server. + {t('userMessageRemove')}

- To confirm, please type the name of the user - below. + {t('userMessageConfirm')}

} - buttonText="Confirm Delete User" + buttonText={t('userDeleteConfirm')} onConfirm={async () => deleteUser(selected!.id)} string={ selected.email || selected.name || selected.username } - title="Delete User from Server" + title={t('userDeleteServer')} /> )} diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 6e2290cb..9a840105 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -6,6 +6,7 @@ import { AdminListUsersResponse } from "@server/routers/user/adminListUsers"; import UsersTable, { GlobalUserRow } from "./AdminUsersTable"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon } from "lucide-react"; +import { getTranslations } from 'next-intl/server'; type PageProps = { params: Promise<{ orgId: string }>; @@ -39,17 +40,19 @@ export default async function UsersPage(props: PageProps) { }; }); + const t = await getTranslations(); + return ( <> - About User Management + {t('userAbount')} - This table displays all root user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their root user object - they will remain in the system. To completely remove a user from the system, you must delete their root user object using the delete action in this table. + {t('userAbountDescription')} From 1e72b0f854ac5d9009b05f9f25e4c958a6e79ca5 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 6 May 2025 09:41:44 +0000 Subject: [PATCH 038/105] add admin/license i18n --- messages/en-US.json | 55 +++++++- .../admin/license/LicenseKeysDataTable.tsx | 18 +-- .../components/SitePriceCalculator.tsx | 31 +++-- src/app/admin/license/page.tsx | 118 +++++++----------- 4 files changed, 124 insertions(+), 98 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 411fbb12..09838cc8 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -297,5 +297,58 @@ "userDeleteServer": "Delete User from Server", "userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.", "userMessageConfirm": "To confirm, please type the name of the user below.", - "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?" + "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", + "licenseKey": "License Key", + "valid": "Valid", + "numberOfSites": "Number of Sites", + "licenseKeySearch": "Search license keys...", + "licenseKeyAdd": "Add License Key", + "type": "Type", + "licenseKeyRequired": "License key is required", + "licenseTermsAgree": "You must agree to the license terms", + "licenseErrorKeyLoad": "Failed to load license keys", + "licenseErrorKeyLoadDescription": "An error occurred loading license keys.", + "licenseErrorKeyDelete": "Failed to delete license key", + "licenseErrorKeyDeleteDescription": "An error occurred deleting license key.", + "licenseKeyDeleted": "License key deleted", + "licenseKeyDeletedDescription": "The license key has been deleted.", + "licenseErrorKeyActivate": "Failed to activate license key", + "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", + "licenseKeyActivated": "License key activated", + "licenseKeyActivatedDescription": "The license key has been successfully activated.", + "licenseErrorKeyRecheck": "Failed to recheck license keys", + "licenseErrorKeyRecheckDescription": "An error occurred rechecking license keys.", + "licenseErrorKeyRechecked": "License keys rechecked", + "licenseErrorKeyRecheckedDescription": "All license keys have been rechecked", + "licenseActivateKey": "Activate License Key", + "licenseActivateKeyDescription": "Enter a license key to activate it.", + "licenseActivate": "Activate License", + "licenseAgreement": "By checking this box, you confirm that you have read and agree to the license terms corresponding to the tier associated with your license key.", + "fossorialLicense": "View Fossorial Commercial License & Subscription Terms", + "licenseMessageRemove": "This will remove the license key and all associated permissions granted by it.", + "licenseMessageConfirm": "To confirm, please type the license key below.", + "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", + "licenseKeyDelete": "Delete License Key", + "licenseKeyDeleteConfirm": "Confirm Delete License Key", + "licenseTitle": "Manage License Status", + "licenseTitleDescription": "View and manage license keys in the system", + "licenseHost": "Host License", + "licenseHostDescription": "Manage the main license key for the host.", + "notLicensed": "Not Licensed", + "hostId": "Host ID", + "licenseReckeckAll": "Recheck All Keys", + "licenseSiteUsage": "Sites Usage", + "licenseSiteUsageDecsription": "View the number of sites using this license.", + "licenseNoSiteLimit": "There is no limit on the number of sites using an unlicensed host.", + "licensePurchase": "Purchase License", + "licensePurchaseSites": "Purchase Additional Sites", + "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", + "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} in system.", + "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", + "licenseFee": "License fee", + "licensePriceSite": "Price per site", + "total": "Total", + "licenseContinuePayment": "Continue to Payment", + "pricingPage": "pricing page", + "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " } diff --git a/src/app/admin/license/LicenseKeysDataTable.tsx b/src/app/admin/license/LicenseKeysDataTable.tsx index 98ed814a..a68d8273 100644 --- a/src/app/admin/license/LicenseKeysDataTable.tsx +++ b/src/app/admin/license/LicenseKeysDataTable.tsx @@ -13,6 +13,7 @@ import { LicenseKeyCache } from "@server/license/license"; import { ArrowUpDown } from "lucide-react"; import moment from "moment"; import CopyToClipboard from "@app/components/CopyToClipboard"; +import { useTranslations } from 'next-intl'; type LicenseKeysDataTableProps = { licenseKeys: LicenseKeyCache[]; @@ -32,6 +33,9 @@ export function LicenseKeysDataTable({ onDelete, onCreate }: LicenseKeysDataTableProps) { + + const t = useTranslations(); + const columns: ColumnDef[] = [ { accessorKey: "licenseKey", @@ -43,7 +47,7 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - License Key + {t('licenseKey')} ); @@ -68,7 +72,7 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Valid + {t('valid')} ); @@ -87,7 +91,7 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Type + {t('type')} ); @@ -112,7 +116,7 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - Number of Sites + {t('numberOfSites')} ); @@ -126,7 +130,7 @@ export function LicenseKeysDataTable({ variant="outlinePrimary" onClick={() => onDelete(row.original)} > - Delete + {t('delete')}
) @@ -138,10 +142,10 @@ export function LicenseKeysDataTable({ columns={columns} data={licenseKeys} title="License Keys" - searchPlaceholder="Search license keys..." + searchPlaceholder={t('licenseKeySearch')} searchColumn="licenseKey" onAdd={onCreate} - addButtonText="Add License Key" + addButtonText={t('licenseKeyAdd')} /> ); } diff --git a/src/app/admin/license/components/SitePriceCalculator.tsx b/src/app/admin/license/components/SitePriceCalculator.tsx index cf771b51..427d56b2 100644 --- a/src/app/admin/license/components/SitePriceCalculator.tsx +++ b/src/app/admin/license/components/SitePriceCalculator.tsx @@ -16,6 +16,7 @@ import { CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; +import { useTranslations } from 'next-intl'; type SitePriceCalculatorProps = { isOpen: boolean; @@ -60,27 +61,26 @@ export function SitePriceCalculator({ ? licenseFlatRate + siteCount * pricePerSite : siteCount * pricePerSite; + const t = useTranslations(); + return ( {mode === "license" - ? "Purchase License" - : "Purchase Additional Sites"} + ? t('licensePurchase') + : t('licensePurchaseSites')} - Choose how many sites you want to{" "} - {mode === "license" - ? "purchase a license for. You can always add more sites later." - : "add to your existing license."} + {t('licensePurchaseDescription', {selectedMode: mode})}
- Number of Sites + {t('numberOfSites')}
+ diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index a9678898..41c159f8 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -57,6 +57,7 @@ import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { SitePriceCalculator } from "./components/SitePriceCalculator"; import Link from "next/link"; import { Checkbox } from "@app/components/ui/checkbox"; +import { useTranslations } from 'next-intl'; const formSchema = z.object({ licenseKey: z @@ -77,6 +78,7 @@ function obfuscateLicenseKey(key: string): string { export default function LicensePage() { const api = createApiClient(useEnvContext()); + const t = useTranslations(); const [rows, setRows] = useState([]); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); @@ -129,11 +131,8 @@ export default function LicensePage() { } } catch (e) { toast({ - title: "Failed to load license keys", - description: formatAxiosError( - e, - "An error occurred loading license keys" - ) + title: t('licenseErrorKeyLoad'), + description: formatAxiosError(e, t('licenseErrorKeyLoadDescription')) }); } } @@ -148,17 +147,14 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: "License key deleted", - description: "The license key has been deleted" + title: t('licenseKeyDeleted'), + description: t('licenseKeyDeletedDescription') }); setIsDeleteModalOpen(false); } catch (e) { toast({ - title: "Failed to delete license key", - description: formatAxiosError( - e, - "An error occurred deleting license key" - ) + title: t('licenseErrorKeyDelete'), + description: formatAxiosError(e, t('licenseErrorKeyDeleteDescription')) }); } finally { setIsDeletingLicense(false); @@ -174,16 +170,13 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: "License keys rechecked", - description: "All license keys have been rechecked" + title: t('licenseErrorKeyRechecked'), + description: t('licenseErrorKeyRecheckedDescription') }); } catch (e) { toast({ - title: "Failed to recheck license keys", - description: formatAxiosError( - e, - "An error occurred rechecking license keys" - ) + title: t('licenseErrorKeyRecheck'), + description: formatAxiosError(e, t('licenseErrorKeyRecheckDescription')) }); } finally { setIsRecheckingLicense(false); @@ -201,8 +194,8 @@ export default function LicensePage() { } toast({ - title: "License key activated", - description: "The license key has been successfully activated." + title: t('licenseKeyActivated'), + description: t('licenseKeyActivatedDescription') }); setIsCreateModalOpen(false); @@ -211,11 +204,8 @@ export default function LicensePage() { } catch (e) { toast({ variant: "destructive", - title: "Failed to activate license key", - description: formatAxiosError( - e, - "An error occurred while activating the license key." - ) + title: t('licenseErrorKeyActivate'), + description: formatAxiosError(e, t('licenseErrorKeyActivateDescription')) }); } finally { setIsActivatingLicense(false); @@ -245,9 +235,9 @@ export default function LicensePage() { > - Activate License Key + {t('licenseActivateKey')} - Enter a license key to activate it. + {t('licenseActivateKeyDescription')} @@ -262,7 +252,7 @@ export default function LicensePage() { name="licenseKey" render={({ field }) => ( - License Key + {t('licenseKey')} @@ -285,12 +275,7 @@ export default function LicensePage() {
- By checking this box, you - confirm that you have read - and agree to the license - terms corresponding to the - tier associated with your - license key. + {t('licenseAgreement')}
- View Fossorial - Commercial License & - Subscription Terms + {t('fossorialLicense')}
@@ -313,7 +296,7 @@ export default function LicensePage() { - + @@ -336,47 +319,40 @@ export default function LicensePage() { }} dialog={
-

- Are you sure you want to delete the license key{" "} - - {obfuscateLicenseKey( - selectedLicenseKey.licenseKey - )} - - ? +

+ {t('licenseQuestionRemove', {selectedKey: obfuscateLicenseKey(selectedLicenseKey.licenseKey)})}

- This will remove the license key and all - associated permissions granted by it. + {t('licenseMessageRemove')}

- To confirm, please type the license key below. + {t('licenseMessageConfirm')}

} - buttonText="Confirm Delete License Key" + buttonText={t('licenseKeyDeleteConfirm')} onConfirm={async () => deleteLicenseKey(selectedLicenseKey.licenseKeyEncrypted) } string={selectedLicenseKey.licenseKey} - title="Delete License Key" + title={t('licenseKeyDelete')} /> )} - Host License + {t('licenseHost')} - Manage the main license key for the host. + {t('licenseHostDescription')}
@@ -397,7 +373,7 @@ export default function LicensePage() { ) : (
- Not Licensed + {t('notLicensed')}
)} @@ -405,7 +381,7 @@ export default function LicensePage() { {licenseStatus?.hostId && (
- Host ID + {t('hostId')}
@@ -413,7 +389,7 @@ export default function LicensePage() { {hostLicense && (
- License Key + {t('licenseKey')}
- Recheck All Keys + {t('licenseReckeckAll')} - Sites Usage + {t('licenseSiteUsage')} - View the number of sites using this license. + {t('licenseSiteUsageDecsription')}
- {licenseStatus?.usedSites || 0}{" "} - {licenseStatus?.usedSites === 1 - ? "site" - : "sites"}{" "} - in system + {t('licenseSitesUsed', {count: licenseStatus?.usedSites || 0})}
{!licenseStatus?.isHostLicensed && (

- There is no limit on the number of sites - using an unlicensed host. + {t('licenseNoSiteLimit')}

)} {licenseStatus?.maxSites && (
- {licenseStatus.usedSites || 0} of{" "} - {licenseStatus.maxSites} sites used + {t('licenseSitesUsedMax', {usedSites: licenseStatus.usedSites || 0, maxSites: licenseStatus.maxSites})} {Math.round( @@ -495,7 +465,7 @@ export default function LicensePage() { setIsPurchaseModalOpen(true); }} > - Purchase License + {t('licensePurchase')} ) : ( @@ -507,7 +477,7 @@ export default function LicensePage() { setIsPurchaseModalOpen(true); }} > - Purchase Additional Sites + {t('licensePurchaseSites')} )} From cae4f5d840cb3dd8fd38265beb937d1650a2dd1d Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:15 +0200 Subject: [PATCH 039/105] New translations en-us.json (French) --- messages/fr-FR.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 9aaf1654..355abe52 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Apprenez à configurer les ressources TCP/UDP", "resourceBack": "Retour aux ressources", "resourceGoTo": "Aller à la ressource", + "resourceDelete": "Supprimer la ressource", + "resourceDeleteConfirm": "Confirmer la suppression de la ressource", "visibility": "Visibilité", "enabled": "Activé", "disabled": "Désactivé", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Confirmer la suppression de la clé API", "apiKeysDelete": "Supprimer la clé API", "apiKeysManage": "Gérer les clés API", - "apiKeysDescription": "Les clés API sont utilisées pour s'authentifier avec l'API d'intégration" + "apiKeysDescription": "Les clés API sont utilisées pour s'authentifier avec l'API d'intégration", + "userTitle": "Gérer tous les utilisateurs", + "userDescription": "Voir et gérer tous les utilisateurs du système", + "userAbount": "À propos de la gestion des utilisateurs", + "userAbountDescription": "Cette table affiche tous les objets utilisateur root du système. Chaque utilisateur peut appartenir à plusieurs organisations. La suppression d'un utilisateur d'une organisation ne supprime pas son objet utilisateur root - il restera dans le système. Pour supprimer complètement un utilisateur du système, vous devez supprimer son objet utilisateur root en utilisant l'action de suppression dans cette table.", + "userServer": "Utilisateurs du serveur", + "userSearch": "Rechercher des utilisateurs du serveur...", + "userErrorDelete": "Erreur lors de la suppression de l'utilisateur", + "userDeleteConfirm": "Confirmer la suppression de l'utilisateur", + "userDeleteServer": "Supprimer l'utilisateur du serveur", + "userMessageRemove": "L'utilisateur sera retiré de toutes les organisations et sera complètement retiré du serveur.", + "userMessageConfirm": "Pour confirmer, veuillez saisir le nom de l'utilisateur ci-dessous.", + "userQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement {selectedUser} du serveur?", + "licenseKey": "Clé de licence", + "valid": "Valid", + "numberOfSites": "Nombre de sites", + "licenseKeySearch": "Rechercher des clés de licence...", + "licenseKeyAdd": "Ajouter une clé de licence", + "type": "Type de texte", + "licenseKeyRequired": "La clé de licence est requise", + "licenseTermsAgree": "Vous devez accepter les conditions de licence", + "licenseErrorKeyLoad": "Impossible de charger les clés de licence", + "licenseErrorKeyLoadDescription": "Une erreur s'est produite lors du chargement des clés de licence.", + "licenseErrorKeyDelete": "Échec de la suppression de la clé de licence", + "licenseErrorKeyDeleteDescription": "Une erreur s'est produite lors de la suppression de la clé de licence.", + "licenseKeyDeleted": "Clé de licence supprimée", + "licenseKeyDeletedDescription": "La clé de licence a été supprimée.", + "licenseErrorKeyActivate": "Échec de l'activation de la clé de licence", + "licenseErrorKeyActivateDescription": "Une erreur s'est produite lors de l'activation de la clé de licence.", + "licenseKeyActivated": "Clé de licence activée", + "licenseKeyActivatedDescription": "La clé de licence a été activée avec succès.", + "licenseErrorKeyRecheck": "Impossible de revérifier les clés de licence", + "licenseErrorKeyRecheckDescription": "Une erreur s'est produite lors de la revérification des clés de licence.", + "licenseErrorKeyRechecked": "Clés de licence revérifiées", + "licenseErrorKeyRecheckedDescription": "Toutes les clés de licence ont été revérifiées", + "licenseActivateKey": "Activer la clé de licence", + "licenseActivateKeyDescription": "Entrez une clé de licence pour l'activer.", + "licenseActivate": "Activer la licence", + "licenseAgreement": "En cochant cette case, vous confirmez avoir lu et accepté les conditions de licence correspondant au niveau associé à votre clé de licence.", + "fossorialLicense": "Voir les conditions de licence commerciale et d'abonnement Fossorial", + "licenseMessageRemove": "Cela supprimera la clé de licence et toutes les autorisations qui lui sont associées.", + "licenseMessageConfirm": "Pour confirmer, veuillez saisir la clé de licence ci-dessous.", + "licenseQuestionRemove": "Êtes-vous sûr de vouloir supprimer la clé de licence {selectedKey}?", + "licenseKeyDelete": "Supprimer la clé de licence", + "licenseKeyDeleteConfirm": "Confirmer la suppression de la clé de licence", + "licenseTitle": "Gérer le statut de la licence", + "licenseTitleDescription": "Voir et gérer les clés de licence dans le système", + "licenseHost": "Licence Hôte", + "licenseHostDescription": "Gérer la clé de licence principale de l'hôte.", + "notLicensed": "Non licencié", + "hostId": "ID de l'hôte", + "licenseReckeckAll": "Revérifier toutes les clés", + "licenseSiteUsage": "Utilisation des sites", + "licenseSiteUsageDecsription": "Voir le nombre de sites utilisant cette licence.", + "licenseNoSiteLimit": "Il n'y a pas de limite sur le nombre de sites utilisant un hôte non autorisé.", + "licensePurchase": "Acheter une licence", + "licensePurchaseSites": "Acheter des sites supplémentaires", + "licenseSitesUsedMax": "{usedSites} des sites {maxSites} utilisés", + "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} dans le système.", + "licensePurchaseDescription": "Choisissez le nombre de sites que vous voulez {selectedMode, select, license {achetez une licence. Vous pouvez toujours ajouter plus de sites plus tard.} other {ajouter à votre licence existante.}}", + "licenseFee": "Frais de licence", + "licensePriceSite": "Prix par site", + "total": "Total", + "licenseContinuePayment": "Continuer vers le paiement", + "pricingPage": "page de tarification", + "licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le " } From 23f9d314df406daeb1526cb57c56040cdcf63162 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:16 +0200 Subject: [PATCH 040/105] New translations en-us.json (German) --- messages/de-DE.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 50173903..08f6834e 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Lernen Sie, wie Sie TCP/UDP Ressourcen konfigurieren", "resourceBack": "Zurück zu den Ressourcen", "resourceGoTo": "Zu Ressource gehen", + "resourceDelete": "Ressource löschen", + "resourceDeleteConfirm": "Ressource löschen bestätigen", "visibility": "Sichtbarkeit", "enabled": "Aktiviert", "disabled": "Deaktiviert", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Löschen des API-Schlüssels bestätigen", "apiKeysDelete": "API-Schlüssel löschen", "apiKeysManage": "API-Schlüssel verwalten", - "apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet" + "apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet", + "userTitle": "Alle Benutzer verwalten", + "userDescription": "Alle Benutzer im System anzeigen und verwalten", + "userAbount": "Über Benutzerverwaltung", + "userAbountDescription": "Diese Tabelle zeigt alle root-Benutzerobjekte im System an. Jeder Benutzer kann zu mehreren Organisationen gehören. Das Entfernen eines Benutzers aus einer Organisation löscht nicht sein Root-Benutzerobjekt - er bleibt im System. Um einen Benutzer komplett aus dem System zu entfernen, müssen Sie sein Root-Benutzerobjekt mit der Lösch-Aktion in dieser Tabelle löschen.", + "userServer": "Server Benutzer", + "userSearch": "Serverbenutzer suchen...", + "userErrorDelete": "Fehler beim Löschen des Benutzers", + "userDeleteConfirm": "Benutzer löschen bestätigen", + "userDeleteServer": "Benutzer vom Server löschen", + "userMessageRemove": "Der Benutzer wird von allen Organisationen entfernt und vollständig vom Server entfernt.", + "userMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Benutzers unten ein.", + "userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?", + "licenseKey": "Lizenzschlüssel", + "valid": "Valid", + "numberOfSites": "Anzahl der Sites", + "licenseKeySearch": "Lizenzschlüssel suchen...", + "licenseKeyAdd": "Lizenzschlüssel hinzufügen", + "type": "Typ", + "licenseKeyRequired": "Lizenzschlüssel ist erforderlich", + "licenseTermsAgree": "Sie müssen den Lizenzbedingungen zustimmen", + "licenseErrorKeyLoad": "Fehler beim Laden der Lizenzschlüssel", + "licenseErrorKeyLoadDescription": "Beim Laden der Lizenzschlüssel ist ein Fehler aufgetreten.", + "licenseErrorKeyDelete": "Fehler beim Löschen des Lizenzschlüssels", + "licenseErrorKeyDeleteDescription": "Beim Löschen des Lizenzschlüssels ist ein Fehler aufgetreten.", + "licenseKeyDeleted": "Lizenzschlüssel gelöscht", + "licenseKeyDeletedDescription": "Der Lizenzschlüssel wurde gelöscht.", + "licenseErrorKeyActivate": "Fehler beim Aktivieren des Lizenzschlüssels", + "licenseErrorKeyActivateDescription": "Beim Aktivieren des Lizenzschlüssels ist ein Fehler aufgetreten.", + "licenseKeyActivated": "Lizenzschlüssel aktiviert", + "licenseKeyActivatedDescription": "Der Lizenzschlüssel wurde erfolgreich aktiviert.", + "licenseErrorKeyRecheck": "Fehler beim Überprüfen der Lizenzschlüssel", + "licenseErrorKeyRecheckDescription": "Ein Fehler trat auf beim Wiederherstellen der Lizenzschlüssel.", + "licenseErrorKeyRechecked": "Lizenzschlüssel neu geladen", + "licenseErrorKeyRecheckedDescription": "Alle Lizenzschlüssel wurden neu geladen", + "licenseActivateKey": "Lizenzschlüssel aktivieren", + "licenseActivateKeyDescription": "Geben Sie einen Lizenzschlüssel ein, um ihn zu aktivieren.", + "licenseActivate": "Lizenz aktivieren", + "licenseAgreement": "Durch Ankreuzung dieses Kästchens bestätigen Sie, dass Sie die Lizenzbedingungen gelesen und akzeptiert haben, die mit dem Lizenzschlüssel in Verbindung stehen.", + "fossorialLicense": "Fossorial Gewerbelizenz & Abonnementbedingungen anzeigen", + "licenseMessageRemove": "Dadurch werden der Lizenzschlüssel und alle zugehörigen Berechtigungen entfernt.", + "licenseMessageConfirm": "Um zu bestätigen, geben Sie bitte den Lizenzschlüssel unten ein.", + "licenseQuestionRemove": "Sind Sie sicher, dass Sie den Lizenzschlüssel {selectedKey} löschen möchten?", + "licenseKeyDelete": "Lizenzschlüssel löschen", + "licenseKeyDeleteConfirm": "Lizenzschlüssel löschen bestätigen", + "licenseTitle": "Lizenzstatus verwalten", + "licenseTitleDescription": "Lizenzschlüssel im System anzeigen und verwalten", + "licenseHost": "Hostlizenz", + "licenseHostDescription": "Verwalten Sie den Haupt-Lizenzschlüssel für den Host.", + "notLicensed": "Nicht lizenziert", + "hostId": "Host-ID", + "licenseReckeckAll": "Überprüfe alle Schlüssel", + "licenseSiteUsage": "Website-Nutzung", + "licenseSiteUsageDecsription": "Sehen Sie sich die Anzahl der Sites an, die diese Lizenz verwenden.", + "licenseNoSiteLimit": "Die Anzahl der Sites, die einen nicht lizenzierten Host verwenden, ist unbegrenzt.", + "licensePurchase": "Lizenz kaufen", + "licensePurchaseSites": "Zusätzliche Seiten kaufen", + "licenseSitesUsedMax": "{usedSites} der {maxSites} Seiten verwendet", + "licenseSitesUsed": "{count, plural, =0 {# Seiten} =1 {# Seite} other {# Seiten}} im System.", + "licensePurchaseDescription": "Wähle aus, für wieviele Seiten du möchtest {selectedMode, select, license {kaufe eine Lizenz. Du kannst später immer weitere Seiten hinzufügen.} other {Füge zu deiner bestehenden Lizenz hinzu.}}", + "licenseFee": "Lizenzgebühr", + "licensePriceSite": "Preis pro Seite", + "total": "Gesamt", + "licenseContinuePayment": "Weiter zur Zahlung", + "pricingPage": "Preisseite", + "licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die " } From d47c2f9dcf2c8be39bdce159500b67dbce12d457 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:17 +0200 Subject: [PATCH 041/105] New translations en-us.json (Italian) --- messages/it-IT.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 3909a646..5f54fe18 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Scopri come configurare le risorse TCP/UDP", "resourceBack": "Torna alle risorse", "resourceGoTo": "Vai alla Risorsa", + "resourceDelete": "Elimina Risorsa", + "resourceDeleteConfirm": "Conferma Eliminazione Risorsa", "visibility": "Visibilità", "enabled": "Abilitato", "disabled": "Disabilitato", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Conferma Eliminazione Chiave API", "apiKeysDelete": "Elimina Chiave API", "apiKeysManage": "Gestisci Chiavi API", - "apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione" + "apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione", + "userTitle": "Gestisci Tutti Gli Utenti", + "userDescription": "Visualizza e gestisci tutti gli utenti del sistema", + "userAbount": "Informazioni Sulla Gestione Utente", + "userAbountDescription": "Questa tabella mostra tutti gli oggetti utente root nel sistema. Ogni utente può appartenere a più organizzazioni. La rimozione di un utente da un'organizzazione non elimina il suo oggetto utente root, che rimarrà nel sistema. Per rimuovere completamente un utente dal sistema, è necessario eliminare il loro oggetto utente root utilizzando l'azione di eliminazione in questa tabella.", + "userServer": "Utenti Server", + "userSearch": "Cerca utenti del server...", + "userErrorDelete": "Errore nell'eliminare l'utente", + "userDeleteConfirm": "Conferma Eliminazione Utente", + "userDeleteServer": "Elimina utente dal server", + "userMessageRemove": "L'utente verrà rimosso da tutte le organizzazioni ed essere completamente rimosso dal server.", + "userMessageConfirm": "Per confermare, digita il nome dell'utente qui sotto.", + "userQuestionRemove": "Sei sicuro di voler eliminare definitivamente {selectedUser} dal server?", + "licenseKey": "Chiave Di Licenza", + "valid": "Valid", + "numberOfSites": "Numero di siti", + "licenseKeySearch": "Cerca chiavi di licenza...", + "licenseKeyAdd": "Aggiungi Chiave Di Licenza", + "type": "Tipo", + "licenseKeyRequired": "La chiave di licenza è obbligatoria", + "licenseTermsAgree": "Devi accettare i termini della licenza", + "licenseErrorKeyLoad": "Impossibile caricare le chiavi di licenza", + "licenseErrorKeyLoadDescription": "Si è verificato un errore durante il caricamento delle chiavi di licenza.", + "licenseErrorKeyDelete": "Impossibile eliminare la chiave di licenza", + "licenseErrorKeyDeleteDescription": "Si è verificato un errore durante l'eliminazione della chiave di licenza.", + "licenseKeyDeleted": "Chiave di licenza eliminata", + "licenseKeyDeletedDescription": "La chiave di licenza è stata eliminata.", + "licenseErrorKeyActivate": "Attivazione della chiave di licenza non riuscita", + "licenseErrorKeyActivateDescription": "Si è verificato un errore nell'attivazione della chiave di licenza.", + "licenseKeyActivated": "Chiave di licenza attivata", + "licenseKeyActivatedDescription": "La chiave di licenza è stata attivata correttamente.", + "licenseErrorKeyRecheck": "Impossibile ricontrollare le chiavi di licenza", + "licenseErrorKeyRecheckDescription": "Si è verificato un errore nel ricontrollare le chiavi di licenza.", + "licenseErrorKeyRechecked": "Chiavi di licenza ricontrollate", + "licenseErrorKeyRecheckedDescription": "Tutte le chiavi di licenza sono state ricontrollate", + "licenseActivateKey": "Attiva Chiave Di Licenza", + "licenseActivateKeyDescription": "Inserisci una chiave di licenza per attivarla.", + "licenseActivate": "Attiva Licenza", + "licenseAgreement": "Selezionando questa casella, confermi di aver letto e accettato i termini di licenza corrispondenti al livello associato alla chiave di licenza.", + "fossorialLicense": "Visualizza I Termini Di Licenza Commerciale Fossorial E Abbonamento", + "licenseMessageRemove": "Questo rimuoverà la chiave di licenza e tutti i permessi associati da essa concessi.", + "licenseMessageConfirm": "Per confermare, digitare la chiave di licenza qui sotto.", + "licenseQuestionRemove": "Sei sicuro di voler eliminare la chiave di licenza {selectedKey}?", + "licenseKeyDelete": "Elimina Chiave Di Licenza", + "licenseKeyDeleteConfirm": "Conferma Elimina Chiave Di Licenza", + "licenseTitle": "Gestisci Stato Licenza", + "licenseTitleDescription": "Visualizza e gestisci le chiavi di licenza nel sistema", + "licenseHost": "Licenza Host", + "licenseHostDescription": "Gestisci la chiave di licenza principale per l'host.", + "notLicensed": "Non Licenziato", + "hostId": "Host ID", + "licenseReckeckAll": "Ricontrolla Tutte Le Tasti", + "licenseSiteUsage": "Utilizzo Siti", + "licenseSiteUsageDecsription": "Visualizza il numero di siti che utilizzano questa licenza.", + "licenseNoSiteLimit": "Non c'è alcun limite al numero di siti che utilizzano un host senza licenza.", + "licensePurchase": "Acquista Licenza", + "licensePurchaseSites": "Acquista Siti Aggiuntivi", + "licenseSitesUsedMax": "{usedSites} di {maxSites} siti utilizzati", + "licenseSitesUsed": "{count, plural, =0 {# siti} =1 {# sito} other {# siti}} nel sistema.", + "licensePurchaseDescription": "Scegli quanti siti vuoi {selectedMode, select, license {acquista una licenza. Puoi sempre aggiungere altri siti più tardi.} other {aggiungi alla tua licenza esistente.}}", + "licenseFee": "Costo della licenza", + "licensePriceSite": "Prezzo per sito", + "total": "Totale", + "licenseContinuePayment": "Continua al pagamento", + "pricingPage": "pagina prezzi", + "licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il " } From afc6ee596d02610411e0a4ae56d7aca8f6849ac4 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:17 +0200 Subject: [PATCH 042/105] New translations en-us.json (Polish) --- messages/pl-PL.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 313ca200..630b88cf 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Dowiedz się, jak skonfigurować zasoby TCP/UDP", "resourceBack": "Powrót do zasobów", "resourceGoTo": "Przejdź do zasobu", + "resourceDelete": "Usuń zasób", + "resourceDeleteConfirm": "Potwierdź usunięcie zasobu", "visibility": "Widoczność", "enabled": "Włączone", "disabled": "Wyłączone", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Potwierdź usunięcie klucza API", "apiKeysDelete": "Usuń klucz API", "apiKeysManage": "Zarządzaj kluczami API", - "apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji" + "apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji", + "userTitle": "Zarządzaj wszystkimi użytkownikami", + "userDescription": "Zobacz i zarządzaj wszystkimi użytkownikami w systemie", + "userAbount": "O zarządzaniu użytkownikami", + "userAbountDescription": "Ta tabela wyświetla wszystkie obiekty użytkownika root w systemie. Każdy użytkownik może należeć do wielu organizacji. Usunięcie użytkownika z organizacji nie usuwa ich głównego obiektu użytkownika - pozostanie on w systemie. Aby całkowicie usunąć użytkownika z systemu, musisz usunąć jego obiekt root użytkownika za pomocą akcji usuwania z tej tabeli.", + "userServer": "Użytkownicy serwera", + "userSearch": "Szukaj użytkowników serwera...", + "userErrorDelete": "Błąd podczas usuwania użytkownika", + "userDeleteConfirm": "Potwierdź usunięcie użytkownika", + "userDeleteServer": "Usuń użytkownika z serwera", + "userMessageRemove": "Użytkownik zostanie usunięty ze wszystkich organizacji i całkowicie usunięty z serwera.", + "userMessageConfirm": "Aby potwierdzić, wpisz nazwę użytkownika poniżej.", + "userQuestionRemove": "Czy na pewno chcesz trwale usunąć {selectedUser} z serwera?", + "licenseKey": "Klucz licencyjny", + "valid": "Valid", + "numberOfSites": "Liczba witryn", + "licenseKeySearch": "Szukaj kluczy licencyjnych...", + "licenseKeyAdd": "Dodaj klucz licencyjny", + "type": "Typ", + "licenseKeyRequired": "Klucz licencyjny jest wymagany", + "licenseTermsAgree": "Musisz wyrazić zgodę na warunki licencji", + "licenseErrorKeyLoad": "Nie udało się załadować kluczy licencyjnych", + "licenseErrorKeyLoadDescription": "Wystąpił błąd podczas ładowania kluczy licencyjnych.", + "licenseErrorKeyDelete": "Nie udało się usunąć klucza licencyjnego", + "licenseErrorKeyDeleteDescription": "Wystąpił błąd podczas usuwania klucza licencyjnego.", + "licenseKeyDeleted": "Klucz licencji został usunięty", + "licenseKeyDeletedDescription": "Klucz licencyjny został usunięty.", + "licenseErrorKeyActivate": "Nie udało się aktywować klucza licencji", + "licenseErrorKeyActivateDescription": "Wystąpił błąd podczas aktywacji klucza licencyjnego.", + "licenseKeyActivated": "Klucz licencyjny aktywowany", + "licenseKeyActivatedDescription": "Klucz licencyjny został pomyślnie aktywowany.", + "licenseErrorKeyRecheck": "Nie udało się ponownie sprawdzić kluczy licencyjnych", + "licenseErrorKeyRecheckDescription": "Wystąpił błąd podczas ponownego sprawdzania kluczy licencyjnych.", + "licenseErrorKeyRechecked": "Klucze licencyjne ponownie sprawdzone", + "licenseErrorKeyRecheckedDescription": "Wszystkie klucze licencyjne zostały ponownie sprawdzone", + "licenseActivateKey": "Aktywuj klucz licencyjny", + "licenseActivateKeyDescription": "Wprowadź klucz licencyjny, aby go aktywować.", + "licenseActivate": "Aktywuj licencję", + "licenseAgreement": "Zaznaczając to pole, potwierdzasz, że przeczytałeś i zgadzasz się na warunki licencji odpowiadające poziomowi powiązanemu z kluczem licencyjnym.", + "fossorialLicense": "Zobacz Fossorial Commercial License & Subskrypcja", + "licenseMessageRemove": "Spowoduje to usunięcie klucza licencyjnego i wszystkich przypisanych przez niego uprawnień.", + "licenseMessageConfirm": "Aby potwierdzić, wpisz klucz licencyjny poniżej.", + "licenseQuestionRemove": "Czy na pewno chcesz usunąć klucz licencyjny {selectedKey}?", + "licenseKeyDelete": "Usuń klucz licencyjny", + "licenseKeyDeleteConfirm": "Potwierdź usunięcie klucza licencyjnego", + "licenseTitle": "Zarządzaj statusem licencji", + "licenseTitleDescription": "Wyświetl i zarządzaj kluczami licencyjnymi w systemie", + "licenseHost": "Licencja hosta", + "licenseHostDescription": "Zarządzaj głównym kluczem licencyjnym hosta.", + "notLicensed": "Brak licencji", + "hostId": "ID hosta", + "licenseReckeckAll": "Sprawdź ponownie wszystkie klucze", + "licenseSiteUsage": "Użycie witryn", + "licenseSiteUsageDecsription": "Zobacz liczbę witryn korzystających z tej licencji.", + "licenseNoSiteLimit": "Nie ma limitu liczby witryn używających nielicencjonowanego hosta.", + "licensePurchase": "Kup licencję", + "licensePurchaseSites": "Kup dodatkowe witryny", + "licenseSitesUsedMax": "Użyte strony {usedSites} z {maxSites}", + "licenseSitesUsed": "{count, plural, =0 {# witryn} =1 {# witryn} other {# witryn}} w systemie.", + "licensePurchaseDescription": "Wybierz ile witryn chcesz {selectedMode, select, license {kupić licencję. Zawsze możesz dodać więcej witryn później.} other {dodaj do swojej istniejącej licencji.}}", + "licenseFee": "Opłata licencyjna", + "licensePriceSite": "Cena za witrynę", + "total": "Łącznie", + "licenseContinuePayment": "Przejdź do płatności", + "pricingPage": "strona cenowa", + "licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź " } From 75dc6edd51db648d10e834ded0758519e673b92e Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:18 +0200 Subject: [PATCH 043/105] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index f45b0b05..1cee3d91 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Aprenda como configurar os recursos TCP/UDP", "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", + "resourceDelete": "Excluir Recurso", + "resourceDeleteConfirm": "Confirmar exclusão de recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", "apiKeysManage": "Gerenciar Chaves API", - "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração" + "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", + "userTitle": "Gerenciar Todos os Usuários", + "userDescription": "Visualizar e gerenciar todos os usuários no sistema", + "userAbount": "Sobre a Gestão de Usuário", + "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", + "userServer": "Usuários do Servidor", + "userSearch": "Pesquisar usuários do servidor...", + "userErrorDelete": "Erro ao excluir usuário", + "userDeleteConfirm": "Confirmar Exclusão do Usuário", + "userDeleteServer": "Excluir usuário do servidor", + "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", + "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", + "licenseKey": "Chave de Licença", + "valid": "Valid", + "numberOfSites": "Número de sites", + "licenseKeySearch": "Pesquisar chaves da licença...", + "licenseKeyAdd": "Adicionar chave de licença", + "type": "tipo", + "licenseKeyRequired": "A chave da licença é necessária", + "licenseTermsAgree": "Você deve concordar com os termos da licença", + "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", + "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", + "licenseErrorKeyDelete": "Falha ao excluir chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", + "licenseKeyDeleted": "Chave da licença excluída", + "licenseKeyDeletedDescription": "A chave da licença foi excluída.", + "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", + "licenseErrorKeyActivateDescription": "Ocorreu um erro ao ativar a chave da licença.", + "licenseKeyActivated": "Chave de licença ativada", + "licenseKeyActivatedDescription": "A chave de licença foi ativada com sucesso.", + "licenseErrorKeyRecheck": "Falha ao verificar novamente as chaves de licença", + "licenseErrorKeyRecheckDescription": "Ocorreu um erro ao reverificar a chave de licença.", + "licenseErrorKeyRechecked": "Chaves de licença reverificadas", + "licenseErrorKeyRecheckedDescription": "Todas as chaves de licença foram remarcadas", + "licenseActivateKey": "Ativar Chave de Licença", + "licenseActivateKeyDescription": "Insira uma chave de licença para ativá-la.", + "licenseActivate": "Ativar Licença", + "licenseAgreement": "Ao marcar esta caixa, você confirma que leu e concorda com os termos de licença correspondentes ao nível associado à sua chave de licença.", + "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", + "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", + "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", + "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", + "licenseKeyDelete": "Excluir Chave de Licença", + "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", + "licenseTitle": "Gerenciar Status da Licença", + "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", + "licenseHost": "Licença do host", + "licenseHostDescription": "Gerenciar a chave de licença principal do host.", + "notLicensed": "Não Licenciado", + "hostId": "ID do host", + "licenseReckeckAll": "Verifique novamente todas as chaves", + "licenseSiteUsage": "Uso de Sites", + "licenseSiteUsageDecsription": "Exibir o número de sites utilizando esta licença.", + "licenseNoSiteLimit": "Não há limite para o número de sites utilizando um host não licenciado.", + "licensePurchase": "Comprar Licença", + "licensePurchaseSites": "Comprar Sites Adicionais", + "licenseSitesUsedMax": "{usedSites} de {maxSites} utilizados", + "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} no sistema.", + "licensePurchaseDescription": "Escolha quantos sites você quer {selectedMode, select, license {Compre uma licença. Você sempre pode adicionar mais sites depois.} other {adicione à sua licença existente.}}", + "licenseFee": "Taxa de licença", + "licensePriceSite": "Preço por site", + "total": "Total:", + "licenseContinuePayment": "Continuar para o pagamento", + "pricingPage": "Página de preços", + "licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite " } From 2bd06ff493d16b9511af1710ef6896a6159900e6 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 6 May 2025 12:13:19 +0200 Subject: [PATCH 044/105] New translations en-us.json (Turkish) --- messages/tr-TR.json | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 6f2c0735..09838cc8 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -190,6 +190,8 @@ "resourceLearnRaw": "Learn how to configure TCP/UDP resources", "resourceBack": "Back to Resources", "resourceGoTo": "Go to Resource", + "resourceDelete": "Delete Resource", + "resourceDeleteConfirm": "Confirm Delete Resource", "visibility": "Visibility", "enabled": "Enabled", "disabled": "Disabled", @@ -283,5 +285,70 @@ "apiKeysDeleteConfirm": "Confirm Delete API Key", "apiKeysDelete": "Delete API Key", "apiKeysManage": "Manage API Keys", - "apiKeysDescription": "API keys are used to authenticate with the integration API" + "apiKeysDescription": "API keys are used to authenticate with the integration API", + "userTitle": "Manage All Users", + "userDescription": "View and manage all users in the system", + "userAbount": "About User Management", + "userAbountDescription": "This table displays all root user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their root user object - they will remain in the system. To completely remove a user from the system, you must delete their root user object using the delete action in this table.", + "userServer": "Server Users", + "userSearch": "Search server users...", + "userErrorDelete": "Error deleting user", + "userDeleteConfirm": "Confirm Delete User", + "userDeleteServer": "Delete User from Server", + "userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.", + "userMessageConfirm": "To confirm, please type the name of the user below.", + "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", + "licenseKey": "License Key", + "valid": "Valid", + "numberOfSites": "Number of Sites", + "licenseKeySearch": "Search license keys...", + "licenseKeyAdd": "Add License Key", + "type": "Type", + "licenseKeyRequired": "License key is required", + "licenseTermsAgree": "You must agree to the license terms", + "licenseErrorKeyLoad": "Failed to load license keys", + "licenseErrorKeyLoadDescription": "An error occurred loading license keys.", + "licenseErrorKeyDelete": "Failed to delete license key", + "licenseErrorKeyDeleteDescription": "An error occurred deleting license key.", + "licenseKeyDeleted": "License key deleted", + "licenseKeyDeletedDescription": "The license key has been deleted.", + "licenseErrorKeyActivate": "Failed to activate license key", + "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", + "licenseKeyActivated": "License key activated", + "licenseKeyActivatedDescription": "The license key has been successfully activated.", + "licenseErrorKeyRecheck": "Failed to recheck license keys", + "licenseErrorKeyRecheckDescription": "An error occurred rechecking license keys.", + "licenseErrorKeyRechecked": "License keys rechecked", + "licenseErrorKeyRecheckedDescription": "All license keys have been rechecked", + "licenseActivateKey": "Activate License Key", + "licenseActivateKeyDescription": "Enter a license key to activate it.", + "licenseActivate": "Activate License", + "licenseAgreement": "By checking this box, you confirm that you have read and agree to the license terms corresponding to the tier associated with your license key.", + "fossorialLicense": "View Fossorial Commercial License & Subscription Terms", + "licenseMessageRemove": "This will remove the license key and all associated permissions granted by it.", + "licenseMessageConfirm": "To confirm, please type the license key below.", + "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", + "licenseKeyDelete": "Delete License Key", + "licenseKeyDeleteConfirm": "Confirm Delete License Key", + "licenseTitle": "Manage License Status", + "licenseTitleDescription": "View and manage license keys in the system", + "licenseHost": "Host License", + "licenseHostDescription": "Manage the main license key for the host.", + "notLicensed": "Not Licensed", + "hostId": "Host ID", + "licenseReckeckAll": "Recheck All Keys", + "licenseSiteUsage": "Sites Usage", + "licenseSiteUsageDecsription": "View the number of sites using this license.", + "licenseNoSiteLimit": "There is no limit on the number of sites using an unlicensed host.", + "licensePurchase": "Purchase License", + "licensePurchaseSites": "Purchase Additional Sites", + "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", + "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} in system.", + "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", + "licenseFee": "License fee", + "licensePriceSite": "Price per site", + "total": "Total", + "licenseContinuePayment": "Continue to Payment", + "pricingPage": "pricing page", + "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " } From c6ff868be839c4f91fdc36b665462f26f3c13850 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 6 May 2025 10:25:00 +0000 Subject: [PATCH 045/105] modified: src/app/setup/page.tsx --- src/app/setup/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 3ef5edf8..c6a63307 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -164,7 +164,7 @@ export default function StepperForm() { : "text-muted-foreground" }`} > - {t('setupCreateSite')} + {t('siteCreate')}
@@ -231,7 +231,7 @@ export default function StepperForm() { - {t('setupDisplayName')} + {t('orgDisplayName')} )} From d88fc132cc2c84cefaa35d339d16d4dec0aa3eb4 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 14:58:41 +0300 Subject: [PATCH 046/105] New translation keys in en-US locale --- messages/en-US.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/en-US.json b/messages/en-US.json index 09838cc8..ecaef172 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Delete API Key", "apiKeysManage": "Manage API Keys", "apiKeysDescription": "API keys are used to authenticate with the integration API", + "apiKeysSettings": "{apiKeyName} Settings", "userTitle": "Manage All Users", "userDescription": "View and manage all users in the system", "userAbount": "About User Management", From 3dba4aa36dad2cb5ef3d9df74926d5c9de8b9646 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 14:59:11 +0300 Subject: [PATCH 047/105] New translation keys in de-DE locale --- messages/de-DE.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/de-DE.json b/messages/de-DE.json index 08f6834e..aa41476f 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -286,6 +286,7 @@ "apiKeysDelete": "API-Schlüssel löschen", "apiKeysManage": "API-Schlüssel verwalten", "apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet", + "apiKeysSettings": "{apiKeyName} Einstellungen", "userTitle": "Alle Benutzer verwalten", "userDescription": "Alle Benutzer im System anzeigen und verwalten", "userAbount": "Über Benutzerverwaltung", From 58c12996f144e96fb6031925983d0670d7875c53 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 14:59:24 +0300 Subject: [PATCH 048/105] New translation keys in fr-FR locale --- messages/fr-FR.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 355abe52..3d879f33 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Supprimer la clé API", "apiKeysManage": "Gérer les clés API", "apiKeysDescription": "Les clés API sont utilisées pour s'authentifier avec l'API d'intégration", + "apiKeysSettings": "Paramètres de {apiKeyName}", "userTitle": "Gérer tous les utilisateurs", "userDescription": "Voir et gérer tous les utilisateurs du système", "userAbount": "À propos de la gestion des utilisateurs", From 31d54eb63cfbd668c3f6aed0d32448ddb085ec1d Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 14:59:41 +0300 Subject: [PATCH 049/105] New translation keys in it-IT locale --- messages/it-IT.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/it-IT.json b/messages/it-IT.json index 5f54fe18..9e42af9d 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Elimina Chiave API", "apiKeysManage": "Gestisci Chiavi API", "apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione", + "apiKeysSettings": "Impostazioni {apiKeyName}", "userTitle": "Gestisci Tutti Gli Utenti", "userDescription": "Visualizza e gestisci tutti gli utenti del sistema", "userAbount": "Informazioni Sulla Gestione Utente", From 99352aa2a9e67179e1f54266642dbe524d2b6442 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 14:59:52 +0300 Subject: [PATCH 050/105] New translation keys in pl-PL locale --- messages/pl-PL.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 630b88cf..6b538257 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Usuń klucz API", "apiKeysManage": "Zarządzaj kluczami API", "apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji", + "apiKeysSettings": "Ustawienia {apiKeyName}", "userTitle": "Zarządzaj wszystkimi użytkownikami", "userDescription": "Zobacz i zarządzaj wszystkimi użytkownikami w systemie", "userAbount": "O zarządzaniu użytkownikami", From bb7421c54e93eabcc329f4c2ecd29c06f5bd68ce Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 15:00:07 +0300 Subject: [PATCH 051/105] New translation keys in pt-PT locale --- messages/pt-PT.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1cee3d91..1f1d2da1 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Excluir Chave API", "apiKeysManage": "Gerenciar Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", + "apiKeysSettings": "Configurações de {apiKeyName}", "userTitle": "Gerenciar Todos os Usuários", "userDescription": "Visualizar e gerenciar todos os usuários no sistema", "userAbount": "Sobre a Gestão de Usuário", From 1ee8561e2ab211d5ddfc144179ee5e6073c085af Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 15:00:21 +0300 Subject: [PATCH 052/105] New translation keys in tr-TR locale --- messages/tr-TR.json | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 09838cc8..ecaef172 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -286,6 +286,7 @@ "apiKeysDelete": "Delete API Key", "apiKeysManage": "Manage API Keys", "apiKeysDescription": "API keys are used to authenticate with the integration API", + "apiKeysSettings": "{apiKeyName} Settings", "userTitle": "Manage All Users", "userDescription": "View and manage all users in the system", "userAbount": "About User Management", From d994a8100d832dfec42d910d58a8b4565a1f7d85 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 15:01:12 +0300 Subject: [PATCH 053/105] Add missed translation keys in settings/api-keys --- src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx | 2 +- src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx index b26bc622..d8372fe1 100644 --- a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx +++ b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx @@ -101,7 +101,7 @@ export default function OrgApiKeysTable({ setIsDeleteModalOpen(true); }} > - Delete + {t('delete')} diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx index 79b28dfd..b4336782 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx @@ -55,7 +55,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <> - + {children} From 8242a66b976d007e221fd7a964d1a56829a29240 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 6 May 2025 15:01:29 +0300 Subject: [PATCH 054/105] Add translation keys in admin/api-keys --- src/app/admin/api-keys/ApiKeysDataTable.tsx | 9 ++-- src/app/admin/api-keys/ApiKeysTable.tsx | 30 +++++------ src/app/admin/api-keys/[apiKeyId]/layout.tsx | 7 ++- .../api-keys/[apiKeyId]/permissions/page.tsx | 21 ++++---- src/app/admin/api-keys/create/page.tsx | 50 +++++++++---------- src/app/admin/api-keys/page.tsx | 6 ++- 6 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/app/admin/api-keys/ApiKeysDataTable.tsx b/src/app/admin/api-keys/ApiKeysDataTable.tsx index f65949a4..35986b5d 100644 --- a/src/app/admin/api-keys/ApiKeysDataTable.tsx +++ b/src/app/admin/api-keys/ApiKeysDataTable.tsx @@ -32,6 +32,7 @@ import { Input } from "@app/components/ui/input"; import { DataTablePagination } from "@app/components/DataTablePagination"; import { Plus, Search } from "lucide-react"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; @@ -44,15 +45,17 @@ export function ApiKeysDataTable({ columns, data }: DataTableProps) { + + const t = useTranslations(); return ( ); } diff --git a/src/app/admin/api-keys/ApiKeysTable.tsx b/src/app/admin/api-keys/ApiKeysTable.tsx index c44d43f3..133b890b 100644 --- a/src/app/admin/api-keys/ApiKeysTable.tsx +++ b/src/app/admin/api-keys/ApiKeysTable.tsx @@ -24,6 +24,7 @@ import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import moment from "moment"; import { ApiKeysDataTable } from "./ApiKeysDataTable"; +import { useTranslations } from "next-intl"; export type ApiKeyRow = { id: string; @@ -45,14 +46,16 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { const api = createApiClient(useEnvContext()); + const t = useTranslations(); + const deleteSite = (apiKeyId: string) => { api.delete(`/api-key/${apiKeyId}`) .catch((e) => { - console.error("Error deleting API key", e); + console.error(t('apiKeysErrorDelete'), e); toast({ variant: "destructive", - title: "Error deleting API key", - description: formatAxiosError(e, "Error deleting API key") + title: t('apiKeysErrorDelete'), + description: formatAxiosError(e, t('apiKeysErrorDeleteMessage')) }); }) .then(() => { @@ -86,7 +89,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { setSelected(apiKeyROw); }} > - View settings + {t('viewSettings')} { @@ -94,7 +97,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { setIsDeleteModalOpen(true); }} > - Delete + {t('delete')} @@ -111,7 +114,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -141,7 +144,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
@@ -163,27 +166,24 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { dialog={

- Are you sure you want to remove the API key{" "} - {selected?.name || selected?.id}? + {t('apiKeysQuestionRemove', {selectedApiKey: selected?.name || selected?.id})}

- Once removed, the API key will no longer be - able to be used. + {t('apiKeysMessageRemove')}

- To confirm, please type the name of the API key - below. + {t('apiKeysMessageConfirm')}

} - buttonText="Confirm Delete API Key" + buttonText={t('apiKeysDeleteConfirm')} onConfirm={async () => deleteSite(selected!.id)} string={selected.name} - title="Delete API Key" + title={t('apiKeysDelete')} /> )} diff --git a/src/app/admin/api-keys/[apiKeyId]/layout.tsx b/src/app/admin/api-keys/[apiKeyId]/layout.tsx index be3147ea..3354b435 100644 --- a/src/app/admin/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/layout.tsx @@ -20,6 +20,7 @@ import { import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; +import { useTranslations } from "next-intl"; interface SettingsLayoutProps { children: React.ReactNode; @@ -29,6 +30,8 @@ interface SettingsLayoutProps { export default async function SettingsLayout(props: SettingsLayoutProps) { const params = await props.params; + const t = useTranslations(); + const { children } = props; let apiKey = null; @@ -45,14 +48,14 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems = [ { - title: "Permissions", + title: t('apiKeysPermissionsTitle'), href: "/admin/api-keys/{apiKeyId}/permissions" } ]; return ( <> - + {children} diff --git a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx index c468c139..46d192ec 100644 --- a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx @@ -23,12 +23,15 @@ import { ListApiKeyActionsResponse } from "@server/routers/apiKeys"; import { AxiosResponse } from "axios"; import { useParams } from "next/navigation"; import { useEffect, useState } from "react"; +import { useTranslations } from "next-intl"; export default function Page() { const { env } = useEnvContext(); const api = createApiClient({ env }); const { apiKeyId } = useParams(); + const t = useTranslations(); + const [loadingPage, setLoadingPage] = useState(true); const [selectedPermissions, setSelectedPermissions] = useState< Record @@ -47,10 +50,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error loading API key actions", + title: t('apiKeysPermissionsErrorLoadingActions'), description: formatAxiosError( e, - "Error loading API key actions" + t('apiKeysPermissionsErrorLoadingActions') ) }); }); @@ -81,18 +84,18 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysPermissionsErrorUpdate'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysPermissionsErrorUpdate'), description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: "Permissions updated", - description: "The permissions have been updated." + title: t('apiKeysPermissionsUpdated'), + description: t('apiKeysPermissionsUpdatedDescription') }); } @@ -106,10 +109,10 @@ export default function Page() { - Permissions + {t('apiKeysPermissionsTitle')} - Determine what this API key can do + {t('apiKeysPermissionsGeneralSettingsDescription')} @@ -127,7 +130,7 @@ export default function Page() { loading={loadingSavePermissions} disabled={loadingSavePermissions} > - Save Permissions + {t('apiKeysPermissionsSave')} diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index c76b1859..6bda9d16 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -59,15 +59,18 @@ import CopyToClipboard from "@app/components/CopyToClipboard"; import moment from "moment"; import CopyTextBox from "@app/components/CopyTextBox"; import PermissionsSelectBox from "@app/components/PermissionsSelectBox"; +import { useTranslations } from "next-intl"; + +const t = useTranslations(); const createFormSchema = z.object({ name: z .string() .min(2, { - message: "Name must be at least 2 characters." + message: t('apiKeysNameMin') }) .max(255, { - message: "Name must not be longer than 255 characters." + message: t('apiKeysNameMax') }) }); @@ -82,7 +85,7 @@ const copiedFormSchema = z return data.copied; }, { - message: "You must confirm that you have copied the API key.", + message: t('apiKeysConfirmCopy2'), path: ["copied"] } ); @@ -127,7 +130,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error creating API key", + title: t('apiKeysErrorCreate'), description: formatAxiosError(e) }); }); @@ -148,10 +151,10 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysErrorSetPermission'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysErrorSetPermission'), description: formatAxiosError(e) }); }); @@ -184,8 +187,8 @@ export default function Page() { <>
@@ -368,7 +368,7 @@ export default function Page() { router.push(`/admin/api-keys`); }} > - Cancel + {t('cancel')} )} {!apiKey && ( @@ -380,7 +380,7 @@ export default function Page() { form.handleSubmit(onSubmit)(); }} > - Generate + {t('generate')} )} @@ -391,7 +391,7 @@ export default function Page() { copiedForm.handleSubmit(onCopiedSubmit)(); }} > - Done + {t('done')} )}
diff --git a/src/app/admin/api-keys/page.tsx b/src/app/admin/api-keys/page.tsx index b4a00806..76929ec5 100644 --- a/src/app/admin/api-keys/page.tsx +++ b/src/app/admin/api-keys/page.tsx @@ -9,6 +9,7 @@ import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { ListRootApiKeysResponse } from "@server/routers/apiKeys"; import ApiKeysTable, { ApiKeyRow } from "./ApiKeysTable"; +import { useTranslations } from "next-intl"; type ApiKeyPageProps = {}; @@ -16,6 +17,7 @@ export const dynamic = "force-dynamic"; export default async function ApiKeysPage(props: ApiKeyPageProps) { let apiKeys: ListRootApiKeysResponse["apiKeys"] = []; + const t = useTranslations(); try { const res = await internal.get>( `/api-keys`, @@ -36,8 +38,8 @@ export default async function ApiKeysPage(props: ApiKeyPageProps) { return ( <> From b03415a0ebaa00a6a7a7c4f04b01ab9ec0b0fdb2 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:43:17 +0300 Subject: [PATCH 055/105] New translation keys in en-US locale --- messages/en-US.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/en-US.json b/messages/en-US.json index ecaef172..b569248f 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "An error occurred while deleting the organization.", "orgDeleted": "Organization deleted", "orgDeletedMessage": "The organization and its data has been deleted.", + "orgMissing": "Organization ID Missing", + "orgMissingMessage": "Unable to regenerate invitation without an organization ID.", "accessUsersManage": "Manage Users", "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", "accessUsersSearch": "Search users...", @@ -248,6 +250,7 @@ "weeks": "Weeks", "months": "Months", "years": "Years", + "day": "{count, plural, =1 {# day} other {# days}}", "apiKeysTitle": "API Key Information", "apiKeysNameMin": "Name must be at least 2 characters.", "apiKeysNameMax": "Name must not be longer than 255 characters.", @@ -351,5 +354,32 @@ "total": "Total", "licenseContinuePayment": "Continue to Payment", "pricingPage": "pricing page", - "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " + "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ", + "invite": "Invitations", + "inviteRegenerate": "Regenerate Invitation", + "inviteRegenerateDescription": "Revoke previous invitation and create a new one", + "inviteRemove": "Remove Invitation", + "inviteRemoveError": "Failed to remove invitation", + "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", + "inviteRemoved": "Invitation removed", + "inviteRemovedDescription": "The invitation for {email} has been removed.", + "inviteQuestionRemove": "Are you sure you want to remove the invitation{email, plural, ='' {}, other { for #}}?", + "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", + "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", + "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for{email, plural, ='' {}, other { for #}}? This will revoke the previous invitation.", + "inviteRemoveConfirm": "Confirm Remove Invitation", + "inviteRegenerated": "Invitation Regenerated", + "inviteSent": "A new invitation has been sent to {email}.", + "inviteSentEmail": "Send email notification to the user", + "inviteGenerate": "A new invitation has been generated for {email}.", + "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateErrorDescription": "An invitation for this user already exists.", + "inviteRateLimitError": "Rate Limit Exceeded", + "inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.", + "inviteRegenerateError": "Failed to Regenerate Invitation", + "inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.", + "inviteValidityPeriod": "Validity Period", + "inviteValidityPeriodSelect": "Select validity period", + "inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.", + "inviteRegenerateButton": "Regenerate" } From 3e9dc4753b5b8e1a62c2c187f50a0cf2f928a45f Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:43:42 +0300 Subject: [PATCH 056/105] New translation keys in de-DE locale --- messages/de-DE.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index aa41476f..b0709526 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.", "orgDeleted": "Organisation gelöscht", "orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.", + "orgMissing": "Organisations-ID fehlt", + "orgMissingMessage": "Einladung kann ohne Organisations-ID nicht neu generiert werden.", "accessUsersManage": "Benutzer verwalten", "accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten", "accessUsersSearch": "Benutzer suchen...", @@ -248,6 +250,7 @@ "weeks": "Wochen", "months": "Monate", "years": "Jahre", + "day": "{count, plural, =1 {# Tag} other {# Tage}}", "apiKeysTitle": "API-Schlüssel Information", "apiKeysNameMin": "Name muss mindestens 2 Zeichen lang sein.", "apiKeysNameMax": "Name darf nicht länger als 255 Zeichen sein.", @@ -351,5 +354,32 @@ "total": "Gesamt", "licenseContinuePayment": "Weiter zur Zahlung", "pricingPage": "Preisseite", - "licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die " + "licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die ", + "invite": "Einladungen", + "inviteRegenerate": "Einladung neu generieren", + "inviteRegenerateDescription": "Vorherige Einladung widerrufen und neue erstellen", + "inviteRemove": "Einladung entfernen", + "inviteRemoveError": "Einladung konnte nicht entfernt werden", + "inviteRemoveErrorDescription": "Beim Entfernen der Einladung ist ein Fehler aufgetreten.", + "inviteRemoved": "Einladung entfernt", + "inviteRemovedDescription": "Die Einladung für {email} wurde entfernt.", + "inviteQuestionRemove": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} entfernen möchten?", + "inviteMessageRemove": "Sobald entfernt, wird diese Einladung nicht mehr gültig sein. Sie können den Benutzer später jederzeit erneut einladen.", + "inviteMessageConfirm": "Bitte geben Sie zur Bestätigung die E-Mail-Adresse der Einladung unten ein.", + "inviteQuestionRegenerate": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} neu generieren möchten? Dies wird die vorherige Einladung widerrufen.", + "inviteRemoveConfirm": "Entfernen der Einladung bestätigen", + "inviteRegenerated": "Einladung neu generiert", + "inviteSent": "Eine neue Einladung wurde an {email} gesendet.", + "inviteSentEmail": "E-Mail-Benachrichtigung an den Benutzer senden", + "inviteGenerate": "Eine neue Einladung wurde für {email} generiert.", + "inviteDuplicateError": "Doppelte Einladung", + "inviteDuplicateErrorDescription": "Eine Einladung für diesen Benutzer existiert bereits.", + "inviteRateLimitError": "Ratenlimit überschritten", + "inviteRateLimitErrorDescription": "Sie haben das Limit von 3 Neugenerierungen pro Stunde überschritten. Bitte versuchen Sie es später erneut.", + "inviteRegenerateError": "Fehler beim Neugenerieren der Einladung", + "inviteRegenerateErrorDescription": "Beim Neugenerieren der Einladung ist ein Fehler aufgetreten.", + "inviteValidityPeriod": "Gültigkeitszeitraum", + "inviteValidityPeriodSelect": "Gültigkeitszeitraum auswählen", + "inviteRegenerateMessage": "Die Einladung wurde neu generiert. Der Benutzer muss den untenstehenden Link aufrufen, um die Einladung anzunehmen.", + "inviteRegenerateButton": "Neu generieren", } From f91a4e88d5361680c50d219536ce99d6e639a305 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:43:51 +0300 Subject: [PATCH 057/105] New translation keys in fr-FR locale --- messages/fr-FR.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 3d879f33..8aceedcd 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "Une erreur s'est produite lors de la suppression de l'organisation.", "orgDeleted": "Organisation supprimée", "orgDeletedMessage": "L'organisation et ses données ont été supprimées.", + "orgMissing": "ID d'organisation manquant", + "orgMissingMessage": "Impossible de régénérer l'invitation sans un ID d'organisation.", "accessUsersManage": "Gérer les utilisateurs", "accessUsersDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à votre organisation", "accessUsersSearch": "Rechercher des utilisateurs...", @@ -248,6 +250,7 @@ "weeks": "Semaines", "months": "Mois", "years": "Années", + "day": "{count, plural, =1 {# jour} other {# jours}}", "apiKeysTitle": "Informations sur la clé API", "apiKeysNameMin": "Le nom doit comporter au moins 2 caractères.", "apiKeysNameMax": "Le nom ne doit pas dépasser 255 caractères.", @@ -351,5 +354,32 @@ "total": "Total", "licenseContinuePayment": "Continuer vers le paiement", "pricingPage": "page de tarification", - "licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le " + "licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le ", + "invite": "Invitations", + "inviteRegenerate": "Régénérer l'invitation", + "inviteRegenerateDescription": "Révoquer l'invitation précédente et en créer une nouvelle", + "inviteRemove": "Supprimer l'invitation", + "inviteRemoveError": "Échec de la suppression de l'invitation", + "inviteRemoveErrorDescription": "Une erreur s'est produite lors de la suppression de l'invitation.", + "inviteRemoved": "Invitation supprimée", + "inviteRemovedDescription": "L'invitation pour {email} a été supprimée.", + "inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation{email, plural, ='' {}, other { pour #}} ?", + "inviteMessageRemove": "Une fois supprimée, cette invitation ne sera plus valide. Vous pourrez toujours réinviter l'utilisateur plus tard.", + "inviteMessageConfirm": "Pour confirmer, veuillez saisir l'adresse e-mail de l'invitation ci-dessous.", + "inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation{email, plural, ='' {}, other { pour #}} ? Cela révoquera l'invitation précédente.", + "inviteRemoveConfirm": "Confirmer la suppression de l'invitation", + "inviteRegenerated": "Invitation régénérée", + "inviteSent": "Une nouvelle invitation a été envoyée à {email}.", + "inviteSentEmail": "Envoyer une notification par e-mail à l'utilisateur", + "inviteGenerate": "Une nouvelle invitation a été générée pour {email}.", + "inviteDuplicateError": "Invitation en double", + "inviteDuplicateErrorDescription": "Une invitation pour cet utilisateur existe déjà.", + "inviteRateLimitError": "Limite de taux dépassée", + "inviteRateLimitErrorDescription": "Vous avez dépassé la limite de 3 régénérations par heure. Veuillez réessayer plus tard.", + "inviteRegenerateError": "Échec de la régénération de l'invitation", + "inviteRegenerateErrorDescription": "Une erreur s'est produite lors de la régénération de l'invitation.", + "inviteValidityPeriod": "Période de validité", + "inviteValidityPeriodSelect": "Sélectionner la période de validité", + "inviteRegenerateMessage": "L'invitation a été régénérée. L'utilisateur doit accéder au lien ci-dessous pour accepter l'invitation.", + "inviteRegenerateButton": "Régénérer" } From fa21934d5d18cb6f6b0e9cb6830d842ee94be55e Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:44:36 +0300 Subject: [PATCH 058/105] New translation keys in it-IT locale --- messages/it-IT.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 9e42af9d..fd5852ef 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione dell'organizzazione.", "orgDeleted": "Organizzazione eliminata", "orgDeletedMessage": "L'organizzazione e i suoi dati sono stati eliminati.", + "orgMissing": "ID Organizzazione Mancante", + "orgMissingMessage": "Impossibile rigenerare l'invito senza un ID organizzazione.", "accessUsersManage": "Gestisci Utenti", "accessUsersDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso alla tua organizzazione", "accessUsersSearch": "Cerca utenti...", @@ -248,6 +250,7 @@ "weeks": "Settimane", "months": "Mesi", "years": "Anni", + "day": "{count, plural, =1 {# giorno} other {# giorni}}", "apiKeysTitle": "Informazioni Chiave API", "apiKeysNameMin": "Il nome deve contenere almeno 2 caratteri.", "apiKeysNameMax": "Il nome non deve essere più lungo di 255 caratteri.", @@ -351,5 +354,32 @@ "total": "Totale", "licenseContinuePayment": "Continua al pagamento", "pricingPage": "pagina prezzi", - "licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il " + "licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il ", + "invite": "Inviti", + "inviteRegenerate": "Rigenera Invito", + "inviteRegenerateDescription": "Revoca l'invito precedente e creane uno nuovo", + "inviteRemove": "Rimuovi Invito", + "inviteRemoveError": "Impossibile rimuovere l'invito", + "inviteRemoveErrorDescription": "Si è verificato un errore durante la rimozione dell'invito.", + "inviteRemoved": "Invito rimosso", + "inviteRemovedDescription": "L'invito per {email} è stato rimosso.", + "inviteQuestionRemove": "Sei sicuro di voler rimuovere l'invito{email, plural, ='' {}, other { per #}}?", + "inviteMessageRemove": "Una volta rimosso, questo invito non sarà più valido. Puoi sempre reinvitare l'utente in seguito.", + "inviteMessageConfirm": "Per confermare, digita l'indirizzo email dell'invito qui sotto.", + "inviteQuestionRegenerate": "Sei sicuro di voler rigenerare l'invito{email, plural, ='' {}, other { per #}}? Questo revocherà l'invito precedente.", + "inviteRemoveConfirm": "Conferma Rimozione Invito", + "inviteRegenerated": "Invito Rigenerato", + "inviteSent": "Un nuovo invito è stato inviato a {email}.", + "inviteSentEmail": "Invia notifica email all'utente", + "inviteGenerate": "Un nuovo invito è stato generato per {email}.", + "inviteDuplicateError": "Invito Duplicato", + "inviteDuplicateErrorDescription": "Esiste già un invito per questo utente.", + "inviteRateLimitError": "Limite di Frequenza Superato", + "inviteRateLimitErrorDescription": "Hai superato il limite di 3 rigenerazioni per ora. Riprova più tardi.", + "inviteRegenerateError": "Impossibile Rigenerare l'Invito", + "inviteRegenerateErrorDescription": "Si è verificato un errore durante la rigenerazione dell'invito.", + "inviteValidityPeriod": "Periodo di Validità", + "inviteValidityPeriodSelect": "Seleziona periodo di validità", + "inviteRegenerateMessage": "L'invito è stato rigenerato. L'utente deve accedere al link qui sotto per accettare l'invito.", + "inviteRegenerateButton": "Rigenera" } From 0fd3271ef47fd2350e4577c190a0654fb3f9baed Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:44:46 +0300 Subject: [PATCH 059/105] New translation keys in pl-PL locale --- messages/pl-PL.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 6b538257..e3bd8550 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "Wystąpił błąd podczas usuwania organizacji.", "orgDeleted": "Organizacja usunięta", "orgDeletedMessage": "Organizacja i jej dane zostały usunięte.", + "orgMissing": "Brak ID organizacji", + "orgMissingMessage": "Nie można ponownie wygenerować zaproszenia bez ID organizacji.", "accessUsersManage": "Zarządzaj użytkownikami", "accessUsersDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do Twojej organizacji", "accessUsersSearch": "Szukaj użytkowników...", @@ -248,6 +250,7 @@ "weeks": "Tygodnie", "months": "Miesiące", "years": "Lata", + "day": "{count, plural, =1 {# dzień} other {# dni}}", "apiKeysTitle": "Informacje o kluczu API", "apiKeysNameMin": "Nazwa musi mieć co najmniej 2 znaki.", "apiKeysNameMax": "Nazwa nie może być dłuższa niż 255 znaków.", @@ -351,5 +354,32 @@ "total": "Łącznie", "licenseContinuePayment": "Przejdź do płatności", "pricingPage": "strona cenowa", - "licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź " + "licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź ", + "invite": "Zaproszenia", + "inviteRegenerate": "Wygeneruj ponownie zaproszenie", + "inviteRegenerateDescription": "Unieważnij poprzednie zaproszenie i utwórz nowe", + "inviteRemove": "Usuń zaproszenie", + "inviteRemoveError": "Nie udało się usunąć zaproszenia", + "inviteRemoveErrorDescription": "Wystąpił błąd podczas usuwania zaproszenia.", + "inviteRemoved": "Zaproszenie usunięte", + "inviteRemovedDescription": "Zaproszenie dla {email} zostało usunięte.", + "inviteQuestionRemove": "Czy na pewno chcesz usunąć zaproszenie{email, plural, ='' {}, other { dla #}}?", + "inviteMessageRemove": "Po usunięciu to zaproszenie nie będzie już ważne. Zawsze możesz ponownie zaprosić użytkownika później.", + "inviteMessageConfirm": "Aby potwierdzić, wpisz poniżej adres email zaproszenia.", + "inviteQuestionRegenerate": "Czy na pewno chcesz ponownie wygenerować zaproszenie{email, plural, ='' {}, other { dla #}}? Spowoduje to unieważnienie poprzedniego zaproszenia.", + "inviteRemoveConfirm": "Potwierdź usunięcie zaproszenia", + "inviteRegenerated": "Zaproszenie wygenerowane ponownie", + "inviteSent": "Nowe zaproszenie zostało wysłane do {email}.", + "inviteSentEmail": "Wyślij powiadomienie email do użytkownika", + "inviteGenerate": "Nowe zaproszenie zostało wygenerowane dla {email}.", + "inviteDuplicateError": "Zduplikowane zaproszenie", + "inviteDuplicateErrorDescription": "Zaproszenie dla tego użytkownika już istnieje.", + "inviteRateLimitError": "Przekroczono limit żądań", + "inviteRateLimitErrorDescription": "Przekroczyłeś limit 3 regeneracji na godzinę. Spróbuj ponownie później.", + "inviteRegenerateError": "Nie udało się ponownie wygenerować zaproszenia", + "inviteRegenerateErrorDescription": "Wystąpił błąd podczas ponownego generowania zaproszenia.", + "inviteValidityPeriod": "Okres ważności", + "inviteValidityPeriodSelect": "Wybierz okres ważności", + "inviteRegenerateMessage": "Zaproszenie zostało ponownie wygenerowane. Użytkownik musi uzyskać dostęp do poniższego linku, aby zaakceptować zaproszenie.", + "inviteRegenerateButton": "Wygeneruj ponownie" } From 89729a451cc434f13970ca7220dced56a8988b89 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:44:56 +0300 Subject: [PATCH 060/105] New translation keys in pt-PT locale --- messages/pt-PT.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1f1d2da1..555a2a33 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", + "orgMissing": "ID da Organização Ausente", + "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", "accessUsersManage": "Gerenciar Usuários", "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", "accessUsersSearch": "Procurar usuários...", @@ -248,6 +250,7 @@ "weeks": "semanas", "months": "Meses", "years": "anos", + "day": "{count, plural, =1 {# dia} other {# dias}}", "apiKeysTitle": "Informações da Chave API", "apiKeysNameMin": "O nome deve ter pelo menos 2 caracteres.", "apiKeysNameMax": "O nome não deve ter mais de 255 caracteres.", @@ -351,5 +354,32 @@ "total": "Total:", "licenseContinuePayment": "Continuar para o pagamento", "pricingPage": "Página de preços", - "licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite " + "licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite ", + "invite": "Convites", + "inviteRegenerate": "Regenerar Convite", + "inviteRegenerateDescription": "Revogar convite anterior e criar um novo", + "inviteRemove": "Remover Convite", + "inviteRemoveError": "Falha ao remover convite", + "inviteRemoveErrorDescription": "Ocorreu um erro ao remover o convite.", + "inviteRemoved": "Convite removido", + "inviteRemovedDescription": "O convite para {email} foi removido.", + "inviteQuestionRemove": "Tem certeza que deseja remover o convite{email, plural, ='' {}, other { para #}}?", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", + "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", + "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", + "inviteRemoveConfirm": "Confirmar Remoção do Convite", + "inviteRegenerated": "Convite Regenerado", + "inviteSent": "Um novo convite foi enviado para {email}.", + "inviteSentEmail": "Enviar notificação por e-mail ao usuário", + "inviteGenerate": "Um novo convite foi gerado para {email}.", + "inviteDuplicateError": "Convite Duplicado", + "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", + "inviteRateLimitError": "Limite de Taxa Excedido", + "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRegenerateError": "Falha ao Regenerar Convite", + "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", + "inviteValidityPeriod": "Período de Validade", + "inviteValidityPeriodSelect": "Selecione o período de validade", + "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", + "inviteRegenerateButton": "Regenerar" } From 491b4e7b18f4deebede6be6f0772c53a1cb8c173 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:45:12 +0300 Subject: [PATCH 061/105] New translation keys in tr-TR locale --- messages/tr-TR.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index ecaef172..dd638953 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -224,6 +224,8 @@ "orgErrorDeleteMessage": "An error occurred while deleting the organization.", "orgDeleted": "Organization deleted", "orgDeletedMessage": "The organization and its data has been deleted.", + "orgMissing": "Organization ID Missing", + "orgMissingMessage": "Unable to regenerate invitation without an organization ID.", "accessUsersManage": "Manage Users", "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", "accessUsersSearch": "Search users...", @@ -248,6 +250,7 @@ "weeks": "Weeks", "months": "Months", "years": "Years", + "day": "{count, plural, =1 {# day} other {# days}}", "apiKeysTitle": "API Key Information", "apiKeysNameMin": "Name must be at least 2 characters.", "apiKeysNameMax": "Name must not be longer than 255 characters.", @@ -351,5 +354,32 @@ "total": "Total", "licenseContinuePayment": "Continue to Payment", "pricingPage": "pricing page", - "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " + "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ", + "invite": "Invitations", + "inviteRegenerate": "Regenerate Invitation", + "inviteRegenerateDescription": "Revoke previous invitation and create a new one", + "inviteRemove": "Remove Invitation", + "inviteRemoveError": "Failed to remove invitation", + "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", + "inviteRemoved": "Invitation removed", + "inviteRemovedDescription": "The invitation for {email} has been removed.", + "inviteQuestionRemove": "Are you sure you want to remove the invitation{email, plural, ='' {}, other { for #}}?", + "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", + "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", + "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for{email, plural, ='' {}, other { for #}}? This will revoke the previous invitation.", + "inviteRemoveConfirm": "Confirm Remove Invitation", + "inviteRegenerated": "Invitation Regenerated", + "inviteSent": "A new invitation has been sent to {email}.", + "inviteSentEmail": "Send email notification to the user", + "inviteGenerate": "A new invitation has been generated for {email}.", + "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateErrorDescription": "An invitation for this user already exists.", + "inviteRateLimitError": "Rate Limit Exceeded", + "inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.", + "inviteRegenerateError": "Failed to Regenerate Invitation", + "inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.", + "inviteValidityPeriod": "Validity Period", + "inviteValidityPeriodSelect": "Select validity period", + "inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.", + "inviteRegenerateButton": "Regenerate", } From 840d5c2b66d624260e7ca67c80a1c6ca37c3f7ce Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Wed, 7 May 2025 17:46:16 +0300 Subject: [PATCH 062/105] Add translation keys in settings/access/invitations --- .../invitations/InvitationsDataTable.tsx | 2 +- .../access/invitations/InvitationsTable.tsx | 31 +++++---- .../invitations/RegenerateInvitationForm.tsx | 64 +++++++++---------- 3 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx index ecce6913..57b1d746 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx @@ -22,7 +22,7 @@ export function InvitationsDataTable({ diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx index 9618df14..5a48646d 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx @@ -17,6 +17,7 @@ import { useOrgContext } from "@app/hooks/useOrgContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; export type InvitationRow = { id: string; @@ -39,6 +40,8 @@ export default function InvitationsTable({ const [selectedInvitation, setSelectedInvitation] = useState(null); + const t = useTranslations(); + const api = createApiClient(useEnvContext()); const { org } = useOrgContext(); @@ -51,7 +54,7 @@ export default function InvitationsTable({ @@ -62,7 +65,7 @@ export default function InvitationsTable({ setSelectedInvitation(invitation); }} > - Regenerate Invitation + {t('inviteRegenerate')} { @@ -71,7 +74,7 @@ export default function InvitationsTable({ }} > - Remove Invitation + {t('inviteRemove')} @@ -112,17 +115,16 @@ export default function InvitationsTable({ .catch((e) => { toast({ variant: "destructive", - title: "Failed to remove invitation", - description: - "An error occurred while removing the invitation." + title: t('inviteRemoveError'), + description: t('inviteRemoveErrorDescription') }); }); if (res && res.status === 200) { toast({ variant: "default", - title: "Invitation removed", - description: `The invitation for ${selectedInvitation.email} has been removed.` + title: t('inviteRemoved'), + description: t('inviteRemovedDescription', {email: selectedInvitation.email}) }); setInvitations((prev) => @@ -146,23 +148,20 @@ export default function InvitationsTable({ dialog={

- Are you sure you want to remove the invitation for{" "} - {selectedInvitation?.email}? + {t('inviteQuestionRemove', {email: selectedInvitation?.email ?? ''})}

- Once removed, this invitation will no longer be - valid. You can always re-invite the user later. + {t('inviteMessageRemove')}

- To confirm, please type the email address of the - invitation below. + {t('inviteMessageConfirm')}

} - buttonText="Confirm Remove Invitation" + buttonText={t('inviteRemoveConfirm')} onConfirm={removeInvitation} string={selectedInvitation?.email ?? ""} - title="Remove Invitation" + title={t('inviteRemove')} /> { @@ -79,9 +82,8 @@ export default function RegenerateInvitationForm({ if (!org?.org.orgId) { toast({ variant: "destructive", - title: "Organization ID Missing", - description: - "Unable to regenerate invitation without an organization ID.", + title: t('orgMissing'), + description: t('orgMissingMessage'), duration: 5000 }); return; @@ -105,15 +107,15 @@ export default function RegenerateInvitationForm({ if (sendEmail) { toast({ variant: "default", - title: "Invitation Regenerated", - description: `A new invitation has been sent to ${invitation.email}.`, + title: t('inviteRegenerated'), + description: t('inviteSent', {email: invitation.email}), duration: 5000 }); } else { toast({ variant: "default", - title: "Invitation Regenerated", - description: `A new invitation has been generated for ${invitation.email}.`, + title: t('inviteRegenerated'), + description: t('inviteGenerate', {email: invitation.email}), duration: 5000 }); } @@ -130,24 +132,22 @@ export default function RegenerateInvitationForm({ if (error.response?.status === 409) { toast({ variant: "destructive", - title: "Duplicate Invite", - description: "An invitation for this user already exists.", + title: t('inviteDuplicateError'), + description: t('inviteDuplicateErrorDescription'), duration: 5000 }); } else if (error.response?.status === 429) { toast({ variant: "destructive", - title: "Rate Limit Exceeded", - description: - "You have exceeded the limit of 3 regenerations per hour. Please try again later.", + title: t('inviteRateLimitError'), + description: t('inviteRateLimitErrorDescription'), duration: 5000 }); } else { toast({ variant: "destructive", - title: "Failed to Regenerate Invitation", - description: - "An error occurred while regenerating the invitation.", + title: t('inviteRegenerateError'), + description: t('inviteRegenerateErrorDescription'), duration: 5000 }); } @@ -168,18 +168,16 @@ export default function RegenerateInvitationForm({ > - Regenerate Invitation + {t('inviteRegenerate')} - Revoke previous invitation and create a new one + {t('inviteRegenerateDescription')} {!inviteLink ? (

- Are you sure you want to regenerate the - invitation for {invitation?.email}? This - will revoke the previous invitation. + {t('inviteQuestionRegenerate', {email: invitation?.email ?? ''})}

@@ -146,7 +148,7 @@ export default function CreateRoleForm({ name="description" render={({ field }) => ( - Description + {t('description')} @@ -159,7 +161,7 @@ export default function CreateRoleForm({ - + diff --git a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx index 80d97267..b6da44ea 100644 --- a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx +++ b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx @@ -38,6 +38,7 @@ import { RoleRow } from "./RolesTable"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; type CreateRoleFormProps = { open: boolean; @@ -46,8 +47,10 @@ type CreateRoleFormProps = { afterDelete?: () => void; }; +const t = useTranslations(); + const formSchema = z.object({ - newRoleId: z.string({ message: "New role is required" }) + newRoleId: z.string({ message: t('accessRoleErrorNewRequired') }) }); export default function DeleteRoleForm({ @@ -73,10 +76,10 @@ export default function DeleteRoleForm({ console.error(e); toast({ variant: "destructive", - title: "Failed to fetch roles", + title: t('accessRoleErrorFetch'), description: formatAxiosError( e, - "An error occurred while fetching the roles" + t('accessRoleErrorFetchDescription') ) }); }); @@ -112,10 +115,10 @@ export default function DeleteRoleForm({ .catch((e) => { toast({ variant: "destructive", - title: "Failed to remove role", + title: t('accessRoleErrorRemove'), description: formatAxiosError( e, - "An error occurred while removing the role." + t('accessRoleErrorRemoveDescription') ) }); }); @@ -123,8 +126,8 @@ export default function DeleteRoleForm({ if (res && res.status === 200) { toast({ variant: "default", - title: "Role removed", - description: "The role has been successfully removed." + title: t('accessRoleRemoved'), + description: t('accessRoleRemovedDescription') }); if (open) { @@ -151,22 +154,19 @@ export default function DeleteRoleForm({ > - Remove Role + {t('accessRoleRemove')} - Remove a role from the organization + {t('accessRoleRemoveDescription')}

- You're about to delete the{" "} - {roleToDelete.name} role. You cannot - undo this action. + {t('accessRoleQuestionRemove', {name: roleToDelete.name})}

- Before deleting this role, please select a - new role to transfer existing members to. + {t('accessRoleRequiredRemove')}

@@ -180,7 +180,7 @@ export default function DeleteRoleForm({ name="newRoleId" render={({ field }) => ( - Role + {t('role')} - + @@ -180,7 +182,7 @@ export default function AccessControlsPage() { disabled={loading} form="access-controls-form" > - Save Access Controls + {t('accessControlsSubmit')} diff --git a/src/app/[orgId]/settings/access/users/[userId]/layout.tsx b/src/app/[orgId]/settings/access/users/[userId]/layout.tsx index 342e8b7c..44c1ee19 100644 --- a/src/app/[orgId]/settings/access/users/[userId]/layout.tsx +++ b/src/app/[orgId]/settings/access/users/[userId]/layout.tsx @@ -15,6 +15,7 @@ import { import Link from "next/link"; import { cache } from "react"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { useTranslations } from "next-intl"; interface UserLayoutProps { children: React.ReactNode; @@ -26,6 +27,8 @@ export default async function UserLayoutProps(props: UserLayoutProps) { const { children } = props; + const t = useTranslations(); + let user = null; try { const getOrgUser = cache(async () => @@ -42,7 +45,7 @@ export default async function UserLayoutProps(props: UserLayoutProps) { const navItems = [ { - title: "Access Controls", + title: t('accessControls'), href: "/{orgId}/settings/access/users/{userId}/access-controls" } ]; @@ -51,7 +54,7 @@ export default async function UserLayoutProps(props: UserLayoutProps) { <> diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index c270b350..0d9d2438 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -44,6 +44,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { Checkbox } from "@app/components/ui/checkbox"; import { ListIdpsResponse } from "@server/routers/idp"; +import { useTranslations } from "next-intl"; type UserType = "internal" | "oidc"; @@ -59,28 +60,30 @@ interface IdpOption { type: string; } +const t = useTranslations(); + const internalFormSchema = z.object({ - email: z.string().email({ message: "Invalid email address" }), - validForHours: z.string().min(1, { message: "Please select a duration" }), - roleId: z.string().min(1, { message: "Please select a role" }) + email: z.string().email({ message: t('emailInvalid') }), + validForHours: z.string().min(1, { message: t('inviteValidityDuration') }), + roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }) }); const externalFormSchema = z.object({ - username: z.string().min(1, { message: "Username is required" }), + username: z.string().min(1, { message: t('usernameRequired') }), email: z .string() - .email({ message: "Invalid email address" }) + .email({ message: t('emailInvalid') }) .optional() .or(z.literal("")), name: z.string().optional(), - roleId: z.string().min(1, { message: "Please select a role" }), - idpId: z.string().min(1, { message: "Please select an identity provider" }) + roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }), + idpId: z.string().min(1, { message: t('idpSelectPlease') }) }); const formatIdpType = (type: string) => { switch (type.toLowerCase()) { case "oidc": - return "Generic OAuth2/OIDC provider."; + return t('idpGenericOidc'); default: return type; } @@ -103,13 +106,13 @@ export default function Page() { const [dataLoaded, setDataLoaded] = useState(false); const validFor = [ - { hours: 24, name: "1 day" }, - { hours: 48, name: "2 days" }, - { hours: 72, name: "3 days" }, - { hours: 96, name: "4 days" }, - { hours: 120, name: "5 days" }, - { hours: 144, name: "6 days" }, - { hours: 168, name: "7 days" } + { hours: 24, name: t('day', {count: 1}) }, + { hours: 48, name: t('day', {count: 2}) }, + { hours: 72, name: t('day', {count: 3}) }, + { hours: 96, name: t('day', {count: 4}) }, + { hours: 120, name: t('day', {count: 5}) }, + { hours: 144, name: t('day', {count: 6}) }, + { hours: 168, name: t('day', {count: 7}) } ]; const internalForm = useForm>({ @@ -155,10 +158,10 @@ export default function Page() { console.error(e); toast({ variant: "destructive", - title: "Failed to fetch roles", + title: t('accessRoleErrorFetch'), description: formatAxiosError( e, - "An error occurred while fetching the roles" + t('accessRoleErrorFetchDescription') ) }); }); @@ -178,10 +181,10 @@ export default function Page() { console.error(e); toast({ variant: "destructive", - title: "Failed to fetch identity providers", + title: t('idpErrorFetch'), description: formatAxiosError( e, - "An error occurred while fetching identity providers" + t('idpErrorFetchDescription') ) }); }); @@ -218,17 +221,16 @@ export default function Page() { if (e.response?.status === 409) { toast({ variant: "destructive", - title: "User Already Exists", - description: - "This user is already a member of the organization." + title: t('userErrorExists'), + description: t('userErrorExistsDescription') }); } else { toast({ variant: "destructive", - title: "Failed to invite user", + title: t('inviteError'), description: formatAxiosError( e, - "An error occurred while inviting the user" + t('inviteErrorDescription') ) }); } @@ -238,8 +240,8 @@ export default function Page() { setInviteLink(res.data.data.inviteLink); toast({ variant: "default", - title: "User invited", - description: "The user has been successfully invited." + title: t('userInvited'), + description: t('userInvitedDescription') }); setExpiresInDays(parseInt(values.validForHours) / 24); @@ -265,10 +267,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to create user", + title: t('userErrorCreate'), description: formatAxiosError( e, - "An error occurred while creating the user" + t('userErrorCreateDescription') ) }); }); @@ -276,8 +278,8 @@ export default function Page() { if (res && res.status === 201) { toast({ variant: "default", - title: "User created", - description: "The user has been successfully created." + title: t('userCreated'), + description: t('userCreatedDescription') }); router.push(`/${orgId}/settings/access/users`); } @@ -288,13 +290,13 @@ export default function Page() { const userTypes: ReadonlyArray = [ { id: "internal", - title: "Internal User", - description: "Invite a user to join your organization directly." + title: t('userTypeInternal'), + description: t('userTypeInternalDescription') }, { id: "oidc", - title: "External User", - description: "Create a user with an external identity provider." + title: t('userTypeExternal'), + description: t('userTypeExternalDescription') } ]; @@ -302,8 +304,8 @@ export default function Page() { <>
@@ -320,10 +322,10 @@ export default function Page() { - User Type + {t('userTypeTitle')} - Determine how you want to create the user + {t('userTypeDescription')} @@ -349,10 +351,10 @@ export default function Page() { - User Information + {t('userInfo')} - Enter the details for the new user + {t('userSettingsDescription')} @@ -373,7 +375,7 @@ export default function Page() { render={({ field }) => ( - Email + {t('email')} - Send invite email to - user + {t('inviteEmailSent')}
)} @@ -416,7 +417,7 @@ export default function Page() { render={({ field }) => ( - Valid For + {t('inviteValid')} - + @@ -503,37 +504,16 @@ export default function Page() {
{sendEmail && (

- An email has - been sent to the - user with the - access link - below. They must - access the link - to accept the - invitation. + {t('inviteEmailSentDescription')}

)} {!sendEmail && (

- The user has - been invited. - They must access - the link below - to accept the - invitation. + {t('inviteSentDescription')}

)}

- The invite will - expire in{" "} - - {expiresInDays}{" "} - {expiresInDays === - 1 - ? "day" - : "days"} - - . + {t('inviteExpiresIn', {days: expiresInDays})}

- Identity Provider + {t('idpTitle')} - Select the identity provider for the - external user + {t('idpSelect')} {idps.length === 0 ? (

- No identity providers are - configured. Please configure an - identity provider before creating - external users. + {t('idpNotConfigured')}

) : ( @@ -621,10 +597,10 @@ export default function Page() { - User Information + {t('userSettings')} - Enter the details for the new user + {t('userSettingsDescription')} @@ -645,7 +621,7 @@ export default function Page() { render={({ field }) => ( - Username + {t('username')}

- This must - match the - unique - username - that exists - in the - selected - identity - provider. + {t('usernameUniq')}

@@ -676,8 +644,7 @@ export default function Page() { render={({ field }) => ( - Email - (Optional) + {t('emailOptional')} ( - Name - (Optional) + {t('nameOptional')} ( - Role + {t('role')} - + diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx index 31ccbea6..686bf9b4 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx @@ -36,6 +36,7 @@ import { } from "@app/components/ui/input-otp"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; const setPincodeFormSchema = z.object({ pincode: z.string().length(6) @@ -69,6 +70,8 @@ export default function SetResourcePincodeForm({ defaultValues }); + const t = useTranslations(); + useEffect(() => { if (!open) { return; @@ -86,18 +89,17 @@ export default function SetResourcePincodeForm({ .catch((e) => { toast({ variant: "destructive", - title: "Error setting resource PIN code", + title: t('resourceErrorPincodeSetup'), description: formatAxiosError( e, - "An error occurred while setting the resource PIN code" + t('resourceErrorPincodeSetupDescription') ) }); }) .then(() => { toast({ - title: "Resource PIN code set", - description: - "The resource pincode has been set successfully" + title: t('resourcePincodeSetup'), + description: t('resourcePincodeSetupDescription') }); if (onSetPincode) { @@ -119,9 +121,9 @@ export default function SetResourcePincodeForm({ > - Set Pincode + {t('resourcePincodeSetupTitle')} - Set a pincode to protect this resource + {t('resourcePincodeSetupTitleDescription')} @@ -136,7 +138,7 @@ export default function SetResourcePincodeForm({ name="pincode" render={({ field }) => ( - PIN Code + {t('resourcePincode')}
- + diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx index 0b0535e8..f8dcb615 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx @@ -48,6 +48,7 @@ import { useRouter } from "next/navigation"; import { UserType } from "@server/types/UserTypes"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon } from "lucide-react"; +import { useTranslations } from "next-intl"; const UsersRolesFormSchema = z.object({ roles: z.array( @@ -129,6 +130,8 @@ export default function ResourceAuthenticationPage() { defaultValues: { emails: [] } }); + const t = useTranslations(); + useEffect(() => { const fetchData = async () => { try { @@ -203,10 +206,10 @@ export default function ResourceAuthenticationPage() { console.error(e); toast({ variant: "destructive", - title: "Failed to fetch data", + title: t('resourceErrorAuthFetch'), description: formatAxiosError( e, - "An error occurred while fetching the data" + t('resourceErrorAuthFetchDescription') ) }); } @@ -233,18 +236,18 @@ export default function ResourceAuthenticationPage() { }); toast({ - title: "Saved successfully", - description: "Whitelist settings have been saved" + title: t('resourceWhitelistSave'), + description: t('resourceWhitelistSaveDescription') }); router.refresh(); } catch (e) { console.error(e); toast({ variant: "destructive", - title: "Failed to save whitelist", + title: t('resourceErrorWhitelistSave'), description: formatAxiosError( e, - "An error occurred while saving the whitelist" + t('resourceErrorWhitelistSaveDescription') ) }); } finally { @@ -281,18 +284,18 @@ export default function ResourceAuthenticationPage() { }); toast({ - title: "Saved successfully", - description: "Authentication settings have been saved" + title: t('resourceAuthSettingsSave'), + description: t('resourceAuthSettingsSaveDescription') }); router.refresh(); } catch (e) { console.error(e); toast({ variant: "destructive", - title: "Failed to set roles", + title: t('resourceErrorUsersRolesSave'), description: formatAxiosError( e, - "An error occurred while setting the roles" + t('resourceErrorUsersRolesSaveDescription') ) }); } finally { @@ -308,9 +311,8 @@ export default function ResourceAuthenticationPage() { }) .then(() => { toast({ - title: "Resource password removed", - description: - "The resource password has been removed successfully" + title: t('resourcePasswordRemove'), + description: t('resourcePasswordRemoveDescription') }); updateAuthInfo({ @@ -321,10 +323,10 @@ export default function ResourceAuthenticationPage() { .catch((e) => { toast({ variant: "destructive", - title: "Error removing resource password", + title: t('resourceErrorPasswordRemove'), description: formatAxiosError( e, - "An error occurred while removing the resource password" + t('resourceErrorPasswordRemoveDescription') ) }); }) @@ -339,9 +341,8 @@ export default function ResourceAuthenticationPage() { }) .then(() => { toast({ - title: "Resource pincode removed", - description: - "The resource password has been removed successfully" + title: t('resourcePincodeRemove'), + description: t('resourcePincodeRemoveDescription') }); updateAuthInfo({ @@ -352,10 +353,10 @@ export default function ResourceAuthenticationPage() { .catch((e) => { toast({ variant: "destructive", - title: "Error removing resource pincode", + title: t('resourceErrorPincodeRemove'), description: formatAxiosError( e, - "An error occurred while removing the resource pincode" + t('resourceErrorPincodeRemoveDescription') ) }); }) @@ -400,18 +401,17 @@ export default function ResourceAuthenticationPage() { - Users & Roles + {t('resourceUsersRoles')} - Configure which users and roles can visit this - resource + {t('resourceUsersRolesDescription')} setSsoEnabled(val)} /> @@ -431,7 +431,7 @@ export default function ResourceAuthenticationPage() { name="roles" render={({ field }) => ( - Roles + {t('roles')} - Admins can always access - this resource. + {t('resourceRoleDescription')} )} @@ -486,7 +485,7 @@ export default function ResourceAuthenticationPage() { name="users" render={({ field }) => ( - Users + {t('users')} - Save Users & Roles + {t('resourceUsersRolesSubmit')} @@ -552,11 +551,10 @@ export default function ResourceAuthenticationPage() { - Authentication Methods + {t('resourceAuthMethods')} - Allow access to the resource via additional auth - methods + {t('resourceAuthMethodsDescriptions')} @@ -568,7 +566,7 @@ export default function ResourceAuthenticationPage() { Password Protection{" "} - {authInfo.password ? "Enabled" : "Disabled"} + {authInfo.password ? t('enabled') : t('disabled')}
@@ -593,8 +591,7 @@ export default function ResourceAuthenticationPage() { > - PIN Code Protection{" "} - {authInfo.pincode ? "Enabled" : "Disabled"} + {t('resourcePincodeProtection', {status: authInfo.pincode ? t('enabled') : t('disabled')})}
@@ -617,11 +614,10 @@ export default function ResourceAuthenticationPage() { - One-time Passwords + {t('otpEmailTitle')} - Require email-based authentication for resource - access + {t('otpEmailTitleDescription')} @@ -629,16 +625,16 @@ export default function ResourceAuthenticationPage() { - SMTP Required + {t('otpEmailSmtpRequired')} - SMTP must be enabled on the server to use one-time password authentication. + {t('otpEmailSmtpRequiredDescription')} )} @@ -678,8 +674,7 @@ export default function ResourceAuthenticationPage() { .regex( /^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { - message: - "Invalid email address. Wildcard (*) must be the entire local part." + message: t('otpEmailErrorInvalid') } ) ) @@ -690,7 +685,7 @@ export default function ResourceAuthenticationPage() { setActiveTagIndex={ setActiveEmailTagIndex } - placeholder="Enter an email" + placeholder={t('otpEmailEnter')} tags={ whitelistForm.getValues() .emails @@ -713,9 +708,7 @@ export default function ResourceAuthenticationPage() { /> - Press enter to add an - email after typing it in - the input field. + {t('otpEmailEnterDescription')} )} @@ -731,7 +724,7 @@ export default function ResourceAuthenticationPage() { loading={loadingSaveWhitelist} disabled={loadingSaveWhitelist} > - Save Whitelist + {t('otpEmailWhitelistSave')} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index f1e152d5..677646de 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -65,6 +65,9 @@ import { updateResourceRule } from "@server/routers/resource"; import { SwitchInput } from "@app/components/SwitchInput"; +import { useTranslations } from "next-intl"; + +const t = useTranslations(); const GeneralFormSchema = z .object({ @@ -88,7 +91,7 @@ const GeneralFormSchema = z return true; }, { - message: "Invalid port number", + message: t('proxyErrorInvalidPort'), path: ["proxyPort"] } ) @@ -100,7 +103,7 @@ const GeneralFormSchema = z return true; }, { - message: "Invalid subdomain", + message: t('subdomainErrorInvalid'), path: ["subdomain"] } ); @@ -174,10 +177,10 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: "Error fetching domains", + title: t('domainErrorFetch'), description: formatAxiosError( e, - "An error occurred when fetching the domains" + t('domainErrorFetchDescription') ) }); }); @@ -216,18 +219,18 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to update resource", + title: t('resourceErrorUpdate'), description: formatAxiosError( e, - "An error occurred while updating the resource" + t('resourceErrorUpdateDescription') ) }); }); if (res && res.status === 200) { toast({ - title: "Resource updated", - description: "The resource has been updated successfully" + title: t('resourceUpdated'), + description: t('resourceUpdatedDescription') }); const resource = res.data.data; @@ -255,18 +258,18 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to transfer resource", + title: t('resourceErrorTransfer'), description: formatAxiosError( e, - "An error occurred while transferring the resource" + t('resourceErrorTransferDescription') ) }); }); if (res && res.status === 200) { toast({ - title: "Resource transferred", - description: "The resource has been transferred successfully" + title: t('resourceTransferred'), + description: t('resourceTransferredDescription') }); router.refresh(); @@ -290,10 +293,10 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to toggle resource", + title: t('resourceErrorToggle'), description: formatAxiosError( e, - "An error occurred while updating the resource" + t('resourceErrorToggleDescription') ) }); }); @@ -308,15 +311,15 @@ export default function GeneralForm() { - Visibility + {t('resourceVisibilityTitle')} - Completely enable or disable resource visibility + {t('resourceVisibilityTitleDescription')} { await toggleResourceEnabled(val); @@ -328,10 +331,10 @@ export default function GeneralForm() { - General Settings + {t('resourceGeneral')} - Configure the general settings for this resource + {t('resourceGeneralDescription')} @@ -348,7 +351,7 @@ export default function GeneralForm() { name="name" render={({ field }) => ( - Name + {t('name')} @@ -367,7 +370,7 @@ export default function GeneralForm() { render={({ field }) => ( - Domain Type + {t('domainType')} @@ -416,7 +418,7 @@ export default function GeneralForm() { {domainType === "subdomain" ? (
- Subdomain + {t('subdomain')}
@@ -502,7 +504,7 @@ export default function GeneralForm() { render={({ field }) => ( - Base Domain + {t('baseDomain')} - Save General Settings + {t('saveGeneralSettings')} @@ -604,10 +606,10 @@ export default function GeneralForm() { - Transfer Resource + {t('resourceTransfer')} - Transfer this resource to a different site + {t('resourceTransferDescription')} @@ -627,7 +629,7 @@ export default function GeneralForm() { render={({ field }) => ( - Destination Site + {t('siteDestination')} - + - No sites found. + {t('sitesNotFound')} {sites.map( @@ -716,7 +718,7 @@ export default function GeneralForm() { disabled={transferLoading} form="transfer-form" > - Transfer Resource + {t('resourceTransferSubmit')} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index ddf255e0..bf452f99 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -73,6 +73,7 @@ import { CollapsibleContent, CollapsibleTrigger } from "@app/components/ui/collapsible"; +import { useTranslations } from "next-intl"; const addTargetSchema = z.object({ ip: z.string().refine(isTargetValid), @@ -92,6 +93,8 @@ type LocalTarget = Omit< "protocol" >; +const t = useTranslations(); + const proxySettingsSchema = z.object({ setHostHeader: z .string() @@ -104,8 +107,7 @@ const proxySettingsSchema = z.object({ return true; }, { - message: - "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header." + message: t('proxyErrorInvalidHeader') } ) }); @@ -123,8 +125,7 @@ const tlsSettingsSchema = z.object({ return true; }, { - message: - "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name." + message: t('proxyErrorTls') } ) }); @@ -199,10 +200,10 @@ export default function ReverseProxyTargets(props: { console.error(err); toast({ variant: "destructive", - title: "Failed to fetch targets", + title: t('targetErrorFetch'), description: formatAxiosError( err, - "An error occurred while fetching targets" + t('targetErrorFetchDescription') ) }); } finally { @@ -224,10 +225,10 @@ export default function ReverseProxyTargets(props: { console.error(err); toast({ variant: "destructive", - title: "Failed to fetch resource", + title: t('siteErrorFetch'), description: formatAxiosError( err, - "An error occurred while fetching resource" + t('siteErrorFetchDescription') ) }); } @@ -247,8 +248,8 @@ export default function ReverseProxyTargets(props: { if (isDuplicate) { toast({ variant: "destructive", - title: "Duplicate target", - description: "A target with these settings already exists" + title: t('targetErrorDuplicate'), + description: t('targetErrorDuplicateDescription') }); return; } @@ -260,8 +261,8 @@ export default function ReverseProxyTargets(props: { if (!isIPInSubnet(targetIp, subnet)) { toast({ variant: "destructive", - title: "Invalid target IP", - description: "Target IP must be within the site subnet" + title: t('targetWireGuardErrorInvalidIp'), + description: t('targetWireGuardErrorInvalidIpDescription') }); return; } @@ -339,8 +340,8 @@ export default function ReverseProxyTargets(props: { updateResource({ stickySession: stickySessionData.stickySession }); toast({ - title: "Targets updated", - description: "Targets and settings updated successfully" + title: t('targetsUpdated'), + description: t('targetsUpdatedDescription') }); setTargetsToRemove([]); @@ -349,10 +350,10 @@ export default function ReverseProxyTargets(props: { console.error(err); toast({ variant: "destructive", - title: "Failed to update targets", + title: t('targetsErrorUpdate'), description: formatAxiosError( err, - "An error occurred while updating targets" + t('targetsErrorUpdateDescription') ) }); } finally { @@ -373,17 +374,17 @@ export default function ReverseProxyTargets(props: { tlsServerName: data.tlsServerName || null }); toast({ - title: "TLS settings updated", - description: "Your TLS settings have been updated successfully" + title: t('targetTlsUpdate'), + description: t('targetTlsUpdateDescription') }); } catch (err) { console.error(err); toast({ variant: "destructive", - title: "Failed to update TLS settings", + title: t('targetErrorTlsUpdate'), description: formatAxiosError( err, - "An error occurred while updating TLS settings" + t('targetErrorTlsUpdateDescription') ) }); } finally { @@ -402,18 +403,17 @@ export default function ReverseProxyTargets(props: { setHostHeader: data.setHostHeader || null }); toast({ - title: "Proxy settings updated", - description: - "Your proxy settings have been updated successfully" + title: t('proxyUpdated'), + description: t('proxyUpdatedDescription') }); } catch (err) { console.error(err); toast({ variant: "destructive", - title: "Failed to update proxy settings", + title: t('proxyErrorUpdate'), description: formatAxiosError( err, - "An error occurred while updating proxy settings" + t('proxyErrorUpdateDescription') ) }); } finally { @@ -424,7 +424,7 @@ export default function ReverseProxyTargets(props: { const columns: ColumnDef[] = [ { accessorKey: "ip", - header: "IP / Hostname", + header: t('targetAddr'), cell: ({ row }) => ( ( ( // - HTTPS & TLS Settings + {t('targetTlsSettings')} - Configure TLS settings for your resource + {t('targetTlsSettingsDescription')} @@ -611,12 +611,12 @@ export default function ReverseProxyTargets(props: { className="p-0 flex items-center justify-start gap-2 w-full" >

- Advanced TLS Settings + {t('targetTlsSettingsAdvanced')}

- Toggle + {t('toggle')}
@@ -631,17 +631,13 @@ export default function ReverseProxyTargets(props: { render={({ field }) => ( - TLS Server Name - (SNI) + {t('targetTlsSni')} - The TLS Server Name - to use for SNI. - Leave empty to use - the default. + {t('targetTlsSniDescription')} @@ -659,7 +655,7 @@ export default function ReverseProxyTargets(props: { loading={httpsTlsLoading} form="tls-settings-form" > - Save Settings + {t('targetTlsSubmit')} @@ -668,10 +664,10 @@ export default function ReverseProxyTargets(props: { - Targets Configuration + {t('targets')} - Set up targets to route traffic to your services + {t('targetsDescription')} @@ -693,8 +689,8 @@ export default function ReverseProxyTargets(props: { ( - Method + {t('method')} @@ -781,7 +777,7 @@ export default function ReverseProxyTargets(props: { name="port" render={({ field }) => ( - Port + {t('targetPort')} - Add Target + {t('targetSubmit')}
@@ -849,14 +845,13 @@ export default function ReverseProxyTargets(props: { colSpan={columns.length} className="h-24 text-center" > - No targets. Add a target using the form. + {t('targetNoOne')} )} - Adding more than one target above will enable load - balancing. + {t('targetNoOneDescription')} @@ -867,7 +862,7 @@ export default function ReverseProxyTargets(props: { disabled={targetsLoading} form="targets-settings-form" > - Save Targets + {t('targetsSubmit')} @@ -876,10 +871,10 @@ export default function ReverseProxyTargets(props: { - Additional Proxy Settings + {t('proxyAdditional')} - Configure how your resource handles proxy settings + {t('proxyAdditionalDescription')} @@ -898,15 +893,13 @@ export default function ReverseProxyTargets(props: { render={({ field }) => ( - Custom Host Header + {t('proxyCustomHeader')} - The host header to set when - proxying requests. Leave - empty to use the default. + {t('proxyCustomHeaderDescription')} @@ -922,7 +915,7 @@ export default function ReverseProxyTargets(props: { loading={proxySettingsLoading} form="proxy-settings-form" > - Save Proxy Settings + {t('proxyAdditionalSubmit')} @@ -937,7 +930,7 @@ function isIPInSubnet(subnet: string, ip: string): boolean { const mask = parseInt(maskBits); if (mask < 0 || mask > 32) { - throw new Error("Invalid subnet mask. Must be between 0 and 32."); + throw new Error(t('subnetMaskErrorInvalid')); } // Convert IP addresses to binary numbers @@ -955,14 +948,14 @@ function ipToNumber(ip: string): number { // Validate IP address format const parts = ip.split("."); if (parts.length !== 4) { - throw new Error("Invalid IP address format"); + throw new Error(t('ipAddressErrorInvalidFormat')); } // Convert IP octets to 32-bit number return parts.reduce((num, octet) => { const oct = parseInt(octet); if (isNaN(oct) || oct < 0 || oct > 255) { - throw new Error("Invalid IP address octet"); + throw new Error(t('ipAddressErrorInvalidOctet')); } return (num << 8) + oct; }, 0); diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 0b50dcfb..af5bf2c1 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -73,6 +73,7 @@ import { } from "@server/lib/validators"; import { Switch } from "@app/components/ui/switch"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; // Schema for rule validation const addRuleSchema = z.object({ @@ -87,16 +88,18 @@ type LocalRule = ArrayElement & { updated?: boolean; }; -enum RuleAction { - ACCEPT = "Always Allow", - DROP = "Always Deny" -} +const t = useTranslations(); -enum RuleMatch { - PATH = "Path", - IP = "IP", - CIDR = "IP Range" -} +const RuleAction = { + ACCEPT: t('alwaysAllow'), + DROP: t('alwaysDeny') +} as const; + +const RuleMatch = { + PATH: t('path'), + IP: "IP", + CIDR: t('ipAddressRange') +} as const; export default function ResourceRules(props: { params: Promise<{ resourceId: number }>; @@ -133,10 +136,10 @@ export default function ResourceRules(props: { console.error(err); toast({ variant: "destructive", - title: "Failed to fetch rules", + title: t('rulesErrorFetch'), description: formatAxiosError( err, - "An error occurred while fetching rules" + t('rulesErrorFetchDescription') ) }); } finally { @@ -157,8 +160,8 @@ export default function ResourceRules(props: { if (isDuplicate) { toast({ variant: "destructive", - title: "Duplicate rule", - description: "A rule with these settings already exists" + title: t('rulesErrorDuplicate'), + description: t('rulesErrorDuplicateDescription') }); return; } @@ -166,8 +169,8 @@ export default function ResourceRules(props: { if (data.match === "CIDR" && !isValidCIDR(data.value)) { toast({ variant: "destructive", - title: "Invalid CIDR", - description: "Please enter a valid CIDR value" + title: t('rulesErrorInvalidIpAddressRange'), + description: t('rulesErrorInvalidIpAddressRangeDescription') }); setLoading(false); return; @@ -175,8 +178,8 @@ export default function ResourceRules(props: { if (data.match === "PATH" && !isValidUrlGlobPattern(data.value)) { toast({ variant: "destructive", - title: "Invalid URL path", - description: "Please enter a valid URL path value" + title: t('rulesErrorInvalidUrl'), + description: t('rulesErrorInvalidUrlDescription') }); setLoading(false); return; @@ -184,8 +187,8 @@ export default function ResourceRules(props: { if (data.match === "IP" && !isValidIP(data.value)) { toast({ variant: "destructive", - title: "Invalid IP", - description: "Please enter a valid IP address" + title: t('rulesErrorInvalidIpAddress'), + description: t('rulesErrorInvalidIpAddressDescription') }); setLoading(false); return; @@ -240,10 +243,10 @@ export default function ResourceRules(props: { console.error(err); toast({ variant: "destructive", - title: "Failed to update rules", + title: t('rulesErrorUpdate'), description: formatAxiosError( err, - "An error occurred while updating rules" + t('rulesErrorUpdateDescription') ) }); }); @@ -253,8 +256,8 @@ export default function ResourceRules(props: { updateResource({ applyRules: val }); toast({ - title: "Enable Rules", - description: "Rule evaluation has been updated" + title: t('rulesUpdated'), + description: t('rulesUpdatedDescription') }); router.refresh(); } @@ -263,11 +266,11 @@ export default function ResourceRules(props: { function getValueHelpText(type: string) { switch (type) { case "CIDR": - return "Enter an address in CIDR format (e.g., 103.21.244.0/22)"; + return t('rulesMatchIpAddressRangeDescription'); case "IP": - return "Enter an IP address (e.g., 103.21.244.12)"; + return t('rulesMatchIpAddress'); case "PATH": - return "Enter a URL path or pattern (e.g., /api/v1/todos or /api/v1/*)"; + return t('rulesMatchUrl'); } } @@ -286,8 +289,8 @@ export default function ResourceRules(props: { if (rule.match === "CIDR" && !isValidCIDR(rule.value)) { toast({ variant: "destructive", - title: "Invalid CIDR", - description: "Please enter a valid CIDR value" + title: t('rulesErrorInvalidIpAddressRange'), + description: t('rulesErrorInvalidIpAddressRangeDescription') }); setLoading(false); return; @@ -298,8 +301,8 @@ export default function ResourceRules(props: { ) { toast({ variant: "destructive", - title: "Invalid URL path", - description: "Please enter a valid URL path value" + title: t('rulesErrorInvalidUrl'), + description: t('rulesErrorInvalidUrlDescription') }); setLoading(false); return; @@ -307,8 +310,8 @@ export default function ResourceRules(props: { if (rule.match === "IP" && !isValidIP(rule.value)) { toast({ variant: "destructive", - title: "Invalid IP", - description: "Please enter a valid IP address" + title: t('rulesErrorInvalidIpAddress'), + description: t('rulesErrorInvalidIpAddressDescription') }); setLoading(false); return; @@ -317,8 +320,8 @@ export default function ResourceRules(props: { if (rule.priority === undefined) { toast({ variant: "destructive", - title: "Invalid Priority", - description: "Please enter a valid priority" + title: t('rulesErrorInvalidPriority'), + description: t('rulesErrorInvalidPriorityDescription') }); setLoading(false); return; @@ -329,8 +332,8 @@ export default function ResourceRules(props: { if (priorities.length !== new Set(priorities).size) { toast({ variant: "destructive", - title: "Duplicate Priorities", - description: "Please enter unique priorities" + title: t('rulesErrorDuplicatePriority'), + description: t('rulesErrorDuplicatePriorityDescription') }); setLoading(false); return; @@ -369,8 +372,8 @@ export default function ResourceRules(props: { } toast({ - title: "Rules updated", - description: "Rules updated successfully" + title: t('ruleUpdated'), + description: t('ruleUpdatedDescription') }); setRulesToRemove([]); @@ -379,10 +382,10 @@ export default function ResourceRules(props: { console.error(err); toast({ variant: "destructive", - title: "Operation failed", + title: t('ruleErrorUpdate'), description: formatAxiosError( err, - "An error occurred during the save operation" + t('ruleErrorUpdateDescription') ) }); } @@ -400,7 +403,7 @@ export default function ResourceRules(props: { column.toggleSorting(column.getIsSorted() === "asc") } > - Priority + {t('rulesPriority')} ); @@ -420,8 +423,8 @@ export default function ResourceRules(props: { if (!parsed.data) { toast({ variant: "destructive", - title: "Invalid IP", - description: "Please enter a valid priority" + title: t('rulesErrorInvalidIpAddress'), // correct priority or IP? + description: t('rulesErrorInvalidPriorityDescription') }); setLoading(false); return; @@ -436,7 +439,7 @@ export default function ResourceRules(props: { }, { accessorKey: "action", - header: "Action", + header: t('rulesAction'), cell: ({ row }) => ( ( ( removeRule(row.original.ruleId)} > - Delete + {t('delete')}
) @@ -542,46 +545,40 @@ export default function ResourceRules(props: { - About Rules + {t('rulesAbout')}

- Rules allow you to control access to your resource - based on a set of criteria. You can create rules to - allow or deny access based on IP address or URL - path. + {t('rulesAboutDescription')}

- Actions + {t('rulesActions')}
  • - Always Allow: Bypass all authentication - methods + {t('rulesActionAlwaysAllow')}
  • - Always Deny: Block all requests; no - authentication can be attempted + {t('rulesActionAlwaysDeny')}
- Matching Criteria + {t('rulesMatchCriteria')}
  • - Match a specific IP address + {t('rulesMatchCriteriaIpAddress')}
  • - Match a range of IP addresses in CIDR - notation + {t('rulesMatchCriteriaIpAddressRange')}
  • - Match a URL path or pattern + {t('rulesMatchCriteriaUrl')}
@@ -591,15 +588,15 @@ export default function ResourceRules(props: { - Enable Rules + {t('rulesEnable')} - Enable or disable rule evaluation for this resource + {t('rulesEnableDescription')} { await saveApplyRules(val); @@ -611,10 +608,10 @@ export default function ResourceRules(props: { - Resource Rules Configuration + {t('rulesResource')} - Configure rules to control access to your resource + {t('rulesResourceDescription')} @@ -629,7 +626,7 @@ export default function ResourceRules(props: { name="action" render={({ field }) => ( - Action + {t('rulesAction')} ( - Add Rule + {t('ruleSubmit')}
@@ -760,13 +757,13 @@ export default function ResourceRules(props: { colSpan={columns.length} className="h-24 text-center" > - No rules. Add a rule using the form. + {t('rulesNoOne')} )} - Rules are evaluated by priority in ascending order. + {t('rulesOrder')}
@@ -776,7 +773,7 @@ export default function ResourceRules(props: { loading={loading} disabled={loading} > - Save Rules + {t('rulesSubmit')}
diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 22ebf1ff..dec102c7 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -242,10 +242,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error fetching sites", + title: t('sitesErrorFetch'), description: formatAxiosError( e, - "An error occurred when fetching the sites" + t('sitesErrorFetchDescription') ) }); }); @@ -270,10 +270,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error fetching domains", + title: t('domainsErrorFetch'), description: formatAxiosError( e, - "An error occurred when fetching the domains" + t('domainsErrorFetchDescription') ) }); }); diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index 707ece63..74eda80e 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -10,6 +10,7 @@ import { GetOrgResponse } from "@server/routers/org"; import OrgProvider from "@app/providers/OrgProvider"; import ResourcesSplashCard from "./ResourcesSplashCard"; import { getTranslations } from 'next-intl/server'; +import { useTranslations } from "next-intl"; type ResourcesPageProps = { params: Promise<{ orgId: string }>; @@ -46,14 +47,16 @@ export default async function ResourcesPage(props: ResourcesPageProps) { redirect(`/${params.orgId}/settings/resources`); } + const t = useTranslations(); + const resourceRows: ResourceRow[] = resources.map((resource) => { return { id: resource.resourceId, name: resource.name, orgId: params.orgId, domain: `${resource.ssl ? "https://" : "http://"}${resource.fullDomain}`, - site: resource.siteName || "None", - siteId: resource.siteId || "Unknown", + site: resource.siteName || t('none'), + siteId: resource.siteId || t('unknown'), protocol: resource.protocol, proxyPort: resource.proxyPort, http: resource.http, @@ -69,8 +72,6 @@ export default async function ResourcesPage(props: ResourcesPageProps) { }; }); - const t = await getTranslations(); - return ( <> {/* */} diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index ff8ae948..3487942c 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -92,7 +92,6 @@ export default function CreateShareLinkForm({ const { env } = useEnvContext(); const api = createApiClient({ env }); - const t = useTranslations(); const [link, setLink] = useState(null); const [accessTokenId, setAccessTokenId] = useState(null); @@ -111,6 +110,8 @@ export default function CreateShareLinkForm({ }[] >([]); + const t = useTranslations(); + const timeUnits = [ { unit: "minutes", name: t('minutes') }, { unit: "hours", name: t('hours') }, @@ -143,8 +144,11 @@ export default function CreateShareLinkForm({ console.error(e); toast({ variant: "destructive", - title: t('shareErrorFetchResource'), - description: formatAxiosError(e, t('shareErrorFetchResourceDescription')) + title: "Failed to fetch resources", + description: formatAxiosError( + e, + "An error occurred while fetching the resources" + ) }); }); @@ -207,8 +211,11 @@ export default function CreateShareLinkForm({ console.error(e); toast({ variant: "destructive", - title: t('shareErrorCreate'), - description: formatAxiosError(e, t('shareErrorCreateDescription')) + title: "Failed to create share link", + description: formatAxiosError( + e, + "An error occurred while creating the share link" + ) }); }); @@ -294,7 +301,7 @@ export default function CreateShareLinkForm({ ? getSelectedResourceName( field.value ) - : "Select resource"} + : t('resourceSelect')} @@ -387,7 +394,7 @@ export default function CreateShareLinkForm({ > - + @@ -497,7 +504,7 @@ export default function CreateShareLinkForm({
- Toggle + {t('toggle')}
@@ -527,7 +534,7 @@ export default function CreateShareLinkForm({
- + @@ -39,26 +42,23 @@ export const ShareableLinksSplash = () => {

- Shareable Links + {t('share')}

- Create shareable links to your resources. Links provide - temporary or unlimited access to your resource. You can - configure the expiration duration of the link when you - create one. + {t('shareDescription2')}

  • - Easy to create and share + {t('shareEasyCreate')}
  • - Configurable expiration duration + {t('shareConfigurableExpirationDuration')}
  • - Secure and revocable + {t('shareSecureAndRevocable')}
diff --git a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx index 767f15ad..ebdf3104 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx @@ -69,8 +69,11 @@ export default function ShareLinksTable({ async function deleteSharelink(id: string) { await api.delete(`/access-token/${id}`).catch((e) => { toast({ - title: t('shareErrorDelete'), - description: formatAxiosError(e,t('shareErrorDeleteMessage')) + title: "Failed to delete link", + description: formatAxiosError( + e, + "An error occurred deleting link" + ) }); }); @@ -78,8 +81,8 @@ export default function ShareLinksTable({ setRows(newRows); toast({ - title: t('shareDeleted'), - description: t('shareDeletedDesciption') + title: "Link deleted", + description: "The link has been deleted" }); } diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 74667af2..7e96593d 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -52,16 +52,14 @@ import { import LoaderPlaceholder from "@app/components/PlaceHolderLoader"; import { useTranslations } from 'next-intl'; -const t = useTranslations(); - const createSiteFormSchema = z.object({ name: z .string() .min(2, { - message: {t('siteNameMin')} + message: "Name must be at least 2 characters." }) .max(30, { - message: {t('siteNameMax')} + message: "Name must not be longer than 30 characters." }), method: z.enum(["wireguard", "newt", "local"]) }); @@ -117,6 +115,8 @@ export default function CreateSiteForm({ const nameField = form.watch("name"); const methodField = form.watch("method"); + const t = useTranslations(); + useEffect(() => { const nameIsValid = nameField?.length >= 2 && nameField?.length <= 30; const isFormValid = methodField === "local" || isChecked; @@ -172,8 +172,8 @@ export default function CreateSiteForm({ if (!keypair || !siteDefaults) { toast({ variant: "destructive", - title: {t('siteErrorCreate')}, - description: {t('siteErrorCreateKeyPair')} + title: "Error creating site", + description: "Key pair or site defaults not found" }); setLoading?.(false); setIsLoading(false); @@ -191,8 +191,8 @@ export default function CreateSiteForm({ if (!siteDefaults) { toast({ variant: "destructive", - title: {t('siteErrorCreate')}, - description: {t('siteErrorCreateDefaults')} + title: "Error creating site", + description: "Site defaults not found" }); setLoading?.(false); setIsLoading(false); @@ -215,7 +215,7 @@ export default function CreateSiteForm({ .catch((e) => { toast({ variant: "destructive", - title: {t('siteErrorCreate')}, + title: "Error creating site", description: formatAxiosError(e) }); }); @@ -315,7 +315,7 @@ PersistentKeepalive = 5` - Local + {t('local')} ({ { @@ -70,7 +70,7 @@ export const SitesSplashCard = () => { className="w-full flex items-center" variant="secondary" > - Install Newt{" "} + {t('siteInstallNewt')}{" "} @@ -78,20 +78,19 @@ export const SitesSplashCard = () => {

- Basic WireGuard + {t('siteWg')}

- Use any WireGuard client to connect. You will have to - address your internal resources using the peer IP. + {t('siteWgAnyClients')}

  • - Compatible with all WireGuard clients + {t('siteWgCompatibleAllClients')}
  • - Manual configuration required + {t('siteWgManualConfigurationRequired')}
diff --git a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx index 63c7b2e8..c2e830a6 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx @@ -25,7 +25,7 @@ export default function SiteInfoCard({}: SiteInfoCardProps) { } else if (type === "local") { return t('local'); } else { - return "Unknown"; + return t('unknown'); } }; diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index 1ed6ad91..512ef808 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -47,7 +47,6 @@ export default function GeneralPage() { const [loading, setLoading] = useState(false); const router = useRouter(); - const t = useTranslations(); const form = useForm({ resolver: zodResolver(GeneralFormSchema), @@ -56,6 +55,7 @@ export default function GeneralPage() { }, mode: "onChange" }); + const t = useTranslations(); async function onSubmit(data: GeneralFormValues) { setLoading(true); @@ -67,16 +67,19 @@ export default function GeneralPage() { .catch((e) => { toast({ variant: "destructive", - title: t('siteErrorUpdate'), - description: formatAxiosError(e,t('siteErrorUpdateDescription')) + title: "Failed to update site", + description: formatAxiosError( + e, + "An error occurred while updating the site." + ) }); }); updateSite({ name: data.name }); toast({ - title: t('siteUpdated'), - description: t('siteUpdatedDescription') + title: "Site updated", + description: "The site has been updated." }); setLoading(false); diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 300faa0e..56e882e3 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -70,7 +70,7 @@ const createSiteFormSchema = z .object({ name: z .string() - .min(2, { message: "Name must be at least 2 characters." }) + .min(2, "Name must be at least 2 characters.") .max(30, { message: "Name must not be longer than 30 characters." }), @@ -324,7 +324,7 @@ WantedBy=default.target` }; const getCommand = () => { - const placeholder = ["Unknown command"]; + const placeholder = [t('unknownCommand')]; if (!commands) { return placeholder; } @@ -381,8 +381,8 @@ WantedBy=default.target` if (!siteDefaults || !wgConfig) { toast({ variant: "destructive", - title: t('siteErrorCreate'), - description: t('siteErrorCreateKeyPair') + title: "Error creating site", + description: "Key pair or site defaults not found" }); setCreateLoading(false); return; @@ -399,8 +399,8 @@ WantedBy=default.target` if (!siteDefaults) { toast({ variant: "destructive", - title: t('siteErrorCreate'), - description: t('siteErrorCreateDefaults') + title: "Error creating site", + description: "Site defaults not found" }); setCreateLoading(false); return; @@ -422,7 +422,7 @@ WantedBy=default.target` .catch((e) => { toast({ variant: "destructive", - title: t('siteErrorCreate'), + title: "Error creating site", description: formatAxiosError(e) }); }); @@ -448,14 +448,14 @@ WantedBy=default.target` ); if (!response.ok) { throw new Error( - `Failed to fetch release info: ${response.statusText}` + t('newtErrorFetchReleases', {err: response.statusText}) ); } const data = await response.json(); const latestVersion = data.tag_name; newtVersion = latestVersion; } catch (error) { - console.error("Error fetching latest release:", error); + console.error(t('newtErrorFetchLatest', {err: error instanceof Error ? error.message : String(error)})); } const generatedKeypair = generateKeypair(); @@ -612,7 +612,7 @@ WantedBy=default.target` - Newt Endpoint + {t('newtEndpoint')} - Newt ID + {t('newtId')} - Newt Secret Key + {t('newtSecretKey')}
{getArchitectures().map( diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index 247c5c59..2bdf27f0 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -62,10 +62,10 @@ const createFormSchema = z.object({ name: z .string() .min(2, { - message: t('apiKeysNameMin') + message: t('nameMin', {len: 2}) }) .max(255, { - message: t('apiKeysNameMax') + message: t('nameMax', {len: 255}) }) }); diff --git a/src/app/admin/idp/[idpId]/general/page.tsx b/src/app/admin/idp/[idpId]/general/page.tsx index 857c7e48..bca4ac7a 100644 --- a/src/app/admin/idp/[idpId]/general/page.tsx +++ b/src/app/admin/idp/[idpId]/general/page.tsx @@ -43,9 +43,12 @@ import { import CopyToClipboard from "@app/components/CopyToClipboard"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; + +const t = useTranslations(); const GeneralFormSchema = z.object({ - name: z.string().min(2, { message: "Name must be at least 2 characters." }), + name: z.string().min(2, { message: t('nameMin', {len: 2}) }), clientId: z.string().min(1, { message: "Client ID is required." }), clientSecret: z.string().min(1, { message: "Client Secret is required." }), authUrl: z.string().url({ message: "Auth URL must be a valid URL." }), diff --git a/src/app/admin/idp/create/page.tsx b/src/app/admin/idp/create/page.tsx index 85ec0f74..63883554 100644 --- a/src/app/admin/idp/create/page.tsx +++ b/src/app/admin/idp/create/page.tsx @@ -37,9 +37,12 @@ import { StrategySelect } from "@app/components/StrategySelect"; import { SwitchInput } from "@app/components/SwitchInput"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; + +const t = useTranslations(); const createIdpFormSchema = z.object({ - name: z.string().min(2, { message: "Name must be at least 2 characters." }), + name: z.string().min(2, { message: t('nameMin', {len: 2}) }), type: z.enum(["oidc"]), clientId: z.string().min(1, { message: "Client ID is required." }), clientSecret: z.string().min(1, { message: "Client Secret is required." }), From d2d84be99abd0510122f84408f17af616598a798 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Sat, 17 May 2025 19:04:19 +0300 Subject: [PATCH 066/105] I18n admin (#22) * New translation keys in en-US locale * New translation keys in de-DE locale * New translation keys in fr-FR locale * New translation keys in it-IT locale * New translation keys in pl-PL locale * New translation keys in pt-PT locale * New translation keys in tr-TR locale * Add translation keys in app/admin * Fix build --------- Co-authored-by: Lokowitz --- messages/de-DE.json | 120 +++++++++++++++++- messages/en-US.json | 116 ++++++++++++++++- messages/fr-FR.json | 116 ++++++++++++++++- messages/it-IT.json | 116 ++++++++++++++++- messages/pl-PL.json | 116 ++++++++++++++++- messages/pt-PT.json | 116 ++++++++++++++++- messages/tr-TR.json | 116 ++++++++++++++++- .../invitations/RegenerateInvitationForm.tsx | 2 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 10 +- src/app/admin/api-keys/ApiKeysTable.tsx | 6 +- .../api-keys/[apiKeyId]/permissions/page.tsx | 8 +- src/app/admin/api-keys/create/page.tsx | 18 +-- src/app/admin/idp/AdminIdpDataTable.tsx | 8 +- src/app/admin/idp/AdminIdpTable.tsx | 37 +++--- src/app/admin/idp/[idpId]/general/page.tsx | 100 ++++++--------- src/app/admin/idp/[idpId]/layout.tsx | 11 +- .../idp/[idpId]/policies/PolicyDataTable.tsx | 8 +- .../idp/[idpId]/policies/PolicyTable.tsx | 14 +- src/app/admin/idp/[idpId]/policies/page.tsx | 99 ++++++--------- src/app/admin/idp/create/page.tsx | 117 +++++++---------- src/app/admin/idp/page.tsx | 6 +- src/app/admin/layout.tsx | 1 + .../admin/license/LicenseKeysDataTable.tsx | 6 +- .../components/SitePriceCalculator.tsx | 4 +- src/app/admin/license/page.tsx | 51 +++++--- src/app/admin/users/page.tsx | 6 +- src/app/setup/page.tsx | 6 +- 27 files changed, 1028 insertions(+), 306 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 38fa7696..f5ee471d 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -5,7 +5,7 @@ "setupCreateResources": "Ressource erstellen", "setupOrgName": "Organisation's Name", "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", - "setupOrgId": "Organisations-ID", + "orgId": "Organisations-ID", "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", @@ -54,8 +54,6 @@ "siteCreateDescription2": "Folgen Sie den Schritten unten, um eine neue Seite zu erstellen und zu verbinden", "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", "close": "Schließen", - "siteNameMin": "Der Name muss mindestens 2 Zeichen lang sein.", - "siteNameMax": "Name darf nicht länger als 30 Zeichen sein.", "siteErrorCreate": "Fehler beim Erstellen der Seite", "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", @@ -252,8 +250,6 @@ "years": "Jahre", "day": "{count, plural, =1 {# Tag} other {# Tage}}", "apiKeysTitle": "API-Schlüssel Information", - "apiKeysNameMin": "Name muss mindestens 2 Zeichen lang sein.", - "apiKeysNameMax": "Name darf nicht länger als 255 Zeichen sein.", "apiKeysConfirmCopy2": "Sie müssen bestätigen, dass Sie den API-Schlüssel kopiert haben.", "apiKeysErrorCreate": "Fehler beim Erstellen des API-Schlüssels", "apiKeysErrorSetPermission": "Fehler beim Setzen der Berechtigungen", @@ -338,7 +334,7 @@ "licenseTitleDescription": "Lizenzschlüssel im System anzeigen und verwalten", "licenseHost": "Hostlizenz", "licenseHostDescription": "Verwalten Sie den Haupt-Lizenzschlüssel für den Host.", - "notLicensed": "Nicht lizenziert", + "licensedNot": "Nicht lizenziert", "hostId": "Host-ID", "licenseReckeckAll": "Überprüfe alle Schlüssel", "licenseSiteUsage": "Website-Nutzung", @@ -696,5 +692,115 @@ "accessRoleRemovedDescription": "Die Rolle wurde erfolgreich entfernt.", "accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.", "manage": "Verwalten", - "sitesNotFound": "Keine Sites gefunden." + "sitesNotFound": "Keine Sites gefunden.", + "expiresAt": "Läuft ab am", + "pangolinServerAdmin": "Server Admin - Pangolin", + "idpNameInternal": "Intern", + "licenseTierProfessional": "Professional Lizenz", + "licenseTierEnterprise": "Enterprise Lizenz", + "licensed": "Lizenziert", + "yes": "Ja", + "no": "Nein", + "sitesAdditional": "Zusätzliche Sites", + "licenseKeys": "Lizenzschlüssel", + "sitestCountDecrease": "Anzahl der Sites verringern", + "sitestCountIncrease": "Anzahl der Sites erhöhen", + "idpManage": "Identitätsanbieter verwalten", + "idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten", + "idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Sind Sie sicher, dass Sie den Identitätsanbieter {name} dauerhaft löschen möchten?", + "idpMessageRemove": "Dies wird den Identitätsanbieter und alle zugehörigen Konfigurationen entfernen. Benutzer, die sich über diesen Anbieter authentifizieren, können sich nicht mehr anmelden.", + "idpMessageConfirm": "Bitte geben Sie zur Bestätigung den Namen des Identitätsanbieters unten ein.", + "idpConfirmDelete": "Löschen des Identitätsanbieters bestätigen", + "idpDelete": "Identitätsanbieter löschen", + "idp": "Identitätsanbieter", + "idpSearch": "Identitätsanbieter suchen...", + "idpAdd": "Identitätsanbieter hinzufügen", + "nameMin": "Der Name muss mindestens {len} Zeichen lang sein.", + "nameMax": "Der Name darf nicht länger als {len} Zeichen sein.", + "idpClientIdRequired": "Client-ID ist erforderlich.", + "idpClientSecretRequired": "Client-Secret ist erforderlich.", + "idpErrorAuthUrlInvalid": "Auth-URL muss eine gültige URL sein.", + "idpErrorTokenUrlInvalid": "Token-URL muss eine gültige URL sein.", + "idpPathRequired": "Identifikationspfad ist erforderlich.", + "idpScopeRequired": "Scopes sind erforderlich.", + "idpOidcDescription": "Konfigurieren Sie einen OpenID Connect Identitätsanbieter", + "idpCreatedDescription": "Identitätsanbieter erfolgreich erstellt", + "idpCreate": "Identitätsanbieter erstellen", + "idpCreateDescription": "Konfigurieren Sie einen neuen Identitätsanbieter für die Benutzerauthentifizierung", + "idpSeeAll": "Alle Identitätsanbieter anzeigen", + "idpTitle": "Allgemeine Informationen", + "idpSettingsDescription": "Konfigurieren Sie die grundlegenden Informationen für Ihren Identitätsanbieter", + "idpDisplayName": "Ein Anzeigename für diesen Identitätsanbieter", + "idpAutoProvisionUsers": "Automatische Benutzerbereitstellung", + "idpAutoProvisionUsersDescription": "Wenn aktiviert, werden Benutzer beim ersten Login automatisch im System erstellt, mit der Möglichkeit, Benutzer Rollen und Organisationen zuzuordnen.", + "licenseBadge": "Professional", + "idpType": "Anbietertyp", + "idpTypeDescription": "Wählen Sie den Typ des Identitätsanbieters, den Sie konfigurieren möchten", + "idpOidcConfigure": "OAuth2/OIDC Konfiguration", + "idpOidcConfigureDescription": "Konfigurieren Sie die OAuth2/OIDC Anbieter-Endpunkte und Anmeldeinformationen", + "idpClientId": "Client-ID", + "idpClientIdDescription": "Die OAuth2 Client-ID von Ihrem Identitätsanbieter", + "idpClientSecret": "Client-Secret", + "idpClientSecretDescription": "Das OAuth2 Client-Secret von Ihrem Identitätsanbieter", + "idpAuthUrl": "Autorisierungs-URL", + "idpAuthUrlDescription": "Die OAuth2 Autorisierungs-Endpunkt-URL", + "idpTokenUrl": "Token-URL", + "idpTokenUrlDescription": "Die OAuth2 Token-Endpunkt-URL", + "idpOidcConfigureAlert": "Wichtige Information", + "idpOidcConfigureAlertDescription": "Nach dem Erstellen des Identitätsanbieters müssen Sie die Callback-URL in den Einstellungen Ihres Identitätsanbieters konfigurieren. Die Callback-URL wird nach erfolgreicher Erstellung bereitgestellt.", + "idpToken": "Token-Konfiguration", + "idpTokenDescription": "Konfigurieren Sie, wie Benutzerinformationen aus dem ID-Token extrahiert werden", + "idpJmespathAbout": "Über JMESPath", + "idpJmespathAboutDescription": "Die unten stehenden Pfade verwenden JMESPath-Syntax, um Werte aus dem ID-Token zu extrahieren.", + "idpJmespathAboutDescriptionLink": "Mehr über JMESPath erfahren", + "idpJmespathLabel": "Identifikationspfad", + "idpJmespathLabelDescription": "Der JMESPath zum Benutzeridentifikator im ID-Token", + "idpJmespathEmailPathOptional": "E-Mail-Pfad (Optional)", + "idpJmespathEmailPathOptionalDescription": "Der JMESPath zur E-Mail-Adresse des Benutzers im ID-Token", + "idpJmespathNamePathOptional": "Namenspfad (Optional)", + "idpJmespathNamePathOptionalDescription": "Der JMESPath zum Namen des Benutzers im ID-Token", + "idpOidcConfigureScopes": "Scopes", + "idpOidcConfigureScopesDescription": "Durch Leerzeichen getrennte Liste der anzufordernden OAuth2-Scopes", + "idpSubmit": "Identitätsanbieter erstellen", + "orgPolicies": "Organisationsrichtlinien", + "idpSettings": "{idpName} Einstellungen", + "idpCreateSettingsDescription": "Konfigurieren Sie die Einstellungen für Ihren Identitätsanbieter", + "roleMapping": "Rollenzuordnung", + "orgMapping": "Organisationszuordnung", + "orgPoliciesSearch": "Organisationsrichtlinien suchen...", + "orgPoliciesAdd": "Organisationsrichtlinie hinzufügen", + "orgRequired": "Organisation ist erforderlich", + "error": "Fehler", + "success": "Erfolg", + "orgPolicyAddedDescription": "Richtlinie erfolgreich hinzugefügt", + "orgPolicyUpdatedDescription": "Richtlinie erfolgreich aktualisiert", + "orgPolicyDeletedDescription": "Richtlinie erfolgreich gelöscht", + "defaultMappingsUpdatedDescription": "Standardzuordnungen erfolgreich aktualisiert", + "orgPoliciesAbout": "Über Organisationsrichtlinien", + "orgPoliciesAboutDescription": "Organisationsrichtlinien werden verwendet, um den Zugriff auf Organisationen basierend auf dem ID-Token des Benutzers zu steuern. Sie können JMESPath-Ausdrücke angeben, um Rollen- und Organisationsinformationen aus dem ID-Token zu extrahieren. Weitere Informationen finden Sie in", + "orgPoliciesAboutDescriptionLink": "der Dokumentation", + "defaultMappingsOptional": "Standardzuordnungen (Optional)", + "defaultMappingsOptionalDescription": "Die Standardzuordnungen werden verwendet, wenn keine Organisationsrichtlinie für eine Organisation definiert ist. Sie können hier die Standard-Rollen- und Organisationszuordnungen festlegen.", + "defaultMappingsRole": "Standard-Rollenzuordnung", + "defaultMappingsRoleDescription": "JMESPath zur Extraktion von Rolleninformationen aus dem ID-Token. Das Ergebnis dieses Ausdrucks muss den Rollennamen als String zurückgeben, wie er in der Organisation definiert ist.", + "defaultMappingsOrg": "Standard-Organisationszuordnung", + "defaultMappingsOrgDescription": "JMESPath zur Extraktion von Organisationsinformationen aus dem ID-Token. Dieser Ausdruck muss die Organisations-ID oder true zurückgeben, damit der Benutzer Zugriff auf die Organisation erhält.", + "defaultMappingsSubmit": "Standardzuordnungen speichern", + "orgPoliciesEdit": "Organisationsrichtlinie bearbeiten", + "org": "Organisation", + "orgSelect": "Organisation auswählen", + "orgSearch": "Organisation suchen", + "orgNotFound": "Keine Organisation gefunden.", + "roleMappingPathOptional": "Rollenzuordnungspfad (Optional)", + "orgMappingPathOptional": "Organisationszuordnungspfad (Optional)", + "orgPolicyUpdate": "Richtlinie aktualisieren", + "orgPolicyAdd": "Richtlinie hinzufügen", + "idpUpdatedDescription": "Identitätsanbieter erfolgreich aktualisiert", + "redirectUrl": "Weiterleitungs-URL", + "redirectUrlAbout": "Über die Weiterleitungs-URL", + "redirectUrlAboutDescription": "Dies ist die URL, zu der Benutzer nach der Authentifizierung weitergeleitet werden. Sie müssen diese URL in den Einstellungen Ihres Identitätsanbieters konfigurieren.", + "key": "Schlüssel", + "createdAt": "Erstellt am" } diff --git a/messages/en-US.json b/messages/en-US.json index aab356d1..d21a8e4c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -5,7 +5,7 @@ "setupCreateResources": "Create Resources", "setupOrgName": "Organization Name", "orgDisplayName": "This is the display name of your organization.", - "setupOrgId": "Organization ID", + "orgId": "Organization ID", "setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.", "setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.", "componentsErrorNoMemberCreate": "You are not currently a member of any organizations. Create an organization to get started.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "View and manage license keys in the system", "licenseHost": "Host License", "licenseHostDescription": "Manage the main license key for the host.", - "notLicensed": "Not Licensed", + "licensedNot": "Not Licensed", "hostId": "Host ID", "licenseReckeckAll": "Recheck All Keys", "licenseSiteUsage": "Sites Usage", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "The role has been successfully removed.", "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", "manage": "Manage", - "sitesNotFound": "No sites found." + "sitesNotFound": "No sites found.", + "expiresAt": "Expires At", + "pangolinServerAdmin": "Server Admin - Pangolin", + "idpNameInternal": "Internal", + "licenseTierProfessional": "Professional License", + "licenseTierEnterprise": "Enterprise License", + "licensed": "Licensed", + "yes": "Yes", + "no": "No", + "sitesAdditional": "Additional Sites", + "licenseKeys": "License Keys", + "sitestCountDecrease": "Decrease site count", + "sitestCountIncrease": "Increase site count", + "idpManage": "Manage Identity Providers", + "idpManageDescription": "View and manage identity providers in the system", + "idpDeletedDescription": "Identity provider deleted successfully", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", + "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", + "idpConfirmDelete": "Confirm Delete Identity Provider", + "idpDelete": "Delete Identity Provider", + "idp": "Identity Providers", + "idpSearch": "Search identity providers...", + "idpAdd": "Add Identity Provider", + "nameMin": "Name must be at least {len} characters.", + "nameMax": "Name must not be longer than {len} characters.", + "idpClientIdRequired": "Client ID is required.", + "idpClientSecretRequired": "Client Secret is required.", + "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", + "idpErrorTokenUrlInvalid": "Token URL must be a valid URL.", + "idpPathRequired": "Identifier Path is required.", + "idpScopeRequired": "Scopes are required.", + "idpOidcDescription": "Configure an OpenID Connect identity provider", + "idpCreatedDescription": "Identity provider created successfully", + "idpCreate": "Create Identity Provider", + "idpCreateDescription": "Configure a new identity provider for user authentication", + "idpSeeAll": "See All Identity Providers", + "idpTitle": "General Information", + "idpSettingsDescription": "Configure the basic information for your identity provider", + "idpDisplayName": "A display name for this identity provider", + "idpAutoProvisionUsers": "Auto Provision Users", + "idpAutoProvisionUsersDescription": "When enabled, users will be automatically created in the system upon first login with the ability to map users to roles and organizations.", + "licenseBadge": "Professional", + "idpType": "Provider Type", + "idpTypeDescription": "Select the type of identity provider you want to configure", + "idpOidcConfigure": "OAuth2/OIDC Configuration", + "idpOidcConfigureDescription": "Configure the OAuth2/OIDC provider endpoints and credentials", + "idpClientId": "Client ID", + "idpClientIdDescription": "The OAuth2 client ID from your identity provider", + "idpClientSecret": "Client Secret", + "idpClientSecretDescription": "The OAuth2 client secret from your identity provider", + "idpAuthUrl": "Authorization URL", + "idpAuthUrlDescription": "The OAuth2 authorization endpoint URL", + "idpTokenUrl": "Token URL", + "idpTokenUrlDescription": "The OAuth2 token endpoint URL", + "idpOidcConfigureAlert": "Important Information", + "idpOidcConfigureAlertDescription": "After creating the identity provider, you will need to configure the callback URL in your identity provider's settings. The callback URL will be provided after successful creation.", + "idpToken": "Token Configuration", + "idpTokenDescription": "Configure how to extract user information from the ID token", + "idpJmespathAbout": "About JMESPath", + "idpJmespathAboutDescription": "The paths below use JMESPath syntax to extract values from the ID token.", + "idpJmespathAboutDescriptionLink": "Learn more about JMESPath", + "idpJmespathLabel": "Identifier Path", + "idpJmespathLabelDescription": "The JMESPath to the user identifier in the ID token", + "idpJmespathEmailPathOptional": "Email Path (Optional)", + "idpJmespathEmailPathOptionalDescription": "The JMESPath to the user's email in the ID token", + "idpJmespathNamePathOptional": "Name Path (Optional)", + "idpJmespathNamePathOptionalDescription": "The JMESPath to the user's name in the ID token", + "idpOidcConfigureScopes": "Scopes", + "idpOidcConfigureScopesDescription": "Space-separated list of OAuth2 scopes to request", + "idpSubmit": "Create Identity Provider", + "orgPolicies": "Organization Policies", + "idpSettings": "{idpName} Settings", + "idpCreateSettingsDescription": "Configure the settings for your identity provider", + "roleMapping": "Role Mapping", + "orgMapping": "Organization Mapping", + "orgPoliciesSearch": "Search organization policies...", + "orgPoliciesAdd": "Add Organization Policy", + "orgRequired": "Organization is required", + "error": "Error", + "success": "Success", + "orgPolicyAddedDescription": "Policy added successfully", + "orgPolicyUpdatedDescription": "Policy updated successfully", + "orgPolicyDeletedDescription": "Policy deleted successfully", + "defaultMappingsUpdatedDescription": "Default mappings updated successfully", + "orgPoliciesAbout": "About Organization Policies", + "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token. For more information, see", + "orgPoliciesAboutDescriptionLink": "the documentation", + "defaultMappingsOptional": "Default Mappings (Optional)", + "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", + "defaultMappingsRole": "Default Role Mapping", + "defaultMappingsRoleDescription": "JMESPath to extract role information from the ID token. The result of this expression must return the role name as defined in the organization as a string.", + "defaultMappingsOrg": "Default Organization Mapping", + "defaultMappingsOrgDescription": "JMESPath to extract organization information from the ID token. This expression must return the org ID or true for the user to be allowed to access the organization.", + "defaultMappingsSubmit": "Save Default Mappings", + "orgPoliciesEdit": "Edit Organization Policy", + "org": "Organization", + "orgSelect": "Select organization", + "orgSearch": "Search org", + "orgNotFound": "No org found.", + "roleMappingPathOptional": "Role Mapping Path (Optional)", + "orgMappingPathOptional": "Organization Mapping Path (Optional)", + "orgPolicyUpdate": "Update Policy", + "orgPolicyAdd": "Add Policy", + "idpUpdatedDescription": "Identity provider updated successfully", + "redirectUrl": "Redirect URL", + "redirectUrlAbout": "About Redirect URL", + "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", + "key": "Key", + "createdAt": "Created At" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 28aa2b29..57482f81 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -5,7 +5,7 @@ "setupCreateResources": "Créer des ressources", "setupOrgName": "Nom de l'organisation", "orgDisplayName": "Ceci est le nom d'affichage de votre organisation.", - "setupOrgId": "ID de l'organisation", + "orgId": "ID de l'organisation", "setupIdentifierMessage": "Ceci est l'identifiant unique pour votre organisation. Il est séparé du nom affiché.", "setupErrorIdentifier": "L'ID de l'organisation est déjà pris. Veuillez en choisir un autre.", "componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "Voir et gérer les clés de licence dans le système", "licenseHost": "Licence Hôte", "licenseHostDescription": "Gérer la clé de licence principale de l'hôte.", - "notLicensed": "Non licencié", + "licensedNot": "Non licencié", "hostId": "ID de l'hôte", "licenseReckeckAll": "Revérifier toutes les clés", "licenseSiteUsage": "Utilisation des sites", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "Le rôle a été supprimé avec succès.", "accessRoleRequiredRemove": "Avant de supprimer ce rôle, veuillez sélectionner un nouveau rôle pour transférer les membres existants.", "manage": "Gérer", - "sitesNotFound": "Aucun site trouvé." + "sitesNotFound": "Aucun site trouvé.", + "expiresAt": "Expire le", + "pangolinServerAdmin": "Admin Serveur - Pangolin", + "idpNameInternal": "Interne", + "licenseTierProfessional": "Licence Professionnelle", + "licenseTierEnterprise": "Licence Entreprise", + "licensed": "Sous licence", + "yes": "Oui", + "no": "Non", + "sitesAdditional": "Sites supplémentaires", + "licenseKeys": "Clés de licence", + "sitestCountDecrease": "Diminuer le nombre de sites", + "sitestCountIncrease": "Augmenter le nombre de sites", + "idpManage": "Gérer les fournisseurs d'identité", + "idpManageDescription": "Voir et gérer les fournisseurs d'identité dans le système", + "idpDeletedDescription": "Fournisseur d'identité supprimé avec succès", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement le fournisseur d'identité {name} ?", + "idpMessageRemove": "Cela supprimera le fournisseur d'identité et toutes les configurations associées. Les utilisateurs qui s'authentifient via ce fournisseur ne pourront plus se connecter.", + "idpMessageConfirm": "Pour confirmer, veuillez saisir le nom du fournisseur d'identité ci-dessous.", + "idpConfirmDelete": "Confirmer la suppression du fournisseur d'identité", + "idpDelete": "Supprimer le fournisseur d'identité", + "idp": "Fournisseurs d'identité", + "idpSearch": "Rechercher des fournisseurs d'identité...", + "idpAdd": "Ajouter un fournisseur d'identité", + "nameMin": "Le nom doit comporter au moins {len} caractères.", + "nameMax": "Le nom ne doit pas dépasser {len} caractères.", + "idpClientIdRequired": "L'ID client est requis.", + "idpClientSecretRequired": "Le secret client est requis.", + "idpErrorAuthUrlInvalid": "L'URL d'authentification doit être une URL valide.", + "idpErrorTokenUrlInvalid": "L'URL du jeton doit être une URL valide.", + "idpPathRequired": "Le chemin d'identification est requis.", + "idpScopeRequired": "Les portées sont requises.", + "idpOidcDescription": "Configurer un fournisseur d'identité OpenID Connect", + "idpCreatedDescription": "Fournisseur d'identité créé avec succès", + "idpCreate": "Créer un fournisseur d'identité", + "idpCreateDescription": "Configurer un nouveau fournisseur d'identité pour l'authentification des utilisateurs", + "idpSeeAll": "Voir tous les fournisseurs d'identité", + "idpTitle": "Informations générales", + "idpSettingsDescription": "Configurer les informations de base de votre fournisseur d'identité", + "idpDisplayName": "Un nom d'affichage pour ce fournisseur d'identité", + "idpAutoProvisionUsers": "Approvisionnement automatique des utilisateurs", + "idpAutoProvisionUsersDescription": "Lorsque cette option est activée, les utilisateurs seront automatiquement créés dans le système lors de leur première connexion avec la possibilité de mapper les utilisateurs aux rôles et aux organisations.", + "licenseBadge": "Professionnel", + "idpType": "Type de fournisseur", + "idpTypeDescription": "Sélectionnez le type de fournisseur d'identité que vous souhaitez configurer", + "idpOidcConfigure": "Configuration OAuth2/OIDC", + "idpOidcConfigureDescription": "Configurer les points de terminaison et les identifiants du fournisseur OAuth2/OIDC", + "idpClientId": "ID Client", + "idpClientIdDescription": "L'ID client OAuth2 de votre fournisseur d'identité", + "idpClientSecret": "Secret Client", + "idpClientSecretDescription": "Le secret client OAuth2 de votre fournisseur d'identité", + "idpAuthUrl": "URL d'autorisation", + "idpAuthUrlDescription": "L'URL du point de terminaison d'autorisation OAuth2", + "idpTokenUrl": "URL du jeton", + "idpTokenUrlDescription": "L'URL du point de terminaison du jeton OAuth2", + "idpOidcConfigureAlert": "Information importante", + "idpOidcConfigureAlertDescription": "Après avoir créé le fournisseur d'identité, vous devrez configurer l'URL de rappel dans les paramètres de votre fournisseur d'identité. L'URL de rappel sera fournie après la création réussie.", + "idpToken": "Configuration du jeton", + "idpTokenDescription": "Configurer comment extraire les informations utilisateur du jeton ID", + "idpJmespathAbout": "À propos de JMESPath", + "idpJmespathAboutDescription": "Les chemins ci-dessous utilisent la syntaxe JMESPath pour extraire des valeurs du jeton ID.", + "idpJmespathAboutDescriptionLink": "En savoir plus sur JMESPath", + "idpJmespathLabel": "Chemin d'identification", + "idpJmespathLabelDescription": "Le JMESPath vers l'identifiant de l'utilisateur dans le jeton ID", + "idpJmespathEmailPathOptional": "Chemin de l'email (Optionnel)", + "idpJmespathEmailPathOptionalDescription": "Le JMESPath vers l'email de l'utilisateur dans le jeton ID", + "idpJmespathNamePathOptional": "Chemin du nom (Optionnel)", + "idpJmespathNamePathOptionalDescription": "Le JMESPath vers le nom de l'utilisateur dans le jeton ID", + "idpOidcConfigureScopes": "Portées", + "idpOidcConfigureScopesDescription": "Liste des portées OAuth2 à demander, séparées par des espaces", + "idpSubmit": "Créer le fournisseur d'identité", + "orgPolicies": "Politiques d'organisation", + "idpSettings": "Paramètres de {idpName}", + "idpCreateSettingsDescription": "Configurer les paramètres de votre fournisseur d'identité", + "roleMapping": "Mappage des rôles", + "orgMapping": "Mappage d'organisation", + "orgPoliciesSearch": "Rechercher des politiques d'organisation...", + "orgPoliciesAdd": "Ajouter une politique d'organisation", + "orgRequired": "L'organisation est requise", + "error": "Erreur", + "success": "Succès", + "orgPolicyAddedDescription": "Politique ajoutée avec succès", + "orgPolicyUpdatedDescription": "Politique mise à jour avec succès", + "orgPolicyDeletedDescription": "Politique supprimée avec succès", + "defaultMappingsUpdatedDescription": "Mappages par défaut mis à jour avec succès", + "orgPoliciesAbout": "À propos des politiques d'organisation", + "orgPoliciesAboutDescription": "Les politiques d'organisation sont utilisées pour contrôler l'accès aux organisations en fonction du jeton ID de l'utilisateur. Vous pouvez spécifier des expressions JMESPath pour extraire les informations de rôle et d'organisation du jeton ID. Pour plus d'informations, voir", + "orgPoliciesAboutDescriptionLink": "la documentation", + "defaultMappingsOptional": "Mappages par défaut (Optionnel)", + "defaultMappingsOptionalDescription": "Les mappages par défaut sont utilisés lorsqu'il n'y a pas de politique d'organisation définie pour une organisation. Vous pouvez spécifier ici les mappages de rôle et d'organisation par défaut à utiliser.", + "defaultMappingsRole": "Mappage de rôle par défaut", + "defaultMappingsRoleDescription": "JMESPath pour extraire les informations de rôle du jeton ID. Le résultat de cette expression doit renvoyer le nom du rôle tel que défini dans l'organisation sous forme de chaîne.", + "defaultMappingsOrg": "Mappage d'organisation par défaut", + "defaultMappingsOrgDescription": "JMESPath pour extraire les informations d'organisation du jeton ID. Cette expression doit renvoyer l'ID de l'organisation ou true pour que l'utilisateur soit autorisé à accéder à l'organisation.", + "defaultMappingsSubmit": "Enregistrer les mappages par défaut", + "orgPoliciesEdit": "Modifier la politique d'organisation", + "org": "Organisation", + "orgSelect": "Sélectionner une organisation", + "orgSearch": "Rechercher une organisation", + "orgNotFound": "Aucune organisation trouvée.", + "roleMappingPathOptional": "Chemin de mappage des rôles (Optionnel)", + "orgMappingPathOptional": "Chemin de mappage d'organisation (Optionnel)", + "orgPolicyUpdate": "Mettre à jour la politique", + "orgPolicyAdd": "Ajouter une politique", + "idpUpdatedDescription": "Fournisseur d'identité mis à jour avec succès", + "redirectUrl": "URL de redirection", + "redirectUrlAbout": "À propos de l'URL de redirection", + "redirectUrlAboutDescription": "C'est l'URL vers laquelle les utilisateurs seront redirigés après l'authentification. Vous devez configurer cette URL dans les paramètres de votre fournisseur d'identité.", + "key": "Clé", + "createdAt": "Créé le" } diff --git a/messages/it-IT.json b/messages/it-IT.json index eb62a660..98d59afb 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -5,7 +5,7 @@ "setupCreateResources": "Crea Risorse", "setupOrgName": "Nome Dell'Organizzazione", "orgDisplayName": "Questo è il nome visualizzato della tua organizzazione.", - "setupOrgId": "Id Organizzazione", + "orgId": "Id Organizzazione", "setupIdentifierMessage": "Questo è l' identificatore univoco della tua organizzazione. Questo è separato dal nome del display.", "setupErrorIdentifier": "L'ID dell'organizzazione è già utilizzato. Si prega di sceglierne uno diverso.", "componentsErrorNoMemberCreate": "Al momento non sei un membro di nessuna organizzazione. Crea un'organizzazione per iniziare.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "Visualizza e gestisci le chiavi di licenza nel sistema", "licenseHost": "Licenza Host", "licenseHostDescription": "Gestisci la chiave di licenza principale per l'host.", - "notLicensed": "Non Licenziato", + "licensedNot": "Non Licenziato", "hostId": "Host ID", "licenseReckeckAll": "Ricontrolla Tutte Le Tasti", "licenseSiteUsage": "Utilizzo Siti", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "Il ruolo è stato rimosso con successo.", "accessRoleRequiredRemove": "Prima di eliminare questo ruolo, seleziona un nuovo ruolo a cui trasferire i membri esistenti.", "manage": "Gestisci", - "sitesNotFound": "Nessun sito trovato." + "sitesNotFound": "Nessun sito trovato.", + "expiresAt": "Scade Il", + "pangolinServerAdmin": "Server Admin - Pangolin", + "idpNameInternal": "Interno", + "licenseTierProfessional": "Licenza Professional", + "licenseTierEnterprise": "Licenza Enterprise", + "licensed": "Con Licenza", + "yes": "Sì", + "no": "No", + "sitesAdditional": "Siti Aggiuntivi", + "licenseKeys": "Chiavi di Licenza", + "sitestCountDecrease": "Diminuisci conteggio siti", + "sitestCountIncrease": "Aumenta conteggio siti", + "idpManage": "Gestisci Provider di Identità", + "idpManageDescription": "Visualizza e gestisci i provider di identità nel sistema", + "idpDeletedDescription": "Provider di identità eliminato con successo", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Sei sicuro di voler eliminare definitivamente il provider di identità {name}?", + "idpMessageRemove": "Questo rimuoverà il provider di identità e tutte le configurazioni associate. Gli utenti che si autenticano tramite questo provider non potranno più accedere.", + "idpMessageConfirm": "Per confermare, digita il nome del provider di identità qui sotto.", + "idpConfirmDelete": "Conferma Eliminazione Provider di Identità", + "idpDelete": "Elimina Provider di Identità", + "idp": "Provider di Identità", + "idpSearch": "Cerca provider di identità...", + "idpAdd": "Aggiungi Provider di Identità", + "nameMin": "Il nome deve essere di almeno {len} caratteri.", + "nameMax": "Il nome non deve superare i {len} caratteri.", + "idpClientIdRequired": "L'ID client è obbligatorio.", + "idpClientSecretRequired": "Il segreto client è obbligatorio.", + "idpErrorAuthUrlInvalid": "L'URL di autenticazione deve essere un URL valido.", + "idpErrorTokenUrlInvalid": "L'URL del token deve essere un URL valido.", + "idpPathRequired": "Il percorso identificativo è obbligatorio.", + "idpScopeRequired": "Gli scope sono obbligatori.", + "idpOidcDescription": "Configura un provider di identità OpenID Connect", + "idpCreatedDescription": "Provider di identità creato con successo", + "idpCreate": "Crea Provider di Identità", + "idpCreateDescription": "Configura un nuovo provider di identità per l'autenticazione degli utenti", + "idpSeeAll": "Vedi Tutti i Provider di Identità", + "idpTitle": "Informazioni Generali", + "idpSettingsDescription": "Configura le informazioni di base per il tuo provider di identità", + "idpDisplayName": "Un nome visualizzato per questo provider di identità", + "idpAutoProvisionUsers": "Provisioning Automatico Utenti", + "idpAutoProvisionUsersDescription": "Quando abilitato, gli utenti verranno creati automaticamente nel sistema al primo accesso con la possibilità di mappare gli utenti a ruoli e organizzazioni.", + "licenseBadge": "Professional", + "idpType": "Tipo di Provider", + "idpTypeDescription": "Seleziona il tipo di provider di identità che desideri configurare", + "idpOidcConfigure": "Configurazione OAuth2/OIDC", + "idpOidcConfigureDescription": "Configura gli endpoint e le credenziali del provider OAuth2/OIDC", + "idpClientId": "ID Client", + "idpClientIdDescription": "L'ID client OAuth2 dal tuo provider di identità", + "idpClientSecret": "Segreto Client", + "idpClientSecretDescription": "Il segreto client OAuth2 dal tuo provider di identità", + "idpAuthUrl": "URL di Autorizzazione", + "idpAuthUrlDescription": "L'URL dell'endpoint di autorizzazione OAuth2", + "idpTokenUrl": "URL del Token", + "idpTokenUrlDescription": "L'URL dell'endpoint del token OAuth2", + "idpOidcConfigureAlert": "Informazioni Importanti", + "idpOidcConfigureAlertDescription": "Dopo aver creato il provider di identità, dovrai configurare l'URL di callback nelle impostazioni del tuo provider di identità. L'URL di callback verrà fornito dopo la creazione riuscita.", + "idpToken": "Configurazione Token", + "idpTokenDescription": "Configura come estrarre le informazioni dell'utente dal token ID", + "idpJmespathAbout": "Informazioni su JMESPath", + "idpJmespathAboutDescription": "I percorsi sottostanti utilizzano la sintassi JMESPath per estrarre valori dal token ID.", + "idpJmespathAboutDescriptionLink": "Scopri di più su JMESPath", + "idpJmespathLabel": "Percorso Identificativo", + "idpJmespathLabelDescription": "Il JMESPath per l'identificatore dell'utente nel token ID", + "idpJmespathEmailPathOptional": "Percorso Email (Opzionale)", + "idpJmespathEmailPathOptionalDescription": "Il JMESPath per l'email dell'utente nel token ID", + "idpJmespathNamePathOptional": "Percorso Nome (Opzionale)", + "idpJmespathNamePathOptionalDescription": "Il JMESPath per il nome dell'utente nel token ID", + "idpOidcConfigureScopes": "Scope", + "idpOidcConfigureScopesDescription": "Lista degli scope OAuth2 da richiedere separati da spazi", + "idpSubmit": "Crea Provider di Identità", + "orgPolicies": "Politiche Organizzazione", + "idpSettings": "Impostazioni {idpName}", + "idpCreateSettingsDescription": "Configura le impostazioni per il tuo provider di identità", + "roleMapping": "Mappatura Ruoli", + "orgMapping": "Mappatura Organizzazione", + "orgPoliciesSearch": "Cerca politiche organizzazione...", + "orgPoliciesAdd": "Aggiungi Politica Organizzazione", + "orgRequired": "L'organizzazione è obbligatoria", + "error": "Errore", + "success": "Successo", + "orgPolicyAddedDescription": "Politica aggiunta con successo", + "orgPolicyUpdatedDescription": "Politica aggiornata con successo", + "orgPolicyDeletedDescription": "Politica eliminata con successo", + "defaultMappingsUpdatedDescription": "Mappature predefinite aggiornate con successo", + "orgPoliciesAbout": "Informazioni sulle Politiche Organizzazione", + "orgPoliciesAboutDescription": "Le politiche organizzazione sono utilizzate per controllare l'accesso alle organizzazioni in base al token ID dell'utente. Puoi specificare espressioni JMESPath per estrarre informazioni su ruoli e organizzazioni dal token ID. Per maggiori informazioni, vedi", + "orgPoliciesAboutDescriptionLink": "la documentazione", + "defaultMappingsOptional": "Mappature Predefinite (Opzionale)", + "defaultMappingsOptionalDescription": "Le mappature predefinite sono utilizzate quando non esiste una politica organizzazione definita per un'organizzazione. Puoi specificare qui le mappature predefinite di ruolo e organizzazione da utilizzare come fallback.", + "defaultMappingsRole": "Mappatura Ruolo Predefinito", + "defaultMappingsRoleDescription": "JMESPath per estrarre informazioni sul ruolo dal token ID. Il risultato di questa espressione deve restituire il nome del ruolo come definito nell'organizzazione come stringa.", + "defaultMappingsOrg": "Mappatura Organizzazione Predefinita", + "defaultMappingsOrgDescription": "JMESPath per estrarre informazioni sull'organizzazione dal token ID. Questa espressione deve restituire l'ID dell'organizzazione o true affinché l'utente possa accedere all'organizzazione.", + "defaultMappingsSubmit": "Salva Mappature Predefinite", + "orgPoliciesEdit": "Modifica Politica Organizzazione", + "org": "Organizzazione", + "orgSelect": "Seleziona organizzazione", + "orgSearch": "Cerca organizzazione", + "orgNotFound": "Nessuna organizzazione trovata.", + "roleMappingPathOptional": "Percorso Mappatura Ruolo (Opzionale)", + "orgMappingPathOptional": "Percorso Mappatura Organizzazione (Opzionale)", + "orgPolicyUpdate": "Aggiorna Politica", + "orgPolicyAdd": "Aggiungi Politica", + "idpUpdatedDescription": "Provider di identità aggiornato con successo", + "redirectUrl": "URL di Reindirizzamento", + "redirectUrlAbout": "Informazioni sull'URL di Reindirizzamento", + "redirectUrlAboutDescription": "Questo è l'URL a cui gli utenti verranno reindirizzati dopo l'autenticazione. Devi configurare questo URL nelle impostazioni del tuo provider di identità.", + "key": "Chiave", + "createdAt": "Creato Il" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index d825eb3a..8999f36d 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -5,7 +5,7 @@ "setupCreateResources": "Utwórz Zasoby", "setupOrgName": "Nazwa organizacji", "orgDisplayName": "To jest wyświetlana nazwa Twojej organizacji.", - "setupOrgId": "Identyfikator organizacji", + "orgId": "Identyfikator organizacji", "setupIdentifierMessage": "To jest unikalny identyfikator Twojej organizacji. Jest to oddzielone od nazwy wyświetlanej.", "setupErrorIdentifier": "Identyfikator organizacji jest już zajęty. Wybierz inny.", "componentsErrorNoMemberCreate": "Nie jesteś obecnie członkiem żadnej organizacji. Aby rozpocząć, utwórz organizację.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "Wyświetl i zarządzaj kluczami licencyjnymi w systemie", "licenseHost": "Licencja hosta", "licenseHostDescription": "Zarządzaj głównym kluczem licencyjnym hosta.", - "notLicensed": "Brak licencji", + "licensedNot": "Brak licencji", "hostId": "ID hosta", "licenseReckeckAll": "Sprawdź ponownie wszystkie klucze", "licenseSiteUsage": "Użycie witryn", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "Rola została pomyślnie usunięta.", "accessRoleRequiredRemove": "Przed usunięciem tej roli, wybierz nową rolę do której zostaną przeniesieni obecni członkowie.", "manage": "Zarządzaj", - "sitesNotFound": "Nie znaleziono witryn." + "sitesNotFound": "Nie znaleziono witryn.", + "expiresAt": "Wygasa w dniu", + "pangolinServerAdmin": "Administrator serwera - Pangolin", + "idpNameInternal": "Wewnętrzny", + "licenseTierProfessional": "Licencja Professional", + "licenseTierEnterprise": "Licencja Enterprise", + "licensed": "Licencjonowany", + "yes": "Tak", + "no": "Nie", + "sitesAdditional": "Dodatkowe witryny", + "licenseKeys": "Klucze licencyjne", + "sitestCountDecrease": "Zmniejsz liczbę witryn", + "sitestCountIncrease": "Zwiększ liczbę witryn", + "idpManage": "Zarządzaj dostawcami tożsamości", + "idpManageDescription": "Wyświetl i zarządzaj dostawcami tożsamości w systemie", + "idpDeletedDescription": "Dostawca tożsamości został pomyślnie usunięty", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Czy na pewno chcesz trwale usunąć dostawcę tożsamości {name}?", + "idpMessageRemove": "Spowoduje to usunięcie dostawcy tożsamości i wszystkich powiązanych konfiguracji. Użytkownicy uwierzytelniający się przez tego dostawcę nie będą mogli się już zalogować.", + "idpMessageConfirm": "Aby potwierdzić, wpisz nazwę dostawcy tożsamości poniżej.", + "idpConfirmDelete": "Potwierdź usunięcie dostawcy tożsamości", + "idpDelete": "Usuń dostawcę tożsamości", + "idp": "Dostawcy tożsamości", + "idpSearch": "Szukaj dostawców tożsamości...", + "idpAdd": "Dodaj dostawcę tożsamości", + "nameMin": "Nazwa musi mieć co najmniej {len} znaków.", + "nameMax": "Nazwa nie może być dłuższa niż {len} znaków.", + "idpClientIdRequired": "Identyfikator klienta jest wymagany.", + "idpClientSecretRequired": "Sekret klienta jest wymagany.", + "idpErrorAuthUrlInvalid": "URL autoryzacji musi być prawidłowym adresem URL.", + "idpErrorTokenUrlInvalid": "URL tokena musi być prawidłowym adresem URL.", + "idpPathRequired": "Ścieżka identyfikatora jest wymagana.", + "idpScopeRequired": "Zakresy są wymagane.", + "idpOidcDescription": "Skonfiguruj dostawcę tożsamości OpenID Connect", + "idpCreatedDescription": "Dostawca tożsamości został pomyślnie utworzony", + "idpCreate": "Utwórz dostawcę tożsamości", + "idpCreateDescription": "Skonfiguruj nowego dostawcę tożsamości do uwierzytelniania użytkowników", + "idpSeeAll": "Zobacz wszystkich dostawców tożsamości", + "idpTitle": "Informacje ogólne", + "idpSettingsDescription": "Skonfiguruj podstawowe informacje dla swojego dostawcy tożsamości", + "idpDisplayName": "Nazwa wyświetlana dla tego dostawcy tożsamości", + "idpAutoProvisionUsers": "Automatyczne tworzenie użytkowników", + "idpAutoProvisionUsersDescription": "Gdy włączone, użytkownicy będą automatycznie tworzeni w systemie przy pierwszym logowaniu z możliwością mapowania użytkowników do ról i organizacji.", + "licenseBadge": "Professional", + "idpType": "Typ dostawcy", + "idpTypeDescription": "Wybierz typ dostawcy tożsamości, który chcesz skonfigurować", + "idpOidcConfigure": "Konfiguracja OAuth2/OIDC", + "idpOidcConfigureDescription": "Skonfiguruj punkty końcowe i poświadczenia dostawcy OAuth2/OIDC", + "idpClientId": "ID klienta", + "idpClientIdDescription": "ID klienta OAuth2 od twojego dostawcy tożsamości", + "idpClientSecret": "Sekret klienta", + "idpClientSecretDescription": "Sekret klienta OAuth2 od twojego dostawcy tożsamości", + "idpAuthUrl": "URL autoryzacji", + "idpAuthUrlDescription": "URL punktu końcowego autoryzacji OAuth2", + "idpTokenUrl": "URL tokena", + "idpTokenUrlDescription": "URL punktu końcowego tokena OAuth2", + "idpOidcConfigureAlert": "Ważna informacja", + "idpOidcConfigureAlertDescription": "Po utworzeniu dostawcy tożsamości, będziesz musiał skonfigurować URL wywołania zwrotnego w ustawieniach swojego dostawcy tożsamości. URL wywołania zwrotnego zostanie podany po pomyślnym utworzeniu.", + "idpToken": "Konfiguracja tokena", + "idpTokenDescription": "Skonfiguruj jak wydobywać informacje o użytkowniku z tokena ID", + "idpJmespathAbout": "O JMESPath", + "idpJmespathAboutDescription": "Poniższe ścieżki używają składni JMESPath do wydobywania wartości z tokena ID.", + "idpJmespathAboutDescriptionLink": "Dowiedz się więcej o JMESPath", + "idpJmespathLabel": "Ścieżka identyfikatora", + "idpJmespathLabelDescription": "JMESPath do identyfikatora użytkownika w tokenie ID", + "idpJmespathEmailPathOptional": "Ścieżka email (Opcjonalnie)", + "idpJmespathEmailPathOptionalDescription": "JMESPath do emaila użytkownika w tokenie ID", + "idpJmespathNamePathOptional": "Ścieżka nazwy (Opcjonalnie)", + "idpJmespathNamePathOptionalDescription": "JMESPath do nazwy użytkownika w tokenie ID", + "idpOidcConfigureScopes": "Zakresy", + "idpOidcConfigureScopesDescription": "Lista zakresów OAuth2 oddzielonych spacjami do żądania", + "idpSubmit": "Utwórz dostawcę tożsamości", + "orgPolicies": "Polityki organizacji", + "idpSettings": "Ustawienia {idpName}", + "idpCreateSettingsDescription": "Skonfiguruj ustawienia dla swojego dostawcy tożsamości", + "roleMapping": "Mapowanie ról", + "orgMapping": "Mapowanie organizacji", + "orgPoliciesSearch": "Szukaj polityk organizacji...", + "orgPoliciesAdd": "Dodaj politykę organizacji", + "orgRequired": "Organizacja jest wymagana", + "error": "Błąd", + "success": "Sukces", + "orgPolicyAddedDescription": "Polityka została pomyślnie dodana", + "orgPolicyUpdatedDescription": "Polityka została pomyślnie zaktualizowana", + "orgPolicyDeletedDescription": "Polityka została pomyślnie usunięta", + "defaultMappingsUpdatedDescription": "Domyślne mapowania zostały pomyślnie zaktualizowane", + "orgPoliciesAbout": "O politykach organizacji", + "orgPoliciesAboutDescription": "Polityki organizacji służą do kontroli dostępu do organizacji na podstawie tokena ID użytkownika. Możesz określić wyrażenia JMESPath do wydobywania informacji o roli i organizacji z tokena ID. Aby dowiedzieć się więcej, zobacz", + "orgPoliciesAboutDescriptionLink": "dokumentację", + "defaultMappingsOptional": "Domyślne mapowania (Opcjonalne)", + "defaultMappingsOptionalDescription": "Domyślne mapowania są używane, gdy nie ma zdefiniowanej polityki organizacji dla organizacji. Możesz tutaj określić domyślne mapowania ról i organizacji.", + "defaultMappingsRole": "Domyślne mapowanie roli", + "defaultMappingsRoleDescription": "JMESPath do wydobycia informacji o roli z tokena ID. Wynik tego wyrażenia musi zwrócić nazwę roli zdefiniowaną w organizacji jako ciąg znaków.", + "defaultMappingsOrg": "Domyślne mapowanie organizacji", + "defaultMappingsOrgDescription": "JMESPath do wydobycia informacji o organizacji z tokena ID. To wyrażenie musi zwrócić ID organizacji lub true, aby użytkownik mógł uzyskać dostęp do organizacji.", + "defaultMappingsSubmit": "Zapisz domyślne mapowania", + "orgPoliciesEdit": "Edytuj politykę organizacji", + "org": "Organizacja", + "orgSelect": "Wybierz organizację", + "orgSearch": "Szukaj organizacji", + "orgNotFound": "Nie znaleziono organizacji.", + "roleMappingPathOptional": "Ścieżka mapowania roli (Opcjonalnie)", + "orgMappingPathOptional": "Ścieżka mapowania organizacji (Opcjonalnie)", + "orgPolicyUpdate": "Aktualizuj politykę", + "orgPolicyAdd": "Dodaj politykę", + "idpUpdatedDescription": "Dostawca tożsamości został pomyślnie zaktualizowany", + "redirectUrl": "URL przekierowania", + "redirectUrlAbout": "O URL przekierowania", + "redirectUrlAboutDescription": "Jest to URL, na który użytkownicy zostaną przekierowani po uwierzytelnieniu. Musisz skonfigurować ten URL w ustawieniach swojego dostawcy tożsamości.", + "key": "Klucz", + "createdAt": "Utworzono" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index e599182b..fa8d8f30 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -5,7 +5,7 @@ "setupCreateResources": "Criar recursos", "setupOrgName": "Nome Da Organização", "orgDisplayName": "Este é o nome de exibição da sua organização.", - "setupOrgId": "ID da organização", + "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", "licenseHost": "Licença do host", "licenseHostDescription": "Gerenciar a chave de licença principal do host.", - "notLicensed": "Não Licenciado", + "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", "licenseSiteUsage": "Uso de Sites", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "A função foi removida com sucesso.", "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", - "sitesNotFound": "Nenhum site encontrado." + "sitesNotFound": "Nenhum site encontrado.", + "expiresAt": "Expira em", + "pangolinServerAdmin": "Administrador do Servidor - Pangolin", + "idpNameInternal": "Interno", + "licenseTierProfessional": "Licença Profissional", + "licenseTierEnterprise": "Licença Empresarial", + "licensed": "Licenciado", + "yes": "Sim", + "no": "Não", + "sitesAdditional": "Sites Adicionais", + "licenseKeys": "Chaves de Licença", + "sitestCountDecrease": "Diminuir contagem de sites", + "sitestCountIncrease": "Aumentar contagem de sites", + "idpManage": "Gerir Provedores de Identidade", + "idpManageDescription": "Visualizar e gerir provedores de identidade no sistema", + "idpDeletedDescription": "Provedor de identidade eliminado com sucesso", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Tem certeza que deseja eliminar permanentemente o provedor de identidade {name}?", + "idpMessageRemove": "Isto irá remover o provedor de identidade e todas as configurações associadas. Os utilizadores que se autenticam através deste provedor não poderão mais fazer login.", + "idpMessageConfirm": "Para confirmar, por favor digite o nome do provedor de identidade abaixo.", + "idpConfirmDelete": "Confirmar Eliminação do Provedor de Identidade", + "idpDelete": "Eliminar Provedor de Identidade", + "idp": "Provedores de Identidade", + "idpSearch": "Pesquisar provedores de identidade...", + "idpAdd": "Adicionar Provedor de Identidade", + "nameMin": "O nome deve ter pelo menos {len} caracteres.", + "nameMax": "O nome não deve ter mais de {len} caracteres.", + "idpClientIdRequired": "O ID do Cliente é obrigatório.", + "idpClientSecretRequired": "O Segredo do Cliente é obrigatório.", + "idpErrorAuthUrlInvalid": "O URL de Autenticação deve ser um URL válido.", + "idpErrorTokenUrlInvalid": "O URL do Token deve ser um URL válido.", + "idpPathRequired": "O Caminho do Identificador é obrigatório.", + "idpScopeRequired": "Os Escopos são obrigatórios.", + "idpOidcDescription": "Configurar um provedor de identidade OpenID Connect", + "idpCreatedDescription": "Provedor de identidade criado com sucesso", + "idpCreate": "Criar Provedor de Identidade", + "idpCreateDescription": "Configurar um novo provedor de identidade para autenticação de utilizadores", + "idpSeeAll": "Ver Todos os Provedores de Identidade", + "idpTitle": "Informações Gerais", + "idpSettingsDescription": "Configurar as informações básicas para o seu provedor de identidade", + "idpDisplayName": "Um nome de exibição para este provedor de identidade", + "idpAutoProvisionUsers": "Provisionamento Automático de Utilizadores", + "idpAutoProvisionUsersDescription": "Quando ativado, os utilizadores serão criados automaticamente no sistema no primeiro login com a capacidade de mapear utilizadores para funções e organizações.", + "licenseBadge": "Profissional", + "idpType": "Tipo de Provedor", + "idpTypeDescription": "Selecione o tipo de provedor de identidade que deseja configurar", + "idpOidcConfigure": "Configuração OAuth2/OIDC", + "idpOidcConfigureDescription": "Configurar os endpoints e credenciais do provedor OAuth2/OIDC", + "idpClientId": "ID do Cliente", + "idpClientIdDescription": "O ID do cliente OAuth2 do seu provedor de identidade", + "idpClientSecret": "Segredo do Cliente", + "idpClientSecretDescription": "O segredo do cliente OAuth2 do seu provedor de identidade", + "idpAuthUrl": "URL de Autorização", + "idpAuthUrlDescription": "O URL do endpoint de autorização OAuth2", + "idpTokenUrl": "URL do Token", + "idpTokenUrlDescription": "O URL do endpoint do token OAuth2", + "idpOidcConfigureAlert": "Informação Importante", + "idpOidcConfigureAlertDescription": "Após criar o provedor de identidade, será necessário configurar o URL de retorno nas configurações do seu provedor de identidade. O URL de retorno será fornecido após a criação bem-sucedida.", + "idpToken": "Configuração do Token", + "idpTokenDescription": "Configurar como extrair informações do utilizador do token ID", + "idpJmespathAbout": "Sobre JMESPath", + "idpJmespathAboutDescription": "Os caminhos abaixo usam a sintaxe JMESPath para extrair valores do token ID.", + "idpJmespathAboutDescriptionLink": "Saiba mais sobre JMESPath", + "idpJmespathLabel": "Caminho do Identificador", + "idpJmespathLabelDescription": "O JMESPath para o identificador do utilizador no token ID", + "idpJmespathEmailPathOptional": "Caminho do Email (Opcional)", + "idpJmespathEmailPathOptionalDescription": "O JMESPath para o email do utilizador no token ID", + "idpJmespathNamePathOptional": "Caminho do Nome (Opcional)", + "idpJmespathNamePathOptionalDescription": "O JMESPath para o nome do utilizador no token ID", + "idpOidcConfigureScopes": "Escopos", + "idpOidcConfigureScopesDescription": "Lista de escopos OAuth2 separados por espaço para solicitar", + "idpSubmit": "Criar Provedor de Identidade", + "orgPolicies": "Políticas da Organização", + "idpSettings": "Configurações de {idpName}", + "idpCreateSettingsDescription": "Configurar as definições para o seu provedor de identidade", + "roleMapping": "Mapeamento de Funções", + "orgMapping": "Mapeamento da Organização", + "orgPoliciesSearch": "Pesquisar políticas da organização...", + "orgPoliciesAdd": "Adicionar Política da Organização", + "orgRequired": "A organização é obrigatória", + "error": "Erro", + "success": "Sucesso", + "orgPolicyAddedDescription": "Política adicionada com sucesso", + "orgPolicyUpdatedDescription": "Política atualizada com sucesso", + "orgPolicyDeletedDescription": "Política eliminada com sucesso", + "defaultMappingsUpdatedDescription": "Mapeamentos padrão atualizados com sucesso", + "orgPoliciesAbout": "Sobre Políticas da Organização", + "orgPoliciesAboutDescription": "As políticas da organização são usadas para controlar o acesso às organizações com base no token ID do utilizador. Pode especificar expressões JMESPath para extrair informações de função e organização do token ID. Para mais informações, consulte", + "orgPoliciesAboutDescriptionLink": "a documentação", + "defaultMappingsOptional": "Mapeamentos Padrão (Opcional)", + "defaultMappingsOptionalDescription": "Os mapeamentos padrão são usados quando não há uma política de organização definida para uma organização. Pode especificar aqui os mapeamentos padrão de função e organização para recorrer.", + "defaultMappingsRole": "Mapeamento de Função Padrão", + "defaultMappingsRoleDescription": "JMESPath para extrair informações de função do token ID. O resultado desta expressão deve retornar o nome da função como definido na organização como uma string.", + "defaultMappingsOrg": "Mapeamento de Organização Padrão", + "defaultMappingsOrgDescription": "JMESPath para extrair informações da organização do token ID. Esta expressão deve retornar o ID da organização ou verdadeiro para que o utilizador tenha permissão para aceder à organização.", + "defaultMappingsSubmit": "Guardar Mapeamentos Padrão", + "orgPoliciesEdit": "Editar Política da Organização", + "org": "Organização", + "orgSelect": "Selecionar organização", + "orgSearch": "Pesquisar organização", + "orgNotFound": "Nenhuma organização encontrada.", + "roleMappingPathOptional": "Caminho de Mapeamento de Função (Opcional)", + "orgMappingPathOptional": "Caminho de Mapeamento da Organização (Opcional)", + "orgPolicyUpdate": "Atualizar Política", + "orgPolicyAdd": "Adicionar Política", + "idpUpdatedDescription": "Provedor de identidade atualizado com sucesso", + "redirectUrl": "URL de Redirecionamento", + "redirectUrlAbout": "Sobre o URL de Redirecionamento", + "redirectUrlAboutDescription": "Este é o URL para o qual os utilizadores serão redirecionados após a autenticação. Precisa configurar este URL nas configurações do seu provedor de identidade.", + "key": "Chave", + "createdAt": "Criado Em" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index aab356d1..d21a8e4c 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -5,7 +5,7 @@ "setupCreateResources": "Create Resources", "setupOrgName": "Organization Name", "orgDisplayName": "This is the display name of your organization.", - "setupOrgId": "Organization ID", + "orgId": "Organization ID", "setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.", "setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.", "componentsErrorNoMemberCreate": "You are not currently a member of any organizations. Create an organization to get started.", @@ -334,7 +334,7 @@ "licenseTitleDescription": "View and manage license keys in the system", "licenseHost": "Host License", "licenseHostDescription": "Manage the main license key for the host.", - "notLicensed": "Not Licensed", + "licensedNot": "Not Licensed", "hostId": "Host ID", "licenseReckeckAll": "Recheck All Keys", "licenseSiteUsage": "Sites Usage", @@ -692,5 +692,115 @@ "accessRoleRemovedDescription": "The role has been successfully removed.", "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", "manage": "Manage", - "sitesNotFound": "No sites found." + "sitesNotFound": "No sites found.", + "expiresAt": "Expires At", + "pangolinServerAdmin": "Server Admin - Pangolin", + "idpNameInternal": "Internal", + "licenseTierProfessional": "Professional License", + "licenseTierEnterprise": "Enterprise License", + "licensed": "Licensed", + "yes": "Yes", + "no": "No", + "sitesAdditional": "Additional Sites", + "licenseKeys": "License Keys", + "sitestCountDecrease": "Decrease site count", + "sitestCountIncrease": "Increase site count", + "idpManage": "Manage Identity Providers", + "idpManageDescription": "View and manage identity providers in the system", + "idpDeletedDescription": "Identity provider deleted successfully", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", + "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", + "idpConfirmDelete": "Confirm Delete Identity Provider", + "idpDelete": "Delete Identity Provider", + "idp": "Identity Providers", + "idpSearch": "Search identity providers...", + "idpAdd": "Add Identity Provider", + "nameMin": "Name must be at least {len} characters.", + "nameMax": "Name must not be longer than {len} characters.", + "idpClientIdRequired": "Client ID is required.", + "idpClientSecretRequired": "Client Secret is required.", + "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", + "idpErrorTokenUrlInvalid": "Token URL must be a valid URL.", + "idpPathRequired": "Identifier Path is required.", + "idpScopeRequired": "Scopes are required.", + "idpOidcDescription": "Configure an OpenID Connect identity provider", + "idpCreatedDescription": "Identity provider created successfully", + "idpCreate": "Create Identity Provider", + "idpCreateDescription": "Configure a new identity provider for user authentication", + "idpSeeAll": "See All Identity Providers", + "idpTitle": "General Information", + "idpSettingsDescription": "Configure the basic information for your identity provider", + "idpDisplayName": "A display name for this identity provider", + "idpAutoProvisionUsers": "Auto Provision Users", + "idpAutoProvisionUsersDescription": "When enabled, users will be automatically created in the system upon first login with the ability to map users to roles and organizations.", + "licenseBadge": "Professional", + "idpType": "Provider Type", + "idpTypeDescription": "Select the type of identity provider you want to configure", + "idpOidcConfigure": "OAuth2/OIDC Configuration", + "idpOidcConfigureDescription": "Configure the OAuth2/OIDC provider endpoints and credentials", + "idpClientId": "Client ID", + "idpClientIdDescription": "The OAuth2 client ID from your identity provider", + "idpClientSecret": "Client Secret", + "idpClientSecretDescription": "The OAuth2 client secret from your identity provider", + "idpAuthUrl": "Authorization URL", + "idpAuthUrlDescription": "The OAuth2 authorization endpoint URL", + "idpTokenUrl": "Token URL", + "idpTokenUrlDescription": "The OAuth2 token endpoint URL", + "idpOidcConfigureAlert": "Important Information", + "idpOidcConfigureAlertDescription": "After creating the identity provider, you will need to configure the callback URL in your identity provider's settings. The callback URL will be provided after successful creation.", + "idpToken": "Token Configuration", + "idpTokenDescription": "Configure how to extract user information from the ID token", + "idpJmespathAbout": "About JMESPath", + "idpJmespathAboutDescription": "The paths below use JMESPath syntax to extract values from the ID token.", + "idpJmespathAboutDescriptionLink": "Learn more about JMESPath", + "idpJmespathLabel": "Identifier Path", + "idpJmespathLabelDescription": "The JMESPath to the user identifier in the ID token", + "idpJmespathEmailPathOptional": "Email Path (Optional)", + "idpJmespathEmailPathOptionalDescription": "The JMESPath to the user's email in the ID token", + "idpJmespathNamePathOptional": "Name Path (Optional)", + "idpJmespathNamePathOptionalDescription": "The JMESPath to the user's name in the ID token", + "idpOidcConfigureScopes": "Scopes", + "idpOidcConfigureScopesDescription": "Space-separated list of OAuth2 scopes to request", + "idpSubmit": "Create Identity Provider", + "orgPolicies": "Organization Policies", + "idpSettings": "{idpName} Settings", + "idpCreateSettingsDescription": "Configure the settings for your identity provider", + "roleMapping": "Role Mapping", + "orgMapping": "Organization Mapping", + "orgPoliciesSearch": "Search organization policies...", + "orgPoliciesAdd": "Add Organization Policy", + "orgRequired": "Organization is required", + "error": "Error", + "success": "Success", + "orgPolicyAddedDescription": "Policy added successfully", + "orgPolicyUpdatedDescription": "Policy updated successfully", + "orgPolicyDeletedDescription": "Policy deleted successfully", + "defaultMappingsUpdatedDescription": "Default mappings updated successfully", + "orgPoliciesAbout": "About Organization Policies", + "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token. For more information, see", + "orgPoliciesAboutDescriptionLink": "the documentation", + "defaultMappingsOptional": "Default Mappings (Optional)", + "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", + "defaultMappingsRole": "Default Role Mapping", + "defaultMappingsRoleDescription": "JMESPath to extract role information from the ID token. The result of this expression must return the role name as defined in the organization as a string.", + "defaultMappingsOrg": "Default Organization Mapping", + "defaultMappingsOrgDescription": "JMESPath to extract organization information from the ID token. This expression must return the org ID or true for the user to be allowed to access the organization.", + "defaultMappingsSubmit": "Save Default Mappings", + "orgPoliciesEdit": "Edit Organization Policy", + "org": "Organization", + "orgSelect": "Select organization", + "orgSearch": "Search org", + "orgNotFound": "No org found.", + "roleMappingPathOptional": "Role Mapping Path (Optional)", + "orgMappingPathOptional": "Organization Mapping Path (Optional)", + "orgPolicyUpdate": "Update Policy", + "orgPolicyAdd": "Add Policy", + "idpUpdatedDescription": "Identity provider updated successfully", + "redirectUrl": "Redirect URL", + "redirectUrlAbout": "About Redirect URL", + "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", + "key": "Key", + "createdAt": "Created At" } diff --git a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx index 59c1b1b4..f151da9d 100644 --- a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx +++ b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx @@ -123,7 +123,7 @@ export default function RegenerateInvitationForm({ onRegenerate({ id: invitation.id, email: invitation.email, - expiresAt: res.data.data.expiresAt, + expiresAt: res.data.data.expiresAt ?? "", role: invitation.role, roleId: invitation.roleId }); diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 7e96593d..a7236d0b 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -172,8 +172,8 @@ export default function CreateSiteForm({ if (!keypair || !siteDefaults) { toast({ variant: "destructive", - title: "Error creating site", - description: "Key pair or site defaults not found" + title: t('siteErrorCreate'), + description: t('siteErrorCreateKeyPair') }); setLoading?.(false); setIsLoading(false); @@ -191,8 +191,8 @@ export default function CreateSiteForm({ if (!siteDefaults) { toast({ variant: "destructive", - title: "Error creating site", - description: "Site defaults not found" + title: t('siteErrorCreate'), + description: t('siteErrorCreateDefaults') }); setLoading?.(false); setIsLoading(false); @@ -215,7 +215,7 @@ export default function CreateSiteForm({ .catch((e) => { toast({ variant: "destructive", - title: "Error creating site", + title: t('siteErrorCreate'), description: formatAxiosError(e) }); }); diff --git a/src/app/admin/api-keys/ApiKeysTable.tsx b/src/app/admin/api-keys/ApiKeysTable.tsx index 5901bade..517505ef 100644 --- a/src/app/admin/api-keys/ApiKeysTable.tsx +++ b/src/app/admin/api-keys/ApiKeysTable.tsx @@ -74,7 +74,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { @@ -117,7 +117,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { }, { accessorKey: "key", - header: "Key", + header: t('key'), cell: ({ row }) => { const r = row.original; return {r.key}; @@ -125,7 +125,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { }, { accessorKey: "createdAt", - header: "Created At", + header: t('createdAt'), cell: ({ row }) => { const r = row.original; return {moment(r.createdAt).format("lll")} ; diff --git a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx index 91ac8de1..f3912e64 100644 --- a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx @@ -79,18 +79,18 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysPermissionsErrorUpdate'), e); + console.error("Error setting permissions", e); toast({ variant: "destructive", - title: t('apiKeysPermissionsErrorUpdate'), + title: "Error setting permissions", description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: t('apiKeysPermissionsUpdated'), - description: t('apiKeysPermissionsUpdatedDescription') + title: "Permissions updated", + description: "The permissions have been updated." }); } diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index 2bdf27f0..4e7fa064 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -56,16 +56,14 @@ import CopyTextBox from "@app/components/CopyTextBox"; import PermissionsSelectBox from "@app/components/PermissionsSelectBox"; import { useTranslations } from "next-intl"; -const t = useTranslations(); - const createFormSchema = z.object({ name: z .string() .min(2, { - message: t('nameMin', {len: 2}) + message: "Name must be at least 2 characters." }) .max(255, { - message: t('nameMax', {len: 255}) + message: "Name must not be longer than 255 characters." }) }); @@ -80,7 +78,7 @@ const copiedFormSchema = z return data.copied; }, { - message: t('apiKeysConfirmCopy2'), + message: "You must confirm that you have copied the API key.", path: ["copied"] } ); @@ -113,6 +111,8 @@ export default function Page() { } }); + const t = useTranslations(); + async function onSubmit(data: CreateFormValues) { setCreateLoading(true); @@ -125,7 +125,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: t('apiKeysErrorCreate'), + title: "Error creating API key", description: formatAxiosError(e) }); }); @@ -146,10 +146,10 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysErrorSetPermission'), e); + console.error("Error setting permissions", e); toast({ variant: "destructive", - title: t('apiKeysErrorSetPermission'), + title: "Error setting permissions", description: formatAxiosError(e) }); }); @@ -191,7 +191,7 @@ export default function Page() { router.push(`/admin/api-keys`); }} > - See All API Keys + {t('apiKeysSeeAll')}
diff --git a/src/app/admin/idp/AdminIdpDataTable.tsx b/src/app/admin/idp/AdminIdpDataTable.tsx index 8d64ce0b..f01b3c03 100644 --- a/src/app/admin/idp/AdminIdpDataTable.tsx +++ b/src/app/admin/idp/AdminIdpDataTable.tsx @@ -3,6 +3,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; @@ -14,15 +15,16 @@ export function IdpDataTable({ data }: DataTableProps) { const router = useRouter(); + const t = useTranslations(); return ( { router.push("/admin/idp/create"); }} diff --git a/src/app/admin/idp/AdminIdpTable.tsx b/src/app/admin/idp/AdminIdpTable.tsx index b2415280..efb9fc35 100644 --- a/src/app/admin/idp/AdminIdpTable.tsx +++ b/src/app/admin/idp/AdminIdpTable.tsx @@ -19,6 +19,7 @@ import { DropdownMenuTrigger } from "@app/components/ui/dropdown-menu"; import Link from "next/link"; +import { useTranslations } from "next-intl"; export type IdpRow = { idpId: number; @@ -36,19 +37,20 @@ export default function IdpTable({ idps }: Props) { const [selectedIdp, setSelectedIdp] = useState(null); const api = createApiClient(useEnvContext()); const router = useRouter(); + const t = useTranslations(); const deleteIdp = async (idpId: number) => { try { await api.delete(`/idp/${idpId}`); toast({ - title: "Success", - description: "Identity provider deleted successfully" + title: t('success'), + description: t('idpDeletedDescription') }); setIsDeleteModalOpen(false); router.refresh(); } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -58,7 +60,7 @@ export default function IdpTable({ idps }: Props) { const getTypeDisplay = (type: string) => { switch (type) { case "oidc": - return "OAuth2/OIDC"; + return t('idpOidc'); default: return type; } @@ -74,7 +76,7 @@ export default function IdpTable({ idps }: Props) { @@ -84,7 +86,7 @@ export default function IdpTable({ idps }: Props) { href={`/admin/idp/${r.idpId}/general`} > - View settings + {t('viewSettings')} - Delete + {t('delete')} @@ -126,7 +128,7 @@ export default function IdpTable({ idps }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Name + {t('name')} ); @@ -142,7 +144,7 @@ export default function IdpTable({ idps }: Props) { column.toggleSorting(column.getIsSorted() === "asc") } > - Type + {t('type')} ); @@ -162,7 +164,7 @@ export default function IdpTable({ idps }: Props) {
@@ -184,27 +186,22 @@ export default function IdpTable({ idps }: Props) { dialog={

- Are you sure you want to permanently delete the - identity provider {selectedIdp.name}? + {t('idpQuestionRemove', {name: selectedIdp.name})}

- This will remove the identity provider and - all associated configurations. Users who - authenticate through this provider will no - longer be able to log in. + {t('idpMessageRemove')}

- To confirm, please type the name of the identity - provider below. + {t('idpMessageConfirm')}

} - buttonText="Confirm Delete Identity Provider" + buttonText={t('idpConfirmDelete')} onConfirm={async () => deleteIdp(selectedIdp.idpId)} string={selectedIdp.name} - title="Delete Identity Provider" + title={t('idpDelete')} /> )} diff --git a/src/app/admin/idp/[idpId]/general/page.tsx b/src/app/admin/idp/[idpId]/general/page.tsx index bca4ac7a..75dad7f8 100644 --- a/src/app/admin/idp/[idpId]/general/page.tsx +++ b/src/app/admin/idp/[idpId]/general/page.tsx @@ -45,10 +45,8 @@ import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; -const t = useTranslations(); - const GeneralFormSchema = z.object({ - name: z.string().min(2, { message: t('nameMin', {len: 2}) }), + name: z.string().min(2, "Name must be at least 2 characters."), clientId: z.string().min(1, { message: "Client ID is required." }), clientSecret: z.string().min(1, { message: "Client Secret is required." }), authUrl: z.string().url({ message: "Auth URL must be a valid URL." }), @@ -91,6 +89,8 @@ export default function GeneralPage() { } }); + const t = useTranslations(); + useEffect(() => { const loadIdp = async () => { try { @@ -112,7 +112,7 @@ export default function GeneralPage() { } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -172,18 +172,17 @@ export default function GeneralPage() { - General Information + {t('idpTitle')} - Configure the basic information for your identity - provider + {t('idpSettingsDescription')} - Redirect URL + {t('redirectUrl')} @@ -194,13 +193,10 @@ export default function GeneralPage() { - About Redirect URL + {t('redirectUrlAbout')} - This is the URL to which users will be - redirected after authentication. You need to - configure this URL in your identity provider - settings. + {t('redirectUrlAboutDescription')} @@ -215,13 +211,12 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - Name + {t('name')} - A display name for this - identity provider + {t('idpDisplayName')} @@ -231,7 +226,7 @@ export default function GeneralPage() {
- When enabled, users will be - automatically created in the system upon - first login with the ability to map - users to roles and organizations. + {t('idpAutoProvisionUsersDescription')} @@ -259,11 +251,10 @@ export default function GeneralPage() { - OAuth2/OIDC Configuration + {t('idpOidcConfigure')} - Configure the OAuth2/OIDC provider endpoints and - credentials + {t('idpOidcConfigureDescription')} @@ -280,15 +271,13 @@ export default function GeneralPage() { render={({ field }) => ( - Client ID + {t('idpClientId')} - The OAuth2 client ID - from your identity - provider + {t('idpClientIdDescription')} @@ -301,7 +290,7 @@ export default function GeneralPage() { render={({ field }) => ( - Client Secret + {t('idpClientSecret')} - The OAuth2 client secret - from your identity - provider + {t('idpClientSecretDescription')} @@ -325,14 +312,13 @@ export default function GeneralPage() { render={({ field }) => ( - Authorization URL + {t('idpAuthUrl')} - The OAuth2 authorization - endpoint URL + {t('idpAuthUrlDescription')} @@ -345,14 +331,13 @@ export default function GeneralPage() { render={({ field }) => ( - Token URL + {t('idpTokenUrl')} - The OAuth2 token - endpoint URL + {t('idpTokenUrlDescription')} @@ -367,11 +352,10 @@ export default function GeneralPage() { - Token Configuration + {t('idpToken')} - Configure how to extract user information from - the ID token + {t('idpTokenDescription')} @@ -385,19 +369,18 @@ export default function GeneralPage() { - About JMESPath + {t('idpJmespathAbout')} - The paths below use JMESPath - syntax to extract values from - the ID token. + {/*TODO(vlalx): Validate replacing */} + {t('idpJmespathAboutDescription')} - Learn more about JMESPath{" "} + {t('idpJmespathAboutDescriptionLink')}{" "} @@ -409,15 +392,13 @@ export default function GeneralPage() { render={({ field }) => ( - Identifier Path + {t('idpJmespathLabel')} - The JMESPath to the user - identifier in the ID - token + {t('idpJmespathLabelDescription')} @@ -430,15 +411,13 @@ export default function GeneralPage() { render={({ field }) => ( - Email Path (Optional) + {t('idpJmespathEmailPathOptional')} - The JMESPath to the - user's email in the ID - token + {t('idpJmespathEmailPathOptionalDescription')} @@ -451,15 +430,13 @@ export default function GeneralPage() { render={({ field }) => ( - Name Path (Optional) + {t('idpJmespathNamePathOptional')} - The JMESPath to the - user's name in the ID - token + {t('idpJmespathNamePathOptionalDescription')} @@ -472,14 +449,13 @@ export default function GeneralPage() { render={({ field }) => ( - Scopes + {t('idpOidcConfigureScopes')} - Space-separated list of - OAuth2 scopes to request + {t('idpOidcConfigureScopesDescription')} @@ -500,7 +476,7 @@ export default function GeneralPage() { loading={loading} disabled={loading} > - Save General Settings + {t('saveGeneralSettings')}
diff --git a/src/app/admin/idp/[idpId]/layout.tsx b/src/app/admin/idp/[idpId]/layout.tsx index 9913c8d6..06082a80 100644 --- a/src/app/admin/idp/[idpId]/layout.tsx +++ b/src/app/admin/idp/[idpId]/layout.tsx @@ -15,6 +15,7 @@ import { BreadcrumbPage, BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; +import { useTranslations } from "next-intl"; interface SettingsLayoutProps { children: React.ReactNode; @@ -36,13 +37,15 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { redirect("/admin/idp"); } + const t = useTranslations(); + const navItems: HorizontalTabs = [ { - title: "General", + title: t('general'), href: `/admin/idp/${params.idpId}/general` }, { - title: "Organization Policies", + title: t('orgPolicies'), href: `/admin/idp/${params.idpId}/policies` } ]; @@ -50,8 +53,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <>
diff --git a/src/app/admin/idp/[idpId]/policies/PolicyDataTable.tsx b/src/app/admin/idp/[idpId]/policies/PolicyDataTable.tsx index 2873b80a..aeef110a 100644 --- a/src/app/admin/idp/[idpId]/policies/PolicyDataTable.tsx +++ b/src/app/admin/idp/[idpId]/policies/PolicyDataTable.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; @@ -14,14 +15,15 @@ export function PolicyDataTable({ data, onAdd }: DataTableProps) { + const t = useTranslations(); return ( ); diff --git a/src/app/admin/idp/[idpId]/policies/PolicyTable.tsx b/src/app/admin/idp/[idpId]/policies/PolicyTable.tsx index 09ba309f..9c11f9b9 100644 --- a/src/app/admin/idp/[idpId]/policies/PolicyTable.tsx +++ b/src/app/admin/idp/[idpId]/policies/PolicyTable.tsx @@ -19,6 +19,7 @@ import { } from "@app/components/ui/dropdown-menu"; import Link from "next/link"; import { InfoPopup } from "@app/components/ui/info-popup"; +import { useTranslations } from "next-intl"; export interface PolicyRow { orgId: string; @@ -34,6 +35,7 @@ interface Props { } export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props) { + const t = useTranslations(); const columns: ColumnDef[] = [ { id: "dots", @@ -44,7 +46,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props @@ -54,7 +56,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props onDelete(r.orgId); }} > - Delete + {t('delete')} @@ -71,7 +73,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props column.toggleSorting(column.getIsSorted() === "asc") } > - Organization ID + {t('orgId')} ); @@ -87,7 +89,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props column.toggleSorting(column.getIsSorted() === "asc") } > - Role Mapping + {t('roleMapping')} ); @@ -114,7 +116,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props column.toggleSorting(column.getIsSorted() === "asc") } > - Organization Mapping + {t('orgMapping')} ); @@ -142,7 +144,7 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props className="ml-2" onClick={() => onEdit(policy)} > - Edit + {t('edit')}
); diff --git a/src/app/admin/idp/[idpId]/policies/page.tsx b/src/app/admin/idp/[idpId]/policies/page.tsx index ba108064..79ed17b4 100644 --- a/src/app/admin/idp/[idpId]/policies/page.tsx +++ b/src/app/admin/idp/[idpId]/policies/page.tsx @@ -63,6 +63,7 @@ import { SettingsSectionFooter, SettingsSectionForm } from "@app/components/Settings"; +import { useTranslations } from "next-intl"; type Organization = { orgId: string; @@ -117,6 +118,8 @@ export default function PoliciesPage() { } }); + const t = useTranslations(); + const loadIdp = async () => { try { const res = await api.get>( @@ -131,7 +134,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -146,7 +149,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -165,7 +168,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -200,15 +203,15 @@ export default function PoliciesPage() { }; setPolicies([...policies, newPolicy]); toast({ - title: "Success", - description: "Policy added successfully" + title: t('success'), + description: t('orgPolicyAddedDescription') }); setShowAddDialog(false); form.reset(); } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -242,8 +245,8 @@ export default function PoliciesPage() { ) ); toast({ - title: "Success", - description: "Policy updated successfully" + title: t('success'), + description: t('orgPolicyUpdatedDescription') }); setShowAddDialog(false); setEditingPolicy(null); @@ -251,7 +254,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -269,13 +272,13 @@ export default function PoliciesPage() { policies.filter((policy) => policy.orgId !== orgId) ); toast({ - title: "Success", - description: "Policy deleted successfully" + title: t('success'), + description: t('orgPolicyDeletedDescription') }); } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -293,13 +296,13 @@ export default function PoliciesPage() { }); if (res.status === 200) { toast({ - title: "Success", - description: "Default mappings updated successfully" + title: t('success'), + description: t('defaultMappingsUpdatedDescription') }); } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -318,21 +321,18 @@ export default function PoliciesPage() { - About Organization Policies + {t('orgPoliciesAbout')} - Organization policies are used to control access to - organizations based on the user's ID token. You can - specify JMESPath expressions to extract role and - organization information from the ID token. For more - information, see{" "} + {/*TODO(vlalx): Validate replacing */} + {t('orgPoliciesAboutDescription')}{" "} - the documentation + {t('orgPoliciesAboutDescriptionLink')} @@ -341,13 +341,10 @@ export default function PoliciesPage() { - Default Mappings (Optional) + {t('defaultMappingsOptional')} - The default mappings are used when when there is not - an organization policy defined for an organization. - You can specify the default role and organization - mappings to fall back to here. + {t('defaultMappingsOptionalDescription')} @@ -366,16 +363,13 @@ export default function PoliciesPage() { render={({ field }) => ( - Default Role Mapping + {t('defaultMappingsRole')} - The result of this - expression must return the - role name as defined in the - organization as a string. + {t('defaultMappingsRoleDescription')} @@ -388,16 +382,13 @@ export default function PoliciesPage() { render={({ field }) => ( - Default Organization Mapping + {t('defaultMappingsOrg')} - This expression must return - thr org ID or true for the - user to be allowed to access - the organization. + {t('defaultMappingsOrgDescription')} @@ -412,7 +403,7 @@ export default function PoliciesPage() { form="policy-default-mappings-form" loading={updateDefaultMappingsLoading} > - Save Default Mappings + {t('defaultMappingsSubmit')} @@ -455,8 +446,8 @@ export default function PoliciesPage() { {editingPolicy - ? "Edit Organization Policy" - : "Add Organization Policy"} + ? t('orgPoliciesEdit') + : t('orgPoliciesAdd')} Configure access for an organization @@ -476,7 +467,7 @@ export default function PoliciesPage() { name="orgId" render={({ field }) => ( - Organization + {t('org')} {editingPolicy ? ( ) : ( @@ -500,18 +491,17 @@ export default function PoliciesPage() { org.orgId === field.value )?.name - : "Select organization"} + : t('orgSelect')} - + - No org - found. + {t('orgNotFound')} {organizations.map( @@ -562,16 +552,13 @@ export default function PoliciesPage() { render={({ field }) => ( - Role Mapping Path (Optional) + {t('roleMappingPathOptional')} - The result of this expression - must return the role name as - defined in the organization as a - string. + {t('defaultMappingsRoleDescription')} @@ -584,17 +571,13 @@ export default function PoliciesPage() { render={({ field }) => ( - Organization Mapping Path - (Optional) + {t('orgMappingPathOptional')} - This expression must return the - org ID or true for the user to - be allowed to access the - organization. + {t('defaultMappingsOrgDescription')} @@ -605,7 +588,7 @@ export default function PoliciesPage() { - + diff --git a/src/app/admin/idp/create/page.tsx b/src/app/admin/idp/create/page.tsx index 63883554..f7f6350e 100644 --- a/src/app/admin/idp/create/page.tsx +++ b/src/app/admin/idp/create/page.tsx @@ -39,10 +39,8 @@ import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; -const t = useTranslations(); - const createIdpFormSchema = z.object({ - name: z.string().min(2, { message: t('nameMin', {len: 2}) }), + name: z.string().min(2, "Name must be at least 2 characters."), type: z.enum(["oidc"]), clientId: z.string().min(1, { message: "Client ID is required." }), clientSecret: z.string().min(1, { message: "Client Secret is required." }), @@ -97,6 +95,8 @@ export default function Page() { } }); + const t = useTranslations(); + async function onSubmit(data: CreateIdpFormValues) { setCreateLoading(true); @@ -118,14 +118,14 @@ export default function Page() { if (res.status === 201) { toast({ - title: "Success", - description: "Identity provider created successfully" + title: t('success'), + description: t('idpCreatedDescription') }); router.push(`/admin/idp/${res.data.data.idpId}`); } } catch (e) { toast({ - title: "Error", + title: t('error'), description: formatAxiosError(e), variant: "destructive" }); @@ -138,8 +138,8 @@ export default function Page() { <>
@@ -155,11 +155,10 @@ export default function Page() { - General Information + {t('idpTitle')} - Configure the basic information for your identity - provider + {t('idpCreateSettingsDescription')} @@ -175,13 +174,12 @@ export default function Page() { name="name" render={({ field }) => ( - Name + {t('name')} - A display name for this - identity provider + {t('idpDisplayName')} @@ -191,7 +189,7 @@ export default function Page() {
- When enabled, users will be - automatically created in the system upon - first login with the ability to map - users to roles and organizations. + {t('idpAutoProvisionUsersDescription')} @@ -218,11 +213,10 @@ export default function Page() { - Provider Type + {t('idpType')} - Select the type of identity provider you want to - configure + {t('idpTypeDescription')} @@ -242,11 +236,10 @@ export default function Page() { - OAuth2/OIDC Configuration + {t('idpOidcConfigure')} - Configure the OAuth2/OIDC provider endpoints - and credentials + {t('idpOidcConfigureDescription')} @@ -262,15 +255,13 @@ export default function Page() { render={({ field }) => ( - Client ID + {t('idpClientId')} - The OAuth2 client ID - from your identity - provider + {t('idpClientIdDescription')} @@ -283,7 +274,7 @@ export default function Page() { render={({ field }) => ( - Client Secret + {t('idpClientSecret')} - The OAuth2 client secret - from your identity - provider + {t('idpClientSecretDescription')} @@ -307,7 +296,7 @@ export default function Page() { render={({ field }) => ( - Authorization URL + {t('idpAuthUrl')} - The OAuth2 authorization - endpoint URL + {t('idpAuthUrlDescription')} @@ -330,7 +318,7 @@ export default function Page() { render={({ field }) => ( - Token URL + {t('idpTokenUrl')} - The OAuth2 token - endpoint URL + {t('idpTokenUrlDescription')} @@ -352,14 +339,10 @@ export default function Page() { - Important Information + {t('idpOidcConfigureAlert')} - After creating the identity provider, - you will need to configure the callback - URL in your identity provider's - settings. The callback URL will be - provided after successful creation. + {t('idpOidcConfigureAlertDescription')} @@ -368,11 +351,10 @@ export default function Page() { - Token Configuration + {t('idpToken')} - Configure how to extract user information - from the ID token + {t('idpTokenDescription')} @@ -385,19 +367,18 @@ export default function Page() { - About JMESPath + {t('idpJmespathAbout')} - The paths below use JMESPath - syntax to extract values from - the ID token. + {/*TODO(vlalx): Validate replacing */} + {t('idpJmespathAboutDescription')} - Learn more about JMESPath{" "} + {t('idpJmespathAboutDescriptionLink')}{" "} @@ -409,15 +390,13 @@ export default function Page() { render={({ field }) => ( - Identifier Path + {t('idpJmespathLabel')} - The path to the user - identifier in the ID - token + {t('idpJmespathLabelDescription')} @@ -430,15 +409,13 @@ export default function Page() { render={({ field }) => ( - Email Path (Optional) + {t('idpJmespathEmailPathOptional')} - The path to the - user's email in the ID - token + {t('idpJmespathEmailPathOptionalDescription')} @@ -451,15 +428,13 @@ export default function Page() { render={({ field }) => ( - Name Path (Optional) + {t('idpJmespathNamePathOptional')} - The path to the - user's name in the ID - token + {t('idpJmespathNamePathOptionalDescription')} @@ -472,14 +447,14 @@ export default function Page() { render={({ field }) => ( - Scopes + "idpOidcConfigureScopes": "Scopes" + {t('')} - Space-separated list of - OAuth2 scopes to request + {t('idpOidcConfigureScopesDescription')} @@ -501,7 +476,7 @@ export default function Page() { router.push("/admin/idp"); }} > - Cancel + {t('cancel')}
diff --git a/src/app/admin/idp/page.tsx b/src/app/admin/idp/page.tsx index 54657c2d..80bb6146 100644 --- a/src/app/admin/idp/page.tsx +++ b/src/app/admin/idp/page.tsx @@ -3,6 +3,7 @@ import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import IdpTable, { IdpRow } from "./AdminIdpTable"; +import { useTranslations } from "next-intl"; export default async function IdpPage() { let idps: IdpRow[] = []; @@ -15,12 +16,13 @@ export default async function IdpPage() { } catch (e) { console.error(e); } + const t = useTranslations(); return ( <> diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index fdc6c8e7..ac5b4b0e 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -10,6 +10,7 @@ import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; import { Layout } from "@app/components/Layout"; import { adminNavItems } from "../navigation"; +import { useTranslations } from "next-intl"; export const dynamic = "force-dynamic"; diff --git a/src/app/admin/license/LicenseKeysDataTable.tsx b/src/app/admin/license/LicenseKeysDataTable.tsx index 428b163d..e98fef1a 100644 --- a/src/app/admin/license/LicenseKeysDataTable.tsx +++ b/src/app/admin/license/LicenseKeysDataTable.tsx @@ -73,7 +73,7 @@ export function LicenseKeysDataTable({ ); }, cell: ({ row }) => { - return row.original.valid ? "Yes" : "No"; + return row.original.valid ? t('yes') : t('no'); } }, { @@ -94,7 +94,7 @@ export function LicenseKeysDataTable({ cell: ({ row }) => { const type = row.original.type; const label = - type === "SITES" ? "Additional Sites" : "Host License"; + type === "SITES" ? t('sitesAdditional') : t('licenseHost'); const variant = type === "SITES" ? "secondary" : "default"; return row.original.valid ? ( {label} @@ -136,7 +136,7 @@ export function LicenseKeysDataTable({ @@ -94,7 +94,7 @@ export function SitePriceCalculator({ variant="ghost" size="icon" onClick={incrementSites} - aria-label="Increase site count" + aria-label={t('sitestCountIncrease')} > diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index a8efd0e1..2b72bfd8 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -75,7 +75,6 @@ function obfuscateLicenseKey(key: string): string { export default function LicensePage() { const api = createApiClient(useEnvContext()); - const t = useTranslations(); const [rows, setRows] = useState([]); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); @@ -104,6 +103,8 @@ export default function LicensePage() { } }); + const t = useTranslations(); + useEffect(() => { async function load() { setIsInitialLoading(true); @@ -129,8 +130,11 @@ export default function LicensePage() { } } catch (e) { toast({ - title: t('licenseErrorKeyLoad'), - description: formatAxiosError(e, t('licenseErrorKeyLoadDescription')) + title: "Failed to load license keys", + description: formatAxiosError( + e, + "An error occurred loading license keys." + ) }); } } @@ -145,14 +149,17 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: t('licenseKeyDeleted'), - description: t('licenseKeyDeletedDescription') + title: "License key deleted", + description: "The license key has been deleted." }); setIsDeleteModalOpen(false); } catch (e) { toast({ - title: t('licenseErrorKeyDelete'), - description: formatAxiosError(e, t('licenseErrorKeyDeleteDescription')) + title: "Failed to delete license key", + description: formatAxiosError( + e, + "An error occurred deleting license key." + ) }); } finally { setIsDeletingLicense(false); @@ -168,13 +175,16 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: t('licenseErrorKeyRechecked'), - description: t('licenseErrorKeyRecheckedDescription') + title: "License keys rechecked", + description: "All license keys have been rechecked" }); } catch (e) { toast({ - title: t('licenseErrorKeyRecheck'), - description: formatAxiosError(e, t('licenseErrorKeyRecheckDescription')) + title: "Failed to recheck license keys", + description: formatAxiosError( + e, + "An error occurred rechecking license keys." + ) }); } finally { setIsRecheckingLicense(false); @@ -192,8 +202,8 @@ export default function LicensePage() { } toast({ - title: t('licenseKeyActivated'), - description: t('licenseKeyActivatedDescription') + title: "License key activated", + description: "The license key has been successfully activated." }); setIsCreateModalOpen(false); @@ -202,8 +212,11 @@ export default function LicensePage() { } catch (e) { toast({ variant: "destructive", - title: t('licenseErrorKeyActivate'), - description: formatAxiosError(e, t('licenseErrorKeyActivateDescription')) + title: "Failed to activate license key", + description: formatAxiosError( + e, + "An error occurred while activating the license key." + ) }); } finally { setIsActivatingLicense(false); @@ -317,7 +330,7 @@ export default function LicensePage() { }} dialog={
-

+

{t('licenseQuestionRemove', {selectedKey: obfuscateLicenseKey(selectedLicenseKey.licenseKey)})}

@@ -373,11 +386,11 @@ export default function LicensePage() { {licenseStatus?.tier === "PROFESSIONAL" - ? "Commercial License" + ? t('licenseTierProfessional') : licenseStatus?.tier === "ENTERPRISE" - ? "Commercial License" - : "Licensed"} + ? t('licenseTierEnterprise') + : t('licensed')}

) : ( diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 9a840105..41e58dc7 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -7,6 +7,7 @@ import UsersTable, { GlobalUserRow } from "./AdminUsersTable"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon } from "lucide-react"; import { getTranslations } from 'next-intl/server'; +import { useTranslations } from "next-intl"; type PageProps = { params: Promise<{ orgId: string }>; @@ -25,6 +26,7 @@ export default async function UsersPage(props: PageProps) { } catch (e) { console.error(e); } + const t = useTranslations(); const userRows: GlobalUserRow[] = rows.map((row) => { return { @@ -34,14 +36,12 @@ export default async function UsersPage(props: PageProps) { username: row.username, type: row.type, idpId: row.idpId, - idpName: row.idpName || "Internal", + idpName: row.idpName || t('idpNameInternal'), dateCreated: row.dateCreated, serverAdmin: row.serverAdmin }; }); - const t = await getTranslations(); - return ( <> {t('setupNewOrg')} - {t('setupCreate')} + {t('setupCreate')} @@ -231,7 +231,7 @@ export default function StepperForm() { - {t('orgDisplayName')} + {t('orgDisplayName')} )} @@ -242,7 +242,7 @@ export default function StepperForm() { render={({ field }) => ( - {t('setupOrgId')} + {t('orgId')} Date: Sat, 17 May 2025 19:11:56 +0300 Subject: [PATCH 067/105] I18n auth (#23) * New translation keys in en-US locale * New translation keys in de-DE locale * New translation keys in fr-FR locale * New translation keys in it-IT locale * New translation keys in pl-PL locale * New translation keys in pt-PT locale * New translation keys in tr-TR locale * Add translation keys in app/auth * Fix build --------- Co-authored-by: Lokowitz --- messages/de-DE.json | 88 ++++++++++++++++++- messages/en-US.json | 88 ++++++++++++++++++- messages/fr-FR.json | 88 ++++++++++++++++++- messages/it-IT.json | 88 ++++++++++++++++++- messages/pl-PL.json | 88 ++++++++++++++++++- messages/pt-PT.json | 88 ++++++++++++++++++- messages/tr-TR.json | 88 ++++++++++++++++++- .../share-links/CreateShareLinkForm.tsx | 2 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 4 + .../oidc/callback/ValidateOidcToken.tsx | 21 ++--- .../auth/idp/[idpId]/oidc/callback/page.tsx | 5 +- src/app/auth/layout.tsx | 2 + src/app/auth/login/DashboardLoginForm.tsx | 9 +- src/app/auth/login/page.tsx | 10 ++- .../auth/reset-password/ResetPasswordForm.tsx | 35 ++++---- src/app/auth/reset-password/page.tsx | 5 +- .../resource/[resourceId]/AccessToken.tsx | 17 ++-- .../[resourceId]/ResourceAccessDenied.tsx | 10 ++- .../[resourceId]/ResourceAuthPortal.tsx | 56 ++++++------ .../[resourceId]/ResourceNotFound.tsx | 9 +- src/app/auth/signup/SignupForm.tsx | 6 +- src/app/auth/signup/page.tsx | 12 +-- src/app/auth/verify-email/VerifyEmailForm.tsx | 23 ++--- 23 files changed, 727 insertions(+), 115 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index f5ee471d..24153a78 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -133,7 +133,7 @@ "shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.", "shareTokenUsage": "Zugriffstoken-Nutzung anzeigen", "createLink": "Link erstellen", - "resourceNotFound": "Keine Ressourcen gefunden", + "resourcesNotFound": "Keine Ressourcen gefunden", "resourceSearch": "Suche Ressourcen", "openMenu": "Menü öffnen", "resource": "Ressource", @@ -802,5 +802,89 @@ "redirectUrlAbout": "Über die Weiterleitungs-URL", "redirectUrlAboutDescription": "Dies ist die URL, zu der Benutzer nach der Authentifizierung weitergeleitet werden. Sie müssen diese URL in den Einstellungen Ihres Identitätsanbieters konfigurieren.", "key": "Schlüssel", - "createdAt": "Erstellt am" + "createdAt": "Erstellt am", + "expiresAt": "Läuft ab am", + "pangolinAuth": "Auth - Pangolin", + "emailInvalid": "Ungültige E-Mail-Adresse", + "verificationCodeLengthRequirements": "Ihr Verifizierungscode muss 8 Zeichen lang sein.", + "errorOccurred": "Ein Fehler ist aufgetreten", + "emailErrorVerify": "E-Mail konnte nicht verifiziert werden:", + "emailVerified": "E-Mail erfolgreich verifiziert! Sie werden weitergeleitet...", + "verificationCodeErrorResend": "Verifizierungscode konnte nicht erneut gesendet werden:", + "verificationCodeResend": "Verifizierungscode erneut gesendet", + "verificationCodeResendDescription": "Wir haben einen neuen Verifizierungscode an Ihre E-Mail-Adresse gesendet. Bitte prüfen Sie Ihren Posteingang.", + "emailVerify": "E-Mail verifizieren", + "emailVerifyDescription": "Geben Sie den an Ihre E-Mail-Adresse gesendeten Verifizierungscode ein.", + "verificationCode": "Verifizierungscode", + "verificationCodeEmailSent": "Wir haben einen Verifizierungscode an Ihre E-Mail-Adresse gesendet.", + "emailVerifySubmit": "Absenden", + "emailVerifyResendProgress": "Wird erneut gesendet...", + "emailVerifyResend": "Keinen Code erhalten? Hier klicken zum erneuten Senden", + "passwordNotMatch": "Passwörter stimmen nicht überein", + "signupError": "Beim Registrieren ist ein Fehler aufgetreten", + "pangolinLogoAlt": "Pangolin Logo", + "inviteAlready": "Sieht aus, als wären Sie eingeladen worden!", + "inviteAlreadyDescription": "Um die Einladung anzunehmen, müssen Sie sich einloggen oder ein Konto erstellen.", + "signupQuestion": "Haben Sie bereits ein Konto?", + "login": "Anmelden", + "resourceNotFound": "Ressource nicht gefunden", + "resourceNotFoundDescription": "Die Ressource, auf die Sie zugreifen möchten, existiert nicht.", + "pincodeRequirementsLength": "PIN muss genau 6 Ziffern lang sein", + "pincodeRequirementsChars": "PIN darf nur Zahlen enthalten", + "passwordRequirementsLength": "Passwort muss mindestens 1 Zeichen lang sein", + "otpEmailRequirementsLength": "OTP muss mindestens 1 Zeichen lang sein", + "otpEmailSent": "OTP gesendet", + "otpEmailSentDescription": "Ein OTP wurde an Ihre E-Mail gesendet", + "otpEmailErrorAuthenticate": "Authentifizierung per E-Mail fehlgeschlagen", + "pincodeErrorAuthenticate": "Authentifizierung per PIN fehlgeschlagen", + "passwordErrorAuthenticate": "Authentifizierung per Passwort fehlgeschlagen", + "poweredBy": "Bereitgestellt von", + "authenticationRequired": "Authentifizierung erforderlich", + "authenticationMethodChoose": "Wählen Sie Ihre bevorzugte Methode für den Zugriff auf {name}", + "authenticationRequest": "Sie müssen sich authentifizieren, um auf {name} zuzugreifen", + "user": "Benutzer", + "pincodeInput": "6-stelliger PIN-Code", + "pincodeSubmit": "Mit PIN anmelden", + "passwordSubmit": "Mit Passwort anmelden", + "otpEmailDescription": "Ein Einmalcode wird an diese E-Mail gesendet.", + "otpEmailSend": "Einmalcode senden", + "otpEmail": "Einmalpasswort (OTP)", + "otpEmailSubmit": "OTP absenden", + "backToEmail": "Zurück zur E-Mail", + "noSupportKey": "Server läuft ohne Unterstützer-Schlüssel.
Erwägen Sie, das Projekt zu unterstützen!", + "accessDenied": "Zugriff verweigert", + "accessDeniedDescription": "Sie haben keine Berechtigung, auf diese Ressource zuzugreifen. Falls dies ein Fehler ist, kontaktieren Sie bitte den Administrator.", + "accessTokenError": "Fehler beim Prüfen des Zugriffstokens", + "accessGranted": "Zugriff gewährt", + "accessUrlInvalid": "Zugriffs-URL ungültig", + "accessGrantedDescription": "Ihnen wurde Zugriff auf diese Ressource gewährt. Sie werden weitergeleitet...", + "accessUrlInvalidDescription": "Diese geteilte Zugriffs-URL ist ungültig. Bitte kontaktieren Sie den Ressourceneigentümer für eine neue URL.", + "tokenInvalid": "Ungültiger Token", + "pincodeInvalid": "Ungültiger Code", + "passwordErrorRequestReset": "Zurücksetzung konnte nicht angefordert werden:", + "passwordErrorReset": "Passwort konnte nicht zurückgesetzt werden:", + "passwordResetSuccess": "Passwort erfolgreich zurückgesetzt! Zurück zur Anmeldung...", + "passwordReset": "Passwort zurücksetzen", + "passwordResetDescription": "Folgen Sie den Schritten, um Ihr Passwort zurückzusetzen", + "passwordResetSent": "Wir senden einen Code zum Zurücksetzen des Passworts an diese E-Mail-Adresse.", + "passwordResetCode": "Reset-Code", + "passwordResetCodeDescription": "Prüfen Sie Ihre E-Mail für den Reset-Code.", + "passwordNew": "Neues Passwort", + "passwordNewConfirm": "Neues Passwort bestätigen", + "pincodeAuth": "Authentifizierungscode", + "pincodeSubmit2": "Code absenden", + "passwordResetSubmit": "Zurücksetzung anfordern", + "passwordBack": "Zurück zum Passwort", + "loginBack": "Zurück zur Anmeldung", + "signup": "Registrieren", + "loginStart": "Melden Sie sich an, um zu beginnen", + "idpOidcTokenValidating": "OIDC-Token wird validiert", + "idpOidcTokenResponse": "OIDC-Token-Antwort validieren", + "idpErrorOidcTokenValidating": "Fehler beim Validieren des OIDC-Tokens", + "idpConnectingTo": "Verbindung zu {name} wird hergestellt", + "idpConnectingToDescription": "Ihre Identität wird überprüft", + "idpConnectingToProcess": "Verbindung wird hergestellt...", + "idpConnectingToFinished": "Verbunden", + "idpErrorConnectingTo": "Es gab ein Problem bei der Verbindung zu {name}. Bitte kontaktieren Sie Ihren Administrator.", + "idpErrorNotFound": "IdP nicht gefunden" } diff --git a/messages/en-US.json b/messages/en-US.json index d21a8e4c..9fce5b9f 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -133,7 +133,7 @@ "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", "shareTokenUsage": "See Access Token Usage", "createLink": "Create Link", - "resourceNotFound": "No resources found", + "resourcesNotFound": "No resources found", "resourceSearch": "Search resources", "openMenu": "Open menu", "resource": "Resource", @@ -802,5 +802,89 @@ "redirectUrlAbout": "About Redirect URL", "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", "key": "Key", - "createdAt": "Created At" + "createdAt": "Created At", + "expiresAt": "Expires At", + "pangolinAuth": "Auth - Pangolin", + "emailInvalid": "Invalid email address", + "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", + "errorOccurred": "An error occurred", + "emailErrorVerify": "Failed to verify email:", + "emailVerified": "Email successfully verified! Redirecting you...", + "verificationCodeErrorResend": "Failed to resend verification code:", + "verificationCodeResend": "Verification code resent", + "verificationCodeResendDescription": "We've resent a verification code to your email address. Please check your inbox.", + "emailVerify": "Verify Email", + "emailVerifyDescription": "Enter the verification code sent to your email address.", + "verificationCode": "Verification Code", + "verificationCodeEmailSent": "We sent a verification code to your email address.", + "emailVerifySubmit": "Submit", + "emailVerifyResendProgress": "Resending...", + "emailVerifyResend": "Didn't receive a code? Click here to resend", + "passwordNotMatch": "Passwords do not match", + "signupError": "An error occurred while signing up", + "pangolinLogoAlt": "Pangolin Logo", + "inviteAlready": "Looks like you've been invited!", + "inviteAlreadyDescription": "To accept the invite, you must log in or create an account.", + "signupQuestion": "Already have an account?", + "login": "Log in", + "resourceNotFound": "Resource Not Found", + "resourceNotFoundDescription": "The resource you're trying to access does not exist.", + "pincodeRequirementsLength": "PIN must be exactly 6 digits", + "pincodeRequirementsChars": "PIN must only contain numbers", + "passwordRequirementsLength": "Password must be at least 1 character long", + "otpEmailRequirementsLength": "OTP must be at least 1 character long", + "otpEmailSent": "OTP Sent", + "otpEmailSentDescription": "An OTP has been sent to your email", + "otpEmailErrorAuthenticate": "Failed to authenticate with email", + "pincodeErrorAuthenticate": "Failed to authenticate with pincode", + "passwordErrorAuthenticate": "Failed to authenticate with password", + "poweredBy": "Powered by", + "authenticationRequired": "Authentication Required", + "authenticationMethodChoose": "Choose your preferred method to access {name}", + "authenticationRequest": "You must authenticate to access {name}", + "user": "User", + "pincodeInput": "6-digit PIN Code", + "pincodeSubmit": "Log in with PIN", + "passwordSubmit": "Log In with Password", + "otpEmailDescription": "A one-time code will be sent to this email.", + "otpEmailSend": "Send One-time Code", + "otpEmail": "One-Time Password (OTP)", + "otpEmailSubmit": "Submit OTP", + "backToEmail": "Back to Email", + "noSupportKey": "Server is running without a supporter key.
Consider supporting the project!", + "accessDenied": "Access Denied", + "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", + "accessTokenError": "Error checking access token", + "accessGranted": "Access Granted", + "accessUrlInvalid": "Access URL Invalid", + "accessGrantedDescription": "You have been granted access to this resource. Redirecting you...", + "accessUrlInvalidDescription": "This shared access URL is invalid. Please contact the resource owner for a new URL.", + "tokenInvalid": "Invalid token", + "pincodeInvalid": "Invalid code", + "passwordErrorRequestReset": "Failed to request reset:", + "passwordErrorReset": "Failed to reset password:", + "passwordResetSuccess": "Password reset successfully! Back to log in...", + "passwordReset": "Reset Password", + "passwordResetDescription": "Follow the steps to reset your password", + "passwordResetSent": "We'll send a password reset code to this email address.", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "Check your email for the reset code.", + "passwordNew": "New Password", + "passwordNewConfirm": "Confirm New Password", + "pincodeAuth": "Authenticator Code", + "pincodeSubmit2": "Submit Code", + "passwordResetSubmit": "Request Reset", + "passwordBack": "Back to Password", + "loginBack": "Go back to log in", + "signup": "Sign up", + "loginStart": "Log in to get started", + "idpOidcTokenValidating": "Validating OIDC token", + "idpOidcTokenResponse": "Validate OIDC token response", + "idpErrorOidcTokenValidating": "Error validating OIDC token", + "idpConnectingTo": "Connecting to {name}", + "idpConnectingToDescription": "Validating your identity", + "idpConnectingToProcess": "Connecting...", + "idpConnectingToFinished": "Connected", + "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", + "idpErrorNotFound": "IdP not found" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 57482f81..ea8727b2 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -133,7 +133,7 @@ "shareAccessHint": "N'importe qui avec ce lien peut accéder à la ressource. Partagez-le avec soin.", "shareTokenUsage": "Voir Utilisation du jeton d'accès", "createLink": "Créer un lien", - "resourceNotFound": "Aucune ressource trouvée", + "resourcesNotFound": "Aucune ressource trouvée", "resourceSearch": "Rechercher des ressources", "openMenu": "Ouvrir le menu", "resource": "Ressource", @@ -802,5 +802,89 @@ "redirectUrlAbout": "À propos de l'URL de redirection", "redirectUrlAboutDescription": "C'est l'URL vers laquelle les utilisateurs seront redirigés après l'authentification. Vous devez configurer cette URL dans les paramètres de votre fournisseur d'identité.", "key": "Clé", - "createdAt": "Créé le" + "createdAt": "Créé le", + "expiresAt": "Expire le", + "pangolinAuth": "Auth - Pangolin", + "emailInvalid": "Adresse e-mail invalide", + "verificationCodeLengthRequirements": "Votre code de vérification doit comporter 8 caractères.", + "errorOccurred": "Une erreur s'est produite", + "emailErrorVerify": "Échec de la vérification de l'e-mail :", + "emailVerified": "E-mail vérifié avec succès ! Redirection...", + "verificationCodeErrorResend": "Échec du renvoi du code de vérification :", + "verificationCodeResend": "Code de vérification renvoyé", + "verificationCodeResendDescription": "Nous avons renvoyé un code de vérification à votre adresse e-mail. Veuillez vérifier votre boîte de réception.", + "emailVerify": "Vérifier l'e-mail", + "emailVerifyDescription": "Entrez le code de vérification envoyé à votre adresse e-mail.", + "verificationCode": "Code de vérification", + "verificationCodeEmailSent": "Nous avons envoyé un code de vérification à votre adresse e-mail.", + "emailVerifySubmit": "Soumettre", + "emailVerifyResendProgress": "Renvoi en cours...", + "emailVerifyResend": "Vous n'avez pas reçu de code ? Cliquez ici pour renvoyer", + "passwordNotMatch": "Les mots de passe ne correspondent pas", + "signupError": "Une erreur s'est produite lors de l'inscription", + "pangolinLogoAlt": "Logo Pangolin", + "inviteAlready": "On dirait que vous avez été invité !", + "inviteAlreadyDescription": "Pour accepter l'invitation, vous devez vous connecter ou créer un compte.", + "signupQuestion": "Vous avez déjà un compte ?", + "login": "Se connecter", + "resourceNotFound": "Ressource introuvable", + "resourceNotFoundDescription": "La ressource que vous essayez d'accéder n'existe pas.", + "pincodeRequirementsLength": "Le code PIN doit comporter exactement 6 chiffres", + "pincodeRequirementsChars": "Le code PIN ne doit contenir que des chiffres", + "passwordRequirementsLength": "Le mot de passe doit comporter au moins 1 caractère", + "otpEmailRequirementsLength": "L'OTP doit comporter au moins 1 caractère", + "otpEmailSent": "OTP envoyé", + "otpEmailSentDescription": "Un OTP a été envoyé à votre e-mail", + "otpEmailErrorAuthenticate": "Échec de l'authentification par e-mail", + "pincodeErrorAuthenticate": "Échec de l'authentification avec le code PIN", + "passwordErrorAuthenticate": "Échec de l'authentification avec le mot de passe", + "poweredBy": "Propulsé par", + "authenticationRequired": "Authentification requise", + "authenticationMethodChoose": "Choisissez votre méthode préférée pour accéder à {name}", + "authenticationRequest": "Vous devez vous authentifier pour accéder à {name}", + "user": "Utilisateur", + "pincodeInput": "Code PIN à 6 chiffres", + "pincodeSubmit": "Se connecter avec le PIN", + "passwordSubmit": "Se connecter avec le mot de passe", + "otpEmailDescription": "Un code à usage unique sera envoyé à cet e-mail.", + "otpEmailSend": "Envoyer le code à usage unique", + "otpEmail": "Mot de passe à usage unique (OTP)", + "otpEmailSubmit": "Soumettre l'OTP", + "backToEmail": "Retour à l'e-mail", + "noSupportKey": "Le serveur fonctionne sans clé de support.
Envisagez de soutenir le projet !", + "accessDenied": "Accès refusé", + "accessDeniedDescription": "Vous n'êtes pas autorisé à accéder à cette ressource. Si c'est une erreur, veuillez contacter l'administrateur.", + "accessTokenError": "Erreur lors de la vérification du jeton d'accès", + "accessGranted": "Accès accordé", + "accessUrlInvalid": "URL d'accès invalide", + "accessGrantedDescription": "L'accès à cette ressource vous a été accordé. Redirection...", + "accessUrlInvalidDescription": "Cette URL d'accès partagé n'est pas valide. Veuillez contacter le propriétaire de la ressource pour obtenir une nouvelle URL.", + "tokenInvalid": "Jeton invalide", + "pincodeInvalid": "Code invalide", + "passwordErrorRequestReset": "Échec de la demande de réinitialisation :", + "passwordErrorReset": "Échec de la réinitialisation du mot de passe :", + "passwordResetSuccess": "Mot de passe réinitialisé avec succès ! Retour à la connexion...", + "passwordReset": "Réinitialiser le mot de passe", + "passwordResetDescription": "Suivez les étapes pour réinitialiser votre mot de passe", + "passwordResetSent": "Nous allons envoyer un code de réinitialisation à cette adresse e-mail.", + "passwordResetCode": "Code de réinitialisation", + "passwordResetCodeDescription": "Vérifiez votre e-mail pour le code de réinitialisation.", + "passwordNew": "Nouveau mot de passe", + "passwordNewConfirm": "Confirmer le nouveau mot de passe", + "pincodeAuth": "Code d'authentification", + "pincodeSubmit2": "Soumettre le code", + "passwordResetSubmit": "Demander la réinitialisation", + "passwordBack": "Retour au mot de passe", + "loginBack": "Retour à la connexion", + "signup": "S'inscrire", + "loginStart": "Connectez-vous pour commencer", + "idpOidcTokenValidating": "Validation du jeton OIDC", + "idpOidcTokenResponse": "Valider la réponse du jeton OIDC", + "idpErrorOidcTokenValidating": "Erreur lors de la validation du jeton OIDC", + "idpConnectingTo": "Connexion à {name}", + "idpConnectingToDescription": "Validation de votre identité", + "idpConnectingToProcess": "Connexion...", + "idpConnectingToFinished": "Connecté", + "idpErrorConnectingTo": "Un problème est survenu lors de la connexion à {name}. Veuillez contacter votre administrateur.", + "idpErrorNotFound": "IdP introuvable" } diff --git a/messages/it-IT.json b/messages/it-IT.json index 98d59afb..8099a8a8 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -133,7 +133,7 @@ "shareAccessHint": "Chiunque abbia questo link può accedere alla risorsa. Condividilo con cura.", "shareTokenUsage": "Vedi Utilizzo Token Di Accesso", "createLink": "Crea Collegamento", - "resourceNotFound": "Nessuna risorsa trovata", + "resourcesNotFound": "Nessuna risorsa trovata", "resourceSearch": "Cerca risorse", "openMenu": "Apri menu", "resource": "Risorsa", @@ -802,5 +802,89 @@ "redirectUrlAbout": "Informazioni sull'URL di Reindirizzamento", "redirectUrlAboutDescription": "Questo è l'URL a cui gli utenti verranno reindirizzati dopo l'autenticazione. Devi configurare questo URL nelle impostazioni del tuo provider di identità.", "key": "Chiave", - "createdAt": "Creato Il" + "createdAt": "Creato Il", + "expiresAt": "Scade Il", + "pangolinAuth": "Auth - Pangolin", + "emailInvalid": "Indirizzo email non valido", + "verificationCodeLengthRequirements": "Il tuo codice di verifica deve essere di 8 caratteri.", + "errorOccurred": "Si è verificato un errore", + "emailErrorVerify": "Impossibile verificare l'email:", + "emailVerified": "Email verificata con successo! Reindirizzamento in corso...", + "verificationCodeErrorResend": "Impossibile reinviare il codice di verifica:", + "verificationCodeResend": "Codice di verifica reinviato", + "verificationCodeResendDescription": "Abbiamo reinviato un codice di verifica al tuo indirizzo email. Controlla la tua casella di posta.", + "emailVerify": "Verifica Email", + "emailVerifyDescription": "Inserisci il codice di verifica inviato al tuo indirizzo email.", + "verificationCode": "Codice di Verifica", + "verificationCodeEmailSent": "Abbiamo inviato un codice di verifica al tuo indirizzo email.", + "emailVerifySubmit": "Invia", + "emailVerifyResendProgress": "Reinvio in corso...", + "emailVerifyResend": "Non hai ricevuto il codice? Clicca qui per reinviare", + "passwordNotMatch": "Le password non coincidono", + "signupError": "Si è verificato un errore durante la registrazione", + "pangolinLogoAlt": "Logo Pangolin", + "inviteAlready": "Sembra che sei stato invitato!", + "inviteAlreadyDescription": "Per accettare l'invito, devi accedere o creare un account.", + "signupQuestion": "Hai già un account?", + "login": "Accedi", + "resourceNotFound": "Risorsa Non Trovata", + "resourceNotFoundDescription": "La risorsa che stai cercando di accedere non esiste.", + "pincodeRequirementsLength": "Il PIN deve essere esattamente di 6 cifre", + "pincodeRequirementsChars": "Il PIN deve contenere solo numeri", + "passwordRequirementsLength": "La password deve essere lunga almeno 1 carattere", + "otpEmailRequirementsLength": "L'OTP deve essere lungo almeno 1 carattere", + "otpEmailSent": "OTP Inviato", + "otpEmailSentDescription": "Un OTP è stato inviato alla tua email", + "otpEmailErrorAuthenticate": "Impossibile autenticare con l'email", + "pincodeErrorAuthenticate": "Impossibile autenticare con il codice PIN", + "passwordErrorAuthenticate": "Impossibile autenticare con la password", + "poweredBy": "Offerto da", + "authenticationRequired": "Autenticazione Richiesta", + "authenticationMethodChoose": "Scegli il tuo metodo preferito per accedere a {name}", + "authenticationRequest": "Devi autenticarti per accedere a {name}", + "user": "Utente", + "pincodeInput": "Codice PIN a 6 cifre", + "pincodeSubmit": "Accedi con PIN", + "passwordSubmit": "Accedi con Password", + "otpEmailDescription": "Un codice usa e getta verrà inviato a questa email.", + "otpEmailSend": "Invia Codice Usa e Getta", + "otpEmail": "Password Usa e Getta (OTP)", + "otpEmailSubmit": "Invia OTP", + "backToEmail": "Torna all'Email", + "noSupportKey": "Il server è in esecuzione senza una chiave di supporto.
Considera di supportare il progetto!", + "accessDenied": "Accesso Negato", + "accessDeniedDescription": "Non sei autorizzato ad accedere a questa risorsa. Se ritieni che sia un errore, contatta l'amministratore.", + "accessTokenError": "Errore nel controllo del token di accesso", + "accessGranted": "Accesso Concesso", + "accessUrlInvalid": "URL di Accesso Non Valido", + "accessGrantedDescription": "Ti è stato concesso l'accesso a questa risorsa. Reindirizzamento in corso...", + "accessUrlInvalidDescription": "Questo URL di accesso condiviso non è valido. Contatta il proprietario della risorsa per un nuovo URL.", + "tokenInvalid": "Token non valido", + "pincodeInvalid": "Codice non valido", + "passwordErrorRequestReset": "Impossibile richiedere il reset:", + "passwordErrorReset": "Impossibile reimpostare la password:", + "passwordResetSuccess": "Password reimpostata con successo! Torna al login...", + "passwordReset": "Reimposta Password", + "passwordResetDescription": "Segui i passaggi per reimpostare la tua password", + "passwordResetSent": "Invieremo un codice di reset della password a questo indirizzo email.", + "passwordResetCode": "Codice di Reset", + "passwordResetCodeDescription": "Controlla la tua email per il codice di reset.", + "passwordNew": "Nuova Password", + "passwordNewConfirm": "Conferma Nuova Password", + "pincodeAuth": "Codice Autenticatore", + "pincodeSubmit2": "Invia Codice", + "passwordResetSubmit": "Richiedi Reset", + "passwordBack": "Torna alla Password", + "loginBack": "Torna al login", + "signup": "Registrati", + "loginStart": "Accedi per iniziare", + "idpOidcTokenValidating": "Convalida token OIDC", + "idpOidcTokenResponse": "Convalida risposta token OIDC", + "idpErrorOidcTokenValidating": "Errore nella convalida del token OIDC", + "idpConnectingTo": "Connessione a {name}", + "idpConnectingToDescription": "Convalida della tua identità", + "idpConnectingToProcess": "Connessione in corso...", + "idpConnectingToFinished": "Connesso", + "idpErrorConnectingTo": "Si è verificato un problema durante la connessione a {name}. Contatta il tuo amministratore.", + "idpErrorNotFound": "IdP non trovato" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 8999f36d..b0dba5cb 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -133,7 +133,7 @@ "shareAccessHint": "Każdy z tym linkiem może uzyskać dostęp do zasobu. Podziel się nim ostrożnie.", "shareTokenUsage": "Zobacz użycie tokenu dostępu", "createLink": "Utwórz link", - "resourceNotFound": "Nie znaleziono zasobów", + "resourcesNotFound": "Nie znaleziono zasobów", "resourceSearch": "Szukaj zasobów", "openMenu": "Otwórz menu", "resource": "Zasoby", @@ -802,5 +802,89 @@ "redirectUrlAbout": "O URL przekierowania", "redirectUrlAboutDescription": "Jest to URL, na który użytkownicy zostaną przekierowani po uwierzytelnieniu. Musisz skonfigurować ten URL w ustawieniach swojego dostawcy tożsamości.", "key": "Klucz", - "createdAt": "Utworzono" + "createdAt": "Utworzono", + "expiresAt": "Wygasa w dniu", + "pangolinAuth": "Autoryzacja - Pangolin", + "emailInvalid": "Nieprawidłowy adres e-mail", + "verificationCodeLengthRequirements": "Twój kod weryfikacyjny musi mieć 8 znaków.", + "errorOccurred": "Wystąpił błąd", + "emailErrorVerify": "Nie udało się zweryfikować adresu e-mail:", + "emailVerified": "E-mail został pomyślnie zweryfikowany! Przekierowywanie...", + "verificationCodeErrorResend": "Nie udało się ponownie wysłać kodu weryfikacyjnego:", + "verificationCodeResend": "Kod weryfikacyjny wysłany ponownie", + "verificationCodeResendDescription": "Wysłaliśmy ponownie kod weryfikacyjny na Twój adres e-mail. Sprawdź swoją skrzynkę odbiorczą.", + "emailVerify": "Zweryfikuj e-mail", + "emailVerifyDescription": "Wprowadź kod weryfikacyjny wysłany na Twój adres e-mail.", + "verificationCode": "Kod weryfikacyjny", + "verificationCodeEmailSent": "Wysłaliśmy kod weryfikacyjny na Twój adres e-mail.", + "emailVerifySubmit": "Wyślij", + "emailVerifyResendProgress": "Ponowne wysyłanie...", + "emailVerifyResend": "Nie otrzymałeś kodu? Kliknij tutaj, aby wysłać ponownie", + "passwordNotMatch": "Hasła nie są zgodne", + "signupError": "Wystąpił błąd podczas rejestracji", + "pangolinLogoAlt": "Logo Pangolin", + "inviteAlready": "Wygląda na to, że zostałeś już zaproszony!", + "inviteAlreadyDescription": "Aby zaakceptować zaproszenie, musisz się zalogować lub utworzyć konto.", + "signupQuestion": "Masz już konto?", + "login": "Zaloguj się", + "resourceNotFound": "Nie znaleziono zasobu", + "resourceNotFoundDescription": "Zasób, do którego próbujesz uzyskać dostęp, nie istnieje.", + "pincodeRequirementsLength": "PIN musi składać się dokładnie z 6 cyfr", + "pincodeRequirementsChars": "PIN może zawierać tylko cyfry", + "passwordRequirementsLength": "Hasło musi mieć co najmniej 1 znak", + "otpEmailRequirementsLength": "Kod jednorazowy musi mieć co najmniej 1 znak", + "otpEmailSent": "Kod jednorazowy wysłany", + "otpEmailSentDescription": "Kod jednorazowy został wysłany na Twój e-mail", + "otpEmailErrorAuthenticate": "Nie udało się uwierzytelnić za pomocą e-maila", + "pincodeErrorAuthenticate": "Nie udało się uwierzytelnić za pomocą kodu PIN", + "passwordErrorAuthenticate": "Nie udało się uwierzytelnić za pomocą hasła", + "poweredBy": "Obsługiwane przez", + "authenticationRequired": "Wymagane uwierzytelnienie", + "authenticationMethodChoose": "Wybierz preferowaną metodę dostępu do {name}", + "authenticationRequest": "Musisz się uwierzytelnić, aby uzyskać dostęp do {name}", + "user": "Użytkownik", + "pincodeInput": "6-cyfrowy kod PIN", + "pincodeSubmit": "Zaloguj się kodem PIN", + "passwordSubmit": "Zaloguj się hasłem", + "otpEmailDescription": "Kod jednorazowy zostanie wysłany na ten adres e-mail.", + "otpEmailSend": "Wyślij kod jednorazowy", + "otpEmail": "Hasło jednorazowe (OTP)", + "otpEmailSubmit": "Wyślij OTP", + "backToEmail": "Powrót do e-maila", + "noSupportKey": "Serwer działa bez klucza wspierającego.
Rozważ wsparcie projektu!", + "accessDenied": "Odmowa dostępu", + "accessDeniedDescription": "Nie masz uprawnień dostępu do tego zasobu. Jeśli to pomyłka, skontaktuj się z administratorem.", + "accessTokenError": "Błąd sprawdzania tokena dostępu", + "accessGranted": "Dostęp przyznany", + "accessUrlInvalid": "Nieprawidłowy URL dostępu", + "accessGrantedDescription": "Otrzymałeś dostęp do tego zasobu. Przekierowywanie...", + "accessUrlInvalidDescription": "Ten udostępniony URL dostępu jest nieprawidłowy. Skontaktuj się z właścicielem zasobu, aby otrzymać nowy URL.", + "tokenInvalid": "Nieprawidłowy token", + "pincodeInvalid": "Nieprawidłowy kod", + "passwordErrorRequestReset": "Nie udało się zażądać resetowania:", + "passwordErrorReset": "Nie udało się zresetować hasła:", + "passwordResetSuccess": "Hasło zostało pomyślnie zresetowane! Powrót do logowania...", + "passwordReset": "Zresetuj hasło", + "passwordResetDescription": "Wykonaj kroki, aby zresetować hasło", + "passwordResetSent": "Wyślemy kod resetowania hasła na ten adres e-mail.", + "passwordResetCode": "Kod resetowania", + "passwordResetCodeDescription": "Sprawdź swój e-mail, aby znaleźć kod resetowania.", + "passwordNew": "Nowe hasło", + "passwordNewConfirm": "Potwierdź nowe hasło", + "pincodeAuth": "Kod uwierzytelniający", + "pincodeSubmit2": "Wyślij kod", + "passwordResetSubmit": "Zażądaj resetowania", + "passwordBack": "Powrót do hasła", + "loginBack": "Wróć do logowania", + "signup": "Zarejestruj się", + "loginStart": "Zaloguj się, aby rozpocząć", + "idpOidcTokenValidating": "Walidacja tokena OIDC", + "idpOidcTokenResponse": "Zweryfikuj odpowiedź tokena OIDC", + "idpErrorOidcTokenValidating": "Błąd walidacji tokena OIDC", + "idpConnectingTo": "Łączenie z {name}", + "idpConnectingToDescription": "Weryfikacja tożsamości", + "idpConnectingToProcess": "Łączenie...", + "idpConnectingToFinished": "Połączono", + "idpErrorConnectingTo": "Wystąpił problem z połączeniem z {name}. Skontaktuj się z administratorem.", + "idpErrorNotFound": "Nie znaleziono IdP" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index fa8d8f30..1192b3af 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -133,7 +133,7 @@ "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", - "resourceNotFound": "Nenhum recurso encontrado", + "resourcesNotFound": "Nenhum recurso encontrado", "resourceSearch": "Recursos de pesquisa", "openMenu": "Abrir menu", "resource": "Recurso", @@ -802,5 +802,89 @@ "redirectUrlAbout": "Sobre o URL de Redirecionamento", "redirectUrlAboutDescription": "Este é o URL para o qual os utilizadores serão redirecionados após a autenticação. Precisa configurar este URL nas configurações do seu provedor de identidade.", "key": "Chave", - "createdAt": "Criado Em" + "createdAt": "Criado Em", + "expiresAt": "Expira em", + "pangolinAuth": "Autenticação - Pangolin", + "emailInvalid": "Endereço de email inválido", + "verificationCodeLengthRequirements": "O seu código de verificação deve ter 8 caracteres.", + "errorOccurred": "Ocorreu um erro", + "emailErrorVerify": "Falha ao verificar o email:", + "emailVerified": "Email verificado com sucesso! Redirecionando...", + "verificationCodeErrorResend": "Falha ao reenviar o código de verificação:", + "verificationCodeResend": "Código de verificação reenviado", + "verificationCodeResendDescription": "Reenviámos um código de verificação para o seu email. Por favor, verifique a sua caixa de entrada.", + "emailVerify": "Verificar Email", + "emailVerifyDescription": "Insira o código de verificação enviado para o seu email.", + "verificationCode": "Código de Verificação", + "verificationCodeEmailSent": "Enviámos um código de verificação para o seu email.", + "emailVerifySubmit": "Submeter", + "emailVerifyResendProgress": "A reenviar...", + "emailVerifyResend": "Não recebeu um código? Clique aqui para reenviar", + "passwordNotMatch": "As palavras-passe não correspondem", + "signupError": "Ocorreu um erro durante o registo", + "pangolinLogoAlt": "Logótipo Pangolin", + "inviteAlready": "Parece que já foi convidado!", + "inviteAlreadyDescription": "Para aceitar o convite, deve iniciar sessão ou criar uma conta.", + "signupQuestion": "Já tem uma conta?", + "login": "Iniciar sessão", + "resourceNotFound": "Recurso Não Encontrado", + "resourceNotFoundDescription": "O recurso que está a tentar aceder não existe.", + "pincodeRequirementsLength": "O PIN deve ter exatamente 6 dígitos", + "pincodeRequirementsChars": "O PIN deve conter apenas números", + "passwordRequirementsLength": "A palavra-passe deve ter pelo menos 1 caractere", + "otpEmailRequirementsLength": "O OTP deve ter pelo menos 1 caractere", + "otpEmailSent": "OTP Enviado", + "otpEmailSentDescription": "Um OTP foi enviado para o seu email", + "otpEmailErrorAuthenticate": "Falha na autenticação por email", + "pincodeErrorAuthenticate": "Falha na autenticação com PIN", + "passwordErrorAuthenticate": "Falha na autenticação com palavra-passe", + "poweredBy": "Desenvolvido por", + "authenticationRequired": "Autenticação Necessária", + "authenticationMethodChoose": "Escolha o seu método preferido para aceder a {name}", + "authenticationRequest": "Deve autenticar-se para aceder a {name}", + "user": "Utilizador", + "pincodeInput": "Código PIN de 6 dígitos", + "pincodeSubmit": "Iniciar sessão com PIN", + "passwordSubmit": "Iniciar Sessão com Palavra-passe", + "otpEmailDescription": "Um código único será enviado para este email.", + "otpEmailSend": "Enviar Código Único", + "otpEmail": "Palavra-passe Única (OTP)", + "otpEmailSubmit": "Submeter OTP", + "backToEmail": "Voltar ao Email", + "noSupportKey": "O servidor está a funcionar sem uma chave de suporte.
Considere apoiar o projeto!", + "accessDenied": "Acesso Negado", + "accessDeniedDescription": "Não tem permissão para aceder a este recurso. Se isto for um erro, contacte o administrador.", + "accessTokenError": "Erro ao verificar o token de acesso", + "accessGranted": "Acesso Concedido", + "accessUrlInvalid": "URL de Acesso Inválido", + "accessGrantedDescription": "Foi-lhe concedido acesso a este recurso. A redirecionar...", + "accessUrlInvalidDescription": "Este URL de acesso partilhado é inválido. Por favor, contacte o proprietário do recurso para obter um novo URL.", + "tokenInvalid": "Token inválido", + "pincodeInvalid": "Código inválido", + "passwordErrorRequestReset": "Falha ao solicitar redefinição:", + "passwordErrorReset": "Falha ao redefinir palavra-passe:", + "passwordResetSuccess": "Palavra-passe redefinida com sucesso! Voltar ao início de sessão...", + "passwordReset": "Redefinir Palavra-passe", + "passwordResetDescription": "Siga os passos para redefinir a sua palavra-passe", + "passwordResetSent": "Enviaremos um código de redefinição de palavra-passe para este email.", + "passwordResetCode": "Código de Redefinição", + "passwordResetCodeDescription": "Verifique o seu email para obter o código de redefinição.", + "passwordNew": "Nova Palavra-passe", + "passwordNewConfirm": "Confirmar Nova Palavra-passe", + "pincodeAuth": "Código do Autenticador", + "pincodeSubmit2": "Submeter Código", + "passwordResetSubmit": "Solicitar Redefinição", + "passwordBack": "Voltar à Palavra-passe", + "loginBack": "Voltar ao início de sessão", + "signup": "Registar", + "loginStart": "Inicie sessão para começar", + "idpOidcTokenValidating": "A validar token OIDC", + "idpOidcTokenResponse": "Validar resposta do token OIDC", + "idpErrorOidcTokenValidating": "Erro ao validar token OIDC", + "idpConnectingTo": "A ligar a {name}", + "idpConnectingToDescription": "A validar a sua identidade", + "idpConnectingToProcess": "A conectar...", + "idpConnectingToFinished": "Conectado", + "idpErrorConnectingTo": "Ocorreu um problema ao ligar a {name}. Por favor, contacte o seu administrador.", + "idpErrorNotFound": "IdP não encontrado" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index d21a8e4c..9fce5b9f 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -133,7 +133,7 @@ "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", "shareTokenUsage": "See Access Token Usage", "createLink": "Create Link", - "resourceNotFound": "No resources found", + "resourcesNotFound": "No resources found", "resourceSearch": "Search resources", "openMenu": "Open menu", "resource": "Resource", @@ -802,5 +802,89 @@ "redirectUrlAbout": "About Redirect URL", "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", "key": "Key", - "createdAt": "Created At" + "createdAt": "Created At", + "expiresAt": "Expires At", + "pangolinAuth": "Auth - Pangolin", + "emailInvalid": "Invalid email address", + "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", + "errorOccurred": "An error occurred", + "emailErrorVerify": "Failed to verify email:", + "emailVerified": "Email successfully verified! Redirecting you...", + "verificationCodeErrorResend": "Failed to resend verification code:", + "verificationCodeResend": "Verification code resent", + "verificationCodeResendDescription": "We've resent a verification code to your email address. Please check your inbox.", + "emailVerify": "Verify Email", + "emailVerifyDescription": "Enter the verification code sent to your email address.", + "verificationCode": "Verification Code", + "verificationCodeEmailSent": "We sent a verification code to your email address.", + "emailVerifySubmit": "Submit", + "emailVerifyResendProgress": "Resending...", + "emailVerifyResend": "Didn't receive a code? Click here to resend", + "passwordNotMatch": "Passwords do not match", + "signupError": "An error occurred while signing up", + "pangolinLogoAlt": "Pangolin Logo", + "inviteAlready": "Looks like you've been invited!", + "inviteAlreadyDescription": "To accept the invite, you must log in or create an account.", + "signupQuestion": "Already have an account?", + "login": "Log in", + "resourceNotFound": "Resource Not Found", + "resourceNotFoundDescription": "The resource you're trying to access does not exist.", + "pincodeRequirementsLength": "PIN must be exactly 6 digits", + "pincodeRequirementsChars": "PIN must only contain numbers", + "passwordRequirementsLength": "Password must be at least 1 character long", + "otpEmailRequirementsLength": "OTP must be at least 1 character long", + "otpEmailSent": "OTP Sent", + "otpEmailSentDescription": "An OTP has been sent to your email", + "otpEmailErrorAuthenticate": "Failed to authenticate with email", + "pincodeErrorAuthenticate": "Failed to authenticate with pincode", + "passwordErrorAuthenticate": "Failed to authenticate with password", + "poweredBy": "Powered by", + "authenticationRequired": "Authentication Required", + "authenticationMethodChoose": "Choose your preferred method to access {name}", + "authenticationRequest": "You must authenticate to access {name}", + "user": "User", + "pincodeInput": "6-digit PIN Code", + "pincodeSubmit": "Log in with PIN", + "passwordSubmit": "Log In with Password", + "otpEmailDescription": "A one-time code will be sent to this email.", + "otpEmailSend": "Send One-time Code", + "otpEmail": "One-Time Password (OTP)", + "otpEmailSubmit": "Submit OTP", + "backToEmail": "Back to Email", + "noSupportKey": "Server is running without a supporter key.
Consider supporting the project!", + "accessDenied": "Access Denied", + "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", + "accessTokenError": "Error checking access token", + "accessGranted": "Access Granted", + "accessUrlInvalid": "Access URL Invalid", + "accessGrantedDescription": "You have been granted access to this resource. Redirecting you...", + "accessUrlInvalidDescription": "This shared access URL is invalid. Please contact the resource owner for a new URL.", + "tokenInvalid": "Invalid token", + "pincodeInvalid": "Invalid code", + "passwordErrorRequestReset": "Failed to request reset:", + "passwordErrorReset": "Failed to reset password:", + "passwordResetSuccess": "Password reset successfully! Back to log in...", + "passwordReset": "Reset Password", + "passwordResetDescription": "Follow the steps to reset your password", + "passwordResetSent": "We'll send a password reset code to this email address.", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "Check your email for the reset code.", + "passwordNew": "New Password", + "passwordNewConfirm": "Confirm New Password", + "pincodeAuth": "Authenticator Code", + "pincodeSubmit2": "Submit Code", + "passwordResetSubmit": "Request Reset", + "passwordBack": "Back to Password", + "loginBack": "Go back to log in", + "signup": "Sign up", + "loginStart": "Log in to get started", + "idpOidcTokenValidating": "Validating OIDC token", + "idpOidcTokenResponse": "Validate OIDC token response", + "idpErrorOidcTokenValidating": "Error validating OIDC token", + "idpConnectingTo": "Connecting to {name}", + "idpConnectingToDescription": "Validating your identity", + "idpConnectingToProcess": "Connecting...", + "idpConnectingToFinished": "Connected", + "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", + "idpErrorNotFound": "IdP not found" } diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index 3487942c..43093626 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -311,7 +311,7 @@ export default function CreateShareLinkForm({ - {t('resourceNotFound')} + {t('resourcesNotFound')} {resources.map( diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index a7236d0b..a045926b 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -57,9 +57,11 @@ const createSiteFormSchema = z.object({ .string() .min(2, { message: "Name must be at least 2 characters." + message: "Name must be at least 2 characters." }) .max(30, { message: "Name must not be longer than 30 characters." + message: "Name must not be longer than 30 characters." }), method: z.enum(["wireguard", "newt", "local"]) }); @@ -273,6 +275,8 @@ PersistentKeepalive = 5` const newtConfigDockerRun = `docker run -it fosrl/newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${env.app.dashboardUrl}`; + const t = useTranslations(); + return loadingPage ? ( ) : ( diff --git a/src/app/auth/idp/[idpId]/oidc/callback/ValidateOidcToken.tsx b/src/app/auth/idp/[idpId]/oidc/callback/ValidateOidcToken.tsx index 87a7683f..6b453ee5 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/ValidateOidcToken.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/ValidateOidcToken.tsx @@ -16,6 +16,7 @@ import { import { Alert, AlertDescription } from "@/components/ui/alert"; import { Loader2, CheckCircle2, AlertCircle } from "lucide-react"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; type ValidateOidcTokenParams = { orgId: string; @@ -36,11 +37,13 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { const { licenseStatus, isLicenseViolation } = useLicenseStatusContext(); + const t = useTranslations(); + useEffect(() => { async function validate() { setLoading(true); - console.log("Validating OIDC token", { + console.log(t('idpOidcTokenValidating'), { code: props.code, expectedState: props.expectedState, stateCookie: props.stateCookie @@ -59,7 +62,7 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { storedState: props.stateCookie }); - console.log("Validate OIDC token response", res.data); + console.log(t('idpOidcTokenResponse'), res.data); const redirectUrl = res.data.data.redirectUrl; @@ -76,7 +79,7 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { router.push(res.data.data.redirectUrl); } } catch (e) { - setError(formatAxiosError(e, "Error validating OIDC token")); + setError(formatAxiosError(e, t('idpErrorOidcTokenValidating'))); } finally { setLoading(false); } @@ -89,20 +92,20 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) {
- Connecting to {props.idp.name} - Validating your identity + {t('idpConnectingTo', {name: props.idp.name})} + {t('idpConnectingToDescription')} {loading && (
- Connecting... + {t('idpConnectingToProcess')}
)} {!loading && !error && (
- Connected + {t('idpConnectingToFinished')}
)} {error && ( @@ -110,9 +113,7 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { - There was a problem connecting to{" "} - {props.idp.name}. Please contact your - administrator. + {t('idpErrorConnectingTo', {name: props.idp.name})} {error} diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index cba74790..36fabbc4 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -3,6 +3,7 @@ import ValidateOidcToken from "./ValidateOidcToken"; import { idp } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; +import { useTranslations } from "next-intl"; export default async function Page(props: { params: Promise<{ orgId: string; idpId: string }>; @@ -23,8 +24,10 @@ export default async function Page(props: { .from(idp) .where(eq(idp.idpId, parseInt(params.idpId!))); + const t = useTranslations(); + if (!idpRes) { - return
IdP not found
; + return
{t('idpErrorNotFound')}
; } return ( diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx index 79f3294a..edce345b 100644 --- a/src/app/auth/layout.tsx +++ b/src/app/auth/layout.tsx @@ -8,6 +8,7 @@ import { AxiosResponse } from "axios"; import { ExternalLink } from "lucide-react"; import { Metadata } from "next"; import { cache } from "react"; +import { useTranslations } from "next-intl"; export const metadata: Metadata = { title: `Auth - Pangolin`, @@ -21,6 +22,7 @@ type AuthLayoutProps = { export default async function AuthLayout({ children }: AuthLayoutProps) { const getUser = cache(verifySession); const user = await getUser(); + const t = useTranslations(); const licenseStatusRes = await cache( async () => diff --git a/src/app/auth/login/DashboardLoginForm.tsx b/src/app/auth/login/DashboardLoginForm.tsx index b15dd518..817cc4b1 100644 --- a/src/app/auth/login/DashboardLoginForm.tsx +++ b/src/app/auth/login/DashboardLoginForm.tsx @@ -14,6 +14,7 @@ import { useRouter } from "next/navigation"; import { useEffect } from "react"; import Image from "next/image"; import { cleanRedirect } from "@app/lib/cleanRedirect"; +import { useTranslations } from "next-intl"; type DashboardLoginFormProps = { redirect?: string; @@ -38,23 +39,25 @@ export default function DashboardLoginForm({ // logout(); // }); + const t = useTranslations(); + return (
Pangolin Logo

- Welcome to Pangolin + {t('welcome')}

- Log in to get started + {t('loginStart')}

diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 8227c1a0..179180a7 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -9,6 +9,7 @@ import { cleanRedirect } from "@app/lib/cleanRedirect"; import db from "@server/db"; import { idp } from "@server/db/schemas"; import { LoginFormIDP } from "@app/components/LoginForm"; +import { useTranslations } from "next-intl"; export const dynamic = "force-dynamic"; @@ -40,6 +41,8 @@ export default async function Page(props: { name: idp.name })) as LoginFormIDP[]; + const t = useTranslations(); + return ( <> {isInvite && ( @@ -47,11 +50,10 @@ export default async function Page(props: {

- Looks like you've been invited! + {t('inviteAlready')}

- To accept the invite, you must log in or create an - account. + {t('inviteAlreadyDescription')}

@@ -70,7 +72,7 @@ export default async function Page(props: { } className="underline" > - Sign up + {t('signup')}

)} diff --git a/src/app/auth/reset-password/ResetPasswordForm.tsx b/src/app/auth/reset-password/ResetPasswordForm.tsx index 7ddac325..95f5c234 100644 --- a/src/app/auth/reset-password/ResetPasswordForm.tsx +++ b/src/app/auth/reset-password/ResetPasswordForm.tsx @@ -44,6 +44,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"; import { passwordSchema } from "@server/auth/passwordSchema"; import { cleanRedirect } from "@app/lib/cleanRedirect"; +import { useTranslations } from "next-intl"; const requestSchema = z.object({ email: z.string().email() @@ -122,6 +123,8 @@ export default function ResetPasswordForm({ } }); + const t = useTranslations(); + async function onRequest(data: z.infer) { const { email } = data; @@ -200,9 +203,9 @@ export default function ResetPasswordForm({
- Reset Password + {t('passwordReset')} - Follow the steps to reset your password + {t('passwordResetDescription')} @@ -221,14 +224,13 @@ export default function ResetPasswordForm({ name="email" render={({ field }) => ( - Email + {t('email')} - We'll send a password reset - code to this email address. + {t('passwordResetSent')} )} @@ -249,7 +251,7 @@ export default function ResetPasswordForm({ name="email" render={({ field }) => ( - Email + {t('email')} ( - Reset Code + {t('passwordResetCode')} - Check your email for the - reset code. + {t('passwordResetCodeDescription')} )} @@ -292,7 +293,7 @@ export default function ResetPasswordForm({ render={({ field }) => ( - New Password + {t('passwordNew')} ( - Confirm New Password + {t('passwordNewConfirm')} ( - Authenticator Code + {t('pincodeAuth')}
@@ -407,8 +408,8 @@ export default function ResetPasswordForm({ )} {state === "reset" - ? "Reset Password" - : "Submit Code"} + ? t('passwordReset') + : t('pincodeSubmit2')} )} @@ -422,7 +423,7 @@ export default function ResetPasswordForm({ {isSubmitting && ( )} - Request Reset + {t('passwordResetSubmit')} )} @@ -436,7 +437,7 @@ export default function ResetPasswordForm({ mfaForm.reset(); }} > - Back to Password + {t('passwordBack')} )} @@ -450,7 +451,7 @@ export default function ResetPasswordForm({ form.reset(); }} > - Back to Email + {t('backToEmail')} )}
diff --git a/src/app/auth/reset-password/page.tsx b/src/app/auth/reset-password/page.tsx index 73654beb..3423bbb5 100644 --- a/src/app/auth/reset-password/page.tsx +++ b/src/app/auth/reset-password/page.tsx @@ -4,6 +4,7 @@ import { cache } from "react"; import ResetPasswordForm from "./ResetPasswordForm"; import Link from "next/link"; import { cleanRedirect } from "@app/lib/cleanRedirect"; +import { useTranslations } from "next-intl"; export const dynamic = "force-dynamic"; @@ -27,6 +28,8 @@ export default async function Page(props: { redirectUrl = cleanRedirect(searchParams.redirect); } + const t = useTranslations(); + return ( <> - Go back to log in + {t('loginBack')}

diff --git a/src/app/auth/resource/[resourceId]/AccessToken.tsx b/src/app/auth/resource/[resourceId]/AccessToken.tsx index 467ea036..181f74cd 100644 --- a/src/app/auth/resource/[resourceId]/AccessToken.tsx +++ b/src/app/auth/resource/[resourceId]/AccessToken.tsx @@ -13,6 +13,7 @@ import { AuthWithAccessTokenResponse } from "@server/routers/resource"; import { AxiosResponse } from "axios"; import Link from "next/link"; import { useEffect, useState } from "react"; +import { useTranslations } from "next-intl"; type AccessTokenProps = { token: string; @@ -29,6 +30,8 @@ export default function AccessToken({ const { env } = useEnvContext(); const api = createApiClient({ env }); + const t = useTranslations(); + function appendRequestToken(url: string, token: string) { const fullUrl = new URL(url); fullUrl.searchParams.append( @@ -76,7 +79,7 @@ export default function AccessToken({ ); } } catch (e) { - console.error("Error checking access token", e); + console.error(t('accessTokenError'), e); } finally { setLoading(false); } @@ -115,9 +118,9 @@ export default function AccessToken({ function renderTitle() { if (isValid) { - return "Access Granted"; + return t('accessGranted'); } else { - return "Access URL Invalid"; + return t('accessUrlInvalid'); } } @@ -125,18 +128,16 @@ export default function AccessToken({ if (isValid) { return (
- You have been granted access to this resource. Redirecting - you... + {t('accessGrantedDescription')}
); } else { return (
- This shared access URL is invalid. Please contact the - resource owner for a new URL. + {t('accessUrlInvalidDescription')}
diff --git a/src/app/auth/resource/[resourceId]/ResourceAccessDenied.tsx b/src/app/auth/resource/[resourceId]/ResourceAccessDenied.tsx index 088782a5..871ef36f 100644 --- a/src/app/auth/resource/[resourceId]/ResourceAccessDenied.tsx +++ b/src/app/auth/resource/[resourceId]/ResourceAccessDenied.tsx @@ -9,21 +9,23 @@ import { CardTitle, } from "@app/components/ui/card"; import Link from "next/link"; +import { useTranslations } from "next-intl"; export default function ResourceAccessDenied() { + const t = useTranslations(); + return ( - Access Denied + {t('accessDenied')} - You're not allowed to access this resource. If this is a mistake, - please contact the administrator. + {t('accessDeniedDescription')}
diff --git a/src/app/auth/resource/[resourceId]/ResourceAuthPortal.tsx b/src/app/auth/resource/[resourceId]/ResourceAuthPortal.tsx index c7eca2c7..e6714b1e 100644 --- a/src/app/auth/resource/[resourceId]/ResourceAuthPortal.tsx +++ b/src/app/auth/resource/[resourceId]/ResourceAuthPortal.tsx @@ -44,6 +44,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import Link from "next/link"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; +import { useTranslations } from "next-intl"; const pinSchema = z.object({ pin: z @@ -170,6 +171,8 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { return fullUrl.toString(); } + const t = useTranslations(); + const onWhitelistSubmit = (values: any) => { setLoadingLogin(true); api.post>( @@ -183,8 +186,8 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { setOtpState("otp_sent"); submitOtpForm.setValue("email", values.email); toast({ - title: "OTP Sent", - description: "An OTP has been sent to your email" + title: t('otpEmailSent'), + description: t('otpEmailSentDescription') }); return; } @@ -200,7 +203,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { .catch((e) => { console.error(e); setWhitelistError( - formatAxiosError(e, "Failed to authenticate with email") + formatAxiosError(e, t('otpEmailErrorAuthenticate')) ); }) .then(() => setLoadingLogin(false)); @@ -225,7 +228,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { .catch((e) => { console.error(e); setPincodeError( - formatAxiosError(e, "Failed to authenticate with pincode") + formatAxiosError(e, t('pincodeErrorAuthenticate')) ); }) .then(() => setLoadingLogin(false)); @@ -253,7 +256,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { .catch((e) => { console.error(e); setPasswordError( - formatAxiosError(e, "Failed to authenticate with password") + formatAxiosError(e, t('passwordErrorAuthenticate')) ); }) .finally(() => setLoadingLogin(false)); @@ -280,7 +283,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
- Powered by{" "} + {t('poweredBy')}{" "} - Authentication Required + {t('authenticationRequired')} {numMethods > 1 - ? `Choose your preferred method to access ${props.resource.name}` - : `You must authenticate to access ${props.resource.name}`} + ? t('authenticationMethodChoose', {name: props.resource.name}) + : t('authenticationRequest', {name: props.resource.name})} @@ -327,19 +330,19 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { {props.methods.password && ( {" "} - Password + {t('password')} )} {props.methods.sso && ( {" "} - User + {t('user')} )} {props.methods.whitelist && ( {" "} - Email + {t('email')} )} @@ -362,7 +365,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { render={({ field }) => ( - 6-digit PIN Code + {t('pincodeInput')}
@@ -431,7 +434,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { disabled={loadingLogin} > - Log in with PIN + {t('pincodeSubmit')} @@ -457,7 +460,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { render={({ field }) => ( - Password + {t('password')} - Log In with Password + {t('passwordSubmit')} @@ -526,7 +529,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { render={({ field }) => ( - Email + {t('email')} - A one-time - code will be - sent to this - email. + {t('otpEmailDescription')} @@ -560,7 +560,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { disabled={loadingLogin} > - Send One-time Code + {t('otpEmailSend')} @@ -582,9 +582,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { render={({ field }) => ( - One-Time - Password - (OTP) + {t('otpEmail')} - Submit OTP + {t('otpEmailSubmit')} @@ -637,9 +635,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { {supporterStatus?.visible && (
- Server is running without a supporter key. -
- Consider supporting the project! + {t('noSupportKey')}
)} diff --git a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx index 5b101297..5a2e863e 100644 --- a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx +++ b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx @@ -7,20 +7,23 @@ import { CardTitle, } from "@app/components/ui/card"; import Link from "next/link"; +import { useTranslations } from "next-intl"; export default async function ResourceNotFound() { + const t = useTranslations(); + return ( - Resource Not Found + {t('resourceNotFound')} - The resource you're trying to access does not exist. + {t('resourceNotFoundDescription')}
diff --git a/src/app/auth/signup/SignupForm.tsx b/src/app/auth/signup/SignupForm.tsx index dc051986..ec321f38 100644 --- a/src/app/auth/signup/SignupForm.tsx +++ b/src/app/auth/signup/SignupForm.tsx @@ -71,6 +71,8 @@ export default function SignupForm({ } }); + const t = useTranslations(); + async function onSubmit(values: z.infer) { const { email, password } = values; @@ -113,15 +115,13 @@ export default function SignupForm({ setLoading(false); } - const t = useTranslations(); - return (
Pangolin Logo diff --git a/src/app/auth/signup/page.tsx b/src/app/auth/signup/page.tsx index 7f2205b4..315b314a 100644 --- a/src/app/auth/signup/page.tsx +++ b/src/app/auth/signup/page.tsx @@ -6,6 +6,7 @@ import { Mail } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { cache } from "react"; +import { useTranslations } from "next-intl"; export const dynamic = "force-dynamic"; @@ -20,6 +21,8 @@ export default async function Page(props: { const isInvite = searchParams?.redirect?.includes("/invite"); + const t = useTranslations(); + if (env.flags.disableSignupWithoutInvite && !isInvite) { redirect("/"); } @@ -54,11 +57,10 @@ export default async function Page(props: {

- Looks like you've been invited! + {t('inviteAlready')}

- To accept the invite, you must log in or create an - account. + {t('inviteAlreadyDescription')}

@@ -71,7 +73,7 @@ export default async function Page(props: { />

- Already have an account?{" "} + {t('signupQuestion')}{" "} - Log in + {t('login')}

diff --git a/src/app/auth/verify-email/VerifyEmailForm.tsx b/src/app/auth/verify-email/VerifyEmailForm.tsx index 7d68263e..235fc70c 100644 --- a/src/app/auth/verify-email/VerifyEmailForm.tsx +++ b/src/app/auth/verify-email/VerifyEmailForm.tsx @@ -37,6 +37,7 @@ import { formatAxiosError } from "@app/lib/api";; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { cleanRedirect } from "@app/lib/cleanRedirect"; +import { useTranslations } from "next-intl"; const FormSchema = z.object({ email: z.string().email({ message: "Invalid email address" }), @@ -71,6 +72,8 @@ export default function VerifyEmailForm({ }, }); + const t = useTranslations(); + async function onSubmit(data: z.infer) { setIsSubmitting(true); @@ -114,8 +117,7 @@ export default function VerifyEmailForm({ toast({ variant: "default", title: "Verification code resent", - description: - "We've resent a verification code to your email address. Please check your inbox.", + description: "We've resent a verification code to your email address. Please check your inbox.", }); } @@ -126,9 +128,9 @@ export default function VerifyEmailForm({
- Verify Email + {t('emailVerify')} - Enter the verification code sent to your email address. + {t('emailVerifyDescription')} @@ -142,7 +144,7 @@ export default function VerifyEmailForm({ name="email" render={({ field }) => ( - Email + {t('email')} ( - Verification Code + {t('verificationCode')}
- We sent a verification code to your - email address. + {t('verificationCodeEmailSent')} )} @@ -226,7 +227,7 @@ export default function VerifyEmailForm({ {isSubmitting && ( )} - Submit + {t('emailVerifySubmit')} @@ -241,8 +242,8 @@ export default function VerifyEmailForm({ disabled={isResending} > {isResending - ? "Resending..." - : "Didn't receive a code? Click here to resend"} + ? t('emailVerifyResendProgress') + : t('emailVerifyResend')}
From 731ec1da69263a3f62c5601e7bc6282222f27412 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Sat, 17 May 2025 19:23:34 +0300 Subject: [PATCH 068/105] I18n app other (#24) * New translation keys in en-US locale * New translation keys in de-DE locale * New translation keys in fr-FR locale * New translation keys in it-IT locale * New translation keys in pl-PL locale * New translation keys in pt-PT locale * New translation keys in tr-TR locale * Add translation keys in app/invite, app/setup and other files * Fix build --------- Co-authored-by: Lokowitz --- messages/de-DE.json | 30 ++++++++++++++++++- messages/en-US.json | 30 ++++++++++++++++++- messages/fr-FR.json | 30 ++++++++++++++++++- messages/it-IT.json | 30 ++++++++++++++++++- messages/pl-PL.json | 30 ++++++++++++++++++- messages/pt-PT.json | 30 ++++++++++++++++++- messages/tr-TR.json | 30 ++++++++++++++++++- .../[orgId]/settings/sites/CreateSiteForm.tsx | 2 -- src/app/invite/InviteStatusCard.tsx | 6 ++-- src/app/invite/page.tsx | 19 +++++++----- src/app/navigation.tsx | 1 + src/app/not-found.tsx | 6 ++-- src/app/setup/layout.tsx | 1 + 13 files changed, 223 insertions(+), 22 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 24153a78..d710e14b 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Verbindung wird hergestellt...", "idpConnectingToFinished": "Verbunden", "idpErrorConnectingTo": "Es gab ein Problem bei der Verbindung zu {name}. Bitte kontaktieren Sie Ihren Administrator.", - "idpErrorNotFound": "IdP nicht gefunden" + "idpErrorNotFound": "IdP nicht gefunden", + "expiresAt": "Läuft ab am", + "inviteInvalid": "Ungültige Einladung", + "inviteInvalidDescription": "Der Einladungslink ist ungültig.", + "inviteErrorWrongUser": "Einladung ist nicht für diesen Benutzer", + "inviteErrorUserNotExists": "Benutzer existiert nicht. Bitte erstelle zuerst ein Konto.", + "inviteErrorLoginRequired": "Du musst angemeldet sein, um eine Einladung anzunehmen", + "inviteErrorExpired": "Die Einladung ist möglicherweise abgelaufen", + "inviteErrorRevoked": "Die Einladung wurde möglicherweise widerrufen", + "inviteErrorTypo": "Es könnte ein Tippfehler im Einladungslink sein", + "pangolinSetup": "Einrichtung - Pangolin", + "orgNameRequired": "Organisationsname ist erforderlich", + "orgIdRequired": "Organisations-ID ist erforderlich", + "orgErrorCreate": "Beim Erstellen der Organisation ist ein Fehler aufgetreten", + "pageNotFound": "Seite nicht gefunden", + "pageNotFoundDescription": "Hoppla! Die gesuchte Seite existiert nicht.", + "overview": "Übersicht", + "home": "Startseite", + "sites": "Standorte", + "resources": "Ressourcen", + "accessControl": "Zugriffskontrolle", + "users": "Benutzer", + "roles": "Rollen", + "share": "Teilbare Links", + "settings": "Einstellungen", + "usersAll": "Alle Benutzer", + "idp": "Identitätsanbieter", + "license": "Lizenz", + "pangolinDashboard": "Dashboard - Pangolin" } diff --git a/messages/en-US.json b/messages/en-US.json index 9fce5b9f..bd279447 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Connecting...", "idpConnectingToFinished": "Connected", "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", - "idpErrorNotFound": "IdP not found" + "idpErrorNotFound": "IdP not found", + "expiresAt": "Expires At", + "inviteInvalid": "Invalid Invite", + "inviteInvalidDescription": "The invite link is invalid.", + "inviteErrorWrongUser": "Invite is not for this user", + "inviteErrorUserNotExists": "User does not exist. Please create an account first.", + "inviteErrorLoginRequired": "You must be logged in to accept an invite", + "inviteErrorExpired": "The invite may have expired", + "inviteErrorRevoked": "The invite might have been revoked", + "inviteErrorTypo": "There could be a typo in the invite link", + "pangolinSetup": "Setup - Pangolin", + "orgNameRequired": "Organization name is required", + "orgIdRequired": "Organization ID is required", + "orgErrorCreate": "An error occurred while creating org", + "pageNotFound": "Page Not Found", + "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", + "overview": "Overview", + "home": "Home", + "sites": "Sites", + "resources": "Resources", + "accessControl": "Access Control", + "users": "Users", + "roles": "Roles", + "share": "Shareable Links", + "settings": "Settings", + "usersAll": "All Users", + "idp": "Identity Providers", + "license": "License", + "pangolinDashboard": "Dashboard - Pangolin" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index ea8727b2..d1ced0f5 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Connexion...", "idpConnectingToFinished": "Connecté", "idpErrorConnectingTo": "Un problème est survenu lors de la connexion à {name}. Veuillez contacter votre administrateur.", - "idpErrorNotFound": "IdP introuvable" + "idpErrorNotFound": "IdP introuvable", + "expiresAt": "Expire le", + "inviteInvalid": "Invitation invalide", + "inviteInvalidDescription": "Le lien d'invitation n'est pas valide.", + "inviteErrorWrongUser": "L'invitation n'est pas pour cet utilisateur", + "inviteErrorUserNotExists": "L'utilisateur n'existe pas. Veuillez d'abord créer un compte.", + "inviteErrorLoginRequired": "Vous devez être connecté pour accepter une invitation", + "inviteErrorExpired": "L'invitation a peut-être expiré", + "inviteErrorRevoked": "L'invitation a peut-être été révoquée", + "inviteErrorTypo": "Il pourrait y avoir une erreur de frappe dans le lien d'invitation", + "pangolinSetup": "Configuration - Pangolin", + "orgNameRequired": "Le nom de l'organisation est requis", + "orgIdRequired": "L'ID de l'organisation est requis", + "orgErrorCreate": "Une erreur s'est produite lors de la création de l'organisation", + "pageNotFound": "Page non trouvée", + "pageNotFoundDescription": "Oups! La page que vous recherchez n'existe pas.", + "overview": "Vue d'ensemble", + "home": "Accueil", + "sites": "Sites", + "resources": "Ressources", + "accessControl": "Contrôle d'accès", + "users": "Utilisateurs", + "roles": "Rôles", + "share": "Liens partageables", + "settings": "Paramètres", + "usersAll": "Tous les utilisateurs", + "idp": "Fournisseurs d'identité", + "license": "Licence", + "pangolinDashboard": "Tableau de bord - Pangolin" } diff --git a/messages/it-IT.json b/messages/it-IT.json index 8099a8a8..c33cd9cd 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Connessione in corso...", "idpConnectingToFinished": "Connesso", "idpErrorConnectingTo": "Si è verificato un problema durante la connessione a {name}. Contatta il tuo amministratore.", - "idpErrorNotFound": "IdP non trovato" + "idpErrorNotFound": "IdP non trovato", + "expiresAt": "Scade Il", + "inviteInvalid": "Invito Non Valido", + "inviteInvalidDescription": "Il link di invito non è valido.", + "inviteErrorWrongUser": "L'invito non è per questo utente", + "inviteErrorUserNotExists": "L'utente non esiste. Si prega di creare prima un account.", + "inviteErrorLoginRequired": "Devi effettuare l'accesso per accettare un invito", + "inviteErrorExpired": "L'invito potrebbe essere scaduto", + "inviteErrorRevoked": "L'invito potrebbe essere stato revocato", + "inviteErrorTypo": "Potrebbe esserci un errore di battitura nel link di invito", + "pangolinSetup": "Configurazione - Pangolin", + "orgNameRequired": "Il nome dell'organizzazione è obbligatorio", + "orgIdRequired": "L'ID dell'organizzazione è obbligatorio", + "orgErrorCreate": "Si è verificato un errore durante la creazione dell'organizzazione", + "pageNotFound": "Pagina Non Trovata", + "pageNotFoundDescription": "Oops! La pagina che stai cercando non esiste.", + "overview": "Panoramica", + "home": "Home", + "sites": "Siti", + "resources": "Risorse", + "accessControl": "Controllo Accessi", + "users": "Utenti", + "roles": "Ruoli", + "share": "Link Condivisibili", + "settings": "Impostazioni", + "usersAll": "Tutti Gli Utenti", + "idp": "Provider Di Identità", + "license": "Licenza", + "pangolinDashboard": "Dashboard - Pangolin" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index b0dba5cb..0336f120 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Łączenie...", "idpConnectingToFinished": "Połączono", "idpErrorConnectingTo": "Wystąpił problem z połączeniem z {name}. Skontaktuj się z administratorem.", - "idpErrorNotFound": "Nie znaleziono IdP" + "idpErrorNotFound": "Nie znaleziono IdP", + "expiresAt": "Wygasa w dniu", + "inviteInvalid": "Nieprawidłowe zaproszenie", + "inviteInvalidDescription": "Link zapraszający jest nieprawidłowy.", + "inviteErrorWrongUser": "Zaproszenie nie jest dla tego użytkownika", + "inviteErrorUserNotExists": "Użytkownik nie istnieje. Najpierw utwórz konto.", + "inviteErrorLoginRequired": "Musisz być zalogowany, aby zaakceptować zaproszenie", + "inviteErrorExpired": "Zaproszenie mogło wygasnąć", + "inviteErrorRevoked": "Zaproszenie mogło zostać odwołane", + "inviteErrorTypo": "W linku zapraszającym może być literówka", + "pangolinSetup": "Konfiguracja - Pangolin", + "orgNameRequired": "Nazwa organizacji jest wymagana", + "orgIdRequired": "ID organizacji jest wymagane", + "orgErrorCreate": "Wystąpił błąd podczas tworzenia organizacji", + "pageNotFound": "Nie znaleziono strony", + "pageNotFoundDescription": "Ups! Strona, której szukasz, nie istnieje.", + "overview": "Przegląd", + "home": "Strona główna", + "sites": "Witryny", + "resources": "Zasoby", + "accessControl": "Kontrola dostępu", + "users": "Użytkownicy", + "roles": "Role", + "share": "Linki do udostępniania", + "settings": "Ustawienia", + "usersAll": "Wszyscy użytkownicy", + "idp": "Dostawcy tożsamości", + "license": "Licencja", + "pangolinDashboard": "Panel - Pangolin" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1192b3af..c35487e1 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "A conectar...", "idpConnectingToFinished": "Conectado", "idpErrorConnectingTo": "Ocorreu um problema ao ligar a {name}. Por favor, contacte o seu administrador.", - "idpErrorNotFound": "IdP não encontrado" + "idpErrorNotFound": "IdP não encontrado", + "expiresAt": "Expira em", + "inviteInvalid": "Convite Inválido", + "inviteInvalidDescription": "O link do convite é inválido.", + "inviteErrorWrongUser": "O convite não é para este usuário", + "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", + "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", + "inviteErrorExpired": "O convite pode ter expirado", + "inviteErrorRevoked": "O convite pode ter sido revogado", + "inviteErrorTypo": "Pode haver um erro de digitação no link do convite", + "pangolinSetup": "Configuração - Pangolin", + "orgNameRequired": "O nome da organização é obrigatório", + "orgIdRequired": "O ID da organização é obrigatório", + "orgErrorCreate": "Ocorreu um erro ao criar a organização", + "pageNotFound": "Página Não Encontrada", + "pageNotFoundDescription": "Ops! A página que você está procurando não existe.", + "overview": "Visão Geral", + "home": "Início", + "sites": "Sites", + "resources": "Recursos", + "accessControl": "Controle de Acesso", + "users": "Usuários", + "roles": "Funções", + "share": "Links Compartilháveis", + "settings": "Configurações", + "usersAll": "Todos os Usuários", + "idp": "Provedores de Identidade", + "license": "Licença", + "pangolinDashboard": "Painel - Pangolin" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 9fce5b9f..bd279447 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -886,5 +886,33 @@ "idpConnectingToProcess": "Connecting...", "idpConnectingToFinished": "Connected", "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", - "idpErrorNotFound": "IdP not found" + "idpErrorNotFound": "IdP not found", + "expiresAt": "Expires At", + "inviteInvalid": "Invalid Invite", + "inviteInvalidDescription": "The invite link is invalid.", + "inviteErrorWrongUser": "Invite is not for this user", + "inviteErrorUserNotExists": "User does not exist. Please create an account first.", + "inviteErrorLoginRequired": "You must be logged in to accept an invite", + "inviteErrorExpired": "The invite may have expired", + "inviteErrorRevoked": "The invite might have been revoked", + "inviteErrorTypo": "There could be a typo in the invite link", + "pangolinSetup": "Setup - Pangolin", + "orgNameRequired": "Organization name is required", + "orgIdRequired": "Organization ID is required", + "orgErrorCreate": "An error occurred while creating org", + "pageNotFound": "Page Not Found", + "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", + "overview": "Overview", + "home": "Home", + "sites": "Sites", + "resources": "Resources", + "accessControl": "Access Control", + "users": "Users", + "roles": "Roles", + "share": "Shareable Links", + "settings": "Settings", + "usersAll": "All Users", + "idp": "Identity Providers", + "license": "License", + "pangolinDashboard": "Dashboard - Pangolin" } diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index a045926b..91db2059 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -57,11 +57,9 @@ const createSiteFormSchema = z.object({ .string() .min(2, { message: "Name must be at least 2 characters." - message: "Name must be at least 2 characters." }) .max(30, { message: "Name must not be longer than 30 characters." - message: "Name must not be longer than 30 characters." }), method: z.enum(["wireguard", "newt", "local"]) }); diff --git a/src/app/invite/InviteStatusCard.tsx b/src/app/invite/InviteStatusCard.tsx index 1eee0174..c2c757c6 100644 --- a/src/app/invite/InviteStatusCard.tsx +++ b/src/app/invite/InviteStatusCard.tsx @@ -45,9 +45,9 @@ export default function InviteStatusCard({ {t('inviteErrorNotValid')}

    -
  • The invite may have expired
  • -
  • The invite might have been revoked
  • -
  • There could be a typo in the invite link
  • +
  • {t('inviteErrorExpired')}
  • +
  • {t('inviteErrorRevoked')}
  • +
  • {t('inviteErrorTypo')}
); diff --git a/src/app/invite/page.tsx b/src/app/invite/page.tsx index b105c0b1..5ba30dd8 100644 --- a/src/app/invite/page.tsx +++ b/src/app/invite/page.tsx @@ -5,7 +5,10 @@ import { AcceptInviteResponse } from "@server/routers/user"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import InviteStatusCard from "./InviteStatusCard"; -import { formatAxiosError } from "@app/lib/api";; +import { formatAxiosError } from "@app/lib/api"; +import { useTranslations } from "next-intl"; + +; export default async function InvitePage(props: { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; @@ -20,12 +23,14 @@ export default async function InvitePage(props: { const user = await verifySession(); + const t = useTranslations(); + const parts = tokenParam.split("-"); if (parts.length !== 2) { return ( <> -

Invalid Invite

-

The invite link is invalid.

+

{t('inviteInvalid')}

+

{t('inviteInvalidDescription')}

); } @@ -52,15 +57,13 @@ export default async function InvitePage(props: { } function cardType() { - if (error.includes("Invite is not for this user")) { + if (error.includes(t('inviteErrorWrongUser'))) { return "wrong_user"; } else if ( - error.includes( - "User does not exist. Please create an account first." - ) + error.includes(t('inviteErrorUserNotExists')) ) { return "user_does_not_exist"; - } else if (error.includes("You must be logged in to accept an invite")) { + } else if (error.includes(t('inviteErrorLoginRequired'))) { return "not_logged_in"; } else { return "rejected"; diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 8ea3c080..7ee9ec75 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -10,6 +10,7 @@ import { KeyRound, TicketCheck } from "lucide-react"; +import { useTranslations } from "next-intl"; export const orgLangingNavItems: SidebarNavItem[] = [ { diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index cb831311..a1cd6103 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,14 +1,16 @@ import Link from "next/link"; +import { useTranslations } from "next-intl"; export default async function NotFound() { + const t = useTranslations(); return (

404

- Page Not Found + {t('pageNotFound')}

- Oops! The page you're looking for doesn't exist. + {t('pageNotFoundDescription')}

); diff --git a/src/app/setup/layout.tsx b/src/app/setup/layout.tsx index e254037d..6b1b80e3 100644 --- a/src/app/setup/layout.tsx +++ b/src/app/setup/layout.tsx @@ -11,6 +11,7 @@ import { ListUserOrgsResponse } from "@server/routers/org"; import { internal } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; +import { useTranslations } from "next-intl"; export const metadata: Metadata = { title: `Setup - Pangolin`, From eff812eaa8b37909163771b5401aa91843dd8417 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sat, 17 May 2025 18:17:22 +0000 Subject: [PATCH 069/105] first fixes --- messages/de-DE.json | 6 ++--- messages/en-US.json | 26 ++++--------------- messages/fr-FR.json | 6 ++--- messages/it-IT.json | 6 ++--- messages/pl-PL.json | 6 ++--- messages/pt-PT.json | 2 +- messages/tr-TR.json | 4 +-- .../settings/access/roles/CreateRoleForm.tsx | 5 ++-- .../settings/access/roles/DeleteRoleForm.tsx | 5 ++-- .../settings/access/users/create/page.tsx | 21 +++++++-------- .../[orgId]/settings/access/users/page.tsx | 4 +-- .../settings/api-keys/[apiKeyId]/layout.tsx | 5 ++-- .../api-keys/[apiKeyId]/permissions/page.tsx | 12 ++++----- src/app/[orgId]/settings/api-keys/page.tsx | 5 ++-- src/app/[orgId]/settings/layout.tsx | 4 +-- .../settings/resources/ResourcesTable.tsx | 7 ++--- .../resources/[resourceId]/general/page.tsx | 21 +++++---------- .../resources/[resourceId]/proxy/page.tsx | 7 +++-- src/app/[orgId]/settings/resources/page.tsx | 5 ++-- src/app/admin/api-keys/[apiKeyId]/layout.tsx | 4 +-- src/app/admin/idp/[idpId]/layout.tsx | 4 +-- src/app/admin/idp/create/page.tsx | 5 ++-- src/app/admin/idp/page.tsx | 5 ++-- src/app/admin/users/page.tsx | 4 +-- src/app/auth/layout.tsx | 4 +-- src/app/auth/login/page.tsx | 4 +-- src/app/not-found.tsx | 7 ++--- 27 files changed, 80 insertions(+), 114 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index d710e14b..c94c7786 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -359,10 +359,10 @@ "inviteRemoveErrorDescription": "Beim Entfernen der Einladung ist ein Fehler aufgetreten.", "inviteRemoved": "Einladung entfernt", "inviteRemovedDescription": "Die Einladung für {email} wurde entfernt.", - "inviteQuestionRemove": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} entfernen möchten?", + "inviteQuestionRemove": "Sind Sie sicher, dass Sie die Einladung {email} entfernen möchten?", "inviteMessageRemove": "Sobald entfernt, wird diese Einladung nicht mehr gültig sein. Sie können den Benutzer später jederzeit erneut einladen.", "inviteMessageConfirm": "Bitte geben Sie zur Bestätigung die E-Mail-Adresse der Einladung unten ein.", - "inviteQuestionRegenerate": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} neu generieren möchten? Dies wird die vorherige Einladung widerrufen.", + "inviteQuestionRegenerate": "Sind Sie sicher, dass Sie die Einladung {email} neu generieren möchten? Dies wird die vorherige Einladung widerrufen.", "inviteRemoveConfirm": "Entfernen der Einladung bestätigen", "inviteRegenerated": "Einladung neu generiert", "inviteSent": "Eine neue Einladung wurde an {email} gesendet.", @@ -431,7 +431,7 @@ "accessRoleSelect": "Rolle auswählen", "inviteEmailSentDescription": "Eine E-Mail mit dem Zugangslink wurde an den Benutzer gesendet. Er muss den Link aufrufen, um die Einladung anzunehmen.", "inviteSentDescription": "Der Benutzer wurde eingeladen. Er muss den unten stehenden Link aufrufen, um die Einladung anzunehmen.", - "inviteExpiresIn": "Die Einladung läuft in {days, plural, =1 {einem Tag} other {# Tagen}} ab.", + "inviteExpiresIn": "Die Einladung läuft in {days, plural, =1 {einem Tag} other {# Tagen}} ab.", "idpTitle": "Identitätsanbieter", "idpSelect": "Wählen Sie den Identitätsanbieter für den externen Benutzer", "idpNotConfigured": "Es sind keine Identitätsanbieter konfiguriert. Bitte konfigurieren Sie einen Identitätsanbieter, bevor Sie externe Benutzer erstellen.", diff --git a/messages/en-US.json b/messages/en-US.json index bd279447..112a39db 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -359,7 +359,7 @@ "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", "inviteRemoved": "Invitation removed", "inviteRemovedDescription": "The invitation for {email} has been removed.", - "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", + "inviteQuestionRemove ": "Are you sure you want to remove the invitation {email}?", "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", @@ -431,7 +431,7 @@ "accessRoleSelect": "Select role", "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", - "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", + "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", "idpTitle": "Identity Provider", "idpSelect": "Select the identity provider for the external user", "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Failed to remove role", "accessRoleErrorRemoveDescription": "An error occurred while removing the role.", "accessRoleName": "Role Name", - "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", + "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", "accessRoleRemove": "Remove Role", "accessRoleRemoveDescription": "Remove a role from the organization", "accessRoleRemoveSubmit": "Remove Role", @@ -693,9 +693,7 @@ "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", "manage": "Manage", "sitesNotFound": "No sites found.", - "expiresAt": "Expires At", "pangolinServerAdmin": "Server Admin - Pangolin", - "idpNameInternal": "Internal", "licenseTierProfessional": "Professional License", "licenseTierEnterprise": "Enterprise License", "licensed": "Licensed", @@ -717,8 +715,6 @@ "idp": "Identity Providers", "idpSearch": "Search identity providers...", "idpAdd": "Add Identity Provider", - "nameMin": "Name must be at least {len} characters.", - "nameMax": "Name must not be longer than {len} characters.", "idpClientIdRequired": "Client ID is required.", "idpClientSecretRequired": "Client Secret is required.", "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", @@ -730,7 +726,6 @@ "idpCreate": "Create Identity Provider", "idpCreateDescription": "Configure a new identity provider for user authentication", "idpSeeAll": "See All Identity Providers", - "idpTitle": "General Information", "idpSettingsDescription": "Configure the basic information for your identity provider", "idpDisplayName": "A display name for this identity provider", "idpAutoProvisionUsers": "Auto Provision Users", @@ -779,8 +774,8 @@ "orgPolicyDeletedDescription": "Policy deleted successfully", "defaultMappingsUpdatedDescription": "Default mappings updated successfully", "orgPoliciesAbout": "About Organization Policies", - "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token. For more information, see", - "orgPoliciesAboutDescriptionLink": "the documentation", + "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token.", + "orgPoliciesAboutDescriptionLink": "See documentation, for more information.", "defaultMappingsOptional": "Default Mappings (Optional)", "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", "defaultMappingsRole": "Default Role Mapping", @@ -801,11 +796,7 @@ "redirectUrl": "Redirect URL", "redirectUrlAbout": "About Redirect URL", "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", - "key": "Key", - "createdAt": "Created At", - "expiresAt": "Expires At", "pangolinAuth": "Auth - Pangolin", - "emailInvalid": "Invalid email address", "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", "errorOccurred": "An error occurred", "emailErrorVerify": "Failed to verify email:", @@ -887,7 +878,6 @@ "idpConnectingToFinished": "Connected", "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", "idpErrorNotFound": "IdP not found", - "expiresAt": "Expires At", "inviteInvalid": "Invalid Invite", "inviteInvalidDescription": "The invite link is invalid.", "inviteErrorWrongUser": "Invite is not for this user", @@ -904,15 +894,9 @@ "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", "overview": "Overview", "home": "Home", - "sites": "Sites", - "resources": "Resources", "accessControl": "Access Control", - "users": "Users", - "roles": "Roles", - "share": "Shareable Links", "settings": "Settings", "usersAll": "All Users", - "idp": "Identity Providers", "license": "License", "pangolinDashboard": "Dashboard - Pangolin" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index d1ced0f5..fe5faedc 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -359,10 +359,10 @@ "inviteRemoveErrorDescription": "Une erreur s'est produite lors de la suppression de l'invitation.", "inviteRemoved": "Invitation supprimée", "inviteRemovedDescription": "L'invitation pour {email} a été supprimée.", - "inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation{email, plural, ='' {}, other { pour #}} ?", + "inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation {email}?", "inviteMessageRemove": "Une fois supprimée, cette invitation ne sera plus valide. Vous pourrez toujours réinviter l'utilisateur plus tard.", "inviteMessageConfirm": "Pour confirmer, veuillez saisir l'adresse e-mail de l'invitation ci-dessous.", - "inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation{email, plural, ='' {}, other { pour #}} ? Cela révoquera l'invitation précédente.", + "inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation {email}? Cela révoquera l'invitation précédente.", "inviteRemoveConfirm": "Confirmer la suppression de l'invitation", "inviteRegenerated": "Invitation régénérée", "inviteSent": "Une nouvelle invitation a été envoyée à {email}.", @@ -431,7 +431,7 @@ "accessRoleSelect": "Sélectionner un rôle", "inviteEmailSentDescription": "Un e-mail a été envoyé à l'utilisateur avec le lien d'accès ci-dessous. Ils doivent accéder au lien pour accepter l'invitation.", "inviteSentDescription": "L'utilisateur a été invité. Ils doivent accéder au lien ci-dessous pour accepter l'invitation.", - "inviteExpiresIn": "L'invitation expirera dans {days, plural, =1 {# jour} other {# jours}}.", + "inviteExpiresIn": "L'invitation expirera dans {days, plural, =1 {# jour} other {# jours}}.", "idpTitle": "Fournisseur d'identité", "idpSelect": "Sélectionnez le fournisseur d'identité pour l'utilisateur externe", "idpNotConfigured": "Aucun fournisseur d'identité n'est configuré. Veuillez configurer un fournisseur d'identité avant de créer des utilisateurs externes.", diff --git a/messages/it-IT.json b/messages/it-IT.json index c33cd9cd..c72f08bc 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -359,10 +359,10 @@ "inviteRemoveErrorDescription": "Si è verificato un errore durante la rimozione dell'invito.", "inviteRemoved": "Invito rimosso", "inviteRemovedDescription": "L'invito per {email} è stato rimosso.", - "inviteQuestionRemove": "Sei sicuro di voler rimuovere l'invito{email, plural, ='' {}, other { per #}}?", + "inviteQuestionRemove": "Sei sicuro di voler rimuovere l'invito {email}?", "inviteMessageRemove": "Una volta rimosso, questo invito non sarà più valido. Puoi sempre reinvitare l'utente in seguito.", "inviteMessageConfirm": "Per confermare, digita l'indirizzo email dell'invito qui sotto.", - "inviteQuestionRegenerate": "Sei sicuro di voler rigenerare l'invito{email, plural, ='' {}, other { per #}}? Questo revocherà l'invito precedente.", + "inviteQuestionRegenerate": "Sei sicuro di voler rigenerare l'invito {email}? Questo revocherà l'invito precedente.", "inviteRemoveConfirm": "Conferma Rimozione Invito", "inviteRegenerated": "Invito Rigenerato", "inviteSent": "Un nuovo invito è stato inviato a {email}.", @@ -431,7 +431,7 @@ "accessRoleSelect": "Seleziona ruolo", "inviteEmailSentDescription": "È stata inviata un'email all'utente con il link di accesso qui sotto. Devono accedere al link per accettare l'invito.", "inviteSentDescription": "L'utente è stato invitato. Deve accedere al link qui sotto per accettare l'invito.", - "inviteExpiresIn": "L'invito scadrà tra {days, plural, =1 {# giorno} other {# giorni}}.", + "inviteExpiresIn": "L'invito scadrà tra {days, plural, =1 {# giorno} other {# giorni}}.", "idpTitle": "Provider di Identità", "idpSelect": "Seleziona il provider di identità per l'utente esterno", "idpNotConfigured": "Nessun provider di identità configurato. Configura un provider di identità prima di creare utenti esterni.", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 0336f120..d11a743b 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -359,10 +359,10 @@ "inviteRemoveErrorDescription": "Wystąpił błąd podczas usuwania zaproszenia.", "inviteRemoved": "Zaproszenie usunięte", "inviteRemovedDescription": "Zaproszenie dla {email} zostało usunięte.", - "inviteQuestionRemove": "Czy na pewno chcesz usunąć zaproszenie{email, plural, ='' {}, other { dla #}}?", + "inviteQuestionRemove": "Czy na pewno chcesz usunąć zaproszenie {email}?", "inviteMessageRemove": "Po usunięciu to zaproszenie nie będzie już ważne. Zawsze możesz ponownie zaprosić użytkownika później.", "inviteMessageConfirm": "Aby potwierdzić, wpisz poniżej adres email zaproszenia.", - "inviteQuestionRegenerate": "Czy na pewno chcesz ponownie wygenerować zaproszenie{email, plural, ='' {}, other { dla #}}? Spowoduje to unieważnienie poprzedniego zaproszenia.", + "inviteQuestionRegenerate": "Czy na pewno chcesz ponownie wygenerować zaproszenie {email}? Spowoduje to unieważnienie poprzedniego zaproszenia.", "inviteRemoveConfirm": "Potwierdź usunięcie zaproszenia", "inviteRegenerated": "Zaproszenie wygenerowane ponownie", "inviteSent": "Nowe zaproszenie zostało wysłane do {email}.", @@ -431,7 +431,7 @@ "accessRoleSelect": "Wybierz rolę", "inviteEmailSentDescription": "Email został wysłany do użytkownika z linkiem dostępu poniżej. Musi on uzyskać dostęp do linku, aby zaakceptować zaproszenie.", "inviteSentDescription": "Użytkownik został zaproszony. Musi uzyskać dostęp do poniższego linku, aby zaakceptować zaproszenie.", - "inviteExpiresIn": "Zaproszenie wygaśnie za {days, plural, =1 {# dzień} other {# dni}}.", + "inviteExpiresIn": "Zaproszenie wygaśnie za {days, plural, =1 {# dzień} other {# dni}}.", "idpTitle": "Dostawca tożsamości", "idpSelect": "Wybierz dostawcę tożsamości dla użytkownika zewnętrznego", "idpNotConfigured": "Nie skonfigurowano żadnych dostawców tożsamości. Skonfiguruj dostawcę tożsamości przed utworzeniem użytkowników zewnętrznych.", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index c35487e1..75b0d8ef 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -431,7 +431,7 @@ "accessRoleSelect": "Selecionar função", "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", - "inviteExpiresIn": "O convite expirará em {days, plural, =1 {# dia} other {# dias}}.", + "inviteExpiresIn": "O convite expirará em {days, plural, =1 {# dia} other {# dias}}.", "idpTitle": "Provedor de Identidade", "idpSelect": "Selecione o provedor de identidade para o usuário externo", "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index bd279447..3209478c 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -431,7 +431,7 @@ "accessRoleSelect": "Select role", "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", - "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", + "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", "idpTitle": "Identity Provider", "idpSelect": "Select the identity provider for the external user", "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Failed to remove role", "accessRoleErrorRemoveDescription": "An error occurred while removing the role.", "accessRoleName": "Role Name", - "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", + "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", "accessRoleRemove": "Remove Role", "accessRoleRemoveDescription": "Remove a role from the organization", "accessRoleRemoveSubmit": "Remove Role", diff --git a/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx b/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx index 3bf2c59d..eb44eb40 100644 --- a/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx +++ b/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx @@ -39,10 +39,8 @@ type CreateRoleFormProps = { afterCreate?: (res: CreateRoleResponse) => Promise; }; -const t = useTranslations(); - const formSchema = z.object({ - name: z.string({ message: t('accessRoleNameRequired') }).max(32), + name: z.string({ message: "Name is required" }).max(32), description: z.string().max(255).optional() }); @@ -52,6 +50,7 @@ export default function CreateRoleForm({ afterCreate }: CreateRoleFormProps) { const { org } = useOrgContext(); + const t = useTranslations(); const [loading, setLoading] = useState(false); diff --git a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx index b6da44ea..0501f0d1 100644 --- a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx +++ b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx @@ -47,10 +47,8 @@ type CreateRoleFormProps = { afterDelete?: () => void; }; -const t = useTranslations(); - const formSchema = z.object({ - newRoleId: z.string({ message: t('accessRoleErrorNewRequired') }) + newRoleId: z.string({ message: "New role is required" }) }); export default function DeleteRoleForm({ @@ -60,6 +58,7 @@ export default function DeleteRoleForm({ afterDelete }: CreateRoleFormProps) { const { org } = useOrgContext(); + const t = useTranslations(); const [loading, setLoading] = useState(false); const [roles, setRoles] = useState([]); diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 0d9d2438..4c96a241 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -60,30 +60,28 @@ interface IdpOption { type: string; } -const t = useTranslations(); - const internalFormSchema = z.object({ - email: z.string().email({ message: t('emailInvalid') }), - validForHours: z.string().min(1, { message: t('inviteValidityDuration') }), - roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }) + email: z.string().email({ message: "Invalid email address" }), + validForHours: z.string().min(1, { message: "Please select a duration" }), + roleId: z.string().min(1, { message: "Please select a role" }) }); const externalFormSchema = z.object({ - username: z.string().min(1, { message: t('usernameRequired') }), + username: z.string().min(1, { message: "Username is required" }), email: z .string() - .email({ message: t('emailInvalid') }) + .email({ message: "Invalid email address" }) .optional() .or(z.literal("")), name: z.string().optional(), - roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }), - idpId: z.string().min(1, { message: t('idpSelectPlease') }) + roleId: z.string().min(1, { message: "Please select a role" }), + idpId: z.string().min(1, { message: "Please select an identity provider" }) }); const formatIdpType = (type: string) => { switch (type.toLowerCase()) { case "oidc": - return t('idpGenericOidc'); + return "Generic OAuth2/OIDC provider."; default: return type; } @@ -94,6 +92,7 @@ export default function Page() { const router = useRouter(); const { env } = useEnvContext(); const api = createApiClient({ env }); + const t = useTranslations(); const [userType, setUserType] = useState("internal"); const [inviteLink, setInviteLink] = useState(null); @@ -351,7 +350,7 @@ export default function Page() { - {t('userInfo')} + {t('userSettings')} {t('userSettingsDescription')} diff --git a/src/app/[orgId]/settings/access/users/page.tsx b/src/app/[orgId]/settings/access/users/page.tsx index 789d4583..27b227fa 100644 --- a/src/app/[orgId]/settings/access/users/page.tsx +++ b/src/app/[orgId]/settings/access/users/page.tsx @@ -11,7 +11,6 @@ import { verifySession } from "@app/lib/auth/verifySession"; import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { getTranslations } from 'next-intl/server'; -import { useTranslations } from "next-intl"; type UsersPageProps = { params: Promise<{ orgId: string }>; @@ -24,8 +23,7 @@ export default async function UsersPage(props: UsersPageProps) { const getUser = cache(verifySession); const user = await getUser(); - - const t = useTranslations(); + const t = await getTranslations(); let users: ListUsersResponse["users"] = []; let hasInvitations = false; diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx index 618bc76e..8343249c 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx @@ -15,7 +15,7 @@ import { import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; interface SettingsLayoutProps { children: React.ReactNode; @@ -24,8 +24,7 @@ interface SettingsLayoutProps { export default async function SettingsLayout(props: SettingsLayoutProps) { const params = await props.params; - - const t = useTranslations(); + const t = await getTranslations(); const { children } = props; diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx index 0cc76d2a..121d9523 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx @@ -45,10 +45,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error loading API key actions", + title: t('apiKeysPermissionsErrorLoadingActions'), description: formatAxiosError( e, - "Error loading API key actions" + t('apiKeysPermissionsErrorLoadingActions') ) }); }); @@ -79,18 +79,18 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysErrorSetPermission'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysErrorSetPermission'), description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: "Permissions updated", - description: "The permissions have been updated." + title: t('apiKeysPermissionsUpdated'), + description: t('apiKeysPermissionsUpdatedDescription') }); } diff --git a/src/app/[orgId]/settings/api-keys/page.tsx b/src/app/[orgId]/settings/api-keys/page.tsx index dee061a9..188921e5 100644 --- a/src/app/[orgId]/settings/api-keys/page.tsx +++ b/src/app/[orgId]/settings/api-keys/page.tsx @@ -4,7 +4,7 @@ import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import OrgApiKeysTable, { OrgApiKeyRow } from "./OrgApiKeysTable"; import { ListOrgApiKeysResponse } from "@server/routers/apiKeys"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; type ApiKeyPageProps = { params: Promise<{ orgId: string }>; @@ -14,7 +14,8 @@ export const dynamic = "force-dynamic"; export default async function ApiKeysPage(props: ApiKeyPageProps) { const params = await props.params; - const t = useTranslations(); + const t = await getTranslations(); + let apiKeys: ListOrgApiKeysResponse["apiKeys"] = []; try { const res = await internal.get>( diff --git a/src/app/[orgId]/settings/layout.tsx b/src/app/[orgId]/settings/layout.tsx index 14053ab6..215f554f 100644 --- a/src/app/[orgId]/settings/layout.tsx +++ b/src/app/[orgId]/settings/layout.tsx @@ -19,7 +19,7 @@ import UserProvider from "@app/providers/UserProvider"; import { Layout } from "@app/components/Layout"; import { SidebarNavItem, SidebarNavProps } from "@app/components/SidebarNav"; import { orgNavItems } from "@app/app/navigation"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export const dynamic = "force-dynamic"; @@ -47,7 +47,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const cookie = await authCookieHeader(); - const t = useTranslations(); + const t = await getTranslations(); try { const getOrgUser = cache(() => diff --git a/src/app/[orgId]/settings/resources/ResourcesTable.tsx b/src/app/[orgId]/settings/resources/ResourcesTable.tsx index 1b96cd28..7c6f4340 100644 --- a/src/app/[orgId]/settings/resources/ResourcesTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesTable.tsx @@ -89,11 +89,8 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { .catch((e) => { toast({ variant: "destructive", - title: "Failed to toggle resource", - description: formatAxiosError( - e, - "An error occurred while updating the resource" - ) + title: t('resourcesErrorUpdate'), + description: formatAxiosError(e, t('resourcesErrorUpdateDescription')) }); }); } diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index 677646de..9facce58 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -67,8 +67,6 @@ import { import { SwitchInput } from "@app/components/SwitchInput"; import { useTranslations } from "next-intl"; -const t = useTranslations(); - const GeneralFormSchema = z .object({ subdomain: z.string().optional(), @@ -91,7 +89,7 @@ const GeneralFormSchema = z return true; }, { - message: t('proxyErrorInvalidPort'), + message: "Invalid port number", path: ["proxyPort"] } ) @@ -103,7 +101,7 @@ const GeneralFormSchema = z return true; }, { - message: t('subdomainErrorInvalid'), + message: "Invalid subdomain", path: ["subdomain"] } ); @@ -121,6 +119,7 @@ export default function GeneralForm() { const { resource, updateResource } = useResourceContext(); const { org } = useOrgContext(); const router = useRouter(); + const t = useTranslations(); const { env } = useEnvContext(); @@ -178,10 +177,7 @@ export default function GeneralForm() { toast({ variant: "destructive", title: t('domainErrorFetch'), - description: formatAxiosError( - e, - t('domainErrorFetchDescription') - ) + description: formatAxiosError(e, t('domainErrorFetchDescription')) }); }); @@ -220,10 +216,7 @@ export default function GeneralForm() { toast({ variant: "destructive", title: t('resourceErrorUpdate'), - description: formatAxiosError( - e, - t('resourceErrorUpdateDescription') - ) + description: formatAxiosError(e, t('resourceErrorUpdateDescription')) }); }); @@ -259,9 +252,7 @@ export default function GeneralForm() { toast({ variant: "destructive", title: t('resourceErrorTransfer'), - description: formatAxiosError( - e, - t('resourceErrorTransferDescription') + description: formatAxiosError(e, t('resourceErrorTransferDescription') ) }); }); diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index bf452f99..dbbb3805 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -93,8 +93,6 @@ type LocalTarget = Omit< "protocol" >; -const t = useTranslations(); - const proxySettingsSchema = z.object({ setHostHeader: z .string() @@ -107,7 +105,7 @@ const proxySettingsSchema = z.object({ return true; }, { - message: t('proxyErrorInvalidHeader') + message: "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header." } ) }); @@ -125,7 +123,7 @@ const tlsSettingsSchema = z.object({ return true; }, { - message: t('proxyErrorTls') + message: "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name." } ) }); @@ -138,6 +136,7 @@ export default function ReverseProxyTargets(props: { params: Promise<{ resourceId: number }>; }) { const params = use(props.params); + const t = useTranslations(); const { resource, updateResource } = useResourceContext(); diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index 74eda80e..908a8691 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -10,7 +10,6 @@ import { GetOrgResponse } from "@server/routers/org"; import OrgProvider from "@app/providers/OrgProvider"; import ResourcesSplashCard from "./ResourcesSplashCard"; import { getTranslations } from 'next-intl/server'; -import { useTranslations } from "next-intl"; type ResourcesPageProps = { params: Promise<{ orgId: string }>; @@ -20,6 +19,8 @@ export const dynamic = "force-dynamic"; export default async function ResourcesPage(props: ResourcesPageProps) { const params = await props.params; + const t = await getTranslations(); + let resources: ListResourcesResponse["resources"] = []; try { const res = await internal.get>( @@ -47,8 +48,6 @@ export default async function ResourcesPage(props: ResourcesPageProps) { redirect(`/${params.orgId}/settings/resources`); } - const t = useTranslations(); - const resourceRows: ResourceRow[] = resources.map((resource) => { return { id: resource.resourceId, diff --git a/src/app/admin/api-keys/[apiKeyId]/layout.tsx b/src/app/admin/api-keys/[apiKeyId]/layout.tsx index d9ce4e8a..818fcccc 100644 --- a/src/app/admin/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/layout.tsx @@ -15,7 +15,7 @@ import { import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; interface SettingsLayoutProps { children: React.ReactNode; @@ -25,7 +25,7 @@ interface SettingsLayoutProps { export default async function SettingsLayout(props: SettingsLayoutProps) { const params = await props.params; - const t = useTranslations(); + const t = await getTranslations(); const { children } = props; diff --git a/src/app/admin/idp/[idpId]/layout.tsx b/src/app/admin/idp/[idpId]/layout.tsx index 06082a80..aab5e26d 100644 --- a/src/app/admin/idp/[idpId]/layout.tsx +++ b/src/app/admin/idp/[idpId]/layout.tsx @@ -15,7 +15,7 @@ import { BreadcrumbPage, BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; interface SettingsLayoutProps { children: React.ReactNode; @@ -37,7 +37,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { redirect("/admin/idp"); } - const t = useTranslations(); + const t = await getTranslations(); const navItems: HorizontalTabs = [ { diff --git a/src/app/admin/idp/create/page.tsx b/src/app/admin/idp/create/page.tsx index f7f6350e..fd19fb41 100644 --- a/src/app/admin/idp/create/page.tsx +++ b/src/app/admin/idp/create/page.tsx @@ -371,7 +371,7 @@ export default function Page() { {/*TODO(vlalx): Validate replacing */} - {t('idpJmespathAboutDescription')} + {t('idpJmespathAboutDescription')}{" "} ( - "idpOidcConfigureScopes": "Scopes" - {t('')} + {t('idpOidcConfigureScopes')} diff --git a/src/app/admin/idp/page.tsx b/src/app/admin/idp/page.tsx index 80bb6146..3f40e960 100644 --- a/src/app/admin/idp/page.tsx +++ b/src/app/admin/idp/page.tsx @@ -3,7 +3,7 @@ import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import IdpTable, { IdpRow } from "./AdminIdpTable"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export default async function IdpPage() { let idps: IdpRow[] = []; @@ -16,7 +16,8 @@ export default async function IdpPage() { } catch (e) { console.error(e); } - const t = useTranslations(); + + const t = await getTranslations(); return ( <> diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 41e58dc7..793c5f31 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -7,7 +7,6 @@ import UsersTable, { GlobalUserRow } from "./AdminUsersTable"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon } from "lucide-react"; import { getTranslations } from 'next-intl/server'; -import { useTranslations } from "next-intl"; type PageProps = { params: Promise<{ orgId: string }>; @@ -26,7 +25,8 @@ export default async function UsersPage(props: PageProps) { } catch (e) { console.error(e); } - const t = useTranslations(); + + const t = await getTranslations(); const userRows: GlobalUserRow[] = rows.map((row) => { return { diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx index edce345b..0079ebe5 100644 --- a/src/app/auth/layout.tsx +++ b/src/app/auth/layout.tsx @@ -8,7 +8,7 @@ import { AxiosResponse } from "axios"; import { ExternalLink } from "lucide-react"; import { Metadata } from "next"; import { cache } from "react"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export const metadata: Metadata = { title: `Auth - Pangolin`, @@ -22,7 +22,7 @@ type AuthLayoutProps = { export default async function AuthLayout({ children }: AuthLayoutProps) { const getUser = cache(verifySession); const user = await getUser(); - const t = useTranslations(); + const t = await getTranslations(); const licenseStatusRes = await cache( async () => diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 179180a7..73935870 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -9,7 +9,7 @@ import { cleanRedirect } from "@app/lib/cleanRedirect"; import db from "@server/db"; import { idp } from "@server/db/schemas"; import { LoginFormIDP } from "@app/components/LoginForm"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export const dynamic = "force-dynamic"; @@ -41,7 +41,7 @@ export default async function Page(props: { name: idp.name })) as LoginFormIDP[]; - const t = useTranslations(); + const t = await getTranslations(); return ( <> diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index a1cd6103..058ccc2b 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,8 +1,9 @@ -import Link from "next/link"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export default async function NotFound() { - const t = useTranslations(); + + const t = await getTranslations(); + return (

404

From d9ee40c8986557b4e49ed32b35fca9021f4dd70e Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sat, 17 May 2025 20:04:56 +0000 Subject: [PATCH 070/105] more fixes --- messages/de-DE.json | 6 +-- messages/en-US.json | 11 +++++- messages/fr-FR.json | 6 +-- messages/it-IT.json | 6 +-- messages/pl-PL.json | 6 +-- messages/pt-PT.json | 6 +-- messages/tr-TR.json | 4 +- .../access/AccessPageHeaderAndNav.tsx | 1 + .../invitations/RegenerateInvitationForm.tsx | 2 +- .../settings/access/invitations/page.tsx | 3 +- .../settings/access/users/UsersTable.tsx | 4 +- .../[orgId]/settings/api-keys/create/page.tsx | 5 +-- src/app/[orgId]/settings/general/page.tsx | 3 +- .../[resourceId]/CustomDomainInput.tsx | 5 +-- .../SetResourcePasswordForm.tsx | 3 +- .../[resourceId]/authentication/page.tsx | 6 +-- .../resources/[resourceId]/proxy/page.tsx | 3 ++ .../resources/[resourceId]/rules/page.tsx | 11 +++--- .../settings/resources/create/page.tsx | 10 ++--- .../share-links/CreateShareLinkForm.tsx | 8 ++-- src/app/admin/api-keys/ApiKeysDataTable.tsx | 1 + .../api-keys/[apiKeyId]/permissions/page.tsx | 8 ++-- src/app/admin/api-keys/create/page.tsx | 3 +- src/app/admin/api-keys/page.tsx | 5 ++- src/app/admin/idp/[idpId]/general/page.tsx | 7 ++-- src/app/admin/idp/[idpId]/layout.tsx | 3 +- .../idp/[idpId]/policies/PolicyDataTable.tsx | 2 + src/app/admin/idp/[idpId]/policies/page.tsx | 5 +-- src/app/admin/idp/create/page.tsx | 4 +- src/app/admin/layout.tsx | 1 - .../components/SitePriceCalculator.tsx | 2 +- src/app/admin/license/page.tsx | 38 +++++++++---------- .../auth/idp/[idpId]/oidc/callback/page.tsx | 5 +-- src/app/auth/layout.tsx | 2 +- src/app/auth/login/page.tsx | 2 +- .../auth/reset-password/ResetPasswordForm.tsx | 10 ++--- src/app/auth/reset-password/page.tsx | 5 +-- .../resource/[resourceId]/AccessToken.tsx | 2 +- .../[resourceId]/ResourceAuthPortal.tsx | 3 +- .../[resourceId]/ResourceNotFound.tsx | 1 + src/app/auth/signup/SignupForm.tsx | 2 +- src/app/auth/signup/page.tsx | 5 +-- src/app/auth/verify-email/VerifyEmailForm.tsx | 17 ++++----- src/app/components/SupporterMessage.tsx | 1 + src/app/invite/page.tsx | 7 +--- src/app/navigation.tsx | 1 - src/app/setup/layout.tsx | 1 - src/app/setup/page.tsx | 5 +-- 48 files changed, 122 insertions(+), 135 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index c94c7786..013c790f 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "Beim Entfernen des Benutzers ist ein Fehler aufgetreten.", "userOrgRemoved": "Benutzer entfernt", "userOrgRemovedDescription": "Der Benutzer {email} wurde aus der Organisation entfernt.", - "userQuestionOrgRemove": "Sind Sie sicher, dass Sie {email} aus der Organisation entfernen möchten?", + "userQuestionOrgRemove": "Sind Sie sicher, dass Sie {email} aus der Organisation entfernen möchten?", "userMessageOrgRemove": "Nach dem Entfernen hat dieser Benutzer keinen Zugriff mehr auf die Organisation. Sie können ihn später jederzeit wieder einladen, aber er muss die Einladung erneut annehmen.", "userMessageOrgConfirm": "Geben Sie zur Bestätigung den Namen des Benutzers unten ein.", "userRemoveOrgConfirm": "Entfernen des Benutzers bestätigen", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Fehler beim Entfernen der Rolle", "accessRoleErrorRemoveDescription": "Beim Entfernen der Rolle ist ein Fehler aufgetreten.", "accessRoleName": "Rollenname", - "accessRoleQuestionRemove": "Sie sind dabei, die Rolle {name} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "accessRoleQuestionRemove": "Sie sind dabei, die Rolle {name} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", "accessRoleRemove": "Rolle entfernen", "accessRoleRemoveDescription": "Eine Rolle aus der Organisation entfernen", "accessRoleRemoveSubmit": "Rolle entfernen", @@ -709,7 +709,7 @@ "idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten", "idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Sind Sie sicher, dass Sie den Identitätsanbieter {name} dauerhaft löschen möchten?", + "idpQuestionRemove": "Sind Sie sicher, dass Sie den Identitätsanbieter {name} dauerhaft löschen möchten?", "idpMessageRemove": "Dies wird den Identitätsanbieter und alle zugehörigen Konfigurationen entfernen. Benutzer, die sich über diesen Anbieter authentifizieren, können sich nicht mehr anmelden.", "idpMessageConfirm": "Bitte geben Sie zur Bestätigung den Namen des Identitätsanbieters unten ein.", "idpConfirmDelete": "Löschen des Identitätsanbieters bestätigen", diff --git a/messages/en-US.json b/messages/en-US.json index 112a39db..ab3ba6bb 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -27,6 +27,7 @@ "createAnAccount": "Create an Account", "inviteNotAccepted": "Invite Not Accepted", "authCreateAccount": "Create an account to get started", + "authNoAccount": "Don't have an account?", "email": "Email", "password": "Password", "confirmPassword": "Confirm Password", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "The license key has been deleted.", "licenseErrorKeyActivate": "Failed to activate license key", "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", + "licenseAbout": "About Licensing", + "communityEdition": "Community Edition", + "licenseAboutDescription": "This is for business and enterprise users who are using Pangolin in a commercial environment. If you are using Pangolin for personal use, you can ignore this section.", "licenseKeyActivated": "License key activated", "licenseKeyActivatedDescription": "The license key has been successfully activated.", "licenseErrorKeyRecheck": "Failed to recheck license keys", @@ -350,6 +354,7 @@ "total": "Total", "licenseContinuePayment": "Continue to Payment", "pricingPage": "pricing page", + "pricingPortal": "See Purchase Portal", "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ", "invite": "Invitations", "inviteRegenerate": "Regenerate Invitation", @@ -385,7 +390,7 @@ "userErrorOrgRemoveDescription": "An error occurred while removing the user.", "userOrgRemoved": "User removed", "userOrgRemovedDescription": "The user {email} has been removed from the organization.", - "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", + "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", "userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.", "userMessageOrgConfirm": "To confirm, please type the name of the of the user below.", "userRemoveOrgConfirm": "Confirm Remove User", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Failed to save whitelist", "resourceErrorWhitelistSaveDescription": "An error occurred while saving the whitelist", "resourcePasswordSubmit": "Enable Password Protection", + "resourcePasswordProtection": "Password Protection {status}", "resourcePasswordRemove": "Resource password removed", "resourcePasswordRemoveDescription": "The resource password has been removed successfully", "resourcePasswordSetup": "Resource password set", @@ -707,7 +713,7 @@ "idpManageDescription": "View and manage identity providers in the system", "idpDeletedDescription": "Identity provider deleted successfully", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", "idpConfirmDelete": "Confirm Delete Identity Provider", @@ -792,6 +798,7 @@ "orgMappingPathOptional": "Organization Mapping Path (Optional)", "orgPolicyUpdate": "Update Policy", "orgPolicyAdd": "Add Policy", + "orgPolicyConfig": "Configure access for an organization", "idpUpdatedDescription": "Identity provider updated successfully", "redirectUrl": "Redirect URL", "redirectUrlAbout": "About Redirect URL", diff --git a/messages/fr-FR.json b/messages/fr-FR.json index fe5faedc..5d964622 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "Une erreur s'est produite lors de la suppression de l'utilisateur.", "userOrgRemoved": "Utilisateur supprimé", "userOrgRemovedDescription": "L'utilisateur {email} a été retiré de l'organisation.", - "userQuestionOrgRemove": "Êtes-vous sûr de vouloir retirer {email} de l'organisation ?", + "userQuestionOrgRemove": "Êtes-vous sûr de vouloir retirer {email} de l'organisation ?", "userMessageOrgRemove": "Une fois retiré, cet utilisateur n'aura plus accès à l'organisation. Vous pouvez toujours le réinviter plus tard, mais il devra accepter l'invitation à nouveau.", "userMessageOrgConfirm": "Pour confirmer, veuillez saisir le nom de l'utilisateur ci-dessous.", "userRemoveOrgConfirm": "Confirmer la suppression de l'utilisateur", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Échec de la suppression du rôle", "accessRoleErrorRemoveDescription": "Une erreur s'est produite lors de la suppression du rôle.", "accessRoleName": "Nom du rôle", - "accessRoleQuestionRemove": "Vous êtes sur le point de supprimer le rôle {name}. Cette action est irréversible.", + "accessRoleQuestionRemove": "Vous êtes sur le point de supprimer le rôle {name}. Cette action est irréversible.", "accessRoleRemove": "Supprimer le rôle", "accessRoleRemoveDescription": "Retirer un rôle de l'organisation", "accessRoleRemoveSubmit": "Supprimer le rôle", @@ -709,7 +709,7 @@ "idpManageDescription": "Voir et gérer les fournisseurs d'identité dans le système", "idpDeletedDescription": "Fournisseur d'identité supprimé avec succès", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement le fournisseur d'identité {name} ?", + "idpQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement le fournisseur d'identité {name}?", "idpMessageRemove": "Cela supprimera le fournisseur d'identité et toutes les configurations associées. Les utilisateurs qui s'authentifient via ce fournisseur ne pourront plus se connecter.", "idpMessageConfirm": "Pour confirmer, veuillez saisir le nom du fournisseur d'identité ci-dessous.", "idpConfirmDelete": "Confirmer la suppression du fournisseur d'identité", diff --git a/messages/it-IT.json b/messages/it-IT.json index c72f08bc..e42735cf 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "Si è verificato un errore durante la rimozione dell'utente.", "userOrgRemoved": "Utente rimosso", "userOrgRemovedDescription": "L'utente {email} è stato rimosso dall'organizzazione.", - "userQuestionOrgRemove": "Sei sicuro di voler rimuovere {email} dall'organizzazione?", + "userQuestionOrgRemove": "Sei sicuro di voler rimuovere {email} dall'organizzazione?", "userMessageOrgRemove": "Una volta rimosso, questo utente non avrà più accesso all'organizzazione. Puoi sempre reinvitarlo in seguito, ma dovrà accettare nuovamente l'invito.", "userMessageOrgConfirm": "Per confermare, digita il nome dell'utente qui sotto.", "userRemoveOrgConfirm": "Conferma Rimozione Utente", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Impossibile rimuovere il ruolo", "accessRoleErrorRemoveDescription": "Si è verificato un errore durante la rimozione del ruolo.", "accessRoleName": "Nome Del Ruolo", - "accessRoleQuestionRemove": "Stai per eliminare il ruolo {name}. Non puoi annullare questa azione.", + "accessRoleQuestionRemove": "Stai per eliminare il ruolo {name}. Non puoi annullare questa azione.", "accessRoleRemove": "Rimuovi Ruolo", "accessRoleRemoveDescription": "Rimuovi un ruolo dall'organizzazione", "accessRoleRemoveSubmit": "Rimuovi Ruolo", @@ -709,7 +709,7 @@ "idpManageDescription": "Visualizza e gestisci i provider di identità nel sistema", "idpDeletedDescription": "Provider di identità eliminato con successo", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Sei sicuro di voler eliminare definitivamente il provider di identità {name}?", + "idpQuestionRemove": "Sei sicuro di voler eliminare definitivamente il provider di identità {name}?", "idpMessageRemove": "Questo rimuoverà il provider di identità e tutte le configurazioni associate. Gli utenti che si autenticano tramite questo provider non potranno più accedere.", "idpMessageConfirm": "Per confermare, digita il nome del provider di identità qui sotto.", "idpConfirmDelete": "Conferma Eliminazione Provider di Identità", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index d11a743b..acc1f3d8 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "Wystąpił błąd podczas usuwania użytkownika.", "userOrgRemoved": "Użytkownik usunięty", "userOrgRemovedDescription": "Użytkownik {email} został usunięty z organizacji.", - "userQuestionOrgRemove": "Czy na pewno chcesz usunąć {email} z organizacji?", + "userQuestionOrgRemove": "Czy na pewno chcesz usunąć {email} z organizacji?", "userMessageOrgRemove": "Po usunięciu ten użytkownik nie będzie miał już dostępu do organizacji. Zawsze możesz ponownie go zaprosić później, ale będzie musiał ponownie zaakceptować zaproszenie.", "userMessageOrgConfirm": "Aby potwierdzić, wpisz nazwę użytkownika poniżej.", "userRemoveOrgConfirm": "Potwierdź usunięcie użytkownika", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Nie udało się usunąć roli", "accessRoleErrorRemoveDescription": "Wystąpił błąd podczas usuwania roli.", "accessRoleName": "Nazwa roli", - "accessRoleQuestionRemove": "Zamierzasz usunąć rolę {name}. Tej akcji nie można cofnąć.", + "accessRoleQuestionRemove": "Zamierzasz usunąć rolę {name}. Tej akcji nie można cofnąć.", "accessRoleRemove": "Usuń rolę", "accessRoleRemoveDescription": "Usuń rolę z organizacji", "accessRoleRemoveSubmit": "Usuń rolę", @@ -709,7 +709,7 @@ "idpManageDescription": "Wyświetl i zarządzaj dostawcami tożsamości w systemie", "idpDeletedDescription": "Dostawca tożsamości został pomyślnie usunięty", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Czy na pewno chcesz trwale usunąć dostawcę tożsamości {name}?", + "idpQuestionRemove": "Czy na pewno chcesz trwale usunąć dostawcę tożsamości {name}?", "idpMessageRemove": "Spowoduje to usunięcie dostawcy tożsamości i wszystkich powiązanych konfiguracji. Użytkownicy uwierzytelniający się przez tego dostawcę nie będą mogli się już zalogować.", "idpMessageConfirm": "Aby potwierdzić, wpisz nazwę dostawcy tożsamości poniżej.", "idpConfirmDelete": "Potwierdź usunięcie dostawcy tożsamości", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 75b0d8ef..9b02e1b8 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", "userOrgRemoved": "Usuário removido", "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", - "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", + "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", @@ -684,7 +684,7 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", @@ -709,7 +709,7 @@ "idpManageDescription": "Visualizar e gerir provedores de identidade no sistema", "idpDeletedDescription": "Provedor de identidade eliminado com sucesso", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Tem certeza que deseja eliminar permanentemente o provedor de identidade {name}?", + "idpQuestionRemove": "Tem certeza que deseja eliminar permanentemente o provedor de identidade {name}?", "idpMessageRemove": "Isto irá remover o provedor de identidade e todas as configurações associadas. Os utilizadores que se autenticam através deste provedor não poderão mais fazer login.", "idpMessageConfirm": "Para confirmar, por favor digite o nome do provedor de identidade abaixo.", "idpConfirmDelete": "Confirmar Eliminação do Provedor de Identidade", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 3209478c..73b10add 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -385,7 +385,7 @@ "userErrorOrgRemoveDescription": "An error occurred while removing the user.", "userOrgRemoved": "User removed", "userOrgRemovedDescription": "The user {email} has been removed from the organization.", - "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", + "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", "userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.", "userMessageOrgConfirm": "To confirm, please type the name of the of the user below.", "userRemoveOrgConfirm": "Confirm Remove User", @@ -709,7 +709,7 @@ "idpManageDescription": "View and manage identity providers in the system", "idpDeletedDescription": "Identity provider deleted successfully", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", "idpConfirmDelete": "Confirm Delete Identity Provider", diff --git a/src/app/[orgId]/settings/access/AccessPageHeaderAndNav.tsx b/src/app/[orgId]/settings/access/AccessPageHeaderAndNav.tsx index 2dcdb679..47690dc6 100644 --- a/src/app/[orgId]/settings/access/AccessPageHeaderAndNav.tsx +++ b/src/app/[orgId]/settings/access/AccessPageHeaderAndNav.tsx @@ -14,6 +14,7 @@ export default function AccessPageHeaderAndNav({ hasInvitations }: AccessPageHeaderAndNavProps) { const t = useTranslations(); + const navItems = [ { title: t('users'), diff --git a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx index f151da9d..59c1b1b4 100644 --- a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx +++ b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx @@ -123,7 +123,7 @@ export default function RegenerateInvitationForm({ onRegenerate({ id: invitation.id, email: invitation.email, - expiresAt: res.data.data.expiresAt ?? "", + expiresAt: res.data.data.expiresAt, role: invitation.role, roleId: invitation.roleId }); diff --git a/src/app/[orgId]/settings/access/invitations/page.tsx b/src/app/[orgId]/settings/access/invitations/page.tsx index 044b0be6..665b9a43 100644 --- a/src/app/[orgId]/settings/access/invitations/page.tsx +++ b/src/app/[orgId]/settings/access/invitations/page.tsx @@ -19,6 +19,7 @@ export const dynamic = "force-dynamic"; export default async function InvitationsPage(props: InvitationsPageProps) { const params = await props.params; + const t = await getTranslations(); const getUser = cache(verifySession); const user = await getUser(); @@ -72,8 +73,6 @@ export default async function InvitationsPage(props: InvitationsPageProps) { }; }); - const t = await getTranslations(); - return ( <> @@ -244,7 +244,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { dialog={

- {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username || ''})} + {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})}

diff --git a/src/app/[orgId]/settings/api-keys/create/page.tsx b/src/app/[orgId]/settings/api-keys/create/page.tsx index dcf2c027..809784e4 100644 --- a/src/app/[orgId]/settings/api-keys/create/page.tsx +++ b/src/app/[orgId]/settings/api-keys/create/page.tsx @@ -92,7 +92,8 @@ export default function Page() { const api = createApiClient({ env }); const { orgId } = useParams(); const router = useRouter(); - + const t = useTranslations(); + const [loadingPage, setLoadingPage] = useState(true); const [createLoading, setCreateLoading] = useState(false); const [apiKey, setApiKey] = useState(null); @@ -114,8 +115,6 @@ export default function Page() { } }); - const t = useTranslations(); - async function onSubmit(data: CreateFormValues) { setCreateLoading(true); diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 967cc21a..463c9463 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -60,6 +60,7 @@ export default function GeneralPage() { const { org } = useOrgContext(); const api = createApiClient(useEnvContext()); const { user } = useUserContext(); + const t = useTranslations(); const [loadingDelete, setLoadingDelete] = useState(false); const [loadingSave, setLoadingSave] = useState(false); @@ -151,8 +152,6 @@ export default function GeneralPage() { }); } - const t = useTranslations(); - return ( void; } -const t = useTranslations(); - export default function CustomDomainInput({ domainOptions, selectedDomainId, - placeholder = t('subdomain'), + placeholder = "Subdomain", value: defaultValue, onChange }: CustomDomainInputProps) { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx index a324b755..3158c9e4 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx @@ -57,6 +57,7 @@ export default function SetResourcePasswordForm({ onSetPassword }: SetPasswordFormProps) { const api = createApiClient(useEnvContext()); + const t = useTranslations(); const [loading, setLoading] = useState(false); @@ -65,8 +66,6 @@ export default function SetResourcePasswordForm({ defaultValues }); - const t = useTranslations(); - useEffect(() => { if (!open) { return; diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx index f8dcb615..6182c04a 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx @@ -83,6 +83,7 @@ export default function ResourceAuthenticationPage() { const api = createApiClient({ env }); const router = useRouter(); + const t = useTranslations(); const [pageLoading, setPageLoading] = useState(true); @@ -130,8 +131,6 @@ export default function ResourceAuthenticationPage() { defaultValues: { emails: [] } }); - const t = useTranslations(); - useEffect(() => { const fetchData = async () => { try { @@ -565,8 +564,7 @@ export default function ResourceAuthenticationPage() { > - Password Protection{" "} - {authInfo.password ? t('enabled') : t('disabled')} + {t('resourcePasswordProtection', {status: authInfo.password? t('enabled') : t('disabled')})}

diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index 2b72bfd8..67d2912e 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -130,10 +130,10 @@ export default function LicensePage() { } } catch (e) { toast({ - title: "Failed to load license keys", + title: t('licenseErrorKeyLoad'), description: formatAxiosError( e, - "An error occurred loading license keys." + t('licenseErrorKeyLoadDescription') ) }); } @@ -149,16 +149,16 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: "License key deleted", - description: "The license key has been deleted." + title: t('licenseKeyDeleted'), + description: t('licenseKeyDeletedDescription') }); setIsDeleteModalOpen(false); } catch (e) { toast({ - title: "Failed to delete license key", + title: t('licenseErrorKeyDelete'), description: formatAxiosError( e, - "An error occurred deleting license key." + t('licenseErrorKeyDeleteDescription') ) }); } finally { @@ -175,15 +175,15 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: "License keys rechecked", - description: "All license keys have been rechecked" + title: t('licenseErrorKeyRechecked'), + description: t('licenseErrorKeyRecheckedDescription') }); } catch (e) { toast({ - title: "Failed to recheck license keys", + title: t('licenseErrorKeyRecheck'), description: formatAxiosError( e, - "An error occurred rechecking license keys." + t('licenseErrorKeyRecheckDescription') ) }); } finally { @@ -202,8 +202,8 @@ export default function LicensePage() { } toast({ - title: "License key activated", - description: "The license key has been successfully activated." + title: t('licenseKeyActivated'), + description: t('licenseKeyActivatedDescription') }); setIsCreateModalOpen(false); @@ -212,10 +212,10 @@ export default function LicensePage() { } catch (e) { toast({ variant: "destructive", - title: "Failed to activate license key", + title: t('licenseErrorKeyActivate'), description: formatAxiosError( e, - "An error occurred while activating the license key." + t('licenseErrorKeyActivateDescription') ) }); } finally { @@ -360,12 +360,10 @@ export default function LicensePage() { - About Licensing + {t('licenseAbout')} - This is for business and enterprise users who are using - Pangolin in a commercial environment. If you are using - Pangolin for personal use, you can ignore this section. + {t('licenseAboutDescription')} @@ -397,12 +395,12 @@ export default function LicensePage() {
{supporterStatus?.visible ? (
- Community Edition + {t('communityEdition')}
) : (
- Community Edition + {t('communityEdition')}
)}
diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 36fabbc4..2bbea496 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -3,7 +3,7 @@ import ValidateOidcToken from "./ValidateOidcToken"; import { idp } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export default async function Page(props: { params: Promise<{ orgId: string; idpId: string }>; @@ -14,6 +14,7 @@ export default async function Page(props: { }) { const params = await props.params; const searchParams = await props.searchParams; + const t = await getTranslations(); const allCookies = await cookies(); const stateCookie = allCookies.get("p_oidc_state")?.value; @@ -24,8 +25,6 @@ export default async function Page(props: { .from(idp) .where(eq(idp.idpId, parseInt(params.idpId!))); - const t = useTranslations(); - if (!idpRes) { return
{t('idpErrorNotFound')}
; } diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx index 0079ebe5..3017030b 100644 --- a/src/app/auth/layout.tsx +++ b/src/app/auth/layout.tsx @@ -73,7 +73,7 @@ export default async function AuthLayout({ children }: AuthLayoutProps) { aria-label="GitHub" className="flex items-center space-x-2 whitespace-nowrap" > - Community Edition + {t('communityEdition')} - Don't have an account?{" "} + {t('authNoAccount')}{" "} { - setError(formatAxiosError(e, "An error occurred")); - console.error("Failed to request reset:", e); + setError(formatAxiosError(e, t('errorOccurred'))); + console.error(t('passwordErrorRequestReset'), e); setIsSubmitting(false); }); @@ -168,8 +168,8 @@ export default function ResetPasswordForm({ } as ResetPasswordBody ) .catch((e) => { - setError(formatAxiosError(e, "An error occurred")); - console.error("Failed to reset password:", e); + setError(formatAxiosError(e, t('errorOccurred'))); + console.error(t('passwordErrorReset'), e); setIsSubmitting(false); }); @@ -185,7 +185,7 @@ export default function ResetPasswordForm({ return; } - setSuccessMessage("Password reset successfully! Back to log in..."); + setSuccessMessage(t('passwordResetSuccess')); setTimeout(() => { if (redirect) { diff --git a/src/app/auth/reset-password/page.tsx b/src/app/auth/reset-password/page.tsx index 3423bbb5..f948c34b 100644 --- a/src/app/auth/reset-password/page.tsx +++ b/src/app/auth/reset-password/page.tsx @@ -4,7 +4,7 @@ import { cache } from "react"; import ResetPasswordForm from "./ResetPasswordForm"; import Link from "next/link"; import { cleanRedirect } from "@app/lib/cleanRedirect"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export const dynamic = "force-dynamic"; @@ -18,6 +18,7 @@ export default async function Page(props: { const searchParams = await props.searchParams; const getUser = cache(verifySession); const user = await getUser(); + const t = await getTranslations(); if (user) { redirect("/"); @@ -28,8 +29,6 @@ export default async function Page(props: { redirectUrl = cleanRedirect(searchParams.redirect); } - const t = useTranslations(); - return ( <> { let colLength = 0; @@ -171,8 +172,6 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { return fullUrl.toString(); } - const t = useTranslations(); - const onWhitelistSubmit = (values: any) => { setLoadingLogin(true); api.post>( diff --git a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx index 5a2e863e..11c7b817 100644 --- a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx +++ b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx @@ -10,6 +10,7 @@ import Link from "next/link"; import { useTranslations } from "next-intl"; export default async function ResourceNotFound() { + const t = useTranslations(); return ( diff --git a/src/app/auth/signup/SignupForm.tsx b/src/app/auth/signup/SignupForm.tsx index ec321f38..d3639a0e 100644 --- a/src/app/auth/signup/SignupForm.tsx +++ b/src/app/auth/signup/SignupForm.tsx @@ -87,7 +87,7 @@ export default function SignupForm({ .catch((e) => { console.error(e); setError( - formatAxiosError(e, "An error occurred while signing up") + formatAxiosError(e, t('signupError')) ); }); diff --git a/src/app/auth/signup/page.tsx b/src/app/auth/signup/page.tsx index 315b314a..242bf10b 100644 --- a/src/app/auth/signup/page.tsx +++ b/src/app/auth/signup/page.tsx @@ -6,7 +6,7 @@ import { Mail } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { cache } from "react"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; export const dynamic = "force-dynamic"; @@ -16,13 +16,12 @@ export default async function Page(props: { const searchParams = await props.searchParams; const getUser = cache(verifySession); const user = await getUser(); + const t = await getTranslations(); const env = pullEnv(); const isInvite = searchParams?.redirect?.includes("/invite"); - const t = useTranslations(); - if (env.flags.disableSignupWithoutInvite && !isInvite) { redirect("/"); } diff --git a/src/app/auth/verify-email/VerifyEmailForm.tsx b/src/app/auth/verify-email/VerifyEmailForm.tsx index 235fc70c..05257c1d 100644 --- a/src/app/auth/verify-email/VerifyEmailForm.tsx +++ b/src/app/auth/verify-email/VerifyEmailForm.tsx @@ -56,6 +56,7 @@ export default function VerifyEmailForm({ redirect, }: VerifyEmailFormProps) { const router = useRouter(); + const t = useTranslations(); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); @@ -72,8 +73,6 @@ export default function VerifyEmailForm({ }, }); - const t = useTranslations(); - async function onSubmit(data: z.infer) { setIsSubmitting(true); @@ -82,15 +81,15 @@ export default function VerifyEmailForm({ code: data.pin, }) .catch((e) => { - setError(formatAxiosError(e, "An error occurred")); - console.error("Failed to verify email:", e); + setError(formatAxiosError(e, t('errorOccurred'))); + console.error(t('emailErrorVerify'), e); setIsSubmitting(false); }); if (res && res.data?.data?.valid) { setError(null); setSuccessMessage( - "Email successfully verified! Redirecting you..." + t('emailVerified') ); setTimeout(() => { if (redirect) { @@ -108,16 +107,16 @@ export default function VerifyEmailForm({ setIsResending(true); const res = await api.post("/auth/verify-email/request").catch((e) => { - setError(formatAxiosError(e, "An error occurred")); - console.error("Failed to resend verification code:", e); + setError(formatAxiosError(e, t('errorOccurred'))); + console.error(t('verificationCodeErrorResend'), e); }); if (res) { setError(null); toast({ variant: "default", - title: "Verification code resent", - description: "We've resent a verification code to your email address. Please check your inbox.", + title: t('verificationCodeResend'), + description: t('verificationCodeResendDescription'), }); } diff --git a/src/app/components/SupporterMessage.tsx b/src/app/components/SupporterMessage.tsx index d9869f7b..2f415e14 100644 --- a/src/app/components/SupporterMessage.tsx +++ b/src/app/components/SupporterMessage.tsx @@ -6,6 +6,7 @@ import { Star } from "lucide-react"; import { useTranslations } from 'next-intl'; export default function SupporterMessage({ tier }: { tier: string }) { + const t = useTranslations(); return ( diff --git a/src/app/invite/page.tsx b/src/app/invite/page.tsx index 5ba30dd8..bf423a75 100644 --- a/src/app/invite/page.tsx +++ b/src/app/invite/page.tsx @@ -6,9 +6,7 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import InviteStatusCard from "./InviteStatusCard"; import { formatAxiosError } from "@app/lib/api"; -import { useTranslations } from "next-intl"; - -; +import { getTranslations } from 'next-intl/server'; export default async function InvitePage(props: { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; @@ -22,8 +20,7 @@ export default async function InvitePage(props: { } const user = await verifySession(); - - const t = useTranslations(); + const t = await getTranslations(); const parts = tokenParam.split("-"); if (parts.length !== 2) { diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 7ee9ec75..8ea3c080 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -10,7 +10,6 @@ import { KeyRound, TicketCheck } from "lucide-react"; -import { useTranslations } from "next-intl"; export const orgLangingNavItems: SidebarNavItem[] = [ { diff --git a/src/app/setup/layout.tsx b/src/app/setup/layout.tsx index 6b1b80e3..e254037d 100644 --- a/src/app/setup/layout.tsx +++ b/src/app/setup/layout.tsx @@ -11,7 +11,6 @@ import { ListUserOrgsResponse } from "@server/routers/org"; import { internal } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; -import { useTranslations } from "next-intl"; export const metadata: Metadata = { title: `Setup - Pangolin`, diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index a6e63f58..293dc24a 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -45,6 +45,7 @@ const orgSchema = z.object({ export default function StepperForm() { const [currentStep, setCurrentStep] = useState("org"); const [orgIdTaken, setOrgIdTaken] = useState(false); + const t = useTranslations(); const [loading, setLoading] = useState(false); const [isChecked, setIsChecked] = useState(false); @@ -106,15 +107,13 @@ export default function StepperForm() { } catch (e) { console.error(e); setError( - formatAxiosError(e, "An error occurred while creating org") + formatAxiosError(e, t('orgErrorCreate')) ); } setLoading(false); } - const t = useTranslations(); - return ( <> From 547e777eb00b41b7691c2d4f89c7cf004b624bbf Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sat, 17 May 2025 20:16:26 +0000 Subject: [PATCH 071/105] more fixes --- messages/en-US.json | 11 ++++++----- .../[orgId]/settings/access/users/[userId]/layout.tsx | 4 ++-- src/app/admin/license/page.tsx | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index ab3ba6bb..6966b6f5 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -702,6 +702,7 @@ "pangolinServerAdmin": "Server Admin - Pangolin", "licenseTierProfessional": "Professional License", "licenseTierEnterprise": "Enterprise License", + "licenseTierCommercial": "Commercial License", "licensed": "Licensed", "yes": "Yes", "no": "No", @@ -757,11 +758,11 @@ "idpJmespathAboutDescription": "The paths below use JMESPath syntax to extract values from the ID token.", "idpJmespathAboutDescriptionLink": "Learn more about JMESPath", "idpJmespathLabel": "Identifier Path", - "idpJmespathLabelDescription": "The JMESPath to the user identifier in the ID token", + "idpJmespathLabelDescription": "The path to the user identifier in the ID token", "idpJmespathEmailPathOptional": "Email Path (Optional)", - "idpJmespathEmailPathOptionalDescription": "The JMESPath to the user's email in the ID token", + "idpJmespathEmailPathOptionalDescription": "The path to the user's email in the ID token", "idpJmespathNamePathOptional": "Name Path (Optional)", - "idpJmespathNamePathOptionalDescription": "The JMESPath to the user's name in the ID token", + "idpJmespathNamePathOptionalDescription": "The path to the user's name in the ID token", "idpOidcConfigureScopes": "Scopes", "idpOidcConfigureScopesDescription": "Space-separated list of OAuth2 scopes to request", "idpSubmit": "Create Identity Provider", @@ -785,9 +786,9 @@ "defaultMappingsOptional": "Default Mappings (Optional)", "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", "defaultMappingsRole": "Default Role Mapping", - "defaultMappingsRoleDescription": "JMESPath to extract role information from the ID token. The result of this expression must return the role name as defined in the organization as a string.", + "defaultMappingsRoleDescription": "The result of this expression must return the role name as defined in the organization as a string.", "defaultMappingsOrg": "Default Organization Mapping", - "defaultMappingsOrgDescription": "JMESPath to extract organization information from the ID token. This expression must return the org ID or true for the user to be allowed to access the organization.", + "defaultMappingsOrgDescription": "This expression must return the org ID or true for the user to be allowed to access the organization.", "defaultMappingsSubmit": "Save Default Mappings", "orgPoliciesEdit": "Edit Organization Policy", "org": "Organization", diff --git a/src/app/[orgId]/settings/access/users/[userId]/layout.tsx b/src/app/[orgId]/settings/access/users/[userId]/layout.tsx index 44c1ee19..82fbba86 100644 --- a/src/app/[orgId]/settings/access/users/[userId]/layout.tsx +++ b/src/app/[orgId]/settings/access/users/[userId]/layout.tsx @@ -15,7 +15,7 @@ import { import Link from "next/link"; import { cache } from "react"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { useTranslations } from "next-intl"; +import { getTranslations } from 'next-intl/server'; interface UserLayoutProps { children: React.ReactNode; @@ -27,7 +27,7 @@ export default async function UserLayoutProps(props: UserLayoutProps) { const { children } = props; - const t = useTranslations(); + const t = await getTranslations(); let user = null; try { diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index 67d2912e..ac2c6d67 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -384,10 +384,10 @@ export default function LicensePage() { {licenseStatus?.tier === "PROFESSIONAL" - ? t('licenseTierProfessional') + ? t('licenseTierCommercial') : licenseStatus?.tier === "ENTERPRISE" - ? t('licenseTierEnterprise') + ? t('licenseTierCommercial') : t('licensed')}
From ae4ef4eb9992188d3dd308662c9d03ec47ce17ef Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Sat, 17 May 2025 20:34:01 +0000 Subject: [PATCH 072/105] modified: messages/en-US.json modified: src/components/tags/autocomplete.tsx --- messages/en-US.json | 5 +++-- src/components/tags/autocomplete.tsx | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 6966b6f5..48bf47ab 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -364,7 +364,7 @@ "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", "inviteRemoved": "Invitation removed", "inviteRemovedDescription": "The invitation for {email} has been removed.", - "inviteQuestionRemove ": "Are you sure you want to remove the invitation {email}?", + "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", @@ -906,5 +906,6 @@ "settings": "Settings", "usersAll": "All Users", "license": "License", - "pangolinDashboard": "Dashboard - Pangolin" + "pangolinDashboard": "Dashboard - Pangolin", + "noResults": "No results found." } diff --git a/src/components/tags/autocomplete.tsx b/src/components/tags/autocomplete.tsx index f2867791..32e6f42d 100644 --- a/src/components/tags/autocomplete.tsx +++ b/src/components/tags/autocomplete.tsx @@ -4,6 +4,7 @@ import { TagInputStyleClassesProps, type Tag as TagType } from "./tag-input"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import { Button } from "../ui/button"; import { cn } from "@app/lib/cn"; +import { useTranslations } from 'next-intl'; type AutocompleteProps = { tags: TagType[]; @@ -40,6 +41,7 @@ export const Autocomplete: React.FC = ({ const triggerRef = useRef(null); const inputRef = useRef(null); const popoverContentRef = useRef(null); + const t = useTranslations(); const [popoverWidth, setPopoverWidth] = useState(0); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -342,7 +344,7 @@ export const Autocomplete: React.FC = ({
) : (
- No results found. + {t('noResults')}
)}
From af3694da342b87f819dff75d63c1b5852d96e3f1 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sat, 17 May 2025 22:39:07 +0200 Subject: [PATCH 073/105] New Crowdin updates (#26) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Turkish) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Turkish) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) --- messages/de-DE.json | 43 ++++++++++++++++++------------------------- messages/fr-FR.json | 33 +++++++++++++-------------------- messages/it-IT.json | 37 +++++++++++++++---------------------- messages/pl-PL.json | 35 ++++++++++++++--------------------- messages/pt-PT.json | 37 +++++++++++++++---------------------- messages/tr-TR.json | 33 +++++++++++++-------------------- 6 files changed, 88 insertions(+), 130 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 013c790f..7f26ea92 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -27,6 +27,7 @@ "createAnAccount": "Konto erstellen", "inviteNotAccepted": "Einladung nicht angenommen", "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", + "authNoAccount": "Sie haben noch kein Konto?", "email": "E-Mail", "password": "Passwort", "confirmPassword": "Passwort bestätigen", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "Der Lizenzschlüssel wurde gelöscht.", "licenseErrorKeyActivate": "Fehler beim Aktivieren des Lizenzschlüssels", "licenseErrorKeyActivateDescription": "Beim Aktivieren des Lizenzschlüssels ist ein Fehler aufgetreten.", + "licenseAbout": "Über Lizenzierung", + "communityEdition": "Community-Edition", + "licenseAboutDescription": "Dies ist für Geschäfts- und Unternehmensanwender, die Pangolin in einem kommerziellen Umfeld einsetzen. Wenn Sie Pangolin für den persönlichen Gebrauch verwenden, können Sie diesen Abschnitt ignorieren.", "licenseKeyActivated": "Lizenzschlüssel aktiviert", "licenseKeyActivatedDescription": "Der Lizenzschlüssel wurde erfolgreich aktiviert.", "licenseErrorKeyRecheck": "Fehler beim Überprüfen der Lizenzschlüssel", @@ -350,6 +354,7 @@ "total": "Gesamt", "licenseContinuePayment": "Weiter zur Zahlung", "pricingPage": "Preisseite", + "pricingPortal": "Einkaufsportal ansehen", "licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die ", "invite": "Einladungen", "inviteRegenerate": "Einladung neu generieren", @@ -432,12 +437,12 @@ "inviteEmailSentDescription": "Eine E-Mail mit dem Zugangslink wurde an den Benutzer gesendet. Er muss den Link aufrufen, um die Einladung anzunehmen.", "inviteSentDescription": "Der Benutzer wurde eingeladen. Er muss den unten stehenden Link aufrufen, um die Einladung anzunehmen.", "inviteExpiresIn": "Die Einladung läuft in {days, plural, =1 {einem Tag} other {# Tagen}} ab.", - "idpTitle": "Identitätsanbieter", + "idpTitle": "Allgemeine Informationen", "idpSelect": "Wählen Sie den Identitätsanbieter für den externen Benutzer", "idpNotConfigured": "Es sind keine Identitätsanbieter konfiguriert. Bitte konfigurieren Sie einen Identitätsanbieter, bevor Sie externe Benutzer erstellen.", "usernameUniq": "Dies muss mit dem eindeutigen Benutzernamen übereinstimmen, der im ausgewählten Identitätsanbieter existiert.", "emailOptional": "E-Mail (Optional)", - "nameOptional": "Name (Optional)", + "nameOptional": "Name (optional)", "accessControls": "Zugriffskontrolle", "userDescription2": "Verwalten Sie die Einstellungen dieses Benutzers", "accessRoleErrorAdd": "Fehler beim Hinzufügen des Benutzers zur Rolle", @@ -573,8 +578,8 @@ "shareEasyCreate": "Einfach zu erstellen und zu teilen", "shareConfigurableExpirationDuration": "Konfigurierbare Ablaufzeit", "shareSecureAndRevocable": "Sicher und widerrufbar", - "nameMin": "Name muss mindestens {len} Zeichen lang sein.", - "nameMax": "Name darf nicht länger als {len} Zeichen sein.", + "nameMin": "Der Name muss mindestens {len} Zeichen lang sein.", + "nameMax": "Der Name darf nicht länger als {len} Zeichen sein.", "sitesConfirmCopy": "Bitte bestätigen Sie, dass Sie die Konfiguration kopiert haben.", "unknownCommand": "Unbekannter Befehl", "newtErrorFetchReleases": "Fehler beim Abrufen der Release-Informationen: {err}", @@ -583,7 +588,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Newt-Geheimschlüssel", "architecture": "Architektur", - "sites": "Sites", + "sites": "Standorte", "siteWgAnyClients": "Verwenden Sie einen beliebigen WireGuard-Client zur Verbindung. Sie müssen Ihre internen Ressourcen über die Peer-IP adressieren.", "siteWgCompatibleAllClients": "Kompatibel mit allen WireGuard-Clients", "siteWgManualConfigurationRequired": "Manuelle Konfiguration erforderlich", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Fehler beim Speichern der Whitelist", "resourceErrorWhitelistSaveDescription": "Beim Speichern der Whitelist ist ein Fehler aufgetreten", "resourcePasswordSubmit": "Passwortschutz aktivieren", + "resourcePasswordProtection": "Passwortschutz {status}", "resourcePasswordRemove": "Ressourcenpasswort entfernt", "resourcePasswordRemoveDescription": "Das Ressourcenpasswort wurde erfolgreich entfernt", "resourcePasswordSetup": "Ressourcenpasswort festgelegt", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.", "manage": "Verwalten", "sitesNotFound": "Keine Sites gefunden.", - "expiresAt": "Läuft ab am", "pangolinServerAdmin": "Server Admin - Pangolin", - "idpNameInternal": "Intern", "licenseTierProfessional": "Professional Lizenz", "licenseTierEnterprise": "Enterprise Lizenz", + "licenseTierCommercial": "Gewerbliche Lizenz", "licensed": "Lizenziert", "yes": "Ja", "no": "Nein", @@ -717,8 +722,6 @@ "idp": "Identitätsanbieter", "idpSearch": "Identitätsanbieter suchen...", "idpAdd": "Identitätsanbieter hinzufügen", - "nameMin": "Der Name muss mindestens {len} Zeichen lang sein.", - "nameMax": "Der Name darf nicht länger als {len} Zeichen sein.", "idpClientIdRequired": "Client-ID ist erforderlich.", "idpClientSecretRequired": "Client-Secret ist erforderlich.", "idpErrorAuthUrlInvalid": "Auth-URL muss eine gültige URL sein.", @@ -730,12 +733,11 @@ "idpCreate": "Identitätsanbieter erstellen", "idpCreateDescription": "Konfigurieren Sie einen neuen Identitätsanbieter für die Benutzerauthentifizierung", "idpSeeAll": "Alle Identitätsanbieter anzeigen", - "idpTitle": "Allgemeine Informationen", "idpSettingsDescription": "Konfigurieren Sie die grundlegenden Informationen für Ihren Identitätsanbieter", "idpDisplayName": "Ein Anzeigename für diesen Identitätsanbieter", "idpAutoProvisionUsers": "Automatische Benutzerbereitstellung", "idpAutoProvisionUsersDescription": "Wenn aktiviert, werden Benutzer beim ersten Login automatisch im System erstellt, mit der Möglichkeit, Benutzer Rollen und Organisationen zuzuordnen.", - "licenseBadge": "Professional", + "licenseBadge": "Profi", "idpType": "Anbietertyp", "idpTypeDescription": "Wählen Sie den Typ des Identitätsanbieters, den Sie konfigurieren möchten", "idpOidcConfigure": "OAuth2/OIDC Konfiguration", @@ -761,7 +763,7 @@ "idpJmespathEmailPathOptionalDescription": "Der JMESPath zur E-Mail-Adresse des Benutzers im ID-Token", "idpJmespathNamePathOptional": "Namenspfad (Optional)", "idpJmespathNamePathOptionalDescription": "Der JMESPath zum Namen des Benutzers im ID-Token", - "idpOidcConfigureScopes": "Scopes", + "idpOidcConfigureScopes": "Bereiche", "idpOidcConfigureScopesDescription": "Durch Leerzeichen getrennte Liste der anzufordernden OAuth2-Scopes", "idpSubmit": "Identitätsanbieter erstellen", "orgPolicies": "Organisationsrichtlinien", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Organisationszuordnungspfad (Optional)", "orgPolicyUpdate": "Richtlinie aktualisieren", "orgPolicyAdd": "Richtlinie hinzufügen", + "orgPolicyConfig": "Zugriff für eine Organisation konfigurieren", "idpUpdatedDescription": "Identitätsanbieter erfolgreich aktualisiert", "redirectUrl": "Weiterleitungs-URL", "redirectUrlAbout": "Über die Weiterleitungs-URL", "redirectUrlAboutDescription": "Dies ist die URL, zu der Benutzer nach der Authentifizierung weitergeleitet werden. Sie müssen diese URL in den Einstellungen Ihres Identitätsanbieters konfigurieren.", - "key": "Schlüssel", - "createdAt": "Erstellt am", - "expiresAt": "Läuft ab am", "pangolinAuth": "Auth - Pangolin", - "emailInvalid": "Ungültige E-Mail-Adresse", "verificationCodeLengthRequirements": "Ihr Verifizierungscode muss 8 Zeichen lang sein.", "errorOccurred": "Ein Fehler ist aufgetreten", "emailErrorVerify": "E-Mail konnte nicht verifiziert werden:", @@ -822,7 +821,7 @@ "emailVerifyResend": "Keinen Code erhalten? Hier klicken zum erneuten Senden", "passwordNotMatch": "Passwörter stimmen nicht überein", "signupError": "Beim Registrieren ist ein Fehler aufgetreten", - "pangolinLogoAlt": "Pangolin Logo", + "pangolinLogoAlt": "Pangolin-Logo", "inviteAlready": "Sieht aus, als wären Sie eingeladen worden!", "inviteAlreadyDescription": "Um die Einladung anzunehmen, müssen Sie sich einloggen oder ein Konto erstellen.", "signupQuestion": "Haben Sie bereits ein Konto?", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Verbunden", "idpErrorConnectingTo": "Es gab ein Problem bei der Verbindung zu {name}. Bitte kontaktieren Sie Ihren Administrator.", "idpErrorNotFound": "IdP nicht gefunden", - "expiresAt": "Läuft ab am", "inviteInvalid": "Ungültige Einladung", "inviteInvalidDescription": "Der Einladungslink ist ungültig.", "inviteErrorWrongUser": "Einladung ist nicht für diesen Benutzer", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Hoppla! Die gesuchte Seite existiert nicht.", "overview": "Übersicht", "home": "Startseite", - "sites": "Standorte", - "resources": "Ressourcen", "accessControl": "Zugriffskontrolle", - "users": "Benutzer", - "roles": "Rollen", - "share": "Teilbare Links", "settings": "Einstellungen", "usersAll": "Alle Benutzer", - "idp": "Identitätsanbieter", "license": "Lizenz", - "pangolinDashboard": "Dashboard - Pangolin" + "pangolinDashboard": "Dashboard - Pangolin", + "noResults": "Keine Ergebnisse gefunden." } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 5d964622..6b7c25ed 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -27,6 +27,7 @@ "createAnAccount": "Créer un compte", "inviteNotAccepted": "Invitation non acceptée", "authCreateAccount": "Créez un compte pour commencer", + "authNoAccount": "Vous n'avez pas de compte ?", "email": "Courriel", "password": "Mot de passe", "confirmPassword": "Confirmer le mot de passe", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "La clé de licence a été supprimée.", "licenseErrorKeyActivate": "Échec de l'activation de la clé de licence", "licenseErrorKeyActivateDescription": "Une erreur s'est produite lors de l'activation de la clé de licence.", + "licenseAbout": "À propos de la licence", + "communityEdition": "Edition Communautaire", + "licenseAboutDescription": "Ceci est destiné aux entreprises qui utilisent Pangolin dans un environnement commercial. Si vous utilisez Pangolin pour un usage personnel, vous pouvez ignorer cette section.", "licenseKeyActivated": "Clé de licence activée", "licenseKeyActivatedDescription": "La clé de licence a été activée avec succès.", "licenseErrorKeyRecheck": "Impossible de revérifier les clés de licence", @@ -350,6 +354,7 @@ "total": "Total", "licenseContinuePayment": "Continuer vers le paiement", "pricingPage": "page de tarification", + "pricingPortal": "Voir le portail d'achat", "licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le ", "invite": "Invitations", "inviteRegenerate": "Régénérer l'invitation", @@ -432,7 +437,7 @@ "inviteEmailSentDescription": "Un e-mail a été envoyé à l'utilisateur avec le lien d'accès ci-dessous. Ils doivent accéder au lien pour accepter l'invitation.", "inviteSentDescription": "L'utilisateur a été invité. Ils doivent accéder au lien ci-dessous pour accepter l'invitation.", "inviteExpiresIn": "L'invitation expirera dans {days, plural, =1 {# jour} other {# jours}}.", - "idpTitle": "Fournisseur d'identité", + "idpTitle": "Informations générales", "idpSelect": "Sélectionnez le fournisseur d'identité pour l'utilisateur externe", "idpNotConfigured": "Aucun fournisseur d'identité n'est configuré. Veuillez configurer un fournisseur d'identité avant de créer des utilisateurs externes.", "usernameUniq": "Ceci doit correspondre au nom d'utilisateur unique qui existe dans le fournisseur d'identité sélectionné.", @@ -573,7 +578,7 @@ "shareEasyCreate": "Facile à créer et à partager", "shareConfigurableExpirationDuration": "Durée d'expiration configurable", "shareSecureAndRevocable": "Sécurisé et révocable", - "nameMin": "Le nom doit contenir au moins {len} caractères.", + "nameMin": "Le nom doit comporter au moins {len} caractères.", "nameMax": "Le nom ne doit pas dépasser {len} caractères.", "sitesConfirmCopy": "Veuillez confirmer que vous avez copié la configuration.", "unknownCommand": "Commande inconnue", @@ -583,7 +588,7 @@ "newtId": "ID Newt", "newtSecretKey": "Clé secrète Newt", "architecture": "Architecture", - "sites": "Sites", + "sites": "Espaces", "siteWgAnyClients": "Utilisez n'importe quel client WireGuard pour vous connecter. Vous devrez adresser vos ressources internes en utilisant l'IP du pair.", "siteWgCompatibleAllClients": "Compatible avec tous les clients WireGuard", "siteWgManualConfigurationRequired": "Configuration manuelle requise", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Échec de l'enregistrement de la liste blanche", "resourceErrorWhitelistSaveDescription": "Une erreur s'est produite lors de l'enregistrement de la liste blanche", "resourcePasswordSubmit": "Activer la protection par mot de passe", + "resourcePasswordProtection": "Protection par mot de passe {status}", "resourcePasswordRemove": "Mot de passe de la ressource supprimé", "resourcePasswordRemoveDescription": "Le mot de passe de la ressource a été supprimé avec succès", "resourcePasswordSetup": "Mot de passe de la ressource défini", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Avant de supprimer ce rôle, veuillez sélectionner un nouveau rôle pour transférer les membres existants.", "manage": "Gérer", "sitesNotFound": "Aucun site trouvé.", - "expiresAt": "Expire le", "pangolinServerAdmin": "Admin Serveur - Pangolin", - "idpNameInternal": "Interne", "licenseTierProfessional": "Licence Professionnelle", "licenseTierEnterprise": "Licence Entreprise", + "licenseTierCommercial": "Licence commerciale", "licensed": "Sous licence", "yes": "Oui", "no": "Non", @@ -717,8 +722,6 @@ "idp": "Fournisseurs d'identité", "idpSearch": "Rechercher des fournisseurs d'identité...", "idpAdd": "Ajouter un fournisseur d'identité", - "nameMin": "Le nom doit comporter au moins {len} caractères.", - "nameMax": "Le nom ne doit pas dépasser {len} caractères.", "idpClientIdRequired": "L'ID client est requis.", "idpClientSecretRequired": "Le secret client est requis.", "idpErrorAuthUrlInvalid": "L'URL d'authentification doit être une URL valide.", @@ -730,7 +733,6 @@ "idpCreate": "Créer un fournisseur d'identité", "idpCreateDescription": "Configurer un nouveau fournisseur d'identité pour l'authentification des utilisateurs", "idpSeeAll": "Voir tous les fournisseurs d'identité", - "idpTitle": "Informations générales", "idpSettingsDescription": "Configurer les informations de base de votre fournisseur d'identité", "idpDisplayName": "Un nom d'affichage pour ce fournisseur d'identité", "idpAutoProvisionUsers": "Approvisionnement automatique des utilisateurs", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Chemin de mappage d'organisation (Optionnel)", "orgPolicyUpdate": "Mettre à jour la politique", "orgPolicyAdd": "Ajouter une politique", + "orgPolicyConfig": "Configurer l'accès pour une organisation", "idpUpdatedDescription": "Fournisseur d'identité mis à jour avec succès", "redirectUrl": "URL de redirection", "redirectUrlAbout": "À propos de l'URL de redirection", "redirectUrlAboutDescription": "C'est l'URL vers laquelle les utilisateurs seront redirigés après l'authentification. Vous devez configurer cette URL dans les paramètres de votre fournisseur d'identité.", - "key": "Clé", - "createdAt": "Créé le", - "expiresAt": "Expire le", "pangolinAuth": "Auth - Pangolin", - "emailInvalid": "Adresse e-mail invalide", "verificationCodeLengthRequirements": "Votre code de vérification doit comporter 8 caractères.", "errorOccurred": "Une erreur s'est produite", "emailErrorVerify": "Échec de la vérification de l'e-mail :", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Connecté", "idpErrorConnectingTo": "Un problème est survenu lors de la connexion à {name}. Veuillez contacter votre administrateur.", "idpErrorNotFound": "IdP introuvable", - "expiresAt": "Expire le", "inviteInvalid": "Invitation invalide", "inviteInvalidDescription": "Le lien d'invitation n'est pas valide.", "inviteErrorWrongUser": "L'invitation n'est pas pour cet utilisateur", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Oups! La page que vous recherchez n'existe pas.", "overview": "Vue d'ensemble", "home": "Accueil", - "sites": "Sites", - "resources": "Ressources", "accessControl": "Contrôle d'accès", - "users": "Utilisateurs", - "roles": "Rôles", - "share": "Liens partageables", "settings": "Paramètres", "usersAll": "Tous les utilisateurs", - "idp": "Fournisseurs d'identité", "license": "Licence", - "pangolinDashboard": "Tableau de bord - Pangolin" + "pangolinDashboard": "Tableau de bord - Pangolin", + "noResults": "Aucun résultat trouvé." } diff --git a/messages/it-IT.json b/messages/it-IT.json index e42735cf..ea108c0e 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -27,6 +27,7 @@ "createAnAccount": "Crea un account", "inviteNotAccepted": "Invito Non Accettato", "authCreateAccount": "Crea un account per iniziare", + "authNoAccount": "Non hai un account?", "email": "Email", "password": "Password", "confirmPassword": "Conferma Password", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "La chiave di licenza è stata eliminata.", "licenseErrorKeyActivate": "Attivazione della chiave di licenza non riuscita", "licenseErrorKeyActivateDescription": "Si è verificato un errore nell'attivazione della chiave di licenza.", + "licenseAbout": "Informazioni Su Licenze", + "communityEdition": "Community Edition", + "licenseAboutDescription": "Questo è per gli utenti aziendali e aziendali che utilizzano Pangolin in un ambiente commerciale. Se stai usando Pangolin per uso personale, puoi ignorare questa sezione.", "licenseKeyActivated": "Chiave di licenza attivata", "licenseKeyActivatedDescription": "La chiave di licenza è stata attivata correttamente.", "licenseErrorKeyRecheck": "Impossibile ricontrollare le chiavi di licenza", @@ -350,6 +354,7 @@ "total": "Totale", "licenseContinuePayment": "Continua al pagamento", "pricingPage": "pagina prezzi", + "pricingPortal": "Vedi Il Portale Di Acquisto", "licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il ", "invite": "Inviti", "inviteRegenerate": "Rigenera Invito", @@ -432,7 +437,7 @@ "inviteEmailSentDescription": "È stata inviata un'email all'utente con il link di accesso qui sotto. Devono accedere al link per accettare l'invito.", "inviteSentDescription": "L'utente è stato invitato. Deve accedere al link qui sotto per accettare l'invito.", "inviteExpiresIn": "L'invito scadrà tra {days, plural, =1 {# giorno} other {# giorni}}.", - "idpTitle": "Provider di Identità", + "idpTitle": "Informazioni Generali", "idpSelect": "Seleziona il provider di identità per l'utente esterno", "idpNotConfigured": "Nessun provider di identità configurato. Configura un provider di identità prima di creare utenti esterni.", "usernameUniq": "Questo deve corrispondere all'username univoco esistente nel provider di identità selezionato.", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Impossibile salvare la lista autorizzazioni", "resourceErrorWhitelistSaveDescription": "Si è verificato un errore durante il salvataggio della lista autorizzazioni", "resourcePasswordSubmit": "Abilita Protezione Password", + "resourcePasswordProtection": "Protezione Password {status}", "resourcePasswordRemove": "Password della risorsa rimossa", "resourcePasswordRemoveDescription": "La password della risorsa è stata rimossa con successo", "resourcePasswordSetup": "Password della risorsa impostata", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Prima di eliminare questo ruolo, seleziona un nuovo ruolo a cui trasferire i membri esistenti.", "manage": "Gestisci", "sitesNotFound": "Nessun sito trovato.", - "expiresAt": "Scade Il", - "pangolinServerAdmin": "Server Admin - Pangolin", - "idpNameInternal": "Interno", + "pangolinServerAdmin": "Server Admin - Pangolina", "licenseTierProfessional": "Licenza Professional", "licenseTierEnterprise": "Licenza Enterprise", + "licenseTierCommercial": "Licenza Commerciale", "licensed": "Con Licenza", "yes": "Sì", "no": "No", @@ -714,11 +719,9 @@ "idpMessageConfirm": "Per confermare, digita il nome del provider di identità qui sotto.", "idpConfirmDelete": "Conferma Eliminazione Provider di Identità", "idpDelete": "Elimina Provider di Identità", - "idp": "Provider di Identità", + "idp": "Provider Di Identità", "idpSearch": "Cerca provider di identità...", "idpAdd": "Aggiungi Provider di Identità", - "nameMin": "Il nome deve essere di almeno {len} caratteri.", - "nameMax": "Il nome non deve superare i {len} caratteri.", "idpClientIdRequired": "L'ID client è obbligatorio.", "idpClientSecretRequired": "Il segreto client è obbligatorio.", "idpErrorAuthUrlInvalid": "L'URL di autenticazione deve essere un URL valido.", @@ -730,12 +733,11 @@ "idpCreate": "Crea Provider di Identità", "idpCreateDescription": "Configura un nuovo provider di identità per l'autenticazione degli utenti", "idpSeeAll": "Vedi Tutti i Provider di Identità", - "idpTitle": "Informazioni Generali", "idpSettingsDescription": "Configura le informazioni di base per il tuo provider di identità", "idpDisplayName": "Un nome visualizzato per questo provider di identità", "idpAutoProvisionUsers": "Provisioning Automatico Utenti", "idpAutoProvisionUsersDescription": "Quando abilitato, gli utenti verranno creati automaticamente nel sistema al primo accesso con la possibilità di mappare gli utenti a ruoli e organizzazioni.", - "licenseBadge": "Professional", + "licenseBadge": "Professionista", "idpType": "Tipo di Provider", "idpTypeDescription": "Seleziona il tipo di provider di identità che desideri configurare", "idpOidcConfigure": "Configurazione OAuth2/OIDC", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Percorso Mappatura Organizzazione (Opzionale)", "orgPolicyUpdate": "Aggiorna Politica", "orgPolicyAdd": "Aggiungi Politica", + "orgPolicyConfig": "Configura l'accesso per un'organizzazione", "idpUpdatedDescription": "Provider di identità aggiornato con successo", "redirectUrl": "URL di Reindirizzamento", "redirectUrlAbout": "Informazioni sull'URL di Reindirizzamento", "redirectUrlAboutDescription": "Questo è l'URL a cui gli utenti verranno reindirizzati dopo l'autenticazione. Devi configurare questo URL nelle impostazioni del tuo provider di identità.", - "key": "Chiave", - "createdAt": "Creato Il", - "expiresAt": "Scade Il", - "pangolinAuth": "Auth - Pangolin", - "emailInvalid": "Indirizzo email non valido", + "pangolinAuth": "Autenticazione - Pangolina", "verificationCodeLengthRequirements": "Il tuo codice di verifica deve essere di 8 caratteri.", "errorOccurred": "Si è verificato un errore", "emailErrorVerify": "Impossibile verificare l'email:", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Connesso", "idpErrorConnectingTo": "Si è verificato un problema durante la connessione a {name}. Contatta il tuo amministratore.", "idpErrorNotFound": "IdP non trovato", - "expiresAt": "Scade Il", "inviteInvalid": "Invito Non Valido", "inviteInvalidDescription": "Il link di invito non è valido.", "inviteErrorWrongUser": "L'invito non è per questo utente", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Oops! La pagina che stai cercando non esiste.", "overview": "Panoramica", "home": "Home", - "sites": "Siti", - "resources": "Risorse", "accessControl": "Controllo Accessi", - "users": "Utenti", - "roles": "Ruoli", - "share": "Link Condivisibili", "settings": "Impostazioni", "usersAll": "Tutti Gli Utenti", - "idp": "Provider Di Identità", "license": "Licenza", - "pangolinDashboard": "Dashboard - Pangolin" + "pangolinDashboard": "Cruscotto - Pangolino", + "noResults": "Nessun risultato trovato." } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index acc1f3d8..4fb99da9 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -27,6 +27,7 @@ "createAnAccount": "Utwórz konto", "inviteNotAccepted": "Zaproszenie nie zaakceptowane", "authCreateAccount": "Utwórz konto, aby rozpocząć", + "authNoAccount": "Nie masz konta?", "email": "E-mail", "password": "Hasło", "confirmPassword": "Potwierdź hasło", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "Klucz licencyjny został usunięty.", "licenseErrorKeyActivate": "Nie udało się aktywować klucza licencji", "licenseErrorKeyActivateDescription": "Wystąpił błąd podczas aktywacji klucza licencyjnego.", + "licenseAbout": "O licencjonowaniu", + "communityEdition": "Edycja Społeczności", + "licenseAboutDescription": "Dotyczy to przedsiębiorstw i przedsiębiorstw, którzy stosują Pangolin w środowisku handlowym. Jeśli używasz Pangolin do użytku osobistego, możesz zignorować tę sekcję.", "licenseKeyActivated": "Klucz licencyjny aktywowany", "licenseKeyActivatedDescription": "Klucz licencyjny został pomyślnie aktywowany.", "licenseErrorKeyRecheck": "Nie udało się ponownie sprawdzić kluczy licencyjnych", @@ -350,6 +354,7 @@ "total": "Łącznie", "licenseContinuePayment": "Przejdź do płatności", "pricingPage": "strona cenowa", + "pricingPortal": "Zobacz portal zakupu", "licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź ", "invite": "Zaproszenia", "inviteRegenerate": "Wygeneruj ponownie zaproszenie", @@ -395,7 +400,7 @@ "accessRoleOwner": "Właściciel", "userConfirmed": "Potwierdzony", "idpNameInternal": "Wewnętrzny", - "emailInvalid": "Nieprawidłowy adres email", + "emailInvalid": "Nieprawidłowy adres e-mail", "inviteValidityDuration": "Proszę wybrać okres ważności", "accessRoleSelectPlease": "Proszę wybrać rolę", "usernameRequired": "Nazwa użytkownika jest wymagana", @@ -432,7 +437,7 @@ "inviteEmailSentDescription": "Email został wysłany do użytkownika z linkiem dostępu poniżej. Musi on uzyskać dostęp do linku, aby zaakceptować zaproszenie.", "inviteSentDescription": "Użytkownik został zaproszony. Musi uzyskać dostęp do poniższego linku, aby zaakceptować zaproszenie.", "inviteExpiresIn": "Zaproszenie wygaśnie za {days, plural, =1 {# dzień} other {# dni}}.", - "idpTitle": "Dostawca tożsamości", + "idpTitle": "Informacje ogólne", "idpSelect": "Wybierz dostawcę tożsamości dla użytkownika zewnętrznego", "idpNotConfigured": "Nie skonfigurowano żadnych dostawców tożsamości. Skonfiguruj dostawcę tożsamości przed utworzeniem użytkowników zewnętrznych.", "usernameUniq": "Musi to odpowiadać unikalnej nazwie użytkownika istniejącej u wybranego dostawcy tożsamości.", @@ -568,7 +573,7 @@ "shareLink": "Link udostępniania {resource}", "resourceSelect": "Wybierz zasób", "shareLinks": "Linki udostępniania", - "share": "Linki do udostępnienia", + "share": "Linki do udostępniania", "shareDescription2": "Twórz linki do udostępniania swoich zasobów. Linki zapewniają tymczasowy lub nieograniczony dostęp do zasobu. Podczas tworzenia linku możesz skonfigurować okres jego ważności.", "shareEasyCreate": "Łatwe tworzenie i udostępnianie", "shareConfigurableExpirationDuration": "Konfigurowalny okres ważności", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Nie udało się zapisać białej listy", "resourceErrorWhitelistSaveDescription": "Wystąpił błąd podczas zapisywania białej listy", "resourcePasswordSubmit": "Włącz ochronę hasłem", + "resourcePasswordProtection": "Ochrona haseł {status}", "resourcePasswordRemove": "Hasło zasobu zostało usunięte", "resourcePasswordRemoveDescription": "Hasło zasobu zostało pomyślnie usunięte", "resourcePasswordSetup": "Ustawiono hasło zasobu", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Przed usunięciem tej roli, wybierz nową rolę do której zostaną przeniesieni obecni członkowie.", "manage": "Zarządzaj", "sitesNotFound": "Nie znaleziono witryn.", - "expiresAt": "Wygasa w dniu", "pangolinServerAdmin": "Administrator serwera - Pangolin", - "idpNameInternal": "Wewnętrzny", "licenseTierProfessional": "Licencja Professional", "licenseTierEnterprise": "Licencja Enterprise", + "licenseTierCommercial": "Licencja handlowa", "licensed": "Licencjonowany", "yes": "Tak", "no": "Nie", @@ -717,8 +722,6 @@ "idp": "Dostawcy tożsamości", "idpSearch": "Szukaj dostawców tożsamości...", "idpAdd": "Dodaj dostawcę tożsamości", - "nameMin": "Nazwa musi mieć co najmniej {len} znaków.", - "nameMax": "Nazwa nie może być dłuższa niż {len} znaków.", "idpClientIdRequired": "Identyfikator klienta jest wymagany.", "idpClientSecretRequired": "Sekret klienta jest wymagany.", "idpErrorAuthUrlInvalid": "URL autoryzacji musi być prawidłowym adresem URL.", @@ -730,12 +733,11 @@ "idpCreate": "Utwórz dostawcę tożsamości", "idpCreateDescription": "Skonfiguruj nowego dostawcę tożsamości do uwierzytelniania użytkowników", "idpSeeAll": "Zobacz wszystkich dostawców tożsamości", - "idpTitle": "Informacje ogólne", "idpSettingsDescription": "Skonfiguruj podstawowe informacje dla swojego dostawcy tożsamości", "idpDisplayName": "Nazwa wyświetlana dla tego dostawcy tożsamości", "idpAutoProvisionUsers": "Automatyczne tworzenie użytkowników", "idpAutoProvisionUsersDescription": "Gdy włączone, użytkownicy będą automatycznie tworzeni w systemie przy pierwszym logowaniu z możliwością mapowania użytkowników do ról i organizacji.", - "licenseBadge": "Professional", + "licenseBadge": "Profesjonalny", "idpType": "Typ dostawcy", "idpTypeDescription": "Wybierz typ dostawcy tożsamości, który chcesz skonfigurować", "idpOidcConfigure": "Konfiguracja OAuth2/OIDC", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Ścieżka mapowania organizacji (Opcjonalnie)", "orgPolicyUpdate": "Aktualizuj politykę", "orgPolicyAdd": "Dodaj politykę", + "orgPolicyConfig": "Skonfiguruj dostęp dla organizacji", "idpUpdatedDescription": "Dostawca tożsamości został pomyślnie zaktualizowany", "redirectUrl": "URL przekierowania", "redirectUrlAbout": "O URL przekierowania", "redirectUrlAboutDescription": "Jest to URL, na który użytkownicy zostaną przekierowani po uwierzytelnieniu. Musisz skonfigurować ten URL w ustawieniach swojego dostawcy tożsamości.", - "key": "Klucz", - "createdAt": "Utworzono", - "expiresAt": "Wygasa w dniu", "pangolinAuth": "Autoryzacja - Pangolin", - "emailInvalid": "Nieprawidłowy adres e-mail", "verificationCodeLengthRequirements": "Twój kod weryfikacyjny musi mieć 8 znaków.", "errorOccurred": "Wystąpił błąd", "emailErrorVerify": "Nie udało się zweryfikować adresu e-mail:", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Połączono", "idpErrorConnectingTo": "Wystąpił problem z połączeniem z {name}. Skontaktuj się z administratorem.", "idpErrorNotFound": "Nie znaleziono IdP", - "expiresAt": "Wygasa w dniu", "inviteInvalid": "Nieprawidłowe zaproszenie", "inviteInvalidDescription": "Link zapraszający jest nieprawidłowy.", "inviteErrorWrongUser": "Zaproszenie nie jest dla tego użytkownika", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Ups! Strona, której szukasz, nie istnieje.", "overview": "Przegląd", "home": "Strona główna", - "sites": "Witryny", - "resources": "Zasoby", "accessControl": "Kontrola dostępu", - "users": "Użytkownicy", - "roles": "Role", - "share": "Linki do udostępniania", "settings": "Ustawienia", "usersAll": "Wszyscy użytkownicy", - "idp": "Dostawcy tożsamości", "license": "Licencja", - "pangolinDashboard": "Panel - Pangolin" + "pangolinDashboard": "Panel - Pangolin", + "noResults": "Nie znaleziono wyników." } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 9b02e1b8..1fb46b25 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -27,6 +27,7 @@ "createAnAccount": "Crie uma conta", "inviteNotAccepted": "Convite não aceito", "authCreateAccount": "Crie uma conta para começar", + "authNoAccount": "Não possui uma conta?", "email": "e-mail", "password": "Palavra-passe", "confirmPassword": "Confirmar senha", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", "licenseErrorKeyActivateDescription": "Ocorreu um erro ao ativar a chave da licença.", + "licenseAbout": "Sobre Licenciamento", + "communityEdition": "Edição da Comunidade", + "licenseAboutDescription": "Isto destina-se aos utilizadores empresariais e empresariais que estão a usar o Pangolin num ambiente comercial. Se você estiver usando o Pangolin para uso pessoal, você pode ignorar esta seção.", "licenseKeyActivated": "Chave de licença ativada", "licenseKeyActivatedDescription": "A chave de licença foi ativada com sucesso.", "licenseErrorKeyRecheck": "Falha ao verificar novamente as chaves de licença", @@ -350,6 +354,7 @@ "total": "Total:", "licenseContinuePayment": "Continuar para o pagamento", "pricingPage": "Página de preços", + "pricingPortal": "Ver Portal de Compra", "licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite ", "invite": "Convites", "inviteRegenerate": "Regenerar Convite", @@ -359,7 +364,7 @@ "inviteRemoveErrorDescription": "Ocorreu um erro ao remover o convite.", "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", - "inviteQuestionRemove": "Tem certeza que deseja remover o convite{email, plural, ='' {}, other { para #}}?", + "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", @@ -395,7 +400,7 @@ "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", "idpNameInternal": "Interno", - "emailInvalid": "Endereço de e-mail inválido", + "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", "usernameRequired": "Nome de usuário é obrigatório", @@ -432,7 +437,7 @@ "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, =1 {# dia} other {# dias}}.", - "idpTitle": "Provedor de Identidade", + "idpTitle": "Informações Gerais", "idpSelect": "Selecione o provedor de identidade para o usuário externo", "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", @@ -574,7 +579,7 @@ "shareConfigurableExpirationDuration": "Duração de expiração configurável", "shareSecureAndRevocable": "Seguro e revogável", "nameMin": "O nome deve ter pelo menos {len} caracteres.", - "nameMax": "O nome não deve ter mais que {len} caracteres.", + "nameMax": "O nome não deve ter mais de {len} caracteres.", "sitesConfirmCopy": "Por favor, confirme que você copiou a configuração.", "unknownCommand": "Comando desconhecido", "newtErrorFetchReleases": "Falha ao buscar informações da versão: {err}", @@ -583,7 +588,7 @@ "newtId": "ID Newt", "newtSecretKey": "Chave Secreta Newt", "architecture": "Arquitetura", - "sites": "Sites", + "sites": "sites", "siteWgAnyClients": "Use qualquer cliente WireGuard para conectar. Você terá que endereçar seus recursos internos usando o IP do par.", "siteWgCompatibleAllClients": "Compatível com todos os clientes WireGuard", "siteWgManualConfigurationRequired": "Configuração manual necessária", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Falha ao salvar lista permitida", "resourceErrorWhitelistSaveDescription": "Ocorreu um erro ao salvar a lista permitida", "resourcePasswordSubmit": "Habilitar Proteção por Senha", + "resourcePasswordProtection": "Proteção com senha {status}", "resourcePasswordRemove": "Senha do recurso removida", "resourcePasswordRemoveDescription": "A senha do recurso foi removida com sucesso", "resourcePasswordSetup": "Senha do recurso definida", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", - "expiresAt": "Expira em", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", - "idpNameInternal": "Interno", "licenseTierProfessional": "Licença Profissional", "licenseTierEnterprise": "Licença Empresarial", + "licenseTierCommercial": "Licença comercial", "licensed": "Licenciado", "yes": "Sim", "no": "Não", @@ -717,8 +722,6 @@ "idp": "Provedores de Identidade", "idpSearch": "Pesquisar provedores de identidade...", "idpAdd": "Adicionar Provedor de Identidade", - "nameMin": "O nome deve ter pelo menos {len} caracteres.", - "nameMax": "O nome não deve ter mais de {len} caracteres.", "idpClientIdRequired": "O ID do Cliente é obrigatório.", "idpClientSecretRequired": "O Segredo do Cliente é obrigatório.", "idpErrorAuthUrlInvalid": "O URL de Autenticação deve ser um URL válido.", @@ -730,7 +733,6 @@ "idpCreate": "Criar Provedor de Identidade", "idpCreateDescription": "Configurar um novo provedor de identidade para autenticação de utilizadores", "idpSeeAll": "Ver Todos os Provedores de Identidade", - "idpTitle": "Informações Gerais", "idpSettingsDescription": "Configurar as informações básicas para o seu provedor de identidade", "idpDisplayName": "Um nome de exibição para este provedor de identidade", "idpAutoProvisionUsers": "Provisionamento Automático de Utilizadores", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Caminho de Mapeamento da Organização (Opcional)", "orgPolicyUpdate": "Atualizar Política", "orgPolicyAdd": "Adicionar Política", + "orgPolicyConfig": "Configurar acesso para uma organização", "idpUpdatedDescription": "Provedor de identidade atualizado com sucesso", "redirectUrl": "URL de Redirecionamento", "redirectUrlAbout": "Sobre o URL de Redirecionamento", "redirectUrlAboutDescription": "Este é o URL para o qual os utilizadores serão redirecionados após a autenticação. Precisa configurar este URL nas configurações do seu provedor de identidade.", - "key": "Chave", - "createdAt": "Criado Em", - "expiresAt": "Expira em", "pangolinAuth": "Autenticação - Pangolin", - "emailInvalid": "Endereço de email inválido", "verificationCodeLengthRequirements": "O seu código de verificação deve ter 8 caracteres.", "errorOccurred": "Ocorreu um erro", "emailErrorVerify": "Falha ao verificar o email:", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Conectado", "idpErrorConnectingTo": "Ocorreu um problema ao ligar a {name}. Por favor, contacte o seu administrador.", "idpErrorNotFound": "IdP não encontrado", - "expiresAt": "Expira em", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", "inviteErrorWrongUser": "O convite não é para este usuário", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Ops! A página que você está procurando não existe.", "overview": "Visão Geral", "home": "Início", - "sites": "Sites", - "resources": "Recursos", "accessControl": "Controle de Acesso", - "users": "Usuários", - "roles": "Funções", - "share": "Links Compartilháveis", "settings": "Configurações", "usersAll": "Todos os Usuários", - "idp": "Provedores de Identidade", "license": "Licença", - "pangolinDashboard": "Painel - Pangolin" + "pangolinDashboard": "Painel - Pangolin", + "noResults": "Nenhum resultado encontrado." } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 73b10add..79d9a647 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -27,6 +27,7 @@ "createAnAccount": "Create an Account", "inviteNotAccepted": "Invite Not Accepted", "authCreateAccount": "Create an account to get started", + "authNoAccount": "Don't have an account?", "email": "Email", "password": "Password", "confirmPassword": "Confirm Password", @@ -314,6 +315,9 @@ "licenseKeyDeletedDescription": "The license key has been deleted.", "licenseErrorKeyActivate": "Failed to activate license key", "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", + "licenseAbout": "About Licensing", + "communityEdition": "Community Edition", + "licenseAboutDescription": "This is for business and enterprise users who are using Pangolin in a commercial environment. If you are using Pangolin for personal use, you can ignore this section.", "licenseKeyActivated": "License key activated", "licenseKeyActivatedDescription": "The license key has been successfully activated.", "licenseErrorKeyRecheck": "Failed to recheck license keys", @@ -350,6 +354,7 @@ "total": "Total", "licenseContinuePayment": "Continue to Payment", "pricingPage": "pricing page", + "pricingPortal": "See Purchase Portal", "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ", "invite": "Invitations", "inviteRegenerate": "Regenerate Invitation", @@ -362,7 +367,7 @@ "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", - "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", + "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for{email, plural, ='' {}, other { for #}}? This will revoke the previous invitation.", "inviteRemoveConfirm": "Confirm Remove Invitation", "inviteRegenerated": "Invitation Regenerated", "inviteSent": "A new invitation has been sent to {email}.", @@ -432,7 +437,7 @@ "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", - "idpTitle": "Identity Provider", + "idpTitle": "General Information", "idpSelect": "Select the identity provider for the external user", "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", "usernameUniq": "This must match the unique username that exists in the selected identity provider.", @@ -626,6 +631,7 @@ "resourceErrorWhitelistSave": "Failed to save whitelist", "resourceErrorWhitelistSaveDescription": "An error occurred while saving the whitelist", "resourcePasswordSubmit": "Enable Password Protection", + "resourcePasswordProtection": "Password Protection {status}", "resourcePasswordRemove": "Resource password removed", "resourcePasswordRemoveDescription": "The resource password has been removed successfully", "resourcePasswordSetup": "Resource password set", @@ -693,11 +699,10 @@ "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", "manage": "Manage", "sitesNotFound": "No sites found.", - "expiresAt": "Expires At", "pangolinServerAdmin": "Server Admin - Pangolin", - "idpNameInternal": "Internal", "licenseTierProfessional": "Professional License", "licenseTierEnterprise": "Enterprise License", + "licenseTierCommercial": "Commercial License", "licensed": "Licensed", "yes": "Yes", "no": "No", @@ -717,8 +722,6 @@ "idp": "Identity Providers", "idpSearch": "Search identity providers...", "idpAdd": "Add Identity Provider", - "nameMin": "Name must be at least {len} characters.", - "nameMax": "Name must not be longer than {len} characters.", "idpClientIdRequired": "Client ID is required.", "idpClientSecretRequired": "Client Secret is required.", "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", @@ -730,7 +733,6 @@ "idpCreate": "Create Identity Provider", "idpCreateDescription": "Configure a new identity provider for user authentication", "idpSeeAll": "See All Identity Providers", - "idpTitle": "General Information", "idpSettingsDescription": "Configure the basic information for your identity provider", "idpDisplayName": "A display name for this identity provider", "idpAutoProvisionUsers": "Auto Provision Users", @@ -797,15 +799,12 @@ "orgMappingPathOptional": "Organization Mapping Path (Optional)", "orgPolicyUpdate": "Update Policy", "orgPolicyAdd": "Add Policy", + "orgPolicyConfig": "Configure access for an organization", "idpUpdatedDescription": "Identity provider updated successfully", "redirectUrl": "Redirect URL", "redirectUrlAbout": "About Redirect URL", "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", - "key": "Key", - "createdAt": "Created At", - "expiresAt": "Expires At", "pangolinAuth": "Auth - Pangolin", - "emailInvalid": "Invalid email address", "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", "errorOccurred": "An error occurred", "emailErrorVerify": "Failed to verify email:", @@ -827,7 +826,7 @@ "inviteAlreadyDescription": "To accept the invite, you must log in or create an account.", "signupQuestion": "Already have an account?", "login": "Log in", - "resourceNotFound": "Resource Not Found", + "resourceNotFound": "No resources found", "resourceNotFoundDescription": "The resource you're trying to access does not exist.", "pincodeRequirementsLength": "PIN must be exactly 6 digits", "pincodeRequirementsChars": "PIN must only contain numbers", @@ -887,7 +886,6 @@ "idpConnectingToFinished": "Connected", "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", "idpErrorNotFound": "IdP not found", - "expiresAt": "Expires At", "inviteInvalid": "Invalid Invite", "inviteInvalidDescription": "The invite link is invalid.", "inviteErrorWrongUser": "Invite is not for this user", @@ -904,15 +902,10 @@ "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", "overview": "Overview", "home": "Home", - "sites": "Sites", - "resources": "Resources", "accessControl": "Access Control", - "users": "Users", - "roles": "Roles", - "share": "Shareable Links", "settings": "Settings", "usersAll": "All Users", - "idp": "Identity Providers", "license": "License", - "pangolinDashboard": "Dashboard - Pangolin" + "pangolinDashboard": "Dashboard - Pangolin", + "noResults": "No results found." } From ea24759bb304674aeb79b2d5c3df408abd15681b Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Sun, 25 May 2025 17:41:38 +0300 Subject: [PATCH 074/105] I18n components (#27) * New translation keys in en-US locale * New translation keys in de-DE locale * New translation keys in fr-FR locale * New translation keys in it-IT locale * New translation keys in pl-PL locale * New translation keys in pt-PT locale * New translation keys in tr-TR locale * Move into function * Replace string matching to boolean check * Add FIXIT in UsersTable * Use localization for size units * Missed and restored translation keys * fixup! New translation keys in tr-TR locale * Add translation keys in components --- messages/de-DE.json | 156 +++++++++++++++++- messages/en-US.json | 156 +++++++++++++++++- messages/fr-FR.json | 156 +++++++++++++++++- messages/it-IT.json | 156 +++++++++++++++++- messages/pl-PL.json | 156 +++++++++++++++++- messages/pt-PT.json | 156 +++++++++++++++++- messages/tr-TR.json | 156 +++++++++++++++++- .../settings/access/roles/CreateRoleForm.tsx | 10 +- .../settings/access/roles/DeleteRoleForm.tsx | 8 +- .../settings/access/roles/RolesDataTable.tsx | 2 +- .../settings/access/users/UsersTable.tsx | 4 +- .../users/[userId]/access-controls/page.tsx | 14 +- .../settings/access/users/create/page.tsx | 36 ++-- .../[orgId]/settings/api-keys/create/page.tsx | 60 +++---- .../[resourceId]/authentication/page.tsx | 10 +- .../resources/[resourceId]/rules/page.tsx | 22 +-- .../share-links/CreateShareLinkForm.tsx | 2 +- .../settings/share-links/ShareLinksTable.tsx | 8 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 8 +- .../settings/sites/[niceId]/general/page.tsx | 8 +- .../[orgId]/settings/sites/create/page.tsx | 10 +- src/app/[orgId]/settings/sites/page.tsx | 10 +- src/app/admin/api-keys/create/page.tsx | 6 +- src/app/admin/users/AdminUsersTable.tsx | 2 +- src/app/auth/verify-email/VerifyEmailForm.tsx | 2 +- src/components/ConfirmDeleteDialog.tsx | 7 +- src/components/CopyTextBox.tsx | 6 +- src/components/CopyToClipboard.tsx | 5 +- src/components/DataTablePagination.tsx | 14 +- src/components/Disable2FaForm.tsx | 25 +-- src/components/Enable2FaForm.tsx | 33 ++-- src/components/HorizontalTabs.tsx | 4 +- src/components/Layout.tsx | 17 +- src/components/LoginForm.tsx | 26 +-- src/components/OrgSelector.tsx | 16 +- src/components/PermissionsSelectBox.tsx | 129 ++++++++------- src/components/ProfessionalContentOverlay.tsx | 8 +- src/components/ProfileIcon.tsx | 21 ++- src/components/SidebarNav.tsx | 5 +- src/components/SupporterStatus.tsx | 82 +++++---- src/components/tags/tag-input.tsx | 29 ++-- src/components/tags/tag-popover.tsx | 7 +- 42 files changed, 1419 insertions(+), 329 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 7f26ea92..22dd5909 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Geben Sie den an Ihre E-Mail-Adresse gesendeten Verifizierungscode ein.", "verificationCode": "Verifizierungscode", "verificationCodeEmailSent": "Wir haben einen Verifizierungscode an Ihre E-Mail-Adresse gesendet.", - "emailVerifySubmit": "Absenden", + "submit": "Absenden", "emailVerifyResendProgress": "Wird erneut gesendet...", "emailVerifyResend": "Keinen Code erhalten? Hier klicken zum erneuten Senden", "passwordNotMatch": "Passwörter stimmen nicht überein", @@ -907,5 +907,157 @@ "usersAll": "Alle Benutzer", "license": "Lizenz", "pangolinDashboard": "Dashboard - Pangolin", - "noResults": "Keine Ergebnisse gefunden." + "noResults": "Keine Ergebnisse gefunden.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Eingegebene Tags", + "tagsEnteredDescription": "Dies sind die von Ihnen eingegebenen Tags.", + "tagsWarnCannotBeLessThanZero": "maxTags und minTags können nicht kleiner als 0 sein", + "tagsWarnNotAllowedAutocompleteOptions": "Tag ist laut Autovervollständigungsoptionen nicht erlaubt", + "tagsWarnInvalid": "Ungültiger Tag laut validateTag", + "tagWarnTooShort": "Tag {tagText} ist zu kurz", + "tagWarnTooLong": "Tag {tagText} ist zu lang", + "tagsWarnReachedMaxNumber": "Maximale Anzahl erlaubter Tags erreicht", + "tagWarnDuplicate": "Doppelter Tag {tagText} nicht hinzugefügt", + "supportKeyInvalid": "Ungültiger Schlüssel", + "supportKeyInvalidDescription": "Ihr Unterstützer-Schlüssel ist ungültig.", + "supportKeyValid": "Gültiger Schlüssel", + "supportKeyValidDescription": "Ihr Unterstützer-Schlüssel wurde validiert. Danke für Ihre Unterstützung!", + "supportKeyErrorValidationDescription": "Unterstützer-Schlüssel konnte nicht validiert werden.", + "supportKey": "Unterstütze die Entwicklung und adoptiere ein Pangolin!", + "supportKeyDescription": "Kaufen Sie einen Unterstützer-Schlüssel, um uns bei der Weiterentwicklung von Pangolin für die Community zu helfen. Ihr Beitrag ermöglicht es uns, mehr Zeit in die Wartung und neue Funktionen für alle zu investieren. Wir werden dies nie für Paywalls nutzen. Dies ist unabhängig von der Commercial Edition.", + "supportKeyPet": "Sie können auch Ihr eigenes Pangolin-Haustier adoptieren und kennenlernen!", + "supportKeyPurchase": "Zahlungen werden über GitHub abgewickelt. Danach können Sie Ihren Schlüssel auf", + "supportKeyPurchaseLink": "unserer Website", + "supportKeyPurchase2": "abrufen und hier einlösen.", + "supportKeyLearnMore": "Mehr erfahren.", + "supportKeyOptions": "Bitte wählen Sie die Option, die am besten zu Ihnen passt.", + "supportKetOptionFull": "Voller Unterstützer", + "forWholeServer": "Für den gesamten Server", + "lifetimePurchase": "Lebenslanger Kauf", + "supporterStatus": "Unterstützer-Status", + "buy": "Kaufen", + "supportKeyOptionLimited": "Eingeschränkter Unterstützer", + "forFiveUsers": "Für 5 oder weniger Benutzer", + "supportKeyRedeem": "Unterstützer-Schlüssel einlösen", + "supportKeyHideSevenDays": "7 Tage ausblenden", + "supportKeyEnter": "Unterstützer-Schlüssel eingeben", + "supportKeyEnterDescription": "Treffen Sie Ihr eigenes Pangolin-Haustier!", + "githubUsername": "GitHub Benutzername", + "supportKeyInput": "Unterstützer-Schlüssel", + "supportKeyBuy": "Unterstützer-Schlüssel kaufen", + "logoutError": "Fehler beim Abmelden", + "signingAs": "Angemeldet als", + "serverAdmin": "Server-Administrator", + "otpEnable": "Zwei-Faktor aktivieren", + "otpDisable": "Zwei-Faktor deaktivieren", + "logout": "Abmelden", + "licenseTierProfessionalRequired": "Professional Edition erforderlich", + "licenseTierProfessionalRequiredDescription": "Diese Funktion ist nur in der Professional Edition verfügbar.", + "actionGetOrg": "Organisation abrufen", + "actionUpdateOrg": "Organisation aktualisieren", + "actionGetOrgUser": "Organisationsbenutzer abrufen", + "actionListOrgDomains": "Organisationsdomänen auflisten", + "actionCreateSite": "Site erstellen", + "actionDeleteSite": "Site löschen", + "actionGetSite": "Site abrufen", + "actionListSites": "Sites auflisten", + "actionUpdateSite": "Site aktualisieren", + "actionListSiteRoles": "Erlaubte Site-Rollen auflisten", + "actionCreateResource": "Ressource erstellen", + "actionDeleteResource": "Ressource löschen", + "actionGetResource": "Ressource abrufen", + "actionListResource": "Ressourcen auflisten", + "actionUpdateResource": "Ressource aktualisieren", + "actionListResourceUsers": "Ressourcenbenutzer auflisten", + "actionSetResourceUsers": "Ressourcenbenutzer festlegen", + "actionSetAllowedResourceRoles": "Erlaubte Ressourcenrollen festlegen", + "actionListAllowedResourceRoles": "Erlaubte Ressourcenrollen auflisten", + "actionSetResourcePassword": "Ressourcenpasswort festlegen", + "actionSetResourcePincode": "Ressourcen-PIN festlegen", + "actionSetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist festlegen", + "actionGetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist abrufen", + "actionCreateTarget": "Ziel erstellen", + "actionDeleteTarget": "Ziel löschen", + "actionGetTarget": "Ziel abrufen", + "actionListTargets": "Ziele auflisten", + "actionUpdateTarget": "Ziel aktualisieren", + "actionCreateRole": "Rolle erstellen", + "actionDeleteRole": "Rolle löschen", + "actionGetRole": "Rolle abrufen", + "actionListRole": "Rollen auflisten", + "actionUpdateRole": "Rolle aktualisieren", + "actionListAllowedRoleResources": "Erlaubte Rollenressourcen auflisten", + "actionInviteUser": "Benutzer einladen", + "actionRemoveUser": "Benutzer entfernen", + "actionListUsers": "Benutzer auflisten", + "actionAddUserRole": "Benutzerrolle hinzufügen", + "actionGenerateAccessToken": "Zugriffstoken generieren", + "actionDeleteAccessToken": "Zugriffstoken löschen", + "actionListAccessTokens": "Zugriffstoken auflisten", + "actionCreateResourceRule": "Ressourcenregel erstellen", + "actionDeleteResourceRule": "Ressourcenregel löschen", + "actionListResourceRules": "Ressourcenregeln auflisten", + "actionUpdateResourceRule": "Ressourcenregel aktualisieren", + "actionListOrgs": "Organisationen auflisten", + "actionCheckOrgId": "ID prüfen", + "actionCreateOrg": "Organisation erstellen", + "actionDeleteOrg": "Organisation löschen", + "actionListApiKeys": "API-Schlüssel auflisten", + "actionListApiKeyActions": "API-Schlüsselaktionen auflisten", + "actionSetApiKeyActions": "Erlaubte API-Schlüsselaktionen festlegen", + "actionCreateApiKey": "API-Schlüssel erstellen", + "actionDeleteApiKey": "API-Schlüssel löschen", + "actionCreateIdp": "IDP erstellen", + "actionUpdateIdp": "IDP aktualisieren", + "actionDeleteIdp": "IDP löschen", + "actionListIdps": "IDP auflisten", + "actionGetIdp": "IDP abrufen", + "actionCreateIdpOrg": "IDP-Organisationsrichtlinie erstellen", + "actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen", + "actionListIdpOrgs": "IDP-Organisationen auflisten", + "actionUpdateIdpOrg": "IDP-Organisation aktualisieren", + "noneSelected": "Keine ausgewählt", + "orgNotFound2": "Keine Organisationen gefunden.", + "searchProgress": "Suche...", + "create": "Erstellen", + "orgs": "Organisationen", + "loginError": "Beim Anmelden ist ein Fehler aufgetreten", + "passwordForgot": "Passwort vergessen?", + "otpAuth": "Zwei-Faktor-Authentifizierung", + "otpAuthDescription": "Geben Sie den Code aus Ihrer Authenticator-App oder einen Ihrer einmaligen Backup-Codes ein.", + "otpAuthSubmit": "Code absenden", + "idpContinue": "Oder weiter mit", + "otpAuthBack": "Zurück zur Anmeldung", + "navbar": "Navigationsmenü", + "navbarDescription": "Hauptnavigationsmenü für die Anwendung", + "navbarDocsLink": "Dokumentation", + "commercialEdition": "Commercial Edition", + "otpErrorEnable": "2FA konnte nicht aktiviert werden", + "otpErrorEnableDescription": "Beim Aktivieren der 2FA ist ein Fehler aufgetreten", + "otpSetupCheckCode": "Bitte geben Sie einen 6-stelligen Code ein", + "otpSetupCheckCodeRetry": "Ungültiger Code. Bitte versuchen Sie es erneut.", + "otpSetup": "Zwei-Faktor-Authentifizierung aktivieren", + "otpSetupDescription": "Sichern Sie Ihr Konto mit einer zusätzlichen Schutzebene", + "otpSetupScanQr": "Scannen Sie diesen QR-Code mit Ihrer Authenticator-App oder geben Sie den Geheimschlüssel manuell ein:", + "otpSetupSecretCode": "Authenticator-Code", + "otpSetupSuccess": "Zwei-Faktor-Authentifizierung aktiviert", + "otpSetupSuccessStoreBackupCodes": "Ihr Konto ist jetzt sicherer. Vergessen Sie nicht, Ihre Backup-Codes zu speichern.", + "otpErrorDisable": "2FA konnte nicht deaktiviert werden", + "otpErrorDisableDescription": "Beim Deaktivieren der 2FA ist ein Fehler aufgetreten", + "otpRemove": "Zwei-Faktor-Authentifizierung deaktivieren", + "otpRemoveDescription": "Deaktivieren Sie die Zwei-Faktor-Authentifizierung für Ihr Konto", + "otpRemoveSuccess": "Zwei-Faktor-Authentifizierung deaktiviert", + "otpRemoveSuccessMessage": "Die Zwei-Faktor-Authentifizierung wurde für Ihr Konto deaktiviert. Sie können sie jederzeit wieder aktivieren.", + "otpRemoveSubmit": "2FA deaktivieren", + "paginator": "Seite {current} von {last}", + "paginatorToFirst": "Zur ersten Seite", + "paginatorToPrevious": "Zur vorherigen Seite", + "paginatorToNext": "Zur nächsten Seite", + "paginatorToLast": "Zur letzten Seite", + "copyText": "Text kopieren", + "copyTextFailed": "Text konnte nicht kopiert werden: ", + "copyTextClipboard": "In die Zwischenablage kopieren", + "inviteErrorInvalidConfirmation": "Ungültige Bestätigung" } diff --git a/messages/en-US.json b/messages/en-US.json index 48bf47ab..c2969842 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Enter the verification code sent to your email address.", "verificationCode": "Verification Code", "verificationCodeEmailSent": "We sent a verification code to your email address.", - "emailVerifySubmit": "Submit", + "submit": "Submit", "emailVerifyResendProgress": "Resending...", "emailVerifyResend": "Didn't receive a code? Click here to resend", "passwordNotMatch": "Passwords do not match", @@ -907,5 +907,157 @@ "usersAll": "All Users", "license": "License", "pangolinDashboard": "Dashboard - Pangolin", - "noResults": "No results found." + "noResults": "No results found.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Entered Tags", + "tagsEnteredDescription": "These are the tags you`ve entered.", + "tagsWarnCannotBeLessThanZero": "maxTags and minTags cannot be less than 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag not allowed as per autocomplete options", + "tagsWarnInvalid": "Invalid tag as per validateTag", + "tagWarnTooShort": "Tag {tagText} is too short", + "tagWarnTooLong": "Tag {tagText} is too long", + "tagsWarnReachedMaxNumber": "Reached the maximum number of tags allowed", + "tagWarnDuplicate": "Duplicate tag {tagText} not added", + "supportKeyInvalid": "Invalid Key", + "supportKeyInvalidDescription": "Your supporter key is invalid.", + "supportKeyValid": "Valid Key", + "supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!", + "supportKeyErrorValidationDescription": "Failed to validate supporter key.", + "supportKey": "Support Development and Adopt a Pangolin!", + "supportKeyDescription": "Purchase a supporter key to help us continue developing Pangolin for the community. Your contribution allows us to commit more time to maintain and add new features to the application for everyone. We will never use this to paywall features. This is separate from any Commercial Edition.", + "supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!", + "supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on", + "supportKeyPurchaseLink": "our website", + "supportKeyPurchase2": "and redeem it here.", + "supportKeyLearnMore": "Learn more.", + "supportKeyOptions": "Please select the option that best suits you.", + "supportKetOptionFull": "Full Supporter", + "forWholeServer": "For the whole server", + "lifetimePurchase": "Lifetime purchase", + "supporterStatus": "Supporter status", + "buy": "Buy", + "supportKeyOptionLimited": "Limited Supporter", + "forFiveUsers": "For 5 or less users", + "supportKeyRedeem": "Redeem Supporter Key", + "supportKeyHideSevenDays": "Hide for 7 days", + "supportKeyEnter": "Enter Supporter Key", + "supportKeyEnterDescription": "Meet your very own pet Pangolin!", + "githubUsername": "GitHub Username", + "supportKeyInput": "Supporter Key", + "supportKeyBuy": "Buy Supporter Key", + "logoutError": "Error logging out", + "signingAs": "Signed in as", + "serverAdmin": "Server Admin", + "otpEnable": "Enable Two-factor", + "otpDisable": "Disable Two-factor", + "logout": "Log Out", + "licenseTierProfessionalRequired": "Professional Edition Required", + "licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.", + "actionGetOrg": "Get Organization", + "actionUpdateOrg": "Update Organization", + "actionGetOrgUser": "Get Organization User", + "actionListOrgDomains": "List Organization Domains", + "actionCreateSite": "Create Site", + "actionDeleteSite": "Delete Site", + "actionGetSite": "Get Site", + "actionListSites": "List Sites", + "actionUpdateSite": "Update Site", + "actionListSiteRoles": "List Allowed Site Roles", + "actionCreateResource": "Create Resource", + "actionDeleteResource": "Delete Resource", + "actionGetResource": "Get Resource", + "actionListResource": "List Resources", + "actionUpdateResource": "Update Resource", + "actionListResourceUsers": "List Resource Users", + "actionSetResourceUsers": "Set Resource Users", + "actionSetAllowedResourceRoles": "Set Allowed Resource Roles", + "actionListAllowedResourceRoles": "List Allowed Resource Roles", + "actionSetResourcePassword": "Set Resource Password", + "actionSetResourcePincode": "Set Resource Pincode", + "actionSetResourceEmailWhitelist": "Set Resource Email Whitelist", + "actionGetResourceEmailWhitelist": "Get Resource Email Whitelist", + "actionCreateTarget": "Create Target", + "actionDeleteTarget": "Delete Target", + "actionGetTarget": "Get Target", + "actionListTargets": "List Targets", + "actionUpdateTarget": "Update Target", + "actionCreateRole": "Create Role", + "actionDeleteRole": "Delete Role", + "actionGetRole": "Get Role", + "actionListRole": "List Roles", + "actionUpdateRole": "Update Role", + "actionListAllowedRoleResources": "List Allowed Role Resources", + "actionInviteUser": "Invite User", + "actionRemoveUser": "Remove User", + "actionListUsers": "List Users", + "actionAddUserRole": "Add User Role", + "actionGenerateAccessToken": "Generate Access Token", + "actionDeleteAccessToken": "Delete Access Token", + "actionListAccessTokens": "List Access Tokens", + "actionCreateResourceRule": "Create Resource Rule", + "actionDeleteResourceRule": "Delete Resource Rule", + "actionListResourceRules": "List Resource Rules", + "actionUpdateResourceRule": "Update Resource Rule", + "actionListOrgs": "List Organizations", + "actionCheckOrgId": "Check ID", + "actionCreateOrg": "Create Organization", + "actionDeleteOrg": "Delete Organization", + "actionListApiKeys": "List API Keys", + "actionListApiKeyActions": "List API Key Actions", + "actionSetApiKeyActions": "Set API Key Allowed Actions", + "actionCreateApiKey": "Create API Key", + "actionDeleteApiKey": "Delete API Key", + "actionCreateIdp": "Create IDP", + "actionUpdateIdp": "Update IDP", + "actionDeleteIdp": "Delete IDP", + "actionListIdps": "List IDP", + "actionGetIdp": "Get IDP", + "actionCreateIdpOrg": "Create IDP Org Policy", + "actionDeleteIdpOrg": "Delete IDP Org Policy", + "actionListIdpOrgs": "List IDP Orgs", + "actionUpdateIdpOrg": "Update IDP Org", + "noneSelected": "None selected", + "orgNotFound2": "No organizations found.", + "searchProgress": "Search...", + "create": "Create", + "orgs": "Organizations", + "loginError": "An error occurred while logging in", + "passwordForgot": "Forgot your password?", + "otpAuth": "Two-Factor Authentication", + "otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.", + "otpAuthSubmit": "Submit Code", + "idpContinue": "Or continue with", + "otpAuthBack": "Back to Log In", + "navbar": "Navigation Menu", + "navbarDescription": "Main navigation menu for the application", + "navbarDocsLink": "Documentation", + "commercialEdition": "Commercial Edition", + "otpErrorEnable": "Unable to enable 2FA", + "otpErrorEnableDescription": "An error occurred while enabling 2FA", + "otpSetupCheckCode": "Please enter a 6-digit code", + "otpSetupCheckCodeRetry": "Invalid code. Please try again.", + "otpSetup": "Enable Two-factor Authentication", + "otpSetupDescription": "Secure your account with an extra layer of protection", + "otpSetupScanQr": "Scan this QR code with your authenticator app or enter the secret key manually:", + "otpSetupSecretCode": "Authenticator Code", + "otpSetupSuccess": "Two-Factor Authentication Enabled", + "otpSetupSuccessStoreBackupCodes": "Your account is now more secure. Don't forget to save your backup codes.", + "otpErrorDisable": "Unable to disable 2FA", + "otpErrorDisableDescription": "An error occurred while disabling 2FA", + "otpRemove": "Disable Two-factor Authentication", + "otpRemoveDescription": "Disable two-factor authentication for your account", + "otpRemoveSuccess": "Two-Factor Authentication Disabled", + "otpRemoveSuccessMessage": "Two-factor authentication has been disabled for your account. You can enable it again at any time.", + "otpRemoveSubmit": "Disable 2FA", + "paginator": "Page {current} of {last}", + "paginatorToFirst": "Go to first page", + "paginatorToPrevious": "Go to previous page", + "paginatorToNext": "Go to next page", + "paginatorToLast": "Go to last page", + "copyText": "Copy text", + "copyTextFailed": "Failed to copy text: ", + "copyTextClipboard": "Copy to clipboard", + "inviteErrorInvalidConfirmation": "Invalid confirmation" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 6b7c25ed..cca3ae5a 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Entrez le code de vérification envoyé à votre adresse e-mail.", "verificationCode": "Code de vérification", "verificationCodeEmailSent": "Nous avons envoyé un code de vérification à votre adresse e-mail.", - "emailVerifySubmit": "Soumettre", + "submit": "Soumettre", "emailVerifyResendProgress": "Renvoi en cours...", "emailVerifyResend": "Vous n'avez pas reçu de code ? Cliquez ici pour renvoyer", "passwordNotMatch": "Les mots de passe ne correspondent pas", @@ -907,5 +907,157 @@ "usersAll": "Tous les utilisateurs", "license": "Licence", "pangolinDashboard": "Tableau de bord - Pangolin", - "noResults": "Aucun résultat trouvé." + "noResults": "Aucun résultat trouvé.", + "terabytes": "{count} To", + "gigabytes": "{count} Go", + "megabytes": "{count} Mo", + "tagsEntered": "Tags saisis", + "tagsEnteredDescription": "Ce sont les tags que vous avez saisis.", + "tagsWarnCannotBeLessThanZero": "maxTags et minTags ne peuvent pas être inférieurs à 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag non autorisé selon les options d'autocomplétion", + "tagsWarnInvalid": "Tag invalide selon validateTag", + "tagWarnTooShort": "Le tag {tagText} est trop court", + "tagWarnTooLong": "Le tag {tagText} est trop long", + "tagsWarnReachedMaxNumber": "Nombre maximum de tags autorisés atteint", + "tagWarnDuplicate": "Tag en double {tagText} non ajouté", + "supportKeyInvalid": "Clé invalide", + "supportKeyInvalidDescription": "Votre clé de support est invalide.", + "supportKeyValid": "Clé valide", + "supportKeyValidDescription": "Votre clé de support a été validée. Merci pour votre soutien !", + "supportKeyErrorValidationDescription": "Échec de la validation de la clé de support.", + "supportKey": "Soutenez le développement et adoptez un Pangolin !", + "supportKeyDescription": "Achetez une clé de support pour nous aider à continuer le développement de Pangolin pour la communauté. Votre contribution nous permet de consacrer plus de temps à maintenir et ajouter de nouvelles fonctionnalités à l'application pour tous. Nous n'utiliserons jamais cela pour verrouiller des fonctionnalités. Ceci est distinct de toute Édition Commerciale.", + "supportKeyPet": "Vous pourrez aussi adopter et rencontrer votre propre Pangolin de compagnie !", + "supportKeyPurchase": "Les paiements sont traités via GitHub. Ensuite, vous pourrez récupérer votre clé sur", + "supportKeyPurchaseLink": "notre site web", + "supportKeyPurchase2": "et l'utiliser ici.", + "supportKeyLearnMore": "En savoir plus.", + "supportKeyOptions": "Veuillez sélectionner l'option qui vous convient le mieux.", + "supportKetOptionFull": "Support complet", + "forWholeServer": "Pour tout le serveur", + "lifetimePurchase": "Achat à vie", + "supporterStatus": "Statut de supporter", + "buy": "Acheter", + "supportKeyOptionLimited": "Support limité", + "forFiveUsers": "Pour 5 utilisateurs ou moins", + "supportKeyRedeem": "Utiliser une clé de support", + "supportKeyHideSevenDays": "Masquer pendant 7 jours", + "supportKeyEnter": "Saisir la clé de support", + "supportKeyEnterDescription": "Rencontrez votre propre Pangolin de compagnie !", + "githubUsername": "Nom d'utilisateur GitHub", + "supportKeyInput": "Clé de support", + "supportKeyBuy": "Acheter une clé de support", + "logoutError": "Erreur lors de la déconnexion", + "signingAs": "Connecté en tant que", + "serverAdmin": "Admin Serveur", + "otpEnable": "Activer l'authentification à deux facteurs", + "otpDisable": "Désactiver l'authentification à deux facteurs", + "logout": "Déconnexion", + "licenseTierProfessionalRequired": "Édition Professionnelle Requise", + "licenseTierProfessionalRequiredDescription": "Cette fonctionnalité n'est disponible que dans l'Édition Professionnelle.", + "actionGetOrg": "Obtenir l'organisation", + "actionUpdateOrg": "Mettre à jour l'organisation", + "actionGetOrgUser": "Obtenir l'utilisateur de l'organisation", + "actionListOrgDomains": "Lister les domaines de l'organisation", + "actionCreateSite": "Créer un site", + "actionDeleteSite": "Supprimer un site", + "actionGetSite": "Obtenir un site", + "actionListSites": "Lister les sites", + "actionUpdateSite": "Mettre à jour un site", + "actionListSiteRoles": "Lister les rôles autorisés du site", + "actionCreateResource": "Créer une ressource", + "actionDeleteResource": "Supprimer une ressource", + "actionGetResource": "Obtenir une ressource", + "actionListResource": "Lister les ressources", + "actionUpdateResource": "Mettre à jour une ressource", + "actionListResourceUsers": "Lister les utilisateurs de la ressource", + "actionSetResourceUsers": "Définir les utilisateurs de la ressource", + "actionSetAllowedResourceRoles": "Définir les rôles autorisés de la ressource", + "actionListAllowedResourceRoles": "Lister les rôles autorisés de la ressource", + "actionSetResourcePassword": "Définir le mot de passe de la ressource", + "actionSetResourcePincode": "Définir le code PIN de la ressource", + "actionSetResourceEmailWhitelist": "Définir la liste blanche des emails de la ressource", + "actionGetResourceEmailWhitelist": "Obtenir la liste blanche des emails de la ressource", + "actionCreateTarget": "Créer une cible", + "actionDeleteTarget": "Supprimer une cible", + "actionGetTarget": "Obtenir une cible", + "actionListTargets": "Lister les cibles", + "actionUpdateTarget": "Mettre à jour une cible", + "actionCreateRole": "Créer un rôle", + "actionDeleteRole": "Supprimer un rôle", + "actionGetRole": "Obtenir un rôle", + "actionListRole": "Lister les rôles", + "actionUpdateRole": "Mettre à jour un rôle", + "actionListAllowedRoleResources": "Lister les ressources autorisées du rôle", + "actionInviteUser": "Inviter un utilisateur", + "actionRemoveUser": "Supprimer un utilisateur", + "actionListUsers": "Lister les utilisateurs", + "actionAddUserRole": "Ajouter un rôle utilisateur", + "actionGenerateAccessToken": "Générer un jeton d'accès", + "actionDeleteAccessToken": "Supprimer un jeton d'accès", + "actionListAccessTokens": "Lister les jetons d'accès", + "actionCreateResourceRule": "Créer une règle de ressource", + "actionDeleteResourceRule": "Supprimer une règle de ressource", + "actionListResourceRules": "Lister les règles de ressource", + "actionUpdateResourceRule": "Mettre à jour une règle de ressource", + "actionListOrgs": "Lister les organisations", + "actionCheckOrgId": "Vérifier l'ID", + "actionCreateOrg": "Créer une organisation", + "actionDeleteOrg": "Supprimer une organisation", + "actionListApiKeys": "Lister les clés API", + "actionListApiKeyActions": "Lister les actions des clés API", + "actionSetApiKeyActions": "Définir les actions autorisées des clés API", + "actionCreateApiKey": "Créer une clé API", + "actionDeleteApiKey": "Supprimer une clé API", + "actionCreateIdp": "Créer un IDP", + "actionUpdateIdp": "Mettre à jour un IDP", + "actionDeleteIdp": "Supprimer un IDP", + "actionListIdps": "Lister les IDP", + "actionGetIdp": "Obtenir un IDP", + "actionCreateIdpOrg": "Créer une politique d'organisation IDP", + "actionDeleteIdpOrg": "Supprimer une politique d'organisation IDP", + "actionListIdpOrgs": "Lister les organisations IDP", + "actionUpdateIdpOrg": "Mettre à jour une organisation IDP", + "noneSelected": "Aucune sélection", + "orgNotFound2": "Aucune organisation trouvée.", + "searchProgress": "Rechercher...", + "create": "Créer", + "orgs": "Organisations", + "loginError": "Une erreur s'est produite lors de la connexion", + "passwordForgot": "Mot de passe oublié ?", + "otpAuth": "Authentification à deux facteurs", + "otpAuthDescription": "Entrez le code de votre application d'authentification ou l'un de vos codes de secours à usage unique.", + "otpAuthSubmit": "Soumettre le code", + "idpContinue": "Ou continuer avec", + "otpAuthBack": "Retour à la connexion", + "navbar": "Menu de navigation", + "navbarDescription": "Menu de navigation principal de l'application", + "navbarDocsLink": "Documentation", + "commercialEdition": "Édition Commerciale", + "otpErrorEnable": "Impossible d'activer l'A2F", + "otpErrorEnableDescription": "Une erreur s'est produite lors de l'activation de l'A2F", + "otpSetupCheckCode": "Veuillez entrer un code à 6 chiffres", + "otpSetupCheckCodeRetry": "Code invalide. Veuillez réessayer.", + "otpSetup": "Activer l'authentification à deux facteurs", + "otpSetupDescription": "Sécurisez votre compte avec une couche de protection supplémentaire", + "otpSetupScanQr": "Scannez ce code QR avec votre application d'authentification ou entrez la clé secrète manuellement :", + "otpSetupSecretCode": "Code d'authentification", + "otpSetupSuccess": "Authentification à deux facteurs activée", + "otpSetupSuccessStoreBackupCodes": "Votre compte est maintenant plus sécurisé. N'oubliez pas de sauvegarder vos codes de secours.", + "otpErrorDisable": "Impossible de désactiver l'A2F", + "otpErrorDisableDescription": "Une erreur s'est produite lors de la désactivation de l'A2F", + "otpRemove": "Désactiver l'authentification à deux facteurs", + "otpRemoveDescription": "Désactiver l'authentification à deux facteurs pour votre compte", + "otpRemoveSuccess": "Authentification à deux facteurs désactivée", + "otpRemoveSuccessMessage": "L'authentification à deux facteurs a été désactivée pour votre compte. Vous pouvez la réactiver à tout moment.", + "otpRemoveSubmit": "Désactiver l'A2F", + "paginator": "Page {current} sur {last}", + "paginatorToFirst": "Aller à la première page", + "paginatorToPrevious": "Aller à la page précédente", + "paginatorToNext": "Aller à la page suivante", + "paginatorToLast": "Aller à la dernière page", + "copyText": "Copier le texte", + "copyTextFailed": "Échec de la copie du texte : ", + "copyTextClipboard": "Copier dans le presse-papiers", + "inviteErrorInvalidConfirmation": "Confirmation invalide" } diff --git a/messages/it-IT.json b/messages/it-IT.json index ea108c0e..bf22c072 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Inserisci il codice di verifica inviato al tuo indirizzo email.", "verificationCode": "Codice di Verifica", "verificationCodeEmailSent": "Abbiamo inviato un codice di verifica al tuo indirizzo email.", - "emailVerifySubmit": "Invia", + "submit": "Invia", "emailVerifyResendProgress": "Reinvio in corso...", "emailVerifyResend": "Non hai ricevuto il codice? Clicca qui per reinviare", "passwordNotMatch": "Le password non coincidono", @@ -907,5 +907,157 @@ "usersAll": "Tutti Gli Utenti", "license": "Licenza", "pangolinDashboard": "Cruscotto - Pangolino", - "noResults": "Nessun risultato trovato." + "noResults": "Nessun risultato trovato.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Tag Inseriti", + "tagsEnteredDescription": "Questi sono i tag che hai inserito.", + "tagsWarnCannotBeLessThanZero": "maxTags e minTags non possono essere minori di 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag non consentito come da opzioni di autocompletamento", + "tagsWarnInvalid": "Tag non valido secondo validateTag", + "tagWarnTooShort": "Il tag {tagText} è troppo corto", + "tagWarnTooLong": "Il tag {tagText} è troppo lungo", + "tagsWarnReachedMaxNumber": "Raggiunto il numero massimo di tag consentiti", + "tagWarnDuplicate": "Tag duplicato {tagText} non aggiunto", + "supportKeyInvalid": "Chiave Non Valida", + "supportKeyInvalidDescription": "La tua chiave di supporto non è valida.", + "supportKeyValid": "Chiave Valida", + "supportKeyValidDescription": "La tua chiave di supporto è stata convalidata. Grazie per il tuo sostegno!", + "supportKeyErrorValidationDescription": "Impossibile convalidare la chiave di supporto.", + "supportKey": "Supporta lo Sviluppo e Adotta un Pangolino!", + "supportKeyDescription": "Acquista una chiave di supporto per aiutarci a continuare a sviluppare Pangolin per la comunità. Il tuo contributo ci permette di dedicare più tempo alla manutenzione e all'aggiunta di nuove funzionalità per tutti. Non useremo mai questo per bloccare le funzionalità. Questo è separato da qualsiasi Edizione Commerciale.", + "supportKeyPet": "Potrai anche adottare e incontrare il tuo pangolino personale!", + "supportKeyPurchase": "I pagamenti sono elaborati tramite GitHub. Successivamente, potrai recuperare la tua chiave su", + "supportKeyPurchaseLink": "il nostro sito web", + "supportKeyPurchase2": "e riscattarla qui.", + "supportKeyLearnMore": "Scopri di più.", + "supportKeyOptions": "Seleziona l'opzione più adatta a te.", + "supportKetOptionFull": "Supporto Completo", + "forWholeServer": "Per l'intero server", + "lifetimePurchase": "Acquisto a vita", + "supporterStatus": "Stato supportatore", + "buy": "Acquista", + "supportKeyOptionLimited": "Supporto Limitato", + "forFiveUsers": "Per 5 o meno utenti", + "supportKeyRedeem": "Riscatta Chiave di Supporto", + "supportKeyHideSevenDays": "Nascondi per 7 giorni", + "supportKeyEnter": "Inserisci Chiave di Supporto", + "supportKeyEnterDescription": "Incontra il tuo pangolino personale!", + "githubUsername": "Username GitHub", + "supportKeyInput": "Chiave di Supporto", + "supportKeyBuy": "Acquista Chiave di Supporto", + "logoutError": "Errore durante il logout", + "signingAs": "Accesso come", + "serverAdmin": "Amministratore Server", + "otpEnable": "Abilita Autenticazione a Due Fattori", + "otpDisable": "Disabilita Autenticazione a Due Fattori", + "logout": "Disconnetti", + "licenseTierProfessionalRequired": "Edizione Professional Richiesta", + "licenseTierProfessionalRequiredDescription": "Questa funzionalità è disponibile solo nell'Edizione Professional.", + "actionGetOrg": "Ottieni Organizzazione", + "actionUpdateOrg": "Aggiorna Organizzazione", + "actionGetOrgUser": "Ottieni Utente Organizzazione", + "actionListOrgDomains": "Elenca Domini Organizzazione", + "actionCreateSite": "Crea Sito", + "actionDeleteSite": "Elimina Sito", + "actionGetSite": "Ottieni Sito", + "actionListSites": "Elenca Siti", + "actionUpdateSite": "Aggiorna Sito", + "actionListSiteRoles": "Elenca Ruoli Sito Consentiti", + "actionCreateResource": "Crea Risorsa", + "actionDeleteResource": "Elimina Risorsa", + "actionGetResource": "Ottieni Risorsa", + "actionListResource": "Elenca Risorse", + "actionUpdateResource": "Aggiorna Risorsa", + "actionListResourceUsers": "Elenca Utenti Risorsa", + "actionSetResourceUsers": "Imposta Utenti Risorsa", + "actionSetAllowedResourceRoles": "Imposta Ruoli Risorsa Consentiti", + "actionListAllowedResourceRoles": "Elenca Ruoli Risorsa Consentiti", + "actionSetResourcePassword": "Imposta Password Risorsa", + "actionSetResourcePincode": "Imposta Codice PIN Risorsa", + "actionSetResourceEmailWhitelist": "Imposta Lista Autorizzazioni Email Risorsa", + "actionGetResourceEmailWhitelist": "Ottieni Lista Autorizzazioni Email Risorsa", + "actionCreateTarget": "Crea Target", + "actionDeleteTarget": "Elimina Target", + "actionGetTarget": "Ottieni Target", + "actionListTargets": "Elenca Target", + "actionUpdateTarget": "Aggiorna Target", + "actionCreateRole": "Crea Ruolo", + "actionDeleteRole": "Elimina Ruolo", + "actionGetRole": "Ottieni Ruolo", + "actionListRole": "Elenca Ruoli", + "actionUpdateRole": "Aggiorna Ruolo", + "actionListAllowedRoleResources": "Elenca Risorse Ruolo Consentite", + "actionInviteUser": "Invita Utente", + "actionRemoveUser": "Rimuovi Utente", + "actionListUsers": "Elenca Utenti", + "actionAddUserRole": "Aggiungi Ruolo Utente", + "actionGenerateAccessToken": "Genera Token di Accesso", + "actionDeleteAccessToken": "Elimina Token di Accesso", + "actionListAccessTokens": "Elenca Token di Accesso", + "actionCreateResourceRule": "Crea Regola Risorsa", + "actionDeleteResourceRule": "Elimina Regola Risorsa", + "actionListResourceRules": "Elenca Regole Risorsa", + "actionUpdateResourceRule": "Aggiorna Regola Risorsa", + "actionListOrgs": "Elenca Organizzazioni", + "actionCheckOrgId": "Controlla ID", + "actionCreateOrg": "Crea Organizzazione", + "actionDeleteOrg": "Elimina Organizzazione", + "actionListApiKeys": "Elenca Chiavi API", + "actionListApiKeyActions": "Elenca Azioni Chiave API", + "actionSetApiKeyActions": "Imposta Azioni Consentite Chiave API", + "actionCreateApiKey": "Crea Chiave API", + "actionDeleteApiKey": "Elimina Chiave API", + "actionCreateIdp": "Crea IDP", + "actionUpdateIdp": "Aggiorna IDP", + "actionDeleteIdp": "Elimina IDP", + "actionListIdps": "Elenca IDP", + "actionGetIdp": "Ottieni IDP", + "actionCreateIdpOrg": "Crea Politica Org IDP", + "actionDeleteIdpOrg": "Elimina Politica Org IDP", + "actionListIdpOrgs": "Elenca Org IDP", + "actionUpdateIdpOrg": "Aggiorna Org IDP", + "noneSelected": "Nessuna selezione", + "orgNotFound2": "Nessuna organizzazione trovata.", + "searchProgress": "Ricerca...", + "create": "Crea", + "orgs": "Organizzazioni", + "loginError": "Si è verificato un errore durante l'accesso", + "passwordForgot": "Password dimenticata?", + "otpAuth": "Autenticazione a Due Fattori", + "otpAuthDescription": "Inserisci il codice dalla tua app di autenticazione o uno dei tuoi codici di backup monouso.", + "otpAuthSubmit": "Invia Codice", + "idpContinue": "O continua con", + "otpAuthBack": "Torna al Login", + "navbar": "Menu di Navigazione", + "navbarDescription": "Menu di navigazione principale dell'applicazione", + "navbarDocsLink": "Documentazione", + "commercialEdition": "Edizione Commerciale", + "otpErrorEnable": "Impossibile abilitare 2FA", + "otpErrorEnableDescription": "Si è verificato un errore durante l'abilitazione di 2FA", + "otpSetupCheckCode": "Inserisci un codice a 6 cifre", + "otpSetupCheckCodeRetry": "Codice non valido. Riprova.", + "otpSetup": "Abilita Autenticazione a Due Fattori", + "otpSetupDescription": "Proteggi il tuo account con un livello extra di protezione", + "otpSetupScanQr": "Scansiona questo codice QR con la tua app di autenticazione o inserisci manualmente la chiave segreta:", + "otpSetupSecretCode": "Codice Autenticatore", + "otpSetupSuccess": "Autenticazione a Due Fattori Abilitata", + "otpSetupSuccessStoreBackupCodes": "Il tuo account è ora più sicuro. Non dimenticare di salvare i tuoi codici di backup.", + "otpErrorDisable": "Impossibile disabilitare 2FA", + "otpErrorDisableDescription": "Si è verificato un errore durante la disabilitazione di 2FA", + "otpRemove": "Disabilita Autenticazione a Due Fattori", + "otpRemoveDescription": "Disabilita l'autenticazione a due fattori per il tuo account", + "otpRemoveSuccess": "Autenticazione a Due Fattori Disabilitata", + "otpRemoveSuccessMessage": "L'autenticazione a due fattori è stata disabilitata per il tuo account. Puoi riattivarla in qualsiasi momento.", + "otpRemoveSubmit": "Disabilita 2FA", + "paginator": "Pagina {current} di {last}", + "paginatorToFirst": "Vai alla prima pagina", + "paginatorToPrevious": "Vai alla pagina precedente", + "paginatorToNext": "Vai alla pagina successiva", + "paginatorToLast": "Vai all'ultima pagina", + "copyText": "Copia testo", + "copyTextFailed": "Impossibile copiare il testo: ", + "copyTextClipboard": "Copia negli appunti", + "inviteErrorInvalidConfirmation": "Conferma non valida" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 4fb99da9..fe5c2b4b 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Wprowadź kod weryfikacyjny wysłany na Twój adres e-mail.", "verificationCode": "Kod weryfikacyjny", "verificationCodeEmailSent": "Wysłaliśmy kod weryfikacyjny na Twój adres e-mail.", - "emailVerifySubmit": "Wyślij", + "submit": "Wyślij", "emailVerifyResendProgress": "Ponowne wysyłanie...", "emailVerifyResend": "Nie otrzymałeś kodu? Kliknij tutaj, aby wysłać ponownie", "passwordNotMatch": "Hasła nie są zgodne", @@ -907,5 +907,157 @@ "usersAll": "Wszyscy użytkownicy", "license": "Licencja", "pangolinDashboard": "Panel - Pangolin", - "noResults": "Nie znaleziono wyników." + "noResults": "Nie znaleziono wyników.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Wprowadzone tagi", + "tagsEnteredDescription": "To są wprowadzone przez ciebie tagi.", + "tagsWarnCannotBeLessThanZero": "maxTags i minTags nie mogą być mniejsze od 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag niedozwolony zgodnie z opcjami autouzupełniania", + "tagsWarnInvalid": "Nieprawidłowy tag według validateTag", + "tagWarnTooShort": "Tag {tagText} jest za krótki", + "tagWarnTooLong": "Tag {tagText} jest za długi", + "tagsWarnReachedMaxNumber": "Osiągnięto maksymalną dozwoloną liczbę tagów", + "tagWarnDuplicate": "Zduplikowany tag {tagText} nie został dodany", + "supportKeyInvalid": "Nieprawidłowy klucz", + "supportKeyInvalidDescription": "Twój klucz wspierający jest nieprawidłowy.", + "supportKeyValid": "Prawidłowy klucz", + "supportKeyValidDescription": "Twój klucz wspierający został zweryfikowany. Dziękujemy za wsparcie!", + "supportKeyErrorValidationDescription": "Nie udało się zweryfikować klucza wspierającego.", + "supportKey": "Wesprzyj rozwój i adoptuj Pangolina!", + "supportKeyDescription": "Kup klucz wspierający, aby pomóc nam w dalszym rozwijaniu Pangolina dla społeczności. Twój wkład pozwala nam poświęcić więcej czasu na utrzymanie i dodawanie nowych funkcji do aplikacji dla wszystkich. Nigdy nie wykorzystamy tego do blokowania funkcji za paywallem. Jest to oddzielne od wydania komercyjnego.", + "supportKeyPet": "Będziesz mógł także zaadoptować i poznać swojego własnego zwierzaka Pangolina!", + "supportKeyPurchase": "Płatności są przetwarzane przez GitHub. Następnie możesz pobrać swój klucz na", + "supportKeyPurchaseLink": "naszej stronie", + "supportKeyPurchase2": "i wykorzystać go tutaj.", + "supportKeyLearnMore": "Dowiedz się więcej.", + "supportKeyOptions": "Wybierz opcję, która najbardziej ci odpowiada.", + "supportKetOptionFull": "Pełne wsparcie", + "forWholeServer": "Dla całego serwera", + "lifetimePurchase": "Zakup dożywotni", + "supporterStatus": "Status wspierającego", + "buy": "Kup", + "supportKeyOptionLimited": "Ograniczone wsparcie", + "forFiveUsers": "Dla 5 lub mniej użytkowników", + "supportKeyRedeem": "Wykorzystaj klucz wspierający", + "supportKeyHideSevenDays": "Ukryj na 7 dni", + "supportKeyEnter": "Wprowadź klucz wspierający", + "supportKeyEnterDescription": "Poznaj swojego własnego zwierzaka Pangolina!", + "githubUsername": "Nazwa użytkownika GitHub", + "supportKeyInput": "Klucz wspierający", + "supportKeyBuy": "Kup klucz wspierający", + "logoutError": "Błąd podczas wylogowywania", + "signingAs": "Zalogowany jako", + "serverAdmin": "Administrator serwera", + "otpEnable": "Włącz uwierzytelnianie dwuskładnikowe", + "otpDisable": "Wyłącz uwierzytelnianie dwuskładnikowe", + "logout": "Wyloguj się", + "licenseTierProfessionalRequired": "Wymagana edycja Professional", + "licenseTierProfessionalRequiredDescription": "Ta funkcja jest dostępna tylko w edycji Professional.", + "actionGetOrg": "Pobierz organizację", + "actionUpdateOrg": "Aktualizuj organizację", + "actionGetOrgUser": "Pobierz użytkownika organizacji", + "actionListOrgDomains": "Lista domen organizacji", + "actionCreateSite": "Utwórz witrynę", + "actionDeleteSite": "Usuń witrynę", + "actionGetSite": "Pobierz witrynę", + "actionListSites": "Lista witryn", + "actionUpdateSite": "Aktualizuj witrynę", + "actionListSiteRoles": "Lista dozwolonych ról witryny", + "actionCreateResource": "Utwórz zasób", + "actionDeleteResource": "Usuń zasób", + "actionGetResource": "Pobierz zasób", + "actionListResource": "Lista zasobów", + "actionUpdateResource": "Aktualizuj zasób", + "actionListResourceUsers": "Lista użytkowników zasobu", + "actionSetResourceUsers": "Ustaw użytkowników zasobu", + "actionSetAllowedResourceRoles": "Ustaw dozwolone role zasobu", + "actionListAllowedResourceRoles": "Lista dozwolonych ról zasobu", + "actionSetResourcePassword": "Ustaw hasło zasobu", + "actionSetResourcePincode": "Ustaw kod PIN zasobu", + "actionSetResourceEmailWhitelist": "Ustaw białą listę email zasobu", + "actionGetResourceEmailWhitelist": "Pobierz białą listę email zasobu", + "actionCreateTarget": "Utwórz cel", + "actionDeleteTarget": "Usuń cel", + "actionGetTarget": "Pobierz cel", + "actionListTargets": "Lista celów", + "actionUpdateTarget": "Aktualizuj cel", + "actionCreateRole": "Utwórz rolę", + "actionDeleteRole": "Usuń rolę", + "actionGetRole": "Pobierz rolę", + "actionListRole": "Lista ról", + "actionUpdateRole": "Aktualizuj rolę", + "actionListAllowedRoleResources": "Lista dozwolonych zasobów roli", + "actionInviteUser": "Zaproś użytkownika", + "actionRemoveUser": "Usuń użytkownika", + "actionListUsers": "Lista użytkowników", + "actionAddUserRole": "Dodaj rolę użytkownika", + "actionGenerateAccessToken": "Wygeneruj token dostępu", + "actionDeleteAccessToken": "Usuń token dostępu", + "actionListAccessTokens": "Lista tokenów dostępu", + "actionCreateResourceRule": "Utwórz regułę zasobu", + "actionDeleteResourceRule": "Usuń regułę zasobu", + "actionListResourceRules": "Lista reguł zasobu", + "actionUpdateResourceRule": "Aktualizuj regułę zasobu", + "actionListOrgs": "Lista organizacji", + "actionCheckOrgId": "Sprawdź ID", + "actionCreateOrg": "Utwórz organizację", + "actionDeleteOrg": "Usuń organizację", + "actionListApiKeys": "Lista kluczy API", + "actionListApiKeyActions": "Lista akcji klucza API", + "actionSetApiKeyActions": "Ustaw dozwolone akcje klucza API", + "actionCreateApiKey": "Utwórz klucz API", + "actionDeleteApiKey": "Usuń klucz API", + "actionCreateIdp": "Utwórz IDP", + "actionUpdateIdp": "Aktualizuj IDP", + "actionDeleteIdp": "Usuń IDP", + "actionListIdps": "Lista IDP", + "actionGetIdp": "Pobierz IDP", + "actionCreateIdpOrg": "Utwórz politykę organizacji IDP", + "actionDeleteIdpOrg": "Usuń politykę organizacji IDP", + "actionListIdpOrgs": "Lista organizacji IDP", + "actionUpdateIdpOrg": "Aktualizuj organizację IDP", + "noneSelected": "Nie wybrano", + "orgNotFound2": "Nie znaleziono organizacji.", + "searchProgress": "Szukaj...", + "create": "Utwórz", + "orgs": "Organizacje", + "loginError": "Wystąpił błąd podczas logowania", + "passwordForgot": "Zapomniałeś hasła?", + "otpAuth": "Uwierzytelnianie dwuskładnikowe", + "otpAuthDescription": "Wprowadź kod z aplikacji uwierzytelniającej lub jeden z jednorazowych kodów zapasowych.", + "otpAuthSubmit": "Wyślij kod", + "idpContinue": "Lub kontynuuj z", + "otpAuthBack": "Powrót do logowania", + "navbar": "Menu nawigacyjne", + "navbarDescription": "Główne menu nawigacyjne aplikacji", + "navbarDocsLink": "Dokumentacja", + "commercialEdition": "Edycja komercyjna", + "otpErrorEnable": "Nie można włączyć 2FA", + "otpErrorEnableDescription": "Wystąpił błąd podczas włączania 2FA", + "otpSetupCheckCode": "Wprowadź 6-cyfrowy kod", + "otpSetupCheckCodeRetry": "Nieprawidłowy kod. Spróbuj ponownie.", + "otpSetup": "Włącz uwierzytelnianie dwuskładnikowe", + "otpSetupDescription": "Zabezpiecz swoje konto dodatkową warstwą ochrony", + "otpSetupScanQr": "Zeskanuj ten kod QR za pomocą aplikacji uwierzytelniającej lub wprowadź klucz tajny ręcznie:", + "otpSetupSecretCode": "Kod uwierzytelniający", + "otpSetupSuccess": "Włączono uwierzytelnianie dwuskładnikowe", + "otpSetupSuccessStoreBackupCodes": "Twoje konto jest teraz bezpieczniejsze. Nie zapomnij zapisać kodów zapasowych.", + "otpErrorDisable": "Nie można wyłączyć 2FA", + "otpErrorDisableDescription": "Wystąpił błąd podczas wyłączania 2FA", + "otpRemove": "Wyłącz uwierzytelnianie dwuskładnikowe", + "otpRemoveDescription": "Wyłącz uwierzytelnianie dwuskładnikowe dla swojego konta", + "otpRemoveSuccess": "Wyłączono uwierzytelnianie dwuskładnikowe", + "otpRemoveSuccessMessage": "Uwierzytelnianie dwuskładnikowe zostało wyłączone dla Twojego konta. Możesz je włączyć ponownie w dowolnym momencie.", + "otpRemoveSubmit": "Wyłącz 2FA", + "paginator": "Strona {current} z {last}", + "paginatorToFirst": "Przejdź do pierwszej strony", + "paginatorToPrevious": "Przejdź do poprzedniej strony", + "paginatorToNext": "Przejdź do następnej strony", + "paginatorToLast": "Przejdź do ostatniej strony", + "copyText": "Kopiuj tekst", + "copyTextFailed": "Nie udało się skopiować tekstu: ", + "copyTextClipboard": "Kopiuj do schowka", + "inviteErrorInvalidConfirmation": "Nieprawidłowe potwierdzenie" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1fb46b25..e36b651c 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Insira o código de verificação enviado para o seu email.", "verificationCode": "Código de Verificação", "verificationCodeEmailSent": "Enviámos um código de verificação para o seu email.", - "emailVerifySubmit": "Submeter", + "submit": "Submeter", "emailVerifyResendProgress": "A reenviar...", "emailVerifyResend": "Não recebeu um código? Clique aqui para reenviar", "passwordNotMatch": "As palavras-passe não correspondem", @@ -907,5 +907,157 @@ "usersAll": "Todos os Usuários", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", - "noResults": "Nenhum resultado encontrado." + "noResults": "Nenhum resultado encontrado.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Tags Inseridas", + "tagsEnteredDescription": "Estas são as tags que você inseriu.", + "tagsWarnCannotBeLessThanZero": "maxTags e minTags não podem ser menores que 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag não permitida conforme as opções de autocompletar", + "tagsWarnInvalid": "Tag inválida conforme validateTag", + "tagWarnTooShort": "A tag {tagText} é muito curta", + "tagWarnTooLong": "A tag {tagText} é muito longa", + "tagsWarnReachedMaxNumber": "Atingido o número máximo de tags permitidas", + "tagWarnDuplicate": "Tag duplicada {tagText} não adicionada", + "supportKeyInvalid": "Chave Inválida", + "supportKeyInvalidDescription": "A sua chave de suporte é inválida.", + "supportKeyValid": "Chave Válida", + "supportKeyValidDescription": "A sua chave de suporte foi validada. Obrigado pelo seu apoio!", + "supportKeyErrorValidationDescription": "Falha ao validar a chave de suporte.", + "supportKey": "Apoie o Desenvolvimento e Adote um Pangolim!", + "supportKeyDescription": "Compre uma chave de suporte para nos ajudar a continuar desenvolvendo o Pangolin para a comunidade. A sua contribuição permite-nos dedicar mais tempo para manter e adicionar novos recursos à aplicação para todos. Nunca usaremos isto para restringir recursos. Isto é separado de qualquer Edição Comercial.", + "supportKeyPet": "Também poderá adotar e conhecer o seu próprio Pangolim de estimação!", + "supportKeyPurchase": "Os pagamentos são processados via GitHub. Depois, pode obter a sua chave em", + "supportKeyPurchaseLink": "nosso site", + "supportKeyPurchase2": "e resgatá-la aqui.", + "supportKeyLearnMore": "Saiba mais.", + "supportKeyOptions": "Por favor, selecione a opção que melhor se adequa a si.", + "supportKetOptionFull": "Apoiante Completo", + "forWholeServer": "Para todo o servidor", + "lifetimePurchase": "Compra vitalícia", + "supporterStatus": "Estado de apoiante", + "buy": "Comprar", + "supportKeyOptionLimited": "Apoiante Limitado", + "forFiveUsers": "Para 5 ou menos utilizadores", + "supportKeyRedeem": "Resgatar Chave de Apoiante", + "supportKeyHideSevenDays": "Ocultar por 7 dias", + "supportKeyEnter": "Inserir Chave de Apoiante", + "supportKeyEnterDescription": "Conheça o seu próprio Pangolim de estimação!", + "githubUsername": "Nome de Utilizador GitHub", + "supportKeyInput": "Chave de Apoiante", + "supportKeyBuy": "Comprar Chave de Apoiante", + "logoutError": "Erro ao terminar sessão", + "signingAs": "Sessão iniciada como", + "serverAdmin": "Administrador do Servidor", + "otpEnable": "Ativar Autenticação de Dois Fatores", + "otpDisable": "Desativar Autenticação de Dois Fatores", + "logout": "Terminar Sessão", + "licenseTierProfessionalRequired": "Edição Profissional Necessária", + "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", + "actionGetOrg": "Obter Organização", + "actionUpdateOrg": "Atualizar Organização", + "actionGetOrgUser": "Obter Utilizador da Organização", + "actionListOrgDomains": "Listar Domínios da Organização", + "actionCreateSite": "Criar Site", + "actionDeleteSite": "Eliminar Site", + "actionGetSite": "Obter Site", + "actionListSites": "Listar Sites", + "actionUpdateSite": "Atualizar Site", + "actionListSiteRoles": "Listar Funções Permitidas do Site", + "actionCreateResource": "Criar Recurso", + "actionDeleteResource": "Eliminar Recurso", + "actionGetResource": "Obter Recurso", + "actionListResource": "Listar Recursos", + "actionUpdateResource": "Atualizar Recurso", + "actionListResourceUsers": "Listar Utilizadores do Recurso", + "actionSetResourceUsers": "Definir Utilizadores do Recurso", + "actionSetAllowedResourceRoles": "Definir Funções Permitidas do Recurso", + "actionListAllowedResourceRoles": "Listar Funções Permitidas do Recurso", + "actionSetResourcePassword": "Definir Palavra-passe do Recurso", + "actionSetResourcePincode": "Definir Código PIN do Recurso", + "actionSetResourceEmailWhitelist": "Definir Lista Permitida de Emails do Recurso", + "actionGetResourceEmailWhitelist": "Obter Lista Permitida de Emails do Recurso", + "actionCreateTarget": "Criar Alvo", + "actionDeleteTarget": "Eliminar Alvo", + "actionGetTarget": "Obter Alvo", + "actionListTargets": "Listar Alvos", + "actionUpdateTarget": "Atualizar Alvo", + "actionCreateRole": "Criar Função", + "actionDeleteRole": "Eliminar Função", + "actionGetRole": "Obter Função", + "actionListRole": "Listar Funções", + "actionUpdateRole": "Atualizar Função", + "actionListAllowedRoleResources": "Listar Recursos Permitidos da Função", + "actionInviteUser": "Convidar Utilizador", + "actionRemoveUser": "Remover Utilizador", + "actionListUsers": "Listar Utilizadores", + "actionAddUserRole": "Adicionar Função ao Utilizador", + "actionGenerateAccessToken": "Gerar Token de Acesso", + "actionDeleteAccessToken": "Eliminar Token de Acesso", + "actionListAccessTokens": "Listar Tokens de Acesso", + "actionCreateResourceRule": "Criar Regra de Recurso", + "actionDeleteResourceRule": "Eliminar Regra de Recurso", + "actionListResourceRules": "Listar Regras de Recurso", + "actionUpdateResourceRule": "Atualizar Regra de Recurso", + "actionListOrgs": "Listar Organizações", + "actionCheckOrgId": "Verificar ID", + "actionCreateOrg": "Criar Organização", + "actionDeleteOrg": "Eliminar Organização", + "actionListApiKeys": "Listar Chaves API", + "actionListApiKeyActions": "Listar Ações da Chave API", + "actionSetApiKeyActions": "Definir Ações Permitidas da Chave API", + "actionCreateApiKey": "Criar Chave API", + "actionDeleteApiKey": "Eliminar Chave API", + "actionCreateIdp": "Criar IDP", + "actionUpdateIdp": "Atualizar IDP", + "actionDeleteIdp": "Eliminar IDP", + "actionListIdps": "Listar IDP", + "actionGetIdp": "Obter IDP", + "actionCreateIdpOrg": "Criar Política de Organização IDP", + "actionDeleteIdpOrg": "Eliminar Política de Organização IDP", + "actionListIdpOrgs": "Listar Organizações IDP", + "actionUpdateIdpOrg": "Atualizar Organização IDP", + "noneSelected": "Nenhum selecionado", + "orgNotFound2": "Nenhuma organização encontrada.", + "searchProgress": "Pesquisar...", + "create": "Criar", + "orgs": "Organizações", + "loginError": "Ocorreu um erro ao iniciar sessão", + "passwordForgot": "Esqueceu a sua palavra-passe?", + "otpAuth": "Autenticação de Dois Fatores", + "otpAuthDescription": "Insira o código da sua aplicação de autenticação ou um dos seus códigos de backup de uso único.", + "otpAuthSubmit": "Submeter Código", + "idpContinue": "Ou continuar com", + "otpAuthBack": "Voltar ao Início de Sessão", + "navbar": "Menu de Navegação", + "navbarDescription": "Menu de navegação principal da aplicação", + "navbarDocsLink": "Documentação", + "commercialEdition": "Edição Comercial", + "otpErrorEnable": "Não foi possível ativar 2FA", + "otpErrorEnableDescription": "Ocorreu um erro ao ativar 2FA", + "otpSetupCheckCode": "Por favor, insira um código de 6 dígitos", + "otpSetupCheckCodeRetry": "Código inválido. Por favor, tente novamente.", + "otpSetup": "Ativar Autenticação de Dois Fatores", + "otpSetupDescription": "Proteja a sua conta com uma camada extra de proteção", + "otpSetupScanQr": "Digitalize este código QR com a sua aplicação de autenticação ou insira a chave secreta manualmente:", + "otpSetupSecretCode": "Código de Autenticação", + "otpSetupSuccess": "Autenticação de Dois Fatores Ativada", + "otpSetupSuccessStoreBackupCodes": "A sua conta está agora mais segura. Não se esqueça de guardar os seus códigos de backup.", + "otpErrorDisable": "Não foi possível desativar 2FA", + "otpErrorDisableDescription": "Ocorreu um erro ao desativar 2FA", + "otpRemove": "Desativar Autenticação de Dois Fatores", + "otpRemoveDescription": "Desativar a autenticação de dois fatores para a sua conta", + "otpRemoveSuccess": "Autenticação de Dois Fatores Desativada", + "otpRemoveSuccessMessage": "A autenticação de dois fatores foi desativada para a sua conta. Pode ativá-la novamente a qualquer momento.", + "otpRemoveSubmit": "Desativar 2FA", + "paginator": "Página {current} de {last}", + "paginatorToFirst": "Ir para a primeira página", + "paginatorToPrevious": "Ir para a página anterior", + "paginatorToNext": "Ir para a próxima página", + "paginatorToLast": "Ir para a última página", + "copyText": "Copiar texto", + "copyTextFailed": "Falha ao copiar texto: ", + "copyTextClipboard": "Copiar para a área de transferência", + "inviteErrorInvalidConfirmation": "Confirmação inválida" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 79d9a647..712e177f 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -816,7 +816,7 @@ "emailVerifyDescription": "Enter the verification code sent to your email address.", "verificationCode": "Verification Code", "verificationCodeEmailSent": "We sent a verification code to your email address.", - "emailVerifySubmit": "Submit", + "submit": "Submit", "emailVerifyResendProgress": "Resending...", "emailVerifyResend": "Didn't receive a code? Click here to resend", "passwordNotMatch": "Passwords do not match", @@ -907,5 +907,157 @@ "usersAll": "All Users", "license": "License", "pangolinDashboard": "Dashboard - Pangolin", - "noResults": "No results found." + "noResults": "No results found.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Entered Tags", + "tagsEnteredDescription": "These are the tags you`ve entered.", + "tagsWarnCannotBeLessThanZero": "maxTags and minTags cannot be less than 0", + "tagsWarnNotAllowedAutocompleteOptions": "Tag not allowed as per autocomplete options", + "tagsWarnInvalid": "Invalid tag as per validateTag", + "tagWarnTooShort": "Tag {tagText} is too short", + "tagWarnTooLong": "Tag {tagText} is too long", + "tagsWarnReachedMaxNumber": "Reached the maximum number of tags allowed", + "tagWarnDuplicate": "Duplicate tag {tagText} not added", + "supportKeyInvalid": "Invalid Key", + "supportKeyInvalidDescription": "Your supporter key is invalid.", + "supportKeyValid": "Valid Key", + "supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!", + "supportKeyErrorValidationDescription": "Failed to validate supporter key.", + "supportKey": "Support Development and Adopt a Pangolin!", + "supportKeyDescription": "Purchase a supporter key to help us continue developing Pangolin for the community. Your contribution allows us to commit more time to maintain and add new features to the application for everyone. We will never use this to paywall features. This is separate from any Commercial Edition.", + "supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!", + "supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on", + "supportKeyPurchaseLink": "our website", + "supportKeyPurchase2": "and redeem it here.", + "supportKeyLearnMore": "Learn more.", + "supportKeyOptions": "Please select the option that best suits you.", + "supportKetOptionFull": "Full Supporter", + "forWholeServer": "For the whole server", + "lifetimePurchase": "Lifetime purchase", + "supporterStatus": "Supporter status", + "buy": "Buy", + "supportKeyOptionLimited": "Limited Supporter", + "forFiveUsers": "For 5 or less users", + "supportKeyRedeem": "Redeem Supporter Key", + "supportKeyHideSevenDays": "Hide for 7 days", + "supportKeyEnter": "Enter Supporter Key", + "supportKeyEnterDescription": "Meet your very own pet Pangolin!", + "githubUsername": "GitHub Username", + "supportKeyInput": "Supporter Key", + "supportKeyBuy": "Buy Supporter Key", + "logoutError": "Error logging out", + "signingAs": "Signed in as", + "serverAdmin": "Server Admin", + "otpEnable": "Enable Two-factor", + "otpDisable": "Disable Two-factor", + "logout": "Log Out", + "licenseTierProfessionalRequired": "Professional Edition Required", + "licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.", + "actionGetOrg": "Get Organization", + "actionUpdateOrg": "Update Organization", + "actionGetOrgUser": "Get Organization User", + "actionListOrgDomains": "List Organization Domains", + "actionCreateSite": "Create Site", + "actionDeleteSite": "Delete Site", + "actionGetSite": "Get Site", + "actionListSites": "List Sites", + "actionUpdateSite": "Update Site", + "actionListSiteRoles": "List Allowed Site Roles", + "actionCreateResource": "Create Resource", + "actionDeleteResource": "Delete Resource", + "actionGetResource": "Get Resource", + "actionListResource": "List Resources", + "actionUpdateResource": "Update Resource", + "actionListResourceUsers": "List Resource Users", + "actionSetResourceUsers": "Set Resource Users", + "actionSetAllowedResourceRoles": "Set Allowed Resource Roles", + "actionListAllowedResourceRoles": "List Allowed Resource Roles", + "actionSetResourcePassword": "Set Resource Password", + "actionSetResourcePincode": "Set Resource Pincode", + "actionSetResourceEmailWhitelist": "Set Resource Email Whitelist", + "actionGetResourceEmailWhitelist": "Get Resource Email Whitelist", + "actionCreateTarget": "Create Target", + "actionDeleteTarget": "Delete Target", + "actionGetTarget": "Get Target", + "actionListTargets": "List Targets", + "actionUpdateTarget": "Update Target", + "actionCreateRole": "Create Role", + "actionDeleteRole": "Delete Role", + "actionGetRole": "Get Role", + "actionListRole": "List Roles", + "actionUpdateRole": "Update Role", + "actionListAllowedRoleResources": "List Allowed Role Resources", + "actionInviteUser": "Invite User", + "actionRemoveUser": "Remove User", + "actionListUsers": "List Users", + "actionAddUserRole": "Add User Role", + "actionGenerateAccessToken": "Generate Access Token", + "actionDeleteAccessToken": "Delete Access Token", + "actionListAccessTokens": "List Access Tokens", + "actionCreateResourceRule": "Create Resource Rule", + "actionDeleteResourceRule": "Delete Resource Rule", + "actionListResourceRules": "List Resource Rules", + "actionUpdateResourceRule": "Update Resource Rule", + "actionListOrgs": "List Organizations", + "actionCheckOrgId": "Check ID", + "actionCreateOrg": "Create Organization", + "actionDeleteOrg": "Delete Organization", + "actionListApiKeys": "List API Keys", + "actionListApiKeyActions": "List API Key Actions", + "actionSetApiKeyActions": "Set API Key Allowed Actions", + "actionCreateApiKey": "Create API Key", + "actionDeleteApiKey": "Delete API Key", + "actionCreateIdp": "Create IDP", + "actionUpdateIdp": "Update IDP", + "actionDeleteIdp": "Delete IDP", + "actionListIdps": "List IDP", + "actionGetIdp": "Get IDP", + "actionCreateIdpOrg": "Create IDP Org Policy", + "actionDeleteIdpOrg": "Delete IDP Org Policy", + "actionListIdpOrgs": "List IDP Orgs", + "actionUpdateIdpOrg": "Update IDP Org", + "noneSelected": "None selected", + "orgNotFound2": "No organizations found.", + "searchProgress": "Search...", + "create": "Create", + "orgs": "Organizations", + "loginError": "An error occurred while logging in", + "passwordForgot": "Forgot your password?", + "otpAuth": "Two-Factor Authentication", + "otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.", + "otpAuthSubmit": "Submit Code", + "idpContinue": "Or continue with", + "otpAuthBack": "Back to Log In", + "navbar": "Navigation Menu", + "navbarDescription": "Main navigation menu for the application", + "navbarDocsLink": "Documentation", + "commercialEdition": "Commercial Edition", + "otpErrorEnable": "Unable to enable 2FA", + "otpErrorEnableDescription": "An error occurred while enabling 2FA", + "otpSetupCheckCode": "Please enter a 6-digit code", + "otpSetupCheckCodeRetry": "Invalid code. Please try again.", + "otpSetup": "Enable Two-factor Authentication", + "otpSetupDescription": "Secure your account with an extra layer of protection", + "otpSetupScanQr": "Scan this QR code with your authenticator app or enter the secret key manually:", + "otpSetupSecretCode": "Authenticator Code", + "otpSetupSuccess": "Two-Factor Authentication Enabled", + "otpSetupSuccessStoreBackupCodes": "Your account is now more secure. Don't forget to save your backup codes.", + "otpErrorDisable": "Unable to disable 2FA", + "otpErrorDisableDescription": "An error occurred while disabling 2FA", + "otpRemove": "Disable Two-factor Authentication", + "otpRemoveDescription": "Disable two-factor authentication for your account", + "otpRemoveSuccess": "Two-Factor Authentication Disabled", + "otpRemoveSuccessMessage": "Two-factor authentication has been disabled for your account. You can enable it again at any time.", + "otpRemoveSubmit": "Disable 2FA", + "paginator": "Page {current} of {last}", + "paginatorToFirst": "Go to first page", + "paginatorToPrevious": "Go to previous page", + "paginatorToNext": "Go to next page", + "paginatorToLast": "Go to last page", + "copyText": "Copy text", + "copyTextFailed": "Failed to copy text: ", + "copyTextClipboard": "Copy to clipboard", + "inviteErrorInvalidConfirmation": "Invalid confirmation" } diff --git a/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx b/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx index eb44eb40..dca75f5a 100644 --- a/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx +++ b/src/app/[orgId]/settings/access/roles/CreateRoleForm.tsx @@ -39,11 +39,6 @@ type CreateRoleFormProps = { afterCreate?: (res: CreateRoleResponse) => Promise; }; -const formSchema = z.object({ - name: z.string({ message: "Name is required" }).max(32), - description: z.string().max(255).optional() -}); - export default function CreateRoleForm({ open, setOpen, @@ -52,6 +47,11 @@ export default function CreateRoleForm({ const { org } = useOrgContext(); const t = useTranslations(); + const formSchema = z.object({ + name: z.string({ message: t('nameRequired') }).max(32), + description: z.string().max(255).optional() + }); + const [loading, setLoading] = useState(false); const api = createApiClient(useEnvContext()); diff --git a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx index 0501f0d1..1e910e29 100644 --- a/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx +++ b/src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx @@ -47,10 +47,6 @@ type CreateRoleFormProps = { afterDelete?: () => void; }; -const formSchema = z.object({ - newRoleId: z.string({ message: "New role is required" }) -}); - export default function DeleteRoleForm({ open, roleToDelete, @@ -65,6 +61,10 @@ export default function DeleteRoleForm({ const api = createApiClient(useEnvContext()); + const formSchema = z.object({ + newRoleId: z.string({ message: t('accessRoleErrorNewRequired') }) + }); + useEffect(() => { async function fetchRoles() { const res = await api diff --git a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx index ab381813..3900a70b 100644 --- a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx @@ -24,7 +24,7 @@ export function RolesDataTable({ @@ -244,7 +244,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { dialog={

- {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})} + {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})} // FIXME

diff --git a/src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx b/src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx index 9263ceb6..82999ad2 100644 --- a/src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx +++ b/src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx @@ -42,11 +42,6 @@ import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; -const formSchema = z.object({ - username: z.string(), - roleId: z.string().min(1, { message: "Please select a role" }) -}); - export default function AccessControlsPage() { const { orgUser: user } = userOrgUserContext(); @@ -57,6 +52,13 @@ export default function AccessControlsPage() { const [loading, setLoading] = useState(false); const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]); + const t = useTranslations(); + + const formSchema = z.object({ + username: z.string(), + roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }) + }); + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -65,8 +67,6 @@ export default function AccessControlsPage() { } }); - const t = useTranslations(); - useEffect(() => { async function fetchRoles() { const res = await api diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 4c96a241..efaf64fd 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -60,24 +60,6 @@ interface IdpOption { type: string; } -const internalFormSchema = z.object({ - email: z.string().email({ message: "Invalid email address" }), - validForHours: z.string().min(1, { message: "Please select a duration" }), - roleId: z.string().min(1, { message: "Please select a role" }) -}); - -const externalFormSchema = z.object({ - username: z.string().min(1, { message: "Username is required" }), - email: z - .string() - .email({ message: "Invalid email address" }) - .optional() - .or(z.literal("")), - name: z.string().optional(), - roleId: z.string().min(1, { message: "Please select a role" }), - idpId: z.string().min(1, { message: "Please select an identity provider" }) -}); - const formatIdpType = (type: string) => { switch (type.toLowerCase()) { case "oidc": @@ -104,6 +86,24 @@ export default function Page() { const [selectedIdp, setSelectedIdp] = useState(null); const [dataLoaded, setDataLoaded] = useState(false); + const internalFormSchema = z.object({ + email: z.string().email({ message: t('emailInvalid') }), + validForHours: z.string().min(1, { message: t('inviteValidityDuration') }), + roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }) + }); + + const externalFormSchema = z.object({ + username: z.string().min(1, { message: t('usernameRequired') }), + email: z + .string() + .email({ message: t('emailInvalid') }) + .optional() + .or(z.literal("")), + name: z.string().optional(), + roleId: z.string().min(1, { message: t('accessRoleSelectPlease') }), + idpId: z.string().min(1, { message: t('idpSelectPlease') }) + }); + const validFor = [ { hours: 24, name: t('day', {count: 1}) }, { hours: 48, name: t('day', {count: 2}) }, diff --git a/src/app/[orgId]/settings/api-keys/create/page.tsx b/src/app/[orgId]/settings/api-keys/create/page.tsx index 809784e4..2ef706fd 100644 --- a/src/app/[orgId]/settings/api-keys/create/page.tsx +++ b/src/app/[orgId]/settings/api-keys/create/page.tsx @@ -58,42 +58,13 @@ import CopyTextBox from "@app/components/CopyTextBox"; import PermissionsSelectBox from "@app/components/PermissionsSelectBox"; import { useTranslations } from "next-intl"; -const createFormSchema = z.object({ - name: z - .string() - .min(2, { - message: "Name must be at least 2 characters." - }) - .max(255, { - message: "Name must not be longer than 255 characters." - }) -}); - -type CreateFormValues = z.infer; - -const copiedFormSchema = z - .object({ - copied: z.boolean() - }) - .refine( - (data) => { - return data.copied; - }, - { - message: "You must confirm that you have copied the API key.", - path: ["copied"] - } - ); - -type CopiedFormValues = z.infer; - export default function Page() { const { env } = useEnvContext(); const api = createApiClient({ env }); const { orgId } = useParams(); const router = useRouter(); const t = useTranslations(); - + const [loadingPage, setLoadingPage] = useState(true); const [createLoading, setCreateLoading] = useState(false); const [apiKey, setApiKey] = useState(null); @@ -101,6 +72,35 @@ export default function Page() { Record >({}); + const createFormSchema = z.object({ + name: z + .string() + .min(2, { + message: t('nameMin', {len: 2}) + }) + .max(255, { + message: t('nameMax', {len: 255}) + }) + }); + + type CreateFormValues = z.infer; + + const copiedFormSchema = z + .object({ + copied: z.boolean() + }) + .refine( + (data) => { + return data.copied; + }, + { + message: t('apiKeysConfirmCopy2'), + path: ["copied"] + } + ); + + type CopiedFormValues = z.infer; + const form = useForm({ resolver: zodResolver(createFormSchema), defaultValues: { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx index 6182c04a..704f8fac 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx @@ -162,9 +162,10 @@ export default function ResourceAuthenticationPage() { rolesResponse.data.data.roles .map((role) => ({ id: role.roleId.toString(), - text: role.name + text: role.name, + isAdmin: role.isAdmin })) - .filter((role) => role.text !== "Admin") + .filter((role) => !role.isAdmin) ); usersRolesForm.setValue( @@ -172,9 +173,10 @@ export default function ResourceAuthenticationPage() { resourceRolesResponse.data.data.roles .map((i) => ({ id: i.roleId.toString(), - text: i.name + text: i.name, + isAdmin: i.isAdmin })) - .filter((role) => role.text !== "Admin") + .filter((role) => !role.isAdmin) ); setAllUsers( diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 1e360772..02833359 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -88,17 +88,6 @@ type LocalRule = ArrayElement & { updated?: boolean; }; -const RuleAction = { - ACCEPT: "Always Allow", - DROP: "Always Deny" -} as const; - -const RuleMatch = { - PATH: "Path", - IP: "IP", - CIDR: "IP Range" -} as const; - export default function ResourceRules(props: { params: Promise<{ resourceId: number }>; }) { @@ -113,6 +102,17 @@ export default function ResourceRules(props: { const router = useRouter(); const t = useTranslations(); + const RuleAction = { + ACCEPT: t('alwaysAllow'), + DROP: t('alwaysDeny') + } as const; + + const RuleMatch = { + PATH: t('path'), + IP: "IP", + CIDR: t('ipAddressRange') + } as const; + const addRuleForm = useForm({ resolver: zodResolver(addRuleSchema), defaultValues: { diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index 4c70e1fa..66bf8fcf 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -204,7 +204,7 @@ export default function CreateShareLinkForm({ validForSeconds: neverExpire ? undefined : timeInSeconds, title: values.title || - `${values.resourceName || "Resource" + values.resourceId} Share Link` + t('shareLink', {resource: (values.resourceName || "Resource" + values.resourceId)}) } ) .catch((e) => { diff --git a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx index ebdf3104..3000575a 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx @@ -69,10 +69,10 @@ export default function ShareLinksTable({ async function deleteSharelink(id: string) { await api.delete(`/access-token/${id}`).catch((e) => { toast({ - title: "Failed to delete link", + title: t('shareErrorDelete'), description: formatAxiosError( e, - "An error occurred deleting link" + t('shareErrorDeleteMessage') ) }); }); @@ -81,8 +81,8 @@ export default function ShareLinksTable({ setRows(newRows); toast({ - title: "Link deleted", - description: "The link has been deleted" + title: t('shareDeleted'), + description: t('shareDeletedDescription') }); } diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 91db2059..9973dcf0 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -229,11 +229,11 @@ export default function CreateSiteForm({ nice: data.niceId.toString(), mbIn: data.type == "wireguard" || data.type == "newt" - ? "0 MB" + ? t('megabytes', {count: 0}) : "-", mbOut: data.type == "wireguard" || data.type == "newt" - ? "0 MB" + ? t('megabytes', {count: 0}) : "-", orgId: orgId as string, type: data.type as any, @@ -273,8 +273,6 @@ PersistentKeepalive = 5` const newtConfigDockerRun = `docker run -it fosrl/newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${env.app.dashboardUrl}`; - const t = useTranslations(); - return loadingPage ? ( ) : ( @@ -313,7 +311,7 @@ PersistentKeepalive = 5` onValueChange={field.onChange} > - + diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index 512ef808..c490f1e9 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -67,10 +67,10 @@ export default function GeneralPage() { .catch((e) => { toast({ variant: "destructive", - title: "Failed to update site", + title: t('siteErrorUpdate'), description: formatAxiosError( e, - "An error occurred while updating the site." + t('siteErrorUpdateDescription') ) }); }); @@ -78,8 +78,8 @@ export default function GeneralPage() { updateSite({ name: data.name }); toast({ - title: "Site updated", - description: "The site has been updated." + title: t('siteUpdated'), + description: t('siteUpdatedDescription') }); setLoading(false); diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 56e882e3..d5b9a9fa 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -381,8 +381,8 @@ WantedBy=default.target` if (!siteDefaults || !wgConfig) { toast({ variant: "destructive", - title: "Error creating site", - description: "Key pair or site defaults not found" + title: t('siteErrorCreate'), + description: t('siteErrorCreateKeyPair') }); setCreateLoading(false); return; @@ -399,8 +399,8 @@ WantedBy=default.target` if (!siteDefaults) { toast({ variant: "destructive", - title: "Error creating site", - description: "Site defaults not found" + title: t('siteErrorCreate'), + description: t('siteErrorCreateDefaults') }); setCreateLoading(false); return; @@ -422,7 +422,7 @@ WantedBy=default.target` .catch((e) => { toast({ variant: "destructive", - title: "Error creating site", + title: t('siteErrorCreate'), description: formatAxiosError(e) }); }); diff --git a/src/app/[orgId]/settings/sites/page.tsx b/src/app/[orgId]/settings/sites/page.tsx index 3ec39224..d76e5fe1 100644 --- a/src/app/[orgId]/settings/sites/page.tsx +++ b/src/app/[orgId]/settings/sites/page.tsx @@ -24,16 +24,18 @@ export default async function SitesPage(props: SitesPageProps) { sites = res.data.data.sites; } catch (e) {} + const t = await getTranslations(); + function formatSize(mb: number, type: string): string { if (type === "local") { return "-"; // because we are not able to track the data use in a local site right now } if (mb >= 1024 * 1024) { - return `${(mb / (1024 * 1024)).toFixed(2)} TB`; + return t('terabytes', {count: (mb / (1024 * 1024)).toFixed(2)}); } else if (mb >= 1024) { - return `${(mb / 1024).toFixed(2)} GB`; + return t('gigabytes', {count: (mb / 1024).toFixed(2)}); } else { - return `${mb.toFixed(2)} MB`; + return t('megabytes', {count: mb.toFixed(2)}); } } @@ -50,8 +52,6 @@ export default async function SitesPage(props: SitesPageProps) { }; }); - const t = await getTranslations(); - return ( <> {/* */} diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index b4c99f1e..4a58385c 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -124,7 +124,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: "Error creating API key", + title: t('apiKeysErrorCreate'), description: formatAxiosError(e) }); }); @@ -145,10 +145,10 @@ export default function Page() { ) }) .catch((e) => { - console.error("Error setting permissions", e); + console.error(t('apiKeysErrorSetPermission'), e); toast({ variant: "destructive", - title: "Error setting permissions", + title: t('apiKeysErrorSetPermission'), description: formatAxiosError(e) }); }); diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index f8cc7506..75d7a731 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -41,7 +41,7 @@ export default function UsersTable({ users }: Props) { const deleteUser = (id: string) => { api.delete(`/user/${id}`) .catch((e) => { - console.error("Error deleting user", e); + console.error(t('userErrorDelete'), e); toast({ variant: "destructive", title: t('userErrorDelete'), diff --git a/src/app/auth/verify-email/VerifyEmailForm.tsx b/src/app/auth/verify-email/VerifyEmailForm.tsx index 05257c1d..eb7c4db0 100644 --- a/src/app/auth/verify-email/VerifyEmailForm.tsx +++ b/src/app/auth/verify-email/VerifyEmailForm.tsx @@ -226,7 +226,7 @@ export default function VerifyEmailForm({ {isSubmitting && ( )} - {t('emailVerifySubmit')} + {t('submit')} diff --git a/src/components/ConfirmDeleteDialog.tsx b/src/components/ConfirmDeleteDialog.tsx index a928ed60..5ca8ca8d 100644 --- a/src/components/ConfirmDeleteDialog.tsx +++ b/src/components/ConfirmDeleteDialog.tsx @@ -43,6 +43,7 @@ import { useOrgContext } from "@app/hooks/useOrgContext"; import { Description } from "@radix-ui/react-toast"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; type InviteUserFormProps = { open: boolean; @@ -67,9 +68,11 @@ export default function InviteUserForm({ const api = createApiClient(useEnvContext()); + const t = useTranslations(); + const formSchema = z.object({ string: z.string().refine((val) => val === string, { - message: "Invalid confirmation" + message: t('inviteErrorInvalidConfirmation') }) }); @@ -129,7 +132,7 @@ export default function InviteUserForm({ - +

); diff --git a/src/components/DataTablePagination.tsx b/src/components/DataTablePagination.tsx index d909b7ea..24a12319 100644 --- a/src/components/DataTablePagination.tsx +++ b/src/components/DataTablePagination.tsx @@ -14,6 +14,7 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; +import { useTranslations } from "next-intl"; interface DataTablePaginationProps { table: Table; @@ -22,6 +23,8 @@ interface DataTablePaginationProps { export function DataTablePagination({ table }: DataTablePaginationProps) { + const t = useTranslations(); + return (
@@ -48,8 +51,7 @@ export function DataTablePagination({
- Page {table.getState().pagination.pageIndex + 1} of{" "} - {table.getPageCount()} + {t('paginator', {current: table.getState().pagination.pageIndex + 1, last: table.getPageCount()})}
diff --git a/src/components/Disable2FaForm.tsx b/src/components/Disable2FaForm.tsx index 0ef05cfc..c9c1d228 100644 --- a/src/components/Disable2FaForm.tsx +++ b/src/components/Disable2FaForm.tsx @@ -32,6 +32,7 @@ import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { useUserContext } from "@app/hooks/useUserContext"; import { CheckCircle2 } from "lucide-react"; +import { useTranslations } from "next-intl"; const disableSchema = z.object({ password: z.string().min(1, { message: "Password is required" }), @@ -60,6 +61,8 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { } }); + const t = useTranslations(); + const request2fa = async (values: z.infer) => { setLoading(true); @@ -70,10 +73,10 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { } as Disable2faBody) .catch((e) => { toast({ - title: "Unable to disable 2FA", + title: t('otpErrorDisable'), description: formatAxiosError( e, - "An error occurred while disabling 2FA" + t('otpErrorDisableDescription') ), variant: "destructive" }); @@ -109,10 +112,10 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { - Disable Two-factor Authentication + {t('otpRemove')} - Disable two-factor authentication for your account + {t('otpRemoveDescription')} @@ -129,7 +132,7 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { name="password" render={({ field }) => ( - Password + {t('password')} ( - Authenticator Code + {t('otpSetupSecretCode')} @@ -168,19 +171,17 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { size={48} />

- Two-Factor Authentication Disabled + {t('otpRemoveSuccess')}

- Two-factor authentication has been disabled for - your account. You can enable it again at any - time. + {t('otpRemoveSuccessMessage')}

)} - + {step === "password" && ( )} diff --git a/src/components/Enable2FaForm.tsx b/src/components/Enable2FaForm.tsx index dcc10d58..80224173 100644 --- a/src/components/Enable2FaForm.tsx +++ b/src/components/Enable2FaForm.tsx @@ -40,6 +40,7 @@ import { formatAxiosError } from "@app/lib/api"; import CopyTextBox from "@app/components/CopyTextBox"; import { QRCodeCanvas, QRCodeSVG } from "qrcode.react"; import { useUserContext } from "@app/hooks/useUserContext"; +import { useTranslations } from "next-intl"; const enableSchema = z.object({ password: z.string().min(1, { message: "Password is required" }) @@ -82,6 +83,8 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { } }); + const t = useTranslations(); + const request2fa = async (values: z.infer) => { setLoading(true); @@ -94,10 +97,10 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { ) .catch((e) => { toast({ - title: "Unable to enable 2FA", + title: t('otpErrorEnable'), description: formatAxiosError( e, - "An error occurred while enabling 2FA" + t('otpErrorEnableDescription') ), variant: "destructive" }); @@ -121,10 +124,10 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { } as VerifyTotpBody) .catch((e) => { toast({ - title: "Unable to enable 2FA", + title: t('otpErrorEnable'), description: formatAxiosError( e, - "An error occurred while enabling 2FA" + t('otpErrorEnableDescription') ), variant: "destructive" }); @@ -141,14 +144,14 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { const handleVerify = () => { if (verificationCode.length !== 6) { - setError("Please enter a 6-digit code"); + setError(t('otpSetupCheckCode')); return; } if (verificationCode === "123456") { setSuccess(true); setStep(3); } else { - setError("Invalid code. Please try again."); + setError(t('otpSetupCheckCodeRetry')); } }; @@ -176,10 +179,10 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { - Enable Two-factor Authentication + {t('otpSetup')} - Secure your account with an extra layer of protection + {t('otpSetupDescription')} @@ -196,7 +199,7 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { name="password" render={({ field }) => ( - Password + {t('password')}

- Scan this QR code with your authenticator app or - enter the secret key manually: + {t('otpSetupScanQr')}

@@ -243,7 +245,7 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { render={({ field }) => ( - Authenticator Code + {t('otpSetupSecretCode')}

- Two-Factor Authentication Enabled + {t('otpSetupSuccess')}

- Your account is now more secure. Don't forget to - save your backup codes. + {t('otpSetupSuccessStoreBackupCodes')}

@@ -298,7 +299,7 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { } }} > - Submit + {t('submit')} )} diff --git a/src/components/HorizontalTabs.tsx b/src/components/HorizontalTabs.tsx index eb590eb0..258bace3 100644 --- a/src/components/HorizontalTabs.tsx +++ b/src/components/HorizontalTabs.tsx @@ -7,6 +7,7 @@ import { cn } from "@app/lib/cn"; import { buttonVariants } from "@/components/ui/button"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; export type HorizontalTabs = Array<{ title: string; @@ -29,6 +30,7 @@ export function HorizontalTabs({ const pathname = usePathname(); const params = useParams(); const { licenseStatus, isUnlocked } = useLicenseStatusContext(); + const t = useTranslations(); function hydrateHref(href: string) { return href @@ -86,7 +88,7 @@ export function HorizontalTabs({ variant="outlinePrimary" className="ml-2" > - Professional + {t('licenseBadge')} )}
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index e0925525..536357f0 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -23,6 +23,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { useUserContext } from "@app/hooks/useUserContext"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; interface LayoutProps { children: React.ReactNode; @@ -60,6 +61,7 @@ export function Layout({ const isAdminPage = pathname?.startsWith("/admin"); const { user } = useUserContext(); const { isUnlocked } = useLicenseStatusContext(); + const t = useTranslations(); return (
@@ -84,11 +86,10 @@ export function Layout({ className="w-64 p-0 flex flex-col h-full" > - Navigation Menu + {t('navbar')} - Main navigation menu for the - application + {t('navbarDescription')}
@@ -114,7 +115,7 @@ export function Layout({ } > - Server Admin + {t('serverAdmin')}
)} @@ -161,7 +162,7 @@ export function Layout({ rel="noopener noreferrer" className="text-muted-foreground hover:text-foreground transition-colors" > - Documentation + {t('navbarDocsLink')}
@@ -193,7 +194,7 @@ export function Layout({ className="flex items-center gap-3 text-muted-foreground hover:text-foreground transition-colors px-3 py-2 rounded-md w-full" > - Server Admin + {t('serverAdmin')}
)} @@ -210,8 +211,8 @@ export function Layout({ className="flex items-center justify-center gap-1" > {!isUnlocked() - ? "Community Edition" - : "Commercial Edition"} + ? t('communityEdition') + : t('commercialEdition')}
diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 4953d18c..d0eee1b7 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -40,6 +40,7 @@ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"; import Image from "next/image"; import { GenerateOidcUrlResponse } from "@server/routers/idp"; import { Separator } from "./ui/separator"; +import { useTranslations } from "next-intl"; export type LoginFormIDP = { idpId: number; @@ -91,6 +92,8 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { } }); + const t = useTranslations(); + async function onSubmit(values: any) { const { email, password } = form.getValues(); const { code } = mfaForm.getValues(); @@ -106,7 +109,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { .catch((e) => { console.error(e); setError( - formatAxiosError(e, "An error occurred while logging in") + formatAxiosError(e, t('loginError')) ); }); @@ -151,7 +154,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { console.log(res); if (!res) { - setError("An error occurred while logging in"); + setError(t('loginError')); return; } @@ -177,7 +180,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { name="email" render={({ field }) => ( - Email + {t('email')} @@ -192,7 +195,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { name="password" render={({ field }) => ( - Password + {t('password')} - Forgot your password? + {t('passwordForgot')}
@@ -222,11 +225,10 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { <>

- Two-Factor Authentication + {t('otpAuth')}

- Enter the code from your authenticator app or one of - your single-use backup codes. + {t('otpAuthDescription')}

@@ -302,7 +304,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { loading={loading} disabled={loading} > - Submit Code + {t('otpAuthSubmit')} )} @@ -316,7 +318,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { disabled={loading} > - Log In + {t('login')} {hasIdp && ( @@ -327,7 +329,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
- Or continue with + {t('idpContinue')}
@@ -360,7 +362,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { mfaForm.reset(); }} > - Back to Log In + {t('otpAuthBack')} )}
diff --git a/src/components/OrgSelector.tsx b/src/components/OrgSelector.tsx index 626156cf..b402e0de 100644 --- a/src/components/OrgSelector.tsx +++ b/src/components/OrgSelector.tsx @@ -22,6 +22,7 @@ import { Check, ChevronsUpDown, Plus } from "lucide-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { useUserContext } from "@app/hooks/useUserContext"; +import { useTranslations } from "next-intl"; interface OrgSelectorProps { orgId?: string; @@ -33,6 +34,7 @@ export function OrgSelector({ orgId, orgs }: OrgSelectorProps) { const [open, setOpen] = useState(false); const router = useRouter(); const { env } = useEnvContext(); + const t = useTranslations(); return ( @@ -47,7 +49,7 @@ export function OrgSelector({ orgId, orgs }: OrgSelectorProps) {
- Organization + {t('org')} {orgId @@ -56,7 +58,7 @@ export function OrgSelector({ orgId, orgs }: OrgSelectorProps) { org.orgId === orgId )?.name - : "None selected"} + : t('noneSelected')}
@@ -65,14 +67,14 @@ export function OrgSelector({ orgId, orgs }: OrgSelectorProps) { - + - No organizations found. + {t('orgNotFound2')} {(!env.flags.disableUserCreateOrg || user.serverAdmin) && ( <> - + - New Organization + {t('setupNewOrg')} )} - + {orgs?.map((org) => ( > = { Organization: { - "Get Organization": "getOrg", - "Update Organization": "updateOrg", - "Get Organization User": "getOrgUser", - "List Organization Domains": "listOrgDomains", + [t('actionGetOrg')]: "getOrg", + [t('actionUpdateOrg')]: "updateOrg", + [t('actionGetOrgUser')]: "getOrgUser", + [t('actionListOrgDomains')]: "listOrgDomains", }, Site: { - "Create Site": "createSite", - "Delete Site": "deleteSite", - "Get Site": "getSite", - "List Sites": "listSites", - "Update Site": "updateSite", - "List Allowed Site Roles": "listSiteRoles" + [t('actionCreateSite')]: "createSite", + [t('actionDeleteSite')]: "deleteSite", + [t('actionGetSite')]: "getSite", + [t('actionListSites')]: "listSites", + [t('actionUpdateSite')]: "updateSite", + [t('actionListSiteRoles')]: "listSiteRoles" }, Resource: { - "Create Resource": "createResource", - "Delete Resource": "deleteResource", - "Get Resource": "getResource", - "List Resources": "listResources", - "Update Resource": "updateResource", - "List Resource Users": "listResourceUsers", - "Set Resource Users": "setResourceUsers", - "Set Allowed Resource Roles": "setResourceRoles", - "List Allowed Resource Roles": "listResourceRoles", - "Set Resource Password": "setResourcePassword", - "Set Resource Pincode": "setResourcePincode", - "Set Resource Email Whitelist": "setResourceWhitelist", - "Get Resource Email Whitelist": "getResourceWhitelist" + [t('actionCreateResource')]: "createResource", + [t('actionDeleteResource')]: "deleteResource", + [t('actionGetResource')]: "getResource", + [t('actionListResource')]: "listResources", + [t('actionUpdateResource')]: "updateResource", + [t('actionListResourceUsers')]: "listResourceUsers", + [t('actionSetResourceUsers')]: "setResourceUsers", + [t('actionSetAllowedResourceRoles')]: "setResourceRoles", + [t('actionListAllowedResourceRoles')]: "listResourceRoles", + [t('actionSetResourcePassword')]: "setResourcePassword", + [t('actionSetResourcePincode')]: "setResourcePincode", + [t('actionSetResourceEmailWhitelist')]: "setResourceWhitelist", + [t('actionGetResourceEmailWhitelist')]: "getResourceWhitelist" }, Target: { - "Create Target": "createTarget", - "Delete Target": "deleteTarget", - "Get Target": "getTarget", - "List Targets": "listTargets", - "Update Target": "updateTarget" + [t('actionCreateTarget')]: "createTarget", + [t('actionDeleteTarget')]: "deleteTarget", + [t('actionGetTarget')]: "getTarget", + [t('actionListTargets')]: "listTargets", + [t('actionUpdateTarget')]: "updateTarget" }, Role: { - "Create Role": "createRole", - "Delete Role": "deleteRole", - "Get Role": "getRole", - "List Roles": "listRoles", - "Update Role": "updateRole", - "List Allowed Role Resources": "listRoleResources" + [t('actionCreateRole')]: "createRole", + [t('actionDeleteRole')]: "deleteRole", + [t('actionGetRole')]: "getRole", + [t('actionListRole')]: "listRoles", + [t('actionUpdateRole')]: "updateRole", + [t('actionListAllowedRoleResources')]: "listRoleResources" }, User: { - "Invite User": "inviteUser", - "Remove User": "removeUser", - "List Users": "listUsers", - "Add User Role": "addUserRole" + [t('actionInviteUser')]: "inviteUser", + [t('actionRemoveUser')]: "removeUser", + [t('actionListUsers')]: "listUsers", + [t('actionAddUserRole')]: "addUserRole" }, "Access Token": { - "Generate Access Token": "generateAccessToken", - "Delete Access Token": "deleteAcessToken", - "List Access Tokens": "listAccessTokens" + [t('actionGenerateAccessToken')]: "generateAccessToken", + [t('actionDeleteAccessToken')]: "deleteAcessToken", + [t('actionListAccessTokens')]: "listAccessTokens" }, "Resource Rule": { - "Create Resource Rule": "createResourceRule", - "Delete Resource Rule": "deleteResourceRule", - "List Resource Rules": "listResourceRules", - "Update Resource Rule": "updateResourceRule" + [t('actionCreateResourceRule')]: "createResourceRule", + [t('actionDeleteResourceRule')]: "deleteResourceRule", + [t('actionListResourceRules')]: "listResourceRules", + [t('actionUpdateResourceRule')]: "updateResourceRule" } }; if (root) { actionsByCategory["Organization"] = { - "List Organizations": "listOrgs", - "Check ID": "checkOrgId", - "Create Organization": "createOrg", - "Delete Organization": "deleteOrg", - "List API Keys": "listApiKeys", - "List API Key Actions": "listApiKeyActions", - "Set API Key Allowed Actions": "setApiKeyActions", - "Create API Key": "createApiKey", - "Delete API Key": "deleteApiKey", + [t('actionListOrgs')]: "listOrgs", + [t('actionCheckOrgId')]: "checkOrgId", + [t('actionCreateOrg')]: "createOrg", + [t('actionDeleteOrg')]: "deleteOrg", + [t('actionListApiKeys')]: "listApiKeys", + [t('actionListApiKeyActions')]: "listApiKeyActions", + [t('actionSetApiKeyActions')]: "setApiKeyActions", + [t('actionCreateApiKey')]: "createApiKey", + [t('actionDeleteApiKey')]: "deleteApiKey", ...actionsByCategory["Organization"] }; actionsByCategory["Identity Provider (IDP)"] = { - "Create IDP": "createIdp", - "Update IDP": "updateIdp", - "Delete IDP": "deleteIdp", - "List IDP": "listIdps", - "Get IDP": "getIdp", - "Create IDP Org Policy": "createIdpOrg", - "Delete IDP Org Policy": "deleteIdpOrg", - "List IDP Orgs": "listIdpOrgs", - "Update IDP Org": "updateIdpOrg" + [t('actionCreateIdp')]: "createIdp", + [t('actionUpdateIdp')]: "updateIdp", + [t('actionDeleteIdp')]: "deleteIdp", + [t('actionListIdps')]: "listIdps", + [t('actionGetIdp')]: "getIdp", + [t('actionCreateIdpOrg')]: "createIdpOrg", + [t('actionDeleteIdpOrg')]: "deleteIdpOrg", + [t('actionListIdpOrgs')]: "listIdpOrgs", + [t('actionUpdateIdpOrg')]: "updateIdpOrg" }; } diff --git a/src/components/ProfessionalContentOverlay.tsx b/src/components/ProfessionalContentOverlay.tsx index d35b09ba..ebd646fd 100644 --- a/src/components/ProfessionalContentOverlay.tsx +++ b/src/components/ProfessionalContentOverlay.tsx @@ -1,6 +1,7 @@ "use client"; import { cn } from "@app/lib/cn"; +import { useTranslations } from "next-intl"; type ProfessionalContentOverlayProps = { children: React.ReactNode; @@ -11,6 +12,8 @@ export function ProfessionalContentOverlay({ children, isProfessional = false }: ProfessionalContentOverlayProps) { + const t = useTranslations(); + return (

- Professional Edition Required + {t('licenseTierProfessionalRequired')}

- This feature is only available in the Professional - Edition. + {t('licenseTierProfessionalRequiredDescription')}

diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx index 443f9ac3..c09447fb 100644 --- a/src/components/ProfileIcon.tsx +++ b/src/components/ProfileIcon.tsx @@ -24,6 +24,7 @@ import Enable2FaForm from "./Enable2FaForm"; import SupporterStatus from "./SupporterStatus"; import { UserType } from "@server/types/UserTypes"; import LocaleSwitcher from '@app/components/LocaleSwitcher'; +import { useTranslations } from "next-intl"; export default function ProfileIcon() { @@ -40,6 +41,8 @@ export default function ProfileIcon() { const [openEnable2fa, setOpenEnable2fa] = useState(false); const [openDisable2fa, setOpenDisable2fa] = useState(false); + const t = useTranslations(); + function getInitials() { return (user.email || user.name || user.username) .substring(0, 1) @@ -54,10 +57,10 @@ export default function ProfileIcon() { function logout() { api.post("/auth/logout") .catch((e) => { - console.error("Error logging out", e); + console.error(t('logoutError'), e); toast({ - title: "Error logging out", - description: formatAxiosError(e, "Error logging out") + title: t('logoutError'), + description: formatAxiosError(e, t('logoutError')) }); }) .then(() => { @@ -94,7 +97,7 @@ export default function ProfileIcon() {

- Signed in as + {t('signingAs')}

{user.email || user.name || user.username} @@ -102,11 +105,11 @@ export default function ProfileIcon() {

{user.serverAdmin ? (

- Server Admin + {t('serverAdmin')}

) : (

- {user.idpName || "Internal"} + {user.idpName || t('idpNameInternal')}

)}
@@ -117,14 +120,14 @@ export default function ProfileIcon() { setOpenEnable2fa(true)} > - Enable Two-factor + {t('otpEnable')} )} {user.twoFactorEnabled && ( setOpenDisable2fa(true)} > - Disable Two-factor + {t('otpDisable')} )} @@ -166,7 +169,7 @@ export default function ProfileIcon() { logout()}> {/* */} - Log Out + {t('logout')} diff --git a/src/components/SidebarNav.tsx b/src/components/SidebarNav.tsx index d6de9615..ae9f4972 100644 --- a/src/components/SidebarNav.tsx +++ b/src/components/SidebarNav.tsx @@ -8,6 +8,7 @@ import { ChevronDown, ChevronRight } from "lucide-react"; import { useUserContext } from "@app/hooks/useUserContext"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; export interface SidebarNavItem { href: string; @@ -65,6 +66,8 @@ export function SidebarNav({ const { user } = useUserContext(); + const t = useTranslations(); + function hydrateHref(val: string): string { return val .replace("{orgId}", orgId) @@ -144,7 +147,7 @@ export function SidebarNav({ variant="outlinePrimary" className="ml-2" > - Professional + {t('licenseBadge')} )} diff --git a/src/components/SupporterStatus.tsx b/src/components/SupporterStatus.tsx index bd092007..5febb624 100644 --- a/src/components/SupporterStatus.tsx +++ b/src/components/SupporterStatus.tsx @@ -3,7 +3,7 @@ import Image from "next/image"; import { Separator } from "@app/components/ui/separator"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; -import { useState } from "react"; +import { useState, useTransition } from "react"; import { Popover, PopoverContent, @@ -48,6 +48,7 @@ import { } from "./ui/card"; import { Check, ExternalLink } from "lucide-react"; import confetti from "canvas-confetti"; +import { useTranslations } from "next-intl"; const formSchema = z.object({ githubUsername: z @@ -73,6 +74,8 @@ export default function SupporterStatus() { } }); + const t = useTranslations(); + async function hide() { await api.post("/supporter-key/hide"); @@ -95,8 +98,8 @@ export default function SupporterStatus() { if (!data || !data.valid) { toast({ variant: "destructive", - title: "Invalid Key", - description: "Your supporter key is invalid." + title: t('supportKeyInvalid'), + description: t('supportKeyInvalidDescription') }); return; } @@ -104,9 +107,8 @@ export default function SupporterStatus() { // Trigger the toast toast({ variant: "default", - title: "Valid Key", - description: - "Your supporter key has been validated. Thank you for your support!" + title: t('supportKeyValid'), + description: t('supportKeyValidDescription') }); // Fireworks-style confetti @@ -162,7 +164,7 @@ export default function SupporterStatus() { } catch (error) { toast({ variant: "destructive", - title: "Error", + title: t('error'), description: formatAxiosError( error, "Failed to validate supporter key." @@ -183,55 +185,47 @@ export default function SupporterStatus() { - Support Development and Adopt a Pangolin! + {t('supportKey')}

- Purchase a supporter key to help us continue - developing Pangolin for the community. Your - contribution allows us to commit more time to - maintain and add new features to the application for - everyone. We will never use this to paywall - features. This is separate from any Commercial - Edition. + {t('supportKeyDescription')}

- You will also get to adopt and meet your very own - pet Pangolin! + {t('supportKeyPet')}

- Payments are processed via GitHub. Afterward, you - can retrieve your key on{" "} + {t('supportKeyPurchase')}{" "} - our website + {t('supportKeyPurchaseLink')} {" "} - and redeem it here.{" "} + {t('supportKeyPurchase2')}{" "} - Learn more. + {t('supportKeyLearnMore')}

- Please select the option that best suits you. + {t('supportKeyOptions')}

- Full Supporter + {t('supportKetOptionFull')}

$95

@@ -239,19 +233,19 @@ export default function SupporterStatus() {
  • - For the whole server + {t('forWholeServer')}
  • - Lifetime purchase + {t('lifetimePurchase')}
  • - Supporter status + {t('supporterStatus')}
  • @@ -264,7 +258,7 @@ export default function SupporterStatus() { className="w-full" > @@ -274,7 +268,7 @@ export default function SupporterStatus() { className={`${supporterStatus?.tier === "Limited Supporter" ? "opacity-50" : ""}`} > - Limited Supporter + {t('supportKeyOptionLimited')}

    $25

    @@ -282,19 +276,19 @@ export default function SupporterStatus() {
  • - For 5 or less users + {t('forFiveUsers')}
  • - Lifetime purchase + {t('lifetimePurchase')}
  • - Supporter status + {t('supporterStatus')}
  • @@ -309,7 +303,7 @@ export default function SupporterStatus() { className="w-full" > ) : ( @@ -320,7 +314,7 @@ export default function SupporterStatus() { "Limited Supporter" } > - Buy + {t('buy')} )} @@ -336,20 +330,20 @@ export default function SupporterStatus() { setKeyOpen(true); }} > - Redeem Supporter Key + {t('supportKeyRedeem')}
    - + @@ -363,9 +357,9 @@ export default function SupporterStatus() { > - Enter Supporter Key + {t('supportKeyEnter')} - Meet your very own pet Pangolin! + {t('supportKeyEnterDescription')} @@ -381,7 +375,7 @@ export default function SupporterStatus() { render={({ field }) => ( - GitHub Username + {t('githubUsername')} @@ -395,7 +389,7 @@ export default function SupporterStatus() { name="key" render={({ field }) => ( - Supporter Key + {t('supportKeyInput')} @@ -408,10 +402,10 @@ export default function SupporterStatus() { - + @@ -426,7 +420,7 @@ export default function SupporterStatus() { setPurchaseOptionsOpen(true); }} > - Buy Supporter Key + {t('supportKeyBuy')} ) : null} diff --git a/src/components/tags/tag-input.tsx b/src/components/tags/tag-input.tsx index 3510e968..ad8c0d03 100644 --- a/src/components/tags/tag-input.tsx +++ b/src/components/tags/tag-input.tsx @@ -10,6 +10,7 @@ import { TagList } from "./tag-list"; import { tagVariants } from "./tag"; import { Autocomplete } from "./autocomplete"; import { cn } from "@app/lib/cn"; +import { useTranslations } from "next-intl"; export enum Delimiter { Comma = ",", @@ -166,11 +167,13 @@ const TagInput = React.forwardRef( ); const inputRef = React.useRef(null); + const t = useTranslations(); + if ( (maxTags !== undefined && maxTags < 0) || (props.minTags !== undefined && props.minTags < 0) ) { - console.warn("maxTags and minTags cannot be less than 0"); + console.warn(t('tagsWarnCannotBeLessThanZero')); // error return null; } @@ -194,24 +197,22 @@ const TagInput = React.forwardRef( (option) => option.text === newTagText ) ) { - console.warn( - "Tag not allowed as per autocomplete options" - ); + console.warn(t('tagsWarnNotAllowedAutocompleteOptions')); return; } if (validateTag && !validateTag(newTagText)) { - console.warn("Invalid tag as per validateTag"); + console.warn(t('tagsWarnInvalid')); return; } if (minLength && newTagText.length < minLength) { - console.warn(`Tag "${newTagText}" is too short`); + console.warn(t('tagWarnTooShort', {tagText: newTagText})); return; } if (maxLength && newTagText.length > maxLength) { - console.warn(`Tag "${newTagText}" is too long`); + console.warn(t('tagWarnTooLong', {tagText: newTagText})); return; } @@ -228,12 +229,10 @@ const TagInput = React.forwardRef( setTags((prevTags) => [...prevTags, newTag]); onTagAdd?.(newTagText); } else { - console.warn( - "Reached the maximum number of tags allowed" - ); + console.warn(t('tagsWarnReachedMaxNumber')); } } else { - console.warn(`Duplicate tag "${newTagText}" not added`); + console.warn(t('tagWarnDuplicate', {tagText: newTagText})); } }); setInputValue(""); @@ -259,12 +258,12 @@ const TagInput = React.forwardRef( } if (minLength && newTagText.length < minLength) { - console.warn("Tag is too short"); + console.warn(t('tagWarnTooShort')); return; } if (maxLength && newTagText.length > maxLength) { - console.warn("Tag is too long"); + console.warn(t('tagWarnTooLong')); return; } @@ -309,7 +308,7 @@ const TagInput = React.forwardRef( } if (minLength && newTagText.length < minLength) { - console.warn("Tag is too short"); + console.warn(t('tagWarnTooShort')); // error return; } @@ -317,7 +316,7 @@ const TagInput = React.forwardRef( // Validate maxLength if (maxLength && newTagText.length > maxLength) { // error - console.warn("Tag is too long"); + console.warn(t('tagWarnTooLong')); return; } diff --git a/src/components/tags/tag-popover.tsx b/src/components/tags/tag-popover.tsx index 6145b498..72871580 100644 --- a/src/components/tags/tag-popover.tsx +++ b/src/components/tags/tag-popover.tsx @@ -4,6 +4,7 @@ import { TagInputStyleClassesProps, type Tag as TagType } from "./tag-input"; import { TagList, TagListProps } from "./tag-list"; import { Button } from "../ui/button"; import { cn } from "@app/lib/cn"; +import { useTranslations } from "next-intl"; type TagPopoverProps = { children: React.ReactNode; @@ -41,6 +42,8 @@ export const TagPopover: React.FC = ({ const [inputFocused, setInputFocused] = useState(false); const [sideOffset, setSideOffset] = useState(0); + const t = useTranslations(); + useEffect(() => { const handleResize = () => { if (triggerContainerRef.current && triggerRef.current) { @@ -183,10 +186,10 @@ export const TagPopover: React.FC = ({ >

    - Entered Tags + {t('tagsEntered')}

    - These are the tags you've entered. + {t('tagsEnteredDescription')}

    Date: Sun, 25 May 2025 19:01:20 +0000 Subject: [PATCH 075/105] update all --- messages/en-US.json | 8 +- next.config.mjs | 6 +- .../access/invitations/InvitationsTable.tsx | 2 +- .../invitations/RegenerateInvitationForm.tsx | 16 ++-- .../settings/access/users/UsersTable.tsx | 4 +- .../settings/access/users/create/page.tsx | 18 ++--- .../settings/api-keys/OrgApiKeysTable.tsx | 2 +- src/app/[orgId]/settings/general/page.tsx | 6 +- .../[resourceId]/ResourceInfoBox.tsx | 2 +- .../[resourceId]/authentication/page.tsx | 10 +-- .../resources/[resourceId]/general/page.tsx | 78 +++++++++--------- .../resources/[resourceId]/proxy/page.tsx | 81 +++++++++---------- .../resources/[resourceId]/rules/page.tsx | 3 +- .../settings/resources/create/page.tsx | 2 +- src/app/[orgId]/settings/resources/page.tsx | 2 +- .../settings/share-links/AccessTokenUsage.tsx | 2 +- .../share-links/CreateShareLinkForm.tsx | 21 +++-- .../share-links/ShareLinksDataTable.tsx | 2 +- .../settings/share-links/ShareLinksTable.tsx | 2 +- src/app/[orgId]/settings/share-links/page.tsx | 2 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 40 ++++----- .../settings/sites/CreateSiteModal.tsx | 2 +- .../[orgId]/settings/sites/SitesDataTable.tsx | 2 +- src/app/[orgId]/settings/sites/SitesTable.tsx | 2 +- .../settings/sites/[niceId]/SiteInfoCard.tsx | 2 +- .../settings/sites/[niceId]/general/page.tsx | 16 ++-- .../settings/sites/[niceId]/layout.tsx | 2 +- .../[orgId]/settings/sites/create/page.tsx | 54 ++++++------- src/app/[orgId]/settings/sites/page.tsx | 2 +- src/app/admin/api-keys/[apiKeyId]/layout.tsx | 2 +- src/app/admin/api-keys/create/page.tsx | 58 ++++++------- src/app/admin/api-keys/page.tsx | 2 +- src/app/admin/idp/AdminIdpTable.tsx | 2 +- src/app/admin/idp/[idpId]/general/page.tsx | 37 +++++---- src/app/admin/idp/[idpId]/layout.tsx | 4 +- src/app/admin/idp/[idpId]/policies/page.tsx | 28 +++---- src/app/admin/idp/create/page.tsx | 64 +++++++-------- src/app/admin/idp/page.tsx | 2 +- .../admin/license/LicenseKeysDataTable.tsx | 2 +- .../components/SitePriceCalculator.tsx | 2 +- src/app/admin/license/page.tsx | 26 +++--- src/app/admin/users/AdminUsersDataTable.tsx | 2 +- src/app/admin/users/AdminUsersTable.tsx | 2 +- src/app/admin/users/page.tsx | 2 +- .../auth/idp/[idpId]/oidc/callback/page.tsx | 2 +- src/app/auth/layout.tsx | 2 +- src/app/auth/login/page.tsx | 2 +- .../auth/reset-password/ResetPasswordForm.tsx | 35 ++++---- src/app/auth/reset-password/page.tsx | 2 +- .../[resourceId]/ResourceNotFound.tsx | 4 +- src/app/auth/signup/SignupForm.tsx | 2 +- src/app/auth/verify-email/VerifyEmailForm.tsx | 14 ++-- src/app/components/LicenseViolation.tsx | 2 +- src/app/components/OrganizationLanding.tsx | 2 +- src/app/invite/InviteStatusCard.tsx | 2 +- src/app/invite/page.tsx | 2 +- src/app/layout.tsx | 4 +- src/app/not-found.tsx | 2 +- src/app/setup/page.tsx | 12 +-- src/components/Enable2FaForm.tsx | 19 +++-- src/components/PermissionsSelectBox.tsx | 6 +- src/components/SupporterStatus.tsx | 19 +++-- src/components/tags/autocomplete.tsx | 2 +- 63 files changed, 380 insertions(+), 381 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index c2969842..d63ef985 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -458,6 +458,7 @@ "createdAt": "Created At", "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", + "proxyEnableSSL": "Enable SSL (https)", "targetErrorFetch": "Failed to fetch targets", "targetErrorFetchDescription": "An error occurred while fetching targets", "siteErrorFetch": "Failed to fetch resource", @@ -850,7 +851,7 @@ "otpEmail": "One-Time Password (OTP)", "otpEmailSubmit": "Submit OTP", "backToEmail": "Back to Email", - "noSupportKey": "Server is running without a supporter key.
    Consider supporting the project!", + "noSupportKey": "Server is running without a supporter key. Consider supporting the project!", "accessDenied": "Access Denied", "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", "accessTokenError": "Error checking access token", @@ -1059,5 +1060,8 @@ "copyText": "Copy text", "copyTextFailed": "Failed to copy text: ", "copyTextClipboard": "Copy to clipboard", - "inviteErrorInvalidConfirmation": "Invalid confirmation" + "inviteErrorInvalidConfirmation": "Invalid confirmation", + "passwordRequired": "Password is required", + "allowAll": "Allow All", + "permissionsAllowAll": "Allow All Permissions" } diff --git a/next.config.mjs b/next.config.mjs index e8856db1..c870f1c1 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,13 +1,13 @@ -import createNextIntlPlugin from 'next-intl/plugin'; +import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin(); -/** @type {import('next').NextConfig} */ +/** @type {import("next").NextConfig} */ const nextConfig = { eslint: { ignoreDuringBuilds: true }, - output: 'standalone' + output: "standalone" }; export default withNextIntl(nextConfig); diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx index 6f947b52..95fadf42 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx @@ -148,7 +148,7 @@ export default function InvitationsTable({ dialog={

    - {t('inviteQuestionRemove', {email: selectedInvitation?.email || ''})} + {t('inviteQuestionRemove', {email: selectedInvitation?.email})}

    {t('inviteMessageRemove')} diff --git a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx index 59c1b1b4..fbd1c4f5 100644 --- a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx +++ b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx @@ -60,13 +60,13 @@ export default function RegenerateInvitationForm({ const t = useTranslations(); const validForOptions = [ - { hours: 24, name: t('day', { count: 1 }) }, - { hours: 48, name: t('day', { count: 2 }) }, - { hours: 72, name: t('day', { count: 3 }) }, - { hours: 96, name: t('day', { count: 4 }) }, - { hours: 120, name: t('day', { count: 5 }) }, - { hours: 144, name: t('day', { count: 6 }) }, - { hours: 168, name: t('day', { count: 7 }) } + { hours: 24, name: t('day', {count: 1}) }, + { hours: 48, name: t('day', {count: 2}) }, + { hours: 72, name: t('day', {count: 3}) }, + { hours: 96, name: t('day', {count: 4}) }, + { hours: 120, name: t('day', {count: 5}) }, + { hours: 144, name: t('day', {count: 6}) }, + { hours: 168, name: t('day', {count: 7}) } ]; useEffect(() => { @@ -177,7 +177,7 @@ export default function RegenerateInvitationForm({ {!inviteLink ? (

    - {t('inviteQuestionRegenerate', {email: invitation?.email || ''})} + {t('inviteQuestionRegenerate', {email: invitation?.email})}

    @@ -244,7 +244,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { dialog={

    - {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})} // FIXME + {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})}

    diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index efaf64fd..e4ea99fe 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -60,15 +60,6 @@ interface IdpOption { type: string; } -const formatIdpType = (type: string) => { - switch (type.toLowerCase()) { - case "oidc": - return "Generic OAuth2/OIDC provider."; - default: - return type; - } -}; - export default function Page() { const { orgId } = useParams(); const router = useRouter(); @@ -104,6 +95,15 @@ export default function Page() { idpId: z.string().min(1, { message: t('idpSelectPlease') }) }); + const formatIdpType = (type: string) => { + switch (type.toLowerCase()) { + case "oidc": + return t('idpGenericOidc'); + default: + return type; + } + }; + const validFor = [ { hours: 24, name: t('day', {count: 1}) }, { hours: 48, name: t('day', {count: 2}) }, diff --git a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx index 8ca7c2fc..b0e55c4b 100644 --- a/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx +++ b/src/app/[orgId]/settings/api-keys/OrgApiKeysTable.tsx @@ -78,7 +78,7 @@ export default function OrgApiKeysTable({ diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index 463c9463..c692fbc9 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -93,7 +93,7 @@ export default function GeneralPage() { toast({ variant: "destructive", title: t('orgErrorDelete'), - description: formatAxiosError(err,t('orgErrorDeleteMessage')) + description: formatAxiosError(err, t('orgErrorDeleteMessage')) }); } finally { setLoadingDelete(false); @@ -121,7 +121,7 @@ export default function GeneralPage() { toast({ variant: "destructive", title: t('orgErrorFetch'), - description: formatAxiosError(err,t('orgErrorFetchMessage')) + description: formatAxiosError(err, t('orgErrorFetchMessage')) }); } } @@ -144,7 +144,7 @@ export default function GeneralPage() { toast({ variant: "destructive", title: t('orgErrorUpdate'), - description: formatAxiosError(e,t('orgErrorUpdateMessage')) + description: formatAxiosError(e, t('orgErrorUpdateMessage')) }); }) .finally(() => { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx index 788652f4..03970bd5 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx @@ -13,7 +13,7 @@ import { } from "@app/components/InfoSection"; import Link from "next/link"; import { Switch } from "@app/components/ui/switch"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type ResourceInfoBoxType = {}; diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx index 704f8fac..6182c04a 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx @@ -162,10 +162,9 @@ export default function ResourceAuthenticationPage() { rolesResponse.data.data.roles .map((role) => ({ id: role.roleId.toString(), - text: role.name, - isAdmin: role.isAdmin + text: role.name })) - .filter((role) => !role.isAdmin) + .filter((role) => role.text !== "Admin") ); usersRolesForm.setValue( @@ -173,10 +172,9 @@ export default function ResourceAuthenticationPage() { resourceRolesResponse.data.data.roles .map((i) => ({ id: i.roleId.toString(), - text: i.name, - isAdmin: i.isAdmin + text: i.name })) - .filter((role) => !role.isAdmin) + .filter((role) => role.text !== "Admin") ); setAllUsers( diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index 9facce58..d571f7b8 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -67,45 +67,6 @@ import { import { SwitchInput } from "@app/components/SwitchInput"; import { useTranslations } from "next-intl"; -const GeneralFormSchema = z - .object({ - subdomain: z.string().optional(), - name: z.string().min(1).max(255), - proxyPort: z.number().optional(), - http: z.boolean(), - isBaseDomain: z.boolean().optional(), - domainId: z.string().optional() - }) - .refine( - (data) => { - if (!data.http) { - return z - .number() - .int() - .min(1) - .max(65535) - .safeParse(data.proxyPort).success; - } - return true; - }, - { - message: "Invalid port number", - path: ["proxyPort"] - } - ) - .refine( - (data) => { - if (data.http && !data.isBaseDomain) { - return subdomainSchema.safeParse(data.subdomain).success; - } - return true; - }, - { - message: "Invalid subdomain", - path: ["subdomain"] - } - ); - const TransferFormSchema = z.object({ siteId: z.number() }); @@ -140,6 +101,45 @@ export default function GeneralForm() { resource.isBaseDomain ? "basedomain" : "subdomain" ); + const GeneralFormSchema = z + .object({ + subdomain: z.string().optional(), + name: z.string().min(1).max(255), + proxyPort: z.number().optional(), + http: z.boolean(), + isBaseDomain: z.boolean().optional(), + domainId: z.string().optional() + }) + .refine( + (data) => { + if (!data.http) { + return z + .number() + .int() + .min(1) + .max(65535) + .safeParse(data.proxyPort).success; + } + return true; + }, + { + message: t('proxyErrorInvalidPort'), + path: ["proxyPort"] + } + ) + .refine( + (data) => { + if (data.http && !data.isBaseDomain) { + return subdomainSchema.safeParse(data.subdomain).success; + } + return true; + }, + { + message: t('subdomainErrorInvalid'), + path: ["subdomain"] + } + ); + const form = useForm({ resolver: zodResolver(GeneralFormSchema), defaultValues: { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 634e82e4..68743286 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -93,45 +93,6 @@ type LocalTarget = Omit< "protocol" >; -const proxySettingsSchema = z.object({ - setHostHeader: z - .string() - .optional() - .refine( - (data) => { - if (data) { - return tlsNameSchema.safeParse(data).success; - } - return true; - }, - { - message: "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header." - } - ) -}); - -const tlsSettingsSchema = z.object({ - ssl: z.boolean(), - tlsServerName: z - .string() - .optional() - .refine( - (data) => { - if (data) { - return tlsNameSchema.safeParse(data).success; - } - return true; - }, - { - message: "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name." - } - ) -}); - -type ProxySettingsValues = z.infer; -type TlsSettingsValues = z.infer; -type TargetsSettingsValues = z.infer; - export default function ReverseProxyTargets(props: { params: Promise<{ resourceId: number }>; }) { @@ -154,6 +115,45 @@ export default function ReverseProxyTargets(props: { const [isAdvancedOpen, setIsAdvancedOpen] = useState(false); const router = useRouter(); + const proxySettingsSchema = z.object({ + setHostHeader: z + .string() + .optional() + .refine( + (data) => { + if (data) { + return tlsNameSchema.safeParse(data).success; + } + return true; + }, + { + message: t('proxyErrorInvalidHeader') + } + ) + }); + + const tlsSettingsSchema = z.object({ + ssl: z.boolean(), + tlsServerName: z + .string() + .optional() + .refine( + (data) => { + if (data) { + return tlsNameSchema.safeParse(data).success; + } + return true; + }, + { + message: t('proxyErrorTls') + } + ) + }); + + type ProxySettingsValues = z.infer; + type TlsSettingsValues = z.infer; + type TargetsSettingsValues = z.infer; + const addTargetForm = useForm({ resolver: zodResolver(addTargetSchema), defaultValues: { @@ -583,7 +583,7 @@ export default function ReverseProxyTargets(props: { 32) { throw new Error(t('subnetMaskErrorInvalid')); diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 02833359..d10e71e6 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -102,7 +102,8 @@ export default function ResourceRules(props: { const router = useRouter(); const t = useTranslations(); - const RuleAction = { + + RuleAction = { ACCEPT: t('alwaysAllow'), DROP: t('alwaysDeny') } as const; diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index ee3d9b30..aeaa258e 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -62,7 +62,7 @@ import { cn } from "@app/lib/cn"; import { SquareArrowOutUpRight } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import Link from "next/link"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; const baseResourceFormSchema = z.object({ name: z.string().min(1).max(255), diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index 908a8691..bbd2a582 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -9,7 +9,7 @@ import { cache } from "react"; import { GetOrgResponse } from "@server/routers/org"; import OrgProvider from "@app/providers/OrgProvider"; import ResourcesSplashCard from "./ResourcesSplashCard"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type ResourcesPageProps = { params: Promise<{ orgId: string }>; diff --git a/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx index 62c223e0..c44f43b7 100644 --- a/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx +++ b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx @@ -15,7 +15,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { useEnvContext } from "@app/hooks/useEnvContext"; import CopyToClipboard from "@app/components/CopyToClipboard"; import CopyTextBox from "@app/components/CopyTextBox"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; interface AccessTokenSectionProps { token: string; diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index 66bf8fcf..cce81da7 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -66,7 +66,7 @@ import { CollapsibleTrigger } from "@app/components/ui/collapsible"; import AccessTokenSection from "./AccessTokenUsage"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type FormProps = { open: boolean; @@ -74,15 +74,6 @@ type FormProps = { onCreated?: (result: ShareLinkRow) => void; }; -const formSchema = z.object({ - resourceId: z.number({ message: "Please select a resource" }), - resourceName: z.string(), - resourceUrl: z.string(), - timeUnit: z.string(), - timeValue: z.coerce.number().int().positive().min(1), - title: z.string().optional() -}); - export default function CreateShareLinkForm({ open, setOpen, @@ -100,6 +91,7 @@ export default function CreateShareLinkForm({ const [neverExpire, setNeverExpire] = useState(false); const [isOpen, setIsOpen] = useState(false); + const t = useTranslations(); const [resources, setResources] = useState< { @@ -110,7 +102,14 @@ export default function CreateShareLinkForm({ }[] >([]); - const t = useTranslations(); + const formSchema = z.object({ + resourceId: z.number({ message: t('shareErrorSelectResource') }), + resourceName: z.string(), + resourceUrl: z.string(), + timeUnit: z.string(), + timeValue: z.coerce.number().int().positive().min(1), + title: z.string().optional() + }); const timeUnits = [ { unit: "minutes", name: t('minutes') }, diff --git a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx index 9e9d914a..e9fc4c6a 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx @@ -4,7 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; diff --git a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx index 3000575a..de419319 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksTable.tsx @@ -33,7 +33,7 @@ import { ListAccessTokensResponse } from "@server/routers/accessToken"; import moment from "moment"; import CreateShareLinkForm from "./CreateShareLinkForm"; import { constructShareLink } from "@app/lib/shareLinks"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; export type ShareLinkRow = { accessTokenId: string; diff --git a/src/app/[orgId]/settings/share-links/page.tsx b/src/app/[orgId]/settings/share-links/page.tsx index 14a0d9b6..e4efabd9 100644 --- a/src/app/[orgId]/settings/share-links/page.tsx +++ b/src/app/[orgId]/settings/share-links/page.tsx @@ -9,7 +9,7 @@ import OrgProvider from "@app/providers/OrgProvider"; import { ListAccessTokensResponse } from "@server/routers/accessToken"; import ShareLinksTable, { ShareLinkRow } from "./ShareLinksTable"; import ShareableLinksSplash from "./ShareLinksSplash"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type ShareLinksPageProps = { params: Promise<{ orgId: string }>; diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 9973dcf0..8d7e711e 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -50,26 +50,7 @@ import { CollapsibleTrigger } from "@app/components/ui/collapsible"; import LoaderPlaceholder from "@app/components/PlaceHolderLoader"; -import { useTranslations } from 'next-intl'; - -const createSiteFormSchema = z.object({ - name: z - .string() - .min(2, { - message: "Name must be at least 2 characters." - }) - .max(30, { - message: "Name must not be longer than 30 characters." - }), - method: z.enum(["wireguard", "newt", "local"]) -}); - -type CreateSiteFormValues = z.infer; - -const defaultValues: Partial = { - name: "", - method: "newt" -}; +import { useTranslations } from "next-intl"; type CreateSiteFormProps = { onCreate?: (site: SiteRow) => void; @@ -97,6 +78,25 @@ export default function CreateSiteForm({ privateKey: string; } | null>(null); + const createSiteFormSchema = z.object({ + name: z + .string() + .min(2, { + message: t('nameMin', {len: 2}) + }) + .max(30, { + message: t('nameMax', {len: 30}) + }), + method: z.enum(["wireguard", "newt", "local"]) + }); + + type CreateSiteFormValues = z.infer; + + const defaultValues: Partial = { + name: "", + method: "newt" + }; + const [siteDefaults, setSiteDefaults] = useState(null); diff --git a/src/app/[orgId]/settings/sites/CreateSiteModal.tsx b/src/app/[orgId]/settings/sites/CreateSiteModal.tsx index 4cecdab4..8ecee55c 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteModal.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteModal.tsx @@ -14,7 +14,7 @@ import { } from "@app/components/Credenza"; import { SiteRow } from "./SitesTable"; import CreateSiteForm from "./CreateSiteForm"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type CreateSiteFormProps = { open: boolean; diff --git a/src/app/[orgId]/settings/sites/SitesDataTable.tsx b/src/app/[orgId]/settings/sites/SitesDataTable.tsx index fdf7805c..99445dea 100644 --- a/src/app/[orgId]/settings/sites/SitesDataTable.tsx +++ b/src/app/[orgId]/settings/sites/SitesDataTable.tsx @@ -2,7 +2,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; diff --git a/src/app/[orgId]/settings/sites/SitesTable.tsx b/src/app/[orgId]/settings/sites/SitesTable.tsx index 5df661e0..6975e24d 100644 --- a/src/app/[orgId]/settings/sites/SitesTable.tsx +++ b/src/app/[orgId]/settings/sites/SitesTable.tsx @@ -27,7 +27,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import CreateSiteFormModal from "./CreateSiteModal"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; export type SiteRow = { id: number; diff --git a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx index c2e830a6..2803e987 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/SiteInfoCard.tsx @@ -9,7 +9,7 @@ import { InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type SiteInfoCardProps = {}; diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index c490f1e9..f406de6c 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -31,13 +31,7 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useState } from "react"; -import { useTranslations } from 'next-intl'; - -const GeneralFormSchema = z.object({ - name: z.string().nonempty("Name is required") -}); - -type GeneralFormValues = z.infer; +import { useTranslations } from "next-intl"; export default function GeneralPage() { const { site, updateSite } = useSiteContext(); @@ -47,6 +41,13 @@ export default function GeneralPage() { const [loading, setLoading] = useState(false); const router = useRouter(); + const t = useTranslations(); + + const GeneralFormSchema = z.object({ + name: z.string().nonempty(t('nameRequired')) + }); + + type GeneralFormValues = z.infer; const form = useForm({ resolver: zodResolver(GeneralFormSchema), @@ -55,7 +56,6 @@ export default function GeneralPage() { }, mode: "onChange" }); - const t = useTranslations(); async function onSubmit(data: GeneralFormValues) { setLoading(true); diff --git a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx index f5c98c31..2eb3bd13 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx @@ -16,7 +16,7 @@ import { BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; import SiteInfoCard from "./SiteInfoCard"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; interface SettingsLayoutProps { children: React.ReactNode; diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index d5b9a9fa..ab60e2e8 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -64,33 +64,7 @@ import { } from "@app/components/ui/breadcrumb"; import Link from "next/link"; import { QRCodeCanvas } from "qrcode.react"; -import { useTranslations } from 'next-intl'; - -const createSiteFormSchema = z - .object({ - name: z - .string() - .min(2, "Name must be at least 2 characters.") - .max(30, { - message: "Name must not be longer than 30 characters." - }), - method: z.enum(["newt", "wireguard", "local"]), - copied: z.boolean() - }) - .refine( - (data) => { - if (data.method !== "local") { - return data.copied; - } - return true; - }, - { - message: "Please confirm that you have copied the config.", - path: ["copied"] - } - ); - -type CreateSiteFormValues = z.infer; +import { useTranslations } from "next-intl"; type SiteType = "newt" | "wireguard" | "local"; @@ -127,6 +101,32 @@ export default function Page() { const router = useRouter(); const t = useTranslations(); + const createSiteFormSchema = z + .object({ + name: z + .string() + .min(2, { message: t('nameMin', {len: 2}) }) + .max(30, { + message: t('nameMax', {len: 30}) + }), + method: z.enum(["newt", "wireguard", "local"]), + copied: z.boolean() + }) + .refine( + (data) => { + if (data.method !== "local") { + return data.copied; + } + return true; + }, + { + message: t('sitesConfirmCopy'), + path: ["copied"] + } + ); + + type CreateSiteFormValues = z.infer; + const [tunnelTypes, setTunnelTypes] = useState< ReadonlyArray >([ diff --git a/src/app/[orgId]/settings/sites/page.tsx b/src/app/[orgId]/settings/sites/page.tsx index d76e5fe1..401fb2e5 100644 --- a/src/app/[orgId]/settings/sites/page.tsx +++ b/src/app/[orgId]/settings/sites/page.tsx @@ -5,7 +5,7 @@ import { AxiosResponse } from "axios"; import SitesTable, { SiteRow } from "./SitesTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import SitesSplashCard from "./SitesSplashCard"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type SitesPageProps = { params: Promise<{ orgId: string }>; diff --git a/src/app/admin/api-keys/[apiKeyId]/layout.tsx b/src/app/admin/api-keys/[apiKeyId]/layout.tsx index 818fcccc..0d6f7bdb 100644 --- a/src/app/admin/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/layout.tsx @@ -15,7 +15,7 @@ import { import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; interface SettingsLayoutProps { children: React.ReactNode; diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index 4a58385c..5ca647c5 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -56,35 +56,6 @@ import CopyTextBox from "@app/components/CopyTextBox"; import PermissionsSelectBox from "@app/components/PermissionsSelectBox"; import { useTranslations } from "next-intl"; -const createFormSchema = z.object({ - name: z - .string() - .min(2, { - message: "Name must be at least 2 characters." - }) - .max(255, { - message: "Name must not be longer than 255 characters." - }) -}); - -type CreateFormValues = z.infer; - -const copiedFormSchema = z - .object({ - copied: z.boolean() - }) - .refine( - (data) => { - return data.copied; - }, - { - message: "You must confirm that you have copied the API key.", - path: ["copied"] - } - ); - -type CopiedFormValues = z.infer; - export default function Page() { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -98,6 +69,35 @@ export default function Page() { Record >({}); + const createFormSchema = z.object({ + name: z + .string() + .min(2, { + message: t('nameMin', {len: 2}) + }) + .max(255, { + message: t('nameMax', {len: 255}) + }) + }); + + type CreateFormValues = z.infer; + + const copiedFormSchema = z + .object({ + copied: z.boolean() + }) + .refine( + (data) => { + return data.copied; + }, + { + message: t('apiKeysConfirmCopy2'), + path: ["copied"] + } + ); + + type CopiedFormValues = z.infer; + const form = useForm({ resolver: zodResolver(createFormSchema), defaultValues: { diff --git a/src/app/admin/api-keys/page.tsx b/src/app/admin/api-keys/page.tsx index bba54e1d..22607f2f 100644 --- a/src/app/admin/api-keys/page.tsx +++ b/src/app/admin/api-keys/page.tsx @@ -4,7 +4,7 @@ import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { ListRootApiKeysResponse } from "@server/routers/apiKeys"; import ApiKeysTable, { ApiKeyRow } from "./ApiKeysTable"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type ApiKeyPageProps = {}; diff --git a/src/app/admin/idp/AdminIdpTable.tsx b/src/app/admin/idp/AdminIdpTable.tsx index efb9fc35..c55a2b35 100644 --- a/src/app/admin/idp/AdminIdpTable.tsx +++ b/src/app/admin/idp/AdminIdpTable.tsx @@ -60,7 +60,7 @@ export default function IdpTable({ idps }: Props) { const getTypeDisplay = (type: string) => { switch (type) { case "oidc": - return t('idpOidc'); + return "OAuth2/OIDC"; default: return type; } diff --git a/src/app/admin/idp/[idpId]/general/page.tsx b/src/app/admin/idp/[idpId]/general/page.tsx index 8621cf26..308bca34 100644 --- a/src/app/admin/idp/[idpId]/general/page.tsx +++ b/src/app/admin/idp/[idpId]/general/page.tsx @@ -45,23 +45,6 @@ import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; -const GeneralFormSchema = z.object({ - name: z.string().min(2, "Name must be at least 2 characters."), - clientId: z.string().min(1, { message: "Client ID is required." }), - clientSecret: z.string().min(1, { message: "Client Secret is required." }), - authUrl: z.string().url({ message: "Auth URL must be a valid URL." }), - tokenUrl: z.string().url({ message: "Token URL must be a valid URL." }), - identifierPath: z - .string() - .min(1, { message: "Identifier Path is required." }), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().min(1, { message: "Scopes are required." }), - autoProvision: z.boolean().default(false) -}); - -type GeneralFormValues = z.infer; - export default function GeneralPage() { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -72,6 +55,24 @@ export default function GeneralPage() { const { isUnlocked } = useLicenseStatusContext(); const redirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`; + const t = useTranslations(); + + const GeneralFormSchema = z.object({ + name: z.string().min(2, { message: t('nameMin', {len: 2}) }), + clientId: z.string().min(1, { message: t('idpClientIdRequired') }), + clientSecret: z.string().min(1, { message: t('idpClientSecretRequired') }), + authUrl: z.string().url({ message: t('idpErrorAuthUrlInvalid') }), + tokenUrl: z.string().url({ message: t('idpErrorTokenUrlInvalid') }), + identifierPath: z + .string() + .min(1, { message: t('idpPathRequired') }), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().min(1, { message: t('idpScopeRequired') }), + autoProvision: z.boolean().default(false) + }); + + type GeneralFormValues = z.infer; const form = useForm({ resolver: zodResolver(GeneralFormSchema), @@ -89,8 +90,6 @@ export default function GeneralPage() { } }); - const t = useTranslations(); - useEffect(() => { const loadIdp = async () => { try { diff --git a/src/app/admin/idp/[idpId]/layout.tsx b/src/app/admin/idp/[idpId]/layout.tsx index 4b8e5be1..79b1e196 100644 --- a/src/app/admin/idp/[idpId]/layout.tsx +++ b/src/app/admin/idp/[idpId]/layout.tsx @@ -15,7 +15,7 @@ import { BreadcrumbPage, BreadcrumbSeparator } from "@app/components/ui/breadcrumb"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; interface SettingsLayoutProps { children: React.ReactNode; @@ -52,7 +52,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <> diff --git a/src/app/admin/idp/[idpId]/policies/page.tsx b/src/app/admin/idp/[idpId]/policies/page.tsx index fa473cd9..8f36434e 100644 --- a/src/app/admin/idp/[idpId]/policies/page.tsx +++ b/src/app/admin/idp/[idpId]/policies/page.tsx @@ -70,20 +70,6 @@ type Organization = { name: string; }; -const policyFormSchema = z.object({ - orgId: z.string().min(1, { message: "Organization is required" }), - roleMapping: z.string().optional(), - orgMapping: z.string().optional() -}); - -const defaultMappingsSchema = z.object({ - defaultRoleMapping: z.string().optional(), - defaultOrgMapping: z.string().optional() -}); - -type PolicyFormValues = z.infer; -type DefaultMappingsValues = z.infer; - export default function PoliciesPage() { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -102,6 +88,20 @@ export default function PoliciesPage() { const [showAddDialog, setShowAddDialog] = useState(false); const [editingPolicy, setEditingPolicy] = useState(null); + const policyFormSchema = z.object({ + orgId: z.string().min(1, { message: t('orgRequired') }), + roleMapping: z.string().optional(), + orgMapping: z.string().optional() + }); + + const defaultMappingsSchema = z.object({ + defaultRoleMapping: z.string().optional(), + defaultOrgMapping: z.string().optional() + }); + + type PolicyFormValues = z.infer; + type DefaultMappingsValues = z.infer; + const form = useForm({ resolver: zodResolver(policyFormSchema), defaultValues: { diff --git a/src/app/admin/idp/create/page.tsx b/src/app/admin/idp/create/page.tsx index d89f14b6..8cd62e65 100644 --- a/src/app/admin/idp/create/page.tsx +++ b/src/app/admin/idp/create/page.tsx @@ -39,38 +39,6 @@ import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; -const createIdpFormSchema = z.object({ - name: z.string().min(2, "Name must be at least 2 characters."), - type: z.enum(["oidc"]), - clientId: z.string().min(1, { message: "Client ID is required." }), - clientSecret: z.string().min(1, { message: "Client Secret is required." }), - authUrl: z.string().url({ message: "Auth URL must be a valid URL." }), - tokenUrl: z.string().url({ message: "Token URL must be a valid URL." }), - identifierPath: z - .string() - .min(1, { message: "Identifier Path is required." }), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().min(1, { message: "Scopes are required." }), - autoProvision: z.boolean().default(false) -}); - -type CreateIdpFormValues = z.infer; - -interface ProviderTypeOption { - id: "oidc"; - title: string; - description: string; -} - -const providerTypes: ReadonlyArray = [ - { - id: "oidc", - title: "OAuth2/OIDC", - description: "Configure an OpenID Connect identity provider" - } -]; - export default function Page() { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -79,6 +47,38 @@ export default function Page() { const { isUnlocked } = useLicenseStatusContext(); const t = useTranslations(); + const createIdpFormSchema = z.object({ + name: z.string().min(2, { message: t('nameMin', {len: 2}) }), + type: z.enum(["oidc"]), + clientId: z.string().min(1, { message: t('idpClientIdRequired') }), + clientSecret: z.string().min(1, { message: t('idpClientSecretRequired') }), + authUrl: z.string().url({ message: t('idpErrorAuthUrlInvalid') }), + tokenUrl: z.string().url({ message: t('idpErrorTokenUrlInvalid') }), + identifierPath: z + .string() + .min(1, { message: t('idpPathRequired') }), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().min(1, { message: t('idpScopeRequired') }), + autoProvision: z.boolean().default(false) + }); + + type CreateIdpFormValues = z.infer; + + interface ProviderTypeOption { + id: "oidc"; + title: string; + description: string; + } + + const providerTypes: ReadonlyArray = [ + { + id: "oidc", + title: "OAuth2/OIDC", + description: t('idpOidcDescription') + } + ]; + const form = useForm({ resolver: zodResolver(createIdpFormSchema), defaultValues: { diff --git a/src/app/admin/idp/page.tsx b/src/app/admin/idp/page.tsx index 3f40e960..4db77785 100644 --- a/src/app/admin/idp/page.tsx +++ b/src/app/admin/idp/page.tsx @@ -3,7 +3,7 @@ import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import IdpTable, { IdpRow } from "./AdminIdpTable"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export default async function IdpPage() { let idps: IdpRow[] = []; diff --git a/src/app/admin/license/LicenseKeysDataTable.tsx b/src/app/admin/license/LicenseKeysDataTable.tsx index e98fef1a..d9ace464 100644 --- a/src/app/admin/license/LicenseKeysDataTable.tsx +++ b/src/app/admin/license/LicenseKeysDataTable.tsx @@ -8,7 +8,7 @@ import { LicenseKeyCache } from "@server/license/license"; import { ArrowUpDown } from "lucide-react"; import moment from "moment"; import CopyToClipboard from "@app/components/CopyToClipboard"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type LicenseKeysDataTableProps = { licenseKeys: LicenseKeyCache[]; diff --git a/src/app/admin/license/components/SitePriceCalculator.tsx b/src/app/admin/license/components/SitePriceCalculator.tsx index d42facc5..5d09fa54 100644 --- a/src/app/admin/license/components/SitePriceCalculator.tsx +++ b/src/app/admin/license/components/SitePriceCalculator.tsx @@ -11,7 +11,7 @@ import { CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type SitePriceCalculatorProps = { isOpen: boolean; diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index ac2c6d67..83549e92 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -54,17 +54,7 @@ import Link from "next/link"; import { Checkbox } from "@app/components/ui/checkbox"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; -import { useTranslations } from 'next-intl'; - -const formSchema = z.object({ - licenseKey: z - .string() - .nonempty({ message: "License key is required" }) - .max(255), - agreeToTerms: z.boolean().refine((val) => val === true, { - message: "You must agree to the license terms" - }) -}); +import { useTranslations } from "next-intl"; function obfuscateLicenseKey(key: string): string { if (key.length <= 8) return key; @@ -95,6 +85,18 @@ export default function LicensePage() { const [isRecheckingLicense, setIsRecheckingLicense] = useState(false); const { supporterStatus } = useSupporterStatusContext(); + const t = useTranslations(); + + const formSchema = z.object({ + licenseKey: z + .string() + .nonempty({ message: t('licenseKeyRequired') }) + .max(255), + agreeToTerms: z.boolean().refine((val) => val === true, { + message: t('licenseTermsAgree') + }) + }); + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -103,8 +105,6 @@ export default function LicensePage() { } }); - const t = useTranslations(); - useEffect(() => { async function load() { setIsInitialLoading(true); diff --git a/src/app/admin/users/AdminUsersDataTable.tsx b/src/app/admin/users/AdminUsersDataTable.tsx index 3a1e85cf..5e1e3ce8 100644 --- a/src/app/admin/users/AdminUsersDataTable.tsx +++ b/src/app/admin/users/AdminUsersDataTable.tsx @@ -4,7 +4,7 @@ import { ColumnDef, } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index 75d7a731..fdb09f8a 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -11,7 +11,7 @@ import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; export type GlobalUserRow = { id: string; diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 793c5f31..1e29a311 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -6,7 +6,7 @@ import { AdminListUsersResponse } from "@server/routers/user/adminListUsers"; import UsersTable, { GlobalUserRow } from "./AdminUsersTable"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon } from "lucide-react"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type PageProps = { params: Promise<{ orgId: string }>; diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 2bbea496..f5e9eb07 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -3,7 +3,7 @@ import ValidateOidcToken from "./ValidateOidcToken"; import { idp } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export default async function Page(props: { params: Promise<{ orgId: string; idpId: string }>; diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx index 3017030b..05a65fce 100644 --- a/src/app/auth/layout.tsx +++ b/src/app/auth/layout.tsx @@ -8,7 +8,7 @@ import { AxiosResponse } from "axios"; import { ExternalLink } from "lucide-react"; import { Metadata } from "next"; import { cache } from "react"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export const metadata: Metadata = { title: `Auth - Pangolin`, diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 13204796..9ac45c66 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -9,7 +9,7 @@ import { cleanRedirect } from "@app/lib/cleanRedirect"; import db from "@server/db"; import { idp } from "@server/db/schemas"; import { LoginFormIDP } from "@app/components/LoginForm"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/reset-password/ResetPasswordForm.tsx b/src/app/auth/reset-password/ResetPasswordForm.tsx index 92cfa7db..8262c738 100644 --- a/src/app/auth/reset-password/ResetPasswordForm.tsx +++ b/src/app/auth/reset-password/ResetPasswordForm.tsx @@ -50,22 +50,6 @@ const requestSchema = z.object({ email: z.string().email() }); -const formSchema = z - .object({ - email: z.string().email({ message: "Invalid email address" }), - token: z.string().min(8, { message: "Invalid token" }), - password: passwordSchema, - confirmPassword: passwordSchema - }) - .refine((data) => data.password === data.confirmPassword, { - path: ["confirmPassword"], - message: "Passwords do not match" - }); - -const mfaSchema = z.object({ - code: z.string().length(6, { message: "Invalid code" }) -}); - export type ResetPasswordFormProps = { emailParam?: string; tokenParam?: string; @@ -82,6 +66,7 @@ export default function ResetPasswordForm({ const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); + const t = useTranslations(); function getState() { if (emailParam && !tokenParam) { @@ -99,6 +84,22 @@ export default function ResetPasswordForm({ const api = createApiClient(useEnvContext()); + const formSchema = z + .object({ + email: z.string().email({ message: t('emailInvalid') }), + token: z.string().min(8, { message: t('tokenInvalid') }), + password: passwordSchema, + confirmPassword: passwordSchema + }) + .refine((data) => data.password === data.confirmPassword, { + path: ["confirmPassword"], + message: t('passwordNotMatch') + }); + + const mfaSchema = z.object({ + code: z.string().length(6, { message: t('pincodeInvalid') }) + }); + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -123,8 +124,6 @@ export default function ResetPasswordForm({ } }); - const t = useTranslations(); - async function onRequest(data: z.infer) { const { email } = data; diff --git a/src/app/auth/reset-password/page.tsx b/src/app/auth/reset-password/page.tsx index f948c34b..a0466208 100644 --- a/src/app/auth/reset-password/page.tsx +++ b/src/app/auth/reset-password/page.tsx @@ -4,7 +4,7 @@ import { cache } from "react"; import ResetPasswordForm from "./ResetPasswordForm"; import Link from "next/link"; import { cleanRedirect } from "@app/lib/cleanRedirect"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx index 11c7b817..518fe488 100644 --- a/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx +++ b/src/app/auth/resource/[resourceId]/ResourceNotFound.tsx @@ -7,11 +7,11 @@ import { CardTitle, } from "@app/components/ui/card"; import Link from "next/link"; -import { useTranslations } from "next-intl"; +import { getTranslations } from "next-intl/server"; export default async function ResourceNotFound() { - const t = useTranslations(); + const t = await getTranslations(); return ( diff --git a/src/app/auth/signup/SignupForm.tsx b/src/app/auth/signup/SignupForm.tsx index d3639a0e..bd693180 100644 --- a/src/app/auth/signup/SignupForm.tsx +++ b/src/app/auth/signup/SignupForm.tsx @@ -31,7 +31,7 @@ import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import Image from "next/image"; import { cleanRedirect } from "@app/lib/cleanRedirect"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type SignupFormProps = { redirect?: string; diff --git a/src/app/auth/verify-email/VerifyEmailForm.tsx b/src/app/auth/verify-email/VerifyEmailForm.tsx index eb7c4db0..cbe1e5fb 100644 --- a/src/app/auth/verify-email/VerifyEmailForm.tsx +++ b/src/app/auth/verify-email/VerifyEmailForm.tsx @@ -39,13 +39,6 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import { useTranslations } from "next-intl"; -const FormSchema = z.object({ - email: z.string().email({ message: "Invalid email address" }), - pin: z.string().min(8, { - message: "Your verification code must be 8 characters.", - }), -}); - export type VerifyEmailFormProps = { email: string; redirect?: string; @@ -65,6 +58,13 @@ export default function VerifyEmailForm({ const api = createApiClient(useEnvContext()); + const FormSchema = z.object({ + email: z.string().email({ message: t('emailInvalid') }), + pin: z.string().min(8, { + message: t('verificationCodeLengthRequirements'), + }), + }); + const form = useForm>({ resolver: zodResolver(FormSchema), defaultValues: { diff --git a/src/app/components/LicenseViolation.tsx b/src/app/components/LicenseViolation.tsx index 980fa7d1..ea025e4c 100644 --- a/src/app/components/LicenseViolation.tsx +++ b/src/app/components/LicenseViolation.tsx @@ -3,7 +3,7 @@ import { Button } from "@app/components/ui/button"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useState } from "react"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; export default function LicenseViolation() { const { licenseStatus } = useLicenseStatusContext(); diff --git a/src/app/components/OrganizationLanding.tsx b/src/app/components/OrganizationLanding.tsx index 1a3a4086..a443fcf3 100644 --- a/src/app/components/OrganizationLanding.tsx +++ b/src/app/components/OrganizationLanding.tsx @@ -11,7 +11,7 @@ import { import { Button } from "@/components/ui/button"; import Link from "next/link"; import { ArrowRight, Plus } from "lucide-react"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; interface Organization { id: string; diff --git a/src/app/invite/InviteStatusCard.tsx b/src/app/invite/InviteStatusCard.tsx index c2c757c6..3ecf16f5 100644 --- a/src/app/invite/InviteStatusCard.tsx +++ b/src/app/invite/InviteStatusCard.tsx @@ -12,7 +12,7 @@ import { import { useEnvContext } from "@app/hooks/useEnvContext"; import { XCircle } from "lucide-react"; import { useRouter } from "next/navigation"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type InviteStatusCardProps = { type: "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in"; diff --git a/src/app/invite/page.tsx b/src/app/invite/page.tsx index bf423a75..014fb45b 100644 --- a/src/app/invite/page.tsx +++ b/src/app/invite/page.tsx @@ -6,7 +6,7 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import InviteStatusCard from "./InviteStatusCard"; import { formatAxiosError } from "@app/lib/api"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export default async function InvitePage(props: { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f7e11ea7..dd02c489 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,8 +13,8 @@ import LicenseStatusProvider from "@app/providers/LicenseStatusProvider"; import { GetLicenseStatusResponse } from "@server/routers/license"; import LicenseViolation from "./components/LicenseViolation"; import { cache } from "react"; -import { NextIntlClientProvider } from 'next-intl'; -import { getLocale } from 'next-intl/server'; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale } from "next-intl/server"; export const metadata: Metadata = { title: `Dashboard - Pangolin`, diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 058ccc2b..60c02bee 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,4 +1,4 @@ -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; export default async function NotFound() { diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 293dc24a..7926f359 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -33,15 +33,10 @@ import { } from "@app/components/ui/form"; import { Alert, AlertDescription } from "@app/components/ui/alert"; import CreateSiteForm from "../[orgId]/settings/sites/CreateSiteForm"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type Step = "org" | "site" | "resources"; -const orgSchema = z.object({ - orgName: z.string().min(1, { message: "Organization name is required" }), - orgId: z.string().min(1, { message: "Organization ID is required" }) -}); - export default function StepperForm() { const [currentStep, setCurrentStep] = useState("org"); const [orgIdTaken, setOrgIdTaken] = useState(false); @@ -51,6 +46,11 @@ export default function StepperForm() { const [isChecked, setIsChecked] = useState(false); const [error, setError] = useState(null); + const orgSchema = z.object({ + orgName: z.string().min(1, { message: t('orgNameRequired') }), + orgId: z.string().min(1, { message: t('orgIdRequired') }) + }); + const orgForm = useForm>({ resolver: zodResolver(orgSchema), defaultValues: { diff --git a/src/components/Enable2FaForm.tsx b/src/components/Enable2FaForm.tsx index 80224173..47cfb8e8 100644 --- a/src/components/Enable2FaForm.tsx +++ b/src/components/Enable2FaForm.tsx @@ -42,14 +42,6 @@ import { QRCodeCanvas, QRCodeSVG } from "qrcode.react"; import { useUserContext } from "@app/hooks/useUserContext"; import { useTranslations } from "next-intl"; -const enableSchema = z.object({ - password: z.string().min(1, { message: "Password is required" }) -}); - -const confirmSchema = z.object({ - code: z.string().length(6, { message: "Invalid code" }) -}); - type Enable2FaProps = { open: boolean; setOpen: (val: boolean) => void; @@ -68,6 +60,15 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { const { user, updateUser } = useUserContext(); const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + const enableSchema = z.object({ + password: z.string().min(1, { message: t('passwordRequired') }) + }); + + const confirmSchema = z.object({ + code: z.string().length(6, { message: t('pincodeInvalid') }) + }); const enableForm = useForm>({ resolver: zodResolver(enableSchema), @@ -83,8 +84,6 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { } }); - const t = useTranslations(); - const request2fa = async (values: z.infer) => { setLoading(true); diff --git a/src/components/PermissionsSelectBox.tsx b/src/components/PermissionsSelectBox.tsx index 3bd90ae4..3f225dee 100644 --- a/src/components/PermissionsSelectBox.tsx +++ b/src/components/PermissionsSelectBox.tsx @@ -163,13 +163,15 @@ export default function PermissionsSelectBox({ onChange(updated); }; + const t = useTranslations(); + return ( <>

    toggleAllPermissions(checked as boolean) @@ -188,7 +190,7 @@ export default function PermissionsSelectBox({ toggleAllInCategory( diff --git a/src/components/SupporterStatus.tsx b/src/components/SupporterStatus.tsx index 5febb624..a17b9b9f 100644 --- a/src/components/SupporterStatus.tsx +++ b/src/components/SupporterStatus.tsx @@ -50,13 +50,6 @@ import { Check, ExternalLink } from "lucide-react"; import confetti from "canvas-confetti"; import { useTranslations } from "next-intl"; -const formSchema = z.object({ - githubUsername: z - .string() - .nonempty({ message: "GitHub username is required" }), - key: z.string().nonempty({ message: "Supporter key is required" }) -}); - export default function SupporterStatus() { const { supporterStatus, updateSupporterStatus } = useSupporterStatusContext(); @@ -65,6 +58,14 @@ export default function SupporterStatus() { const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false); const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + const formSchema = z.object({ + githubUsername: z + .string() + .nonempty({ message: "GitHub username is required" }), + key: z.string().nonempty({ message: "Supporter key is required" }) + }); const form = useForm>({ resolver: zodResolver(formSchema), @@ -74,8 +75,6 @@ export default function SupporterStatus() { } }); - const t = useTranslations(); - async function hide() { await api.post("/supporter-key/hide"); @@ -167,7 +166,7 @@ export default function SupporterStatus() { title: t('error'), description: formatAxiosError( error, - "Failed to validate supporter key." + t('supportKeyErrorValidationDescription') ) }); return; diff --git a/src/components/tags/autocomplete.tsx b/src/components/tags/autocomplete.tsx index 32e6f42d..04806643 100644 --- a/src/components/tags/autocomplete.tsx +++ b/src/components/tags/autocomplete.tsx @@ -4,7 +4,7 @@ import { TagInputStyleClassesProps, type Tag as TagType } from "./tag-input"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import { Button } from "../ui/button"; import { cn } from "@app/lib/cn"; -import { useTranslations } from 'next-intl'; +import { useTranslations } from "next-intl"; type AutocompleteProps = { tags: TagType[]; From dba5a73e0e0d6c6ca02f366a63b7dde8a8753357 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 25 May 2025 21:09:05 +0200 Subject: [PATCH 076/105] New Crowdin updates (#28) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Turkish) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) --- messages/de-DE.json | 10 +++++++--- messages/fr-FR.json | 8 ++++++-- messages/it-IT.json | 10 +++++++--- messages/pl-PL.json | 8 ++++++-- messages/pt-PT.json | 8 ++++++-- messages/tr-TR.json | 8 ++++++-- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 22dd5909..780377d8 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -458,6 +458,7 @@ "createdAt": "Erstellt am", "proxyErrorInvalidHeader": "Ungültiger benutzerdefinierter Host-Header-Wert. Verwenden Sie das Domänennamensformat oder speichern Sie leer, um den benutzerdefinierten Host-Header zu deaktivieren.", "proxyErrorTls": "Ungültiger TLS-Servername. Verwenden Sie das Domänennamensformat oder speichern Sie leer, um den TLS-Servernamen zu entfernen.", + "proxyEnableSSL": "SSL aktivieren (https)", "targetErrorFetch": "Fehler beim Abrufen der Ziele", "targetErrorFetchDescription": "Beim Abrufen der Ziele ist ein Fehler aufgetreten", "siteErrorFetch": "Fehler beim Abrufen der Ressource", @@ -850,7 +851,7 @@ "otpEmail": "Einmalpasswort (OTP)", "otpEmailSubmit": "OTP absenden", "backToEmail": "Zurück zur E-Mail", - "noSupportKey": "Server läuft ohne Unterstützer-Schlüssel.
    Erwägen Sie, das Projekt zu unterstützen!", + "noSupportKey": "Server läuft ohne Unterstützungsschlüssel. Erwägen Sie die Unterstützung des Projekts!", "accessDenied": "Zugriff verweigert", "accessDeniedDescription": "Sie haben keine Berechtigung, auf diese Ressource zuzugreifen. Falls dies ein Fehler ist, kontaktieren Sie bitte den Administrator.", "accessTokenError": "Fehler beim Prüfen des Zugriffstokens", @@ -1033,7 +1034,7 @@ "navbar": "Navigationsmenü", "navbarDescription": "Hauptnavigationsmenü für die Anwendung", "navbarDocsLink": "Dokumentation", - "commercialEdition": "Commercial Edition", + "commercialEdition": "Kommerzielle Edition", "otpErrorEnable": "2FA konnte nicht aktiviert werden", "otpErrorEnableDescription": "Beim Aktivieren der 2FA ist ein Fehler aufgetreten", "otpSetupCheckCode": "Bitte geben Sie einen 6-stelligen Code ein", @@ -1059,5 +1060,8 @@ "copyText": "Text kopieren", "copyTextFailed": "Text konnte nicht kopiert werden: ", "copyTextClipboard": "In die Zwischenablage kopieren", - "inviteErrorInvalidConfirmation": "Ungültige Bestätigung" + "inviteErrorInvalidConfirmation": "Ungültige Bestätigung", + "passwordRequired": "Passwort ist erforderlich", + "allowAll": "Alle erlauben", + "permissionsAllowAll": "Alle Berechtigungen erlauben" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index cca3ae5a..14935896 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -458,6 +458,7 @@ "createdAt": "Créé le", "proxyErrorInvalidHeader": "Valeur d'en-tête Host personnalisée invalide. Utilisez le format de nom de domaine, ou laissez vide pour désactiver l'en-tête Host personnalisé.", "proxyErrorTls": "Nom de serveur TLS invalide. Utilisez le format de nom de domaine, ou laissez vide pour supprimer le nom de serveur TLS.", + "proxyEnableSSL": "Activer SSL (https)", "targetErrorFetch": "Échec de la récupération des cibles", "targetErrorFetchDescription": "Une erreur s'est produite lors de la récupération des cibles", "siteErrorFetch": "Échec de la récupération de la ressource", @@ -850,7 +851,7 @@ "otpEmail": "Mot de passe à usage unique (OTP)", "otpEmailSubmit": "Soumettre l'OTP", "backToEmail": "Retour à l'e-mail", - "noSupportKey": "Le serveur fonctionne sans clé de support.
    Envisagez de soutenir le projet !", + "noSupportKey": "Le serveur fonctionne sans clé de supporteur. Pensez à soutenir le projet !", "accessDenied": "Accès refusé", "accessDeniedDescription": "Vous n'êtes pas autorisé à accéder à cette ressource. Si c'est une erreur, veuillez contacter l'administrateur.", "accessTokenError": "Erreur lors de la vérification du jeton d'accès", @@ -1059,5 +1060,8 @@ "copyText": "Copier le texte", "copyTextFailed": "Échec de la copie du texte : ", "copyTextClipboard": "Copier dans le presse-papiers", - "inviteErrorInvalidConfirmation": "Confirmation invalide" + "inviteErrorInvalidConfirmation": "Confirmation invalide", + "passwordRequired": "Le mot de passe est requis", + "allowAll": "Tout autoriser", + "permissionsAllowAll": "Autoriser toutes les autorisations" } diff --git a/messages/it-IT.json b/messages/it-IT.json index bf22c072..3ca4f87c 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -458,6 +458,7 @@ "createdAt": "Creato Il", "proxyErrorInvalidHeader": "Valore dell'intestazione Host personalizzata non valido. Usa il formato nome dominio o salva vuoto per rimuovere l'intestazione Host personalizzata.", "proxyErrorTls": "Nome Server TLS non valido. Usa il formato nome dominio o salva vuoto per rimuovere il Nome Server TLS.", + "proxyEnableSSL": "Abilita SSL (https)", "targetErrorFetch": "Impossibile recuperare i target", "targetErrorFetchDescription": "Si è verificato un errore durante il recupero dei target", "siteErrorFetch": "Impossibile recuperare la risorsa", @@ -850,7 +851,7 @@ "otpEmail": "Password Usa e Getta (OTP)", "otpEmailSubmit": "Invia OTP", "backToEmail": "Torna all'Email", - "noSupportKey": "Il server è in esecuzione senza una chiave di supporto.
    Considera di supportare il progetto!", + "noSupportKey": "Il server è in esecuzione senza una chiave di supporto. Considera di supportare il progetto!", "accessDenied": "Accesso Negato", "accessDeniedDescription": "Non sei autorizzato ad accedere a questa risorsa. Se ritieni che sia un errore, contatta l'amministratore.", "accessTokenError": "Errore nel controllo del token di accesso", @@ -909,7 +910,7 @@ "pangolinDashboard": "Cruscotto - Pangolino", "noResults": "Nessun risultato trovato.", "terabytes": "{count} TB", - "gigabytes": "{count} GB", + "gigabytes": "{count}GB", "megabytes": "{count} MB", "tagsEntered": "Tag Inseriti", "tagsEnteredDescription": "Questi sono i tag che hai inserito.", @@ -1059,5 +1060,8 @@ "copyText": "Copia testo", "copyTextFailed": "Impossibile copiare il testo: ", "copyTextClipboard": "Copia negli appunti", - "inviteErrorInvalidConfirmation": "Conferma non valida" + "inviteErrorInvalidConfirmation": "Conferma non valida", + "passwordRequired": "La password è obbligatoria", + "allowAll": "Consenti Tutto", + "permissionsAllowAll": "Consenti Tutti I Permessi" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index fe5c2b4b..fbdd3c1d 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -458,6 +458,7 @@ "createdAt": "Utworzono", "proxyErrorInvalidHeader": "Nieprawidłowa wartość niestandardowego nagłówka hosta. Użyj formatu nazwy domeny lub zapisz pusty, aby usunąć niestandardowy nagłówek hosta.", "proxyErrorTls": "Nieprawidłowa nazwa serwera TLS. Użyj formatu nazwy domeny lub zapisz pusty, aby usunąć nazwę serwera TLS.", + "proxyEnableSSL": "Włącz SSL (https)", "targetErrorFetch": "Nie udało się pobrać celów", "targetErrorFetchDescription": "Wystąpił błąd podczas pobierania celów", "siteErrorFetch": "Nie udało się pobrać zasobu", @@ -850,7 +851,7 @@ "otpEmail": "Hasło jednorazowe (OTP)", "otpEmailSubmit": "Wyślij OTP", "backToEmail": "Powrót do e-maila", - "noSupportKey": "Serwer działa bez klucza wspierającego.
    Rozważ wsparcie projektu!", + "noSupportKey": "Serwer działa bez klucza wspierającego. Rozważ wsparcie projektu!", "accessDenied": "Odmowa dostępu", "accessDeniedDescription": "Nie masz uprawnień dostępu do tego zasobu. Jeśli to pomyłka, skontaktuj się z administratorem.", "accessTokenError": "Błąd sprawdzania tokena dostępu", @@ -1059,5 +1060,8 @@ "copyText": "Kopiuj tekst", "copyTextFailed": "Nie udało się skopiować tekstu: ", "copyTextClipboard": "Kopiuj do schowka", - "inviteErrorInvalidConfirmation": "Nieprawidłowe potwierdzenie" + "inviteErrorInvalidConfirmation": "Nieprawidłowe potwierdzenie", + "passwordRequired": "Hasło jest wymagane", + "allowAll": "Zezwól wszystkim", + "permissionsAllowAll": "Zezwól na wszystkie uprawnienia" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index e36b651c..2bd0a29e 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -458,6 +458,7 @@ "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", "proxyErrorTls": "Nome do Servidor TLS inválido. Use o formato de nome de domínio ou salve vazio para remover o Nome do Servidor TLS.", + "proxyEnableSSL": "Habilitar SSL (https)", "targetErrorFetch": "Falha ao buscar alvos", "targetErrorFetchDescription": "Ocorreu um erro ao buscar alvos", "siteErrorFetch": "Falha ao buscar recurso", @@ -850,7 +851,7 @@ "otpEmail": "Palavra-passe Única (OTP)", "otpEmailSubmit": "Submeter OTP", "backToEmail": "Voltar ao Email", - "noSupportKey": "O servidor está a funcionar sem uma chave de suporte.
    Considere apoiar o projeto!", + "noSupportKey": "O servidor está rodando sem uma chave de suporte. Considere apoiar o projeto!", "accessDenied": "Acesso Negado", "accessDeniedDescription": "Não tem permissão para aceder a este recurso. Se isto for um erro, contacte o administrador.", "accessTokenError": "Erro ao verificar o token de acesso", @@ -1059,5 +1060,8 @@ "copyText": "Copiar texto", "copyTextFailed": "Falha ao copiar texto: ", "copyTextClipboard": "Copiar para a área de transferência", - "inviteErrorInvalidConfirmation": "Confirmação inválida" + "inviteErrorInvalidConfirmation": "Confirmação inválida", + "passwordRequired": "A senha é obrigatória", + "allowAll": "Permitir todos", + "permissionsAllowAll": "Permitir Todas as Permissões" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 712e177f..574737bb 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -458,6 +458,7 @@ "createdAt": "Created At", "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", + "proxyEnableSSL": "Enable SSL (https)", "targetErrorFetch": "Failed to fetch targets", "targetErrorFetchDescription": "An error occurred while fetching targets", "siteErrorFetch": "Failed to fetch resource", @@ -850,7 +851,7 @@ "otpEmail": "One-Time Password (OTP)", "otpEmailSubmit": "Submit OTP", "backToEmail": "Back to Email", - "noSupportKey": "Server is running without a supporter key.
    Consider supporting the project!", + "noSupportKey": "Server is running without a supporter key. Consider supporting the project!", "accessDenied": "Access Denied", "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", "accessTokenError": "Error checking access token", @@ -1059,5 +1060,8 @@ "copyText": "Copy text", "copyTextFailed": "Failed to copy text: ", "copyTextClipboard": "Copy to clipboard", - "inviteErrorInvalidConfirmation": "Invalid confirmation" + "inviteErrorInvalidConfirmation": "Invalid confirmation", + "passwordRequired": "Password is required", + "allowAll": "Allow All", + "permissionsAllowAll": "Allow All Permissions" } From dc6fafba41d9f489e0c3f56f00094a2a9dae3517 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 3 Jun 2025 20:48:20 +0300 Subject: [PATCH 077/105] Fixes for build (#124) --- .../[orgId]/settings/resources/[resourceId]/proxy/page.tsx | 4 +++- .../[orgId]/settings/resources/[resourceId]/rules/page.tsx | 4 ++-- src/app/[orgId]/settings/sites/CreateSiteForm.tsx | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 68743286..05c029e9 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -928,6 +928,8 @@ function isIPInSubnet(subnet: string, ip: string): boolean { const [subnetIP, maskBits] = subnet.split("/"); const mask = parseInt(maskBits); + const t = useTranslations(); + if (mask < 0 || mask > 32) { throw new Error(t('subnetMaskErrorInvalid')); } @@ -947,7 +949,7 @@ function ipToNumber(ip: string): number { // Validate IP address format const parts = ip.split("."); const t = useTranslations(); - + if (parts.length !== 4) { throw new Error(t('ipAddressErrorInvalidFormat')); } diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index d10e71e6..90c86aea 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -102,8 +102,8 @@ export default function ResourceRules(props: { const router = useRouter(); const t = useTranslations(); - - RuleAction = { + + const RuleAction = { ACCEPT: t('alwaysAllow'), DROP: t('alwaysDeny') } as const; diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 8d7e711e..2eb670f3 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -78,6 +78,8 @@ export default function CreateSiteForm({ privateKey: string; } | null>(null); + const t = useTranslations(); + const createSiteFormSchema = z.object({ name: z .string() @@ -115,8 +117,6 @@ export default function CreateSiteForm({ const nameField = form.watch("name"); const methodField = form.watch("method"); - const t = useTranslations(); - useEffect(() => { const nameIsValid = nameField?.length >= 2 && nameField?.length <= 30; const isFormValid = methodField === "local" || isChecked; From d768bb163a4854ca618ad99ba5857e13c201a3c4 Mon Sep 17 00:00:00 2001 From: vlalx <143875984+vlalx@users.noreply.github.com> Date: Tue, 3 Jun 2025 21:10:00 +0300 Subject: [PATCH 078/105] I18n additionals (#125) * New translation keys * Updates in src/components * Updates in src/providers * remove lable in selector, not needed --------- Co-authored-by: Lokowitz --- messages/de-DE.json | 14 +++++++++++++- messages/en-US.json | 14 +++++++++++++- messages/fr-FR.json | 14 +++++++++++++- messages/it-IT.json | 14 +++++++++++++- messages/pl-PL.json | 14 +++++++++++++- messages/pt-PT.json | 14 +++++++++++++- messages/tr-TR.json | 14 +++++++++++++- src/components/Disable2FaForm.tsx | 14 +++++++------- src/components/LocaleSwitcher.tsx | 31 +++++++++++++++--------------- src/components/LoginForm.tsx | 26 ++++++++++++------------- src/providers/ApiKeyProvider.tsx | 5 ++++- src/providers/OrgProvider.tsx | 7 +++++-- src/providers/OrgUserProvider.tsx | 5 ++++- src/providers/ResourceProvider.tsx | 7 +++++-- src/providers/SiteProvider.tsx | 5 ++++- src/providers/UserProvider.tsx | 5 ++++- 16 files changed, 152 insertions(+), 51 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 780377d8..4ae478f1 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Ungültige Bestätigung", "passwordRequired": "Passwort ist erforderlich", "allowAll": "Alle erlauben", - "permissionsAllowAll": "Alle Berechtigungen erlauben" + "permissionsAllowAll": "Alle Berechtigungen erlauben", + "githubUsernameRequired": "GitHub-Benutzername ist erforderlich", + "supportKeyRequired": "Unterstützer-Schlüssel ist erforderlich", + "passwordRequirementsChars": "Das Passwort muss mindestens 8 Zeichen lang sein", + "language": "Sprache", + "verificationCodeRequired": "Code ist erforderlich", + "userErrorNoUpdate": "Kein Benutzer zum Aktualisieren", + "siteErrorNoUpdate": "Keine Site zum Aktualisieren", + "resourceErrorNoUpdate": "Keine Ressource zum Aktualisieren", + "authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren", + "orgErrorNoUpdate": "Keine Organisation zum Aktualisieren", + "orgErrorNoProvided": "Keine Organisation angegeben", + "apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren" } diff --git a/messages/en-US.json b/messages/en-US.json index d63ef985..6eb8d8af 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Invalid confirmation", "passwordRequired": "Password is required", "allowAll": "Allow All", - "permissionsAllowAll": "Allow All Permissions" + "permissionsAllowAll": "Allow All Permissions", + "githubUsernameRequired": "GitHub username is required", + "supportKeyRequired": "Supporter key is required", + "passwordRequirementsChars": "Password must be at least 8 characters", + "language": "Language", + "verificationCodeRequired": "Code is required", + "userErrorNoUpdate": "No user to update", + "siteErrorNoUpdate": "No site to update", + "resourceErrorNoUpdate": "No resource to update", + "authErrorNoUpdate": "No auth info to update", + "orgErrorNoUpdate": "No org to update", + "orgErrorNoProvided": "No org provided", + "apiKeysErrorNoUpdate": "No API key to update" } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 14935896..fb59e65a 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Confirmation invalide", "passwordRequired": "Le mot de passe est requis", "allowAll": "Tout autoriser", - "permissionsAllowAll": "Autoriser toutes les autorisations" + "permissionsAllowAll": "Autoriser toutes les autorisations", + "githubUsernameRequired": "Le nom d'utilisateur GitHub est requis", + "supportKeyRequired": "La clé de supporter est requise", + "passwordRequirementsChars": "Le mot de passe doit comporter au moins 8 caractères", + "language": "Langue", + "verificationCodeRequired": "Le code est requis", + "userErrorNoUpdate": "Pas d'utilisateur à mettre à jour", + "siteErrorNoUpdate": "Pas de site à mettre à jour", + "resourceErrorNoUpdate": "Pas de ressource à mettre à jour", + "authErrorNoUpdate": "Pas d'informations d'authentification à mettre à jour", + "orgErrorNoUpdate": "Pas d'organisation à mettre à jour", + "orgErrorNoProvided": "Aucune organisation fournie", + "apiKeysErrorNoUpdate": "Pas de clé API à mettre à jour" } diff --git a/messages/it-IT.json b/messages/it-IT.json index 3ca4f87c..3e49a833 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Conferma non valida", "passwordRequired": "La password è obbligatoria", "allowAll": "Consenti Tutto", - "permissionsAllowAll": "Consenti Tutti I Permessi" + "permissionsAllowAll": "Consenti Tutti I Permessi", + "githubUsernameRequired": "È richiesto l'username GitHub", + "supportKeyRequired": "È richiesta la chiave di supporto", + "passwordRequirementsChars": "La password deve essere di almeno 8 caratteri", + "language": "Lingua", + "verificationCodeRequired": "È richiesto un codice", + "userErrorNoUpdate": "Nessun utente da aggiornare", + "siteErrorNoUpdate": "Nessun sito da aggiornare", + "resourceErrorNoUpdate": "Nessuna risorsa da aggiornare", + "authErrorNoUpdate": "Nessuna informazione di autenticazione da aggiornare", + "orgErrorNoUpdate": "Nessuna organizzazione da aggiornare", + "orgErrorNoProvided": "Nessuna organizzazione fornita", + "apiKeysErrorNoUpdate": "Nessuna chiave API da aggiornare" } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index fbdd3c1d..299e5502 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Nieprawidłowe potwierdzenie", "passwordRequired": "Hasło jest wymagane", "allowAll": "Zezwól wszystkim", - "permissionsAllowAll": "Zezwól na wszystkie uprawnienia" + "permissionsAllowAll": "Zezwól na wszystkie uprawnienia", + "githubUsernameRequired": "Nazwa użytkownika GitHub jest wymagana", + "supportKeyRequired": "Klucz wspierający jest wymagany", + "passwordRequirementsChars": "Hasło musi mieć co najmniej 8 znaków", + "language": "Język", + "verificationCodeRequired": "Kod jest wymagany", + "userErrorNoUpdate": "Brak użytkownika do aktualizacji", + "siteErrorNoUpdate": "Brak witryny do aktualizacji", + "resourceErrorNoUpdate": "Brak zasobu do aktualizacji", + "authErrorNoUpdate": "Brak danych uwierzytelniania do aktualizacji", + "orgErrorNoUpdate": "Brak organizacji do aktualizacji", + "orgErrorNoProvided": "Nie podano organizacji", + "apiKeysErrorNoUpdate": "Brak klucza API do aktualizacji" } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 2bd0a29e..986d889c 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Confirmação inválida", "passwordRequired": "A senha é obrigatória", "allowAll": "Permitir todos", - "permissionsAllowAll": "Permitir Todas as Permissões" + "permissionsAllowAll": "Permitir Todas as Permissões", + "githubUsernameRequired": "O nome de utilizador GitHub é obrigatório", + "supportKeyRequired": "A chave de apoiante é obrigatória", + "passwordRequirementsChars": "A palavra-passe deve ter pelo menos 8 caracteres", + "language": "Idioma", + "verificationCodeRequired": "O código é obrigatório", + "userErrorNoUpdate": "Não existe utilizador para atualizar", + "siteErrorNoUpdate": "Não existe site para atualizar", + "resourceErrorNoUpdate": "Não existe recurso para atualizar", + "authErrorNoUpdate": "Não existem informações de autenticação para atualizar", + "orgErrorNoUpdate": "Não existe organização para atualizar", + "orgErrorNoProvided": "Nenhuma organização fornecida", + "apiKeysErrorNoUpdate": "Não existe chave API para atualizar" } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 574737bb..5e1b88db 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1063,5 +1063,17 @@ "inviteErrorInvalidConfirmation": "Invalid confirmation", "passwordRequired": "Password is required", "allowAll": "Allow All", - "permissionsAllowAll": "Allow All Permissions" + "permissionsAllowAll": "Allow All Permissions", + "githubUsernameRequired": "GitHub username is required", + "supportKeyRequired": "Supporter key is required", + "passwordRequirementsChars": "Password must be at least 8 characters", + "language": "Language", + "verificationCodeRequired": "Code is required", + "userErrorNoUpdate": "No user to update", + "siteErrorNoUpdate": "No site to update", + "resourceErrorNoUpdate": "No resource to update", + "authErrorNoUpdate": "No auth info to update", + "orgErrorNoUpdate": "No org to update", + "orgErrorNoProvided": "No org provided", + "apiKeysErrorNoUpdate": "No API key to update" } diff --git a/src/components/Disable2FaForm.tsx b/src/components/Disable2FaForm.tsx index c9c1d228..06f57d4f 100644 --- a/src/components/Disable2FaForm.tsx +++ b/src/components/Disable2FaForm.tsx @@ -34,11 +34,6 @@ import { useUserContext } from "@app/hooks/useUserContext"; import { CheckCircle2 } from "lucide-react"; import { useTranslations } from "next-intl"; -const disableSchema = z.object({ - password: z.string().min(1, { message: "Password is required" }), - code: z.string().min(1, { message: "Code is required" }) -}); - type Disable2FaProps = { open: boolean; setOpen: (val: boolean) => void; @@ -53,6 +48,13 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + const disableSchema = z.object({ + password: z.string().min(1, { message: t('passwordRequired') }), + code: z.string().min(1, { message: t('verificationCodeRequired') }) + }); + const disableForm = useForm>({ resolver: zodResolver(disableSchema), defaultValues: { @@ -61,8 +63,6 @@ export default function Disable2FaForm({ open, setOpen }: Disable2FaProps) { } }); - const t = useTranslations(); - const request2fa = async (values: z.infer) => { setLoading(true); diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index 962b39bb..651a70e6 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -1,4 +1,4 @@ -import { useLocale } from 'next-intl'; +import { useLocale } from "next-intl"; import LocaleSwitcherSelect from './LocaleSwitcherSelect'; export default function LocaleSwitcher() { @@ -9,35 +9,34 @@ export default function LocaleSwitcher() { defaultValue={locale} items={[ { - value: 'en-US', - label: 'Englisch' + value: 'en-US', + label: 'English' }, { - value: 'fr-FR', - label: 'French' + value: 'fr-FR', + label: "Français" }, { - value: 'de-DE', - label: 'German' + value: 'de-DE', + label: 'Deutsch' }, { - value: 'it-IT', - label: 'Italian' + value: 'it-IT', + label: 'Italiano' }, { - value: 'pl-PL', - label: 'Polish' + value: 'pl-PL', + label: 'Polski' }, { - value: 'pt-PT', - label: 'Portuguese' + value: 'pt-PT', + label: 'Português' }, { - value: 'tr-TR', - label: 'Turkish' + value: 'tr-TR', + label: 'Türkçe' } ]} - label='Language' /> ); } diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index d0eee1b7..14189c37 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -53,17 +53,6 @@ type LoginFormProps = { idps?: LoginFormIDP[]; }; -const formSchema = z.object({ - email: z.string().email({ message: "Invalid email address" }), - password: z - .string() - .min(8, { message: "Password must be at least 8 characters" }) -}); - -const mfaSchema = z.object({ - code: z.string().length(6, { message: "Invalid code" }) -}); - export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const router = useRouter(); @@ -77,6 +66,19 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const [mfaRequested, setMfaRequested] = useState(false); + const t = useTranslations(); + + const formSchema = z.object({ + email: z.string().email({ message: t('emailInvalid') }), + password: z + .string() + .min(8, { message: t('passwordRequirementsChars') }) + }); + + const mfaSchema = z.object({ + code: z.string().length(6, { message: t('pincodeInvalid') }) + }); + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -92,8 +94,6 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { } }); - const t = useTranslations(); - async function onSubmit(values: any) { const { email, password } = form.getValues(); const { code } = mfaForm.getValues(); diff --git a/src/providers/ApiKeyProvider.tsx b/src/providers/ApiKeyProvider.tsx index 43a2a9b6..80563ac0 100644 --- a/src/providers/ApiKeyProvider.tsx +++ b/src/providers/ApiKeyProvider.tsx @@ -3,6 +3,7 @@ import ApiKeyContext from "@app/contexts/apiKeyContext"; import { GetApiKeyResponse } from "@server/routers/apiKeys"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface ApiKeyProviderProps { children: React.ReactNode; @@ -12,9 +13,11 @@ interface ApiKeyProviderProps { export function ApiKeyProvider({ children, apiKey: ak }: ApiKeyProviderProps) { const [apiKey, setApiKey] = useState(ak); + const t = useTranslations(); + const updateApiKey = (updatedApiKey: Partial) => { if (!apiKey) { - throw new Error("No API key to update"); + throw new Error(t('apiKeysErrorNoUpdate')); } setApiKey((prev) => { if (!prev) { diff --git a/src/providers/OrgProvider.tsx b/src/providers/OrgProvider.tsx index 73531931..0eff0183 100644 --- a/src/providers/OrgProvider.tsx +++ b/src/providers/OrgProvider.tsx @@ -3,6 +3,7 @@ import OrgContext from "@app/contexts/orgContext"; import { GetOrgResponse } from "@server/routers/org"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface OrgProviderProps { children: React.ReactNode; @@ -12,13 +13,15 @@ interface OrgProviderProps { export function OrgProvider({ children, org: serverOrg }: OrgProviderProps) { const [org, setOrg] = useState(serverOrg); + const t = useTranslations(); + if (!org) { - throw new Error("No org provided"); + throw new Error(t('orgErrorNoProvided')); } const updateOrg = (updatedOrg: Partial) => { if (!org) { - throw new Error("No org to update"); + throw new Error(t('orgErrorNoUpdate')); } setOrg((prev) => { diff --git a/src/providers/OrgUserProvider.tsx b/src/providers/OrgUserProvider.tsx index 9234f9ba..7606cab1 100644 --- a/src/providers/OrgUserProvider.tsx +++ b/src/providers/OrgUserProvider.tsx @@ -3,6 +3,7 @@ import OrgUserContext from "@app/contexts/orgUserContext"; import { GetOrgUserResponse } from "@server/routers/user"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface OrgUserProviderProps { children: React.ReactNode; @@ -15,9 +16,11 @@ export function OrgUserProvider({ }: OrgUserProviderProps) { const [orgUser, setOrgUser] = useState(serverOrgUser); + const t = useTranslations(); + const updateOrgUser = (updateOrgUser: Partial) => { if (!orgUser) { - throw new Error("No org to update"); + throw new Error(t('orgErrorNoUpdate')); } setOrgUser((prev) => { diff --git a/src/providers/ResourceProvider.tsx b/src/providers/ResourceProvider.tsx index cd6229a4..5e4ce6ea 100644 --- a/src/providers/ResourceProvider.tsx +++ b/src/providers/ResourceProvider.tsx @@ -4,6 +4,7 @@ import ResourceContext from "@app/contexts/resourceContext"; import { GetResourceAuthInfoResponse } from "@server/routers/resource"; import { GetResourceResponse } from "@server/routers/resource/getResource"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface ResourceProviderProps { children: React.ReactNode; @@ -22,9 +23,11 @@ export function ResourceProvider({ const [authInfo, setAuthInfo] = useState(serverAuthInfo); + const t = useTranslations(); + const updateResource = (updatedResource: Partial) => { if (!resource) { - throw new Error("No resource to update"); + throw new Error(t('resourceErrorNoUpdate')); } setResource((prev) => { @@ -43,7 +46,7 @@ export function ResourceProvider({ updatedAuthInfo: Partial ) => { if (!authInfo) { - throw new Error("No auth info to update"); + throw new Error(t('authErrorNoUpdate')); } setAuthInfo((prev) => { diff --git a/src/providers/SiteProvider.tsx b/src/providers/SiteProvider.tsx index 73b70053..0bb35a50 100644 --- a/src/providers/SiteProvider.tsx +++ b/src/providers/SiteProvider.tsx @@ -3,6 +3,7 @@ import SiteContext from "@app/contexts/siteContext"; import { GetSiteResponse } from "@server/routers/site/getSite"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface SiteProviderProps { children: React.ReactNode; @@ -15,9 +16,11 @@ export function SiteProvider({ }: SiteProviderProps) { const [site, setSite] = useState(serverSite); + const t = useTranslations(); + const updateSite = (updatedSite: Partial) => { if (!site) { - throw new Error("No site to update"); + throw new Error(t('siteErrorNoUpdate')); } setSite((prev) => { if (!prev) { diff --git a/src/providers/UserProvider.tsx b/src/providers/UserProvider.tsx index faa37fa7..59bff234 100644 --- a/src/providers/UserProvider.tsx +++ b/src/providers/UserProvider.tsx @@ -3,6 +3,7 @@ import UserContext from "@app/contexts/userContext"; import { GetUserResponse } from "@server/routers/user"; import { useState } from "react"; +import { useTranslations } from "next-intl"; interface UserProviderProps { children: React.ReactNode; @@ -12,9 +13,11 @@ interface UserProviderProps { export function UserProvider({ children, user: u }: UserProviderProps) { const [user, setUser] = useState(u); + const t = useTranslations(); + const updateUser = (updatedUser: Partial) => { if (!user) { - throw new Error("No user to update"); + throw new Error(t('userErrorNoUpdate')); } setUser((prev) => { if (!prev) { From fbc1aa25a31716250910c0ab2e38aa95976301b2 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:01:43 +0000 Subject: [PATCH 079/105] merge with dev and resolved confics --- package-lock.json | 1037 +++++++++++++++++++++++++++++++-------------- 1 file changed, 711 insertions(+), 326 deletions(-) diff --git a/package-lock.json b/package-lock.json index 82ef53db..df17aa90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,14 +178,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -194,22 +194,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -231,9 +215,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", - "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dev": true, "license": "MIT", "dependencies": { @@ -247,36 +231,20 @@ } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/traverse": { "version": "7.27.4", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", @@ -296,22 +264,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -323,9 +275,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "dev": true, "license": "MIT", "dependencies": { @@ -1351,9 +1303,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -1430,30 +1382,18 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@floating-ui/core": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", @@ -1619,9 +1559,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -1675,28 +1615,6 @@ "@img/sharp-libvips-darwin-x64": "1.1.0" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", @@ -1713,6 +1631,342 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1796,6 +2050,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz", + "integrity": "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, "node_modules/@next/env": { "version": "15.3.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", @@ -3738,24 +4004,6 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, - "node_modules/@react-email/components/node_modules/@react-email/render": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", - "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", - "license": "MIT", - "dependencies": { - "html-to-text": "9.0.5", - "prettier": "3.5.3", - "react-promise-suspense": "0.3.4" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, "node_modules/@react-email/container": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", @@ -3877,9 +4125,9 @@ } }, "node_modules/@react-email/render": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", - "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.2.tgz", + "integrity": "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -4015,23 +4263,25 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", - "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", + "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", - "lightningcss": "1.29.2", - "tailwindcss": "4.1.4" + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.8" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", - "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", + "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4043,24 +4293,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-x64": "4.1.4", - "@tailwindcss/oxide-freebsd-x64": "4.1.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-x64-musl": "4.1.4", - "@tailwindcss/oxide-wasm32-wasi": "4.1.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + "@tailwindcss/oxide-android-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-x64": "4.1.8", + "@tailwindcss/oxide-freebsd-x64": "4.1.8", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-x64-musl": "4.1.8", + "@tailwindcss/oxide-wasm32-wasi": "4.1.8", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", - "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", + "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", "cpu": [ "arm64" ], @@ -4075,9 +4325,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", - "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", + "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", "cpu": [ "arm64" ], @@ -4092,9 +4342,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", - "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", + "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", "cpu": [ "x64" ], @@ -4109,9 +4359,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", - "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", + "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", "cpu": [ "x64" ], @@ -4126,9 +4376,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", - "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", + "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", "cpu": [ "arm" ], @@ -4143,9 +4393,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", - "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", + "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", "cpu": [ "arm64" ], @@ -4160,9 +4410,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", - "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", + "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", "cpu": [ "arm64" ], @@ -4177,9 +4427,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", - "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", + "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==", "cpu": [ "x64" ], @@ -4194,9 +4444,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", - "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz", + "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==", "cpu": [ "x64" ], @@ -4211,9 +4461,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", - "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", + "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -4229,10 +4479,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", - "@emnapi/wasi-threads": "^1.0.1", - "@napi-rs/wasm-runtime": "^0.2.8", + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -4240,70 +4490,10 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.4.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.4.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.10", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.0", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", - "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", + "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", "cpu": [ "arm64" ], @@ -4318,9 +4508,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", - "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", + "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", "cpu": [ "x64" ], @@ -4335,17 +4525,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.4.tgz", - "integrity": "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", + "integrity": "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.4", - "@tailwindcss/oxide": "4.1.4", + "@tailwindcss/node": "4.1.8", + "@tailwindcss/oxide": "4.1.8", "postcss": "^8.4.41", - "tailwindcss": "4.1.4" + "tailwindcss": "4.1.8" } }, "node_modules/@tanstack/react-table": { @@ -4696,16 +4886,25 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", + "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.33.1", + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/typescript-estree": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1", "debug": "^4.3.4" }, "engines": { @@ -4717,7 +4916,168 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", + "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", + "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", + "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.33.1", + "@typescript-eslint/tsconfig-utils": "8.33.1", + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", + "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.33.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", + "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.33.1", + "@typescript-eslint/types": "^8.33.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", + "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/scope-manager": { @@ -4738,9 +5098,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz", - "integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==", + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", + "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5962,16 +6322,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -6375,19 +6725,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -10643,6 +10980,15 @@ "node": ">= 8.0.0" } }, + "node_modules/node-cache/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -10701,9 +11047,9 @@ } }, "node_modules/npm": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.2.0.tgz", - "integrity": "sha512-PcnFC6gTo9VDkxVaQ1/mZAS3JoWrDjAI+a6e2NgfYQSGDwftJlbdV0jBMi2V8xQPqbGcWaa7p3UP0SKF+Bhm2g==", + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.4.1.tgz", + "integrity": "sha512-/O5DiEFmtvnF0EU1+5VlDpcItpSKH3l+3fQOl3hkZ3ilGN+jJlGxxi/zb0rEK+zxd8pGyifVPyS1ORkMjZGAKw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -10782,16 +11128,16 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.0.1", - "@npmcli/config": "^10.1.0", + "@npmcli/arborist": "^9.1.1", + "@npmcli/config": "^10.3.0", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^4.0.2", "@npmcli/package-json": "^6.1.1", "@npmcli/promise-spawn": "^8.0.2", "@npmcli/redact": "^3.1.1", - "@npmcli/run-script": "^9.0.1", - "@sigstore/tuf": "^3.0.0", - "abbrev": "^3.0.0", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", "archy": "~1.0.0", "cacache": "^19.0.1", "chalk": "^5.4.1", @@ -10806,12 +11152,12 @@ "init-package-json": "^8.2.1", "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.0", - "libnpmdiff": "^8.0.1", - "libnpmexec": "^10.1.0", - "libnpmfund": "^7.0.1", + "libnpmaccess": "^10.0.1", + "libnpmdiff": "^8.0.4", + "libnpmexec": "^10.1.3", + "libnpmfund": "^7.0.4", "libnpmorg": "^8.0.0", - "libnpmpack": "^9.0.1", + "libnpmpack": "^9.0.4", "libnpmpublish": "^11.0.0", "libnpmsearch": "^9.0.0", "libnpmteam": "^8.0.1", @@ -10837,7 +11183,7 @@ "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", "read": "^4.1.0", - "semver": "7.7.2", + "semver": "^7.7.2", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", "supports-color": "^10.0.0", @@ -10963,7 +11309,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.0.1", + "version": "9.1.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11010,7 +11356,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.1.0", + "version": "10.3.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -11890,11 +12236,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.1", + "version": "8.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.1.1", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", "diff": "^7.0.0", @@ -11908,11 +12254,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.0", + "version": "10.1.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.1.1", "@npmcli/package-json": "^6.1.1", "@npmcli/run-script": "^9.0.1", "ci-info": "^4.0.0", @@ -11929,11 +12275,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.1", + "version": "7.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1" + "@npmcli/arborist": "^9.1.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -11952,11 +12298,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.1", + "version": "9.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.0.1", + "@npmcli/arborist": "^9.1.1", "@npmcli/run-script": "^9.0.1", "npm-package-arg": "^12.0.0", "pacote": "^21.0.0" @@ -12924,6 +13270,45 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.13", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", "inBundle": true, @@ -15575,15 +15960,15 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", - "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", + "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { @@ -15866,9 +16251,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.8.tgz", - "integrity": "sha512-AxSnYRvyFnAiZCUndS3zQZhNfV/B77ZhJ+O7d3K6wfg/jKJY+yv6ahuyXwnyaYA9UdLqnpCwhTRv9pPTBnPR2g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.3.tgz", + "integrity": "sha512-tXE2TRWrskc4TU3RDd7T8n8Np/wCfoeH9gz22c7PzYqNPQ9FBGFbWWzwL0JyHcFp+jHozmF76tbHfPAx22ua2Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -15974,9 +16359,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", From c2449ce79524cd543137753450e1b4b73718bedf Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:18:22 +0200 Subject: [PATCH 080/105] New translations en-us.json (Chinese Simplified) (#149) --- messages/zh-CN.json | 1079 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1079 insertions(+) create mode 100644 messages/zh-CN.json diff --git a/messages/zh-CN.json b/messages/zh-CN.json new file mode 100644 index 00000000..122ac723 --- /dev/null +++ b/messages/zh-CN.json @@ -0,0 +1,1079 @@ +{ + "setupCreate": "创建您的组织、网站和资源", + "setupNewOrg": "新建组织", + "setupCreateOrg": "创建组织", + "setupCreateResources": "创建资源", + "setupOrgName": "组织名称", + "orgDisplayName": "这是您组织的显示名称。", + "orgId": "组织ID", + "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", + "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", + "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", + "componentsErrorNoMember": "您目前不是任何组织的成员。", + "welcome": "欢迎使用 Pangolin", + "componentsCreateOrg": "创建组织", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", + "dismiss": "关闭", + "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", + "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", + "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", + "inviteLoginUser": "请确保您以正确的用户登录。", + "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", + "inviteCreateUser": "请先创建一个帐户。", + "goHome": "返回首页", + "inviteLogInOtherUser": "以不同的用户登录", + "createAnAccount": "创建帐户", + "inviteNotAccepted": "邀请未接受", + "authCreateAccount": "创建一个帐户以开始", + "authNoAccount": "没有账户?", + "email": "电子邮件地址", + "password": "密码", + "confirmPassword": "确认密码", + "createAccount": "创建帐户", + "viewSettings": "查看设置", + "delete": "删除", + "name": "名称", + "online": "在线", + "offline": "离线的", + "site": "站点", + "dataIn": "数据输入", + "dataOut": "数据输出", + "connectionType": "连接类型", + "tunnelType": "隧道类型", + "local": "本地的", + "edit": "编辑", + "siteConfirmDelete": "确认删除站点", + "siteDelete": "删除站点", + "siteMessageRemove": "一旦删除,该站点将无法访问。与该站点相关的所有资源和目标也将被删除。", + "siteMessageConfirm": "请在下面输入站点名称以确认。", + "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", + "siteManageSites": "管理站点", + "siteDescription": "允许通过安全隧道连接到您的网络", + "siteCreate": "创建站点", + "siteCreateDescription2": "按照下面的步骤创建和连接一个新站点", + "siteCreateDescription": "创建一个新站点开始连接您的资源", + "close": "关闭", + "siteErrorCreate": "创建站点出错", + "siteErrorCreateKeyPair": "找不到密钥对或站点默认值", + "siteErrorCreateDefaults": "未找到站点默认值", + "siteNameDescription": "这是站点的显示名称。", + "method": "方法", + "siteMethodDescription": "这是您将如何显示连接。", + "siteLearnNewt": "学习如何在您的系统上安装Newt", + "siteSeeConfigOnce": "您只能看到一次配置。", + "siteLoadWGConfig": "正在载入Wire保护配置...", + "siteDocker": "扩展 Docker 部署详细信息", + "toggle": "切换键", + "dockerCompose": "Docker 配置", + "dockerRun": "停靠栏", + "siteLearnLocal": "本地站点没有隧道,学习更多", + "siteConfirmCopy": "我已复制配置", + "searchSitesProgress": "搜索站点...", + "siteAdd": "添加站点", + "siteInstallNewt": "安装新的", + "siteInstallNewtDescription": "在您的系统上获得新的运行", + "WgConfiguration": "Wire护卫配置", + "WgConfigurationDescription": "使用以下配置连接到您的网络", + "operatingSystem": "操作系统", + "commands": "命令", + "recommended": "推荐的", + "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", + "siteRunsInDocker": "运行在停靠栏", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", + "siteErrorDelete": "删除站点出错", + "siteErrorUpdate": "更新站点失败", + "siteErrorUpdateDescription": "更新站点时出错。", + "siteUpdated": "站点已更新", + "siteUpdatedDescription": "网站已更新。", + "siteGeneralDescription": "配置此站点的常规设置", + "siteSettingDescription": "配置您网站上的设置", + "siteSetting": "{siteName} Settings", + "siteNewtTunnel": "新隧道(推荐)", + "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", + "siteWg": "基本线甲", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", + "siteLocalDescription": "仅本地资源。没有隧道。", + "siteSeeAll": "查看所有站点", + "siteTunnelDescription": "确定如何连接到您的网站", + "siteNewtCredentials": "新建凭据", + "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", + "siteCredentialsSave": "保存您的证书", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "siteInfo": "站点信息", + "status": "状态", + "shareTitle": "管理共享链接", + "shareDescription": "创建可共享的链接,允许暂时或永久访问您的资源", + "shareSearch": "搜索共享链接...", + "shareCreate": "创建共享链接", + "shareErrorDelete": "删除链接失败", + "shareErrorDeleteMessage": "删除链接时出错", + "shareDeleted": "链接已删除", + "shareDeletedDescription": "链接已删除", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", + "accessToken": "访问令牌", + "usageExamples": "用法示例", + "tokenId": "Token ID", + "requestHeades": "请求标题", + "queryParameter": "查询参数", + "importantNote": "重要笔记", + "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", + "token": "令牌", + "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", + "shareErrorFetchResource": "获取资源失败", + "shareErrorFetchResourceDescription": "获取资源时出错", + "shareErrorCreate": "无法创建共享链接", + "shareErrorCreateDescription": "创建共享链接时出错", + "shareCreateDescription": "任何具有此链接的人都可以访问资源", + "shareTitleOptional": "标题 (可选)", + "expireIn": "过期时间", + "neverExpire": "永不过期", + "shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。", + "shareSeeOnce": "您只能看到此链接。请确保复制它。", + "shareAccessHint": "任何具有此链接的人都可以访问该资源。小心地分享它。", + "shareTokenUsage": "查看访问令牌使用情况", + "createLink": "创建链接", + "resourcesNotFound": "找不到资源", + "resourceSearch": "搜索资源", + "openMenu": "打开菜单", + "resource": "资源", + "title": "标题", + "created": "已创建", + "expires": "过期时间", + "never": "从不使用", + "shareErrorSelectResource": "请选择一个资源", + "resourceTitle": "管理资源", + "resourceDescription": "为您的私人应用程序创建安全代理", + "resourcesSearch": "搜索资源...", + "resourceAdd": "添加资源", + "resourceErrorDelte": "删除资源时出错", + "authentication": "认证", + "protected": "保护", + "notProtected": "不保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", + "resourceMessageConfirm": "请在下面输入资源名称以确认。", + "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", + "resourceHTTP": "HTTPS 资源", + "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "Raw TCP/UDP 资源", + "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", + "resourceCreate": "创建资源", + "resourceCreateDescription": "按照下面的步骤创建新资源", + "resourceSeeAll": "查看所有资源", + "resourceInfo": "资源信息", + "resourceNameDescription": "这是资源的显示名称。", + "siteSelect": "选择站点", + "siteSearch": "搜索站点", + "siteNotFound": "未找到站点。", + "siteSelectionDescription": "此站点将为资源提供连接。", + "resourceType": "资源类型", + "resourceTypeDescription": "确定如何访问您的资源", + "resourceHTTPSSettings": "HTTPS 设置", + "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", + "domainType": "域类型", + "subdomain": "子域", + "baseDomain": "基础域", + "subdomnainDescription": "您的资源可以访问的子域。", + "resourceRawSettings": "TCP/UDP 设置", + "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "端口号", + "resourcePortNumberDescription": "代理请求的外部端口号。", + "cancel": "取消", + "resourceConfig": "配置片段", + "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", + "resourceAddEntrypoints": "Traefik: 添加入口点", + "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", + "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", + "resourceBack": "返回资源", + "resourceGoTo": "转到资源", + "resourceDelete": "删除资源", + "resourceDeleteConfirm": "确认删除资源", + "visibility": "可见性", + "enabled": "已启用", + "disabled": "已禁用", + "general": "A. 概况", + "generalSettings": "常规设置", + "proxy": "代理服务器", + "rules": "规则", + "resourceSettingDescription": "配置您资源上的设置", + "resourceSetting": "{resourceName} Settings", + "alwaysAllow": "总是允许", + "alwaysDeny": "总是拒绝", + "orgSettingsDescription": "配置您组织的一般设置", + "orgGeneralSettings": "组织设置", + "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", + "saveGeneralSettings": "保存常规设置", + "orgDangerZone": "危险区域", + "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", + "orgDelete": "删除组织", + "orgDeleteConfirm": "确认删除组织", + "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", + "orgMessageConfirm": "要确认,请在下面输入组织名称。", + "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", + "orgUpdated": "组织已更新", + "orgUpdatedDescription": "组织已更新。", + "orgErrorUpdate": "更新组织失败", + "orgErrorUpdateMessage": "更新组织时出错。", + "orgErrorFetch": "获取组织失败", + "orgErrorFetchMessage": "列出您的组织时出错", + "orgErrorDelete": "删除组织失败", + "orgErrorDeleteMessage": "删除组织时出错。", + "orgDeleted": "组织已删除", + "orgDeletedMessage": "组织及其数据已被删除。", + "orgMissing": "缺少组织 ID", + "orgMissingMessage": "没有机构ID,无法重新生成邀请。", + "accessUsersManage": "管理用户", + "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", + "accessUsersSearch": "搜索用户...", + "accessUserCreate": "创建用户", + "accessUserRemove": "删除用户", + "username": "用户名", + "identityProvider": "身份提供商", + "role": "作用", + "nameRequired": "名称是必填项", + "accessRolesManage": "管理角色", + "accessRolesDescription": "配置角色来管理访问您的组织", + "accessRolesSearch": "搜索角色...", + "accessRolesAdd": "添加角色", + "accessRoleDelete": "删除角色", + "description": "描述", + "inviteTitle": "打开邀请", + "inviteDescription": "管理您给其他用户的邀请", + "inviteSearch": "搜索邀请...", + "minutes": "分钟", + "hours": "小时数", + "days": "天", + "weeks": "周", + "months": "月", + "years": "年", + "day": "{count, plural, =1 {# 天} other {# 天}}", + "apiKeysTitle": "API 密钥信息", + "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", + "apiKeysErrorCreate": "创建 API 密钥出错", + "apiKeysErrorSetPermission": "设置权限出错", + "apiKeysCreate": "生成 API 密钥", + "apiKeysCreateDescription": "为您的组织生成一个新的 API 密钥", + "apiKeysGeneralSettings": "权限", + "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", + "apiKeysList": "您的 API 密钥", + "apiKeysSave": "保存您的 API 密钥", + "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "apiKeysInfo": "您的 API 密钥是:", + "apiKeysConfirmCopy": "我已复制 API 密钥", + "generate": "生成", + "done": "完成", + "apiKeysSeeAll": "查看所有 API 密钥", + "apiKeysPermissionsErrorLoadingActions": "加载 API 密钥操作时出错", + "apiKeysPermissionsErrorUpdate": "设置权限出错", + "apiKeysPermissionsUpdated": "权限已更新", + "apiKeysPermissionsUpdatedDescription": "权限已更新。", + "apiKeysPermissionsGeneralSettings": "权限", + "apiKeysPermissionsGeneralSettingsDescription": "确定此 API 密钥可以做什么", + "apiKeysPermissionsSave": "保存权限", + "apiKeysPermissionsTitle": "权限", + "apiKeys": "API 密钥", + "searchApiKeys": "搜索 API 密钥...", + "apiKeysAdd": "生成 API 密钥", + "apiKeysErrorDelete": "删除 API 密钥出错", + "apiKeysErrorDeleteMessage": "删除 API 密钥出错", + "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", + "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", + "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", + "apiKeysDeleteConfirm": "确认删除 API 密钥", + "apiKeysDelete": "删除 API 密钥", + "apiKeysManage": "管理 API 密钥", + "apiKeysDescription": "API 密钥用于认证集成 API", + "apiKeysSettings": "{apiKeyName} Settings", + "userTitle": "管理所有用户", + "userDescription": "查看和管理系统中的所有用户", + "userAbount": "关于用户管理", + "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", + "userServer": "服务器用户", + "userSearch": "搜索服务器用户...", + "userErrorDelete": "删除用户时出错", + "userDeleteConfirm": "确认删除用户", + "userDeleteServer": "从服务器删除用户", + "userMessageRemove": "该用户将被从所有组织中删除并完全从服务器中删除。", + "userMessageConfirm": "请在下面输入用户名称以确认。", + "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", + "licenseKey": "许可证密钥", + "valid": "Valid", + "numberOfSites": "站点数量", + "licenseKeySearch": "搜索许可证密钥...", + "licenseKeyAdd": "添加许可证密钥", + "type": "类型", + "licenseKeyRequired": "需要许可证密钥", + "licenseTermsAgree": "您必须同意许可条款", + "licenseErrorKeyLoad": "加载许可证密钥失败", + "licenseErrorKeyLoadDescription": "加载许可证密钥时出错。", + "licenseErrorKeyDelete": "删除许可证密钥失败", + "licenseErrorKeyDeleteDescription": "删除许可证密钥时出错。", + "licenseKeyDeleted": "许可证密钥已删除", + "licenseKeyDeletedDescription": "许可证密钥已被删除。", + "licenseErrorKeyActivate": "激活许可证密钥失败", + "licenseErrorKeyActivateDescription": "激活许可证密钥时出错。", + "licenseAbout": "关于许可协议", + "communityEdition": "社区版", + "licenseAboutDescription": "这是针对商业环境中使用Pangolin的商业和企业用户。 如果您正在使用 Pangolin 供个人使用,您可以忽略此部分。", + "licenseKeyActivated": "授权密钥已激活", + "licenseKeyActivatedDescription": "已成功激活许可证密钥。", + "licenseErrorKeyRecheck": "重新检查许可证密钥失败", + "licenseErrorKeyRecheckDescription": "重新检查许可证密钥时出错。", + "licenseErrorKeyRechecked": "重新检查许可证密钥", + "licenseErrorKeyRecheckedDescription": "已重新检查所有许可证密钥", + "licenseActivateKey": "激活许可证密钥", + "licenseActivateKeyDescription": "输入一个许可密钥来激活它。", + "licenseActivate": "激活许可证", + "licenseAgreement": "通过检查此框,您确认您已经阅读并同意与您的许可证密钥相关的许可条款。", + "fossorialLicense": "查看Fossorial Commercial License和订阅条款", + "licenseMessageRemove": "这将删除许可证密钥和它授予的所有相关权限。", + "licenseMessageConfirm": "要确认,请在下面输入许可证密钥。", + "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", + "licenseKeyDelete": "删除许可证密钥", + "licenseKeyDeleteConfirm": "确认删除许可证密钥", + "licenseTitle": "管理许可状态", + "licenseTitleDescription": "查看和管理系统中的许可证密钥", + "licenseHost": "主机许可证", + "licenseHostDescription": "管理主机的主许可证密钥。", + "licensedNot": "未授权", + "hostId": "主机 ID", + "licenseReckeckAll": "重新检查所有密钥", + "licenseSiteUsage": "站点使用情况", + "licenseSiteUsageDecsription": "查看使用此许可的站点数量。", + "licenseNoSiteLimit": "使用未经许可主机的站点数量没有限制。", + "licensePurchase": "购买许可证", + "licensePurchaseSites": "购买更多站点", + "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", + "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", + "licenseFee": "许可费", + "licensePriceSite": "每个站点价格", + "total": "总计", + "licenseContinuePayment": "继续付款", + "pricingPage": "定价页面", + "pricingPortal": "查看购买门户网站", + "licensePricingPage": "关于最新的价格和折扣,请访问 ", + "invite": "邀请", + "inviteRegenerate": "重新生成邀请", + "inviteRegenerateDescription": "撤销以前的邀请并创建一个新的邀请", + "inviteRemove": "移除邀请", + "inviteRemoveError": "删除邀请失败", + "inviteRemoveErrorDescription": "删除邀请时出错。", + "inviteRemoved": "邀请已删除", + "inviteRemovedDescription": "The invitation for {email} has been removed.", + "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", + "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", + "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", + "inviteRemoveConfirm": "确认删除邀请", + "inviteRegenerated": "重新生成邀请", + "inviteSent": "A new invitation has been sent to {email}.", + "inviteSentEmail": "发送电子邮件通知给用户", + "inviteGenerate": "A new invitation has been generated for {email}.", + "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateErrorDescription": "此用户的邀请已存在。", + "inviteRateLimitError": "超出速率限制", + "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", + "inviteRegenerateError": "重新生成邀请失败", + "inviteRegenerateErrorDescription": "重新生成邀请时出错。", + "inviteValidityPeriod": "有效期", + "inviteValidityPeriodSelect": "选择有效期", + "inviteRegenerateMessage": "邀请已重新生成。用户必须访问下面的链接才能接受邀请。", + "inviteRegenerateButton": "重新生成", + "expiresAt": "到期于", + "accessRoleUnknown": "未知角色", + "placeholder": "占位符", + "userErrorOrgRemove": "删除用户失败", + "userErrorOrgRemoveDescription": "删除用户时出错。", + "userOrgRemoved": "用户已删除", + "userOrgRemovedDescription": "The user {email} has been removed from the organization.", + "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", + "userMessageOrgRemove": "一旦删除,这个用户将不再能够访问组织。 你总是可以稍后重新邀请他们,但他们需要再次接受邀请。", + "userMessageOrgConfirm": "请在下面输入用户名称以确认。", + "userRemoveOrgConfirm": "确认删除用户", + "userRemoveOrg": "从组织中删除用户", + "users": "用户", + "accessRoleMember": "成员", + "accessRoleOwner": "所有者", + "userConfirmed": "已确认", + "idpNameInternal": "内部设置", + "emailInvalid": "无效的电子邮件地址", + "inviteValidityDuration": "请选择持续时间", + "accessRoleSelectPlease": "请选择一个角色", + "usernameRequired": "必须输入用户名", + "idpSelectPlease": "请选择身份提供商", + "idpGenericOidc": "通用的 OAuth2/OIDC 提供商。", + "accessRoleErrorFetch": "获取角色失败", + "accessRoleErrorFetchDescription": "获取角色时出错", + "idpErrorFetch": "获取身份提供者失败", + "idpErrorFetchDescription": "获取身份提供者时出错", + "userErrorExists": "用户已存在", + "userErrorExistsDescription": "此用户已经是组织成员。", + "inviteError": "邀请用户失败", + "inviteErrorDescription": "邀请用户时出错", + "userInvited": "用户邀请", + "userInvitedDescription": "用户已被成功邀请。", + "userErrorCreate": "创建用户失败", + "userErrorCreateDescription": "创建用户时出错", + "userCreated": "用户已创建", + "userCreatedDescription": "用户已成功创建。", + "userTypeInternal": "内部用户", + "userTypeInternalDescription": "邀请用户直接加入您的组织。", + "userTypeExternal": "外部用户", + "userTypeExternalDescription": "创建一个具有外部身份提供商的用户。", + "accessUserCreateDescription": "按照下面的步骤创建一个新用户", + "userSeeAll": "查看所有用户", + "userTypeTitle": "用户类型", + "userTypeDescription": "确定如何创建用户", + "userSettings": "用户信息", + "userSettingsDescription": "输入新用户的详细信息", + "inviteEmailSent": "发送邀请邮件给用户", + "inviteValid": "有效", + "selectDuration": "选择持续时间", + "accessRoleSelect": "选择角色", + "inviteEmailSentDescription": "一封电子邮件已经发送给用户,带有下面的访问链接。他们必须访问该链接才能接受邀请。", + "inviteSentDescription": "用户已被邀请。他们必须访问下面的链接才能接受邀请。", + "inviteExpiresIn": "邀请将于 {days, plural, =1 {# 天} other {# 天}}", + "idpTitle": "身份提供商", + "idpSelect": "为外部用户选择身份提供商", + "idpNotConfigured": "没有配置身份提供者。请在创建外部用户之前配置身份提供者。", + "usernameUniq": "这必须匹配所选身份提供者中存在的唯一用户名。", + "emailOptional": "电子邮件(可选)", + "nameOptional": "名称(可选)", + "accessControls": "访问控制", + "userDescription2": "管理此用户的设置", + "accessRoleErrorAdd": "添加用户到角色失败", + "accessRoleErrorAddDescription": "添加用户到角色时出错。", + "userSaved": "用户已保存", + "userSavedDescription": "用户已更新。", + "accessControlsDescription": "管理此用户在组织中可以访问和做什么", + "accessControlsSubmit": "保存访问控制", + "roles": "角色", + "accessUsersRoles": "管理用户和角色", + "accessUsersRolesDescription": "邀请用户并将他们添加到角色以管理访问您的组织", + "key": "关键字", + "createdAt": "创建于", + "proxyErrorInvalidHeader": "无效的自定义主机头值。使用域名格式,或将空保存为取消自定义主机头。", + "proxyErrorTls": "无效的 TLS 服务器名称。使用域名格式,或保存空以删除 TLS 服务器名称。", + "proxyEnableSSL": "启用 SSL (https)", + "targetErrorFetch": "获取目标失败", + "targetErrorFetchDescription": "获取目标时出错", + "siteErrorFetch": "获取资源失败", + "siteErrorFetchDescription": "获取资源时出错", + "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicateDescription": "具有这些设置的目标已存在", + "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", + "targetsUpdated": "目标已更新", + "targetsUpdatedDescription": "目标和设置更新成功", + "targetsErrorUpdate": "更新目标失败", + "targetsErrorUpdateDescription": "更新目标时出错", + "targetTlsUpdate": "TLS 设置已更新", + "targetTlsUpdateDescription": "您的 TLS 设置已成功更新", + "targetErrorTlsUpdate": "更新 TLS 设置失败", + "targetErrorTlsUpdateDescription": "更新 TLS 设置时出错", + "proxyUpdated": "代理设置已更新", + "proxyUpdatedDescription": "您的代理设置已成功更新", + "proxyErrorUpdate": "更新代理设置失败", + "proxyErrorUpdateDescription": "更新代理设置时出错", + "targetAddr": "IP / Hostname", + "targetPort": "端口", + "targetProtocol": "Protocol", + "targetTlsSettings": "HTTPS & TLS 设置", + "targetTlsSettingsDescription": "配置资源的 TLS 设置", + "targetTlsSettingsAdvanced": "高级TLS设置", + "targetTlsSni": "TLS 服务器名称 (SNI)", + "targetTlsSniDescription": "SNI使用的 TLS 服务器名称。留空使用默认值。", + "targetTlsSubmit": "保存设置", + "targets": "目标配置", + "targetsDescription": "设置目标来路由流量到您的服务", + "targetStickySessions": "启用置顶会话", + "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", + "methodSelect": "选择方法", + "targetSubmit": "Add Target", + "targetNoOne": "没有目标。使用表单添加目标。", + "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", + "targetsSubmit": "保存目标", + "proxyAdditional": "附加代理设置", + "proxyAdditionalDescription": "配置你的资源如何处理代理设置", + "proxyCustomHeader": "自定义主机标题", + "proxyCustomHeaderDescription": "代理请求时设置的主机头。留空则使用默认值。", + "proxyAdditionalSubmit": "保存代理设置", + "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", + "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", + "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", + "path": "路径", + "ipAddressRange": "IP 范围", + "rulesErrorFetch": "获取规则失败", + "rulesErrorFetchDescription": "获取规则时出错", + "rulesErrorDuplicate": "复制规则", + "rulesErrorDuplicateDescription": "带有这些设置的规则已存在", + "rulesErrorInvalidIpAddressRange": "无效的 CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "请输入一个有效的 CIDR 值", + "rulesErrorInvalidUrl": "无效的 URL 路径", + "rulesErrorInvalidUrlDescription": "请输入一个有效的 URL 路径值", + "rulesErrorInvalidIpAddress": "无效的 IP", + "rulesErrorInvalidIpAddressDescription": "请输入一个有效的IP地址", + "rulesErrorUpdate": "更新规则失败", + "rulesErrorUpdateDescription": "更新规则时出错", + "rulesUpdated": "启用规则", + "rulesUpdatedDescription": "规则评价已更新", + "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", + "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", + "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", + "rulesErrorInvalidPriority": "无效的优先级", + "rulesErrorInvalidPriorityDescription": "请输入一个有效的优先级", + "rulesErrorDuplicatePriority": "重复的优先级", + "rulesErrorDuplicatePriorityDescription": "请输入唯一的优先级", + "ruleUpdated": "规则已更新", + "ruleUpdatedDescription": "规则更新成功", + "ruleErrorUpdate": "操作失败", + "ruleErrorUpdateDescription": "保存过程中发生错误", + "rulesPriority": "优先权", + "rulesAction": "行 动", + "rulesMatchType": "比赛类型", + "value": "值", + "rulesAbout": "关于规则", + "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", + "rulesActions": "行动", + "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", + "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", + "rulesMatchCriteria": "匹配条件", + "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", + "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", + "rulesEnable": "启用规则", + "rulesEnableDescription": "启用或禁用此资源的规则评估", + "rulesResource": "资源规则配置", + "rulesResourceDescription": "配置规则来控制对您资源的访问", + "ruleSubmit": "添加规则", + "rulesNoOne": "没有规则。使用表单添加规则。", + "rulesOrder": "规则按优先顺序评定。", + "rulesSubmit": "保存规则", + "resourceErrorCreate": "创建资源时出错", + "resourceErrorCreateDescription": "创建资源时出错", + "resourceErrorCreateMessage": "创建资源时发生错误:", + "resourceErrorCreateMessageDescription": "发生意外错误", + "sitesErrorFetch": "获取站点出错", + "sitesErrorFetchDescription": "获取站点时出错", + "domainsErrorFetch": "获取域名出错", + "domainsErrorFetchDescription": "获取域时出错", + "none": "无", + "unknown": "未知的", + "resources": "资源", + "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", + "resourcesWireGuardConnect": "与Wire护卫加密安全连接", + "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", + "resourcesUsersRolesAccess": "基于用户和角色的访问控制", + "resourcesErrorUpdate": "切换资源失败", + "resourcesErrorUpdateDescription": "更新资源时出错", + "access": "访问权限", + "shareLink": "{resource} Share Link", + "resourceSelect": "选择资源", + "shareLinks": "分享链接", + "share": "可共享链接", + "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", + "shareEasyCreate": "轻松创建和分享", + "shareConfigurableExpirationDuration": "可配置的过期时间", + "shareSecureAndRevocable": "安全和可撤销的", + "nameMin": "Name must be at least {len} characters.", + "nameMax": "Name must not be longer than {len} characters.", + "sitesConfirmCopy": "请确认您已经复制了配置。", + "unknownCommand": "未知命令", + "newtErrorFetchReleases": "Failed to fetch release info: {err}", + "newtErrorFetchLatest": "Error fetching latest release: {err}", + "newtEndpoint": "Newt Endpoint", + "newtId": "Newt ID", + "newtSecretKey": "新的秘密密钥", + "architecture": "结构", + "sites": "站点", + "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", + "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", + "siteWgManualConfigurationRequired": "需要手动配置", + "userErrorNotAdminOrOwner": "用户不是管理员或所有者", + "pangolinSettings": "设置-Pangolin", + "accessRoleYour": "您的角色:", + "accessRoleSelect2": "选择角色", + "accessUserSelect": "选择一个用户", + "otpEmailEnter": "输入电子邮件", + "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", + "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", + "otpEmailSmtpRequired": "需要SMTP", + "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", + "otpEmailTitle": "一次性密码", + "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", + "otpEmailWhitelist": "电子邮件白名单", + "otpEmailWhitelistList": "白名单邮件", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", + "otpEmailWhitelistSave": "保存白名单", + "passwordAdd": "添加密码", + "passwordRemove": "删除密码", + "pincodeAdd": "添加 PIN 码", + "pincodeRemove": "移除 PIN 码", + "resourceAuthMethods": "身份验证方法", + "resourceAuthMethodsDescriptions": "允许通过额外的认证方法访问资源", + "resourceAuthSettingsSave": "保存成功", + "resourceAuthSettingsSaveDescription": "已保存身份验证设置", + "resourceErrorAuthFetch": "获取数据失败", + "resourceErrorAuthFetchDescription": "获取数据时出错", + "resourceErrorPasswordRemove": "删除资源密码出错", + "resourceErrorPasswordRemoveDescription": "删除资源密码时出错", + "resourceErrorPasswordSetup": "设置资源密码出错", + "resourceErrorPasswordSetupDescription": "设置资源密码时出错", + "resourceErrorPincodeRemove": "删除资源固定码时出错", + "resourceErrorPincodeRemoveDescription": "删除资源PIN码时出错", + "resourceErrorPincodeSetup": "设置资源 PIN 码时出错", + "resourceErrorPincodeSetupDescription": "设置资源 PIN 码时发生错误", + "resourceErrorUsersRolesSave": "设置角色失败", + "resourceErrorUsersRolesSaveDescription": "设置角色时出错", + "resourceErrorWhitelistSave": "保存白名单失败", + "resourceErrorWhitelistSaveDescription": "保存白名单时出错", + "resourcePasswordSubmit": "启用密码保护", + "resourcePasswordProtection": "Password Protection {status}", + "resourcePasswordRemove": "已删除资源密码", + "resourcePasswordRemoveDescription": "已成功删除资源密码", + "resourcePasswordSetup": "设置资源密码", + "resourcePasswordSetupDescription": "已成功设置资源密码", + "resourcePasswordSetupTitle": "设置密码", + "resourcePasswordSetupTitleDescription": "设置密码来保护此资源", + "resourcePincode": "PIN 码", + "resourcePincodeSubmit": "启用 PIN 码保护", + "resourcePincodeProtection": "PIN Code Protection {status}", + "resourcePincodeRemove": "资源粉码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源密码", + "resourcePincodeSetup": "资源PIN 码已设置", + "resourcePincodeSetupDescription": "资源固定码已成功设置", + "resourcePincodeSetupTitle": "设置粉码", + "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", + "resourceRoleDescription": "管理员总是可以访问此资源。", + "resourceUsersRoles": "用户和角色", + "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", + "resourceUsersRolesSubmit": "保存用户和角色", + "resourceWhitelistSave": "保存成功", + "resourceWhitelistSaveDescription": "白名单设置已保存", + "ssoUse": "使用平台 SSO", + "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", + "proxyErrorInvalidPort": "无效的端口号", + "subdomainErrorInvalid": "无效的子域", + "domainErrorFetch": "获取域名出错", + "domainErrorFetchDescription": "获取域时出错", + "resourceErrorUpdate": "更新资源失败", + "resourceErrorUpdateDescription": "更新资源时出错", + "resourceUpdated": "资源已更新", + "resourceUpdatedDescription": "资源已成功更新", + "resourceErrorTransfer": "传输资源失败", + "resourceErrorTransferDescription": "传输资源时出错", + "resourceTransferred": "资源已传输", + "resourceTransferredDescription": "资源已成功传输", + "resourceErrorToggle": "切换资源失败", + "resourceErrorToggleDescription": "更新资源时出错", + "resourceVisibilityTitle": "可见性", + "resourceVisibilityTitleDescription": "完全启用或禁用资源可见性", + "resourceGeneral": "常规设置", + "resourceGeneralDescription": "配置此资源的常规设置", + "resourceEnable": "启用资源", + "resourceTransfer": "传输资源", + "resourceTransferDescription": "将此资源转移到另一个站点", + "resourceTransferSubmit": "传输资源", + "siteDestination": "目标站点", + "searchSites": "搜索站点", + "accessRoleCreate": "创建角色", + "accessRoleCreateDescription": "创建一个新角色来分组用户并管理他们的权限。", + "accessRoleCreateSubmit": "创建角色", + "accessRoleCreated": "角色已创建", + "accessRoleCreatedDescription": "角色已成功创建。", + "accessRoleErrorCreate": "创建角色失败", + "accessRoleErrorCreateDescription": "创建角色时出错。", + "accessRoleErrorNewRequired": "需要新角色", + "accessRoleErrorRemove": "删除角色失败", + "accessRoleErrorRemoveDescription": "删除角色时出错。", + "accessRoleName": "角色名称", + "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", + "accessRoleRemove": "删除角色", + "accessRoleRemoveDescription": "从组织中删除角色", + "accessRoleRemoveSubmit": "删除角色", + "accessRoleRemoved": "角色已删除", + "accessRoleRemovedDescription": "角色已成功删除。", + "accessRoleRequiredRemove": "删除此角色之前,请选择一个新角色来转移现有成员。", + "manage": "管理", + "sitesNotFound": "未找到站点。", + "pangolinServerAdmin": "服务器管理员 - Pangolin", + "licenseTierProfessional": "专业许可证", + "licenseTierEnterprise": "企业许可证", + "licenseTierCommercial": "商业许可证", + "licensed": "许可的", + "yes": "否", + "no": "否", + "sitesAdditional": "其他站点", + "licenseKeys": "许可证密钥", + "sitestCountDecrease": "减少站点计数", + "sitestCountIncrease": "增加站点计数", + "idpManage": "管理身份提供商", + "idpManageDescription": "查看和管理系统中的身份提供商", + "idpDeletedDescription": "身份提供商删除成功", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpMessageRemove": "这将删除身份提供者和所有相关的配置。通过此提供者进行身份验证的用户将无法登录。", + "idpMessageConfirm": "要确认,请在下面输入身份提供者的名称。", + "idpConfirmDelete": "确认删除身份提供商", + "idpDelete": "删除身份提供商", + "idp": "身份提供商", + "idpSearch": "搜索身份提供者...", + "idpAdd": "添加身份提供商", + "idpClientIdRequired": "客户端ID是必需的。", + "idpClientSecretRequired": "客户端密码是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", + "idpPathRequired": "标识路径是必需的。", + "idpScopeRequired": "范围是必需的。", + "idpOidcDescription": "配置 OpenID 连接身份提供商", + "idpCreatedDescription": "身份提供商创建成功", + "idpCreate": "创建身份提供商", + "idpCreateDescription": "配置用户身份验证的新身份提供商", + "idpSeeAll": "查看所有身份提供商", + "idpSettingsDescription": "配置身份提供者的基本信息", + "idpDisplayName": "此身份提供商的显示名称", + "idpAutoProvisionUsers": "自动提供用户", + "idpAutoProvisionUsersDescription": "如果启用,用户将在首次登录时自动在系统中创建,并且能够映射用户到角色和组织。", + "licenseBadge": "专业版", + "idpType": "提供者类型", + "idpTypeDescription": "选择您想要配置的身份提供者类型", + "idpOidcConfigure": "OAuth2/OIDC 配置", + "idpOidcConfigureDescription": "配置 OAuth2/OIDC 供应商端点和凭据", + "idpClientId": "客户端ID", + "idpClientIdDescription": "来自您身份提供商的 OAuth2 客户端 ID", + "idpClientSecret": "客户端密钥", + "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", + "idpAuthUrl": "授权 URL", + "idpAuthUrlDescription": "OAuth2 授权终点 URL", + "idpTokenUrl": "令牌网址", + "idpTokenUrlDescription": "OAuth2 令牌端点URL", + "idpOidcConfigureAlert": "重要信息", + "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", + "idpToken": "令牌配置", + "idpTokenDescription": "配置如何从ID令牌中提取用户信息", + "idpJmespathAbout": "关于 JMESPath", + "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", + "idpJmespathLabel": "标识路径", + "idpJmespathLabelDescription": "用户标识符的路径", + "idpJmespathEmailPathOptional": "电子邮件路径(可选)", + "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", + "idpJmespathNamePathOptional": "名称路径(可选)", + "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", + "idpOidcConfigureScopes": "范围", + "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", + "idpSubmit": "创建身份提供商", + "orgPolicies": "组织策略", + "idpSettings": "{idpName} Settings", + "idpCreateSettingsDescription": "配置身份提供商的设置", + "roleMapping": "角色映射", + "orgMapping": "组织映射", + "orgPoliciesSearch": "搜索组织策略...", + "orgPoliciesAdd": "添加组织策略", + "orgRequired": "组织是必填项", + "error": "错误", + "success": "成功", + "orgPolicyAddedDescription": "策略添加成功", + "orgPolicyUpdatedDescription": "策略更新成功", + "orgPolicyDeletedDescription": "已成功删除策略", + "defaultMappingsUpdatedDescription": "默认映射更新成功", + "orgPoliciesAbout": "关于组织政策", + "orgPoliciesAboutDescription": "组织策略用于根据用户的 ID 令牌来控制对组织的访问。 您可以指定 JMESPath 表达式来提取角色和组织信息从 ID 令牌中提取信息。", + "orgPoliciesAboutDescriptionLink": "欲了解更多信息,请参阅文件。", + "defaultMappingsOptional": "默认映射(可选)", + "defaultMappingsOptionalDescription": "当没有为某个组织定义组织的政策时,使用默认映射。 您可以指定默认角色和组织映射回到这里。", + "defaultMappingsRole": "默认角色映射", + "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", + "defaultMappingsOrg": "默认组织映射", + "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", + "defaultMappingsSubmit": "保存默认映射", + "orgPoliciesEdit": "编辑组织策略", + "org": "组织", + "orgSelect": "选择组织", + "orgSearch": "搜索", + "orgNotFound": "找不到 org 。", + "roleMappingPathOptional": "角色映射路径(可选)", + "orgMappingPathOptional": "组织映射路径(可选)", + "orgPolicyUpdate": "更新策略", + "orgPolicyAdd": "添加策略", + "orgPolicyConfig": "配置组织访问权限", + "idpUpdatedDescription": "身份提供商更新成功", + "redirectUrl": "重定向网址", + "redirectUrlAbout": "关于重定向网址", + "redirectUrlAboutDescription": "这是用户在验证后将被重定向到的URL。您需要在身份提供商设置中配置此URL。", + "pangolinAuth": "认证 - Pangolin", + "verificationCodeLengthRequirements": "您的验证码必须是8个字符。", + "errorOccurred": "发生错误", + "emailErrorVerify": "验证电子邮件失败:", + "emailVerified": "电子邮件验证成功!重定向您...", + "verificationCodeErrorResend": "无法重新发送验证码:", + "verificationCodeResend": "验证码已重新发送", + "verificationCodeResendDescription": "我们已将验证码重新发送到您的电子邮件地址。请检查您的收件箱。", + "emailVerify": "验证电子邮件", + "emailVerifyDescription": "输入验证码发送到您的电子邮件地址。", + "verificationCode": "验证码", + "verificationCodeEmailSent": "我们向您的电子邮件地址发送了验证码。", + "submit": "提交", + "emailVerifyResendProgress": "正在重新发送...", + "emailVerifyResend": "没有收到代码?点击此处重新发送", + "passwordNotMatch": "密码不匹配", + "signupError": "注册时出错", + "pangolinLogoAlt": "邦戈林徽标", + "inviteAlready": "看起来您已被邀请!", + "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", + "signupQuestion": "已经有一个帐户?", + "login": "登录", + "resourceNotFound": "找不到资源", + "resourceNotFoundDescription": "您要访问的资源不存在。", + "pincodeRequirementsLength": "PIN码必须是6位数字", + "pincodeRequirementsChars": "PIN 必须只包含数字", + "passwordRequirementsLength": "密码必须至少 1 个字符长", + "otpEmailRequirementsLength": "OTP 必须至少 1 个字符长", + "otpEmailSent": "OTP 已发送", + "otpEmailSentDescription": "OTP 已经发送到您的电子邮件", + "otpEmailErrorAuthenticate": "通过电子邮件身份验证失败", + "pincodeErrorAuthenticate": "Pincode 验证失败", + "passwordErrorAuthenticate": "密码验证失败", + "poweredBy": "支持者:", + "authenticationRequired": "需要身份验证", + "authenticationMethodChoose": "Choose your preferred method to access {name}", + "authenticationRequest": "You must authenticate to access {name}", + "user": "用户", + "pincodeInput": "6位数字 PIN 码", + "pincodeSubmit": "使用PIN登录", + "passwordSubmit": "使用密码登录", + "otpEmailDescription": "一次性代码将发送到此电子邮件。", + "otpEmailSend": "发送一次性代码", + "otpEmail": "一次性密码 (OTP)", + "otpEmailSubmit": "提交 OTP", + "backToEmail": "回到电子邮件", + "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", + "accessDenied": "访问被拒绝", + "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", + "accessTokenError": "检查访问令牌时出错", + "accessGranted": "已授予访问", + "accessUrlInvalid": "访问 URL 无效", + "accessGrantedDescription": "您已获准访问此资源。重定向您...", + "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", + "tokenInvalid": "无效的令牌", + "pincodeInvalid": "无效的代码", + "passwordErrorRequestReset": "请求重置失败:", + "passwordErrorReset": "重置密码失败:", + "passwordResetSuccess": "密码重置成功!返回登录...", + "passwordReset": "重置密码", + "passwordResetDescription": "按照步骤重置您的密码", + "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", + "passwordNew": "新密码", + "passwordNewConfirm": "确认新密码", + "pincodeAuth": "验证器代码", + "pincodeSubmit2": "提交代码", + "passwordResetSubmit": "请求重置", + "passwordBack": "回到密码", + "loginBack": "返回登录", + "signup": "注册", + "loginStart": "登录以开始", + "idpOidcTokenValidating": "正在验证 OIDC 令牌", + "idpOidcTokenResponse": "验证 OIDC 令牌响应", + "idpErrorOidcTokenValidating": "验证 OIDC 令牌出错", + "idpConnectingTo": "Connecting to {name}", + "idpConnectingToDescription": "正在验证您的身份", + "idpConnectingToProcess": "正在连接...", + "idpConnectingToFinished": "已连接", + "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", + "idpErrorNotFound": "找不到 IdP", + "inviteInvalid": "无效邀请", + "inviteInvalidDescription": "邀请链接无效。", + "inviteErrorWrongUser": "邀请不是该用户的", + "inviteErrorUserNotExists": "用户不存在。请先创建帐户。", + "inviteErrorLoginRequired": "您必须登录才能接受邀请", + "inviteErrorExpired": "邀请可能已过期", + "inviteErrorRevoked": "邀请可能已被吊销了", + "inviteErrorTypo": "邀请链接中可能有一个类型", + "pangolinSetup": "Setup - Pangolin", + "orgNameRequired": "组织名称是必需的", + "orgIdRequired": "组织ID是必需的", + "orgErrorCreate": "创建 org 时出错", + "pageNotFound": "找不到页面", + "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", + "overview": "概览", + "home": "首页", + "accessControl": "访问控制", + "settings": "设置", + "usersAll": "所有用户", + "license": "许可协议", + "pangolinDashboard": "仪表板 - Pangolin", + "noResults": "未找到任何结果。", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "已输入的标签", + "tagsEnteredDescription": "这些是您输入的标签。", + "tagsWarnCannotBeLessThanZero": "最大标签和最小标签不能小于 0", + "tagsWarnNotAllowedAutocompleteOptions": "标记不允许为每个自动完成选项", + "tagsWarnInvalid": "无效的标签,每个有效标签", + "tagWarnTooShort": "Tag {tagText} is too short", + "tagWarnTooLong": "Tag {tagText} is too long", + "tagsWarnReachedMaxNumber": "已达到允许标签的最大数量", + "tagWarnDuplicate": "Duplicate tag {tagText} not added", + "supportKeyInvalid": "无效密钥", + "supportKeyInvalidDescription": "您的支持者密钥无效。", + "supportKeyValid": "Valid Key", + "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", + "supportKeyErrorValidationDescription": "验证支持者密钥失败。", + "supportKey": "支持开发和通过一个潘戈林!", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", + "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", + "supportKeyPurchaseLink": "我们的网站", + "supportKeyPurchase2": "并在这里兑换。", + "supportKeyLearnMore": "了解更多。", + "supportKeyOptions": "请选择最适合您的选项。", + "supportKetOptionFull": "完全支持者", + "forWholeServer": "适用于整个服务器", + "lifetimePurchase": "终身购买", + "supporterStatus": "支持者状态", + "buy": "购买", + "supportKeyOptionLimited": "有限支持者", + "forFiveUsers": "适用于 5 或更少用户", + "supportKeyRedeem": "兑换支持者密钥", + "supportKeyHideSevenDays": "隐藏7天", + "supportKeyEnter": "输入支持者密钥", + "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", + "githubUsername": "GitHub Username", + "supportKeyInput": "支持者密钥", + "supportKeyBuy": "购买支持者密钥", + "logoutError": "注销错误", + "signingAs": "登录为", + "serverAdmin": "服务器管理员", + "otpEnable": "启用双因子", + "otpDisable": "禁用双因子", + "logout": "登出", + "licenseTierProfessionalRequired": "需要专业版", + "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", + "actionGetOrg": "获取组织", + "actionUpdateOrg": "更新组织", + "actionGetOrgUser": "获取组织用户", + "actionListOrgDomains": "列出组织域", + "actionCreateSite": "创建站点", + "actionDeleteSite": "删除站点", + "actionGetSite": "获取站点", + "actionListSites": "站点列表", + "actionUpdateSite": "更新站点", + "actionListSiteRoles": "允许站点角色列表", + "actionCreateResource": "创建资源", + "actionDeleteResource": "删除资源", + "actionGetResource": "获取资源", + "actionListResource": "列出资源", + "actionUpdateResource": "更新资源", + "actionListResourceUsers": "列出资源用户", + "actionSetResourceUsers": "设置资源用户", + "actionSetAllowedResourceRoles": "设置允许的资源角色", + "actionListAllowedResourceRoles": "列出允许的资源角色", + "actionSetResourcePassword": "设置资源密码", + "actionSetResourcePincode": "设置资源粉码", + "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", + "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", + "actionCreateTarget": "Create Target", + "actionDeleteTarget": "删除目标", + "actionGetTarget": "获取目标", + "actionListTargets": "列表目标", + "actionUpdateTarget": "Update Target", + "actionCreateRole": "创建角色", + "actionDeleteRole": "删除角色", + "actionGetRole": "获取角色", + "actionListRole": "角色列表", + "actionUpdateRole": "更新角色", + "actionListAllowedRoleResources": "列表允许的角色资源", + "actionInviteUser": "邀请用户", + "actionRemoveUser": "删除用户", + "actionListUsers": "列出用户", + "actionAddUserRole": "添加用户角色", + "actionGenerateAccessToken": "生成访问令牌", + "actionDeleteAccessToken": "删除访问令牌", + "actionListAccessTokens": "访问令牌", + "actionCreateResourceRule": "创建资源规则", + "actionDeleteResourceRule": "删除资源规则", + "actionListResourceRules": "列出资源规则", + "actionUpdateResourceRule": "更新资源规则", + "actionListOrgs": "列出组织", + "actionCheckOrgId": "检查 ID", + "actionCreateOrg": "创建组织", + "actionDeleteOrg": "删除组织", + "actionListApiKeys": "列出API密钥", + "actionListApiKeyActions": "列出API密钥动作", + "actionSetApiKeyActions": "设置 API 密钥允许的操作", + "actionCreateApiKey": "创建 API 密钥", + "actionDeleteApiKey": "删除 API 密钥", + "actionCreateIdp": "创建IDP", + "actionUpdateIdp": "更新IDP", + "actionDeleteIdp": "删除IDP", + "actionListIdps": "列出国内流离失所者", + "actionGetIdp": "获取IDP", + "actionCreateIdpOrg": "创建IDP Org 策略", + "actionDeleteIdpOrg": "删除IDP Org 策略", + "actionListIdpOrgs": "列出国内流离失所者组织", + "actionUpdateIdpOrg": "更新IDP Org", + "noneSelected": "未选择", + "orgNotFound2": "未找到组织。", + "searchProgress": "搜索...", + "create": "创建", + "orgs": "组织", + "loginError": "登录时出错", + "passwordForgot": "忘记密码?", + "otpAuth": "两步验证", + "otpAuthDescription": "从您的身份验证程序中输入代码或您的单次备份代码。", + "otpAuthSubmit": "提交代码", + "idpContinue": "或者继续", + "otpAuthBack": "返回登录", + "navbar": "Navigation Menu", + "navbarDescription": "应用程序的主导航菜单", + "navbarDocsLink": "文件", + "commercialEdition": "商业版", + "otpErrorEnable": "无法启用 2FA", + "otpErrorEnableDescription": "启用2FA 时出错", + "otpSetupCheckCode": "请输入一个6位数字", + "otpSetupCheckCodeRetry": "无效的代码。请重试。", + "otpSetup": "启用两步验证", + "otpSetupDescription": "用额外的保护层来保护您的帐户", + "otpSetupScanQr": "用您的身份验证程序扫描此二维码或手动输入密钥:", + "otpSetupSecretCode": "验证器代码", + "otpSetupSuccess": "启用两步验证", + "otpSetupSuccessStoreBackupCodes": "您的帐户现在更加安全。不要忘记保存您的备份代码。", + "otpErrorDisable": "无法禁用 2FA", + "otpErrorDisableDescription": "禁用2FA 时出错", + "otpRemove": "禁用两步验证", + "otpRemoveDescription": "为您的帐户禁用两步验证", + "otpRemoveSuccess": "双重身份验证已禁用", + "otpRemoveSuccessMessage": "您的帐户已禁用双重身份验证。您可以随时再次启用它。", + "otpRemoveSubmit": "禁用两步验证", + "paginator": "Page {current} of {last}", + "paginatorToFirst": "转到第一页", + "paginatorToPrevious": "转到上一页", + "paginatorToNext": "转到下一页", + "paginatorToLast": "转到最后一页", + "copyText": "复制文本", + "copyTextFailed": "复制文本失败: ", + "copyTextClipboard": "复制到剪贴板", + "inviteErrorInvalidConfirmation": "无效确认", + "passwordRequired": "密码是必需的", + "allowAll": "允许所有", + "permissionsAllowAll": "允许所有权限", + "githubUsernameRequired": "GitHub 用户名是必需的", + "supportKeyRequired": "支持者密钥是必需的", + "passwordRequirementsChars": "密码必须至少 8 个字符", + "language": "语言", + "verificationCodeRequired": "必须输入代码", + "userErrorNoUpdate": "没有要更新的用户", + "siteErrorNoUpdate": "没有要更新的站点", + "resourceErrorNoUpdate": "没有可更新的资源", + "authErrorNoUpdate": "没有要更新的身份验证信息", + "orgErrorNoUpdate": "没有要更新的 org", + "orgErrorNoProvided": "未提供 org", + "apiKeysErrorNoUpdate": "没有要更新的 API 密钥" +} From b75d0a921e4dd3f76933df9e66f9f4211a129331 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:27:38 +0000 Subject: [PATCH 081/105] resolve conflicts --- package-lock.json | 146 +++++++++------------------------------------- 1 file changed, 27 insertions(+), 119 deletions(-) diff --git a/package-lock.json b/package-lock.json index 388effda..f6e3a754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -356,40 +356,6 @@ "@noble/ciphers": "^1.0.0" } }, -<<<<<<< HEAD - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, -======= ->>>>>>> main "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -1144,7 +1110,7 @@ "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, - "node_modules/@node-rs/bcrypt-darwin-arm64": { + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", @@ -1266,26 +1232,6 @@ "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==", "license": "MIT" }, -<<<<<<< HEAD - "node_modules/@petamoriken/float16": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", - "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", - "dev": true, - "license": "MIT" -======= - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } ->>>>>>> main - }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -2748,70 +2694,6 @@ "node": ">= 10" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", - "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.10", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", - "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", - "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/postcss": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", @@ -3545,6 +3427,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz", + "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz", + "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", From e885676ad8585d4f9786419f71c5fab1b60f0471 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:30:49 +0000 Subject: [PATCH 082/105] add chinese --- src/components/LocaleSwitcher.tsx | 4 ++++ src/i18n/config.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index 651a70e6..a47c6898 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -35,6 +35,10 @@ export default function LocaleSwitcher() { { value: 'tr-TR', label: 'Türkçe' + }, + { + value: 'zh-CN', + label: '中国人' } ]} /> diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 030d7f71..7580320b 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,4 +1,4 @@ export type Locale = (typeof locales)[number]; -export const locales = ['en-US', 'fr-FR', 'de-DE', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR'] as const; +export const locales = ['en-US', 'fr-FR', 'de-DE', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN'] as const; export const defaultLocale: Locale = 'en-US'; \ No newline at end of file From 75212f1e05e698b36daecb7b4d55bd9809d1f674 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Fri, 6 Jun 2025 07:55:44 +0200 Subject: [PATCH 083/105] New Crowdin updates (#153) * New translations en-us.json (Spanish) * New translations en-us.json (Dutch) --- messages/es-ES.json | 1079 +++++++++++++++++++++++++++++++++++++++++++ messages/nl-NL.json | 1079 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2158 insertions(+) create mode 100644 messages/es-ES.json create mode 100644 messages/nl-NL.json diff --git a/messages/es-ES.json b/messages/es-ES.json new file mode 100644 index 00000000..2c05cc8b --- /dev/null +++ b/messages/es-ES.json @@ -0,0 +1,1079 @@ +{ + "setupCreate": "Crea tu organización, sitio y recursos", + "setupNewOrg": "Nueva organización", + "setupCreateOrg": "Crear organización", + "setupCreateResources": "Crear Recursos", + "setupOrgName": "Nombre de la organización", + "orgDisplayName": "Este es el nombre mostrado de su organización.", + "orgId": "ID de la organización", + "setupIdentifierMessage": "Este es el identificador único para su organización. Esto es independiente del nombre de la pantalla.", + "setupErrorIdentifier": "El ID de la organización ya está en uso. Por favor, elija uno diferente.", + "componentsErrorNoMemberCreate": "Actualmente no eres miembro de ninguna organización. Crea una organización para empezar.", + "componentsErrorNoMember": "Actualmente no eres miembro de ninguna organización.", + "welcome": "Bienvenido a Pangolin", + "componentsCreateOrg": "Crear una organización", + "componentsMember": "¡Eres un miembro de {count, plural, =0 {¡Ninguna organización} =1 {¡una organización} other {# organizaciones}}.", + "componentsInvalidKey": "Se han detectado claves de licencia inválidas o caducadas. Siga los términos de licencia para seguir usando todas las características.", + "dismiss": "Descartar", + "componentsLicenseViolation": "Violación de la Licencia: Este servidor está usando sitios {usedSites} que exceden su límite de licencias de sitios {maxSites} . Siga los términos de licencia para seguir usando todas las características.", + "componentsSupporterMessage": "¡Gracias por apoyar a Pangolin como {tier}!", + "inviteErrorNotValid": "Lo sentimos, pero parece que la invitación a la que intentas acceder no ha sido aceptada o ya no es válida.", + "inviteErrorUser": "Lo sentimos, pero parece que la invitación a la que intentas acceder no es para este usuario.", + "inviteLoginUser": "Por favor, asegúrese de que ha iniciado sesión como el usuario correcto.", + "inviteErrorNoUser": "Lo sentimos, pero parece que la invitación a la que intentas acceder no es para un usuario que existe.", + "inviteCreateUser": "Por favor, cree una cuenta primero.", + "goHome": "Ir a casa", + "inviteLogInOtherUser": "Iniciar sesión como un usuario diferente", + "createAnAccount": "Crear una cuenta", + "inviteNotAccepted": "Invitación no aceptada", + "authCreateAccount": "Crear una cuenta para empezar", + "authNoAccount": "¿No tienes una cuenta?", + "email": "E-mail", + "password": "Contraseña", + "confirmPassword": "Confirmar contraseña", + "createAccount": "Crear cuenta", + "viewSettings": "Ver ajustes", + "delete": "Eliminar", + "name": "Nombre", + "online": "En línea", + "offline": "Desconectado", + "site": "Sitio", + "dataIn": "Datos en", + "dataOut": "Datos Fuentes", + "connectionType": "Tipo de conexión", + "tunnelType": "Tipo de túnel", + "local": "Local", + "edit": "Editar", + "siteConfirmDelete": "Confirmar Borrar Sitio", + "siteDelete": "Eliminar sitio", + "siteMessageRemove": "Una vez eliminado, el sitio ya no será accesible. Todos los recursos y objetivos asociados con el sitio también serán eliminados.", + "siteMessageConfirm": "Para confirmar, por favor escriba el nombre del sitio a continuación.", + "siteQuestionRemove": "¿Está seguro de que desea eliminar el sitio {selectedSite} de la organización?", + "siteManageSites": "Administrar Sitios", + "siteDescription": "Permitir conectividad a tu red a través de túneles seguros", + "siteCreate": "Crear sitio", + "siteCreateDescription2": "Siga los pasos siguientes para crear y conectar un nuevo sitio", + "siteCreateDescription": "Crear un nuevo sitio para comenzar a conectar sus recursos", + "close": "Cerrar", + "siteErrorCreate": "Error al crear el sitio", + "siteErrorCreateKeyPair": "Por defecto no se encuentra el par de claves o el sitio", + "siteErrorCreateDefaults": "Sitio por defecto no encontrado", + "siteNameDescription": "Este es el nombre para mostrar el sitio.", + "method": "Método", + "siteMethodDescription": "Así es como se expondrán las conexiones.", + "siteLearnNewt": "Aprende cómo instalar Newt en tu sistema", + "siteSeeConfigOnce": "Sólo podrá ver la configuración una vez.", + "siteLoadWGConfig": "Cargando configuración de WireGuard...", + "siteDocker": "Expandir para detalles de despliegue de Docker", + "toggle": "Cambiar", + "dockerCompose": "Componer Docker", + "dockerRun": "Docker Run", + "siteLearnLocal": "Los sitios locales no tienen túnel, aprender más", + "siteConfirmCopy": "He copiado la configuración", + "searchSitesProgress": "Buscar sitios...", + "siteAdd": "Añadir sitio", + "siteInstallNewt": "Instalar Newt", + "siteInstallNewtDescription": "Recibe Newt corriendo en tu sistema", + "WgConfiguration": "Configuración de Wirex Guard", + "WgConfigurationDescription": "Utilice la siguiente configuración para conectarse a su red", + "operatingSystem": "Sistema operativo", + "commands": "Comandos", + "recommended": "Recomendado", + "siteNewtDescription": "Para la mejor experiencia de usuario, utilice Newt. Utiliza Wirex Guard bajo la capa y te permite dirigirte a tus recursos privados mediante su dirección LAN en tu red privada desde el panel de control de Pangolin.", + "siteRunsInDocker": "Ejecutar en Docker", + "siteRunsInShell": "Ejecuta en el shell en macOS, Linux y Windows", + "siteErrorDelete": "Error al eliminar el sitio", + "siteErrorUpdate": "Error al actualizar el sitio", + "siteErrorUpdateDescription": "Se ha producido un error al actualizar el sitio.", + "siteUpdated": "Sitio actualizado", + "siteUpdatedDescription": "El sitio ha sido actualizado.", + "siteGeneralDescription": "Configurar la configuración general de este sitio", + "siteSettingDescription": "Configurar la configuración de su sitio", + "siteSetting": "Ajustes {siteName}", + "siteNewtTunnel": "Túnel Nuevo (Recomendado)", + "siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en tu red. Sin configuración adicional.", + "siteWg": "Wirex Guardia Básica", + "siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.", + "siteLocalDescription": "Solo recursos locales. Sin túneles.", + "siteSeeAll": "Ver todos los sitios", + "siteTunnelDescription": "Determina cómo quieres conectarte a tu sitio", + "siteNewtCredentials": "Credenciales nuevas", + "siteNewtCredentialsDescription": "Así es como Newt se autentificará con el servidor", + "siteCredentialsSave": "Guarda tus credenciales", + "siteCredentialsSaveDescription": "Sólo podrás verlo una vez. Asegúrate de copiarlo a un lugar seguro.", + "siteInfo": "Información del sitio", + "status": "Estado", + "shareTitle": "Administrar Enlaces de Compartir", + "shareDescription": "Crear enlaces compartidos para conceder acceso temporal o permanente a tus recursos", + "shareSearch": "Buscar enlaces compartidos...", + "shareCreate": "Crear enlace Compartir", + "shareErrorDelete": "Error al eliminar el enlace", + "shareErrorDeleteMessage": "Se ha producido un error al eliminar el enlace", + "shareDeleted": "Enlace eliminado", + "shareDeletedDescription": "El enlace ha sido eliminado", + "shareTokenDescription": "Su token de acceso puede ser pasado de dos maneras: como parámetro de consulta o en las cabeceras de solicitud. Estos deben ser pasados del cliente en cada solicitud de acceso autenticado.", + "accessToken": "Token de acceso", + "usageExamples": "Ejemplos de uso", + "tokenId": "Token ID", + "requestHeades": "Solicitar cabeceras", + "queryParameter": "Parámetro de consulta", + "importantNote": "Nota Importante", + "shareImportantDescription": "Por razones de seguridad, el uso de cabeceras se recomienda sobre parámetros de consulta cuando sea posible, ya que los parámetros de consulta pueden ser registrados en los registros del servidor o en el historial del navegador.", + "token": "Token", + "shareTokenSecurety": "Mantenga su token de acceso seguro. No lo comparta en áreas de acceso público o código del lado del cliente.", + "shareErrorFetchResource": "No se pudo obtener recursos", + "shareErrorFetchResourceDescription": "Se ha producido un error al recuperar los recursos", + "shareErrorCreate": "Error al crear el enlace compartir", + "shareErrorCreateDescription": "Se ha producido un error al crear el enlace compartido", + "shareCreateDescription": "Cualquiera con este enlace puede acceder al recurso", + "shareTitleOptional": "Título (opcional)", + "expireIn": "Caduca en", + "neverExpire": "Nunca expirar", + "shareExpireDescription": "El tiempo de caducidad es cuánto tiempo el enlace será utilizable y proporcionará acceso al recurso. Después de este tiempo, el enlace ya no funcionará, y los usuarios que usaron este enlace perderán el acceso al recurso.", + "shareSeeOnce": "Sólo podrá ver este enlace una vez. Asegúrese de copiarlo.", + "shareAccessHint": "Cualquiera con este enlace puede acceder al recurso. Compártelo con cuidado.", + "shareTokenUsage": "Ver Uso de Token de Acceso", + "createLink": "Crear enlace", + "resourcesNotFound": "No se encontraron recursos", + "resourceSearch": "Buscar recursos", + "openMenu": "Abrir menú", + "resource": "Recurso", + "title": "Título", + "created": "Creado", + "expires": "Caduca", + "never": "Nunca", + "shareErrorSelectResource": "Por favor, seleccione un recurso", + "resourceTitle": "Administrar recursos", + "resourceDescription": "Crea proxies seguros para tus aplicaciones privadas", + "resourcesSearch": "Buscar recursos...", + "resourceAdd": "Añadir Recurso", + "resourceErrorDelte": "Error al eliminar el recurso", + "authentication": "Autenticación", + "protected": "Protegido", + "notProtected": "No protegido", + "resourceMessageRemove": "Una vez eliminado, el recurso ya no será accesible. Todos los objetivos asociados con el recurso también serán eliminados.", + "resourceMessageConfirm": "Para confirmar, por favor escriba el nombre del recurso a continuación.", + "resourceQuestionRemove": "¿Está seguro de que desea eliminar el recurso {selectedResource} de la organización?", + "resourceHTTP": "HTTPS Recurso", + "resourceHTTPDescription": "Solicitudes de proxy a tu aplicación sobre HTTPS usando un subdominio o dominio base.", + "resourceRaw": "Recurso TCP/UDP sin procesar", + "resourceRawDescription": "Solicitudes de proxy a tu aplicación a través de TCP/UDP usando un número de puerto.", + "resourceCreate": "Crear Recurso", + "resourceCreateDescription": "Siga los siguientes pasos para crear un nuevo recurso", + "resourceSeeAll": "Ver todos los recursos", + "resourceInfo": "Información del recurso", + "resourceNameDescription": "Este es el nombre para mostrar el recurso.", + "siteSelect": "Seleccionar sitio", + "siteSearch": "Buscar sitio", + "siteNotFound": "Sitio no encontrado.", + "siteSelectionDescription": "Este sitio proporcionará conectividad al recurso.", + "resourceType": "Tipo de recurso", + "resourceTypeDescription": "Determina cómo quieres acceder a tu recurso", + "resourceHTTPSSettings": "Configuración HTTPS", + "resourceHTTPSSettingsDescription": "Configurar cómo se accederá a tu recurso a través de HTTPS", + "domainType": "Tipo de dominio", + "subdomain": "Subdominio", + "baseDomain": "Dominio base", + "subdomnainDescription": "El subdominio al que su recurso será accesible.", + "resourceRawSettings": "Configuración TCP/UDP", + "resourceRawSettingsDescription": "Configurar cómo se accederá a su recurso a través de TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Número de puerto", + "resourcePortNumberDescription": "El número de puerto externo a las solicitudes de proxy.", + "cancel": "Cancelar", + "resourceConfig": "Fragmentos de configuración", + "resourceConfigDescription": "Copia y pega estos fragmentos de configuración para configurar tu recurso TCP/UDP", + "resourceAddEntrypoints": "Traefik: Añadir puntos de entrada", + "resourceExposePorts": "Gerbil: Exponer puertos en Docker Compose", + "resourceLearnRaw": "Aprende cómo configurar los recursos TCP/UDP", + "resourceBack": "Volver a Recursos", + "resourceGoTo": "Ir a Recurso", + "resourceDelete": "Eliminar Recurso", + "resourceDeleteConfirm": "Confirmar Borrar Recurso", + "visibility": "Visibilidad", + "enabled": "Activado", + "disabled": "Deshabilitado", + "general": "General", + "generalSettings": "Configuración General", + "proxy": "Proxy", + "rules": "Reglas", + "resourceSettingDescription": "Configure la configuración de su recurso", + "resourceSetting": "Ajustes {resourceName}", + "alwaysAllow": "Permitir siempre", + "alwaysDeny": "Denegar siempre", + "orgSettingsDescription": "Configurar la configuración general de su organización", + "orgGeneralSettings": "Configuración de la organización", + "orgGeneralSettingsDescription": "Administra los detalles y la configuración de tu organización", + "saveGeneralSettings": "Guardar ajustes generales", + "orgDangerZone": "Zona de peligro", + "orgDangerZoneDescription": "Una vez que elimines este órgano, no hay vuelta atrás. Por favor, asegúrate de ello.", + "orgDelete": "Eliminar organización", + "orgDeleteConfirm": "Confirmar eliminación de organización", + "orgMessageRemove": "Esta acción es irreversible y eliminará todos los datos asociados.", + "orgMessageConfirm": "Para confirmar, por favor escriba el nombre de la organización a continuación.", + "orgQuestionRemove": "¿Está seguro que desea eliminar la organización {selectedOrg}?", + "orgUpdated": "Organización actualizada", + "orgUpdatedDescription": "La organización ha sido actualizada.", + "orgErrorUpdate": "Error al actualizar la organización", + "orgErrorUpdateMessage": "Se ha producido un error al actualizar la organización.", + "orgErrorFetch": "Error al recuperar organizaciones", + "orgErrorFetchMessage": "Se ha producido un error al listar sus organizaciones", + "orgErrorDelete": "Error al eliminar la organización", + "orgErrorDeleteMessage": "Se ha producido un error al eliminar la organización.", + "orgDeleted": "Organización eliminada", + "orgDeletedMessage": "La organización y sus datos han sido eliminados.", + "orgMissing": "Falta el ID de la organización", + "orgMissingMessage": "No se puede regenerar la invitación sin el ID de la organización.", + "accessUsersManage": "Administrar usuarios", + "accessUsersDescription": "Invitar usuarios y añadirlos a roles para administrar el acceso a su organización", + "accessUsersSearch": "Buscar usuarios...", + "accessUserCreate": "Crear usuario", + "accessUserRemove": "Eliminar usuario", + "username": "Usuario", + "identityProvider": "Proveedor de identidad", + "role": "Rol", + "nameRequired": "Se requiere nombre", + "accessRolesManage": "Administrar roles", + "accessRolesDescription": "Configurar roles para administrar el acceso a su organización", + "accessRolesSearch": "Buscar roles...", + "accessRolesAdd": "Añadir rol", + "accessRoleDelete": "Eliminar rol", + "description": "Descripción", + "inviteTitle": "Invitaciones abiertas", + "inviteDescription": "Administra tus invitaciones a otros usuarios", + "inviteSearch": "Buscar invitaciones...", + "minutes": "Minutos", + "hours": "Horas", + "days": "Días", + "weeks": "Semanas", + "months": "Meses", + "years": "Años", + "day": "{count, plural, =1 {# día} other {# días}}", + "apiKeysTitle": "Información de Clave API", + "apiKeysConfirmCopy2": "Debes confirmar que has copiado la clave API.", + "apiKeysErrorCreate": "Error al crear la clave API", + "apiKeysErrorSetPermission": "Error al establecer permisos", + "apiKeysCreate": "Generar clave API", + "apiKeysCreateDescription": "Generar una nueva clave API para su organización", + "apiKeysGeneralSettings": "Permisos", + "apiKeysGeneralSettingsDescription": "Determinar qué puede hacer esta clave API", + "apiKeysList": "Tu clave API", + "apiKeysSave": "Guarda tu clave API", + "apiKeysSaveDescription": "Sólo podrás verlo una vez. Asegúrate de copiarlo a un lugar seguro.", + "apiKeysInfo": "Tu clave API es:", + "apiKeysConfirmCopy": "He copiado la clave API", + "generate": "Generar", + "done": "Hecho", + "apiKeysSeeAll": "Ver todas las claves API", + "apiKeysPermissionsErrorLoadingActions": "Error al cargar las acciones clave API", + "apiKeysPermissionsErrorUpdate": "Error al establecer permisos", + "apiKeysPermissionsUpdated": "Permisos actualizados", + "apiKeysPermissionsUpdatedDescription": "Los permisos han sido actualizados.", + "apiKeysPermissionsGeneralSettings": "Permisos", + "apiKeysPermissionsGeneralSettingsDescription": "Determinar qué puede hacer esta clave API", + "apiKeysPermissionsSave": "Guardar permisos", + "apiKeysPermissionsTitle": "Permisos", + "apiKeys": "Claves API", + "searchApiKeys": "Buscar claves API...", + "apiKeysAdd": "Generar clave API", + "apiKeysErrorDelete": "Error al eliminar la clave API", + "apiKeysErrorDeleteMessage": "Error al eliminar la clave API", + "apiKeysQuestionRemove": "¿Está seguro de que desea eliminar la clave de API {selectedApiKey} de la organización?", + "apiKeysMessageRemove": "Una vez eliminada, la clave API ya no podrá ser utilizada.", + "apiKeysMessageConfirm": "Para confirmar, por favor escriba el nombre de la clave API a continuación.", + "apiKeysDeleteConfirm": "Confirmar Borrar Clave API", + "apiKeysDelete": "Borrar Clave API", + "apiKeysManage": "Administrar claves API", + "apiKeysDescription": "Las claves API se utilizan para autenticar con la API de integración", + "apiKeysSettings": "Ajustes {apiKeyName}", + "userTitle": "Administrar todos los usuarios", + "userDescription": "Ver y administrar todos los usuarios en el sistema", + "userAbount": "Acerca de Gestión de Usuarios", + "userAbountDescription": "Esta tabla muestra todos los objetos de usuario root en el sistema. Cada usuario puede pertenecer a varias organizaciones. Eliminar un usuario de una organización no elimina su objeto de usuario root - permanecerán en el sistema. Para eliminar completamente un usuario del sistema, debe eliminar su objeto de usuario root usando la acción de borrar en esta tabla.", + "userServer": "Usuarios del servidor", + "userSearch": "Buscar usuarios del servidor...", + "userErrorDelete": "Error al eliminar el usuario", + "userDeleteConfirm": "Confirmar Borrar Usuario", + "userDeleteServer": "Eliminar usuario del servidor", + "userMessageRemove": "El usuario será eliminado de todas las organizaciones y será eliminado completamente del servidor.", + "userMessageConfirm": "Para confirmar, por favor escriba el nombre del usuario a continuación.", + "userQuestionRemove": "¿Está seguro que desea eliminar permanentemente {selectedUser} del servidor?", + "licenseKey": "Clave de licencia", + "valid": "Valid", + "numberOfSites": "Número de sitios", + "licenseKeySearch": "Buscar claves de licencia...", + "licenseKeyAdd": "Añadir clave de licencia", + "type": "Tipo", + "licenseKeyRequired": "La clave de licencia es necesaria", + "licenseTermsAgree": "Debe aceptar los términos de la licencia", + "licenseErrorKeyLoad": "Error al cargar las claves de licencia", + "licenseErrorKeyLoadDescription": "Se ha producido un error al cargar las claves de licencia.", + "licenseErrorKeyDelete": "Error al eliminar la clave de licencia", + "licenseErrorKeyDeleteDescription": "Se ha producido un error al eliminar la clave de licencia.", + "licenseKeyDeleted": "Clave de licencia eliminada", + "licenseKeyDeletedDescription": "La clave de licencia ha sido eliminada.", + "licenseErrorKeyActivate": "Error al activar la clave de licencia", + "licenseErrorKeyActivateDescription": "Se ha producido un error al activar la clave de licencia.", + "licenseAbout": "Acerca de la licencia", + "communityEdition": "Edición comunitaria", + "licenseAboutDescription": "Esto es para usuarios empresariales y empresariales que utilizan Pangolin en un entorno comercial. Si estás usando Pangolin para uso personal, puedes ignorar esta sección.", + "licenseKeyActivated": "Clave de licencia activada", + "licenseKeyActivatedDescription": "La clave de licencia se ha activado correctamente.", + "licenseErrorKeyRecheck": "Error al revisar las claves de licencia", + "licenseErrorKeyRecheckDescription": "Se ha producido un error al revisar las claves de licencia.", + "licenseErrorKeyRechecked": "Claves de licencia remarcadas", + "licenseErrorKeyRecheckedDescription": "Todas las claves de licencia han sido revisadas", + "licenseActivateKey": "Activar clave de licencia", + "licenseActivateKeyDescription": "Introduzca una clave de licencia para activarla.", + "licenseActivate": "Activar licencia", + "licenseAgreement": "Al marcar esta casilla, confirma que ha leído y aceptado los términos de licencia correspondientes al nivel asociado con su clave de licencia.", + "fossorialLicense": "Ver Términos de suscripción y licencia comercial", + "licenseMessageRemove": "Esto eliminará la clave de licencia y todos los permisos asociados otorgados por ella.", + "licenseMessageConfirm": "Para confirmar, por favor escriba la clave de licencia a continuación.", + "licenseQuestionRemove": "¿Está seguro que desea eliminar la clave de licencia {selectedKey}?", + "licenseKeyDelete": "Eliminar clave de licencia", + "licenseKeyDeleteConfirm": "Confirmar eliminar clave de licencia", + "licenseTitle": "Administrar estado de licencia", + "licenseTitleDescription": "Ver y administrar claves de licencia en el sistema", + "licenseHost": "Licencia de host", + "licenseHostDescription": "Administrar la clave de licencia principal para el host.", + "licensedNot": "Sin licencia", + "hostId": "ID del Host", + "licenseReckeckAll": "Revisar todas las claves", + "licenseSiteUsage": "Uso de Sitios", + "licenseSiteUsageDecsription": "Ver el número de sitios que utilizan esta licencia.", + "licenseNoSiteLimit": "No hay límite en el número de sitios que utilizan un host sin licencia.", + "licensePurchase": "Comprar Licencia", + "licensePurchaseSites": "Comprar sitios adicionales", + "licenseSitesUsedMax": "{usedSites} de {maxSites} sitios usados", + "licenseSitesUsed": "{count, plural, =0 {# sitios} =1 {# sitio} other {# sitios}} en el sistema.", + "licensePurchaseDescription": "Elige cuántos sitios quieres {selectedMode, select, license {compra una licencia para. Siempre puedes añadir más sitios más tarde.} other {añadir a tu licencia existente.}}", + "licenseFee": "Tarifa de licencia", + "licensePriceSite": "Precio por sitio", + "total": "Total", + "licenseContinuePayment": "Continuar con el pago", + "pricingPage": "página de precios", + "pricingPortal": "Ver Portal de Compra", + "licensePricingPage": "Para obtener los precios y descuentos más actualizados, por favor visite el ", + "invite": "Invitaciones", + "inviteRegenerate": "Regenerar invitación", + "inviteRegenerateDescription": "Revocar invitación anterior y crear una nueva", + "inviteRemove": "Eliminar invitación", + "inviteRemoveError": "Error al eliminar la invitación", + "inviteRemoveErrorDescription": "Ocurrió un error mientras se eliminaba la invitación.", + "inviteRemoved": "Invitación eliminada", + "inviteRemovedDescription": "La invitación para {email} ha sido eliminada.", + "inviteQuestionRemove": "¿Está seguro de que desea eliminar la invitación {email}?", + "inviteMessageRemove": "Una vez eliminada, esta invitación ya no será válida. Siempre puede volver a invitar al usuario más tarde.", + "inviteMessageConfirm": "Para confirmar, por favor escriba la dirección de correo electrónico de la invitación a continuación.", + "inviteQuestionRegenerate": "¿Estás seguro de que quieres regenerar la invitación para {email}? Esto revocará la invitación anterior.", + "inviteRemoveConfirm": "Confirmar eliminación de invitación", + "inviteRegenerated": "Invitación Regenerada", + "inviteSent": "Se ha enviado una nueva invitación a {email}.", + "inviteSentEmail": "Enviar notificación por correo electrónico al usuario", + "inviteGenerate": "Se ha generado una nueva invitación para {email}.", + "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateErrorDescription": "Ya existe una invitación para este usuario.", + "inviteRateLimitError": "Límite de tasa excedido", + "inviteRateLimitErrorDescription": "Has superado el límite de 3 regeneraciones por hora. Inténtalo de nuevo más tarde.", + "inviteRegenerateError": "No se pudo regenerar la invitación", + "inviteRegenerateErrorDescription": "Se ha producido un error al regenerar la invitación.", + "inviteValidityPeriod": "Periodo de validez", + "inviteValidityPeriodSelect": "Seleccionar período de validez", + "inviteRegenerateMessage": "La invitación ha sido regenerada. El usuario debe acceder al enlace de abajo para aceptar la invitación.", + "inviteRegenerateButton": "Regenerar", + "expiresAt": "Caduca el", + "accessRoleUnknown": "Rol desconocido", + "placeholder": "Marcador de posición", + "userErrorOrgRemove": "Error al eliminar el usuario", + "userErrorOrgRemoveDescription": "Ocurrió un error mientras se eliminaba el usuario.", + "userOrgRemoved": "Usuario eliminado", + "userOrgRemovedDescription": "El usuario {email} ha sido eliminado de la organización.", + "userQuestionOrgRemove": "¿Estás seguro de que quieres eliminar {email} de la organización?", + "userMessageOrgRemove": "Una vez eliminado, este usuario ya no tendrá acceso a la organización. Siempre puede volver a invitarlos más tarde, pero tendrán que aceptar la invitación de nuevo.", + "userMessageOrgConfirm": "Para confirmar, por favor escriba el nombre del usuario a continuación.", + "userRemoveOrgConfirm": "Confirmar eliminar usuario", + "userRemoveOrg": "Eliminar usuario de la organización", + "users": "Usuarios", + "accessRoleMember": "Miembro", + "accessRoleOwner": "Propietario", + "userConfirmed": "Confirmada", + "idpNameInternal": "Interno", + "emailInvalid": "Dirección de correo inválida", + "inviteValidityDuration": "Por favor, seleccione una duración", + "accessRoleSelectPlease": "Por favor, seleccione un rol", + "usernameRequired": "Nombre de usuario requerido", + "idpSelectPlease": "Por favor, seleccione un proveedor de identidad", + "idpGenericOidc": "Proveedor OAuth2/OIDC genérico.", + "accessRoleErrorFetch": "Error al recuperar roles", + "accessRoleErrorFetchDescription": "Se ha producido un error al recuperar los roles", + "idpErrorFetch": "Error al recuperar proveedores de identidad", + "idpErrorFetchDescription": "Se ha producido un error al recuperar proveedores de identidad", + "userErrorExists": "El usuario ya existe", + "userErrorExistsDescription": "Este usuario ya es miembro de la organización.", + "inviteError": "Error al invitar al usuario", + "inviteErrorDescription": "Ocurrió un error mientras se invitaba al usuario", + "userInvited": "Usuario invitado", + "userInvitedDescription": "El usuario ha sido invitado con éxito.", + "userErrorCreate": "Error al crear el usuario", + "userErrorCreateDescription": "Se ha producido un error al crear el usuario", + "userCreated": "Usuario creado", + "userCreatedDescription": "El usuario se ha creado correctamente.", + "userTypeInternal": "Usuario interno", + "userTypeInternalDescription": "Invita a un usuario a unirse a tu organización directamente.", + "userTypeExternal": "Usuario externo", + "userTypeExternalDescription": "Crear un usuario con un proveedor de identidad externo.", + "accessUserCreateDescription": "Siga los pasos siguientes para crear un nuevo usuario", + "userSeeAll": "Ver todos los usuarios", + "userTypeTitle": "Tipo de usuario", + "userTypeDescription": "Determina cómo quieres crear el usuario", + "userSettings": "Información del usuario", + "userSettingsDescription": "Introduzca los detalles del nuevo usuario", + "inviteEmailSent": "Enviar correo de invitación al usuario", + "inviteValid": "Válido para", + "selectDuration": "Seleccionar duración", + "accessRoleSelect": "Seleccionar rol", + "inviteEmailSentDescription": "Se ha enviado un correo electrónico al usuario con el siguiente enlace de acceso. Debe acceder al enlace para aceptar la invitación.", + "inviteSentDescription": "El usuario ha sido invitado. Debe acceder al enlace de abajo para aceptar la invitación.", + "inviteExpiresIn": "La invitación expirará en {days, plural, =1 {# día} other {# días}}.", + "idpTitle": "Proveedor de identidad", + "idpSelect": "Seleccione el proveedor de identidad para el usuario externo", + "idpNotConfigured": "No hay proveedores de identidad configurados. Por favor, configure un proveedor de identidad antes de crear usuarios externos.", + "usernameUniq": "Esto debe coincidir con el nombre de usuario único que existe en el proveedor de identidad seleccionado.", + "emailOptional": "Email (opcional)", + "nameOptional": "Nombre (opcional)", + "accessControls": "Controles de acceso", + "userDescription2": "Administrar la configuración de este usuario", + "accessRoleErrorAdd": "No se pudo agregar el usuario al rol", + "accessRoleErrorAddDescription": "Ocurrió un error mientras se añadía el usuario al rol.", + "userSaved": "Usuario guardado", + "userSavedDescription": "El usuario ha sido actualizado.", + "accessControlsDescription": "Administrar lo que este usuario puede acceder y hacer en la organización", + "accessControlsSubmit": "Guardar controles de acceso", + "roles": "Roles", + "accessUsersRoles": "Administrar usuarios y roles", + "accessUsersRolesDescription": "Invitar usuarios y añadirlos a roles para administrar el acceso a su organización", + "key": "Clave", + "createdAt": "Creado el", + "proxyErrorInvalidHeader": "Valor de cabecera de host personalizado no válido. Utilice el formato de nombre de dominio, o guarde en blanco para desestablecer cabecera de host personalizada.", + "proxyErrorTls": "Nombre de servidor TLS inválido. Utilice el formato de nombre de dominio o guarde en blanco para eliminar el nombre de servidor TLS.", + "proxyEnableSSL": "Habilitar SSL (https)", + "targetErrorFetch": "Error al recuperar los objetivos", + "targetErrorFetchDescription": "Se ha producido un error al recuperar los objetivos", + "siteErrorFetch": "No se pudo obtener el recurso", + "siteErrorFetchDescription": "Se ha producido un error al recuperar el recurso", + "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicateDescription": "Ya existe un objetivo con estos ajustes", + "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIpDescription": "La IP de destino debe estar dentro de la subred del sitio", + "targetsUpdated": "Objetivos actualizados", + "targetsUpdatedDescription": "Objetivos y ajustes actualizados correctamente", + "targetsErrorUpdate": "Error al actualizar los objetivos", + "targetsErrorUpdateDescription": "Se ha producido un error al actualizar los objetivos", + "targetTlsUpdate": "Ajustes TLS actualizados", + "targetTlsUpdateDescription": "La configuración de TLS se ha actualizado correctamente", + "targetErrorTlsUpdate": "Error al actualizar los ajustes de TLS", + "targetErrorTlsUpdateDescription": "Ocurrió un error mientras se actualizaban los ajustes de TLS", + "proxyUpdated": "Configuración del proxy actualizada", + "proxyUpdatedDescription": "La configuración del proxy se ha actualizado correctamente", + "proxyErrorUpdate": "Error al actualizar la configuración del proxy", + "proxyErrorUpdateDescription": "Se ha producido un error al actualizar la configuración del proxy", + "targetAddr": "IP / Hostname", + "targetPort": "Puerto", + "targetProtocol": "Protocol", + "targetTlsSettings": "Configuración HTTPS y TLS", + "targetTlsSettingsDescription": "Configurar ajustes TLS para su recurso", + "targetTlsSettingsAdvanced": "Ajustes avanzados de TLS", + "targetTlsSni": "Nombre del servidor TLS (SNI)", + "targetTlsSniDescription": "El nombre del servidor TLS a usar para SNI. Deje en blanco para usar el valor predeterminado.", + "targetTlsSubmit": "Guardar ajustes", + "targets": "Configuración de objetivos", + "targetsDescription": "Configurar objetivos para enrutar tráfico a sus servicios", + "targetStickySessions": "Activar Sesiones Pegadas", + "targetStickySessionsDescription": "Mantener conexiones en el mismo objetivo de backend para toda su sesión.", + "methodSelect": "Seleccionar método", + "targetSubmit": "Add Target", + "targetNoOne": "No hay objetivos. Agregue un objetivo usando el formulario.", + "targetNoOneDescription": "Si se añade más de un objetivo anterior se activará el balance de carga.", + "targetsSubmit": "Guardar objetivos", + "proxyAdditional": "Ajustes adicionales del proxy", + "proxyAdditionalDescription": "Configura cómo tu recurso maneja la configuración del proxy", + "proxyCustomHeader": "Cabecera de host personalizada", + "proxyCustomHeaderDescription": "La cabecera del host a establecer cuando se realizan peticiones de reemplazo. Deje en blanco para usar el valor predeterminado.", + "proxyAdditionalSubmit": "Guardar ajustes de proxy", + "subnetMaskErrorInvalid": "Máscara de subred inválida. Debe estar entre 0 y 32.", + "ipAddressErrorInvalidFormat": "Formato de dirección IP inválido", + "ipAddressErrorInvalidOctet": "Octet de dirección IP no válido", + "path": "Ruta", + "ipAddressRange": "Rango IP", + "rulesErrorFetch": "Error al obtener las reglas", + "rulesErrorFetchDescription": "Se ha producido un error al recuperar las reglas", + "rulesErrorDuplicate": "Duplicar regla", + "rulesErrorDuplicateDescription": "Ya existe una regla con estos ajustes", + "rulesErrorInvalidIpAddressRange": "CIDR inválido", + "rulesErrorInvalidIpAddressRangeDescription": "Por favor, introduzca un valor CIDR válido", + "rulesErrorInvalidUrl": "Ruta URL inválida", + "rulesErrorInvalidUrlDescription": "Por favor, introduzca un valor de ruta de URL válido", + "rulesErrorInvalidIpAddress": "IP inválida", + "rulesErrorInvalidIpAddressDescription": "Por favor, introduzca una dirección IP válida", + "rulesErrorUpdate": "Error al actualizar las reglas", + "rulesErrorUpdateDescription": "Se ha producido un error al actualizar las reglas", + "rulesUpdated": "Activar Reglas", + "rulesUpdatedDescription": "La evaluación de la regla ha sido actualizada", + "rulesMatchIpAddressRangeDescription": "Introduzca una dirección en formato CIDR (por ejemplo, 103.21.244.0/22)", + "rulesMatchIpAddress": "Introduzca una dirección IP (por ejemplo, 103.21.244.12)", + "rulesMatchUrl": "Introduzca una ruta URL o patrón (por ej., /api/v1/todos o /api/v1/*)", + "rulesErrorInvalidPriority": "Prioridad inválida", + "rulesErrorInvalidPriorityDescription": "Por favor, introduzca una prioridad válida", + "rulesErrorDuplicatePriority": "Prioridades duplicadas", + "rulesErrorDuplicatePriorityDescription": "Por favor, introduzca prioridades únicas", + "ruleUpdated": "Reglas actualizadas", + "ruleUpdatedDescription": "Reglas actualizadas correctamente", + "ruleErrorUpdate": "Operación fallida", + "ruleErrorUpdateDescription": "Se ha producido un error durante la operación de guardado", + "rulesPriority": "Prioridad", + "rulesAction": "Accin", + "rulesMatchType": "Tipo de partida", + "value": "Valor", + "rulesAbout": "Sobre Reglas", + "rulesAboutDescription": "Las reglas le permiten controlar el acceso a su recurso basado en un conjunto de criterios. Puede crear reglas para permitir o denegar el acceso basándose en la dirección IP o ruta de la URL.", + "rulesActions": "Acciones", + "rulesActionAlwaysAllow": "Permitir siempre: pasar todos los métodos de autenticación", + "rulesActionAlwaysDeny": "Denegar siempre: Bloquear todas las peticiones; no se puede intentar autenticación", + "rulesMatchCriteria": "Criterios coincidentes", + "rulesMatchCriteriaIpAddress": "Coincidir con una dirección IP específica", + "rulesMatchCriteriaIpAddressRange": "Coincide con un rango de direcciones IP en notación CIDR", + "rulesMatchCriteriaUrl": "Coincidir con una ruta de URL o patrón", + "rulesEnable": "Activar Reglas", + "rulesEnableDescription": "Activar o desactivar la evaluación de reglas para este recurso", + "rulesResource": "Configuración de reglas de recursos", + "rulesResourceDescription": "Configurar reglas para controlar el acceso a su recurso", + "ruleSubmit": "Añadir Regla", + "rulesNoOne": "No hay reglas. Agregue una regla usando el formulario.", + "rulesOrder": "Las reglas son evaluadas por prioridad en orden ascendente.", + "rulesSubmit": "Guardar Reglas", + "resourceErrorCreate": "Error al crear recurso", + "resourceErrorCreateDescription": "Se ha producido un error al crear el recurso", + "resourceErrorCreateMessage": "Error al crear el recurso:", + "resourceErrorCreateMessageDescription": "Se ha producido un error inesperado", + "sitesErrorFetch": "Error obteniendo sitios", + "sitesErrorFetchDescription": "Se ha producido un error al recuperar los sitios", + "domainsErrorFetch": "Error obteniendo dominios", + "domainsErrorFetchDescription": "Se ha producido un error al recuperar los dominios", + "none": "Ninguna", + "unknown": "Desconocido", + "resources": "Recursos", + "resourcesDescription": "Los recursos son proxies para aplicaciones que se ejecutan en su red privada. Cree un recurso para cualquier servicio HTTP/HTTPS o TCP/UDP crudo en su red privada. Cada recurso debe estar conectado a un sitio para permitir una conectividad privada y segura a través de un túnel encriptado de WireGuard.", + "resourcesWireGuardConnect": "Conectividad segura con cifrado de Wirex Guard", + "resourcesMultipleAuthenticationMethods": "Configurar múltiples métodos de autenticación", + "resourcesUsersRolesAccess": "Control de acceso basado en usuarios y roles", + "resourcesErrorUpdate": "Error al cambiar el recurso", + "resourcesErrorUpdateDescription": "Se ha producido un error al actualizar el recurso", + "access": "Acceder", + "shareLink": "{resource} Compartir Enlace", + "resourceSelect": "Seleccionar recurso", + "shareLinks": "Compartir enlaces", + "share": "Enlaces compartibles", + "shareDescription2": "Crea enlaces compartidos con tus recursos. Los enlaces proporcionan acceso temporal o ilimitado a tu recurso. Puede configurar la duración de caducidad del enlace cuando cree uno.", + "shareEasyCreate": "Fácil de crear y compartir", + "shareConfigurableExpirationDuration": "Duración de caducidad configurable", + "shareSecureAndRevocable": "Seguro y revocable", + "nameMin": "El nombre debe tener al menos caracteres {len}.", + "nameMax": "El nombre no debe tener más de {len} caracteres.", + "sitesConfirmCopy": "Por favor, confirme que ha copiado la configuración.", + "unknownCommand": "Comando desconocido", + "newtErrorFetchReleases": "No se pudo obtener la información del lanzamiento: {err}", + "newtErrorFetchLatest": "Error obteniendo la última versión: {err}", + "newtEndpoint": "Newt Endpoint", + "newtId": "Newt ID", + "newtSecretKey": "Clave secreta de Newt", + "architecture": "Arquitectura", + "sites": "Sitios", + "siteWgAnyClients": "Usa cualquier cliente de Wirex para conectarte. Tendrás que dirigirte a tus recursos internos usando la IP de compañeros.", + "siteWgCompatibleAllClients": "Compatible con todos los clientes de Wirex Guard", + "siteWgManualConfigurationRequired": "Configuración manual requerida", + "userErrorNotAdminOrOwner": "El usuario no es un administrador o propietario", + "pangolinSettings": "Ajustes - Pangolin", + "accessRoleYour": "Tu rol:", + "accessRoleSelect2": "Seleccione un rol", + "accessUserSelect": "Seleccione un usuario", + "otpEmailEnter": "Escribe un email", + "otpEmailEnterDescription": "Pulse Enter para añadir un correo electrónico después de teclearlo en el campo de entrada.", + "otpEmailErrorInvalid": "Dirección de correo electrónico no válida. El comodín (*) debe ser la parte local completa.", + "otpEmailSmtpRequired": "SMTP Requerido", + "otpEmailSmtpRequiredDescription": "SMTP debe estar habilitado en el servidor para usar autenticación de contraseña de una sola vez.", + "otpEmailTitle": "Contraseñas de una sola vez", + "otpEmailTitleDescription": "Requiere autenticación por correo electrónico para acceso a recursos", + "otpEmailWhitelist": "Lista blanca de correo", + "otpEmailWhitelistList": "Correos en la lista blanca", + "otpEmailWhitelistListDescription": "Sólo los usuarios con estas direcciones de correo electrónico podrán acceder a este recurso. Se les pedirá que introduzcan una contraseña de una sola vez enviada a su correo electrónico. Los comodines (*@ejemplo.com) pueden utilizarse para permitir cualquier dirección de correo electrónico de un dominio.", + "otpEmailWhitelistSave": "Guardar lista blanca", + "passwordAdd": "Añadir contraseña", + "passwordRemove": "Eliminar contraseña", + "pincodeAdd": "Añadir código PIN", + "pincodeRemove": "Eliminar código PIN", + "resourceAuthMethods": "Métodos de autenticación", + "resourceAuthMethodsDescriptions": "Permitir el acceso al recurso a través de métodos de autenticación adicionales", + "resourceAuthSettingsSave": "Guardado correctamente", + "resourceAuthSettingsSaveDescription": "Se han guardado los ajustes de autenticación", + "resourceErrorAuthFetch": "Error al recuperar datos", + "resourceErrorAuthFetchDescription": "Se ha producido un error al recuperar los datos", + "resourceErrorPasswordRemove": "Error al eliminar la contraseña del recurso", + "resourceErrorPasswordRemoveDescription": "Se ha producido un error al eliminar la contraseña del recurso", + "resourceErrorPasswordSetup": "Error al establecer la contraseña del recurso", + "resourceErrorPasswordSetupDescription": "Se ha producido un error al establecer la contraseña del recurso", + "resourceErrorPincodeRemove": "Error al eliminar el código pin del recurso", + "resourceErrorPincodeRemoveDescription": "Ocurrió un error mientras se eliminaba el código pin del recurso", + "resourceErrorPincodeSetup": "Error al establecer el código PIN del recurso", + "resourceErrorPincodeSetupDescription": "Se ha producido un error al establecer el código PIN del recurso", + "resourceErrorUsersRolesSave": "Error al establecer roles", + "resourceErrorUsersRolesSaveDescription": "Se ha producido un error al establecer los roles", + "resourceErrorWhitelistSave": "Error al guardar la lista blanca", + "resourceErrorWhitelistSaveDescription": "Ocurrió un error mientras se guardaba la lista blanca", + "resourcePasswordSubmit": "Activar la protección de contraseña", + "resourcePasswordProtection": "Protección de contraseña {status}", + "resourcePasswordRemove": "Contraseña de recurso eliminada", + "resourcePasswordRemoveDescription": "La contraseña del recurso se ha eliminado correctamente", + "resourcePasswordSetup": "Contraseña de recurso establecida", + "resourcePasswordSetupDescription": "La contraseña del recurso se ha establecido correctamente", + "resourcePasswordSetupTitle": "Establecer contraseña", + "resourcePasswordSetupTitleDescription": "Establecer una contraseña para proteger este recurso", + "resourcePincode": "Código PIN", + "resourcePincodeSubmit": "Activar protección de código PIN", + "resourcePincodeProtection": "Protección del código PIN {status}", + "resourcePincodeRemove": "Código del recurso eliminado", + "resourcePincodeRemoveDescription": "La contraseña del recurso se ha eliminado correctamente", + "resourcePincodeSetup": "Código PIN del recurso establecido", + "resourcePincodeSetupDescription": "El código del recurso se ha establecido correctamente", + "resourcePincodeSetupTitle": "Definir Pincode", + "resourcePincodeSetupTitleDescription": "Establecer un pincode para proteger este recurso", + "resourceRoleDescription": "Los administradores siempre pueden acceder a este recurso.", + "resourceUsersRoles": "Usuarios y roles", + "resourceUsersRolesDescription": "Configurar qué usuarios y roles pueden visitar este recurso", + "resourceUsersRolesSubmit": "Guardar usuarios y roles", + "resourceWhitelistSave": "Guardado correctamente", + "resourceWhitelistSaveDescription": "Se han guardado los ajustes de la lista blanca", + "ssoUse": "Usar Plataforma SSO", + "ssoUseDescription": "Los usuarios existentes sólo tendrán que iniciar sesión una vez para todos los recursos que tengan esto habilitado.", + "proxyErrorInvalidPort": "Número de puerto inválido", + "subdomainErrorInvalid": "Subdominio inválido", + "domainErrorFetch": "Error obteniendo dominios", + "domainErrorFetchDescription": "Se ha producido un error al recuperar los dominios", + "resourceErrorUpdate": "Error al actualizar el recurso", + "resourceErrorUpdateDescription": "Se ha producido un error al actualizar el recurso", + "resourceUpdated": "Recurso actualizado", + "resourceUpdatedDescription": "El recurso se ha actualizado correctamente", + "resourceErrorTransfer": "Error al transferir el recurso", + "resourceErrorTransferDescription": "Se ha producido un error al transferir el recurso", + "resourceTransferred": "Recurso transferido", + "resourceTransferredDescription": "El recurso ha sido transferido con éxito", + "resourceErrorToggle": "Error al cambiar el recurso", + "resourceErrorToggleDescription": "Se ha producido un error al actualizar el recurso", + "resourceVisibilityTitle": "Visibilidad", + "resourceVisibilityTitleDescription": "Activar o desactivar completamente la visibilidad de los recursos", + "resourceGeneral": "Configuración General", + "resourceGeneralDescription": "Configurar la configuración general de este recurso", + "resourceEnable": "Activar recurso", + "resourceTransfer": "Transferir recursos", + "resourceTransferDescription": "Transferir este recurso a un sitio diferente", + "resourceTransferSubmit": "Transferir recursos", + "siteDestination": "Sitio de destino", + "searchSites": "Buscar sitios", + "accessRoleCreate": "Crear rol", + "accessRoleCreateDescription": "Crear un nuevo rol para agrupar usuarios y administrar sus permisos.", + "accessRoleCreateSubmit": "Crear rol", + "accessRoleCreated": "Rol creado", + "accessRoleCreatedDescription": "El rol se ha creado correctamente.", + "accessRoleErrorCreate": "Error al crear el rol", + "accessRoleErrorCreateDescription": "Se ha producido un error al crear el rol.", + "accessRoleErrorNewRequired": "Se requiere un nuevo rol", + "accessRoleErrorRemove": "Error al eliminar el rol", + "accessRoleErrorRemoveDescription": "Ocurrió un error mientras se eliminaba el rol.", + "accessRoleName": "Nombre del Rol", + "accessRoleQuestionRemove": "Estás a punto de eliminar el rol {name} . No puedes deshacer esta acción.", + "accessRoleRemove": "Quitar rol", + "accessRoleRemoveDescription": "Eliminar un rol de la organización", + "accessRoleRemoveSubmit": "Quitar rol", + "accessRoleRemoved": "Rol eliminado", + "accessRoleRemovedDescription": "El rol se ha eliminado correctamente.", + "accessRoleRequiredRemove": "Antes de eliminar este rol, seleccione un nuevo rol al que transferir miembros existentes.", + "manage": "Gestionar", + "sitesNotFound": "Sitios no encontrados.", + "pangolinServerAdmin": "Admin Servidor - Pangolin", + "licenseTierProfessional": "Licencia profesional", + "licenseTierEnterprise": "Licencia Enterprise", + "licenseTierCommercial": "Licencia comercial", + "licensed": "Licenciado", + "yes": "Sí", + "no": "Nu", + "sitesAdditional": "Sitios adicionales", + "licenseKeys": "Claves de licencia", + "sitestCountDecrease": "Reducir el número de sitios", + "sitestCountIncrease": "Aumentar el número de sitios", + "idpManage": "Administrar proveedores de identidad", + "idpManageDescription": "Ver y administrar proveedores de identidad en el sistema", + "idpDeletedDescription": "Proveedor de identidad eliminado correctamente", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "¿Está seguro que desea eliminar permanentemente el proveedor de identidad {name}?", + "idpMessageRemove": "Esto eliminará el proveedor de identidad y todas las configuraciones asociadas. Los usuarios que se autentifiquen a través de este proveedor ya no podrán iniciar sesión.", + "idpMessageConfirm": "Para confirmar, por favor escriba el nombre del proveedor de identidad a continuación.", + "idpConfirmDelete": "Confirmar eliminar proveedor de identidad", + "idpDelete": "Eliminar proveedor de identidad", + "idp": "Proveedores de identidad", + "idpSearch": "Buscar proveedores de identidad...", + "idpAdd": "Añadir proveedor de identidad", + "idpClientIdRequired": "Se requiere ID de cliente.", + "idpClientSecretRequired": "El secreto del cliente es obligatorio.", + "idpErrorAuthUrlInvalid": "La URL de autenticación debe ser una URL válida.", + "idpErrorTokenUrlInvalid": "La URL del token debe ser una URL válida.", + "idpPathRequired": "La ruta identificadora es requerida.", + "idpScopeRequired": "Se requiere alcance.", + "idpOidcDescription": "Configurar un proveedor de identidad OpenID Connect", + "idpCreatedDescription": "Proveedor de identidad creado correctamente", + "idpCreate": "Crear proveedor de identidad", + "idpCreateDescription": "Configurar un nuevo proveedor de identidad para la autenticación de usuario", + "idpSeeAll": "Ver todos los proveedores de identidad", + "idpSettingsDescription": "Configure la información básica para su proveedor de identidad", + "idpDisplayName": "Un nombre mostrado para este proveedor de identidad", + "idpAutoProvisionUsers": "Auto-Provisión de Usuarios", + "idpAutoProvisionUsersDescription": "Cuando está habilitado, los usuarios serán creados automáticamente en el sistema al iniciar sesión con la capacidad de asignar a los usuarios a roles y organizaciones.", + "licenseBadge": "Profesional", + "idpType": "Tipo de proveedor", + "idpTypeDescription": "Seleccione el tipo de proveedor de identidad que desea configurar", + "idpOidcConfigure": "Configuración OAuth2/OIDC", + "idpOidcConfigureDescription": "Configurar los puntos finales y credenciales del proveedor OAuth2/OIDC", + "idpClientId": "ID de cliente", + "idpClientIdDescription": "El ID del cliente OAuth2 de su proveedor de identidad", + "idpClientSecret": "Cliente secreto", + "idpClientSecretDescription": "El secreto del cliente OAuth2 de su proveedor de identidad", + "idpAuthUrl": "URL de autorización", + "idpAuthUrlDescription": "La URL final de autorización de OAuth2", + "idpTokenUrl": "URL del token", + "idpTokenUrlDescription": "La URL del endpoint del token OAuth2", + "idpOidcConfigureAlert": "Información importante", + "idpOidcConfigureAlertDescription": "Después de crear el proveedor de identidad, necesitará configurar la URL de callback en la configuración de su proveedor de identidad. La URL de devolución de llamada se proporcionará después de la creación exitosa.", + "idpToken": "Configuración del token", + "idpTokenDescription": "Configurar cómo extraer la información del usuario del token de ID", + "idpJmespathAbout": "Acerca de JMESPath", + "idpJmespathAboutDescription": "Las siguientes rutas utilizan la sintaxis JMESPath para extraer valores del token ID.", + "idpJmespathAboutDescriptionLink": "Más información sobre JMESPath", + "idpJmespathLabel": "Ruta del identificador", + "idpJmespathLabelDescription": "La ruta al identificador de usuario en el token de ID", + "idpJmespathEmailPathOptional": "Ruta de correo (opcional)", + "idpJmespathEmailPathOptionalDescription": "La ruta al correo electrónico del usuario en el token de ID", + "idpJmespathNamePathOptional": "Ruta del nombre (opcional)", + "idpJmespathNamePathOptionalDescription": "La ruta al nombre del usuario en el token de ID", + "idpOidcConfigureScopes": "Ámbitos", + "idpOidcConfigureScopesDescription": "Lista separada por espacios de los ámbitos OAuth2 a solicitar", + "idpSubmit": "Crear proveedor de identidad", + "orgPolicies": "Políticas de organización", + "idpSettings": "Ajustes {idpName}", + "idpCreateSettingsDescription": "Configurar la configuración de su proveedor de identidad", + "roleMapping": "Mapeo de Rol", + "orgMapping": "Mapeo de organización", + "orgPoliciesSearch": "Buscar políticas de organización...", + "orgPoliciesAdd": "Añadir Política de Organización", + "orgRequired": "La organización es obligatoria", + "error": "Error", + "success": "Éxito", + "orgPolicyAddedDescription": "Política añadida correctamente", + "orgPolicyUpdatedDescription": "Política actualizada correctamente", + "orgPolicyDeletedDescription": "Política eliminada correctamente", + "defaultMappingsUpdatedDescription": "Mapeos por defecto actualizados correctamente", + "orgPoliciesAbout": "Acerca de políticas de organización", + "orgPoliciesAboutDescription": "Las políticas de la organización se utilizan para controlar el acceso a las organizaciones basándose en el token de identificación del usuario. Puede especificar expresiones JMESPath para extraer información de rol y organización del token de identificación.", + "orgPoliciesAboutDescriptionLink": "Vea la documentación, para más información.", + "defaultMappingsOptional": "Mapeo por defecto (opcional)", + "defaultMappingsOptionalDescription": "Los mapeos por defecto se utilizan cuando no hay una política de organización definida para una organización. Puede especificar las asignaciones predeterminadas de rol y organización a las que volver aquí.", + "defaultMappingsRole": "Mapeo de Rol por defecto", + "defaultMappingsRoleDescription": "El resultado de esta expresión debe devolver el nombre del rol tal y como se define en la organización como una cadena.", + "defaultMappingsOrg": "Mapeo de organización por defecto", + "defaultMappingsOrgDescription": "Esta expresión debe devolver el ID de org o verdadero para que el usuario pueda acceder a la organización.", + "defaultMappingsSubmit": "Guardar asignaciones por defecto", + "orgPoliciesEdit": "Editar Política de Organización", + "org": "Organización", + "orgSelect": "Seleccionar organización", + "orgSearch": "Buscar org", + "orgNotFound": "No se encontró org.", + "roleMappingPathOptional": "Ruta de Mapeo de Rol (opcional)", + "orgMappingPathOptional": "Ruta de mapeo de organización (opcional)", + "orgPolicyUpdate": "Actualizar política", + "orgPolicyAdd": "Añadir Política", + "orgPolicyConfig": "Configurar acceso para una organización", + "idpUpdatedDescription": "Proveedor de identidad actualizado correctamente", + "redirectUrl": "URL de redirección", + "redirectUrlAbout": "Acerca de la URL de redirección", + "redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.", + "pangolinAuth": "Auth - Pangolin", + "verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.", + "errorOccurred": "Se ha producido un error", + "emailErrorVerify": "No se pudo verificar el email:", + "emailVerified": "¡Correo electrónico verificado con éxito! Redirigiendo...", + "verificationCodeErrorResend": "Error al reenviar el código de verificación:", + "verificationCodeResend": "Código de verificación reenviado", + "verificationCodeResendDescription": "Hemos reenviado un código de verificación a tu dirección de correo electrónico. Por favor, comprueba tu bandeja de entrada.", + "emailVerify": "Verificar Email", + "emailVerifyDescription": "Introduzca el código de verificación enviado a su dirección de correo electrónico.", + "verificationCode": "Código de verificación", + "verificationCodeEmailSent": "Hemos enviado un código de verificación a tu dirección de correo electrónico.", + "submit": "Enviar", + "emailVerifyResendProgress": "Reenviando...", + "emailVerifyResend": "¿No has recibido un código? Haz clic aquí para reenviar", + "passwordNotMatch": "Las contraseñas no coinciden", + "signupError": "Se ha producido un error al registrarse", + "pangolinLogoAlt": "Logo de Pangolin", + "inviteAlready": "¡Parece que has sido invitado!", + "inviteAlreadyDescription": "Para aceptar la invitación, debes iniciar sesión o crear una cuenta.", + "signupQuestion": "¿Ya tienes una cuenta?", + "login": "Iniciar sesión", + "resourceNotFound": "Recurso no encontrado", + "resourceNotFoundDescription": "El recurso al que intentas acceder no existe.", + "pincodeRequirementsLength": "El PIN debe tener exactamente 6 dígitos", + "pincodeRequirementsChars": "El PIN sólo debe contener números", + "passwordRequirementsLength": "La contraseña debe tener al menos 1 carácter", + "otpEmailRequirementsLength": "OTP debe tener al menos 1 carácter", + "otpEmailSent": "OTP enviado", + "otpEmailSentDescription": "Un OTP ha sido enviado a tu correo electrónico", + "otpEmailErrorAuthenticate": "Error al autenticar con el correo electrónico", + "pincodeErrorAuthenticate": "Error al autenticar con pincode", + "passwordErrorAuthenticate": "Error al autenticar con contraseña", + "poweredBy": "Desarrollado por", + "authenticationRequired": "Autenticación requerida", + "authenticationMethodChoose": "Elige tu método preferido para acceder a {name}", + "authenticationRequest": "Debes autenticarte para acceder a {name}", + "user": "Usuario", + "pincodeInput": "Código PIN de 6 dígitos", + "pincodeSubmit": "Iniciar sesión con PIN", + "passwordSubmit": "Iniciar sesión con contraseña", + "otpEmailDescription": "Se enviará un código único a este correo electrónico.", + "otpEmailSend": "Enviar código de una sola vez", + "otpEmail": "Contraseña de una sola vez (OTP)", + "otpEmailSubmit": "Enviar OTP", + "backToEmail": "Volver al Email", + "noSupportKey": "El servidor se está ejecutando sin una clave de soporte. ¡Considere apoyar el proyecto!", + "accessDenied": "Acceso denegado", + "accessDeniedDescription": "No tienes permiso para acceder a este recurso. Si esto es un error, por favor contacta con el administrador.", + "accessTokenError": "Error comprobando el token de acceso", + "accessGranted": "Acceso concedido", + "accessUrlInvalid": "URL de acceso inválida", + "accessGrantedDescription": "Se te ha concedido acceso a este recurso. Redirigiendo...", + "accessUrlInvalidDescription": "Esta URL de acceso compartido no es válida. Por favor, póngase en contacto con el propietario del recurso para una nueva URL.", + "tokenInvalid": "Token inválido", + "pincodeInvalid": "Código inválido", + "passwordErrorRequestReset": "Error al solicitar reinicio:", + "passwordErrorReset": "Error al restablecer la contraseña:", + "passwordResetSuccess": "¡Contraseña restablecida! Volver para iniciar sesión...", + "passwordReset": "Restablecer contraseña", + "passwordResetDescription": "Siga los pasos para restablecer su contraseña", + "passwordResetSent": "Enviaremos un código para restablecer la contraseña a esta dirección de correo electrónico.", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "Revisa tu correo electrónico para ver el código de restablecimiento.", + "passwordNew": "Nueva contraseña", + "passwordNewConfirm": "Confirmar nueva contraseña", + "pincodeAuth": "Código de autenticación", + "pincodeSubmit2": "Enviar código", + "passwordResetSubmit": "Reiniciar Solicitud", + "passwordBack": "Volver a la contraseña", + "loginBack": "Volver a iniciar sesión", + "signup": "Regístrate", + "loginStart": "Inicia sesión para empezar", + "idpOidcTokenValidating": "Validando token OIDC", + "idpOidcTokenResponse": "Validar respuesta de token OIDC", + "idpErrorOidcTokenValidating": "Error al validar token OIDC", + "idpConnectingTo": "Conectando a {name}", + "idpConnectingToDescription": "Validando tu identidad", + "idpConnectingToProcess": "Conectando...", + "idpConnectingToFinished": "Conectado", + "idpErrorConnectingTo": "Hubo un problema al conectar con {name}. Por favor, póngase en contacto con su administrador.", + "idpErrorNotFound": "IdP no encontrado", + "inviteInvalid": "Invitación inválida", + "inviteInvalidDescription": "El enlace de invitación no es válido.", + "inviteErrorWrongUser": "La invitación no es para este usuario", + "inviteErrorUserNotExists": "El usuario no existe. Por favor, cree una cuenta primero.", + "inviteErrorLoginRequired": "Debes estar conectado para aceptar una invitación", + "inviteErrorExpired": "La invitación puede haber caducado", + "inviteErrorRevoked": "La invitación podría haber sido revocada", + "inviteErrorTypo": "Puede haber un error en el enlace de invitación", + "pangolinSetup": "Setup - Pangolin", + "orgNameRequired": "El nombre de la organización es obligatorio", + "orgIdRequired": "El ID de la organización es obligatorio", + "orgErrorCreate": "Se ha producido un error al crear el org", + "pageNotFound": "Página no encontrada", + "pageNotFoundDescription": "¡Vaya! La página que estás buscando no existe.", + "overview": "Resumen", + "home": "Inicio", + "accessControl": "Control de acceso", + "settings": "Ajustes", + "usersAll": "Todos los usuarios", + "license": "Licencia", + "pangolinDashboard": "Tablero - Pangolin", + "noResults": "No se han encontrado resultados.", + "terabytes": "TB {count}", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Etiquetas introducidas", + "tagsEnteredDescription": "Estas son las etiquetas que has introducido.", + "tagsWarnCannotBeLessThanZero": "maxTags y minTags no pueden ser menores que 0", + "tagsWarnNotAllowedAutocompleteOptions": "Etiqueta no permitida como opciones de autocompletado", + "tagsWarnInvalid": "Etiqueta no válida según validateTag", + "tagWarnTooShort": "La etiqueta {tagText} es demasiado corta", + "tagWarnTooLong": "La etiqueta {tagText} es demasiado larga", + "tagsWarnReachedMaxNumber": "Alcanzado el número máximo de etiquetas permitidas", + "tagWarnDuplicate": "Etiqueta {tagText} duplicada no añadida", + "supportKeyInvalid": "Clave inválida", + "supportKeyInvalidDescription": "Tu clave de seguidor no es válida.", + "supportKeyValid": "Valid Key", + "supportKeyValidDescription": "Su clave de seguidor ha sido validada. ¡Gracias por su apoyo!", + "supportKeyErrorValidationDescription": "Error al validar la clave de seguidor.", + "supportKey": "¡Apoya el Desarrollo y Adopte un Pangolin!", + "supportKeyDescription": "Compra una clave de seguidor para ayudarnos a seguir desarrollando Pangolin para la comunidad. Su contribución nos permite comprometer más tiempo para mantener y añadir nuevas características a la aplicación para todos. Nunca usaremos esto para las características de paywall. Esto está separado de cualquier Edición Comercial.", + "supportKeyPet": "También podrás adoptar y conocer a tu propio Pangolin mascota.", + "supportKeyPurchase": "Los pagos se procesan a través de GitHub. Después, puede recuperar su clave en", + "supportKeyPurchaseLink": "nuestro sitio web", + "supportKeyPurchase2": "y canjéelo aquí.", + "supportKeyLearnMore": "Más información.", + "supportKeyOptions": "Por favor, seleccione la opción que más le convenga.", + "supportKetOptionFull": "Asistente completo", + "forWholeServer": "Para todo el servidor", + "lifetimePurchase": "Compra de por vida", + "supporterStatus": "Estado del soporte", + "buy": "Comprar", + "supportKeyOptionLimited": "Apoyador limitado", + "forFiveUsers": "Para 5 o menos usuarios", + "supportKeyRedeem": "Canjear Clave de Apoyo", + "supportKeyHideSevenDays": "Ocultar durante 7 días", + "supportKeyEnter": "Introduzca Clave de Soporter", + "supportKeyEnterDescription": "Conoce a tu propia mascota Pangolin!", + "githubUsername": "GitHub Username", + "supportKeyInput": "Clave de apoyo", + "supportKeyBuy": "Comprar Clave de Apoyo", + "logoutError": "Error al cerrar sesión", + "signingAs": "Conectado como", + "serverAdmin": "Admin Servidor", + "otpEnable": "Activar doble factor", + "otpDisable": "Desactivar doble factor", + "logout": "Cerrar sesión", + "licenseTierProfessionalRequired": "Edición Profesional requerida", + "licenseTierProfessionalRequiredDescription": "Esta característica sólo está disponible en la Edición Profesional.", + "actionGetOrg": "Obtener organización", + "actionUpdateOrg": "Actualizar organización", + "actionGetOrgUser": "Obtener usuario de la organización", + "actionListOrgDomains": "Listar dominios de la organización", + "actionCreateSite": "Crear sitio", + "actionDeleteSite": "Eliminar sitio", + "actionGetSite": "Obtener sitio", + "actionListSites": "Listar sitios", + "actionUpdateSite": "Actualizar sitio", + "actionListSiteRoles": "Lista de roles permitidos del sitio", + "actionCreateResource": "Crear Recurso", + "actionDeleteResource": "Eliminar Recurso", + "actionGetResource": "Obtener recursos", + "actionListResource": "Listar recursos", + "actionUpdateResource": "Actualizar Recurso", + "actionListResourceUsers": "Listar usuarios de recursos", + "actionSetResourceUsers": "Establecer usuarios de recursos", + "actionSetAllowedResourceRoles": "Establecer roles de recursos permitidos", + "actionListAllowedResourceRoles": "Lista de roles de recursos permitidos", + "actionSetResourcePassword": "Establecer contraseña de recurso", + "actionSetResourcePincode": "Establecer Pincode del recurso", + "actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos", + "actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos", + "actionCreateTarget": "Create Target", + "actionDeleteTarget": "Eliminar destino", + "actionGetTarget": "Obtener objetivo", + "actionListTargets": "Lista de objetivos", + "actionUpdateTarget": "Update Target", + "actionCreateRole": "Crear rol", + "actionDeleteRole": "Eliminar rol", + "actionGetRole": "Obtener rol", + "actionListRole": "Lista de roles", + "actionUpdateRole": "Actualizar rol", + "actionListAllowedRoleResources": "Lista de recursos de rol permitidos", + "actionInviteUser": "Invitar usuario", + "actionRemoveUser": "Eliminar usuario", + "actionListUsers": "Listar usuarios", + "actionAddUserRole": "Añadir rol de usuario", + "actionGenerateAccessToken": "Generar token de acceso", + "actionDeleteAccessToken": "Eliminar token de acceso", + "actionListAccessTokens": "Lista de Tokens de Acceso", + "actionCreateResourceRule": "Crear Regla de Recursos", + "actionDeleteResourceRule": "Eliminar Regla de Recurso", + "actionListResourceRules": "Lista de Reglas de Recursos", + "actionUpdateResourceRule": "Actualizar regla de recursos", + "actionListOrgs": "Listar organizaciones", + "actionCheckOrgId": "Comprobar ID", + "actionCreateOrg": "Crear organización", + "actionDeleteOrg": "Eliminar organización", + "actionListApiKeys": "Lista de claves API", + "actionListApiKeyActions": "Listar acciones clave API", + "actionSetApiKeyActions": "Establecer acciones de clave API permitidas", + "actionCreateApiKey": "Crear Clave API", + "actionDeleteApiKey": "Borrar Clave API", + "actionCreateIdp": "Crear IDP", + "actionUpdateIdp": "Actualizar IDP", + "actionDeleteIdp": "Eliminar IDP", + "actionListIdps": "Listar IDP", + "actionGetIdp": "Obtener IDP", + "actionCreateIdpOrg": "Crear política de IDP Org", + "actionDeleteIdpOrg": "Eliminar política de IDP Org", + "actionListIdpOrgs": "Listar Orgs IDP", + "actionUpdateIdpOrg": "Actualizar IDP Org", + "noneSelected": "Ninguno seleccionado", + "orgNotFound2": "No se encontraron organizaciones.", + "searchProgress": "Buscar...", + "create": "Crear", + "orgs": "Organizaciones", + "loginError": "Se ha producido un error al iniciar sesión", + "passwordForgot": "¿Olvidaste tu contraseña?", + "otpAuth": "Autenticación de dos factores", + "otpAuthDescription": "Introduzca el código de su aplicación de autenticación o uno de sus códigos de copia de seguridad de un solo uso.", + "otpAuthSubmit": "Enviar código", + "idpContinue": "O continuar con", + "otpAuthBack": "Volver a iniciar sesión", + "navbar": "Navigation Menu", + "navbarDescription": "Menú de navegación principal para la aplicación", + "navbarDocsLink": "Documentación", + "commercialEdition": "Edición Comercial", + "otpErrorEnable": "No se puede habilitar 2FA", + "otpErrorEnableDescription": "Se ha producido un error al habilitar 2FA", + "otpSetupCheckCode": "Por favor, introduzca un código de 6 dígitos", + "otpSetupCheckCodeRetry": "Código no válido. Vuelve a intentarlo.", + "otpSetup": "Habilitar autenticación de doble factor", + "otpSetupDescription": "Asegure su cuenta con una capa extra de protección", + "otpSetupScanQr": "Escanea este código QR con tu aplicación de autenticación o introduce la clave secreta manualmente:", + "otpSetupSecretCode": "Código de autenticación", + "otpSetupSuccess": "Autenticación de dos factores habilitada", + "otpSetupSuccessStoreBackupCodes": "Tu cuenta ahora es más segura. No olvides guardar tus códigos de respaldo.", + "otpErrorDisable": "No se puede desactivar 2FA", + "otpErrorDisableDescription": "Se ha producido un error al desactivar 2FA", + "otpRemove": "Desactivar autenticación de doble factor", + "otpRemoveDescription": "Desactivar autenticación de doble factor para su cuenta", + "otpRemoveSuccess": "Autenticación de dos factores desactivada", + "otpRemoveSuccessMessage": "La autenticación de doble factor ha sido deshabilitada para su cuenta. Puede activarla de nuevo en cualquier momento.", + "otpRemoveSubmit": "Desactivar 2FA", + "paginator": "Página {current} de {last}", + "paginatorToFirst": "Ir a la primera página", + "paginatorToPrevious": "Ir a la página anterior", + "paginatorToNext": "Ir a la página siguiente", + "paginatorToLast": "Ir a la última página", + "copyText": "Copiar texto", + "copyTextFailed": "Error al copiar texto: ", + "copyTextClipboard": "Copiar al portapapeles", + "inviteErrorInvalidConfirmation": "Confirmación no válida", + "passwordRequired": "Se requiere contraseña", + "allowAll": "Permitir todo", + "permissionsAllowAll": "Permitir todos los permisos", + "githubUsernameRequired": "Se requiere el nombre de usuario de GitHub", + "supportKeyRequired": "Clave de apoyo es requerida", + "passwordRequirementsChars": "La contraseña debe tener al menos 8 caracteres", + "language": "Idioma", + "verificationCodeRequired": "El código es requerido", + "userErrorNoUpdate": "Ningún usuario para actualizar", + "siteErrorNoUpdate": "No hay sitio para actualizar", + "resourceErrorNoUpdate": "Ningún recurso para actualizar", + "authErrorNoUpdate": "No hay información de autenticación para actualizar", + "orgErrorNoUpdate": "No hay org para actualizar", + "orgErrorNoProvided": "No hay org proporcionado", + "apiKeysErrorNoUpdate": "Ninguna clave API para actualizar" +} diff --git a/messages/nl-NL.json b/messages/nl-NL.json new file mode 100644 index 00000000..2752f6dd --- /dev/null +++ b/messages/nl-NL.json @@ -0,0 +1,1079 @@ +{ + "setupCreate": "Maak uw organisatie, site en bronnen aan", + "setupNewOrg": "Nieuwe organisatie", + "setupCreateOrg": "Nieuwe organisatie aanmaken", + "setupCreateResources": "Bronnen aanmaken", + "setupOrgName": "Naam organisatie", + "orgDisplayName": "Dit is de weergavenaam van uw organisatie.", + "orgId": "Organisatie ID", + "setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.", + "setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.", + "componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.", + "componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.", + "welcome": "Welkom bij Pangolin", + "componentsCreateOrg": "Maak een Organisatie", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "Ongeldige of verlopen licentiesleutels gedetecteerd. Volg de licentievoorwaarden om alle functies te blijven gebruiken.", + "dismiss": "Uitschakelen", + "componentsLicenseViolation": "Licentie overtreding: Deze server gebruikt {usedSites} sites die de gelicentieerde limiet van {maxSites} sites overschrijden. Volg de licentievoorwaarden om door te gaan met het gebruik van alle functies.", + "componentsSupporterMessage": "Bedankt voor het ondersteunen van Pangolin als {tier}!", + "inviteErrorNotValid": "Het spijt ons, maar de uitnodiging die je probeert te bezoeken is niet geaccepteerd of is niet meer geldig.", + "inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.", + "inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.", + "inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.", + "inviteCreateUser": "U moet eerst een account aanmaken", + "goHome": "Ga naar huis", + "inviteLogInOtherUser": "Log in als een andere gebruiker", + "createAnAccount": "Account aanmaken", + "inviteNotAccepted": "Uitnodiging niet geaccepteerd", + "authCreateAccount": "Maak een account aan om te beginnen", + "authNoAccount": "Nog geen account?", + "email": "E-mailadres", + "password": "Wachtwoord", + "confirmPassword": "Bevestig wachtwoord", + "createAccount": "Account Aanmaken", + "viewSettings": "Instellingen weergeven", + "delete": "Verwijderen", + "name": "naam", + "online": "Online", + "offline": "Offline", + "site": "Website", + "dataIn": "Gegevens in", + "dataOut": "Data Uit", + "connectionType": "Type verbinding", + "tunnelType": "Tunnel type", + "local": "lokaal", + "edit": "Bewerken", + "siteConfirmDelete": "Verwijderen van site bevestigen", + "siteDelete": "Site verwijderen", + "siteMessageRemove": "Eenmaal verwijderd, zal de site niet langer toegankelijk zijn. Alle bronnen en doelen die aan de site zijn gekoppeld, zullen ook worden verwijderd.", + "siteMessageConfirm": "Typ ter bevestiging de naam van de site hieronder.", + "siteQuestionRemove": "Weet u zeker dat u de site {selectedSite} uit de organisatie wilt verwijderen?", + "siteManageSites": "Sites beheren", + "siteDescription": "Verbindt met uw netwerk via beveiligde tunnels", + "siteCreate": "Site maken", + "siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden", + "siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen", + "close": "Afsluiten", + "siteErrorCreate": "Fout bij maken site", + "siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden", + "siteErrorCreateDefaults": "Standaardinstellingen niet gevonden", + "siteNameDescription": "Dit is de weergavenaam van de site.", + "method": "Methode", + "siteMethodDescription": "Op deze manier legt u verbindingen bloot.", + "siteLearnNewt": "Leer hoe u Newt kunt installeren op uw systeem", + "siteSeeConfigOnce": "U kunt de configuratie maar één keer zien.", + "siteLoadWGConfig": "WireGuard configuratie wordt geladen...", + "siteDocker": "Details Docker implementatie uitvouwen", + "toggle": "Omschakelen", + "dockerCompose": "Docker opstellen", + "dockerRun": "Docker Uitvoeren", + "siteLearnLocal": "Lokale sites doen geen tunnel, leren meer", + "siteConfirmCopy": "Ik heb de configuratie gekopieerd", + "searchSitesProgress": "Sites zoeken...", + "siteAdd": "Site toevoegen", + "siteInstallNewt": "Installeer Newt", + "siteInstallNewtDescription": "Laat Newt draaien op uw systeem", + "WgConfiguration": "WireGuard Configuratie", + "WgConfigurationDescription": "Gebruik de volgende configuratie om verbinding te maken met je netwerk", + "operatingSystem": "Operating systeem", + "commands": "Opdrachten", + "recommended": "Aanbevolen", + "siteNewtDescription": "Gebruik Newt voor de beste gebruikerservaring. Het maakt gebruik van WireGuard onder de capuchon en laat je toe om contact op te nemen met je privébronnen via hun LAN-adres op je privénetwerk vanuit het Pangolin dashboard.", + "siteRunsInDocker": "Loopt in Docker", + "siteRunsInShell": "Voert in shell op macOS, Linux en Windows", + "siteErrorDelete": "Fout bij verwijderen site", + "siteErrorUpdate": "Bijwerken site mislukt", + "siteErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de site.", + "siteUpdated": "Site bijgewerkt", + "siteUpdatedDescription": "De site is bijgewerkt.", + "siteGeneralDescription": "Algemene instellingen voor deze site configureren", + "siteSettingDescription": "Configureer de instellingen op uw site", + "siteSetting": "{siteName} instellingen", + "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", + "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", + "siteWg": "Basic WireGuard", + "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", + "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", + "siteSeeAll": "Alle werkruimtes bekijken", + "siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site", + "siteNewtCredentials": "Nieuwste aanmeldgegevens", + "siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server", + "siteCredentialsSave": "Uw referenties opslaan", + "siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", + "siteInfo": "Site informatie", + "status": "status", + "shareTitle": "Beheer deellinks", + "shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen", + "shareSearch": "Zoek share links...", + "shareCreate": "Maak Share link", + "shareErrorDelete": "Kan link niet verwijderen", + "shareErrorDeleteMessage": "Fout opgetreden tijdens het verwijderen link", + "shareDeleted": "Link verwijderd", + "shareDeletedDescription": "De link is verwijderd", + "shareTokenDescription": "Uw toegangstoken kan op twee manieren worden doorgegeven: als queryparameter of in de header van de aanvraag. Deze moeten worden doorgegeven van de client op elk verzoek voor geverifieerde toegang.", + "accessToken": "Toegangs-token", + "usageExamples": "Voorbeelden van gebruik", + "tokenId": "Token ID", + "requestHeades": "Aanvraag van headers", + "queryParameter": "Query Parameter", + "importantNote": "Belangrijke opmerking", + "shareImportantDescription": "Om veiligheidsredenen wordt het gebruik van headers aanbevolen over queryparameters indien mogelijk, omdat query parameters kunnen worden aangemeld in serverlogboeken of browsergeschiedenis.", + "token": "Token", + "shareTokenSecurety": "Houd uw toegangstoken veilig. Deel deze niet in openbaar toegankelijke gebieden of client-side code.", + "shareErrorFetchResource": "Fout bij het ophalen van bronnen", + "shareErrorFetchResourceDescription": "Er is een fout opgetreden bij het ophalen van de resources", + "shareErrorCreate": "Aanmaken van link delen mislukt", + "shareErrorCreateDescription": "Fout opgetreden tijdens het maken van de share link", + "shareCreateDescription": "Iedereen met deze link heeft toegang tot de pagina", + "shareTitleOptional": "Titel (optioneel)", + "expireIn": "Vervalt in", + "neverExpire": "Nooit verlopen", + "shareExpireDescription": "Vervaltijd is hoe lang de link bruikbaar is en geeft toegang tot de bron. Na deze tijd zal de link niet meer werken en zullen gebruikers die deze link hebben gebruikt de toegang tot de pagina verliezen.", + "shareSeeOnce": "Je kunt deze koppeling alleen zien. Zorg ervoor dat je het kopieert.", + "shareAccessHint": "Iedereen met deze link heeft toegang tot de bron. Deel deze met zorg.", + "shareTokenUsage": "Zie Toegangstoken Gebruik", + "createLink": "Koppeling aanmaken", + "resourcesNotFound": "Geen bronnen gevonden", + "resourceSearch": "Zoek bronnen", + "openMenu": "Menu openen", + "resource": "Bron", + "title": "Aanspreektitel", + "created": "Aangemaakt", + "expires": "Verloopt", + "never": "Nooit", + "shareErrorSelectResource": "Selecteer een bron", + "resourceTitle": "Bronnen beheren", + "resourceDescription": "Veilige proxy's voor uw privé applicaties maken", + "resourcesSearch": "Zoek bronnen...", + "resourceAdd": "Bron toevoegen", + "resourceErrorDelte": "Fout bij verwijderen document", + "authentication": "Authenticatie", + "protected": "Beschermd", + "notProtected": "Niet beschermd", + "resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.", + "resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.", + "resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?", + "resourceHTTP": "HTTPS bron", + "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", + "resourceRaw": "Ruwe TCP/UDP bron", + "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", + "resourceCreate": "Bron maken", + "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", + "resourceSeeAll": "Alle bronnen bekijken", + "resourceInfo": "Bron informatie", + "resourceNameDescription": "Dit is de weergavenaam voor het document.", + "siteSelect": "Selecteer site", + "siteSearch": "Zoek site", + "siteNotFound": "Geen site gevonden.", + "siteSelectionDescription": "Deze site zal connectiviteit met de bron geven.", + "resourceType": "Type bron", + "resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron", + "resourceHTTPSSettings": "HTTPS instellingen", + "resourceHTTPSSettingsDescription": "Stel in hoe de bron wordt benaderd via HTTPS", + "domainType": "Domein type", + "subdomain": "Subdomein", + "baseDomain": "Basis domein", + "subdomnainDescription": "Het subdomein waar de bron toegankelijk is.", + "resourceRawSettings": "TCP/UDP instellingen", + "resourceRawSettingsDescription": "Stel in hoe je bron wordt benaderd via TCP/UDP", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", + "resourcePortNumber": "Nummer van poort", + "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", + "cancel": "annuleren", + "resourceConfig": "Configuratie tekstbouwstenen", + "resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen", + "resourceAddEntrypoints": "Traefik: Entrypoints toevoegen", + "resourceExposePorts": "Gerbild: Gevangen blootstellen in Docker Compose", + "resourceLearnRaw": "Leer hoe je TCP/UDP bronnen kunt configureren", + "resourceBack": "Terug naar bronnen", + "resourceGoTo": "Ga naar Resource", + "resourceDelete": "Document verwijderen", + "resourceDeleteConfirm": "Bevestig Verwijderen Document", + "visibility": "Zichtbaarheid", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "general": "Algemeen", + "generalSettings": "Algemene instellingen", + "proxy": "Proxy", + "rules": "Regels", + "resourceSettingDescription": "Configureer de instellingen op uw bron", + "resourceSetting": "{resourceName} instellingen", + "alwaysAllow": "Altijd toestaan", + "alwaysDeny": "Altijd weigeren", + "orgSettingsDescription": "Configureer de algemene instellingen van je organisatie", + "orgGeneralSettings": "Organisatie Instellingen", + "orgGeneralSettingsDescription": "Beheer de details en configuratie van uw organisatie", + "saveGeneralSettings": "Algemene instellingen opslaan", + "orgDangerZone": "Gevaarlijke zone", + "orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.", + "orgDelete": "Verwijder organisatie", + "orgDeleteConfirm": "Bevestig Verwijderen Organisatie", + "orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.", + "orgMessageConfirm": "Om te bevestigen, typ de naam van de onderstaande organisatie in.", + "orgQuestionRemove": "Weet u zeker dat u de organisatie {selectedOrg} wilt verwijderen?", + "orgUpdated": "Organisatie bijgewerkt", + "orgUpdatedDescription": "De organisatie is bijgewerkt.", + "orgErrorUpdate": "Bijwerken organisatie mislukt", + "orgErrorUpdateMessage": "Fout opgetreden tijdens het bijwerken van de organisatie.", + "orgErrorFetch": "Organisaties ophalen mislukt", + "orgErrorFetchMessage": "Er is een fout opgetreden tijdens het plaatsen van uw organisaties", + "orgErrorDelete": "Kan organisatie niet verwijderen", + "orgErrorDeleteMessage": "Er is een fout opgetreden tijdens het verwijderen van de organisatie.", + "orgDeleted": "Organisatie verwijderd", + "orgDeletedMessage": "De organisatie en haar gegevens zijn verwijderd.", + "orgMissing": "Organisatie-ID ontbreekt", + "orgMissingMessage": "Niet in staat om de uitnodiging te regenereren zonder organisatie-ID.", + "accessUsersManage": "Gebruikers beheren", + "accessUsersDescription": "Nodig gebruikers uit en voeg ze toe aan de rollen om toegang tot uw organisatie te beheren", + "accessUsersSearch": "Gebruikers zoeken...", + "accessUserCreate": "Gebruiker aanmaken", + "accessUserRemove": "Gebruiker verwijderen", + "username": "Gebruikersnaam", + "identityProvider": "Identiteit Provider", + "role": "Functie", + "nameRequired": "Naam is verplicht", + "accessRolesManage": "Rollen beheren", + "accessRolesDescription": "Configureer rollen om toegang tot uw organisatie te beheren", + "accessRolesSearch": "Rollen zoeken...", + "accessRolesAdd": "Rol toevoegen", + "accessRoleDelete": "Verwijder rol", + "description": "Beschrijving", + "inviteTitle": "Open uitnodigingen", + "inviteDescription": "Beheer je uitnodigingen aan andere gebruikers", + "inviteSearch": "Uitnodigingen zoeken...", + "minutes": "minuten", + "hours": "Uren", + "days": "dagen", + "weeks": "Weken", + "months": "maanden", + "years": "Jaar", + "day": "{count, plural, =1 {# dag} other {# dagen}}", + "apiKeysTitle": "API Key Informatie", + "apiKeysConfirmCopy2": "Bevestig dat u de API-sleutel hebt gekopieerd.", + "apiKeysErrorCreate": "Fout bij maken API-sleutel", + "apiKeysErrorSetPermission": "Fout instellen permissies", + "apiKeysCreate": "API-sleutel genereren", + "apiKeysCreateDescription": "Genereer een nieuwe API-sleutel voor uw organisatie", + "apiKeysGeneralSettings": "Machtigingen", + "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", + "apiKeysList": "Uw API-sleutel", + "apiKeysSave": "Uw API-sleutel opslaan", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", + "apiKeysInfo": "Uw API-sleutel is:", + "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", + "generate": "Genereren", + "done": "Voltooid", + "apiKeysSeeAll": "Alle API-sleutels bekijken", + "apiKeysPermissionsErrorLoadingActions": "Fout bij het laden van API key acties", + "apiKeysPermissionsErrorUpdate": "Fout instellen permissies", + "apiKeysPermissionsUpdated": "Permissies bijgewerkt", + "apiKeysPermissionsUpdatedDescription": "De bevoegdheden zijn bijgewerkt.", + "apiKeysPermissionsGeneralSettings": "Machtigingen", + "apiKeysPermissionsGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", + "apiKeysPermissionsSave": "Rechten opslaan", + "apiKeysPermissionsTitle": "Machtigingen", + "apiKeys": "API sleutels", + "searchApiKeys": "API-sleutels zoeken...", + "apiKeysAdd": "API-sleutel genereren", + "apiKeysErrorDelete": "Fout bij verwijderen API-sleutel", + "apiKeysErrorDeleteMessage": "Fout bij verwijderen API-sleutel", + "apiKeysQuestionRemove": "Weet u zeker dat u de API-sleutel {selectedApiKey} van de organisatie wilt verwijderen?", + "apiKeysMessageRemove": "Eenmaal verwijderd, kan de API-sleutel niet meer worden gebruikt.", + "apiKeysMessageConfirm": "Om dit te bevestigen, typt u de naam van de API-sleutel hieronder.", + "apiKeysDeleteConfirm": "Bevestig Verwijderen API-sleutel", + "apiKeysDelete": "API-sleutel verwijderen", + "apiKeysManage": "API-sleutels beheren", + "apiKeysDescription": "API-sleutels worden gebruikt om te verifiëren met de integratie-API", + "apiKeysSettings": "{apiKeyName} instellingen", + "userTitle": "Alle gebruikers beheren", + "userDescription": "Bekijk en beheer alle gebruikers in het systeem", + "userAbount": "Over gebruikersbeheer", + "userAbountDescription": "Deze tabel toont alle root user objecten in het systeem. Elke gebruiker kan tot meerdere organisaties behoren. Een gebruiker verwijderen uit een organisatie verwijdert hun root gebruiker object niet - ze zullen in het systeem blijven. Om een gebruiker volledig te verwijderen uit het systeem, moet u hun root gebruiker object verwijderen met behulp van de actie in deze tabel.", + "userServer": "Server Gebruikers", + "userSearch": "Zoek server gebruikers...", + "userErrorDelete": "Fout bij verwijderen gebruiker", + "userDeleteConfirm": "Bevestig verwijderen gebruiker", + "userDeleteServer": "Gebruiker verwijderen van de server", + "userMessageRemove": "De gebruiker zal uit alle organisaties verwijderd worden en volledig verwijderd worden van de server.", + "userMessageConfirm": "Typ de naam van de gebruiker hieronder om te bevestigen.", + "userQuestionRemove": "Weet je zeker dat je {selectedUser} permanent van de server wilt verwijderen?", + "licenseKey": "Licentie sleutel", + "valid": "Valid", + "numberOfSites": "Aantal sites", + "licenseKeySearch": "Licentiesleutels zoeken...", + "licenseKeyAdd": "Licentiesleutel toevoegen", + "type": "Type", + "licenseKeyRequired": "Licentiesleutel is vereist", + "licenseTermsAgree": "U moet akkoord gaan met de licentievoorwaarden", + "licenseErrorKeyLoad": "Laden van licentiesleutels mislukt", + "licenseErrorKeyLoadDescription": "Er is een fout opgetreden bij het laden van licentiecodes.", + "licenseErrorKeyDelete": "Licentiesleutel verwijderen mislukt", + "licenseErrorKeyDeleteDescription": "Er is een fout opgetreden bij het verwijderen van licentiesleutel.", + "licenseKeyDeleted": "Licentiesleutel verwijderd", + "licenseKeyDeletedDescription": "De licentiesleutel is verwijderd.", + "licenseErrorKeyActivate": "Licentiesleutel activeren mislukt", + "licenseErrorKeyActivateDescription": "Er is een fout opgetreden tijdens het activeren van de licentiesleutel.", + "licenseAbout": "Over licenties", + "communityEdition": "Community editie", + "licenseAboutDescription": "Dit geldt voor gebruikers van bedrijven en ondernemingen die Pangolin in gebruiken in een commerciële omgeving. Als u Pangolin gebruikt voor persoonlijk gebruik, kunt u dit gedeelte negeren.", + "licenseKeyActivated": "Licentiesleutel geactiveerd", + "licenseKeyActivatedDescription": "De licentiesleutel is geactiveerd.", + "licenseErrorKeyRecheck": "Kon licentiesleutels niet opnieuw controleren", + "licenseErrorKeyRecheckDescription": "Er is een fout opgetreden bij het opnieuw controleren van licentiecodes.", + "licenseErrorKeyRechecked": "Licentiesleutels opnieuw gecontroleerd", + "licenseErrorKeyRecheckedDescription": "Alle licentiesleutels zijn opnieuw gecontroleerd", + "licenseActivateKey": "Activeer licentiesleutel", + "licenseActivateKeyDescription": "Voer een licentiesleutel in om deze te activeren.", + "licenseActivate": "Licentie activeren", + "licenseAgreement": "Door dit selectievakje aan te vinken, bevestigt u dat u de licentievoorwaarden hebt gelezen en ermee akkoord gaat die overeenkomen met de rang die is gekoppeld aan uw licentiesleutel.", + "fossorialLicense": "Fossorial Commerciële licentie- en abonnementsvoorwaarden bekijken", + "licenseMessageRemove": "Dit zal de licentiesleutel en alle bijbehorende machtigingen verwijderen die hierdoor zijn verleend.", + "licenseMessageConfirm": "Typ de licentiesleutel hieronder om te bevestigen.", + "licenseQuestionRemove": "Weet u zeker dat u de licentiesleutel {selectedKey} wilt verwijderen?", + "licenseKeyDelete": "Licentiesleutel verwijderen", + "licenseKeyDeleteConfirm": "Bevestig verwijderen licentiesleutel", + "licenseTitle": "Licentiestatus beheren", + "licenseTitleDescription": "Bekijk en beheer licentiesleutels in het systeem", + "licenseHost": "Host Licentie", + "licenseHostDescription": "Beheer de belangrijkste licentiesleutel voor de host.", + "licensedNot": "Niet gelicentieerd", + "hostId": "Host ID", + "licenseReckeckAll": "Alle sleutels opnieuw selecteren", + "licenseSiteUsage": "Websites gebruik", + "licenseSiteUsageDecsription": "Bekijk het aantal sites dat deze licentie gebruikt.", + "licenseNoSiteLimit": "Er is geen limiet op het aantal sites dat een ongelicentieerde host gebruikt.", + "licensePurchase": "Licentie kopen", + "licensePurchaseSites": "Extra sites kopen", + "licenseSitesUsedMax": "{usedSites} van {maxSites} sites gebruikt", + "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} in het systeem.", + "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", + "licenseFee": "Licentie vergoeding", + "licensePriceSite": "Prijs per site", + "total": "Totaal", + "licenseContinuePayment": "Doorgaan naar betaling", + "pricingPage": "prijsaanduiding pagina", + "pricingPortal": "Inkoopportaal bekijken", + "licensePricingPage": "Bezoek voor de meest recente prijzen en kortingen, a.u.b. de ", + "invite": "Uitnodigingen", + "inviteRegenerate": "Uitnodiging opnieuw genereren", + "inviteRegenerateDescription": "Verwijder vorige uitnodiging en maak een nieuwe", + "inviteRemove": "Verwijder uitnodiging", + "inviteRemoveError": "Uitnodiging verwijderen mislukt", + "inviteRemoveErrorDescription": "Er is een fout opgetreden tijdens het verwijderen van de uitnodiging.", + "inviteRemoved": "Uitnodiging verwijderd", + "inviteRemovedDescription": "De uitnodiging voor {email} is verwijderd.", + "inviteQuestionRemove": "Weet u zeker dat u de uitnodiging {email} wilt verwijderen?", + "inviteMessageRemove": "Eenmaal verwijderd, zal deze uitnodiging niet meer geldig zijn. U kunt de gebruiker later altijd opnieuw uitnodigen.", + "inviteMessageConfirm": "Om dit te bevestigen, typ dan het e-mailadres van onderstaande uitnodiging.", + "inviteQuestionRegenerate": "Weet u zeker dat u de uitnodiging voor {email}opnieuw wilt genereren? Dit zal de vorige uitnodiging intrekken.", + "inviteRemoveConfirm": "Bevestig verwijderen uitnodiging", + "inviteRegenerated": "Uitnodiging opnieuw gegenereerd", + "inviteSent": "Een nieuwe uitnodiging is verstuurd naar {email}.", + "inviteSentEmail": "Stuur e-mail notificatie naar de gebruiker", + "inviteGenerate": "Er is een nieuwe uitnodiging aangemaakt voor {email}.", + "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateErrorDescription": "Er bestaat al een uitnodiging voor deze gebruiker.", + "inviteRateLimitError": "Tarief limiet overschreden", + "inviteRateLimitErrorDescription": "U hebt de limiet van 3 regeneratie per uur overschreden. Probeer het later opnieuw.", + "inviteRegenerateError": "Kan uitnodiging niet opnieuw aanmaken", + "inviteRegenerateErrorDescription": "Fout opgetreden tijdens het opnieuw genereren van de uitnodiging.", + "inviteValidityPeriod": "Geldigheid periode", + "inviteValidityPeriodSelect": "Geldigheid kiezen", + "inviteRegenerateMessage": "De uitnodiging is opnieuw gegenereerd. De gebruiker moet toegang krijgen tot de link hieronder om de uitnodiging te accepteren.", + "inviteRegenerateButton": "Hergenereren", + "expiresAt": "Verloopt op", + "accessRoleUnknown": "Onbekende rol", + "placeholder": "Plaatsaanduiding", + "userErrorOrgRemove": "Kan gebruiker niet verwijderen", + "userErrorOrgRemoveDescription": "Er is een fout opgetreden tijdens het verwijderen van de gebruiker.", + "userOrgRemoved": "Gebruiker verwijderd", + "userOrgRemovedDescription": "De gebruiker {email} is verwijderd uit de organisatie.", + "userQuestionOrgRemove": "Weet u zeker dat u {email} wilt verwijderen uit de organisatie?", + "userMessageOrgRemove": "Eenmaal verwijderd, heeft deze gebruiker geen toegang meer tot de organisatie. Je kunt ze later altijd opnieuw uitnodigen, maar ze zullen de uitnodiging opnieuw moeten accepteren.", + "userMessageOrgConfirm": "Typ om te bevestigen de naam van de gebruiker hieronder.", + "userRemoveOrgConfirm": "Bevestig verwijderen gebruiker", + "userRemoveOrg": "Gebruiker uit organisatie verwijderen", + "users": "Gebruikers", + "accessRoleMember": "Lid", + "accessRoleOwner": "Eigenaar", + "userConfirmed": "Bevestigd", + "idpNameInternal": "Intern", + "emailInvalid": "Ongeldig e-mailadres", + "inviteValidityDuration": "Selecteer een tijdsduur", + "accessRoleSelectPlease": "Selecteer een rol", + "usernameRequired": "Gebruikersnaam is verplicht", + "idpSelectPlease": "Selecteer een identiteitsprovider", + "idpGenericOidc": "Algemene OAuth2/OIDC provider.", + "accessRoleErrorFetch": "Rollen ophalen mislukt", + "accessRoleErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de rollen", + "idpErrorFetch": "Kan identiteitsaanbieders niet ophalen", + "idpErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van identiteitsproviders", + "userErrorExists": "Gebruiker bestaat al", + "userErrorExistsDescription": "Deze gebruiker is al lid van de organisatie.", + "inviteError": "Uitnodigen van gebruiker mislukt", + "inviteErrorDescription": "Er is een fout opgetreden tijdens het uitnodigen van de gebruiker", + "userInvited": "Gebruiker uitgenodigd", + "userInvitedDescription": "De gebruiker is succesvol uitgenodigd.", + "userErrorCreate": "Gebruiker aanmaken mislukt", + "userErrorCreateDescription": "Fout opgetreden tijdens het aanmaken van de gebruiker", + "userCreated": "Gebruiker aangemaakt", + "userCreatedDescription": "De gebruiker is succesvol aangemaakt.", + "userTypeInternal": "Interne gebruiker", + "userTypeInternalDescription": "Nodig een gebruiker uit om direct lid te worden van je organisatie.", + "userTypeExternal": "Externe gebruiker", + "userTypeExternalDescription": "Maak een gebruiker aan met een externe identiteitsprovider.", + "accessUserCreateDescription": "Volg de onderstaande stappen om een nieuwe gebruiker te maken", + "userSeeAll": "Alle gebruikers bekijken", + "userTypeTitle": "Type gebruiker", + "userTypeDescription": "Bepaal hoe u de gebruiker wilt aanmaken", + "userSettings": "Gebruikers informatie", + "userSettingsDescription": "Voer de gegevens van de nieuwe gebruiker in", + "inviteEmailSent": "Stuur uitnodigingsmail naar de gebruiker", + "inviteValid": "Geldig voor", + "selectDuration": "Selecteer duur", + "accessRoleSelect": "Selecteer rol", + "inviteEmailSentDescription": "Een e-mail is verstuurd naar de gebruiker met de link hieronder. Ze moeten toegang krijgen tot de link om de uitnodiging te accepteren.", + "inviteSentDescription": "De gebruiker is uitgenodigd. Ze moeten toegang krijgen tot de link hieronder om de uitnodiging te accepteren.", + "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", + "idpTitle": "Identiteit Provider", + "idpSelect": "Identiteitsprovider voor de externe gebruiker selecteren", + "idpNotConfigured": "Er zijn geen identiteitsproviders geconfigureerd. Configureer een identiteitsprovider voordat u externe gebruikers aanmaakt.", + "usernameUniq": "Dit moet overeenkomen met de unieke gebruikersnaam die bestaat in de geselecteerde identiteitsprovider.", + "emailOptional": "E-mailadres (optioneel)", + "nameOptional": "Naam (optioneel)", + "accessControls": "Toegang Bediening", + "userDescription2": "Beheer de instellingen van deze gebruiker", + "accessRoleErrorAdd": "Gebruiker aan rol toevoegen mislukt", + "accessRoleErrorAddDescription": "Er is een fout opgetreden tijdens het toevoegen van de rol.", + "userSaved": "Gebruiker opgeslagen", + "userSavedDescription": "De gebruiker is bijgewerkt.", + "accessControlsDescription": "Beheer wat deze gebruiker toegang heeft tot en doet in de organisatie", + "accessControlsSubmit": "Bewaar Toegangsbesturing", + "roles": "Rollen", + "accessUsersRoles": "Beheer Gebruikers & Rollen", + "accessUsersRolesDescription": "Nodig gebruikers uit en voeg ze toe aan de rollen om toegang tot uw organisatie te beheren", + "key": "Sleutel", + "createdAt": "Aangemaakt op", + "proxyErrorInvalidHeader": "Ongeldige aangepaste Header waarde. Gebruik het domeinnaam formaat, of sla leeg op om de aangepaste Host header ongedaan te maken.", + "proxyErrorTls": "Ongeldige TLS servernaam. Gebruik de domeinnaam of sla leeg op om de TLS servernaam te verwijderen.", + "proxyEnableSSL": "SSL (https) inschakelen", + "targetErrorFetch": "Ophalen van doelen mislukt", + "targetErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de objecten", + "siteErrorFetch": "Mislukt om resource op te halen", + "siteErrorFetchDescription": "Er is een fout opgetreden tijdens het ophalen van het document", + "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicateDescription": "Een doel met deze instellingen bestaat al", + "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIpDescription": "Doel IP moet binnen de site subnet zijn", + "targetsUpdated": "Doelstellingen bijgewerkt", + "targetsUpdatedDescription": "Doelstellingen en instellingen succesvol bijgewerkt", + "targetsErrorUpdate": "Kan doelen niet bijwerken", + "targetsErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de doelen", + "targetTlsUpdate": "TLS instellingen bijgewerkt", + "targetTlsUpdateDescription": "Uw TLS instellingen zijn succesvol bijgewerkt", + "targetErrorTlsUpdate": "Bijwerken van TLS instellingen mislukt", + "targetErrorTlsUpdateDescription": "Fout opgetreden tijdens het bijwerken van de TLS-instellingen", + "proxyUpdated": "Proxyinstellingen bijgewerkt", + "proxyUpdatedDescription": "Uw proxyinstellingen zijn succesvol bijgewerkt", + "proxyErrorUpdate": "Bijwerken van proxy-instellingen mislukt", + "proxyErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de proxy-instellingen", + "targetAddr": "IP / Hostname", + "targetPort": "Poort", + "targetProtocol": "Protocol", + "targetTlsSettings": "HTTPS & TLS instellingen", + "targetTlsSettingsDescription": "Configureer TLS instellingen voor jouw bron", + "targetTlsSettingsAdvanced": "Geavanceerde TLS instellingen", + "targetTlsSni": "TLS Server Naam (SNI)", + "targetTlsSniDescription": "De TLS servernaam om te gebruiken voor SNI. Laat leeg om de standaard te gebruiken.", + "targetTlsSubmit": "Instellingen opslaan", + "targets": "Doelstellingen configuratie", + "targetsDescription": "Stel doelen in om verkeer naar uw diensten te leiden", + "targetStickySessions": "Sticky sessies inschakelen", + "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", + "methodSelect": "Selecteer methode", + "targetSubmit": "Add Target", + "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", + "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", + "targetsSubmit": "Doelstellingen opslaan", + "proxyAdditional": "Extra Proxy-instellingen", + "proxyAdditionalDescription": "Configureer hoe de proxy-instellingen van uw bron worden afgehandeld", + "proxyCustomHeader": "Aangepaste Host-header", + "proxyCustomHeaderDescription": "De hostkop om in te stellen bij proxying verzoeken. Laat leeg om de standaard te gebruiken.", + "proxyAdditionalSubmit": "Proxyinstellingen opslaan", + "subnetMaskErrorInvalid": "Ongeldig subnet masker. Moet tussen 0 en 32 zijn.", + "ipAddressErrorInvalidFormat": "Ongeldig IP-adresformaat", + "ipAddressErrorInvalidOctet": "Ongeldige IP adres octet", + "path": "Pad", + "ipAddressRange": "IP Bereik", + "rulesErrorFetch": "Regels ophalen mislukt", + "rulesErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de regels", + "rulesErrorDuplicate": "Dupliceer regel", + "rulesErrorDuplicateDescription": "Een regel met deze instellingen bestaat al", + "rulesErrorInvalidIpAddressRange": "Ongeldige CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "Voer een geldige CIDR waarde in", + "rulesErrorInvalidUrl": "Ongeldige URL pad", + "rulesErrorInvalidUrlDescription": "Voer een geldige URL padwaarde in", + "rulesErrorInvalidIpAddress": "Ongeldig IP", + "rulesErrorInvalidIpAddressDescription": "Voer een geldig IP-adres in", + "rulesErrorUpdate": "Regels bijwerken mislukt", + "rulesErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de regels", + "rulesUpdated": "Regels inschakelen", + "rulesUpdatedDescription": "Regel evaluatie is bijgewerkt", + "rulesMatchIpAddressRangeDescription": "Voer een adres in in het CIDR-formaat (bijv. 103.21.244.0/22)", + "rulesMatchIpAddress": "Voer een IP-adres in (bijv. 103.21.244.12)", + "rulesMatchUrl": "Voer een URL-pad of patroon in (bijv. /api/v1/todos of /api/v1/*)", + "rulesErrorInvalidPriority": "Ongeldige prioriteit", + "rulesErrorInvalidPriorityDescription": "Voer een geldige prioriteit in", + "rulesErrorDuplicatePriority": "Dubbele prioriteiten", + "rulesErrorDuplicatePriorityDescription": "Voer unieke prioriteiten in", + "ruleUpdated": "Regels bijgewerkt", + "ruleUpdatedDescription": "Regels met succes bijgewerkt", + "ruleErrorUpdate": "Bewerking mislukt", + "ruleErrorUpdateDescription": "Er is een fout opgetreden tijdens het opslaan", + "rulesPriority": "Prioriteit", + "rulesAction": "actie", + "rulesMatchType": "Wedstrijd Type", + "value": "Waarde", + "rulesAbout": "Over regels", + "rulesAboutDescription": "Regels stellen u in staat om de toegang tot uw bron te controleren op basis van een aantal criteria. U kunt regels maken om toegang te toestaan of weigeren op basis van IP-adres of URL pad.", + "rulesActions": "acties", + "rulesActionAlwaysAllow": "Altijd toegestaan: Omzeil alle authenticatiemethoden", + "rulesActionAlwaysDeny": "Altijd weigeren: Blokkeer alle aanvragen, er kan geen verificatie worden geprobeerd", + "rulesMatchCriteria": "Overeenkomende criteria", + "rulesMatchCriteriaIpAddress": "Overeenkomen met een specifiek IP-adres", + "rulesMatchCriteriaIpAddressRange": "Overeenkomen met een bereik van IP-adressen in de CIDR-notatie", + "rulesMatchCriteriaUrl": "Koppel een URL-pad of patroon", + "rulesEnable": "Regels inschakelen", + "rulesEnableDescription": "In- of uitschakelen van regelevaluatie voor deze bron", + "rulesResource": "Configuratie Resource Regels", + "rulesResourceDescription": "Regels instellen om toegang tot uw bron te beheren", + "ruleSubmit": "Regel toevoegen", + "rulesNoOne": "Geen regels. Voeg een regel toe via het formulier.", + "rulesOrder": "Regels worden in oplopende volgorde volgens prioriteit beoordeeld.", + "rulesSubmit": "Regels opslaan", + "resourceErrorCreate": "Fout bij maken document", + "resourceErrorCreateDescription": "Er is een fout opgetreden bij het maken van het document", + "resourceErrorCreateMessage": "Fout bij maken bron:", + "resourceErrorCreateMessageDescription": "Er is een onverwachte fout opgetreden", + "sitesErrorFetch": "Fout bij ophalen sites", + "sitesErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de sites", + "domainsErrorFetch": "Fout bij ophalen domeinen", + "domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen", + "none": "geen", + "unknown": "onbekend", + "resources": "Hulpmiddelen", + "resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.", + "resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling", + "resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren", + "resourcesUsersRolesAccess": "Gebruiker en rol-gebaseerde toegangsbeheer", + "resourcesErrorUpdate": "Bron wisselen mislukt", + "resourcesErrorUpdateDescription": "Er is een fout opgetreden tijdens het bijwerken van het document", + "access": "Toegangsrechten", + "shareLink": "{resource} Share link", + "resourceSelect": "Selecteer resource", + "shareLinks": "Links delen", + "share": "Deelbare links", + "shareDescription2": "Maak deelbare links naar uw bronnen. Links bieden tijdelijke of onbeperkte toegang tot uw bron. U kunt de vervalduur van de link configureren wanneer u er een aanmaakt.", + "shareEasyCreate": "Makkelijk te maken en te delen", + "shareConfigurableExpirationDuration": "Configureerbare vervalduur", + "shareSecureAndRevocable": "Veilig en herroepbaar", + "nameMin": "De naam moet minstens {len} tekens bevatten.", + "nameMax": "Naam mag niet langer zijn dan {len} tekens.", + "sitesConfirmCopy": "Bevestig dat u de configuratie hebt gekopieerd.", + "unknownCommand": "Onbekende opdracht", + "newtErrorFetchReleases": "Kan release-informatie niet ophalen: {err}", + "newtErrorFetchLatest": "Fout bij ophalen van laatste release: {err}", + "newtEndpoint": "Newt Endpoint", + "newtId": "Newt ID", + "newtSecretKey": "Nieuwe geheime sleutel", + "architecture": "Architectuur", + "sites": "Werkruimtes", + "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", + "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", + "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", + "userErrorNotAdminOrOwner": "Gebruiker is geen beheerder of eigenaar", + "pangolinSettings": "Instellingen - Pangolin", + "accessRoleYour": "Jouw rol:", + "accessRoleSelect2": "Selecteer lidmaatschap", + "accessUserSelect": "Selecteer een gebruiker", + "otpEmailEnter": "Voer e-mailadres in", + "otpEmailEnterDescription": "Druk op enter om een e-mail toe te voegen na het typen in het invoerveld.", + "otpEmailErrorInvalid": "Ongeldig e-mailadres. Wildcard (*) moet het hele lokale deel zijn.", + "otpEmailSmtpRequired": "SMTP vereist", + "otpEmailSmtpRequiredDescription": "SMTP moet ingeschakeld zijn op de server om eenmalige wachtwoordauthenticatie te gebruiken.", + "otpEmailTitle": "Eenmalige wachtwoorden", + "otpEmailTitleDescription": "Vereis e-mailgebaseerde authenticatie voor brontoegang", + "otpEmailWhitelist": "E-mail whitelist", + "otpEmailWhitelistList": "Toegestane e-mails", + "otpEmailWhitelistListDescription": "Alleen gebruikers met deze e-mailadressen hebben toegang tot dit document. Ze zullen worden gevraagd om een eenmalig wachtwoord in te voeren dat naar hun e-mail is verzonden. Wildcards (*@example.com) kunnen worden gebruikt om elk e-mailadres van een domein toe te staan.", + "otpEmailWhitelistSave": "Whitelist opslaan", + "passwordAdd": "Wachtwoord toevoegen", + "passwordRemove": "Wachtwoord verwijderen", + "pincodeAdd": "PIN-code toevoegen", + "pincodeRemove": "PIN-code verwijderen", + "resourceAuthMethods": "Authenticatie methoden", + "resourceAuthMethodsDescriptions": "Sta toegang tot de bron toe via extra autorisatiemethoden", + "resourceAuthSettingsSave": "Succesvol opgeslagen", + "resourceAuthSettingsSaveDescription": "Verificatie-instellingen zijn opgeslagen", + "resourceErrorAuthFetch": "Gegevens ophalen mislukt", + "resourceErrorAuthFetchDescription": "Er is een fout opgetreden bij het ophalen van de gegevens", + "resourceErrorPasswordRemove": "Fout bij verwijderen resource wachtwoord", + "resourceErrorPasswordRemoveDescription": "Er is een fout opgetreden tijdens het verwijderen van het bronwachtwoord", + "resourceErrorPasswordSetup": "Fout bij instellen resource wachtwoord", + "resourceErrorPasswordSetupDescription": "Er is een fout opgetreden bij het instellen van het wachtwoord bron", + "resourceErrorPincodeRemove": "Fout bij verwijderen resource pincode", + "resourceErrorPincodeRemoveDescription": "Er is een fout opgetreden tijdens het verwijderen van de bronpincode", + "resourceErrorPincodeSetup": "Fout bij instellen resource PIN code", + "resourceErrorPincodeSetupDescription": "Er is een fout opgetreden bij het instellen van de PIN-code van de bron", + "resourceErrorUsersRolesSave": "Kan rollen niet instellen", + "resourceErrorUsersRolesSaveDescription": "Er is een fout opgetreden tijdens het instellen van de rollen", + "resourceErrorWhitelistSave": "Kan whitelist niet opslaan", + "resourceErrorWhitelistSaveDescription": "Er is een fout opgetreden tijdens het opslaan van de whitelist", + "resourcePasswordSubmit": "Wachtwoordbescherming inschakelen", + "resourcePasswordProtection": "Wachtwoordbescherming {status}", + "resourcePasswordRemove": "Wachtwoord document verwijderd", + "resourcePasswordRemoveDescription": "Het wachtwoord van de resource is met succes verwijderd", + "resourcePasswordSetup": "Wachtwoord document ingesteld", + "resourcePasswordSetupDescription": "Het wachtwoord voor de bron is succesvol ingesteld", + "resourcePasswordSetupTitle": "Wachtwoord instellen", + "resourcePasswordSetupTitleDescription": "Stel een wachtwoord in om deze bron te beschermen", + "resourcePincode": "PIN Code", + "resourcePincodeSubmit": "PIN-Code bescherming inschakelen", + "resourcePincodeProtection": "PIN Code bescherming {status}", + "resourcePincodeRemove": "Pijncode van resource verwijderd", + "resourcePincodeRemoveDescription": "Het wachtwoord van de resource is met succes verwijderd", + "resourcePincodeSetup": "PIN-code voor hulpbron ingesteld", + "resourcePincodeSetupDescription": "De bronpincode is succesvol ingesteld", + "resourcePincodeSetupTitle": "Pincode instellen", + "resourcePincodeSetupTitleDescription": "Stel een pincode in om deze hulpbron te beschermen", + "resourceRoleDescription": "Beheerders hebben altijd toegang tot deze bron.", + "resourceUsersRoles": "Gebruikers & Rollen", + "resourceUsersRolesDescription": "Configureer welke gebruikers en rollen deze pagina kunnen bezoeken", + "resourceUsersRolesSubmit": "Gebruikers opslaan & rollen", + "resourceWhitelistSave": "Succesvol opgeslagen", + "resourceWhitelistSaveDescription": "Whitelist instellingen zijn opgeslagen", + "ssoUse": "Gebruik Platform SSO", + "ssoUseDescription": "Bestaande gebruikers hoeven slechts eenmaal in te loggen voor alle bronnen die dit ingeschakeld hebben.", + "proxyErrorInvalidPort": "Ongeldig poortnummer", + "subdomainErrorInvalid": "Ongeldig subdomein", + "domainErrorFetch": "Fout bij ophalen domeinen", + "domainErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen", + "resourceErrorUpdate": "Bijwerken van resource mislukt", + "resourceErrorUpdateDescription": "Er is een fout opgetreden tijdens het bijwerken van het document", + "resourceUpdated": "Bron bijgewerkt", + "resourceUpdatedDescription": "Het document is met succes bijgewerkt", + "resourceErrorTransfer": "Mislukt om resource over te dragen", + "resourceErrorTransferDescription": "Er is een fout opgetreden tijdens het overzetten van het document", + "resourceTransferred": "Bron overgedragen", + "resourceTransferredDescription": "De bron is met succes overgedragen.", + "resourceErrorToggle": "Bron wisselen mislukt", + "resourceErrorToggleDescription": "Er is een fout opgetreden tijdens het bijwerken van het document", + "resourceVisibilityTitle": "Zichtbaarheid", + "resourceVisibilityTitleDescription": "Zichtbaarheid van bestanden volledig in- of uitschakelen", + "resourceGeneral": "Algemene instellingen", + "resourceGeneralDescription": "Configureer de algemene instellingen voor deze bron", + "resourceEnable": "Resource inschakelen", + "resourceTransfer": "Bronnen overdragen", + "resourceTransferDescription": "Verplaats dit document naar een andere site", + "resourceTransferSubmit": "Bronnen overdragen", + "siteDestination": "Bestemming site", + "searchSites": "Sites zoeken", + "accessRoleCreate": "Rol aanmaken", + "accessRoleCreateDescription": "Maak een nieuwe rol aan om gebruikers te groeperen en hun rechten te beheren.", + "accessRoleCreateSubmit": "Rol aanmaken", + "accessRoleCreated": "Rol aangemaakt", + "accessRoleCreatedDescription": "De rol is succesvol aangemaakt.", + "accessRoleErrorCreate": "Rol aanmaken mislukt", + "accessRoleErrorCreateDescription": "Fout opgetreden tijdens het aanmaken van de rol.", + "accessRoleErrorNewRequired": "Nieuwe rol is vereist", + "accessRoleErrorRemove": "Rol verwijderen mislukt", + "accessRoleErrorRemoveDescription": "Er is een fout opgetreden tijdens het verwijderen van de rol.", + "accessRoleName": "Rol naam", + "accessRoleQuestionRemove": "U staat op het punt de {name} rol te verwijderen. U kunt deze actie niet ongedaan maken.", + "accessRoleRemove": "Rol verwijderen", + "accessRoleRemoveDescription": "Verwijder een rol van de organisatie", + "accessRoleRemoveSubmit": "Rol verwijderen", + "accessRoleRemoved": "Rol verwijderd", + "accessRoleRemovedDescription": "De rol is succesvol verwijderd.", + "accessRoleRequiredRemove": "Voordat u deze rol verwijdert, selecteer een nieuwe rol om bestaande leden aan te dragen.", + "manage": "Beheren", + "sitesNotFound": "Geen sites gevonden.", + "pangolinServerAdmin": "Server Admin - Pangolin", + "licenseTierProfessional": "Professionele licentie", + "licenseTierEnterprise": "Enterprise Licentie", + "licenseTierCommercial": "Commerciële licentie", + "licensed": "Gelicentieerd", + "yes": "ja", + "no": "Neen", + "sitesAdditional": "Extra sites", + "licenseKeys": "Licentie Sleutels", + "sitestCountDecrease": "Verlaag het aantal sites", + "sitestCountIncrease": "Toename van site vergroten", + "idpManage": "Identiteitsaanbieders beheren", + "idpManageDescription": "Identiteitsaanbieders in het systeem bekijken en beheren", + "idpDeletedDescription": "Identity provider succesvol verwijderd", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?", + "idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.", + "idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.", + "idpConfirmDelete": "Bevestig verwijderen Identity Provider", + "idpDelete": "Identity Provider verwijderen", + "idp": "Identiteit aanbieders", + "idpSearch": "Identiteitsaanbieders zoeken...", + "idpAdd": "Identity Provider toevoegen", + "idpClientIdRequired": "Client-ID is vereist.", + "idpClientSecretRequired": "Clientgeheim is vereist.", + "idpErrorAuthUrlInvalid": "Authenticatie-URL moet een geldige URL zijn.", + "idpErrorTokenUrlInvalid": "Token-URL moet een geldige URL zijn.", + "idpPathRequired": "ID-pad is vereist.", + "idpScopeRequired": "Toepassingsgebieden zijn vereist.", + "idpOidcDescription": "Een OpenID Connect identity provider configureren", + "idpCreatedDescription": "Identity provider succesvol aangemaakt", + "idpCreate": "Identity Provider aanmaken", + "idpCreateDescription": "Een nieuwe identiteitsprovider voor gebruikersauthenticatie configureren", + "idpSeeAll": "Zie alle identiteitsaanbieders", + "idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider", + "idpDisplayName": "Een weergavenaam voor deze identiteitsprovider", + "idpAutoProvisionUsers": "Auto Provisie Gebruikers", + "idpAutoProvisionUsersDescription": "Wanneer ingeschakeld, worden gebruikers automatisch in het systeem aangemaakt wanneer ze de eerste keer inloggen met de mogelijkheid om gebruikers toe te wijzen aan rollen en organisaties.", + "licenseBadge": "Professioneel", + "idpType": "Type provider", + "idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren", + "idpOidcConfigure": "OAuth2/OIDC configuratie", + "idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties", + "idpClientId": "Klant ID", + "idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider", + "idpClientSecret": "Client Secret", + "idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider", + "idpAuthUrl": "URL autorisatie", + "idpAuthUrlDescription": "De URL voor autorisatie OAuth2", + "idpTokenUrl": "URL token", + "idpTokenUrlDescription": "De URL van het OAuth2 token eindpunt", + "idpOidcConfigureAlert": "Belangrijke informatie", + "idpOidcConfigureAlertDescription": "Na het aanmaken van de identity provider moet u de callback URL configureren in de instellingen van uw identity provider. De callback URL zal worden opgegeven na het succesvol aanmaken.", + "idpToken": "Token configuratie", + "idpTokenDescription": "Stel in hoe gebruikersgegevens uit het ID token uit te pakken", + "idpJmespathAbout": "Over JMESPath", + "idpJmespathAboutDescription": "De onderstaande paden gebruiken JMESPath syntaxis om waarden van de ID-token te extraheren.", + "idpJmespathAboutDescriptionLink": "Meer informatie over JMESPath", + "idpJmespathLabel": "ID pad", + "idpJmespathLabelDescription": "Het pad naar het gebruiker-id in het ID-token", + "idpJmespathEmailPathOptional": "E-mail pad (optioneel)", + "idpJmespathEmailPathOptionalDescription": "Het pad naar het e-mailadres van de gebruiker in het ID-token", + "idpJmespathNamePathOptional": "Naam pad (optioneel)", + "idpJmespathNamePathOptionalDescription": "Het pad naar de naam van de gebruiker in de ID-token", + "idpOidcConfigureScopes": "Toepassingsgebieden", + "idpOidcConfigureScopesDescription": "Te vragen ruimtescheiden lijst van OAuth2 toepassingsgebieden", + "idpSubmit": "Identity Provider aanmaken", + "orgPolicies": "Organisatie beleid", + "idpSettings": "{idpName} instellingen", + "idpCreateSettingsDescription": "Configureer de instellingen voor uw identiteitsprovider", + "roleMapping": "Rol Toewijzing", + "orgMapping": "Organisatie toewijzing", + "orgPoliciesSearch": "Zoek het organisatiebeleid...", + "orgPoliciesAdd": "Organisatiebeleid toevoegen", + "orgRequired": "Organisatie is vereist", + "error": "Foutmelding", + "success": "Geslaagd", + "orgPolicyAddedDescription": "Beleid succesvol toegevoegd", + "orgPolicyUpdatedDescription": "Beleid succesvol bijgewerkt", + "orgPolicyDeletedDescription": "Beleid succesvol verwijderd", + "defaultMappingsUpdatedDescription": "Standaard toewijzingen met succes bijgewerkt", + "orgPoliciesAbout": "Over organisatiebeleid", + "orgPoliciesAboutDescription": "Organisatiebeleid wordt gebruikt om toegang tot organisaties te beheren op basis van de gebruiker-ID-token. U kunt JMESPath expressies opgeven om rol en organisatie informatie van de ID-token te extraheren.", + "orgPoliciesAboutDescriptionLink": "Zie documentatie, voor meer informatie.", + "defaultMappingsOptional": "Standaard toewijzingen (optioneel)", + "defaultMappingsOptionalDescription": "De standaard toewijzingen worden gebruikt wanneer er geen organisatiebeleid is gedefinieerd voor een organisatie. Je kunt de standaard rol en organisatietoewijzingen opgeven waar je naar terug kunt vallen.", + "defaultMappingsRole": "Standaard Rol Toewijzing", + "defaultMappingsRoleDescription": "Het resultaat van deze uitdrukking moet de rolnaam zoals gedefinieerd in de organisatie als tekenreeks teruggeven.", + "defaultMappingsOrg": "Standaard organisatie mapping", + "defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.", + "defaultMappingsSubmit": "Standaard toewijzingen opslaan", + "orgPoliciesEdit": "Organisatie beleid bewerken", + "org": "Rekening", + "orgSelect": "Selecteer organisatie", + "orgSearch": "Zoek in org", + "orgNotFound": "Geen org gevonden.", + "roleMappingPathOptional": "Rol toewijzing pad (optioneel)", + "orgMappingPathOptional": "Organisatie mapping pad (optioneel)", + "orgPolicyUpdate": "Update beleid", + "orgPolicyAdd": "Beleid toevoegen", + "orgPolicyConfig": "Toegang voor een organisatie configureren", + "idpUpdatedDescription": "Identity provider succesvol bijgewerkt", + "redirectUrl": "Omleidings URL", + "redirectUrlAbout": "Over omleidings-URL", + "redirectUrlAboutDescription": "Dit is de URL waarnaar gebruikers worden doorverwezen na verificatie. U moet deze URL configureren in uw identiteitsprovider-instellingen.", + "pangolinAuth": "Authenticatie - Pangolin", + "verificationCodeLengthRequirements": "Je verificatiecode moet 8 tekens bevatten.", + "errorOccurred": "Er is een fout opgetreden", + "emailErrorVerify": "E-mail verifiëren is mislukt:", + "emailVerified": "E-mail met succes geverifieerd! Doorsturen naar u...", + "verificationCodeErrorResend": "Fout bij het opnieuw verzenden van de verificatiecode:", + "verificationCodeResend": "Verificatiecode opnieuw verzonden", + "verificationCodeResendDescription": "We hebben een verificatiecode opnieuw naar je e-mailadres gestuurd. Controleer je inbox.", + "emailVerify": "Bevestig e-mailadres", + "emailVerifyDescription": "Voer de verificatiecode in die naar uw e-mailadres is verzonden.", + "verificationCode": "Verificatie Code", + "verificationCodeEmailSent": "We hebben een verificatiecode naar je e-mailadres gestuurd.", + "submit": "Bevestigen", + "emailVerifyResendProgress": "Opnieuw verzenden...", + "emailVerifyResend": "Geen code ontvangen? Klik hier om opnieuw te verzenden", + "passwordNotMatch": "Wachtwoorden komen niet overeen", + "signupError": "Er is een fout opgetreden tijdens het aanmelden", + "pangolinLogoAlt": "Pangolin logo", + "inviteAlready": "Het lijkt erop dat je bent uitgenodigd!", + "inviteAlreadyDescription": "Om de uitnodiging te accepteren, moet je inloggen of een account aanmaken.", + "signupQuestion": "Heeft u al een account?", + "login": "Log in", + "resourceNotFound": "Bron niet gevonden", + "resourceNotFoundDescription": "De bron die u probeert te benaderen bestaat niet.", + "pincodeRequirementsLength": "Pincode moet precies 6 cijfers zijn", + "pincodeRequirementsChars": "Pincode mag alleen cijfers bevatten", + "passwordRequirementsLength": "Wachtwoord moet ten minste 1 teken lang zijn", + "otpEmailRequirementsLength": "OTP moet minstens 1 teken lang zijn", + "otpEmailSent": "OTP verzonden", + "otpEmailSentDescription": "Een OTP is naar uw e-mail verzonden", + "otpEmailErrorAuthenticate": "Authenticatie met e-mail mislukt", + "pincodeErrorAuthenticate": "Authenticatie met pincode mislukt", + "passwordErrorAuthenticate": "Authenticatie met wachtwoord mislukt", + "poweredBy": "Mogelijk gemaakt door", + "authenticationRequired": "Authenticatie vereist", + "authenticationMethodChoose": "Kies uw voorkeursmethode voor toegang tot {name}", + "authenticationRequest": "U moet zich aanmelden om {name} te kunnen gebruiken", + "user": "Gebruiker", + "pincodeInput": "6-cijferige PIN-Code", + "pincodeSubmit": "Inloggen met PIN", + "passwordSubmit": "Log in met wachtwoord", + "otpEmailDescription": "Een eenmalige code zal worden verzonden naar deze e-mail.", + "otpEmailSend": "Verstuur éénmalige code", + "otpEmail": "Eenmalig wachtwoord (OTP)", + "otpEmailSubmit": "OTP inzenden", + "backToEmail": "Terug naar E-mail", + "noSupportKey": "Server draait zonder een supporter sleutel. Overweeg het project te ondersteunen!", + "accessDenied": "Toegang geweigerd", + "accessDeniedDescription": "U heeft geen toegang tot deze resource. Als dit een vergissing is, neem dan contact op met de beheerder.", + "accessTokenError": "Fout bij controleren toegangstoken", + "accessGranted": "Toegang verleend", + "accessUrlInvalid": "URL ongeldig", + "accessGrantedDescription": "Er is u toegang verleend tot deze resource. U wordt doorgestuurd...", + "accessUrlInvalidDescription": "Deze URL voor gedeelde toegang is ongeldig. Neem contact op met de documenteigenaar voor een nieuwe URL.", + "tokenInvalid": "Ongeldig token", + "pincodeInvalid": "Ongeldige code", + "passwordErrorRequestReset": "Verzoek om resetten mislukt:", + "passwordErrorReset": "Wachtwoord opnieuw instellen mislukt:", + "passwordResetSuccess": "Wachtwoord succesvol gereset! Terug naar inloggen...", + "passwordReset": "Wachtwoord opnieuw instellen", + "passwordResetDescription": "Volg de stappen om uw wachtwoord opnieuw in te stellen", + "passwordResetSent": "We sturen een wachtwoord reset code naar dit e-mailadres.", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "Controleer je e-mail voor de reset code.", + "passwordNew": "Nieuw wachtwoord", + "passwordNewConfirm": "Bevestig nieuw wachtwoord", + "pincodeAuth": "Authenticator Code", + "pincodeSubmit2": "Code indienen", + "passwordResetSubmit": "Opnieuw instellen aanvragen", + "passwordBack": "Terug naar wachtwoord", + "loginBack": "Ga terug naar login", + "signup": "Registreer nu", + "loginStart": "Log in om te beginnen", + "idpOidcTokenValidating": "Valideer OIDC-token", + "idpOidcTokenResponse": "Valideer OIDC token antwoord", + "idpErrorOidcTokenValidating": "Fout bij valideren OIDC-token", + "idpConnectingTo": "Verbinden met {name}", + "idpConnectingToDescription": "Uw identiteit bevestigen", + "idpConnectingToProcess": "Verbinden...", + "idpConnectingToFinished": "Verbonden", + "idpErrorConnectingTo": "Er was een probleem bij het verbinden met {name}. Neem contact op met uw beheerder.", + "idpErrorNotFound": "IdP niet gevonden", + "inviteInvalid": "Ongeldige uitnodiging", + "inviteInvalidDescription": "Uitnodigingslink is ongeldig.", + "inviteErrorWrongUser": "Uitnodiging is niet voor deze gebruiker", + "inviteErrorUserNotExists": "Gebruiker bestaat niet. Maak eerst een account aan.", + "inviteErrorLoginRequired": "Je moet ingelogd zijn om een uitnodiging te accepteren", + "inviteErrorExpired": "De uitnodiging is mogelijk verlopen", + "inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken", + "inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink", + "pangolinSetup": "Setup - Pangolin", + "orgNameRequired": "Organisatienaam is vereist", + "orgIdRequired": "Organisatie-ID is vereist", + "orgErrorCreate": "Fout opgetreden tijdens het aanmaken org", + "pageNotFound": "Pagina niet gevonden", + "pageNotFoundDescription": "Oeps! De pagina die je zoekt bestaat niet.", + "overview": "Overzicht.", + "home": "Startpagina", + "accessControl": "Toegangs controle", + "settings": "Instellingen", + "usersAll": "Alle gebruikers", + "license": "Licentie", + "pangolinDashboard": "Dashboard - Pangolin", + "noResults": "Geen resultaten gevonden.", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "Ingevoerde tags", + "tagsEnteredDescription": "Dit zijn de tags die u hebt ingevoerd.", + "tagsWarnCannotBeLessThanZero": "maxTags en minTags kunnen niet minder dan 0 zijn", + "tagsWarnNotAllowedAutocompleteOptions": "Tag niet toegestaan als per autocomplete opties", + "tagsWarnInvalid": "Ongeldige tag per validateTag", + "tagWarnTooShort": "Tag {tagText} is te kort", + "tagWarnTooLong": "Tag {tagText} is te lang", + "tagsWarnReachedMaxNumber": "Het maximum aantal toegestane tags bereikt", + "tagWarnDuplicate": "Dubbele tag {tagText} niet toegevoegd", + "supportKeyInvalid": "Ongeldige sleutel", + "supportKeyInvalidDescription": "Je supporter sleutel is ongeldig.", + "supportKeyValid": "Valid Key", + "supportKeyValidDescription": "Uw supporter sleutel is gevalideerd. Bedankt voor uw steun!", + "supportKeyErrorValidationDescription": "Niet gelukt om de supportersleutel te valideren.", + "supportKey": "Ondersteun ontwikkeling en Adopt een Pangolin!", + "supportKeyDescription": "Koop een supporter sleutel om ons te helpen Pangolin voor de gemeenschap te blijven ontwikkelen. Je bijdrage geeft ons meer tijd om nieuwe functies te behouden en toe te voegen aan de applicatie voor iedereen. We zullen dit nooit gebruiken voor paywall-functies. Dit staat los van elke commerciële editie.", + "supportKeyPet": "U zult ook uw eigen huisdier Pangolin moeten adopteren en ontmoeten!", + "supportKeyPurchase": "Betalingen worden verwerkt via GitHub. Daarna kunt u de sleutel ophalen op", + "supportKeyPurchaseLink": "onze website", + "supportKeyPurchase2": "en verzilver het hier.", + "supportKeyLearnMore": "Meer informatie.", + "supportKeyOptions": "Selecteer de optie die het beste bij u past.", + "supportKetOptionFull": "Volledige supporter", + "forWholeServer": "Voor de hele server", + "lifetimePurchase": "Levenslange aankoop", + "supporterStatus": "Status supporter", + "buy": "Kopen", + "supportKeyOptionLimited": "Beperkte Supporter", + "forFiveUsers": "Voor 5 of minder gebruikers", + "supportKeyRedeem": "Supportersleutel inwisselen", + "supportKeyHideSevenDays": "Verbergen voor 7 dagen", + "supportKeyEnter": "Voer de supportersleutel in", + "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", + "githubUsername": "GitHub Username", + "supportKeyInput": "Supporter Sleutel", + "supportKeyBuy": "Koop Supportersleutel", + "logoutError": "Fout bij uitloggen", + "signingAs": "Ingelogd als", + "serverAdmin": "Server Beheerder", + "otpEnable": "Twee-factor inschakelen", + "otpDisable": "Tweestapsverificatie uitschakelen", + "logout": "Log uit", + "licenseTierProfessionalRequired": "Professionele editie vereist", + "licenseTierProfessionalRequiredDescription": "Deze functie is alleen beschikbaar in de Professional Edition.", + "actionGetOrg": "Krijg Organisatie", + "actionUpdateOrg": "Organisatie bijwerken", + "actionGetOrgUser": "Krijg organisatie-gebruiker", + "actionListOrgDomains": "Lijst organisatie domeinen", + "actionCreateSite": "Site maken", + "actionDeleteSite": "Site verwijderen", + "actionGetSite": "Site ophalen", + "actionListSites": "Sites weergeven", + "actionUpdateSite": "Site bijwerken", + "actionListSiteRoles": "Toon toegestane sitenollen", + "actionCreateResource": "Bron maken", + "actionDeleteResource": "Document verwijderen", + "actionGetResource": "Bron ophalen", + "actionListResource": "Bronnen weergeven", + "actionUpdateResource": "Document bijwerken", + "actionListResourceUsers": "Lijst van documentgebruikers", + "actionSetResourceUsers": "Stel document gebruikers in", + "actionSetAllowedResourceRoles": "Toegestane Resource Rollen instellen", + "actionListAllowedResourceRoles": "Lijst Toegestane Resource Rollen", + "actionSetResourcePassword": "Stel bronwachtwoord in", + "actionSetResourcePincode": "Stel Resource Pincode in", + "actionSetResourceEmailWhitelist": "Stel Resource e-mail whitelist in", + "actionGetResourceEmailWhitelist": "Verkrijg Resource E-mail Whitelist", + "actionCreateTarget": "Create Target", + "actionDeleteTarget": "Verwijder doel", + "actionGetTarget": "Verkrijg Doel", + "actionListTargets": "Doelstellingen weergeven", + "actionUpdateTarget": "Update Target", + "actionCreateRole": "Rol aanmaken", + "actionDeleteRole": "Verwijder rol", + "actionGetRole": "Krijg Rol", + "actionListRole": "Toon rollen", + "actionUpdateRole": "Rol bijwerken", + "actionListAllowedRoleResources": "Lijst toegestane rolbronnen", + "actionInviteUser": "Gebruiker uitnodigen", + "actionRemoveUser": "Gebruiker verwijderen", + "actionListUsers": "Gebruikers weergeven", + "actionAddUserRole": "Gebruikersrol toevoegen", + "actionGenerateAccessToken": "Genereer Toegangstoken", + "actionDeleteAccessToken": "Verwijder toegangstoken", + "actionListAccessTokens": "Lijst toegangstokens", + "actionCreateResourceRule": "Bronregel aanmaken", + "actionDeleteResourceRule": "Verwijder Resource Regel", + "actionListResourceRules": "Bron regels weergeven", + "actionUpdateResourceRule": "Bronregel bewerken", + "actionListOrgs": "Organisaties weergeven", + "actionCheckOrgId": "ID controleren", + "actionCreateOrg": "Nieuwe organisatie aanmaken", + "actionDeleteOrg": "Verwijder organisatie", + "actionListApiKeys": "API-sleutels weergeven", + "actionListApiKeyActions": "Lijst van API Key Acties", + "actionSetApiKeyActions": "Stel API Key Toegestane Acties", + "actionCreateApiKey": "API-sleutel aanmaken", + "actionDeleteApiKey": "API-sleutel verwijderen", + "actionCreateIdp": "IDP aanmaken", + "actionUpdateIdp": "IDP bijwerken", + "actionDeleteIdp": "Verwijder IDP", + "actionListIdps": "Toon IDP", + "actionGetIdp": "IDP ophalen", + "actionCreateIdpOrg": "Maak IDP Org Policy", + "actionDeleteIdpOrg": "Verwijder IDP Org Beleid", + "actionListIdpOrgs": "Toon IDP Orgs", + "actionUpdateIdpOrg": "Update IDP Org", + "noneSelected": "Niet geselecteerd", + "orgNotFound2": "Geen organisaties gevonden.", + "searchProgress": "Zoeken...", + "create": "Aanmaken", + "orgs": "Organisaties", + "loginError": "Er is een fout opgetreden tijdens het inloggen", + "passwordForgot": "Wachtwoord vergeten?", + "otpAuth": "Tweestapsverificatie verificatie", + "otpAuthDescription": "Voer de code van je authenticator-app of een van je reservekopiecodes voor het eenmalig gebruik in.", + "otpAuthSubmit": "Code indienen", + "idpContinue": "Of ga verder met", + "otpAuthBack": "Terug naar inloggen", + "navbar": "Navigation Menu", + "navbarDescription": "Hoofd navigatie menu voor de applicatie", + "navbarDocsLink": "Documentatie", + "commercialEdition": "Commerciële editie", + "otpErrorEnable": "Kan 2FA niet inschakelen", + "otpErrorEnableDescription": "Er is een fout opgetreden tijdens het inschakelen van 2FA", + "otpSetupCheckCode": "Voer een 6-cijferige code in", + "otpSetupCheckCodeRetry": "Ongeldige code. Probeer het opnieuw.", + "otpSetup": "Tweestapsverificatie inschakelen", + "otpSetupDescription": "Beveilig je account met een extra beveiligingslaag", + "otpSetupScanQr": "Scan deze QR-code met je authenticator-app of voer de geheime sleutel handmatig in:", + "otpSetupSecretCode": "Authenticator Code", + "otpSetupSuccess": "Tweestapsverificatie ingeschakeld", + "otpSetupSuccessStoreBackupCodes": "Uw account is nu veiliger. Vergeet niet uw back-upcodes op te slaan.", + "otpErrorDisable": "Kan 2FA niet uitschakelen", + "otpErrorDisableDescription": "Er is een fout opgetreden tijdens het uitschakelen van 2FA", + "otpRemove": "Tweestapsverificatie uitschakelen", + "otpRemoveDescription": "Tweestapsverificatie uitschakelen voor je account", + "otpRemoveSuccess": "Tweestapsverificatie uitgeschakeld", + "otpRemoveSuccessMessage": "Tweestapsverificatie is uitgeschakeld voor uw account. U kunt dit op elk gewenst moment opnieuw inschakelen.", + "otpRemoveSubmit": "2FA uitschakelen", + "paginator": "Pagina {current} van {last}", + "paginatorToFirst": "Ga naar eerste pagina", + "paginatorToPrevious": "Ga naar vorige pagina", + "paginatorToNext": "Ga naar de volgende pagina", + "paginatorToLast": "Ga naar de laatste pagina", + "copyText": "Tekst kopiëren", + "copyTextFailed": "Kan tekst niet kopiëren: ", + "copyTextClipboard": "Kopiëren naar klembord", + "inviteErrorInvalidConfirmation": "Ongeldige bevestiging", + "passwordRequired": "Wachtwoord is vereist", + "allowAll": "Alles toestaan", + "permissionsAllowAll": "Alle machtigingen toestaan", + "githubUsernameRequired": "GitHub gebruikersnaam is vereist", + "supportKeyRequired": "Supportersleutel is vereist", + "passwordRequirementsChars": "Wachtwoord moet ten minste 8 tekens bevatten", + "language": "Taal", + "verificationCodeRequired": "Code is vereist", + "userErrorNoUpdate": "Geen gebruiker om te updaten", + "siteErrorNoUpdate": "Geen site om bij te werken", + "resourceErrorNoUpdate": "Geen document om bij te werken", + "authErrorNoUpdate": "Geen authenticatie informatie om bij te werken", + "orgErrorNoUpdate": "Geen org om bij te werken", + "orgErrorNoProvided": "Geen org opgegeven", + "apiKeysErrorNoUpdate": "Geen API-sleutel om bij te werken" +} From c7c39676d168fb15a79fa1b1c0f8cc47431107de Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Fri, 6 Jun 2025 06:06:56 +0000 Subject: [PATCH 084/105] add NL and ES resolve conflicts --- package-lock.json | 3090 ++++++----------------------- src/components/LocaleSwitcher.tsx | 8 + src/i18n/config.ts | 2 +- 3 files changed, 668 insertions(+), 2432 deletions(-) diff --git a/package-lock.json b/package-lock.json index 980b034d..f6e3a754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.2", + "@heroicons/react": "^2.2.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -64,6 +65,7 @@ "lucide-react": "0.511.0", "moment": "2.30.1", "next": "15.3.3", + "next-intl": "^4.1.0", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", @@ -177,13 +179,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", - "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.3", + "@babel/parser": "^7.27.5", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", @@ -214,9 +216,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", - "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dev": true, "license": "MIT", "dependencies": { @@ -340,9 +342,9 @@ "license": "Apache-2.0" }, "node_modules/@ecies/ciphers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.2.tgz", - "integrity": "sha512-ylfGR7PyTd+Rm2PqQowG08BCKA22QuX8NzrL+LxAAvazN10DMwdJ2fWwAzRj05FI/M8vNFGm3cv9Wq/GFWCBLg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.3.tgz", + "integrity": "sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==", "dev": true, "license": "MIT", "engines": { @@ -354,40 +356,6 @@ "@noble/ciphers": "^1.0.0" } }, -<<<<<<< HEAD - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, -======= ->>>>>>> main "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -400,261 +368,6 @@ "source-map-support": "^0.5.21" } }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", @@ -672,108 +385,6 @@ "node": ">=12" } }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", @@ -840,278 +451,6 @@ "typescript": "*" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", @@ -1129,142 +468,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -1397,21 +600,21 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, @@ -1434,6 +637,75 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", + "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, "node_modules/@hookform/resolvers": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", @@ -1504,146 +776,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", @@ -1660,22 +792,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", @@ -1692,72 +808,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" - } - }, "node_modules/@img/sharp-linux-x64": { "version": "0.34.2", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", @@ -1780,28 +830,6 @@ "@img/sharp-libvips-linux-x64": "1.1.0" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" - } - }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.34.2", "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", @@ -1824,82 +852,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1983,18 +935,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz", - "integrity": "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, "node_modules/@next/env": { "version": "15.3.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", @@ -2010,70 +950,6 @@ "fast-glob": "3.3.1" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz", - "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz", - "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz", - "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz", - "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-linux-x64-gnu": { "version": "15.3.3", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz", @@ -2106,42 +982,10 @@ "node": ">= 10" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz", - "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz", - "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@noble/ciphers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz", - "integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "dev": true, "license": "MIT", "engines": { @@ -2152,13 +996,13 @@ } }, "node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.0.tgz", + "integrity": "sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==", "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.7.1" + "@noble/hashes": "1.8.0" }, "engines": { "node": "^14.21.3 || >=16" @@ -2168,9 +1012,9 @@ } }, "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, "license": "MIT", "engines": { @@ -2205,134 +1049,6 @@ "@node-rs/argon2-win32-x64-msvc": "2.0.2" } }, - "node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-2.0.2.tgz", - "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-android-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-2.0.2.tgz", - "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-2.0.2.tgz", - "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-2.0.2.tgz", - "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-freebsd-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-2.0.2.tgz", - "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-2.0.2.tgz", - "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-2.0.2.tgz", - "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-2.0.2.tgz", - "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", @@ -2365,70 +1081,6 @@ "node": ">= 10" } }, - "node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-2.0.2.tgz", - "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-2.0.2.tgz", - "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-2.0.2.tgz", - "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-2.0.2.tgz", - "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/bcrypt": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", @@ -2458,17 +1110,33 @@ "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, - "node_modules/@node-rs/bcrypt-darwin-arm64": { + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", "cpu": [ - "arm64" + "x64" ], "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" ], "engines": { "node": ">= 10" @@ -2564,26 +1232,6 @@ "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==", "license": "MIT" }, -<<<<<<< HEAD - "node_modules/@petamoriken/float16": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", - "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", - "dev": true, - "license": "MIT" -======= - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } ->>>>>>> main - }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -3902,9 +2550,9 @@ "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz", - "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", "license": "MIT" }, "node_modules/@scarf/scarf": { @@ -3914,6 +2562,12 @@ "hasInstallScript": true, "license": "Apache-2.0" }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", + "license": "MIT" + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", @@ -4006,125 +2660,6 @@ "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", - "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", - "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", - "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", - "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", - "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", - "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", - "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", @@ -4159,155 +2694,6 @@ "node": ">= 10" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", - "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.10", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { -<<<<<<< HEAD - "version": "1.4.3", -======= - "version": "1.4.0", ->>>>>>> main - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { -<<<<<<< HEAD - "@emnapi/wasi-threads": "1.0.2", -======= - "@emnapi/wasi-threads": "1.0.1", ->>>>>>> main - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { -<<<<<<< HEAD - "version": "1.4.3", -======= - "version": "1.4.0", ->>>>>>> main - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { -<<<<<<< HEAD - "version": "1.0.2", -======= - "version": "1.0.1", ->>>>>>> main - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { -<<<<<<< HEAD - "version": "0.2.10", -======= - "version": "0.2.8", ->>>>>>> main - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { -<<<<<<< HEAD - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", -======= - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", ->>>>>>> main - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.0", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", - "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", - "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/postcss": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", @@ -4355,16 +2741,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/better-sqlite3": { "version": "7.6.12", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", @@ -4424,9 +2800,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, "node_modules/@types/express": { @@ -4443,9 +2819,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.5.tgz", - "integrity": "sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4514,9 +2890,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4642,20 +3018,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz", - "integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", + "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.33.0", - "@typescript-eslint/type-utils": "8.33.0", - "@typescript-eslint/utils": "8.33.0", - "@typescript-eslint/visitor-keys": "8.33.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/type-utils": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4667,7 +3043,7 @@ "peerDependencies": { "@typescript-eslint/parser": "^8.33.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -4680,15 +3056,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz", - "integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==", + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", + "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.33.0", - "@typescript-eslint/types": "8.33.0", - "@typescript-eslint/typescript-estree": "8.33.0", - "@typescript-eslint/visitor-keys": "8.33.0", + "@typescript-eslint/scope-manager": "8.33.1", + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/typescript-estree": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1", "debug": "^4.3.4" }, "engines": { @@ -4703,15 +3079,14 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz", - "integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", + "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.33.0", - "@typescript-eslint/types": "^8.33.0", - "debug": "^4.3.4" + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4721,14 +3096,158 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz", - "integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", + "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", + "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.0", - "@typescript-eslint/visitor-keys": "8.33.0" + "@typescript-eslint/project-service": "8.33.1", + "@typescript-eslint/tsconfig-utils": "8.33.1", + "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/visitor-keys": "8.33.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", + "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.33.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", + "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.33.1", + "@typescript-eslint/types": "^8.33.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", + "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", + "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4739,9 +3258,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz", - "integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==", + "version": "8.33.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", + "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4755,15 +3274,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz", - "integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", + "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.33.0", - "@typescript-eslint/utils": "8.33.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/utils": "8.21.0", "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4778,9 +3297,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz", - "integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", + "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4791,21 +3310,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz", - "integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", + "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.33.0", - "@typescript-eslint/tsconfig-utils": "8.33.0", - "@typescript-eslint/types": "8.33.0", - "@typescript-eslint/visitor-keys": "8.33.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4871,15 +3388,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz", - "integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", + "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.33.0", - "@typescript-eslint/types": "8.33.0", - "@typescript-eslint/typescript-estree": "8.33.0" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4894,12 +3411,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz", - "integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", + "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/types": "8.21.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4910,6 +3427,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz", + "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz", + "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5137,17 +3680,18 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5272,9 +3816,9 @@ } }, "node_modules/axe-core": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", - "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", "license": "MPL-2.0", "engines": { "node": ">=4" @@ -5517,9 +4061,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5530,13 +4074,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -5555,9 +4099,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "version": "1.0.30001716", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz", + "integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==", "funding": [ { "type": "opencollective", @@ -6098,6 +4642,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "license": "MIT" + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -6295,9 +4845,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -6478,9 +5028,9 @@ } }, "node_modules/eciesjs": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.13.tgz", - "integrity": "sha512-zBdtR4K+wbj10bWPpIOF9DW+eFYQu8miU5ypunh0t4Bvt83ZPlEWgT5Dq/0G6uwEXumZKjfb5BZxYUZQ2Hzn/Q==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.14.tgz", + "integrity": "sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==", "dev": true, "license": "MIT", "dependencies": { @@ -6616,6 +5166,7 @@ "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6775,12 +5326,15 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -7006,25 +5560,24 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz", - "integrity": "sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.7", - "enhanced-resolve": "^5.15.0", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3", - "stable-hash": "^0.0.4" + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -7040,34 +5593,6 @@ } } }, - "node_modules/eslint-import-resolver-typescript/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/eslint-module-utils": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", @@ -7175,9 +5700,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "license": "MIT", "dependencies": { "array-includes": "^3.1.8", @@ -7190,7 +5715,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -7207,9 +5732,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", - "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "license": "MIT", "engines": { "node": ">=10" @@ -7505,19 +6030,18 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", - "dev": true, + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -7659,9 +6183,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "license": "ISC" }, "node_modules/fn.name": { @@ -7691,9 +6215,9 @@ } }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -7706,12 +6230,12 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -7734,13 +6258,14 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -7783,28 +6308,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense", - "optional": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7867,17 +6370,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -8084,6 +6587,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -8359,6 +6863,18 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8439,12 +6955,12 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -8455,12 +6971,24 @@ } }, "node_modules/is-bun-module": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", - "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/is-callable": { @@ -8767,12 +7295,12 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8831,9 +7359,9 @@ } }, "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8913,6 +7441,18 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -9067,132 +7607,6 @@ "lightningcss-win32-x64-msvc": "1.30.1" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", @@ -9235,48 +7649,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -9387,9 +7759,9 @@ } }, "node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "license": "ISC", "engines": { "node": "20 || >=22" @@ -9456,29 +7828,6 @@ "node": ">= 0.6" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memfs-browser": { - "version": "3.5.10302", - "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", - "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "memfs": "3.5.3" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -9733,6 +8082,21 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/napi-postinstall": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.3.tgz", + "integrity": "sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==", + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9802,6 +8166,42 @@ } } }, + "node_modules/next-intl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.1.0.tgz", + "integrity": "sha512-JNJRjc7sdnfUxhZmGcvzDszZ60tQKrygV/VLsgzXhnJDxQPn1cN2rVpc53adA1SvBJwPK2O6Sc6b4gYSILjCzw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/intl-localematcher": "^0.5.4", + "negotiator": "^1.0.0", + "use-intl": "^4.1.0" + }, + "peerDependencies": { + "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/next-intl/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -9841,9 +8241,9 @@ } }, "node_modules/node-abi": { - "version": "3.73.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.73.0.tgz", - "integrity": "sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==", + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -9877,6 +8277,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -10066,7 +8467,7 @@ "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", "read": "^4.1.0", - "semver": "7.7.2", + "semver": "^7.7.2", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", "supports-color": "^10.0.0", @@ -12425,9 +10826,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -12476,14 +10877,15 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -12723,26 +11125,6 @@ "@node-rs/bcrypt": "1.9.0" } }, - "node_modules/oslo/node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/oslo/node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", @@ -12768,134 +11150,6 @@ "@node-rs/argon2-win32-x64-msvc": "1.7.0" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", - "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", - "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", - "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", @@ -12928,83 +11182,6 @@ "node": ">= 10" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@tybys/wasm-util": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", - "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -13256,7 +11433,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -13279,18 +11455,18 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -13814,9 +11990,9 @@ } }, "node_modules/readdirp": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", - "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { @@ -13968,9 +12144,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -14598,9 +12774,9 @@ } }, "node_modules/stable-hash": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", - "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "license": "MIT" }, "node_modules/stack-trace": { @@ -14973,9 +13149,10 @@ "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -15043,6 +13220,22 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -15193,18 +13386,6 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -15361,9 +13542,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -15407,6 +13588,38 @@ "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", + "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/JounQin" + }, + "optionalDependencies": { + "@unrs/resolver-binding-darwin-arm64": "1.7.2", + "@unrs/resolver-binding-darwin-x64": "1.7.2", + "@unrs/resolver-binding-freebsd-x64": "1.7.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-musl": "1.7.2", + "@unrs/resolver-binding-wasm32-wasi": "1.7.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -15437,6 +13650,20 @@ } } }, + "node_modules/use-intl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.1.0.tgz", + "integrity": "sha512-mQvDYFvoGn+bm/PWvlQOtluKCknsQ5a9F1Cj0hMfBjMBVTwnOqLPd6srhjvVdEQEQFVyHM1PfyifKqKYb11M9Q==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "^2.2.0", + "@schummar/icu-type-parser": "1.21.5", + "intl-messageformat": "^10.5.14" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + } + }, "node_modules/use-sidecar": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", @@ -15608,15 +13835,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index a47c6898..bcfe5b4b 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -24,6 +24,10 @@ export default function LocaleSwitcher() { value: 'it-IT', label: 'Italiano' }, + { + value: 'nl-NL', + label: 'Nederlands' + }, { value: 'pl-PL', label: 'Polski' @@ -32,6 +36,10 @@ export default function LocaleSwitcher() { value: 'pt-PT', label: 'Português' }, + { + value: 'es-ES', + label: 'Español' + }, { value: 'tr-TR', label: 'Türkçe' diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 7580320b..305d66d3 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,4 +1,4 @@ export type Locale = (typeof locales)[number]; -export const locales = ['en-US', 'fr-FR', 'de-DE', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN'] as const; +export const locales = ['en-US', 'es-ES', 'fr-FR', 'de-DE', 'nl-NL', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN'] as const; export const defaultLocale: Locale = 'en-US'; \ No newline at end of file From b4b19d2263850805d3cb5f9c32de0d93dfe20a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=97=B4=E8=8B=8F=E8=8B=8F?= Date: Fri, 6 Jun 2025 14:29:49 +0800 Subject: [PATCH 085/105] chore(i18n): partial Simplified Chinese localization (draft) (#143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Chinese i18n * fix(i18n): corrected mislabeled language name from "中国人" to "简体中文" * chore(i18n): initial Simplified Chinese translation --- messages/zh-CN.json | 400 +++++++++++++++--------------- src/components/LocaleSwitcher.tsx | 2 +- 2 files changed, 201 insertions(+), 201 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 122ac723..1373015f 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1,5 +1,5 @@ { - "setupCreate": "创建您的组织、网站和资源", + "setupCreate": "创建您的第一个组织、网站和资源", "setupNewOrg": "新建组织", "setupCreateOrg": "创建组织", "setupCreateResources": "创建资源", @@ -7,17 +7,17 @@ "orgDisplayName": "这是您组织的显示名称。", "orgId": "组织ID", "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", - "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", + "setupErrorIdentifier": "组织ID 已被使用。请另选一个。", "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", - "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", - "dismiss": "关闭", - "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", - "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", - "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "componentsMember": "{count, plural, =0 {您目前不是任何组织的成员。} other {你已加入 # 个组织。}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", + "dismiss": "忽略", + "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", + "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", + "inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", "inviteLoginUser": "请确保您以正确的用户登录。", "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", @@ -48,7 +48,7 @@ "siteDelete": "删除站点", "siteMessageRemove": "一旦删除,该站点将无法访问。与该站点相关的所有资源和目标也将被删除。", "siteMessageConfirm": "请在下面输入站点名称以确认。", - "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", + "siteQuestionRemove": "您确定要从组织中删除 {selectedSite} 站点吗?", "siteManageSites": "管理站点", "siteDescription": "允许通过安全隧道连接到您的网络", "siteCreate": "创建站点", @@ -61,27 +61,27 @@ "siteNameDescription": "这是站点的显示名称。", "method": "方法", "siteMethodDescription": "这是您将如何显示连接。", - "siteLearnNewt": "学习如何在您的系统上安装Newt", + "siteLearnNewt": "学习如何在您的系统上安装 Newt", "siteSeeConfigOnce": "您只能看到一次配置。", - "siteLoadWGConfig": "正在载入Wire保护配置...", + "siteLoadWGConfig": "正在载入 WireGuard 配置...", "siteDocker": "扩展 Docker 部署详细信息", - "toggle": "切换键", - "dockerCompose": "Docker 配置", - "dockerRun": "停靠栏", - "siteLearnLocal": "本地站点没有隧道,学习更多", - "siteConfirmCopy": "我已复制配置", + "toggle": "切换", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", + "siteConfirmCopy": "我已经复制了配置信息", "searchSitesProgress": "搜索站点...", "siteAdd": "添加站点", - "siteInstallNewt": "安装新的", - "siteInstallNewtDescription": "在您的系统上获得新的运行", - "WgConfiguration": "Wire护卫配置", + "siteInstallNewt": "安装 Newt", + "siteInstallNewtDescription": "在您的系统中运行 Newt", + "WgConfiguration": "WireGuard 配置", "WgConfigurationDescription": "使用以下配置连接到您的网络", "operatingSystem": "操作系统", "commands": "命令", - "recommended": "推荐的", - "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", - "siteRunsInDocker": "运行在停靠栏", - "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", + "recommended": "推荐", + "siteNewtDescription": "为获得最佳用户体验,请使用 Newt。其底层采用 WireGuard 技术,可直接通过 Pangolin 控制台,使用局域网地址访问您私有网络中的资源。", + "siteRunsInDocker": "在 Docker 中运行", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中运行", "siteErrorDelete": "删除站点出错", "siteErrorUpdate": "更新站点失败", "siteErrorUpdateDescription": "更新站点时出错。", @@ -89,18 +89,18 @@ "siteUpdatedDescription": "网站已更新。", "siteGeneralDescription": "配置此站点的常规设置", "siteSettingDescription": "配置您网站上的设置", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "新隧道(推荐)", - "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", - "siteWg": "基本线甲", - "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", - "siteLocalDescription": "仅本地资源。没有隧道。", + "siteSetting": "{siteName} 设置", + "siteNewtTunnel": "Newt 隧道 (推荐)", + "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", + "siteWg": "基本 WireGuard", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", + "siteLocalDescription": "仅限本地资源。不需要隧道。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", - "siteNewtCredentials": "新建凭据", - "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", - "siteCredentialsSave": "保存您的证书", - "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "siteNewtCredentials": "Newt 凭据", + "siteNewtCredentialsDescription": "这是 Newt 服务器的身份验证凭据", + "siteCredentialsSave": "保存您的凭据", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制并保存到一个安全的地方。", "siteInfo": "站点信息", "status": "状态", "shareTitle": "管理共享链接", @@ -111,16 +111,16 @@ "shareErrorDeleteMessage": "删除链接时出错", "shareDeleted": "链接已删除", "shareDeletedDescription": "链接已删除", - "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求头。 每次验证访问请求都必须从客户端传递。", "accessToken": "访问令牌", "usageExamples": "用法示例", - "tokenId": "Token ID", - "requestHeades": "请求标题", + "tokenId": "令牌 ID", + "requestHeades": "请求头", "queryParameter": "查询参数", - "importantNote": "重要笔记", - "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", + "importantNote": "重要提示", + "shareImportantDescription": "出于安全考虑,建议尽可能在使用请求头传递参数,因为查询参数可能会被浏览器历史记录或服务器日志记录。", "token": "令牌", - "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", + "shareTokenSecurety": "请妥善保管您的访问令牌,不要将其暴露在公开访问的区域或客户端代码中。", "shareErrorFetchResource": "获取资源失败", "shareErrorFetchResourceDescription": "获取资源时出错", "shareErrorCreate": "无法创建共享链接", @@ -141,7 +141,7 @@ "title": "标题", "created": "已创建", "expires": "过期时间", - "never": "从不使用", + "never": "永不过期", "shareErrorSelectResource": "请选择一个资源", "resourceTitle": "管理资源", "resourceDescription": "为您的私人应用程序创建安全代理", @@ -149,14 +149,14 @@ "resourceAdd": "添加资源", "resourceErrorDelte": "删除资源时出错", "authentication": "认证", - "protected": "保护", - "notProtected": "不保护", - "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", + "protected": "受到保护", + "notProtected": "未受到保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与该资源相关的所有目标也将被删除。", "resourceMessageConfirm": "请在下面输入资源名称以确认。", - "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", + "resourceQuestionRemove": "您确定要从组织中删除 {selectedResource} 吗?", "resourceHTTP": "HTTPS 资源", - "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", - "resourceRaw": "Raw TCP/UDP 资源", + "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "TCP/UDP 资源", "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", @@ -172,20 +172,20 @@ "resourceHTTPSSettings": "HTTPS 设置", "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", "domainType": "域类型", - "subdomain": "子域", - "baseDomain": "基础域", - "subdomnainDescription": "您的资源可以访问的子域。", + "subdomain": "子域名", + "baseDomain": "根域名", + "subdomnainDescription": "您的资源可以访问的子域名。", "resourceRawSettings": "TCP/UDP 设置", "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "协议", + "protocolSelect": "选择协议", "resourcePortNumber": "端口号", "resourcePortNumberDescription": "代理请求的外部端口号。", "cancel": "取消", "resourceConfig": "配置片段", "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", "resourceAddEntrypoints": "Traefik: 添加入口点", - "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", + "resourceExposePorts": "Gerbil:在 Docker Compose 中显示端口", "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", "resourceBack": "返回资源", "resourceGoTo": "转到资源", @@ -194,25 +194,25 @@ "visibility": "可见性", "enabled": "已启用", "disabled": "已禁用", - "general": "A. 概况", + "general": "概览", "generalSettings": "常规设置", "proxy": "代理服务器", "rules": "规则", "resourceSettingDescription": "配置您资源上的设置", - "resourceSetting": "{resourceName} Settings", - "alwaysAllow": "总是允许", - "alwaysDeny": "总是拒绝", + "resourceSetting": "{resourceName} 设置", + "alwaysAllow": "一律允许", + "alwaysDeny": "一律拒绝", "orgSettingsDescription": "配置您组织的一般设置", "orgGeneralSettings": "组织设置", "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", "saveGeneralSettings": "保存常规设置", "orgDangerZone": "危险区域", - "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", + "orgDangerZoneDescription": "一旦删除该组织,将无法恢复,请务必确认。", "orgDelete": "删除组织", "orgDeleteConfirm": "确认删除组织", - "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", + "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", - "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", + "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", "orgUpdated": "组织已更新", "orgUpdatedDescription": "组织已更新。", "orgErrorUpdate": "更新组织失败", @@ -224,15 +224,15 @@ "orgDeleted": "组织已删除", "orgDeletedMessage": "组织及其数据已被删除。", "orgMissing": "缺少组织 ID", - "orgMissingMessage": "没有机构ID,无法重新生成邀请。", + "orgMissingMessage": "没有组织ID,无法重新生成邀请。", "accessUsersManage": "管理用户", - "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", + "accessUsersDescription": "邀请用户并位他们添加角色以管理访问您的组织", "accessUsersSearch": "搜索用户...", "accessUserCreate": "创建用户", "accessUserRemove": "删除用户", "username": "用户名", "identityProvider": "身份提供商", - "role": "作用", + "role": "角色", "nameRequired": "名称是必填项", "accessRolesManage": "管理角色", "accessRolesDescription": "配置角色来管理访问您的组织", @@ -244,13 +244,13 @@ "inviteDescription": "管理您给其他用户的邀请", "inviteSearch": "搜索邀请...", "minutes": "分钟", - "hours": "小时数", + "hours": "小时", "days": "天", "weeks": "周", "months": "月", "years": "年", "day": "{count, plural, =1 {# 天} other {# 天}}", - "apiKeysTitle": "API 密钥信息", + "apiKeysTitle": "API 密钥", "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", "apiKeysErrorCreate": "创建 API 密钥出错", "apiKeysErrorSetPermission": "设置权限出错", @@ -260,7 +260,7 @@ "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", "apiKeysList": "您的 API 密钥", "apiKeysSave": "保存您的 API 密钥", - "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "apiKeysSaveDescription": "该信息仅会显示一次,请确保将其复制到安全的位置。", "apiKeysInfo": "您的 API 密钥是:", "apiKeysConfirmCopy": "我已复制 API 密钥", "generate": "生成", @@ -279,18 +279,18 @@ "apiKeysAdd": "生成 API 密钥", "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", - "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", - "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", + "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", + "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", "apiKeysDelete": "删除 API 密钥", "apiKeysManage": "管理 API 密钥", "apiKeysDescription": "API 密钥用于认证集成 API", - "apiKeysSettings": "{apiKeyName} Settings", + "apiKeysSettings": "{apiKeyName} 设置", "userTitle": "管理所有用户", "userDescription": "查看和管理系统中的所有用户", "userAbount": "关于用户管理", - "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", + "userAbountDescription": "此表格显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表格中的删除操作删除其根用户对象。", "userServer": "服务器用户", "userSearch": "搜索服务器用户...", "userErrorDelete": "删除用户时出错", @@ -298,9 +298,9 @@ "userDeleteServer": "从服务器删除用户", "userMessageRemove": "该用户将被从所有组织中删除并完全从服务器中删除。", "userMessageConfirm": "请在下面输入用户名称以确认。", - "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", + "userQuestionRemove": "您确定要从服务器中永久删除 {selectedUser} 吗?", "licenseKey": "许可证密钥", - "valid": "Valid", + "valid": "有效", "numberOfSites": "站点数量", "licenseKeySearch": "搜索许可证密钥...", "licenseKeyAdd": "添加许可证密钥", @@ -334,7 +334,7 @@ "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", - "licenseTitle": "管理许可状态", + "licenseTitle": "管理许可证状态", "licenseTitleDescription": "查看和管理系统中的许可证密钥", "licenseHost": "主机许可证", "licenseHostDescription": "管理主机的主许可证密钥。", @@ -346,15 +346,15 @@ "licenseNoSiteLimit": "使用未经许可主机的站点数量没有限制。", "licensePurchase": "购买许可证", "licensePurchaseSites": "购买更多站点", - "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", - "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", - "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", - "licenseFee": "许可费", - "licensePriceSite": "每个站点价格", + "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 个站点", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {# 站点}}", + "licensePurchaseDescription": "请选择您希望 {selectedMode, select, license {直接购买许可证,您可以随时增加更多站点。} other {为现有许可证购买更多站点}}", + "licenseFee": "许可证费用", + "licensePriceSite": "每个站点的价格", "total": "总计", "licenseContinuePayment": "继续付款", "pricingPage": "定价页面", - "pricingPortal": "查看购买门户网站", + "pricingPortal": "前往付款页面", "licensePricingPage": "关于最新的价格和折扣,请访问 ", "invite": "邀请", "inviteRegenerate": "重新生成邀请", @@ -363,17 +363,17 @@ "inviteRemoveError": "删除邀请失败", "inviteRemoveErrorDescription": "删除邀请时出错。", "inviteRemoved": "邀请已删除", - "inviteRemovedDescription": "The invitation for {email} has been removed.", - "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", - "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", + "inviteRemovedDescription": "为 {email} 创建的邀请已删除", + "inviteQuestionRemove": "您确定要删除 {email} 的邀请吗?", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。", "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", - "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", + "inviteQuestionRegenerate": "您确定要重新邀请 {email} 吗?这将会撤销掉之前的邀请", "inviteRemoveConfirm": "确认删除邀请", "inviteRegenerated": "重新生成邀请", - "inviteSent": "A new invitation has been sent to {email}.", + "inviteSent": "邀请邮件已成功发送至 {email}。", "inviteSentEmail": "发送电子邮件通知给用户", - "inviteGenerate": "A new invitation has been generated for {email}.", - "inviteDuplicateError": "Duplicate Invite", + "inviteGenerate": "已为 {email} 创建新的邀请。", + "inviteDuplicateError": "重复的邀请", "inviteDuplicateErrorDescription": "此用户的邀请已存在。", "inviteRateLimitError": "超出速率限制", "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", @@ -389,8 +389,8 @@ "userErrorOrgRemove": "删除用户失败", "userErrorOrgRemoveDescription": "删除用户时出错。", "userOrgRemoved": "用户已删除", - "userOrgRemovedDescription": "The user {email} has been removed from the organization.", - "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", + "userOrgRemovedDescription": "已将 {email} 从组织中移除。", + "userQuestionOrgRemove": "你确定要将 {email} 从组织中移除吗?", "userMessageOrgRemove": "一旦删除,这个用户将不再能够访问组织。 你总是可以稍后重新邀请他们,但他们需要再次接受邀请。", "userMessageOrgConfirm": "请在下面输入用户名称以确认。", "userRemoveOrgConfirm": "确认删除用户", @@ -463,7 +463,7 @@ "targetErrorFetchDescription": "获取目标时出错", "siteErrorFetch": "获取资源失败", "siteErrorFetchDescription": "获取资源时出错", - "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicate": "重复的目标", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", "targetWireGuardErrorInvalidIp": "Invalid target IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", @@ -479,9 +479,9 @@ "proxyUpdatedDescription": "您的代理设置已成功更新", "proxyErrorUpdate": "更新代理设置失败", "proxyErrorUpdateDescription": "更新代理设置时出错", - "targetAddr": "IP / Hostname", + "targetAddr": "IP / 域名", "targetPort": "端口", - "targetProtocol": "Protocol", + "targetProtocol": "协议", "targetTlsSettings": "HTTPS & TLS 设置", "targetTlsSettingsDescription": "配置资源的 TLS 设置", "targetTlsSettingsAdvanced": "高级TLS设置", @@ -493,7 +493,7 @@ "targetStickySessions": "启用置顶会话", "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", "methodSelect": "选择方法", - "targetSubmit": "Add Target", + "targetSubmit": "添加目标", "targetNoOne": "没有目标。使用表单添加目标。", "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", "targetsSubmit": "保存目标", @@ -504,7 +504,7 @@ "proxyAdditionalSubmit": "保存代理设置", "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", - "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", + "ipAddressErrorInvalidOctet": "无效的 IP 地址", "path": "路径", "ipAddressRange": "IP 范围", "rulesErrorFetch": "获取规则失败", @@ -520,7 +520,7 @@ "rulesErrorUpdate": "更新规则失败", "rulesErrorUpdateDescription": "更新规则时出错", "rulesUpdated": "启用规则", - "rulesUpdatedDescription": "规则评价已更新", + "rulesUpdatedDescription": "规则已更新", "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", @@ -533,18 +533,18 @@ "ruleErrorUpdate": "操作失败", "ruleErrorUpdateDescription": "保存过程中发生错误", "rulesPriority": "优先权", - "rulesAction": "行 动", - "rulesMatchType": "比赛类型", + "rulesAction": "行为", + "rulesMatchType": "匹配类型", "value": "值", "rulesAbout": "关于规则", - "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", + "rulesAboutDescription": "规则使您能够依据特定条件控制资源访问权限。您可以创建基于 IP 地址或 URL 路径的规则,以允许或拒绝访问。", "rulesActions": "行动", "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", "rulesMatchCriteria": "匹配条件", "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", - "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或模式", "rulesEnable": "启用规则", "rulesEnableDescription": "启用或禁用此资源的规则评估", "rulesResource": "资源规则配置", @@ -562,52 +562,52 @@ "domainsErrorFetch": "获取域名出错", "domainsErrorFetchDescription": "获取域时出错", "none": "无", - "unknown": "未知的", + "unknown": "未知", "resources": "资源", - "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", - "resourcesWireGuardConnect": "与Wire护卫加密安全连接", + "resourcesDescription": "资源是您私有网络中运行的应用程序的代理。您可以为私有网络中的任何 HTTP/HTTPS 或 TCP/UDP 服务创建资源。每个资源都必须连接到一个站点,以通过加密的 WireGuard 隧道实现私密且安全的连接。", + "resourcesWireGuardConnect": "采用 WireGuard 提供的加密安全连接", "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", "resourcesUsersRolesAccess": "基于用户和角色的访问控制", "resourcesErrorUpdate": "切换资源失败", "resourcesErrorUpdateDescription": "更新资源时出错", "access": "访问权限", - "shareLink": "{resource} Share Link", + "shareLink": "{resource} 的分享链接", "resourceSelect": "选择资源", "shareLinks": "分享链接", - "share": "可共享链接", + "share": "分享链接", "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", "shareEasyCreate": "轻松创建和分享", "shareConfigurableExpirationDuration": "可配置的过期时间", "shareSecureAndRevocable": "安全和可撤销的", - "nameMin": "Name must be at least {len} characters.", - "nameMax": "Name must not be longer than {len} characters.", + "nameMin": "名称长度必须大于 {len} 字符。", + "nameMax": "名称长度必须小于 {len} 字符。", "sitesConfirmCopy": "请确认您已经复制了配置。", "unknownCommand": "未知命令", - "newtErrorFetchReleases": "Failed to fetch release info: {err}", - "newtErrorFetchLatest": "Error fetching latest release: {err}", - "newtEndpoint": "Newt Endpoint", + "newtErrorFetchReleases": "无法获取版本信息: {err}", + "newtErrorFetchLatest": "无法获取最新版信息: {err}", + "newtEndpoint": "Newt 端点", "newtId": "Newt ID", - "newtSecretKey": "新的秘密密钥", - "architecture": "结构", + "newtSecretKey": "Newt 私钥", + "architecture": "架构", "sites": "站点", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", "siteWgManualConfigurationRequired": "需要手动配置", "userErrorNotAdminOrOwner": "用户不是管理员或所有者", - "pangolinSettings": "设置-Pangolin", + "pangolinSettings": "设置 - Pangolin", "accessRoleYour": "您的角色:", "accessRoleSelect2": "选择角色", "accessUserSelect": "选择一个用户", "otpEmailEnter": "输入电子邮件", "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", - "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", - "otpEmailSmtpRequired": "需要SMTP", + "otpEmailErrorInvalid": "无效的邮箱地址。通配符(*)必须占据整个开头部分。", + "otpEmailSmtpRequired": "需要先配置SMTP", "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", "otpEmailTitle": "一次性密码", "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", "otpEmailWhitelist": "电子邮件白名单", "otpEmailWhitelistList": "白名单邮件", - "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域名的任何电子邮件地址。", "otpEmailWhitelistSave": "保存白名单", "passwordAdd": "添加密码", "passwordRemove": "删除密码", @@ -632,7 +632,7 @@ "resourceErrorWhitelistSave": "保存白名单失败", "resourceErrorWhitelistSaveDescription": "保存白名单时出错", "resourcePasswordSubmit": "启用密码保护", - "resourcePasswordProtection": "Password Protection {status}", + "resourcePasswordProtection": "密码保护 {status}", "resourcePasswordRemove": "已删除资源密码", "resourcePasswordRemoveDescription": "已成功删除资源密码", "resourcePasswordSetup": "设置资源密码", @@ -641,13 +641,13 @@ "resourcePasswordSetupTitleDescription": "设置密码来保护此资源", "resourcePincode": "PIN 码", "resourcePincodeSubmit": "启用 PIN 码保护", - "resourcePincodeProtection": "PIN Code Protection {status}", - "resourcePincodeRemove": "资源粉码已删除", - "resourcePincodeRemoveDescription": "已成功删除资源密码", - "resourcePincodeSetup": "资源PIN 码已设置", - "resourcePincodeSetupDescription": "资源固定码已成功设置", - "resourcePincodeSetupTitle": "设置粉码", - "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", + "resourcePincodeProtection": "PIN 码保护 {status}", + "resourcePincodeRemove": "资源 PIN 码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源 PIN 码", + "resourcePincodeSetup": "资源 PIN 码已设置", + "resourcePincodeSetupDescription": "资源 PIN 码已成功设置", + "resourcePincodeSetupTitle": "设置 PIN 码", + "resourcePincodeSetupTitleDescription": "设置 PIN 码来保护此资源", "resourceRoleDescription": "管理员总是可以访问此资源。", "resourceUsersRoles": "用户和角色", "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", @@ -658,14 +658,14 @@ "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", "proxyErrorInvalidPort": "无效的端口号", "subdomainErrorInvalid": "无效的子域", - "domainErrorFetch": "获取域名出错", - "domainErrorFetchDescription": "获取域时出错", + "domainErrorFetch": "获取域名失败", + "domainErrorFetchDescription": "获取域名时出错", "resourceErrorUpdate": "更新资源失败", "resourceErrorUpdateDescription": "更新资源时出错", "resourceUpdated": "资源已更新", "resourceUpdatedDescription": "资源已成功更新", - "resourceErrorTransfer": "传输资源失败", - "resourceErrorTransferDescription": "传输资源时出错", + "resourceErrorTransfer": "转移资源失败", + "resourceErrorTransferDescription": "转移资源时出错", "resourceTransferred": "资源已传输", "resourceTransferredDescription": "资源已成功传输", "resourceErrorToggle": "切换资源失败", @@ -675,9 +675,9 @@ "resourceGeneral": "常规设置", "resourceGeneralDescription": "配置此资源的常规设置", "resourceEnable": "启用资源", - "resourceTransfer": "传输资源", + "resourceTransfer": "转移资源", "resourceTransferDescription": "将此资源转移到另一个站点", - "resourceTransferSubmit": "传输资源", + "resourceTransferSubmit": "转移资源", "siteDestination": "目标站点", "searchSites": "搜索站点", "accessRoleCreate": "创建角色", @@ -704,18 +704,18 @@ "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", "licenseTierCommercial": "商业许可证", - "licensed": "许可的", - "yes": "否", + "licensed": "已授权", + "yes": "是", "no": "否", "sitesAdditional": "其他站点", "licenseKeys": "许可证密钥", - "sitestCountDecrease": "减少站点计数", - "sitestCountIncrease": "增加站点计数", + "sitestCountDecrease": "减少站点数量", + "sitestCountIncrease": "增加站点数量", "idpManage": "管理身份提供商", "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", + "idpQuestionRemove": "你确定要永久删除 “{name}” 这个身份提供商吗?", "idpMessageRemove": "这将删除身份提供者和所有相关的配置。通过此提供者进行身份验证的用户将无法登录。", "idpMessageConfirm": "要确认,请在下面输入身份提供者的名称。", "idpConfirmDelete": "确认删除身份提供商", @@ -723,12 +723,12 @@ "idp": "身份提供商", "idpSearch": "搜索身份提供者...", "idpAdd": "添加身份提供商", - "idpClientIdRequired": "客户端ID是必需的。", - "idpClientSecretRequired": "客户端密码是必需的。", - "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", - "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", - "idpPathRequired": "标识路径是必需的。", - "idpScopeRequired": "范围是必需的。", + "idpClientIdRequired": "客户端ID 是必需的。", + "idpClientSecretRequired": "客户端密钥是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL 必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL 必须是有效的 URL。", + "idpPathRequired": "标识符路径是必需的。", + "idpScopeRequired": "授权范围是必需的。", "idpOidcDescription": "配置 OpenID 连接身份提供商", "idpCreatedDescription": "身份提供商创建成功", "idpCreate": "创建身份提供商", @@ -748,27 +748,27 @@ "idpClientSecret": "客户端密钥", "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", "idpAuthUrl": "授权 URL", - "idpAuthUrlDescription": "OAuth2 授权终点 URL", - "idpTokenUrl": "令牌网址", - "idpTokenUrlDescription": "OAuth2 令牌端点URL", - "idpOidcConfigureAlert": "重要信息", - "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", + "idpAuthUrlDescription": "OAuth2 授权端点的 URL", + "idpTokenUrl": "令牌 URL", + "idpTokenUrlDescription": "OAuth2 令牌端点的 URL", + "idpOidcConfigureAlert": "重要提示", + "idpOidcConfigureAlertDescription": "创建身份提供方后,您需要在其设置中配置回调 URL。回调 URL 会在创建成功后提供。", "idpToken": "令牌配置", - "idpTokenDescription": "配置如何从ID令牌中提取用户信息", + "idpTokenDescription": "配置如何从 ID 令牌中提取用户信息", "idpJmespathAbout": "关于 JMESPath", - "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", - "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", - "idpJmespathLabel": "标识路径", - "idpJmespathLabelDescription": "用户标识符的路径", - "idpJmespathEmailPathOptional": "电子邮件路径(可选)", - "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", - "idpJmespathNamePathOptional": "名称路径(可选)", - "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", - "idpOidcConfigureScopes": "范围", - "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", + "idpJmespathAboutDescription": "以下路径使用 JMESPath 语法从 ID 令牌中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息", + "idpJmespathLabel": "标识符路径", + "idpJmespathLabelDescription": "ID 令牌中用户标识符的路径", + "idpJmespathEmailPathOptional": "邮箱路径(可选)", + "idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径", + "idpJmespathNamePathOptional": "用户名路径(可选)", + "idpJmespathNamePathOptionalDescription": "ID 令牌中用户名的路径", + "idpOidcConfigureScopes": "作用域(Scopes)", + "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 请求作用域列表", "idpSubmit": "创建身份提供商", "orgPolicies": "组织策略", - "idpSettings": "{idpName} Settings", + "idpSettings": "{idpName} 设置", "idpCreateSettingsDescription": "配置身份提供商的设置", "roleMapping": "角色映射", "orgMapping": "组织映射", @@ -789,13 +789,13 @@ "defaultMappingsRole": "默认角色映射", "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", "defaultMappingsOrg": "默认组织映射", - "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", + "defaultMappingsOrgDescription": "此表达式必须返回 组织ID 或 true 才能允许用户访问组织。", "defaultMappingsSubmit": "保存默认映射", "orgPoliciesEdit": "编辑组织策略", "org": "组织", "orgSelect": "选择组织", "orgSearch": "搜索", - "orgNotFound": "找不到 org 。", + "orgNotFound": "找不到组织。", "roleMappingPathOptional": "角色映射路径(可选)", "orgMappingPathOptional": "组织映射路径(可选)", "orgPolicyUpdate": "更新策略", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "邦戈林徽标", + "pangolinLogoAlt": "Pangolin Logo", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -840,8 +840,8 @@ "passwordErrorAuthenticate": "密码验证失败", "poweredBy": "支持者:", "authenticationRequired": "需要身份验证", - "authenticationMethodChoose": "Choose your preferred method to access {name}", - "authenticationRequest": "You must authenticate to access {name}", + "authenticationMethodChoose": "请选择您偏好的方式来访问 {name}", + "authenticationRequest": "您必须通过身份验证才能访问 {name}", "user": "用户", "pincodeInput": "6位数字 PIN 码", "pincodeSubmit": "使用PIN登录", @@ -851,13 +851,13 @@ "otpEmail": "一次性密码 (OTP)", "otpEmailSubmit": "提交 OTP", "backToEmail": "回到电子邮件", - "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", + "noSupportKey": "服务器当前未使用支持者密钥,欢迎支持本项目!", "accessDenied": "访问被拒绝", - "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", + "accessDeniedDescription": "当前账户无权访问此资源。如认为这是错误,请与管理员联系。", "accessTokenError": "检查访问令牌时出错", "accessGranted": "已授予访问", "accessUrlInvalid": "访问 URL 无效", - "accessGrantedDescription": "您已获准访问此资源。重定向您...", + "accessGrantedDescription": "您已获准访问此资源,正在为您跳转...", "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", "tokenInvalid": "无效的令牌", "pincodeInvalid": "无效的代码", @@ -866,9 +866,9 @@ "passwordResetSuccess": "密码重置成功!返回登录...", "passwordReset": "重置密码", "passwordResetDescription": "按照步骤重置您的密码", - "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", - "passwordResetCode": "Reset Code", - "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", + "passwordResetSent": "我们将发送一个验证码到这个电子邮件地址。", + "passwordResetCode": "验证码", + "passwordResetCodeDescription": "请检查您的电子邮件以获取验证码。", "passwordNew": "新密码", "passwordNewConfirm": "确认新密码", "pincodeAuth": "验证器代码", @@ -885,7 +885,7 @@ "idpConnectingToDescription": "正在验证您的身份", "idpConnectingToProcess": "正在连接...", "idpConnectingToFinished": "已连接", - "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", + "idpErrorConnectingTo": "无法连接到 {name},请联系管理员协助处理。", "idpErrorNotFound": "找不到 IdP", "inviteInvalid": "无效邀请", "inviteInvalidDescription": "邀请链接无效。", @@ -898,7 +898,7 @@ "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", - "orgErrorCreate": "创建 org 时出错", + "orgErrorCreate": "创建组织时出错", "pageNotFound": "找不到页面", "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", "overview": "概览", @@ -917,19 +917,19 @@ "tagsWarnCannotBeLessThanZero": "最大标签和最小标签不能小于 0", "tagsWarnNotAllowedAutocompleteOptions": "标记不允许为每个自动完成选项", "tagsWarnInvalid": "无效的标签,每个有效标签", - "tagWarnTooShort": "Tag {tagText} is too short", - "tagWarnTooLong": "Tag {tagText} is too long", + "tagWarnTooShort": "标签 {tagText} 太短", + "tagWarnTooLong": "标签 {tagText} 太长", "tagsWarnReachedMaxNumber": "已达到允许标签的最大数量", - "tagWarnDuplicate": "Duplicate tag {tagText} not added", + "tagWarnDuplicate": "未添加重复标签 {tagText}", "supportKeyInvalid": "无效密钥", "supportKeyInvalidDescription": "您的支持者密钥无效。", - "supportKeyValid": "Valid Key", + "supportKeyValid": "有效的密钥", "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", "supportKeyErrorValidationDescription": "验证支持者密钥失败。", - "supportKey": "支持开发和通过一个潘戈林!", - "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", - "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", - "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", + "supportKey": "支持开发和通过一个 Pangolin !", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展 Pangolin 。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "您还可以领养并见到属于自己的 Pangolin!", + "supportKeyPurchase": "付款通过 GitHub 进行处理,之后您可以在以下位置获取您的密钥:", "supportKeyPurchaseLink": "我们的网站", "supportKeyPurchase2": "并在这里兑换。", "supportKeyLearnMore": "了解更多。", @@ -944,15 +944,15 @@ "supportKeyRedeem": "兑换支持者密钥", "supportKeyHideSevenDays": "隐藏7天", "supportKeyEnter": "输入支持者密钥", - "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", - "githubUsername": "GitHub Username", + "supportKeyEnterDescription": "见到你自己的 Pangolin!", + "githubUsername": "GitHub 用户名", "supportKeyInput": "支持者密钥", "supportKeyBuy": "购买支持者密钥", "logoutError": "注销错误", "signingAs": "登录为", "serverAdmin": "服务器管理员", - "otpEnable": "启用双因子", - "otpDisable": "禁用双因子", + "otpEnable": "启用双因子认证", + "otpDisable": "禁用双因子认证", "logout": "登出", "licenseTierProfessionalRequired": "需要专业版", "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "设置资源粉码", "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", - "actionCreateTarget": "Create Target", + "actionCreateTarget": "创建目标", "actionDeleteTarget": "删除目标", "actionGetTarget": "获取目标", "actionListTargets": "列表目标", - "actionUpdateTarget": "Update Target", + "actionUpdateTarget": "更新目标", "actionCreateRole": "创建角色", "actionDeleteRole": "删除角色", "actionGetRole": "获取角色", @@ -1002,7 +1002,7 @@ "actionListResourceRules": "列出资源规则", "actionUpdateResourceRule": "更新资源规则", "actionListOrgs": "列出组织", - "actionCheckOrgId": "检查 ID", + "actionCheckOrgId": "检查组织ID", "actionCreateOrg": "创建组织", "actionDeleteOrg": "删除组织", "actionListApiKeys": "列出API密钥", @@ -1013,15 +1013,15 @@ "actionCreateIdp": "创建IDP", "actionUpdateIdp": "更新IDP", "actionDeleteIdp": "删除IDP", - "actionListIdps": "列出国内流离失所者", + "actionListIdps": "列出IDP", "actionGetIdp": "获取IDP", - "actionCreateIdpOrg": "创建IDP Org 策略", - "actionDeleteIdpOrg": "删除IDP Org 策略", - "actionListIdpOrgs": "列出国内流离失所者组织", - "actionUpdateIdpOrg": "更新IDP Org", + "actionCreateIdpOrg": "创建 IDP组织策略", + "actionDeleteIdpOrg": "删除 IDP组织策略", + "actionListIdpOrgs": "列出 IDP组织", + "actionUpdateIdpOrg": "更新 IDP组织", "noneSelected": "未选择", "orgNotFound2": "未找到组织。", - "searchProgress": "搜索...", + "searchProgress": "搜索中...", "create": "创建", "orgs": "组织", "loginError": "登录时出错", @@ -1031,13 +1031,13 @@ "otpAuthSubmit": "提交代码", "idpContinue": "或者继续", "otpAuthBack": "返回登录", - "navbar": "Navigation Menu", + "navbar": "导航菜单", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", - "otpErrorEnableDescription": "启用2FA 时出错", - "otpSetupCheckCode": "请输入一个6位数字", + "otpErrorEnableDescription": "启用 2FA 时出错", + "otpSetupCheckCode": "请输入您的6位数字代码", "otpSetupCheckCodeRetry": "无效的代码。请重试。", "otpSetup": "启用两步验证", "otpSetupDescription": "用额外的保护层来保护您的帐户", @@ -1052,7 +1052,7 @@ "otpRemoveSuccess": "双重身份验证已禁用", "otpRemoveSuccessMessage": "您的帐户已禁用双重身份验证。您可以随时再次启用它。", "otpRemoveSubmit": "禁用两步验证", - "paginator": "Page {current} of {last}", + "paginator": "第 {current} 页,共 {last} 页", "paginatorToFirst": "转到第一页", "paginatorToPrevious": "转到上一页", "paginatorToNext": "转到下一页", @@ -1061,19 +1061,19 @@ "copyTextFailed": "复制文本失败: ", "copyTextClipboard": "复制到剪贴板", "inviteErrorInvalidConfirmation": "无效确认", - "passwordRequired": "密码是必需的", + "passwordRequired": "必须填写密码", "allowAll": "允许所有", "permissionsAllowAll": "允许所有权限", - "githubUsernameRequired": "GitHub 用户名是必需的", - "supportKeyRequired": "支持者密钥是必需的", - "passwordRequirementsChars": "密码必须至少 8 个字符", + "githubUsernameRequired": "必须填写 GitHub 用户名", + "supportKeyRequired": "必须填写支持者密钥", + "passwordRequirementsChars": "密码至少需要 8 个字符", "language": "语言", "verificationCodeRequired": "必须输入代码", "userErrorNoUpdate": "没有要更新的用户", "siteErrorNoUpdate": "没有要更新的站点", "resourceErrorNoUpdate": "没有可更新的资源", "authErrorNoUpdate": "没有要更新的身份验证信息", - "orgErrorNoUpdate": "没有要更新的 org", - "orgErrorNoProvided": "未提供 org", + "orgErrorNoUpdate": "没有要更新的组织", + "orgErrorNoProvided": "未提供组织", "apiKeysErrorNoUpdate": "没有要更新的 API 密钥" -} +} \ No newline at end of file diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index bcfe5b4b..3c0fbca8 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -46,7 +46,7 @@ export default function LocaleSwitcher() { }, { value: 'zh-CN', - label: '中国人' + label: '简体中文' } ]} /> From cb85ad460e84d3c06106d70e1e38a15a1d11850f Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 9 Jun 2025 17:39:29 -0400 Subject: [PATCH 086/105] Remove redundant icons; make update selector --- package-lock.json | 90 +++++++++++++++++++++++++ package.json | 1 - src/components/LocaleSwitcherSelect.tsx | 75 ++++++++++----------- 3 files changed, 126 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f91d940..77798c0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -950,6 +950,66 @@ "fast-glob": "3.3.1" } }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz", + "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz", + "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz", + "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz", + "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@next/swc-linux-x64-gnu": { "version": "15.3.3", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz", @@ -982,6 +1042,36 @@ "node": ">= 10" } }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz", + "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz", + "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", diff --git a/package.json b/package.json index a311cde1..714984e4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.2", - "@heroicons/react": "^2.2.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", diff --git a/src/components/LocaleSwitcherSelect.tsx b/src/components/LocaleSwitcherSelect.tsx index 93fe1a43..1d3f448f 100644 --- a/src/components/LocaleSwitcherSelect.tsx +++ b/src/components/LocaleSwitcherSelect.tsx @@ -1,7 +1,13 @@ 'use client'; -import { CheckIcon, LanguageIcon } from '@heroicons/react/24/solid'; -import * as Select from '@radix-ui/react-select'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from '@app/components/ui/dropdown-menu'; +import { Button } from '@app/components/ui/button'; +import { Check, Globe, Languages } from 'lucide-react'; import clsx from 'clsx'; import { useTransition } from 'react'; import { Locale } from '@/i18n/config'; @@ -9,7 +15,7 @@ import { setUserLocale } from '@/services/locale'; type Props = { defaultValue: string; - items: Array<{value: string; label: string}>; + items: Array<{ value: string; label: string }>; label: string; }; @@ -27,46 +33,37 @@ export default function LocaleSwitcherSelect({ }); } + const selected = items.find((item) => item.value === defaultValue); + return ( -
    - - + + + + + {items.map((item) => ( + onChange(item.value)} + className="flex items-center gap-2" > - - {items.map((item) => ( - -
    - {item.value === defaultValue && ( - - )} -
    - {item.label} -
    - ))} -
    - - - -
    -
    + {item.value === defaultValue && ( + + )} + {item.label} + + ))} + + ); } From 454d7c4a8824ccec1d72242dd6728dd8dfc3ad1a Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 9 Jun 2025 17:40:10 -0400 Subject: [PATCH 087/105] Update lock --- package-lock.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77798c0a..f8326188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.2", - "@heroicons/react": "^2.2.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -697,15 +696,6 @@ "tslib": "2" } }, - "node_modules/@heroicons/react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", - "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", - "license": "MIT", - "peerDependencies": { - "react": ">= 16 || ^19.0.0-rc" - } - }, "node_modules/@hookform/resolvers": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", From a2cf4ffac1ba095710163f9193ab3940f2d831bf Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 9 Jun 2025 17:41:31 -0400 Subject: [PATCH 088/105] Remove old internationalization --- internationalization/de.md | 287 ---------------------------------- internationalization/es.md | 291 ---------------------------------- internationalization/pl.md | 287 ---------------------------------- internationalization/tr.md | 310 ------------------------------------- 4 files changed, 1175 deletions(-) delete mode 100644 internationalization/de.md delete mode 100644 internationalization/es.md delete mode 100644 internationalization/pl.md delete mode 100644 internationalization/tr.md diff --git a/internationalization/de.md b/internationalization/de.md deleted file mode 100644 index c84249f7..00000000 --- a/internationalization/de.md +++ /dev/null @@ -1,287 +0,0 @@ -## Authentication Site - -| EN | DE | Notes | -| -------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------- | -| Powered by [Pangolin](https://github.com/fosrl/pangolin) | Bereitgestellt von [Pangolin](https://github.com/fosrl/pangolin) | | -| Authentication Required | Authentifizierung erforderlich | | -| Choose your preferred method to access {resource} | Wählen Sie Ihre bevorzugte Methode, um auf {resource} zuzugreifen | | -| PIN | PIN | | -| User | Benutzer | | -| 6-digit PIN Code | 6-stelliger PIN-Code | pin login | -| Login in with PIN | Mit PIN anmelden | pin login | -| Email | E-Mail | user login | -| Enter your email | Geben Sie Ihre E-Mail-Adresse ein | user login | -| Password | Passwort | user login | -| Enter your password | Geben Sie Ihr Passwort ein | user login | -| Forgot your password? | Passwort vergessen? | user login | -| Log in | Anmelden | user login | - ---- - -## Login site - -| EN | DE | Notes | -| --------------------- | ---------------------------------- | ----------- | -| Welcome to Pangolin | Willkommen bei Pangolin | | -| Log in to get started | Melden Sie sich an, um zu beginnen | | -| Email | E-Mail | | -| Enter your email | Geben Sie Ihre E-Mail-Adresse ein | placeholder | -| Password | Passwort | | -| Enter your password | Geben Sie Ihr Passwort ein | placeholder | -| Forgot your password? | Passwort vergessen? | | -| Log in | Anmelden | | - -# Ogranization site after successful login - -| EN | DE | Notes | -| ----------------------------------------- | -------------------------------------------- | ----- | -| Welcome to Pangolin | Willkommen bei Pangolin | | -| You're a member of {number} organization. | Sie sind Mitglied von {number} Organisation. | | - -## Shared Header, Navbar and Footer -##### Header - -| EN | DE | Notes | -| ------------------- | ------------------- | ----- | -| Documentation | Dokumentation | | -| Support | Support | | -| Organization {name} | Organisation {name} | | -##### Organization selector - -| EN | DE | Notes | -| ---------------- | ----------------- | ----- | -| Search… | Suchen… | | -| Create | Erstellen | | -| New Organization | Neue Organisation | | -| Organizations | Organisationen | | - -##### Navbar - -| EN | DE | Notes | -| --------------- | ----------------- | ----- | -| Sites | Websites | | -| Resources | Ressourcen | | -| User & Roles | Benutzer & Rollen | | -| Shareable Links | Teilbare Links | | -| General | Allgemein | | -##### Footer -| EN | DE | | -| ------------------------- | --------------------------- | ------------------- | -| Page {number} of {number} | Seite {number} von {number} | | -| Rows per page | Zeilen pro Seite | | -| Pangolin | Pangolin | unten auf der Seite | -| Built by Fossorial | Erstellt von Fossorial | unten auf der Seite | -| Open Source | Open Source | unten auf der Seite | -| Documentation | Dokumentation | unten auf der Seite | -| {version} | {version} | unten auf der Seite | - -## Main “Sites” -##### “Hero” section - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Newt (Recommended) | Newt (empfohlen) | | -| For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard. | Für das beste Benutzererlebnis verwenden Sie Newt. Es nutzt WireGuard im Hintergrund und ermöglicht es Ihnen, auf Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk direkt aus dem Pangolin-Dashboard zuzugreifen. | | -| Runs in Docker | Läuft in Docker | | -| Runs in shell on macOS, Linux, and Windows | Läuft in der Shell auf macOS, Linux und Windows | | -| Install Newt | Newt installieren | | -| Basic WireGuard
    | Verwenden Sie einen beliebigen WireGuard-Client, um eine Verbindung herzustellen. Sie müssen auf Ihre internen Ressourcen über die Peer-IP-Adresse zugreifen. | | -| Compatible with all WireGuard clients
    | Kompatibel mit allen WireGuard-Clients
    | | -| Manual configuration required | Manuelle Konfiguration erforderlich
    | | -##### Content - -| EN | DE | Notes | -| --------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------- | -| Manage Sites | Seiten verwalten | | -| Allow connectivity to your network through secure tunnels | Ermöglichen Sie die Verbindung zu Ihrem Netzwerk über ein sicheren Tunnel | | -| Search sites | Seiten suchen | placeholder | -| Add Site | Seite hinzufügen | | -| Name | Name | table header | -| Online | Status | table header | -| Site | Seite | table header | -| Data In | Eingehende Daten | table header | -| Data Out | Ausgehende Daten | table header | -| Connection Type | Verbindungstyp | table header | -| Online | Online | site state | -| Offline | Offline | site state | -| Edit → | Bearbeiten → | | -| View settings | Einstellungen anzeigen | Popup after clicking “…” on site | -| Delete | Löschen | Popup after clicking “…” on site | -##### Add Site Popup - -| EN | DE | Notes | -| ------------------------------------------------------ | ----------------------------------------------------------- | ----------- | -| Create Site | Seite erstellen | | -| Create a new site to start connection for this site | Erstellen Sie eine neue Seite, um die Verbindung zu starten | | -| Name | Name | | -| Site name | Seiten-Name | placeholder | -| This is the name that will be displayed for this site. | So wird Ihre Seite angezeigt | desc | -| Method | Methode | | -| Local | Lokal | | -| Newt | Newt | | -| WireGuard | WireGuard | | -| This is how you will expose connections. | So werden Verbindungen freigegeben. | | -| You will only be able to see the configuration once. | Diese Konfiguration können Sie nur einmal sehen. | | -| Learn how to install Newt on your system | Erfahren Sie, wie Sie Newt auf Ihrem System installieren | | -| I have copied the config | Ich habe die Konfiguration kopiert | | -| Create Site | Website erstellen | | -| Close | Schließen | | - -## Main “Resources” - -##### “Hero” section - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Resources | Ressourcen | | -| Ressourcen sind Proxy-Server für Anwendungen, die in Ihrem privaten Netzwerk laufen. Erstellen Sie eine Ressource für jede HTTP- oder HTTPS-Anwendung in Ihrem privaten Netzwerk. Jede Ressource muss mit einer Website verbunden sein, um eine private und sichere Verbindung über den verschlüsselten WireGuard-Tunnel zu ermöglichen. | Ressourcen sind Proxy-Server für Anwendungen, die in Ihrem privaten Netzwerk laufen. Erstellen Sie eine Ressource für jede HTTP- oder HTTPS-Anwendung in Ihrem privaten Netzwerk. Jede Ressource muss mit einer Website verbunden sein, um eine private und sichere Verbindung über den verschlüsselten WireGuard-Tunnel zu ermöglichen. | | -| Secure connectivity with WireGuard encryption | Sichere Verbindung mit WireGuard-Verschlüsselung | | -| Configure multiple authentication methods | Konfigurieren Sie mehrere Authentifizierungsmethoden | | -| User and role-based access control | Benutzer- und rollenbasierte Zugriffskontrolle | | -##### Content - -| EN | DE | Notes | -| -------------------------------------------------- | ---------------------------------------------------------- | -------------------- | -| Manage Resources | Ressourcen verwalten | | -| Create secure proxies to your private applications | Erstellen Sie sichere Proxys für Ihre privaten Anwendungen | | -| Search resources | Ressourcen durchsuchen | placeholder | -| Name | Name | | -| Site | Website | | -| Full URL | Vollständige URL | | -| Authentication | Authentifizierung | | -| Not Protected | Nicht geschützt | authentication state | -| Protected | Geschützt | authentication state | -| Edit → | Bearbeiten → | | -| Add Resource | Ressource hinzufügen | | -##### Add Resource Popup - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------- | -| Create Resource | Ressource erstellen | | -| Create a new resource to proxy request to your app | Erstellen Sie eine neue Ressource, um Anfragen an Ihre App zu proxen | | -| Name | Name | | -| My Resource | Neue Ressource | name placeholder | -| This is the name that will be displayed for this resource. | Dies ist der Name, der für diese Ressource angezeigt wird | | -| Subdomain | Subdomain | | -| Enter subdomain | Subdomain eingeben | | -| This is the fully qualified domain name that will be used to access the resource. | Dies ist der vollständige Domainname, der für den Zugriff auf die Ressource verwendet wird. | | -| Site | Website | | -| Search site… | Website suchen… | Site selector popup | -| This is the site that will be used in the dashboard. | Dies ist die Website, die im Dashboard verwendet wird. | | -| Create Resource | Ressource erstellen | | -| Close | Schließen | | - - -## Main “User & Roles” -##### Content - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------------- | -| Manage User & Roles | Benutzer & Rollen verwalten | | -| Invite users and add them to roles to manage access to your organization | Laden Sie Benutzer ein und weisen Sie ihnen Rollen zu, um den Zugriff auf Ihre Organisation zu verwalten | | -| Users | Benutzer | sidebar item | -| Roles | Rollen | sidebar item | -| **User tab** | | | -| Search users | Benutzer suchen | placeholder | -| Invite User | Benutzer einladen | addbutton | -| Email | E-Mail | table header | -| Status | Status | table header | -| Role | Rolle | table header | -| Confirmed | Bestätigt | account status | -| Not confirmed (?) | Nicht bestätigt (?) | unknown for me account status | -| Owner | Besitzer | role | -| Admin | Administrator | role | -| Member | Mitglied | role | -| **Roles Tab** | | | -| Search roles | Rollen suchen | placeholder | -| Add Role | Rolle hinzufügen | addbutton | -| Name | Name | table header | -| Description | Beschreibung | table header | -| Admin | Administrator | role | -| Member | Mitglied | role | -| Admin role with the most permissions | Administratorrolle mit den meisten Berechtigungen | admin role desc | -| Members can only view resources | Mitglieder können nur Ressourcen anzeigen | member role desc | - -##### Invite User popup - -| EN | DE | Notes | -| ----------------- | ------------------------------------------------------- | ----------- | -| Invite User | Geben Sie neuen Benutzern Zugriff auf Ihre Organisation | | -| Email | E-Mail | | -| Enter an email | E-Mail eingeben | placeholder | -| Role | Rolle | | -| Select role | Rolle auswählen | placeholder | -| Gültig für | Gültig bis | | -| 1 day | Tag | | -| 2 days | 2 Tage | | -| 3 days | 3 Tage | | -| 4 days | 4 Tage | | -| 5 days | 5 Tage | | -| 6 days | 6 Tage | | -| 7 days | 7 Tage | | -| Create Invitation | Einladung erstellen | | -| Close | Schließen | | - - -## Main “Shareable Links” -##### “Hero” section - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Shareable Links | Teilbare Links | | -| Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one. | Erstellen Sie teilbare Links zu Ihren Ressourcen. Links bieten temporären oder unbegrenzten Zugriff auf Ihre Ressource. Sie können die Gültigkeitsdauer des Links beim Erstellen konfigurieren. | | -| Easy to create and share | Einfach zu erstellen und zu teilen | | -| Configurable expiration duration | Konfigurierbare Gültigkeitsdauer | | -| Secure and revocable | Sicher und widerrufbar | | -##### Content - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------- | -| Manage Shareable Links | Teilbare Links verwalten | | -| Create shareable links to grant temporary or permanent access to your resources | Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren | | -| Search links | Links suchen | placeholder | -| Create Share Link | Neuen Link erstellen | addbutton | -| Resource | Ressource | table header | -| Title | Titel | table header | -| Created | Erstellt | table header | -| Expires | Gültig bis | table header | -| No links. Create one to get started. | Keine Links. Erstellen Sie einen, um zu beginnen. | table placeholder | - -##### Create Shareable Link popup - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------- | -| Create Shareable Link | Teilbaren Link erstellen | | -| Anyone with this link can access the resource | Jeder mit diesem Link kann auf die Ressource zugreifen | | -| Resource | Ressource | | -| Select resource | Ressource auswählen | | -| Search resources… | Ressourcen suchen… | resource selector popup | -| Title (optional) | Titel (optional) | | -| Enter title | Titel eingeben | placeholder | -| Expire in | Gültig bis | | -| Minutes | Minuten | | -| Hours | Stunden | | -| Days | Tage | | -| Months | Monate | | -| Years | Jahre | | -| Never expire | Nie ablaufen | | -| Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource. | Die Gültigkeitsdauer bestimmt, wie lange der Link nutzbar ist und Zugriff auf die Ressource bietet. Nach Ablauf dieser Zeit funktioniert der Link nicht mehr, und Benutzer, die diesen Link verwendet haben, verlieren den Zugriff auf die Ressource. | | -| Create Link | Link erstellen | | -| Close | Schließen | | - - -## Main “General” - -| EN | DE | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------ | -| General | Allgemein | | -| Configure your organization’s general settings | Konfigurieren Sie die allgemeinen Einstellungen Ihrer Organisation | | -| General | Allgemein | sidebar item | -| Organization Settings | Organisationseinstellungen | | -| Manage your organization details and configuration | Verwalten Sie die Details und Konfiguration Ihrer Organisation | | -| Name | Name | | -| This is the display name of the org | Dies ist der Anzeigename Ihrer Organisation | | -| Save Settings | Einstellungen speichern | | -| Danger Zone | Gefahrenzone | | -| Once you delete this org, there is no going back. Please be certain. | Wenn Sie diese Organisation löschen, gibt es kein Zurück. Bitte seien Sie sicher. | | -| Delete Organization Data | Organisationsdaten löschen | | \ No newline at end of file diff --git a/internationalization/es.md b/internationalization/es.md deleted file mode 100644 index c4477fbf..00000000 --- a/internationalization/es.md +++ /dev/null @@ -1,291 +0,0 @@ -## Authentication Site - - -| EN | ES | Notes | -| -------------------------------------------------------- | ------------------------------------------------------------ | ---------- | -| Powered by [Pangolin](https://github.com/fosrl/pangolin) | Desarrollado por [Pangolin](https://github.com/fosrl/pangolin) | | -| Authentication Required | Se requiere autenticación | | -| Choose your preferred method to access {resource} | Elije tu método requerido para acceder a {resource} | | -| PIN | PIN | | -| User | Usuario | | -| 6-digit PIN Code | Código PIN de 6 dígitos | pin login | -| Login in with PIN | Registrate con PIN | pin login | -| Email | Email | user login | -| Enter your email | Introduce tu email | user login | -| Password | Contraseña | user login | -| Enter your password | Introduce tu contraseña | user login | -| Forgot your password? | ¿Olvidaste tu contraseña? | user login | -| Log in | Iniciar sesión | user login | - - -## Login site - -| EN | ES | Notes | -| --------------------- | ---------------------------------- | ----------- | -| Welcome to Pangolin | Binvenido a Pangolin | | -| Log in to get started | Registrate para comenzar | | -| Email | Email | | -| Enter your email | Introduce tu email | placeholder | -| Password | Contraseña | | -| Enter your password | Introduce tu contraseña | placeholder | -| Forgot your password? | ¿Olvidaste tu contraseña? | | -| Log in | Iniciar sesión | | - -# Ogranization site after successful login - -| EN | ES | Notes | -| ----------------------------------------- | -------------------------------------------- | ----- | -| Welcome to Pangolin | Binvenido a Pangolin | | -| You're a member of {number} organization. | Eres miembro de la organización {number}. | | - -## Shared Header, Navbar and Footer -##### Header - -| EN | ES | Notes | -| ------------------- | ------------------- | ----- | -| Documentation | Documentación | | -| Support | Soporte | | -| Organization {name} | Organización {name} | | -##### Organization selector - -| EN | ES | Notes | -| ---------------- | ----------------- | ----- | -| Search… | Buscar… | | -| Create | Crear | | -| New Organization | Nueva Organización| | -| Organizations | Organizaciones | | - -##### Navbar - -| EN | ES | Notes | -| --------------- | -----------------------| ----- | -| Sites | Sitios | | -| Resources | Recursos | | -| User & Roles | Usuarios y roles | | -| Shareable Links | Enlaces para compartir | | -| General | General | | - -##### Footer -| EN | ES | | -| ------------------------- | --------------------------- | -------| -| Page {number} of {number} | Página {number} de {number} | footer | -| Rows per page | Filas por página | footer | -| Pangolin | Pangolin | footer | -| Built by Fossorial | Construido por Fossorial | footer | -| Open Source | Código abierto | footer | -| Documentation | Documentación | footer | -| {version} | {version} | footer | - -## Main “Sites” -##### “Hero” section - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Newt (Recommended) | Newt (Recomendado) | | -| For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard. | Para obtener la mejor experiencia de usuario, utiliza Newt. Utiliza WireGuard internamente y te permite abordar tus recursos privados mediante tu dirección LAN en tu red privada desde el panel de Pangolin. | | -| Runs in Docker | Se ejecuta en Docker | | -| Runs in shell on macOS, Linux, and Windows | Se ejecuta en shell en macOS, Linux y Windows | | -| Install Newt | Instalar Newt | | -| Basic WireGuard
    | WireGuard básico
    | | -| Compatible with all WireGuard clients
    | Compatible con todos los clientes WireGuard
    | | -| Manual configuration required | Se requiere configuración manual | | - -##### Content - -| EN | ES | Notes | -| --------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------- | -| Manage Sites | Administrar sitios | | -| Allow connectivity to your network through secure tunnels | Permitir la conectividad a tu red a través de túneles seguros| | -| Search sites | Buscar sitios | placeholder | -| Add Site | Agregar sitio | | -| Name | Nombre | table header | -| Online | Conectado | table header | -| Site | Sitio | table header | -| Data In | Datos en | table header | -| Data Out | Datos de salida | table header | -| Connection Type | Tipo de conexión | table header | -| Online | Conectado | site state | -| Offline | Desconectado | site state | -| Edit → | Editar → | | -| View settings | Ver configuración | Popup after clicking “…” on site | -| Delete | Borrar | Popup after clicking “…” on site | - -##### Add Site Popup - -| EN | ES | Notes | -| ------------------------------------------------------ | ----------------------------------------------------------- | ----------- | -| Create Site | Crear sitio | | -| Create a new site to start connection for this site | Crear un nuevo sitio para iniciar la conexión para este sitio | | -| Name | Nombre | | -| Site name | Nombre del sitio | placeholder | -| This is the name that will be displayed for this site. | Este es el nombre que se mostrará para este sitio. | desc | -| Method | Método | | -| Local | Local | | -| Newt | Newt | | -| WireGuard | WireGuard | | -| This is how you will expose connections. | Así es como expondrás las conexiones. | | -| You will only be able to see the configuration once. | Solo podrás ver la configuración una vez. | | -| Learn how to install Newt on your system | Aprende a instalar Newt en tu sistema | | -| I have copied the config | He copiado la configuración | | -| Create Site | Crear sitio | | -| Close | Cerrar | | - -## Main “Resources” - -##### “Hero” section - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Resources | Recursos | | -| Ressourcen sind Proxy-Server für Anwendungen, die in Ihrem privaten Netzwerk laufen. Erstellen Sie eine Ressource für jede HTTP- oder HTTPS-Anwendung in Ihrem privaten Netzwerk. Jede Ressource muss mit einer Website verbunden sein, um eine private und sichere Verbindung über den verschlüsselten WireGuard-Tunnel zu ermöglichen. |Los recursos son servidores proxy para aplicaciones que se ejecutan en su red privada. Cree un recurso para cada aplicación HTTP o HTTPS en su red privada. Cada recurso debe estar conectado a un sitio web para proporcionar una conexión privada y segura a través del túnel cifrado WireGuard. | | -| Secure connectivity with WireGuard encryption | Conectividad segura con encriptación WireGuard | | -| Configure multiple authentication methods | Configura múltiples métodos de autenticación | | -| User and role-based access control | Control de acceso basado en usuarios y roles | | - -##### Content - -| EN | ES | Notes | -| -------------------------------------------------- | ---------------------------------------------------------- | -------------------- | -| Manage Resources | Administrar recursos | | -| Create secure proxies to your private applications | Crea servidores proxy seguros para tus aplicaciones privadas | | -| Search resources | Buscar recursos | placeholder | -| Name | Nombre | | -| Site | Sitio | | -| Full URL | URL completa | | -| Authentication | Autenticación | | -| Not Protected | No protegido | authentication state | -| Protected | Protegido | authentication state | -| Edit → | Editar → | | -| Add Resource | Agregar recurso | | - -##### Add Resource Popup - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------- | -| Create Resource | Crear recurso | | -| Create a new resource to proxy request to your app | Crea un nuevo recurso para enviar solicitudes a tu aplicación | | -| Name | Nombre | | -| My Resource | Mi recurso | name placeholder | -| This is the name that will be displayed for this resource. | Este es el nombre que se mostrará para este recurso. | | -| Subdomain | Subdominio | | -| Enter subdomain | Ingresar subdominio | | -| This is the fully qualified domain name that will be used to access the resource. | Este es el nombre de dominio completo que se utilizará para acceder al recurso. | | -| Site | Sitio | | -| Search site… | Buscar sitio… | Site selector popup | -| This is the site that will be used in the dashboard. | Este es el sitio que se utilizará en el panel de control. | | -| Create Resource | Crear recurso | | -| Close | Cerrar | | - -## Main “User & Roles” -##### Content - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------------- | -| Manage User & Roles | Administrar usuarios y roles | | -| Invite users and add them to roles to manage access to your organization | Invita a usuarios y agrégalos a roles para administrar el acceso a tu organización | | -| Users | Usuarios | sidebar item | -| Roles | Roles | sidebar item | -| **User tab** | **Pestaña de usuario** | | -| Search users | Buscar usuarios | placeholder | -| Invite User | Invitar usuario | addbutton | -| Email | Email | table header | -| Status | Estado | table header | -| Role | Role | table header | -| Confirmed | Confirmado | account status | -| Not confirmed (?) | No confirmado (?) | unknown for me account status | -| Owner | Dueño | role | -| Admin | Administrador | role | -| Member | Miembro | role | -| **Roles Tab** | **Pestaña Roles** | | -| Search roles | Buscar roles | placeholder | -| Add Role | Agregar rol | addbutton | -| Name | Nombre | table header | -| Description | Descripción | table header | -| Admin | Administrador | role | -| Member | Miembro | role | -| Admin role with the most permissions | Rol de administrador con más permisos | admin role desc | -| Members can only view resources | Los miembros sólo pueden ver los recursos | member role desc | - -##### Invite User popup - -| EN | ES | Notes | -| ----------------- | ------------------------------------------------------- | ----------- | -| Invite User | Invitar usuario | | -| Email | Email | | -| Enter an email | Introduzca un email | placeholder | -| Role | Rol | | -| Select role | Seleccionar rol | placeholder | -| Gültig für | Válido para | | -| 1 day | 1 día | | -| 2 days | 2 días | | -| 3 days | 3 días | | -| 4 days | 4 días | | -| 5 days | 5 días | | -| 6 days | 6 días | | -| 7 days | 7 días | | -| Create Invitation | Crear invitación | | -| Close | Cerrar | | - - -## Main “Shareable Links” -##### “Hero” section - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----- | -| Shareable Links | Enlaces para compartir | | -| Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one. | Crear enlaces que se puedan compartir a tus recursos. Los enlaces proporcionan acceso temporal o ilimitado a tu recurso. Puedes configurar la duración de caducidad del enlace cuando lo creas. | | -| Easy to create and share | Fácil de crear y compartir | | -| Configurable expiration duration | Duración de expiración configurable | | -| Secure and revocable | Seguro y revocable | | -##### Content - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------- | -| Manage Shareable Links | Administrar enlaces compartibles | | -| Create shareable links to grant temporary or permanent access to your resources | Crear enlaces compartibles para otorgar acceso temporal o permanente a tus recursos | | -| Search links | Buscar enlaces | placeholder | -| Create Share Link | Crear enlace para compartir | addbutton | -| Resource | Recurso | table header | -| Title | Título | table header | -| Created | Creado | table header | -| Expires | Caduca | table header | -| No links. Create one to get started. | No hay enlaces. Crea uno para comenzar. | table placeholder | - -##### Create Shareable Link popup - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------- | -| Create Shareable Link | Crear un enlace para compartir | | -| Anyone with this link can access the resource | Cualquier persona con este enlace puede acceder al recurso. | | -| Resource | Recurso | | -| Select resource | Seleccionar recurso | | -| Search resources… | Buscar recursos… | resource selector popup | -| Title (optional) | Título (opcional) | | -| Enter title | Introducir título | placeholder | -| Expire in | Caduca en | | -| Minutes | Minutos | | -| Hours | Horas | | -| Days | Días | | -| Months | Meses | | -| Years | Años | | -| Never expire | Nunca caduca | | -| Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource. | El tiempo de expiración es el tiempo durante el cual el enlace se podrá utilizar y brindará acceso al recurso. Después de este tiempo, el enlace dejará de funcionar y los usuarios que lo hayan utilizado perderán el acceso al recurso. | | -| Create Link | Crear enlace | | -| Close | Cerrar | | - - -## Main “General” - -| EN | ES | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------ | -| General | General | | -| Configure your organization’s general settings | Configura los ajustes generales de tu organización | | -| General | General | sidebar item | -| Organization Settings | Configuración de la organización | | -| Manage your organization details and configuration | Administra los detalles y la configuración de tu organización| | -| Name | Nombre | | -| This is the display name of the org | Este es el nombre para mostrar de la organización. | | -| Save Settings | Guardar configuración | | -| Danger Zone | Zona de peligro | | -| Once you delete this org, there is no going back. Please be certain. | Una vez que elimines esta organización, no habrá vuelta atrás. Asegúrate de hacerlo. | | -| Delete Organization Data | Eliminar datos de la organización | | \ No newline at end of file diff --git a/internationalization/pl.md b/internationalization/pl.md deleted file mode 100644 index a55866e2..00000000 --- a/internationalization/pl.md +++ /dev/null @@ -1,287 +0,0 @@ -## Authentication Site - - -| EN | PL | Notes | -| -------------------------------------------------------- | ------------------------------------------------------------ | ---------- | -| Powered by [Pangolin](https://github.com/fosrl/pangolin) | Zasilane przez [Pangolin](https://github.com/fosrl/pangolin) | | -| Authentication Required | Wymagane uwierzytelnienie | | -| Choose your preferred method to access {resource} | Wybierz preferowaną metodę dostępu do {resource} | | -| PIN | PIN | | -| User | Zaloguj | | -| 6-digit PIN Code | 6-cyfrowy kod PIN | pin login | -| Login in with PIN | Zaloguj się PIN’em | pin login | -| Email | Email | user login | -| Enter your email | Wprowadź swój email | user login | -| Password | Hasło | user login | -| Enter your password | Wprowadź swoje hasło | user login | -| Forgot your password? | Zapomniałeś hasła? | user login | -| Log in | Zaloguj | user login | - - -## Login site - -| EN | PL | Notes | -| --------------------- | ------------------------------ | ----------- | -| Welcome to Pangolin | Witaj w Pangolin | | -| Log in to get started | Zaloguj się, aby rozpocząć
    | | -| Email | Email | | -| Enter your email | Wprowadź swój adres e-mail
    | placeholder | -| Password | Hasło | | -| Enter your password | Wprowadź swoje hasło | placeholder | -| Forgot your password? | Nie pamiętasz hasła? | | -| Log in | Zaloguj | | - -# Ogranization site after successful login - - -| EN | PL | Notes | -| ----------------------------------------- | ------------------------------------------ | ----- | -| Welcome to Pangolin | Witaj w Pangolin | | -| You're a member of {number} organization. | Jesteś użytkownikiem {number} organizacji. | | - -## Shared Header, Navbar and Footer -##### Header - -| EN | PL | Notes | -| ------------------- | ------------------ | ----- | -| Documentation | Dokumentacja | | -| Support | Wsparcie | | -| Organization {name} | Organizacja {name} | | -##### Organization selector - -| EN | PL | Notes | -| ---------------- | ---------------- | ----- | -| Search… | Szukaj… | | -| Create | Utwórz | | -| New Organization | Nowa organizacja | | -| Organizations | Organizacje | | - -##### Navbar - -| EN | PL | Notes | -| --------------- | ---------------------- | ----- | -| Sites | Witryny | | -| Resources | Zasoby | | -| User & Roles | Użytkownicy i Role | | -| Shareable Links | Łącza do udostępniania | | -| General | Ogólne | | -##### Footer -| EN | PL | | -| ------------------------- | -------------------------- | -------------- | -| Page {number} of {number} | Strona {number} z {number} | | -| Rows per page | Wierszy na stronę | | -| Pangolin | Pangolin | bottom of site | -| Built by Fossorial | Stworzone przez Fossorial | bottom of site | -| Open Source | Open source | bottom of site | -| Documentation | Dokumentacja | bottom of site | -| {version} | {version} | bottom of site | -## Main “Sites” -##### “Hero” section - -| EN | PL | Notes | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | -| Newt (Recommended) | Newt (zalecane) | | -| For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard. | Aby zapewnić najlepsze doświadczenie użytkownika, korzystaj z Newt. Wykorzystuje on technologię WireGuard w tle i pozwala na dostęp do Twoich prywatnych zasobów za pomocą ich adresu LAN w prywatnej sieci bezpośrednio z poziomu pulpitu nawigacyjnego Pangolin. | | -| Runs in Docker | Działa w Dockerze | | -| Runs in shell on macOS, Linux, and Windows | Działa w powłoce na systemach macOS, Linux i Windows | | -| Install Newt | Zainstaluj Newt | | -| Podstawowy WireGuard
    | Użyj dowolnego klienta WireGuard, aby się połączyć. Będziesz musiał uzyskiwać dostęp do swoich wewnętrznych zasobów za pomocą adresu IP równorzędnego | | -| Compatible with all WireGuard clients
    | Kompatybilny ze wszystkimi klientami WireGuard
    | | -| Manual configuration required | Wymagana ręczna konfiguracja
    | | -##### Content - -| EN | PL | Notes | -| --------------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------- | -| Manage Sites | Zarządzanie witrynami | | -| Allow connectivity to your network through secure tunnels | Zezwalaj na łączność z Twoją siecią za pośrednictwem bezpiecznych tuneli | | -| Search sites | Szukaj witryny | placeholder | -| Add Site | Dodaj witrynę | | -| Name | Nazwa | table header | -| Online | Status | table header | -| Site | Witryna | table header | -| Data In | Dane wchodzące | table header | -| Data Out | Dane wychodzące | table header | -| Connection Type | Typ połączenia | table header | -| Online | Online | site state | -| Offline | Poza siecią | site state | -| Edit → | Edytuj → | | -| View settings | Pokaż ustawienia | Popup after clicking “…” on site | -| Delete | Usuń | Popup after clicking “…” on site | -##### Add Site Popup - -| EN | PL | Notes | -| ------------------------------------------------------ | --------------------------------------------------- | ----------- | -| Create Site | Utwórz witrynę | | -| Create a new site to start connection for this site | Utwórz nową witrynę aby rozpocząć połączenie | | -| Name | Nazwa | | -| Site name | Nazwa witryny | placeholder | -| This is the name that will be displayed for this site. | Tak będzie wyświetlana twoja witryna | desc | -| Method | Metoda | | -| Local | Lokalna | | -| Newt | Newt | | -| WireGuard | WireGuard | | -| This is how you will expose connections. | Tak będą eksponowane połączenie. | | -| You will only be able to see the configuration once. | Tą konfigurację możesz zobaczyć tylko raz. | | -| Learn how to install Newt on your system | Dowiedz się jak zainstalować Newt na twoim systemie | | -| I have copied the config | Skopiowałem konfigurację | | -| Create Site | Utwórz witrynę | | -| Close | Zamknij | | - -## Main “Resources” - -##### “Hero” section - -| EN | PL | Notes | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | -| Resources | Zasoby | | -| Zasoby to serwery proxy dla aplikacji działających w Twojej prywatnej sieci. Utwórz zasób dla dowolnej aplikacji HTTP lub HTTPS w swojej prywatnej sieci. Każdy zasób musi być połączony z witryną, aby umożliwić prywatne i bezpieczne połączenie przez szyfrowany tunel WireGuard. | Zasoby to serwery proxy dla aplikacji działających w Twojej prywatnej sieci. Utwórz zasób dla dowolnej aplikacji HTTP lub HTTPS w swojej prywatnej sieci. Każdy zasób musi być połączony z witryną, aby umożliwić prywatne i bezpieczne połączenie przez szyfrowany tunel WireGuard. | | -| Secure connectivity with WireGuard encryption | Bezpieczna łączność z szyfrowaniem WireGuard | | -| Configure multiple authentication methods | Konfigurowanie wielu metod uwierzytelniania | | -| User and role-based access control | Kontrola dostępu oparta na użytkownikach i rolach | | -##### Content - -| EN | PL | Notes | -| -------------------------------------------------- | -------------------------------------------------------------- | -------------------- | -| Manage Resources | Zarządzaj zasobami | | -| Create secure proxies to your private applications | Twórz bezpieczne serwery proxy dla swoich prywatnych aplikacji | | -| Search resources | Szukaj w zasobach | placeholder | -| Name | Nazwa | | -| Site | Witryna | | -| Full URL | Pełny URL | | -| Authentication | Uwierzytelnianie | | -| Not Protected | Niezabezpieczony | authentication state | -| Protected | Zabezpieczony | authentication state | -| Edit → | Edytuj → | | -| Add Resource | Dodaj zasób | | -##### Add Resource Popup - -| EN | PL | Notes | -| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------- | -| Create Resource | Utwórz zasób | | -| Create a new resource to proxy request to your app | Utwórz nowy zasób, aby przekazywać żądania do swojej aplikacji | | -| Name | Nazwa | | -| My Resource | Nowy zasób | name placeholder | -| This is the name that will be displayed for this resource. | To jest nazwa, która będzie wyświetlana dla tego zasobu | | -| Subdomain | Subdomena | | -| Enter subdomain | Wprowadź subdomenę | | -| This is the fully qualified domain name that will be used to access the resource. | To jest pełna nazwa domeny, która będzie używana do dostępu do zasobu. | | -| Site | Witryna | | -| Search site… | Szukaj witryny… | Site selector popup | -| This is the site that will be used in the dashboard. | To jest witryna, która będzie używana w pulpicie nawigacyjnym. | | -| Create Resource | Utwórz zasób | | -| Close | Zamknij | | - - -## Main “User & Roles” -##### Content - -| EN | PL | Notes | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ----------------------------- | -| Manage User & Roles | Zarządzanie użytkownikami i rolami | | -| Invite users and add them to roles to manage access to your organization | Zaproś użytkowników i przypisz im role, aby zarządzać dostępem do Twojej organizacji | | -| Users | Użytkownicy | sidebar item | -| Roles | Role | sidebar item | -| **User tab** | | | -| Search users | Wyszukaj użytkownika | placeholder | -| Invite User | Zaproś użytkownika | addbutton | -| Email | Email | table header | -| Status | Status | table header | -| Role | Rola | table header | -| Confirmed | Zatwierdzony | account status | -| Not confirmed (?) | Niezatwierdzony (?) | unknown for me account status | -| Owner | Właściciel | role | -| Admin | Administrator | role | -| Member | Użytkownik | role | -| **Roles Tab** | | | -| Search roles | Wyszukaj role | placeholder | -| Add Role | Dodaj role | addbutton | -| Name | Nazwa | table header | -| Description | Opis | table header | -| Admin | Administrator | role | -| Member | Użytkownik | role | -| Admin role with the most permissions | Rola administratora z najszerszymi uprawnieniami | admin role desc | -| Members can only view resources | Członkowie mogą jedynie przeglądać zasoby | member role desc | - -##### Invite User popup - -| EN | PL | Notes | -| ----------------- | ------------------------------------------ | ----------- | -| Invite User | Give new users access to your organization | | -| Email | Email | | -| Enter an email | Wprowadź email | placeholder | -| Role | Rola | | -| Select role | Wybierz role | placeholder | -| Vaild for | Ważne do | | -| 1 day | Dzień | | -| 2 days | 2 dni | | -| 3 days | 3 dni | | -| 4 days | 4 dni | | -| 5 days | 5 dni | | -| 6 days | 6 dni | | -| 7 days | 7 dni | | -| Create Invitation | Utwórz zaproszenie | | -| Close | Zamknij | | - - -## Main “Shareable Links” -##### “Hero” section - -| EN | PL | Notes | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| Shareable Links | Łącza do udostępniania | | -| Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one. | Twórz linki do udostępniania swoich zasobów. Linki zapewniają tymczasowy lub nieograniczony dostęp do zasobu. Możesz skonfigurować czas wygaśnięcia linku podczas jego tworzenia. | | -| Easy to create and share | Łatwe tworzenie i udostępnianie | | -| Configurable expiration duration | Konfigurowalny czas wygaśnięcia | | -| Secure and revocable | Bezpieczne i odwołalne | | -##### Content - -| EN | PL | Notes | -| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------- | -| Manage Shareable Links | Zarządzaj łączami do udostępniania | | -| Create shareable links to grant temporary or permament access to your resources | Utwórz łącze do udostępniania w celu przyznania tymczasowego lub stałego dostępu do zasobów | | -| Search links | Szukaj łączy | placeholder | -| Create Share Link | Utwórz nowe łącze | addbutton | -| Resource | Zasób | table header | -| Title | Tytuł | table header | -| Created | Utworzone | table header | -| Expires | Wygasa | table header | -| No links. Create one to get started. | Brak łączy. Utwórz, aby rozpocząć. | table placeholder | - -##### Create Shareable Link popup - -| EN | PL | Notes | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | -| Create Shareable Link | Utwórz łącze do udostępnienia | | -| Anyone with this link can access the resource | Każdy kto ma ten link może korzystać z zasobu | | -| Resource | Zasób | | -| Select resource | Wybierz zasób | | -| Search resources… | Szukaj zasobów… | resource selector popup | -| Title (optional) | Tytuł (opcjonalny) | | -| Enter title | Wprowadź tytuł | placeholder | -| Expire in | Wygasa za | | -| Minutes | Minut | | -| Hours | Godzin | | -| Days | Dni | | -| Months | Miesięcy | | -| Years | Lat | | -| Never expire | Nie wygasa | | -| Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource. | Czas wygaśnięcia to okres, przez który link będzie aktywny i zapewni dostęp do zasobu. Po upływie tego czasu link przestanie działać, a użytkownicy, którzy go użyli, stracą dostęp do zasobu. | | -| Create Link | Utwórz łącze | | -| Close | Zamknij | | - - -## Main “General” - -| EN | PL | Notes | -| -------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------ | -| General | Ogólne | | -| Configure your organization’s general settings | Zarządzaj ogólnymi ustawieniami twoich organizacji | | -| General | Ogólne | sidebar item | -| Organization Settings | Ustawienia organizacji | | -| Manage your organization details and configuration | Zarządzaj szczegółami i konfiguracją organizacji | | -| Name | Nazwa | | -| This is the display name of the org | To jest wyświetlana nazwa Twojej organizacji | | -| Save Settings | Zapisz ustawienia | | -| Danger Zone | Niebezpieczna strefa | | -| Once you delete this org, there is no going back. Please be certain. | Jeśli usuniesz swoją tą organizację, nie ma odwrotu. Bądź ostrożny! | | -| Delete Organization Data | Usuń dane organizacji | | diff --git a/internationalization/tr.md b/internationalization/tr.md deleted file mode 100644 index 9e5bd274..00000000 --- a/internationalization/tr.md +++ /dev/null @@ -1,310 +0,0 @@ -## Authentication Site - -| EN | TR | Notes | -| -------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------- | -| Powered by [Pangolin](https://github.com/fosrl/pangolin) | Pangolin Tarafından Destekleniyor | | -| Authentication Required | Kimlik Doğrulaması Gerekli | | -| Choose your preferred method to access {resource} | {resource}'a erişmek için tercih ettiğiniz yöntemi seçin | | -| PIN | PIN | | -| User | Kullanıcı | | -| 6-digit PIN Code | 6 haneli PIN Kodu | pin login | -| Login in with PIN | PIN ile Giriş Yap | pin login | -| Email | E-posta | user login | -| Enter your email | E-postanızı girin | user login | -| Password | Şifre | user login | -| Enter your password | Şifrenizi girin | user login | -| Forgot your password? | Şifrenizi mi unuttunuz? | user login | -| Log in | Giriş Yap | user login | - ---- - -## Login site - -| EN | TR | Notes | -| --------------------- | ------------------------------------------------------ | ----------- | -| Welcome to Pangolin | Pangolin'e Hoşgeldiniz | | -| Log in to get started | Başlamak için giriş yapın | | -| Email | E-posta | | -| Enter your email | E-posta adresinizi girin | placeholder | -| Password | Şifre | | -| Enter your password | Şifrenizi girin | placeholder | -| Forgot your password? | Şifrenizi mi unuttunuz? | | -| Log in | Giriş Yap | | - ---- - -# Organization site after successful login - -| EN | TR | Notes | -| ----------------------------------------- | ------------------------------------------------------------------- | ----- | -| Welcome to Pangolin | Pangolin'e Hoşgeldiniz | | -| You're a member of {number} organization. | {number} organizasyonunun üyesiniz. | | - ---- - -## Shared Header, Navbar and Footer - -##### Header - -| EN | TR | Notes | -| ------------------- | -------------------------- | ----- | -| Documentation | Dokümantasyon | | -| Support | Destek | | -| Organization {name} | Organizasyon {name} | | - -##### Organization selector - -| EN | TR | Notes | -| ---------------- | ---------------------- | ----- | -| Search… | Ara… | | -| Create | Oluştur | | -| New Organization | Yeni Organizasyon | | -| Organizations | Organizasyonlar | | - -##### Navbar - -| EN | TR | Notes | -| --------------- | ------------------------------- | ----- | -| Sites | Siteler | | -| Resources | Kaynaklar | | -| User & Roles | Kullanıcılar ve Roller | | -| Shareable Links | Paylaşılabilir Linkler | | -| General | Genel | | - -##### Footer - -| EN | TR | Notes | -| ------------------------- | ------------------------------------------------ | -------------------- | -| Page {number} of {number} | Sayfa {number} / {number} | | -| Rows per page | Sayfa başına satırlar | | -| Pangolin | Pangolin | Footer'da yer alır | -| Built by Fossorial | Fossorial tarafından oluşturuldu | Footer'da yer alır | -| Open Source | Açık Kaynak | Footer'da yer alır | -| Documentation | Dokümantasyon | Footer'da yer alır | -| {version} | {version} | Footer'da yer alır | - ---- - -## Main “Sites” - -##### “Hero” section - -| EN | TR | Notes | -| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | ----- | -| Newt (Recommended) | Newt (Tavsiye Edilen) | | -| For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard. | En iyi kullanıcı deneyimi için Newt'i kullanın. Newt, arka planda WireGuard kullanır ve Pangolin kontrol paneli üzerinden özel ağınızdaki kaynaklarınıza LAN adresleriyle erişmenizi sağlar. | | -| Runs in Docker | Docker üzerinde çalışır | | -| Runs in shell on macOS, Linux, and Windows | macOS, Linux ve Windows’ta komut satırında çalışır | | -| Install Newt | Newt'i Yükle | | -| Basic WireGuard
    | Temel WireGuard
    | | -| Compatible with all WireGuard clients
    | Tüm WireGuard istemcileriyle uyumlu
    | | -| Manual configuration required | Manuel yapılandırma gereklidir | | - -##### Content - -| EN | TR | Notes | -| --------------------------------------------------------- | --------------------------------------------------------------------------- | ------------ | -| Manage Sites | Siteleri Yönet | | -| Allow connectivity to your network through secure tunnels | Güvenli tüneller aracılığıyla ağınıza bağlantı sağlayın | | -| Search sites | Siteleri ara | placeholder | -| Add Site | Site Ekle | | -| Name | Ad | Table Header | -| Online | Çevrimiçi | Table Header | -| Site | Site | Table Header | -| Data In | Gelen Veri | Table Header | -| Data Out | Giden Veri | Table Header | -| Connection Type | Bağlantı Türü | Table Header | -| Online | Çevrimiçi | Site state | -| Offline | Çevrimdışı | Site state | -| Edit → | Düzenle → | | -| View settings | Ayarları Görüntüle | Popup | -| Delete | Sil | Popup | - -##### Add Site Popup - -| EN | TR | Notes | -| ------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ----------- | -| Create Site | Site Oluştur | | -| Create a new site to start connection for this site | Bu site için bağlantıyı başlatmak amacıyla yeni bir site oluşturun | | -| Name | Ad | | -| Site name | Site adı | placeholder | -| This is the name that will be displayed for this site. | Bu, site için görüntülenecek addır. | desc | -| Method | Yöntem | | -| Local | Yerel | | -| Newt | Newt | | -| WireGuard | WireGuard | | -| This is how you will expose connections. | Bağlantılarınızı bu şekilde açığa çıkaracaksınız. | | -| You will only be able to see the configuration once. | Yapılandırmayı yalnızca bir kez görüntüleyebilirsiniz. | | -| Learn how to install Newt on your system | Sisteminizde Newt'in nasıl kurulacağını öğrenin | | -| I have copied the config | Yapılandırmayı kopyaladım | | -| Create Site | Site Oluştur | | -| Close | Kapat | | - ---- - -## Main “Resources” - -##### “Hero” section - -| EN | TR | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ----- | -| Resources | Kaynaklar | | -| Ressourcen sind Proxy-Server für Anwendungen, die in Ihrem privaten Netzwerk laufen. Erstellen Sie eine Ressource für jede HTTP- oder HTTPS-Anwendung in Ihrem privaten Netzwerk. Jede Ressource muss mit einer Website verbunden sein, um eine private und sichere Verbindung über den verschlüsselten WireGuard-Tunnel zu ermöglichen. | Kaynaklar, özel ağınızda çalışan uygulamalar için proxy sunucularıdır. Özel ağınızdaki her HTTP veya HTTPS uygulaması için bir kaynak oluşturun. Her kaynağın, şifrelenmiş WireGuard tüneli üzerinden özel ve güvenli bağlantı sağlamak üzere bir siteyle ilişkili olması gerekir. | | -| Secure connectivity with WireGuard encryption | WireGuard şifrelemesiyle güvenli bağlantı | | -| Configure multiple authentication methods | Birden çok kimlik doğrulama yöntemini yapılandırın | | -| User and role-based access control | Kullanıcı ve role dayalı erişim kontrolü | | - -##### Content - -| EN | TR | Notes | -| -------------------------------------------------- | ------------------------------------------------------------- | -------------------- | -| Manage Resources | Kaynakları Yönet | | -| Create secure proxies to your private applications | Özel uygulamalarınız için güvenli proxy’ler oluşturun | | -| Search resources | Kaynakları ara | placeholder | -| Name | Ad | | -| Site | Site | | -| Full URL | Tam URL | | -| Authentication | Kimlik Doğrulama | | -| Not Protected | Korunmayan | authentication state | -| Protected | Korunan | authentication state | -| Edit → | Düzenle → | | -| Add Resource | Kaynak Ekle | | - -##### Add Resource Popup - -| EN | TR | Notes | -| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ------------- | -| Create Resource | Kaynak Oluştur | | -| Create a new resource to proxy request to your app | Uygulamanıza gelen istekleri yönlendirmek için yeni bir kaynak oluşturun | | -| Name | Ad | | -| My Resource | Kaynağım | name placeholder | -| This is the name that will be displayed for this resource. | Bu, kaynağın görüntülenecek adıdır. | | -| Subdomain | Alt alan adı | | -| Enter subdomain | Alt alan adını girin | | -| This is the fully qualified domain name that will be used to access the resource. | Kaynağa erişmek için kullanılacak tam nitelikli alan adıdır. | | -| Site | Site | | -| Search site… | Site ara… | Site selector popup | -| This is the site that will be used in the dashboard. | Kontrol panelinde kullanılacak sitedir. | | -| Create Resource | Kaynak Oluştur | | -| Close | Kapat | | - ---- - -## Main “User & Roles” - -##### Content - -| EN | TR | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ----------------------------- | -| Manage User & Roles | Kullanıcılar ve Rolleri Yönet | | -| Invite users and add them to roles to manage access to your organization | Organizasyonunuza erişimi yönetmek için kullanıcıları davet edin ve rollere atayın | | -| Users | Kullanıcılar | sidebar item | -| Roles | Roller | sidebar item | -| **User tab** | **Kullanıcı Sekmesi** | | -| Search users | Kullanıcıları ara | placeholder | -| Invite User | Kullanıcı Davet Et | addbutton | -| Email | E-posta | table header | -| Status | Durum | table header | -| Role | Rol | table header | -| Confirmed | Onaylandı | account status | -| Not confirmed (?) | Onaylanmadı (?) | account status | -| Owner | Sahip | role | -| Admin | Yönetici | role | -| Member | Üye | role | -| **Roles Tab** | **Roller Sekmesi** | | -| Search roles | Rolleri ara | placeholder | -| Add Role | Rol Ekle | addbutton | -| Name | Ad | table header | -| Description | Açıklama | table header | -| Admin | Yönetici | role | -| Member | Üye | role | -| Admin role with the most permissions | En fazla yetkiye sahip yönetici rolü | admin role desc | -| Members can only view resources | Üyeler yalnızca kaynakları görüntüleyebilir | member role desc | - -##### Invite User popup - -| EN | TR | Notes | -| ----------------- | ----------------------------------------------------------------------- | ----------- | -| Invite User | Kullanıcı Davet Et | | -| Email | E-posta | | -| Enter an email | Bir e-posta adresi girin | placeholder | -| Role | Rol | | -| Select role | Rol seçin | placeholder | -| Gültig für | Geçerlilik Süresi | | -| 1 day | 1 gün | | -| 2 days | 2 gün | | -| 3 days | 3 gün | | -| 4 days | 4 gün | | -| 5 days | 5 gün | | -| 6 days | 6 gün | | -| 7 days | 7 gün | | -| Create Invitation | Davetiye Oluştur | | -| Close | Kapat | | - ---- - -## Main “Shareable Links” - -##### “Hero” section - -| EN | TR | Notes | -| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ----- | -| Shareable Links | Paylaşılabilir Bağlantılar | | -| Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one. | Kaynaklarınıza paylaşılabilir bağlantılar oluşturun. Bağlantılar, kaynağınıza geçici veya sınırsız erişim sağlar. Oluştururken bağlantının geçerlilik süresini ayarlayabilirsiniz. | | -| Easy to create and share | Oluşturması ve paylaşması kolay | | -| Configurable expiration duration | Yapılandırılabilir geçerlilik süresi | | -| Secure and revocable | Güvenli ve iptal edilebilir | | - -##### Content - -| EN | TR | Notes | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | -------------- | -| Manage Shareable Links | Paylaşılabilir Bağlantıları Yönet | | -| Create shareable links to grant temporary or permanent access to your resources | Kaynaklarınıza geçici veya kalıcı erişim sağlamak için paylaşılabilir bağlantılar oluşturun | | -| Search links | Bağlantıları ara | placeholder | -| Create Share Link | Bağlantı Oluştur | addbutton | -| Resource | Kaynak | table header | -| Title | Başlık | table header | -| Created | Oluşturulma Tarihi | table header | -| Expires | Son Kullanma Tarihi | table header | -| No links. Create one to get started. | Bağlantı yok. Başlamak için bir tane oluşturun. | table placeholder | - -##### Create Shareable Link popup - -| EN | TR | Notes | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ----------------------- | -| Create Shareable Link | Paylaşılabilir Bağlantı Oluştur | | -| Anyone with this link can access the resource | Bu bağlantıya sahip olan herkes kaynağa erişebilir | | -| Resource | Kaynak | | -| Select resource | Kaynak seçin | | -| Search resources… | Kaynak ara… | resource selector popup | -| Title (optional) | Başlık (isteğe bağlı) | | -| Enter title | Başlık girin | placeholder | -| Expire in | Sona Erme Süresi | | -| Minutes | Dakika | | -| Hours | Saat | | -| Days | Gün | | -| Months | Ay | | -| Years | Yıl | | -| Never expire | Asla sona erme | | -| Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource. | Bağlantının geçerlilik süresi, bağlantının ne kadar süreyle kullanılabilir olacağını ve kaynağa erişim sağlayacağını belirler. Bu sürenin sonunda bağlantı çalışmaz hale gelir ve bağlantıyı kullananlar kaynağa erişimini kaybeder. | | -| Create Link | Bağlantı Oluştur | | -| Close | Kapat | | - ---- - -## Main “General” - -| EN | TR | Notes | -| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------ | -| General | Genel | | -| Configure your organization’s general settings | Organizasyonunuzun genel ayarlarını yapılandırın | | -| General | Genel | sidebar item | -| Organization Settings | Organizasyon Ayarları | | -| Manage your organization details and configuration | Organizasyonunuzun detaylarını ve yapılandırmasını yönetin | | -| Name | Ad | | -| This is the display name of the org | Bu, organizasyonunuzun görüntülenecek adıdır. | | -| Save Settings | Ayarları Kaydet | | -| Danger Zone | Tehlikeli Bölge | | -| Once you delete this org, there is no going back. Please be certain. | Bu organizasyonu sildikten sonra geri dönüş yoktur. Lütfen emin olun. | | -| Delete Organization Data | Organizasyon Verilerini Sil | | From 915581dfe7b23bc0c6ae7af2c5ffc8aeba69a1fd Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 9 Jun 2025 17:42:28 -0400 Subject: [PATCH 089/105] Add label --- src/components/LocaleSwitcher.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index 3c0fbca8..080c8f7b 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -6,6 +6,7 @@ export default function LocaleSwitcher() { return ( Date: Mon, 9 Jun 2025 18:04:45 -0400 Subject: [PATCH 090/105] Translate sidebar --- src/components/SidebarNav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidebarNav.tsx b/src/components/SidebarNav.tsx index ae9f4972..d90a5093 100644 --- a/src/components/SidebarNav.tsx +++ b/src/components/SidebarNav.tsx @@ -140,7 +140,7 @@ export function SidebarNav({ {item.icon} )} - {item.title} + {t(item.title)}
    {isProfessional && ( Date: Mon, 9 Jun 2025 18:10:04 -0400 Subject: [PATCH 091/105] Add sidebar - needs translations --- messages/en-US.json | 18 ++++++++++++++++-- src/app/navigation.tsx | 30 +++++++++++++++--------------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 6eb8d8af..c640e6c1 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "No auth info to update", "orgErrorNoUpdate": "No org to update", "orgErrorNoProvided": "No org provided", - "apiKeysErrorNoUpdate": "No API key to update" -} + "apiKeysErrorNoUpdate": "No API key to update", + "sidebarOverview": "Overview", + "sidebarHome": "Home", + "sidebarSites": "Sites", + "sidebarResources": "Resources", + "sidebarAccessControl": "Access Control", + "sidebarUsers": "Users", + "sidebarInvitations": "Invitations", + "sidebarRoles": "Roles", + "sidebarShareableLinks": "Shareable Links", + "sidebarApiKeys": "API Keys", + "sidebarSettings": "Settings", + "sidebarAllUsers": "All Users", + "sidebarIdentityProviders": "Identity Providers", + "sidebarLicense": "License" +} \ No newline at end of file diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 8ea3c080..af4eea75 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -13,7 +13,7 @@ import { export const orgLangingNavItems: SidebarNavItem[] = [ { - title: "Overview", + title: "sidebarOverview", href: "/{orgId}", icon: } @@ -21,7 +21,7 @@ export const orgLangingNavItems: SidebarNavItem[] = [ export const rootNavItems: SidebarNavItem[] = [ { - title: "Home", + title: "sidebarHome", href: "/", icon: } @@ -29,49 +29,49 @@ export const rootNavItems: SidebarNavItem[] = [ export const orgNavItems: SidebarNavItem[] = [ { - title: "Sites", + title: "sidebarSites", href: "/{orgId}/settings/sites", icon: }, { - title: "Resources", + title: "sidebarResources", href: "/{orgId}/settings/resources", icon: }, { - title: "Access Control", + title: "sidebarAccessControl", href: "/{orgId}/settings/access", icon: , autoExpand: true, children: [ { - title: "Users", + title: "sidebarUsers", href: "/{orgId}/settings/access/users", children: [ { - title: "Invitations", + title: "sidebarInvitations", href: "/{orgId}/settings/access/invitations" } ] }, { - title: "Roles", + title: "sidebarRoles", href: "/{orgId}/settings/access/roles" } ] }, { - title: "Shareable Links", + title: "sidebarShareableLinks", href: "/{orgId}/settings/share-links", icon: }, { - title: "API Keys", + title: "sidebarApiKeys", href: "/{orgId}/settings/api-keys", icon: }, { - title: "Settings", + title: "sidebarSettings", href: "/{orgId}/settings/general", icon: } @@ -79,22 +79,22 @@ export const orgNavItems: SidebarNavItem[] = [ export const adminNavItems: SidebarNavItem[] = [ { - title: "All Users", + title: "sidebarAllUsers", href: "/admin/users", icon: }, { - title: "API Keys", + title: "sidebarApiKeys", href: "/admin/api-keys", icon: }, { - title: "Identity Providers", + title: "sidebarIdentityProviders", href: "/admin/idp", icon: }, { - title: "License", + title: "sidebarLicense", href: "/admin/license", icon: } From d62a3c64cfe2f0c9be18b74037010b240683b71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=97=B4=E8=8B=8F=E8=8B=8F?= Date: Tue, 10 Jun 2025 13:27:28 +0800 Subject: [PATCH 092/105] feat(i18n): chinese i18n (#178) --- messages/zh-CN.json | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 1373015f..c2420ca4 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "没有要更新的身份验证信息", "orgErrorNoUpdate": "没有要更新的组织", "orgErrorNoProvided": "未提供组织", - "apiKeysErrorNoUpdate": "没有要更新的 API 密钥" -} \ No newline at end of file + "apiKeysErrorNoUpdate": "没有要更新的 API 密钥", + "sidebarOverview": "概览", + "sidebarHome": "首页", + "sidebarSites": "站点", + "sidebarResources": "资源", + "sidebarAccessControl": "访问控制", + "sidebarUsers": "用户", + "sidebarInvitations": "邀请", + "sidebarRoles": "角色", + "sidebarShareableLinks": "分享链接", + "sidebarApiKeys": "API密钥", + "sidebarSettings": "设置", + "sidebarAllUsers": "所有用户", + "sidebarIdentityProviders": "身份提供商", + "sidebarLicense": "证书" +} From 2b64c0e84e950e109d1d52a5316b45cb2db77768 Mon Sep 17 00:00:00 2001 From: Timo <43761260+clemone210@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:29:02 +0200 Subject: [PATCH 093/105] Update de-DE.json (#154) Updated DE until Line 100 --- messages/de-DE.json | 102 ++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 4ae478f1..6432ae56 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1,33 +1,33 @@ { - "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", + "setupCreate": "Erstelle eine Organisation, Site und Ressourcen", "setupNewOrg": "Neue Organisation", "setupCreateOrg": "Organisation erstellen", "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Organisation's Name", - "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", + "setupOrgName": "Name der Organisation", + "orgDisplayName": "Anzeigename der Organisation.", "orgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", - "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", + "setupIdentifierMessage": "Dies ist eine Eindeutige ID für Ihre Organisation. Diese ist unabhängig vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wähle eine andere.", + "componentsErrorNoMemberCreate": "Du bist derzeit kein Mitglied einer Organisation. Erstelle eine Organisation, um zu starten.", "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", "welcome": "Willkommen zu Pangolin", - "componentsCreateOrg": "Erstelle eine Organisation", + "componentsCreateOrg": "Erstelleeine Organisation", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", - "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "dismiss": "Verwerfen", - "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", - "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", - "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", - "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", - "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", - "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", - "goHome": "Nach Hause", + "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.", + "inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.", + "inviteLoginUser": "Bitte stelle sicher, dass du als korrekter Benutzer angemeldet bist.", + "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als sei die Einladung, auf die du zugreifen möchtest, nicht für einen existierenden Benutzer bestimmt.", + "inviteCreateUser": "Bitte erstelle zuerst ein Konto.", + "goHome": "Zur Startseite", "inviteLogInOtherUser": "Als anderer Benutzer anmelden", "createAnAccount": "Konto erstellen", "inviteNotAccepted": "Einladung nicht angenommen", - "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", - "authNoAccount": "Sie haben noch kein Konto?", + "authCreateAccount": "Erstellen ein Konto um loszulegen", + "authNoAccount": "Du besitzt noch kein Konto?", "email": "E-Mail", "password": "Passwort", "confirmPassword": "Passwort bestätigen", @@ -38,8 +38,8 @@ "online": "Online", "offline": "Offline", "site": "Site", - "dataIn": "Daten in", - "dataOut": "Daten raus", + "dataIn": "Daten eingehend", + "dataOut": "Daten ausgehend", "connectionType": "Verbindungstyp", "tunnelType": "Tunneltyp", "local": "Lokal", @@ -47,57 +47,57 @@ "siteConfirmDelete": "Site löschen bestätigen", "siteDelete": "Site löschen", "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", - "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", - "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", + "siteMessageConfirm": "Um zu bestätigen, gib den Namen der Site ein.", + "siteQuestionRemove": "Bist du sicher, dass Sie die Site {selectedSite} aus der Organisation entfernt werden soll?", "siteManageSites": "Sites verwalten", - "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", + "siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben", "siteCreate": "Site erstellen", - "siteCreateDescription2": "Folgen Sie den Schritten unten, um eine neue Seite zu erstellen und zu verbinden", - "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", + "siteCreateDescription2": "Folge den nachfolgenden Schritten, um eine neue Site zu erstellen und zu verbinden", + "siteCreateDescription": "Erstelle eine neue Site, um Ressourcen zu verbinden", "close": "Schließen", - "siteErrorCreate": "Fehler beim Erstellen der Seite", + "siteErrorCreate": "Fehler beim Erstellen der Site", "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", - "siteNameDescription": "Dies ist der Anzeigename für die Website.", + "siteNameDescription": "Dies ist der Anzeigename für die Site.", "method": "Methode", - "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", - "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", - "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", + "siteMethodDescription": "So werden Verbindungen freigegeben.", + "siteLearnNewt": "Wie du Newt auf deinem System installieren kannst", + "siteSeeConfigOnce": "Du kannst die Konfiguration nur einmalig ansehen.", "siteLoadWGConfig": "Lade WireGuard Konfiguration...", - "siteDocker": "Erweitern für Docker-Details", + "siteDocker": "Erweitern für Docker Details", "toggle": "Umschalten", - "dockerCompose": "Docker komponieren", + "dockerCompose": "Docker Compose", "dockerRun": "Docker Run", - "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", + "siteLearnLocal": "Mehr Infos zu lokalen Sites", "siteConfirmCopy": "Ich habe die Konfiguration kopiert", - "searchSitesProgress": "Seiten suchen...", + "searchSitesProgress": "Sites durchsuchen...", "siteAdd": "Site hinzufügen", - "siteInstallNewt": "Neustart installieren", - "siteInstallNewtDescription": "Lass Newt auf deinem System laufen", + "siteInstallNewt": "Newt installieren", + "siteInstallNewtDescription": "Installiere Newt auf deinem System.", "WgConfiguration": "WireGuard Konfiguration", - "WgConfigurationDescription": "Verwenden Sie folgende Konfiguration, um sich mit Ihrem Netzwerk zu verbinden", + "WgConfigurationDescription": "Verwende folgende Konfiguration, um dich mit deinem Netzwerk zu verbinden", "operatingSystem": "Betriebssystem", "commands": "Befehle", "recommended": "Empfohlen", - "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", - "siteRunsInDocker": "Läuft im Docker", - "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", - "siteErrorDelete": "Fehler beim Löschen der Seite", - "siteErrorUpdate": "Fehler beim Aktualisieren der Seite", - "siteErrorUpdateDescription": "Beim Aktualisieren der Seite ist ein Fehler aufgetreten.", + "siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", + "siteRunsInDocker": "Läuft in Docker", + "siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows", + "siteErrorDelete": "Fehler beim Löschen der Site", + "siteErrorUpdate": "Fehler beim Aktualisieren der Site", + "siteErrorUpdateDescription": "Beim Aktualisieren der Site ist ein Fehler aufgetreten.", "siteUpdated": "Site aktualisiert", - "siteUpdatedDescription": "Die Seite wurde aktualisiert.", - "siteGeneralDescription": "Allgemeine Einstellungen für diese Seite konfigurieren", - "siteSettingDescription": "Konfigurieren Sie die Einstellungen auf Ihrer Seite", + "siteUpdatedDescription": "Die Site wurde aktualisiert.", + "siteGeneralDescription": "Allgemeine Einstellungen für diese Site konfigurieren", + "siteSettingDescription": "Konfigurieren der Site Einstellungen", "siteSetting": "{siteName} Einstellungen", "siteNewtTunnel": "Newt-Tunnel (empfohlen)", - "siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in Ihr Netzwerk zu erstellen. Keine zusätzliche Einrichtung.", - "siteWg": "Einfacher WireGuard", - "siteWgDescription": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu errichten. Manuelle NAT-Setup erforderlich.", + "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", + "siteWg": "Einfacher WireGuard Tunnel", + "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", - "siteSeeAll": "Alle Seiten anzeigen", - "siteTunnelDescription": "Legen Sie fest, wie Sie sich mit Ihrer Website verbinden möchten", - "siteNewtCredentials": "Anmeldedaten neu", + "siteSeeAll": "Alle Sites anzeigen", + "siteTunnelDescription": "Lege fest, wie du dich mit deiner Site verbinden möchtest", + "siteNewtCredentials": "Neue Newt Zugangsdaten", "siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren", "siteCredentialsSave": "Ihre Zugangsdaten speichern", "siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.", From 19ccf098f0bee301c6fb6c90baa084584a053386 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:45:23 +0200 Subject: [PATCH 094/105] New Crowdin updates (#179) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Turkish) * New translations en-us.json (Chinese Simplified) * New translations en-us.json (Spanish) * New translations en-us.json (Dutch) * New translations en-us.json (French) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Spanish) * New translations en-us.json (Dutch) --- messages/de-DE.json | 18 ++- messages/es-ES.json | 18 ++- messages/fr-FR.json | 18 ++- messages/it-IT.json | 18 ++- messages/nl-NL.json | 20 ++- messages/pl-PL.json | 18 ++- messages/pt-PT.json | 18 ++- messages/tr-TR.json | 18 ++- messages/zh-CN.json | 332 ++++++++++++++++++++++---------------------- 9 files changed, 295 insertions(+), 183 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 6432ae56..db24894c 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren", "orgErrorNoUpdate": "Keine Organisation zum Aktualisieren", "orgErrorNoProvided": "Keine Organisation angegeben", - "apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren" -} + "apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren", + "sidebarOverview": "Übersicht", + "sidebarHome": "Zuhause", + "sidebarSites": "Seiten", + "sidebarResources": "Ressourcen", + "sidebarAccessControl": "Zugriffskontrolle", + "sidebarUsers": "Benutzer", + "sidebarInvitations": "Einladungen", + "sidebarRoles": "Rollen", + "sidebarShareableLinks": "Teilbare Links", + "sidebarApiKeys": "API-Schlüssel", + "sidebarSettings": "Einstellungen", + "sidebarAllUsers": "Alle Benutzer", + "sidebarIdentityProviders": "Identitätsanbieter", + "sidebarLicense": "Lizenz" +} \ No newline at end of file diff --git a/messages/es-ES.json b/messages/es-ES.json index 2c05cc8b..145a78e2 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "No hay información de autenticación para actualizar", "orgErrorNoUpdate": "No hay org para actualizar", "orgErrorNoProvided": "No hay org proporcionado", - "apiKeysErrorNoUpdate": "Ninguna clave API para actualizar" -} + "apiKeysErrorNoUpdate": "Ninguna clave API para actualizar", + "sidebarOverview": "Resumen", + "sidebarHome": "Inicio", + "sidebarSites": "Sitios", + "sidebarResources": "Recursos", + "sidebarAccessControl": "Control de acceso", + "sidebarUsers": "Usuarios", + "sidebarInvitations": "Invitaciones", + "sidebarRoles": "Roles", + "sidebarShareableLinks": "Enlaces compartibles", + "sidebarApiKeys": "Claves API", + "sidebarSettings": "Ajustes", + "sidebarAllUsers": "Todos los usuarios", + "sidebarIdentityProviders": "Proveedores de identidad", + "sidebarLicense": "Licencia" +} \ No newline at end of file diff --git a/messages/fr-FR.json b/messages/fr-FR.json index fb59e65a..36ac30af 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Pas d'informations d'authentification à mettre à jour", "orgErrorNoUpdate": "Pas d'organisation à mettre à jour", "orgErrorNoProvided": "Aucune organisation fournie", - "apiKeysErrorNoUpdate": "Pas de clé API à mettre à jour" -} + "apiKeysErrorNoUpdate": "Pas de clé API à mettre à jour", + "sidebarOverview": "Aperçu", + "sidebarHome": "Domicile", + "sidebarSites": "Espaces", + "sidebarResources": "Ressource", + "sidebarAccessControl": "Contrôle d'accès", + "sidebarUsers": "Utilisateurs", + "sidebarInvitations": "Invitations", + "sidebarRoles": "Rôles", + "sidebarShareableLinks": "Liens partagables", + "sidebarApiKeys": "Clés API", + "sidebarSettings": "Réglages", + "sidebarAllUsers": "Tous les utilisateurs", + "sidebarIdentityProviders": "Fournisseurs d'identité", + "sidebarLicense": "Licence" +} \ No newline at end of file diff --git a/messages/it-IT.json b/messages/it-IT.json index 3e49a833..ee37f218 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Nessuna informazione di autenticazione da aggiornare", "orgErrorNoUpdate": "Nessuna organizzazione da aggiornare", "orgErrorNoProvided": "Nessuna organizzazione fornita", - "apiKeysErrorNoUpdate": "Nessuna chiave API da aggiornare" -} + "apiKeysErrorNoUpdate": "Nessuna chiave API da aggiornare", + "sidebarOverview": "Panoramica", + "sidebarHome": "Home", + "sidebarSites": "Siti", + "sidebarResources": "Risorse", + "sidebarAccessControl": "Controllo Accesso", + "sidebarUsers": "Utenti", + "sidebarInvitations": "Inviti", + "sidebarRoles": "Ruoli", + "sidebarShareableLinks": "Collegamenti Condividibili", + "sidebarApiKeys": "Chiavi API", + "sidebarSettings": "Impostazioni", + "sidebarAllUsers": "Tutti Gli Utenti", + "sidebarIdentityProviders": "Fornitori Di Identità", + "sidebarLicense": "Licenza" +} \ No newline at end of file diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 2752f6dd..ef8007c7 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -348,7 +348,7 @@ "licensePurchaseSites": "Extra sites kopen", "licenseSitesUsedMax": "{usedSites} van {maxSites} sites gebruikt", "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} in het systeem.", - "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", + "licensePurchaseDescription": "Kies hoeveel sites je wilt {selectedMode, select, license {Koop een licentie. Je kunt later altijd meer sites toevoegen.} other {Voeg je bestaande licentie toe}}", "licenseFee": "Licentie vergoeding", "licensePriceSite": "Prijs per site", "total": "Totaal", @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Geen authenticatie informatie om bij te werken", "orgErrorNoUpdate": "Geen org om bij te werken", "orgErrorNoProvided": "Geen org opgegeven", - "apiKeysErrorNoUpdate": "Geen API-sleutel om bij te werken" -} + "apiKeysErrorNoUpdate": "Geen API-sleutel om bij te werken", + "sidebarOverview": "Overzicht.", + "sidebarHome": "Startpagina", + "sidebarSites": "Werkruimtes", + "sidebarResources": "Hulpmiddelen", + "sidebarAccessControl": "Toegangs controle", + "sidebarUsers": "Gebruikers", + "sidebarInvitations": "Uitnodigingen", + "sidebarRoles": "Rollen", + "sidebarShareableLinks": "Deelbare links", + "sidebarApiKeys": "API sleutels", + "sidebarSettings": "Instellingen", + "sidebarAllUsers": "Alle gebruikers", + "sidebarIdentityProviders": "Identiteit aanbieders", + "sidebarLicense": "Licentie" +} \ No newline at end of file diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 299e5502..b20c4040 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Brak danych uwierzytelniania do aktualizacji", "orgErrorNoUpdate": "Brak organizacji do aktualizacji", "orgErrorNoProvided": "Nie podano organizacji", - "apiKeysErrorNoUpdate": "Brak klucza API do aktualizacji" -} + "apiKeysErrorNoUpdate": "Brak klucza API do aktualizacji", + "sidebarOverview": "Przegląd", + "sidebarHome": "Strona główna", + "sidebarSites": "Witryny", + "sidebarResources": "Zasoby", + "sidebarAccessControl": "Kontrola dostępu", + "sidebarUsers": "Użytkownicy", + "sidebarInvitations": "Zaproszenia", + "sidebarRoles": "Role", + "sidebarShareableLinks": "Linki do udostępnienia", + "sidebarApiKeys": "Klucze API", + "sidebarSettings": "Ustawienia", + "sidebarAllUsers": "Wszyscy użytkownicy", + "sidebarIdentityProviders": "Dostawcy tożsamości", + "sidebarLicense": "Licencja" +} \ No newline at end of file diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 986d889c..4a84d262 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "Não existem informações de autenticação para atualizar", "orgErrorNoUpdate": "Não existe organização para atualizar", "orgErrorNoProvided": "Nenhuma organização fornecida", - "apiKeysErrorNoUpdate": "Não existe chave API para atualizar" -} + "apiKeysErrorNoUpdate": "Não existe chave API para atualizar", + "sidebarOverview": "Geral", + "sidebarHome": "Residencial", + "sidebarSites": "sites", + "sidebarResources": "Recursos", + "sidebarAccessControl": "Controle de Acesso", + "sidebarUsers": "Utilizadores", + "sidebarInvitations": "Convites", + "sidebarRoles": "Papéis", + "sidebarShareableLinks": "Links compartilháveis", + "sidebarApiKeys": "Chaves API", + "sidebarSettings": "Confirgurações", + "sidebarAllUsers": "Todos os usuários", + "sidebarIdentityProviders": "Provedores de identidade", + "sidebarLicense": "Tipo:" +} \ No newline at end of file diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 5e1b88db..f09dc233 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1075,5 +1075,19 @@ "authErrorNoUpdate": "No auth info to update", "orgErrorNoUpdate": "No org to update", "orgErrorNoProvided": "No org provided", - "apiKeysErrorNoUpdate": "No API key to update" -} + "apiKeysErrorNoUpdate": "No API key to update", + "sidebarOverview": "Overview", + "sidebarHome": "Home", + "sidebarSites": "Sites", + "sidebarResources": "Resources", + "sidebarAccessControl": "Access Control", + "sidebarUsers": "Users", + "sidebarInvitations": "Invitations", + "sidebarRoles": "Roles", + "sidebarShareableLinks": "Shareable Links", + "sidebarApiKeys": "API Keys", + "sidebarSettings": "Settings", + "sidebarAllUsers": "All Users", + "sidebarIdentityProviders": "Identity Providers", + "sidebarLicense": "License" +} \ No newline at end of file diff --git a/messages/zh-CN.json b/messages/zh-CN.json index c2420ca4..110f1964 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1,5 +1,5 @@ { - "setupCreate": "创建您的第一个组织、网站和资源", + "setupCreate": "创建您的组织、网站和资源", "setupNewOrg": "新建组织", "setupCreateOrg": "创建组织", "setupCreateResources": "创建资源", @@ -7,17 +7,17 @@ "orgDisplayName": "这是您组织的显示名称。", "orgId": "组织ID", "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", - "setupErrorIdentifier": "组织ID 已被使用。请另选一个。", + "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "{count, plural, =0 {您目前不是任何组织的成员。} other {你已加入 # 个组织。}}.", - "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", - "dismiss": "忽略", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", + "dismiss": "关闭", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", - "inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", "inviteLoginUser": "请确保您以正确的用户登录。", "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", @@ -61,27 +61,27 @@ "siteNameDescription": "这是站点的显示名称。", "method": "方法", "siteMethodDescription": "这是您将如何显示连接。", - "siteLearnNewt": "学习如何在您的系统上安装 Newt", + "siteLearnNewt": "学习如何在您的系统上安装Newt", "siteSeeConfigOnce": "您只能看到一次配置。", - "siteLoadWGConfig": "正在载入 WireGuard 配置...", + "siteLoadWGConfig": "正在载入Wire保护配置...", "siteDocker": "扩展 Docker 部署详细信息", - "toggle": "切换", - "dockerCompose": "Docker Compose", - "dockerRun": "Docker Run", - "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", - "siteConfirmCopy": "我已经复制了配置信息", + "toggle": "切换键", + "dockerCompose": "Docker 配置", + "dockerRun": "停靠栏", + "siteLearnLocal": "本地站点没有隧道,学习更多", + "siteConfirmCopy": "我已复制配置", "searchSitesProgress": "搜索站点...", "siteAdd": "添加站点", - "siteInstallNewt": "安装 Newt", - "siteInstallNewtDescription": "在您的系统中运行 Newt", - "WgConfiguration": "WireGuard 配置", + "siteInstallNewt": "安装新的", + "siteInstallNewtDescription": "在您的系统上获得新的运行", + "WgConfiguration": "Wire护卫配置", "WgConfigurationDescription": "使用以下配置连接到您的网络", "operatingSystem": "操作系统", "commands": "命令", - "recommended": "推荐", - "siteNewtDescription": "为获得最佳用户体验,请使用 Newt。其底层采用 WireGuard 技术,可直接通过 Pangolin 控制台,使用局域网地址访问您私有网络中的资源。", - "siteRunsInDocker": "在 Docker 中运行", - "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中运行", + "recommended": "推荐的", + "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", + "siteRunsInDocker": "运行在停靠栏", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", "siteErrorDelete": "删除站点出错", "siteErrorUpdate": "更新站点失败", "siteErrorUpdateDescription": "更新站点时出错。", @@ -90,17 +90,17 @@ "siteGeneralDescription": "配置此站点的常规设置", "siteSettingDescription": "配置您网站上的设置", "siteSetting": "{siteName} 设置", - "siteNewtTunnel": "Newt 隧道 (推荐)", - "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", - "siteWg": "基本 WireGuard", - "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", - "siteLocalDescription": "仅限本地资源。不需要隧道。", + "siteNewtTunnel": "新隧道(推荐)", + "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", + "siteWg": "基本线甲", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", + "siteLocalDescription": "仅本地资源。没有隧道。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", - "siteNewtCredentials": "Newt 凭据", - "siteNewtCredentialsDescription": "这是 Newt 服务器的身份验证凭据", - "siteCredentialsSave": "保存您的凭据", - "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制并保存到一个安全的地方。", + "siteNewtCredentials": "新建凭据", + "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", + "siteCredentialsSave": "保存您的证书", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", "siteInfo": "站点信息", "status": "状态", "shareTitle": "管理共享链接", @@ -111,16 +111,16 @@ "shareErrorDeleteMessage": "删除链接时出错", "shareDeleted": "链接已删除", "shareDeletedDescription": "链接已删除", - "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求头。 每次验证访问请求都必须从客户端传递。", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", "accessToken": "访问令牌", "usageExamples": "用法示例", - "tokenId": "令牌 ID", - "requestHeades": "请求头", + "tokenId": "Token ID", + "requestHeades": "请求标题", "queryParameter": "查询参数", - "importantNote": "重要提示", - "shareImportantDescription": "出于安全考虑,建议尽可能在使用请求头传递参数,因为查询参数可能会被浏览器历史记录或服务器日志记录。", + "importantNote": "重要笔记", + "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", "token": "令牌", - "shareTokenSecurety": "请妥善保管您的访问令牌,不要将其暴露在公开访问的区域或客户端代码中。", + "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", "shareErrorFetchResource": "获取资源失败", "shareErrorFetchResourceDescription": "获取资源时出错", "shareErrorCreate": "无法创建共享链接", @@ -141,7 +141,7 @@ "title": "标题", "created": "已创建", "expires": "过期时间", - "never": "永不过期", + "never": "从不使用", "shareErrorSelectResource": "请选择一个资源", "resourceTitle": "管理资源", "resourceDescription": "为您的私人应用程序创建安全代理", @@ -149,14 +149,14 @@ "resourceAdd": "添加资源", "resourceErrorDelte": "删除资源时出错", "authentication": "认证", - "protected": "受到保护", - "notProtected": "未受到保护", - "resourceMessageRemove": "一旦删除,资源将不再可访问。与该资源相关的所有目标也将被删除。", + "protected": "保护", + "notProtected": "不保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", "resourceMessageConfirm": "请在下面输入资源名称以确认。", "resourceQuestionRemove": "您确定要从组织中删除 {selectedResource} 吗?", "resourceHTTP": "HTTPS 资源", - "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", - "resourceRaw": "TCP/UDP 资源", + "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "Raw TCP/UDP 资源", "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", @@ -172,20 +172,20 @@ "resourceHTTPSSettings": "HTTPS 设置", "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", "domainType": "域类型", - "subdomain": "子域名", - "baseDomain": "根域名", - "subdomnainDescription": "您的资源可以访问的子域名。", + "subdomain": "子域", + "baseDomain": "基础域", + "subdomnainDescription": "您的资源可以访问的子域。", "resourceRawSettings": "TCP/UDP 设置", "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", - "protocol": "协议", - "protocolSelect": "选择协议", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", "resourcePortNumber": "端口号", "resourcePortNumberDescription": "代理请求的外部端口号。", "cancel": "取消", "resourceConfig": "配置片段", "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", "resourceAddEntrypoints": "Traefik: 添加入口点", - "resourceExposePorts": "Gerbil:在 Docker Compose 中显示端口", + "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", "resourceBack": "返回资源", "resourceGoTo": "转到资源", @@ -194,23 +194,23 @@ "visibility": "可见性", "enabled": "已启用", "disabled": "已禁用", - "general": "概览", + "general": "A. 概况", "generalSettings": "常规设置", "proxy": "代理服务器", "rules": "规则", "resourceSettingDescription": "配置您资源上的设置", "resourceSetting": "{resourceName} 设置", - "alwaysAllow": "一律允许", - "alwaysDeny": "一律拒绝", + "alwaysAllow": "总是允许", + "alwaysDeny": "总是拒绝", "orgSettingsDescription": "配置您组织的一般设置", "orgGeneralSettings": "组织设置", "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", "saveGeneralSettings": "保存常规设置", "orgDangerZone": "危险区域", - "orgDangerZoneDescription": "一旦删除该组织,将无法恢复,请务必确认。", + "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", "orgDelete": "删除组织", "orgDeleteConfirm": "确认删除组织", - "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", + "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", "orgUpdated": "组织已更新", @@ -224,15 +224,15 @@ "orgDeleted": "组织已删除", "orgDeletedMessage": "组织及其数据已被删除。", "orgMissing": "缺少组织 ID", - "orgMissingMessage": "没有组织ID,无法重新生成邀请。", + "orgMissingMessage": "没有机构ID,无法重新生成邀请。", "accessUsersManage": "管理用户", - "accessUsersDescription": "邀请用户并位他们添加角色以管理访问您的组织", + "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", "accessUsersSearch": "搜索用户...", "accessUserCreate": "创建用户", "accessUserRemove": "删除用户", "username": "用户名", "identityProvider": "身份提供商", - "role": "角色", + "role": "作用", "nameRequired": "名称是必填项", "accessRolesManage": "管理角色", "accessRolesDescription": "配置角色来管理访问您的组织", @@ -244,13 +244,13 @@ "inviteDescription": "管理您给其他用户的邀请", "inviteSearch": "搜索邀请...", "minutes": "分钟", - "hours": "小时", + "hours": "小时数", "days": "天", "weeks": "周", "months": "月", "years": "年", "day": "{count, plural, =1 {# 天} other {# 天}}", - "apiKeysTitle": "API 密钥", + "apiKeysTitle": "API 密钥信息", "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", "apiKeysErrorCreate": "创建 API 密钥出错", "apiKeysErrorSetPermission": "设置权限出错", @@ -260,7 +260,7 @@ "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", "apiKeysList": "您的 API 密钥", "apiKeysSave": "保存您的 API 密钥", - "apiKeysSaveDescription": "该信息仅会显示一次,请确保将其复制到安全的位置。", + "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", "apiKeysInfo": "您的 API 密钥是:", "apiKeysConfirmCopy": "我已复制 API 密钥", "generate": "生成", @@ -280,7 +280,7 @@ "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", - "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", + "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", "apiKeysDelete": "删除 API 密钥", @@ -290,7 +290,7 @@ "userTitle": "管理所有用户", "userDescription": "查看和管理系统中的所有用户", "userAbount": "关于用户管理", - "userAbountDescription": "此表格显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表格中的删除操作删除其根用户对象。", + "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", "userServer": "服务器用户", "userSearch": "搜索服务器用户...", "userErrorDelete": "删除用户时出错", @@ -300,7 +300,7 @@ "userMessageConfirm": "请在下面输入用户名称以确认。", "userQuestionRemove": "您确定要从服务器中永久删除 {selectedUser} 吗?", "licenseKey": "许可证密钥", - "valid": "有效", + "valid": "Valid", "numberOfSites": "站点数量", "licenseKeySearch": "搜索许可证密钥...", "licenseKeyAdd": "添加许可证密钥", @@ -334,7 +334,7 @@ "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", - "licenseTitle": "管理许可证状态", + "licenseTitle": "管理许可状态", "licenseTitleDescription": "查看和管理系统中的许可证密钥", "licenseHost": "主机许可证", "licenseHostDescription": "管理主机的主许可证密钥。", @@ -347,14 +347,14 @@ "licensePurchase": "购买许可证", "licensePurchaseSites": "购买更多站点", "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 个站点", - "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {# 站点}}", - "licensePurchaseDescription": "请选择您希望 {selectedMode, select, license {直接购买许可证,您可以随时增加更多站点。} other {为现有许可证购买更多站点}}", - "licenseFee": "许可证费用", - "licensePriceSite": "每个站点的价格", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", + "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", + "licenseFee": "许可费", + "licensePriceSite": "每个站点价格", "total": "总计", "licenseContinuePayment": "继续付款", "pricingPage": "定价页面", - "pricingPortal": "前往付款页面", + "pricingPortal": "查看购买门户网站", "licensePricingPage": "关于最新的价格和折扣,请访问 ", "invite": "邀请", "inviteRegenerate": "重新生成邀请", @@ -365,7 +365,7 @@ "inviteRemoved": "邀请已删除", "inviteRemovedDescription": "为 {email} 创建的邀请已删除", "inviteQuestionRemove": "您确定要删除 {email} 的邀请吗?", - "inviteMessageRemove": "一旦删除,这个邀请将不再有效。", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", "inviteQuestionRegenerate": "您确定要重新邀请 {email} 吗?这将会撤销掉之前的邀请", "inviteRemoveConfirm": "确认删除邀请", @@ -373,7 +373,7 @@ "inviteSent": "邀请邮件已成功发送至 {email}。", "inviteSentEmail": "发送电子邮件通知给用户", "inviteGenerate": "已为 {email} 创建新的邀请。", - "inviteDuplicateError": "重复的邀请", + "inviteDuplicateError": "Duplicate Invite", "inviteDuplicateErrorDescription": "此用户的邀请已存在。", "inviteRateLimitError": "超出速率限制", "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", @@ -463,7 +463,7 @@ "targetErrorFetchDescription": "获取目标时出错", "siteErrorFetch": "获取资源失败", "siteErrorFetchDescription": "获取资源时出错", - "targetErrorDuplicate": "重复的目标", + "targetErrorDuplicate": "Duplicate target", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", "targetWireGuardErrorInvalidIp": "Invalid target IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", @@ -479,9 +479,9 @@ "proxyUpdatedDescription": "您的代理设置已成功更新", "proxyErrorUpdate": "更新代理设置失败", "proxyErrorUpdateDescription": "更新代理设置时出错", - "targetAddr": "IP / 域名", + "targetAddr": "IP / Hostname", "targetPort": "端口", - "targetProtocol": "协议", + "targetProtocol": "Protocol", "targetTlsSettings": "HTTPS & TLS 设置", "targetTlsSettingsDescription": "配置资源的 TLS 设置", "targetTlsSettingsAdvanced": "高级TLS设置", @@ -493,7 +493,7 @@ "targetStickySessions": "启用置顶会话", "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", "methodSelect": "选择方法", - "targetSubmit": "添加目标", + "targetSubmit": "Add Target", "targetNoOne": "没有目标。使用表单添加目标。", "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", "targetsSubmit": "保存目标", @@ -504,7 +504,7 @@ "proxyAdditionalSubmit": "保存代理设置", "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", - "ipAddressErrorInvalidOctet": "无效的 IP 地址", + "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", "path": "路径", "ipAddressRange": "IP 范围", "rulesErrorFetch": "获取规则失败", @@ -520,7 +520,7 @@ "rulesErrorUpdate": "更新规则失败", "rulesErrorUpdateDescription": "更新规则时出错", "rulesUpdated": "启用规则", - "rulesUpdatedDescription": "规则已更新", + "rulesUpdatedDescription": "规则评价已更新", "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", @@ -533,18 +533,18 @@ "ruleErrorUpdate": "操作失败", "ruleErrorUpdateDescription": "保存过程中发生错误", "rulesPriority": "优先权", - "rulesAction": "行为", - "rulesMatchType": "匹配类型", + "rulesAction": "行 动", + "rulesMatchType": "比赛类型", "value": "值", "rulesAbout": "关于规则", - "rulesAboutDescription": "规则使您能够依据特定条件控制资源访问权限。您可以创建基于 IP 地址或 URL 路径的规则,以允许或拒绝访问。", + "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", "rulesActions": "行动", "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", "rulesMatchCriteria": "匹配条件", "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", - "rulesMatchCriteriaUrl": "匹配一个 URL 路径或模式", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", "rulesEnable": "启用规则", "rulesEnableDescription": "启用或禁用此资源的规则评估", "rulesResource": "资源规则配置", @@ -562,10 +562,10 @@ "domainsErrorFetch": "获取域名出错", "domainsErrorFetchDescription": "获取域时出错", "none": "无", - "unknown": "未知", + "unknown": "未知的", "resources": "资源", - "resourcesDescription": "资源是您私有网络中运行的应用程序的代理。您可以为私有网络中的任何 HTTP/HTTPS 或 TCP/UDP 服务创建资源。每个资源都必须连接到一个站点,以通过加密的 WireGuard 隧道实现私密且安全的连接。", - "resourcesWireGuardConnect": "采用 WireGuard 提供的加密安全连接", + "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", + "resourcesWireGuardConnect": "与Wire护卫加密安全连接", "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", "resourcesUsersRolesAccess": "基于用户和角色的访问控制", "resourcesErrorUpdate": "切换资源失败", @@ -574,7 +574,7 @@ "shareLink": "{resource} 的分享链接", "resourceSelect": "选择资源", "shareLinks": "分享链接", - "share": "分享链接", + "share": "可共享链接", "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", "shareEasyCreate": "轻松创建和分享", "shareConfigurableExpirationDuration": "可配置的过期时间", @@ -585,29 +585,29 @@ "unknownCommand": "未知命令", "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", - "newtEndpoint": "Newt 端点", + "newtEndpoint": "Newt Endpoint", "newtId": "Newt ID", - "newtSecretKey": "Newt 私钥", - "architecture": "架构", + "newtSecretKey": "新的秘密密钥", + "architecture": "结构", "sites": "站点", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", "siteWgManualConfigurationRequired": "需要手动配置", "userErrorNotAdminOrOwner": "用户不是管理员或所有者", - "pangolinSettings": "设置 - Pangolin", + "pangolinSettings": "设置-Pangolin", "accessRoleYour": "您的角色:", "accessRoleSelect2": "选择角色", "accessUserSelect": "选择一个用户", "otpEmailEnter": "输入电子邮件", "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", - "otpEmailErrorInvalid": "无效的邮箱地址。通配符(*)必须占据整个开头部分。", - "otpEmailSmtpRequired": "需要先配置SMTP", + "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", + "otpEmailSmtpRequired": "需要SMTP", "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", "otpEmailTitle": "一次性密码", "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", "otpEmailWhitelist": "电子邮件白名单", "otpEmailWhitelistList": "白名单邮件", - "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域名的任何电子邮件地址。", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", "otpEmailWhitelistSave": "保存白名单", "passwordAdd": "添加密码", "passwordRemove": "删除密码", @@ -642,12 +642,12 @@ "resourcePincode": "PIN 码", "resourcePincodeSubmit": "启用 PIN 码保护", "resourcePincodeProtection": "PIN 码保护 {status}", - "resourcePincodeRemove": "资源 PIN 码已删除", - "resourcePincodeRemoveDescription": "已成功删除资源 PIN 码", - "resourcePincodeSetup": "资源 PIN 码已设置", - "resourcePincodeSetupDescription": "资源 PIN 码已成功设置", - "resourcePincodeSetupTitle": "设置 PIN 码", - "resourcePincodeSetupTitleDescription": "设置 PIN 码来保护此资源", + "resourcePincodeRemove": "资源粉码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源密码", + "resourcePincodeSetup": "资源PIN 码已设置", + "resourcePincodeSetupDescription": "资源固定码已成功设置", + "resourcePincodeSetupTitle": "设置粉码", + "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", "resourceRoleDescription": "管理员总是可以访问此资源。", "resourceUsersRoles": "用户和角色", "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", @@ -658,14 +658,14 @@ "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", "proxyErrorInvalidPort": "无效的端口号", "subdomainErrorInvalid": "无效的子域", - "domainErrorFetch": "获取域名失败", - "domainErrorFetchDescription": "获取域名时出错", + "domainErrorFetch": "获取域名出错", + "domainErrorFetchDescription": "获取域时出错", "resourceErrorUpdate": "更新资源失败", "resourceErrorUpdateDescription": "更新资源时出错", "resourceUpdated": "资源已更新", "resourceUpdatedDescription": "资源已成功更新", - "resourceErrorTransfer": "转移资源失败", - "resourceErrorTransferDescription": "转移资源时出错", + "resourceErrorTransfer": "传输资源失败", + "resourceErrorTransferDescription": "传输资源时出错", "resourceTransferred": "资源已传输", "resourceTransferredDescription": "资源已成功传输", "resourceErrorToggle": "切换资源失败", @@ -675,9 +675,9 @@ "resourceGeneral": "常规设置", "resourceGeneralDescription": "配置此资源的常规设置", "resourceEnable": "启用资源", - "resourceTransfer": "转移资源", + "resourceTransfer": "传输资源", "resourceTransferDescription": "将此资源转移到另一个站点", - "resourceTransferSubmit": "转移资源", + "resourceTransferSubmit": "传输资源", "siteDestination": "目标站点", "searchSites": "搜索站点", "accessRoleCreate": "创建角色", @@ -704,13 +704,13 @@ "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", "licenseTierCommercial": "商业许可证", - "licensed": "已授权", - "yes": "是", + "licensed": "许可的", + "yes": "否", "no": "否", "sitesAdditional": "其他站点", "licenseKeys": "许可证密钥", - "sitestCountDecrease": "减少站点数量", - "sitestCountIncrease": "增加站点数量", + "sitestCountDecrease": "减少站点计数", + "sitestCountIncrease": "增加站点计数", "idpManage": "管理身份提供商", "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", @@ -723,12 +723,12 @@ "idp": "身份提供商", "idpSearch": "搜索身份提供者...", "idpAdd": "添加身份提供商", - "idpClientIdRequired": "客户端ID 是必需的。", - "idpClientSecretRequired": "客户端密钥是必需的。", - "idpErrorAuthUrlInvalid": "身份验证URL 必须是有效的 URL。", - "idpErrorTokenUrlInvalid": "令牌URL 必须是有效的 URL。", - "idpPathRequired": "标识符路径是必需的。", - "idpScopeRequired": "授权范围是必需的。", + "idpClientIdRequired": "客户端ID是必需的。", + "idpClientSecretRequired": "客户端密码是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", + "idpPathRequired": "标识路径是必需的。", + "idpScopeRequired": "范围是必需的。", "idpOidcDescription": "配置 OpenID 连接身份提供商", "idpCreatedDescription": "身份提供商创建成功", "idpCreate": "创建身份提供商", @@ -748,24 +748,24 @@ "idpClientSecret": "客户端密钥", "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", "idpAuthUrl": "授权 URL", - "idpAuthUrlDescription": "OAuth2 授权端点的 URL", - "idpTokenUrl": "令牌 URL", - "idpTokenUrlDescription": "OAuth2 令牌端点的 URL", - "idpOidcConfigureAlert": "重要提示", - "idpOidcConfigureAlertDescription": "创建身份提供方后,您需要在其设置中配置回调 URL。回调 URL 会在创建成功后提供。", + "idpAuthUrlDescription": "OAuth2 授权终点 URL", + "idpTokenUrl": "令牌网址", + "idpTokenUrlDescription": "OAuth2 令牌端点URL", + "idpOidcConfigureAlert": "重要信息", + "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", "idpToken": "令牌配置", - "idpTokenDescription": "配置如何从 ID 令牌中提取用户信息", + "idpTokenDescription": "配置如何从ID令牌中提取用户信息", "idpJmespathAbout": "关于 JMESPath", - "idpJmespathAboutDescription": "以下路径使用 JMESPath 语法从 ID 令牌中提取值。", - "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息", - "idpJmespathLabel": "标识符路径", - "idpJmespathLabelDescription": "ID 令牌中用户标识符的路径", - "idpJmespathEmailPathOptional": "邮箱路径(可选)", - "idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径", - "idpJmespathNamePathOptional": "用户名路径(可选)", - "idpJmespathNamePathOptionalDescription": "ID 令牌中用户名的路径", - "idpOidcConfigureScopes": "作用域(Scopes)", - "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 请求作用域列表", + "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", + "idpJmespathLabel": "标识路径", + "idpJmespathLabelDescription": "用户标识符的路径", + "idpJmespathEmailPathOptional": "电子邮件路径(可选)", + "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", + "idpJmespathNamePathOptional": "名称路径(可选)", + "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", + "idpOidcConfigureScopes": "范围", + "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", "idpSubmit": "创建身份提供商", "orgPolicies": "组织策略", "idpSettings": "{idpName} 设置", @@ -789,13 +789,13 @@ "defaultMappingsRole": "默认角色映射", "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", "defaultMappingsOrg": "默认组织映射", - "defaultMappingsOrgDescription": "此表达式必须返回 组织ID 或 true 才能允许用户访问组织。", + "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", "defaultMappingsSubmit": "保存默认映射", "orgPoliciesEdit": "编辑组织策略", "org": "组织", "orgSelect": "选择组织", "orgSearch": "搜索", - "orgNotFound": "找不到组织。", + "orgNotFound": "找不到 org 。", "roleMappingPathOptional": "角色映射路径(可选)", "orgMappingPathOptional": "组织映射路径(可选)", "orgPolicyUpdate": "更新策略", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "Pangolin Logo", + "pangolinLogoAlt": "邦戈林徽标", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -851,13 +851,13 @@ "otpEmail": "一次性密码 (OTP)", "otpEmailSubmit": "提交 OTP", "backToEmail": "回到电子邮件", - "noSupportKey": "服务器当前未使用支持者密钥,欢迎支持本项目!", + "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", "accessDenied": "访问被拒绝", - "accessDeniedDescription": "当前账户无权访问此资源。如认为这是错误,请与管理员联系。", + "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", "accessTokenError": "检查访问令牌时出错", "accessGranted": "已授予访问", "accessUrlInvalid": "访问 URL 无效", - "accessGrantedDescription": "您已获准访问此资源,正在为您跳转...", + "accessGrantedDescription": "您已获准访问此资源。重定向您...", "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", "tokenInvalid": "无效的令牌", "pincodeInvalid": "无效的代码", @@ -866,9 +866,9 @@ "passwordResetSuccess": "密码重置成功!返回登录...", "passwordReset": "重置密码", "passwordResetDescription": "按照步骤重置您的密码", - "passwordResetSent": "我们将发送一个验证码到这个电子邮件地址。", - "passwordResetCode": "验证码", - "passwordResetCodeDescription": "请检查您的电子邮件以获取验证码。", + "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", "passwordNew": "新密码", "passwordNewConfirm": "确认新密码", "pincodeAuth": "验证器代码", @@ -898,7 +898,7 @@ "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", - "orgErrorCreate": "创建组织时出错", + "orgErrorCreate": "创建 org 时出错", "pageNotFound": "找不到页面", "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", "overview": "概览", @@ -923,13 +923,13 @@ "tagWarnDuplicate": "未添加重复标签 {tagText}", "supportKeyInvalid": "无效密钥", "supportKeyInvalidDescription": "您的支持者密钥无效。", - "supportKeyValid": "有效的密钥", + "supportKeyValid": "Valid Key", "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", "supportKeyErrorValidationDescription": "验证支持者密钥失败。", - "supportKey": "支持开发和通过一个 Pangolin !", - "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展 Pangolin 。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", - "supportKeyPet": "您还可以领养并见到属于自己的 Pangolin!", - "supportKeyPurchase": "付款通过 GitHub 进行处理,之后您可以在以下位置获取您的密钥:", + "supportKey": "支持开发和通过一个潘戈林!", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", + "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", "supportKeyPurchaseLink": "我们的网站", "supportKeyPurchase2": "并在这里兑换。", "supportKeyLearnMore": "了解更多。", @@ -944,15 +944,15 @@ "supportKeyRedeem": "兑换支持者密钥", "supportKeyHideSevenDays": "隐藏7天", "supportKeyEnter": "输入支持者密钥", - "supportKeyEnterDescription": "见到你自己的 Pangolin!", - "githubUsername": "GitHub 用户名", + "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", + "githubUsername": "GitHub Username", "supportKeyInput": "支持者密钥", "supportKeyBuy": "购买支持者密钥", "logoutError": "注销错误", "signingAs": "登录为", "serverAdmin": "服务器管理员", - "otpEnable": "启用双因子认证", - "otpDisable": "禁用双因子认证", + "otpEnable": "启用双因子", + "otpDisable": "禁用双因子", "logout": "登出", "licenseTierProfessionalRequired": "需要专业版", "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "设置资源粉码", "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", - "actionCreateTarget": "创建目标", + "actionCreateTarget": "Create Target", "actionDeleteTarget": "删除目标", "actionGetTarget": "获取目标", "actionListTargets": "列表目标", - "actionUpdateTarget": "更新目标", + "actionUpdateTarget": "Update Target", "actionCreateRole": "创建角色", "actionDeleteRole": "删除角色", "actionGetRole": "获取角色", @@ -1002,7 +1002,7 @@ "actionListResourceRules": "列出资源规则", "actionUpdateResourceRule": "更新资源规则", "actionListOrgs": "列出组织", - "actionCheckOrgId": "检查组织ID", + "actionCheckOrgId": "检查 ID", "actionCreateOrg": "创建组织", "actionDeleteOrg": "删除组织", "actionListApiKeys": "列出API密钥", @@ -1013,15 +1013,15 @@ "actionCreateIdp": "创建IDP", "actionUpdateIdp": "更新IDP", "actionDeleteIdp": "删除IDP", - "actionListIdps": "列出IDP", + "actionListIdps": "列出国内流离失所者", "actionGetIdp": "获取IDP", - "actionCreateIdpOrg": "创建 IDP组织策略", - "actionDeleteIdpOrg": "删除 IDP组织策略", - "actionListIdpOrgs": "列出 IDP组织", - "actionUpdateIdpOrg": "更新 IDP组织", + "actionCreateIdpOrg": "创建IDP Org 策略", + "actionDeleteIdpOrg": "删除IDP Org 策略", + "actionListIdpOrgs": "列出国内流离失所者组织", + "actionUpdateIdpOrg": "更新IDP Org", "noneSelected": "未选择", "orgNotFound2": "未找到组织。", - "searchProgress": "搜索中...", + "searchProgress": "搜索...", "create": "创建", "orgs": "组织", "loginError": "登录时出错", @@ -1031,13 +1031,13 @@ "otpAuthSubmit": "提交代码", "idpContinue": "或者继续", "otpAuthBack": "返回登录", - "navbar": "导航菜单", + "navbar": "Navigation Menu", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", - "otpErrorEnableDescription": "启用 2FA 时出错", - "otpSetupCheckCode": "请输入您的6位数字代码", + "otpErrorEnableDescription": "启用2FA 时出错", + "otpSetupCheckCode": "请输入一个6位数字", "otpSetupCheckCodeRetry": "无效的代码。请重试。", "otpSetup": "启用两步验证", "otpSetupDescription": "用额外的保护层来保护您的帐户", @@ -1061,20 +1061,20 @@ "copyTextFailed": "复制文本失败: ", "copyTextClipboard": "复制到剪贴板", "inviteErrorInvalidConfirmation": "无效确认", - "passwordRequired": "必须填写密码", + "passwordRequired": "密码是必需的", "allowAll": "允许所有", "permissionsAllowAll": "允许所有权限", - "githubUsernameRequired": "必须填写 GitHub 用户名", - "supportKeyRequired": "必须填写支持者密钥", - "passwordRequirementsChars": "密码至少需要 8 个字符", + "githubUsernameRequired": "GitHub 用户名是必需的", + "supportKeyRequired": "支持者密钥是必需的", + "passwordRequirementsChars": "密码必须至少 8 个字符", "language": "语言", "verificationCodeRequired": "必须输入代码", "userErrorNoUpdate": "没有要更新的用户", "siteErrorNoUpdate": "没有要更新的站点", "resourceErrorNoUpdate": "没有可更新的资源", "authErrorNoUpdate": "没有要更新的身份验证信息", - "orgErrorNoUpdate": "没有要更新的组织", - "orgErrorNoProvided": "未提供组织", + "orgErrorNoUpdate": "没有要更新的 org", + "orgErrorNoProvided": "未提供 org", "apiKeysErrorNoUpdate": "没有要更新的 API 密钥", "sidebarOverview": "概览", "sidebarHome": "首页", @@ -1090,4 +1090,4 @@ "sidebarAllUsers": "所有用户", "sidebarIdentityProviders": "身份提供商", "sidebarLicense": "证书" -} +} \ No newline at end of file From cd35148e4895ce3f4764cda0e73f678c0e01b110 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:48:36 +0200 Subject: [PATCH 095/105] Update zh-CN.json --- messages/zh-CN.json | 332 ++++++++++++++++++++++---------------------- 1 file changed, 166 insertions(+), 166 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 110f1964..c2420ca4 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1,5 +1,5 @@ { - "setupCreate": "创建您的组织、网站和资源", + "setupCreate": "创建您的第一个组织、网站和资源", "setupNewOrg": "新建组织", "setupCreateOrg": "创建组织", "setupCreateResources": "创建资源", @@ -7,17 +7,17 @@ "orgDisplayName": "这是您组织的显示名称。", "orgId": "组织ID", "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", - "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", + "setupErrorIdentifier": "组织ID 已被使用。请另选一个。", "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", - "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", - "dismiss": "关闭", + "componentsMember": "{count, plural, =0 {您目前不是任何组织的成员。} other {你已加入 # 个组织。}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", + "dismiss": "忽略", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", - "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", "inviteLoginUser": "请确保您以正确的用户登录。", "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", @@ -61,27 +61,27 @@ "siteNameDescription": "这是站点的显示名称。", "method": "方法", "siteMethodDescription": "这是您将如何显示连接。", - "siteLearnNewt": "学习如何在您的系统上安装Newt", + "siteLearnNewt": "学习如何在您的系统上安装 Newt", "siteSeeConfigOnce": "您只能看到一次配置。", - "siteLoadWGConfig": "正在载入Wire保护配置...", + "siteLoadWGConfig": "正在载入 WireGuard 配置...", "siteDocker": "扩展 Docker 部署详细信息", - "toggle": "切换键", - "dockerCompose": "Docker 配置", - "dockerRun": "停靠栏", - "siteLearnLocal": "本地站点没有隧道,学习更多", - "siteConfirmCopy": "我已复制配置", + "toggle": "切换", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", + "siteConfirmCopy": "我已经复制了配置信息", "searchSitesProgress": "搜索站点...", "siteAdd": "添加站点", - "siteInstallNewt": "安装新的", - "siteInstallNewtDescription": "在您的系统上获得新的运行", - "WgConfiguration": "Wire护卫配置", + "siteInstallNewt": "安装 Newt", + "siteInstallNewtDescription": "在您的系统中运行 Newt", + "WgConfiguration": "WireGuard 配置", "WgConfigurationDescription": "使用以下配置连接到您的网络", "operatingSystem": "操作系统", "commands": "命令", - "recommended": "推荐的", - "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", - "siteRunsInDocker": "运行在停靠栏", - "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", + "recommended": "推荐", + "siteNewtDescription": "为获得最佳用户体验,请使用 Newt。其底层采用 WireGuard 技术,可直接通过 Pangolin 控制台,使用局域网地址访问您私有网络中的资源。", + "siteRunsInDocker": "在 Docker 中运行", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中运行", "siteErrorDelete": "删除站点出错", "siteErrorUpdate": "更新站点失败", "siteErrorUpdateDescription": "更新站点时出错。", @@ -90,17 +90,17 @@ "siteGeneralDescription": "配置此站点的常规设置", "siteSettingDescription": "配置您网站上的设置", "siteSetting": "{siteName} 设置", - "siteNewtTunnel": "新隧道(推荐)", - "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", - "siteWg": "基本线甲", - "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", - "siteLocalDescription": "仅本地资源。没有隧道。", + "siteNewtTunnel": "Newt 隧道 (推荐)", + "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", + "siteWg": "基本 WireGuard", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", + "siteLocalDescription": "仅限本地资源。不需要隧道。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", - "siteNewtCredentials": "新建凭据", - "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", - "siteCredentialsSave": "保存您的证书", - "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "siteNewtCredentials": "Newt 凭据", + "siteNewtCredentialsDescription": "这是 Newt 服务器的身份验证凭据", + "siteCredentialsSave": "保存您的凭据", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制并保存到一个安全的地方。", "siteInfo": "站点信息", "status": "状态", "shareTitle": "管理共享链接", @@ -111,16 +111,16 @@ "shareErrorDeleteMessage": "删除链接时出错", "shareDeleted": "链接已删除", "shareDeletedDescription": "链接已删除", - "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求头。 每次验证访问请求都必须从客户端传递。", "accessToken": "访问令牌", "usageExamples": "用法示例", - "tokenId": "Token ID", - "requestHeades": "请求标题", + "tokenId": "令牌 ID", + "requestHeades": "请求头", "queryParameter": "查询参数", - "importantNote": "重要笔记", - "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", + "importantNote": "重要提示", + "shareImportantDescription": "出于安全考虑,建议尽可能在使用请求头传递参数,因为查询参数可能会被浏览器历史记录或服务器日志记录。", "token": "令牌", - "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", + "shareTokenSecurety": "请妥善保管您的访问令牌,不要将其暴露在公开访问的区域或客户端代码中。", "shareErrorFetchResource": "获取资源失败", "shareErrorFetchResourceDescription": "获取资源时出错", "shareErrorCreate": "无法创建共享链接", @@ -141,7 +141,7 @@ "title": "标题", "created": "已创建", "expires": "过期时间", - "never": "从不使用", + "never": "永不过期", "shareErrorSelectResource": "请选择一个资源", "resourceTitle": "管理资源", "resourceDescription": "为您的私人应用程序创建安全代理", @@ -149,14 +149,14 @@ "resourceAdd": "添加资源", "resourceErrorDelte": "删除资源时出错", "authentication": "认证", - "protected": "保护", - "notProtected": "不保护", - "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", + "protected": "受到保护", + "notProtected": "未受到保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与该资源相关的所有目标也将被删除。", "resourceMessageConfirm": "请在下面输入资源名称以确认。", "resourceQuestionRemove": "您确定要从组织中删除 {selectedResource} 吗?", "resourceHTTP": "HTTPS 资源", - "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", - "resourceRaw": "Raw TCP/UDP 资源", + "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "TCP/UDP 资源", "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", @@ -172,20 +172,20 @@ "resourceHTTPSSettings": "HTTPS 设置", "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", "domainType": "域类型", - "subdomain": "子域", - "baseDomain": "基础域", - "subdomnainDescription": "您的资源可以访问的子域。", + "subdomain": "子域名", + "baseDomain": "根域名", + "subdomnainDescription": "您的资源可以访问的子域名。", "resourceRawSettings": "TCP/UDP 设置", "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "协议", + "protocolSelect": "选择协议", "resourcePortNumber": "端口号", "resourcePortNumberDescription": "代理请求的外部端口号。", "cancel": "取消", "resourceConfig": "配置片段", "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", "resourceAddEntrypoints": "Traefik: 添加入口点", - "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", + "resourceExposePorts": "Gerbil:在 Docker Compose 中显示端口", "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", "resourceBack": "返回资源", "resourceGoTo": "转到资源", @@ -194,23 +194,23 @@ "visibility": "可见性", "enabled": "已启用", "disabled": "已禁用", - "general": "A. 概况", + "general": "概览", "generalSettings": "常规设置", "proxy": "代理服务器", "rules": "规则", "resourceSettingDescription": "配置您资源上的设置", "resourceSetting": "{resourceName} 设置", - "alwaysAllow": "总是允许", - "alwaysDeny": "总是拒绝", + "alwaysAllow": "一律允许", + "alwaysDeny": "一律拒绝", "orgSettingsDescription": "配置您组织的一般设置", "orgGeneralSettings": "组织设置", "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", "saveGeneralSettings": "保存常规设置", "orgDangerZone": "危险区域", - "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", + "orgDangerZoneDescription": "一旦删除该组织,将无法恢复,请务必确认。", "orgDelete": "删除组织", "orgDeleteConfirm": "确认删除组织", - "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", + "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", "orgUpdated": "组织已更新", @@ -224,15 +224,15 @@ "orgDeleted": "组织已删除", "orgDeletedMessage": "组织及其数据已被删除。", "orgMissing": "缺少组织 ID", - "orgMissingMessage": "没有机构ID,无法重新生成邀请。", + "orgMissingMessage": "没有组织ID,无法重新生成邀请。", "accessUsersManage": "管理用户", - "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", + "accessUsersDescription": "邀请用户并位他们添加角色以管理访问您的组织", "accessUsersSearch": "搜索用户...", "accessUserCreate": "创建用户", "accessUserRemove": "删除用户", "username": "用户名", "identityProvider": "身份提供商", - "role": "作用", + "role": "角色", "nameRequired": "名称是必填项", "accessRolesManage": "管理角色", "accessRolesDescription": "配置角色来管理访问您的组织", @@ -244,13 +244,13 @@ "inviteDescription": "管理您给其他用户的邀请", "inviteSearch": "搜索邀请...", "minutes": "分钟", - "hours": "小时数", + "hours": "小时", "days": "天", "weeks": "周", "months": "月", "years": "年", "day": "{count, plural, =1 {# 天} other {# 天}}", - "apiKeysTitle": "API 密钥信息", + "apiKeysTitle": "API 密钥", "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", "apiKeysErrorCreate": "创建 API 密钥出错", "apiKeysErrorSetPermission": "设置权限出错", @@ -260,7 +260,7 @@ "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", "apiKeysList": "您的 API 密钥", "apiKeysSave": "保存您的 API 密钥", - "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "apiKeysSaveDescription": "该信息仅会显示一次,请确保将其复制到安全的位置。", "apiKeysInfo": "您的 API 密钥是:", "apiKeysConfirmCopy": "我已复制 API 密钥", "generate": "生成", @@ -280,7 +280,7 @@ "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", - "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", + "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", "apiKeysDelete": "删除 API 密钥", @@ -290,7 +290,7 @@ "userTitle": "管理所有用户", "userDescription": "查看和管理系统中的所有用户", "userAbount": "关于用户管理", - "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", + "userAbountDescription": "此表格显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表格中的删除操作删除其根用户对象。", "userServer": "服务器用户", "userSearch": "搜索服务器用户...", "userErrorDelete": "删除用户时出错", @@ -300,7 +300,7 @@ "userMessageConfirm": "请在下面输入用户名称以确认。", "userQuestionRemove": "您确定要从服务器中永久删除 {selectedUser} 吗?", "licenseKey": "许可证密钥", - "valid": "Valid", + "valid": "有效", "numberOfSites": "站点数量", "licenseKeySearch": "搜索许可证密钥...", "licenseKeyAdd": "添加许可证密钥", @@ -334,7 +334,7 @@ "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", - "licenseTitle": "管理许可状态", + "licenseTitle": "管理许可证状态", "licenseTitleDescription": "查看和管理系统中的许可证密钥", "licenseHost": "主机许可证", "licenseHostDescription": "管理主机的主许可证密钥。", @@ -347,14 +347,14 @@ "licensePurchase": "购买许可证", "licensePurchaseSites": "购买更多站点", "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 个站点", - "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", - "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", - "licenseFee": "许可费", - "licensePriceSite": "每个站点价格", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {# 站点}}", + "licensePurchaseDescription": "请选择您希望 {selectedMode, select, license {直接购买许可证,您可以随时增加更多站点。} other {为现有许可证购买更多站点}}", + "licenseFee": "许可证费用", + "licensePriceSite": "每个站点的价格", "total": "总计", "licenseContinuePayment": "继续付款", "pricingPage": "定价页面", - "pricingPortal": "查看购买门户网站", + "pricingPortal": "前往付款页面", "licensePricingPage": "关于最新的价格和折扣,请访问 ", "invite": "邀请", "inviteRegenerate": "重新生成邀请", @@ -365,7 +365,7 @@ "inviteRemoved": "邀请已删除", "inviteRemovedDescription": "为 {email} 创建的邀请已删除", "inviteQuestionRemove": "您确定要删除 {email} 的邀请吗?", - "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。", "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", "inviteQuestionRegenerate": "您确定要重新邀请 {email} 吗?这将会撤销掉之前的邀请", "inviteRemoveConfirm": "确认删除邀请", @@ -373,7 +373,7 @@ "inviteSent": "邀请邮件已成功发送至 {email}。", "inviteSentEmail": "发送电子邮件通知给用户", "inviteGenerate": "已为 {email} 创建新的邀请。", - "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateError": "重复的邀请", "inviteDuplicateErrorDescription": "此用户的邀请已存在。", "inviteRateLimitError": "超出速率限制", "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", @@ -463,7 +463,7 @@ "targetErrorFetchDescription": "获取目标时出错", "siteErrorFetch": "获取资源失败", "siteErrorFetchDescription": "获取资源时出错", - "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicate": "重复的目标", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", "targetWireGuardErrorInvalidIp": "Invalid target IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", @@ -479,9 +479,9 @@ "proxyUpdatedDescription": "您的代理设置已成功更新", "proxyErrorUpdate": "更新代理设置失败", "proxyErrorUpdateDescription": "更新代理设置时出错", - "targetAddr": "IP / Hostname", + "targetAddr": "IP / 域名", "targetPort": "端口", - "targetProtocol": "Protocol", + "targetProtocol": "协议", "targetTlsSettings": "HTTPS & TLS 设置", "targetTlsSettingsDescription": "配置资源的 TLS 设置", "targetTlsSettingsAdvanced": "高级TLS设置", @@ -493,7 +493,7 @@ "targetStickySessions": "启用置顶会话", "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", "methodSelect": "选择方法", - "targetSubmit": "Add Target", + "targetSubmit": "添加目标", "targetNoOne": "没有目标。使用表单添加目标。", "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", "targetsSubmit": "保存目标", @@ -504,7 +504,7 @@ "proxyAdditionalSubmit": "保存代理设置", "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", - "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", + "ipAddressErrorInvalidOctet": "无效的 IP 地址", "path": "路径", "ipAddressRange": "IP 范围", "rulesErrorFetch": "获取规则失败", @@ -520,7 +520,7 @@ "rulesErrorUpdate": "更新规则失败", "rulesErrorUpdateDescription": "更新规则时出错", "rulesUpdated": "启用规则", - "rulesUpdatedDescription": "规则评价已更新", + "rulesUpdatedDescription": "规则已更新", "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", @@ -533,18 +533,18 @@ "ruleErrorUpdate": "操作失败", "ruleErrorUpdateDescription": "保存过程中发生错误", "rulesPriority": "优先权", - "rulesAction": "行 动", - "rulesMatchType": "比赛类型", + "rulesAction": "行为", + "rulesMatchType": "匹配类型", "value": "值", "rulesAbout": "关于规则", - "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", + "rulesAboutDescription": "规则使您能够依据特定条件控制资源访问权限。您可以创建基于 IP 地址或 URL 路径的规则,以允许或拒绝访问。", "rulesActions": "行动", "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", "rulesMatchCriteria": "匹配条件", "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", - "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或模式", "rulesEnable": "启用规则", "rulesEnableDescription": "启用或禁用此资源的规则评估", "rulesResource": "资源规则配置", @@ -562,10 +562,10 @@ "domainsErrorFetch": "获取域名出错", "domainsErrorFetchDescription": "获取域时出错", "none": "无", - "unknown": "未知的", + "unknown": "未知", "resources": "资源", - "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", - "resourcesWireGuardConnect": "与Wire护卫加密安全连接", + "resourcesDescription": "资源是您私有网络中运行的应用程序的代理。您可以为私有网络中的任何 HTTP/HTTPS 或 TCP/UDP 服务创建资源。每个资源都必须连接到一个站点,以通过加密的 WireGuard 隧道实现私密且安全的连接。", + "resourcesWireGuardConnect": "采用 WireGuard 提供的加密安全连接", "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", "resourcesUsersRolesAccess": "基于用户和角色的访问控制", "resourcesErrorUpdate": "切换资源失败", @@ -574,7 +574,7 @@ "shareLink": "{resource} 的分享链接", "resourceSelect": "选择资源", "shareLinks": "分享链接", - "share": "可共享链接", + "share": "分享链接", "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", "shareEasyCreate": "轻松创建和分享", "shareConfigurableExpirationDuration": "可配置的过期时间", @@ -585,29 +585,29 @@ "unknownCommand": "未知命令", "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", - "newtEndpoint": "Newt Endpoint", + "newtEndpoint": "Newt 端点", "newtId": "Newt ID", - "newtSecretKey": "新的秘密密钥", - "architecture": "结构", + "newtSecretKey": "Newt 私钥", + "architecture": "架构", "sites": "站点", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", "siteWgManualConfigurationRequired": "需要手动配置", "userErrorNotAdminOrOwner": "用户不是管理员或所有者", - "pangolinSettings": "设置-Pangolin", + "pangolinSettings": "设置 - Pangolin", "accessRoleYour": "您的角色:", "accessRoleSelect2": "选择角色", "accessUserSelect": "选择一个用户", "otpEmailEnter": "输入电子邮件", "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", - "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", - "otpEmailSmtpRequired": "需要SMTP", + "otpEmailErrorInvalid": "无效的邮箱地址。通配符(*)必须占据整个开头部分。", + "otpEmailSmtpRequired": "需要先配置SMTP", "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", "otpEmailTitle": "一次性密码", "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", "otpEmailWhitelist": "电子邮件白名单", "otpEmailWhitelistList": "白名单邮件", - "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域名的任何电子邮件地址。", "otpEmailWhitelistSave": "保存白名单", "passwordAdd": "添加密码", "passwordRemove": "删除密码", @@ -642,12 +642,12 @@ "resourcePincode": "PIN 码", "resourcePincodeSubmit": "启用 PIN 码保护", "resourcePincodeProtection": "PIN 码保护 {status}", - "resourcePincodeRemove": "资源粉码已删除", - "resourcePincodeRemoveDescription": "已成功删除资源密码", - "resourcePincodeSetup": "资源PIN 码已设置", - "resourcePincodeSetupDescription": "资源固定码已成功设置", - "resourcePincodeSetupTitle": "设置粉码", - "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", + "resourcePincodeRemove": "资源 PIN 码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源 PIN 码", + "resourcePincodeSetup": "资源 PIN 码已设置", + "resourcePincodeSetupDescription": "资源 PIN 码已成功设置", + "resourcePincodeSetupTitle": "设置 PIN 码", + "resourcePincodeSetupTitleDescription": "设置 PIN 码来保护此资源", "resourceRoleDescription": "管理员总是可以访问此资源。", "resourceUsersRoles": "用户和角色", "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", @@ -658,14 +658,14 @@ "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", "proxyErrorInvalidPort": "无效的端口号", "subdomainErrorInvalid": "无效的子域", - "domainErrorFetch": "获取域名出错", - "domainErrorFetchDescription": "获取域时出错", + "domainErrorFetch": "获取域名失败", + "domainErrorFetchDescription": "获取域名时出错", "resourceErrorUpdate": "更新资源失败", "resourceErrorUpdateDescription": "更新资源时出错", "resourceUpdated": "资源已更新", "resourceUpdatedDescription": "资源已成功更新", - "resourceErrorTransfer": "传输资源失败", - "resourceErrorTransferDescription": "传输资源时出错", + "resourceErrorTransfer": "转移资源失败", + "resourceErrorTransferDescription": "转移资源时出错", "resourceTransferred": "资源已传输", "resourceTransferredDescription": "资源已成功传输", "resourceErrorToggle": "切换资源失败", @@ -675,9 +675,9 @@ "resourceGeneral": "常规设置", "resourceGeneralDescription": "配置此资源的常规设置", "resourceEnable": "启用资源", - "resourceTransfer": "传输资源", + "resourceTransfer": "转移资源", "resourceTransferDescription": "将此资源转移到另一个站点", - "resourceTransferSubmit": "传输资源", + "resourceTransferSubmit": "转移资源", "siteDestination": "目标站点", "searchSites": "搜索站点", "accessRoleCreate": "创建角色", @@ -704,13 +704,13 @@ "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", "licenseTierCommercial": "商业许可证", - "licensed": "许可的", - "yes": "否", + "licensed": "已授权", + "yes": "是", "no": "否", "sitesAdditional": "其他站点", "licenseKeys": "许可证密钥", - "sitestCountDecrease": "减少站点计数", - "sitestCountIncrease": "增加站点计数", + "sitestCountDecrease": "减少站点数量", + "sitestCountIncrease": "增加站点数量", "idpManage": "管理身份提供商", "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", @@ -723,12 +723,12 @@ "idp": "身份提供商", "idpSearch": "搜索身份提供者...", "idpAdd": "添加身份提供商", - "idpClientIdRequired": "客户端ID是必需的。", - "idpClientSecretRequired": "客户端密码是必需的。", - "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", - "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", - "idpPathRequired": "标识路径是必需的。", - "idpScopeRequired": "范围是必需的。", + "idpClientIdRequired": "客户端ID 是必需的。", + "idpClientSecretRequired": "客户端密钥是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL 必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL 必须是有效的 URL。", + "idpPathRequired": "标识符路径是必需的。", + "idpScopeRequired": "授权范围是必需的。", "idpOidcDescription": "配置 OpenID 连接身份提供商", "idpCreatedDescription": "身份提供商创建成功", "idpCreate": "创建身份提供商", @@ -748,24 +748,24 @@ "idpClientSecret": "客户端密钥", "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", "idpAuthUrl": "授权 URL", - "idpAuthUrlDescription": "OAuth2 授权终点 URL", - "idpTokenUrl": "令牌网址", - "idpTokenUrlDescription": "OAuth2 令牌端点URL", - "idpOidcConfigureAlert": "重要信息", - "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", + "idpAuthUrlDescription": "OAuth2 授权端点的 URL", + "idpTokenUrl": "令牌 URL", + "idpTokenUrlDescription": "OAuth2 令牌端点的 URL", + "idpOidcConfigureAlert": "重要提示", + "idpOidcConfigureAlertDescription": "创建身份提供方后,您需要在其设置中配置回调 URL。回调 URL 会在创建成功后提供。", "idpToken": "令牌配置", - "idpTokenDescription": "配置如何从ID令牌中提取用户信息", + "idpTokenDescription": "配置如何从 ID 令牌中提取用户信息", "idpJmespathAbout": "关于 JMESPath", - "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", - "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", - "idpJmespathLabel": "标识路径", - "idpJmespathLabelDescription": "用户标识符的路径", - "idpJmespathEmailPathOptional": "电子邮件路径(可选)", - "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", - "idpJmespathNamePathOptional": "名称路径(可选)", - "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", - "idpOidcConfigureScopes": "范围", - "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", + "idpJmespathAboutDescription": "以下路径使用 JMESPath 语法从 ID 令牌中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息", + "idpJmespathLabel": "标识符路径", + "idpJmespathLabelDescription": "ID 令牌中用户标识符的路径", + "idpJmespathEmailPathOptional": "邮箱路径(可选)", + "idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径", + "idpJmespathNamePathOptional": "用户名路径(可选)", + "idpJmespathNamePathOptionalDescription": "ID 令牌中用户名的路径", + "idpOidcConfigureScopes": "作用域(Scopes)", + "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 请求作用域列表", "idpSubmit": "创建身份提供商", "orgPolicies": "组织策略", "idpSettings": "{idpName} 设置", @@ -789,13 +789,13 @@ "defaultMappingsRole": "默认角色映射", "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", "defaultMappingsOrg": "默认组织映射", - "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", + "defaultMappingsOrgDescription": "此表达式必须返回 组织ID 或 true 才能允许用户访问组织。", "defaultMappingsSubmit": "保存默认映射", "orgPoliciesEdit": "编辑组织策略", "org": "组织", "orgSelect": "选择组织", "orgSearch": "搜索", - "orgNotFound": "找不到 org 。", + "orgNotFound": "找不到组织。", "roleMappingPathOptional": "角色映射路径(可选)", "orgMappingPathOptional": "组织映射路径(可选)", "orgPolicyUpdate": "更新策略", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "邦戈林徽标", + "pangolinLogoAlt": "Pangolin Logo", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -851,13 +851,13 @@ "otpEmail": "一次性密码 (OTP)", "otpEmailSubmit": "提交 OTP", "backToEmail": "回到电子邮件", - "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", + "noSupportKey": "服务器当前未使用支持者密钥,欢迎支持本项目!", "accessDenied": "访问被拒绝", - "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", + "accessDeniedDescription": "当前账户无权访问此资源。如认为这是错误,请与管理员联系。", "accessTokenError": "检查访问令牌时出错", "accessGranted": "已授予访问", "accessUrlInvalid": "访问 URL 无效", - "accessGrantedDescription": "您已获准访问此资源。重定向您...", + "accessGrantedDescription": "您已获准访问此资源,正在为您跳转...", "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", "tokenInvalid": "无效的令牌", "pincodeInvalid": "无效的代码", @@ -866,9 +866,9 @@ "passwordResetSuccess": "密码重置成功!返回登录...", "passwordReset": "重置密码", "passwordResetDescription": "按照步骤重置您的密码", - "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", - "passwordResetCode": "Reset Code", - "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", + "passwordResetSent": "我们将发送一个验证码到这个电子邮件地址。", + "passwordResetCode": "验证码", + "passwordResetCodeDescription": "请检查您的电子邮件以获取验证码。", "passwordNew": "新密码", "passwordNewConfirm": "确认新密码", "pincodeAuth": "验证器代码", @@ -898,7 +898,7 @@ "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", - "orgErrorCreate": "创建 org 时出错", + "orgErrorCreate": "创建组织时出错", "pageNotFound": "找不到页面", "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", "overview": "概览", @@ -923,13 +923,13 @@ "tagWarnDuplicate": "未添加重复标签 {tagText}", "supportKeyInvalid": "无效密钥", "supportKeyInvalidDescription": "您的支持者密钥无效。", - "supportKeyValid": "Valid Key", + "supportKeyValid": "有效的密钥", "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", "supportKeyErrorValidationDescription": "验证支持者密钥失败。", - "supportKey": "支持开发和通过一个潘戈林!", - "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", - "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", - "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", + "supportKey": "支持开发和通过一个 Pangolin !", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展 Pangolin 。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "您还可以领养并见到属于自己的 Pangolin!", + "supportKeyPurchase": "付款通过 GitHub 进行处理,之后您可以在以下位置获取您的密钥:", "supportKeyPurchaseLink": "我们的网站", "supportKeyPurchase2": "并在这里兑换。", "supportKeyLearnMore": "了解更多。", @@ -944,15 +944,15 @@ "supportKeyRedeem": "兑换支持者密钥", "supportKeyHideSevenDays": "隐藏7天", "supportKeyEnter": "输入支持者密钥", - "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", - "githubUsername": "GitHub Username", + "supportKeyEnterDescription": "见到你自己的 Pangolin!", + "githubUsername": "GitHub 用户名", "supportKeyInput": "支持者密钥", "supportKeyBuy": "购买支持者密钥", "logoutError": "注销错误", "signingAs": "登录为", "serverAdmin": "服务器管理员", - "otpEnable": "启用双因子", - "otpDisable": "禁用双因子", + "otpEnable": "启用双因子认证", + "otpDisable": "禁用双因子认证", "logout": "登出", "licenseTierProfessionalRequired": "需要专业版", "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "设置资源粉码", "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", - "actionCreateTarget": "Create Target", + "actionCreateTarget": "创建目标", "actionDeleteTarget": "删除目标", "actionGetTarget": "获取目标", "actionListTargets": "列表目标", - "actionUpdateTarget": "Update Target", + "actionUpdateTarget": "更新目标", "actionCreateRole": "创建角色", "actionDeleteRole": "删除角色", "actionGetRole": "获取角色", @@ -1002,7 +1002,7 @@ "actionListResourceRules": "列出资源规则", "actionUpdateResourceRule": "更新资源规则", "actionListOrgs": "列出组织", - "actionCheckOrgId": "检查 ID", + "actionCheckOrgId": "检查组织ID", "actionCreateOrg": "创建组织", "actionDeleteOrg": "删除组织", "actionListApiKeys": "列出API密钥", @@ -1013,15 +1013,15 @@ "actionCreateIdp": "创建IDP", "actionUpdateIdp": "更新IDP", "actionDeleteIdp": "删除IDP", - "actionListIdps": "列出国内流离失所者", + "actionListIdps": "列出IDP", "actionGetIdp": "获取IDP", - "actionCreateIdpOrg": "创建IDP Org 策略", - "actionDeleteIdpOrg": "删除IDP Org 策略", - "actionListIdpOrgs": "列出国内流离失所者组织", - "actionUpdateIdpOrg": "更新IDP Org", + "actionCreateIdpOrg": "创建 IDP组织策略", + "actionDeleteIdpOrg": "删除 IDP组织策略", + "actionListIdpOrgs": "列出 IDP组织", + "actionUpdateIdpOrg": "更新 IDP组织", "noneSelected": "未选择", "orgNotFound2": "未找到组织。", - "searchProgress": "搜索...", + "searchProgress": "搜索中...", "create": "创建", "orgs": "组织", "loginError": "登录时出错", @@ -1031,13 +1031,13 @@ "otpAuthSubmit": "提交代码", "idpContinue": "或者继续", "otpAuthBack": "返回登录", - "navbar": "Navigation Menu", + "navbar": "导航菜单", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", - "otpErrorEnableDescription": "启用2FA 时出错", - "otpSetupCheckCode": "请输入一个6位数字", + "otpErrorEnableDescription": "启用 2FA 时出错", + "otpSetupCheckCode": "请输入您的6位数字代码", "otpSetupCheckCodeRetry": "无效的代码。请重试。", "otpSetup": "启用两步验证", "otpSetupDescription": "用额外的保护层来保护您的帐户", @@ -1061,20 +1061,20 @@ "copyTextFailed": "复制文本失败: ", "copyTextClipboard": "复制到剪贴板", "inviteErrorInvalidConfirmation": "无效确认", - "passwordRequired": "密码是必需的", + "passwordRequired": "必须填写密码", "allowAll": "允许所有", "permissionsAllowAll": "允许所有权限", - "githubUsernameRequired": "GitHub 用户名是必需的", - "supportKeyRequired": "支持者密钥是必需的", - "passwordRequirementsChars": "密码必须至少 8 个字符", + "githubUsernameRequired": "必须填写 GitHub 用户名", + "supportKeyRequired": "必须填写支持者密钥", + "passwordRequirementsChars": "密码至少需要 8 个字符", "language": "语言", "verificationCodeRequired": "必须输入代码", "userErrorNoUpdate": "没有要更新的用户", "siteErrorNoUpdate": "没有要更新的站点", "resourceErrorNoUpdate": "没有可更新的资源", "authErrorNoUpdate": "没有要更新的身份验证信息", - "orgErrorNoUpdate": "没有要更新的 org", - "orgErrorNoProvided": "未提供 org", + "orgErrorNoUpdate": "没有要更新的组织", + "orgErrorNoProvided": "未提供组织", "apiKeysErrorNoUpdate": "没有要更新的 API 密钥", "sidebarOverview": "概览", "sidebarHome": "首页", @@ -1090,4 +1090,4 @@ "sidebarAllUsers": "所有用户", "sidebarIdentityProviders": "身份提供商", "sidebarLicense": "证书" -} \ No newline at end of file +} From 0ae5ac9947ba1316a4d445e66300bd157c5dc6a8 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:50:11 +0200 Subject: [PATCH 096/105] Update de-DE.json --- messages/de-DE.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index db24894c..2a9668d8 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -11,7 +11,7 @@ "componentsErrorNoMemberCreate": "Du bist derzeit kein Mitglied einer Organisation. Erstelle eine Organisation, um zu starten.", "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", "welcome": "Willkommen zu Pangolin", - "componentsCreateOrg": "Erstelleeine Organisation", + "componentsCreateOrg": "Erstelle eine Organisation", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "dismiss": "Verwerfen", @@ -1090,4 +1090,4 @@ "sidebarAllUsers": "Alle Benutzer", "sidebarIdentityProviders": "Identitätsanbieter", "sidebarLicense": "Lizenz" -} \ No newline at end of file +} From d66739f69e03dbc1e1dfc62083229bffb8be6522 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 10 Jun 2025 07:30:30 +0000 Subject: [PATCH 097/105] update proxy section in resource --- messages/en-US.json | 4 +- .../resources/[resourceId]/proxy/page.tsx | 139 ++---------------- 2 files changed, 14 insertions(+), 129 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index c640e6c1..9ecea382 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Hostname", "targetPort": "Port", "targetProtocol": "Protocol", - "targetTlsSettings": "HTTPS & TLS Settings", - "targetTlsSettingsDescription": "Configure TLS settings for your resource", + "targetTlsSettings": "Secure Connection Configuration", + "targetTlsSettingsDescription": "Configure SSL/TLS settings for your resource", "targetTlsSettingsAdvanced": "Advanced TLS Settings", "targetTlsSni": "TLS Server Name (SNI)", "targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.", diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 6f318357..8232d9f4 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -560,111 +560,6 @@ export default function ReverseProxyTargets(props: { return ( - {resource.http && ( - - - - {t('targetTlsSettings')} - - - {t('targetTlsSettingsDescription')} - - - - - - - ( - - - { - field.onChange(val); - }} - /> - - - )} - /> - -
    - - - -
    - - ( - - - {t('targetTlsSni')} - - - - - - {t('targetTlsSniDescription')} - - - - )} - /> - -
    - - -
    -
    - - - -
    - )} - @@ -891,10 +786,10 @@ export default function ReverseProxyTargets(props: { - Secure Connection Configuration + {t('targetTlsSettings')} - Configure SSL/TLS settings for your resource + {t('targetTlsSettingsDescription')} @@ -915,7 +810,7 @@ export default function ReverseProxyTargets(props: {

    - Advanced TLS - Settings + {t('targetTlsSettingsAdvanced')}

    @@ -965,8 +859,7 @@ export default function ReverseProxyTargets(props: { render={({ field }) => ( - TLS Server Name - (SNI) + {t('targetTlsSni')} - The TLS Server - Name to use for - SNI. Leave empty - to use the - default. + {t('targetTlsSniDescription')} @@ -996,18 +885,17 @@ export default function ReverseProxyTargets(props: { loading={httpsTlsLoading} form="tls-settings-form" > - Save Settings + {t('targetTlsSubmit')} - Additional Proxy Settings + {t('proxyAdditional')} - Configure how your resource handles proxy - settings + {t('proxyAdditionalDescription')} @@ -1026,16 +914,13 @@ export default function ReverseProxyTargets(props: { render={({ field }) => ( - Custom Host Header + {t('proxyCustomHeader')} - The host header to set - when proxying requests. - Leave empty to use the - default. + {t('proxyCustomHeaderDescription')} @@ -1051,7 +936,7 @@ export default function ReverseProxyTargets(props: { loading={proxySettingsLoading} form="proxy-settings-form" > - Save Settings + {t('targetTlsSubmit')} From 3c2ea1a75f370752e9d6c1c12dac6966c92901d0 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 10 Jun 2025 18:34:04 -0400 Subject: [PATCH 098/105] Add translation and fix ts issues --- eslint.config.js | 3 +- messages/en-US.json | 37 ++++++- .../access/invitations/InvitationsTable.tsx | 2 +- .../invitations/RegenerateInvitationForm.tsx | 2 +- .../settings/access/users/UsersTable.tsx | 4 +- .../resources/[resourceId]/general/page.tsx | 101 ++++++++++++------ .../resources/[resourceId]/proxy/page.tsx | 1 - .../settings/sites/[niceId]/general/page.tsx | 44 ++++---- src/components/ContainersSelector.tsx | 85 +++++++-------- 9 files changed, 167 insertions(+), 112 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 71dc862c..afdfdd54 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,7 +3,8 @@ export default [ { rules: { semi: "error", - "prefer-const": "error" + "prefer-const": "error", + quotes: ["error", "double"] } } ]; diff --git a/messages/en-US.json b/messages/en-US.json index 9ecea382..8f518e02 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1089,5 +1089,40 @@ "sidebarSettings": "Settings", "sidebarAllUsers": "All Users", "sidebarIdentityProviders": "Identity Providers", - "sidebarLicense": "License" + "sidebarLicense": "License", + "enableDockerSocket": "Enable Docker Socket", + "enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "enableDockerSocketLink": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "viewDockerContainers": "View Docker Containers", + "containersIn": "Containers in {siteName}", + "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", + "containerName": "Name", + "containerImage": "Image", + "containerState": "State", + "containerNetworks": "Networks", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Labels", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Container Labels", + "containerLabelEmpty": "", + "containerPorts": "Ports", + "containerPortsMore": "+{count} more", + "containerActions": "Actions", + "select": "Select", + "noContainersMatchingFilters": "No containers found matching the current filters.", + "showContainersWithoutPorts": "Show containers without ports", + "showStoppedContainers": "Show stopped containers", + "noContainersFound": "No containers found. Make sure Docker containers are running.", + "searchContainersPlaceholder": "Search across {count} containers...", + "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "filters": "Filters", + "filterOptions": "Filter Options", + "filterPorts": "Ports", + "filterStopped": "Stopped", + "clearAllFilters": "Clear all filters", + "columns": "Columns", + "toggleColumns": "Toggle Columns", + "refreshContainersList": "Refresh containers list", + "searching": "Searching...", + "noContainersFoundMatching": "No containers found matching \"{filter}\"." } \ No newline at end of file diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx index 95fadf42..322d67fa 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsTable.tsx @@ -148,7 +148,7 @@ export default function InvitationsTable({ dialog={

    - {t('inviteQuestionRemove', {email: selectedInvitation?.email})} + {t('inviteQuestionRemove', {email: selectedInvitation?.email || ""})}

    {t('inviteMessageRemove')} diff --git a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx index fbd1c4f5..a5850b60 100644 --- a/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx +++ b/src/app/[orgId]/settings/access/invitations/RegenerateInvitationForm.tsx @@ -177,7 +177,7 @@ export default function RegenerateInvitationForm({ {!inviteLink ? (

    - {t('inviteQuestionRegenerate', {email: invitation?.email})} + {t('inviteQuestionRegenerate', {email: invitation?.email || ""})}

    @@ -244,7 +244,7 @@ export default function UsersTable({ users: u }: UsersTableProps) { dialog={

    - {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username})} + {t('userQuestionOrgRemove', {email: selectedUser?.email || selectedUser?.name || selectedUser?.username || ""})}

    diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index d571f7b8..a0c89773 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -71,7 +71,6 @@ const TransferFormSchema = z.object({ siteId: z.number() }); -type GeneralFormValues = z.infer; type TransferFormValues = z.infer; export default function GeneralForm() { @@ -123,7 +122,7 @@ export default function GeneralForm() { return true; }, { - message: t('proxyErrorInvalidPort'), + message: t("proxyErrorInvalidPort"), path: ["proxyPort"] } ) @@ -135,11 +134,13 @@ export default function GeneralForm() { return true; }, { - message: t('subdomainErrorInvalid'), + message: t("subdomainErrorInvalid"), path: ["subdomain"] } ); + type GeneralFormValues = z.infer; + const form = useForm({ resolver: zodResolver(GeneralFormSchema), defaultValues: { @@ -176,8 +177,11 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: t('domainErrorFetch'), - description: formatAxiosError(e, t('domainErrorFetchDescription')) + title: t("domainErrorFetch"), + description: formatAxiosError( + e, + t("domainErrorFetchDescription") + ) }); }); @@ -215,15 +219,18 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: t('resourceErrorUpdate'), - description: formatAxiosError(e, t('resourceErrorUpdateDescription')) + title: t("resourceErrorUpdate"), + description: formatAxiosError( + e, + t("resourceErrorUpdateDescription") + ) }); }); if (res && res.status === 200) { toast({ - title: t('resourceUpdated'), - description: t('resourceUpdatedDescription') + title: t("resourceUpdated"), + description: t("resourceUpdatedDescription") }); const resource = res.data.data; @@ -251,16 +258,18 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: t('resourceErrorTransfer'), - description: formatAxiosError(e, t('resourceErrorTransferDescription') + title: t("resourceErrorTransfer"), + description: formatAxiosError( + e, + t("resourceErrorTransferDescription") ) }); }); if (res && res.status === 200) { toast({ - title: t('resourceTransferred'), - description: t('resourceTransferredDescription') + title: t("resourceTransferred"), + description: t("resourceTransferredDescription") }); router.refresh(); @@ -284,10 +293,10 @@ export default function GeneralForm() { .catch((e) => { toast({ variant: "destructive", - title: t('resourceErrorToggle'), + title: t("resourceErrorToggle"), description: formatAxiosError( e, - t('resourceErrorToggleDescription') + t("resourceErrorToggleDescription") ) }); }); @@ -302,15 +311,17 @@ export default function GeneralForm() { - {t('resourceVisibilityTitle')} + + {t("resourceVisibilityTitle")} + - {t('resourceVisibilityTitleDescription')} + {t("resourceVisibilityTitleDescription")} { await toggleResourceEnabled(val); @@ -322,10 +333,10 @@ export default function GeneralForm() { - {t('resourceGeneral')} + {t("resourceGeneral")} - {t('resourceGeneralDescription')} + {t("resourceGeneralDescription")} @@ -342,7 +353,9 @@ export default function GeneralForm() { name="name" render={({ field }) => ( - {t('name')} + + {t("name")} + @@ -361,7 +374,9 @@ export default function GeneralForm() { render={({ field }) => ( - {t('domainType')} + {t( + "domainType" + )} @@ -409,7 +428,7 @@ export default function GeneralForm() { {domainType === "subdomain" ? (

    - {t('subdomain')} + {t("subdomain")}
    @@ -495,7 +514,9 @@ export default function GeneralForm() { render={({ field }) => ( - {t('baseDomain')} + {t( + "baseDomain" + )} - {t('saveGeneralSettings')} + {t("saveGeneralSettings")} @@ -597,10 +620,10 @@ export default function GeneralForm() { - {t('resourceTransfer')} + {t("resourceTransfer")} - {t('resourceTransferDescription')} + {t("resourceTransferDescription")} @@ -620,7 +643,7 @@ export default function GeneralForm() { render={({ field }) => ( - {t('siteDestination')} + {t("siteDestination")} - + - {t('sitesNotFound')} + {t( + "sitesNotFound" + )} {sites.map( @@ -709,7 +740,7 @@ export default function GeneralForm() { disabled={transferLoading} form="transfer-form" > - {t('resourceTransferSubmit')} + {t("resourceTransferSubmit")} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 8232d9f4..2ca6244f 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -74,7 +74,6 @@ import { CollapsibleTrigger } from "@app/components/ui/collapsible"; import { ContainersSelector } from "@app/components/ContainersSelector"; -import { FaDocker } from "react-icons/fa"; import { useTranslations } from "next-intl"; const addTargetSchema = z.object({ diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index 190a5745..5619911b 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -34,7 +34,6 @@ import { useState } from "react"; import { SwitchInput } from "@app/components/SwitchInput"; import { useTranslations } from "next-intl"; import Link from "next/link"; -import { ArrowRight } from "lucide-react"; const GeneralFormSchema = z.object({ name: z.string().nonempty("Name is required"), @@ -53,13 +52,6 @@ export default function GeneralPage() { const router = useRouter(); const t = useTranslations(); - const GeneralFormSchema = z.object({ - name: z.string().nonempty("Name is required"), - dockerSocketEnabled: z.boolean().optional() - }); - - type GeneralFormValues = z.infer; - const form = useForm({ resolver: zodResolver(GeneralFormSchema), defaultValues: { @@ -80,10 +72,10 @@ export default function GeneralPage() { .catch((e) => { toast({ variant: "destructive", - title: t('siteErrorUpdate'), + title: t("siteErrorUpdate"), description: formatAxiosError( e, - t('siteErrorUpdateDescription') + t("siteErrorUpdateDescription") ) }); }); @@ -94,8 +86,8 @@ export default function GeneralPage() { }); toast({ - title: t('siteUpdated'), - description: t('siteUpdatedDescription') + title: t("siteUpdated"), + description: t("siteUpdatedDescription") }); setLoading(false); @@ -108,10 +100,10 @@ export default function GeneralPage() { - {t('generalSettings')} + {t("generalSettings")} - {t('siteGeneralDescription')} + {t("siteGeneralDescription")} @@ -128,13 +120,13 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - {t('name')} + {t("name")} - {t('siteNameDescription')} + {t("siteNameDescription")} )} @@ -148,7 +140,9 @@ export default function GeneralPage() { - Enable Docker Socket - discovery for populating - container information, - useful in resource targets. + {t( + "enableDockerSocketDescription" + )} {" "} - Docker socket path - must be provided to - Newt in order to use - this feature. + {t( + "enableDockerSocketLink" + )} @@ -194,7 +186,7 @@ export default function GeneralPage() { loading={loading} disabled={loading} > - {t('saveGeneralSettings')} + {t("saveGeneralSettings")} diff --git a/src/components/ContainersSelector.tsx b/src/components/ContainersSelector.tsx index edc6b77c..0f09fb5a 100644 --- a/src/components/ContainersSelector.tsx +++ b/src/components/ContainersSelector.tsx @@ -45,7 +45,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import { Search, RefreshCw, Filter, Columns } from "lucide-react"; import { GetSiteResponse, Container } from "@server/routers/site"; import { useDockerSocket } from "@app/hooks/useDockerSocket"; -import { FaDocker } from "react-icons/fa"; +import { useTranslations } from "next-intl"; // Type definitions based on the JSON structure @@ -60,6 +60,8 @@ export const ContainersSelector: FC = ({ }) => { const [open, setOpen] = useState(false); + const t = useTranslations(); + const { isAvailable, containers, fetchContainers } = useDockerSocket(site); useEffect(() => { @@ -87,15 +89,16 @@ export const ContainersSelector: FC = ({ className="text-sm text-primary hover:underline cursor-pointer" onClick={() => setOpen(true)} > - View Docker Containers + {t("viewDockerContainers")} - Containers in {site.name} + + {t("containersIn", { siteName: site.name })} + - Select any container to use as a hostname for this - target. Click a port to use a port. + {t("selectContainerDescription")} @@ -109,7 +112,7 @@ export const ContainersSelector: FC = ({ - + @@ -132,6 +135,8 @@ const DockerContainersTable: FC<{ labels: false }); + const t = useTranslations(); + useEffect(() => { const timer = setTimeout(() => { setGlobalFilter(searchInput); @@ -182,14 +187,14 @@ const DockerContainersTable: FC<{ const columns: ColumnDef[] = [ { accessorKey: "name", - header: "Name", + header: t("containerName"), cell: ({ row }) => (
    {row.original.name}
    ) }, { accessorKey: "image", - header: "Image", + header: t("containerImage"), cell: ({ row }) => (
    {row.original.image} @@ -198,7 +203,7 @@ const DockerContainersTable: FC<{ }, { accessorKey: "state", - header: "State", + header: t("containerState"), cell: ({ row }) => ( { const networks = Object.keys(row.original.networks); return ( @@ -231,7 +236,7 @@ const DockerContainersTable: FC<{ }, { accessorKey: "hostname", - header: "Hostname/IP", + header: t("containerHostnameIp"), enableHiding: false, cell: ({ row }) => (
    @@ -241,7 +246,7 @@ const DockerContainersTable: FC<{ }, { accessorKey: "labels", - header: "Labels", + header: t("containerLabels"), cell: ({ row }) => { const labels = row.original.labels || {}; const labelEntries = Object.entries(labels); @@ -258,15 +263,14 @@ const DockerContainersTable: FC<{ size="sm" className="h-6 px-2 text-xs hover:bg-muted" > - {labelEntries.length} label - {labelEntries.length !== 1 ? "s" : ""} + {t("containerLabelsCount", { count: labelEntries.length })}

    - Container Labels + {t("containerLabelsTitle")}

    {labelEntries.map(([key, value]) => ( @@ -275,7 +279,7 @@ const DockerContainersTable: FC<{ {key}
    - {value || ""} + {value || t("containerLabelEmpty")}
    ))} @@ -289,7 +293,7 @@ const DockerContainersTable: FC<{ }, { accessorKey: "ports", - header: "Ports", + header: t("containerPorts"), enableHiding: false, cell: ({ row }) => { const ports = getExposedPorts(row.original); @@ -312,7 +316,7 @@ const DockerContainersTable: FC<{ { const ports = getExposedPorts(row.original); return ( @@ -355,7 +359,7 @@ const DockerContainersTable: FC<{ onClick={() => onContainerSelect(row.original, ports[0])} disabled={row.original.state !== "running"} > - Select + {t("select")} ); } @@ -412,8 +416,7 @@ const DockerContainersTable: FC<{ containers.length > 0 ? ( <>

    - No containers found matching the current - filters. + {t("noContainersMatchingFilters")}

    {hideContainersWithoutPorts && ( @@ -426,7 +429,7 @@ const DockerContainersTable: FC<{ ) } > - Show containers without ports + {t("showContainersWithoutPorts")} )} {hideStoppedContainers && ( @@ -437,15 +440,14 @@ const DockerContainersTable: FC<{ setHideStoppedContainers(false) } > - Show stopped containers + {t("showStoppedContainers")} )}
    ) : (

    - No containers found. Make sure Docker containers - are running. + {t("noContainersFound")}

    )}
    @@ -461,7 +463,7 @@ const DockerContainersTable: FC<{
    setSearchInput(event.target.value) @@ -471,12 +473,7 @@ const DockerContainersTable: FC<{ {searchInput && table.getFilteredRowModel().rows.length > 0 && (
    - {table.getFilteredRowModel().rows.length}{" "} - result - {table.getFilteredRowModel().rows.length !== - 1 - ? "s" - : ""} + {t("searchResultsCount", { count: table.getFilteredRowModel().rows.length })}
    )}
    @@ -489,7 +486,7 @@ const DockerContainersTable: FC<{ className="gap-2" > - Filters + {t("filters")} {(hideContainersWithoutPorts || hideStoppedContainers) && ( @@ -502,7 +499,7 @@ const DockerContainersTable: FC<{ - Filter Options + {t("filterOptions")} - Ports + {t("filterPorts")} - Stopped + {t("filterStopped")} {(hideContainersWithoutPorts || hideStoppedContainers) && ( @@ -537,7 +534,7 @@ const DockerContainersTable: FC<{ }} className="w-full text-xs" > - Clear all filters + {t("clearAllFilters")}
    @@ -553,12 +550,12 @@ const DockerContainersTable: FC<{ className="gap-2" > - Columns + {t("columns")} - Toggle Columns + {t("toggleColumns")} {table @@ -577,7 +574,7 @@ const DockerContainersTable: FC<{ } > {column.id === "hostname" - ? "Hostname/IP" + ? t("containerHostnameIp") : column.id} ); @@ -589,7 +586,7 @@ const DockerContainersTable: FC<{ variant="outline" size="icon" onClick={onRefresh} - title="Refresh containers list" + title={t("refreshContainersList")} > @@ -644,10 +641,10 @@ const DockerContainersTable: FC<{ {searchInput && !globalFilter ? (
    - Searching... + {t("searching")}
    ) : ( - `No containers found matching "${globalFilter}".` + t("noContainersFoundMatching", { filter: globalFilter }) )} From d26ec69445e129b883743d91a1af5da8add88f6a Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 10 Jun 2025 18:37:26 -0400 Subject: [PATCH 099/105] Add quotes --- postcss.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postcss.config.mjs b/postcss.config.mjs index 8dde23ef..9d3299ad 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,7 +1,7 @@ /** @type {import('postcss-load-config').Config} */ const config = { plugins: { - '@tailwindcss/postcss': {}, + "@tailwindcss/postcss": {}, }, }; From a6afce5c0ea3cbf5b752345d1cb34e61fdb8879b Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 10 Jun 2025 18:38:11 -0400 Subject: [PATCH 100/105] Restore eslint --- eslint.config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index afdfdd54..71dc862c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,8 +3,7 @@ export default [ { rules: { semi: "error", - "prefer-const": "error", - quotes: ["error", "double"] + "prefer-const": "error" } } ]; From 768e4745e1de78afa4a68a2c2e9276fb0348d291 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:45:16 +0200 Subject: [PATCH 101/105] New Crowdin updates (#184) * New translations en-us.json (French) * New translations en-us.json (Spanish) * New translations en-us.json (German) * New translations en-us.json (Italian) * New translations en-us.json (Dutch) * New translations en-us.json (Polish) * New translations en-us.json (Portuguese) * New translations en-us.json (Turkish) * New translations en-us.json (Chinese Simplified) --- messages/de-DE.json | 143 ++++++++++------- messages/es-ES.json | 41 ++++- messages/fr-FR.json | 41 ++++- messages/it-IT.json | 41 ++++- messages/nl-NL.json | 39 ++++- messages/pl-PL.json | 41 ++++- messages/pt-PT.json | 41 ++++- messages/tr-TR.json | 37 ++++- messages/zh-CN.json | 373 ++++++++++++++++++++++++-------------------- 9 files changed, 556 insertions(+), 241 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 2a9668d8..43374cc3 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1,33 +1,33 @@ { - "setupCreate": "Erstelle eine Organisation, Site und Ressourcen", + "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", "setupNewOrg": "Neue Organisation", "setupCreateOrg": "Organisation erstellen", "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Name der Organisation", - "orgDisplayName": "Anzeigename der Organisation.", + "setupOrgName": "Organisation's Name", + "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", "orgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist eine Eindeutige ID für Ihre Organisation. Diese ist unabhängig vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wähle eine andere.", - "componentsErrorNoMemberCreate": "Du bist derzeit kein Mitglied einer Organisation. Erstelle eine Organisation, um zu starten.", + "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", + "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", "welcome": "Willkommen zu Pangolin", "componentsCreateOrg": "Erstelle eine Organisation", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", - "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", + "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", "dismiss": "Verwerfen", - "componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", + "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", - "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.", - "inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.", - "inviteLoginUser": "Bitte stelle sicher, dass du als korrekter Benutzer angemeldet bist.", - "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als sei die Einladung, auf die du zugreifen möchtest, nicht für einen existierenden Benutzer bestimmt.", - "inviteCreateUser": "Bitte erstelle zuerst ein Konto.", - "goHome": "Zur Startseite", + "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", + "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", + "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", + "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", + "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", + "goHome": "Nach Hause", "inviteLogInOtherUser": "Als anderer Benutzer anmelden", "createAnAccount": "Konto erstellen", "inviteNotAccepted": "Einladung nicht angenommen", - "authCreateAccount": "Erstellen ein Konto um loszulegen", - "authNoAccount": "Du besitzt noch kein Konto?", + "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", + "authNoAccount": "Sie haben noch kein Konto?", "email": "E-Mail", "password": "Passwort", "confirmPassword": "Passwort bestätigen", @@ -38,8 +38,8 @@ "online": "Online", "offline": "Offline", "site": "Site", - "dataIn": "Daten eingehend", - "dataOut": "Daten ausgehend", + "dataIn": "Daten in", + "dataOut": "Daten raus", "connectionType": "Verbindungstyp", "tunnelType": "Tunneltyp", "local": "Lokal", @@ -47,57 +47,57 @@ "siteConfirmDelete": "Site löschen bestätigen", "siteDelete": "Site löschen", "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", - "siteMessageConfirm": "Um zu bestätigen, gib den Namen der Site ein.", - "siteQuestionRemove": "Bist du sicher, dass Sie die Site {selectedSite} aus der Organisation entfernt werden soll?", + "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", + "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", "siteManageSites": "Sites verwalten", - "siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben", + "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", "siteCreate": "Site erstellen", - "siteCreateDescription2": "Folge den nachfolgenden Schritten, um eine neue Site zu erstellen und zu verbinden", - "siteCreateDescription": "Erstelle eine neue Site, um Ressourcen zu verbinden", + "siteCreateDescription2": "Folgen Sie den Schritten unten, um eine neue Seite zu erstellen und zu verbinden", + "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", "close": "Schließen", - "siteErrorCreate": "Fehler beim Erstellen der Site", + "siteErrorCreate": "Fehler beim Erstellen der Seite", "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", - "siteNameDescription": "Dies ist der Anzeigename für die Site.", + "siteNameDescription": "Dies ist der Anzeigename für die Website.", "method": "Methode", - "siteMethodDescription": "So werden Verbindungen freigegeben.", - "siteLearnNewt": "Wie du Newt auf deinem System installieren kannst", - "siteSeeConfigOnce": "Du kannst die Konfiguration nur einmalig ansehen.", + "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", + "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", + "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", "siteLoadWGConfig": "Lade WireGuard Konfiguration...", - "siteDocker": "Erweitern für Docker Details", + "siteDocker": "Erweitern für Docker-Details", "toggle": "Umschalten", - "dockerCompose": "Docker Compose", + "dockerCompose": "Docker komponieren", "dockerRun": "Docker Run", - "siteLearnLocal": "Mehr Infos zu lokalen Sites", + "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", "siteConfirmCopy": "Ich habe die Konfiguration kopiert", - "searchSitesProgress": "Sites durchsuchen...", + "searchSitesProgress": "Seiten suchen...", "siteAdd": "Site hinzufügen", - "siteInstallNewt": "Newt installieren", - "siteInstallNewtDescription": "Installiere Newt auf deinem System.", + "siteInstallNewt": "Neustart installieren", + "siteInstallNewtDescription": "Lass Newt auf deinem System laufen", "WgConfiguration": "WireGuard Konfiguration", - "WgConfigurationDescription": "Verwende folgende Konfiguration, um dich mit deinem Netzwerk zu verbinden", + "WgConfigurationDescription": "Verwenden Sie folgende Konfiguration, um sich mit Ihrem Netzwerk zu verbinden", "operatingSystem": "Betriebssystem", "commands": "Befehle", "recommended": "Empfohlen", - "siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", - "siteRunsInDocker": "Läuft in Docker", - "siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows", - "siteErrorDelete": "Fehler beim Löschen der Site", - "siteErrorUpdate": "Fehler beim Aktualisieren der Site", - "siteErrorUpdateDescription": "Beim Aktualisieren der Site ist ein Fehler aufgetreten.", + "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", + "siteRunsInDocker": "Läuft im Docker", + "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", + "siteErrorDelete": "Fehler beim Löschen der Seite", + "siteErrorUpdate": "Fehler beim Aktualisieren der Seite", + "siteErrorUpdateDescription": "Beim Aktualisieren der Seite ist ein Fehler aufgetreten.", "siteUpdated": "Site aktualisiert", - "siteUpdatedDescription": "Die Site wurde aktualisiert.", - "siteGeneralDescription": "Allgemeine Einstellungen für diese Site konfigurieren", - "siteSettingDescription": "Konfigurieren der Site Einstellungen", + "siteUpdatedDescription": "Die Seite wurde aktualisiert.", + "siteGeneralDescription": "Allgemeine Einstellungen für diese Seite konfigurieren", + "siteSettingDescription": "Konfigurieren Sie die Einstellungen auf Ihrer Seite", "siteSetting": "{siteName} Einstellungen", "siteNewtTunnel": "Newt-Tunnel (empfohlen)", - "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", - "siteWg": "Einfacher WireGuard Tunnel", - "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", + "siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in Ihr Netzwerk zu erstellen. Keine zusätzliche Einrichtung.", + "siteWg": "Einfacher WireGuard", + "siteWgDescription": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu errichten. Manuelle NAT-Setup erforderlich.", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", - "siteSeeAll": "Alle Sites anzeigen", - "siteTunnelDescription": "Lege fest, wie du dich mit deiner Site verbinden möchtest", - "siteNewtCredentials": "Neue Newt Zugangsdaten", + "siteSeeAll": "Alle Seiten anzeigen", + "siteTunnelDescription": "Legen Sie fest, wie Sie sich mit Ihrer Website verbinden möchten", + "siteNewtCredentials": "Anmeldedaten neu", "siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren", "siteCredentialsSave": "Ihre Zugangsdaten speichern", "siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.", @@ -482,8 +482,8 @@ "targetAddr": "IP / Hostname", "targetPort": "Port", "targetProtocol": "Protokoll", - "targetTlsSettings": "HTTPS & TLS Einstellungen", - "targetTlsSettingsDescription": "Konfigurieren Sie TLS-Einstellungen für Ihre Ressource", + "targetTlsSettings": "Sicherheitskonfiguration", + "targetTlsSettingsDescription": "Konfiguriere SSL/TLS Einstellungen für deine Ressource", "targetTlsSettingsAdvanced": "Erweiterte TLS-Einstellungen", "targetTlsSni": "TLS-Servername (SNI)", "targetTlsSniDescription": "Der zu verwendende TLS-Servername für SNI. Leer lassen, um den Standard zu verwenden.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Einstellungen", "sidebarAllUsers": "Alle Benutzer", "sidebarIdentityProviders": "Identitätsanbieter", - "sidebarLicense": "Lizenz" -} + "sidebarLicense": "Lizenz", + "enableDockerSocket": "Docker Socket aktivieren", + "enableDockerSocketDescription": "Aktiviere Docker Socket-Erkennung, um Containerinformationen zu füllen, nützlich für Ressourcenziele.", + "enableDockerSocketLink": "Aktiviere Docker Socket-Erkennung, um Containerinformationen zu füllen, nützlich für Ressourcenziele.", + "viewDockerContainers": "Docker Container anzeigen", + "containersIn": "Container in {siteName}", + "selectContainerDescription": "Wählen Sie einen Container, der als Hostname für dieses Ziel verwendet werden soll. Klicken Sie auf einen Port, um einen Port zu verwenden.", + "containerName": "Name", + "containerImage": "Bild", + "containerState": "Bundesland", + "containerNetworks": "Netzwerke", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Etiketten", + "containerLabelsCount": "{count} Label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Container-Labels", + "containerLabelEmpty": "", + "containerPorts": "Häfen", + "containerPortsMore": "+{count} mehr", + "containerActions": "Aktionen", + "select": "Auswählen", + "noContainersMatchingFilters": "Es wurden keine Container gefunden, die den aktuellen Filtern entsprechen.", + "showContainersWithoutPorts": "Container ohne Ports anzeigen", + "showStoppedContainers": "Stoppte Container anzeigen", + "noContainersFound": "Keine Container gefunden. Stellen Sie sicher, dass Docker Container laufen.", + "searchContainersPlaceholder": "Durchsuche {count} Container...", + "searchResultsCount": "{count} Ergebnis{s,plural,one{} other{s}}", + "filters": "Filter", + "filterOptions": "Filteroptionen", + "filterPorts": "Häfen", + "filterStopped": "Stoppt", + "clearAllFilters": "Alle Filter löschen", + "columns": "Spalten", + "toggleColumns": "Spalten umschalten", + "refreshContainersList": "Container-Liste aktualisieren", + "searching": "Suche...", + "noContainersFoundMatching": "Keine Container gefunden mit \"{filter}\"." +} \ No newline at end of file diff --git a/messages/es-ES.json b/messages/es-ES.json index 145a78e2..da914d5b 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Hostname", "targetPort": "Puerto", "targetProtocol": "Protocol", - "targetTlsSettings": "Configuración HTTPS y TLS", - "targetTlsSettingsDescription": "Configurar ajustes TLS para su recurso", + "targetTlsSettings": "Configuración de conexión segura", + "targetTlsSettingsDescription": "Configurar ajustes SSL/TLS para su recurso", "targetTlsSettingsAdvanced": "Ajustes avanzados de TLS", "targetTlsSni": "Nombre del servidor TLS (SNI)", "targetTlsSniDescription": "El nombre del servidor TLS a usar para SNI. Deje en blanco para usar el valor predeterminado.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Ajustes", "sidebarAllUsers": "Todos los usuarios", "sidebarIdentityProviders": "Proveedores de identidad", - "sidebarLicense": "Licencia" + "sidebarLicense": "Licencia", + "enableDockerSocket": "Habilitar conector Docker", + "enableDockerSocketDescription": "Habilita el descubrimiento del conector Docker para rellenar la información del contenedor, útil en los objetivos de recursos.", + "enableDockerSocketLink": "Habilita el descubrimiento del conector Docker para rellenar la información del contenedor, útil en los objetivos de recursos.", + "viewDockerContainers": "Ver contenedores Docker", + "containersIn": "Contenedores en {siteName}", + "selectContainerDescription": "Seleccione cualquier contenedor para usar como nombre de host para este objetivo. Haga clic en un puerto para usar un puerto.", + "containerName": "Nombre", + "containerImage": "Imagen", + "containerState": "Estado", + "containerNetworks": "Redes", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Etiquetas", + "containerLabelsCount": "{count} etiqueta{s,plural,one{} other{s}}", + "containerLabelsTitle": "Etiquetas de contenedor", + "containerLabelEmpty": "", + "containerPorts": "Puertos", + "containerPortsMore": "+{count} más", + "containerActions": "Acciones", + "select": "Seleccionar", + "noContainersMatchingFilters": "No se encontraron contenedores que coincidan con los filtros actuales.", + "showContainersWithoutPorts": "Mostrar contenedores sin puertos", + "showStoppedContainers": "Mostrar contenedores parados", + "noContainersFound": "No se han encontrado contenedores. Asegúrate de que los contenedores Docker se estén ejecutando.", + "searchContainersPlaceholder": "Buscar a través de contenedores {count}...", + "searchResultsCount": "{count} resultado{s,plural,one{} other{s}}", + "filters": "Filtros", + "filterOptions": "Opciones de filtro", + "filterPorts": "Puertos", + "filterStopped": "Detenido", + "clearAllFilters": "Borrar todos los filtros", + "columns": "Columnas", + "toggleColumns": "Cambiar Columnas", + "refreshContainersList": "Actualizar lista de contenedores", + "searching": "Buscando...", + "noContainersFoundMatching": "No se han encontrado contenedores que coincidan con \"{filter}\"." } \ No newline at end of file diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 36ac30af..ccfd5bf2 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Nom d'hôte", "targetPort": "Port", "targetProtocol": "Protocole", - "targetTlsSettings": "Paramètres HTTPS & TLS", - "targetTlsSettingsDescription": "Configurer les paramètres TLS pour votre ressource", + "targetTlsSettings": "Configuration sécurisée de connexion", + "targetTlsSettingsDescription": "Configurer les paramètres SSL/TLS pour votre ressource", "targetTlsSettingsAdvanced": "Paramètres TLS avancés", "targetTlsSni": "Nom de serveur TLS (SNI)", "targetTlsSniDescription": "Le nom de serveur TLS à utiliser pour SNI. Laissez vide pour utiliser la valeur par défaut.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Réglages", "sidebarAllUsers": "Tous les utilisateurs", "sidebarIdentityProviders": "Fournisseurs d'identité", - "sidebarLicense": "Licence" + "sidebarLicense": "Licence", + "enableDockerSocket": "Activer Docker Socket", + "enableDockerSocketDescription": "Activer la découverte de sockets Docker pour le remplissage des informations de conteneur, utile dans les cibles de ressources.", + "enableDockerSocketLink": "Activer la découverte de sockets Docker pour le remplissage des informations de conteneur, utile dans les cibles de ressources.", + "viewDockerContainers": "Voir les conteneurs Docker", + "containersIn": "Conteneurs en {siteName}", + "selectContainerDescription": "Sélectionnez n'importe quel conteneur à utiliser comme nom d'hôte pour cette cible. Cliquez sur un port pour utiliser un port.", + "containerName": "Nom", + "containerImage": "Image", + "containerState": "État", + "containerNetworks": "Réseaux", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Étiquettes", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Étiquettes de conteneur", + "containerLabelEmpty": "", + "containerPorts": "Ports", + "containerPortsMore": "+{count} de plus", + "containerActions": "Actions", + "select": "Sélectionner", + "noContainersMatchingFilters": "Aucun conteneur ne correspond aux filtres actuels.", + "showContainersWithoutPorts": "Afficher les conteneurs sans ports", + "showStoppedContainers": "Afficher les conteneurs arrêtés", + "noContainersFound": "Aucun conteneur trouvé. Assurez-vous que les conteneurs Docker sont en cours d'exécution.", + "searchContainersPlaceholder": "Rechercher dans les conteneurs {count}...", + "searchResultsCount": "{count} résultat{s,plural,one{} other{s}}", + "filters": "Filtres", + "filterOptions": "Options de filtre", + "filterPorts": "Ports", + "filterStopped": "Arrêté", + "clearAllFilters": "Effacer tous les filtres", + "columns": "Colonnes", + "toggleColumns": "Activer/désactiver les colonnes", + "refreshContainersList": "Rafraîchir la liste des conteneurs", + "searching": "Recherche en cours...", + "noContainersFoundMatching": "Aucun conteneur correspondant à \"{filter}\"." } \ No newline at end of file diff --git a/messages/it-IT.json b/messages/it-IT.json index ee37f218..12a58713 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Nome host", "targetPort": "Porta", "targetProtocol": "Protocollo", - "targetTlsSettings": "Impostazioni HTTPS e TLS", - "targetTlsSettingsDescription": "Configura le impostazioni TLS per la tua risorsa", + "targetTlsSettings": "Configurazione Connessione Sicura", + "targetTlsSettingsDescription": "Configura le impostazioni SSL/TLS per la tua risorsa", "targetTlsSettingsAdvanced": "Impostazioni TLS Avanzate", "targetTlsSni": "Nome Server TLS (SNI)", "targetTlsSniDescription": "Il Nome Server TLS da usare per SNI. Lascia vuoto per usare quello predefinito.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Impostazioni", "sidebarAllUsers": "Tutti Gli Utenti", "sidebarIdentityProviders": "Fornitori Di Identità", - "sidebarLicense": "Licenza" + "sidebarLicense": "Licenza", + "enableDockerSocket": "Abilita Docker Socket", + "enableDockerSocketDescription": "Abilita la scoperta del Docker Socket per la popolazione delle informazioni del contenitore, utili negli obiettivi delle risorse.", + "enableDockerSocketLink": "Abilita la scoperta del Docker Socket per la popolazione delle informazioni del contenitore, utili negli obiettivi delle risorse.", + "viewDockerContainers": "Visualizza Contenitori Docker", + "containersIn": "Contenitori in {siteName}", + "selectContainerDescription": "Seleziona qualsiasi contenitore da usare come hostname per questo obiettivo. Fai clic su una porta per usare una porta.", + "containerName": "Nome", + "containerImage": "Immagine", + "containerState": "Stato", + "containerNetworks": "Reti", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Etichette", + "containerLabelsCount": "{count} etichetta{s,plural,one{} other{s}}", + "containerLabelsTitle": "Etichette Del Contenitore", + "containerLabelEmpty": "", + "containerPorts": "Porte", + "containerPortsMore": "+{count} in più", + "containerActions": "Azioni", + "select": "Seleziona", + "noContainersMatchingFilters": "Nessun contenitore trovato corrispondente ai filtri correnti.", + "showContainersWithoutPorts": "Mostra contenitori senza porte", + "showStoppedContainers": "Mostra contenitori fermati", + "noContainersFound": "Nessun contenitore trovato. Assicurarsi che i contenitori Docker siano in esecuzione.", + "searchContainersPlaceholder": "Cerca tra i contenitori {count}...", + "searchResultsCount": "{count} risultato{s,plural,one{} other{s}}", + "filters": "Filtri", + "filterOptions": "Opzioni Filtro", + "filterPorts": "Porte", + "filterStopped": "Fermato", + "clearAllFilters": "Cancella tutti i filtri", + "columns": "Colonne", + "toggleColumns": "Attiva/Disattiva Colonne", + "refreshContainersList": "Aggiorna elenco contenitori", + "searching": "Ricerca...", + "noContainersFoundMatching": "Nessun contenitore trovato corrispondente \"{filter}\"." } \ No newline at end of file diff --git a/messages/nl-NL.json b/messages/nl-NL.json index ef8007c7..46fb6aa8 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -483,7 +483,7 @@ "targetPort": "Poort", "targetProtocol": "Protocol", "targetTlsSettings": "HTTPS & TLS instellingen", - "targetTlsSettingsDescription": "Configureer TLS instellingen voor jouw bron", + "targetTlsSettingsDescription": "SSL/TLS-instellingen voor uw bron configureren", "targetTlsSettingsAdvanced": "Geavanceerde TLS instellingen", "targetTlsSni": "TLS Server Naam (SNI)", "targetTlsSniDescription": "De TLS servernaam om te gebruiken voor SNI. Laat leeg om de standaard te gebruiken.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Instellingen", "sidebarAllUsers": "Alle gebruikers", "sidebarIdentityProviders": "Identiteit aanbieders", - "sidebarLicense": "Licentie" + "sidebarLicense": "Licentie", + "enableDockerSocket": "Docker Socket inschakelen", + "enableDockerSocketDescription": "Schakel Docker Socket ontdekking in voor het vullen van containerinformatie, handig in brondoelen.", + "enableDockerSocketLink": "Schakel Docker Socket ontdekking in voor het vullen van containerinformatie, handig in brondoelen.", + "viewDockerContainers": "Bekijk Docker containers", + "containersIn": "Containers in {siteName}", + "selectContainerDescription": "Selecteer een container om als hostnaam voor dit doel te gebruiken. Klik op een poort om een poort te gebruiken.", + "containerName": "naam", + "containerImage": "Afbeelding", + "containerState": "Provincie", + "containerNetworks": "Netwerken", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Labels", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Container labels", + "containerLabelEmpty": "", + "containerPorts": "Poorten", + "containerPortsMore": "+{count} meer", + "containerActions": "acties", + "select": "Selecteren", + "noContainersMatchingFilters": "Geen containers gevonden die overeenkomen met de huidige filters.", + "showContainersWithoutPorts": "Toon containers zonder poorten", + "showStoppedContainers": "Toon gestopte containers", + "noContainersFound": "Geen containers gevonden. Zorg ervoor dat Docker containers draaien.", + "searchContainersPlaceholder": "Zoek tussen {count} containers...", + "searchResultsCount": "{count} resultaat{s,plural,one{} other{s}}", + "filters": "Filters", + "filterOptions": "Filter opties", + "filterPorts": "Poorten", + "filterStopped": "Gestopt", + "clearAllFilters": "Alle filters wissen", + "columns": "Kolommen", + "toggleColumns": "Kolommen omschakelen", + "refreshContainersList": "Vernieuw containers lijst", + "searching": "Zoeken...", + "noContainersFoundMatching": "Geen containers gevonden die overeenkomen met \"{filter}\"." } \ No newline at end of file diff --git a/messages/pl-PL.json b/messages/pl-PL.json index b20c4040..713f2749 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Nazwa hosta", "targetPort": "Port", "targetProtocol": "Protokół", - "targetTlsSettings": "Ustawienia HTTPS & TLS", - "targetTlsSettingsDescription": "Skonfiguruj ustawienia TLS dla swojego zasobu", + "targetTlsSettings": "Konfiguracja bezpiecznego połączenia", + "targetTlsSettingsDescription": "Skonfiguruj ustawienia SSL/TLS dla twojego zasobu", "targetTlsSettingsAdvanced": "Zaawansowane ustawienia TLS", "targetTlsSni": "Nazwa serwera TLS (SNI)", "targetTlsSniDescription": "Nazwa serwera TLS do użycia dla SNI. Pozostaw puste, aby użyć domyślnej.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Ustawienia", "sidebarAllUsers": "Wszyscy użytkownicy", "sidebarIdentityProviders": "Dostawcy tożsamości", - "sidebarLicense": "Licencja" + "sidebarLicense": "Licencja", + "enableDockerSocket": "Włącz gniazdo dokera", + "enableDockerSocketDescription": "Włącz wyszukiwanie gniazda dokującego w celu zbierania informacji o pojemniku, przydatne w celach zasobowych.", + "enableDockerSocketLink": "Włącz wyszukiwanie gniazda dokującego w celu zbierania informacji o pojemniku, przydatne w celach zasobowych.", + "viewDockerContainers": "Zobacz kontenery dokujące", + "containersIn": "Pojemniki w {siteName}", + "selectContainerDescription": "Wybierz dowolny kontener do użycia jako nazwa hosta dla tego celu. Kliknij port, aby użyć portu.", + "containerName": "Nazwisko", + "containerImage": "Obraz", + "containerState": "Stan", + "containerNetworks": "Sieci", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Etykiety", + "containerLabelsCount": "{count} etykieta{s,plural,one{} other{s}}", + "containerLabelsTitle": "Etykiety kontenera", + "containerLabelEmpty": "", + "containerPorts": "Porty", + "containerPortsMore": "+{count} więcej", + "containerActions": "Akcje", + "select": "Wybierz", + "noContainersMatchingFilters": "Nie znaleziono kontenerów pasujących do obecnych filtrów.", + "showContainersWithoutPorts": "Pokaż kontenery bez portów", + "showStoppedContainers": "Pokaż zatrzymane kontenery", + "noContainersFound": "Nie znaleziono kontenerów. Upewnij się, że kontenery dokujące są uruchomione.", + "searchContainersPlaceholder": "Szukaj w {count} kontenerach...", + "searchResultsCount": "{count} wynik{s,plural,one{} other{s}}", + "filters": "Filtry", + "filterOptions": "Opcje filtru", + "filterPorts": "Porty", + "filterStopped": "Zatrzymano", + "clearAllFilters": "Wyczyść wszystkie filtry", + "columns": "Kwota, którą należy zgłosić w kolumnie 060 tego wiersza: pierwotne odliczenie, art. 36 ust. 1 lit. b) CRR.", + "toggleColumns": "Przełącz kolumny", + "refreshContainersList": "Odśwież listę kontenerów", + "searching": "Wyszukiwanie...", + "noContainersFoundMatching": "Nie znaleziono kontenerów pasujących do \"{filter}\"." } \ No newline at end of file diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 4a84d262..261bbdb7 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -482,8 +482,8 @@ "targetAddr": "IP / Nome do Host", "targetPort": "Porta", "targetProtocol": "Protocolo", - "targetTlsSettings": "Configurações HTTPS & TLS", - "targetTlsSettingsDescription": "Configure as configurações TLS para seu recurso", + "targetTlsSettings": "Configuração de conexão segura", + "targetTlsSettingsDescription": "Configurar configurações SSL/TLS para seu recurso", "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", @@ -1089,5 +1089,40 @@ "sidebarSettings": "Confirgurações", "sidebarAllUsers": "Todos os usuários", "sidebarIdentityProviders": "Provedores de identidade", - "sidebarLicense": "Tipo:" + "sidebarLicense": "Tipo:", + "enableDockerSocket": "Habilitar Docker Socket", + "enableDockerSocketDescription": "Habilitar a descoberta de Socket Docker para preencher informações de contêineres, útil em alvos de recursos.", + "enableDockerSocketLink": "Habilitar a descoberta de Socket Docker para preencher informações de contêineres, útil em alvos de recursos.", + "viewDockerContainers": "Ver contêineres Docker", + "containersIn": "Contêineres em {siteName}", + "selectContainerDescription": "Selecione qualquer contêiner para usar como hostname para este alvo. Clique em uma porta para usar uma porta.", + "containerName": "Nome:", + "containerImage": "Imagem:", + "containerState": "Estado:", + "containerNetworks": "Redes", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Marcadores", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Etiquetas do Contêiner", + "containerLabelEmpty": "", + "containerPorts": "Portas", + "containerPortsMore": "+ Mais{count}", + "containerActions": "Ações.", + "select": "Selecionar", + "noContainersMatchingFilters": "Nenhum contêiner encontrado corresponde aos filtros atuais.", + "showContainersWithoutPorts": "Mostrar contêineres sem portas", + "showStoppedContainers": "Mostrar contêineres parados", + "noContainersFound": "Nenhum contêiner encontrado. Certifique-se de que os contêineres Docker estão em execução.", + "searchContainersPlaceholder": "Pesquisar entre os contêineres {count}...", + "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "filters": "Filtros", + "filterOptions": "Opções de Filtro", + "filterPorts": "Portas", + "filterStopped": "Parado", + "clearAllFilters": "Limpar todos os filtros", + "columns": "Colunas", + "toggleColumns": "Alternar Colunas", + "refreshContainersList": "Atualizar lista de contêineres", + "searching": "Buscando...", + "noContainersFoundMatching": "Nenhum recipiente encontrado \"{filter}\"." } \ No newline at end of file diff --git a/messages/tr-TR.json b/messages/tr-TR.json index f09dc233..a986f0d1 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1089,5 +1089,40 @@ "sidebarSettings": "Settings", "sidebarAllUsers": "All Users", "sidebarIdentityProviders": "Identity Providers", - "sidebarLicense": "License" + "sidebarLicense": "License", + "enableDockerSocket": "Enable Docker Socket", + "enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "enableDockerSocketLink": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "viewDockerContainers": "View Docker Containers", + "containersIn": "Containers in {siteName}", + "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", + "containerName": "Name", + "containerImage": "Image", + "containerState": "State", + "containerNetworks": "Networks", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "Labels", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "Container Labels", + "containerLabelEmpty": "", + "containerPorts": "Ports", + "containerPortsMore": "+{count} more", + "containerActions": "Actions", + "select": "Select", + "noContainersMatchingFilters": "No containers found matching the current filters.", + "showContainersWithoutPorts": "Show containers without ports", + "showStoppedContainers": "Show stopped containers", + "noContainersFound": "No containers found. Make sure Docker containers are running.", + "searchContainersPlaceholder": "Search across {count} containers...", + "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "filters": "Filters", + "filterOptions": "Filter Options", + "filterPorts": "Ports", + "filterStopped": "Stopped", + "clearAllFilters": "Clear all filters", + "columns": "Columns", + "toggleColumns": "Toggle Columns", + "refreshContainersList": "Refresh containers list", + "searching": "Searching...", + "noContainersFoundMatching": "No containers found matching \"{filter}\"." } \ No newline at end of file diff --git a/messages/zh-CN.json b/messages/zh-CN.json index c2420ca4..71628a0c 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1,5 +1,5 @@ { - "setupCreate": "创建您的第一个组织、网站和资源", + "setupCreate": "创建您的组织、网站和资源", "setupNewOrg": "新建组织", "setupCreateOrg": "创建组织", "setupCreateResources": "创建资源", @@ -7,17 +7,17 @@ "orgDisplayName": "这是您组织的显示名称。", "orgId": "组织ID", "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", - "setupErrorIdentifier": "组织ID 已被使用。请另选一个。", + "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "{count, plural, =0 {您目前不是任何组织的成员。} other {你已加入 # 个组织。}}.", - "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", - "dismiss": "忽略", + "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", + "dismiss": "关闭", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", - "inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", "inviteLoginUser": "请确保您以正确的用户登录。", "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", @@ -61,27 +61,27 @@ "siteNameDescription": "这是站点的显示名称。", "method": "方法", "siteMethodDescription": "这是您将如何显示连接。", - "siteLearnNewt": "学习如何在您的系统上安装 Newt", + "siteLearnNewt": "学习如何在您的系统上安装Newt", "siteSeeConfigOnce": "您只能看到一次配置。", - "siteLoadWGConfig": "正在载入 WireGuard 配置...", + "siteLoadWGConfig": "正在载入Wire保护配置...", "siteDocker": "扩展 Docker 部署详细信息", - "toggle": "切换", - "dockerCompose": "Docker Compose", - "dockerRun": "Docker Run", - "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", - "siteConfirmCopy": "我已经复制了配置信息", + "toggle": "切换键", + "dockerCompose": "Docker 配置", + "dockerRun": "停靠栏", + "siteLearnLocal": "本地站点没有隧道,学习更多", + "siteConfirmCopy": "我已复制配置", "searchSitesProgress": "搜索站点...", "siteAdd": "添加站点", - "siteInstallNewt": "安装 Newt", - "siteInstallNewtDescription": "在您的系统中运行 Newt", - "WgConfiguration": "WireGuard 配置", + "siteInstallNewt": "安装新的", + "siteInstallNewtDescription": "在您的系统上获得新的运行", + "WgConfiguration": "Wire护卫配置", "WgConfigurationDescription": "使用以下配置连接到您的网络", "operatingSystem": "操作系统", "commands": "命令", - "recommended": "推荐", - "siteNewtDescription": "为获得最佳用户体验,请使用 Newt。其底层采用 WireGuard 技术,可直接通过 Pangolin 控制台,使用局域网地址访问您私有网络中的资源。", - "siteRunsInDocker": "在 Docker 中运行", - "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中运行", + "recommended": "推荐的", + "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", + "siteRunsInDocker": "运行在停靠栏", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", "siteErrorDelete": "删除站点出错", "siteErrorUpdate": "更新站点失败", "siteErrorUpdateDescription": "更新站点时出错。", @@ -90,17 +90,17 @@ "siteGeneralDescription": "配置此站点的常规设置", "siteSettingDescription": "配置您网站上的设置", "siteSetting": "{siteName} 设置", - "siteNewtTunnel": "Newt 隧道 (推荐)", - "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", - "siteWg": "基本 WireGuard", - "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", - "siteLocalDescription": "仅限本地资源。不需要隧道。", + "siteNewtTunnel": "新隧道(推荐)", + "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", + "siteWg": "基本线甲", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", + "siteLocalDescription": "仅本地资源。没有隧道。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", - "siteNewtCredentials": "Newt 凭据", - "siteNewtCredentialsDescription": "这是 Newt 服务器的身份验证凭据", - "siteCredentialsSave": "保存您的凭据", - "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制并保存到一个安全的地方。", + "siteNewtCredentials": "新建凭据", + "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", + "siteCredentialsSave": "保存您的证书", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", "siteInfo": "站点信息", "status": "状态", "shareTitle": "管理共享链接", @@ -111,16 +111,16 @@ "shareErrorDeleteMessage": "删除链接时出错", "shareDeleted": "链接已删除", "shareDeletedDescription": "链接已删除", - "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求头。 每次验证访问请求都必须从客户端传递。", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", "accessToken": "访问令牌", "usageExamples": "用法示例", - "tokenId": "令牌 ID", - "requestHeades": "请求头", + "tokenId": "Token ID", + "requestHeades": "请求标题", "queryParameter": "查询参数", - "importantNote": "重要提示", - "shareImportantDescription": "出于安全考虑,建议尽可能在使用请求头传递参数,因为查询参数可能会被浏览器历史记录或服务器日志记录。", + "importantNote": "重要笔记", + "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", "token": "令牌", - "shareTokenSecurety": "请妥善保管您的访问令牌,不要将其暴露在公开访问的区域或客户端代码中。", + "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", "shareErrorFetchResource": "获取资源失败", "shareErrorFetchResourceDescription": "获取资源时出错", "shareErrorCreate": "无法创建共享链接", @@ -141,7 +141,7 @@ "title": "标题", "created": "已创建", "expires": "过期时间", - "never": "永不过期", + "never": "从不使用", "shareErrorSelectResource": "请选择一个资源", "resourceTitle": "管理资源", "resourceDescription": "为您的私人应用程序创建安全代理", @@ -149,14 +149,14 @@ "resourceAdd": "添加资源", "resourceErrorDelte": "删除资源时出错", "authentication": "认证", - "protected": "受到保护", - "notProtected": "未受到保护", - "resourceMessageRemove": "一旦删除,资源将不再可访问。与该资源相关的所有目标也将被删除。", + "protected": "保护", + "notProtected": "不保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", "resourceMessageConfirm": "请在下面输入资源名称以确认。", "resourceQuestionRemove": "您确定要从组织中删除 {selectedResource} 吗?", "resourceHTTP": "HTTPS 资源", - "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", - "resourceRaw": "TCP/UDP 资源", + "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "Raw TCP/UDP 资源", "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", @@ -172,20 +172,20 @@ "resourceHTTPSSettings": "HTTPS 设置", "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", "domainType": "域类型", - "subdomain": "子域名", - "baseDomain": "根域名", - "subdomnainDescription": "您的资源可以访问的子域名。", + "subdomain": "子域", + "baseDomain": "基础域", + "subdomnainDescription": "您的资源可以访问的子域。", "resourceRawSettings": "TCP/UDP 设置", "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", - "protocol": "协议", - "protocolSelect": "选择协议", + "protocol": "Protocol", + "protocolSelect": "Select a protocol", "resourcePortNumber": "端口号", "resourcePortNumberDescription": "代理请求的外部端口号。", "cancel": "取消", "resourceConfig": "配置片段", "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", "resourceAddEntrypoints": "Traefik: 添加入口点", - "resourceExposePorts": "Gerbil:在 Docker Compose 中显示端口", + "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", "resourceBack": "返回资源", "resourceGoTo": "转到资源", @@ -194,23 +194,23 @@ "visibility": "可见性", "enabled": "已启用", "disabled": "已禁用", - "general": "概览", + "general": "A. 概况", "generalSettings": "常规设置", "proxy": "代理服务器", "rules": "规则", "resourceSettingDescription": "配置您资源上的设置", "resourceSetting": "{resourceName} 设置", - "alwaysAllow": "一律允许", - "alwaysDeny": "一律拒绝", + "alwaysAllow": "总是允许", + "alwaysDeny": "总是拒绝", "orgSettingsDescription": "配置您组织的一般设置", "orgGeneralSettings": "组织设置", "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", "saveGeneralSettings": "保存常规设置", "orgDangerZone": "危险区域", - "orgDangerZoneDescription": "一旦删除该组织,将无法恢复,请务必确认。", + "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", "orgDelete": "删除组织", "orgDeleteConfirm": "确认删除组织", - "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", + "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", "orgUpdated": "组织已更新", @@ -224,15 +224,15 @@ "orgDeleted": "组织已删除", "orgDeletedMessage": "组织及其数据已被删除。", "orgMissing": "缺少组织 ID", - "orgMissingMessage": "没有组织ID,无法重新生成邀请。", + "orgMissingMessage": "没有机构ID,无法重新生成邀请。", "accessUsersManage": "管理用户", - "accessUsersDescription": "邀请用户并位他们添加角色以管理访问您的组织", + "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", "accessUsersSearch": "搜索用户...", "accessUserCreate": "创建用户", "accessUserRemove": "删除用户", "username": "用户名", "identityProvider": "身份提供商", - "role": "角色", + "role": "作用", "nameRequired": "名称是必填项", "accessRolesManage": "管理角色", "accessRolesDescription": "配置角色来管理访问您的组织", @@ -244,13 +244,13 @@ "inviteDescription": "管理您给其他用户的邀请", "inviteSearch": "搜索邀请...", "minutes": "分钟", - "hours": "小时", + "hours": "小时数", "days": "天", "weeks": "周", "months": "月", "years": "年", "day": "{count, plural, =1 {# 天} other {# 天}}", - "apiKeysTitle": "API 密钥", + "apiKeysTitle": "API 密钥信息", "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", "apiKeysErrorCreate": "创建 API 密钥出错", "apiKeysErrorSetPermission": "设置权限出错", @@ -260,7 +260,7 @@ "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", "apiKeysList": "您的 API 密钥", "apiKeysSave": "保存您的 API 密钥", - "apiKeysSaveDescription": "该信息仅会显示一次,请确保将其复制到安全的位置。", + "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", "apiKeysInfo": "您的 API 密钥是:", "apiKeysConfirmCopy": "我已复制 API 密钥", "generate": "生成", @@ -280,7 +280,7 @@ "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", - "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", + "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", "apiKeysDelete": "删除 API 密钥", @@ -290,7 +290,7 @@ "userTitle": "管理所有用户", "userDescription": "查看和管理系统中的所有用户", "userAbount": "关于用户管理", - "userAbountDescription": "此表格显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表格中的删除操作删除其根用户对象。", + "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", "userServer": "服务器用户", "userSearch": "搜索服务器用户...", "userErrorDelete": "删除用户时出错", @@ -300,7 +300,7 @@ "userMessageConfirm": "请在下面输入用户名称以确认。", "userQuestionRemove": "您确定要从服务器中永久删除 {selectedUser} 吗?", "licenseKey": "许可证密钥", - "valid": "有效", + "valid": "Valid", "numberOfSites": "站点数量", "licenseKeySearch": "搜索许可证密钥...", "licenseKeyAdd": "添加许可证密钥", @@ -334,7 +334,7 @@ "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", - "licenseTitle": "管理许可证状态", + "licenseTitle": "管理许可状态", "licenseTitleDescription": "查看和管理系统中的许可证密钥", "licenseHost": "主机许可证", "licenseHostDescription": "管理主机的主许可证密钥。", @@ -347,14 +347,14 @@ "licensePurchase": "购买许可证", "licensePurchaseSites": "购买更多站点", "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 个站点", - "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {# 站点}}", - "licensePurchaseDescription": "请选择您希望 {selectedMode, select, license {直接购买许可证,您可以随时增加更多站点。} other {为现有许可证购买更多站点}}", - "licenseFee": "许可证费用", - "licensePriceSite": "每个站点的价格", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", + "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", + "licenseFee": "许可费", + "licensePriceSite": "每个站点价格", "total": "总计", "licenseContinuePayment": "继续付款", "pricingPage": "定价页面", - "pricingPortal": "前往付款页面", + "pricingPortal": "查看购买门户网站", "licensePricingPage": "关于最新的价格和折扣,请访问 ", "invite": "邀请", "inviteRegenerate": "重新生成邀请", @@ -365,7 +365,7 @@ "inviteRemoved": "邀请已删除", "inviteRemovedDescription": "为 {email} 创建的邀请已删除", "inviteQuestionRemove": "您确定要删除 {email} 的邀请吗?", - "inviteMessageRemove": "一旦删除,这个邀请将不再有效。", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", "inviteQuestionRegenerate": "您确定要重新邀请 {email} 吗?这将会撤销掉之前的邀请", "inviteRemoveConfirm": "确认删除邀请", @@ -373,7 +373,7 @@ "inviteSent": "邀请邮件已成功发送至 {email}。", "inviteSentEmail": "发送电子邮件通知给用户", "inviteGenerate": "已为 {email} 创建新的邀请。", - "inviteDuplicateError": "重复的邀请", + "inviteDuplicateError": "Duplicate Invite", "inviteDuplicateErrorDescription": "此用户的邀请已存在。", "inviteRateLimitError": "超出速率限制", "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", @@ -463,7 +463,7 @@ "targetErrorFetchDescription": "获取目标时出错", "siteErrorFetch": "获取资源失败", "siteErrorFetchDescription": "获取资源时出错", - "targetErrorDuplicate": "重复的目标", + "targetErrorDuplicate": "Duplicate target", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", "targetWireGuardErrorInvalidIp": "Invalid target IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", @@ -479,11 +479,11 @@ "proxyUpdatedDescription": "您的代理设置已成功更新", "proxyErrorUpdate": "更新代理设置失败", "proxyErrorUpdateDescription": "更新代理设置时出错", - "targetAddr": "IP / 域名", + "targetAddr": "IP / Hostname", "targetPort": "端口", - "targetProtocol": "协议", - "targetTlsSettings": "HTTPS & TLS 设置", - "targetTlsSettingsDescription": "配置资源的 TLS 设置", + "targetProtocol": "Protocol", + "targetTlsSettings": "安全连接配置", + "targetTlsSettingsDescription": "配置资源的 SSL/TLS 设置", "targetTlsSettingsAdvanced": "高级TLS设置", "targetTlsSni": "TLS 服务器名称 (SNI)", "targetTlsSniDescription": "SNI使用的 TLS 服务器名称。留空使用默认值。", @@ -493,7 +493,7 @@ "targetStickySessions": "启用置顶会话", "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", "methodSelect": "选择方法", - "targetSubmit": "添加目标", + "targetSubmit": "Add Target", "targetNoOne": "没有目标。使用表单添加目标。", "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", "targetsSubmit": "保存目标", @@ -504,7 +504,7 @@ "proxyAdditionalSubmit": "保存代理设置", "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", - "ipAddressErrorInvalidOctet": "无效的 IP 地址", + "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", "path": "路径", "ipAddressRange": "IP 范围", "rulesErrorFetch": "获取规则失败", @@ -520,7 +520,7 @@ "rulesErrorUpdate": "更新规则失败", "rulesErrorUpdateDescription": "更新规则时出错", "rulesUpdated": "启用规则", - "rulesUpdatedDescription": "规则已更新", + "rulesUpdatedDescription": "规则评价已更新", "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", @@ -533,18 +533,18 @@ "ruleErrorUpdate": "操作失败", "ruleErrorUpdateDescription": "保存过程中发生错误", "rulesPriority": "优先权", - "rulesAction": "行为", - "rulesMatchType": "匹配类型", + "rulesAction": "行 动", + "rulesMatchType": "比赛类型", "value": "值", "rulesAbout": "关于规则", - "rulesAboutDescription": "规则使您能够依据特定条件控制资源访问权限。您可以创建基于 IP 地址或 URL 路径的规则,以允许或拒绝访问。", + "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", "rulesActions": "行动", "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", "rulesMatchCriteria": "匹配条件", "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", - "rulesMatchCriteriaUrl": "匹配一个 URL 路径或模式", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", "rulesEnable": "启用规则", "rulesEnableDescription": "启用或禁用此资源的规则评估", "rulesResource": "资源规则配置", @@ -562,10 +562,10 @@ "domainsErrorFetch": "获取域名出错", "domainsErrorFetchDescription": "获取域时出错", "none": "无", - "unknown": "未知", + "unknown": "未知的", "resources": "资源", - "resourcesDescription": "资源是您私有网络中运行的应用程序的代理。您可以为私有网络中的任何 HTTP/HTTPS 或 TCP/UDP 服务创建资源。每个资源都必须连接到一个站点,以通过加密的 WireGuard 隧道实现私密且安全的连接。", - "resourcesWireGuardConnect": "采用 WireGuard 提供的加密安全连接", + "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", + "resourcesWireGuardConnect": "与Wire护卫加密安全连接", "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", "resourcesUsersRolesAccess": "基于用户和角色的访问控制", "resourcesErrorUpdate": "切换资源失败", @@ -574,7 +574,7 @@ "shareLink": "{resource} 的分享链接", "resourceSelect": "选择资源", "shareLinks": "分享链接", - "share": "分享链接", + "share": "可共享链接", "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", "shareEasyCreate": "轻松创建和分享", "shareConfigurableExpirationDuration": "可配置的过期时间", @@ -585,29 +585,29 @@ "unknownCommand": "未知命令", "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", - "newtEndpoint": "Newt 端点", + "newtEndpoint": "Newt Endpoint", "newtId": "Newt ID", - "newtSecretKey": "Newt 私钥", - "architecture": "架构", + "newtSecretKey": "新的秘密密钥", + "architecture": "结构", "sites": "站点", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", "siteWgManualConfigurationRequired": "需要手动配置", "userErrorNotAdminOrOwner": "用户不是管理员或所有者", - "pangolinSettings": "设置 - Pangolin", + "pangolinSettings": "设置-Pangolin", "accessRoleYour": "您的角色:", "accessRoleSelect2": "选择角色", "accessUserSelect": "选择一个用户", "otpEmailEnter": "输入电子邮件", "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", - "otpEmailErrorInvalid": "无效的邮箱地址。通配符(*)必须占据整个开头部分。", - "otpEmailSmtpRequired": "需要先配置SMTP", + "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", + "otpEmailSmtpRequired": "需要SMTP", "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", "otpEmailTitle": "一次性密码", "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", "otpEmailWhitelist": "电子邮件白名单", "otpEmailWhitelistList": "白名单邮件", - "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域名的任何电子邮件地址。", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", "otpEmailWhitelistSave": "保存白名单", "passwordAdd": "添加密码", "passwordRemove": "删除密码", @@ -642,12 +642,12 @@ "resourcePincode": "PIN 码", "resourcePincodeSubmit": "启用 PIN 码保护", "resourcePincodeProtection": "PIN 码保护 {status}", - "resourcePincodeRemove": "资源 PIN 码已删除", - "resourcePincodeRemoveDescription": "已成功删除资源 PIN 码", - "resourcePincodeSetup": "资源 PIN 码已设置", - "resourcePincodeSetupDescription": "资源 PIN 码已成功设置", - "resourcePincodeSetupTitle": "设置 PIN 码", - "resourcePincodeSetupTitleDescription": "设置 PIN 码来保护此资源", + "resourcePincodeRemove": "资源粉码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源密码", + "resourcePincodeSetup": "资源PIN 码已设置", + "resourcePincodeSetupDescription": "资源固定码已成功设置", + "resourcePincodeSetupTitle": "设置粉码", + "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", "resourceRoleDescription": "管理员总是可以访问此资源。", "resourceUsersRoles": "用户和角色", "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", @@ -658,14 +658,14 @@ "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", "proxyErrorInvalidPort": "无效的端口号", "subdomainErrorInvalid": "无效的子域", - "domainErrorFetch": "获取域名失败", - "domainErrorFetchDescription": "获取域名时出错", + "domainErrorFetch": "获取域名出错", + "domainErrorFetchDescription": "获取域时出错", "resourceErrorUpdate": "更新资源失败", "resourceErrorUpdateDescription": "更新资源时出错", "resourceUpdated": "资源已更新", "resourceUpdatedDescription": "资源已成功更新", - "resourceErrorTransfer": "转移资源失败", - "resourceErrorTransferDescription": "转移资源时出错", + "resourceErrorTransfer": "传输资源失败", + "resourceErrorTransferDescription": "传输资源时出错", "resourceTransferred": "资源已传输", "resourceTransferredDescription": "资源已成功传输", "resourceErrorToggle": "切换资源失败", @@ -675,9 +675,9 @@ "resourceGeneral": "常规设置", "resourceGeneralDescription": "配置此资源的常规设置", "resourceEnable": "启用资源", - "resourceTransfer": "转移资源", + "resourceTransfer": "传输资源", "resourceTransferDescription": "将此资源转移到另一个站点", - "resourceTransferSubmit": "转移资源", + "resourceTransferSubmit": "传输资源", "siteDestination": "目标站点", "searchSites": "搜索站点", "accessRoleCreate": "创建角色", @@ -704,13 +704,13 @@ "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", "licenseTierCommercial": "商业许可证", - "licensed": "已授权", - "yes": "是", + "licensed": "许可的", + "yes": "否", "no": "否", "sitesAdditional": "其他站点", "licenseKeys": "许可证密钥", - "sitestCountDecrease": "减少站点数量", - "sitestCountIncrease": "增加站点数量", + "sitestCountDecrease": "减少站点计数", + "sitestCountIncrease": "增加站点计数", "idpManage": "管理身份提供商", "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", @@ -723,12 +723,12 @@ "idp": "身份提供商", "idpSearch": "搜索身份提供者...", "idpAdd": "添加身份提供商", - "idpClientIdRequired": "客户端ID 是必需的。", - "idpClientSecretRequired": "客户端密钥是必需的。", - "idpErrorAuthUrlInvalid": "身份验证URL 必须是有效的 URL。", - "idpErrorTokenUrlInvalid": "令牌URL 必须是有效的 URL。", - "idpPathRequired": "标识符路径是必需的。", - "idpScopeRequired": "授权范围是必需的。", + "idpClientIdRequired": "客户端ID是必需的。", + "idpClientSecretRequired": "客户端密码是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", + "idpPathRequired": "标识路径是必需的。", + "idpScopeRequired": "范围是必需的。", "idpOidcDescription": "配置 OpenID 连接身份提供商", "idpCreatedDescription": "身份提供商创建成功", "idpCreate": "创建身份提供商", @@ -748,24 +748,24 @@ "idpClientSecret": "客户端密钥", "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", "idpAuthUrl": "授权 URL", - "idpAuthUrlDescription": "OAuth2 授权端点的 URL", - "idpTokenUrl": "令牌 URL", - "idpTokenUrlDescription": "OAuth2 令牌端点的 URL", - "idpOidcConfigureAlert": "重要提示", - "idpOidcConfigureAlertDescription": "创建身份提供方后,您需要在其设置中配置回调 URL。回调 URL 会在创建成功后提供。", + "idpAuthUrlDescription": "OAuth2 授权终点 URL", + "idpTokenUrl": "令牌网址", + "idpTokenUrlDescription": "OAuth2 令牌端点URL", + "idpOidcConfigureAlert": "重要信息", + "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", "idpToken": "令牌配置", - "idpTokenDescription": "配置如何从 ID 令牌中提取用户信息", + "idpTokenDescription": "配置如何从ID令牌中提取用户信息", "idpJmespathAbout": "关于 JMESPath", - "idpJmespathAboutDescription": "以下路径使用 JMESPath 语法从 ID 令牌中提取值。", - "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息", - "idpJmespathLabel": "标识符路径", - "idpJmespathLabelDescription": "ID 令牌中用户标识符的路径", - "idpJmespathEmailPathOptional": "邮箱路径(可选)", - "idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径", - "idpJmespathNamePathOptional": "用户名路径(可选)", - "idpJmespathNamePathOptionalDescription": "ID 令牌中用户名的路径", - "idpOidcConfigureScopes": "作用域(Scopes)", - "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 请求作用域列表", + "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", + "idpJmespathLabel": "标识路径", + "idpJmespathLabelDescription": "用户标识符的路径", + "idpJmespathEmailPathOptional": "电子邮件路径(可选)", + "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", + "idpJmespathNamePathOptional": "名称路径(可选)", + "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", + "idpOidcConfigureScopes": "范围", + "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", "idpSubmit": "创建身份提供商", "orgPolicies": "组织策略", "idpSettings": "{idpName} 设置", @@ -789,13 +789,13 @@ "defaultMappingsRole": "默认角色映射", "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", "defaultMappingsOrg": "默认组织映射", - "defaultMappingsOrgDescription": "此表达式必须返回 组织ID 或 true 才能允许用户访问组织。", + "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", "defaultMappingsSubmit": "保存默认映射", "orgPoliciesEdit": "编辑组织策略", "org": "组织", "orgSelect": "选择组织", "orgSearch": "搜索", - "orgNotFound": "找不到组织。", + "orgNotFound": "找不到 org 。", "roleMappingPathOptional": "角色映射路径(可选)", "orgMappingPathOptional": "组织映射路径(可选)", "orgPolicyUpdate": "更新策略", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "Pangolin Logo", + "pangolinLogoAlt": "邦戈林徽标", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -851,13 +851,13 @@ "otpEmail": "一次性密码 (OTP)", "otpEmailSubmit": "提交 OTP", "backToEmail": "回到电子邮件", - "noSupportKey": "服务器当前未使用支持者密钥,欢迎支持本项目!", + "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", "accessDenied": "访问被拒绝", - "accessDeniedDescription": "当前账户无权访问此资源。如认为这是错误,请与管理员联系。", + "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", "accessTokenError": "检查访问令牌时出错", "accessGranted": "已授予访问", "accessUrlInvalid": "访问 URL 无效", - "accessGrantedDescription": "您已获准访问此资源,正在为您跳转...", + "accessGrantedDescription": "您已获准访问此资源。重定向您...", "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", "tokenInvalid": "无效的令牌", "pincodeInvalid": "无效的代码", @@ -866,9 +866,9 @@ "passwordResetSuccess": "密码重置成功!返回登录...", "passwordReset": "重置密码", "passwordResetDescription": "按照步骤重置您的密码", - "passwordResetSent": "我们将发送一个验证码到这个电子邮件地址。", - "passwordResetCode": "验证码", - "passwordResetCodeDescription": "请检查您的电子邮件以获取验证码。", + "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", + "passwordResetCode": "Reset Code", + "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", "passwordNew": "新密码", "passwordNewConfirm": "确认新密码", "pincodeAuth": "验证器代码", @@ -898,7 +898,7 @@ "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", - "orgErrorCreate": "创建组织时出错", + "orgErrorCreate": "创建 org 时出错", "pageNotFound": "找不到页面", "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", "overview": "概览", @@ -923,13 +923,13 @@ "tagWarnDuplicate": "未添加重复标签 {tagText}", "supportKeyInvalid": "无效密钥", "supportKeyInvalidDescription": "您的支持者密钥无效。", - "supportKeyValid": "有效的密钥", + "supportKeyValid": "Valid Key", "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", "supportKeyErrorValidationDescription": "验证支持者密钥失败。", - "supportKey": "支持开发和通过一个 Pangolin !", - "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展 Pangolin 。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", - "supportKeyPet": "您还可以领养并见到属于自己的 Pangolin!", - "supportKeyPurchase": "付款通过 GitHub 进行处理,之后您可以在以下位置获取您的密钥:", + "supportKey": "支持开发和通过一个潘戈林!", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", + "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", "supportKeyPurchaseLink": "我们的网站", "supportKeyPurchase2": "并在这里兑换。", "supportKeyLearnMore": "了解更多。", @@ -944,15 +944,15 @@ "supportKeyRedeem": "兑换支持者密钥", "supportKeyHideSevenDays": "隐藏7天", "supportKeyEnter": "输入支持者密钥", - "supportKeyEnterDescription": "见到你自己的 Pangolin!", - "githubUsername": "GitHub 用户名", + "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", + "githubUsername": "GitHub Username", "supportKeyInput": "支持者密钥", "supportKeyBuy": "购买支持者密钥", "logoutError": "注销错误", "signingAs": "登录为", "serverAdmin": "服务器管理员", - "otpEnable": "启用双因子认证", - "otpDisable": "禁用双因子认证", + "otpEnable": "启用双因子", + "otpDisable": "禁用双因子", "logout": "登出", "licenseTierProfessionalRequired": "需要专业版", "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "设置资源粉码", "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", - "actionCreateTarget": "创建目标", + "actionCreateTarget": "Create Target", "actionDeleteTarget": "删除目标", "actionGetTarget": "获取目标", "actionListTargets": "列表目标", - "actionUpdateTarget": "更新目标", + "actionUpdateTarget": "Update Target", "actionCreateRole": "创建角色", "actionDeleteRole": "删除角色", "actionGetRole": "获取角色", @@ -1002,7 +1002,7 @@ "actionListResourceRules": "列出资源规则", "actionUpdateResourceRule": "更新资源规则", "actionListOrgs": "列出组织", - "actionCheckOrgId": "检查组织ID", + "actionCheckOrgId": "检查 ID", "actionCreateOrg": "创建组织", "actionDeleteOrg": "删除组织", "actionListApiKeys": "列出API密钥", @@ -1013,15 +1013,15 @@ "actionCreateIdp": "创建IDP", "actionUpdateIdp": "更新IDP", "actionDeleteIdp": "删除IDP", - "actionListIdps": "列出IDP", + "actionListIdps": "列出国内流离失所者", "actionGetIdp": "获取IDP", - "actionCreateIdpOrg": "创建 IDP组织策略", - "actionDeleteIdpOrg": "删除 IDP组织策略", - "actionListIdpOrgs": "列出 IDP组织", - "actionUpdateIdpOrg": "更新 IDP组织", + "actionCreateIdpOrg": "创建IDP Org 策略", + "actionDeleteIdpOrg": "删除IDP Org 策略", + "actionListIdpOrgs": "列出国内流离失所者组织", + "actionUpdateIdpOrg": "更新IDP Org", "noneSelected": "未选择", "orgNotFound2": "未找到组织。", - "searchProgress": "搜索中...", + "searchProgress": "搜索...", "create": "创建", "orgs": "组织", "loginError": "登录时出错", @@ -1031,13 +1031,13 @@ "otpAuthSubmit": "提交代码", "idpContinue": "或者继续", "otpAuthBack": "返回登录", - "navbar": "导航菜单", + "navbar": "Navigation Menu", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", - "otpErrorEnableDescription": "启用 2FA 时出错", - "otpSetupCheckCode": "请输入您的6位数字代码", + "otpErrorEnableDescription": "启用2FA 时出错", + "otpSetupCheckCode": "请输入一个6位数字", "otpSetupCheckCodeRetry": "无效的代码。请重试。", "otpSetup": "启用两步验证", "otpSetupDescription": "用额外的保护层来保护您的帐户", @@ -1061,20 +1061,20 @@ "copyTextFailed": "复制文本失败: ", "copyTextClipboard": "复制到剪贴板", "inviteErrorInvalidConfirmation": "无效确认", - "passwordRequired": "必须填写密码", + "passwordRequired": "密码是必需的", "allowAll": "允许所有", "permissionsAllowAll": "允许所有权限", - "githubUsernameRequired": "必须填写 GitHub 用户名", - "supportKeyRequired": "必须填写支持者密钥", - "passwordRequirementsChars": "密码至少需要 8 个字符", + "githubUsernameRequired": "GitHub 用户名是必需的", + "supportKeyRequired": "支持者密钥是必需的", + "passwordRequirementsChars": "密码必须至少 8 个字符", "language": "语言", "verificationCodeRequired": "必须输入代码", "userErrorNoUpdate": "没有要更新的用户", "siteErrorNoUpdate": "没有要更新的站点", "resourceErrorNoUpdate": "没有可更新的资源", "authErrorNoUpdate": "没有要更新的身份验证信息", - "orgErrorNoUpdate": "没有要更新的组织", - "orgErrorNoProvided": "未提供组织", + "orgErrorNoUpdate": "没有要更新的 org", + "orgErrorNoProvided": "未提供 org", "apiKeysErrorNoUpdate": "没有要更新的 API 密钥", "sidebarOverview": "概览", "sidebarHome": "首页", @@ -1089,5 +1089,40 @@ "sidebarSettings": "设置", "sidebarAllUsers": "所有用户", "sidebarIdentityProviders": "身份提供商", - "sidebarLicense": "证书" -} + "sidebarLicense": "证书", + "enableDockerSocket": "启用停靠套接字", + "enableDockerSocketDescription": "启用 Docker Socket 发现用于散布容器信息,在资源目标中有用。", + "enableDockerSocketLink": "启用 Docker Socket 发现用于散布容器信息,在资源目标中有用。", + "viewDockerContainers": "查看停靠容器", + "containersIn": "Containers in {siteName}", + "selectContainerDescription": "选择任何容器作为目标的主机名。点击端口使用端口。", + "containerName": "名称", + "containerImage": "图片", + "containerState": "状态", + "containerNetworks": "网络", + "containerHostnameIp": "Hostname/IP", + "containerLabels": "标签", + "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsTitle": "容器标签", + "containerLabelEmpty": "", + "containerPorts": "端口", + "containerPortsMore": "+{count} more", + "containerActions": "行动", + "select": "选择", + "noContainersMatchingFilters": "没有找到匹配当前过滤器的容器。", + "showContainersWithoutPorts": "显示没有端口的容器", + "showStoppedContainers": "显示已停止的容器", + "noContainersFound": "未找到容器。请确保Docker容器正在运行。", + "searchContainersPlaceholder": "Search across {count} containers...", + "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "filters": "筛选器", + "filterOptions": "过滤器选项", + "filterPorts": "端口", + "filterStopped": "已停止", + "clearAllFilters": "清除所有过滤器", + "columns": "列", + "toggleColumns": "切换列", + "refreshContainersList": "刷新容器列表", + "searching": "搜索中...", + "noContainersFoundMatching": "No containers found matching \"{filter}\"." +} \ No newline at end of file From b395b65b8600a4b5d4508f2e5549ffffc42e9468 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:48:19 +0200 Subject: [PATCH 102/105] Update de-DE.json --- messages/de-DE.json | 102 ++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 43374cc3..629ce825 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1,33 +1,33 @@ { - "setupCreate": "Erstelle deine Organisation, Seite und Ressourcen", + "setupCreate": "Erstelle eine Organisation, Site und Ressourcen", "setupNewOrg": "Neue Organisation", "setupCreateOrg": "Organisation erstellen", "setupCreateResources": "Ressource erstellen", - "setupOrgName": "Organisation's Name", - "orgDisplayName": "Dies ist der Anzeigename Ihrer Organisation.", + "setupOrgName": "Name der Organisation", + "orgDisplayName": "Anzeigename der Organisation.", "orgId": "Organisations-ID", - "setupIdentifierMessage": "Dies ist der eindeutige Bezeichner für Ihre Organisation. Dies ist getrennt vom Anzeigenamen.", - "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wählen Sie eine andere.", - "componentsErrorNoMemberCreate": "Sie sind derzeit kein Mitglied einer Organisation. Erstellen Sie eine Organisation, um loszulegen.", + "setupIdentifierMessage": "Dies ist eine Eindeutige ID für Ihre Organisation. Diese ist unabhängig vom Anzeigenamen.", + "setupErrorIdentifier": "Organisations-ID ist bereits vergeben. Bitte wähle eine andere.", + "componentsErrorNoMemberCreate": "Du bist derzeit kein Mitglied einer Organisation. Erstelle eine Organisation, um zu starten.", "componentsErrorNoMember": "Du bist aktuell kein Mitglied einer Organisation.", "welcome": "Willkommen zu Pangolin", "componentsCreateOrg": "Erstelle eine Organisation", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} =1 {einer Organisation} other {# Organisationen}}.", - "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "dismiss": "Verwerfen", - "componentsLicenseViolation": "Lizenzverletzung: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Folgen Sie den Lizenzbedingungen, um alle Funktionen weiter zu nutzen.", + "componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", - "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht angenommen wurde oder nicht mehr gültig ist.", - "inviteErrorUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer ist.", - "inviteLoginUser": "Bitte stellen Sie sicher, dass Sie als korrekter Benutzer angemeldet sind.", - "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als ob die Einladung, auf die du zugreifen möchtest, nicht für einen Benutzer ist, der existiert.", - "inviteCreateUser": "Bitte erstellen Sie zuerst ein Konto.", - "goHome": "Nach Hause", + "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.", + "inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.", + "inviteLoginUser": "Bitte stelle sicher, dass du als korrekter Benutzer angemeldet bist.", + "inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als sei die Einladung, auf die du zugreifen möchtest, nicht für einen existierenden Benutzer bestimmt.", + "inviteCreateUser": "Bitte erstelle zuerst ein Konto.", + "goHome": "Zur Startseite", "inviteLogInOtherUser": "Als anderer Benutzer anmelden", "createAnAccount": "Konto erstellen", "inviteNotAccepted": "Einladung nicht angenommen", - "authCreateAccount": "Erstellen Sie ein Konto um loszulegen", - "authNoAccount": "Sie haben noch kein Konto?", + "authCreateAccount": "Erstellen ein Konto um loszulegen", + "authNoAccount": "Du besitzt noch kein Konto?", "email": "E-Mail", "password": "Passwort", "confirmPassword": "Passwort bestätigen", @@ -38,8 +38,8 @@ "online": "Online", "offline": "Offline", "site": "Site", - "dataIn": "Daten in", - "dataOut": "Daten raus", + "dataIn": "Daten eingehend", + "dataOut": "Daten ausgehend", "connectionType": "Verbindungstyp", "tunnelType": "Tunneltyp", "local": "Lokal", @@ -47,57 +47,57 @@ "siteConfirmDelete": "Site löschen bestätigen", "siteDelete": "Site löschen", "siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", - "siteMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen der Seite unten ein.", - "siteQuestionRemove": "Sind Sie sicher, dass Sie die Site {selectedSite} aus der Organisation entfernen möchten?", + "siteMessageConfirm": "Um zu bestätigen, gib den Namen der Site ein.", + "siteQuestionRemove": "Bist du sicher, dass Sie die Site {selectedSite} aus der Organisation entfernt werden soll?", "siteManageSites": "Sites verwalten", - "siteDescription": "Verbindung zu Ihrem Netzwerk durch sichere Tunnel erlauben", + "siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben", "siteCreate": "Site erstellen", - "siteCreateDescription2": "Folgen Sie den Schritten unten, um eine neue Seite zu erstellen und zu verbinden", - "siteCreateDescription": "Erstellen Sie eine neue Seite, um Ihre Ressourcen zu verbinden", + "siteCreateDescription2": "Folge den nachfolgenden Schritten, um eine neue Site zu erstellen und zu verbinden", + "siteCreateDescription": "Erstelle eine neue Site, um Ressourcen zu verbinden", "close": "Schließen", - "siteErrorCreate": "Fehler beim Erstellen der Seite", + "siteErrorCreate": "Fehler beim Erstellen der Site", "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", - "siteNameDescription": "Dies ist der Anzeigename für die Website.", + "siteNameDescription": "Dies ist der Anzeigename für die Site.", "method": "Methode", - "siteMethodDescription": "Auf diese Weise werden Sie Verbindungen freigeben.", - "siteLearnNewt": "Erfahren Sie, wie Sie Newt auf Ihrem System installieren", - "siteSeeConfigOnce": "Sie können die Konfiguration nur einmal sehen.", + "siteMethodDescription": "So werden Verbindungen freigegeben.", + "siteLearnNewt": "Wie du Newt auf deinem System installieren kannst", + "siteSeeConfigOnce": "Du kannst die Konfiguration nur einmalig ansehen.", "siteLoadWGConfig": "Lade WireGuard Konfiguration...", - "siteDocker": "Erweitern für Docker-Details", + "siteDocker": "Erweitern für Docker Details", "toggle": "Umschalten", - "dockerCompose": "Docker komponieren", + "dockerCompose": "Docker Compose", "dockerRun": "Docker Run", - "siteLearnLocal": "Lokale Sites nicht Tunnel, erfahren Sie mehr", + "siteLearnLocal": "Mehr Infos zu lokalen Sites", "siteConfirmCopy": "Ich habe die Konfiguration kopiert", - "searchSitesProgress": "Seiten suchen...", + "searchSitesProgress": "Sites durchsuchen...", "siteAdd": "Site hinzufügen", - "siteInstallNewt": "Neustart installieren", - "siteInstallNewtDescription": "Lass Newt auf deinem System laufen", + "siteInstallNewt": "Newt installieren", + "siteInstallNewtDescription": "Installiere Newt auf deinem System.", "WgConfiguration": "WireGuard Konfiguration", - "WgConfigurationDescription": "Verwenden Sie folgende Konfiguration, um sich mit Ihrem Netzwerk zu verbinden", + "WgConfigurationDescription": "Verwende folgende Konfiguration, um dich mit deinem Netzwerk zu verbinden", "operatingSystem": "Betriebssystem", "commands": "Befehle", "recommended": "Empfohlen", - "siteNewtDescription": "Nutzen Sie Newt für die beste Benutzererfahrung. Es verwendet WireGuard unter der Haube und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", - "siteRunsInDocker": "Läuft im Docker", - "siteRunsInShell": "Läuft in der Shell auf macOS, Linux und Windows", - "siteErrorDelete": "Fehler beim Löschen der Seite", - "siteErrorUpdate": "Fehler beim Aktualisieren der Seite", - "siteErrorUpdateDescription": "Beim Aktualisieren der Seite ist ein Fehler aufgetreten.", + "siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", + "siteRunsInDocker": "Läuft in Docker", + "siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows", + "siteErrorDelete": "Fehler beim Löschen der Site", + "siteErrorUpdate": "Fehler beim Aktualisieren der Site", + "siteErrorUpdateDescription": "Beim Aktualisieren der Site ist ein Fehler aufgetreten.", "siteUpdated": "Site aktualisiert", - "siteUpdatedDescription": "Die Seite wurde aktualisiert.", - "siteGeneralDescription": "Allgemeine Einstellungen für diese Seite konfigurieren", - "siteSettingDescription": "Konfigurieren Sie die Einstellungen auf Ihrer Seite", + "siteUpdatedDescription": "Die Site wurde aktualisiert.", + "siteGeneralDescription": "Allgemeine Einstellungen für diese Site konfigurieren", + "siteSettingDescription": "Konfigurieren der Site Einstellungen", "siteSetting": "{siteName} Einstellungen", "siteNewtTunnel": "Newt-Tunnel (empfohlen)", - "siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in Ihr Netzwerk zu erstellen. Keine zusätzliche Einrichtung.", - "siteWg": "Einfacher WireGuard", - "siteWgDescription": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu errichten. Manuelle NAT-Setup erforderlich.", + "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", + "siteWg": "Einfacher WireGuard Tunnel", + "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", - "siteSeeAll": "Alle Seiten anzeigen", - "siteTunnelDescription": "Legen Sie fest, wie Sie sich mit Ihrer Website verbinden möchten", - "siteNewtCredentials": "Anmeldedaten neu", + "siteSeeAll": "Alle Sites anzeigen", + "siteTunnelDescription": "Lege fest, wie du dich mit deiner Site verbinden möchtest", + "siteNewtCredentials": "Neue Newt Zugangsdaten", "siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren", "siteCredentialsSave": "Ihre Zugangsdaten speichern", "siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.", @@ -1125,4 +1125,4 @@ "refreshContainersList": "Container-Liste aktualisieren", "searching": "Suche...", "noContainersFoundMatching": "Keine Container gefunden mit \"{filter}\"." -} \ No newline at end of file +} From 335c9b1fea3ea7a17e335059859dabe89c1095b2 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:50:46 +0200 Subject: [PATCH 103/105] Update zh-CN.json --- messages/zh-CN.json | 332 ++++++++++++++++++++++---------------------- 1 file changed, 166 insertions(+), 166 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 71628a0c..0b4e73fb 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1,5 +1,5 @@ { - "setupCreate": "创建您的组织、网站和资源", + "setupCreate": "创建您的第一个组织、网站和资源", "setupNewOrg": "新建组织", "setupCreateOrg": "创建组织", "setupCreateResources": "创建资源", @@ -7,17 +7,17 @@ "orgDisplayName": "这是您组织的显示名称。", "orgId": "组织ID", "setupIdentifierMessage": "这是您组织的唯一标识符。这是与显示名称分开的。", - "setupErrorIdentifier": "组织 ID 已被使用。请另选一个。", + "setupErrorIdentifier": "组织ID 已被使用。请另选一个。", "componentsErrorNoMemberCreate": "您目前不是任何组织的成员。创建组织以开始操作。", "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", - "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款继续使用所有功能。", - "dismiss": "关闭", + "componentsMember": "{count, plural, =0 {您目前不是任何组织的成员。} other {你已加入 # 个组织。}}.", + "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", + "dismiss": "忽略", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", - "inviteErrorNotValid": "我们很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", + "inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。", "inviteErrorUser": "很抱歉,但看起来你想要访问的邀请不是这个用户。", "inviteLoginUser": "请确保您以正确的用户登录。", "inviteErrorNoUser": "很抱歉,但看起来你想访问的邀请不是一个存在的用户。", @@ -61,27 +61,27 @@ "siteNameDescription": "这是站点的显示名称。", "method": "方法", "siteMethodDescription": "这是您将如何显示连接。", - "siteLearnNewt": "学习如何在您的系统上安装Newt", + "siteLearnNewt": "学习如何在您的系统上安装 Newt", "siteSeeConfigOnce": "您只能看到一次配置。", - "siteLoadWGConfig": "正在载入Wire保护配置...", + "siteLoadWGConfig": "正在载入 WireGuard 配置...", "siteDocker": "扩展 Docker 部署详细信息", - "toggle": "切换键", - "dockerCompose": "Docker 配置", - "dockerRun": "停靠栏", - "siteLearnLocal": "本地站点没有隧道,学习更多", - "siteConfirmCopy": "我已复制配置", + "toggle": "切换", + "dockerCompose": "Docker Compose", + "dockerRun": "Docker Run", + "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", + "siteConfirmCopy": "我已经复制了配置信息", "searchSitesProgress": "搜索站点...", "siteAdd": "添加站点", - "siteInstallNewt": "安装新的", - "siteInstallNewtDescription": "在您的系统上获得新的运行", - "WgConfiguration": "Wire护卫配置", + "siteInstallNewt": "安装 Newt", + "siteInstallNewtDescription": "在您的系统中运行 Newt", + "WgConfiguration": "WireGuard 配置", "WgConfigurationDescription": "使用以下配置连接到您的网络", "operatingSystem": "操作系统", "commands": "命令", - "recommended": "推荐的", - "siteNewtDescription": "为了获得最好的用户体验,请使用新的。 它使用ireGuard,让您能够使用Pangolin仪表板内他们的局域网地址处理您的私人资源。", - "siteRunsInDocker": "运行在停靠栏", - "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 shell 中运行", + "recommended": "推荐", + "siteNewtDescription": "为获得最佳用户体验,请使用 Newt。其底层采用 WireGuard 技术,可直接通过 Pangolin 控制台,使用局域网地址访问您私有网络中的资源。", + "siteRunsInDocker": "在 Docker 中运行", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中运行", "siteErrorDelete": "删除站点出错", "siteErrorUpdate": "更新站点失败", "siteErrorUpdateDescription": "更新站点时出错。", @@ -90,17 +90,17 @@ "siteGeneralDescription": "配置此站点的常规设置", "siteSettingDescription": "配置您网站上的设置", "siteSetting": "{siteName} 设置", - "siteNewtTunnel": "新隧道(推荐)", - "siteNewtTunnelDescription": "最简单的方式来创建一个入口到您的网络。没有额外的设置。", - "siteWg": "基本线甲", - "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动设置NAT。", - "siteLocalDescription": "仅本地资源。没有隧道。", + "siteNewtTunnel": "Newt 隧道 (推荐)", + "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", + "siteWg": "基本 WireGuard", + "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", + "siteLocalDescription": "仅限本地资源。不需要隧道。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", - "siteNewtCredentials": "新建凭据", - "siteNewtCredentialsDescription": "这是新建服务器的身份验证方式", - "siteCredentialsSave": "保存您的证书", - "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "siteNewtCredentials": "Newt 凭据", + "siteNewtCredentialsDescription": "这是 Newt 服务器的身份验证凭据", + "siteCredentialsSave": "保存您的凭据", + "siteCredentialsSaveDescription": "您只能看到一次。请确保将其复制并保存到一个安全的地方。", "siteInfo": "站点信息", "status": "状态", "shareTitle": "管理共享链接", @@ -111,16 +111,16 @@ "shareErrorDeleteMessage": "删除链接时出错", "shareDeleted": "链接已删除", "shareDeletedDescription": "链接已删除", - "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求标题。 每次验证访问请求都必须从客户端传递。", + "shareTokenDescription": "您的访问令牌可以通过两种方式传递:作为查询参数或请求头。 每次验证访问请求都必须从客户端传递。", "accessToken": "访问令牌", "usageExamples": "用法示例", - "tokenId": "Token ID", - "requestHeades": "请求标题", + "tokenId": "令牌 ID", + "requestHeades": "请求头", "queryParameter": "查询参数", - "importantNote": "重要笔记", - "shareImportantDescription": "出于安全考虑,建议尽可能在查询参数中使用头部,因为查询参数可能会在服务器日志或浏览器历史记录中记录。", + "importantNote": "重要提示", + "shareImportantDescription": "出于安全考虑,建议尽可能在使用请求头传递参数,因为查询参数可能会被浏览器历史记录或服务器日志记录。", "token": "令牌", - "shareTokenSecurety": "保持您的访问令牌安全。请不要在公开可访问的区域或客户端代码中共享。", + "shareTokenSecurety": "请妥善保管您的访问令牌,不要将其暴露在公开访问的区域或客户端代码中。", "shareErrorFetchResource": "获取资源失败", "shareErrorFetchResourceDescription": "获取资源时出错", "shareErrorCreate": "无法创建共享链接", @@ -141,7 +141,7 @@ "title": "标题", "created": "已创建", "expires": "过期时间", - "never": "从不使用", + "never": "永不过期", "shareErrorSelectResource": "请选择一个资源", "resourceTitle": "管理资源", "resourceDescription": "为您的私人应用程序创建安全代理", @@ -149,14 +149,14 @@ "resourceAdd": "添加资源", "resourceErrorDelte": "删除资源时出错", "authentication": "认证", - "protected": "保护", - "notProtected": "不保护", - "resourceMessageRemove": "一旦删除,资源将不再可访问。与资源相关的所有目标也将被删除。", + "protected": "受到保护", + "notProtected": "未受到保护", + "resourceMessageRemove": "一旦删除,资源将不再可访问。与该资源相关的所有目标也将被删除。", "resourceMessageConfirm": "请在下面输入资源名称以确认。", "resourceQuestionRemove": "您确定要从组织中删除 {selectedResource} 吗?", "resourceHTTP": "HTTPS 资源", - "resourceHTTPDescription": "使用子域或基本域名通过 HTTPS 向您的应用程序提出代理请求。", - "resourceRaw": "Raw TCP/UDP 资源", + "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", + "resourceRaw": "TCP/UDP 资源", "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", @@ -172,20 +172,20 @@ "resourceHTTPSSettings": "HTTPS 设置", "resourceHTTPSSettingsDescription": "配置如何通过 HTTPS 访问您的资源", "domainType": "域类型", - "subdomain": "子域", - "baseDomain": "基础域", - "subdomnainDescription": "您的资源可以访问的子域。", + "subdomain": "子域名", + "baseDomain": "根域名", + "subdomnainDescription": "您的资源可以访问的子域名。", "resourceRawSettings": "TCP/UDP 设置", "resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "协议", + "protocolSelect": "选择协议", "resourcePortNumber": "端口号", "resourcePortNumberDescription": "代理请求的外部端口号。", "cancel": "取消", "resourceConfig": "配置片段", "resourceConfigDescription": "复制并粘贴这些配置片段以设置您的 TCP/UDP 资源", "resourceAddEntrypoints": "Traefik: 添加入口点", - "resourceExposePorts": "Gerbil:在Docker Compose 中显示端口", + "resourceExposePorts": "Gerbil:在 Docker Compose 中显示端口", "resourceLearnRaw": "学习如何配置 TCP/UDP 资源", "resourceBack": "返回资源", "resourceGoTo": "转到资源", @@ -194,23 +194,23 @@ "visibility": "可见性", "enabled": "已启用", "disabled": "已禁用", - "general": "A. 概况", + "general": "概览", "generalSettings": "常规设置", "proxy": "代理服务器", "rules": "规则", "resourceSettingDescription": "配置您资源上的设置", "resourceSetting": "{resourceName} 设置", - "alwaysAllow": "总是允许", - "alwaysDeny": "总是拒绝", + "alwaysAllow": "一律允许", + "alwaysDeny": "一律拒绝", "orgSettingsDescription": "配置您组织的一般设置", "orgGeneralSettings": "组织设置", "orgGeneralSettingsDescription": "管理您的机构详细信息和配置", "saveGeneralSettings": "保存常规设置", "orgDangerZone": "危险区域", - "orgDangerZoneDescription": "一旦你删除了这个组织,就没有回去了。请放心。", + "orgDangerZoneDescription": "一旦删除该组织,将无法恢复,请务必确认。", "orgDelete": "删除组织", "orgDeleteConfirm": "确认删除组织", - "orgMessageRemove": "此操作不可逆,将删除所有相关数据。", + "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", "orgUpdated": "组织已更新", @@ -224,15 +224,15 @@ "orgDeleted": "组织已删除", "orgDeletedMessage": "组织及其数据已被删除。", "orgMissing": "缺少组织 ID", - "orgMissingMessage": "没有机构ID,无法重新生成邀请。", + "orgMissingMessage": "没有组织ID,无法重新生成邀请。", "accessUsersManage": "管理用户", - "accessUsersDescription": "邀请用户并将他们添加到角色以管理访问您的组织", + "accessUsersDescription": "邀请用户并位他们添加角色以管理访问您的组织", "accessUsersSearch": "搜索用户...", "accessUserCreate": "创建用户", "accessUserRemove": "删除用户", "username": "用户名", "identityProvider": "身份提供商", - "role": "作用", + "role": "角色", "nameRequired": "名称是必填项", "accessRolesManage": "管理角色", "accessRolesDescription": "配置角色来管理访问您的组织", @@ -244,13 +244,13 @@ "inviteDescription": "管理您给其他用户的邀请", "inviteSearch": "搜索邀请...", "minutes": "分钟", - "hours": "小时数", + "hours": "小时", "days": "天", "weeks": "周", "months": "月", "years": "年", "day": "{count, plural, =1 {# 天} other {# 天}}", - "apiKeysTitle": "API 密钥信息", + "apiKeysTitle": "API 密钥", "apiKeysConfirmCopy2": "您必须确认您已复制 API 密钥。", "apiKeysErrorCreate": "创建 API 密钥出错", "apiKeysErrorSetPermission": "设置权限出错", @@ -260,7 +260,7 @@ "apiKeysGeneralSettingsDescription": "确定此 API 密钥可以做什么", "apiKeysList": "您的 API 密钥", "apiKeysSave": "保存您的 API 密钥", - "apiKeysSaveDescription": "您只能看到一次。请确保将其复制到一个安全的地方。", + "apiKeysSaveDescription": "该信息仅会显示一次,请确保将其复制到安全的位置。", "apiKeysInfo": "您的 API 密钥是:", "apiKeysConfirmCopy": "我已复制 API 密钥", "generate": "生成", @@ -280,7 +280,7 @@ "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", - "apiKeysMessageRemove": "一旦移除,API密钥将无法再使用。", + "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", "apiKeysDelete": "删除 API 密钥", @@ -290,7 +290,7 @@ "userTitle": "管理所有用户", "userDescription": "查看和管理系统中的所有用户", "userAbount": "关于用户管理", - "userAbountDescription": "此表显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表中的删除操作删除其根用户对象。", + "userAbountDescription": "此表格显示系统中所有根用户对象。每个用户可能属于多个组织。 从组织中删除用户不会删除其根用户对象 - 他们将保留在系统中。 要从系统中完全删除用户,您必须使用此表格中的删除操作删除其根用户对象。", "userServer": "服务器用户", "userSearch": "搜索服务器用户...", "userErrorDelete": "删除用户时出错", @@ -300,7 +300,7 @@ "userMessageConfirm": "请在下面输入用户名称以确认。", "userQuestionRemove": "您确定要从服务器中永久删除 {selectedUser} 吗?", "licenseKey": "许可证密钥", - "valid": "Valid", + "valid": "有效", "numberOfSites": "站点数量", "licenseKeySearch": "搜索许可证密钥...", "licenseKeyAdd": "添加许可证密钥", @@ -334,7 +334,7 @@ "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", - "licenseTitle": "管理许可状态", + "licenseTitle": "管理许可证状态", "licenseTitleDescription": "查看和管理系统中的许可证密钥", "licenseHost": "主机许可证", "licenseHostDescription": "管理主机的主许可证密钥。", @@ -347,14 +347,14 @@ "licensePurchase": "购买许可证", "licensePurchaseSites": "购买更多站点", "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 个站点", - "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {#站点}}", - "licensePurchaseDescription": "选择你想要多少站点 {selectedMode, select, license {购买许可证。 您可以稍后添加更多网站} other {添加到您现有的许可证}}", - "licenseFee": "许可费", - "licensePriceSite": "每个站点价格", + "licenseSitesUsed": "{count, plural, =0 {# 站点} =1 {# 站点} other {# 站点}}", + "licensePurchaseDescription": "请选择您希望 {selectedMode, select, license {直接购买许可证,您可以随时增加更多站点。} other {为现有许可证购买更多站点}}", + "licenseFee": "许可证费用", + "licensePriceSite": "每个站点的价格", "total": "总计", "licenseContinuePayment": "继续付款", "pricingPage": "定价页面", - "pricingPortal": "查看购买门户网站", + "pricingPortal": "前往付款页面", "licensePricingPage": "关于最新的价格和折扣,请访问 ", "invite": "邀请", "inviteRegenerate": "重新生成邀请", @@ -365,7 +365,7 @@ "inviteRemoved": "邀请已删除", "inviteRemovedDescription": "为 {email} 创建的邀请已删除", "inviteQuestionRemove": "您确定要删除 {email} 的邀请吗?", - "inviteMessageRemove": "一旦删除,这个邀请将不再有效。您可以随时重新邀请用户。", + "inviteMessageRemove": "一旦删除,这个邀请将不再有效。", "inviteMessageConfirm": "要确认,请在下面输入邀请的电子邮件地址。", "inviteQuestionRegenerate": "您确定要重新邀请 {email} 吗?这将会撤销掉之前的邀请", "inviteRemoveConfirm": "确认删除邀请", @@ -373,7 +373,7 @@ "inviteSent": "邀请邮件已成功发送至 {email}。", "inviteSentEmail": "发送电子邮件通知给用户", "inviteGenerate": "已为 {email} 创建新的邀请。", - "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateError": "重复的邀请", "inviteDuplicateErrorDescription": "此用户的邀请已存在。", "inviteRateLimitError": "超出速率限制", "inviteRateLimitErrorDescription": "您超过了每小时3次再生的限制。请稍后再试。", @@ -463,7 +463,7 @@ "targetErrorFetchDescription": "获取目标时出错", "siteErrorFetch": "获取资源失败", "siteErrorFetchDescription": "获取资源时出错", - "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicate": "重复的目标", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", "targetWireGuardErrorInvalidIp": "Invalid target IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", @@ -479,9 +479,9 @@ "proxyUpdatedDescription": "您的代理设置已成功更新", "proxyErrorUpdate": "更新代理设置失败", "proxyErrorUpdateDescription": "更新代理设置时出错", - "targetAddr": "IP / Hostname", + "targetAddr": "IP / 域名", "targetPort": "端口", - "targetProtocol": "Protocol", + "targetProtocol": "协议", "targetTlsSettings": "安全连接配置", "targetTlsSettingsDescription": "配置资源的 SSL/TLS 设置", "targetTlsSettingsAdvanced": "高级TLS设置", @@ -493,7 +493,7 @@ "targetStickySessions": "启用置顶会话", "targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。", "methodSelect": "选择方法", - "targetSubmit": "Add Target", + "targetSubmit": "添加目标", "targetNoOne": "没有目标。使用表单添加目标。", "targetNoOneDescription": "在上面添加多个目标将启用负载平衡。", "targetsSubmit": "保存目标", @@ -504,7 +504,7 @@ "proxyAdditionalSubmit": "保存代理设置", "subnetMaskErrorInvalid": "子网掩码无效。必须在 0 和 32 之间。", "ipAddressErrorInvalidFormat": "无效的 IP 地址格式", - "ipAddressErrorInvalidOctet": "无效的 IP 地址octet", + "ipAddressErrorInvalidOctet": "无效的 IP 地址", "path": "路径", "ipAddressRange": "IP 范围", "rulesErrorFetch": "获取规则失败", @@ -520,7 +520,7 @@ "rulesErrorUpdate": "更新规则失败", "rulesErrorUpdateDescription": "更新规则时出错", "rulesUpdated": "启用规则", - "rulesUpdatedDescription": "规则评价已更新", + "rulesUpdatedDescription": "规则已更新", "rulesMatchIpAddressRangeDescription": "以 CIDR 格式输入地址(如:103.21.244.0/22)", "rulesMatchIpAddress": "输入IP地址(例如,103.21.244.12)", "rulesMatchUrl": "输入一个 URL 路径或模式(例如/api/v1/todos 或 /api/v1/*)", @@ -533,18 +533,18 @@ "ruleErrorUpdate": "操作失败", "ruleErrorUpdateDescription": "保存过程中发生错误", "rulesPriority": "优先权", - "rulesAction": "行 动", - "rulesMatchType": "比赛类型", + "rulesAction": "行为", + "rulesMatchType": "匹配类型", "value": "值", "rulesAbout": "关于规则", - "rulesAboutDescription": "规则允许您根据一组标准控制对资源的访问。 您可以创建规则允许或拒绝基于IP地址或 URL 路径的访问。", + "rulesAboutDescription": "规则使您能够依据特定条件控制资源访问权限。您可以创建基于 IP 地址或 URL 路径的规则,以允许或拒绝访问。", "rulesActions": "行动", "rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法", "rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证", "rulesMatchCriteria": "匹配条件", "rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址", "rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址", - "rulesMatchCriteriaUrl": "匹配一个 URL 路径或图案", + "rulesMatchCriteriaUrl": "匹配一个 URL 路径或模式", "rulesEnable": "启用规则", "rulesEnableDescription": "启用或禁用此资源的规则评估", "rulesResource": "资源规则配置", @@ -562,10 +562,10 @@ "domainsErrorFetch": "获取域名出错", "domainsErrorFetchDescription": "获取域时出错", "none": "无", - "unknown": "未知的", + "unknown": "未知", "resources": "资源", - "resourcesDescription": "资源是在您的私人网络上运行的应用程序的代理。在您的私人网络上为任何 HTTP/HTTPS 或raw TCP/UDP 服务创建资源。 每个资源必须连接到一个站点,以便通过加密的 WireGuard 隧道启用私密安全连接。", - "resourcesWireGuardConnect": "与Wire护卫加密安全连接", + "resourcesDescription": "资源是您私有网络中运行的应用程序的代理。您可以为私有网络中的任何 HTTP/HTTPS 或 TCP/UDP 服务创建资源。每个资源都必须连接到一个站点,以通过加密的 WireGuard 隧道实现私密且安全的连接。", + "resourcesWireGuardConnect": "采用 WireGuard 提供的加密安全连接", "resourcesMultipleAuthenticationMethods": "配置多个身份验证方法", "resourcesUsersRolesAccess": "基于用户和角色的访问控制", "resourcesErrorUpdate": "切换资源失败", @@ -574,7 +574,7 @@ "shareLink": "{resource} 的分享链接", "resourceSelect": "选择资源", "shareLinks": "分享链接", - "share": "可共享链接", + "share": "分享链接", "shareDescription2": "创建资源共享链接。链接提供对资源的临时或无限制访问。 当您创建链接时,您可以配置链接的到期时间。", "shareEasyCreate": "轻松创建和分享", "shareConfigurableExpirationDuration": "可配置的过期时间", @@ -585,29 +585,29 @@ "unknownCommand": "未知命令", "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", - "newtEndpoint": "Newt Endpoint", + "newtEndpoint": "Newt 端点", "newtId": "Newt ID", - "newtSecretKey": "新的秘密密钥", - "architecture": "结构", + "newtSecretKey": "Newt 私钥", + "architecture": "架构", "sites": "站点", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决您的内部资源。", "siteWgCompatibleAllClients": "与所有WireGuard客户端兼容", "siteWgManualConfigurationRequired": "需要手动配置", "userErrorNotAdminOrOwner": "用户不是管理员或所有者", - "pangolinSettings": "设置-Pangolin", + "pangolinSettings": "设置 - Pangolin", "accessRoleYour": "您的角色:", "accessRoleSelect2": "选择角色", "accessUserSelect": "选择一个用户", "otpEmailEnter": "输入电子邮件", "otpEmailEnterDescription": "在输入字段输入后按回车键添加电子邮件。", - "otpEmailErrorInvalid": "无效的电子邮件地址。通用卡 (*) 必须是整个本地部分。", - "otpEmailSmtpRequired": "需要SMTP", + "otpEmailErrorInvalid": "无效的邮箱地址。通配符(*)必须占据整个开头部分。", + "otpEmailSmtpRequired": "需要先配置SMTP", "otpEmailSmtpRequiredDescription": "必须在服务器上启用SMTP才能使用一次性密码验证。", "otpEmailTitle": "一次性密码", "otpEmailTitleDescription": "资源访问需要基于电子邮件的身份验证", "otpEmailWhitelist": "电子邮件白名单", "otpEmailWhitelistList": "白名单邮件", - "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域的任何电子邮件地址。", + "otpEmailWhitelistListDescription": "只有拥有这些电子邮件地址的用户才能访问此资源。 他们将被提示输入一次性密码发送到他们的电子邮件。 通配符 (*@example.com) 可以用来允许来自一个域名的任何电子邮件地址。", "otpEmailWhitelistSave": "保存白名单", "passwordAdd": "添加密码", "passwordRemove": "删除密码", @@ -642,12 +642,12 @@ "resourcePincode": "PIN 码", "resourcePincodeSubmit": "启用 PIN 码保护", "resourcePincodeProtection": "PIN 码保护 {status}", - "resourcePincodeRemove": "资源粉码已删除", - "resourcePincodeRemoveDescription": "已成功删除资源密码", - "resourcePincodeSetup": "资源PIN 码已设置", - "resourcePincodeSetupDescription": "资源固定码已成功设置", - "resourcePincodeSetupTitle": "设置粉码", - "resourcePincodeSetupTitleDescription": "设置置顶码来保护此资源", + "resourcePincodeRemove": "资源 PIN 码已删除", + "resourcePincodeRemoveDescription": "已成功删除资源 PIN 码", + "resourcePincodeSetup": "资源 PIN 码已设置", + "resourcePincodeSetupDescription": "资源 PIN 码已成功设置", + "resourcePincodeSetupTitle": "设置 PIN 码", + "resourcePincodeSetupTitleDescription": "设置 PIN 码来保护此资源", "resourceRoleDescription": "管理员总是可以访问此资源。", "resourceUsersRoles": "用户和角色", "resourceUsersRolesDescription": "配置用户和角色可以访问此资源", @@ -658,14 +658,14 @@ "ssoUseDescription": "对于所有启用此功能的资源,现有用户只需登录一次。", "proxyErrorInvalidPort": "无效的端口号", "subdomainErrorInvalid": "无效的子域", - "domainErrorFetch": "获取域名出错", - "domainErrorFetchDescription": "获取域时出错", + "domainErrorFetch": "获取域名失败", + "domainErrorFetchDescription": "获取域名时出错", "resourceErrorUpdate": "更新资源失败", "resourceErrorUpdateDescription": "更新资源时出错", "resourceUpdated": "资源已更新", "resourceUpdatedDescription": "资源已成功更新", - "resourceErrorTransfer": "传输资源失败", - "resourceErrorTransferDescription": "传输资源时出错", + "resourceErrorTransfer": "转移资源失败", + "resourceErrorTransferDescription": "转移资源时出错", "resourceTransferred": "资源已传输", "resourceTransferredDescription": "资源已成功传输", "resourceErrorToggle": "切换资源失败", @@ -675,9 +675,9 @@ "resourceGeneral": "常规设置", "resourceGeneralDescription": "配置此资源的常规设置", "resourceEnable": "启用资源", - "resourceTransfer": "传输资源", + "resourceTransfer": "转移资源", "resourceTransferDescription": "将此资源转移到另一个站点", - "resourceTransferSubmit": "传输资源", + "resourceTransferSubmit": "转移资源", "siteDestination": "目标站点", "searchSites": "搜索站点", "accessRoleCreate": "创建角色", @@ -704,13 +704,13 @@ "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", "licenseTierCommercial": "商业许可证", - "licensed": "许可的", - "yes": "否", + "licensed": "已授权", + "yes": "是", "no": "否", "sitesAdditional": "其他站点", "licenseKeys": "许可证密钥", - "sitestCountDecrease": "减少站点计数", - "sitestCountIncrease": "增加站点计数", + "sitestCountDecrease": "减少站点数量", + "sitestCountIncrease": "增加站点数量", "idpManage": "管理身份提供商", "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", @@ -723,12 +723,12 @@ "idp": "身份提供商", "idpSearch": "搜索身份提供者...", "idpAdd": "添加身份提供商", - "idpClientIdRequired": "客户端ID是必需的。", - "idpClientSecretRequired": "客户端密码是必需的。", - "idpErrorAuthUrlInvalid": "身份验证URL必须是有效的 URL。", - "idpErrorTokenUrlInvalid": "令牌URL必须是有效的 URL。", - "idpPathRequired": "标识路径是必需的。", - "idpScopeRequired": "范围是必需的。", + "idpClientIdRequired": "客户端ID 是必需的。", + "idpClientSecretRequired": "客户端密钥是必需的。", + "idpErrorAuthUrlInvalid": "身份验证URL 必须是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌URL 必须是有效的 URL。", + "idpPathRequired": "标识符路径是必需的。", + "idpScopeRequired": "授权范围是必需的。", "idpOidcDescription": "配置 OpenID 连接身份提供商", "idpCreatedDescription": "身份提供商创建成功", "idpCreate": "创建身份提供商", @@ -748,24 +748,24 @@ "idpClientSecret": "客户端密钥", "idpClientSecretDescription": "来自身份提供商的 OAuth2 客户端密钥", "idpAuthUrl": "授权 URL", - "idpAuthUrlDescription": "OAuth2 授权终点 URL", - "idpTokenUrl": "令牌网址", - "idpTokenUrlDescription": "OAuth2 令牌端点URL", - "idpOidcConfigureAlert": "重要信息", - "idpOidcConfigureAlertDescription": "在创建身份提供商后,您需要在身份提供商的设置中配置回调URL。 成功创建后将提供回调URL。", + "idpAuthUrlDescription": "OAuth2 授权端点的 URL", + "idpTokenUrl": "令牌 URL", + "idpTokenUrlDescription": "OAuth2 令牌端点的 URL", + "idpOidcConfigureAlert": "重要提示", + "idpOidcConfigureAlertDescription": "创建身份提供方后,您需要在其设置中配置回调 URL。回调 URL 会在创建成功后提供。", "idpToken": "令牌配置", - "idpTokenDescription": "配置如何从ID令牌中提取用户信息", + "idpTokenDescription": "配置如何从 ID 令牌中提取用户信息", "idpJmespathAbout": "关于 JMESPath", - "idpJmespathAboutDescription": "下面的路径使用 JMESPath 语法从ID标记中提取值。", - "idpJmespathAboutDescriptionLink": "了解更多关于 JMESPath", - "idpJmespathLabel": "标识路径", - "idpJmespathLabelDescription": "用户标识符的路径", - "idpJmespathEmailPathOptional": "电子邮件路径(可选)", - "idpJmespathEmailPathOptionalDescription": "用户的 ID 令牌电子邮件的路径", - "idpJmespathNamePathOptional": "名称路径(可选)", - "idpJmespathNamePathOptionalDescription": "用户名在ID令牌中的路径", - "idpOidcConfigureScopes": "范围", - "idpOidcConfigureScopesDescription": "要请求的 OAuth2 范围空间分隔列表", + "idpJmespathAboutDescription": "以下路径使用 JMESPath 语法从 ID 令牌中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息", + "idpJmespathLabel": "标识符路径", + "idpJmespathLabelDescription": "ID 令牌中用户标识符的路径", + "idpJmespathEmailPathOptional": "邮箱路径(可选)", + "idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径", + "idpJmespathNamePathOptional": "用户名路径(可选)", + "idpJmespathNamePathOptionalDescription": "ID 令牌中用户名的路径", + "idpOidcConfigureScopes": "作用域(Scopes)", + "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 请求作用域列表", "idpSubmit": "创建身份提供商", "orgPolicies": "组织策略", "idpSettings": "{idpName} 设置", @@ -789,13 +789,13 @@ "defaultMappingsRole": "默认角色映射", "defaultMappingsRoleDescription": "此表达式的结果必须返回组织中定义的角色名称作为字符串。", "defaultMappingsOrg": "默认组织映射", - "defaultMappingsOrgDescription": "此表达式必须返回 org ID或true 才能允许用户访问组织。", + "defaultMappingsOrgDescription": "此表达式必须返回 组织ID 或 true 才能允许用户访问组织。", "defaultMappingsSubmit": "保存默认映射", "orgPoliciesEdit": "编辑组织策略", "org": "组织", "orgSelect": "选择组织", "orgSearch": "搜索", - "orgNotFound": "找不到 org 。", + "orgNotFound": "找不到组织。", "roleMappingPathOptional": "角色映射路径(可选)", "orgMappingPathOptional": "组织映射路径(可选)", "orgPolicyUpdate": "更新策略", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "邦戈林徽标", + "pangolinLogoAlt": "Pangolin Logo", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -851,13 +851,13 @@ "otpEmail": "一次性密码 (OTP)", "otpEmailSubmit": "提交 OTP", "backToEmail": "回到电子邮件", - "noSupportKey": "服务器运行时没有支持者密钥。请考虑支持项目!", + "noSupportKey": "服务器当前未使用支持者密钥,欢迎支持本项目!", "accessDenied": "访问被拒绝", - "accessDeniedDescription": "您无权访问此资源。如果这是错误,请与管理员联系。", + "accessDeniedDescription": "当前账户无权访问此资源。如认为这是错误,请与管理员联系。", "accessTokenError": "检查访问令牌时出错", "accessGranted": "已授予访问", "accessUrlInvalid": "访问 URL 无效", - "accessGrantedDescription": "您已获准访问此资源。重定向您...", + "accessGrantedDescription": "您已获准访问此资源,正在为您跳转...", "accessUrlInvalidDescription": "此共享访问URL无效。请联系资源所有者获取新URL。", "tokenInvalid": "无效的令牌", "pincodeInvalid": "无效的代码", @@ -866,9 +866,9 @@ "passwordResetSuccess": "密码重置成功!返回登录...", "passwordReset": "重置密码", "passwordResetDescription": "按照步骤重置您的密码", - "passwordResetSent": "我们将发送一个密码重置代码到这个电子邮件地址。", - "passwordResetCode": "Reset Code", - "passwordResetCodeDescription": "请检查您的电子邮件以获取重置代码。", + "passwordResetSent": "我们将发送一个验证码到这个电子邮件地址。", + "passwordResetCode": "验证码", + "passwordResetCodeDescription": "请检查您的电子邮件以获取验证码。", "passwordNew": "新密码", "passwordNewConfirm": "确认新密码", "pincodeAuth": "验证器代码", @@ -898,7 +898,7 @@ "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", - "orgErrorCreate": "创建 org 时出错", + "orgErrorCreate": "创建组织时出错", "pageNotFound": "找不到页面", "pageNotFoundDescription": "哎呀!您正在查找的页面不存在。", "overview": "概览", @@ -923,13 +923,13 @@ "tagWarnDuplicate": "未添加重复标签 {tagText}", "supportKeyInvalid": "无效密钥", "supportKeyInvalidDescription": "您的支持者密钥无效。", - "supportKeyValid": "Valid Key", + "supportKeyValid": "有效的密钥", "supportKeyValidDescription": "您的支持者密钥已被验证。感谢您的支持!", "supportKeyErrorValidationDescription": "验证支持者密钥失败。", - "supportKey": "支持开发和通过一个潘戈林!", - "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展潘戈林。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", - "supportKeyPet": "你也会通过并与你自己的宠物Pangolin会面!", - "supportKeyPurchase": "付款通过 GitHub 处理。然后您可以检索您的密钥", + "supportKey": "支持开发和通过一个 Pangolin !", + "supportKeyDescription": "购买支持者钥匙,帮助我们继续为社区发展 Pangolin 。 您的贡献使我们能够投入更多的时间来维护和添加所有人的新功能。 我们永远不会用这个来支付墙上的功能。这与任何商业版是分开的。", + "supportKeyPet": "您还可以领养并见到属于自己的 Pangolin!", + "supportKeyPurchase": "付款通过 GitHub 进行处理,之后您可以在以下位置获取您的密钥:", "supportKeyPurchaseLink": "我们的网站", "supportKeyPurchase2": "并在这里兑换。", "supportKeyLearnMore": "了解更多。", @@ -944,15 +944,15 @@ "supportKeyRedeem": "兑换支持者密钥", "supportKeyHideSevenDays": "隐藏7天", "supportKeyEnter": "输入支持者密钥", - "supportKeyEnterDescription": "见到你自己的宠物Pangolin!", - "githubUsername": "GitHub Username", + "supportKeyEnterDescription": "见到你自己的 Pangolin!", + "githubUsername": "GitHub 用户名", "supportKeyInput": "支持者密钥", "supportKeyBuy": "购买支持者密钥", "logoutError": "注销错误", "signingAs": "登录为", "serverAdmin": "服务器管理员", - "otpEnable": "启用双因子", - "otpDisable": "禁用双因子", + "otpEnable": "启用双因子认证", + "otpDisable": "禁用双因子认证", "logout": "登出", "licenseTierProfessionalRequired": "需要专业版", "licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "设置资源粉码", "actionSetResourceEmailWhitelist": "设置资源电子邮件白名单", "actionGetResourceEmailWhitelist": "获取资源电子邮件白名单", - "actionCreateTarget": "Create Target", + "actionCreateTarget": "创建目标", "actionDeleteTarget": "删除目标", "actionGetTarget": "获取目标", "actionListTargets": "列表目标", - "actionUpdateTarget": "Update Target", + "actionUpdateTarget": "更新目标", "actionCreateRole": "创建角色", "actionDeleteRole": "删除角色", "actionGetRole": "获取角色", @@ -1002,7 +1002,7 @@ "actionListResourceRules": "列出资源规则", "actionUpdateResourceRule": "更新资源规则", "actionListOrgs": "列出组织", - "actionCheckOrgId": "检查 ID", + "actionCheckOrgId": "检查组织ID", "actionCreateOrg": "创建组织", "actionDeleteOrg": "删除组织", "actionListApiKeys": "列出API密钥", @@ -1013,15 +1013,15 @@ "actionCreateIdp": "创建IDP", "actionUpdateIdp": "更新IDP", "actionDeleteIdp": "删除IDP", - "actionListIdps": "列出国内流离失所者", + "actionListIdps": "列出IDP", "actionGetIdp": "获取IDP", - "actionCreateIdpOrg": "创建IDP Org 策略", - "actionDeleteIdpOrg": "删除IDP Org 策略", - "actionListIdpOrgs": "列出国内流离失所者组织", - "actionUpdateIdpOrg": "更新IDP Org", + "actionCreateIdpOrg": "创建 IDP组织策略", + "actionDeleteIdpOrg": "删除 IDP组织策略", + "actionListIdpOrgs": "列出 IDP组织", + "actionUpdateIdpOrg": "更新 IDP组织", "noneSelected": "未选择", "orgNotFound2": "未找到组织。", - "searchProgress": "搜索...", + "searchProgress": "搜索中...", "create": "创建", "orgs": "组织", "loginError": "登录时出错", @@ -1031,13 +1031,13 @@ "otpAuthSubmit": "提交代码", "idpContinue": "或者继续", "otpAuthBack": "返回登录", - "navbar": "Navigation Menu", + "navbar": "导航菜单", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", - "otpErrorEnableDescription": "启用2FA 时出错", - "otpSetupCheckCode": "请输入一个6位数字", + "otpErrorEnableDescription": "启用 2FA 时出错", + "otpSetupCheckCode": "请输入您的6位数字代码", "otpSetupCheckCodeRetry": "无效的代码。请重试。", "otpSetup": "启用两步验证", "otpSetupDescription": "用额外的保护层来保护您的帐户", @@ -1061,20 +1061,20 @@ "copyTextFailed": "复制文本失败: ", "copyTextClipboard": "复制到剪贴板", "inviteErrorInvalidConfirmation": "无效确认", - "passwordRequired": "密码是必需的", + "passwordRequired": "必须填写密码", "allowAll": "允许所有", "permissionsAllowAll": "允许所有权限", - "githubUsernameRequired": "GitHub 用户名是必需的", - "supportKeyRequired": "支持者密钥是必需的", - "passwordRequirementsChars": "密码必须至少 8 个字符", + "githubUsernameRequired": "必须填写 GitHub 用户名", + "supportKeyRequired": "必须填写支持者密钥", + "passwordRequirementsChars": "密码至少需要 8 个字符", "language": "语言", "verificationCodeRequired": "必须输入代码", "userErrorNoUpdate": "没有要更新的用户", "siteErrorNoUpdate": "没有要更新的站点", "resourceErrorNoUpdate": "没有可更新的资源", "authErrorNoUpdate": "没有要更新的身份验证信息", - "orgErrorNoUpdate": "没有要更新的 org", - "orgErrorNoProvided": "未提供 org", + "orgErrorNoUpdate": "没有要更新的组织", + "orgErrorNoProvided": "未提供组织", "apiKeysErrorNoUpdate": "没有要更新的 API 密钥", "sidebarOverview": "概览", "sidebarHome": "首页", @@ -1125,4 +1125,4 @@ "refreshContainersList": "刷新容器列表", "searching": "搜索中...", "noContainersFoundMatching": "No containers found matching \"{filter}\"." -} \ No newline at end of file +} From 31a41576d8607cb2907ecd67a01a12f57a2ef48a Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 11 Jun 2025 09:37:21 -0400 Subject: [PATCH 104/105] Fix translation & add space --- messages/de-DE.json | 4 ++-- messages/en-US.json | 4 ++-- messages/es-ES.json | 4 ++-- messages/fr-FR.json | 4 ++-- messages/it-IT.json | 4 ++-- messages/nl-NL.json | 4 ++-- messages/pl-PL.json | 4 ++-- messages/pt-PT.json | 4 ++-- messages/tr-TR.json | 4 ++-- messages/zh-CN.json | 4 ++-- src/app/[orgId]/settings/sites/[niceId]/general/page.tsx | 3 +-- 11 files changed, 21 insertions(+), 22 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 629ce825..2b908f18 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Identitätsanbieter", "sidebarLicense": "Lizenz", "enableDockerSocket": "Docker Socket aktivieren", - "enableDockerSocketDescription": "Aktiviere Docker Socket-Erkennung, um Containerinformationen zu füllen, nützlich für Ressourcenziele.", - "enableDockerSocketLink": "Aktiviere Docker Socket-Erkennung, um Containerinformationen zu füllen, nützlich für Ressourcenziele.", + "enableDockerSocketDescription": "Docker Socket-Erkennung aktivieren, um Container-Informationen zu befüllen. Socket-Pfad muss Newt bereitgestellt werden.", + "enableDockerSocketLink": "Mehr erfahren", "viewDockerContainers": "Docker Container anzeigen", "containersIn": "Container in {siteName}", "selectContainerDescription": "Wählen Sie einen Container, der als Hostname für dieses Ziel verwendet werden soll. Klicken Sie auf einen Port, um einen Port zu verwenden.", diff --git a/messages/en-US.json b/messages/en-US.json index 8f518e02..87be4dcf 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Identity Providers", "sidebarLicense": "License", "enableDockerSocket": "Enable Docker Socket", - "enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information, useful in resource targets.", - "enableDockerSocketLink": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information. Socket path must be provided to Newt.", + "enableDockerSocketLink": "Learn More", "viewDockerContainers": "View Docker Containers", "containersIn": "Containers in {siteName}", "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", diff --git a/messages/es-ES.json b/messages/es-ES.json index da914d5b..4c720c58 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Proveedores de identidad", "sidebarLicense": "Licencia", "enableDockerSocket": "Habilitar conector Docker", - "enableDockerSocketDescription": "Habilita el descubrimiento del conector Docker para rellenar la información del contenedor, útil en los objetivos de recursos.", - "enableDockerSocketLink": "Habilita el descubrimiento del conector Docker para rellenar la información del contenedor, útil en los objetivos de recursos.", + "enableDockerSocketDescription": "Habilitar el descubrimiento de Docker Socket para completar la información del contenedor. La ruta del socket debe proporcionarse a Newt.", + "enableDockerSocketLink": "Saber más", "viewDockerContainers": "Ver contenedores Docker", "containersIn": "Contenedores en {siteName}", "selectContainerDescription": "Seleccione cualquier contenedor para usar como nombre de host para este objetivo. Haga clic en un puerto para usar un puerto.", diff --git a/messages/fr-FR.json b/messages/fr-FR.json index ccfd5bf2..97fbb89c 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Fournisseurs d'identité", "sidebarLicense": "Licence", "enableDockerSocket": "Activer Docker Socket", - "enableDockerSocketDescription": "Activer la découverte de sockets Docker pour le remplissage des informations de conteneur, utile dans les cibles de ressources.", - "enableDockerSocketLink": "Activer la découverte de sockets Docker pour le remplissage des informations de conteneur, utile dans les cibles de ressources.", + "enableDockerSocketDescription": "Activer la découverte Docker Socket pour remplir les informations du conteneur. Le chemin du socket doit être fourni à Newt.", + "enableDockerSocketLink": "En savoir plus", "viewDockerContainers": "Voir les conteneurs Docker", "containersIn": "Conteneurs en {siteName}", "selectContainerDescription": "Sélectionnez n'importe quel conteneur à utiliser comme nom d'hôte pour cette cible. Cliquez sur un port pour utiliser un port.", diff --git a/messages/it-IT.json b/messages/it-IT.json index 12a58713..258b2bb1 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Fornitori Di Identità", "sidebarLicense": "Licenza", "enableDockerSocket": "Abilita Docker Socket", - "enableDockerSocketDescription": "Abilita la scoperta del Docker Socket per la popolazione delle informazioni del contenitore, utili negli obiettivi delle risorse.", - "enableDockerSocketLink": "Abilita la scoperta del Docker Socket per la popolazione delle informazioni del contenitore, utili negli obiettivi delle risorse.", + "enableDockerSocketDescription": "Abilita il rilevamento Docker Socket per popolare le informazioni del contenitore. Il percorso del socket deve essere fornito a Newt.", + "enableDockerSocketLink": "Scopri di più", "viewDockerContainers": "Visualizza Contenitori Docker", "containersIn": "Contenitori in {siteName}", "selectContainerDescription": "Seleziona qualsiasi contenitore da usare come hostname per questo obiettivo. Fai clic su una porta per usare una porta.", diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 46fb6aa8..a6c4ae9f 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Identiteit aanbieders", "sidebarLicense": "Licentie", "enableDockerSocket": "Docker Socket inschakelen", - "enableDockerSocketDescription": "Schakel Docker Socket ontdekking in voor het vullen van containerinformatie, handig in brondoelen.", - "enableDockerSocketLink": "Schakel Docker Socket ontdekking in voor het vullen van containerinformatie, handig in brondoelen.", + "enableDockerSocketDescription": "Docker Socket-ontdekking inschakelen voor het invullen van containerinformatie. Socket-pad moet aan Newt worden verstrekt.", + "enableDockerSocketLink": "Meer informatie", "viewDockerContainers": "Bekijk Docker containers", "containersIn": "Containers in {siteName}", "selectContainerDescription": "Selecteer een container om als hostnaam voor dit doel te gebruiken. Klik op een poort om een poort te gebruiken.", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 713f2749..d49ec82a 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Dostawcy tożsamości", "sidebarLicense": "Licencja", "enableDockerSocket": "Włącz gniazdo dokera", - "enableDockerSocketDescription": "Włącz wyszukiwanie gniazda dokującego w celu zbierania informacji o pojemniku, przydatne w celach zasobowych.", - "enableDockerSocketLink": "Włącz wyszukiwanie gniazda dokującego w celu zbierania informacji o pojemniku, przydatne w celach zasobowych.", + "enableDockerSocketDescription": "Włącz wykrywanie Docker Socket w celu wypełnienia informacji o kontenerach. Ścieżka gniazda musi być dostarczona do Newt.", + "enableDockerSocketLink": "Dowiedz się więcej", "viewDockerContainers": "Zobacz kontenery dokujące", "containersIn": "Pojemniki w {siteName}", "selectContainerDescription": "Wybierz dowolny kontener do użycia jako nazwa hosta dla tego celu. Kliknij port, aby użyć portu.", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 261bbdb7..05d5c1f5 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "enableDockerSocket": "Habilitar Docker Socket", - "enableDockerSocketDescription": "Habilitar a descoberta de Socket Docker para preencher informações de contêineres, útil em alvos de recursos.", - "enableDockerSocketLink": "Habilitar a descoberta de Socket Docker para preencher informações de contêineres, útil em alvos de recursos.", + "enableDockerSocketDescription": "Ativar a descoberta do Docker Socket para preencher informações do contêiner. O caminho do socket deve ser fornecido ao Newt.", + "enableDockerSocketLink": "Saiba mais", "viewDockerContainers": "Ver contêineres Docker", "containersIn": "Contêineres em {siteName}", "selectContainerDescription": "Selecione qualquer contêiner para usar como hostname para este alvo. Clique em uma porta para usar uma porta.", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index a986f0d1..ba53f43d 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "Identity Providers", "sidebarLicense": "License", "enableDockerSocket": "Enable Docker Socket", - "enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information, useful in resource targets.", - "enableDockerSocketLink": "Enable Docker Socket discovery for populating container information, useful in resource targets.", + "enableDockerSocketDescription": "Konteyner bilgilerini doldurmak için Docker Socket keşfini etkinleştirin. Socket yolu Newt'e sağlanmalıdır.", + "enableDockerSocketLink": "Daha fazla bilgi", "viewDockerContainers": "View Docker Containers", "containersIn": "Containers in {siteName}", "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 0b4e73fb..6e77af4c 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1091,8 +1091,8 @@ "sidebarIdentityProviders": "身份提供商", "sidebarLicense": "证书", "enableDockerSocket": "启用停靠套接字", - "enableDockerSocketDescription": "启用 Docker Socket 发现用于散布容器信息,在资源目标中有用。", - "enableDockerSocketLink": "启用 Docker Socket 发现用于散布容器信息,在资源目标中有用。", + "enableDockerSocketDescription": "启用 Docker Socket 发现以填充容器信息。必须向 Newt 提供 Socket 路径。", + "enableDockerSocketLink": "了解更多", "viewDockerContainers": "查看停靠容器", "containersIn": "Containers in {siteName}", "selectContainerDescription": "选择任何容器作为目标的主机名。点击端口使用端口。", diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index e951971e..7b97f99f 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -155,7 +155,7 @@ export default function GeneralPage() { {t( "enableDockerSocketDescription" - )} + )}{" "} - {" "} {t( "enableDockerSocketLink" )} From 8aa95db9bca070d58e243eab552213a143bfc80c Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 11 Jun 2025 10:00:38 -0400 Subject: [PATCH 105/105] Adjust button and add profile translations --- messages/de-DE.json | 6 +++++- messages/en-US.json | 6 +++++- messages/es-ES.json | 6 +++++- messages/fr-FR.json | 6 +++++- messages/it-IT.json | 6 +++++- messages/nl-NL.json | 6 +++++- messages/pl-PL.json | 6 +++++- messages/pt-PT.json | 6 +++++- messages/tr-TR.json | 6 +++++- messages/zh-CN.json | 16 ++++++++++------ src/components/LocaleSwitcherSelect.tsx | 6 ++++-- src/components/ProfileIcon.tsx | 6 ++---- 12 files changed, 61 insertions(+), 21 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 2b908f18..e48a080b 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Spalten umschalten", "refreshContainersList": "Container-Liste aktualisieren", "searching": "Suche...", - "noContainersFoundMatching": "Keine Container gefunden mit \"{filter}\"." + "noContainersFoundMatching": "Keine Container gefunden mit \"{filter}\".", + "light": "hell", + "dark": "dunkel", + "system": "system", + "theme": "Design" } diff --git a/messages/en-US.json b/messages/en-US.json index 87be4dcf..6873d7f3 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Toggle Columns", "refreshContainersList": "Refresh containers list", "searching": "Searching...", - "noContainersFoundMatching": "No containers found matching \"{filter}\"." + "noContainersFoundMatching": "No containers found matching \"{filter}\".", + "light": "light", + "dark": "dark", + "system": "system", + "theme": "Theme" } \ No newline at end of file diff --git a/messages/es-ES.json b/messages/es-ES.json index 4c720c58..23ae5310 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Cambiar Columnas", "refreshContainersList": "Actualizar lista de contenedores", "searching": "Buscando...", - "noContainersFoundMatching": "No se han encontrado contenedores que coincidan con \"{filter}\"." + "noContainersFoundMatching": "No se han encontrado contenedores que coincidan con \"{filter}\".", + "light": "claro", + "dark": "oscuro", + "system": "sistema", + "theme": "Tema" } \ No newline at end of file diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 97fbb89c..e1a7dbd9 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Activer/désactiver les colonnes", "refreshContainersList": "Rafraîchir la liste des conteneurs", "searching": "Recherche en cours...", - "noContainersFoundMatching": "Aucun conteneur correspondant à \"{filter}\"." + "noContainersFoundMatching": "Aucun conteneur correspondant à \"{filter}\".", + "light": "clair", + "dark": "sombre", + "system": "système", + "theme": "Thème" } \ No newline at end of file diff --git a/messages/it-IT.json b/messages/it-IT.json index 258b2bb1..92ea2ee3 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Attiva/Disattiva Colonne", "refreshContainersList": "Aggiorna elenco contenitori", "searching": "Ricerca...", - "noContainersFoundMatching": "Nessun contenitore trovato corrispondente \"{filter}\"." + "noContainersFoundMatching": "Nessun contenitore trovato corrispondente \"{filter}\".", + "light": "chiaro", + "dark": "scuro", + "system": "sistema", + "theme": "Tema" } \ No newline at end of file diff --git a/messages/nl-NL.json b/messages/nl-NL.json index a6c4ae9f..e656544b 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Kolommen omschakelen", "refreshContainersList": "Vernieuw containers lijst", "searching": "Zoeken...", - "noContainersFoundMatching": "Geen containers gevonden die overeenkomen met \"{filter}\"." + "noContainersFoundMatching": "Geen containers gevonden die overeenkomen met \"{filter}\".", + "light": "licht", + "dark": "donker", + "system": "systeem", + "theme": "Thema" } \ No newline at end of file diff --git a/messages/pl-PL.json b/messages/pl-PL.json index d49ec82a..fcbe1aa6 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Przełącz kolumny", "refreshContainersList": "Odśwież listę kontenerów", "searching": "Wyszukiwanie...", - "noContainersFoundMatching": "Nie znaleziono kontenerów pasujących do \"{filter}\"." + "noContainersFoundMatching": "Nie znaleziono kontenerów pasujących do \"{filter}\".", + "light": "jasny", + "dark": "ciemny", + "system": "system", + "theme": "Motyw" } \ No newline at end of file diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 05d5c1f5..15089ce7 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Alternar Colunas", "refreshContainersList": "Atualizar lista de contêineres", "searching": "Buscando...", - "noContainersFoundMatching": "Nenhum recipiente encontrado \"{filter}\"." + "noContainersFoundMatching": "Nenhum recipiente encontrado \"{filter}\".", + "light": "claro", + "dark": "escuro", + "system": "sistema", + "theme": "Tema" } \ No newline at end of file diff --git a/messages/tr-TR.json b/messages/tr-TR.json index ba53f43d..2431373d 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1124,5 +1124,9 @@ "toggleColumns": "Toggle Columns", "refreshContainersList": "Refresh containers list", "searching": "Searching...", - "noContainersFoundMatching": "No containers found matching \"{filter}\"." + "noContainersFoundMatching": "No containers found matching \"{filter}\".", + "light": "açık", + "dark": "koyu", + "system": "sistem", + "theme": "Tema" } \ No newline at end of file diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 6e77af4c..ee6957de 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1100,21 +1100,21 @@ "containerImage": "图片", "containerState": "状态", "containerNetworks": "网络", - "containerHostnameIp": "Hostname/IP", + "containerHostnameIp": "主机名/IP", "containerLabels": "标签", - "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsCount": "{count} 个标签", "containerLabelsTitle": "容器标签", "containerLabelEmpty": "", "containerPorts": "端口", - "containerPortsMore": "+{count} more", + "containerPortsMore": "+{count} 更多", "containerActions": "行动", "select": "选择", "noContainersMatchingFilters": "没有找到匹配当前过滤器的容器。", "showContainersWithoutPorts": "显示没有端口的容器", "showStoppedContainers": "显示已停止的容器", "noContainersFound": "未找到容器。请确保Docker容器正在运行。", - "searchContainersPlaceholder": "Search across {count} containers...", - "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "searchContainersPlaceholder": "在 {count} 个容器中搜索...", + "searchResultsCount": "{count} 个结果", "filters": "筛选器", "filterOptions": "过滤器选项", "filterPorts": "端口", @@ -1124,5 +1124,9 @@ "toggleColumns": "切换列", "refreshContainersList": "刷新容器列表", "searching": "搜索中...", - "noContainersFoundMatching": "No containers found matching \"{filter}\"." + "noContainersFoundMatching": "未找到与 \"{filter}\" 匹配的容器。", + "light": "浅色", + "dark": "深色", + "system": "系统", + "theme": "主题" } diff --git a/src/components/LocaleSwitcherSelect.tsx b/src/components/LocaleSwitcherSelect.tsx index 1d3f448f..53c25d8f 100644 --- a/src/components/LocaleSwitcherSelect.tsx +++ b/src/components/LocaleSwitcherSelect.tsx @@ -41,13 +41,15 @@ export default function LocaleSwitcherSelect({ diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx index c09447fb..eab2f51d 100644 --- a/src/components/ProfileIcon.tsx +++ b/src/components/ProfileIcon.tsx @@ -133,7 +133,7 @@ export default function ProfileIcon() { )} - Theme + {t("theme")} {(["light", "dark", "system"] as const).map( (themeOption) => ( )} - {themeOption} + {t(themeOption)} {userTheme === themeOption && ( @@ -163,9 +163,7 @@ export default function ProfileIcon() { ) )} -
    -
    logout()}> {/* */}