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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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