feat: add translations to landing (#18)

This commit is contained in:
RustamRu 2024-11-16 22:43:51 +03:00
parent 425a47b33c
commit 3cdb9a997e
11 changed files with 84 additions and 46 deletions

View File

@ -1,4 +1,5 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
MdEco, MdEco,
MdMiscellaneousServices, MdMiscellaneousServices,
@ -10,33 +11,39 @@ import { CtaButton, PageSection } from '../';
import { ListItem } from './ListItem'; import { ListItem } from './ListItem';
export const BenefitsSection: FC = () => { export const BenefitsSection: FC = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.landing.benefits-section',
});
const listData = [
{
Icon: MdEco,
children: t('list.0'),
},
{
Icon: MdMiscellaneousServices,
children: t('list.1'),
},
{
Icon: MdPlace,
children: t('list.2'),
},
{
Icon: MdHandshake,
children: t('list.3'),
},
];
return ( return (
<PageSection> <PageSection>
<VStack w='full' spacing={2}> <VStack w='full' spacing={2}>
<Heading as='h2'>Преимущества экологичной автомойки</Heading> <Heading as='h2'>{t('heading')}</Heading>
<Text> <Text>
Откройте для себя преимущества наших услуг по химчистке автомобилей {t('description')}
</Text> </Text>
</VStack> </VStack>
<List display='flex' flexDirection='column' spacing={3}> <List display='flex' flexDirection='column' spacing={3}>
{[ {listData.map((props, i) => (
{
Icon: MdEco,
children: 'Экологически безопасные продукты',
},
{
Icon: MdMiscellaneousServices,
children: 'Быстрое и эффективное обслуживание',
},
{
Icon: MdPlace,
children: 'Удобный мобильный доступ',
},
{
Icon: MdHandshake,
children: 'Надежный и заслуживающий доверия',
},
].map((props, i) => (
<ListItem key={i} {...props} /> <ListItem key={i} {...props} />
))} ))}
</List> </List>

View File

@ -2,7 +2,7 @@ import React, { FC, PropsWithChildren } from 'react';
import { ListIcon, ListItem as ChakraListItem } from '@chakra-ui/react'; import { ListIcon, ListItem as ChakraListItem } from '@chakra-ui/react';
import { IconType } from 'react-icons'; import { IconType } from 'react-icons';
type ListItemProps = PropsWithChildren & { export type ListItemProps = PropsWithChildren & {
Icon: IconType; Icon: IconType;
}; };

View File

@ -1 +1 @@
export { ListItem } from './ListItem'; export { type ListItemProps, ListItem } from './ListItem';

View File

@ -1,12 +1,20 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { ButtonProps, Button } from '@chakra-ui/react'; import { ButtonProps, Button } from '@chakra-ui/react';
import { URLs } from '../../../__data__/urls'; import { URLs } from '../../../__data__/urls';
export const CtaButton: FC<ButtonProps> = (props) => { export const CtaButton: FC<ButtonProps> = (props) => {
const { t } = useTranslation();
return ( return (
<Button as={RouterLink} to={URLs.orderForm.getUrl()} colorScheme='primary' {...props}> <Button
Сделать заказ as={RouterLink}
to={URLs.orderForm.getUrl()}
colorScheme='primary'
{...props}
>
{t('dry-wash.landing.make-order-button')}
</Button> </Button>
); );
}; };

View File

@ -1,8 +1,11 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Text } from '@chakra-ui/react'; import { Text } from '@chakra-ui/react';
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
export const Copyright: FC = () => { export const Copyright: FC = () => {
return <Text color='whiteAlpha.500'>&copy; {currentYear} DryMaster. Все права защищены</Text>; const { t } = useTranslation();
return <Text color='whiteAlpha.500'>{t('dry-wash.landing.footer.copyright', { currentYear })}</Text>;
}; };

View File

@ -1,20 +1,27 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, List, ListItem } from '@chakra-ui/react'; import { Link, List, ListItem } from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { SiteLogo, PageSection } from '../'; import { SiteLogo, PageSection } from '../';
import { Copyright } from './Copyright'; import { Copyright } from './Copyright';
export const Footer: FC = () => { export const Footer: FC = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.landing.footer.links',
});
const listData = [
{ to: '#', label: t('privacy-policy') },
{ to: '#', label: t('service-terms') },
{ to: '#', label: t('faq') },
];
return ( return (
<PageSection as='footer' py={5} bg='gray.700' color='white'> <PageSection as='footer' py={5} bg='gray.700' color='white'>
<SiteLogo /> <SiteLogo />
<Copyright /> <Copyright />
<List spacing={2}> <List spacing={2}>
{[ {listData.map(({ to, label }, i) => (
{ to: '#', label: 'Политика конфиденциальности' },
{ to: '#', label: 'Условия обслуживания' },
{ to: '#', label: 'FAQ' },
].map(({ to, label }, i) => (
<ListItem key={i}> <ListItem key={i}>
<Link as={RouterLink} to={to}> <Link as={RouterLink} to={to}>
{label} {label}

View File

@ -1,4 +1,5 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Heading, Text, Center, VStack, BoxProps } from '@chakra-ui/react'; import { Box, Heading, Text, Center, VStack, BoxProps } from '@chakra-ui/react';
import { DemoVideoPosterImg } from '../../../assets/images'; import { DemoVideoPosterImg } from '../../../assets/images';
import { CtaButton, SiteLogo, PageSection } from '../'; import { CtaButton, SiteLogo, PageSection } from '../';
@ -6,6 +7,10 @@ import { CtaButton, SiteLogo, PageSection } from '../';
type HeroSectionProps = Pick<BoxProps, 'flexShrink'>; type HeroSectionProps = Pick<BoxProps, 'flexShrink'>;
export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => { export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.landing.hero-section',
});
return ( return (
<Box flexShrink={flexShrink} as='header' pos='relative' zIndex={0}> <Box flexShrink={flexShrink} as='header' pos='relative' zIndex={0}>
<Box <Box
@ -41,19 +46,18 @@ export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => {
color='white' color='white'
__css={{ textWrap: 'balance' }} __css={{ textWrap: 'balance' }}
> >
Оживите свою поездку с помощью экологически чистого ухода! {t('headline')}
</Heading> </Heading>
<Text <Text
textAlign='center' textAlign='center'
__css={{ textWrap: 'balance' }} __css={{ textWrap: 'balance' }}
color='white' color='white'
> >
Ощутите максимальное удобство сухой мойки автомобилей, созданной для {t('description')}
того, чтобы планета стала чище
</Text> </Text>
</VStack> </VStack>
<CtaButton size='lg' /> <CtaButton size='lg' />
</PageSection> </PageSection>
</Box> </Box>
); );
}; };

View File

@ -1,10 +1,12 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Image } from '@chakra-ui/react'; import { Image } from '@chakra-ui/react';
import { LogoSvg } from '../../../assets/icons'; import { LogoSvg } from '../../../assets/icons';
export const SiteLogo: FC = () => { export const SiteLogo: FC = () => {
return <Image src={LogoSvg} alt='Логотип компании "Сухой мастер"' w={40} />; const { t } = useTranslation();
return <Image src={LogoSvg} alt={t('dry-wash.landing.site-logo')} w={40} />;
}; };
// todo: add i18n for alt // todo: replace Image by SVG React component
// todo: replace Image by SVG React component

View File

@ -1,4 +1,5 @@
import React, { FC, useEffect, useState } from 'react'; import React, { FC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
Tab, Tab,
TabList, TabList,
@ -7,13 +8,17 @@ import {
Tabs, Tabs,
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { mockReviews } from '../../../../mocks/landing'; import { mockReview } from '../../../../mocks/landing';
import { Review } from '../../../../models';
import { ReviewCard } from './ReviewCard'; import { ReviewCard } from './ReviewCard';
const reviewsCount = mockReviews.length;
const SLIDE_CHANGE_INTERVAL = 5000; const SLIDE_CHANGE_INTERVAL = 5000;
export const ReviewsSlider: FC = () => { export const ReviewsSlider: FC = () => {
const { i18n } = useTranslation();
const mockReviews: Review.View[] = mockReview.getReviewsByLocale(i18n.language);
const reviewsCount = mockReviews.length;
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const [isSlideShowStopped, setIsSlideShowStopped] = useState(false); const [isSlideShowStopped, setIsSlideShowStopped] = useState(false);

View File

@ -1,15 +1,17 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { import { useTranslation } from 'react-i18next';
Heading, import { Heading, HStack } from '@chakra-ui/react';
HStack,
} from '@chakra-ui/react';
import { CtaButton, PageSection } from '../'; import { CtaButton, PageSection } from '../';
import { ReviewsSlider } from './ReviewsSlider'; import { ReviewsSlider } from './ReviewsSlider';
export const SocialProofSection: FC = () => { export const SocialProofSection: FC = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.landing.social-proof-section',
});
return ( return (
<PageSection> <PageSection>
<Heading as='h2'>Нас выбирают</Heading> <Heading as='h2'>{t('heading')}</Heading>
<ReviewsSlider /> <ReviewsSlider />
<HStack w='full' justify='flex-end'> <HStack w='full' justify='flex-end'>
<CtaButton /> <CtaButton />

View File

@ -2,13 +2,13 @@ import React from 'react';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { Button } from '@chakra-ui/react'; import { Button } from '@chakra-ui/react';
import { URLs } from '../../__data__/urls'; import { URLs } from '../../__data__/urls';
import { mockOrders } from '../../mocks/landing'; import { mockOrder } from '../../mocks/landing';
const Page = () => { const Page = () => {
return ( return (
<> <>
<h1>Order form</h1> <h1>Order form</h1>
{mockOrders.map(({ id }) => ( {mockOrder.orders.map(({ id }) => (
<Button key={id} as={RouterLink} to={URLs.orderView.getUrl(id)}> <Button key={id} as={RouterLink} to={URLs.orderView.getUrl(id)}>
Посмотреть заказ {id} Посмотреть заказ {id}
</Button> </Button>