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')}