feat: add translations to landing (#18)
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good

This commit is contained in:
RustamRu 2024-11-16 22:43:51 +03:00
parent 752fe72786
commit c92c879b31
11 changed files with 84 additions and 46 deletions

View File

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

View File

@ -2,7 +2,7 @@ import React, { FC, PropsWithChildren } from 'react';
import { ListIcon, ListItem as ChakraListItem } from '@chakra-ui/react';
import { IconType } from 'react-icons';
type ListItemProps = PropsWithChildren & {
export type ListItemProps = PropsWithChildren & {
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 { useTranslation } from 'react-i18next';
import { Link as RouterLink } from 'react-router-dom';
import { ButtonProps, Button } from '@chakra-ui/react';
import { URLs } from '../../../__data__/urls';
export const CtaButton: FC<ButtonProps> = (props) => {
const { t } = useTranslation();
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>
);
};

View File

@ -1,8 +1,11 @@
import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Text } from '@chakra-ui/react';
const currentYear = new Date().getFullYear();
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 { useTranslation } from 'react-i18next';
import { Link, List, ListItem } from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom';
import { SiteLogo, PageSection } from '../';
import { Copyright } from './Copyright';
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 (
<PageSection as='footer' py={5} bg='gray.700' color='white'>
<SiteLogo />
<Copyright />
<List spacing={2}>
{[
{ to: '#', label: 'Политика конфиденциальности' },
{ to: '#', label: 'Условия обслуживания' },
{ to: '#', label: 'FAQ' },
].map(({ to, label }, i) => (
{listData.map(({ to, label }, i) => (
<ListItem key={i}>
<Link as={RouterLink} to={to}>
{label}

View File

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

View File

@ -1,10 +1,12 @@
import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Image } from '@chakra-ui/react';
import { LogoSvg } from '../../../assets/icons';
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 { useTranslation } from 'react-i18next';
import {
Tab,
TabList,
@ -7,13 +8,17 @@ import {
Tabs,
Text,
} from '@chakra-ui/react';
import { mockReviews } from '../../../../mocks/landing';
import { mockReview } from '../../../../mocks/landing';
import { Review } from '../../../../models';
import { ReviewCard } from './ReviewCard';
const reviewsCount = mockReviews.length;
const SLIDE_CHANGE_INTERVAL = 5000;
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 [isSlideShowStopped, setIsSlideShowStopped] = useState(false);

View File

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

View File

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