translation added
This commit is contained in:
@@ -9,6 +9,7 @@ import DocIllustration from './DocIllustration';
|
||||
import Section from './Section';
|
||||
import Stat from './Stat';
|
||||
import StartButton from './StartButton';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface HeroProps {
|
||||
onStart: () => void;
|
||||
@@ -17,6 +18,23 @@ interface HeroProps {
|
||||
|
||||
const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
const isMobile = useIsMobile();
|
||||
const t = useTranslations('Hero');
|
||||
const tStats = useTranslations('Stats');
|
||||
|
||||
const getTranslatedLabel = (label: string) => {
|
||||
switch (label) {
|
||||
case 'Detection accuracy':
|
||||
return tStats('accuracy');
|
||||
case 'Documents checked':
|
||||
return tStats('documents');
|
||||
case 'Supported formats':
|
||||
return tStats('formats');
|
||||
case 'Report turnaround':
|
||||
return tStats('turnaround');
|
||||
default:
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', overflow: 'hidden', background: C.bg }}>
|
||||
@@ -71,7 +89,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
>
|
||||
{/* Text block */}
|
||||
<div style={{ maxWidth: 600, flex: 1 }}>
|
||||
<Badge>Academic Integrity Platform</Badge>
|
||||
<Badge>{t('badge')}</Badge>
|
||||
|
||||
<motion.div variants={stagger} initial="hidden" animate="visible">
|
||||
<motion.h1
|
||||
@@ -86,7 +104,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
marginBottom: 4,
|
||||
}}
|
||||
>
|
||||
Is Your Work
|
||||
{t('mainHeading')}
|
||||
</motion.h1>
|
||||
<motion.h1
|
||||
variants={fadeUp(0.1)}
|
||||
@@ -101,7 +119,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
marginBottom: 28,
|
||||
}}
|
||||
>
|
||||
Truly Original?
|
||||
{t('mainHeadingItalic')}
|
||||
</motion.h1>
|
||||
</motion.div>
|
||||
|
||||
@@ -117,10 +135,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
marginBottom: 40,
|
||||
}}
|
||||
>
|
||||
Plagiarism is presenting someone else‘s ideas or words as
|
||||
your own. In academia and professional life, it carries serious
|
||||
consequences. Our platform detects it in seconds — so you can
|
||||
submit with full confidence.
|
||||
{t('description')}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
@@ -142,7 +157,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
fontFamily: "'DM Mono', monospace",
|
||||
}}
|
||||
>
|
||||
Certificate issued within 24h
|
||||
{t('certificateNote')}
|
||||
</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
@@ -165,9 +180,15 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||
borderTop: `1px solid ${C.border}`,
|
||||
}}
|
||||
>
|
||||
{STATS.map((s) => (
|
||||
<Stat key={s.label} value={s.value} label={s.label} />
|
||||
))}
|
||||
{STATS.map((s) => {
|
||||
return (
|
||||
<Stat
|
||||
key={s.label}
|
||||
value={s.value}
|
||||
label={getTranslatedLabel(s.label)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
</Section>
|
||||
</div>
|
||||
|
||||
@@ -5,51 +5,82 @@ import { C } from '../tokens';
|
||||
import { INFO_CARDS } from '../constants';
|
||||
import InfoCard from './InfoCard';
|
||||
import Section from './Section';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
const InfoSection: FC = () => (
|
||||
<div style={{ background: C.surfaceWarm }}>
|
||||
<Section style={{ paddingTop: 96, paddingBottom: 96 }}>
|
||||
{/* Heading */}
|
||||
<motion.div
|
||||
variants={fadeUp()}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
style={{ marginBottom: 48 }}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "'DM Mono', monospace",
|
||||
fontSize: 11,
|
||||
color: C.accent,
|
||||
letterSpacing: '0.14em',
|
||||
textTransform: 'uppercase',
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
Why It Matters
|
||||
</p>
|
||||
<h2
|
||||
style={{
|
||||
fontFamily: "'Playfair Display', serif",
|
||||
fontSize: 'clamp(26px, 3.5vw, 40px)',
|
||||
fontWeight: 700,
|
||||
color: C.text,
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
Understanding Plagiarism
|
||||
</h2>
|
||||
</motion.div>
|
||||
const InfoSection: FC = () => {
|
||||
const t = useTranslations('InfoSection');
|
||||
const tCards = useTranslations('InfoCards');
|
||||
|
||||
{/* Cards */}
|
||||
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
||||
{INFO_CARDS.map((card) => (
|
||||
<InfoCard key={card.title} {...card} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
);
|
||||
const getTranslatedCard = (card: (typeof INFO_CARDS)[0]) => {
|
||||
switch (card.title) {
|
||||
case 'What is Plagiarism?':
|
||||
return {
|
||||
...card,
|
||||
title: tCards('card1Title'),
|
||||
text: tCards('card1Desc'),
|
||||
};
|
||||
case 'Why Check Your Document?':
|
||||
return {
|
||||
...card,
|
||||
title: tCards('card2Title'),
|
||||
text: tCards('card2Desc'),
|
||||
};
|
||||
case 'What You Get':
|
||||
return {
|
||||
...card,
|
||||
title: tCards('card3Title'),
|
||||
text: tCards('card3Desc'),
|
||||
};
|
||||
default:
|
||||
return card;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ background: C.surfaceWarm }}>
|
||||
<Section style={{ paddingTop: 96, paddingBottom: 96 }}>
|
||||
{/* Heading */}
|
||||
<motion.div
|
||||
variants={fadeUp()}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
style={{ marginBottom: 48 }}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "'DM Mono', monospace",
|
||||
fontSize: 11,
|
||||
color: C.accent,
|
||||
letterSpacing: '0.14em',
|
||||
textTransform: 'uppercase',
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
{t('label')}
|
||||
</p>
|
||||
<h2
|
||||
style={{
|
||||
fontFamily: "'Playfair Display', serif",
|
||||
fontSize: 'clamp(26px, 3.5vw, 40px)',
|
||||
fontWeight: 700,
|
||||
color: C.text,
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
{t('heading')}
|
||||
</h2>
|
||||
</motion.div>
|
||||
|
||||
{/* Cards */}
|
||||
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
||||
{INFO_CARDS.map((card) => (
|
||||
<InfoCard key={card.title} {...getTranslatedCard(card)} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoSection;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { useState, type FC } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { C } from '../tokens';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface StartButtonProps {
|
||||
onClick: () => void;
|
||||
@@ -10,6 +11,7 @@ interface StartButtonProps {
|
||||
|
||||
const StartButton: FC<StartButtonProps> = ({ onClick, small = false }) => {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const t = useTranslations('Common');
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
@@ -35,7 +37,7 @@ const StartButton: FC<StartButtonProps> = ({ onClick, small = false }) => {
|
||||
: `0 2px 8px ${C.accent}22`,
|
||||
}}
|
||||
>
|
||||
Start Checking →
|
||||
{t('startButton')}
|
||||
</motion.button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { fadeUp } from '../animations';
|
||||
import { C } from '../tokens';
|
||||
import { STEPS } from '../constants';
|
||||
import type { Step } from '../types';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface StepCardProps {
|
||||
step: Step;
|
||||
@@ -14,6 +15,45 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const inView = useInView(ref, { once: true, margin: '-50px' });
|
||||
const isLast = index === STEPS.length - 1;
|
||||
const t = useTranslations('Steps');
|
||||
|
||||
const getStepTitle = (num: string) => {
|
||||
switch (num) {
|
||||
case '01':
|
||||
return t('step1Title');
|
||||
case '02':
|
||||
return t('step2Title');
|
||||
case '03':
|
||||
return t('step3Title');
|
||||
case '04':
|
||||
return t('step4Title');
|
||||
case '05':
|
||||
return t('step5Title');
|
||||
case '06':
|
||||
return t('step6Title');
|
||||
default:
|
||||
return step.title;
|
||||
}
|
||||
};
|
||||
|
||||
const getStepDesc = (num: string) => {
|
||||
switch (num) {
|
||||
case '01':
|
||||
return t('step1Desc');
|
||||
case '02':
|
||||
return t('step2Desc');
|
||||
case '03':
|
||||
return t('step3Desc');
|
||||
case '04':
|
||||
return t('step4Desc');
|
||||
case '05':
|
||||
return t('step5Desc');
|
||||
case '06':
|
||||
return t('step6Desc');
|
||||
default:
|
||||
return step.desc;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -87,7 +127,7 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
{getStepTitle(step.num)}
|
||||
</h3>
|
||||
</div>
|
||||
<p
|
||||
@@ -98,7 +138,7 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{step.desc}
|
||||
{getStepDesc(step.num)}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import Section from './Section';
|
||||
import StartButton from './StartButton';
|
||||
import StepCard from './StepCard';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface StepsSectionProps {
|
||||
stepsRef: React.RefObject<HTMLDivElement | null>;
|
||||
@@ -15,6 +16,7 @@ interface StepsSectionProps {
|
||||
|
||||
const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||
const isMobile = useIsMobile();
|
||||
const t = useTranslations('StepsSection');
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -44,7 +46,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
Process
|
||||
{t('label')}
|
||||
</p>
|
||||
<h2
|
||||
style={{
|
||||
@@ -55,7 +57,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
How It Works
|
||||
{t('heading')}
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
@@ -65,7 +67,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||
lineHeight: 1.75,
|
||||
}}
|
||||
>
|
||||
Six simple steps from upload to certified report.
|
||||
{t('description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -112,10 +114,10 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
Ready to verify your document?
|
||||
{t('ctaHeading')}
|
||||
</h3>
|
||||
<p style={{ color: C.textMuted, fontSize: 13 }}>
|
||||
Get your originality certificate in under 24 hours.
|
||||
{t('ctaDescription')}
|
||||
</p>
|
||||
</div>
|
||||
<StartButton onClick={onScrollTop} />
|
||||
|
||||
@@ -2,10 +2,32 @@ import type { FC } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { C } from '../tokens';
|
||||
import { TICKER_ITEMS } from '../constants';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
const Ticker: FC = () => {
|
||||
const t = useTranslations('Ticker');
|
||||
const doubled = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
||||
|
||||
const getTranslatedItem = (item: string, index: number) => {
|
||||
const itemIndex = index % TICKER_ITEMS.length;
|
||||
switch (itemIndex) {
|
||||
case 0:
|
||||
return t('item1');
|
||||
case 1:
|
||||
return t('item2');
|
||||
case 2:
|
||||
return t('item3');
|
||||
case 3:
|
||||
return t('item4');
|
||||
case 4:
|
||||
return t('item5');
|
||||
case 5:
|
||||
return t('item6');
|
||||
default:
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -37,7 +59,7 @@ const Ticker: FC = () => {
|
||||
color: i % 3 === 0 ? C.textMid : C.textMuted,
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
{getTranslatedItem(item, i)}
|
||||
<span style={{ marginLeft: 56, color: C.accent, opacity: 0.3 }}>
|
||||
✦
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user