More translation in FR & PT (#436)

Related to #210
This commit is contained in:
Baptiste Arnaud
2023-04-06 17:31:23 +02:00
committed by GitHub
parent 44975f9742
commit 75d2a95d08
37 changed files with 751 additions and 286 deletions

View File

@@ -25,10 +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'
type Props = { user: User }
export const ApiTokensList = ({ user }: Props) => {
const scopedT = useScopedI18n('account.apiTokens')
const { showToast } = useToast()
const { apiTokens, isLoading, mutate } = useApiTokens({
userId: user.id,
@@ -55,13 +57,10 @@ export const ApiTokensList = ({ user }: Props) => {
return (
<Stack spacing={4}>
<Heading fontSize="2xl">API tokens</Heading>
<Text>
These tokens allow other apps to control your whole account and
typebots. Be careful!
</Text>
<Heading fontSize="2xl">{scopedT('heading')}</Heading>
<Text>{scopedT('description')}</Text>
<Flex justifyContent="flex-end">
<Button onClick={onCreateOpen}>Create</Button>
<Button onClick={onCreateOpen}>{scopedT('createButton.label')}</Button>
<CreateTokenModal
userId={user.id}
isOpen={isCreateOpen}
@@ -74,8 +73,8 @@ export const ApiTokensList = ({ user }: Props) => {
<Table>
<Thead>
<Tr>
<Th>Name</Th>
<Th w="130px">Created</Th>
<Th>{scopedT('table.nameHeader')}</Th>
<Th w="130px">{scopedT('table.createdHeader')}</Th>
<Th w="0" />
</Tr>
</Thead>
@@ -91,7 +90,7 @@ export const ApiTokensList = ({ user }: Props) => {
variant="outline"
onClick={() => setDeletingId(token.id)}
>
Delete
{scopedT('deleteButton.label')}
</Button>
</Td>
</Tr>
@@ -119,11 +118,14 @@ export const ApiTokensList = ({ user }: Props) => {
onClose={() => setDeletingId(undefined)}
message={
<Text>
The token <strong>{apiTokens?.find(byId(deletingId))?.name}</strong>{' '}
will be permanently deleted, are you sure you want to continue?
{scopedT('deleteConfirmationMessage', {
tokenName: (
<strong>{apiTokens?.find(byId(deletingId))?.name}</strong>
),
})}
</Text>
}
confirmButtonLabel="Delete"
confirmButtonLabel={scopedT('deleteButton.label')}
/>
</Stack>
)

View File

@@ -10,61 +10,65 @@ 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'
const appearanceData = [
{
value: 'light',
label: 'Light',
image: lightModeIllustration,
},
{
value: 'dark',
label: 'Dark',
image: darkModeIllustration,
},
{
value: 'system',
label: 'System',
image: systemModeIllustration,
},
]
import { useScopedI18n } from '@/locales'
type Props = {
defaultValue: string
onChange: (value: string) => void
}
export const AppearanceRadioGroup = ({ defaultValue, onChange }: Props) => (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{appearanceData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
spacing={2}
justifyContent="space-between"
pb={6}
>
<VStack spacing={4}>
<Image
src={option.image}
alt="Theme preview"
style={{ borderRadius: '0.250rem' }}
placeholder="blur"
/>
<Stack>
<Text fontWeight="bold">{option.label}</Text>
</Stack>
</VStack>
export const AppearanceRadioGroup = ({ defaultValue, onChange }: Props) => {
const scopedT = useScopedI18n('account.preferences.appearance')
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)
const appearanceData = [
{
value: 'light',
label: scopedT('lightLabel'),
image: lightModeIllustration,
},
{
value: 'dark',
label: scopedT('darkLabel'),
image: darkModeIllustration,
},
{
value: 'system',
label: scopedT('systemLabel'),
image: systemModeIllustration,
},
]
return (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{appearanceData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
spacing={2}
justifyContent="space-between"
pb={6}
>
<VStack spacing={4}>
<Image
src={option.image}
alt="Theme preview"
style={{ borderRadius: '0.250rem' }}
placeholder="blur"
/>
<Stack>
<Text fontWeight="bold">{option.label}</Text>
</Stack>
</VStack>
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)
}

View File

@@ -1,4 +1,5 @@
import { CopyButton } from '@/components/CopyButton'
import { useScopedI18n } from '@/locales'
import {
Modal,
ModalOverlay,
@@ -31,6 +32,7 @@ export const CreateTokenModal = ({
onClose,
onNewToken,
}: Props) => {
const scopedT = useScopedI18n('account.apiTokens.createModal')
const [name, setName] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [newTokenValue, setNewTokenValue] = useState<string>()
@@ -50,14 +52,14 @@ export const CreateTokenModal = ({
<ModalOverlay />
<ModalContent>
<ModalHeader>
{newTokenValue ? 'Token Created' : 'Create Token'}
{newTokenValue ? scopedT('createdHeading') : scopedT('createHeading')}
</ModalHeader>
<ModalCloseButton />
{newTokenValue ? (
<ModalBody as={Stack} spacing="4">
<Text>
Please copy your token and store it in a safe place.{' '}
<strong>For security reasons we cannot show it again.</strong>
{scopedT('copyInstruction')}
<strong>{scopedT('securityWarning')}</strong>
</Text>
<InputGroup size="md">
<Input readOnly pr="4.5rem" value={newTokenValue} />
@@ -68,12 +70,9 @@ export const CreateTokenModal = ({
</ModalBody>
) : (
<ModalBody as="form" onSubmit={createToken}>
<Text mb="4">
Enter a unique name for your token to differentiate it from other
tokens.
</Text>
<Text mb="4">{scopedT('nameInput.label')}</Text>
<Input
placeholder="I.e. Zapier, Github, Make.com"
placeholder={scopedT('nameInput.placeholder')}
onChange={(e) => setName(e.target.value)}
/>
</ModalBody>
@@ -82,7 +81,7 @@ export const CreateTokenModal = ({
<ModalFooter>
{newTokenValue ? (
<Button onClick={onClose} colorScheme="blue">
Done
{scopedT('doneButton.label')}
</Button>
) : (
<Button
@@ -91,7 +90,7 @@ export const CreateTokenModal = ({
isLoading={isSubmitting}
onClick={createToken}
>
Create token
{scopedT('createButton.label')}
</Button>
)}
</ModalFooter>

View File

@@ -1,4 +1,5 @@
import { MouseIcon, LaptopIcon } from '@/components/icons'
import { useScopedI18n } from '@/locales'
import {
HStack,
Radio,
@@ -9,22 +10,6 @@ import {
} from '@chakra-ui/react'
import { GraphNavigation } from '@typebot.io/prisma'
const graphNavigationData = [
{
value: GraphNavigation.MOUSE,
label: 'Mouse',
description:
'Move by dragging the board and zoom in/out using the scroll wheel',
icon: <MouseIcon boxSize="35px" />,
},
{
value: GraphNavigation.TRACKPAD,
label: 'Trackpad',
description: 'Move the board using 2 fingers and zoom in/out by pinching',
icon: <LaptopIcon boxSize="35px" />,
},
]
type Props = {
defaultValue: string
onChange: (value: string) => void
@@ -32,33 +17,50 @@ type Props = {
export const GraphNavigationRadioGroup = ({
defaultValue,
onChange,
}: Props) => (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{graphNavigationData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
p="6"
spacing={6}
justifyContent="space-between"
>
<VStack spacing={6}>
{option.icon}
<Stack>
<Text fontWeight="bold">{option.label}</Text>
<Text>{option.description}</Text>
</Stack>
</VStack>
}: Props) => {
const scopedT = useScopedI18n('account.preferences.graphNavigation')
const graphNavigationData = [
{
value: GraphNavigation.MOUSE,
label: scopedT('mouse.label'),
description: scopedT('mouse.description'),
icon: <MouseIcon boxSize="35px" />,
},
{
value: GraphNavigation.TRACKPAD,
label: scopedT('trackpad.label'),
description: scopedT('trackpad.description'),
icon: <LaptopIcon boxSize="35px" />,
},
]
return (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{graphNavigationData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
p="6"
spacing={6}
justifyContent="space-between"
>
<VStack spacing={6}>
{option.icon}
<Stack>
<Text fontWeight="bold">{option.label}</Text>
<Text>{option.description}</Text>
</Stack>
</VStack>
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)
}

View File

@@ -5,8 +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'
export const MyAccountForm = () => {
const scopedT = useScopedI18n('account.myAccount')
const { user, updateUser } = useUser()
const [name, setName] = useState(user?.name ?? '')
const [email, setEmail] = useState(user?.email ?? '')
@@ -41,10 +43,10 @@ export const MyAccountForm = () => {
leftIcon={<UploadIcon />}
onFileUploaded={handleFileUploaded}
>
Change photo
{scopedT('changePhotoButton.label')}
</UploadButton>
<Text color="gray.500" fontSize="sm">
.jpg or.png, max 1MB
{scopedT('changePhotoButton.specification')}
</Text>
</Stack>
</HStack>
@@ -52,17 +54,17 @@ export const MyAccountForm = () => {
<TextInput
defaultValue={name}
onChange={handleNameChange}
label="Name:"
label={scopedT('nameInput.label')}
withVariableButton={false}
debounceTimeout={0}
/>
<Tooltip label="Updating email is not available. Contact the support if you want to change it.">
<Tooltip label={scopedT('emailInput.disabledTooltip')}>
<span>
<TextInput
type="email"
defaultValue={email}
onChange={handleEmailChange}
label="Email address:"
label={scopedT('emailInput.label')}
withVariableButton={false}
debounceTimeout={0}
isDisabled

View File

@@ -4,8 +4,10 @@ import React, { useEffect } from 'react'
import { GraphNavigationRadioGroup } from './GraphNavigationRadioGroup'
import { AppearanceRadioGroup } from './AppearanceRadioGroup'
import { useUser } from '../hooks/useUser'
import { useScopedI18n } from '@/locales'
export const UserPreferencesForm = () => {
const scopedT = useScopedI18n('account.preferences')
const { colorMode, setColorMode } = useColorMode()
const { user, updateUser } = useUser()
@@ -26,14 +28,14 @@ export const UserPreferencesForm = () => {
return (
<Stack spacing={12}>
<Stack spacing={6}>
<Heading size="md">Editor Navigation</Heading>
<Heading size="md">{scopedT('graphNavigation.heading')}</Heading>
<GraphNavigationRadioGroup
defaultValue={user?.graphNavigation ?? GraphNavigation.TRACKPAD}
onChange={changeGraphNavigation}
/>
</Stack>
<Stack spacing={6}>
<Heading size="md">Appearance</Heading>
<Heading size="md">{scopedT('appearance.heading')}</Heading>
<AppearanceRadioGroup
defaultValue={
user?.preferredAppAppearance