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

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

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

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

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

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

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

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

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

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

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

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

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

diff --git a/src/app/auth/verify-email/VerifyEmailForm.tsx b/src/app/auth/verify-email/VerifyEmailForm.tsx index 7d68263e..235fc70c 100644 --- a/src/app/auth/verify-email/VerifyEmailForm.tsx +++ b/src/app/auth/verify-email/VerifyEmailForm.tsx @@ -37,6 +37,7 @@ import { formatAxiosError } from "@app/lib/api";; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { cleanRedirect } from "@app/lib/cleanRedirect"; +import { useTranslations } from "next-intl"; const FormSchema = z.object({ email: z.string().email({ message: "Invalid email address" }), @@ -71,6 +72,8 @@ export default function VerifyEmailForm({ }, }); + const t = useTranslations(); + async function onSubmit(data: z.infer) { setIsSubmitting(true); @@ -114,8 +117,7 @@ export default function VerifyEmailForm({ toast({ variant: "default", title: "Verification code resent", - description: - "We've resent a verification code to your email address. Please check your inbox.", + description: "We've resent a verification code to your email address. Please check your inbox.", }); } @@ -126,9 +128,9 @@ export default function VerifyEmailForm({
- Verify Email + {t('emailVerify')} - Enter the verification code sent to your email address. + {t('emailVerifyDescription')} @@ -142,7 +144,7 @@ export default function VerifyEmailForm({ name="email" render={({ field }) => ( - Email + {t('email')} ( - Verification Code + {t('verificationCode')}
- We sent a verification code to your - email address. + {t('verificationCodeEmailSent')} )} @@ -226,7 +227,7 @@ export default function VerifyEmailForm({ {isSubmitting && ( )} - Submit + {t('emailVerifySubmit')} @@ -241,8 +242,8 @@ export default function VerifyEmailForm({ disabled={isResending} > {isResending - ? "Resending..." - : "Didn't receive a code? Click here to resend"} + ? t('emailVerifyResendProgress') + : t('emailVerifyResend')}