2
0

🌐 Add translation keys for input blocks (#1114)

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

- **New Features**
- Integrated localization support across various components using the
`useTranslate` hook for dynamic translations.

- **Enhancements**
- Replaced hardcoded text with localized strings to support multiple
languages in the user interface.

- **User Interface**
- Updated labels, placeholders, tooltips, and button texts to utilize
translated content for a multilingual experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Baptiste Arnaud <baptiste.arnaud95@gmail.com>
This commit is contained in:
Gabriel Pavão
2023-12-29 08:42:50 -03:00
committed by GitHub
parent 5fbbe9d86e
commit 53b702e8b1
37 changed files with 550 additions and 152 deletions

View File

@ -1,4 +1,5 @@
import { useColorModeValue, HStack, Tag, Text } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { Variable } from '@typebot.io/schemas'
export const SetVariableLabel = ({
@ -8,6 +9,7 @@ export const SetVariableLabel = ({
variableId: string
variables?: Variable[]
}) => {
const { t } = useTranslate()
const textColor = useColorModeValue('gray.600', 'gray.400')
const variableName = variables?.find(
(variable) => variable.id === variableId
@ -17,7 +19,7 @@ export const SetVariableLabel = ({
return (
<HStack fontStyle="italic" spacing={1}>
<Text fontSize="sm" color={textColor}>
Set
{t('variables.set')}
</Text>
<Tag bg="orange.400" color="white" size="sm">
{variableName}

View File

@ -33,6 +33,7 @@ import { byId, isDefined, isNotDefined } from '@typebot.io/lib'
import { useOutsideClick } from '@/hooks/useOutsideClick'
import { useParentModal } from '@/features/graph/providers/ParentModalProvider'
import { MoreInfoTooltip } from '../MoreInfoTooltip'
import { useTranslate } from '@tolgee/react'
type Props = {
initialVariableId: string | undefined
@ -78,6 +79,7 @@ export const VariableSearchInput = ({
const createVariableItemRef = useRef<HTMLButtonElement | null>(null)
const itemsRef = useRef<(HTMLButtonElement | null)[]>([])
const { ref: parentModalRef } = useParentModal()
const { t } = useTranslate()
useOutsideClick({
ref: dropdownRef,
@ -137,7 +139,7 @@ export const VariableSearchInput = ({
const handleRenameVariableClick =
(variable: Variable) => (e: React.MouseEvent) => {
e.stopPropagation()
const name = prompt('Rename variable', variable.name)
const name = prompt(t('variables.rename'), variable.name)
if (!name) return
updateVariable(variable.id, { name })
setFilteredItems(
@ -221,7 +223,7 @@ export const VariableSearchInput = ({
onChange={onInputChange}
onFocus={openDropdown}
onKeyDown={handleKeyUp}
placeholder={placeholder ?? 'Select a variable'}
placeholder={placeholder ?? t('variables.select')}
autoComplete="off"
{...inputProps}
/>
@ -255,7 +257,7 @@ export const VariableSearchInput = ({
: 'transparent'
}
>
Create
{t('create')}
<Tag colorScheme="orange" ml="1">
<Text noOfLines={0} display="block">
{inputValue}
@ -296,13 +298,13 @@ export const VariableSearchInput = ({
<HStack>
<IconButton
icon={<EditIcon />}
aria-label="Rename variable"
aria-label={t('variables.rename')}
size="xs"
onClick={handleRenameVariableClick(item)}
/>
<IconButton
icon={<TrashIcon />}
aria-label="Remove variable"
aria-label={t('variables.remove')}
size="xs"
onClick={handleDeleteVariableClick(item)}
/>

View File

@ -4,6 +4,7 @@ import { Stack, Tag, Text, Wrap } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { SetVariableLabel } from '@/components/SetVariableLabel'
import { ItemNodesList } from '@/features/graph/components/nodes/item/ItemNodesList'
import { useTranslate } from '@tolgee/react'
type Props = {
block: ChoiceInputBlock
@ -12,6 +13,7 @@ type Props = {
export const ButtonsBlockNode = ({ block, indices }: Props) => {
const { typebot } = useTypebot()
const { t } = useTranslate()
const dynamicVariableName = typebot?.variables.find(
(variable) => variable.id === block.options?.dynamicVariableId
)?.name
@ -20,11 +22,11 @@ export const ButtonsBlockNode = ({ block, indices }: Props) => {
<Stack w="full">
{block.options?.dynamicVariableId ? (
<Wrap spacing={1}>
<Text>Display</Text>
<Text>{t('blocks.inputs.button.variables.display.label')}</Text>
<Tag bg="orange.400" color="white">
{dynamicVariableName}
</Tag>
<Text>buttons</Text>
<Text>{t('blocks.inputs.button.variables.buttons.label')}</Text>
</Wrap>
) : (
<ItemNodesList block={block} indices={indices} />

View File

@ -6,6 +6,7 @@ import { ChoiceInputBlock, Variable } from '@typebot.io/schemas'
import React from 'react'
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
import { defaultChoiceInputOptions } from '@typebot.io/schemas/features/blocks/inputs/choice/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options?: ChoiceInputBlock['options']
@ -13,6 +14,7 @@ type Props = {
}
export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const updateIsMultiple = (isMultipleChoice: boolean) =>
onOptionsChange({ ...options, isMultipleChoice })
const updateIsSearchable = (isSearchable: boolean) =>
@ -29,7 +31,7 @@ export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithRelatedSettings
label="Multiple choice?"
label={t('blocks.inputs.settings.multipleChoice.label')}
initialValue={
options?.isMultipleChoice ??
defaultChoiceInputOptions.isMultipleChoice
@ -37,35 +39,34 @@ export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
onCheckChange={updateIsMultiple}
>
<TextInput
label="Submit button label:"
label={t('blocks.inputs.settings.submitButton.label')}
defaultValue={
options?.buttonLabel ?? defaultChoiceInputOptions.buttonLabel
options?.buttonLabel ?? t('blocks.inputs.settings.buttonText.label')
}
onChange={updateButtonLabel}
/>
</SwitchWithRelatedSettings>
<SwitchWithRelatedSettings
label="Is searchable?"
label={t('blocks.inputs.settings.isSearchable.label')}
initialValue={
options?.isSearchable ?? defaultChoiceInputOptions.isSearchable
}
onCheckChange={updateIsSearchable}
>
<TextInput
label="Input placeholder:"
label={t('blocks.inputs.settings.input.placeholder.label')}
defaultValue={
options?.searchInputPlaceholder ??
defaultChoiceInputOptions.searchInputPlaceholder
t('blocks.inputs.settings.input.filterOptions.label')
}
onChange={updateSearchInputPlaceholder}
/>
</SwitchWithRelatedSettings>
<FormControl>
<FormLabel>
Dynamic data:{' '}
{t('blocks.inputs.button.settings.dynamicData.label')}{' '}
<MoreInfoTooltip>
If defined, buttons will be dynamically displayed based on what the
variable contains.
{t('blocks.inputs.button.settings.dynamicData.infoText.label')}
</MoreInfoTooltip>
</FormLabel>
<VariableSearchInput
@ -75,7 +76,7 @@ export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
</FormControl>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -21,6 +21,7 @@ import React, { useRef, useState } from 'react'
import { isNotDefined } from '@typebot.io/lib'
import { useGraph } from '@/features/graph/providers/GraphProvider'
import { ButtonsItemSettings } from './ButtonsItemSettings'
import { useTranslate } from '@tolgee/react'
type Props = {
item: ButtonItem
@ -29,9 +30,12 @@ type Props = {
}
export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
const { t } = useTranslate()
const { deleteItem, updateItem, createItem } = useTypebot()
const { openedItemId, setOpenedItemId } = useGraph()
const [itemValue, setItemValue] = useState(item.content ?? 'Click to edit')
const [itemValue, setItemValue] = useState(
item.content ?? t('blocks.inputs.button.clickToEdit.label')
)
const editableRef = useRef<HTMLDivElement | null>(null)
const ref = useRef<HTMLDivElement | null>(null)
const arrowColor = useColorModeValue('white', 'gray.800')
@ -47,8 +51,16 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
}
const handleKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Escape' && itemValue === 'Click to edit') deleteItem(indices)
if (e.key === 'Enter' && itemValue !== '' && itemValue !== 'Click to edit')
if (
e.key === 'Escape' &&
itemValue === t('blocks.inputs.button.clickToEdit.label')
)
deleteItem(indices)
if (
e.key === 'Enter' &&
itemValue !== '' &&
itemValue !== t('blocks.inputs.button.clickToEdit.label')
)
handlePlusClick()
}
@ -82,7 +94,11 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
>
<EditablePreview
w="full"
color={item.content !== 'Click to edit' ? 'inherit' : 'gray.500'}
color={
item.content !== t('blocks.inputs.button.clickToEdit.label')
? 'inherit'
: 'gray.500'
}
cursor="pointer"
/>
<EditableInput onMouseDownCapture={(e) => e.stopPropagation()} />
@ -101,7 +117,7 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
>
<Flex bgColor={useColorModeValue('white', 'gray.800')} rounded="md">
<IconButton
aria-label="Open settings"
aria-label={t('blocks.inputs.button.openSettings.ariaLabel')}
icon={<SettingsIcon />}
variant="ghost"
size="sm"
@ -121,7 +137,7 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
unmountOnExit
>
<IconButton
aria-label="Add item"
aria-label={t('blocks.inputs.button.addItem.ariaLabel')}
icon={<PlusIcon />}
size="xs"
shadow="md"

View File

@ -4,6 +4,7 @@ import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSetting
import { ConditionForm } from '@/features/blocks/logic/condition/components/ConditionForm'
import { ButtonItem, Condition } from '@typebot.io/schemas'
import { LogicalOperator } from '@typebot.io/schemas/features/blocks/logic/condition/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
item: ButtonItem
@ -11,6 +12,8 @@ type Props = {
}
export const ButtonsItemSettings = ({ item, onSettingsChange }: Props) => {
const { t } = useTranslate()
const updateIsDisplayConditionEnabled = (isEnabled: boolean) =>
onSettingsChange({
...item,
@ -32,8 +35,10 @@ export const ButtonsItemSettings = ({ item, onSettingsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithRelatedSettings
label="Display condition"
moreInfoContent="Only display this item if a condition is met."
label={t('blocks.inputs.settings.displayCondition.label')}
moreInfoContent={t(
'blocks.inputs.button.buttonSettings.displayCondition.infoText.label'
)}
initialValue={item.displayCondition?.isEnabled ?? false}
onCheckChange={updateIsDisplayConditionEnabled}
>

View File

@ -6,6 +6,7 @@ import { FormLabel, Stack } from '@chakra-ui/react'
import { DateInputBlock, Variable } from '@typebot.io/schemas'
import React from 'react'
import { defaultDateInputOptions } from '@typebot.io/schemas/features/blocks/inputs/date/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options: DateInputBlock['options']
@ -13,6 +14,7 @@ type Props = {
}
export const DateInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const updateFromLabel = (from: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, from } })
const updateToLabel = (to: string) =>
@ -41,64 +43,69 @@ export const DateInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithRelatedSettings
label="Is range?"
label={t('blocks.inputs.date.settings.isRange.label')}
initialValue={options?.isRange ?? defaultDateInputOptions.isRange}
onCheckChange={updateIsRange}
>
<TextInput
label="From label:"
label={t('blocks.inputs.date.settings.from.label')}
defaultValue={
options?.labels?.from ?? defaultDateInputOptions.labels.from
}
onChange={updateFromLabel}
/>
<TextInput
label="To label:"
label={t('blocks.inputs.date.settings.to.label')}
defaultValue={
options?.labels?.to ?? defaultDateInputOptions.labels.to
options?.labels?.to ??
t('blocks.inputs.date.settings.toInputValue.label')
}
onChange={updateToLabel}
/>
</SwitchWithRelatedSettings>
<SwitchWithLabel
label="With time?"
label={t('blocks.inputs.date.settings.withTime.label')}
initialValue={options?.hasTime ?? defaultDateInputOptions.hasTime}
onCheckChange={updateHasTime}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultDateInputOptions.labels.button
}
onChange={updateButtonLabel}
/>
<TextInput
label="Min:"
label={t('blocks.inputs.settings.min.label')}
defaultValue={options?.min}
placeholder={options?.hasTime ? 'YYYY-MM-DDTHH:mm' : 'YYYY-MM-DD'}
onChange={updateMin}
/>
<TextInput
label="Max:"
label={t('blocks.inputs.settings.max.label')}
defaultValue={options?.max}
placeholder={options?.hasTime ? 'YYYY-MM-DDTHH:mm' : 'YYYY-MM-DD'}
onChange={updateMax}
/>
<TextInput
label="Format:"
label={t('blocks.inputs.date.settings.format.label')}
defaultValue={
options?.format ??
(options?.hasTime
? defaultDateInputOptions.formatWithTime
: defaultDateInputOptions.format)
}
moreInfoTooltip="i.e dd/MM/yyyy, MM/dd/yy, yyyy-MM-dd"
moreInfoTooltip={`
${t(
'blocks.inputs.date.settings.format.example.label'
)} dd/MM/yyyy, MM/dd/yy, yyyy-MM-dd
`}
placeholder={options?.hasTime ? 'dd/MM/yyyy HH:mm' : 'dd/MM/yyyy'}
onChange={updateFormat}
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -1,13 +1,17 @@
import React from 'react'
import { Text } from '@chakra-ui/react'
import { WithVariableContent } from '@/features/graph/components/nodes/block/WithVariableContent'
import { useTranslate } from '@tolgee/react'
type Props = {
variableId?: string
}
export const DateNodeContent = ({ variableId }: Props) =>
variableId ? (
export const DateNodeContent = ({ variableId }: Props) => {
const { t } = useTranslate()
return variableId ? (
<WithVariableContent variableId={variableId} />
) : (
<Text color={'gray.500'}>Pick a date...</Text>
<Text color={'gray.500'}>{t('blocks.inputs.date.placeholder.label')}</Text>
)
}

View File

@ -1,6 +1,7 @@
import { TextInput } from '@/components/inputs'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { FormLabel, Stack } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { EmailInputBlock, Variable } from '@typebot.io/schemas'
import { defaultEmailInputOptions } from '@typebot.io/schemas/features/blocks/inputs/email/constants'
import React from 'react'
@ -11,6 +12,7 @@ type Props = {
}
export const EmailInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
@ -23,7 +25,7 @@ export const EmailInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<TextInput
label="Placeholder:"
label={t('blocks.inputs.settings.placeholder.label')}
defaultValue={
options?.labels?.placeholder ??
defaultEmailInputOptions.labels.placeholder
@ -31,14 +33,14 @@ export const EmailInputSettings = ({ options, onOptionsChange }: Props) => {
onChange={handlePlaceholderChange}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultEmailInputOptions.labels.button
}
onChange={handleButtonLabelChange}
/>
<TextInput
label="Retry message:"
label={t('blocks.inputs.settings.retryMessage.label')}
defaultValue={
options?.retryMessageContent ??
defaultEmailInputOptions.retryMessageContent
@ -47,7 +49,7 @@ export const EmailInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -1,16 +1,22 @@
import { WithVariableContent } from '@/features/graph/components/nodes/block/WithVariableContent'
import { Text } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { FileInputBlock } from '@typebot.io/schemas'
type Props = {
options: FileInputBlock['options']
}
export const FileInputContent = ({ options }: Props) =>
options?.variableId ? (
export const FileInputContent = ({ options }: Props) => {
const { t } = useTranslate()
return options?.variableId ? (
<WithVariableContent variableId={options.variableId} />
) : (
<Text noOfLines={1} pr="6">
Collect {options?.isMultipleAllowed ? 'files' : 'file'}
{options?.isMultipleAllowed
? t('blocks.inputs.file.collectMultiple.label')
: t('blocks.inputs.file.collectSingle.label')}
</Text>
)
}

View File

@ -6,6 +6,7 @@ import { TextInput } from '@/components/inputs'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { defaultFileInputOptions } from '@typebot.io/schemas/features/blocks/inputs/file/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options: FileInputBlock['options']
@ -13,6 +14,8 @@ type Props = {
}
export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handleButtonLabelChange = (button: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, button } })
@ -37,12 +40,12 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithLabel
label="Required?"
label={t('blocks.inputs.file.settings.required.label')}
initialValue={options?.isRequired ?? defaultFileInputOptions.isRequired}
onCheckChange={handleRequiredChange}
/>
<SwitchWithLabel
label="Allow multiple files?"
label={t('blocks.inputs.file.settings.allowMultiple.label')}
initialValue={
options?.isMultipleAllowed ??
defaultFileInputOptions.isMultipleAllowed
@ -50,7 +53,9 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
onCheckChange={handleMultipleFilesChange}
/>
<Stack>
<FormLabel mb="0">Placeholder:</FormLabel>
<FormLabel mb="0">
{t('blocks.inputs.settings.placeholder.label')}
</FormLabel>
<CodeEditor
lang="html"
onChange={handlePlaceholderLabelChange}
@ -63,7 +68,7 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
/>
</Stack>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultFileInputOptions.labels.button
}
@ -71,7 +76,7 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
withVariableButton={false}
/>
<TextInput
label="Clear button label:"
label={t('blocks.inputs.file.settings.clear.label')}
defaultValue={
options?.labels?.clear ?? defaultFileInputOptions.labels.clear
}
@ -79,7 +84,7 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
withVariableButton={false}
/>
<TextInput
label="Skip button label:"
label={t('blocks.inputs.file.settings.skip.label')}
defaultValue={
options?.labels?.skip ?? defaultFileInputOptions.labels.skip
}
@ -88,7 +93,9 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save upload URL{options?.isMultipleAllowed ? 's' : ''} in a variable:
{options?.isMultipleAllowed
? t('blocks.inputs.file.settings.saveMultipleUpload.label')
: t('blocks.inputs.file.settings.saveSingleUpload.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -1,6 +1,7 @@
import { TextInput, NumberInput } from '@/components/inputs'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { FormLabel, Stack } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { NumberInputBlock, Variable } from '@typebot.io/schemas'
import { defaultNumberInputOptions } from '@typebot.io/schemas/features/blocks/inputs/number/constants'
import React from 'react'
@ -11,6 +12,7 @@ type Props = {
}
export const NumberInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
@ -31,7 +33,7 @@ export const NumberInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<TextInput
label="Placeholder:"
label={t('blocks.inputs.settings.placeholder.label')}
defaultValue={
options?.labels?.placeholder ??
defaultNumberInputOptions.labels.placeholder
@ -39,30 +41,30 @@ export const NumberInputSettings = ({ options, onOptionsChange }: Props) => {
onChange={handlePlaceholderChange}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultNumberInputOptions.labels.button
}
onChange={handleButtonLabelChange}
/>
<NumberInput
label="Min:"
label={t('blocks.inputs.settings.min.label')}
defaultValue={options?.min}
onValueChange={handleMinChange}
/>
<NumberInput
label="Max:"
label={t('blocks.inputs.settings.max.label')}
defaultValue={options?.max}
onValueChange={handleMaxChange}
/>
<NumberInput
label="Step:"
label={t('blocks.inputs.number.settings.step.label')}
defaultValue={options?.step}
onValueChange={handleStepChange}
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -9,6 +9,7 @@ import {
import React from 'react'
import { TextInput } from '@/components/inputs'
import { PaymentAddress } from '@typebot.io/schemas'
import { useTranslate } from '@tolgee/react'
type Props = {
address: PaymentAddress
@ -16,6 +17,8 @@ type Props = {
}
export const PaymentAddressSettings = ({ address, onAddressChange }: Props) => {
const { t } = useTranslate()
const updateCountry = (country: string) =>
onAddressChange({
...address,
@ -56,37 +59,41 @@ export const PaymentAddressSettings = ({ address, onAddressChange }: Props) => {
<Accordion allowToggle>
<AccordionItem>
<AccordionButton justifyContent="space-between">
Address
{t('blocks.inputs.payment.settings.address.label')}
<AccordionIcon />
</AccordionButton>
<AccordionPanel py={4} as={Stack} spacing="4">
<TextInput
label="Country:"
label={t('blocks.inputs.payment.settings.address.country.label')}
defaultValue={address?.country ?? ''}
onChange={updateCountry}
/>
<TextInput
label="Line 1:"
label={t('blocks.inputs.payment.settings.address.line.label', {
line: '1',
})}
defaultValue={address?.line1 ?? ''}
onChange={updateLine1}
/>
<TextInput
label="Line 2:"
label={t('blocks.inputs.payment.settings.address.line.label', {
line: '2',
})}
defaultValue={address?.line2 ?? ''}
onChange={updateLine2}
/>
<TextInput
label="City:"
label={t('blocks.inputs.payment.settings.address.city.label')}
defaultValue={address?.city ?? ''}
onChange={updateCity}
/>
<TextInput
label="State:"
label={t('blocks.inputs.payment.settings.address.state.label')}
defaultValue={address?.state ?? ''}
onChange={updateState}
/>
<TextInput
label="Postal code:"
label={t('blocks.inputs.payment.settings.address.postalCode.label')}
defaultValue={address?.postalCode ?? ''}
onChange={updatePostalCode}
/>

View File

@ -1,4 +1,5 @@
import { Text } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { PaymentInputBlock } from '@typebot.io/schemas'
type Props = {
@ -6,15 +7,22 @@ type Props = {
}
export const PaymentInputContent = ({ block }: Props) => {
const { t } = useTranslate()
if (
!block.options?.amount ||
!block.options.credentialsId ||
!block.options.currency
)
return <Text color="gray.500">Configure...</Text>
return (
<Text color="gray.500">
{t('blocks.inputs.payment.placeholder.label')}
</Text>
)
return (
<Text noOfLines={1} pr="6">
Collect {block.options.amount} {block.options.currency}
{t('blocks.inputs.payment.collect.label')} {block.options.amount}{' '}
{block.options.currency}
</Text>
)
}

View File

@ -23,6 +23,7 @@ import {
PaymentProvider,
defaultPaymentInputOptions,
} from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options: PaymentInputBlock['options']
@ -32,6 +33,7 @@ type Props = {
export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
const { workspace } = useWorkspace()
const { isOpen, onOpen, onClose } = useDisclosure()
const { t } = useTranslate()
const updateProvider = (provider: PaymentProvider) => {
onOptionsChange({
@ -104,7 +106,7 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<Stack>
<Text>Provider:</Text>
<Text>{t('blocks.inputs.payment.settings.provider.label')}</Text>
<DropdownList
onItemSelect={updateProvider}
items={Object.values(PaymentProvider)}
@ -112,7 +114,7 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
/>
</Stack>
<Stack>
<Text>Account:</Text>
<Text>{t('blocks.inputs.payment.settings.account.label')}</Text>
{workspace && (
<CredentialsDropdown
type="stripe"
@ -120,19 +122,24 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
currentCredentialsId={options?.credentialsId}
onCredentialsSelect={updateCredentials}
onCreateNewClick={onOpen}
credentialsName="Stripe account"
credentialsName={t(
'blocks.inputs.payment.settings.accountText.label',
{
provider: 'Stripe',
}
)}
/>
)}
</Stack>
<HStack>
<TextInput
label="Price amount:"
label={t('blocks.inputs.payment.settings.priceAmount.label')}
onChange={updateAmount}
defaultValue={options?.amount}
placeholder="30.00"
/>
<Stack>
<Text>Currency:</Text>
<Text>{t('blocks.inputs.payment.settings.currency.label')}</Text>
<Select
placeholder="Select option"
value={options?.currency ?? defaultPaymentInputOptions.currency}
@ -147,14 +154,14 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
</Stack>
</HStack>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
onChange={updateButtonLabel}
defaultValue={
options?.labels?.button ?? defaultPaymentInputOptions.labels.button
}
/>
<TextInput
label="Success message:"
label={t('blocks.inputs.payment.settings.successMessage.label')}
onChange={updateSuccessLabel}
defaultValue={
options?.labels?.success ?? defaultPaymentInputOptions.labels.success
@ -163,30 +170,38 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
<Accordion allowToggle>
<AccordionItem>
<AccordionButton justifyContent="space-between">
Additional information
{t('blocks.inputs.payment.settings.additionalInformation.label')}
<AccordionIcon />
</AccordionButton>
<AccordionPanel py={4} as={Stack} spacing="6">
<TextInput
label="Description:"
label={t('blocks.inputs.settings.description.label')}
defaultValue={options?.additionalInformation?.description}
onChange={updateDescription}
placeholder="A digital product"
placeholder={t(
'blocks.inputs.payment.settings.additionalInformation.description.placeholder.label'
)}
/>
<TextInput
label="Name:"
label={t(
'blocks.inputs.payment.settings.additionalInformation.name.label'
)}
defaultValue={options?.additionalInformation?.name}
onChange={updateName}
placeholder="John Smith"
/>
<TextInput
label="Email:"
label={t(
'blocks.inputs.payment.settings.additionalInformation.email.label'
)}
defaultValue={options?.additionalInformation?.email}
onChange={updateEmail}
placeholder="john@gmail.com"
/>
<TextInput
label="Phone number:"
label={t(
'blocks.inputs.payment.settings.additionalInformation.phone.label'
)}
defaultValue={options?.additionalInformation?.phoneNumber}
onChange={updatePhoneNumber}
placeholder="+33XXXXXXXXX"

View File

@ -23,6 +23,7 @@ import { StripeCredentials } from '@typebot.io/schemas'
import { trpc } from '@/lib/trpc'
import { isNotEmpty } from '@typebot.io/lib'
import { useUser } from '@/features/account/hooks/useUser'
import { useTranslate } from '@tolgee/react'
type Props = {
isOpen: boolean
@ -35,6 +36,7 @@ export const StripeConfigModal = ({
onNewCredentials,
onClose,
}: Props) => {
const { t } = useTranslate()
const { user } = useUser()
const { workspace } = useWorkspace()
const [isCreating, setIsCreating] = useState(false)
@ -122,13 +124,17 @@ export const StripeConfigModal = ({
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Connect Stripe account</ModalHeader>
<ModalHeader>
{t('blocks.inputs.payment.settings.stripeConfig.title.label')}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack as="form" spacing={4}>
<TextInput
isRequired
label="Account name:"
label={t(
'blocks.inputs.payment.settings.stripeConfig.accountName.label'
)}
onChange={handleNameChange}
placeholder="Typebot"
withVariableButton={false}
@ -136,9 +142,13 @@ export const StripeConfigModal = ({
/>
<Stack>
<FormLabel>
Test keys:{' '}
{t(
'blocks.inputs.payment.settings.stripeConfig.testKeys.label'
)}{' '}
<MoreInfoTooltip>
Will be used when previewing the bot.
{t(
'blocks.inputs.payment.settings.stripeConfig.testKeys.infoText.label'
)}
</MoreInfoTooltip>
</FormLabel>
<HStack>
@ -157,7 +167,11 @@ export const StripeConfigModal = ({
</HStack>
</Stack>
<Stack>
<FormLabel>Live keys:</FormLabel>
<FormLabel>
{t(
'blocks.inputs.payment.settings.stripeConfig.liveKeys.label'
)}
</FormLabel>
<HStack>
<FormControl>
<TextInput
@ -179,9 +193,11 @@ export const StripeConfigModal = ({
</Stack>
<Text>
(You can find your keys{' '}
({t('blocks.inputs.payment.settings.stripeConfig.findKeys.label')}{' '}
<TextLink href="https://dashboard.stripe.com/apikeys" isExternal>
here
{t(
'blocks.inputs.payment.settings.stripeConfig.findKeys.here.label'
)}
</TextLink>
)
</Text>
@ -199,7 +215,7 @@ export const StripeConfigModal = ({
}
isLoading={isCreating}
>
Connect
{t('connect')}
</Button>
</ModalFooter>
</ModalContent>

View File

@ -1,4 +1,5 @@
import { Select } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import React, { ChangeEvent } from 'react'
type Props = {
@ -7,12 +8,15 @@ type Props = {
}
export const CountryCodeSelect = ({ countryCode, onSelect }: Props) => {
const { t } = useTranslate()
const handleOnChange = (e: ChangeEvent<HTMLSelectElement>) => {
onSelect(e.target.value)
}
return (
<Select
placeholder="International"
placeholder={t(
'blocks.inputs.phone.settings.international.placeholder.label'
)}
value={countryCode}
onChange={handleOnChange}
>

View File

@ -4,6 +4,7 @@ import { FormLabel, Stack } from '@chakra-ui/react'
import { PhoneNumberInputBlock, Variable } from '@typebot.io/schemas'
import React from 'react'
import { CountryCodeSelect } from './CountryCodeSelect'
import { useTranslate } from '@tolgee/react'
import { defaultPhoneInputOptions } from '@typebot.io/schemas/features/blocks/inputs/phone/constants'
type Props = {
@ -12,6 +13,7 @@ type Props = {
}
export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
@ -26,7 +28,7 @@ export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<TextInput
label="Placeholder:"
label={t('blocks.inputs.settings.placeholder.label')}
defaultValue={
options?.labels?.placeholder ??
defaultPhoneInputOptions.labels.placeholder
@ -34,7 +36,7 @@ export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
onChange={handlePlaceholderChange}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultPhoneInputOptions.labels.button
}
@ -42,7 +44,7 @@ export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="button">
Default country:
{t('blocks.inputs.phone.settings.defaultCountry.label')}
</FormLabel>
<CountryCodeSelect
onSelect={handleDefaultCountryChange}
@ -50,7 +52,7 @@ export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
/>
</Stack>
<TextInput
label="Retry message:"
label={t('blocks.inputs.settings.retryMessage.label')}
defaultValue={
options?.retryMessageContent ??
defaultPhoneInputOptions.retryMessageContent
@ -59,7 +61,7 @@ export const PhoneInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -15,6 +15,7 @@ import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSetting
import { ConditionForm } from '@/features/blocks/logic/condition/components/ConditionForm'
import { Condition } from '@typebot.io/schemas'
import { LogicalOperator } from '@typebot.io/schemas/features/blocks/logic/condition/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
workspaceId: string
@ -31,6 +32,8 @@ export const PictureChoiceItemSettings = ({
item,
onItemChange,
}: Props) => {
const { t } = useTranslate()
const updateTitle = (title: string) => onItemChange({ ...item, title })
const updateImage = (pictureSrc: string) => {
@ -61,13 +64,17 @@ export const PictureChoiceItemSettings = ({
return (
<Stack spacing={4}>
<HStack>
<Text fontWeight="medium">Image:</Text>
<Text fontWeight="medium">
{t('blocks.inputs.picture.itemSettings.image.label')}
</Text>
<Popover isLazy>
{({ onClose }) => (
<>
<PopoverTrigger>
<Button size="sm">
{item.pictureSrc ? 'Change image' : 'Pick an image'}
{item.pictureSrc
? t('blocks.inputs.picture.itemSettings.image.change.label')
: t('blocks.inputs.picture.itemSettings.image.pick.label')}
</Button>
</PopoverTrigger>
<PopoverContent p="4" w="500px">
@ -91,17 +98,17 @@ export const PictureChoiceItemSettings = ({
</Popover>
</HStack>
<TextInput
label="Title:"
label={t('blocks.inputs.picture.itemSettings.title.label')}
defaultValue={item.title}
onChange={updateTitle}
/>
<Textarea
label="Description:"
label={t('blocks.inputs.settings.description.label')}
defaultValue={item.description}
onChange={updateDescription}
/>
<SwitchWithRelatedSettings
label="Display condition"
label={t('blocks.inputs.settings.displayCondition.label')}
initialValue={item.displayCondition?.isEnabled ?? false}
onCheckChange={updateIsDisplayConditionEnabled}
>

View File

@ -5,6 +5,7 @@ import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { SetVariableLabel } from '@/components/SetVariableLabel'
import { ItemNodesList } from '@/features/graph/components/nodes/item/ItemNodesList'
import { PictureChoiceBlock } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
import { useTranslate } from '@tolgee/react'
type Props = {
block: PictureChoiceBlock
@ -12,6 +13,7 @@ type Props = {
}
export const PictureChoiceNode = ({ block, indices }: Props) => {
const { t } = useTranslate()
const { typebot } = useTypebot()
const dynamicVariableName = typebot?.variables.find(
(variable) =>
@ -22,11 +24,17 @@ export const PictureChoiceNode = ({ block, indices }: Props) => {
<Stack w="full">
{block.options?.dynamicItems?.isEnabled && dynamicVariableName ? (
<Wrap spacing={1}>
<Text>Display</Text>
<Text>
{t('blocks.inputs.picture.settings.dynamicVariables.display.label')}
</Text>
<Tag bg="orange.400" color="white">
{dynamicVariableName}
</Tag>
<Text>pictures</Text>
<Text>
{t(
'blocks.inputs.picture.settings.dynamicVariables.pictures.label'
)}
</Text>
</Wrap>
) : (
<ItemNodesList block={block} indices={indices} />

View File

@ -6,6 +6,7 @@ import React from 'react'
import { PictureChoiceBlock } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options?: PictureChoiceBlock['options']
@ -13,6 +14,8 @@ type Props = {
}
export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const updateIsMultiple = (isMultipleChoice: boolean) =>
onOptionsChange({ ...options, isMultipleChoice })
const updateButtonLabel = (buttonLabel: string) =>
@ -63,14 +66,14 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithRelatedSettings
label="Is searchable?"
label={t('blocks.inputs.settings.isSearchable.label')}
initialValue={
options?.isSearchable ?? defaultPictureChoiceOptions.isSearchable
}
onCheckChange={updateIsSearchable}
>
<TextInput
label="Input placeholder:"
label={t('blocks.inputs.settings.input.placeholder.label')}
defaultValue={
options?.searchInputPlaceholder ??
defaultPictureChoiceOptions.searchInputPlaceholder
@ -79,7 +82,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
/>
</SwitchWithRelatedSettings>
<SwitchWithRelatedSettings
label="Multiple choice?"
label={t('blocks.inputs.settings.multipleChoice.label')}
initialValue={
options?.isMultipleChoice ??
defaultPictureChoiceOptions.isMultipleChoice
@ -87,7 +90,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
onCheckChange={updateIsMultiple}
>
<TextInput
label="Submit button label:"
label={t('blocks.inputs.settings.submitButton.label')}
defaultValue={
options?.buttonLabel ?? defaultPictureChoiceOptions.buttonLabel
}
@ -96,7 +99,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
</SwitchWithRelatedSettings>
<SwitchWithRelatedSettings
label="Dynamic items?"
label={t('blocks.inputs.picture.settings.dynamicItems.label')}
initialValue={
options?.dynamicItems?.isEnabled ??
defaultPictureChoiceOptions.dynamicItems.isEnabled
@ -105,7 +108,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Images:
{t('blocks.inputs.picture.settings.dynamicItems.images.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.dynamicItems?.pictureSrcsVariableId}
@ -114,7 +117,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Titles:
{t('blocks.inputs.picture.settings.dynamicItems.titles.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.dynamicItems?.titlesVariableId}
@ -123,7 +126,9 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Descriptions:
{t(
'blocks.inputs.picture.settings.dynamicItems.descriptions.label'
)}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.dynamicItems?.descriptionsVariableId}
@ -134,7 +139,7 @@ export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -1,5 +1,6 @@
import { WithVariableContent } from '@/features/graph/components/nodes/block/WithVariableContent'
import { Text } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { RatingInputBlock } from '@typebot.io/schemas'
import { defaultRatingInputOptions } from '@typebot.io/schemas/features/blocks/inputs/rating/constants'
@ -8,12 +9,17 @@ type Props = {
block: RatingInputBlock
}
export const RatingInputContent = ({ variableId, block }: Props) =>
variableId ? (
export const RatingInputContent = ({ variableId, block }: Props) => {
const { t } = useTranslate()
return variableId ? (
<WithVariableContent variableId={variableId} />
) : (
<Text noOfLines={1} pr="6">
Rate from {block.options?.buttonType === 'Icons' ? 1 : 0} to{' '}
{t('blocks.inputs.rating.from.label')}{' '}
{block.options?.buttonType === 'Icons' ? 1 : 0}{' '}
{t('blocks.inputs.rating.to.label')}{' '}
{block.options?.length ?? defaultRatingInputOptions.length}
</Text>
)
}

View File

@ -6,6 +6,7 @@ import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { TextInput } from '@/components/inputs'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { defaultRatingInputOptions } from '@typebot.io/schemas/features/blocks/inputs/rating/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
options: RatingInputBlock['options']
@ -13,6 +14,8 @@ type Props = {
}
export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handleLengthChange = (length: number) =>
onOptionsChange({ ...options, length })
@ -52,7 +55,7 @@ export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
<Stack spacing={4}>
<Stack>
<FormLabel mb="0" htmlFor="button">
Maximum:
{t('blocks.inputs.rating.settings.maximum.label')}
</FormLabel>
<DropdownList
onItemSelect={handleLengthChange}
@ -63,7 +66,7 @@ export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
<Stack>
<FormLabel mb="0" htmlFor="button">
Type:
{t('blocks.inputs.rating.settings.type.label')}
</FormLabel>
<DropdownList
onItemSelect={handleTypeChange}
@ -76,7 +79,7 @@ export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
{options?.buttonType === 'Icons' && (
<SwitchWithLabel
label="Custom icon?"
label={t('blocks.inputs.rating.settings.customIcon.label')}
initialValue={
options?.customIcon?.isEnabled ??
defaultRatingInputOptions.customIcon.isEnabled
@ -86,33 +89,43 @@ export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
)}
{options?.buttonType === 'Icons' && options.customIcon?.isEnabled && (
<TextInput
label="Icon SVG:"
label={t('blocks.inputs.rating.settings.iconSVG.label')}
defaultValue={options.customIcon.svg}
onChange={handleIconSvgChange}
placeholder="<svg>...</svg>"
/>
)}
<TextInput
label={`${options?.buttonType === 'Icons' ? '1' : '0'} label:`}
label={t('blocks.inputs.rating.settings.rateLabel.label', {
rate: options?.buttonType === 'Icons' ? '1' : '0',
})}
defaultValue={options?.labels?.left}
onChange={handleLeftLabelChange}
placeholder="Not likely at all"
placeholder={t(
'blocks.inputs.rating.settings.notLikely.placeholder.label'
)}
/>
<TextInput
label={`${length} label:`}
label={t('blocks.inputs.rating.settings.rateLabel.label', {
rate: length,
})}
defaultValue={options?.labels?.right}
onChange={handleRightLabelChange}
placeholder="Extremely likely"
placeholder={t(
'blocks.inputs.rating.settings.extremelyLikely.placeholder.label'
)}
/>
<SwitchWithLabel
label="One click submit"
moreInfoContent='If enabled, the answer will be submitted as soon as the user clicks on a rating instead of showing the "Send" button.'
label={t('blocks.inputs.rating.settings.oneClickSubmit.label')}
moreInfoContent={t(
'blocks.inputs.rating.settings.oneClickSubmit.infoText.label'
)}
initialValue={isOneClickSubmitEnabled}
onCheckChange={handleOneClickSubmitChange}
/>
{!isOneClickSubmitEnabled && (
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultRatingInputOptions.labels.button
}
@ -121,7 +134,7 @@ export const RatingInputSettings = ({ options, onOptionsChange }: Props) => {
)}
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -2,6 +2,7 @@ import { TextInput } from '@/components/inputs'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { FormLabel, Stack } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { TextInputBlock, Variable } from '@typebot.io/schemas'
import { defaultTextInputOptions } from '@typebot.io/schemas/features/blocks/inputs/text/constants'
import React from 'react'
@ -12,6 +13,7 @@ type Props = {
}
export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
@ -24,12 +26,12 @@ export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<SwitchWithLabel
label="Long text?"
label={t('blocks.inputs.text.settings.longText.label')}
initialValue={options?.isLong ?? defaultTextInputOptions.isLong}
onCheckChange={handleLongChange}
/>
<TextInput
label="Placeholder:"
label={t('blocks.inputs.settings.placeholder.label')}
defaultValue={
options?.labels?.placeholder ??
defaultTextInputOptions.labels.placeholder
@ -37,7 +39,7 @@ export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
onChange={handlePlaceholderChange}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultTextInputOptions.labels.button
}
@ -45,7 +47,7 @@ export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -1,6 +1,7 @@
import { TextInput } from '@/components/inputs'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { FormLabel, Stack } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { UrlInputBlock, Variable } from '@typebot.io/schemas'
import { defaultUrlInputOptions } from '@typebot.io/schemas/features/blocks/inputs/url/constants'
import React from 'react'
@ -11,6 +12,7 @@ type Props = {
}
export const UrlInputSettings = ({ options, onOptionsChange }: Props) => {
const { t } = useTranslate()
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
@ -23,7 +25,7 @@ export const UrlInputSettings = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<TextInput
label="Placeholder:"
label={t('blocks.inputs.settings.placeholder.label')}
defaultValue={
options?.labels?.placeholder ??
defaultUrlInputOptions.labels.placeholder
@ -31,14 +33,14 @@ export const UrlInputSettings = ({ options, onOptionsChange }: Props) => {
onChange={handlePlaceholderChange}
/>
<TextInput
label="Button label:"
label={t('blocks.inputs.settings.button.label')}
defaultValue={
options?.labels?.button ?? defaultUrlInputOptions.labels.button
}
onChange={handleButtonLabelChange}
/>
<TextInput
label="Retry message:"
label={t('blocks.inputs.settings.retryMessage.label')}
defaultValue={
options?.retryMessageContent ??
defaultUrlInputOptions.retryMessageContent
@ -47,7 +49,7 @@ export const UrlInputSettings = ({ options, onOptionsChange }: Props) => {
/>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
{t('blocks.inputs.settings.saveAnswer.label')}
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}

View File

@ -8,8 +8,8 @@ type Props = {
options: UrlInputBlock['options']
}
export const UrlNodeContent = ({ options }: Props) =>
options?.variableId ? (
export const UrlNodeContent = ({ options }: Props) => {
return options?.variableId ? (
<WithVariableContent variableId={options.variableId} />
) : (
<Text color={'gray.500'} w="90%">
@ -17,3 +17,4 @@ export const UrlNodeContent = ({ options }: Props) =>
defaultUrlInputOptions.labels.placeholder}
</Text>
)
}

View File

@ -5,11 +5,14 @@ import { TableListItemProps } from '@/components/TableList'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { TextInput } from '@/components/inputs'
import { ComparisonOperators } from '@typebot.io/schemas/features/blocks/logic/condition/constants'
import { useTranslate } from '@tolgee/react'
export const ComparisonItem = ({
item,
onItemChange,
}: TableListItemProps<Comparison>) => {
const { t } = useTranslate()
const handleSelectVariable = (variable?: Variable) => {
if (variable?.id === item.variableId) return
onItemChange({ ...item, variableId: variable?.id })
@ -31,13 +34,15 @@ export const ComparisonItem = ({
<VariableSearchInput
initialVariableId={item.variableId}
onSelectVariable={handleSelectVariable}
placeholder="Search for a variable"
placeholder={t('variables.search')}
/>
<DropdownList
currentItem={item.comparisonOperator}
onItemSelect={handleSelectComparisonOperator}
items={Object.values(ComparisonOperators)}
placeholder="Select an operator"
placeholder={t(
'blocks.inputs.button.buttonSettings.displayCondition.selectOperator.label'
)}
/>
{item.comparisonOperator !== ComparisonOperators.IS_SET &&
item.comparisonOperator !== ComparisonOperators.IS_EMPTY && (

View File

@ -1,4 +1,5 @@
import { Stack, Wrap, Tag, Text, useColorModeValue } from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import { byId } from '@typebot.io/lib'
import { Condition, Variable } from '@typebot.io/schemas'
import { ComparisonOperators } from '@typebot.io/schemas/features/blocks/logic/condition/constants'
@ -15,6 +16,7 @@ export const ConditionContent = ({
size = 'sm',
displaySemicolon,
}: Props) => {
const { t } = useTranslate()
const comparisonValueBg = useColorModeValue('gray.200', 'gray.700')
return (
<Stack>
@ -22,7 +24,11 @@ export const ConditionContent = ({
const variable = variables.find(byId(comparison.variableId))
return (
<Wrap key={comparison.id} spacing={1} noOfLines={1}>
{idx === 0 && <Text fontSize={size}>IF</Text>}
{idx === 0 && (
<Text fontSize={size}>
{t('blocks.inputs.button.conditionContent.if.label')}
</Text>
)}
{idx > 0 && (
<Text fontSize={size}>{condition.logicalOperator ?? ''}</Text>
)}

View File

@ -8,6 +8,7 @@ import {
LogicalOperator,
defaultConditionItemContent,
} from '@typebot.io/schemas/features/blocks/logic/condition/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
condition: Condition | undefined
@ -15,6 +16,7 @@ type Props = {
}
export const ConditionForm = ({ condition, onConditionChange }: Props) => {
const { t } = useTranslate()
const handleComparisonsChange = (comparisons: Comparison[]) =>
onConditionChange({ ...condition, comparisons })
const handleLogicalOperatorChange = (logicalOperator: LogicalOperator) =>
@ -36,7 +38,9 @@ export const ConditionForm = ({ condition, onConditionChange }: Props) => {
/>
</Flex>
)}
addLabel="Add a comparison"
addLabel={t(
'blocks.inputs.button.buttonSettings.addComparisonButton.label'
)}
>
{(props) => <ComparisonItem {...props} />}
</TableList>

View File

@ -15,6 +15,7 @@ import { useToast } from '../../../hooks/useToast'
import { Credentials } from '@typebot.io/schemas'
import { trpc } from '@/lib/trpc'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { useTranslate } from '@tolgee/react'
type Props = Omit<ButtonProps, 'type'> & {
type: Credentials['type']
@ -36,6 +37,7 @@ export const CredentialsDropdown = ({
credentialsName,
...props
}: Props) => {
const { t } = useTranslate()
const { showToast } = useToast()
const { currentRole } = useWorkspace()
const { data, refetch } = trpc.credentials.listCredentials.useQuery({
@ -62,7 +64,7 @@ export const CredentialsDropdown = ({
})
const defaultCredentialsLabel =
defaultCredentialLabel ?? `Select ${credentialsName}`
defaultCredentialLabel ?? `${t('select')} ${credentialsName}`
const currentCredential = data?.credentials.find(
(c) => c.id === currentCredentialsId
@ -91,7 +93,7 @@ export const CredentialsDropdown = ({
isDisabled={currentRole === 'GUEST'}
{...props}
>
Add {credentialsName}
{t('add')} {credentialsName}
</Button>
)
}
@ -140,7 +142,9 @@ export const CredentialsDropdown = ({
{credentials.name}
<IconButton
icon={<TrashIcon />}
aria-label="Remove credentials"
aria-label={t(
'blocks.inputs.payment.settings.credentials.removeCredentials.label'
)}
size="xs"
onClick={deleteCredentials(credentials.id)}
isLoading={isDeleting === credentials.id}
@ -156,7 +160,7 @@ export const CredentialsDropdown = ({
icon={<PlusIcon />}
onClick={onCreateNewClick}
>
Connect new
{t('blocks.inputs.payment.settings.credentials.connectNew.label')}
</MenuItem>
)}
</Stack>

View File

@ -31,9 +31,11 @@ const groupsActions = (setTypebot: SetTypebot): GroupsActions => ({
id,
block,
indices,
groupLabel,
...graphCoordinates
}: Coordinates & {
id: string
groupLabel?: string
block: BlockV6 | BlockV6['type']
indices: BlockIndices
}) =>
@ -42,7 +44,7 @@ const groupsActions = (setTypebot: SetTypebot): GroupsActions => ({
const newGroup: GroupV6 = {
id,
graphCoordinates,
title: `Group #${typebot.groups.length + 1}`,
title: `${groupLabel ?? 'Group'} #${typebot.groups.length + 1}`,
blocks: [],
}
typebot.groups.push(newGroup)

View File

@ -2,9 +2,11 @@ import { MenuList, MenuItem } from '@chakra-ui/react'
import { CopyIcon, TrashIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { BlockIndices } from '@typebot.io/schemas'
import { useTranslate } from '@tolgee/react'
type Props = { indices: BlockIndices }
export const BlockNodeContextMenu = ({ indices }: Props) => {
const { t } = useTranslate()
const { deleteBlock, duplicateBlock } = useTypebot()
const handleDuplicateClick = () => duplicateBlock(indices)
@ -14,10 +16,10 @@ export const BlockNodeContextMenu = ({ indices }: Props) => {
return (
<MenuList>
<MenuItem icon={<CopyIcon />} onClick={handleDuplicateClick}>
Duplicate
{t('duplicate')}
</MenuItem>
<MenuItem icon={<TrashIcon />} onClick={handleDeleteClick}>
Delete
{t('delete')}
</MenuItem>
</MenuList>
)

View File

@ -9,6 +9,7 @@ import {
import { BlockWithOptions } from '@typebot.io/schemas'
import { getHelpDocUrl } from '@/features/graph/helpers/getHelpDocUrl'
import { useForgedBlock } from '@/features/forge/hooks/useForgedBlock'
import { useTranslate } from '@tolgee/react'
type Props = {
blockType: BlockWithOptions['type']
@ -16,6 +17,7 @@ type Props = {
}
export const SettingsHoverBar = ({ blockType, onExpandClick }: Props) => {
const { t } = useTranslate()
const { blockDef } = useForgedBlock(blockType)
const helpDocUrl = getHelpDocUrl(blockType, blockDef)
return (
@ -46,7 +48,7 @@ export const SettingsHoverBar = ({ blockType, onExpandClick }: Props) => {
href={helpDocUrl}
isExternal
>
Help
{t('help')}
</Button>
)}
</HStack>

View File

@ -1,12 +1,14 @@
import { MenuList, MenuItem } from '@chakra-ui/react'
import { CopyIcon, TrashIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { useTranslate } from '@tolgee/react'
export const GroupNodeContextMenu = ({
groupIndex,
}: {
groupIndex: number
}) => {
const { t } = useTranslate()
const { deleteGroup, duplicateGroup } = useTypebot()
const handleDeleteClick = () => deleteGroup(groupIndex)
@ -16,10 +18,10 @@ export const GroupNodeContextMenu = ({
return (
<MenuList>
<MenuItem icon={<CopyIcon />} onClick={handleDuplicateClick}>
Duplicate
{t('duplicate')}
</MenuItem>
<MenuItem icon={<TrashIcon />} onClick={handleDeleteClick}>
Delete
{t('delete')}
</MenuItem>
</MenuList>
)

View File

@ -22,6 +22,7 @@ import { Coordinates } from '@dnd-kit/utilities'
import { BlockSourceEndpoint } from '../../endpoints/BlockSourceEndpoint'
import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/constants'
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
import { useTranslate } from '@tolgee/react'
type Props = {
block: BlockWithItems
@ -192,6 +193,8 @@ const DefaultItemNode = ({
block: BlockWithItems
groupId: string
}) => {
const { t } = useTranslate()
return (
<Flex
px="4"
@ -205,7 +208,9 @@ const DefaultItemNode = ({
cursor="not-allowed"
>
<Text color="gray.500">
{block.type === LogicBlockType.CONDITION ? 'Else' : 'Default'}
{block.type === LogicBlockType.CONDITION
? t('blocks.inputs.button.else.label')
: t('blocks.inputs.button.default.label')}
</Text>
<BlockSourceEndpoint
source={{

View File

@ -30,6 +30,7 @@
"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. \uD83E\uDD13",
"add": "Add",
"analytics.completionRateLabel": "Completion rate",
"analytics.notAvailableLabel": "Not available",
"analytics.startsLabel": "Starts",
@ -112,7 +113,103 @@
"billing.usage.heading": "Usage",
"billing.usage.unlimited": "Unlimited",
"blocks.bubbles.embed.blockCard.tooltip": "Embed a pdf, an iframe, a website...",
"blocks.inputs.button.addItem.ariaLabel": "Add item",
"blocks.inputs.button.buttonSettings.addComparisonButton.label": "Add comparison",
"blocks.inputs.button.buttonSettings.displayCondition.infoText.label": "Only display this item if a condition is met.",
"blocks.inputs.button.buttonSettings.displayCondition.selectOperator.label": "Select an operator",
"blocks.inputs.button.clickToEdit.label": "Click to edit",
"blocks.inputs.button.conditionContent.if.label": "IF",
"blocks.inputs.button.default.label": "Default",
"blocks.inputs.button.else.label": "Else",
"blocks.inputs.button.openSettings.ariaLabel": "Open settings",
"blocks.inputs.button.settings.dynamicData.infoText.label": "If defined, buttons will be dynamically displayed based on what the variable contains.",
"blocks.inputs.button.settings.dynamicData.label": "Dynamic data:",
"blocks.inputs.button.variables.buttons.label": "buttons",
"blocks.inputs.button.variables.display.label": "Display",
"blocks.inputs.date.placeholder.label": "Pick a date...",
"blocks.inputs.date.settings.format.example.label": "i.e",
"blocks.inputs.date.settings.format.label": "Format:",
"blocks.inputs.date.settings.from.label": "From label:",
"blocks.inputs.date.settings.isRange.label": "Is range?",
"blocks.inputs.date.settings.to.label": "To label:",
"blocks.inputs.date.settings.toInputValue.label": "To:",
"blocks.inputs.date.settings.withTime.label": "With time?",
"blocks.inputs.file.collectMultiple.label": "Collect files",
"blocks.inputs.file.collectSingle.label": "Collect file",
"blocks.inputs.file.settings.allowMultiple.label": "Allow multiple files?",
"blocks.inputs.file.settings.clear.label": "Clear button label:",
"blocks.inputs.file.settings.required.label": "Required?",
"blocks.inputs.file.settings.saveMultipleUpload.label": "Save upload URLs in a variable:",
"blocks.inputs.file.settings.saveSingleUpload.label": "Save upload URL in a variable:",
"blocks.inputs.file.settings.skip.label": "Skip button label:",
"blocks.inputs.fileUpload.blockCard.tooltip": "Upload Files",
"blocks.inputs.number.settings.step.label": "Step:",
"blocks.inputs.payment.collect.label": "Coletar",
"blocks.inputs.payment.placeholder.label": "Configure...",
"blocks.inputs.payment.settings.account.label": "Account:",
"blocks.inputs.payment.settings.accountText.label": "{provider} account",
"blocks.inputs.payment.settings.additionalInformation.description.placeholder.label": "A digital product",
"blocks.inputs.payment.settings.additionalInformation.email.label": "Email:",
"blocks.inputs.payment.settings.additionalInformation.label": "Additional information",
"blocks.inputs.payment.settings.additionalInformation.name.label": "Name:",
"blocks.inputs.payment.settings.additionalInformation.phone.label": "Phone number:",
"blocks.inputs.payment.settings.address.city.label": "City:",
"blocks.inputs.payment.settings.address.country.label": "Country:",
"blocks.inputs.payment.settings.address.label": "Address",
"blocks.inputs.payment.settings.address.line.label": "Line {line}:",
"blocks.inputs.payment.settings.address.postalCode.label": "Postal code:",
"blocks.inputs.payment.settings.address.state.label": "State:",
"blocks.inputs.payment.settings.credentials.connectNew.label": "Connect new",
"blocks.inputs.payment.settings.credentials.removeCredentials.label": "Remove credentials",
"blocks.inputs.payment.settings.currency.label": "Currency:",
"blocks.inputs.payment.settings.priceAmount.label": "Price amount:",
"blocks.inputs.payment.settings.provider.label": "Provider:",
"blocks.inputs.payment.settings.stripeConfig.accountName.label": "Account name:",
"blocks.inputs.payment.settings.stripeConfig.findKeys.here.label": "here",
"blocks.inputs.payment.settings.stripeConfig.findKeys.label": "You can find your keys",
"blocks.inputs.payment.settings.stripeConfig.liveKeys.label": "Live keys:",
"blocks.inputs.payment.settings.stripeConfig.testKeys.infoText.label": "Will be used when previewing the bot.",
"blocks.inputs.payment.settings.stripeConfig.testKeys.label": "Test keys:",
"blocks.inputs.payment.settings.stripeConfig.title.label": "Connect Stripe account",
"blocks.inputs.payment.settings.successMessage.label": "Success message:",
"blocks.inputs.phone.settings.defaultCountry.label": "Default country:",
"blocks.inputs.phone.settings.international.placeholder.label": "International",
"blocks.inputs.picture.itemSettings.image.change.label": "Change image",
"blocks.inputs.picture.itemSettings.image.label": "Image:",
"blocks.inputs.picture.itemSettings.image.pick.label": "Pick an image",
"blocks.inputs.picture.itemSettings.title.label": "Title:",
"blocks.inputs.picture.settings.dynamicItems.descriptions.label": "Descriptions:",
"blocks.inputs.picture.settings.dynamicItems.images.label": "Images:",
"blocks.inputs.picture.settings.dynamicItems.label": "Dynamic items?",
"blocks.inputs.picture.settings.dynamicItems.titles.label": "Titles:",
"blocks.inputs.picture.settings.dynamicVariables.display.label": "Display",
"blocks.inputs.picture.settings.dynamicVariables.pictures.label": "pictures",
"blocks.inputs.rating.from.label": "Rate from",
"blocks.inputs.rating.settings.customIcon.label": "Custom icon?",
"blocks.inputs.rating.settings.extremelyLikely.placeholder.label": "Extremely likely",
"blocks.inputs.rating.settings.iconSVG.label": "Icon SVG:",
"blocks.inputs.rating.settings.maximum.label": "Maximum:",
"blocks.inputs.rating.settings.notLikely.placeholder.label": "Not likely at all",
"blocks.inputs.rating.settings.oneClickSubmit.infoText.label": "If enabled, the answer will be submitted as soon as the user clicks on a rating instead of showing the \"Send\" button.",
"blocks.inputs.rating.settings.oneClickSubmit.label": "One click submit",
"blocks.inputs.rating.settings.rateLabel.label": "{rate} label",
"blocks.inputs.rating.settings.type.label": "Type:",
"blocks.inputs.rating.to.label": "to",
"blocks.inputs.settings.button.label": "Button label:",
"blocks.inputs.settings.buttonText.label": "Send",
"blocks.inputs.settings.description.label": "Description:",
"blocks.inputs.settings.displayCondition.label": "Display condition",
"blocks.inputs.settings.input.filterOptions.label": "Filter the options...",
"blocks.inputs.settings.input.placeholder.label": "Input placeholder:",
"blocks.inputs.settings.isSearchable.label": "Is searchable?",
"blocks.inputs.settings.max.label": "Max:",
"blocks.inputs.settings.min.label": "Min:",
"blocks.inputs.settings.multipleChoice.label": "Multiple choice?",
"blocks.inputs.settings.placeholder.label": "Placeholder:",
"blocks.inputs.settings.retryMessage.label": "Retry message:",
"blocks.inputs.settings.saveAnswer.label": "Save the answer in a variable:",
"blocks.inputs.settings.submitButton.label": "Submit button label:",
"blocks.inputs.text.settings.longText.label": "Long text?",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"cancel": "Cancel",
@ -124,13 +221,16 @@
"colorPicker.colorValue.ariaLabel": "Color value",
"colorPicker.pickColor.ariaLabel": "Pick a color",
"confirmModal.defaultTitle": "Are you sure?",
"connect": "Connect",
"copied": "Copied",
"copy": "Copy",
"create": "Create",
"dashboard.header.settingsButton.label": "Settings & Members",
"dashboard.redirectionMessage": "You are being redirected...",
"dashboard.title": "My typebots",
"delete": "Delete",
"downgrade": "Downgrade",
"duplicate": "Duplicate",
"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",
@ -238,6 +338,7 @@
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Show more options",
"folders.typebotButton.unpublish": "Unpublish",
"help": "Help",
"pending": "Pending",
"preview.restartButton.label": "Restart",
"publish.error.label": "Error while publishing typebot",
@ -258,6 +359,7 @@
"remove": "Remove",
"rename": "Rename",
"save": "Save",
"select": "Select",
"share.button.label": "Share",
"share.button.popover.ariaLabel": "Open share popover",
"share.button.popover.collaboratorsFetch.error.label": "Couldn't fetch collaborators",
@ -355,6 +457,11 @@
"upgrade": "Upgrade",
"variables.button.searchInput.placeholder": "Search for a variable",
"variables.button.tooltip": "Insert a variable",
"variables.remove": "Remove variable",
"variables.rename": "Rename variable",
"variables.search": "Search for a variable",
"variables.select": "Select a variable",
"variables.set": "Set",
"video.aspectRatioInput.label": "Aspect ratio",
"video.aspectRatioInput.moreInfoTooltip": "Example: \"16/9\" or \"9/16\"",
"video.maxWidthInput.label": "Max width",

View File

@ -30,6 +30,7 @@
"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",
"add": "Adicionar",
"analytics.completionRateLabel": "Taxa de conclusão",
"analytics.notAvailableLabel": "Não disponível",
"analytics.startsLabel": "Inícios",
@ -112,7 +113,103 @@
"billing.usage.heading": "Uso",
"billing.usage.unlimited": "Ilimitado",
"blocks.bubbles.embed.blockCard.tooltip": "Incorporar pdf, iframe, website...",
"blocks.inputs.button.addItem.ariaLabel": "Adicionar item",
"blocks.inputs.button.buttonSettings.addComparisonButton.label": "Adicionar comparação",
"blocks.inputs.button.buttonSettings.displayCondition.infoText.label": "Apenas exiba esse item se a condição for cumprida.",
"blocks.inputs.button.buttonSettings.displayCondition.selectOperator.label": "Selecione um operador",
"blocks.inputs.button.clickToEdit.label": "Clique para editar",
"blocks.inputs.button.conditionContent.if.label": "SE",
"blocks.inputs.button.default.label": "Padrão",
"blocks.inputs.button.else.label": "Senão",
"blocks.inputs.button.openSettings.ariaLabel": "Abrir configurações",
"blocks.inputs.button.settings.dynamicData.infoText.label": "Se definido, os botões serão exibidos dinamicamente com base no que a variável contém.",
"blocks.inputs.button.settings.dynamicData.label": "Dados dinâmicos:",
"blocks.inputs.button.variables.buttons.label": "botões",
"blocks.inputs.button.variables.display.label": "Exibir",
"blocks.inputs.date.placeholder.label": "Escolha uma data...",
"blocks.inputs.date.settings.format.example.label": "Ex:",
"blocks.inputs.date.settings.format.label": "Formatar:",
"blocks.inputs.date.settings.from.label": "Legenda de:",
"blocks.inputs.date.settings.isRange.label": "Intervalo?",
"blocks.inputs.date.settings.to.label": "Legenda até:",
"blocks.inputs.date.settings.toInputValue.label": "Até:",
"blocks.inputs.date.settings.withTime.label": "Com tempo?",
"blocks.inputs.file.collectMultiple.label": "Coletar arquivos",
"blocks.inputs.file.collectSingle.label": "Coletar arquivo",
"blocks.inputs.file.settings.allowMultiple.label": "Permitir múltiplos arquivos?",
"blocks.inputs.file.settings.clear.label": "Legenda do botão de limpar:",
"blocks.inputs.file.settings.required.label": "Obrigatório?",
"blocks.inputs.file.settings.saveMultipleUpload.label": "Salve as URLs carregadas numa variável:",
"blocks.inputs.file.settings.saveSingleUpload.label": "Salve a URL carregada numa variável:",
"blocks.inputs.file.settings.skip.label": "Legenda do botão de pular:",
"blocks.inputs.fileUpload.blockCard.tooltip": "Anexar arquivos",
"blocks.inputs.number.settings.step.label": "Intervalo",
"blocks.inputs.payment.collect.label": "Coletar",
"blocks.inputs.payment.placeholder.label": "Configure...",
"blocks.inputs.payment.settings.account.label": "Conta:",
"blocks.inputs.payment.settings.accountText.label": "conta {provider}",
"blocks.inputs.payment.settings.additionalInformation.description.placeholder.label": "Um produto digital",
"blocks.inputs.payment.settings.additionalInformation.email.label": "Email:",
"blocks.inputs.payment.settings.additionalInformation.label": "Informação adicional",
"blocks.inputs.payment.settings.additionalInformation.name.label": "Nome:",
"blocks.inputs.payment.settings.additionalInformation.phone.label": "Número de telefone:",
"blocks.inputs.payment.settings.address.city.label": "Cidade:",
"blocks.inputs.payment.settings.address.country.label": "País:",
"blocks.inputs.payment.settings.address.label": "Endereço",
"blocks.inputs.payment.settings.address.line.label": "Linha {line}:",
"blocks.inputs.payment.settings.address.postalCode.label": "Código postal:",
"blocks.inputs.payment.settings.address.state.label": "Estado:",
"blocks.inputs.payment.settings.credentials.connectNew.label": "Nova conexão",
"blocks.inputs.payment.settings.credentials.removeCredentials.label": "Remover credenciais",
"blocks.inputs.payment.settings.currency.label": "Moeda:",
"blocks.inputs.payment.settings.priceAmount.label": "Valor do preço:",
"blocks.inputs.payment.settings.provider.label": "Provedor:",
"blocks.inputs.payment.settings.stripeConfig.accountName.label": "Nome da conta:",
"blocks.inputs.payment.settings.stripeConfig.findKeys.here.label": "aqui",
"blocks.inputs.payment.settings.stripeConfig.findKeys.label": "Você pode encontrar suas chaves",
"blocks.inputs.payment.settings.stripeConfig.liveKeys.label": "Chaves de produção:",
"blocks.inputs.payment.settings.stripeConfig.testKeys.infoText.label": "Será usada durante a visualização do bot.",
"blocks.inputs.payment.settings.stripeConfig.testKeys.label": "Chaves teste:",
"blocks.inputs.payment.settings.stripeConfig.title.label": "Conectar conta Stripe",
"blocks.inputs.payment.settings.successMessage.label": "Mensagem de sucesso:",
"blocks.inputs.phone.settings.defaultCountry.label": "País padrão:",
"blocks.inputs.phone.settings.international.placeholder.label": "Internacional",
"blocks.inputs.picture.itemSettings.image.change.label": "Alterar imagem",
"blocks.inputs.picture.itemSettings.image.label": "Imagem",
"blocks.inputs.picture.itemSettings.image.pick.label": "Escolha uma imagem",
"blocks.inputs.picture.itemSettings.title.label": "Título:",
"blocks.inputs.picture.settings.dynamicItems.descriptions.label": "Descrições:",
"blocks.inputs.picture.settings.dynamicItems.images.label": "Imagens:",
"blocks.inputs.picture.settings.dynamicItems.label": "Itens dinâmicos?",
"blocks.inputs.picture.settings.dynamicItems.titles.label": "Títulos:",
"blocks.inputs.picture.settings.dynamicVariables.display.label": "Exibir",
"blocks.inputs.picture.settings.dynamicVariables.pictures.label": "imagens",
"blocks.inputs.rating.from.label": "Avalie de",
"blocks.inputs.rating.settings.customIcon.label": "Customizar ícone?",
"blocks.inputs.rating.settings.extremelyLikely.placeholder.label": "Extremamente provável",
"blocks.inputs.rating.settings.iconSVG.label": "Ícone SVG:",
"blocks.inputs.rating.settings.maximum.label": "Máximo:",
"blocks.inputs.rating.settings.notLikely.placeholder.label": "Nada provável",
"blocks.inputs.rating.settings.oneClickSubmit.infoText.label": "Se habilitado, a resposta será enviada no momento em que o usuário clicar em uma avaliação ao invés de mostrar o botão de envio",
"blocks.inputs.rating.settings.oneClickSubmit.label": "Enviar em um clique",
"blocks.inputs.rating.settings.rateLabel.label": "Legenda {rate}",
"blocks.inputs.rating.settings.type.label": "Tipo:",
"blocks.inputs.rating.to.label": "a",
"blocks.inputs.settings.button.label": "Legenda do botão:",
"blocks.inputs.settings.buttonText.label": "Enviar",
"blocks.inputs.settings.description.label": "Descrição:",
"blocks.inputs.settings.displayCondition.label": "Condição de exibição",
"blocks.inputs.settings.input.filterOptions.label": "Filtre as opções...",
"blocks.inputs.settings.input.placeholder.label": "Placeholder do input:",
"blocks.inputs.settings.isSearchable.label": "É pesquisável?",
"blocks.inputs.settings.max.label": "Máx:",
"blocks.inputs.settings.min.label": "Mín:",
"blocks.inputs.settings.multipleChoice.label": "Múltipla escolha?",
"blocks.inputs.settings.placeholder.label": "Placeholder:",
"blocks.inputs.settings.retryMessage.label": "Mensagem de nova tentativa:",
"blocks.inputs.settings.saveAnswer.label": "Salve a resposta em uma variável:",
"blocks.inputs.settings.submitButton.label": "Legenda do botão de envio:",
"blocks.inputs.text.settings.longText.label": "Texto longo?",
"blocks.integrations.googleAnalytics.blockCard.tooltip": "Google Analytics",
"blocks.integrations.googleSheets.blockCard.tooltip": "Google Sheets",
"cancel": "Cancelar",
@ -124,13 +221,16 @@
"colorPicker.colorValue.ariaLabel": "Valor da cor",
"colorPicker.pickColor.ariaLabel": "Escolha uma cor",
"confirmModal.defaultTitle": "Tem certeza?",
"connect": "Conectar",
"copied": "Copiado",
"copy": "Copiar",
"create": "Criar",
"dashboard.header.settingsButton.label": "Configurações & Membros",
"dashboard.redirectionMessage": "Você está sendo redirecionado...",
"dashboard.title": "Meus typebots",
"delete": "Apagar",
"downgrade": "Downgrade",
"duplicate": "Duplicar",
"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",
@ -238,6 +338,7 @@
"folders.typebotButton.live": "Live",
"folders.typebotButton.showMoreOptions": "Mostrar mais opções",
"folders.typebotButton.unpublish": "Despublicar",
"help": "Ajuda",
"pending": "Pendente",
"preview.restartButton.label": "Reiniciar",
"publish.error.label": "Erro durante a publicação do typebot",
@ -257,6 +358,7 @@
"remove": "Remover",
"rename": "Renomear",
"save": "Salvar",
"select": "Selecionar",
"share.button.label": "Compartilhar",
"share.button.popover.ariaLabel": "Abrir popover de compartilhamento",
"share.button.popover.collaboratorsFetch.error.label": "Falha ao buscar os colaboradores",
@ -348,6 +450,11 @@
"upgrade": "Upgrade",
"variables.button.searchInput.placeholder": "Procure por uma variável",
"variables.button.tooltip": "Insira uma variável",
"variables.remove": "Remova a variável",
"variables.rename": "Renomeie a variável",
"variables.search": "Pesquise uma variável",
"variables.select": "Selecione uma variável",
"variables.set": "Definir",
"video.aspectRatioInput.label": "Proporção",
"video.aspectRatioInput.moreInfoTooltip": "Exemplo: \"16/9\" ou \"9/16\"",
"video.maxWidthInput.label": "Largura máxima",