2
0

🧑‍💻 Migrate to Tolgee (#976)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

### Summary by CodeRabbit

- Refactor: Transitioned to a new translation library (`@tolgee/react`)
across the application, enhancing the localization capabilities and
consistency.
- New Feature: Introduced a JSON configuration file for application
settings, improving customization and flexibility.
- Refactor: Updated SVG attribute naming convention in the
`WhatsAppLogo` component to align with React standards.
- Chore: Adjusted the `.gitignore` file and added a new line at the end.
- Documentation: Added instructions for setting up environment variables
for the Tolgee i18n contribution dev tool, improving the self-hosting
configuration guide.
- Style: Updated the `CollaborationMenuButton` to hide the
`PopoverContent` component by scaling it down to zero.
- Refactor: Simplified error handling logic for fetching and updating
typebots in `TypebotProvider.tsx`, improving code readability and
maintenance.
- Refactor: Removed the dependency on the `parseGroupTitle` function,
simplifying the code in several components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Baptiste Arnaud
2023-10-27 09:23:50 +02:00
committed by GitHub
parent 31b3fc311e
commit bed8b42a2e
101 changed files with 2141 additions and 2210 deletions

5
.tolgeerc Normal file
View File

@ -0,0 +1,5 @@
{
"apiUrl": "https://tolgee.server.baptistearno.com",
"projectId": "1",
"delimiter": null
}

27
.vscode/i18n-ally-custom-framework.yml vendored Normal file
View File

@ -0,0 +1,27 @@
# An array of strings which contain Language Ids defined by VS Code
# You can check avaliable language ids here: https://code.visualstudio.com/docs/languages/overview#_language-id
languageIds:
- javascript
- typescript
- javascriptreact
- typescriptreact
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
usageMatchRegex:
# The following example shows how to detect `t("your.i18n.keys")`
# the `{key}` will be placed by a proper keypath matching regex,
# you can ignore it and use your own matching rules as well
- "[^\\w\\d]t\\([\\s\\n]*'({key})'"
- 'keyName="({key})"'
# An array of strings containing refactor templates.
# The "$1" will be replaced by the keypath specified.
# Optional: uncomment the following two lines to use
# refactorTemplates:
# - i18n.get("$1")
# If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true

View File

@ -1,9 +1,9 @@
{
"i18n-ally.localesPaths": ["apps/builder/src/locales"],
"i18n-ally.enabledFrameworks": ["next-international"],
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.localesPaths": ["apps/builder/public/locales"],
"i18n-ally.keystyle": "flat",
"i18n-ally.displayLanguage": "en",
"i18n-ally.enabledFrameworks": ["custom"],
"i18n-ally.sortKeys": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},

View File

@ -29,6 +29,8 @@
"@sentry/nextjs": "7.73.0",
"@tanstack/react-query": "^4.29.19",
"@tanstack/react-table": "8.9.3",
"@tolgee/format-icu": "^5.13.3",
"@tolgee/react": "^5.13.3",
"@trpc/client": "10.40.0",
"@trpc/next": "10.40.0",
"@trpc/react-query": "10.40.0",
@ -68,7 +70,6 @@
"micro-cors": "0.1.1",
"next": "13.5.4",
"next-auth": "4.22.1",
"next-international": "0.9.5",
"nextjs-cors": "^2.1.2",
"nodemailer": "6.9.3",
"nprogress": "0.2.0",

View File

@ -0,0 +1,251 @@
{
"account.apiTokens.createButton.label": "Erstellen",
"account.apiTokens.createModal.copyInstruction": "Bitte kopiere deinen Token und bewahre ihn an einem sicheren Ort auf.",
"account.apiTokens.createModal.createButton.label": "Token erstellen",
"account.apiTokens.createModal.createHeading": "Token erstellen",
"account.apiTokens.createModal.createdHeading": "Token erstellt",
"account.apiTokens.createModal.doneButton.label": "Fertig",
"account.apiTokens.createModal.nameInput.label": "Gib einen eindeutigen Namen für deinen Token ein, um ihn von anderen Token zu unterscheiden.",
"account.apiTokens.createModal.nameInput.placeholder": "Z.B. Zapier, Github, Make.com",
"account.apiTokens.createModal.securityWarning": "Aus Sicherheitsgründen können wir ihn nicht erneut anzeigen.",
"account.apiTokens.deleteButton.label": "Löschen",
"account.apiTokens.deleteConfirmationMessage": "Der Token <strong>tokenName</strong> wird dauerhaft widerrufen, bist du sicher, dass du fortfahren möchtest?",
"account.apiTokens.description": "Diese Token ermöglichen es anderen Apps, dein gesamtes Konto und Typebots zu steuern. Sei vorsichtig!",
"account.apiTokens.heading": "API-Token",
"account.apiTokens.table.createdHeader": "Erstellt",
"account.apiTokens.table.nameHeader": "Name",
"account.myAccount.changePhotoButton.label": "Foto ändern",
"account.myAccount.changePhotoButton.specification": ".jpg oder .png, max 1MB",
"account.myAccount.emailInput.disabledTooltip": "Das Aktualisieren der E-Mail-Adresse ist nicht verfügbar. Kontaktiere den Support, wenn du sie ändern möchtest.",
"account.myAccount.emailInput.label": "E-Mail-Adresse:",
"account.myAccount.nameInput.label": "Name:",
"account.preferences.appearance.darkLabel": "Dunkel",
"account.preferences.appearance.heading": "Erscheinungsbild",
"account.preferences.appearance.lightLabel": "Hell",
"account.preferences.appearance.systemLabel": "System",
"account.preferences.graphNavigation.heading": "Editor-Navigation",
"account.preferences.graphNavigation.mouse.description": "Bewege dich, indem du das Board ziehst und zoome rein/raus mit dem Mausrad",
"account.preferences.graphNavigation.mouse.label": "Maus",
"account.preferences.graphNavigation.trackpad.description": "Bewege das Board mit 2 Fingern und zoome rein/raus, indem du kneifst",
"account.preferences.graphNavigation.trackpad.label": "Trackpad",
"account.preferences.language.heading": "Sprache",
"account.preferences.language.tooltip": "Die Übersetzungen sind noch nicht vollständig. Es ist eine laufende Arbeit. \uD83E\uDD13",
"analytics.completionRateLabel": "Abschlussrate",
"analytics.notAvailableLabel": "Nicht verfügbar",
"analytics.startsLabel": "Starts",
"analytics.viewsLabel": "Ansichten",
"auth.emailSubmitButton.label": "Absenden",
"auth.error.default": "Versuche, dich mit einem anderen Konto anzumelden.",
"auth.error.email": "E-Mail nicht gefunden. Versuche, dich mit einem anderen Anbieter anzumelden.",
"auth.error.oauthNotLinked": "Um deine Identität zu bestätigen, melde dich mit demselben Konto an, das du ursprünglich verwendet hast.",
"auth.error.unknown": "Ein Fehler ist aufgetreten. Bitte versuche es erneut.",
"auth.magicLink.description": "Vergiss nicht, deinen Spam-Ordner zu überprüfen.",
"auth.magicLink.title": "Eine E-Mail mit magischem Link wurde gesendet. \uD83E\uDE84",
"auth.noProvider.link": "mindestens einen Authentifizierungsanbieter konfigurieren (E-Mail, Google, GitHub, Facebook oder Azure AD).",
"auth.noProvider.preLink": "Du musst",
"auth.orEmailLabel": "Oder mit deiner E-Mail",
"auth.register.aggreeToTerms": "Durch die Registrierung stimmst du unseren <terms>termsOfService</terms> und <privacy>privacyPolicy</privacy> zu.",
"auth.register.alreadyHaveAccountLabel.link": "Anmelden",
"auth.register.alreadyHaveAccountLabel.preLink": "Bereits ein Konto vorhanden?",
"auth.register.heading": "Konto erstellen",
"auth.signin.heading": "Anmelden",
"auth.signin.noAccountLabel.link": "Kostenlos anmelden",
"auth.signin.noAccountLabel.preLink": "Noch kein Konto?",
"auth.signinErrorToast.description": "Anmeldungen sind deaktiviert.",
"auth.signinErrorToast.title": "Nicht autorisiert",
"auth.signinErrorToast.tooManyRequests": "Zu viele Anfragen. Versuche es später erneut.",
"auth.socialLogin.azureButton.label": "Mit {azureProviderName} fortfahren",
"auth.socialLogin.customButton.label": "Mit {customProviderName} fortfahren",
"auth.socialLogin.facebookButton.label": "Mit Facebook fortfahren",
"auth.socialLogin.githubButton.label": "Mit GitHub fortfahren",
"auth.socialLogin.gitlabButton.label": "Mit {gitlabProviderName} fortfahren",
"auth.socialLogin.googleButton.label": "Mit Google fortfahren",
"back": "Zurück",
"billing.billingPortalButton.label": "Abrechnungsportal",
"billing.contribution.link": "Erfahre mehr.",
"billing.contribution.preLink": "Typebot trägt 1% deines Abonnements dazu bei, CO₂ aus der Atmosphäre zu entfernen.",
"billing.currentSubscription.cancelDate": "Wird storniert am",
"billing.currentSubscription.heading": "Abonnement",
"billing.currentSubscription.pastDueAlert": "Die letzte Zahlung ist fehlgeschlagen. Gehen Sie zum Abrechnungsportal, um fortzufahren und eine Kündigung Ihres Abonnements zu vermeiden.",
"billing.currentSubscription.subheading": "Aktuelles Workspace-Abonnement:",
"billing.customLimit.link": "Lass uns darüber sprechen!",
"billing.customLimit.preLink": "Brauchst du individuelle Limits? Spezielle Funktionen?",
"billing.invoices.empty": "Keine Rechnungen für diesen Workspace gefunden.",
"billing.invoices.heading": "Rechnungen",
"billing.invoices.paidAt": "Bezahlt am",
"billing.invoices.subtotal": "Zwischensumme",
"billing.limitMessage.analytics": "Detaillierte Analysen freischalten",
"billing.limitMessage.brand": "Branding entfernen",
"billing.limitMessage.customDomain": "Eigene Domains hinzufügen",
"billing.limitMessage.fileInput": "Datei-Eingabefelder verwenden",
"billing.limitMessage.folder": "Ordner erstellen",
"billing.preCheckoutModal.companyInput.label": "Firmenname:",
"billing.preCheckoutModal.emailInput.label": "E-Mail:",
"billing.preCheckoutModal.submitButton.label": "Zur Kasse gehen",
"billing.preCheckoutModal.taxId.label": "Steuernummer:",
"billing.preCheckoutModal.taxId.placeholder": "ID-Typ",
"billing.pricingCard.chatsPerMonth": "Chats/Monat",
"billing.pricingCard.chatsTooltip": "Ein Chat wird gezählt, wenn ein Benutzer eine Diskussion startet. Es ist unabhängig von der Anzahl der gesendeten und empfangenen Nachrichten.",
"billing.pricingCard.heading": "Upgrade auf <strong>plan</strong>",
"billing.pricingCard.perMonth": "/ Monat",
"billing.pricingCard.plus": ", plus:",
"billing.pricingCard.pro.analytics": "Detaillierte Analysen",
"billing.pricingCard.pro.customDomains": "Eigene Domains",
"billing.pricingCard.pro.description": "Für Agenturen & wachsende Start-ups.",
"billing.pricingCard.pro.everythingFromStarter": "Alles in Starter",
"billing.pricingCard.pro.includedSeats": "5 Plätze inklusive",
"billing.pricingCard.pro.mostPopularLabel": "Am beliebtesten",
"billing.pricingCard.pro.whatsAppIntegration": "WhatsApp-Integration",
"billing.pricingCard.starter.brandingRemoved": "Branding entfernt",
"billing.pricingCard.starter.createFolders": "Ordner erstellen",
"billing.pricingCard.starter.description": "Für Einzelpersonen & kleine Unternehmen.",
"billing.pricingCard.starter.fileUploadBlock": "Datei-Upload Eingabefeld",
"billing.pricingCard.starter.includedSeats": "2 Plätze inklusive",
"billing.pricingCard.upgradeButton.current": "Dein aktueller Tarif",
"billing.updateSuccessToast.description": "Workspace {plan} Plan erfolgreich aktualisiert \uD83C\uDF89",
"billing.upgradeAlert.buttonDefaultLabel": "Mehr Informationen",
"billing.upgradeLimitLabel": "Um {type} hinzuzufügen, musst du deinen Tarif aktualisieren",
"billing.usage.chats.alert.soonReach": "Deine Typebots sind beliebt! Du wirst bald das Chat-Limit deines Tarifs erreichen. \uD83D\uDE80",
"billing.usage.chats.alert.updatePlan": "Vergewissere dich, dass du deinen Tarif aktualisierst, um dieses Limit zu erhöhen und weiterhin mit deinen Benutzern zu chatten.",
"billing.usage.chats.heading": "Chats",
"billing.usage.heading": "Nutzung",
"billing.usage.unlimited": "Unbegrenzt",
"cancel": "Abbrechen",
"confirmModal.defaultTitle": "Bist du sicher?",
"dashboard.header.settingsButton.label": "Einstellungen & Mitglieder",
"dashboard.redirectionMessage": "Du wirst weitergeleitet...",
"dashboard.title": "Meine Typebots",
"delete": "Löschen",
"downgrade": "Downgrade",
"blocks.bubbles.embed.blockCard.tooltip": "Ein PDF, ein iframe, eine Website einbetten...",
"blocks.inputs.fileUpload.blockCard.tooltip": "Dateien hochladen",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Tabellen",
"editor.blockCard.logicBlock.tooltip.code.label": "JavaScript-Code ausführen",
"editor.blockCard.logicBlock.tooltip.jump.label": "Ablauf zu einer anderen Gruppe beschleunigen",
"editor.blockCard.logicBlock.tooltip.typebotLink.label": "Verlinkung zu einem anderen Typebot",
"editor.blocks.bubbles.audio.node.clickToEdit.text": "Zum Bearbeiten klicken...",
"editor.blocks.bubbles.audio.settings.autoplay.label": "Autoplay aktivieren",
"editor.blocks.bubbles.audio.settings.chooseFile.label": "Datei auswählen",
"editor.blocks.bubbles.audio.settings.embedLink.label": "Link einbetten",
"editor.blocks.bubbles.audio.settings.upload.label": "Hochladen",
"editor.blocks.bubbles.audio.settings.worksWith.placeholder": "Füge den Audio-Dateilink ein...",
"editor.blocks.bubbles.audio.settings.worksWith.text": "Funktioniert mit .MP3- und .WAV-Dateien",
"editor.blocks.bubbles.embed.node.clickToEdit.text": "Zum Bearbeiten klicken...",
"editor.blocks.bubbles.embed.node.show.text": "Einbetten anzeigen",
"editor.blocks.bubbles.embed.settings.numberInput.unit": "px",
"editor.blocks.bubbles.embed.settings.worksWith.placeholder": "Füge den Link oder Code ein...",
"editor.blocks.bubbles.embed.settings.worksWith.text": "Funktioniert mit PDFs, iframes, Websites...",
"editor.blocks.bubbles.image.node.clickToEdit.text": "Zum Bearbeiten klicken...",
"editor.blocks.bubbles.image.switchWithLabel.onClick.label": "Beim Klicken Link",
"editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder": "Link Alternativtext (Beschreibung)",
"editor.blocks.bubbles.textEditor.plate.label": "Texteditor",
"editor.blocks.bubbles.textEditor.searchVariable.placeholder": "Nach einer Variable suchen",
"editor.blocks.bubbles.video.node.clickToEdit.text": "Zum Bearbeiten klicken...",
"editor.blocks.bubbles.video.settings.numberInput.unit": "px",
"editor.blocks.bubbles.video.settings.worksWith.placeholder": "Füge den Videolink ein...",
"editor.blocks.bubbles.video.settings.worksWith.text": "Funktioniert mit YouTube, Vimeo und anderen",
"editor.blocks.start.text": "Start",
"editor.editableTypebotName.tooltip.rename.label": "Umbenennen",
"editor.gettingStartedModal.editorBasics.heading": "Grundlagen des Editors",
"editor.gettingStartedModal.editorBasics.list.four.label": "Klicke auf die Vorschau-Schaltfläche oben rechts, um deinen Bot anzusehen.",
"editor.gettingStartedModal.editorBasics.list.label": "Wenn du Fragen hast, verwende gerne die Sprechblase unten rechts, um sie mir zu stellen. Ich beantworte normalerweise innerhalb der nächsten 24 Stunden. \uD83D\uDE03",
"editor.gettingStartedModal.editorBasics.list.one.label": "Die Seitenleiste enthält Blöcke, die du auf das Board ziehen und ablegen kannst.",
"editor.gettingStartedModal.editorBasics.list.three.label": "Verbinde die Gruppen miteinander.",
"editor.gettingStartedModal.editorBasics.list.two.label": "Du kannst Blöcke gruppieren, indem du sie unter oder über einander ablegst.",
"editor.gettingStartedModal.seeAction.item.label": "Weitere Videos",
"editor.gettingStartedModal.seeAction.label": "In Aktion sehen",
"editor.gettingStartedModal.seeAction.time": "5 Minuten",
"editor.headers.flowButton.label": "Ablauf",
"editor.headers.helpButton.label": "Hilfe",
"editor.headers.previewButton.label": "Vorschau",
"editor.headers.resultsButton.label": "Ergebnisse",
"editor.headers.savingSpinner.label": "Speichern...",
"editor.headers.settingsButton.label": "Einstellungen",
"editor.headers.shareButton.label": "Teilen",
"editor.headers.themeButton.label": "Design",
"editor.sidebarBlock.abTest.label": "AB-Test",
"editor.sidebarBlock.analytics.label": "Analytics",
"editor.sidebarBlock.audio.label": "Audio",
"editor.sidebarBlock.button.label": "Buttons",
"editor.sidebarBlock.chatwoot.label": "Chatwoot",
"editor.sidebarBlock.condition.label": "Bedingung",
"editor.sidebarBlock.date.label": "Datum",
"editor.sidebarBlock.email.label": "E-Mail",
"editor.sidebarBlock.embed.label": "Einbetten",
"editor.sidebarBlock.file.label": "Datei",
"editor.sidebarBlock.image.label": "Bild",
"editor.sidebarBlock.jump.label": "Springen",
"editor.sidebarBlock.makecom.label": "Make.com",
"editor.sidebarBlock.number.label": "Nummer",
"editor.sidebarBlock.openai.label": "OpenAI",
"editor.sidebarBlock.pabbly.label": "Pabbly",
"editor.sidebarBlock.payment.label": "Zahlung",
"editor.sidebarBlock.phone.label": "Telefon",
"editor.sidebarBlock.picChoice.label": "Bildauswahl",
"editor.sidebarBlock.pixel.label": "Pixel",
"editor.sidebarBlock.rating.label": "Bewertung",
"editor.sidebarBlock.redirect.label": "Weiterleitung",
"editor.sidebarBlock.script.label": "Skript",
"editor.sidebarBlock.setVariable.label": "Variable setzen",
"editor.sidebarBlock.sheets.label": "Tabellen",
"editor.sidebarBlock.start.label": "Start",
"editor.sidebarBlock.text.label": "Text",
"editor.sidebarBlock.typebot.label": "Typebot",
"editor.sidebarBlock.video.label": "Video",
"editor.sidebarBlock.wait.label": "Warten",
"editor.sidebarBlock.webhook.label": "Webhook",
"editor.sidebarBlock.website.label": "Website",
"editor.sidebarBlock.zapier.label": "Zapier",
"editor.sidebarBlock.zemanticAi.label": "Zemantic AI",
"editor.sidebarBlocks.blockType.bubbles.heading": "Blasen",
"editor.sidebarBlocks.blockType.inputs.heading": "Eingaben",
"editor.sidebarBlocks.blockType.integrations.heading": "Integrationen",
"editor.sidebarBlocks.blockType.logic.heading": "Logik",
"editor.sidebarBlocks.sidebar.icon.lock.label": "Sperren",
"editor.sidebarBlocks.sidebar.icon.unlock.label": "Entsperren",
"editor.sidebarBlocks.sidebar.lock.label": "Seitenleiste sperren",
"editor.sidebarBlocks.sidebar.unlock.label": "Seitenleiste entsperren",
"errorMessage": "Ein Fehler ist aufgetreten",
"folders.createFolderButton.label": "Ordner erstellen",
"folders.createTypebotButton.label": "Typebot erstellen",
"folders.folderButton.deleteConfirmationMessage": "Möchtest du den Ordner <strong>folderName</strong> wirklich löschen? (Alles im Inneren wird in dein Dashboard verschoben)",
"folders.typebotButton.delete": "Löschen",
"folders.typebotButton.deleteConfirmationMessage": "Möchtest du deinen Typebot <strong>typebotName</strong> wirklich löschen?",
"folders.typebotButton.deleteConfirmationMessageWarning": "Alle zugehörigen Daten werden gelöscht und können nicht wiederhergestellt werden.",
"folders.typebotButton.duplicate": "Duplizieren",
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Mehr Optionen anzeigen",
"folders.typebotButton.unpublish": "Veröffentlichung aufheben",
"pending": "Ausstehend",
"remove": "Entfernen",
"skip": "Überspringen",
"templates.buttons.fromScratchButton.label": "Von Grund auf starten",
"templates.buttons.fromTemplateButton.label": "Von einer Vorlage starten",
"templates.buttons.heading": "Erstelle einen neuen Typebot",
"templates.buttons.importFileButton.label": "Datei importieren",
"templates.importFromFileButon.toastError.description": "Konnte die Datei nicht verarbeiten. Bist du sicher, dass es sich um einen Typebot handelt?",
"templates.modal.menuHeading.marketing": "Marketing",
"templates.modal.menuHeading.new.tag": "Neu",
"templates.modal.menuHeading.other": "Andere",
"templates.modal.menuHeading.product": "Produkt",
"templates.modal.useTemplateButton.label": "Diese Vorlage verwenden",
"upgrade": "Upgrade",
"workspace.dropdown.logoutButton.label": "Abmelden",
"workspace.dropdown.newButton.label": "Neuer Workspace",
"workspace.membersList.inviteButton.label": "Einladen",
"workspace.membersList.inviteInput.placeholder": "name@unternehmen.de",
"workspace.membersList.title": "Mitglieder",
"workspace.membersList.unlockBanner.label": "Aktualisiere deinen Plan, um mit mehr Teammitgliedern zu arbeiten und neue Limits freizuschalten \uD83D\uDE80",
"workspace.settings.deleteButton.confirmMessage": "Sind Sie sicher, dass Sie den Workspace {workspaceName} löschen möchten? Alle seine Ordner, Typebots und Ergebnisse werden dauerhaft gelöscht.",
"workspace.settings.deleteButton.label": "Workspace löschen",
"workspace.settings.icon.title": "Symbol",
"workspace.settings.modal.menu.billingAndUsage.label": "Abrechnung & Nutzung",
"workspace.settings.modal.menu.members.label": "Mitglieder",
"workspace.settings.modal.menu.myAccount.label": "Mein Konto",
"workspace.settings.modal.menu.preferences.label": "Einstellungen",
"workspace.settings.modal.menu.settings.label": "Einstellungen",
"workspace.settings.modal.menu.version.label": "Version: {version}",
"workspace.settings.modal.menu.workspace.label": "Workspace",
"workspace.settings.name.label": "Name:"
}

View File

@ -0,0 +1,252 @@
{
"account.apiTokens.createButton.label": "Create",
"account.apiTokens.createModal.copyInstruction": "Please copy your token and store it in a safe place.",
"account.apiTokens.createModal.createButton.label": "Create token",
"account.apiTokens.createModal.createHeading": "Create Token",
"account.apiTokens.createModal.createdHeading": "Token Created",
"account.apiTokens.createModal.doneButton.label": "Done",
"account.apiTokens.createModal.nameInput.label": "Enter a unique name for your token to differentiate it from other tokens.",
"account.apiTokens.createModal.nameInput.placeholder": "I.e. Zapier, Github, Make.com",
"account.apiTokens.createModal.securityWarning": "For security reasons we cannot show it again.",
"account.apiTokens.deleteButton.label": "Delete",
"account.apiTokens.deleteConfirmationMessage": "The token <strong>tokenName</strong> will be permanently revoked, are you sure you want to continue?",
"account.apiTokens.description": "These tokens allow other apps to control your whole account and typebots. Be careful!",
"account.apiTokens.heading": "API tokens",
"account.apiTokens.table.createdHeader": "Created",
"account.apiTokens.table.nameHeader": "Name",
"account.myAccount.changePhotoButton.label": "Change photo",
"account.myAccount.changePhotoButton.specification": ".jpg or.png, max 1MB",
"account.myAccount.emailInput.disabledTooltip": "Updating email is not available. Contact the support if you want to change it.",
"account.myAccount.emailInput.label": "Email address:",
"account.myAccount.nameInput.label": "Name:",
"account.preferences.appearance.darkLabel": "Dark",
"account.preferences.appearance.heading": "Appearance",
"account.preferences.appearance.lightLabel": "Light",
"account.preferences.appearance.systemLabel": "System",
"account.preferences.graphNavigation.heading": "Editor Navigation",
"account.preferences.graphNavigation.mouse.description": "Move by dragging the board and zoom in/out using the scroll wheel",
"account.preferences.graphNavigation.mouse.label": "Mouse",
"account.preferences.graphNavigation.trackpad.description": "Move the board using 2 fingers and zoom in/out by pinching",
"account.preferences.graphNavigation.trackpad.label": "Trackpad",
"account.preferences.language.heading": "Language",
"account.preferences.language.tooltip": "The translations are not complete yet. It is a work in progress. 🤓",
"analytics.completionRateLabel": "Completion rate",
"analytics.notAvailableLabel": "Not available",
"analytics.startsLabel": "Starts",
"analytics.viewsLabel": "Views",
"auth.emailSubmitButton.label": "Submit",
"auth.error.default": "Try signing with a different account.",
"auth.error.email": "Email not found. Try signing with a different provider.",
"auth.error.oauthNotLinked": "To confirm your identity, sign in with the same account you used originally.",
"auth.error.unknown": "An error occurred. Please try again.",
"auth.magicLink.description": "Make sure to check your spam folder.",
"auth.magicLink.title": "A magic link email was sent. 🪄",
"auth.noProvider.link": "configure at least one auth provider (Email, Google, GitHub, Facebook or Azure AD).",
"auth.noProvider.preLink": "You need to",
"auth.orEmailLabel": "Or with your email",
"auth.register.aggreeToTerms": "By signing up, you agree to our <terms>termsOfService</terms> and <privacy>privacyPolicy</privacy>.",
"auth.register.alreadyHaveAccountLabel.link": "Sign in",
"auth.register.alreadyHaveAccountLabel.preLink": "Already have an account?",
"auth.register.heading": "Create an account",
"auth.signin.heading": "Sign In",
"auth.signin.noAccountLabel.link": "Sign up for free",
"auth.signin.noAccountLabel.preLink": "Don't have an account?",
"auth.signinErrorToast.description": "Sign ups are disabled.",
"auth.signinErrorToast.title": "Unauthorized",
"auth.signinErrorToast.tooManyRequests": "Too many requests. Try again later.",
"auth.socialLogin.azureButton.label": "Continue with {azureProviderName}",
"auth.socialLogin.customButton.label": "Continue with {customProviderName}",
"auth.socialLogin.facebookButton.label": "Continue with Facebook",
"auth.socialLogin.githubButton.label": "Continue with GitHub",
"auth.socialLogin.gitlabButton.label": "Continue with {gitlabProviderName}",
"auth.socialLogin.googleButton.label": "Continue with Google",
"back": "Back",
"billing.billingPortalButton.label": "Billing portal",
"billing.contribution.link": "Learn more.",
"billing.contribution.preLink": "Typebot is contributing 1% of your subscription to remove CO₂ from the atmosphere.",
"billing.currentSubscription.cancelDate": "Will be cancelled on",
"billing.currentSubscription.heading": "Subscription",
"billing.currentSubscription.pastDueAlert": "The latest payment failed. Head over to the billing portal to proceed and avoid having your subscription canceled.",
"billing.currentSubscription.subheading": "Current workspace subscription:",
"billing.customLimit.link": "Let's chat!",
"billing.customLimit.preLink": "Need custom limits? Specific features?",
"billing.invoices.empty": "No invoices found for this workspace.",
"billing.invoices.heading": "Invoices",
"billing.invoices.paidAt": "Paid at",
"billing.invoices.subtotal": "Subtotal",
"billing.limitMessage.analytics": "unlock in-depth analytics",
"billing.limitMessage.brand": "remove branding",
"billing.limitMessage.customDomain": "add custom domains",
"billing.limitMessage.fileInput": "use file input blocks",
"billing.limitMessage.folder": "create folders",
"billing.preCheckoutModal.companyInput.label": "Company name:",
"billing.preCheckoutModal.emailInput.label": "Email:",
"billing.preCheckoutModal.submitButton.label": "Go to checkout",
"billing.preCheckoutModal.taxId.label": "Tax ID:",
"billing.preCheckoutModal.taxId.placeholder": "ID type",
"billing.pricingCard.chatsPerMonth": "chats/mo",
"billing.pricingCard.chatsTooltip": "A chat is counted whenever a user starts a discussion. It is independant of the number of messages he sends and receives.",
"billing.pricingCard.heading": "Upgrade to <strong>plan</strong>",
"billing.pricingCard.perMonth": "/ month",
"billing.pricingCard.plus": ", plus:",
"billing.pricingCard.pro.analytics": "In-depth analytics",
"billing.pricingCard.pro.customDomains": "Custom domains",
"billing.pricingCard.pro.description": "For agencies & growing startups.",
"billing.pricingCard.pro.everythingFromStarter": "Everything in Starter",
"billing.pricingCard.pro.includedSeats": "5 seats included",
"billing.pricingCard.pro.mostPopularLabel": "Most popular",
"billing.pricingCard.pro.whatsAppIntegration": "WhatsApp integration",
"billing.pricingCard.starter.brandingRemoved": "Branding removed",
"billing.pricingCard.starter.createFolders": "Create folders",
"billing.pricingCard.starter.description": "For individuals & small businesses.",
"billing.pricingCard.starter.fileUploadBlock": "File upload input block",
"billing.pricingCard.starter.includedSeats": "2 seats included",
"billing.pricingCard.upgradeButton.current": "Your current plan",
"billing.updateSuccessToast.description": "Workspace {plan} plan successfully updated 🎉",
"billing.upgradeAlert.buttonDefaultLabel": "More info",
"billing.upgradeLimitLabel": "You need to upgrade your plan in order to {type}",
"billing.usage.chats.alert.soonReach": "Your typebots are popular! You will soon reach your plan's chats limit. 🚀",
"billing.usage.chats.alert.updatePlan": "Make sure to update your plan to increase this limit and continue chatting with your users.",
"billing.usage.chats.heading": "Chats",
"billing.usage.heading": "Usage",
"billing.usage.unlimited": "Unlimited",
"cancel": "Cancel",
"confirmModal.defaultTitle": "Are you sure?",
"dashboard.header.settingsButton.label": "Settings & Members",
"dashboard.redirectionMessage": "You are being redirected...",
"dashboard.title": "My typebots",
"delete": "Delete",
"downgrade": "Downgrade",
"blocks.bubbles.embed.blockCard.tooltip": "Embed a pdf, an iframe, a website...",
"blocks.inputs.fileUpload.blockCard.tooltip": "Upload Files",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"editor.blockCard.logicBlock.tooltip.code.label": "Execute Javascript code",
"editor.blockCard.logicBlock.tooltip.jump.label": "Fast forward the flow to another group",
"editor.blockCard.logicBlock.tooltip.typebotLink.label": "Link and jump to another typebot",
"editor.blocks.bubbles.audio.node.clickToEdit.text": "Click to edit...",
"editor.blocks.bubbles.audio.settings.autoplay.label": "Enable autoplay",
"editor.blocks.bubbles.audio.settings.chooseFile.label": "Choose a file",
"editor.blocks.bubbles.audio.settings.embedLink.label": "Embed link",
"editor.blocks.bubbles.audio.settings.upload.label": "Upload",
"editor.blocks.bubbles.audio.settings.worksWith.placeholder": "Paste the audio file link...",
"editor.blocks.bubbles.audio.settings.worksWith.text": "Works with .MP3s and .WAVs",
"editor.blocks.bubbles.embed.node.clickToEdit.text": "Click to edit...",
"editor.blocks.bubbles.embed.node.show.text": "Show embed",
"editor.blocks.bubbles.embed.settings.numberInput.unit": "px",
"editor.blocks.bubbles.embed.settings.worksWith.placeholder": "Paste the link or code...",
"editor.blocks.bubbles.embed.settings.worksWith.text": "Works with PDFs, iframes, websites...",
"editor.blocks.bubbles.image.node.clickToEdit.text": "Click to edit...",
"editor.blocks.bubbles.image.switchWithLabel.onClick.label": "On click link",
"editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder": "Link alt text (description)",
"editor.blocks.bubbles.textEditor.plate.label": "Text editor",
"editor.blocks.bubbles.textEditor.searchVariable.placeholder": "Search for a variable",
"editor.blocks.bubbles.video.node.clickToEdit.text": "Click to edit...",
"editor.blocks.bubbles.video.settings.numberInput.unit": "px",
"editor.blocks.bubbles.video.settings.worksWith.placeholder": "Paste the video link...",
"editor.blocks.bubbles.video.settings.worksWith.text": "Works with Youtube, Vimeo and others",
"editor.blocks.start.text": "Start",
"editor.editableTypebotName.tooltip.rename.label": "Rename",
"editor.gettingStartedModal.editorBasics.heading": "Editor Basics",
"editor.gettingStartedModal.editorBasics.list.four.label": "Preview your bot by clicking the preview button on the top right",
"editor.gettingStartedModal.editorBasics.list.label": "Feel free to use the bottom-right bubble to reach out if you have any question. I usually answer within the next 24 hours. 😃",
"editor.gettingStartedModal.editorBasics.list.one.label": "The left side bar contains blocks that you can drag and drop to the board.",
"editor.gettingStartedModal.editorBasics.list.three.label": "Connect the groups together",
"editor.gettingStartedModal.editorBasics.list.two.label": "You can group blocks together by dropping them below or above each other",
"editor.gettingStartedModal.seeAction.item.label": "Other videos",
"editor.gettingStartedModal.seeAction.label": "See it in action",
"editor.gettingStartedModal.seeAction.time": "5 minutes",
"editor.headers.flowButton.label": "Flow",
"editor.headers.helpButton.label": "Help",
"editor.headers.previewButton.label": "Preview",
"editor.headers.resultsButton.label": "Results",
"editor.headers.savingSpinner.label": "Saving...",
"editor.headers.settingsButton.label": "Settings",
"editor.headers.shareButton.label": "Share",
"editor.headers.themeButton.label": "Theme",
"editor.sidebarBlock.abTest.label": "AB Test",
"editor.sidebarBlock.analytics.label": "Analytics",
"editor.sidebarBlock.audio.label": "Audio",
"editor.sidebarBlock.button.label": "Buttons",
"editor.sidebarBlock.chatwoot.label": "Chatwoot",
"editor.sidebarBlock.condition.label": "Condition",
"editor.sidebarBlock.date.label": "Date",
"editor.sidebarBlock.email.label": "Email",
"editor.sidebarBlock.embed.label": "Embed",
"editor.sidebarBlock.file.label": "File",
"editor.sidebarBlock.image.label": "Image",
"editor.sidebarBlock.jump.label": "Jump",
"editor.sidebarBlock.makecom.label": "Make.com",
"editor.sidebarBlock.number.label": "Number",
"editor.sidebarBlock.openai.label": "OpenAI",
"editor.sidebarBlock.pabbly.label": "Pabbly",
"editor.sidebarBlock.payment.label": "Payment",
"editor.sidebarBlock.phone.label": "Phone",
"editor.sidebarBlock.picChoice.label": "Pic choice",
"editor.sidebarBlock.pixel.label": "Pixel",
"editor.sidebarBlock.rating.label": "Rating",
"editor.sidebarBlock.redirect.label": "Redirect",
"editor.sidebarBlock.script.label": "Script",
"editor.sidebarBlock.setVariable.label": "Set variable",
"editor.sidebarBlock.sheets.label": "Sheets",
"editor.sidebarBlock.start.label": "Start",
"editor.sidebarBlock.text.label": "Text",
"editor.sidebarBlock.typebot.label": "Typebot",
"editor.sidebarBlock.video.label": "Video",
"editor.sidebarBlock.wait.label": "Wait",
"editor.sidebarBlock.webhook.label": "Webhook",
"editor.sidebarBlock.website.label": "Website",
"editor.sidebarBlock.zapier.label": "Zapier",
"editor.sidebarBlock.zemanticAi.label": "Zemantic AI",
"editor.sidebarBlocks.blockType.bubbles.heading": "Bubbles",
"editor.sidebarBlocks.blockType.inputs.heading": "Inputs",
"editor.sidebarBlocks.blockType.integrations.heading": "Integrations",
"editor.sidebarBlocks.blockType.logic.heading": "Logic",
"editor.sidebarBlocks.sidebar.icon.lock.label": "Lock",
"editor.sidebarBlocks.sidebar.icon.unlock.label": "Unlock",
"editor.sidebarBlocks.sidebar.lock.label": "Lock sidebar",
"editor.sidebarBlocks.sidebar.unlock.label": "Unlock sidebar",
"errorMessage": "An error occured",
"folders.createFolderButton.label": "Create a folder",
"folders.createTypebotButton.label": "Create a typebot",
"folders.folderButton.deleteConfirmationMessage": "Are you sure you want to delete <strong>folderName</strong> folder? (Everything inside will be move to your dashboard)",
"folders.typebotButton.delete": "Delete",
"folders.typebotButton.deleteConfirmationMessage": "Are you sure you want to delete your typebot <strong>typebotName</strong>?",
"folders.typebotButton.deleteConfirmationMessageWarning": "All its associated data will be deleted and won't be recoverable.",
"folders.typebotButton.duplicate": "Duplicate",
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Show more options",
"folders.typebotButton.unpublish": "Unpublish",
"pending": "Pending",
"remove": "Remove",
"skip": "Skip",
"templates.buttons.fromScratchButton.label": "Start from scratch",
"templates.buttons.fromTemplateButton.label": "Start from a template",
"templates.buttons.heading": "Create a new typebot",
"templates.buttons.importFileButton.label": "Import a file",
"templates.importFromFileButon.toastError.description": "Failed to parse the file. Are you sure it's a typebot?",
"templates.modal.menuHeading.marketing": "Marketing",
"templates.modal.menuHeading.new.tag": "New",
"templates.modal.menuHeading.other": "Other",
"templates.modal.menuHeading.product": "Product",
"templates.modal.useTemplateButton.label": "Use this template",
"upgrade": "Upgrade",
"workspace.dropdown.logoutButton.label": "Log out",
"workspace.dropdown.newButton.label": "New workspace",
"workspace.membersList.inviteButton.label": "Invite",
"workspace.membersList.inviteInput.placeholder": "colleague@company.com",
"workspace.membersList.title": "Members",
"workspace.membersList.unlockBanner.label": "Upgrade your plan to work with more team members, and unlock awesome power features 🚀",
"workspace.settings.deleteButton.confirmMessage": "Are you sure you want to delete {workspaceName} workspace? All its folders, typebots and results will be deleted forever.",
"workspace.settings.deleteButton.label": "Delete workspace",
"workspace.settings.icon.title": "Icon",
"workspace.settings.modal.menu.billingAndUsage.label": "Billing & Usage",
"workspace.settings.modal.menu.members.label": "Members",
"workspace.settings.modal.menu.myAccount.label": "My account",
"workspace.settings.modal.menu.preferences.label": "Preferences",
"workspace.settings.modal.menu.settings.label": "Settings",
"workspace.settings.modal.menu.version.label": "Version: {version}",
"workspace.settings.modal.menu.workspace.label": "Workspace",
"workspace.settings.name.label": "Name:",
"billing.tiersModal.heading": "Chats pricing table"
}

View File

@ -0,0 +1,251 @@
{
"account.apiTokens.createButton.label": "Créer",
"account.apiTokens.createModal.copyInstruction": "Copie ton token et enregistre le dans un endroit sûr.",
"account.apiTokens.createModal.createButton.label": "Créer un token",
"account.apiTokens.createModal.createHeading": "Créer un token",
"account.apiTokens.createModal.createdHeading": "Token créé",
"account.apiTokens.createModal.doneButton.label": "Terminé",
"account.apiTokens.createModal.nameInput.label": "Tape un nom unique pour votre token afin de le différencier des autres tokens.",
"account.apiTokens.createModal.nameInput.placeholder": "Ex. Zapier, Github, Make.com",
"account.apiTokens.createModal.securityWarning": "Pour des raisons de sécurité, nous ne pourrons pas le montrer à nouveau.",
"account.apiTokens.deleteButton.label": "Supprimer",
"account.apiTokens.deleteConfirmationMessage": "Le token <strong>tokenName</strong> sera définitivement révoqué, es-tu sûr de vouloir continuer ?",
"account.apiTokens.description": "Ces tokens permettent à d'autres applications de contrôler ton compte et tes typebots. Prudence !",
"account.apiTokens.heading": "Tokens API",
"account.apiTokens.table.createdHeader": "Créé",
"account.apiTokens.table.nameHeader": "Nom",
"account.myAccount.changePhotoButton.label": "Changer de photo",
"account.myAccount.changePhotoButton.specification": ".jpg ou.png, max 1MB",
"account.myAccount.emailInput.disabledTooltip": "La mise à jour de l'adresse e-mail n'est pas disponible. Contacte le service d'assistance si tu souhaites la modifier.",
"account.myAccount.emailInput.label": "Adresse e-mail:",
"account.myAccount.nameInput.label": "Nom:",
"account.preferences.appearance.darkLabel": "Sombre",
"account.preferences.appearance.heading": "Apparence",
"account.preferences.appearance.lightLabel": "Clair",
"account.preferences.appearance.systemLabel": "Système",
"account.preferences.graphNavigation.heading": "Navigation de l'éditeur",
"account.preferences.graphNavigation.mouse.description": "Déplace le board en cliquant avec la souris et zoom utilisant la molette",
"account.preferences.graphNavigation.mouse.label": "Souris",
"account.preferences.graphNavigation.trackpad.description": "Déplace le board en déplaçant les 2 doigts et zoom pincant",
"account.preferences.graphNavigation.trackpad.label": "Trackpad",
"account.preferences.language.heading": "Langue",
"account.preferences.language.tooltip": "Les traductions ne sont pas encore complètes. C'est un travail en cours. \uD83E\uDD13",
"analytics.completionRateLabel": "Taux de complétion",
"analytics.notAvailableLabel": "Non disponible",
"analytics.startsLabel": "Démarrés",
"analytics.viewsLabel": "Vues",
"auth.emailSubmitButton.label": "Se connecter",
"auth.error.default": "Essaye de te connecter avec un compte différent.",
"auth.error.email": "Email non trouvé. Essaye de te connecter avec un fournisseur différent.",
"auth.error.oauthNotLinked": "Pour confirmer ton identité, connecte-toi avec le même compte que tu as utilisé à lorigine.",
"auth.error.unknown": "Une erreur est survenue. Essaye à nouveau.",
"auth.magicLink.description": "N'oublie pas de vérifier ton dossier spam.",
"auth.magicLink.title": "Un email avec un lien d'authentification a été envoyé. \uD83E\uDE84",
"auth.noProvider.link": "configurer au moins un fournisseur d'authentification (E-mail, Google, GitHub, Facebook ou Azure AD).",
"auth.noProvider.preLink": "Tu as besoin de",
"auth.orEmailLabel": "Ou avec votre email",
"auth.register.aggreeToTerms": "En vous inscrivant, vous acceptez nos <terms>termsOfService</terms> et <privacy>privacyPolicy</privacy>.",
"auth.register.alreadyHaveAccountLabel.link": "Se connecter",
"auth.register.alreadyHaveAccountLabel.preLink": "Tu as déjà un compte?",
"auth.register.heading": "Créer un compte",
"auth.signin.heading": "Se connecter",
"auth.signin.noAccountLabel.link": "Inscris-toi gratuitement",
"auth.signin.noAccountLabel.preLink": "Tu n'as pas de compte?",
"auth.signinErrorToast.description": "Les inscriptions sont désactivées.",
"auth.signinErrorToast.title": "Non autorisé",
"auth.signinErrorToast.tooManyRequests": "Trop de tentatives de connexion.",
"auth.socialLogin.azureButton.label": "Continuer avec {azureProviderName}",
"auth.socialLogin.customButton.label": "Continuer avec {customProviderName}",
"auth.socialLogin.facebookButton.label": "Continuer avec Facebook",
"auth.socialLogin.githubButton.label": "Continuer avec GitHub",
"auth.socialLogin.gitlabButton.label": "Continuer avec {gitlabProviderName}",
"auth.socialLogin.googleButton.label": "Continuer avec Google",
"back": "Retour",
"billing.billingPortalButton.label": "Portail de facturation",
"billing.contribution.link": "En savoir plus.",
"billing.contribution.preLink": "Typebot contribue à hauteur de 1% de votre abonnement pour éliminer le CO₂ de l'atmosphère.",
"billing.currentSubscription.cancelDate": "Sera annulé le",
"billing.currentSubscription.heading": "Abonnement",
"billing.currentSubscription.pastDueAlert": "Le dernier paiement a échoué. Rendez-vous sur le portail de facturation pour effectuer la procédure et éviter l'annulation de votre abonnement.",
"billing.currentSubscription.subheading": "Abonnement actuel du workspace :",
"billing.customLimit.link": "Discutons-en!",
"billing.customLimit.preLink": "Tu as besoin de limites personnalisées ? De fonctionnalités spécifiques ?",
"billing.invoices.empty": "Aucune facture trouvée pour ce workspace.",
"billing.invoices.heading": "Factures",
"billing.invoices.paidAt": "Payé le",
"billing.invoices.subtotal": "Sous-total",
"billing.limitMessage.analytics": "débloquer des analyses approfondies",
"billing.limitMessage.brand": "supprimer la marque",
"billing.limitMessage.customDomain": "ajouter des domaines personnalisés",
"billing.limitMessage.fileInput": "utiliser des blocs de saisie de fichiers",
"billing.limitMessage.folder": "créer des dossiers",
"billing.preCheckoutModal.companyInput.label": "Nom de l'entreprise :",
"billing.preCheckoutModal.emailInput.label": "E-mail :",
"billing.preCheckoutModal.submitButton.label": "Continuer",
"billing.preCheckoutModal.taxId.label": "Numéro de TVA :",
"billing.preCheckoutModal.taxId.placeholder": "Type",
"billing.pricingCard.chatsPerMonth": "chats/mois",
"billing.pricingCard.chatsTooltip": "Un chat est comptabilisé chaque fois qu'un utilisateur démarre une discussion. Il est indépendant du nombre de messages qu'il envoie et reçoit.",
"billing.pricingCard.heading": "Passer à <strong>plan</strong>",
"billing.pricingCard.perMonth": "/ mois",
"billing.pricingCard.plus": ", plus :",
"billing.pricingCard.pro.analytics": "Analyses approfondies",
"billing.pricingCard.pro.customDomains": "Domaines personnalisés",
"billing.pricingCard.pro.description": "Pour les agences et les startups en croissance.",
"billing.pricingCard.pro.everythingFromStarter": "Tout ce qu'il y a dans Starter",
"billing.pricingCard.pro.includedSeats": "5 collègues inclus",
"billing.pricingCard.pro.mostPopularLabel": "Le plus populaire",
"billing.pricingCard.pro.whatsAppIntegration": "Intégration WhatsApp",
"billing.pricingCard.starter.brandingRemoved": "Marque enlevée",
"billing.pricingCard.starter.createFolders": "Créer des dossiers",
"billing.pricingCard.starter.description": "Pour les particuliers et les petites entreprises.",
"billing.pricingCard.starter.fileUploadBlock": "Bloc d'upload de fichier",
"billing.pricingCard.starter.includedSeats": "2 collègues inclus",
"billing.pricingCard.upgradeButton.current": "Abonnement actuel",
"billing.updateSuccessToast.description": "Ton abonnement {plan} a été mis à jour avec succès \uD83C\uDF89",
"billing.upgradeAlert.buttonDefaultLabel": "Plus d'informations",
"billing.upgradeLimitLabel": "Tu dois mettre à niveau ton abonnement pour {type}",
"billing.usage.chats.alert.soonReach": "Tes typebots sont populaires ! Tu atteindras bientôt la limite de chats de votre abonnement. \uD83D\uDE80",
"billing.usage.chats.alert.updatePlan": "Assure-toi de mettre à jour votre abonnement pour augmenter cette limite et continuer à discuter avec vos utilisateurs.",
"billing.usage.chats.heading": "Chats",
"billing.usage.heading": "Utilisation",
"billing.usage.unlimited": "Illimité",
"cancel": "Annuler",
"confirmModal.defaultTitle": "Es-tu sûr ?",
"dashboard.header.settingsButton.label": "Paramètres & Membres",
"dashboard.redirectionMessage": "Redirection en cours...",
"dashboard.title": "Mes typebots",
"delete": "Supprimer",
"downgrade": "Downgrade",
"blocks.bubbles.embed.blockCard.tooltip": "Intégrer un pdf, un iframe, un site web...",
"blocks.inputs.fileUpload.blockCard.tooltip": "Télécharger des fichiers",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"editor.blockCard.logicBlock.tooltip.code.label": "Exécuter du code Javascript",
"editor.blockCard.logicBlock.tooltip.jump.label": "Passer rapidement au groupe suivant",
"editor.blockCard.logicBlock.tooltip.typebotLink.label": "Lier et exécuter un autre typebot",
"editor.blocks.bubbles.audio.node.clickToEdit.text": "Cliquez pour modifier...",
"editor.blocks.bubbles.audio.settings.autoplay.label": "Activer la lecture automatique",
"editor.blocks.bubbles.audio.settings.chooseFile.label": "Choisir un fichier",
"editor.blocks.bubbles.audio.settings.embedLink.label": "Lien intégré",
"editor.blocks.bubbles.audio.settings.upload.label": "Uploader",
"editor.blocks.bubbles.audio.settings.worksWith.placeholder": "Collez le lien du fichier audio...",
"editor.blocks.bubbles.audio.settings.worksWith.text": "Fonctionne avec les fichiers .MP3 et .WAV",
"editor.blocks.bubbles.embed.node.clickToEdit.text": "Cliquez pour modifier...",
"editor.blocks.bubbles.embed.node.show.text": "Afficher l'intégration",
"editor.blocks.bubbles.embed.settings.numberInput.unit": "px",
"editor.blocks.bubbles.embed.settings.worksWith.placeholder": "Collez le lien ou le code...",
"editor.blocks.bubbles.embed.settings.worksWith.text": "Fonctionne avec les PDF, les iframes, les sites web...",
"editor.blocks.bubbles.image.node.clickToEdit.text": "Cliquez pour modifier...",
"editor.blocks.bubbles.image.switchWithLabel.onClick.label": "Redirection au clic",
"editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder": "Texte alternatif du lien (description)",
"editor.blocks.bubbles.textEditor.plate.label": "Éditeur de texte",
"editor.blocks.bubbles.textEditor.searchVariable.placeholder": "Rechercher une variable",
"editor.blocks.bubbles.video.node.clickToEdit.text": "Cliquez pour modifier...",
"editor.blocks.bubbles.video.settings.numberInput.unit": "px",
"editor.blocks.bubbles.video.settings.worksWith.placeholder": "Collez le lien de la vidéo...",
"editor.blocks.bubbles.video.settings.worksWith.text": "Fonctionne avec Youtube, Vimeo et autres",
"editor.blocks.start.text": "Démarrer",
"editor.editableTypebotName.tooltip.rename.label": "Renommer",
"editor.gettingStartedModal.editorBasics.heading": "Principes de base de l'éditeur",
"editor.gettingStartedModal.editorBasics.list.four.label": "Prévisualisez votre bot en cliquant sur le bouton \"Tester\" en haut à droite.",
"editor.gettingStartedModal.editorBasics.list.label": "N'hésitez pas à utiliser la bulle en bas à droite pour me poser des questions. Je réponds généralement dans les 24 heures. \uD83D\uDE03",
"editor.gettingStartedModal.editorBasics.list.one.label": "La barre latérale de gauche contient des blocs que vous pouvez glisser-déposer sur le graph.",
"editor.gettingStartedModal.editorBasics.list.three.label": "Connectez les groupes ensemble.",
"editor.gettingStartedModal.editorBasics.list.two.label": "Vous pouvez regrouper les blocs en les déposant les uns au-dessus ou en-dessous des autres.",
"editor.gettingStartedModal.seeAction.item.label": "Autres vidéos",
"editor.gettingStartedModal.seeAction.label": "Voir en action",
"editor.gettingStartedModal.seeAction.time": "5 minutes",
"editor.headers.flowButton.label": "Flow",
"editor.headers.helpButton.label": "Aide",
"editor.headers.previewButton.label": "Tester",
"editor.headers.resultsButton.label": "Résultats",
"editor.headers.savingSpinner.label": "Enregistrement...",
"editor.headers.settingsButton.label": "Paramètres",
"editor.headers.shareButton.label": "Partage",
"editor.headers.themeButton.label": "Thème",
"editor.sidebarBlock.abTest.label": "AB Test",
"editor.sidebarBlock.analytics.label": "Analytics",
"editor.sidebarBlock.audio.label": "Audio",
"editor.sidebarBlock.button.label": "Boutons",
"editor.sidebarBlock.chatwoot.label": "Chatwoot",
"editor.sidebarBlock.condition.label": "Condition",
"editor.sidebarBlock.date.label": "Date",
"editor.sidebarBlock.email.label": "Email",
"editor.sidebarBlock.embed.label": "Iframe",
"editor.sidebarBlock.file.label": "Fichier",
"editor.sidebarBlock.image.label": "Image",
"editor.sidebarBlock.jump.label": "Sauter",
"editor.sidebarBlock.makecom.label": "Make.com",
"editor.sidebarBlock.number.label": "Nombre",
"editor.sidebarBlock.openai.label": "OpenAI",
"editor.sidebarBlock.pabbly.label": "Pabbly",
"editor.sidebarBlock.payment.label": "Paiement",
"editor.sidebarBlock.phone.label": "Téléphone",
"editor.sidebarBlock.picChoice.label": "Choix image",
"editor.sidebarBlock.pixel.label": "Pixel",
"editor.sidebarBlock.rating.label": "Évaluation",
"editor.sidebarBlock.redirect.label": "Rediriger",
"editor.sidebarBlock.script.label": "Script",
"editor.sidebarBlock.setVariable.label": "Définir variable",
"editor.sidebarBlock.sheets.label": "Sheets",
"editor.sidebarBlock.start.label": "Démarrer",
"editor.sidebarBlock.text.label": "Texte",
"editor.sidebarBlock.typebot.label": "Typebot",
"editor.sidebarBlock.video.label": "Vidéo",
"editor.sidebarBlock.wait.label": "Attendre",
"editor.sidebarBlock.webhook.label": "Webhook",
"editor.sidebarBlock.website.label": "Site web",
"editor.sidebarBlock.zapier.label": "Zapier",
"editor.sidebarBlock.zemanticAi.label": "Zemantic AI",
"editor.sidebarBlocks.blockType.bubbles.heading": "Bulles",
"editor.sidebarBlocks.blockType.inputs.heading": "Inputs",
"editor.sidebarBlocks.blockType.integrations.heading": "Intégrations",
"editor.sidebarBlocks.blockType.logic.heading": "Logique",
"editor.sidebarBlocks.sidebar.icon.lock.label": "Fermée",
"editor.sidebarBlocks.sidebar.icon.unlock.label": "Ouverte",
"editor.sidebarBlocks.sidebar.lock.label": "Fermer la barre latérale",
"editor.sidebarBlocks.sidebar.unlock.label": "Ouvrir la barre latérale",
"errorMessage": "Une erreur s'est produite",
"folders.createFolderButton.label": "Créer un dossier",
"folders.createTypebotButton.label": "Créer un typebot",
"folders.folderButton.deleteConfirmationMessage": "Es-tu sûr de vouloir supprimer le dossier <strong>folderName</strong> ? (Tout ce qui est à l'intérieur sera déplacé dans le dossier parent ou sur votre tableau de bord)",
"folders.typebotButton.delete": "Supprimer",
"folders.typebotButton.deleteConfirmationMessage": "Es-tu sûr de vouloir supprimer votre typebot <strong>typebotName</strong> ?",
"folders.typebotButton.deleteConfirmationMessageWarning": "Toutes les données associées seront supprimées et ne pourront pas être récupérées.",
"folders.typebotButton.duplicate": "Dupliquer",
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Afficher plus d'options",
"folders.typebotButton.unpublish": "Dépublier",
"pending": "En attente",
"remove": "Retirer",
"skip": "Passer",
"templates.buttons.fromScratchButton.label": "Commencer à partir de zéro",
"templates.buttons.fromTemplateButton.label": "Commencer à partir d'un modèle",
"templates.buttons.heading": "Créer un nouveau typebot",
"templates.buttons.importFileButton.label": "Importer un fichier",
"templates.importFromFileButon.toastError.description": "Échec de l'analyse du fichier. Es-tu sûr que c'est un typebot ?",
"templates.modal.menuHeading.marketing": "Marketing",
"templates.modal.menuHeading.new.tag": "Nouveau",
"templates.modal.menuHeading.other": "Autre",
"templates.modal.menuHeading.product": "Produit",
"templates.modal.useTemplateButton.label": "Utiliser ce modèle",
"upgrade": "Upgrade",
"workspace.dropdown.logoutButton.label": "Déconnexion",
"workspace.dropdown.newButton.label": "Nouveau workspace",
"workspace.membersList.inviteButton.label": "Inviter",
"workspace.membersList.inviteInput.placeholder": "collegue@entreprise.fr",
"workspace.membersList.title": "Membres",
"workspace.membersList.unlockBanner.label": "Upgrade ton plan pour travailler les membres de ton équipe et débloquer d'autres fonctionnalités puissantes \uD83D\uDE80",
"workspace.settings.deleteButton.confirmMessage": "Es-tu sûr(e) de vouloir supprimer le workspace {workspaceName} ? Tous ses dossiers, typebots et résultats seront supprimés pour toujours.",
"workspace.settings.deleteButton.label": "Supprimer le workspace",
"workspace.settings.icon.title": "Icône",
"workspace.settings.modal.menu.billingAndUsage.label": "Facturation et utilisation",
"workspace.settings.modal.menu.members.label": "Membres",
"workspace.settings.modal.menu.myAccount.label": "Mon compte",
"workspace.settings.modal.menu.preferences.label": "Préférences",
"workspace.settings.modal.menu.settings.label": "Paramètres",
"workspace.settings.modal.menu.version.label": "Version : {version}",
"workspace.settings.modal.menu.workspace.label": "Workspace",
"workspace.settings.name.label": "Nom:"
}

View File

@ -0,0 +1,251 @@
{
"account.apiTokens.createButton.label": "Criar",
"account.apiTokens.createModal.copyInstruction": "Por favor, copie seu token e guarde-o em um lugar seguro.",
"account.apiTokens.createModal.createButton.label": "Criar token",
"account.apiTokens.createModal.createHeading": "Criar Token",
"account.apiTokens.createModal.createdHeading": "Token Criado",
"account.apiTokens.createModal.doneButton.label": "Concluído",
"account.apiTokens.createModal.nameInput.label": "Insira um nome único para o seu token para diferenciá-lo de outros tokens.",
"account.apiTokens.createModal.nameInput.placeholder": "Ex. Zapier, Github, Make.com",
"account.apiTokens.createModal.securityWarning": "Por motivos de segurança, não podemos mostrá-lo novamente.",
"account.apiTokens.deleteButton.label": "Excluir",
"account.apiTokens.deleteConfirmationMessage": "O token <strong>tokenName</strong> será revogado permanentemente. Tem certeza de que deseja continuar?",
"account.apiTokens.description": "Esses tokens permitem que outros aplicativos controlem toda a sua conta e typebots. Tenha cuidado!",
"account.apiTokens.heading": "Tokens de API",
"account.apiTokens.table.createdHeader": "Criado",
"account.apiTokens.table.nameHeader": "Nome",
"account.myAccount.changePhotoButton.label": "Alterar foto",
"account.myAccount.changePhotoButton.specification": ".jpg ou.png, máximo 1MB",
"account.myAccount.emailInput.disabledTooltip": "A atualização do e-mail não está disponível. Entre em contato com o suporte se quiser alterá-lo.",
"account.myAccount.emailInput.label": "Endereço de e-mail:",
"account.myAccount.nameInput.label": "Nome:",
"account.preferences.appearance.darkLabel": "Escuro",
"account.preferences.appearance.heading": "Aparência",
"account.preferences.appearance.lightLabel": "Claro",
"account.preferences.appearance.systemLabel": "Sistema",
"account.preferences.graphNavigation.heading": "Navegação do Editor",
"account.preferences.graphNavigation.mouse.description": "Mova arrastando o quadro e amplie/reduza usando a roda de rolagem",
"account.preferences.graphNavigation.mouse.label": "Mouse",
"account.preferences.graphNavigation.trackpad.description": "Mova o quadro usando 2 dedos e amplie/reduza fazendo pinça",
"account.preferences.graphNavigation.trackpad.label": "Trackpad",
"account.preferences.language.heading": "Idioma",
"account.preferences.language.tooltip": "As traduções ainda não estão completas. É um trabalho em andamento. \uD83E\uDD13",
"analytics.completionRateLabel": "Taxa de conclusão",
"analytics.notAvailableLabel": "Não disponível",
"analytics.startsLabel": "Inícios",
"analytics.viewsLabel": "Visualizações",
"auth.emailSubmitButton.label": "Enviar",
"auth.error.default": "Tente entrar com uma conta diferente.",
"auth.error.email": "E-mail não encontrado. Tente entrar com um provedor diferente.",
"auth.error.oauthNotLinked": "Já existe uma conta vinculada a esse E-mail, entre com a mesma conta que você usou originalmente.",
"auth.error.unknown": "Ocorreu um erro. Tente novamente.",
"auth.magicLink.description": "Certifique-se de verificar sua pasta de spam.",
"auth.magicLink.title": "Um email com o link mágico foi enviado. \uD83E\uDE84",
"auth.noProvider.link": "configurar pelo menos um provedor de autenticação (E-mail, Google, GitHub, Facebook ou Azure AD).",
"auth.noProvider.preLink": "Você precisa",
"auth.orEmailLabel": "Ou com seu email",
"auth.register.aggreeToTerms": "Ao se cadastrar, você concorda com nossos <terms>termsOfService</terms> e <privacy>privacyPolicy</privacy>.",
"auth.register.alreadyHaveAccountLabel.link": "Entrar",
"auth.register.alreadyHaveAccountLabel.preLink": "Já tem uma conta?",
"auth.register.heading": "Criar uma conta",
"auth.signin.heading": "Entrar",
"auth.signin.noAccountLabel.link": "Registre-se gratuitamente",
"auth.signin.noAccountLabel.preLink": "Não tem uma conta?",
"auth.signinErrorToast.description": "As inscrições estão desativadas.",
"auth.signinErrorToast.title": "Não autorizado",
"auth.signinErrorToast.tooManyRequests": "Muitas tentativas. Tente novamente mais tarde.",
"auth.socialLogin.azureButton.label": "Continuar com {azureProviderName}",
"auth.socialLogin.customButton.label": "Continuar com {customProviderName}",
"auth.socialLogin.facebookButton.label": "Continuar com Facebook",
"auth.socialLogin.githubButton.label": "Continuar com GitHub",
"auth.socialLogin.gitlabButton.label": "Continuar com {gitlabProviderName}",
"auth.socialLogin.googleButton.label": "Continuar com Google",
"back": "Voltar",
"billing.billingPortalButton.label": "Portal de cobrança",
"billing.contribution.link": "Saiba mais.",
"billing.contribution.preLink": "A Typebot está contribuindo com 1% da sua assinatura para remover o CO₂ da atmosfera.",
"billing.currentSubscription.cancelDate": "Será cancelado em",
"billing.currentSubscription.heading": "Assinatura",
"billing.currentSubscription.pastDueAlert": "O último pagamento falhou. Acesse o portal de faturamento para prosseguir e evitar o cancelamento da sua assinatura.",
"billing.currentSubscription.subheading": "Assinatura atual do espaço de trabalho:",
"billing.customLimit.link": "Vamos conversar!",
"billing.customLimit.preLink": "Precisa de limites personalizados? Recursos específicos?",
"billing.invoices.empty": "Nenhuma fatura encontrada para este espaço de trabalho.",
"billing.invoices.heading": "Faturas",
"billing.invoices.paidAt": "Pago em",
"billing.invoices.subtotal": "Subtotal",
"billing.limitMessage.analytics": "desbloquear análises aprofundadas",
"billing.limitMessage.brand": "remover a marca",
"billing.limitMessage.customDomain": "adicionar domínios personalizados",
"billing.limitMessage.fileInput": "usar blocos de envio de arquivo",
"billing.limitMessage.folder": "criar pastas",
"billing.preCheckoutModal.companyInput.label": "Nome da empresa:",
"billing.preCheckoutModal.emailInput.label": "E-mail:",
"billing.preCheckoutModal.submitButton.label": "Ir para a finalização da compra",
"billing.preCheckoutModal.taxId.label": "Identificação fiscal (CPF):",
"billing.preCheckoutModal.taxId.placeholder": "Tipo de ID",
"billing.pricingCard.chatsPerMonth": "chats/mês",
"billing.pricingCard.chatsTooltip": "Um chat é contado sempre que um usuário inicia uma discussão. Ele é independente do número de mensagens que ele envia e recebe.",
"billing.pricingCard.heading": "Mudar para <strong>plan</strong>",
"billing.pricingCard.perMonth": "/ mês",
"billing.pricingCard.plus": ", mais:",
"billing.pricingCard.pro.analytics": "Análises aprofundadas",
"billing.pricingCard.pro.customDomains": "Domínios personalizados",
"billing.pricingCard.pro.description": "Para agências e startups em crescimento.",
"billing.pricingCard.pro.everythingFromStarter": "Tudo em Starter",
"billing.pricingCard.pro.includedSeats": "5 assentos incluídos",
"billing.pricingCard.pro.mostPopularLabel": "Mais popular",
"billing.pricingCard.pro.whatsAppIntegration": "Integração do WhatsApp",
"billing.pricingCard.starter.brandingRemoved": "Marca removida",
"billing.pricingCard.starter.createFolders": "Criar pastas",
"billing.pricingCard.starter.description": "Para indivíduos e pequenas empresas.",
"billing.pricingCard.starter.fileUploadBlock": "Bloco de envio de arquivo",
"billing.pricingCard.starter.includedSeats": "2 assentos incluídos",
"billing.pricingCard.upgradeButton.current": "Sua assinatura atual",
"billing.updateSuccessToast.description": "Sua assinatura {plan} foi atualizada com sucesso \uD83C\uDF89",
"billing.upgradeAlert.buttonDefaultLabel": "Mais informações",
"billing.upgradeLimitLabel": "Você precisa atualizar sua assinatura para {type}",
"billing.usage.chats.alert.soonReach": "Seus typebots são populares! Você logo alcançará o limite de chats de seu plano. \uD83D\uDE80",
"billing.usage.chats.alert.updatePlan": "Certifique-se de atualizar seu plano para aumentar esse limite e continuar conversando com seus usuários.",
"billing.usage.chats.heading": "Chats",
"billing.usage.heading": "Uso",
"billing.usage.unlimited": "Ilimitado",
"cancel": "Cancelar",
"confirmModal.defaultTitle": "Tem certeza?",
"dashboard.header.settingsButton.label": "Configurações & Membros",
"dashboard.redirectionMessage": "Você está sendo redirecionado...",
"dashboard.title": "Meus typebots",
"delete": "Apagar",
"downgrade": "Downgrade",
"blocks.bubbles.embed.blockCard.tooltip": "Incorporar pdf, iframe, website...",
"blocks.inputs.fileUpload.blockCard.tooltip": "Carregar Ficheiros",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"editor.blockCard.logicBlock.tooltip.code.label": "Executar código Javascript",
"editor.blockCard.logicBlock.tooltip.jump.label": "Encaminhar fluxo para outro grupo",
"editor.blockCard.logicBlock.tooltip.typebotLink.label": "Link e salte para outro typebot",
"editor.blocks.bubbles.audio.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.audio.settings.autoplay.label": "Ativar reprodução automática",
"editor.blocks.bubbles.audio.settings.chooseFile.label": "Escolher um arquivo",
"editor.blocks.bubbles.audio.settings.embedLink.label": "Incorporar link",
"editor.blocks.bubbles.audio.settings.upload.label": "Carregar",
"editor.blocks.bubbles.audio.settings.worksWith.placeholder": "Colar o link do arquivo de áudio...",
"editor.blocks.bubbles.audio.settings.worksWith.text": "Compatível com .MP3s e .WAVs",
"editor.blocks.bubbles.embed.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.embed.node.show.text": "Mostrar incorporação",
"editor.blocks.bubbles.embed.settings.numberInput.unit": "px",
"editor.blocks.bubbles.embed.settings.worksWith.placeholder": "Colar o link ou código...",
"editor.blocks.bubbles.embed.settings.worksWith.text": "Compatível com PDFs, iframes, websites...",
"editor.blocks.bubbles.image.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.image.switchWithLabel.onClick.label": "Link ao clicar",
"editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder": "Texto alternativo do link (descrição)",
"editor.blocks.bubbles.textEditor.plate.label": "Editor de texto",
"editor.blocks.bubbles.textEditor.searchVariable.placeholder": "Pesquisar uma variável",
"editor.blocks.bubbles.video.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.video.settings.numberInput.unit": "px",
"editor.blocks.bubbles.video.settings.worksWith.placeholder": "Colar o link do vídeo...",
"editor.blocks.bubbles.video.settings.worksWith.text": "Compatível com Youtube, Vimeo e outros",
"editor.blocks.start.text": "Início",
"editor.editableTypebotName.tooltip.rename.label": "Renomear",
"editor.gettingStartedModal.editorBasics.heading": "Fundamentos do Editor",
"editor.gettingStartedModal.editorBasics.list.four.label": "Pré-visualize o seu bot ao clicar no botão de visualizar no canto superior direito",
"editor.gettingStartedModal.editorBasics.list.label": "Sinta-se à vontade para usar o chat no canto inferior direito para entrar em contato se tiver alguma dúvida. Normalmente, respondo nas próximas 24 horas. \uD83D\uDE03",
"editor.gettingStartedModal.editorBasics.list.one.label": "A barra lateral esquerda contém blocos que podem ser arrastados e soltos no quadro.",
"editor.gettingStartedModal.editorBasics.list.three.label": "Conecte os grupos entre eles",
"editor.gettingStartedModal.editorBasics.list.two.label": "Você pode agrupar blocos juntos, colocando-os abaixo ou acima dos outros",
"editor.gettingStartedModal.seeAction.item.label": "Outros vídeos",
"editor.gettingStartedModal.seeAction.label": "Veja como funciona em",
"editor.gettingStartedModal.seeAction.time": "5 minutos",
"editor.headers.flowButton.label": "Fluxo",
"editor.headers.helpButton.label": "Ajuda",
"editor.headers.previewButton.label": "Visualizar",
"editor.headers.resultsButton.label": "Resultados",
"editor.headers.savingSpinner.label": "Salvando...",
"editor.headers.settingsButton.label": "Configurações",
"editor.headers.shareButton.label": "Compartilhar",
"editor.headers.themeButton.label": "Tema",
"editor.sidebarBlock.abTest.label": "Teste AB",
"editor.sidebarBlock.analytics.label": "Analytics",
"editor.sidebarBlock.audio.label": "Áudio",
"editor.sidebarBlock.button.label": "Botão",
"editor.sidebarBlock.chatwoot.label": "Chatwoot",
"editor.sidebarBlock.condition.label": "Condição",
"editor.sidebarBlock.date.label": "Data",
"editor.sidebarBlock.email.label": "Email",
"editor.sidebarBlock.embed.label": "Incorporar",
"editor.sidebarBlock.file.label": "Arquivo",
"editor.sidebarBlock.image.label": "Imagem",
"editor.sidebarBlock.jump.label": "Pular",
"editor.sidebarBlock.makecom.label": "Make.com",
"editor.sidebarBlock.number.label": "Número",
"editor.sidebarBlock.openai.label": "OpenAI",
"editor.sidebarBlock.pabbly.label": "Pabbly",
"editor.sidebarBlock.payment.label": "Pagamento",
"editor.sidebarBlock.phone.label": "Telefone",
"editor.sidebarBlock.picChoice.label": "Seleção de Imagem",
"editor.sidebarBlock.pixel.label": "Pixel",
"editor.sidebarBlock.rating.label": "Avaliação",
"editor.sidebarBlock.redirect.label": "Redirecionar",
"editor.sidebarBlock.script.label": "Script",
"editor.sidebarBlock.setVariable.label": "Variável",
"editor.sidebarBlock.sheets.label": "Sheets",
"editor.sidebarBlock.start.label": "Início",
"editor.sidebarBlock.text.label": "Texto",
"editor.sidebarBlock.typebot.label": "Typebot",
"editor.sidebarBlock.video.label": "Vídeo",
"editor.sidebarBlock.wait.label": "Espera",
"editor.sidebarBlock.webhook.label": "Webhook",
"editor.sidebarBlock.website.label": "Website",
"editor.sidebarBlock.zapier.label": "Zapier",
"editor.sidebarBlock.zemanticAi.label": "Zemantic AI",
"editor.sidebarBlocks.blockType.bubbles.heading": "Bubbles",
"editor.sidebarBlocks.blockType.inputs.heading": "Inputs",
"editor.sidebarBlocks.blockType.integrations.heading": "Integrações",
"editor.sidebarBlocks.blockType.logic.heading": "Condicionais",
"editor.sidebarBlocks.sidebar.icon.lock.label": "Bloquear",
"editor.sidebarBlocks.sidebar.icon.unlock.label": "Desbloquear",
"editor.sidebarBlocks.sidebar.lock.label": "Bloquear barra lateral",
"editor.sidebarBlocks.sidebar.unlock.label": "Desbloquear barra lateral",
"errorMessage": "Ocorreu um erro",
"folders.createFolderButton.label": "Criar uma pasta",
"folders.createTypebotButton.label": "Criar um typebot",
"folders.folderButton.deleteConfirmationMessage": "Tem certeza de que deseja excluir a pasta <strong>folderName</strong>? (Tudo o que estiver dentro será movido para o seu painel)",
"folders.typebotButton.delete": "Apagar",
"folders.typebotButton.deleteConfirmationMessage": "Tem certeza de que deseja excluir seu typebot <strong>typebotName</strong>?",
"folders.typebotButton.deleteConfirmationMessageWarning": "Todos os dados associados serão excluídos e não poderão ser recuperados.",
"folders.typebotButton.duplicate": "Duplicar",
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Mostrar mais opções",
"folders.typebotButton.unpublish": "Despublicar",
"pending": "Pendente",
"remove": "Remover",
"skip": "Pular",
"templates.buttons.fromScratchButton.label": "Comece do zero",
"templates.buttons.fromTemplateButton.label": "Comece a partir de um modelo",
"templates.buttons.heading": "Criar um novo typebot",
"templates.buttons.importFileButton.label": "Importar um arquivo",
"templates.importFromFileButon.toastError.description": "Falha ao analisar o arquivo. Tem certeza de que é um typebot?",
"templates.modal.menuHeading.marketing": "Marketing",
"templates.modal.menuHeading.new.tag": "Novo",
"templates.modal.menuHeading.other": "Outros",
"templates.modal.menuHeading.product": "Produto",
"templates.modal.useTemplateButton.label": "Usar esse modelo",
"upgrade": "Upgrade",
"workspace.dropdown.logoutButton.label": "Sair",
"workspace.dropdown.newButton.label": "Novo espaço de trabalho",
"workspace.membersList.inviteButton.label": "Convidar",
"workspace.membersList.inviteInput.placeholder": "colega@empresa.com",
"workspace.membersList.title": "Membros",
"workspace.membersList.unlockBanner.label": "Atualize seu plano para trabalhar com mais membros da equipe e desbloqueie recursos incríveis \uD83D\uDE80",
"workspace.settings.deleteButton.confirmMessage": "Você tem certeza de que deseja excluir o espaço de trabalho {workspaceName}? Todas as suas pastas, typebots e resultados serão excluídos permanentemente.",
"workspace.settings.deleteButton.label": "Excluir espaço de trabalho",
"workspace.settings.icon.title": "Ícone",
"workspace.settings.modal.menu.billingAndUsage.label": "Faturamento e uso",
"workspace.settings.modal.menu.members.label": "Membros",
"workspace.settings.modal.menu.myAccount.label": "Minha conta",
"workspace.settings.modal.menu.preferences.label": "Preferências",
"workspace.settings.modal.menu.settings.label": "Configurações",
"workspace.settings.modal.menu.version.label": "Versão: {version}",
"workspace.settings.modal.menu.workspace.label": "Espaço de trabalho",
"workspace.settings.name.label": "Nome:"
}

View File

@ -0,0 +1,251 @@
{
"account.apiTokens.createButton.label": "Criar",
"account.apiTokens.createModal.copyInstruction": "Por favor, copie o seu token e guarde-o num lugar seguro.",
"account.apiTokens.createModal.createButton.label": "Criar token",
"account.apiTokens.createModal.createHeading": "Criar Token",
"account.apiTokens.createModal.createdHeading": "Token Criado",
"account.apiTokens.createModal.doneButton.label": "Concluído",
"account.apiTokens.createModal.nameInput.label": "Insira um nome único para o seu token para o diferenciar de outros tokens.",
"account.apiTokens.createModal.nameInput.placeholder": "Ex. Zapier, Github, Make.com",
"account.apiTokens.createModal.securityWarning": "Por razões de segurança, não o podemos mostrar novamente.",
"account.apiTokens.deleteButton.label": "Excluir",
"account.apiTokens.deleteConfirmationMessage": "O token <strong>tokenName</strong> será revogado permanentemente. Tem a certeza de que deseja continuar?",
"account.apiTokens.description": "Estes tokens permitem que outras aplicações controlem toda a sua conta e typebots. Tenha cuidado!",
"account.apiTokens.heading": "Tokens de API",
"account.apiTokens.table.createdHeader": "Criado",
"account.apiTokens.table.nameHeader": "Nome",
"account.myAccount.changePhotoButton.label": "Alterar foto",
"account.myAccount.changePhotoButton.specification": ".jpg ou.png, máximo 1MB",
"account.myAccount.emailInput.disabledTooltip": "A atualização do e-mail não está disponível. Entre em contacto com o apoio se quiser alterá-lo.",
"account.myAccount.emailInput.label": "Endereço de e-mail:",
"account.myAccount.nameInput.label": "Nome:",
"account.preferences.appearance.darkLabel": "Escuro",
"account.preferences.appearance.heading": "Aparência",
"account.preferences.appearance.lightLabel": "Claro",
"account.preferences.appearance.systemLabel": "Sistema",
"account.preferences.graphNavigation.heading": "Navegação do Editor",
"account.preferences.graphNavigation.mouse.description": "Mova arrastando o quadro e amplie/reduza usando a roda de deslocamento",
"account.preferences.graphNavigation.mouse.label": "Rato",
"account.preferences.graphNavigation.trackpad.description": "Mova o quadro usando 2 dedos e amplie/reduza fazendo pinça",
"account.preferences.graphNavigation.trackpad.label": "Trackpad",
"account.preferences.language.heading": "Idioma",
"account.preferences.language.tooltip": "As traduções ainda não estão completas. É um trabalho em curso. \uD83E\uDD13",
"analytics.completionRateLabel": "Taxa de conclusão",
"analytics.notAvailableLabel": "Não disponível",
"analytics.startsLabel": "Inícios",
"analytics.viewsLabel": "Visualizações",
"auth.emailSubmitButton.label": "Enviar",
"auth.error.default": "Tente entrar com uma conta diferente.",
"auth.error.email": "E-mail não encontrado. Tente entrar com um fornecedor diferente.",
"auth.error.oauthNotLinked": "Para confirmar a sua identidade, entre com a mesma conta que usou originalmente.",
"auth.error.unknown": "Ocorreu um erro. Tente novamente.",
"auth.magicLink.description": "Certifique-se de verificar a sua pasta de spam.",
"auth.magicLink.title": "Foi enviado um e-mail com a ligação mágica. \uD83E\uDE84",
"auth.noProvider.link": "configurar pelo menos um fornecedor de autenticação (E-mail, Google, GitHub, Facebook ou Azure AD).",
"auth.noProvider.preLink": "Precisa de",
"auth.orEmailLabel": "Ou com o seu e-mail",
"auth.register.aggreeToTerms": "Ao registar-se, concorda com os nossos <terms>termsOfService</terms> e <privacy>privacyPolicy</privacy>.",
"auth.register.alreadyHaveAccountLabel.link": "Entrar",
"auth.register.alreadyHaveAccountLabel.preLink": "Já tem uma conta?",
"auth.register.heading": "Criar uma conta",
"auth.signin.heading": "Entrar",
"auth.signin.noAccountLabel.link": "Registe-se gratuitamente",
"auth.signin.noAccountLabel.preLink": "Não tem uma conta?",
"auth.signinErrorToast.description": "As inscrições estão desativadas.",
"auth.signinErrorToast.title": "Não autorizado",
"auth.signinErrorToast.tooManyRequests": "Muitas tentativas. Tente novamente mais tarde.",
"auth.socialLogin.azureButton.label": "Continuar com {azureProviderName}",
"auth.socialLogin.customButton.label": "Continuar com {customProviderName}",
"auth.socialLogin.facebookButton.label": "Continuar com Facebook",
"auth.socialLogin.githubButton.label": "Continuar com GitHub",
"auth.socialLogin.gitlabButton.label": "Continuar com {gitlabProviderName}",
"auth.socialLogin.googleButton.label": "Continuar com Google",
"back": "Voltar",
"billing.billingPortalButton.label": "Portal de facturação",
"billing.contribution.link": "Saiba mais.",
"billing.contribution.preLink": "A Typebot está a contribuir com 1% da sua subscrição para remover o CO₂ da atmosfera.",
"billing.currentSubscription.cancelDate": "Será cancelado em",
"billing.currentSubscription.heading": "Subscrição",
"billing.currentSubscription.pastDueAlert": "O último pagamento falhou. Acesse o portal de faturamento para continuar e evitar o cancelamento da sua assinatura.",
"billing.currentSubscription.subheading": "Subscrição actual do espaço de trabalho:",
"billing.customLimit.link": "Vamos falar!",
"billing.customLimit.preLink": "Precisa de limites personalizados? Funcionalidades específicas?",
"billing.invoices.empty": "Nenhuma factura encontrada para este espaço de trabalho.",
"billing.invoices.heading": "Facturas",
"billing.invoices.paidAt": "Pago em",
"billing.invoices.subtotal": "Subtotal",
"billing.limitMessage.analytics": "desbloquear análises aprofundadas",
"billing.limitMessage.brand": "remover a marca",
"billing.limitMessage.customDomain": "adicionar domínios personalizados",
"billing.limitMessage.fileInput": "usar blocos de envio de ficheiros",
"billing.limitMessage.folder": "criar pastas",
"billing.preCheckoutModal.companyInput.label": "Nome da empresa:",
"billing.preCheckoutModal.emailInput.label": "E-mail:",
"billing.preCheckoutModal.submitButton.label": "Ir para a finalização da compra",
"billing.preCheckoutModal.taxId.label": "Identificação fiscal (NIF):",
"billing.preCheckoutModal.taxId.placeholder": "Tipo de ID",
"billing.pricingCard.chatsPerMonth": "chats/mês",
"billing.pricingCard.chatsTooltip": "Um chat é contado sempre que um utilizador inicia uma discussão. Ele é independente do número de mensagens que envia e recebe.",
"billing.pricingCard.heading": "Mudar para <strong>plan</strong>",
"billing.pricingCard.perMonth": "/ mês",
"billing.pricingCard.plus": ", mais:",
"billing.pricingCard.pro.analytics": "Análises aprofundadas",
"billing.pricingCard.pro.customDomains": "Domínios personalizados",
"billing.pricingCard.pro.description": "Para agências e startups em crescimento.",
"billing.pricingCard.pro.everythingFromStarter": "Tudo em Starter",
"billing.pricingCard.pro.includedSeats": "5 lugares incluídos",
"billing.pricingCard.pro.mostPopularLabel": "Mais popular",
"billing.pricingCard.pro.whatsAppIntegration": "Integração do WhatsApp",
"billing.pricingCard.starter.brandingRemoved": "Marca removida",
"billing.pricingCard.starter.createFolders": "Criar pastas",
"billing.pricingCard.starter.description": "Para indivíduos e pequenas empresas.",
"billing.pricingCard.starter.fileUploadBlock": "Bloco de envio de ficheiro",
"billing.pricingCard.starter.includedSeats": "2 lugares incluídos",
"billing.pricingCard.upgradeButton.current": "A sua subscrição atual",
"billing.updateSuccessToast.description": "A sua subscrição {plan} foi atualizada com sucesso \uD83C\uDF89",
"billing.upgradeAlert.buttonDefaultLabel": "Mais informações",
"billing.upgradeLimitLabel": "Precisa de atualizar a sua subscrição para {type}",
"billing.usage.chats.alert.soonReach": "Os seus typebots são populares! Vai alcançar em breve o limite de chats do seu plano. \uD83D\uDE80",
"billing.usage.chats.alert.updatePlan": "Certifique-se de atualizar o seu plano para aumentar esse limite e continuar a conversar com os seus utilizadores.",
"billing.usage.chats.heading": "Chats",
"billing.usage.heading": "Uso",
"billing.usage.unlimited": "Ilimitado",
"cancel": "Cancelar",
"confirmModal.defaultTitle": "Tem a certeza?",
"dashboard.header.settingsButton.label": "Configurações & Membros",
"dashboard.redirectionMessage": "Está a ser redirecionado...",
"dashboard.title": "Os meus typebots",
"delete": "Apagar",
"downgrade": "Downgrade",
"blocks.bubbles.embed.blockCard.tooltip": "Incorporar pdf, iframe, website...",
"blocks.inputs.fileUpload.blockCard.tooltip": "Carregar Ficheiros",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"editor.blockCard.logicBlock.tooltip.code.label": "Executar código Javascript",
"editor.blockCard.logicBlock.tooltip.jump.label": "Encaminhar fluxo para outro grupo",
"editor.blockCard.logicBlock.tooltip.typebotLink.label": "Link e salte para outro typebot",
"editor.blocks.bubbles.audio.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.audio.settings.autoplay.label": "Ativar reprodução automática",
"editor.blocks.bubbles.audio.settings.chooseFile.label": "Escolher um ficheiro",
"editor.blocks.bubbles.audio.settings.embedLink.label": "Incorporar link",
"editor.blocks.bubbles.audio.settings.upload.label": "Carregar",
"editor.blocks.bubbles.audio.settings.worksWith.placeholder": "Colar o link do ficheiro de áudio...",
"editor.blocks.bubbles.audio.settings.worksWith.text": "Compatível com .MP3s e .WAVs",
"editor.blocks.bubbles.embed.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.embed.node.show.text": "Mostrar incorporação",
"editor.blocks.bubbles.embed.settings.numberInput.unit": "px",
"editor.blocks.bubbles.embed.settings.worksWith.placeholder": "Colar o link ou código...",
"editor.blocks.bubbles.embed.settings.worksWith.text": "Compatível com PDFs, iframes, websites...",
"editor.blocks.bubbles.image.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.image.switchWithLabel.onClick.label": "Link ao clicar",
"editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder": "Texto alternativo do link (descrição)",
"editor.blocks.bubbles.textEditor.plate.label": "Editor de texto",
"editor.blocks.bubbles.textEditor.searchVariable.placeholder": "Pesquisar uma variável",
"editor.blocks.bubbles.video.node.clickToEdit.text": "Clique para editar...",
"editor.blocks.bubbles.video.settings.numberInput.unit": "px",
"editor.blocks.bubbles.video.settings.worksWith.placeholder": "Colar o link do vídeo...",
"editor.blocks.bubbles.video.settings.worksWith.text": "Compatível com Youtube, Vimeo e outros",
"editor.blocks.start.text": "Começar",
"editor.editableTypebotName.tooltip.rename.label": "Renomear",
"editor.gettingStartedModal.editorBasics.heading": "Noções básicas de editor",
"editor.gettingStartedModal.editorBasics.list.four.label": "Pré-visualize o seu bot ao clicar no botão de visualizar no canto superior direito",
"editor.gettingStartedModal.editorBasics.list.label": "Sinta-se à vontade para usar o chat no canto inferior direito para entrar em contacto se tiver alguma questão. Normalmente, respondo nas próximas 24 horas. \uD83D\uDE03",
"editor.gettingStartedModal.editorBasics.list.one.label": "A barra lateral esquerda contém blocos que pode arrastar e largar no quadro.",
"editor.gettingStartedModal.editorBasics.list.three.label": "Ligue os grupos entre si",
"editor.gettingStartedModal.editorBasics.list.two.label": "Pode agrupar blocos juntos, colocando-os uns abaixo ou acima dos outros",
"editor.gettingStartedModal.seeAction.item.label": "Outros vídeos",
"editor.gettingStartedModal.seeAction.label": "Veja o funcionamento em",
"editor.gettingStartedModal.seeAction.time": "5 minutos",
"editor.headers.flowButton.label": "Fluxo",
"editor.headers.helpButton.label": "Ajuda",
"editor.headers.previewButton.label": "Visualizar",
"editor.headers.resultsButton.label": "Resultados",
"editor.headers.savingSpinner.label": "Salvando...",
"editor.headers.settingsButton.label": "Configurações",
"editor.headers.shareButton.label": "Compartilhar",
"editor.headers.themeButton.label": "Tema",
"editor.sidebarBlock.abTest.label": "Teste AB",
"editor.sidebarBlock.analytics.label": "Analytics",
"editor.sidebarBlock.audio.label": "Áudio",
"editor.sidebarBlock.button.label": "Botão",
"editor.sidebarBlock.chatwoot.label": "Chatwoot",
"editor.sidebarBlock.condition.label": "Condição",
"editor.sidebarBlock.date.label": "Data",
"editor.sidebarBlock.email.label": "Email",
"editor.sidebarBlock.embed.label": "Incorporar",
"editor.sidebarBlock.file.label": "Ficheiro",
"editor.sidebarBlock.image.label": "Imagem",
"editor.sidebarBlock.jump.label": "Saltar",
"editor.sidebarBlock.makecom.label": "Make.com",
"editor.sidebarBlock.number.label": "Número",
"editor.sidebarBlock.openai.label": "OpenAI",
"editor.sidebarBlock.pabbly.label": "Pabbly",
"editor.sidebarBlock.payment.label": "Pagamento",
"editor.sidebarBlock.phone.label": "Telefone",
"editor.sidebarBlock.picChoice.label": "Seleção de Imagem",
"editor.sidebarBlock.pixel.label": "Pixel",
"editor.sidebarBlock.rating.label": "Classificação",
"editor.sidebarBlock.redirect.label": "Redirecionar",
"editor.sidebarBlock.script.label": "Script",
"editor.sidebarBlock.setVariable.label": "Variável",
"editor.sidebarBlock.sheets.label": "Sheets",
"editor.sidebarBlock.start.label": "Início",
"editor.sidebarBlock.text.label": "Texto",
"editor.sidebarBlock.typebot.label": "Typebot",
"editor.sidebarBlock.video.label": "Vídeo",
"editor.sidebarBlock.wait.label": "Espera",
"editor.sidebarBlock.webhook.label": "Webhook",
"editor.sidebarBlock.website.label": "Website",
"editor.sidebarBlock.zapier.label": "Zapier",
"editor.sidebarBlock.zemanticAi.label": "Zemantic AI",
"editor.sidebarBlocks.blockType.bubbles.heading": "Bubbles",
"editor.sidebarBlocks.blockType.inputs.heading": "Inputs",
"editor.sidebarBlocks.blockType.integrations.heading": "Integrações",
"editor.sidebarBlocks.blockType.logic.heading": "Condicionais",
"editor.sidebarBlocks.sidebar.icon.lock.label": "Bloquear",
"editor.sidebarBlocks.sidebar.icon.unlock.label": "Desbloquear",
"editor.sidebarBlocks.sidebar.lock.label": "Bloquear barra lateral",
"editor.sidebarBlocks.sidebar.unlock.label": "Desbloquear barra lateral",
"errorMessage": "Ocorreu um erro",
"folders.createFolderButton.label": "Criar uma pasta",
"folders.createTypebotButton.label": "Criar um typebot",
"folders.folderButton.deleteConfirmationMessage": "Tem a certeza de que deseja excluir a pasta <strong>folderName</strong>? (Tudo o que estiver dentro será movido para o seu painel)",
"folders.typebotButton.delete": "Apagar",
"folders.typebotButton.deleteConfirmationMessage": "Tem a certeza de que deseja excluir o seu typebot <strong>typebotName</strong>?",
"folders.typebotButton.deleteConfirmationMessageWarning": "Todos os dados associados serão excluídos e não poderão ser recuperados.",
"folders.typebotButton.duplicate": "Duplicar",
"folders.typebotButton.live": "Ao Vivo",
"folders.typebotButton.showMoreOptions": "Mostrar mais opções",
"folders.typebotButton.unpublish": "Despublicar",
"pending": "Pendente",
"remove": "Remover",
"skip": "Saltar",
"templates.buttons.fromScratchButton.label": "Comece do zero",
"templates.buttons.fromTemplateButton.label": "Comece a partir de um modelo",
"templates.buttons.heading": "Criar um novo typebot",
"templates.buttons.importFileButton.label": "Importar um ficheiro",
"templates.importFromFileButon.toastError.description": "Falha ao analisar o ficheiro. Tem certeza de que é um typebot?",
"templates.modal.menuHeading.marketing": "Marketing",
"templates.modal.menuHeading.new.tag": "Novo",
"templates.modal.menuHeading.other": "Outros",
"templates.modal.menuHeading.product": "Produto",
"templates.modal.useTemplateButton.label": "Usar este modelo",
"upgrade": "Upgrade",
"workspace.dropdown.logoutButton.label": "Sair",
"workspace.dropdown.newButton.label": "Novo espaço de trabalho",
"workspace.membersList.inviteButton.label": "Convidar",
"workspace.membersList.inviteInput.placeholder": "colega@empresa.com",
"workspace.membersList.title": "Membros",
"workspace.membersList.unlockBanner.label": "Atualize o seu plano para trabalhar com mais membros da equipa e desbloquear funcionalidades incríveis \uD83D\uDE80",
"workspace.settings.deleteButton.confirmMessage": "Tem a certeza de que deseja eliminar o espaço de trabalho {workspaceName}? Todas as suas pastas, typebots e resultados serão excluídos permanentemente.",
"workspace.settings.deleteButton.label": "Eliminar espaço de trabalho",
"workspace.settings.icon.title": "Ícone",
"workspace.settings.modal.menu.billingAndUsage.label": "Faturação e uso",
"workspace.settings.modal.menu.members.label": "Membros",
"workspace.settings.modal.menu.myAccount.label": "A minha conta",
"workspace.settings.modal.menu.preferences.label": "Preferências",
"workspace.settings.modal.menu.settings.label": "Configurações",
"workspace.settings.modal.menu.version.label": "Versão: {version}",
"workspace.settings.modal.menu.workspace.label": "Espaço de trabalho",
"workspace.settings.name.label": "Nome:"
}

View File

@ -8,7 +8,7 @@ import {
AlertDialogOverlay,
Button,
} from '@chakra-ui/react'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type ConfirmDeleteModalProps = {
isOpen: boolean
@ -29,7 +29,7 @@ export const ConfirmModal = ({
onConfirm,
confirmButtonColor = 'red',
}: ConfirmDeleteModalProps) => {
const scopedT = useScopedI18n('confirmModal')
const { t } = useTranslate()
const [confirmLoading, setConfirmLoading] = useState(false)
const cancelRef = useRef(null)
@ -54,7 +54,7 @@ export const ConfirmModal = ({
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{title ?? scopedT('defaultTitle')}
{title ?? t('confirmModal.defaultTitle')}
</AlertDialogHeader>
<AlertDialogBody>{message}</AlertDialogBody>

View File

@ -12,7 +12,7 @@ import {
ChangePlanModal,
ChangePlanModalProps,
} from '@/features/billing/components/ChangePlanModal'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
buttonLabel?: string
@ -25,7 +25,7 @@ export const UnlockPlanAlertInfo = ({
excludedPlans,
...props
}: Props) => {
const t = useI18n()
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<Alert

View File

@ -5,8 +5,8 @@ export const whatsAppBrandColor = '#25D366'
export const WhatsAppLogo = (props: IconProps) => (
<Icon viewBox="0 0 510 513" {...props}>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M435.689 74.468C387.754 26.471 324 0.025 256.071 0C116.098 0 2.18 113.906 2.131 253.916C2.107 298.674 13.808 342.361 36.029 380.862L0 512.459L134.617 477.148C171.704 497.386 213.467 508.039 255.962 508.051H256.071C396.02 508.051 509.951 394.134 509.999 254.123C510.023 186.268 483.638 122.478 435.689 74.48V74.468ZM256.071 465.168H255.986C218.118 465.157 180.97 454.976 148.558 435.751L140.851 431.174L60.965 452.127L82.285 374.238L77.268 366.251C56.143 332.646 44.978 293.804 45.002 253.929C45.051 137.563 139.731 42.883 256.157 42.883C312.53 42.908 365.521 64.886 405.371 104.786C445.224 144.674 467.152 197.713 467.128 254.099C467.078 370.476 372.4 465.157 256.071 465.157V465.168ZM371.839 307.101C365.495 303.923 334.302 288.581 328.481 286.462C322.661 284.343 318.437 283.285 314.211 289.64C309.986 295.997 297.823 310.291 294.121 314.515C290.419 318.753 286.718 319.277 280.374 316.098C274.031 312.92 253.587 306.224 229.345 284.611C210.485 267.784 197.748 247.013 194.048 240.656C190.346 234.301 193.658 230.867 196.823 227.713C199.672 224.865 203.167 220.299 206.345 216.597C209.523 212.895 210.57 210.242 212.688 206.016C214.808 201.778 213.748 198.079 212.166 194.899C210.582 191.722 197.895 160.49 192.598 147.791C187.447 135.421 182.213 137.101 178.329 136.894C174.626 136.711 170.402 136.675 166.165 136.675C161.928 136.675 155.06 138.257 149.24 144.614C143.42 150.968 127.031 166.323 127.031 197.541C127.031 228.761 149.764 258.946 152.942 263.183C156.119 267.42 197.687 331.501 261.331 358.995C276.466 365.533 288.288 369.441 297.506 372.363C312.702 377.197 326.533 376.516 337.466 374.883C349.656 373.058 375.006 359.53 380.29 344.711C385.573 329.893 385.573 317.182 383.991 314.539C382.409 311.898 378.172 310.302 371.828 307.125L371.839 307.101Z"
fill="currentColor"
/>

View File

@ -25,12 +25,12 @@ import { useApiTokens } from '../hooks/useApiTokens'
import { ApiTokenFromServer } from '../types'
import { parseTimeSince } from '@/helpers/parseTimeSince'
import { deleteApiTokenQuery } from '../queries/deleteApiTokenQuery'
import { useScopedI18n } from '@/locales'
import { T, useTranslate } from '@tolgee/react'
type Props = { user: User }
export const ApiTokensList = ({ user }: Props) => {
const scopedT = useScopedI18n('account.apiTokens')
const { t } = useTranslate()
const { showToast } = useToast()
const { apiTokens, isLoading, mutate } = useApiTokens({
userId: user.id,
@ -57,10 +57,12 @@ export const ApiTokensList = ({ user }: Props) => {
return (
<Stack spacing={4}>
<Heading fontSize="2xl">{scopedT('heading')}</Heading>
<Text>{scopedT('description')}</Text>
<Heading fontSize="2xl">{t('account.apiTokens.heading')}</Heading>
<Text>{t('account.apiTokens.description')}</Text>
<Flex justifyContent="flex-end">
<Button onClick={onCreateOpen}>{scopedT('createButton.label')}</Button>
<Button onClick={onCreateOpen}>
{t('account.apiTokens.createButton.label')}
</Button>
<CreateTokenModal
userId={user.id}
isOpen={isCreateOpen}
@ -73,8 +75,8 @@ export const ApiTokensList = ({ user }: Props) => {
<Table>
<Thead>
<Tr>
<Th>{scopedT('table.nameHeader')}</Th>
<Th w="130px">{scopedT('table.createdHeader')}</Th>
<Th>{t('account.apiTokens.table.nameHeader')}</Th>
<Th w="130px">{t('account.apiTokens.table.createdHeader')}</Th>
<Th w="0" />
</Tr>
</Thead>
@ -90,7 +92,7 @@ export const ApiTokensList = ({ user }: Props) => {
variant="outline"
onClick={() => setDeletingId(token.id)}
>
{scopedT('deleteButton.label')}
{t('account.apiTokens.deleteButton.label')}
</Button>
</Td>
</Tr>
@ -118,14 +120,17 @@ export const ApiTokensList = ({ user }: Props) => {
onClose={() => setDeletingId(undefined)}
message={
<Text>
{scopedT('deleteConfirmationMessage', {
tokenName: (
<T
keyName="account.apiTokens.deleteConfirmationMessage"
params={{
strong: (
<strong>{apiTokens?.find(byId(deletingId))?.name}</strong>
),
})}
}}
/>
</Text>
}
confirmButtonLabel={scopedT('deleteButton.label')}
confirmButtonLabel={t('account.apiTokens.deleteButton.label')}
/>
</Stack>
)

View File

@ -10,7 +10,7 @@ import Image from 'next/image'
import lightModeIllustration from 'public/images/light-mode.png'
import darkModeIllustration from 'public/images/dark-mode.png'
import systemModeIllustration from 'public/images/system-mode.png'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
defaultValue: string
@ -18,22 +18,22 @@ type Props = {
}
export const AppearanceRadioGroup = ({ defaultValue, onChange }: Props) => {
const scopedT = useScopedI18n('account.preferences.appearance')
const { t } = useTranslate()
const appearanceData = [
{
value: 'light',
label: scopedT('lightLabel'),
label: t('account.preferences.appearance.lightLabel'),
image: lightModeIllustration,
},
{
value: 'dark',
label: scopedT('darkLabel'),
label: t('account.preferences.appearance.darkLabel'),
image: darkModeIllustration,
},
{
value: 'system',
label: scopedT('systemLabel'),
label: t('account.preferences.appearance.systemLabel'),
image: systemModeIllustration,
},
]

View File

@ -1,5 +1,5 @@
import { CopyButton } from '@/components/CopyButton'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import {
Modal,
ModalOverlay,
@ -33,7 +33,7 @@ export const CreateTokenModal = ({
onNewToken,
}: Props) => {
const inputRef = useRef<HTMLInputElement>(null)
const scopedT = useScopedI18n('account.apiTokens.createModal')
const { t } = useTranslate()
const [name, setName] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [newTokenValue, setNewTokenValue] = useState<string>()
@ -54,14 +54,18 @@ export const CreateTokenModal = ({
<ModalOverlay />
<ModalContent>
<ModalHeader>
{newTokenValue ? scopedT('createdHeading') : scopedT('createHeading')}
{newTokenValue
? t('account.apiTokens.createModal.createdHeading')
: t('account.apiTokens.createModal.createHeading')}
</ModalHeader>
<ModalCloseButton />
{newTokenValue ? (
<ModalBody as={Stack} spacing="4">
<Text>
{scopedT('copyInstruction')}{' '}
<strong>{scopedT('securityWarning')}</strong>
{t('account.apiTokens.createModal.copyInstruction')}{' '}
<strong>
{t('account.apiTokens.createModal.securityWarning')}
</strong>
</Text>
<InputGroup size="md">
<Input readOnly pr="4.5rem" value={newTokenValue} />
@ -72,10 +76,14 @@ export const CreateTokenModal = ({
</ModalBody>
) : (
<ModalBody as="form" onSubmit={createToken}>
<Text mb="4">{scopedT('nameInput.label')}</Text>
<Text mb="4">
{t('account.apiTokens.createModal.nameInput.label')}
</Text>
<Input
ref={inputRef}
placeholder={scopedT('nameInput.placeholder')}
placeholder={t(
'account.apiTokens.createModal.nameInput.placeholder'
)}
onChange={(e) => setName(e.target.value)}
/>
</ModalBody>
@ -84,7 +92,7 @@ export const CreateTokenModal = ({
<ModalFooter>
{newTokenValue ? (
<Button onClick={onClose} colorScheme="blue">
{scopedT('doneButton.label')}
{t('account.apiTokens.createModal.doneButton.label')}
</Button>
) : (
<Button
@ -93,7 +101,7 @@ export const CreateTokenModal = ({
isLoading={isSubmitting}
onClick={createToken}
>
{scopedT('createButton.label')}
{t('account.apiTokens.createModal.createButton.label')}
</Button>
)}
</ModalFooter>

View File

@ -1,5 +1,5 @@
import { MouseIcon, LaptopIcon } from '@/components/icons'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import {
HStack,
Radio,
@ -18,18 +18,20 @@ export const GraphNavigationRadioGroup = ({
defaultValue,
onChange,
}: Props) => {
const scopedT = useScopedI18n('account.preferences.graphNavigation')
const { t } = useTranslate()
const graphNavigationData = [
{
value: GraphNavigation.MOUSE,
label: scopedT('mouse.label'),
description: scopedT('mouse.description'),
label: t('account.preferences.graphNavigation.mouse.label'),
description: t('account.preferences.graphNavigation.mouse.description'),
icon: <MouseIcon boxSize="35px" />,
},
{
value: GraphNavigation.TRACKPAD,
label: scopedT('trackpad.label'),
description: scopedT('trackpad.description'),
label: t('account.preferences.graphNavigation.trackpad.label'),
description: t(
'account.preferences.graphNavigation.trackpad.description'
),
icon: <LaptopIcon boxSize="35px" />,
},
]

View File

@ -5,10 +5,10 @@ import { ApiTokensList } from './ApiTokensList'
import { UploadButton } from '@/components/ImageUploadContent/UploadButton'
import { useUser } from '../hooks/useUser'
import { TextInput } from '@/components/inputs/TextInput'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const MyAccountForm = () => {
const scopedT = useScopedI18n('account.myAccount')
const { t } = useTranslate()
const { user, updateUser } = useUser()
const [name, setName] = useState(user?.name ?? '')
const [email, setEmail] = useState(user?.email ?? '')
@ -47,11 +47,11 @@ export const MyAccountForm = () => {
leftIcon={<UploadIcon />}
onFileUploaded={handleFileUploaded}
>
{scopedT('changePhotoButton.label')}
{t('account.myAccount.changePhotoButton.label')}
</UploadButton>
)}
<Text color="gray.500" fontSize="sm">
{scopedT('changePhotoButton.specification')}
{t('account.myAccount.changePhotoButton.specification')}
</Text>
</Stack>
</HStack>
@ -59,17 +59,17 @@ export const MyAccountForm = () => {
<TextInput
defaultValue={name}
onChange={handleNameChange}
label={scopedT('nameInput.label')}
label={t('account.myAccount.nameInput.label')}
withVariableButton={false}
debounceTimeout={0}
/>
<Tooltip label={scopedT('emailInput.disabledTooltip')}>
<Tooltip label={t('account.myAccount.emailInput.disabledTooltip')}>
<span>
<TextInput
type="email"
defaultValue={email}
onChange={handleEmailChange}
label={scopedT('emailInput.label')}
label={t('account.myAccount.emailInput.label')}
withVariableButton={false}
debounceTimeout={0}
isDisabled

View File

@ -14,9 +14,10 @@ import React, { useEffect } from 'react'
import { GraphNavigationRadioGroup } from './GraphNavigationRadioGroup'
import { AppearanceRadioGroup } from './AppearanceRadioGroup'
import { useUser } from '../hooks/useUser'
import { useChangeLocale, useCurrentLocale, useScopedI18n } from '@/locales'
import { ChevronDownIcon } from '@/components/icons'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
import { useTranslate, useTolgee } from '@tolgee/react'
import { useRouter } from 'next/router'
const localeHumanReadable = {
en: 'English',
@ -27,11 +28,11 @@ const localeHumanReadable = {
} as const
export const UserPreferencesForm = () => {
const scopedT = useScopedI18n('account.preferences')
const { getLanguage } = useTolgee()
const router = useRouter()
const { t } = useTranslate()
const { colorMode } = useColorMode()
const { user, updateUser } = useUser()
const changeLocale = useChangeLocale()
const currentLocale = useCurrentLocale()
useEffect(() => {
if (!user?.graphNavigation)
@ -47,17 +48,23 @@ export const UserPreferencesForm = () => {
}
const updateLocale = (locale: keyof typeof localeHumanReadable) => () => {
changeLocale(locale)
document.cookie = `NEXT_LOCALE=${locale}; path=/; max-age=31536000`
router.replace(router.pathname, undefined, { locale })
}
const currentLanguage = getLanguage()
return (
<Stack spacing={12}>
<HStack spacing={4}>
<Heading size="md">{scopedT('language.heading')}</Heading>
<Heading size="md">{t('account.preferences.language.heading')}</Heading>
<Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
{localeHumanReadable[currentLocale]}
{currentLanguage
? localeHumanReadable[
currentLanguage as keyof typeof localeHumanReadable
]
: 'Loading...'}
</MenuButton>
<MenuList>
{Object.keys(localeHumanReadable).map((locale) => (
@ -76,19 +83,25 @@ export const UserPreferencesForm = () => {
))}
</MenuList>
</Menu>
{currentLocale !== 'en' && (
<MoreInfoTooltip>{scopedT('language.tooltip')}</MoreInfoTooltip>
{currentLanguage !== 'en' && (
<MoreInfoTooltip>
{t('account.preferences.language.tooltip')}
</MoreInfoTooltip>
)}
</HStack>
<Stack spacing={6}>
<Heading size="md">{scopedT('graphNavigation.heading')}</Heading>
<Heading size="md">
{t('account.preferences.graphNavigation.heading')}
</Heading>
<GraphNavigationRadioGroup
defaultValue={user?.graphNavigation ?? GraphNavigation.TRACKPAD}
onChange={changeGraphNavigation}
/>
</Stack>
<Stack spacing={6}>
<Heading size="md">{scopedT('appearance.heading')}</Heading>
<Heading size="md">
{t('account.preferences.appearance.heading')}
</Heading>
<AppearanceRadioGroup
defaultValue={
user?.preferredAppAppearance

View File

@ -12,12 +12,12 @@ import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { Graph } from '@/features/graph/components/Graph'
import { GraphProvider } from '@/features/graph/providers/GraphProvider'
import { GroupsCoordinatesProvider } from '@/features/graph/providers/GroupsCoordinateProvider'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { trpc } from '@/lib/trpc'
import { isDefined } from '@typebot.io/lib'
export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
const t = useI18n()
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
const { typebot, publishedTypebot } = useTypebot()
const { data } = trpc.analytics.getTotalAnswersInBlocks.useQuery(

View File

@ -1,4 +1,4 @@
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import {
GridProps,
SimpleGrid,
@ -22,13 +22,13 @@ export const StatsCards = ({
stats,
...props
}: { stats?: Stats } & GridProps) => {
const scopedT = useScopedI18n('analytics')
const { t } = useTranslate()
const bg = useColorModeValue('white', 'gray.900')
return (
<SimpleGrid columns={{ base: 1, md: 3 }} spacing="6" {...props}>
<Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>{scopedT('viewsLabel')}</StatLabel>
<StatLabel>{t('analytics.viewsLabel')}</StatLabel>
{stats ? (
<StatNumber>{stats.totalViews}</StatNumber>
) : (
@ -36,7 +36,7 @@ export const StatsCards = ({
)}
</Stat>
<Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>{scopedT('startsLabel')}</StatLabel>
<StatLabel>{t('analytics.startsLabel')}</StatLabel>
{stats ? (
<StatNumber>{stats.totalStarts}</StatNumber>
) : (
@ -44,10 +44,10 @@ export const StatsCards = ({
)}
</Stat>
<Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>{scopedT('completionRateLabel')}</StatLabel>
<StatLabel>{t('analytics.completionRateLabel')}</StatLabel>
{stats ? (
<StatNumber>
{computeCompletionRate(scopedT('notAvailableLabel'))(
{computeCompletionRate(t('analytics.notAvailableLabel'))(
stats.totalCompleted,
stats.totalStarts
)}

View File

@ -13,13 +13,13 @@ import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import confetti from 'canvas-confetti'
import { useUser } from '@/features/account/hooks/useUser'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { env } from '@typebot.io/env'
const totalSteps = 5
export const OnboardingPage = () => {
const t = useI18n()
const { t } = useTranslate()
const { push, replace } = useRouter()
const confettiCanvaContainer = useRef<HTMLCanvasElement | null>(null)
const confettiCanon = useRef<confetti.CreateTypes>()

View File

@ -1,4 +1,4 @@
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Alert } from '@chakra-ui/react'
type Props = {
@ -6,16 +6,16 @@ type Props = {
}
export const SignInError = ({ error }: Props) => {
const scopedT = useScopedI18n('auth.error')
const { t } = useTranslate()
const errors: Record<string, string> = {
Signin: scopedT('default'),
OAuthSignin: scopedT('default'),
OAuthCallback: scopedT('default'),
OAuthCreateAccount: scopedT('email'),
EmailCreateAccount: scopedT('default'),
Callback: scopedT('default'),
OAuthAccountNotLinked: scopedT('oauthNotLinked'),
default: scopedT('unknown'),
Signin: t('auth.error.default'),
OAuthSignin: t('auth.error.default'),
OAuthCallback: t('auth.error.default'),
OAuthCreateAccount: t('auth.error.email'),
EmailCreateAccount: t('auth.error.default'),
Callback: t('auth.error.default'),
OAuthAccountNotLinked: t('auth.error.oauthNotLinked'),
default: t('auth.error.unknown'),
}
return (
<Alert status="error" variant="solid" rounded="md">

View File

@ -27,7 +27,7 @@ import { BuiltInProviderType } from 'next-auth/providers'
import { useToast } from '@/hooks/useToast'
import { TextLink } from '@/components/TextLink'
import { SignInError } from './SignInError'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
defaultEmail?: string
@ -35,7 +35,7 @@ type Props = {
export const SignInForm = ({
defaultEmail,
}: Props & HTMLChakraProps<'form'>) => {
const scopedT = useScopedI18n('auth')
const { t } = useTranslate()
const router = useRouter()
const { status } = useSession()
const [authLoading, setAuthLoading] = useState(false)
@ -79,8 +79,8 @@ export const SignInForm = ({
})
if (response?.error) {
showToast({
title: scopedT('signinErrorToast.title'),
description: scopedT('signinErrorToast.description'),
title: t('auth.signinErrorToast.title'),
description: t('auth.signinErrorToast.description'),
})
} else {
setIsMagicLinkSent(true)
@ -88,7 +88,7 @@ export const SignInForm = ({
} catch {
showToast({
status: 'info',
description: scopedT('signinErrorToast.tooManyRequests'),
description: t('auth.signinErrorToast.tooManyRequests'),
})
}
setAuthLoading(false)
@ -98,12 +98,12 @@ export const SignInForm = ({
if (hasNoAuthProvider)
return (
<Text>
{scopedT('noProvider.preLink')}{' '}
{t('auth.noProvider.preLink')}{' '}
<TextLink
href="https://docs.typebot.io/self-hosting/configuration"
isExternal
>
{scopedT('noProvider.link')}
{t('auth.noProvider.link')}
</TextLink>
</Text>
)
@ -114,9 +114,7 @@ export const SignInForm = ({
<SocialLoginButtons providers={providers} />
{providers?.email && (
<>
<DividerWithText mt="6">
{scopedT('orEmailLabel')}
</DividerWithText>
<DividerWithText mt="6">{t('auth.orEmailLabel')}</DividerWithText>
<HStack as="form" onSubmit={handleEmailSubmit}>
<Input
name="email"
@ -134,7 +132,7 @@ export const SignInForm = ({
}
isDisabled={isMagicLinkSent}
>
{scopedT('emailSubmitButton.label')}
{t('auth.emailSubmitButton.label')}
</Button>
</HStack>
</>
@ -150,8 +148,8 @@ export const SignInForm = ({
<HStack>
<AlertIcon />
<Stack spacing={1}>
<Text fontWeight="semibold">{scopedT('magicLink.title')}</Text>
<Text fontSize="sm">{scopedT('magicLink.description')}</Text>
<Text fontWeight="semibold">{t('auth.magicLink.title')}</Text>
<Text fontSize="sm">{t('auth.magicLink.description')}</Text>
</Stack>
</HStack>
</Alert>

View File

@ -1,6 +1,6 @@
import { Seo } from '@/components/Seo'
import { TextLink } from '@/components/TextLink'
import { useScopedI18n } from '@/locales'
import { T, useTranslate } from '@tolgee/react'
import { VStack, Heading, Text } from '@chakra-ui/react'
import { useRouter } from 'next/router'
import { SignInForm } from './SignInForm'
@ -11,7 +11,7 @@ type Props = {
}
export const SignInPage = ({ type }: Props) => {
const scopedT = useScopedI18n('auth')
const { t } = useTranslate()
const { query } = useRouter()
return (
@ -19,8 +19,8 @@ export const SignInPage = ({ type }: Props) => {
<Seo
title={
type === 'signin'
? scopedT('signin.heading')
: scopedT('register.heading')
? t('auth.signin.heading')
: t('auth.register.heading')
}
/>
<Heading
@ -29,39 +29,36 @@ export const SignInPage = ({ type }: Props) => {
}}
>
{type === 'signin'
? scopedT('signin.heading')
: scopedT('register.heading')}
? t('auth.signin.heading')
: t('auth.register.heading')}
</Heading>
{type === 'signin' ? (
<Text>
{scopedT('signin.noAccountLabel.preLink')}{' '}
{t('auth.signin.noAccountLabel.preLink')}{' '}
<TextLink href="/register">
{scopedT('signin.noAccountLabel.link')}
{t('auth.signin.noAccountLabel.link')}
</TextLink>
</Text>
) : (
<Text>
{scopedT('register.alreadyHaveAccountLabel.preLink')}{' '}
{t('auth.register.alreadyHaveAccountLabel.preLink')}{' '}
<TextLink href="/signin">
{scopedT('register.alreadyHaveAccountLabel.link')}
{t('auth.register.alreadyHaveAccountLabel.link')}
</TextLink>
</Text>
)}
<SignInForm defaultEmail={query.g?.toString()} />
{type === 'signup' ? (
<Text fontSize="sm" maxW="400px" textAlign="center">
{scopedT('register.aggreeToTerms', {
termsOfService: (
<TextLink href={'https://typebot.io/terms-of-service'}>
{scopedT('register.termsOfService')}
</TextLink>
<T
keyName="auth.register.aggreeToTerms"
params={{
terms: <TextLink href={'https://typebot.io/terms-of-service'} />,
privacy: (
<TextLink href={'https://typebot.io/privacy-policies'} />
),
privacyPolicy: (
<TextLink href={'https://typebot.io/privacy-policies'}>
{scopedT('register.privacyPolicy')}
</TextLink>
),
})}
}}
/>
</Text>
) : null}
</VStack>

View File

@ -15,7 +15,7 @@ import { omit } from '@typebot.io/lib'
import { AzureAdLogo } from '@/components/logos/AzureAdLogo'
import { FacebookLogo } from '@/components/logos/FacebookLogo'
import { GitlabLogo } from '@/components/logos/GitlabLogo'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
providers:
@ -24,7 +24,7 @@ type Props = {
}
export const SocialLoginButtons = ({ providers }: Props) => {
const scopedT = useScopedI18n('auth.socialLogin')
const { t } = useTranslate()
const { query } = useRouter()
const { status } = useSession()
const [authLoading, setAuthLoading] =
@ -65,7 +65,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('githubButton.label')}
{t('auth.socialLogin.githubButton.label')}
</Button>
)}
{providers?.google && (
@ -79,7 +79,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('googleButton.label')}
{t('auth.socialLogin.googleButton.label')}
</Button>
)}
{providers?.facebook && (
@ -93,7 +93,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('facebookButton.label')}
{t('auth.socialLogin.facebookButton.label')}
</Button>
)}
{providers?.gitlab && (
@ -107,7 +107,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('gitlabButton.label', {
{t('auth.socialLogin.gitlabButton.label', {
gitlabProviderName: providers.gitlab.name,
})}
</Button>
@ -123,7 +123,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('azureButton.label', {
{t('auth.socialLogin.azureButton.label', {
azureProviderName: providers['azure-ad'].name,
})}
</Button>
@ -137,7 +137,7 @@ export const SocialLoginButtons = ({ providers }: Props) => {
}
variant="outline"
>
{scopedT('customButton.label', {
{t('auth.socialLogin.customButton.label', {
customProviderName: providers['custom-oauth'].name,
})}
</Button>

View File

@ -1,6 +1,6 @@
import { useToast } from '@/hooks/useToast'
import { trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Button, ButtonProps, Link } from '@chakra-ui/react'
type Props = {
@ -8,7 +8,7 @@ type Props = {
} & Pick<ButtonProps, 'colorScheme'>
export const BillingPortalButton = ({ workspaceId, colorScheme }: Props) => {
const scopedT = useScopedI18n('billing')
const { t } = useTranslate()
const { showToast } = useToast()
const { data } = trpc.billing.getBillingPortalUrl.useQuery(
{
@ -29,7 +29,7 @@ export const BillingPortalButton = ({ workspaceId, colorScheme }: Props) => {
isLoading={!data}
colorScheme={colorScheme}
>
{scopedT('billingPortalButton.label')}
{t('billing.billingPortalButton.label')}
</Button>
)
}

View File

@ -10,7 +10,7 @@ import { ParentModalProvider } from '@/features/graph/providers/ParentModalProvi
import { useUser } from '@/features/account/hooks/useUser'
import { StarterPlanPricingCard } from './StarterPlanPricingCard'
import { ProPlanPricingCard } from './ProPlanPricingCard'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { StripeClimateLogo } from './StripeClimateLogo'
import { guessIfUserIsEuropean } from '@typebot.io/lib/billing/guessIfUserIsEuropean'
@ -20,7 +20,7 @@ type Props = {
}
export const ChangePlanForm = ({ workspace, excludedPlans }: Props) => {
const scopedT = useScopedI18n('billing')
const { t } = useTranslate()
const { user } = useUser()
const { showToast } = useToast()
@ -49,7 +49,7 @@ export const ChangePlanForm = ({ workspace, excludedPlans }: Props) => {
trpcContext.workspace.getWorkspace.invalidate()
showToast({
status: 'success',
description: scopedT('updateSuccessToast.description', {
description: t('billing.updateSuccessToast.description', {
plan: workspace?.plan,
}),
})
@ -87,9 +87,9 @@ export const ChangePlanForm = ({ workspace, excludedPlans }: Props) => {
<HStack maxW="500px">
<StripeClimateLogo />
<Text fontSize="xs" color="gray.500">
{scopedT('contribution.preLink')}{' '}
{t('billing.contribution.preLink')}{' '}
<TextLink href="https://climate.stripe.com/5VCRAq" isExternal>
{scopedT('contribution.link')}
{t('billing.contribution.link')}
</TextLink>
</Text>
</HStack>
@ -128,9 +128,9 @@ export const ChangePlanForm = ({ workspace, excludedPlans }: Props) => {
)}
<Text color="gray.500">
{scopedT('customLimit.preLink')}{' '}
{t('billing.customLimit.preLink')}{' '}
<TextLink href={'https://typebot.io/enterprise-lead-form'} isExternal>
{scopedT('customLimit.link')}
{t('billing.customLimit.link')}
</TextLink>
</Text>
</Stack>

View File

@ -1,6 +1,6 @@
import { AlertInfo } from '@/components/AlertInfo'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import {
Modal,
ModalBody,
@ -26,7 +26,7 @@ export const ChangePlanModal = ({
type,
excludedPlans,
}: ChangePlanModalProps) => {
const t = useI18n()
const { t } = useTranslate()
const { workspace } = useWorkspace()
return (
<Modal

View File

@ -16,6 +16,7 @@ import {
Thead,
Tr,
} from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { proChatTiers } from '@typebot.io/lib/billing/constants'
import { formatPrice } from '@typebot.io/lib/billing/formatPrice'
@ -25,12 +26,14 @@ type Props = {
}
export const ChatsProTiersModal = ({ isOpen, onClose }: Props) => {
const { t } = useTranslate()
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Heading size="lg">Chats pricing table</Heading>
<Heading size="lg">{t('billing.tiersModal.heading')}</Heading>
</ModalHeader>
<ModalCloseButton />
<ModalBody as={Stack} spacing="6">

View File

@ -12,14 +12,14 @@ import { PlanTag } from './PlanTag'
import { BillingPortalButton } from './BillingPortalButton'
import { trpc } from '@/lib/trpc'
import { Workspace } from '@typebot.io/schemas'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
workspace: Pick<Workspace, 'id' | 'plan' | 'stripeId'>
}
export const CurrentSubscriptionSummary = ({ workspace }: Props) => {
const scopedT = useScopedI18n('billing.currentSubscription')
const { t } = useTranslate()
const { data } = trpc.billing.getSubscription.useQuery({
workspaceId: workspace.id,
@ -31,13 +31,15 @@ export const CurrentSubscriptionSummary = ({ workspace }: Props) => {
return (
<Stack spacing="4">
<Heading fontSize="3xl">{scopedT('heading')}</Heading>
<Heading fontSize="3xl">
{t('billing.currentSubscription.heading')}
</Heading>
<HStack data-testid="current-subscription">
<Text>{scopedT('subheading')} </Text>
<Text>{t('billing.currentSubscription.subheading')} </Text>
<PlanTag plan={workspace.plan} />
{data?.subscription?.cancelDate && (
<Text fontSize="sm">
({scopedT('cancelDate')}{' '}
({t('billing.currentSubscription.cancelDate')}{' '}
{data.subscription.cancelDate.toDateString()})
</Text>
)}
@ -45,7 +47,7 @@ export const CurrentSubscriptionSummary = ({ workspace }: Props) => {
{data?.subscription?.status === 'past_due' && (
<Alert fontSize="sm" status="error">
<AlertIcon />
{scopedT('pastDueAlert')}
{t('billing.currentSubscription.pastDueAlert')}
</Alert>
)}

View File

@ -18,14 +18,14 @@ import Link from 'next/link'
import React from 'react'
import { trpc } from '@/lib/trpc'
import { useToast } from '@/hooks/useToast'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
workspaceId: string
}
export const InvoicesList = ({ workspaceId }: Props) => {
const scopedT = useScopedI18n('billing.invoices')
const { t } = useTranslate()
const { showToast } = useToast()
const { data, status } = trpc.billing.listInvoices.useQuery(
{
@ -40,9 +40,9 @@ export const InvoicesList = ({ workspaceId }: Props) => {
return (
<Stack spacing={6}>
<Heading fontSize="3xl">{scopedT('heading')}</Heading>
<Heading fontSize="3xl">{t('billing.invoices.heading')}</Heading>
{data?.invoices.length === 0 && status !== 'loading' ? (
<Text>{scopedT('empty')}</Text>
<Text>{t('billing.invoices.empty')}</Text>
) : (
<TableContainer>
<Table>
@ -50,8 +50,8 @@ export const InvoicesList = ({ workspaceId }: Props) => {
<Tr>
<Th w="0" />
<Th>#</Th>
<Th>{scopedT('paidAt')}</Th>
<Th>{scopedT('subtotal')}</Th>
<Th>{t('billing.invoices.paidAt')}</Th>
<Th>{t('billing.invoices.subtotal')}</Th>
<Th w="0" />
</Tr>
</Thead>

View File

@ -18,7 +18,7 @@ import { useRouter } from 'next/router'
import React, { FormEvent, useState } from 'react'
import { isDefined } from '@typebot.io/lib'
import { taxIdTypes } from '../taxIdTypes'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export type PreCheckoutModalProps = {
selectedSubscription:
@ -47,7 +47,7 @@ export const PreCheckoutModal = ({
existingEmail,
onClose,
}: PreCheckoutModalProps) => {
const scopedT = useScopedI18n('billing.preCheckoutModal')
const { t } = useTranslate()
const { ref } = useParentModal()
const vatValueInputRef = React.useRef<HTMLInputElement>(null)
const router = useRouter()
@ -131,7 +131,7 @@ export const PreCheckoutModal = ({
<Stack as="form" spacing="4" onSubmit={goToCheckout}>
<TextInput
isRequired
label={scopedT('companyInput.label')}
label={t('billing.preCheckoutModal.companyInput.label')}
defaultValue={customer.company}
onChange={updateCustomerCompany}
withVariableButton={false}
@ -140,17 +140,17 @@ export const PreCheckoutModal = ({
<TextInput
isRequired
type="email"
label={scopedT('emailInput.label')}
label={t('billing.preCheckoutModal.emailInput.label')}
defaultValue={customer.email}
onChange={updateCustomerEmail}
withVariableButton={false}
debounceTimeout={0}
/>
<FormControl>
<FormLabel>{scopedT('taxId.label')}</FormLabel>
<FormLabel>{t('billing.preCheckoutModal.taxId.label')}</FormLabel>
<HStack>
<Select
placeholder={scopedT('taxId.placeholder')}
placeholder={t('billing.preCheckoutModal.taxId.placeholder')}
items={vatCodeLabels}
isPopoverMatchingInputWidth={false}
onSelect={updateVatType}
@ -171,7 +171,7 @@ export const PreCheckoutModal = ({
colorScheme="blue"
isDisabled={customer.company === '' || customer.email === ''}
>
{scopedT('submitButton.label')}
{t('billing.preCheckoutModal.submitButton.label')}
</Button>
</Stack>
</ModalBody>

View File

@ -14,10 +14,10 @@ import {
import { Plan } from '@typebot.io/prisma'
import { FeaturesList } from './FeaturesList'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
import { useI18n, useScopedI18n } from '@/locales'
import { formatPrice } from '@typebot.io/lib/billing/formatPrice'
import { ChatsProTiersModal } from './ChatsProTiersModal'
import { prices } from '@typebot.io/lib/billing/constants'
import { T, useTranslate } from '@tolgee/react'
type Props = {
currentPlan: Plan
@ -32,12 +32,12 @@ export const ProPlanPricingCard = ({
isLoading,
onPayClick,
}: Props) => {
const t = useI18n()
const scopedT = useScopedI18n('billing.pricingCard')
const { isOpen, onOpen, onClose } = useDisclosure()
const { t } = useTranslate()
const getButtonLabel = () => {
if (currentPlan === Plan.PRO) return scopedT('upgradeButton.current')
if (currentPlan === Plan.PRO)
return t('billing.pricingCard.upgradeButton.current')
return t('upgrade')
}
@ -65,38 +65,41 @@ export const ProPlanPricingCard = ({
fontWeight="semibold"
style={{ marginTop: 0 }}
>
{scopedT('pro.mostPopularLabel')}
{t('billing.pricingCard.pro.mostPopularLabel')}
</Tag>
</Flex>
<Stack justifyContent="space-between" h="full">
<Stack spacing="4" mt={2}>
<Heading fontSize="2xl">
{scopedT('heading', {
plan: (
<T
keyName="billing.pricingCard.heading"
params={{
strong: (
<chakra.span
color={useColorModeValue('blue.400', 'blue.300')}
>
Pro
</chakra.span>
/>
),
})}
}}
/>
</Heading>
<Text>{scopedT('pro.description')}</Text>
<Text>{t('billing.pricingCard.pro.description')}</Text>
</Stack>
<Stack spacing="8">
<Stack spacing="4">
<Heading>
{formatPrice(prices.PRO, { currency })}
<chakra.span fontSize="md">{scopedT('perMonth')}</chakra.span>
<chakra.span fontSize="md">
{t('billing.pricingCard.perMonth')}
</chakra.span>
</Heading>
<Text fontWeight="bold">
<Tooltip
label={
<FeaturesList
features={[
scopedT('starter.brandingRemoved'),
scopedT('starter.fileUploadBlock'),
scopedT('starter.createFolders'),
t('billing.pricingCard.starter.brandingRemoved'),
t('billing.pricingCard.starter.fileUploadBlock'),
t('billing.pricingCard.starter.createFolders'),
]}
spacing="0"
/>
@ -105,19 +108,21 @@ export const ProPlanPricingCard = ({
placement="top"
>
<chakra.span textDecoration="underline" cursor="pointer">
{scopedT('pro.everythingFromStarter')}
{t('billing.pricingCard.pro.everythingFromStarter')}
</chakra.span>
</Tooltip>
{scopedT('plus')}
{t('billing.pricingCard.plus')}
</Text>
<FeaturesList
features={[
scopedT('pro.includedSeats'),
t('billing.pricingCard.pro.includedSeats'),
<Stack key="starter-chats" spacing={1}>
<HStack key="test">
<Text>10,000 {scopedT('chatsPerMonth')}</Text>
<Text>
10,000 {t('billing.pricingCard.chatsPerMonth')}
</Text>
<MoreInfoTooltip>
{scopedT('chatsTooltip')}
{t('billing.pricingCard.chatsTooltip')}
</MoreInfoTooltip>
</HStack>
<Text
@ -130,9 +135,9 @@ export const ProPlanPricingCard = ({
</Button>
</Text>
</Stack>,
scopedT('pro.whatsAppIntegration'),
scopedT('pro.customDomains'),
scopedT('pro.analytics'),
t('billing.pricingCard.pro.whatsAppIntegration'),
t('billing.pricingCard.pro.customDomains'),
t('billing.pricingCard.pro.analytics'),
]}
/>
</Stack>

View File

@ -10,9 +10,9 @@ import {
import { Plan } from '@typebot.io/prisma'
import { FeaturesList } from './FeaturesList'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
import { useI18n, useScopedI18n } from '@/locales'
import { formatPrice } from '@typebot.io/lib/billing/formatPrice'
import { prices } from '@typebot.io/lib/billing/constants'
import { T, useTranslate } from '@tolgee/react'
type Props = {
currentPlan: Plan
@ -27,12 +27,12 @@ export const StarterPlanPricingCard = ({
currency,
onPayClick,
}: Props) => {
const t = useI18n()
const scopedT = useScopedI18n('billing.pricingCard')
const { t } = useTranslate()
const getButtonLabel = () => {
if (currentPlan === Plan.PRO) return t('downgrade')
if (currentPlan === Plan.STARTER) return scopedT('upgradeButton.current')
if (currentPlan === Plan.STARTER)
return t('billing.pricingCard.upgradeButton.current')
return t('upgrade')
}
@ -51,25 +51,30 @@ export const StarterPlanPricingCard = ({
<Stack>
<Stack spacing="4">
<Heading fontSize="2xl">
{scopedT('heading', {
plan: <chakra.span color="orange.400">Starter</chakra.span>,
})}
<T
keyName="billing.pricingCard.heading"
params={{ strong: <chakra.span color="orange.400" /> }}
/>
</Heading>
<Text>{scopedT('starter.description')}</Text>
<Text>{t('billing.pricingCard.starter.description')}</Text>
</Stack>
<Heading>
{formatPrice(prices.STARTER, { currency })}
<chakra.span fontSize="md">{scopedT('perMonth')}</chakra.span>
<chakra.span fontSize="md">
{t('billing.pricingCard.perMonth')}
</chakra.span>
</Heading>
</Stack>
<FeaturesList
features={[
scopedT('starter.includedSeats'),
t('billing.pricingCard.starter.includedSeats'),
<Stack key="starter-chats" spacing={0}>
<HStack>
<Text>2,000 {scopedT('chatsPerMonth')}</Text>
<MoreInfoTooltip>{scopedT('chatsTooltip')}</MoreInfoTooltip>
<Text>2,000 {t('billing.pricingCard.chatsPerMonth')}</Text>
<MoreInfoTooltip>
{t('billing.pricingCard.chatsTooltip')}
</MoreInfoTooltip>
</HStack>
<Text
fontSize="sm"
@ -78,9 +83,9 @@ export const StarterPlanPricingCard = ({
Extra chats: $10 per 500
</Text>
</Stack>,
scopedT('starter.brandingRemoved'),
scopedT('starter.fileUploadBlock'),
scopedT('starter.createFolders'),
t('billing.pricingCard.starter.brandingRemoved'),
t('billing.pricingCard.starter.fileUploadBlock'),
t('billing.pricingCard.starter.createFolders'),
]}
/>
</Stack>

View File

@ -3,7 +3,7 @@ import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import React from 'react'
import { isNotDefined } from '@typebot.io/lib'
import { ChangePlanModal } from './ChangePlanModal'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
limitReachedType?: string
@ -15,7 +15,7 @@ export const UpgradeButton = ({
excludedPlans,
...props
}: Props) => {
const t = useI18n()
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
const { workspace } = useWorkspace()
return (

View File

@ -13,15 +13,15 @@ import { Workspace } from '@typebot.io/prisma'
import React from 'react'
import { parseNumberWithCommas } from '@typebot.io/lib'
import { defaultQueryOptions, trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
import { getChatsLimit } from '@typebot.io/lib/billing/getChatsLimit'
import { useTranslate } from '@tolgee/react'
type Props = {
workspace: Workspace
}
export const UsageProgressBars = ({ workspace }: Props) => {
const scopedT = useScopedI18n('billing.usage')
const { t } = useTranslate()
const { data, isLoading } = trpc.billing.getUsage.useQuery(
{
workspaceId: workspace.id,
@ -39,12 +39,12 @@ export const UsageProgressBars = ({ workspace }: Props) => {
return (
<Stack spacing={6}>
<Heading fontSize="3xl">{scopedT('heading')}</Heading>
<Heading fontSize="3xl">{t('billing.usage.heading')}</Heading>
<Stack spacing={3}>
<Flex justifyContent="space-between">
<HStack>
<Heading fontSize="xl" as="h3">
{scopedT('chats.heading')}
{t('billing.usage.chats.heading')}
</Heading>
{chatsPercentage >= 80 && (
<Tooltip
@ -53,10 +53,10 @@ export const UsageProgressBars = ({ workspace }: Props) => {
p="3"
label={
<Text>
{scopedT('chats.alert.soonReach')}
{t('billing.usage.chats.alert.soonReach')}
<br />
<br />
{scopedT('chats.alert.updatePlan')}
{t('billing.usage.chats.alert.updatePlan')}
</Text>
}
>
@ -81,7 +81,7 @@ export const UsageProgressBars = ({ workspace }: Props) => {
<Text>
/{' '}
{workspaceChatsLimit === 'inf'
? scopedT('unlimited')
? t('billing.usage.unlimited')
: parseNumberWithCommas(workspaceChatsLimit)}
</Text>
</HStack>

View File

@ -4,7 +4,7 @@ import { TextInput } from '@/components/inputs'
import { useState } from 'react'
import { UploadButton } from '@/components/ImageUploadContent/UploadButton'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { FilePathUploadProps } from '@/features/upload/api/generateUploadUrl'
type Props = {
@ -18,7 +18,7 @@ export const AudioBubbleForm = ({
content,
onContentChange,
}: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.audio.settings')
const { t } = useTranslate()
const [currentTab, setCurrentTab] = useState<'link' | 'upload'>('link')
const updateUrl = (url: string) => onContentChange({ ...content, url })
@ -34,14 +34,14 @@ export const AudioBubbleForm = ({
onClick={() => setCurrentTab('upload')}
size="sm"
>
{scopedT('upload.label')}
{t('editor.blocks.bubbles.audio.settings.upload.label')}
</Button>
<Button
variant={currentTab === 'link' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('link')}
size="sm"
>
{scopedT('embedLink.label')}
{t('editor.blocks.bubbles.audio.settings.embedLink.label')}
</Button>
</HStack>
<Stack p="2" spacing={4}>
@ -54,25 +54,27 @@ export const AudioBubbleForm = ({
onFileUploaded={updateUrl}
colorScheme="blue"
>
{scopedT('chooseFile.label')}
{t('editor.blocks.bubbles.audio.settings.chooseFile.label')}
</UploadButton>
</Flex>
)}
{currentTab === 'link' && (
<>
<TextInput
placeholder={scopedT('worksWith.placeholder')}
placeholder={t(
'editor.blocks.bubbles.audio.settings.worksWith.placeholder'
)}
defaultValue={content.url ?? ''}
onChange={updateUrl}
/>
<Text fontSize="sm" color="gray.400" textAlign="center">
{scopedT('worksWith.text')}
{t('editor.blocks.bubbles.audio.settings.worksWith.text')}
</Text>
</>
)}
</Stack>
<SwitchWithLabel
label={scopedT('autoplay.label')}
label={t('editor.blocks.bubbles.audio.settings.autoplay.label')}
initialValue={content.isAutoplayEnabled ?? true}
onCheckChange={updateAutoPlay}
/>

View File

@ -1,17 +1,19 @@
import { Text } from '@chakra-ui/react'
import { AudioBubbleContent } from '@typebot.io/schemas'
import { isDefined } from '@typebot.io/lib'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
url: AudioBubbleContent['url']
}
export const AudioBubbleNode = ({ url }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.audio.node')
const { t } = useTranslate()
return isDefined(url) ? (
<audio src={url} controls />
) : (
<Text color={'gray.500'}>{scopedT('clickToEdit.text')}</Text>
<Text color={'gray.500'}>
{t('editor.blocks.bubbles.audio.node.clickToEdit.text')}
</Text>
)
}

View File

@ -1,4 +1,4 @@
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Text } from '@chakra-ui/react'
import { EmbedBubbleBlock } from '@typebot.io/schemas'
@ -7,8 +7,12 @@ type Props = {
}
export const EmbedBubbleContent = ({ block }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.embed.node')
const { t } = useTranslate()
if (!block.content?.url)
return <Text color="gray.500">{scopedT('clickToEdit.text')}</Text>
return <Text>{scopedT('show.text')}</Text>
return (
<Text color="gray.500">
{t('editor.blocks.bubbles.embed.node.clickToEdit.text')}
</Text>
)
return <Text>{t('editor.blocks.bubbles.embed.node.show.text')}</Text>
}

View File

@ -2,7 +2,7 @@ import { TextInput, NumberInput } from '@/components/inputs'
import { Stack, Text } from '@chakra-ui/react'
import { EmbedBubbleContent } from '@typebot.io/schemas'
import { sanitizeUrl } from '@typebot.io/lib'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
content: EmbedBubbleContent
@ -10,7 +10,7 @@ type Props = {
}
export const EmbedUploadContent = ({ content, onSubmit }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.embed.settings')
const { t } = useTranslate()
const handleUrlChange = (url: string) => {
const iframeUrl = sanitizeUrl(
url.trim().startsWith('<iframe') ? extractUrlFromIframe(url) : url
@ -25,12 +25,14 @@ export const EmbedUploadContent = ({ content, onSubmit }: Props) => {
<Stack p="2" spacing={6}>
<Stack>
<TextInput
placeholder={scopedT('worksWith.placeholder')}
placeholder={t(
'editor.blocks.bubbles.embed.settings.worksWith.placeholder'
)}
defaultValue={content?.url ?? ''}
onChange={handleUrlChange}
/>
<Text fontSize="sm" color="gray.400" textAlign="center">
{scopedT('worksWith.text')}
{t('editor.blocks.bubbles.embed.settings.worksWith.text')}
</Text>
</Stack>
@ -38,7 +40,7 @@ export const EmbedUploadContent = ({ content, onSubmit }: Props) => {
label="Height:"
defaultValue={content?.height}
onValueChange={handleHeightChange}
suffix={scopedT('numberInput.unit')}
suffix={t('editor.blocks.bubbles.embed.settings.numberInput.unit')}
width="150px"
/>
</Stack>

View File

@ -1,4 +1,4 @@
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Box, Text, Image } from '@chakra-ui/react'
import { ImageBubbleBlock } from '@typebot.io/schemas'
@ -7,11 +7,13 @@ type Props = {
}
export const ImageBubbleContent = ({ block }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.image.node')
const { t } = useTranslate()
const containsVariables =
block.content?.url?.includes('{{') && block.content.url.includes('}}')
return !block.content?.url ? (
<Text color={'gray.500'}>{scopedT('clickToEdit.text')}</Text>
<Text color={'gray.500'}>
{t('editor.blocks.bubbles.image.node.clickToEdit.text')}
</Text>
) : (
<Box w="full">
<Image

View File

@ -2,7 +2,7 @@ import { ImageUploadContent } from '@/components/ImageUploadContent'
import { TextInput } from '@/components/inputs'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { FilePathUploadProps } from '@/features/upload/api/generateUploadUrl'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Stack } from '@chakra-ui/react'
import { isDefined, isNotEmpty } from '@typebot.io/lib'
import { ImageBubbleBlock } from '@typebot.io/schemas'
@ -19,9 +19,7 @@ export const ImageBubbleSettings = ({
block,
onContentChange,
}: Props) => {
const scopedT = useScopedI18n(
'editor.blocks.bubbles.image.switchWithLabel.onClick'
)
const { t } = useTranslate()
const [showClickLinkInput, setShowClickLinkInput] = useState(
isNotEmpty(block.content.clickLink?.url)
)
@ -60,7 +58,7 @@ export const ImageBubbleSettings = ({
/>
<Stack>
<SwitchWithLabel
label={scopedT('label')}
label={t('editor.blocks.bubbles.image.switchWithLabel.onClick.label')}
initialValue={showClickLinkInput}
onCheckChange={toggleClickLink}
/>
@ -73,7 +71,9 @@ export const ImageBubbleSettings = ({
defaultValue={block.content.clickLink?.url}
/>
<TextInput
placeholder={scopedT('placeholder')}
placeholder={t(
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder'
)}
onChange={updateClickLinkAltText}
defaultValue={block.content.clickLink?.alt}
/>

View File

@ -18,7 +18,7 @@ import { colors } from '@/lib/theme'
import { useOutsideClick } from '@/hooks/useOutsideClick'
import { selectEditor, TElement } from '@udecode/plate-common'
import { TextEditorToolBar } from './TextEditorToolBar'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type TextBubbleEditorContentProps = {
id: string
@ -31,7 +31,7 @@ const TextBubbleEditorContent = ({
textEditorValue,
onClose,
}: TextBubbleEditorContentProps) => {
const scopedT = useScopedI18n('editor.blocks.bubbles')
const { t } = useTranslate()
const editor = usePlateEditorRef()
const varDropdownRef = useRef<HTMLDivElement | null>(null)
const rememberedSelection = useRef<BaseSelection | null>(null)
@ -137,7 +137,7 @@ const TextBubbleEditorContent = ({
})
setIsFirstFocus(false)
},
'aria-label': `${scopedT('textEditor.plate.label')}`,
'aria-label': `${t('editor.blocks.bubbles.textEditor.plate.label')}`,
onBlur: () => {
rememberedSelection.current = editor?.selection
},
@ -156,7 +156,9 @@ const TextBubbleEditorContent = ({
<VariableSearchInput
initialVariableId={undefined}
onSelectVariable={handleVariableSelected}
placeholder={scopedT('textEditor.searchVariable.placeholder')}
placeholder={t(
'editor.blocks.bubbles.textEditor.searchVariable.placeholder'
)}
autoFocus
/>
</PopoverContent>

View File

@ -1,4 +1,4 @@
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Box, Text, Image } from '@chakra-ui/react'
import { VideoBubbleBlock, VideoBubbleContentType } from '@typebot.io/schemas'
@ -7,9 +7,13 @@ type Props = {
}
export const VideoBubbleContent = ({ block }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.video.node')
const { t } = useTranslate()
if (!block.content?.url || !block.content.type)
return <Text color="gray.500">{scopedT('clickToEdit.text')}</Text>
return (
<Text color="gray.500">
{t('editor.blocks.bubbles.video.node.clickToEdit.text')}
</Text>
)
const containsVariables =
block.content?.url?.includes('{{') && block.content.url.includes('}}')
switch (block.content.type) {

View File

@ -5,7 +5,7 @@ import {
VideoBubbleContentType,
} from '@typebot.io/schemas'
import { NumberInput, TextInput } from '@/components/inputs'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { parseVideoUrl } from '@typebot.io/lib/parseVideoUrl'
type Props = {
@ -14,7 +14,7 @@ type Props = {
}
export const VideoUploadContent = ({ content, onSubmit }: Props) => {
const scopedT = useScopedI18n('editor.blocks.bubbles.video.settings')
const { t } = useTranslate()
const updateUrl = (url: string) => {
const info = parseVideoUrl(url)
return onSubmit({
@ -33,12 +33,14 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
<Stack p="2" spacing={4}>
<Stack>
<TextInput
placeholder={scopedT('worksWith.placeholder')}
placeholder={t(
'editor.blocks.bubbles.video.settings.worksWith.placeholder'
)}
defaultValue={content?.url ?? ''}
onChange={updateUrl}
/>
<Text fontSize="sm" color="gray.400" textAlign="center">
{scopedT('worksWith.text')}
{t('editor.blocks.bubbles.video.settings.worksWith.text')}
</Text>
</Stack>
@ -47,7 +49,7 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
label="Height:"
defaultValue={content?.height ?? 400}
onValueChange={updateHeight}
suffix={scopedT('numberInput.unit')}
suffix={t('editor.blocks.bubbles.video.settings.numberInput.unit')}
width="150px"
/>
)}

View File

@ -3,7 +3,7 @@ import { canReadTypebots } from '@/helpers/databaseRules'
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { Group, IntegrationBlockType, Typebot } from '@typebot.io/schemas'
import { byId, isWebhookBlock, parseGroupTitle } from '@typebot.io/lib'
import { byId, isWebhookBlock } from '@typebot.io/lib'
import { z } from 'zod'
import { Webhook } from '@typebot.io/prisma'
@ -70,7 +70,7 @@ export const listWebhookBlocks = authenticatedProcedure
...blocks.map((block) => ({
id: block.id,
type: block.type,
label: `${parseGroupTitle(group.title)} > ${block.id}`,
label: `${group.title} > ${block.id}`,
url: block.options.webhook
? block.options.webhook.url
: typebot?.webhooks.find(byId(block.webhookId))?.url ?? undefined,

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Tag, Text } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { byId, isDefined, parseGroupTitle } from '@typebot.io/lib'
import { byId, isDefined } from '@typebot.io/lib'
import { JumpBlock } from '@typebot.io/schemas/features/blocks/logic/jump'
type Props = {
@ -15,8 +15,7 @@ export const JumpNodeBody = ({ options }: Props) => {
if (!selectedGroup) return <Text color="gray.500">Configure...</Text>
return (
<Text>
Jump to{' '}
<Tag colorScheme="blue">{parseGroupTitle(selectedGroup.title)}</Tag>{' '}
Jump to <Tag colorScheme="blue">{selectedGroup.title}</Tag>{' '}
{isDefined(blockIndex) && blockIndex >= 0 ? (
<>
at block <Tag colorScheme="blue">{blockIndex + 1}</Tag>

View File

@ -3,7 +3,7 @@ import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { Stack } from '@chakra-ui/react'
import { JumpBlock } from '@typebot.io/schemas/features/blocks/logic/jump'
import React from 'react'
import { byId, parseGroupTitle } from '@typebot.io/lib'
import { byId } from '@typebot.io/lib'
type Props = {
groupId: string
@ -32,7 +32,7 @@ export const JumpSettings = ({ groupId, options, onOptionsChange }: Props) => {
items={typebot.groups
.filter((group) => group.id !== currentGroupId)
.map((group) => ({
label: parseGroupTitle(group.title),
label: group.title,
value: group.id,
}))}
selectedItem={selectedGroup?.id}

View File

@ -1,7 +1,6 @@
import { Select } from '@/components/inputs/Select'
import { Input } from '@chakra-ui/react'
import { Group } from '@typebot.io/schemas'
import { parseGroupTitle } from '@typebot.io/lib'
type Props = {
groups: Group[]
@ -23,7 +22,7 @@ export const GroupsDropdown = ({
<Select
selectedItem={groupId}
items={(groups ?? []).map((group) => ({
label: parseGroupTitle(group.title),
label: group.title,
value: group.id,
}))}
onSelect={onGroupIdSelected}

View File

@ -28,7 +28,11 @@ export const CollaborationMenuButton = ({
</Tooltip>
</span>
</PopoverTrigger>
<PopoverContent shadow="lg" width="430px">
<PopoverContent
shadow="lg"
width="430px"
rootProps={{ style: { transform: 'scale(0)' } }}
>
<CollaborationList />
</PopoverContent>
</Popover>

View File

@ -5,13 +5,13 @@ import { useUser } from '@/features/account/hooks/useUser'
import { isNotDefined } from '@typebot.io/lib'
import Link from 'next/link'
import { EmojiOrImageIcon } from '@/components/EmojiOrImageIcon'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { WorkspaceDropdown } from '@/features/workspace/components/WorkspaceDropdown'
import { WorkspaceSettingsModal } from '@/features/workspace/components/WorkspaceSettingsModal'
export const DashboardHeader = () => {
const scopedT = useScopedI18n('dashboard.header')
const { t } = useTranslate()
const { user, logOut } = useUser()
const { workspace, switchWorkspace, createWorkspace } = useWorkspace()
@ -50,7 +50,7 @@ export const DashboardHeader = () => {
onClick={onOpen}
isLoading={isNotDefined(workspace)}
>
{scopedT('settingsButton.label')}
{t('dashboard.header.settingsButton.label')}
</Button>
<WorkspaceDropdown
currentWorkspace={workspace}

View File

@ -5,7 +5,6 @@ import {
PreCheckoutModalProps,
} from '@/features/billing/components/PreCheckoutModal'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { useScopedI18n } from '@/locales'
import { Stack, VStack, Spinner, Text } from '@chakra-ui/react'
import { Plan } from '@typebot.io/prisma'
import { useRouter } from 'next/router'
@ -16,9 +15,10 @@ import { TypebotDndProvider } from '@/features/folders/TypebotDndProvider'
import { ParentModalProvider } from '@/features/graph/providers/ParentModalProvider'
import { trpc } from '@/lib/trpc'
import { guessIfUserIsEuropean } from '@typebot.io/lib/billing/guessIfUserIsEuropean'
import { useTranslate } from '@tolgee/react'
export const DashboardPage = () => {
const scopedT = useScopedI18n('dashboard')
const { t } = useTranslate()
const [isLoading, setIsLoading] = useState(false)
const router = useRouter()
const { user } = useUser()
@ -58,7 +58,7 @@ export const DashboardPage = () => {
return (
<Stack minH="100vh">
<Seo title={workspace?.name ?? scopedT('title')} />
<Seo title={workspace?.name ?? t('dashboard.title')} />
<DashboardHeader />
{!workspace?.stripeId && (
<ParentModalProvider>
@ -73,7 +73,7 @@ export const DashboardPage = () => {
<TypebotDndProvider>
{isLoading ? (
<VStack w="full" justifyContent="center" pt="10" spacing={6}>
<Text>{scopedT('redirectionMessage')}</Text>
<Text>{t('dashboard.redirectionMessage')}</Text>
<Spinner />
</VStack>
) : (

View File

@ -14,7 +14,7 @@ import { Plan } from '@typebot.io/prisma'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { BlockLabel } from './BlockLabel'
import { LockTag } from '@/features/billing/components/LockTag'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
type: DraggableBlockType
@ -27,7 +27,7 @@ type Props = {
export const BlockCard = (
props: Pick<Props, 'type' | 'onMouseDown'>
): JSX.Element => {
const scopedT = useScopedI18n('editor.blockCard')
const { t } = useTranslate()
const { workspace } = useWorkspace()
switch (props.type) {
@ -35,7 +35,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('bubbleBlock.tooltip.label')}
tooltip={t('blocks.bubbles.embed.blockCard.tooltip')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />
@ -45,7 +45,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('inputBlock.tooltip.files.label')}
tooltip={t('blocks.inputs.fileUpload.blockCard.tooltip')}
>
<BlockIcon type={props.type} />
<HStack>
@ -58,7 +58,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('logicBlock.tooltip.code.label')}
tooltip={t('editor.blockCard.logicBlock.tooltip.code.label')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />
@ -68,7 +68,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('logicBlock.tooltip.typebotLink.label')}
tooltip={t('editor.blockCard.logicBlock.tooltip.typebotLink.label')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />
@ -78,7 +78,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('logicBlock.tooltip.jump.label')}
tooltip={t('editor.blockCard.logicBlock.tooltip.jump.label')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />
@ -88,7 +88,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('integrationBlock.tooltip.googleSheets.label')}
tooltip={t('blocks.integrations.googleSheets.blockCard.tooltip')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />
@ -98,7 +98,7 @@ export const BlockCard = (
return (
<BlockCardLayout
{...props}
tooltip={scopedT('integrationBlock.tooltip.googleAnalytics.label')}
tooltip={t('blocks.integrations.googleAnalytics.blockCard.tooltip')}
>
<BlockIcon type={props.type} />
<BlockLabel type={props.type} />

View File

@ -7,84 +7,98 @@ import {
BlockType,
} from '@typebot.io/schemas'
import React from 'react'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = { type: BlockType }
export const BlockLabel = ({ type }: Props): JSX.Element => {
const scopedT = useScopedI18n('editor.sidebarBlock')
const { t } = useTranslate()
switch (type) {
case 'start':
return <Text fontSize="sm">{scopedT('start.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.start.label')}</Text>
case BubbleBlockType.TEXT:
case InputBlockType.TEXT:
return <Text fontSize="sm">{scopedT('text.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.text.label')}</Text>
case BubbleBlockType.IMAGE:
return <Text fontSize="sm">{scopedT('image.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.image.label')}</Text>
case BubbleBlockType.VIDEO:
return <Text fontSize="sm">{scopedT('video.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.video.label')}</Text>
case BubbleBlockType.EMBED:
return <Text fontSize="sm">{scopedT('embed.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.embed.label')}</Text>
case BubbleBlockType.AUDIO:
return <Text fontSize="sm">{scopedT('audio.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.audio.label')}</Text>
case InputBlockType.NUMBER:
return <Text fontSize="sm">{scopedT('number.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.number.label')}</Text>
case InputBlockType.EMAIL:
return <Text fontSize="sm">{scopedT('email.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.email.label')}</Text>
case InputBlockType.URL:
return <Text fontSize="sm">{scopedT('website.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.website.label')}</Text>
case InputBlockType.DATE:
return <Text fontSize="sm">{scopedT('date.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.date.label')}</Text>
case InputBlockType.PHONE:
return <Text fontSize="sm">{scopedT('phone.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.phone.label')}</Text>
case InputBlockType.CHOICE:
return <Text fontSize="sm">{scopedT('button.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.button.label')}</Text>
case InputBlockType.PICTURE_CHOICE:
return <Text fontSize="sm">{scopedT('picChoice.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.picChoice.label')}</Text>
)
case InputBlockType.PAYMENT:
return <Text fontSize="sm">{scopedT('payment.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.payment.label')}</Text>
case InputBlockType.RATING:
return <Text fontSize="sm">{scopedT('rating.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.rating.label')}</Text>
case InputBlockType.FILE:
return <Text fontSize="sm">{scopedT('file.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.file.label')}</Text>
case LogicBlockType.SET_VARIABLE:
return <Text fontSize="sm">{scopedT('setVariable.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.setVariable.label')}</Text>
)
case LogicBlockType.CONDITION:
return <Text fontSize="sm">{scopedT('condition.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.condition.label')}</Text>
)
case LogicBlockType.REDIRECT:
return <Text fontSize="sm">{scopedT('redirect.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.redirect.label')}</Text>
)
case LogicBlockType.SCRIPT:
return <Text fontSize="sm">{scopedT('script.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.script.label')}</Text>
case LogicBlockType.TYPEBOT_LINK:
return <Text fontSize="sm">{scopedT('typebot.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.typebot.label')}</Text>
case LogicBlockType.WAIT:
return <Text fontSize="sm">{scopedT('wait.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.wait.label')}</Text>
case LogicBlockType.JUMP:
return <Text fontSize="sm">{scopedT('jump.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.jump.label')}</Text>
case LogicBlockType.AB_TEST:
return <Text fontSize="sm">{scopedT('abTest.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.abTest.label')}</Text>
case IntegrationBlockType.GOOGLE_SHEETS:
return <Text fontSize="sm">{scopedT('sheets.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.sheets.label')}</Text>
case IntegrationBlockType.GOOGLE_ANALYTICS:
return <Text fontSize="sm">{scopedT('analytics.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.analytics.label')}</Text>
)
case IntegrationBlockType.WEBHOOK:
return <Text fontSize="sm">{scopedT('webhook.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.webhook.label')}</Text>
case IntegrationBlockType.ZAPIER:
return <Text fontSize="sm">{scopedT('zapier.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.zapier.label')}</Text>
case IntegrationBlockType.MAKE_COM:
return <Text fontSize="sm">{scopedT('makecom.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.makecom.label')}</Text>
case IntegrationBlockType.PABBLY_CONNECT:
return <Text fontSize="sm">{scopedT('pabbly.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.pabbly.label')}</Text>
case IntegrationBlockType.EMAIL:
return <Text fontSize="sm">{scopedT('email.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.email.label')}</Text>
case IntegrationBlockType.CHATWOOT:
return <Text fontSize="sm">{scopedT('chatwoot.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.chatwoot.label')}</Text>
)
case IntegrationBlockType.OPEN_AI:
return <Text fontSize="sm">{scopedT('openai.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.openai.label')}</Text>
case IntegrationBlockType.PIXEL:
return <Text fontSize="sm">{scopedT('pixel.label')}</Text>
return <Text fontSize="sm">{t('editor.sidebarBlock.pixel.label')}</Text>
case IntegrationBlockType.ZEMANTIC_AI:
return <Text fontSize="sm">{scopedT('zemanticAi.label')}</Text>
return (
<Text fontSize="sm">{t('editor.sidebarBlock.zemanticAi.label')}</Text>
)
}
}

View File

@ -23,10 +23,10 @@ import { BlockCard } from './BlockCard'
import { LockedIcon, UnlockedIcon } from '@/components/icons'
import { BlockCardOverlay } from './BlockCardOverlay'
import { headerHeight } from '../constants'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const BlocksSideBar = () => {
const scopedT = useScopedI18n('editor.sidebarBlocks')
const { t } = useTranslate()
const { setDraggedBlockType, draggedBlockType } = useBlockDnd()
const [position, setPosition] = useState({
x: 0,
@ -107,16 +107,16 @@ export const BlocksSideBar = () => {
<Tooltip
label={
isLocked
? scopedT('sidebar.unlock.label')
: scopedT('sidebar.lock.label')
? t('editor.sidebarBlocks.sidebar.unlock.label')
: t('editor.sidebarBlocks.sidebar.lock.label')
}
>
<IconButton
icon={isLocked ? <LockedIcon /> : <UnlockedIcon />}
aria-label={
isLocked
? scopedT('sidebar.icon.unlock.label')
: scopedT('sidebar.icon.lock.label')
? t('editor.sidebarBlocks.sidebar.icon.unlock.label')
: t('editor.sidebarBlocks.sidebar.icon.lock.label')
}
size="sm"
onClick={handleLockClick}
@ -126,7 +126,7 @@ export const BlocksSideBar = () => {
<Stack>
<Text fontSize="sm" fontWeight="semibold">
{scopedT('blockType.bubbles.heading')}
{t('editor.sidebarBlocks.blockType.bubbles.heading')}
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(BubbleBlockType).map((type) => (
@ -137,7 +137,7 @@ export const BlocksSideBar = () => {
<Stack>
<Text fontSize="sm" fontWeight="semibold">
{scopedT('blockType.inputs.heading')}
{t('editor.sidebarBlocks.blockType.inputs.heading')}
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(InputBlockType).map((type) => (
@ -148,7 +148,7 @@ export const BlocksSideBar = () => {
<Stack>
<Text fontSize="sm" fontWeight="semibold">
{scopedT('blockType.logic.heading')}
{t('editor.sidebarBlocks.blockType.logic.heading')}
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(LogicBlockType).map((type) => (
@ -159,7 +159,7 @@ export const BlocksSideBar = () => {
<Stack>
<Text fontSize="sm" fontWeight="semibold">
{scopedT('blockType.integrations.heading')}
{t('editor.sidebarBlocks.blockType.integrations.heading')}
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(IntegrationBlockType).map((type) => (

View File

@ -6,7 +6,7 @@ import {
useColorModeValue,
} from '@chakra-ui/react'
import React, { useState } from 'react'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type EditableProps = {
defaultName: string
@ -16,7 +16,7 @@ export const EditableTypebotName = ({
defaultName,
onNewName,
}: EditableProps) => {
const scopedT = useScopedI18n('editor.editableTypebotName')
const { t } = useTranslate()
const emptyNameBg = useColorModeValue('gray.100', 'gray.700')
const [currentName, setCurrentName] = useState(defaultName)
@ -27,7 +27,7 @@ export const EditableTypebotName = ({
}
return (
<Tooltip label={scopedT('tooltip.rename.label')}>
<Tooltip label={t('editor.editableTypebotName.tooltip.rename.label')}>
<Editable
value={currentName}
onChange={setCurrentName}

View File

@ -21,10 +21,10 @@ import {
} from '@chakra-ui/react'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const GettingStartedModal = () => {
const scopedT = useScopedI18n('editor.gettingStartedModal')
const { t } = useTranslate()
const { query } = useRouter()
const { isOpen, onOpen, onClose } = useDisclosure()
@ -40,7 +40,9 @@ export const GettingStartedModal = () => {
<ModalCloseButton />
<ModalBody as={Stack} spacing="8" py="10">
<Stack spacing={4}>
<Heading fontSize="xl">{scopedT('editorBasics.heading')}</Heading>
<Heading fontSize="xl">
{t('editor.gettingStartedModal.editorBasics.heading')}
</Heading>
<List spacing={4}>
<HStack as={ListItem}>
<Flex
@ -56,7 +58,9 @@ export const GettingStartedModal = () => {
>
1
</Flex>
<Text>{scopedT('editorBasics.list.one.label')}</Text>
<Text>
{t('editor.gettingStartedModal.editorBasics.list.one.label')}
</Text>
</HStack>
<HStack as={ListItem}>
<Flex
@ -72,7 +76,9 @@ export const GettingStartedModal = () => {
>
2
</Flex>
<Text>{scopedT('editorBasics.list.two.label')}</Text>
<Text>
{t('editor.gettingStartedModal.editorBasics.list.two.label')}
</Text>
</HStack>
<HStack as={ListItem}>
<Flex
@ -88,7 +94,11 @@ export const GettingStartedModal = () => {
>
3
</Flex>
<Text>{scopedT('editorBasics.list.three.label')}</Text>
<Text>
{t(
'editor.gettingStartedModal.editorBasics.list.three.label'
)}
</Text>
</HStack>
<HStack as={ListItem}>
<Flex
@ -104,15 +114,18 @@ export const GettingStartedModal = () => {
>
4
</Flex>
<Text>{scopedT('editorBasics.list.four.label')}</Text>
<Text>
{t('editor.gettingStartedModal.editorBasics.list.four.label')}
</Text>
</HStack>
</List>
</Stack>
<Text>{scopedT('editorBasics.list.label')}</Text>
<Text>{t('editor.gettingStartedModal.editorBasics.list.label')}</Text>
<Stack spacing={4}>
<Heading fontSize="xl">
{scopedT('seeAction.label')} ({`<`} {scopedT('seeAction.time')})
{t('editor.gettingStartedModal.seeAction.label')} ({`<`}{' '}
{t('editor.gettingStartedModal.seeAction.time')})
</Heading>
<iframe
width="100%"
@ -127,7 +140,7 @@ export const GettingStartedModal = () => {
<AccordionItem>
<AccordionButton>
<Box flex="1" textAlign="left">
{scopedT('seeAction.item.label')}
{t('editor.gettingStartedModal.seeAction.item.label')}
</Box>
<AccordionIcon />
</AccordionButton>

View File

@ -30,10 +30,10 @@ import { RightPanel, useEditor } from '../providers/EditorProvider'
import { useTypebot } from '../providers/TypebotProvider'
import { SupportBubble } from '@/components/SupportBubble'
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const TypebotHeader = () => {
const scopedT = useScopedI18n('editor.headers')
const { t } = useTranslate()
const router = useRouter()
const {
typebot,
@ -105,7 +105,7 @@ export const TypebotHeader = () => {
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
size="sm"
>
{scopedT('flowButton.label')}
{t('editor.headers.flowButton.label')}
</Button>
<Button
as={Link}
@ -114,7 +114,7 @@ export const TypebotHeader = () => {
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
size="sm"
>
{scopedT('themeButton.label')}
{t('editor.headers.themeButton.label')}
</Button>
<Button
as={Link}
@ -123,7 +123,7 @@ export const TypebotHeader = () => {
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
size="sm"
>
{scopedT('settingsButton.label')}
{t('editor.headers.settingsButton.label')}
</Button>
<Button
as={Link}
@ -132,7 +132,7 @@ export const TypebotHeader = () => {
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
size="sm"
>
{scopedT('shareButton.label')}
{t('editor.headers.shareButton.label')}
</Button>
{isDefined(publishedTypebot) && (
<Button
@ -142,7 +142,7 @@ export const TypebotHeader = () => {
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
size="sm"
>
{scopedT('resultsButton.label')}
{t('editor.headers.resultsButton.label')}
</Button>
)}
</HStack>
@ -225,21 +225,23 @@ export const TypebotHeader = () => {
</Tooltip>
</HStack>
<Button leftIcon={<BuoyIcon />} onClick={handleHelpClick} size="sm">
{scopedT('helpButton.label')}
{t('editor.headers.helpButton.label')}
</Button>
</HStack>
{isSavingLoading && (
<HStack>
<Spinner speed="0.7s" size="sm" color="gray.400" />
<Text fontSize="sm" color="gray.400">
{scopedT('savingSpinner.label')}
{t('editor.headers.savingSpinner.label')}
</Text>
</HStack>
)}
</HStack>
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
<Flex pos="relative">
<CollaborationMenuButton isLoading={isNotDefined(typebot)} />
</Flex>
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button
colorScheme="gray"
@ -247,7 +249,7 @@ export const TypebotHeader = () => {
isLoading={isNotDefined(typebot)}
size="sm"
>
{scopedT('previewButton.label')}
{t('editor.headers.previewButton.label')}
</Button>
)}
<PublishButton size="sm" />

View File

@ -23,7 +23,6 @@ import { areTypebotsEqual } from '@/features/publish/helpers/areTypebotsEqual'
import { isPublished as isPublishedHelper } from '@/features/publish/helpers/isPublished'
import { convertPublicTypebotToTypebot } from '@/features/publish/helpers/convertPublicTypebotToTypebot'
import { trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
const autoSaveTimeout = 10000
@ -80,7 +79,6 @@ export const TypebotProvider = ({
children: ReactNode
typebotId?: string
}) => {
const scopedT = useScopedI18n('editor.provider')
const { push } = useRouter()
const { showToast } = useToast()
@ -96,15 +94,11 @@ export const TypebotProvider = ({
if (error.data?.httpStatus === 404) {
showToast({
status: 'info',
description: scopedT('messages.getTypebotError.description'),
description: "Couldn't find typebot.",
})
push('/typebots')
return
}
showToast({
title: scopedT('messages.getTypebotError.title'),
description: error.message,
})
},
}
)
@ -114,13 +108,6 @@ export const TypebotProvider = ({
{ typebotId: typebotId as string },
{
enabled: isDefined(typebotId),
onError: (error) => {
if (error.data?.httpStatus === 404) return
showToast({
title: scopedT('messages.publishedTypebotError.title'),
description: error.message,
})
},
}
)
@ -128,7 +115,7 @@ export const TypebotProvider = ({
trpc.typebot.updateTypebot.useMutation({
onError: (error) =>
showToast({
title: scopedT('messages.updateTypebotError.title'),
title: 'Error while updating typebot',
description: error.message,
}),
onSuccess: () => {
@ -264,10 +251,7 @@ export const TypebotProvider = ({
isPublished,
updateTypebot: updateLocalTypebot,
restorePublishedTypebot,
...groupsActions(
setLocalTypebot as SetTypebot,
scopedT('groups.copy.title')
),
...groupsActions(setLocalTypebot as SetTypebot),
...blocksAction(setLocalTypebot as SetTypebot),
...variablesAction(setLocalTypebot as SetTypebot),
...edgesAction(setLocalTypebot as SetTypebot),

View File

@ -12,7 +12,7 @@ import {
createBlockDraft,
duplicateBlockDraft,
} from './blocks'
import { isEmpty, parseGroupTitle } from '@typebot.io/lib'
import { isEmpty } from '@typebot.io/lib'
import { Coordinates } from '@/features/graph/types'
export type GroupsActions = {
@ -28,10 +28,7 @@ export type GroupsActions = {
deleteGroup: (groupIndex: number) => void
}
const groupsActions = (
setTypebot: SetTypebot,
groupCopyLabel: string
): GroupsActions => ({
const groupsActions = (setTypebot: SetTypebot): GroupsActions => ({
createGroup: ({
id,
block,
@ -67,11 +64,19 @@ const groupsActions = (
const group = typebot.groups[groupIndex]
const id = createId()
const totalGroupsWithSameTitle = typebot.groups.filter(
(group) => group.title === group.title
).length
const newGroup: Group = {
...group,
title: isEmpty(group.title)
? ''
: `${parseGroupTitle(group.title)} ${groupCopyLabel}`,
: `${group.title}${
totalGroupsWithSameTitle > 0
? ` (${totalGroupsWithSameTitle})`
: ''
}}`,
id,
blocks: group.blocks.map((block) => duplicateBlockDraft(id)(block)),
graphCoordinates: {

View File

@ -3,10 +3,10 @@ import { ChevronLeftIcon } from '@/components/icons'
import { useTypebotDnd } from '../TypebotDndProvider'
import Link from 'next/link'
import React, { useMemo } from 'react'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const BackButton = ({ id }: { id: string | null }) => {
const t = useI18n()
const { t } = useTranslate()
const { draggedTypebot, setMouseOverFolderId, mouseOverFolderId } =
useTypebotDnd()

View File

@ -3,14 +3,14 @@ import { PlusIcon } from '@/components/icons'
import { useRouter } from 'next/router'
import { stringify } from 'qs'
import React from 'react'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const CreateBotButton = ({
folderId,
isFirstBot,
...props
}: { folderId?: string; isFirstBot: boolean } & ButtonProps) => {
const scopedT = useScopedI18n('folders.createTypebotButton')
const { t } = useTranslate()
const router = useRouter()
const handleClick = () =>
@ -39,7 +39,7 @@ export const CreateBotButton = ({
textAlign="center"
mt="6"
>
{scopedT('label')}
{t('folders.createTypebotButton.label')}
</Text>
</VStack>
</Button>

View File

@ -3,16 +3,15 @@ import { FolderPlusIcon } from '@/components/icons'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { Plan } from '@typebot.io/prisma'
import React from 'react'
import { useI18n, useScopedI18n } from '@/locales'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { LockTag } from '@/features/billing/components/LockTag'
import { isFreePlan } from '@/features/billing/helpers/isFreePlan'
import { useTranslate } from '@tolgee/react'
type Props = { isLoading: boolean; onClick: () => void }
export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
const t = useI18n()
const scopedT = useScopedI18n('folders.createFolderButton')
const { t } = useTranslate()
const { workspace } = useWorkspace()
const { isOpen, onOpen, onClose } = useDisclosure()
@ -27,7 +26,7 @@ export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
isLoading={isLoading}
>
<HStack>
<Text>{scopedT('label')}</Text>
<Text>{t('folders.createFolderButton.label')}</Text>
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
</HStack>
<ChangePlanModal

View File

@ -24,8 +24,8 @@ import { useRouter } from 'next/router'
import React, { useMemo } from 'react'
import { deleteFolderQuery } from '../queries/deleteFolderQuery'
import { useToast } from '@/hooks/useToast'
import { useI18n, useScopedI18n } from '@/locales'
import { updateFolderQuery } from '../queries/updateFolderQuery'
import { T, useTranslate } from '@tolgee/react'
export const FolderButton = ({
folder,
@ -36,8 +36,7 @@ export const FolderButton = ({
onFolderDeleted: () => void
onFolderRenamed: (newName: string) => void
}) => {
const t = useI18n()
const scopedT = useScopedI18n('folders.folderButton')
const { t } = useTranslate()
const router = useRouter()
const { draggedTypebot, setMouseOverFolderId, mouseOverFolderId } =
useTypebotDnd()
@ -140,9 +139,12 @@ export const FolderButton = ({
confirmButtonLabel={'Delete'}
message={
<Text>
{scopedT('deleteConfirmationMessage', {
folderName: <strong>{folder.name}</strong>,
})}
<T
keyName="folders.folderButton.deleteConfirmationMessage"
params={{
strong: <strong>{folder.name}</strong>,
}}
/>
</Text>
}
title={`Delete ${folder.name}?`}

View File

@ -21,7 +21,7 @@ import { CreateFolderButton } from './CreateFolderButton'
import { ButtonSkeleton, FolderButton } from './FolderButton'
import { TypebotButton } from './TypebotButton'
import { TypebotCardOverlay } from './TypebotButtonOverlay'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { useTypebots } from '@/features/dashboard/hooks/useTypebots'
import { TypebotInDashboard } from '@/features/dashboard/types'
import { trpc } from '@/lib/trpc'
@ -31,7 +31,7 @@ type Props = { folder: DashboardFolder | null }
const dragDistanceTolerance = 20
export const FolderContent = ({ folder }: Props) => {
const t = useI18n()
const { t } = useTranslate()
const { workspace, currentRole } = useWorkspace()
const [isCreatingFolder, setIsCreatingFolder] = useState(false)
const {

View File

@ -1,7 +1,7 @@
import { Seo } from '@/components/Seo'
import { DashboardHeader } from '@/features/dashboard/components/DashboardHeader'
import { useToast } from '@/hooks/useToast'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { Stack, Flex, Spinner } from '@chakra-ui/react'
import { useRouter } from 'next/router'
import { useFolder } from '../hooks/useFolder'
@ -9,7 +9,7 @@ import { TypebotDndProvider } from '../TypebotDndProvider'
import { FolderContent } from './FolderContent'
export const FolderPage = () => {
const t = useI18n()
const { t } = useTranslate()
const router = useRouter()
const { showToast } = useToast()

View File

@ -21,7 +21,7 @@ import { useDebounce } from 'use-debounce'
import { useToast } from '@/hooks/useToast'
import { MoreButton } from './MoreButton'
import { EmojiOrImageIcon } from '@/components/EmojiOrImageIcon'
import { useScopedI18n } from '@/locales'
import { T, useTranslate } from '@tolgee/react'
import { TypebotInDashboard } from '@/features/dashboard/types'
import { isMobile } from '@/helpers/isMobile'
import { trpc, trpcVanilla } from '@/lib/trpc'
@ -40,7 +40,7 @@ export const TypebotButton = ({
onTypebotUpdated,
onMouseDown,
}: Props) => {
const scopedT = useScopedI18n('folders.typebotButton')
const { t } = useTranslate()
const router = useRouter()
const { draggedTypebot } = useTypebotDnd()
const [draggedTypebotDebounced] = useDebounce(draggedTypebot, 200)
@ -149,7 +149,7 @@ export const TypebotButton = ({
top="27px"
size="sm"
>
{scopedT('live')}
{t('folders.typebotButton.live')}
</Tag>
)}
{!isReadOnly && (
@ -169,18 +169,18 @@ export const TypebotButton = ({
pos="absolute"
top="20px"
right="20px"
aria-label={scopedT('showMoreOptions')}
aria-label={t('folders.typebotButton.showMoreOptions')}
>
{typebot.publishedTypebotId && (
<MenuItem onClick={handleUnpublishClick}>
{scopedT('unpublish')}
{t('folders.typebotButton.unpublish')}
</MenuItem>
)}
<MenuItem onClick={handleDuplicateClick}>
{scopedT('duplicate')}
{t('folders.typebotButton.duplicate')}
</MenuItem>
<MenuItem color="red.400" onClick={handleDeleteClick}>
{scopedT('delete')}
{t('folders.typebotButton.delete')}
</MenuItem>
</MoreButton>
</>
@ -203,13 +203,16 @@ export const TypebotButton = ({
message={
<Stack spacing="4">
<Text>
{scopedT('deleteConfirmationMessage', {
typebotName: <strong>{typebot.name}</strong>,
})}
<T
keyName="folders.typebotButton.deleteConfirmationMessage"
params={{
strong: <strong>{typebot.name}</strong>,
}}
/>
</Text>
<Alert status="warning">
<AlertIcon />
{scopedT('deleteConfirmationMessageWarning')}
{t('folders.typebotButton.deleteConfirmationMessageWarning')}
</Alert>
</Stack>
}

View File

@ -42,7 +42,7 @@ import { ChatwootNodeBody } from '@/features/blocks/integrations/chatwoot/compon
import { AbTestNodeBody } from '@/features/blocks/logic/abTest/components/AbTestNodeBody'
import { PictureChoiceNode } from '@/features/blocks/inputs/pictureChoice/components/PictureChoiceNode'
import { PixelNodeBody } from '@/features/blocks/integrations/pixel/components/PixelNodeBody'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { ZemanticAiNodeBody } from '@/features/blocks/integrations/zemanticAi/ZemanticAiNodeBody'
type Props = {
@ -50,7 +50,7 @@ type Props = {
indices: BlockIndices
}
export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
const scopedT = useScopedI18n('editor.blocks.start')
const { t } = useTranslate()
switch (block.type) {
case BubbleBlockType.TEXT: {
return <TextBubbleContent block={block} />
@ -201,7 +201,7 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
return <ZemanticAiNodeBody options={block.options} />
}
case 'start': {
return <Text>{scopedT('text')}</Text>
return <Text>{t('editor.blocks.start.text')}</Text>
}
}
}

View File

@ -27,7 +27,7 @@ import { isNotDefined } from '@typebot.io/lib'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { isFreePlan } from '@/features/billing/helpers/isFreePlan'
import { parseTimeSince } from '@/helpers/parseTimeSince'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { trpc } from '@/lib/trpc'
import { useToast } from '@/hooks/useToast'
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
@ -39,7 +39,7 @@ export const PublishButton = ({
isMoreMenuDisabled = false,
...props
}: Props) => {
const t = useI18n()
const { t } = useTranslate()
const warningTextColor = useColorModeValue('red.300', 'red.600')
const { workspace } = useWorkspace()
const { push, query, pathname } = useRouter()

View File

@ -24,12 +24,12 @@ import { hasProPerks } from '@/features/billing/helpers/hasProPerks'
import { CustomDomainsDropdown } from '@/features/customDomains/components/CustomDomainsDropdown'
import { TypebotHeader } from '@/features/editor/components/TypebotHeader'
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import { env } from '@typebot.io/env'
import DomainStatusIcon from '@/features/customDomains/components/DomainStatusIcon'
export const SharePage = () => {
const t = useI18n()
const { t } = useTranslate()
const { workspace } = useWorkspace()
const { typebot, updateTypebot, publishedTypebot } = useTypebot()
const { showToast } = useToast()

View File

@ -16,10 +16,10 @@ import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { useUser } from '@/features/account/hooks/useUser'
import { useToast } from '@/hooks/useToast'
import { trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const CreateNewTypebotButtons = () => {
const scopedT = useScopedI18n('templates.buttons')
const { t } = useTranslate()
const { workspace } = useWorkspace()
const { user } = useUser()
const router = useRouter()
@ -72,7 +72,7 @@ export const CreateNewTypebotButtons = () => {
return (
<VStack maxW="600px" w="full" flex="1" pt="20" spacing={10}>
<Heading>{scopedT('heading')}</Heading>
<Heading>{t('templates.buttons.heading')}</Heading>
<Stack w="full" spacing={6}>
<Button
variant="outline"
@ -89,7 +89,7 @@ export const CreateNewTypebotButtons = () => {
onClick={() => handleCreateSubmit()}
isLoading={isLoading}
>
{scopedT('fromScratchButton.label')}
{t('templates.buttons.fromScratchButton.label')}
</Button>
<Button
variant="outline"
@ -106,7 +106,7 @@ export const CreateNewTypebotButtons = () => {
onClick={onOpen}
isLoading={isLoading}
>
{scopedT('fromTemplateButton.label')}
{t('templates.buttons.fromTemplateButton.label')}
</Button>
<ImportTypebotFromFileButton
variant="outline"
@ -123,7 +123,7 @@ export const CreateNewTypebotButtons = () => {
isLoading={isLoading}
onNewTypebot={handleCreateSubmit}
>
{scopedT('importFileButton.label')}
{t('templates.buttons.importFileButton.label')}
</ImportTypebotFromFileButton>
</Stack>
<TemplatesModal

View File

@ -4,7 +4,7 @@ import { Typebot, typebotCreateSchema } from '@typebot.io/schemas'
import { preprocessTypebot } from '@typebot.io/schemas/features/typebot/helpers/preprocessTypebot'
import React, { ChangeEvent } from 'react'
import { z } from 'zod'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
onNewTypebot: (typebot: Typebot) => void
@ -14,7 +14,7 @@ export const ImportTypebotFromFileButton = ({
onNewTypebot,
...props
}: Props) => {
const scopedT = useScopedI18n('templates.importFromFileButon')
const { t } = useTranslate()
const { showToast } = useToast()
const handleInputChange = async (e: ChangeEvent<HTMLInputElement>) => {
@ -29,7 +29,7 @@ export const ImportTypebotFromFileButton = ({
} catch (err) {
console.error(err)
showToast({
description: scopedT('toastError.description'),
description: t('templates.importFromFileButon.toastError.description'),
details: {
content: JSON.stringify(err, null, 2),
lang: 'json',

View File

@ -19,7 +19,7 @@ import { templates } from '../data'
import { TemplateProps } from '../types'
import { useToast } from '@/hooks/useToast'
import { sendRequest } from '@typebot.io/lib'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
isOpen: boolean
@ -28,7 +28,7 @@ type Props = {
}
export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
const scopedT = useScopedI18n('templates.modal')
const { t } = useTranslate()
const templateCardBackgroundColor = useColorModeValue('white', 'gray.800')
const [typebot, setTypebot] = useState<Typebot>()
const [selectedTemplate, setSelectedTemplate] = useState<TemplateProps>(
@ -90,7 +90,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
pl="1"
color="gray.500"
>
{scopedT('menuHeading.marketing')}
{t('templates.modal.menuHeading.marketing')}
</Text>
{templates
.filter((template) => template.category === 'marketing')
@ -112,7 +112,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
<Text>{template.name}</Text>
{template.isNew && (
<Tag colorScheme="orange" size="sm" flexShrink={0}>
{scopedT('menuHeading.new.tag')}
{t('templates.modal.menuHeading.new.tag')}
</Tag>
)}
</HStack>
@ -126,7 +126,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
pl="1"
color="gray.500"
>
{scopedT('menuHeading.product')}
{t('templates.modal.menuHeading.product')}
</Text>
{templates
.filter((template) => template.category === 'product')
@ -148,7 +148,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
<Text>{template.name}</Text>
{template.isNew && (
<Tag colorScheme="orange" size="sm" flexShrink={0}>
{scopedT('menuHeading.new.tag')}
{t('templates.modal.menuHeading.new.tag')}
</Tag>
)}
</HStack>
@ -162,7 +162,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
pl="1"
color="gray.500"
>
{scopedT('menuHeading.other')}
{t('templates.modal.menuHeading.other')}
</Text>
{templates
.filter((template) => template.category === undefined)
@ -184,7 +184,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
<Text>{template.name}</Text>
{template.isNew && (
<Tag colorScheme="orange" size="sm" flexShrink={0}>
{scopedT('menuHeading.new.tag')}
{t('templates.modal.menuHeading.new.tag')}
</Tag>
)}
</HStack>
@ -231,7 +231,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
onClick={onUseThisTemplateClick}
isLoading={isLoading}
>
{scopedT('useTemplateButton.label')}
{t('templates.modal.useTemplateButton.label')}
</Button>
</HStack>
</Stack>

View File

@ -8,7 +8,7 @@ import { Plan } from '@typebot.io/prisma'
import { isFreePlan } from '@/features/billing/helpers/isFreePlan'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
isBrandingEnabled: boolean
@ -23,7 +23,7 @@ export const GeneralSettings = ({
onGeneralThemeChange,
onBrandingChange,
}: Props) => {
const t = useI18n()
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
const { workspace } = useWorkspace()
const isWorkspaceFreePlan = isFreePlan(workspace)

View File

@ -13,7 +13,7 @@ import { WorkspaceInvitation, WorkspaceRole } from '@typebot.io/prisma'
import { FormEvent, useState } from 'react'
import { Member } from '../types'
import { sendInvitationQuery } from '../queries/sendInvitationQuery'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
workspaceId: string
@ -29,7 +29,7 @@ export const AddMemberForm = ({
isLoading,
isLocked,
}: Props) => {
const scopedT = useScopedI18n('workspace.membersList')
const { t } = useTranslate()
const [invitationEmail, setInvitationEmail] = useState('')
const [invitationRole, setInvitationRole] = useState<WorkspaceRole>(
WorkspaceRole.MEMBER
@ -54,7 +54,7 @@ export const AddMemberForm = ({
return (
<HStack as="form" onSubmit={handleInvitationSubmit}>
<Input
placeholder={scopedT('inviteInput.placeholder')}
placeholder={t('workspace.membersList.inviteInput.placeholder')}
name="inviteEmail"
value={invitationEmail}
onChange={(e) => setInvitationEmail(e.target.value)}
@ -75,7 +75,7 @@ export const AddMemberForm = ({
type="submit"
isDisabled={isLoading || isLocked || invitationEmail === ''}
>
{scopedT('inviteButton.label')}
{t('workspace.membersList.inviteButton.label')}
</Button>
</HStack>
)

View File

@ -13,7 +13,7 @@ import {
import { WorkspaceRole } from '@typebot.io/prisma'
import React from 'react'
import { convertWorkspaceRoleToReadable } from './AddMemberForm'
import { useI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
image?: string
@ -38,7 +38,7 @@ export const MemberItem = ({
onDeleteClick,
onSelectNewRole,
}: Props) => {
const t = useI18n()
const { t } = useTranslate()
const handleAdminClick = () => onSelectNewRole(WorkspaceRole.ADMIN)
const handleMemberClick = () => onSelectNewRole(WorkspaceRole.MEMBER)
@ -88,7 +88,7 @@ export const MemberIdentityContent = ({
isGuest?: boolean
email: string
}) => {
const t = useI18n()
const { t } = useTranslate()
return (
<HStack justifyContent="space-between" maxW="full" p="2">

View File

@ -19,11 +19,11 @@ import { updateInvitationQuery } from '../queries/updateInvitationQuery'
import { updateMemberQuery } from '../queries/updateMemberQuery'
import { Member } from '../types'
import { useWorkspace } from '../WorkspaceProvider'
import { useScopedI18n } from '@/locales'
import { getSeatsLimit } from '@typebot.io/lib/billing/getSeatsLimit'
import { useTranslate } from '@tolgee/react'
export const MembersList = () => {
const scopedT = useScopedI18n('workspace.membersList')
const { t } = useTranslate()
const { user } = useUser()
const { workspace, currentRole } = useWorkspace()
const { members, invitations, isLoading, mutate } = useMembers({
@ -103,12 +103,12 @@ export const MembersList = () => {
<Stack w="full" spacing={3}>
{!canInviteNewMember && (
<UnlockPlanAlertInfo>
{scopedT('unlockBanner.label')}
{t('workspace.membersList.unlockBanner.label')}
</UnlockPlanAlertInfo>
)}
{isDefined(seatsLimit) && (
<Heading fontSize="2xl">
{scopedT('title')}{' '}
{t('workspace.membersList.title')}{' '}
{seatsLimit === -1 ? '' : `(${currentMembersCount}/${seatsLimit})`}
</Heading>
)}

View File

@ -7,7 +7,7 @@ import {
} from '@/components/icons'
import { PlanTag } from '@/features/billing/components/PlanTag'
import { trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
import {
Menu,
MenuButton,
@ -32,7 +32,7 @@ export const WorkspaceDropdown = ({
onLogoutClick,
onCreateNewWorkspaceClick,
}: Props) => {
const scopedT = useScopedI18n('workspace.dropdown')
const { t } = useTranslate()
const { data } = trpc.workspace.listWorkspaces.useQuery()
const workspaces = data?.workspaces ?? []
@ -72,14 +72,14 @@ export const WorkspaceDropdown = ({
</MenuItem>
))}
<MenuItem onClick={onCreateNewWorkspaceClick} icon={<PlusIcon />}>
{scopedT('newButton.label')}
{t('workspace.dropdown.newButton.label')}
</MenuItem>
<MenuItem
onClick={onLogoutClick}
icon={<LogOutIcon />}
color="orange.500"
>
{scopedT('logoutButton.label')}
{t('workspace.dropdown.logoutButton.label')}
</MenuItem>
</MenuList>
</Menu>

View File

@ -12,10 +12,10 @@ import React from 'react'
import { EditableEmojiOrImageIcon } from '@/components/EditableEmojiOrImageIcon'
import { useWorkspace } from '../WorkspaceProvider'
import { TextInput } from '@/components/inputs'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
const scopedT = useScopedI18n('workspace.settings')
const { t } = useTranslate()
const { workspace, workspaces, updateWorkspace, deleteCurrentWorkspace } =
useWorkspace()
@ -34,7 +34,7 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
return (
<Stack spacing="6" w="full">
<FormControl>
<FormLabel>{scopedT('icon.title')}</FormLabel>
<FormLabel>{t('workspace.settings.icon.title')}</FormLabel>
<Flex>
{workspace && (
<EditableEmojiOrImageIcon
@ -51,7 +51,7 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
</FormControl>
{workspace && (
<TextInput
label={scopedT('name.label')}
label={t('workspace.settings.name.label')}
withVariableButton={false}
defaultValue={workspace?.name}
onChange={handleNameChange}
@ -74,12 +74,12 @@ const DeleteWorkspaceButton = ({
workspaceName: string
onConfirm: () => Promise<void>
}) => {
const scopedT = useScopedI18n('workspace.settings')
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<>
<Button colorScheme="red" variant="outline" onClick={onOpen}>
{scopedT('deleteButton.label')}
{t('workspace.settings.deleteButton.label')}
</Button>
<ConfirmModal
isOpen={isOpen}
@ -87,7 +87,7 @@ const DeleteWorkspaceButton = ({
onClose={onClose}
message={
<Text>
{scopedT('deleteButton.confirmMessage', {
{t('workspace.settings.deleteButton.confirmMessage', {
workspaceName,
})}
</Text>

View File

@ -24,7 +24,7 @@ import packageJson from '../../../../../../package.json'
import { UserPreferencesForm } from '@/features/account/components/UserPreferencesForm'
import { MyAccountForm } from '@/features/account/components/MyAccountForm'
import { BillingSettingsLayout } from '@/features/billing/components/BillingSettingsLayout'
import { useScopedI18n } from '@/locales'
import { useTranslate } from '@tolgee/react'
type Props = {
isOpen: boolean
@ -46,7 +46,7 @@ export const WorkspaceSettingsModal = ({
workspace,
onClose,
}: Props) => {
const scopedT = useScopedI18n('workspace.settings.modal')
const { t } = useTranslate()
const { currentRole } = useWorkspace()
const [selectedTab, setSelectedTab] = useState<SettingsTab>('my-account')
@ -82,7 +82,7 @@ export const WorkspaceSettingsModal = ({
justifyContent="flex-start"
pl="4"
>
{scopedT('menu.myAccount.label')}
{t('workspace.settings.modal.menu.myAccount.label')}
</Button>
<Button
variant={selectedTab === 'user-settings' ? 'solid' : 'ghost'}
@ -92,12 +92,12 @@ export const WorkspaceSettingsModal = ({
justifyContent="flex-start"
pl="4"
>
{scopedT('menu.preferences.label')}
{t('workspace.settings.modal.menu.preferences.label')}
</Button>
</Stack>
<Stack>
<Text pl="4" color="gray.500">
{scopedT('menu.workspace.label')}
{t('workspace.settings.modal.menu.workspace.label')}
</Text>
{canEditWorkspace && (
<Button
@ -116,7 +116,7 @@ export const WorkspaceSettingsModal = ({
justifyContent="flex-start"
pl="4"
>
{scopedT('menu.settings.label')}
{t('workspace.settings.modal.menu.settings.label')}
</Button>
)}
<Button
@ -127,7 +127,7 @@ export const WorkspaceSettingsModal = ({
justifyContent="flex-start"
pl="4"
>
{scopedT('menu.members.label')}
{t('workspace.settings.modal.menu.members.label')}
</Button>
{canEditWorkspace && (
<Button
@ -140,7 +140,7 @@ export const WorkspaceSettingsModal = ({
overflow="scroll"
className="hide-scrollbar"
>
{scopedT('menu.billingAndUsage.label')}
{t('workspace.settings.modal.menu.billingAndUsage.label')}
</Button>
)}
</Stack>
@ -148,7 +148,9 @@ export const WorkspaceSettingsModal = ({
<Flex justify="center" pt="10">
<Text color="gray.500" fontSize="xs">
{scopedT('menu.version.label', { version: packageJson.version })}
{t('workspace.settings.modal.menu.version.label', {
version: packageJson.version,
})}
</Text>
</Flex>
</Stack>

View File

@ -0,0 +1,25 @@
import { DevTools, Tolgee } from '@tolgee/react'
import { FormatIcu } from '@tolgee/format-icu'
import en from '../../public/locales/en.json'
import fr from '../../public/locales/fr.json'
import de from '../../public/locales/de.json'
import pt from '../../public/locales/pt.json'
import ptBR from '../../public/locales/pt-BR.json'
export const tolgee = Tolgee()
.use(DevTools())
.use(FormatIcu())
.init({
apiKey: process.env.NEXT_PUBLIC_TOLGEE_API_KEY,
apiUrl: process.env.NEXT_PUBLIC_TOLGEE_API_URL,
defaultLanguage: 'en',
availableLanguages: ['en', 'fr', 'de', 'pt', 'pt-BR'],
fallbackLanguage: 'en',
staticData: {
en,
fr,
de,
pt,
'pt-BR': ptBR,
},
})

View File

@ -1,329 +0,0 @@
export default {
back: 'Zurück',
'confirmModal.defaultTitle': 'Bist du sicher?',
'dashboard.header.settingsButton.label': 'Einstellungen & Mitglieder',
'dashboard.redirectionMessage': 'Du wirst weitergeleitet...',
'dashboard.title': 'Meine Typebots',
delete: 'Löschen',
errorMessage: 'Ein Fehler ist aufgetreten',
cancel: 'Abbrechen',
update: 'Aktualisieren',
upgrade: 'Upgrade',
downgrade: 'Downgrade',
remove: 'Entfernen',
pending: 'Ausstehend',
skip: 'Überspringen',
'folders.createFolderButton.label': 'Ordner erstellen',
'folders.createTypebotButton.label': 'Typebot erstellen',
'folders.folderButton.deleteConfirmationMessage':
'Möchtest du den Ordner {folderName} wirklich löschen? (Alles im Inneren wird in dein Dashboard verschoben)',
'folders.typebotButton.live': 'Live',
'folders.typebotButton.showMoreOptions': 'Mehr Optionen anzeigen',
'folders.typebotButton.unpublish': 'Veröffentlichung aufheben',
'folders.typebotButton.duplicate': 'Duplizieren',
'folders.typebotButton.delete': 'Löschen',
'folders.typebotButton.deleteConfirmationMessage':
'Möchtest du deinen Typebot {typebotName} wirklich löschen?',
'folders.typebotButton.deleteConfirmationMessageWarning':
'Alle zugehörigen Daten werden gelöscht und können nicht wiederhergestellt werden.',
'account.apiTokens.heading': 'API-Token',
'account.apiTokens.description':
'Diese Token ermöglichen es anderen Apps, dein gesamtes Konto und Typebots zu steuern. Sei vorsichtig!',
'account.apiTokens.createButton.label': 'Erstellen',
'account.apiTokens.deleteButton.label': 'Löschen',
'account.apiTokens.table.nameHeader': 'Name',
'account.apiTokens.table.createdHeader': 'Erstellt',
'account.apiTokens.deleteConfirmationMessage':
'Der Token {tokenName} wird dauerhaft widerrufen, bist du sicher, dass du fortfahren möchtest?',
'account.apiTokens.createModal.createHeading': 'Token erstellen',
'account.apiTokens.createModal.createdHeading': 'Token erstellt',
'account.apiTokens.createModal.nameInput.label':
'Gib einen eindeutigen Namen für deinen Token ein, um ihn von anderen Token zu unterscheiden.',
'account.apiTokens.createModal.nameInput.placeholder':
'Z.B. Zapier, Github, Make.com',
'account.apiTokens.createModal.createButton.label': 'Token erstellen',
'account.apiTokens.createModal.doneButton.label': 'Fertig',
'account.apiTokens.createModal.copyInstruction':
'Bitte kopiere deinen Token und bewahre ihn an einem sicheren Ort auf.',
'account.apiTokens.createModal.securityWarning':
'Aus Sicherheitsgründen können wir ihn nicht erneut anzeigen.',
'account.preferences.language.heading': 'Sprache',
'account.preferences.language.tooltip':
'Die Übersetzungen sind noch nicht vollständig. Es ist eine laufende Arbeit. 🤓',
'account.preferences.graphNavigation.heading': 'Editor-Navigation',
'account.preferences.graphNavigation.mouse.label': 'Maus',
'account.preferences.graphNavigation.mouse.description':
'Bewege dich, indem du das Board ziehst und zoome rein/raus mit dem Mausrad',
'account.preferences.graphNavigation.trackpad.label': 'Trackpad',
'account.preferences.graphNavigation.trackpad.description':
'Bewege das Board mit 2 Fingern und zoome rein/raus, indem du kneifst',
'account.preferences.appearance.heading': 'Erscheinungsbild',
'account.preferences.appearance.systemLabel': 'System',
'account.preferences.appearance.lightLabel': 'Hell',
'account.preferences.appearance.darkLabel': 'Dunkel',
'account.myAccount.changePhotoButton.label': 'Foto ändern',
'account.myAccount.changePhotoButton.specification':
'.jpg oder .png, max 1MB',
'account.myAccount.emailInput.disabledTooltip':
'Das Aktualisieren der E-Mail-Adresse ist nicht verfügbar. Kontaktiere den Support, wenn du sie ändern möchtest.',
'account.myAccount.emailInput.label': 'E-Mail-Adresse:',
'account.myAccount.nameInput.label': 'Name:',
'analytics.viewsLabel': 'Ansichten',
'analytics.startsLabel': 'Starts',
'analytics.notAvailableLabel': 'Nicht verfügbar',
'analytics.completionRateLabel': 'Abschlussrate',
'auth.signin.heading': 'Anmelden',
'auth.signin.noAccountLabel.preLink': 'Noch kein Konto?',
'auth.signin.noAccountLabel.link': 'Kostenlos anmelden',
'auth.register.heading': 'Konto erstellen',
'auth.register.alreadyHaveAccountLabel.preLink':
'Bereits ein Konto vorhanden?',
'auth.register.alreadyHaveAccountLabel.link': 'Anmelden',
'auth.register.aggreeToTerms':
'Durch die Registrierung stimmst du unseren {termsOfService} und {privacyPolicy} zu.',
'auth.register.termsOfService': 'Nutzungsbedingungen',
'auth.register.privacyPolicy': 'Datenschutzrichtlinie',
'auth.error.default': 'Versuche, dich mit einem anderen Konto anzumelden.',
'auth.error.email':
'E-Mail nicht gefunden. Versuche, dich mit einem anderen Anbieter anzumelden.',
'auth.error.oauthNotLinked':
'Um deine Identität zu bestätigen, melde dich mit demselben Konto an, das du ursprünglich verwendet hast.',
'auth.error.unknown': 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
'auth.signinErrorToast.title': 'Nicht autorisiert',
'auth.signinErrorToast.description': 'Anmeldungen sind deaktiviert.',
'auth.signinErrorToast.tooManyRequests':
'Zu viele Anfragen. Versuche es später erneut.',
'auth.noProvider.preLink': 'Du musst',
'auth.noProvider.link':
'mindestens einen Authentifizierungsanbieter konfigurieren (E-Mail, Google, GitHub, Facebook oder Azure AD).',
'auth.orEmailLabel': 'Oder mit deiner E-Mail',
'auth.emailSubmitButton.label': 'Absenden',
'auth.magicLink.title': 'Eine E-Mail mit magischem Link wurde gesendet. 🪄',
'auth.magicLink.description':
'Vergiss nicht, deinen Spam-Ordner zu überprüfen.',
'auth.socialLogin.githubButton.label': 'Mit GitHub fortfahren',
'auth.socialLogin.googleButton.label': 'Mit Google fortfahren',
'auth.socialLogin.facebookButton.label': 'Mit Facebook fortfahren',
'auth.socialLogin.azureButton.label': 'Mit {azureProviderName} fortfahren',
'auth.socialLogin.gitlabButton.label': 'Mit {gitlabProviderName} fortfahren',
'auth.socialLogin.customButton.label': 'Mit {customProviderName} fortfahren',
'billing.billingPortalButton.label': 'Abrechnungsportal',
'billing.contribution.preLink':
'Typebot trägt 1% deines Abonnements dazu bei, CO₂ aus der Atmosphäre zu entfernen.',
'billing.contribution.link': 'Erfahre mehr.',
'billing.updateSuccessToast.description':
'Workspace {plan} Plan erfolgreich aktualisiert 🎉',
'billing.customLimit.preLink':
'Brauchst du individuelle Limits? Spezielle Funktionen?',
'billing.customLimit.link': 'Lass uns darüber sprechen!',
'billing.upgradeLimitLabel':
'Um {type} hinzuzufügen, musst du deinen Tarif aktualisieren',
'billing.currentSubscription.heading': 'Abonnement',
'billing.currentSubscription.subheading': 'Aktuelles Workspace-Abonnement:',
'billing.currentSubscription.cancelDate': 'Wird storniert am',
'billing.currentSubscription.pastDueAlert':
'Die letzte Zahlung ist fehlgeschlagen. Gehen Sie zum Abrechnungsportal, um fortzufahren und eine Kündigung Ihres Abonnements zu vermeiden.',
'billing.invoices.heading': 'Rechnungen',
'billing.invoices.empty': 'Keine Rechnungen für diesen Workspace gefunden.',
'billing.invoices.paidAt': 'Bezahlt am',
'billing.invoices.subtotal': 'Zwischensumme',
'billing.preCheckoutModal.companyInput.label': 'Firmenname:',
'billing.preCheckoutModal.emailInput.label': 'E-Mail:',
'billing.preCheckoutModal.taxId.label': 'Steuernummer:',
'billing.preCheckoutModal.taxId.placeholder': 'ID-Typ',
'billing.preCheckoutModal.submitButton.label': 'Zur Kasse gehen',
'billing.pricingCard.heading': 'Upgrade auf {plan}',
'billing.pricingCard.perMonth': '/ Monat',
'billing.pricingCard.plus': ', plus:',
'billing.pricingCard.upgradeButton.current': 'Dein aktueller Tarif',
'billing.pricingCard.chatsPerMonth': 'Chats/Monat',
'billing.pricingCard.chatsTooltip':
'Ein Chat wird gezählt, wenn ein Benutzer eine Diskussion startet. Es ist unabhängig von der Anzahl der gesendeten und empfangenen Nachrichten.',
'billing.pricingCard.storageLimit': 'GB Speicherplatz',
'billing.pricingCard.storageLimitTooltip':
'Du belegst Speicherplatz für jede Datei, die dein Benutzer in deinem Bot hochlädt. Wenn du das Ergebnis löschst, wird der Platz freigegeben.',
'billing.pricingCard.starter.description':
'Für Einzelpersonen & kleine Unternehmen.',
'billing.pricingCard.starter.includedSeats': '2 Plätze inklusive',
'billing.pricingCard.starter.brandingRemoved': 'Branding entfernt',
'billing.pricingCard.starter.fileUploadBlock': 'Datei-Upload Eingabefeld',
'billing.pricingCard.starter.createFolders': 'Ordner erstellen',
'billing.pricingCard.pro.mostPopularLabel': 'Am beliebtesten',
'billing.pricingCard.pro.description': 'Für Agenturen & wachsende Start-ups.',
'billing.pricingCard.pro.everythingFromStarter': 'Alles in Starter',
'billing.pricingCard.pro.includedSeats': '5 Plätze inklusive',
'billing.pricingCard.pro.whatsAppIntegration': 'WhatsApp-Integration',
'billing.pricingCard.pro.customDomains': 'Eigene Domains',
'billing.pricingCard.pro.analytics': 'Detaillierte Analysen',
'billing.usage.heading': 'Nutzung',
'billing.usage.unlimited': 'Unbegrenzt',
'billing.usage.chats.heading': 'Chats',
'billing.usage.chats.alert.soonReach':
'Deine Typebots sind beliebt! Du wirst bald das Chat-Limit deines Tarifs erreichen. 🚀',
'billing.usage.chats.alert.updatePlan':
'Vergewissere dich, dass du deinen Tarif aktualisierst, um dieses Limit zu erhöhen und weiterhin mit deinen Benutzern zu chatten.',
'billing.usage.chats.resetInfo': '(setzt sich am 1. jeden Monats zurück)',
'billing.usage.storage.heading': 'Speicherplatz',
'billing.usage.storage.alert.soonReach':
'Deine Typebots sind beliebt! Du wirst bald das Speicherlimit deines Tarifs erreichen. 🚀',
'billing.usage.storage.alert.updatePlan':
'Stelle sicher, dass du deinen Tarif aktualisierst, um weiterhin hochgeladene Dateien zu sammeln. Du kannst auch Dateien löschen, um Speicherplatz freizugeben.',
'billing.limitMessage.brand': 'Branding entfernen',
'billing.limitMessage.customDomain': 'Eigene Domains hinzufügen',
'billing.limitMessage.analytics': 'Detaillierte Analysen freischalten',
'billing.limitMessage.fileInput': 'Datei-Eingabefelder verwenden',
'billing.limitMessage.folder': 'Ordner erstellen',
'billing.upgradeAlert.buttonDefaultLabel': 'Mehr Informationen',
'workspace.membersList.inviteInput.placeholder': 'name@unternehmen.de',
'workspace.membersList.inviteButton.label': 'Einladen',
'workspace.membersList.unlockBanner.label':
'Aktualisiere deinen Plan, um mit mehr Teammitgliedern zu arbeiten und neue Limits freizuschalten 🚀',
'workspace.membersList.title': 'Mitglieder',
'workspace.settings.icon.title': 'Symbol',
'workspace.settings.name.label': 'Name:',
'workspace.settings.deleteButton.label': 'Workspace löschen',
'workspace.settings.deleteButton.confirmMessage':
'Sind Sie sicher, dass Sie den Workspace {workspaceName} löschen möchten? Alle seine Ordner, Typebots und Ergebnisse werden dauerhaft gelöscht.',
'workspace.settings.modal.menu.myAccount.label': 'Mein Konto',
'workspace.settings.modal.menu.preferences.label': 'Einstellungen',
'workspace.settings.modal.menu.workspace.label': 'Workspace',
'workspace.settings.modal.menu.settings.label': 'Einstellungen',
'workspace.settings.modal.menu.members.label': 'Mitglieder',
'workspace.settings.modal.menu.billingAndUsage.label': 'Abrechnung & Nutzung',
'workspace.settings.modal.menu.version.label': 'Version: {version}',
'workspace.dropdown.newButton.label': 'Neuer Workspace',
'workspace.dropdown.logoutButton.label': 'Abmelden',
'templates.buttons.heading': 'Erstelle einen neuen Typebot',
'templates.buttons.fromScratchButton.label': 'Von Grund auf starten',
'templates.buttons.fromTemplateButton.label': 'Von einer Vorlage starten',
'templates.buttons.importFileButton.label': 'Datei importieren',
'templates.modal.menuHeading.marketing': 'Marketing',
'templates.modal.menuHeading.product': 'Produkt',
'templates.modal.menuHeading.other': 'Andere',
'templates.modal.menuHeading.new.tag': 'Neu',
'templates.modal.useTemplateButton.label': 'Diese Vorlage verwenden',
'templates.importFromFileButon.toastError.description':
'Konnte die Datei nicht verarbeiten. Bist du sicher, dass es sich um einen Typebot handelt?',
'editor.headers.flowButton.label': 'Ablauf',
'editor.headers.themeButton.label': 'Design',
'editor.headers.settingsButton.label': 'Einstellungen',
'editor.headers.shareButton.label': 'Teilen',
'editor.headers.resultsButton.label': 'Ergebnisse',
'editor.headers.helpButton.label': 'Hilfe',
'editor.headers.savingSpinner.label': 'Speichern...',
'editor.headers.previewButton.label': 'Vorschau',
'editor.sidebarBlocks.sidebar.lock.label': 'Seitenleiste sperren',
'editor.sidebarBlocks.sidebar.unlock.label': 'Seitenleiste entsperren',
'editor.sidebarBlocks.sidebar.icon.lock.label': 'Sperren',
'editor.sidebarBlocks.sidebar.icon.unlock.label': 'Entsperren',
'editor.sidebarBlocks.blockType.bubbles.heading': 'Blasen',
'editor.sidebarBlocks.blockType.inputs.heading': 'Eingaben',
'editor.sidebarBlocks.blockType.logic.heading': 'Logik',
'editor.sidebarBlocks.blockType.integrations.heading': 'Integrationen',
'editor.sidebarBlock.start.label': 'Start',
'editor.sidebarBlock.text.label': 'Text',
'editor.sidebarBlock.image.label': 'Bild',
'editor.sidebarBlock.video.label': 'Video',
'editor.sidebarBlock.embed.label': 'Einbetten',
'editor.sidebarBlock.audio.label': 'Audio',
'editor.sidebarBlock.number.label': 'Nummer',
'editor.sidebarBlock.email.label': 'E-Mail',
'editor.sidebarBlock.website.label': 'Website',
'editor.sidebarBlock.date.label': 'Datum',
'editor.sidebarBlock.phone.label': 'Telefon',
'editor.sidebarBlock.button.label': 'Buttons',
'editor.sidebarBlock.picChoice.label': 'Bildauswahl',
'editor.sidebarBlock.payment.label': 'Zahlung',
'editor.sidebarBlock.rating.label': 'Bewertung',
'editor.sidebarBlock.file.label': 'Datei',
'editor.sidebarBlock.setVariable.label': 'Variable setzen',
'editor.sidebarBlock.condition.label': 'Bedingung',
'editor.sidebarBlock.redirect.label': 'Weiterleitung',
'editor.sidebarBlock.script.label': 'Skript',
'editor.sidebarBlock.typebot.label': 'Typebot',
'editor.sidebarBlock.wait.label': 'Warten',
'editor.sidebarBlock.jump.label': 'Springen',
'editor.sidebarBlock.abTest.label': 'AB-Test',
'editor.sidebarBlock.sheets.label': 'Tabellen',
'editor.sidebarBlock.analytics.label': 'Analytics',
'editor.sidebarBlock.webhook.label': 'Webhook',
'editor.sidebarBlock.zapier.label': 'Zapier',
'editor.sidebarBlock.makecom.label': 'Make.com',
'editor.sidebarBlock.pabbly.label': 'Pabbly',
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
'editor.sidebarBlock.openai.label': 'OpenAI',
'editor.sidebarBlock.pixel.label': 'Pixel',
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
'editor.blockCard.bubbleBlock.tooltip.label':
'Ein PDF, ein iframe, eine Website einbetten...',
'editor.blockCard.inputBlock.tooltip.files.label': 'Dateien hochladen',
'editor.blockCard.logicBlock.tooltip.code.label': 'JavaScript-Code ausführen',
'editor.blockCard.logicBlock.tooltip.typebotLink.label':
'Verlinkung zu einem anderen Typebot',
'editor.blockCard.logicBlock.tooltip.jump.label':
'Ablauf zu einer anderen Gruppe beschleunigen',
'editor.blockCard.integrationBlock.tooltip.googleSheets.label':
'Google Tabellen',
'editor.blockCard.integrationBlock.tooltip.googleAnalytics.label':
'Google Analytics',
'editor.editableTypebotName.tooltip.rename.label': 'Umbenennen',
'editor.gettingStartedModal.editorBasics.heading': 'Grundlagen des Editors',
'editor.gettingStartedModal.editorBasics.list.one.label':
'Die Seitenleiste enthält Blöcke, die du auf das Board ziehen und ablegen kannst.',
'editor.gettingStartedModal.editorBasics.list.two.label':
'Du kannst Blöcke gruppieren, indem du sie unter oder über einander ablegst.',
'editor.gettingStartedModal.editorBasics.list.three.label':
'Verbinde die Gruppen miteinander.',
'editor.gettingStartedModal.editorBasics.list.four.label':
'Klicke auf die Vorschau-Schaltfläche oben rechts, um deinen Bot anzusehen.',
'editor.gettingStartedModal.editorBasics.list.label':
'Wenn du Fragen hast, verwende gerne die Sprechblase unten rechts, um sie mir zu stellen. Ich beantworte normalerweise innerhalb der nächsten 24 Stunden. 😃',
'editor.gettingStartedModal.seeAction.label': 'In Aktion sehen',
'editor.gettingStartedModal.seeAction.time': '5 Minuten',
'editor.gettingStartedModal.seeAction.item.label': 'Weitere Videos',
'editor.provider.messages.getTypebotError.title':
'Fehler beim Abrufen des Typebots. Aktualisiere die Seite.',
'editor.provider.messages.getTypebotError.description':
'Typebot konnte nicht gefunden werden.',
'editor.provider.messages.publishedTypebotError.title':
'Fehler beim Abrufen des veröffentlichten Typebots',
'editor.provider.messages.updateTypebotError.title':
'Fehler beim Aktualisieren des Typebots',
'editor.provider.groups.copy.title': 'kopieren',
'editor.blocks.start.text': 'Start',
'editor.blocks.bubbles.audio.settings.upload.label': 'Hochladen',
'editor.blocks.bubbles.audio.settings.embedLink.label': 'Link einbetten',
'editor.blocks.bubbles.audio.settings.chooseFile.label': 'Datei auswählen',
'editor.blocks.bubbles.audio.settings.worksWith.text':
'Funktioniert mit .MP3- und .WAV-Dateien',
'editor.blocks.bubbles.audio.settings.worksWith.placeholder':
'Füge den Audio-Dateilink ein...',
'editor.blocks.bubbles.audio.settings.autoplay.label': 'Autoplay aktivieren',
'editor.blocks.bubbles.audio.node.clickToEdit.text':
'Zum Bearbeiten klicken...',
'editor.blocks.bubbles.embed.node.clickToEdit.text':
'Zum Bearbeiten klicken...',
'editor.blocks.bubbles.embed.node.show.text': 'Einbetten anzeigen',
'editor.blocks.bubbles.embed.settings.worksWith.placeholder':
'Füge den Link oder Code ein...',
'editor.blocks.bubbles.embed.settings.worksWith.text':
'Funktioniert mit PDFs, iframes, Websites...',
'editor.blocks.bubbles.embed.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.image.node.clickToEdit.text':
'Zum Bearbeiten klicken...',
'editor.blocks.bubbles.image.switchWithLabel.onClick.label':
'Beim Klicken Link',
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder':
'Link Alternativtext (Beschreibung)',
'editor.blocks.bubbles.video.node.clickToEdit.text':
'Zum Bearbeiten klicken...',
'editor.blocks.bubbles.video.settings.worksWith.text':
'Funktioniert mit YouTube, Vimeo und anderen',
'editor.blocks.bubbles.video.settings.worksWith.placeholder':
'Füge den Videolink ein...',
'editor.blocks.bubbles.video.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.textEditor.plate.label': 'Texteditor',
'editor.blocks.bubbles.textEditor.searchVariable.placeholder':
'Nach einer Variable suchen',
} as const

View File

@ -1,318 +0,0 @@
export default {
back: 'Back',
'confirmModal.defaultTitle': 'Are you sure?',
'dashboard.header.settingsButton.label': 'Settings & Members',
'dashboard.redirectionMessage': 'You are being redirected...',
'dashboard.title': 'My typebots',
delete: 'Delete',
errorMessage: 'An error occured',
cancel: 'Cancel',
update: 'Update',
upgrade: 'Upgrade',
downgrade: 'Downgrade',
remove: 'Remove',
pending: 'Pending',
skip: 'Skip',
'folders.createFolderButton.label': 'Create a folder',
'folders.createTypebotButton.label': 'Create a typebot',
'folders.folderButton.deleteConfirmationMessage':
'Are you sure you want to delete {folderName} folder? (Everything inside will be move to your dashboard)',
'folders.typebotButton.live': 'Live',
'folders.typebotButton.showMoreOptions': 'Show more options',
'folders.typebotButton.unpublish': 'Unpublish',
'folders.typebotButton.duplicate': 'Duplicate',
'folders.typebotButton.delete': 'Delete',
'folders.typebotButton.deleteConfirmationMessage':
'Are you sure you want to delete your typebot {typebotName}?',
'folders.typebotButton.deleteConfirmationMessageWarning':
"All its associated data will be deleted and won't be recoverable.",
'account.apiTokens.heading': 'API tokens',
'account.apiTokens.description':
'These tokens allow other apps to control your whole account and typebots. Be careful!',
'account.apiTokens.createButton.label': 'Create',
'account.apiTokens.deleteButton.label': 'Delete',
'account.apiTokens.table.nameHeader': 'Name',
'account.apiTokens.table.createdHeader': 'Created',
'account.apiTokens.deleteConfirmationMessage':
'The token {tokenName} will be permanently revoked, are you sure you want to continue?',
'account.apiTokens.createModal.createHeading': 'Create Token',
'account.apiTokens.createModal.createdHeading': 'Token Created',
'account.apiTokens.createModal.nameInput.label':
'Enter a unique name for your token to differentiate it from other tokens.',
'account.apiTokens.createModal.nameInput.placeholder':
'I.e. Zapier, Github, Make.com',
'account.apiTokens.createModal.createButton.label': 'Create token',
'account.apiTokens.createModal.doneButton.label': 'Done',
'account.apiTokens.createModal.copyInstruction':
'Please copy your token and store it in a safe place.',
'account.apiTokens.createModal.securityWarning':
'For security reasons we cannot show it again.',
'account.preferences.language.heading': 'Language',
'account.preferences.language.tooltip':
'The translations are not complete yet. It is a work in progress. 🤓',
'account.preferences.graphNavigation.heading': 'Editor Navigation',
'account.preferences.graphNavigation.mouse.label': 'Mouse',
'account.preferences.graphNavigation.mouse.description':
'Move by dragging the board and zoom in/out using the scroll wheel',
'account.preferences.graphNavigation.trackpad.label': 'Trackpad',
'account.preferences.graphNavigation.trackpad.description':
'Move the board using 2 fingers and zoom in/out by pinching',
'account.preferences.appearance.heading': 'Appearance',
'account.preferences.appearance.systemLabel': 'System',
'account.preferences.appearance.lightLabel': 'Light',
'account.preferences.appearance.darkLabel': 'Dark',
'account.myAccount.changePhotoButton.label': 'Change photo',
'account.myAccount.changePhotoButton.specification': '.jpg or.png, max 1MB',
'account.myAccount.emailInput.disabledTooltip':
'Updating email is not available. Contact the support if you want to change it.',
'account.myAccount.emailInput.label': 'Email address:',
'account.myAccount.nameInput.label': 'Name:',
'analytics.viewsLabel': 'Views',
'analytics.startsLabel': 'Starts',
'analytics.notAvailableLabel': 'Not available',
'analytics.completionRateLabel': 'Completion rate',
'auth.signin.heading': 'Sign In',
'auth.signin.noAccountLabel.preLink': "Don't have an account?",
'auth.signin.noAccountLabel.link': 'Sign up for free',
'auth.register.heading': 'Create an account',
'auth.register.alreadyHaveAccountLabel.preLink': 'Already have an account?',
'auth.register.alreadyHaveAccountLabel.link': 'Sign in',
'auth.register.aggreeToTerms':
'By signing up, you agree to our {termsOfService} and {privacyPolicy}.',
'auth.register.termsOfService': 'terms of service',
'auth.register.privacyPolicy': 'privacy policy',
'auth.error.default': 'Try signing with a different account.',
'auth.error.email': 'Email not found. Try signing with a different provider.',
'auth.error.oauthNotLinked':
'To confirm your identity, sign in with the same account you used originally.',
'auth.error.unknown': 'An error occurred. Please try again.',
'auth.signinErrorToast.title': 'Unauthorized',
'auth.signinErrorToast.description': 'Sign ups are disabled.',
'auth.signinErrorToast.tooManyRequests':
'Too many requests. Try again later.',
'auth.noProvider.preLink': 'You need to',
'auth.noProvider.link':
'configure at least one auth provider (Email, Google, GitHub, Facebook or Azure AD).',
'auth.orEmailLabel': 'Or with your email',
'auth.emailSubmitButton.label': 'Submit',
'auth.magicLink.title': 'A magic link email was sent. 🪄',
'auth.magicLink.description': 'Make sure to check your spam folder.',
'auth.socialLogin.githubButton.label': 'Continue with GitHub',
'auth.socialLogin.googleButton.label': 'Continue with Google',
'auth.socialLogin.facebookButton.label': 'Continue with Facebook',
'auth.socialLogin.azureButton.label': 'Continue with {azureProviderName}',
'auth.socialLogin.gitlabButton.label': 'Continue with {gitlabProviderName}',
'auth.socialLogin.customButton.label': 'Continue with {customProviderName}',
'billing.billingPortalButton.label': 'Billing portal',
'billing.contribution.preLink':
'Typebot is contributing 1% of your subscription to remove CO₂ from the atmosphere.',
'billing.contribution.link': 'Learn more.',
'billing.updateSuccessToast.description':
'Workspace {plan} plan successfully updated 🎉',
'billing.customLimit.preLink': 'Need custom limits? Specific features?',
'billing.customLimit.link': "Let's chat!",
'billing.upgradeLimitLabel':
'You need to upgrade your plan in order to {type}',
'billing.currentSubscription.heading': 'Subscription',
'billing.currentSubscription.subheading': 'Current workspace subscription:',
'billing.currentSubscription.cancelDate': 'Will be cancelled on',
'billing.currentSubscription.pastDueAlert':
'The latest payment failed. Head over to the billing portal to proceed and avoid having your subscription canceled.',
'billing.invoices.heading': 'Invoices',
'billing.invoices.empty': 'No invoices found for this workspace.',
'billing.invoices.paidAt': 'Paid at',
'billing.invoices.subtotal': 'Subtotal',
'billing.preCheckoutModal.companyInput.label': 'Company name:',
'billing.preCheckoutModal.emailInput.label': 'Email:',
'billing.preCheckoutModal.taxId.label': 'Tax ID:',
'billing.preCheckoutModal.taxId.placeholder': 'ID type',
'billing.preCheckoutModal.submitButton.label': 'Go to checkout',
'billing.pricingCard.heading': 'Upgrade to {plan}',
'billing.pricingCard.perMonth': '/ month',
'billing.pricingCard.plus': ', plus:',
'billing.pricingCard.upgradeButton.current': 'Your current plan',
'billing.pricingCard.chatsPerMonth': 'chats/mo',
'billing.pricingCard.chatsTooltip':
'A chat is counted whenever a user starts a discussion. It is independant of the number of messages he sends and receives.',
'billing.pricingCard.storageLimit': 'GB of storage',
'billing.pricingCard.storageLimitTooltip':
'You accumulate storage for every file that your user upload into your bot. If you delete the result, it will free up the space.',
'billing.pricingCard.starter.description':
'For individuals & small businesses.',
'billing.pricingCard.starter.includedSeats': '2 seats included',
'billing.pricingCard.starter.brandingRemoved': 'Branding removed',
'billing.pricingCard.starter.fileUploadBlock': 'File upload input block',
'billing.pricingCard.starter.createFolders': 'Create folders',
'billing.pricingCard.pro.mostPopularLabel': 'Most popular',
'billing.pricingCard.pro.description': 'For agencies & growing startups.',
'billing.pricingCard.pro.everythingFromStarter': 'Everything in Starter',
'billing.pricingCard.pro.includedSeats': '5 seats included',
'billing.pricingCard.pro.whatsAppIntegration': 'WhatsApp integration',
'billing.pricingCard.pro.customDomains': 'Custom domains',
'billing.pricingCard.pro.analytics': 'In-depth analytics',
'billing.usage.heading': 'Usage',
'billing.usage.unlimited': 'Unlimited',
'billing.usage.chats.heading': 'Chats',
'billing.usage.chats.alert.soonReach':
"Your typebots are popular! You will soon reach your plan's chats limit. 🚀",
'billing.usage.chats.alert.updatePlan':
'Make sure to update your plan to increase this limit and continue chatting with your users.',
'billing.usage.chats.resetInfo': '(resets on 1st of every month)',
'billing.usage.storage.heading': 'Storage',
'billing.usage.storage.alert.soonReach':
"Your typebots are popular! You will soon reach your plan's storage limit. 🚀",
'billing.usage.storage.alert.updatePlan':
'Make sure to update your plan in order to continue collecting uploaded files. You can also delete files to free up space.',
'billing.limitMessage.brand': 'remove branding',
'billing.limitMessage.customDomain': 'add custom domains',
'billing.limitMessage.analytics': 'unlock in-depth analytics',
'billing.limitMessage.fileInput': 'use file input blocks',
'billing.limitMessage.folder': 'create folders',
'billing.upgradeAlert.buttonDefaultLabel': 'More info',
'workspace.membersList.inviteInput.placeholder': 'colleague@company.com',
'workspace.membersList.inviteButton.label': 'Invite',
'workspace.membersList.unlockBanner.label':
'Upgrade your plan to work with more team members, and unlock awesome power features 🚀',
'workspace.membersList.title': 'Members',
'workspace.settings.icon.title': 'Icon',
'workspace.settings.name.label': 'Name:',
'workspace.settings.deleteButton.label': 'Delete workspace',
'workspace.settings.deleteButton.confirmMessage':
'Are you sure you want to delete {workspaceName} workspace? All its folders, typebots and results will be deleted forever.',
'workspace.settings.modal.menu.myAccount.label': 'My account',
'workspace.settings.modal.menu.preferences.label': 'Preferences',
'workspace.settings.modal.menu.workspace.label': 'Workspace',
'workspace.settings.modal.menu.settings.label': 'Settings',
'workspace.settings.modal.menu.members.label': 'Members',
'workspace.settings.modal.menu.billingAndUsage.label': 'Billing & Usage',
'workspace.settings.modal.menu.version.label': 'Version: {version}',
'workspace.dropdown.newButton.label': 'New workspace',
'workspace.dropdown.logoutButton.label': 'Log out',
'templates.buttons.heading': 'Create a new typebot',
'templates.buttons.fromScratchButton.label': 'Start from scratch',
'templates.buttons.fromTemplateButton.label': 'Start from a template',
'templates.buttons.importFileButton.label': 'Import a file',
'templates.modal.menuHeading.marketing': 'Marketing',
'templates.modal.menuHeading.product': 'Product',
'templates.modal.menuHeading.other': 'Other',
'templates.modal.menuHeading.new.tag': 'New',
'templates.modal.useTemplateButton.label': 'Use this template',
'templates.importFromFileButon.toastError.description':
"Failed to parse the file. Are you sure it's a typebot?",
'editor.headers.flowButton.label': 'Flow',
'editor.headers.themeButton.label': 'Theme',
'editor.headers.settingsButton.label': 'Settings',
'editor.headers.shareButton.label': 'Share',
'editor.headers.resultsButton.label': 'Results',
'editor.headers.helpButton.label': 'Help',
'editor.headers.savingSpinner.label': 'Saving...',
'editor.headers.previewButton.label': 'Preview',
'editor.sidebarBlocks.sidebar.lock.label': 'Lock sidebar',
'editor.sidebarBlocks.sidebar.unlock.label': 'Unlock sidebar',
'editor.sidebarBlocks.sidebar.icon.lock.label': 'Lock',
'editor.sidebarBlocks.sidebar.icon.unlock.label': 'Unlock',
'editor.sidebarBlocks.blockType.bubbles.heading': 'Bubbles',
'editor.sidebarBlocks.blockType.inputs.heading': 'Inputs',
'editor.sidebarBlocks.blockType.logic.heading': 'Logic',
'editor.sidebarBlocks.blockType.integrations.heading': 'Integrations',
'editor.sidebarBlock.start.label': 'Start',
'editor.sidebarBlock.text.label': 'Text',
'editor.sidebarBlock.image.label': 'Image',
'editor.sidebarBlock.video.label': 'Video',
'editor.sidebarBlock.embed.label': 'Embed',
'editor.sidebarBlock.audio.label': 'Audio',
'editor.sidebarBlock.number.label': 'Number',
'editor.sidebarBlock.email.label': 'Email',
'editor.sidebarBlock.website.label': 'Website',
'editor.sidebarBlock.date.label': 'Date',
'editor.sidebarBlock.phone.label': 'Phone',
'editor.sidebarBlock.button.label': 'Buttons',
'editor.sidebarBlock.picChoice.label': 'Pic choice',
'editor.sidebarBlock.payment.label': 'Payment',
'editor.sidebarBlock.rating.label': 'Rating',
'editor.sidebarBlock.file.label': 'File',
'editor.sidebarBlock.setVariable.label': 'Set variable',
'editor.sidebarBlock.condition.label': 'Condition',
'editor.sidebarBlock.redirect.label': 'Redirect',
'editor.sidebarBlock.script.label': 'Script',
'editor.sidebarBlock.typebot.label': 'Typebot',
'editor.sidebarBlock.wait.label': 'Wait',
'editor.sidebarBlock.jump.label': 'Jump',
'editor.sidebarBlock.abTest.label': 'AB Test',
'editor.sidebarBlock.sheets.label': 'Sheets',
'editor.sidebarBlock.analytics.label': 'Analytics',
'editor.sidebarBlock.webhook.label': 'Webhook',
'editor.sidebarBlock.zapier.label': 'Zapier',
'editor.sidebarBlock.makecom.label': 'Make.com',
'editor.sidebarBlock.pabbly.label': 'Pabbly',
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
'editor.sidebarBlock.openai.label': 'OpenAI',
'editor.sidebarBlock.pixel.label': 'Pixel',
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
'editor.blockCard.bubbleBlock.tooltip.label':
'Embed a pdf, an iframe, a website...',
'editor.blockCard.inputBlock.tooltip.files.label': 'Upload Files',
'editor.blockCard.logicBlock.tooltip.code.label': 'Execute Javascript code',
'editor.blockCard.logicBlock.tooltip.typebotLink.label':
'Link and jump to another typebot',
'editor.blockCard.logicBlock.tooltip.jump.label':
'Fast forward the flow to another group',
'editor.blockCard.integrationBlock.tooltip.googleSheets.label':
'Google Sheets',
'editor.blockCard.integrationBlock.tooltip.googleAnalytics.label':
'Google Analytics',
'editor.editableTypebotName.tooltip.rename.label': 'Rename',
'editor.gettingStartedModal.editorBasics.heading': 'Editor Basics',
'editor.gettingStartedModal.editorBasics.list.one.label':
'The left side bar contains blocks that you can drag and drop to the board.',
'editor.gettingStartedModal.editorBasics.list.two.label':
'You can group blocks together by dropping them below or above each other',
'editor.gettingStartedModal.editorBasics.list.three.label':
'Connect the groups together',
'editor.gettingStartedModal.editorBasics.list.four.label':
'Preview your bot by clicking the preview button on the top right',
'editor.gettingStartedModal.editorBasics.list.label':
'Feel free to use the bottom-right bubble to reach out if you have any question. I usually answer within the next 24 hours. 😃',
'editor.gettingStartedModal.seeAction.label': 'See it in action',
'editor.gettingStartedModal.seeAction.time': '5 minutes',
'editor.gettingStartedModal.seeAction.item.label': 'Other videos',
'editor.provider.messages.getTypebotError.title':
'Error while fetching typebot. Refresh the page.',
'editor.provider.messages.getTypebotError.description':
"Couldn't find typebot",
'editor.provider.messages.publishedTypebotError.title':
'Error while fetching published typebot',
'editor.provider.messages.updateTypebotError.title':
'Error while updating typebot',
'editor.provider.groups.copy.title': 'copy',
'editor.blocks.start.text': 'Start',
'editor.blocks.bubbles.audio.settings.upload.label': 'Upload',
'editor.blocks.bubbles.audio.settings.embedLink.label': 'Embed link',
'editor.blocks.bubbles.audio.settings.chooseFile.label': 'Choose a file',
'editor.blocks.bubbles.audio.settings.worksWith.text':
'Works with .MP3s and .WAVs',
'editor.blocks.bubbles.audio.settings.worksWith.placeholder':
'Paste the audio file link...',
'editor.blocks.bubbles.audio.settings.autoplay.label': 'Enable autoplay',
'editor.blocks.bubbles.audio.node.clickToEdit.text': 'Click to edit...',
'editor.blocks.bubbles.embed.node.clickToEdit.text': 'Click to edit...',
'editor.blocks.bubbles.embed.node.show.text': 'Show embed',
'editor.blocks.bubbles.embed.settings.worksWith.placeholder':
'Paste the link or code...',
'editor.blocks.bubbles.embed.settings.worksWith.text':
'Works with PDFs, iframes, websites...',
'editor.blocks.bubbles.embed.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.image.node.clickToEdit.text': 'Click to edit...',
'editor.blocks.bubbles.image.switchWithLabel.onClick.label': 'On click link',
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder':
'Link alt text (description)',
'editor.blocks.bubbles.video.node.clickToEdit.text': 'Click to edit...',
'editor.blocks.bubbles.video.settings.worksWith.text':
'Works with Youtube, Vimeo and others',
'editor.blocks.bubbles.video.settings.worksWith.placeholder':
'Paste the video link...',
'editor.blocks.bubbles.video.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.textEditor.plate.label': 'Text editor',
'editor.blocks.bubbles.textEditor.searchVariable.placeholder':
'Search for a variable',
} as const

View File

@ -1,332 +0,0 @@
export default {
back: 'Retour',
'confirmModal.defaultTitle': 'Es-tu sûr ?',
'dashboard.header.settingsButton.label': 'Paramètres & Membres',
'dashboard.redirectionMessage': 'Redirection en cours...',
'dashboard.title': 'Mes typebots',
delete: 'Supprimer',
errorMessage: "Une erreur s'est produite",
cancel: 'Annuler',
update: 'Mettre à jour',
upgrade: 'Upgrade',
downgrade: 'Downgrade',
remove: 'Retirer',
pending: 'En attente',
skip: 'Passer',
'folders.createFolderButton.label': 'Créer un dossier',
'folders.createTypebotButton.label': 'Créer un typebot',
'folders.folderButton.deleteConfirmationMessage':
"Es-tu sûr de vouloir supprimer le dossier {folderName} ? (Tout ce qui est à l'intérieur sera déplacé dans le dossier parent ou sur votre tableau de bord)",
'folders.typebotButton.live': 'Live',
'folders.typebotButton.showMoreOptions': "Afficher plus d'options",
'folders.typebotButton.unpublish': 'Dépublier',
'folders.typebotButton.duplicate': 'Dupliquer',
'folders.typebotButton.delete': 'Supprimer',
'folders.typebotButton.deleteConfirmationMessage':
'Es-tu sûr de vouloir supprimer votre typebot {typebotName} ?',
'folders.typebotButton.deleteConfirmationMessageWarning':
'Toutes les données associées seront supprimées et ne pourront pas être récupérées.',
'account.apiTokens.heading': 'Tokens API',
'account.apiTokens.description':
"Ces tokens permettent à d'autres applications de contrôler ton compte et tes typebots. Prudence !",
'account.apiTokens.createButton.label': 'Créer',
'account.apiTokens.deleteButton.label': 'Supprimer',
'account.apiTokens.table.nameHeader': 'Nom',
'account.apiTokens.table.createdHeader': 'Créé',
'account.apiTokens.deleteConfirmationMessage':
'Le token {tokenName} sera définitivement révoqué, es-tu sûr de vouloir continuer ?',
'account.apiTokens.createModal.createHeading': 'Créer un token',
'account.apiTokens.createModal.createdHeading': 'Token créé',
'account.apiTokens.createModal.nameInput.label':
'Tape un nom unique pour votre token afin de le différencier des autres tokens.',
'account.apiTokens.createModal.nameInput.placeholder':
'Ex. Zapier, Github, Make.com',
'account.apiTokens.createModal.createButton.label': 'Créer un token',
'account.apiTokens.createModal.doneButton.label': 'Terminé',
'account.apiTokens.createModal.copyInstruction':
'Copie ton token et enregistre le dans un endroit sûr.',
'account.apiTokens.createModal.securityWarning':
'Pour des raisons de sécurité, nous ne pourrons pas le montrer à nouveau.',
'account.preferences.language.heading': 'Langue',
'account.preferences.language.tooltip':
"Les traductions ne sont pas encore complètes. C'est un travail en cours. 🤓",
'account.preferences.graphNavigation.heading': "Navigation de l'éditeur",
'account.preferences.graphNavigation.mouse.label': 'Souris',
'account.preferences.graphNavigation.mouse.description':
'Déplace le board en cliquant avec la souris et zoom utilisant la molette',
'account.preferences.graphNavigation.trackpad.label': 'Trackpad',
'account.preferences.graphNavigation.trackpad.description':
'Déplace le board en déplaçant les 2 doigts et zoom pincant',
'account.preferences.appearance.heading': 'Apparence',
'account.preferences.appearance.systemLabel': 'Système',
'account.preferences.appearance.lightLabel': 'Clair',
'account.preferences.appearance.darkLabel': 'Sombre',
'account.myAccount.changePhotoButton.label': 'Changer de photo',
'account.myAccount.changePhotoButton.specification': '.jpg ou.png, max 1MB',
'account.myAccount.emailInput.disabledTooltip':
"La mise à jour de l'adresse e-mail n'est pas disponible. Contacte le service d'assistance si tu souhaites la modifier.",
'account.myAccount.emailInput.label': 'Adresse e-mail:',
'account.myAccount.nameInput.label': 'Nom:',
'analytics.viewsLabel': 'Vues',
'analytics.startsLabel': 'Démarrés',
'analytics.notAvailableLabel': 'Non disponible',
'analytics.completionRateLabel': 'Taux de complétion',
'auth.signin.heading': 'Se connecter',
'auth.signin.noAccountLabel.preLink': "Tu n'as pas de compte?",
'auth.signin.noAccountLabel.link': 'Inscris-toi gratuitement',
'auth.register.heading': 'Créer un compte',
'auth.register.alreadyHaveAccountLabel.preLink': 'Tu as déjà un compte?',
'auth.register.alreadyHaveAccountLabel.link': 'Se connecter',
'auth.register.aggreeToTerms':
'En vous inscrivant, vous acceptez nos {termsOfService} et {privacyPolicy}.',
'auth.register.termsOfService': "conditions d'utilisation",
'auth.register.privacyPolicy': 'politique de confidentialité',
'auth.error.default': 'Essaye de te connecter avec un compte différent.',
'auth.error.email':
'Email non trouvé. Essaye de te connecter avec un fournisseur différent.',
'auth.error.oauthNotLinked':
'Pour confirmer ton identité, connecte-toi avec le même compte que tu as utilisé à lorigine.',
'auth.error.unknown': 'Une erreur est survenue. Essaye à nouveau.',
'auth.signinErrorToast.title': 'Non autorisé',
'auth.signinErrorToast.description': 'Les inscriptions sont désactivées.',
'auth.signinErrorToast.tooManyRequests': 'Trop de tentatives de connexion.',
'auth.noProvider.preLink': 'Tu as besoin de',
'auth.noProvider.link':
"configurer au moins un fournisseur d'authentification (E-mail, Google, GitHub, Facebook ou Azure AD).",
'auth.orEmailLabel': 'Ou avec votre email',
'auth.emailSubmitButton.label': 'Se connecter',
'auth.magicLink.title':
"Un email avec un lien d'authentification a été envoyé. 🪄",
'auth.magicLink.description': "N'oublie pas de vérifier ton dossier spam.",
'auth.socialLogin.githubButton.label': 'Continuer avec GitHub',
'auth.socialLogin.googleButton.label': 'Continuer avec Google',
'auth.socialLogin.facebookButton.label': 'Continuer avec Facebook',
'auth.socialLogin.azureButton.label': 'Continuer avec {azureProviderName}',
'auth.socialLogin.gitlabButton.label': 'Continuer avec {gitlabProviderName}',
'auth.socialLogin.customButton.label': 'Continuer avec {customProviderName}',
'billing.billingPortalButton.label': 'Portail de facturation',
'billing.contribution.preLink':
"Typebot contribue à hauteur de 1% de votre abonnement pour éliminer le CO₂ de l'atmosphère.",
'billing.contribution.link': 'En savoir plus.',
'billing.updateSuccessToast.description':
'Ton abonnement {plan} a été mis à jour avec succès 🎉',
'billing.customLimit.preLink':
'Tu as besoin de limites personnalisées ? De fonctionnalités spécifiques ?',
'billing.customLimit.link': 'Discutons-en!',
'billing.upgradeLimitLabel':
'Tu dois mettre à niveau ton abonnement pour {type}',
'billing.currentSubscription.heading': 'Abonnement',
'billing.currentSubscription.subheading': 'Abonnement actuel du workspace :',
'billing.currentSubscription.cancelDate': 'Sera annulé le',
'billing.currentSubscription.pastDueAlert':
"Le dernier paiement a échoué. Rendez-vous sur le portail de facturation pour effectuer la procédure et éviter l'annulation de votre abonnement.",
'billing.invoices.heading': 'Factures',
'billing.invoices.empty': 'Aucune facture trouvée pour ce workspace.',
'billing.invoices.paidAt': 'Payé le',
'billing.invoices.subtotal': 'Sous-total',
'billing.preCheckoutModal.companyInput.label': "Nom de l'entreprise :",
'billing.preCheckoutModal.emailInput.label': 'E-mail :',
'billing.preCheckoutModal.taxId.label': 'Numéro de TVA :',
'billing.preCheckoutModal.taxId.placeholder': 'Type',
'billing.preCheckoutModal.submitButton.label': 'Continuer',
'billing.pricingCard.heading': 'Passer à {plan}',
'billing.pricingCard.perMonth': '/ mois',
'billing.pricingCard.plus': ', plus :',
'billing.pricingCard.upgradeButton.current': 'Abonnement actuel',
'billing.pricingCard.chatsPerMonth': 'chats/mois',
'billing.pricingCard.chatsTooltip':
"Un chat est comptabilisé chaque fois qu'un utilisateur démarre une discussion. Il est indépendant du nombre de messages qu'il envoie et reçoit.",
'billing.pricingCard.storageLimit': 'Go de stockage',
'billing.pricingCard.storageLimitTooltip':
"Tu accumules du stockage pour chaque fichier que ton utilisateur télécharge dans ton bot. Si tu supprimes le résultat, ça libérera de l'espace.",
'billing.pricingCard.starter.description':
'Pour les particuliers et les petites entreprises.',
'billing.pricingCard.starter.includedSeats': '2 collègues inclus',
'billing.pricingCard.starter.brandingRemoved': 'Marque enlevée',
'billing.pricingCard.starter.fileUploadBlock': "Bloc d'upload de fichier",
'billing.pricingCard.starter.createFolders': 'Créer des dossiers',
'billing.pricingCard.pro.mostPopularLabel': 'Le plus populaire',
'billing.pricingCard.pro.description':
'Pour les agences et les startups en croissance.',
'billing.pricingCard.pro.everythingFromStarter':
"Tout ce qu'il y a dans Starter",
'billing.pricingCard.pro.includedSeats': '5 collègues inclus',
'billing.pricingCard.pro.whatsAppIntegration': 'Intégration WhatsApp',
'billing.pricingCard.pro.customDomains': 'Domaines personnalisés',
'billing.pricingCard.pro.analytics': 'Analyses approfondies',
'billing.usage.heading': 'Utilisation',
'billing.usage.unlimited': 'Illimité',
'billing.usage.chats.heading': 'Chats',
'billing.usage.chats.alert.soonReach':
'Tes typebots sont populaires ! Tu atteindras bientôt la limite de chats de votre abonnement. 🚀',
'billing.usage.chats.alert.updatePlan':
'Assure-toi de mettre à jour votre abonnement pour augmenter cette limite et continuer à discuter avec vos utilisateurs.',
'billing.usage.chats.resetInfo': '(réinitialisé le 1er de chaque mois)',
'billing.usage.storage.heading': 'Stockage',
'billing.usage.storage.alert.soonReach':
'Tes typebots sont populaires ! Tu atteindras bientôt la limite de stockage de ton abonnement. 🚀',
'billing.usage.storage.alert.updatePlan':
"Assure-toi de mettre à jour votre abonnement pour continuer à collecter des fichiers téléchargés. Tu peux également supprimer des fichiers pour libérer de l'espace.",
'billing.limitMessage.brand': 'supprimer la marque',
'billing.limitMessage.customDomain': 'ajouter des domaines personnalisés',
'billing.limitMessage.analytics': 'débloquer des analyses approfondies',
'billing.limitMessage.fileInput': 'utiliser des blocs de saisie de fichiers',
'billing.limitMessage.folder': 'créer des dossiers',
'billing.upgradeAlert.buttonDefaultLabel': "Plus d'informations",
'workspace.membersList.inviteInput.placeholder': 'collegue@entreprise.fr',
'workspace.membersList.inviteButton.label': 'Inviter',
'workspace.membersList.unlockBanner.label':
"Upgrade ton plan pour travailler les membres de ton équipe et débloquer d'autres fonctionnalités puissantes 🚀",
'workspace.membersList.title': 'Membres',
'workspace.settings.icon.title': 'Icône',
'workspace.settings.name.label': 'Nom:',
'workspace.settings.deleteButton.label': 'Supprimer le workspace',
'workspace.settings.deleteButton.confirmMessage':
'Es-tu sûr(e) de vouloir supprimer le workspace {workspaceName} ? Tous ses dossiers, typebots et résultats seront supprimés pour toujours.',
'workspace.settings.modal.menu.myAccount.label': 'Mon compte',
'workspace.settings.modal.menu.preferences.label': 'Préférences',
'workspace.settings.modal.menu.workspace.label': 'Workspace',
'workspace.settings.modal.menu.settings.label': 'Paramètres',
'workspace.settings.modal.menu.members.label': 'Membres',
'workspace.settings.modal.menu.billingAndUsage.label':
'Facturation et utilisation',
'workspace.settings.modal.menu.version.label': 'Version : {version}',
'workspace.dropdown.newButton.label': 'Nouveau workspace',
'workspace.dropdown.logoutButton.label': 'Déconnexion',
'templates.buttons.heading': 'Créer un nouveau typebot',
'templates.buttons.fromScratchButton.label': 'Commencer à partir de zéro',
'templates.buttons.fromTemplateButton.label':
"Commencer à partir d'un modèle",
'templates.buttons.importFileButton.label': 'Importer un fichier',
'templates.modal.menuHeading.marketing': 'Marketing',
'templates.modal.menuHeading.product': 'Produit',
'templates.modal.menuHeading.other': 'Autre',
'templates.modal.menuHeading.new.tag': 'Nouveau',
'templates.modal.useTemplateButton.label': 'Utiliser ce modèle',
'templates.importFromFileButon.toastError.description':
"Échec de l'analyse du fichier. Es-tu sûr que c'est un typebot ?",
'editor.headers.flowButton.label': 'Flow',
'editor.headers.themeButton.label': 'Thème',
'editor.headers.settingsButton.label': 'Paramètres',
'editor.headers.shareButton.label': 'Partager',
'editor.headers.resultsButton.label': 'Résultats',
'editor.headers.helpButton.label': 'Aide',
'editor.headers.savingSpinner.label': 'Enregistrement...',
'editor.headers.previewButton.label': 'Tester',
'editor.sidebarBlocks.sidebar.lock.label': 'Fermer la barre latérale',
'editor.sidebarBlocks.sidebar.unlock.label': 'Ouvrir la barre latérale',
'editor.sidebarBlocks.sidebar.icon.lock.label': 'Fermée',
'editor.sidebarBlocks.sidebar.icon.unlock.label': 'Ouverte',
'editor.sidebarBlocks.blockType.bubbles.heading': 'Bulles',
'editor.sidebarBlocks.blockType.inputs.heading': 'Inputs',
'editor.sidebarBlocks.blockType.logic.heading': 'Logique',
'editor.sidebarBlocks.blockType.integrations.heading': 'Intégrations',
'editor.sidebarBlock.start.label': 'Démarrer',
'editor.sidebarBlock.text.label': 'Texte',
'editor.sidebarBlock.image.label': 'Image',
'editor.sidebarBlock.video.label': 'Vidéo',
'editor.sidebarBlock.embed.label': 'Iframe',
'editor.sidebarBlock.audio.label': 'Audio',
'editor.sidebarBlock.number.label': 'Nombre',
'editor.sidebarBlock.email.label': 'Email',
'editor.sidebarBlock.website.label': 'Site web',
'editor.sidebarBlock.date.label': 'Date',
'editor.sidebarBlock.phone.label': 'Téléphone',
'editor.sidebarBlock.button.label': 'Boutons',
'editor.sidebarBlock.picChoice.label': 'Choix image',
'editor.sidebarBlock.payment.label': 'Paiement',
'editor.sidebarBlock.rating.label': 'Évaluation',
'editor.sidebarBlock.file.label': 'Fichier',
'editor.sidebarBlock.setVariable.label': 'Définir variable',
'editor.sidebarBlock.condition.label': 'Condition',
'editor.sidebarBlock.redirect.label': 'Rediriger',
'editor.sidebarBlock.script.label': 'Script',
'editor.sidebarBlock.typebot.label': 'Typebot',
'editor.sidebarBlock.wait.label': 'Attendre',
'editor.sidebarBlock.jump.label': 'Sauter',
'editor.sidebarBlock.abTest.label': 'AB Test',
'editor.sidebarBlock.sheets.label': 'Sheets',
'editor.sidebarBlock.analytics.label': 'Analytics',
'editor.sidebarBlock.webhook.label': 'Webhook',
'editor.sidebarBlock.zapier.label': 'Zapier',
'editor.sidebarBlock.makecom.label': 'Make.com',
'editor.sidebarBlock.pabbly.label': 'Pabbly',
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
'editor.sidebarBlock.openai.label': 'OpenAI',
'editor.sidebarBlock.pixel.label': 'Pixel',
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
'editor.blockCard.bubbleBlock.tooltip.label':
'Intégrer un pdf, un iframe, un site web...',
'editor.blockCard.inputBlock.tooltip.files.label': 'Télécharger des fichiers',
'editor.blockCard.logicBlock.tooltip.code.label':
'Exécuter du code Javascript',
'editor.blockCard.logicBlock.tooltip.typebotLink.label':
'Lier et exécuter un autre typebot',
'editor.blockCard.logicBlock.tooltip.jump.label':
'Passer rapidement au groupe suivant',
'editor.blockCard.integrationBlock.tooltip.googleSheets.label':
'Google Sheets',
'editor.blockCard.integrationBlock.tooltip.googleAnalytics.label':
'Google Analytics',
'editor.editableTypebotName.tooltip.rename.label': 'Renommer',
'editor.gettingStartedModal.editorBasics.heading':
"Principes de base de l'éditeur",
'editor.gettingStartedModal.editorBasics.list.one.label':
'La barre latérale de gauche contient des blocs que vous pouvez glisser-déposer sur le graph.',
'editor.gettingStartedModal.editorBasics.list.two.label':
'Vous pouvez regrouper les blocs en les déposant les uns au-dessus ou en-dessous des autres.',
'editor.gettingStartedModal.editorBasics.list.three.label':
'Connectez les groupes ensemble.',
'editor.gettingStartedModal.editorBasics.list.four.label':
'Prévisualisez votre bot en cliquant sur le bouton "Tester" en haut à droite.',
'editor.gettingStartedModal.editorBasics.list.label':
"N'hésitez pas à utiliser la bulle en bas à droite pour me poser des questions. Je réponds généralement dans les 24 heures. 😃",
'editor.gettingStartedModal.seeAction.label': 'Voir en action',
'editor.gettingStartedModal.seeAction.time': '5 minutes',
'editor.gettingStartedModal.seeAction.item.label': 'Autres vidéos',
'editor.provider.messages.getTypebotError.title':
'Erreur lors de la récupération du typebot. Rafraîchissez la page.',
'editor.provider.messages.getTypebotError.description':
'Impossible de trouver le typebot.',
'editor.provider.messages.publishedTypebotError.title':
'Erreur lors de la récupération du typebot publié',
'editor.provider.messages.updateTypebotError.title':
'Erreur lors de la mise à jour du typebot',
'editor.provider.groups.copy.title': 'copier',
'editor.blocks.start.text': 'Démarrer',
'editor.blocks.bubbles.audio.settings.upload.label': 'Uploader',
'editor.blocks.bubbles.audio.settings.embedLink.label': 'Lien intégré',
'editor.blocks.bubbles.audio.settings.chooseFile.label': 'Choisir un fichier',
'editor.blocks.bubbles.audio.settings.worksWith.text':
'Fonctionne avec les fichiers .MP3 et .WAV',
'editor.blocks.bubbles.audio.settings.worksWith.placeholder':
'Collez le lien du fichier audio...',
'editor.blocks.bubbles.audio.settings.autoplay.label':
'Activer la lecture automatique',
'editor.blocks.bubbles.audio.node.clickToEdit.text':
'Cliquez pour modifier...',
'editor.blocks.bubbles.embed.node.clickToEdit.text':
'Cliquez pour modifier...',
'editor.blocks.bubbles.embed.node.show.text': "Afficher l'intégration",
'editor.blocks.bubbles.embed.settings.worksWith.placeholder':
'Collez le lien ou le code...',
'editor.blocks.bubbles.embed.settings.worksWith.text':
'Fonctionne avec les PDF, les iframes, les sites web...',
'editor.blocks.bubbles.embed.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.image.node.clickToEdit.text':
'Cliquez pour modifier...',
'editor.blocks.bubbles.image.switchWithLabel.onClick.label':
'Redirection au clic',
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder':
'Texte alternatif du lien (description)',
'editor.blocks.bubbles.video.node.clickToEdit.text':
'Cliquez pour modifier...',
'editor.blocks.bubbles.video.settings.worksWith.text':
'Fonctionne avec Youtube, Vimeo et autres',
'editor.blocks.bubbles.video.settings.worksWith.placeholder':
'Collez le lien de la vidéo...',
'editor.blocks.bubbles.video.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.textEditor.plate.label': 'Éditeur de texte',
'editor.blocks.bubbles.textEditor.searchVariable.placeholder':
'Rechercher une variable',
} as const

View File

@ -1,19 +0,0 @@
import { createI18n } from 'next-international'
export type I18nFunction = (key: string) => string
export const {
useI18n,
useScopedI18n,
I18nProvider,
getLocaleProps,
useCurrentLocale,
useChangeLocale,
defineLocale,
} = createI18n({
en: () => import('./en'),
fr: () => import('./fr'),
pt: () => import('./pt'),
'pt-BR': () => import('./pt-BR'),
de: () => import('./de'),
})

View File

@ -1,328 +0,0 @@
export default {
back: 'Voltar',
'confirmModal.defaultTitle': 'Tem certeza?',
'dashboard.header.settingsButton.label': 'Configurações & Membros',
'dashboard.redirectionMessage': 'Você está sendo redirecionado...',
'dashboard.title': 'Meus typebots',
delete: 'Apagar',
errorMessage: 'Ocorreu um erro',
cancel: 'Cancelar',
update: 'Atualizar',
upgrade: 'Upgrade',
downgrade: 'Downgrade',
remove: 'Remover',
pending: 'Pendente',
skip: 'Pular',
'folders.createFolderButton.label': 'Criar uma pasta',
'folders.createTypebotButton.label': 'Criar um typebot',
'folders.folderButton.deleteConfirmationMessage':
'Tem certeza de que deseja excluir a pasta {folderName}? (Tudo o que estiver dentro será movido para o seu painel)',
'folders.typebotButton.live': 'Live',
'folders.typebotButton.showMoreOptions': 'Mostrar mais opções',
'folders.typebotButton.unpublish': 'Despublicar',
'folders.typebotButton.duplicate': 'Duplicar',
'folders.typebotButton.delete': 'Apagar',
'folders.typebotButton.deleteConfirmationMessage':
'Tem certeza de que deseja excluir seu typebot {typebotName}?',
'folders.typebotButton.deleteConfirmationMessageWarning':
'Todos os dados associados serão excluídos e não poderão ser recuperados.',
'account.apiTokens.heading': 'Tokens de API',
'account.apiTokens.description':
'Esses tokens permitem que outros aplicativos controlem toda a sua conta e typebots. Tenha cuidado!',
'account.apiTokens.createButton.label': 'Criar',
'account.apiTokens.deleteButton.label': 'Excluir',
'account.apiTokens.table.nameHeader': 'Nome',
'account.apiTokens.table.createdHeader': 'Criado',
'account.apiTokens.deleteConfirmationMessage':
'O token {tokenName} será revogado permanentemente. Tem certeza de que deseja continuar?',
'account.apiTokens.createModal.createHeading': 'Criar Token',
'account.apiTokens.createModal.createdHeading': 'Token Criado',
'account.apiTokens.createModal.nameInput.label':
'Insira um nome único para o seu token para diferenciá-lo de outros tokens.',
'account.apiTokens.createModal.nameInput.placeholder':
'Ex. Zapier, Github, Make.com',
'account.apiTokens.createModal.createButton.label': 'Criar token',
'account.apiTokens.createModal.doneButton.label': 'Concluído',
'account.apiTokens.createModal.copyInstruction':
'Por favor, copie seu token e guarde-o em um lugar seguro.',
'account.apiTokens.createModal.securityWarning':
'Por motivos de segurança, não podemos mostrá-lo novamente.',
'account.preferences.language.heading': 'Idioma',
'account.preferences.language.tooltip':
'As traduções ainda não estão completas. É um trabalho em andamento. 🤓',
'account.preferences.graphNavigation.heading': 'Navegação do Editor',
'account.preferences.graphNavigation.mouse.label': 'Mouse',
'account.preferences.graphNavigation.mouse.description':
'Mova arrastando o quadro e amplie/reduza usando a roda de rolagem',
'account.preferences.graphNavigation.trackpad.label': 'Trackpad',
'account.preferences.graphNavigation.trackpad.description':
'Mova o quadro usando 2 dedos e amplie/reduza fazendo pinça',
'account.preferences.appearance.heading': 'Aparência',
'account.preferences.appearance.systemLabel': 'Sistema',
'account.preferences.appearance.lightLabel': 'Claro',
'account.preferences.appearance.darkLabel': 'Escuro',
'account.myAccount.changePhotoButton.label': 'Alterar foto',
'account.myAccount.changePhotoButton.specification':
'.jpg ou.png, máximo 1MB',
'account.myAccount.emailInput.disabledTooltip':
'A atualização do e-mail não está disponível. Entre em contato com o suporte se quiser alterá-lo.',
'account.myAccount.emailInput.label': 'Endereço de e-mail:',
'account.myAccount.nameInput.label': 'Nome:',
'analytics.viewsLabel': 'Visualizações',
'analytics.startsLabel': 'Inícios',
'analytics.completionRateLabel': 'Taxa de conclusão',
'analytics.notAvailableLabel': 'Não disponível',
'auth.signin.heading': 'Entrar',
'auth.signin.noAccountLabel.preLink': 'Não tem uma conta?',
'auth.signin.noAccountLabel.link': 'Registre-se gratuitamente',
'auth.register.heading': 'Criar uma conta',
'auth.register.alreadyHaveAccountLabel.preLink': 'Já tem uma conta?',
'auth.register.alreadyHaveAccountLabel.link': 'Entrar',
'auth.register.aggreeToTerms':
'Ao se cadastrar, você concorda com nossos {termsOfService} e {privacyPolicy}.',
'auth.register.termsOfService': 'termos de serviço',
'auth.register.privacyPolicy': 'política de privacidade',
'auth.error.default': 'Tente entrar com uma conta diferente.',
'auth.error.email':
'E-mail não encontrado. Tente entrar com um provedor diferente.',
'auth.error.oauthNotLinked':
'Já existe uma conta vinculada a esse E-mail, entre com a mesma conta que você usou originalmente.',
'auth.error.unknown': 'Ocorreu um erro. Tente novamente.',
'auth.signinErrorToast.title': 'Não autorizado',
'auth.signinErrorToast.description': 'As inscrições estão desativadas.',
'auth.signinErrorToast.tooManyRequests':
'Muitas tentativas. Tente novamente mais tarde.',
'auth.noProvider.preLink': 'Você precisa',
'auth.noProvider.link':
'configurar pelo menos um provedor de autenticação (E-mail, Google, GitHub, Facebook ou Azure AD).',
'auth.orEmailLabel': 'Ou com seu email',
'auth.emailSubmitButton.label': 'Enviar',
'auth.magicLink.title': 'Um email com o link mágico foi enviado. 🪄',
'auth.magicLink.description': 'Certifique-se de verificar sua pasta de spam.',
'auth.socialLogin.githubButton.label': 'Continuar com GitHub',
'auth.socialLogin.googleButton.label': 'Continuar com Google',
'auth.socialLogin.facebookButton.label': 'Continuar com Facebook',
'auth.socialLogin.azureButton.label': 'Continuar com {azureProviderName}',
'auth.socialLogin.gitlabButton.label': 'Continuar com {gitlabProviderName}',
'auth.socialLogin.customButton.label': 'Continuar com {customProviderName}',
'billing.billingPortalButton.label': 'Portal de cobrança',
'billing.contribution.preLink':
'A Typebot está contribuindo com 1% da sua assinatura para remover o CO₂ da atmosfera.',
'billing.contribution.link': 'Saiba mais.',
'billing.updateSuccessToast.description':
'Sua assinatura {plan} foi atualizada com sucesso 🎉',
'billing.customLimit.preLink':
'Precisa de limites personalizados? Recursos específicos?',
'billing.customLimit.link': 'Vamos conversar!',
'billing.upgradeLimitLabel':
'Você precisa atualizar sua assinatura para {type}',
'billing.currentSubscription.heading': 'Assinatura',
'billing.currentSubscription.subheading':
'Assinatura atual do espaço de trabalho:',
'billing.currentSubscription.cancelDate': 'Será cancelado em',
'billing.currentSubscription.pastDueAlert':
'O último pagamento falhou. Acesse o portal de faturamento para prosseguir e evitar o cancelamento da sua assinatura.',
'billing.invoices.heading': 'Faturas',
'billing.invoices.empty':
'Nenhuma fatura encontrada para este espaço de trabalho.',
'billing.invoices.paidAt': 'Pago em',
'billing.invoices.subtotal': 'Subtotal',
'billing.preCheckoutModal.companyInput.label': 'Nome da empresa:',
'billing.preCheckoutModal.emailInput.label': 'E-mail:',
'billing.preCheckoutModal.taxId.label': 'Identificação fiscal (CPF):',
'billing.preCheckoutModal.taxId.placeholder': 'Tipo de ID',
'billing.preCheckoutModal.submitButton.label':
'Ir para a finalização da compra',
'billing.pricingCard.heading': 'Mudar para {plan}',
'billing.pricingCard.perMonth': '/ mês',
'billing.pricingCard.plus': ', mais:',
'billing.pricingCard.upgradeButton.current': 'Sua assinatura atual',
'billing.pricingCard.chatsPerMonth': 'chats/mês',
'billing.pricingCard.chatsTooltip':
'Um chat é contado sempre que um usuário inicia uma discussão. Ele é independente do número de mensagens que ele envia e recebe.',
'billing.pricingCard.storageLimit': 'GB de armazenamento',
'billing.pricingCard.storageLimitTooltip':
'Você acumula armazenamento para cada arquivo que seu usuário carrega em seu bot. Se você excluir o resultado, ele liberará espaço.',
'billing.pricingCard.starter.description':
'Para indivíduos e pequenas empresas.',
'billing.pricingCard.starter.includedSeats': '2 assentos incluídos',
'billing.pricingCard.starter.brandingRemoved': 'Marca removida',
'billing.pricingCard.starter.fileUploadBlock': 'Bloco de envio de arquivo',
'billing.pricingCard.starter.createFolders': 'Criar pastas',
'billing.pricingCard.pro.mostPopularLabel': 'Mais popular',
'billing.pricingCard.pro.description':
'Para agências e startups em crescimento.',
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
'billing.pricingCard.pro.includedSeats': '5 assentos incluídos',
'billing.pricingCard.pro.whatsAppIntegration': 'Integração do WhatsApp',
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
'billing.usage.heading': 'Uso',
'billing.usage.unlimited': 'Ilimitado',
'billing.usage.chats.heading': 'Chats',
'billing.usage.chats.alert.soonReach':
'Seus typebots são populares! Você logo alcançará o limite de chats de seu plano. 🚀',
'billing.usage.chats.alert.updatePlan':
'Certifique-se de atualizar seu plano para aumentar esse limite e continuar conversando com seus usuários.',
'billing.usage.chats.resetInfo': '(reiniciado todo dia 1)',
'billing.usage.storage.heading': 'Armazenamento',
'billing.usage.storage.alert.soonReach':
'Seus typebots são populares! Você logo alcançará o limite de armazenamento de seu plano. 🚀',
'billing.usage.storage.alert.updatePlan':
'Certifique-se de atualizar seu plano para continuar coletando arquivos enviados. Você também pode excluir arquivos para liberar espaço.',
'billing.limitMessage.brand': 'remover a marca',
'billing.limitMessage.customDomain': 'adicionar domínios personalizados',
'billing.limitMessage.analytics': 'desbloquear análises aprofundadas',
'billing.limitMessage.fileInput': 'usar blocos de envio de arquivo',
'billing.limitMessage.folder': 'criar pastas',
'billing.upgradeAlert.buttonDefaultLabel': 'Mais informações',
'workspace.membersList.inviteInput.placeholder': 'colega@empresa.com',
'workspace.membersList.inviteButton.label': 'Convidar',
'workspace.membersList.unlockBanner.label':
'Atualize seu plano para trabalhar com mais membros da equipe e desbloqueie recursos incríveis 🚀',
'workspace.membersList.title': 'Membros',
'workspace.settings.icon.title': 'Ícone',
'workspace.settings.name.label': 'Nome:',
'workspace.settings.deleteButton.label': 'Excluir espaço de trabalho',
'workspace.settings.deleteButton.confirmMessage':
'Você tem certeza de que deseja excluir o espaço de trabalho {workspaceName}? Todas as suas pastas, typebots e resultados serão excluídos permanentemente.',
'workspace.settings.modal.menu.myAccount.label': 'Minha conta',
'workspace.settings.modal.menu.preferences.label': 'Preferências',
'workspace.settings.modal.menu.workspace.label': 'Espaço de trabalho',
'workspace.settings.modal.menu.settings.label': 'Configurações',
'workspace.settings.modal.menu.members.label': 'Membros',
'workspace.settings.modal.menu.billingAndUsage.label': 'Faturamento e uso',
'workspace.settings.modal.menu.version.label': 'Versão: {version}',
'workspace.dropdown.newButton.label': 'Novo espaço de trabalho',
'workspace.dropdown.logoutButton.label': 'Sair',
'templates.buttons.heading': 'Criar um novo typebot',
'templates.buttons.fromScratchButton.label': 'Comece do zero',
'templates.buttons.fromTemplateButton.label': 'Comece a partir de um modelo',
'templates.buttons.importFileButton.label': 'Importar um arquivo',
'templates.modal.menuHeading.marketing': 'Marketing',
'templates.modal.menuHeading.product': 'Produto',
'templates.modal.menuHeading.other': 'Outros',
'templates.modal.menuHeading.new.tag': 'Novo',
'templates.modal.useTemplateButton.label': 'Usar esse modelo',
'templates.importFromFileButon.toastError.description':
'Falha ao analisar o arquivo. Tem certeza de que é um typebot?',
'editor.headers.flowButton.label': 'Fluxo',
'editor.headers.themeButton.label': 'Tema',
'editor.headers.settingsButton.label': 'Configurações',
'editor.headers.shareButton.label': 'Compartilhar',
'editor.headers.resultsButton.label': 'Resultados',
'editor.headers.helpButton.label': 'Ajuda',
'editor.headers.savingSpinner.label': 'Salvando...',
'editor.headers.previewButton.label': 'Visualizar',
'editor.sidebarBlocks.sidebar.lock.label': 'Bloquear barra lateral',
'editor.sidebarBlocks.sidebar.unlock.label': 'Desbloquear barra lateral',
'editor.sidebarBlocks.sidebar.icon.lock.label': 'Bloquear',
'editor.sidebarBlocks.sidebar.icon.unlock.label': 'Desbloquear',
'editor.sidebarBlocks.blockType.bubbles.heading': 'Bubbles',
'editor.sidebarBlocks.blockType.inputs.heading': 'Inputs',
'editor.sidebarBlocks.blockType.logic.heading': 'Condicionais',
'editor.sidebarBlocks.blockType.integrations.heading': 'Integrações',
'editor.sidebarBlock.start.label': 'Início',
'editor.sidebarBlock.text.label': 'Texto',
'editor.sidebarBlock.image.label': 'Imagem',
'editor.sidebarBlock.video.label': 'Vídeo',
'editor.sidebarBlock.embed.label': 'Incorporar',
'editor.sidebarBlock.audio.label': 'Áudio',
'editor.sidebarBlock.number.label': 'Número',
'editor.sidebarBlock.email.label': 'Email',
'editor.sidebarBlock.website.label': 'Website',
'editor.sidebarBlock.date.label': 'Data',
'editor.sidebarBlock.phone.label': 'Telefone',
'editor.sidebarBlock.button.label': 'Botão',
'editor.sidebarBlock.picChoice.label': 'Seleção de Imagem',
'editor.sidebarBlock.payment.label': 'Pagamento',
'editor.sidebarBlock.rating.label': 'Avaliação',
'editor.sidebarBlock.file.label': 'Arquivo',
'editor.sidebarBlock.setVariable.label': 'Variável',
'editor.sidebarBlock.condition.label': 'Condição',
'editor.sidebarBlock.redirect.label': 'Redirecionar',
'editor.sidebarBlock.script.label': 'Script',
'editor.sidebarBlock.typebot.label': 'Typebot',
'editor.sidebarBlock.wait.label': 'Espera',
'editor.sidebarBlock.jump.label': 'Pular',
'editor.sidebarBlock.abTest.label': 'Teste AB',
'editor.sidebarBlock.sheets.label': 'Sheets',
'editor.sidebarBlock.analytics.label': 'Analytics',
'editor.sidebarBlock.webhook.label': 'Webhook',
'editor.sidebarBlock.zapier.label': 'Zapier',
'editor.sidebarBlock.makecom.label': 'Make.com',
'editor.sidebarBlock.pabbly.label': 'Pabbly',
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
'editor.sidebarBlock.openai.label': 'OpenAI',
'editor.sidebarBlock.pixel.label': 'Pixel',
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
'editor.blockCard.bubbleBlock.tooltip.label':
'Incorporar pdf, iframe, website...',
'editor.blockCard.inputBlock.tooltip.files.label': 'Carregar Ficheiros',
'editor.blockCard.logicBlock.tooltip.code.label':
'Executar código Javascript',
'editor.blockCard.logicBlock.tooltip.typebotLink.label':
'Link e salte para outro typebot',
'editor.blockCard.logicBlock.tooltip.jump.label':
'Encaminhar fluxo para outro grupo',
'editor.blockCard.integrationBlock.tooltip.googleSheets.label':
'Google Sheets',
'editor.blockCard.integrationBlock.tooltip.googleAnalytics.label':
'Google Analytics',
'editor.editableTypebotName.tooltip.rename.label': 'Renomear',
'editor.gettingStartedModal.editorBasics.heading': 'Fundamentos do Editor',
'editor.gettingStartedModal.editorBasics.list.one.label':
'A barra lateral esquerda contém blocos que podem ser arrastados e soltos no quadro.',
'editor.gettingStartedModal.editorBasics.list.two.label':
'Você pode agrupar blocos juntos, colocando-os abaixo ou acima dos outros',
'editor.gettingStartedModal.editorBasics.list.three.label':
'Conecte os grupos entre eles',
'editor.gettingStartedModal.editorBasics.list.four.label':
'Pré-visualize o seu bot ao clicar no botão de visualizar no canto superior direito',
'editor.gettingStartedModal.editorBasics.list.label':
'Sinta-se à vontade para usar o chat no canto inferior direito para entrar em contato se tiver alguma dúvida. Normalmente, respondo nas próximas 24 horas. 😃',
'editor.gettingStartedModal.seeAction.label': 'Veja como funciona em',
'editor.gettingStartedModal.seeAction.time': '5 minutos',
'editor.gettingStartedModal.seeAction.item.label': 'Outros vídeos',
'editor.provider.messages.getTypebotError.title':
'Erro ao obter o typebot. Atualize a página.',
'editor.provider.messages.getTypebotError.description':
'Não foi possível encontrar o typebot',
'editor.provider.messages.publishedTypebotError.title':
'Erro ao obter o typebot publicado',
'editor.provider.messages.updateTypebotError.title':
'Erro ao atualizar o typebot',
'editor.provider.groups.copy.title': 'Cópia',
'editor.blocks.start.text': 'Início',
'editor.blocks.bubbles.audio.settings.upload.label': 'Carregar',
'editor.blocks.bubbles.audio.settings.embedLink.label': 'Incorporar link',
'editor.blocks.bubbles.audio.settings.chooseFile.label':
'Escolher um arquivo',
'editor.blocks.bubbles.audio.settings.worksWith.text':
'Compatível com .MP3s e .WAVs',
'editor.blocks.bubbles.audio.settings.worksWith.placeholder':
'Colar o link do arquivo de áudio...',
'editor.blocks.bubbles.audio.settings.autoplay.label':
'Ativar reprodução automática',
'editor.blocks.bubbles.audio.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.embed.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.embed.node.show.text': 'Mostrar incorporação',
'editor.blocks.bubbles.embed.settings.worksWith.placeholder':
'Colar o link ou código...',
'editor.blocks.bubbles.embed.settings.worksWith.text':
'Compatível com PDFs, iframes, websites...',
'editor.blocks.bubbles.embed.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.image.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.image.switchWithLabel.onClick.label': 'Link ao clicar',
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder':
'Texto alternativo do link (descrição)',
'editor.blocks.bubbles.video.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.video.settings.worksWith.text':
'Compatível com Youtube, Vimeo e outros',
'editor.blocks.bubbles.video.settings.worksWith.placeholder':
'Colar o link do vídeo...',
'editor.blocks.bubbles.video.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.textEditor.plate.label': 'Editor de texto',
'editor.blocks.bubbles.textEditor.searchVariable.placeholder':
'Pesquisar uma variável',
} as const

View File

@ -1,329 +0,0 @@
export default {
back: 'Voltar',
'confirmModal.defaultTitle': 'Tem a certeza?',
'dashboard.header.settingsButton.label': 'Configurações & Membros',
'dashboard.redirectionMessage': 'Está a ser redirecionado...',
'dashboard.title': 'Os meus typebots',
delete: 'Apagar',
errorMessage: 'Ocorreu um erro',
cancel: 'Cancelar',
update: 'Atualizar',
upgrade: 'Upgrade',
downgrade: 'Downgrade',
remove: 'Remover',
pending: 'Pendente',
skip: 'Saltar',
'folders.createFolderButton.label': 'Criar uma pasta',
'folders.createTypebotButton.label': 'Criar um typebot',
'folders.folderButton.deleteConfirmationMessage':
'Tem a certeza de que deseja excluir a pasta {folderName}? (Tudo o que estiver dentro será movido para o seu painel)',
'folders.typebotButton.live': 'Ao Vivo',
'folders.typebotButton.showMoreOptions': 'Mostrar mais opções',
'folders.typebotButton.unpublish': 'Despublicar',
'folders.typebotButton.duplicate': 'Duplicar',
'folders.typebotButton.delete': 'Apagar',
'folders.typebotButton.deleteConfirmationMessage':
'Tem a certeza de que deseja excluir o seu typebot {typebotName}?',
'folders.typebotButton.deleteConfirmationMessageWarning':
'Todos os dados associados serão excluídos e não poderão ser recuperados.',
'account.apiTokens.heading': 'Tokens de API',
'account.apiTokens.description':
'Estes tokens permitem que outras aplicações controlem toda a sua conta e typebots. Tenha cuidado!',
'account.apiTokens.createButton.label': 'Criar',
'account.apiTokens.deleteButton.label': 'Excluir',
'account.apiTokens.table.nameHeader': 'Nome',
'account.apiTokens.table.createdHeader': 'Criado',
'account.apiTokens.deleteConfirmationMessage':
'O token {tokenName} será revogado permanentemente. Tem a certeza de que deseja continuar?',
'account.apiTokens.createModal.createHeading': 'Criar Token',
'account.apiTokens.createModal.createdHeading': 'Token Criado',
'account.apiTokens.createModal.nameInput.label':
'Insira um nome único para o seu token para o diferenciar de outros tokens.',
'account.apiTokens.createModal.nameInput.placeholder':
'Ex. Zapier, Github, Make.com',
'account.apiTokens.createModal.createButton.label': 'Criar token',
'account.apiTokens.createModal.doneButton.label': 'Concluído',
'account.apiTokens.createModal.copyInstruction':
'Por favor, copie o seu token e guarde-o num lugar seguro.',
'account.apiTokens.createModal.securityWarning':
'Por razões de segurança, não o podemos mostrar novamente.',
'account.preferences.language.heading': 'Idioma',
'account.preferences.language.tooltip':
'As traduções ainda não estão completas. É um trabalho em curso. 🤓',
'account.preferences.graphNavigation.heading': 'Navegação do Editor',
'account.preferences.graphNavigation.mouse.label': 'Rato',
'account.preferences.graphNavigation.mouse.description':
'Mova arrastando o quadro e amplie/reduza usando a roda de deslocamento',
'account.preferences.graphNavigation.trackpad.label': 'Trackpad',
'account.preferences.graphNavigation.trackpad.description':
'Mova o quadro usando 2 dedos e amplie/reduza fazendo pinça',
'account.preferences.appearance.heading': 'Aparência',
'account.preferences.appearance.systemLabel': 'Sistema',
'account.preferences.appearance.lightLabel': 'Claro',
'account.preferences.appearance.darkLabel': 'Escuro',
'account.myAccount.changePhotoButton.label': 'Alterar foto',
'account.myAccount.changePhotoButton.specification':
'.jpg ou.png, máximo 1MB',
'account.myAccount.emailInput.disabledTooltip':
'A atualização do e-mail não está disponível. Entre em contacto com o apoio se quiser alterá-lo.',
'account.myAccount.emailInput.label': 'Endereço de e-mail:',
'account.myAccount.nameInput.label': 'Nome:',
'analytics.viewsLabel': 'Visualizações',
'analytics.startsLabel': 'Inícios',
'analytics.notAvailableLabel': 'Não disponível',
'analytics.completionRateLabel': 'Taxa de conclusão',
'auth.signin.heading': 'Entrar',
'auth.signin.noAccountLabel.preLink': 'Não tem uma conta?',
'auth.signin.noAccountLabel.link': 'Registe-se gratuitamente',
'auth.register.heading': 'Criar uma conta',
'auth.register.alreadyHaveAccountLabel.preLink': 'Já tem uma conta?',
'auth.register.alreadyHaveAccountLabel.link': 'Entrar',
'auth.register.aggreeToTerms':
'Ao registar-se, concorda com os nossos {termsOfService} e {privacyPolicy}.',
'auth.register.termsOfService': 'termos de serviço',
'auth.register.privacyPolicy': 'política de privacidade',
'auth.error.default': 'Tente entrar com uma conta diferente.',
'auth.error.email':
'E-mail não encontrado. Tente entrar com um fornecedor diferente.',
'auth.error.oauthNotLinked':
'Para confirmar a sua identidade, entre com a mesma conta que usou originalmente.',
'auth.error.unknown': 'Ocorreu um erro. Tente novamente.',
'auth.signinErrorToast.title': 'Não autorizado',
'auth.signinErrorToast.description': 'As inscrições estão desativadas.',
'auth.signinErrorToast.tooManyRequests':
'Muitas tentativas. Tente novamente mais tarde.',
'auth.noProvider.preLink': 'Precisa de',
'auth.noProvider.link':
'configurar pelo menos um fornecedor de autenticação (E-mail, Google, GitHub, Facebook ou Azure AD).',
'auth.orEmailLabel': 'Ou com o seu e-mail',
'auth.emailSubmitButton.label': 'Enviar',
'auth.magicLink.title': 'Foi enviado um e-mail com a ligação mágica. 🪄',
'auth.magicLink.description':
'Certifique-se de verificar a sua pasta de spam.',
'auth.socialLogin.githubButton.label': 'Continuar com GitHub',
'auth.socialLogin.googleButton.label': 'Continuar com Google',
'auth.socialLogin.facebookButton.label': 'Continuar com Facebook',
'auth.socialLogin.azureButton.label': 'Continuar com {azureProviderName}',
'auth.socialLogin.gitlabButton.label': 'Continuar com {gitlabProviderName}',
'auth.socialLogin.customButton.label': 'Continuar com {customProviderName}',
'billing.billingPortalButton.label': 'Portal de facturação',
'billing.contribution.preLink':
'A Typebot está a contribuir com 1% da sua subscrição para remover o CO₂ da atmosfera.',
'billing.contribution.link': 'Saiba mais.',
'billing.updateSuccessToast.description':
'A sua subscrição {plan} foi atualizada com sucesso 🎉',
'billing.customLimit.preLink':
'Precisa de limites personalizados? Funcionalidades específicas?',
'billing.customLimit.link': 'Vamos falar!',
'billing.upgradeLimitLabel':
'Precisa de atualizar a sua subscrição para {type}',
'billing.currentSubscription.heading': 'Subscrição',
'billing.currentSubscription.subheading':
'Subscrição actual do espaço de trabalho:',
'billing.currentSubscription.cancelDate': 'Será cancelado em',
'billing.currentSubscription.pastDueAlert':
'O último pagamento falhou. Acesse o portal de faturamento para continuar e evitar o cancelamento da sua assinatura.',
'billing.invoices.heading': 'Facturas',
'billing.invoices.empty':
'Nenhuma factura encontrada para este espaço de trabalho.',
'billing.invoices.paidAt': 'Pago em',
'billing.invoices.subtotal': 'Subtotal',
'billing.preCheckoutModal.companyInput.label': 'Nome da empresa:',
'billing.preCheckoutModal.emailInput.label': 'E-mail:',
'billing.preCheckoutModal.taxId.label': 'Identificação fiscal (NIF):',
'billing.preCheckoutModal.taxId.placeholder': 'Tipo de ID',
'billing.preCheckoutModal.submitButton.label':
'Ir para a finalização da compra',
'billing.pricingCard.heading': 'Mudar para {plan}',
'billing.pricingCard.perMonth': '/ mês',
'billing.pricingCard.plus': ', mais:',
'billing.pricingCard.upgradeButton.current': 'A sua subscrição atual',
'billing.pricingCard.chatsPerMonth': 'chats/mês',
'billing.pricingCard.chatsTooltip':
'Um chat é contado sempre que um utilizador inicia uma discussão. Ele é independente do número de mensagens que envia e recebe.',
'billing.pricingCard.storageLimit': 'GB de armazenamento',
'billing.pricingCard.storageLimitTooltip':
'Acumula armazenamento para cada ficheiro que o seu utilizador carrega no seu bot. Se excluir o resultado, ele libertará espaço.',
'billing.pricingCard.starter.description':
'Para indivíduos e pequenas empresas.',
'billing.pricingCard.starter.includedSeats': '2 lugares incluídos',
'billing.pricingCard.starter.brandingRemoved': 'Marca removida',
'billing.pricingCard.starter.fileUploadBlock': 'Bloco de envio de ficheiro',
'billing.pricingCard.starter.createFolders': 'Criar pastas',
'billing.pricingCard.pro.mostPopularLabel': 'Mais popular',
'billing.pricingCard.pro.description':
'Para agências e startups em crescimento.',
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
'billing.pricingCard.pro.includedSeats': '5 lugares incluídos',
'billing.pricingCard.pro.whatsAppIntegration': 'Integração do WhatsApp',
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
'billing.usage.heading': 'Uso',
'billing.usage.unlimited': 'Ilimitado',
'billing.usage.chats.heading': 'Chats',
'billing.usage.chats.alert.soonReach':
'Os seus typebots são populares! Vai alcançar em breve o limite de chats do seu plano. 🚀',
'billing.usage.chats.alert.updatePlan':
'Certifique-se de atualizar o seu plano para aumentar esse limite e continuar a conversar com os seus utilizadores.',
'billing.usage.chats.resetInfo': '(reiniciado a cada dia 1)',
'billing.usage.storage.heading': 'Armazenamento',
'billing.usage.storage.alert.soonReach':
'Os seus typebots são populares! Vai alcançar em breve o limite de armazenamento do seu plano. 🚀',
'billing.usage.storage.alert.updatePlan':
'Certifique-se de atualizar o seu plano para continuar a recolher ficheiros enviados. Também pode excluir ficheiros para libertar espaço.',
'billing.limitMessage.brand': 'remover a marca',
'billing.limitMessage.customDomain': 'adicionar domínios personalizados',
'billing.limitMessage.analytics': 'desbloquear análises aprofundadas',
'billing.limitMessage.fileInput': 'usar blocos de envio de ficheiros',
'billing.limitMessage.folder': 'criar pastas',
'billing.upgradeAlert.buttonDefaultLabel': 'Mais informações',
'workspace.membersList.inviteInput.placeholder': 'colega@empresa.com',
'workspace.membersList.inviteButton.label': 'Convidar',
'workspace.membersList.unlockBanner.label':
'Atualize o seu plano para trabalhar com mais membros da equipa e desbloquear funcionalidades incríveis 🚀',
'workspace.membersList.title': 'Membros',
'workspace.settings.icon.title': 'Ícone',
'workspace.settings.name.label': 'Nome:',
'workspace.settings.deleteButton.label': 'Eliminar espaço de trabalho',
'workspace.settings.deleteButton.confirmMessage':
'Tem a certeza de que deseja eliminar o espaço de trabalho {workspaceName}? Todas as suas pastas, typebots e resultados serão excluídos permanentemente.',
'workspace.settings.modal.menu.myAccount.label': 'A minha conta',
'workspace.settings.modal.menu.preferences.label': 'Preferências',
'workspace.settings.modal.menu.workspace.label': 'Espaço de trabalho',
'workspace.settings.modal.menu.settings.label': 'Configurações',
'workspace.settings.modal.menu.members.label': 'Membros',
'workspace.settings.modal.menu.billingAndUsage.label': 'Faturação e uso',
'workspace.settings.modal.menu.version.label': 'Versão: {version}',
'workspace.dropdown.newButton.label': 'Novo espaço de trabalho',
'workspace.dropdown.logoutButton.label': 'Sair',
'templates.buttons.heading': 'Criar um novo typebot',
'templates.buttons.fromScratchButton.label': 'Comece do zero',
'templates.buttons.fromTemplateButton.label': 'Comece a partir de um modelo',
'templates.buttons.importFileButton.label': 'Importar um ficheiro',
'templates.modal.menuHeading.marketing': 'Marketing',
'templates.modal.menuHeading.product': 'Produto',
'templates.modal.menuHeading.other': 'Outros',
'templates.modal.menuHeading.new.tag': 'Novo',
'templates.modal.useTemplateButton.label': 'Usar este modelo',
'templates.importFromFileButon.toastError.description':
'Falha ao analisar o ficheiro. Tem certeza de que é um typebot?',
'editor.headers.flowButton.label': 'Fluxo',
'editor.headers.themeButton.label': 'Tema',
'editor.headers.settingsButton.label': 'Configurações',
'editor.headers.shareButton.label': 'Compartilhar',
'editor.headers.resultsButton.label': 'Resultados',
'editor.headers.helpButton.label': 'Ajuda',
'editor.headers.savingSpinner.label': 'Salvando...',
'editor.headers.previewButton.label': 'Visualizar',
'editor.sidebarBlocks.sidebar.lock.label': 'Bloquear barra lateral',
'editor.sidebarBlocks.sidebar.unlock.label': 'Desbloquear barra lateral',
'editor.sidebarBlocks.sidebar.icon.lock.label': 'Bloquear',
'editor.sidebarBlocks.sidebar.icon.unlock.label': 'Desbloquear',
'editor.sidebarBlocks.blockType.bubbles.heading': 'Bubbles',
'editor.sidebarBlocks.blockType.inputs.heading': 'Inputs',
'editor.sidebarBlocks.blockType.logic.heading': 'Condicionais',
'editor.sidebarBlocks.blockType.integrations.heading': 'Integrações',
'editor.sidebarBlock.start.label': 'Início',
'editor.sidebarBlock.text.label': 'Texto',
'editor.sidebarBlock.image.label': 'Imagem',
'editor.sidebarBlock.video.label': 'Vídeo',
'editor.sidebarBlock.embed.label': 'Incorporar',
'editor.sidebarBlock.audio.label': 'Áudio',
'editor.sidebarBlock.number.label': 'Número',
'editor.sidebarBlock.email.label': 'Email',
'editor.sidebarBlock.website.label': 'Website',
'editor.sidebarBlock.date.label': 'Data',
'editor.sidebarBlock.phone.label': 'Telefone',
'editor.sidebarBlock.button.label': 'Botão',
'editor.sidebarBlock.picChoice.label': 'Seleção de Imagem',
'editor.sidebarBlock.payment.label': 'Pagamento',
'editor.sidebarBlock.rating.label': 'Classificação',
'editor.sidebarBlock.file.label': 'Ficheiro',
'editor.sidebarBlock.setVariable.label': 'Variável',
'editor.sidebarBlock.condition.label': 'Condição',
'editor.sidebarBlock.redirect.label': 'Redirecionar',
'editor.sidebarBlock.script.label': 'Script',
'editor.sidebarBlock.typebot.label': 'Typebot',
'editor.sidebarBlock.wait.label': 'Espera',
'editor.sidebarBlock.jump.label': 'Saltar',
'editor.sidebarBlock.abTest.label': 'Teste AB',
'editor.sidebarBlock.sheets.label': 'Sheets',
'editor.sidebarBlock.analytics.label': 'Analytics',
'editor.sidebarBlock.webhook.label': 'Webhook',
'editor.sidebarBlock.zapier.label': 'Zapier',
'editor.sidebarBlock.makecom.label': 'Make.com',
'editor.sidebarBlock.pabbly.label': 'Pabbly',
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
'editor.sidebarBlock.openai.label': 'OpenAI',
'editor.sidebarBlock.pixel.label': 'Pixel',
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
'editor.blockCard.bubbleBlock.tooltip.label':
'Incorporar pdf, iframe, website...',
'editor.blockCard.inputBlock.tooltip.files.label': 'Carregar Ficheiros',
'editor.blockCard.logicBlock.tooltip.code.label':
'Executar código Javascript',
'editor.blockCard.logicBlock.tooltip.typebotLink.label':
'Link e salte para outro typebot',
'editor.blockCard.logicBlock.tooltip.jump.label':
'Encaminhar fluxo para outro grupo',
'editor.blockCard.integrationBlock.tooltip.googleSheets.label':
'Google Sheets',
'editor.blockCard.integrationBlock.tooltip.googleAnalytics.label':
'Google Analytics',
'editor.editableTypebotName.tooltip.rename.label': 'Renomear',
'editor.gettingStartedModal.editorBasics.heading': 'Noções básicas de editor',
'editor.gettingStartedModal.editorBasics.list.one.label':
'A barra lateral esquerda contém blocos que pode arrastar e largar no quadro.',
'editor.gettingStartedModal.editorBasics.list.two.label':
'Pode agrupar blocos juntos, colocando-os uns abaixo ou acima dos outros',
'editor.gettingStartedModal.editorBasics.list.three.label':
'Ligue os grupos entre si',
'editor.gettingStartedModal.editorBasics.list.four.label':
'Pré-visualize o seu bot ao clicar no botão de visualizar no canto superior direito',
'editor.gettingStartedModal.editorBasics.list.label':
'Sinta-se à vontade para usar o chat no canto inferior direito para entrar em contacto se tiver alguma questão. Normalmente, respondo nas próximas 24 horas. 😃',
'editor.gettingStartedModal.seeAction.label': 'Veja o funcionamento em',
'editor.gettingStartedModal.seeAction.time': '5 minutos',
'editor.gettingStartedModal.seeAction.item.label': 'Outros vídeos',
'editor.provider.messages.getTypebotError.title':
'Erro ao obter o typebot. Atualize a página.',
'editor.provider.messages.getTypebotError.description':
'Não foi possível encontrar o typebot',
'editor.provider.messages.publishedTypebotError.title':
'Erro ao obter o typebot publicado',
'editor.provider.messages.updateTypebotError.title':
'Erro ao atualizar o typebot',
'editor.provider.groups.copy.title': 'Cópia',
'editor.blocks.start.text': 'Começar',
'editor.blocks.bubbles.audio.settings.upload.label': 'Carregar',
'editor.blocks.bubbles.audio.settings.embedLink.label': 'Incorporar link',
'editor.blocks.bubbles.audio.settings.chooseFile.label':
'Escolher um ficheiro',
'editor.blocks.bubbles.audio.settings.worksWith.text':
'Compatível com .MP3s e .WAVs',
'editor.blocks.bubbles.audio.settings.worksWith.placeholder':
'Colar o link do ficheiro de áudio...',
'editor.blocks.bubbles.audio.settings.autoplay.label':
'Ativar reprodução automática',
'editor.blocks.bubbles.audio.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.embed.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.embed.node.show.text': 'Mostrar incorporação',
'editor.blocks.bubbles.embed.settings.worksWith.placeholder':
'Colar o link ou código...',
'editor.blocks.bubbles.embed.settings.worksWith.text':
'Compatível com PDFs, iframes, websites...',
'editor.blocks.bubbles.embed.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.image.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.image.switchWithLabel.onClick.label': 'Link ao clicar',
'editor.blocks.bubbles.image.switchWithLabel.onClick.placeholder':
'Texto alternativo do link (descrição)',
'editor.blocks.bubbles.video.node.clickToEdit.text': 'Clique para editar...',
'editor.blocks.bubbles.video.settings.worksWith.text':
'Compatível com Youtube, Vimeo e outros',
'editor.blocks.bubbles.video.settings.worksWith.placeholder':
'Colar o link do vídeo...',
'editor.blocks.bubbles.video.settings.numberInput.unit': 'px',
'editor.blocks.bubbles.textEditor.plate.label': 'Editor de texto',
'editor.blocks.bubbles.textEditor.searchVariable.placeholder':
'Pesquisar uma variável',
} as const

View File

@ -15,20 +15,21 @@ import { toTitleCase } from '@typebot.io/lib'
import { Plan } from '@typebot.io/prisma'
import { trpc } from '@/lib/trpc'
import { NewVersionPopup } from '@/components/NewVersionPopup'
import { I18nProvider } from '@/locales'
import en from '@/locales/en'
import { TypebotProvider } from '@/features/editor/providers/TypebotProvider'
import { WorkspaceProvider } from '@/features/workspace/WorkspaceProvider'
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
import { initPostHogIfEnabled } from '@/features/telemetry/posthog'
import { TolgeeProvider, useTolgeeSSR } from '@tolgee/react'
import { tolgee } from '@/lib/tolgee'
initPostHogIfEnabled()
const { ToastContainer, toast } = createStandaloneToast(customTheme)
const App = ({ Component, pageProps }: AppProps) => {
useRouterProgressBar()
const { query, pathname } = useRouter()
const { query, pathname, locale } = useRouter()
const ssrTolgee = useTolgeeSSR(tolgee, locale)
useEffect(() => {
if (pathname.endsWith('/edit') || pathname.endsWith('/analytics')) {
@ -56,7 +57,7 @@ const App = ({ Component, pageProps }: AppProps) => {
return (
<>
<ToastContainer />
<I18nProvider locale={pageProps.locale} fallbackLocale={en}>
<TolgeeProvider tolgee={ssrTolgee}>
<ChakraProvider theme={customTheme}>
<SessionProvider session={pageProps.session}>
<UserProvider>
@ -72,7 +73,7 @@ const App = ({ Component, pageProps }: AppProps) => {
</UserProvider>
</SessionProvider>
</ChakraProvider>
</I18nProvider>
</TolgeeProvider>
</>
)
}

View File

@ -1,4 +1,3 @@
import { getLocaleProps } from '@/locales'
import { GetServerSidePropsContext } from 'next'
import { getServerSession } from 'next-auth'
import { authOptions } from './api/auth/[...nextauth]'
@ -7,13 +6,10 @@ export default function Page() {
return null
}
export const getServerSideProps = getLocaleProps(
async (context: GetServerSidePropsContext) => {
const session = await getServerSession(
context.req,
context.res,
authOptions
)
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const session = await getServerSession(context.req, context.res, authOptions)
if (!session?.user) {
return {
redirect: {
@ -35,4 +31,3 @@ export const getServerSideProps = getLocaleProps(
},
}
}
)

View File

@ -1,13 +1,13 @@
import { DashboardPage } from '@/features/dashboard/components/DashboardPage'
import { getLocaleProps } from '@/locales'
import { GetServerSidePropsContext } from 'next'
export default function Page() {
return <DashboardPage />
}
export const getServerSideProps = getLocaleProps(
async (context: GetServerSidePropsContext) => {
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const redirectPath = context.query.redirectPath?.toString()
return redirectPath
? {
@ -18,4 +18,3 @@ export const getServerSideProps = getLocaleProps(
}
: { props: {} }
}
)

26
apps/builder/tolgee.d.ts vendored Normal file
View File

@ -0,0 +1,26 @@
import type en from './src/locales/en.json'
declare module '@tolgee/core/lib/types' {
type TranslationsType = typeof en
// ensures that nested keys are accessible with "."
type DotNotationEntries<T> = T extends object
? {
[K in keyof T]: `${K & string}${T[K] extends undefined
? ''
: T[K] extends object
? `.${DotNotationEntries<T[K]>}`
: ''}`
}[keyof T]
: ''
// enables both intellisense and new keys without an error
type LiteralUnion<LiteralType extends BaseType, BaseType extends Primitive> =
| LiteralType
| (BaseType & { _?: never })
export type TranslationKey = LiteralUnion<
DotNotationEntries<TranslationsType>,
string
>
}

View File

@ -6,5 +6,5 @@
"@/*": ["src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
"include": ["next-env.d.ts", "tolgee.d.ts", "**/*.ts", "**/*.tsx"]
}

View File

@ -166,6 +166,19 @@ Used to search for images. You can create a Giphy app [here](https://unsplash.co
| NEXT_PUBLIC_UNSPLASH_APP_NAME | | Unsplash App name |
| NEXT_PUBLIC_UNSPLASH_ACCESS_KEY | | Unsplash API key |
## Tolgee (i18n contribution dev tool)
:::note
If you'd like to join contribute to Typebot's translation join the [Discord server](https://discord.gg/FYjghmSKRS) and ask for an access to Tolgee in the [#contributors channel](https://discord.com/channels/1155799591220953138/1155883114455900190).
:::
Set up these environment variables to enable [Tolgee dev tool](https://tolgee.io/features/dev-tools).
| Parameter | Default | Description |
| -------------------------- | -------------------------------------- | ----------------------- |
| NEXT_PUBLIC_TOLGEE_API_KEY | | Your Tolgee API key |
| NEXT_PUBLIC_TOLGEE_API_URL | https://tolgee.server.baptistearno.com | The Tolgee API base URL |
## WhatsApp (Preview)
In order to be able to test your bot on WhatsApp from the Preview drawer, you need to set up a WhatsApp business app.

View File

@ -2,7 +2,7 @@ import { authenticateUser } from '@/helpers/authenticateUser'
import prisma from '@typebot.io/lib/prisma'
import { Group, WebhookBlock } from '@typebot.io/schemas'
import { NextApiRequest, NextApiResponse } from 'next'
import { byId, isWebhookBlock, parseGroupTitle } from '@typebot.io/lib'
import { byId, isWebhookBlock } from '@typebot.io/lib'
import { methodNotAllowed } from '@typebot.io/lib/api'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
@ -27,7 +27,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
...emptyWebhookBlocks,
...blocks.map((b) => ({
blockId: b.id,
name: `${parseGroupTitle(group.title)} > ${b.id}`,
name: `${group.title} > ${b.id}`,
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
})),
]

View File

@ -2,12 +2,7 @@ import { authenticateUser } from '@/helpers/authenticateUser'
import prisma from '@typebot.io/lib/prisma'
import { Group, WebhookBlock } from '@typebot.io/schemas'
import { NextApiRequest, NextApiResponse } from 'next'
import {
byId,
isNotDefined,
isWebhookBlock,
parseGroupTitle,
} from '@typebot.io/lib'
import { byId, isNotDefined, isWebhookBlock } from '@typebot.io/lib'
import { methodNotAllowed } from '@typebot.io/lib/api'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
@ -37,7 +32,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
...blocks.map((s) => ({
id: s.id,
groupId: s.groupId,
name: `${parseGroupTitle(group.title)} > ${s.id}`,
name: `${group.title} > ${s.id}`,
})),
]
}, [])

View File

@ -12,14 +12,16 @@
"build": "pnpm docker:up && turbo run build",
"build:apps": "turbo run build --filter=builder... --filter=viewer...",
"db:migrate": "cd packages/prisma && pnpm run db:migrate",
"generate-change-log": "git fetch --all && pnpx gitmoji-changelog"
"generate-change-log": "git fetch --all && pnpx gitmoji-changelog",
"locales:sync": "tolgee sync './apps/builder/src/**/*.ts?(x)' --continue-on-warning --remove-unused"
},
"devDependencies": {
"cross-env": "7.0.3",
"cz-emoji": "1.3.2-canary.2",
"husky": "^8.0.3",
"prettier": "2.8.8",
"turbo": "1.10.12"
"turbo": "1.10.12",
"@tolgee/cli": "1.2.0"
},
"config": {
"commitizen": {

21
packages/env/env.ts vendored
View File

@ -273,6 +273,25 @@ const posthogEnv = {
},
}
const tolgeeEnv = {
client: {
NEXT_PUBLIC_TOLGEE_API_KEY: z.string().min(1).optional(),
NEXT_PUBLIC_TOLGEE_API_URL: z
.string()
.url()
.optional()
.default('https://tolgee.server.baptistearno.com"'),
},
runtimeEnv: {
NEXT_PUBLIC_TOLGEE_API_KEY: getRuntimeVariable(
'NEXT_PUBLIC_TOLGEE_API_KEY'
),
NEXT_PUBLIC_TOLGEE_API_URL: getRuntimeVariable(
'NEXT_PUBLIC_TOLGEE_API_URL'
),
},
}
export const env = createEnv({
server: {
...baseEnv.server,
@ -302,6 +321,7 @@ export const env = createEnv({
...unsplashEnv.client,
...sentryEnv.client,
...posthogEnv.client,
...tolgeeEnv.client,
},
experimental__runtimeEnv: {
...baseEnv.runtimeEnv,
@ -313,6 +333,7 @@ export const env = createEnv({
...unsplashEnv.runtimeEnv,
...sentryEnv.runtimeEnv,
...posthogEnv.runtimeEnv,
...tolgeeEnv.runtimeEnv,
},
skipValidation:
process.env.SKIP_ENV_CHECK === 'true' ||

View File

@ -9,14 +9,7 @@ import {
InputBlockType,
AnswerInSessionState,
} from '@typebot.io/schemas'
import {
isInputBlock,
isDefined,
byId,
isNotEmpty,
parseGroupTitle,
isEmpty,
} from './utils'
import { isInputBlock, isDefined, byId, isNotEmpty, isEmpty } from './utils'
export const parseResultHeader = (
typebot: Pick<Typebot, 'groups' | 'variables'>,
@ -61,7 +54,7 @@ const parseInputsResultHeader = ({
.flatMap((group) =>
group.blocks.map((block) => ({
...block,
groupTitle: parseGroupTitle(group.title),
groupTitle: group.title,
}))
)
.filter((block) => isInputBlock(block)) as (InputBlock & {

View File

@ -244,8 +244,5 @@ export const getAtPath = <T>(obj: T, path: string): unknown => {
return current
}
export const parseGroupTitle = (title: string) =>
isEmpty(title) ? 'Untitled' : title
export const isSvgSrc = (src: string | undefined) =>
src?.startsWith('data:image/svg') || src?.endsWith('.svg')

202
pnpm-lock.yaml generated
View File

@ -8,6 +8,9 @@ importers:
.:
devDependencies:
'@tolgee/cli':
specifier: 1.2.0
version: 1.2.0
cross-env:
specifier: 7.0.3
version: 7.0.3
@ -80,6 +83,12 @@ importers:
'@tanstack/react-table':
specifier: 8.9.3
version: 8.9.3(react-dom@18.2.0)(react@18.2.0)
'@tolgee/format-icu':
specifier: ^5.13.3
version: 5.13.3
'@tolgee/react':
specifier: ^5.13.3
version: 5.13.3(react@18.2.0)
'@trpc/client':
specifier: 10.40.0
version: 10.40.0(@trpc/server@10.40.0)
@ -197,9 +206,6 @@ importers:
next-auth:
specifier: 4.22.1
version: 4.22.1(next@13.5.4)(nodemailer@6.9.3)(react-dom@18.2.0)(react@18.2.0)
next-international:
specifier: 0.9.5
version: 0.9.5
nextjs-cors:
specifier: ^2.1.2
version: 2.1.2(next@13.5.4)
@ -6658,6 +6664,11 @@ packages:
resolution: {integrity: sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==}
dev: false
/@fastify/busboy@2.0.0:
resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==}
engines: {node: '>=14'}
dev: true
/@floating-ui/core@1.5.0:
resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==}
dependencies:
@ -6802,6 +6813,18 @@ packages:
/@humanwhocodes/object-schema@1.2.1:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
dev: true
/@istanbuljs/load-nyc-config@1.1.0:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
@ -7438,6 +7461,13 @@ packages:
dependencies:
'@noble/hashes': 1.3.2
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
dev: true
optional: true
/@planetscale/database@1.8.0:
resolution: {integrity: sha512-+zk04eXRiaJGaRnJZkCxXbBtBvQDQJXCoxqlXhLY3HzAovXfsBnh6DjXRujPRQQ7GKtT8/tOlyvZ9h6ReM+GLQ==}
engines: {node: '>=16'}
@ -8646,6 +8676,50 @@ packages:
tippy.js: 6.3.7
dev: false
/@tolgee/cli@1.2.0:
resolution: {integrity: sha512-1DBHdidNR0efuUAMjAtf9MDLC/nIwwYArCHjuscHvd/a3hgsdZYwrj926Ass1npj7RbtBYYTlBNju7qdLKD3Wg==}
engines: {node: '>= 18'}
hasBin: true
dependencies:
ansi-colors: 4.1.3
base32-decode: 1.0.0
commander: 11.1.0
cosmiconfig: 8.3.6(typescript@4.9.5)
form-data: 4.0.0
glob: 10.3.10
json5: 2.2.3
undici: 5.26.5
vscode-oniguruma: 1.7.0
vscode-textmate: 9.0.0
xstate: 4.38.3
yauzl: 2.10.0
transitivePeerDependencies:
- typescript
dev: true
/@tolgee/core@5.13.3:
resolution: {integrity: sha512-w6O+HVMOci6zqUPjuvY+o1aNfKxEMaHvKn6Px5aELAhhM0McNxVfHdfGMaTsfYvcQT/1UfjIqctLtTOqKaylsw==}
dev: false
/@tolgee/format-icu@5.13.3:
resolution: {integrity: sha512-TyyeHRskPhVCcaZ1Ns/JKR7scy2AIOYVt+uOZ3wHn9FXN67F1+9Xgf/WnYB6+F47l7Cp719kWnB+uovIoIZNeg==}
dev: false
/@tolgee/react@5.13.3(react@18.2.0):
resolution: {integrity: sha512-zWbQZDsdU0GYqCDVq7YASLiMQkMpMvumjOIcucCHk8VS6EIBxAJq/cSelT+3iUXVNtgGHEe+GGZp1MGune67gA==}
peerDependencies:
react: ^16.14.0 || ^17.0.1 || ^18.1.0
dependencies:
'@tolgee/web': 5.13.3
react: 18.2.0
dev: false
/@tolgee/web@5.13.3:
resolution: {integrity: sha512-CwOyRQl+V21QhGFNWPmSV8L7cIhxsOeU5ivLyO6YXV9LWnTXNxHlS5apDzfXR2RCGbwHHYAsii7bghXn77pNhA==}
dependencies:
'@tolgee/core': 5.13.3
dev: false
/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'}
@ -11361,7 +11435,6 @@ packages:
/ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
dev: false
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
@ -11383,7 +11456,6 @@ packages:
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: false
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@ -11864,6 +11936,10 @@ packages:
resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==}
dev: false
/base32-decode@1.0.0:
resolution: {integrity: sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==}
dev: true
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -12041,7 +12117,6 @@ packages:
/buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: false
/buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
@ -12562,6 +12637,11 @@ packages:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
/commander@11.1.0:
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
engines: {node: '>=16'}
dev: true
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@ -12833,7 +12913,6 @@ packages:
parse-json: 5.2.0
path-type: 4.0.0
typescript: 4.9.5
dev: false
/country-flag-icons@1.5.7:
resolution: {integrity: sha512-AdvXhMcmSp7nBSkpGfW4qR/luAdRUutJqya9PuwRbsBzuoknThfultbv7Ib6fWsHXC43Es/4QJ8gzQQdBNm75A==}
@ -13648,7 +13727,6 @@ packages:
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: false
/ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@ -13682,7 +13760,6 @@ packages:
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: false
/emojilib@3.0.10:
resolution: {integrity: sha512-VQtCRroFykPTJaoEBEGFg5tI+rEluabjuaVDDbSftDtiRJ5GuqRG/LGV1mmDzkJP4bh5rzuEBOafMN68/YXQcQ==}
@ -14814,6 +14891,12 @@ packages:
- encoding
dev: false
/fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
dependencies:
pend: 1.2.0
dev: true
/feed@4.2.2:
resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==}
engines: {node: '>=0.4.0'}
@ -14972,6 +15055,14 @@ packages:
dependencies:
is-callable: 1.2.7
/foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
dev: true
/fork-ts-checker-webpack-plugin@6.5.3(typescript@4.9.5)(webpack@5.88.2):
resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==}
engines: {node: '>=10', yarn: '>=1.0.0'}
@ -15281,6 +15372,18 @@ packages:
/glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.6
minimatch: 9.0.1
minipass: 7.0.4
path-scurry: 1.10.1
dev: true
/glob@7.1.6:
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
dependencies:
@ -16184,10 +16287,6 @@ packages:
has: 1.0.4
side-channel: 1.0.4
/international-types@0.8.0:
resolution: {integrity: sha512-cJwagIBzMG0FTK81TABeSBU0eReIXWmvW18m/URn8ZA+GLoWIcdL43kfjArwHVmZJ1n+IFchsBj8tQLYUqmwnA==}
dev: false
/interpret@1.4.0:
resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
engines: {node: '>= 0.10'}
@ -16620,6 +16719,15 @@ packages:
istanbul-lib-report: 3.0.1
dev: true
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
dev: true
/jest-changed-files@29.7.0:
resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -17652,6 +17760,11 @@ packages:
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
/lru-cache@10.0.1:
resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==}
engines: {node: 14 || >=16.14}
dev: true
/lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
dependencies:
@ -18007,6 +18120,11 @@ packages:
xml2js: 0.5.0
dev: false
/minipass@7.0.4:
resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
engines: {node: '>=16 || 14 >=14.17'}
dev: true
/mjml-accordion@4.14.1:
resolution: {integrity: sha512-dpNXyjnhYwhM75JSjD4wFUa9JgHm86M2pa0CoTzdv1zOQz67ilc4BoK5mc2S0gOjJpjBShM5eOJuCyVIuAPC6w==}
dependencies:
@ -18459,14 +18577,6 @@ packages:
uuid: 8.3.2
dev: false
/next-international@0.9.5:
resolution: {integrity: sha512-yS6DnuNAK+2EO5KWLgw2FPGwBkQ8gylrG0vNSeJD0/BAHSyIpPCn6E+1KPzawQffB5r6wVP6vHjDoSlUOxWWew==}
dependencies:
client-only: 0.0.1
international-types: 0.8.0
server-only: 0.0.1
dev: false
/next-runtime-env@1.6.2:
resolution: {integrity: sha512-JETzGBe4v1gjb3dbu3unZ/kUPEOgtyz+R5YJNTfdVpWFK8YotyM9sitj1/NPNZ7knjmLDVrljnwvamjBT7UALQ==}
dependencies:
@ -19109,6 +19219,14 @@ packages:
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
/path-scurry@1.10.1:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 10.0.1
minipass: 7.0.4
dev: true
/path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
dev: false
@ -19138,6 +19256,10 @@ packages:
resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
dev: false
/pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: true
/periscopic@3.1.0:
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
dependencies:
@ -21411,10 +21533,6 @@ packages:
- supports-color
dev: false
/server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
dev: false
/set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: false
@ -21505,6 +21623,11 @@ packages:
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
dev: true
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
dev: false
@ -21926,7 +22049,6 @@ packages:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
dev: false
/string.prototype.matchall@4.0.10:
resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
@ -22003,7 +22125,6 @@ packages:
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
dev: false
/strip-bom-string@1.0.0:
resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
@ -22981,6 +23102,13 @@ packages:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
dev: false
/undici@5.26.5:
resolution: {integrity: sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw==}
engines: {node: '>=14.0'}
dependencies:
'@fastify/busboy': 2.0.0
dev: true
/unenv@1.7.4:
resolution: {integrity: sha512-fjYsXYi30It0YCQYqLOcT6fHfMXsBr2hw9XC7ycf8rTG7Xxpe3ZssiqUnD0khrjiZEmkBXWLwm42yCSCH46fMw==}
dependencies:
@ -23507,6 +23635,14 @@ packages:
fsevents: 2.3.3
dev: false
/vscode-oniguruma@1.7.0:
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
dev: true
/vscode-textmate@9.0.0:
resolution: {integrity: sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==}
dev: true
/vue@3.3.4:
resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
dependencies:
@ -23894,7 +24030,6 @@ packages:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
dev: false
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@ -23987,6 +24122,10 @@ packages:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: true
/xstate@4.38.3:
resolution: {integrity: sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==}
dev: true
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@ -24084,6 +24223,13 @@ packages:
yargs-parser: 21.1.1
dev: true
/yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
dependencies:
buffer-crc32: 0.2.13
fd-slicer: 1.1.0
dev: true
/yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}