Merge pull request #17 from vlalx/i18n_invitations

I18n invitations
This commit is contained in:
Marvin
2025-05-10 16:17:40 +02:00
committed by GitHub
10 changed files with 273 additions and 61 deletions

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.", "orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.",
"orgDeleted": "Organisation gelöscht", "orgDeleted": "Organisation gelöscht",
"orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.", "orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.",
"orgMissing": "Organisations-ID fehlt",
"orgMissingMessage": "Einladung kann ohne Organisations-ID nicht neu generiert werden.",
"accessUsersManage": "Benutzer verwalten", "accessUsersManage": "Benutzer verwalten",
"accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten", "accessUsersDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf deine Organisation zu verwalten",
"accessUsersSearch": "Benutzer suchen...", "accessUsersSearch": "Benutzer suchen...",
@@ -248,6 +250,7 @@
"weeks": "Wochen", "weeks": "Wochen",
"months": "Monate", "months": "Monate",
"years": "Jahre", "years": "Jahre",
"day": "{count, plural, =1 {# Tag} other {# Tage}}",
"apiKeysTitle": "API-Schlüssel Information", "apiKeysTitle": "API-Schlüssel Information",
"apiKeysNameMin": "Name muss mindestens 2 Zeichen lang sein.", "apiKeysNameMin": "Name muss mindestens 2 Zeichen lang sein.",
"apiKeysNameMax": "Name darf nicht länger als 255 Zeichen sein.", "apiKeysNameMax": "Name darf nicht länger als 255 Zeichen sein.",
@@ -351,5 +354,33 @@
"total": "Gesamt", "total": "Gesamt",
"licenseContinuePayment": "Weiter zur Zahlung", "licenseContinuePayment": "Weiter zur Zahlung",
"pricingPage": "Preisseite", "pricingPage": "Preisseite",
"licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die " "licensePricingPage": "Für die aktuellsten Preise und Rabatte, besuchen Sie bitte die ",
"invite": "Einladungen",
"inviteRegenerate": "Einladung neu generieren",
"inviteRegenerateDescription": "Vorherige Einladung widerrufen und neue erstellen",
"inviteRemove": "Einladung entfernen",
"inviteRemoveError": "Einladung konnte nicht entfernt werden",
"inviteRemoveErrorDescription": "Beim Entfernen der Einladung ist ein Fehler aufgetreten.",
"inviteRemoved": "Einladung entfernt",
"inviteRemovedDescription": "Die Einladung für {email} wurde entfernt.",
"inviteQuestionRemove": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} entfernen möchten?",
"inviteMessageRemove": "Sobald entfernt, wird diese Einladung nicht mehr gültig sein. Sie können den Benutzer später jederzeit erneut einladen.",
"inviteMessageConfirm": "Bitte geben Sie zur Bestätigung die E-Mail-Adresse der Einladung unten ein.",
"inviteQuestionRegenerate": "Sind Sie sicher, dass Sie die Einladung{email, plural, ='' {}, other { für #}} neu generieren möchten? Dies wird die vorherige Einladung widerrufen.",
"inviteRemoveConfirm": "Entfernen der Einladung bestätigen",
"inviteRegenerated": "Einladung neu generiert",
"inviteSent": "Eine neue Einladung wurde an {email} gesendet.",
"inviteSentEmail": "E-Mail-Benachrichtigung an den Benutzer senden",
"inviteGenerate": "Eine neue Einladung wurde für {email} generiert.",
"inviteDuplicateError": "Doppelte Einladung",
"inviteDuplicateErrorDescription": "Eine Einladung für diesen Benutzer existiert bereits.",
"inviteRateLimitError": "Ratenlimit überschritten",
"inviteRateLimitErrorDescription": "Sie haben das Limit von 3 Neugenerierungen pro Stunde überschritten. Bitte versuchen Sie es später erneut.",
"inviteRegenerateError": "Fehler beim Neugenerieren der Einladung",
"inviteRegenerateErrorDescription": "Beim Neugenerieren der Einladung ist ein Fehler aufgetreten.",
"inviteValidityPeriod": "Gültigkeitszeitraum",
"inviteValidityPeriodSelect": "Gültigkeitszeitraum auswählen",
"inviteRegenerateMessage": "Die Einladung wurde neu generiert. Der Benutzer muss den untenstehenden Link aufrufen, um die Einladung anzunehmen.",
"inviteRegenerateButton": "Neu generieren",
"expiresAt": "Läuft ab am"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "An error occurred while deleting the organization.", "orgErrorDeleteMessage": "An error occurred while deleting the organization.",
"orgDeleted": "Organization deleted", "orgDeleted": "Organization deleted",
"orgDeletedMessage": "The organization and its data has been deleted.", "orgDeletedMessage": "The organization and its data has been deleted.",
"orgMissing": "Organization ID Missing",
"orgMissingMessage": "Unable to regenerate invitation without an organization ID.",
"accessUsersManage": "Manage Users", "accessUsersManage": "Manage Users",
"accessUsersDescription": "Invite users and add them to roles to manage access to your organization", "accessUsersDescription": "Invite users and add them to roles to manage access to your organization",
"accessUsersSearch": "Search users...", "accessUsersSearch": "Search users...",
@@ -248,6 +250,7 @@
"weeks": "Weeks", "weeks": "Weeks",
"months": "Months", "months": "Months",
"years": "Years", "years": "Years",
"day": "{count, plural, =1 {# day} other {# days}}",
"apiKeysTitle": "API Key Information", "apiKeysTitle": "API Key Information",
"apiKeysNameMin": "Name must be at least 2 characters.", "apiKeysNameMin": "Name must be at least 2 characters.",
"apiKeysNameMax": "Name must not be longer than 255 characters.", "apiKeysNameMax": "Name must not be longer than 255 characters.",
@@ -351,5 +354,33 @@
"total": "Total", "total": "Total",
"licenseContinuePayment": "Continue to Payment", "licenseContinuePayment": "Continue to Payment",
"pricingPage": "pricing page", "pricingPage": "pricing page",
"licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ",
"invite": "Invitations",
"inviteRegenerate": "Regenerate Invitation",
"inviteRegenerateDescription": "Revoke previous invitation and create a new one",
"inviteRemove": "Remove Invitation",
"inviteRemoveError": "Failed to remove invitation",
"inviteRemoveErrorDescription": "An error occurred while removing the invitation.",
"inviteRemoved": "Invitation removed",
"inviteRemovedDescription": "The invitation for {email} has been removed.",
"inviteQuestionRemove": "Are you sure you want to remove the invitation{email, plural, ='' {}, other { for #}}?",
"inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.",
"inviteMessageConfirm": "To confirm, please type the email address of the invitation below.",
"inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for{email, plural, ='' {}, other { for #}}? This will revoke the previous invitation.",
"inviteRemoveConfirm": "Confirm Remove Invitation",
"inviteRegenerated": "Invitation Regenerated",
"inviteSent": "A new invitation has been sent to {email}.",
"inviteSentEmail": "Send email notification to the user",
"inviteGenerate": "A new invitation has been generated for {email}.",
"inviteDuplicateError": "Duplicate Invite",
"inviteDuplicateErrorDescription": "An invitation for this user already exists.",
"inviteRateLimitError": "Rate Limit Exceeded",
"inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.",
"inviteRegenerateError": "Failed to Regenerate Invitation",
"inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.",
"inviteValidityPeriod": "Validity Period",
"inviteValidityPeriodSelect": "Select validity period",
"inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.",
"inviteRegenerateButton": "Regenerate",
"expiresAt": "Expires At"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "Une erreur s'est produite lors de la suppression de l'organisation.", "orgErrorDeleteMessage": "Une erreur s'est produite lors de la suppression de l'organisation.",
"orgDeleted": "Organisation supprimée", "orgDeleted": "Organisation supprimée",
"orgDeletedMessage": "L'organisation et ses données ont été supprimées.", "orgDeletedMessage": "L'organisation et ses données ont été supprimées.",
"orgMissing": "ID d'organisation manquant",
"orgMissingMessage": "Impossible de régénérer l'invitation sans un ID d'organisation.",
"accessUsersManage": "Gérer les utilisateurs", "accessUsersManage": "Gérer les utilisateurs",
"accessUsersDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à votre organisation", "accessUsersDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à votre organisation",
"accessUsersSearch": "Rechercher des utilisateurs...", "accessUsersSearch": "Rechercher des utilisateurs...",
@@ -248,6 +250,7 @@
"weeks": "Semaines", "weeks": "Semaines",
"months": "Mois", "months": "Mois",
"years": "Années", "years": "Années",
"day": "{count, plural, =1 {# jour} other {# jours}}",
"apiKeysTitle": "Informations sur la clé API", "apiKeysTitle": "Informations sur la clé API",
"apiKeysNameMin": "Le nom doit comporter au moins 2 caractères.", "apiKeysNameMin": "Le nom doit comporter au moins 2 caractères.",
"apiKeysNameMax": "Le nom ne doit pas dépasser 255 caractères.", "apiKeysNameMax": "Le nom ne doit pas dépasser 255 caractères.",
@@ -351,5 +354,33 @@
"total": "Total", "total": "Total",
"licenseContinuePayment": "Continuer vers le paiement", "licenseContinuePayment": "Continuer vers le paiement",
"pricingPage": "page de tarification", "pricingPage": "page de tarification",
"licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le " "licensePricingPage": "Pour les prix et les remises les plus récentes, veuillez visiter le ",
"invite": "Invitations",
"inviteRegenerate": "Régénérer l'invitation",
"inviteRegenerateDescription": "Révoquer l'invitation précédente et en créer une nouvelle",
"inviteRemove": "Supprimer l'invitation",
"inviteRemoveError": "Échec de la suppression de l'invitation",
"inviteRemoveErrorDescription": "Une erreur s'est produite lors de la suppression de l'invitation.",
"inviteRemoved": "Invitation supprimée",
"inviteRemovedDescription": "L'invitation pour {email} a été supprimée.",
"inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation{email, plural, ='' {}, other { pour #}} ?",
"inviteMessageRemove": "Une fois supprimée, cette invitation ne sera plus valide. Vous pourrez toujours réinviter l'utilisateur plus tard.",
"inviteMessageConfirm": "Pour confirmer, veuillez saisir l'adresse e-mail de l'invitation ci-dessous.",
"inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation{email, plural, ='' {}, other { pour #}} ? Cela révoquera l'invitation précédente.",
"inviteRemoveConfirm": "Confirmer la suppression de l'invitation",
"inviteRegenerated": "Invitation régénérée",
"inviteSent": "Une nouvelle invitation a été envoyée à {email}.",
"inviteSentEmail": "Envoyer une notification par e-mail à l'utilisateur",
"inviteGenerate": "Une nouvelle invitation a été générée pour {email}.",
"inviteDuplicateError": "Invitation en double",
"inviteDuplicateErrorDescription": "Une invitation pour cet utilisateur existe déjà.",
"inviteRateLimitError": "Limite de taux dépassée",
"inviteRateLimitErrorDescription": "Vous avez dépassé la limite de 3 régénérations par heure. Veuillez réessayer plus tard.",
"inviteRegenerateError": "Échec de la régénération de l'invitation",
"inviteRegenerateErrorDescription": "Une erreur s'est produite lors de la régénération de l'invitation.",
"inviteValidityPeriod": "Période de validité",
"inviteValidityPeriodSelect": "Sélectionner la période de validité",
"inviteRegenerateMessage": "L'invitation a été régénérée. L'utilisateur doit accéder au lien ci-dessous pour accepter l'invitation.",
"inviteRegenerateButton": "Régénérer",
"expiresAt": "Expire le"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione dell'organizzazione.", "orgErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione dell'organizzazione.",
"orgDeleted": "Organizzazione eliminata", "orgDeleted": "Organizzazione eliminata",
"orgDeletedMessage": "L'organizzazione e i suoi dati sono stati eliminati.", "orgDeletedMessage": "L'organizzazione e i suoi dati sono stati eliminati.",
"orgMissing": "ID Organizzazione Mancante",
"orgMissingMessage": "Impossibile rigenerare l'invito senza un ID organizzazione.",
"accessUsersManage": "Gestisci Utenti", "accessUsersManage": "Gestisci Utenti",
"accessUsersDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso alla tua organizzazione", "accessUsersDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso alla tua organizzazione",
"accessUsersSearch": "Cerca utenti...", "accessUsersSearch": "Cerca utenti...",
@@ -248,6 +250,7 @@
"weeks": "Settimane", "weeks": "Settimane",
"months": "Mesi", "months": "Mesi",
"years": "Anni", "years": "Anni",
"day": "{count, plural, =1 {# giorno} other {# giorni}}",
"apiKeysTitle": "Informazioni Chiave API", "apiKeysTitle": "Informazioni Chiave API",
"apiKeysNameMin": "Il nome deve contenere almeno 2 caratteri.", "apiKeysNameMin": "Il nome deve contenere almeno 2 caratteri.",
"apiKeysNameMax": "Il nome non deve essere più lungo di 255 caratteri.", "apiKeysNameMax": "Il nome non deve essere più lungo di 255 caratteri.",
@@ -351,5 +354,33 @@
"total": "Totale", "total": "Totale",
"licenseContinuePayment": "Continua al pagamento", "licenseContinuePayment": "Continua al pagamento",
"pricingPage": "pagina prezzi", "pricingPage": "pagina prezzi",
"licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il " "licensePricingPage": "Per i prezzi e gli sconti più aggiornati, visita il ",
"invite": "Inviti",
"inviteRegenerate": "Rigenera Invito",
"inviteRegenerateDescription": "Revoca l'invito precedente e creane uno nuovo",
"inviteRemove": "Rimuovi Invito",
"inviteRemoveError": "Impossibile rimuovere l'invito",
"inviteRemoveErrorDescription": "Si è verificato un errore durante la rimozione dell'invito.",
"inviteRemoved": "Invito rimosso",
"inviteRemovedDescription": "L'invito per {email} è stato rimosso.",
"inviteQuestionRemove": "Sei sicuro di voler rimuovere l'invito{email, plural, ='' {}, other { per #}}?",
"inviteMessageRemove": "Una volta rimosso, questo invito non sarà più valido. Puoi sempre reinvitare l'utente in seguito.",
"inviteMessageConfirm": "Per confermare, digita l'indirizzo email dell'invito qui sotto.",
"inviteQuestionRegenerate": "Sei sicuro di voler rigenerare l'invito{email, plural, ='' {}, other { per #}}? Questo revocherà l'invito precedente.",
"inviteRemoveConfirm": "Conferma Rimozione Invito",
"inviteRegenerated": "Invito Rigenerato",
"inviteSent": "Un nuovo invito è stato inviato a {email}.",
"inviteSentEmail": "Invia notifica email all'utente",
"inviteGenerate": "Un nuovo invito è stato generato per {email}.",
"inviteDuplicateError": "Invito Duplicato",
"inviteDuplicateErrorDescription": "Esiste già un invito per questo utente.",
"inviteRateLimitError": "Limite di Frequenza Superato",
"inviteRateLimitErrorDescription": "Hai superato il limite di 3 rigenerazioni per ora. Riprova più tardi.",
"inviteRegenerateError": "Impossibile Rigenerare l'Invito",
"inviteRegenerateErrorDescription": "Si è verificato un errore durante la rigenerazione dell'invito.",
"inviteValidityPeriod": "Periodo di Validità",
"inviteValidityPeriodSelect": "Seleziona periodo di validità",
"inviteRegenerateMessage": "L'invito è stato rigenerato. L'utente deve accedere al link qui sotto per accettare l'invito.",
"inviteRegenerateButton": "Rigenera",
"expiresAt": "Scade Il"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "Wystąpił błąd podczas usuwania organizacji.", "orgErrorDeleteMessage": "Wystąpił błąd podczas usuwania organizacji.",
"orgDeleted": "Organizacja usunięta", "orgDeleted": "Organizacja usunięta",
"orgDeletedMessage": "Organizacja i jej dane zostały usunięte.", "orgDeletedMessage": "Organizacja i jej dane zostały usunięte.",
"orgMissing": "Brak ID organizacji",
"orgMissingMessage": "Nie można ponownie wygenerować zaproszenia bez ID organizacji.",
"accessUsersManage": "Zarządzaj użytkownikami", "accessUsersManage": "Zarządzaj użytkownikami",
"accessUsersDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do Twojej organizacji", "accessUsersDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do Twojej organizacji",
"accessUsersSearch": "Szukaj użytkowników...", "accessUsersSearch": "Szukaj użytkowników...",
@@ -248,6 +250,7 @@
"weeks": "Tygodnie", "weeks": "Tygodnie",
"months": "Miesiące", "months": "Miesiące",
"years": "Lata", "years": "Lata",
"day": "{count, plural, =1 {# dzień} other {# dni}}",
"apiKeysTitle": "Informacje o kluczu API", "apiKeysTitle": "Informacje o kluczu API",
"apiKeysNameMin": "Nazwa musi mieć co najmniej 2 znaki.", "apiKeysNameMin": "Nazwa musi mieć co najmniej 2 znaki.",
"apiKeysNameMax": "Nazwa nie może być dłuższa niż 255 znaków.", "apiKeysNameMax": "Nazwa nie może być dłuższa niż 255 znaków.",
@@ -351,5 +354,33 @@
"total": "Łącznie", "total": "Łącznie",
"licenseContinuePayment": "Przejdź do płatności", "licenseContinuePayment": "Przejdź do płatności",
"pricingPage": "strona cenowa", "pricingPage": "strona cenowa",
"licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź " "licensePricingPage": "Aby uzyskać najnowsze ceny i rabaty, odwiedź ",
"invite": "Zaproszenia",
"inviteRegenerate": "Wygeneruj ponownie zaproszenie",
"inviteRegenerateDescription": "Unieważnij poprzednie zaproszenie i utwórz nowe",
"inviteRemove": "Usuń zaproszenie",
"inviteRemoveError": "Nie udało się usunąć zaproszenia",
"inviteRemoveErrorDescription": "Wystąpił błąd podczas usuwania zaproszenia.",
"inviteRemoved": "Zaproszenie usunięte",
"inviteRemovedDescription": "Zaproszenie dla {email} zostało usunięte.",
"inviteQuestionRemove": "Czy na pewno chcesz usunąć zaproszenie{email, plural, ='' {}, other { dla #}}?",
"inviteMessageRemove": "Po usunięciu to zaproszenie nie będzie już ważne. Zawsze możesz ponownie zaprosić użytkownika później.",
"inviteMessageConfirm": "Aby potwierdzić, wpisz poniżej adres email zaproszenia.",
"inviteQuestionRegenerate": "Czy na pewno chcesz ponownie wygenerować zaproszenie{email, plural, ='' {}, other { dla #}}? Spowoduje to unieważnienie poprzedniego zaproszenia.",
"inviteRemoveConfirm": "Potwierdź usunięcie zaproszenia",
"inviteRegenerated": "Zaproszenie wygenerowane ponownie",
"inviteSent": "Nowe zaproszenie zostało wysłane do {email}.",
"inviteSentEmail": "Wyślij powiadomienie email do użytkownika",
"inviteGenerate": "Nowe zaproszenie zostało wygenerowane dla {email}.",
"inviteDuplicateError": "Zduplikowane zaproszenie",
"inviteDuplicateErrorDescription": "Zaproszenie dla tego użytkownika już istnieje.",
"inviteRateLimitError": "Przekroczono limit żądań",
"inviteRateLimitErrorDescription": "Przekroczyłeś limit 3 regeneracji na godzinę. Spróbuj ponownie później.",
"inviteRegenerateError": "Nie udało się ponownie wygenerować zaproszenia",
"inviteRegenerateErrorDescription": "Wystąpił błąd podczas ponownego generowania zaproszenia.",
"inviteValidityPeriod": "Okres ważności",
"inviteValidityPeriodSelect": "Wybierz okres ważności",
"inviteRegenerateMessage": "Zaproszenie zostało ponownie wygenerowane. Użytkownik musi uzyskać dostęp do poniższego linku, aby zaakceptować zaproszenie.",
"inviteRegenerateButton": "Wygeneruj ponownie",
"expiresAt": "Wygasa w dniu"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.",
"orgDeleted": "Organização excluída", "orgDeleted": "Organização excluída",
"orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgDeletedMessage": "A organização e seus dados foram excluídos.",
"orgMissing": "ID da Organização Ausente",
"orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.",
"accessUsersManage": "Gerenciar Usuários", "accessUsersManage": "Gerenciar Usuários",
"accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização",
"accessUsersSearch": "Procurar usuários...", "accessUsersSearch": "Procurar usuários...",
@@ -248,6 +250,7 @@
"weeks": "semanas", "weeks": "semanas",
"months": "Meses", "months": "Meses",
"years": "anos", "years": "anos",
"day": "{count, plural, =1 {# dia} other {# dias}}",
"apiKeysTitle": "Informações da Chave API", "apiKeysTitle": "Informações da Chave API",
"apiKeysNameMin": "O nome deve ter pelo menos 2 caracteres.", "apiKeysNameMin": "O nome deve ter pelo menos 2 caracteres.",
"apiKeysNameMax": "O nome não deve ter mais de 255 caracteres.", "apiKeysNameMax": "O nome não deve ter mais de 255 caracteres.",
@@ -351,5 +354,33 @@
"total": "Total:", "total": "Total:",
"licenseContinuePayment": "Continuar para o pagamento", "licenseContinuePayment": "Continuar para o pagamento",
"pricingPage": "Página de preços", "pricingPage": "Página de preços",
"licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite " "licensePricingPage": "Para os preços e descontos mais atualizados, por favor, visite ",
"invite": "Convites",
"inviteRegenerate": "Regenerar Convite",
"inviteRegenerateDescription": "Revogar convite anterior e criar um novo",
"inviteRemove": "Remover Convite",
"inviteRemoveError": "Falha ao remover convite",
"inviteRemoveErrorDescription": "Ocorreu um erro ao remover o convite.",
"inviteRemoved": "Convite removido",
"inviteRemovedDescription": "O convite para {email} foi removido.",
"inviteQuestionRemove": "Tem certeza que deseja remover o convite{email, plural, ='' {}, other { para #}}?",
"inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.",
"inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.",
"inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.",
"inviteRemoveConfirm": "Confirmar Remoção do Convite",
"inviteRegenerated": "Convite Regenerado",
"inviteSent": "Um novo convite foi enviado para {email}.",
"inviteSentEmail": "Enviar notificação por e-mail ao usuário",
"inviteGenerate": "Um novo convite foi gerado para {email}.",
"inviteDuplicateError": "Convite Duplicado",
"inviteDuplicateErrorDescription": "Já existe um convite para este usuário.",
"inviteRateLimitError": "Limite de Taxa Excedido",
"inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.",
"inviteRegenerateError": "Falha ao Regenerar Convite",
"inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.",
"inviteValidityPeriod": "Período de Validade",
"inviteValidityPeriodSelect": "Selecione o período de validade",
"inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.",
"inviteRegenerateButton": "Regenerar",
"expiresAt": "Expira em"
} }

View File

@@ -224,6 +224,8 @@
"orgErrorDeleteMessage": "An error occurred while deleting the organization.", "orgErrorDeleteMessage": "An error occurred while deleting the organization.",
"orgDeleted": "Organization deleted", "orgDeleted": "Organization deleted",
"orgDeletedMessage": "The organization and its data has been deleted.", "orgDeletedMessage": "The organization and its data has been deleted.",
"orgMissing": "Organization ID Missing",
"orgMissingMessage": "Unable to regenerate invitation without an organization ID.",
"accessUsersManage": "Manage Users", "accessUsersManage": "Manage Users",
"accessUsersDescription": "Invite users and add them to roles to manage access to your organization", "accessUsersDescription": "Invite users and add them to roles to manage access to your organization",
"accessUsersSearch": "Search users...", "accessUsersSearch": "Search users...",
@@ -248,6 +250,7 @@
"weeks": "Weeks", "weeks": "Weeks",
"months": "Months", "months": "Months",
"years": "Years", "years": "Years",
"day": "{count, plural, =1 {# day} other {# days}}",
"apiKeysTitle": "API Key Information", "apiKeysTitle": "API Key Information",
"apiKeysNameMin": "Name must be at least 2 characters.", "apiKeysNameMin": "Name must be at least 2 characters.",
"apiKeysNameMax": "Name must not be longer than 255 characters.", "apiKeysNameMax": "Name must not be longer than 255 characters.",
@@ -351,5 +354,33 @@
"total": "Total", "total": "Total",
"licenseContinuePayment": "Continue to Payment", "licenseContinuePayment": "Continue to Payment",
"pricingPage": "pricing page", "pricingPage": "pricing page",
"licensePricingPage": "For the most up-to-date pricing and discounts, please visit the " "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ",
"invite": "Invitations",
"inviteRegenerate": "Regenerate Invitation",
"inviteRegenerateDescription": "Revoke previous invitation and create a new one",
"inviteRemove": "Remove Invitation",
"inviteRemoveError": "Failed to remove invitation",
"inviteRemoveErrorDescription": "An error occurred while removing the invitation.",
"inviteRemoved": "Invitation removed",
"inviteRemovedDescription": "The invitation for {email} has been removed.",
"inviteQuestionRemove": "Are you sure you want to remove the invitation{email, plural, ='' {}, other { for #}}?",
"inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.",
"inviteMessageConfirm": "To confirm, please type the email address of the invitation below.",
"inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for{email, plural, ='' {}, other { for #}}? This will revoke the previous invitation.",
"inviteRemoveConfirm": "Confirm Remove Invitation",
"inviteRegenerated": "Invitation Regenerated",
"inviteSent": "A new invitation has been sent to {email}.",
"inviteSentEmail": "Send email notification to the user",
"inviteGenerate": "A new invitation has been generated for {email}.",
"inviteDuplicateError": "Duplicate Invite",
"inviteDuplicateErrorDescription": "An invitation for this user already exists.",
"inviteRateLimitError": "Rate Limit Exceeded",
"inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.",
"inviteRegenerateError": "Failed to Regenerate Invitation",
"inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.",
"inviteValidityPeriod": "Validity Period",
"inviteValidityPeriodSelect": "Select validity period",
"inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.",
"inviteRegenerateButton": "Regenerate",
"expiresAt": "Expires At"
} }

View File

@@ -22,7 +22,7 @@ export function InvitationsDataTable<TData, TValue>({
<DataTable <DataTable
columns={columns} columns={columns}
data={data} data={data}
title="Invitations" title={t('invite')}
searchPlaceholder={t('inviteSearch')} searchPlaceholder={t('inviteSearch')}
searchColumn="email" searchColumn="email"
/> />

View File

@@ -17,6 +17,7 @@ import { useOrgContext } from "@app/hooks/useOrgContext";
import { toast } from "@app/hooks/useToast"; import { toast } from "@app/hooks/useToast";
import { createApiClient } from "@app/lib/api"; import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext"; import { useEnvContext } from "@app/hooks/useEnvContext";
import { useTranslations } from "next-intl";
export type InvitationRow = { export type InvitationRow = {
id: string; id: string;
@@ -39,6 +40,8 @@ export default function InvitationsTable({
const [selectedInvitation, setSelectedInvitation] = const [selectedInvitation, setSelectedInvitation] =
useState<InvitationRow | null>(null); useState<InvitationRow | null>(null);
const t = useTranslations();
const api = createApiClient(useEnvContext()); const api = createApiClient(useEnvContext());
const { org } = useOrgContext(); const { org } = useOrgContext();
@@ -51,7 +54,7 @@ export default function InvitationsTable({
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0"> <Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span> <span className="sr-only">{t('openMenu')}</span>
<MoreHorizontal className="h-4 w-4" /> <MoreHorizontal className="h-4 w-4" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
@@ -62,7 +65,7 @@ export default function InvitationsTable({
setSelectedInvitation(invitation); setSelectedInvitation(invitation);
}} }}
> >
<span>Regenerate Invitation</span> <span>{t('inviteRegenerate')}</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
@@ -71,7 +74,7 @@ export default function InvitationsTable({
}} }}
> >
<span className="text-red-500"> <span className="text-red-500">
Remove Invitation {t('inviteRemove')}
</span> </span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
@@ -81,11 +84,11 @@ export default function InvitationsTable({
}, },
{ {
accessorKey: "email", accessorKey: "email",
header: "Email" header: t('email')
}, },
{ {
accessorKey: "expiresAt", accessorKey: "expiresAt",
header: "Expires At", header: t('expiresAt'),
cell: ({ row }) => { cell: ({ row }) => {
const expiresAt = new Date(row.original.expiresAt); const expiresAt = new Date(row.original.expiresAt);
const isExpired = expiresAt < new Date(); const isExpired = expiresAt < new Date();
@@ -99,7 +102,7 @@ export default function InvitationsTable({
}, },
{ {
accessorKey: "role", accessorKey: "role",
header: "Role" header: t('role')
} }
]; ];
@@ -112,17 +115,16 @@ export default function InvitationsTable({
.catch((e) => { .catch((e) => {
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Failed to remove invitation", title: t('inviteRemoveError'),
description: description: t('inviteRemoveErrorDescription')
"An error occurred while removing the invitation."
}); });
}); });
if (res && res.status === 200) { if (res && res.status === 200) {
toast({ toast({
variant: "default", variant: "default",
title: "Invitation removed", title: t('inviteRemoved'),
description: `The invitation for ${selectedInvitation.email} has been removed.` description: t('inviteRemovedDescription', {email: selectedInvitation.email})
}); });
setInvitations((prev) => setInvitations((prev) =>
@@ -146,23 +148,20 @@ export default function InvitationsTable({
dialog={ dialog={
<div className="space-y-4"> <div className="space-y-4">
<p> <p>
Are you sure you want to remove the invitation for{" "} {t('inviteQuestionRemove', {email: selectedInvitation?.email ?? ''})}
<b>{selectedInvitation?.email}</b>?
</p> </p>
<p> <p>
Once removed, this invitation will no longer be {t('inviteMessageRemove')}
valid. You can always re-invite the user later.
</p> </p>
<p> <p>
To confirm, please type the email address of the {t('inviteMessageConfirm')}
invitation below.
</p> </p>
</div> </div>
} }
buttonText="Confirm Remove Invitation" buttonText={t('inviteRemoveConfirm')}
onConfirm={removeInvitation} onConfirm={removeInvitation}
string={selectedInvitation?.email ?? ""} string={selectedInvitation?.email ?? ""}
title="Remove Invitation" title={t('inviteRemove')}
/> />
<RegenerateInvitationForm <RegenerateInvitationForm
open={isRegenerateModalOpen} open={isRegenerateModalOpen}

View File

@@ -24,6 +24,7 @@ import {
SelectValue SelectValue
} from "@app/components/ui/select"; } from "@app/components/ui/select";
import { Label } from "@app/components/ui/label"; import { Label } from "@app/components/ui/label";
import { useTranslations } from "next-intl";
type RegenerateInvitationFormProps = { type RegenerateInvitationFormProps = {
open: boolean; open: boolean;
@@ -56,14 +57,16 @@ export default function RegenerateInvitationForm({
const api = createApiClient(useEnvContext()); const api = createApiClient(useEnvContext());
const { org } = useOrgContext(); const { org } = useOrgContext();
const t = useTranslations();
const validForOptions = [ const validForOptions = [
{ hours: 24, name: "1 day" }, { hours: 24, name: t('day', { count: 1 }) },
{ hours: 48, name: "2 days" }, { hours: 48, name: t('day', { count: 2 }) },
{ hours: 72, name: "3 days" }, { hours: 72, name: t('day', { count: 3 }) },
{ hours: 96, name: "4 days" }, { hours: 96, name: t('day', { count: 4 }) },
{ hours: 120, name: "5 days" }, { hours: 120, name: t('day', { count: 5 }) },
{ hours: 144, name: "6 days" }, { hours: 144, name: t('day', { count: 6 }) },
{ hours: 168, name: "7 days" } { hours: 168, name: t('day', { count: 7 }) }
]; ];
useEffect(() => { useEffect(() => {
@@ -79,9 +82,8 @@ export default function RegenerateInvitationForm({
if (!org?.org.orgId) { if (!org?.org.orgId) {
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Organization ID Missing", title: t('orgMissing'),
description: description: t('orgMissingMessage'),
"Unable to regenerate invitation without an organization ID.",
duration: 5000 duration: 5000
}); });
return; return;
@@ -105,15 +107,15 @@ export default function RegenerateInvitationForm({
if (sendEmail) { if (sendEmail) {
toast({ toast({
variant: "default", variant: "default",
title: "Invitation Regenerated", title: t('inviteRegenerated'),
description: `A new invitation has been sent to ${invitation.email}.`, description: t('inviteSent', {email: invitation.email}),
duration: 5000 duration: 5000
}); });
} else { } else {
toast({ toast({
variant: "default", variant: "default",
title: "Invitation Regenerated", title: t('inviteRegenerated'),
description: `A new invitation has been generated for ${invitation.email}.`, description: t('inviteGenerate', {email: invitation.email}),
duration: 5000 duration: 5000
}); });
} }
@@ -130,24 +132,22 @@ export default function RegenerateInvitationForm({
if (error.response?.status === 409) { if (error.response?.status === 409) {
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Duplicate Invite", title: t('inviteDuplicateError'),
description: "An invitation for this user already exists.", description: t('inviteDuplicateErrorDescription'),
duration: 5000 duration: 5000
}); });
} else if (error.response?.status === 429) { } else if (error.response?.status === 429) {
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Rate Limit Exceeded", title: t('inviteRateLimitError'),
description: description: t('inviteRateLimitErrorDescription'),
"You have exceeded the limit of 3 regenerations per hour. Please try again later.",
duration: 5000 duration: 5000
}); });
} else { } else {
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Failed to Regenerate Invitation", title: t('inviteRegenerateError'),
description: description: t('inviteRegenerateErrorDescription'),
"An error occurred while regenerating the invitation.",
duration: 5000 duration: 5000
}); });
} }
@@ -168,18 +168,16 @@ export default function RegenerateInvitationForm({
> >
<CredenzaContent> <CredenzaContent>
<CredenzaHeader> <CredenzaHeader>
<CredenzaTitle>Regenerate Invitation</CredenzaTitle> <CredenzaTitle>{t('inviteRegenerate')}</CredenzaTitle>
<CredenzaDescription> <CredenzaDescription>
Revoke previous invitation and create a new one {t('inviteRegenerateDescription')}
</CredenzaDescription> </CredenzaDescription>
</CredenzaHeader> </CredenzaHeader>
<CredenzaBody> <CredenzaBody>
{!inviteLink ? ( {!inviteLink ? (
<div> <div>
<p> <p>
Are you sure you want to regenerate the {t('inviteQuestionRegenerate', {email: invitation?.email ?? ''})}
invitation for <b>{invitation?.email}</b>? This
will revoke the previous invitation.
</p> </p>
<div className="flex items-center space-x-2 mt-4"> <div className="flex items-center space-x-2 mt-4">
<Checkbox <Checkbox
@@ -190,12 +188,12 @@ export default function RegenerateInvitationForm({
} }
/> />
<label htmlFor="send-email"> <label htmlFor="send-email">
Send email notification to the user {t('inviteSentEmail')}
</label> </label>
</div> </div>
<div className="mt-4 space-y-2"> <div className="mt-4 space-y-2">
<Label> <Label>
Validity Period {t('inviteValidityPeriod')}
</Label> </Label>
<Select <Select
value={validHours.toString()} value={validHours.toString()}
@@ -204,7 +202,7 @@ export default function RegenerateInvitationForm({
} }
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select validity period" /> <SelectValue placeholder={t('inviteValidityPeriodSelect')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{validForOptions.map((option) => ( {validForOptions.map((option) => (
@@ -222,9 +220,7 @@ export default function RegenerateInvitationForm({
) : ( ) : (
<div className="space-y-4 max-w-md"> <div className="space-y-4 max-w-md">
<p> <p>
The invitation has been regenerated. The user {t('inviteRegenerateMessage')}
must access the link below to accept the
invitation.
</p> </p>
<CopyTextBox text={inviteLink} wrapText={false} /> <CopyTextBox text={inviteLink} wrapText={false} />
</div> </div>
@@ -240,7 +236,7 @@ export default function RegenerateInvitationForm({
onClick={handleRegenerate} onClick={handleRegenerate}
loading={loading} loading={loading}
> >
Regenerate {t('inviteRegenerateButton')}
</Button> </Button>
</> </>
) : ( ) : (