fix: integrate deepseek, resolve silent google api errors, fix google cloud keys
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2s
This commit is contained in:
664
wordly.art---traduction-de-documents/src/App.tsx
Normal file
664
wordly.art---traduction-de-documents/src/App.tsx
Normal file
@@ -0,0 +1,664 @@
|
||||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
FileText,
|
||||
Layers,
|
||||
MousePointer2,
|
||||
Download,
|
||||
Globe,
|
||||
Cpu,
|
||||
CheckCircle2,
|
||||
ArrowRight,
|
||||
Layout,
|
||||
PieChart,
|
||||
FileSpreadsheet,
|
||||
Presentation,
|
||||
MessageSquare,
|
||||
ShieldCheck,
|
||||
Zap,
|
||||
Menu,
|
||||
X,
|
||||
Languages,
|
||||
Monitor,
|
||||
Eye,
|
||||
Lock,
|
||||
Clock
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const Navbar = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const toggleLanguage = () => {
|
||||
const nextLang = i18n.language === 'fr' ? 'en' : 'fr';
|
||||
i18n.changeLanguage(nextLang);
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="fixed top-4 left-1/2 -translate-x-1/2 w-[calc(100%-2rem)] max-w-7xl z-50">
|
||||
<div className="glass rounded-3xl px-6 py-4 shadow-warm">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-primary via-secondary to-accent rounded-xl flex items-center justify-center shadow-lg shadow-primary/10">
|
||||
<span className="text-white font-black text-xl">W</span>
|
||||
</div>
|
||||
<div className="flex flex-col -gap-1">
|
||||
<span className="text-xl font-black tracking-tight text-primary">WORDLY<span className="text-secondary">.art</span></span>
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-gray-400 leading-none">Translation Suite</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex items-center space-x-8">
|
||||
<a href="#features" className="text-gray-500 hover:text-primary transition-colors text-sm font-semibold tracking-tight">{t('nav.why')}</a>
|
||||
<a href="#formats" className="text-gray-500 hover:text-primary transition-colors text-sm font-semibold tracking-tight">{t('nav.formats')}</a>
|
||||
<a href="#pricing" className="text-gray-500 hover:text-primary transition-colors text-sm font-semibold tracking-tight">{t('nav.pricing')}</a>
|
||||
<div className="h-4 w-[1px] bg-gray-200"></div>
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="text-[10px] font-black bg-gray-50 px-2 py-1 rounded border border-gray-100 text-primary hover:bg-gray-100 transition-colors uppercase"
|
||||
>
|
||||
{i18n.language === 'fr' ? 'EN' : 'FR'}
|
||||
</button>
|
||||
<a href="#" className="text-primary hover:opacity-80 transition-opacity text-sm font-bold tracking-tight">{t('nav.login')}</a>
|
||||
<a href="#" className="bg-primary text-white px-6 py-2.5 rounded-2xl text-sm font-bold hover:shadow-lg hover:shadow-primary/20 transition-all active:scale-95">
|
||||
{t('nav.startFree')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="md:hidden">
|
||||
<button onClick={() => setIsOpen(!isOpen)} className="text-primary p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
{isOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="md:hidden bg-white border-b border-gray-100 overflow-hidden"
|
||||
>
|
||||
<div className="px-6 pt-4 pb-10 space-y-4 text-center">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="w-full py-2 text-sm font-black text-[#004777] bg-gray-50 rounded-xl"
|
||||
>
|
||||
{t('common.language', { lang: i18n.language === 'fr' ? 'English' : 'Français' })}
|
||||
<span className="ml-2 opacity-50 uppercase">{i18n.language === 'fr' ? 'en' : 'fr'}</span>
|
||||
</button>
|
||||
<a href="#features" onClick={() => setIsOpen(false)} className="block py-3 text-lg font-bold text-gray-700">{t('nav.why')}</a>
|
||||
<a href="#formats" onClick={() => setIsOpen(false)} className="block py-3 text-lg font-bold text-gray-700">{t('nav.formats')}</a>
|
||||
<a href="#pricing" onClick={() => setIsOpen(false)} className="block py-3 text-lg font-bold text-gray-700">{t('nav.pricing')}</a>
|
||||
<div className="pt-6 grid grid-cols-2 gap-4">
|
||||
<a href="#" className="flex items-center justify-center py-4 text-[#004777] font-black border-2 border-[#004777]/10 rounded-2xl">{t('nav.login')}</a>
|
||||
<a href="#" className="flex items-center justify-center py-4 bg-[#004777] text-white rounded-2xl font-black">{t('nav.startFree')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
const Hero = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section className="relative pt-40 pb-24 lg:pt-56 lg:pb-40 px-4 overflow-hidden bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-secondary/5 via-white to-white">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full max-w-7xl pointer-events-none -z-10">
|
||||
<div className="absolute top-[-10%] right-[-10%] w-[600px] h-[600px] bg-secondary/10 blur-[120px] rounded-full"></div>
|
||||
<div className="absolute bottom-[20%] left-[-10%] w-[500px] h-[500px] bg-accent/10 blur-[120px] rounded-full"></div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto grid lg:grid-cols-12 gap-16 items-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||
className="lg:col-span-7"
|
||||
>
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 mb-8 text-[10px] font-black tracking-[0.25em] text-secondary uppercase bg-secondary/5 rounded-full border border-secondary/10">
|
||||
<Languages size={12} /> {t('hero.tag')}
|
||||
</div>
|
||||
<h1 className="text-6xl lg:text-[5.5rem] font-black tracking-tight text-primary mb-8 leading-[0.9] lg:max-w-3xl">
|
||||
{t('hero.titleLine1')}<br />
|
||||
<span className="gradient-text">{t('hero.titleLine2')}</span>
|
||||
</h1>
|
||||
<p className="text-xl text-gray-500 mb-10 max-w-xl leading-relaxed font-medium">
|
||||
{t('hero.description')}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-5 mb-16">
|
||||
<button className="flex items-center justify-center gap-3 bg-primary text-white px-10 py-5 rounded-2xl text-lg font-bold hover:scale-[1.02] active:scale-95 transition-all shadow-xl shadow-primary/20">
|
||||
{t('hero.ctaMain')} <ArrowRight size={20} className="text-accent" />
|
||||
</button>
|
||||
<button className="flex items-center justify-center gap-2 text-primary font-bold text-lg hover:translate-x-1 transition-transform bg-white border border-gray-200 px-8 py-5 rounded-2xl shadow-sm hover:shadow-md">
|
||||
{t('hero.ctaSec')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-8 items-center pt-8 border-t border-gray-100">
|
||||
{[
|
||||
{ icon: <Clock size={16} className="text-accent" />, text: t('hero.deleted') },
|
||||
{ icon: <ShieldCheck size={16} className="text-secondary" />, text: t('hero.noHidden') },
|
||||
{ icon: <Eye size={16} className="text-primary" />, text: t('hero.preview') }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="flex items-center gap-2.5 text-[10px] font-black uppercase tracking-widest text-gray-400">
|
||||
{item.icon} {item.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 40 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
transition={{ duration: 1, ease: [0.16, 1, 0.3, 1], delay: 0.2 }}
|
||||
className="lg:col-span-5 relative"
|
||||
>
|
||||
<div className="absolute -inset-4 bg-gradient-to-tr from-primary/10 via-secondary/10 to-accent/10 rounded-[40px] blur-2xl opacity-50"></div>
|
||||
<div className="relative bg-white p-4 rounded-[36px] shadow-2xl border border-white/50">
|
||||
<div className="aspect-[4/5] lg:aspect-[4/3] bg-[#F1F5F9] rounded-[28px] relative overflow-hidden group">
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center p-8">
|
||||
<div className="relative mb-10 inline-block">
|
||||
<div className="absolute inset-0 bg-secondary/10 blur-3xl rounded-full scale-150"></div>
|
||||
<motion.div
|
||||
animate={{ rotate: [12, -12, 12] }}
|
||||
transition={{ duration: 10, repeat: Infinity, ease: "easeInOut" }}
|
||||
className="relative z-10 w-28 h-28 bg-white rounded-[32px] shadow-2xl flex items-center justify-center"
|
||||
>
|
||||
<FileText size={56} className="text-primary" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{ rotate: [-12, 12, -12] }}
|
||||
transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }}
|
||||
className="absolute -bottom-4 -right-4 w-14 h-14 bg-secondary rounded-2xl shadow-lg flex items-center justify-center text-white"
|
||||
>
|
||||
<Languages size={24} />
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 max-w-[200px] mx-auto">
|
||||
<div className="h-1.5 w-full bg-gray-200 rounded-full"></div>
|
||||
<div className="h-1.5 w-[80%] bg-gray-200 rounded-full mx-auto opacity-60"></div>
|
||||
<div className="h-1.5 w-[60%] bg-gray-200 rounded-full mx-auto opacity-40"></div>
|
||||
<div className="pt-6">
|
||||
<span className="text-2xl font-black text-primary tracking-tight">WORDLY<span className="text-secondary">.art</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-6 right-6 p-4 glass shadow-lg rounded-2xl text-[10px] font-bold">
|
||||
<div className="flex items-center gap-2 text-secondary"><CheckCircle2 size={14} /> {t('hero.formattedOk')}</div>
|
||||
</div>
|
||||
<div className="absolute bottom-6 left-6 p-4 bg-primary text-white shadow-xl rounded-2xl text-[10px] font-bold ring-1 ring-white/20">
|
||||
<div className="flex items-center gap-2"><Zap size={14} className="text-accent" /> {t('hero.aiActive')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const Steps = () => {
|
||||
const { t } = useTranslation();
|
||||
const steps = [
|
||||
{
|
||||
num: t('steps.step1.num'),
|
||||
title: t('steps.step1.title'),
|
||||
desc: t('steps.step1.desc'),
|
||||
icon: <MousePointer2 size={28} />,
|
||||
gradient: "from-blue-500/10 to-primary/10",
|
||||
iconColor: "text-primary"
|
||||
},
|
||||
{
|
||||
num: t('steps.step2.num'),
|
||||
title: t('steps.step2.title'),
|
||||
desc: t('steps.step2.desc'),
|
||||
icon: <Cpu size={28} />,
|
||||
gradient: "from-secondary/10 to-secondary/20",
|
||||
iconColor: "text-secondary"
|
||||
},
|
||||
{
|
||||
num: t('steps.step3.num'),
|
||||
title: t('steps.step3.title'),
|
||||
desc: t('steps.step3.desc'),
|
||||
icon: <Download size={28} />,
|
||||
gradient: "from-accent/20 to-accent/40",
|
||||
iconColor: "text-primary"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-24 px-4 bg-white relative overflow-hidden">
|
||||
<div className="max-w-7xl mx-auto text-center relative z-10">
|
||||
<h2 className="text-4xl lg:text-5xl font-black text-primary mb-6">{t('steps.title')}</h2>
|
||||
<p className="text-gray-400 mb-20 text-lg max-w-2xl mx-auto">{t('steps.subtitle')}</p>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8 relative">
|
||||
{steps.map((st, i) => (
|
||||
<div key={i} className="group p-10 rounded-[40px] bg-gray-50/50 hover:bg-white border border-transparent hover:border-gray-100 hover:shadow-warm transition-all duration-500 text-left">
|
||||
<div className={`w-16 h-16 bg-gradient-to-br ${st.gradient} rounded-2xl flex items-center justify-center ${st.iconColor} mb-8 transition-transform duration-500 group-hover:scale-110 group-hover:rotate-3`}>
|
||||
{st.icon}
|
||||
</div>
|
||||
<div className="text-[10px] font-black tracking-[0.25em] text-secondary mb-4 uppercase">{t('steps.stepLabel', { num: st.num })}</div>
|
||||
<h3 className="text-2xl font-black text-primary mb-4">{st.title}</h3>
|
||||
<p className="text-gray-500 leading-relaxed text-sm font-medium">{st.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
const FeaturesContext = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section id="features" className="py-24 px-4 bg-gray-50/30">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-20 items-center">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 text-[10px] font-black tracking-[0.2em] text-secondary uppercase mb-6">
|
||||
<Zap size={14} className="text-accent" /> {t('features.tag')}
|
||||
</div>
|
||||
<h2 className="text-4xl lg:text-5xl font-black text-primary mb-8 leading-tight">
|
||||
{t('features.title')}
|
||||
</h2>
|
||||
<p className="text-lg text-gray-500 mb-12 font-medium">
|
||||
{t('features.description')}
|
||||
</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
{[
|
||||
{ icon: <Monitor className="text-primary" />, title: t('features.context.title'), desc: t('features.context.desc') },
|
||||
{ icon: <Layers className="text-secondary" />, title: t('features.glossary.title'), desc: t('features.glossary.desc') },
|
||||
{ icon: <Eye className="text-primary" />, title: t('features.vision.title'), desc: t('features.vision.desc') }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="flex gap-6 group">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-white rounded-2xl shadow-sm flex items-center justify-center border border-gray-100 group-hover:shadow-md transition-shadow">
|
||||
{item.icon}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-lg font-black text-primary mb-1">{item.title}</h4>
|
||||
<p className="text-gray-500 text-sm leading-relaxed font-medium">{item.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute -inset-10 bg-primary/5 blur-[100px] rounded-full"></div>
|
||||
<div className="relative bg-white rounded-[48px] shadow-warm border border-gray-100 p-8 lg:p-12">
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-primary/40 block mb-4">{t('features.demo.source')}</span>
|
||||
<div className="text-sm font-bold bg-gray-50 p-6 rounded-2xl italic text-primary/80">“La batterie de chauffe est raccordée sur la CTA”</div>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
<div className="p-6 border border-red-50 bg-red-50/10 rounded-2xl">
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-red-400 block mb-3">{t('features.demo.google')}</span>
|
||||
<div className="text-xs font-semibold text-red-900/40 line-through">“The heating battery is connected on the <span className="text-red-700">call to action</span>”</div>
|
||||
</div>
|
||||
<div className="p-6 bg-secondary/5 rounded-2xl ring-1 ring-secondary/20 shadow-sm">
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-secondary block mb-3">{t('features.demo.ours')}</span>
|
||||
<div className="text-sm font-bold text-primary">“The heating coil is connected to the <span className="bg-accent/30 px-1 rounded">AHU</span>”</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const LayoutFeatures = () => {
|
||||
const { t } = useTranslation();
|
||||
const points = (t('layout.points', { returnObjects: true }) as any[]).map((p, i) => ({
|
||||
...p,
|
||||
icon: i === 0 ? <Layers size={22} /> :
|
||||
i === 1 ? <Layout size={22} /> :
|
||||
i === 2 ? <PieChart size={22} /> :
|
||||
i === 3 ? <Zap size={22} /> :
|
||||
i === 4 ? <MessageSquare size={22} /> :
|
||||
<Globe size={22} />
|
||||
}));
|
||||
|
||||
return (
|
||||
<section className="py-24 px-4 bg-white relative overflow-hidden">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="text-center mb-24 transition-opacity">
|
||||
<h2 className="text-4xl lg:text-6xl font-black text-primary mb-8 leading-tight">
|
||||
{t('layout.title')}<br /> <span className="gradient-text">{t('layout.title2')}</span>
|
||||
</h2>
|
||||
<p className="text-gray-400 max-w-2xl mx-auto text-lg font-medium">
|
||||
{t('layout.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{points.map((p, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
whileHover={{ y: -8 }}
|
||||
className="group p-10 bg-gray-50/50 hover:bg-white rounded-[40px] border border-transparent hover:border-gray-100 hover:shadow-warm transition-all duration-500"
|
||||
>
|
||||
<div className="w-14 h-14 bg-white rounded-2xl flex items-center justify-center text-primary mb-8 shadow-sm group-hover:bg-primary group-hover:text-white transition-all transform group-hover:rotate-6">
|
||||
{p.icon}
|
||||
</div>
|
||||
<h4 className="text-xl font-black text-primary mb-4">{p.title}</h4>
|
||||
<p className="text-gray-500 text-sm leading-relaxed font-medium">{p.desc}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const FormatsSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const formats = [
|
||||
{
|
||||
id: ".DOCX",
|
||||
name: t('formats.word.name'),
|
||||
icon: <FileText className="text-blue-500" />,
|
||||
items: t('formats.word.items', { returnObjects: true }) as string[],
|
||||
},
|
||||
{
|
||||
id: ".XLSX",
|
||||
name: t('formats.excel.name'),
|
||||
icon: <FileSpreadsheet className="text-green-600" />,
|
||||
items: t('formats.excel.items', { returnObjects: true }) as string[],
|
||||
},
|
||||
{
|
||||
id: ".PPTX",
|
||||
name: t('formats.pptx.name'),
|
||||
icon: <Presentation className="text-orange-500" />,
|
||||
items: t('formats.pptx.items', { returnObjects: true }) as string[],
|
||||
},
|
||||
{
|
||||
id: ".PDF",
|
||||
name: t('formats.pdf.name'),
|
||||
icon: <FileText className="text-red-500" />,
|
||||
items: t('formats.pdf.items', { returnObjects: true }) as string[],
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="formats" className="py-24 px-4 bg-primary text-white overflow-hidden relative">
|
||||
<div className="absolute top-0 left-0 w-full h-full pointer-events-none opacity-20">
|
||||
<div className="absolute top-[-20%] right-[-10%] w-[800px] h-[800px] bg-secondary/30 blur-[150px] rounded-full"></div>
|
||||
<div className="absolute bottom-[-20%] left-[-10%] w-[600px] h-[600px] bg-accent/20 blur-[120px] rounded-full"></div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto relative z-10">
|
||||
<div className="text-center mb-24">
|
||||
<h2 className="text-4xl lg:text-6xl font-black mb-8 leading-tight">{t('formats.title')} <span className="text-accent">{t('formats.title2')}</span></h2>
|
||||
<p className="text-gray-300 max-w-xl mx-auto text-lg font-medium">{t('formats.subtitle')}</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{formats.map((f, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
whileHover={{ y: -10 }}
|
||||
className="p-10 rounded-[48px] bg-white/5 border border-white/10 backdrop-blur-xl group hover:bg-white/10 transition-all duration-300"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-10">
|
||||
<div className={`w-16 h-16 bg-white rounded-[20px] flex items-center justify-center text-3xl group-hover:rotate-12 transition-transform shadow-2xl`}>
|
||||
{f.icon}
|
||||
</div>
|
||||
<span className="text-[10px] font-black text-white/30 tracking-[0.2em]">{f.id}</span>
|
||||
</div>
|
||||
<h3 className="text-2xl font-black mb-8">{f.name}</h3>
|
||||
<ul className="space-y-4">
|
||||
{f.items.map((item, j) => (
|
||||
<li key={j} className="flex items-start gap-3 text-xs text-white/60 group-hover:text-white transition-colors tracking-tight font-medium">
|
||||
<div className="w-1.5 h-1.5 bg-accent rounded-full mt-1 shrink-0"></div> {item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const PricingSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isAnnual, setIsAnnual] = useState(true);
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: t('pricing.starter.name'),
|
||||
desc: t('pricing.starter.desc'),
|
||||
monthlyPrice: 9,
|
||||
annualPrice: 7,
|
||||
features: t('pricing.starter.features', { returnObjects: true }) as string[],
|
||||
cta: t('pricing.starter.cta'),
|
||||
popular: false
|
||||
},
|
||||
{
|
||||
name: t('pricing.pro.name'),
|
||||
desc: t('pricing.pro.desc'),
|
||||
monthlyPrice: 25,
|
||||
annualPrice: 19,
|
||||
features: t('pricing.pro.features', { returnObjects: true }) as string[],
|
||||
cta: t('pricing.pro.cta'),
|
||||
popular: true
|
||||
},
|
||||
{
|
||||
name: t('pricing.business.name'),
|
||||
desc: t('pricing.business.desc'),
|
||||
monthlyPrice: 65,
|
||||
annualPrice: 49,
|
||||
features: t('pricing.business.features', { returnObjects: true }) as string[],
|
||||
cta: t('pricing.business.cta'),
|
||||
popular: false
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="pricing" className="py-24 px-4 bg-white relative">
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
<h2 className="text-4xl lg:text-6xl font-black text-primary mb-8 leading-tight">{t('pricing.title')}</h2>
|
||||
<p className="text-gray-400 mb-16 text-lg font-medium max-w-2xl mx-auto">{t('pricing.subtitle')}</p>
|
||||
|
||||
<div className="flex items-center justify-center gap-4 mb-24 bg-gray-50 p-2 rounded-2xl w-fit mx-auto border border-gray-100">
|
||||
<button
|
||||
onClick={() => setIsAnnual(false)}
|
||||
className={`px-10 py-3 rounded-xl font-bold text-sm transition-all ${!isAnnual ? 'bg-white shadow-lg text-primary' : 'text-gray-400'}`}
|
||||
>
|
||||
{t('pricing.monthly')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsAnnual(true)}
|
||||
className={`px-10 py-3 rounded-xl font-bold text-sm transition-all relative ${isAnnual ? 'bg-white shadow-lg text-primary' : 'text-gray-400'}`}
|
||||
>
|
||||
{t('pricing.annual')}
|
||||
<span className="absolute -top-3 -right-4 bg-accent text-primary px-2.5 py-1 rounded-full text-[9px] font-black uppercase shadow-sm">
|
||||
-20%
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 items-start">
|
||||
{plans.map((p, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
whileHover={{ y: -10 }}
|
||||
className={`p-12 rounded-[56px] border flex flex-col text-left transition-all duration-500 ${p.popular ? 'border-primary shadow-teal border-2 ring-8 ring-primary/5 scale-[1.05] bg-white relative z-10' : 'border-gray-50 bg-white hover:border-gray-100'}`}
|
||||
>
|
||||
{p.popular && (
|
||||
<div className="absolute top-0 left-12 -translate-y-1/2 bg-primary text-white px-6 py-2 rounded-full text-[10px] font-black uppercase tracking-widest shadow-xl">
|
||||
{t('pricing.bestValue')}
|
||||
</div>
|
||||
)}
|
||||
<h3 className="text-2xl font-black text-primary mb-2">{p.name}</h3>
|
||||
<p className="text-gray-400 text-xs font-bold mb-10 tracking-tight">{p.desc}</p>
|
||||
|
||||
<div className="mb-12">
|
||||
<span className="text-6xl font-black text-primary tracking-tighter">€{isAnnual ? p.annualPrice : p.monthlyPrice}</span>
|
||||
<span className="text-gray-400 font-black ml-2 uppercase text-[10px] tracking-widest">{t('pricing.month')}</span>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-5 mb-12 flex-grow">
|
||||
{p.features.map((f, j) => (
|
||||
<li key={j} className="flex items-start gap-4 text-sm text-gray-500 font-bold leading-snug">
|
||||
<CheckCircle2 size={18} className="text-secondary shrink-0 mt-0.5" /> {f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button className={`w-full py-6 rounded-3xl font-black text-lg transition-all active:scale-95 shadow-sm ${p.popular ? 'bg-primary text-white hover:bg-primary/95 shadow-primary/20 shadow-xl' : 'bg-gray-50 text-primary hover:bg-gray-100'}`}>
|
||||
{p.cta}
|
||||
</button>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="mt-20 text-[10px] font-black text-gray-300 uppercase tracking-[0.3em]">
|
||||
{t('pricing.footer')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const CTASection = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section className="py-24 px-4">
|
||||
<div className="max-w-6xl mx-auto rounded-[80px] bg-gradient-to-br from-primary via-primary to-secondary p-12 lg:p-24 text-center text-white relative overflow-hidden shadow-teal shadow-2xl">
|
||||
<div className="absolute top-0 left-0 w-full h-full opacity-30 pointer-events-none">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,#fff_0%,transparent_50%)] overflow-hidden"></div>
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_80%_80%,var(--color-accent)_0%,transparent_50%)]"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10">
|
||||
<h2 className="text-5xl lg:text-8xl font-black mb-10 leading-[0.9] tracking-tighter">
|
||||
{t('cta.title')}
|
||||
</h2>
|
||||
<p className="text-xl opacity-80 mb-12 max-w-2xl mx-auto font-bold text-blue-50 leading-relaxed">
|
||||
{t('cta.subtitle')}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-6 justify-center items-center">
|
||||
<button className="bg-accent text-primary px-14 py-6 rounded-[34px] text-xl font-black hover:scale-105 active:scale-95 transition-all shadow-2xl shadow-black/20">
|
||||
{t('cta.button')}
|
||||
</button>
|
||||
<div className="flex items-center gap-3 text-xs font-black uppercase tracking-[0.2em] text-white/50">
|
||||
<ShieldCheck size={16} /> {t('cta.secure')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const Footer = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<footer className="bg-white border-t border-gray-100 pt-32 pb-12 px-4">
|
||||
<div className="max-w-7xl mx-auto grid md:grid-cols-12 gap-16 mb-32 text-left">
|
||||
<div className="md:col-span-4 lg:col-span-5">
|
||||
<div className="flex items-center gap-3 mb-10">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-primary to-secondary rounded-xl flex items-center justify-center text-white font-black">W</div>
|
||||
<span className="text-2xl font-black tracking-tight text-primary">WORDLY<span className="text-secondary">.art</span></span>
|
||||
</div>
|
||||
<p className="text-gray-400 text-base font-medium leading-relaxed mb-10 max-w-xs">
|
||||
{t('footer.desc')}
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
{[Globe, MessageSquare, Zap].map((Icon, i) => (
|
||||
<div key={i} className="w-12 h-12 bg-gray-50 rounded-2xl flex items-center justify-center text-gray-300 hover:text-primary transition-all cursor-pointer border border-transparent hover:border-gray-100 shadow-sm">
|
||||
<Icon size={20} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 lg:col-span-2">
|
||||
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-primary mb-10">{t('footer.product')}</h4>
|
||||
<ul className="space-y-5 text-sm text-gray-500 font-bold">
|
||||
<li><a href="#features" className="hover:text-primary transition-colors">{t('nav.why')}</a></li>
|
||||
<li><a href="#formats" className="hover:text-primary transition-colors">{t('nav.formats')}</a></li>
|
||||
<li><a href="#pricing" className="hover:text-primary transition-colors">{t('nav.pricing')}</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Integrations</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-3 lg:col-span-2">
|
||||
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-primary mb-10">{t('footer.resources')}</h4>
|
||||
<ul className="space-y-5 text-sm text-gray-500 font-bold">
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Documentation</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Security</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Developer Portal</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">API Docs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-3 lg:col-span-3">
|
||||
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-primary mb-10">{t('footer.legal')}</h4>
|
||||
<ul className="space-y-5 text-sm text-gray-500 font-bold">
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Privacy Policy</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Terms of Service</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Cookie Policy</a></li>
|
||||
<li><a href="#" className="hover:text-primary transition-colors">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto pt-10 border-t border-gray-100 flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div className="text-[10px] font-black text-gray-300 uppercase tracking-[0.3em]">
|
||||
{t('footer.rights')}
|
||||
</div>
|
||||
<div className="flex items-center gap-10 text-[10px] font-black text-gray-400 uppercase tracking-[0.3em]">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="opacity-50">Translation engine</span>
|
||||
<div className="flex gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-secondary rounded-full"></div>
|
||||
<div className="w-1.5 h-1.5 bg-accent rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span>v2.4.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="min-h-screen font-sans bg-white selection:bg-[#00A8A8]/30 overflow-x-hidden">
|
||||
<Navbar />
|
||||
<main>
|
||||
<Hero />
|
||||
<Steps />
|
||||
<FeaturesContext />
|
||||
<LayoutFeatures />
|
||||
<FormatsSection />
|
||||
<PricingSection />
|
||||
<CTASection />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
299
wordly.art---traduction-de-documents/src/i18n.ts
Normal file
299
wordly.art---traduction-de-documents/src/i18n.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
fallbackLng: 'fr',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
resources: {
|
||||
fr: {
|
||||
translation: {
|
||||
nav: {
|
||||
why: "Pourquoi nous ?",
|
||||
formats: "Formats",
|
||||
pricing: "Tarification",
|
||||
login: "Connexion",
|
||||
startFree: "Essai gratuit",
|
||||
},
|
||||
hero: {
|
||||
tag: "IA Documentaire Professionnelle",
|
||||
titleLine1: "Traduisez vos documents.",
|
||||
titleLine2: "Mise en forme parfaite.",
|
||||
description: "Le seul traducteur qui préserve les SmartArt, graphiques, tables des matières, formes, en-têtes et pieds de page — exactement tels qu'ils étaient.",
|
||||
ctaMain: "Essai gratuit — 2 docs/mois",
|
||||
ctaSec: "Voir les offres",
|
||||
deleted: "Fichiers supprimés après 60 min",
|
||||
noHidden: "Pas de prix cachés",
|
||||
preview: "Aperçu avant paiement",
|
||||
formattedOk: "Formatage OK",
|
||||
aiActive: "Traduction IA active"
|
||||
},
|
||||
steps: {
|
||||
title: "Comment ça marche ?",
|
||||
subtitle: "Trois étapes. Zéro perte de format.",
|
||||
step1: {
|
||||
num: "01",
|
||||
title: "Déposez votre fichier",
|
||||
desc: "Glissez-déposez votre document Excel, Word, PowerPoint ou PDF."
|
||||
},
|
||||
step2: {
|
||||
num: "02",
|
||||
title: "Choisissez la langue",
|
||||
desc: "Sélectionnez la langue cible et le moteur de traduction — classique ou IA contextuelle."
|
||||
},
|
||||
step3: {
|
||||
num: "03",
|
||||
title: "Téléchargez le résultat",
|
||||
desc: "Récupérez votre document traduit avec un formatage identique à l'original."
|
||||
}
|
||||
},
|
||||
features: {
|
||||
tag: "Moteur de Traduction IA",
|
||||
title: "La traduction qui comprend votre métier",
|
||||
description: "Nos modèles IA analysent le contexte, respectent votre terminologie et traduisent même le texte dans vos images.",
|
||||
context: {
|
||||
title: "Contexte métier",
|
||||
desc: "Décrivez votre domaine et obtenez des traductions adaptées, pas génériques."
|
||||
},
|
||||
glossary: {
|
||||
title: "Glossaires métier",
|
||||
desc: "Définissez vos termes techniques. CTA restera « Air Handling Unit », jamais « Call To Action »."
|
||||
},
|
||||
vision: {
|
||||
title: "Vision sur images",
|
||||
desc: "Le texte incrusté dans vos images, diagrammes et graphiques est détecté et traduit."
|
||||
},
|
||||
demo: {
|
||||
source: "Source (FR)",
|
||||
google: "Google Translate",
|
||||
ours: "Notre IA"
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
title: "Votre mise en forme,",
|
||||
title2: "parfaitement préservée",
|
||||
subtitle: "Les autres traducteurs détruisent votre mise en page. Pas nous.",
|
||||
points: [
|
||||
{ title: "SmartArt et diagrammes", desc: "Organigrammes, flux, hiérarchies — tout traduit à l'identique." },
|
||||
{ title: "Tables des matières", desc: "Entrées de TDM, numéros et renvois mis à jour correctement." },
|
||||
{ title: "Graphiques", desc: "Titres, étiquettes, légendes et séries — tout est traduit." },
|
||||
{ title: "Formes et zones de texte", desc: "Rectangles, blocs arrondis, légendes — localisé partout." },
|
||||
{ title: "En-têtes et pieds de page", desc: "En-têtes, pieds de page et notes ne sont jamais oubliés." },
|
||||
{ title: "130+ langues", desc: "Google Traduction, DeepL et moteurs IA haute performance." }
|
||||
]
|
||||
},
|
||||
formats: {
|
||||
title: "Chaque format,",
|
||||
title2: "chaque élément",
|
||||
subtitle: "Nous traduisons ce que les autres oublient. Votre entreprise mérite une documentation irréprochable.",
|
||||
word: {
|
||||
name: "Word",
|
||||
items: ["Paragraphes et titres", "Tableaux et graphiques", "Diagrammes SmartArt", "Table des matières", "En-têtes et pieds", "Formes et zones de texte", "Notes de bas de page"]
|
||||
},
|
||||
excel: {
|
||||
name: "Excel",
|
||||
items: ["Valeurs de cellules", "Noms de feuilles", "Graphiques et étiquettes", "En-têtes et pieds", "Cellules fusionnées"]
|
||||
},
|
||||
pptx: {
|
||||
name: "PowerPoint",
|
||||
items: ["Texte et notes", "Graphiques et diagrammes", "Formes et zones de texte", "Masques de diapositives", "Animations préservées"]
|
||||
},
|
||||
pdf: {
|
||||
name: "PDF",
|
||||
items: ["PDF textuels", "Mise en page préservée", "Images conservées", "Tableaux maintenus", "Sortie DOCX ou PDF"]
|
||||
}
|
||||
},
|
||||
pricing: {
|
||||
title: "Des prix simples et honnêtes",
|
||||
subtitle: "Ce que vous voyez est ce que vous payez. Aucun frais caché.",
|
||||
monthly: "Mensuel",
|
||||
annual: "Annuel",
|
||||
bestValue: "Le plus populaire",
|
||||
month: "/mois",
|
||||
footer: "Le prix affiché est le prix que vous payez. Aucun frais caché après traduction.",
|
||||
starter: {
|
||||
name: "Starter",
|
||||
desc: "Pour les particuliers et petits projets",
|
||||
features: ["50 documents / mois", "Jusqu'à 50 pages par doc", "Google Traduction + DeepL", "Fichiers jusqu'à 10 Mo"],
|
||||
cta: "Commencer"
|
||||
},
|
||||
pro: {
|
||||
name: "Pro",
|
||||
desc: "Pour les professionnels exigeants",
|
||||
features: ["200 documents / mois", "Jusqu'à 200 pages par doc", "Traduction par IA (DeepSeek)", "Google + DeepL inclus", "Glossaires et prompts", "Support prioritaire"],
|
||||
cta: "Essayer Pro"
|
||||
},
|
||||
business: {
|
||||
name: "Business",
|
||||
desc: "Pour les équipes avec des besoins élevés",
|
||||
features: ["1 000 documents / mois", "Jusqu'à 500 pages par doc", "IA Premium (Claude)", "Tous les fournisseurs + API", "Webhooks et automatisation", "5 postes d'équipe"],
|
||||
cta: "Nous contacter"
|
||||
}
|
||||
},
|
||||
cta: {
|
||||
title: "Commencez à traduire en 30 secondes",
|
||||
subtitle: "Aucune carte bancaire requise. Essayez gratuitement dès maintenant et redonnez vie à vos documents multilingues.",
|
||||
button: "Créer un compte gratuit",
|
||||
secure: "Sécurisé par chiffrement AES-256"
|
||||
},
|
||||
footer: {
|
||||
desc: "Spécialiste de la traduction documentaire intelligente. Nous marions l'art de la mise en page et la science de l'IA contextuelle.",
|
||||
product: "Produit",
|
||||
resources: "Ressources",
|
||||
legal: "Légal",
|
||||
rights: "© 2026 Wordly.art — Tous droits réservés.",
|
||||
hosted: "Hébergé en Europe",
|
||||
powered: "Propulsé par"
|
||||
}
|
||||
}
|
||||
},
|
||||
en: {
|
||||
translation: {
|
||||
nav: {
|
||||
why: "Why Us?",
|
||||
formats: "Formats",
|
||||
pricing: "Pricing",
|
||||
login: "Log in",
|
||||
startFree: "Start Free",
|
||||
},
|
||||
hero: {
|
||||
tag: "Professional Document AI",
|
||||
titleLine1: "Translate your documents.",
|
||||
titleLine2: "Keep the formatting perfect.",
|
||||
description: "The only translator that preserves SmartArt, charts, tables of contents, shapes, and complex layouts — exactly as they were.",
|
||||
ctaMain: "Start Free — 2 docs/month",
|
||||
ctaSec: "See Offers",
|
||||
deleted: "Files deleted after 60 min",
|
||||
noHidden: "No hidden fees",
|
||||
preview: "Preview before payment",
|
||||
formattedOk: "Formatting OK",
|
||||
aiActive: "AI Translation active"
|
||||
},
|
||||
steps: {
|
||||
title: "How it works?",
|
||||
subtitle: "Three steps. Zero formatting loss.",
|
||||
step1: {
|
||||
num: "01",
|
||||
title: "Upload your file",
|
||||
desc: "Drag & drop your Excel, Word, PowerPoint or PDF document."
|
||||
},
|
||||
step2: {
|
||||
num: "02",
|
||||
title: "Pick language & engine",
|
||||
desc: "Select target language and engine — classic or context-aware AI."
|
||||
},
|
||||
step3: {
|
||||
num: "03",
|
||||
title: "Download the result",
|
||||
desc: "Get your translated document with formatting identical to the original."
|
||||
}
|
||||
},
|
||||
features: {
|
||||
tag: "AI Translation Engine",
|
||||
title: "Translation that understands your craft",
|
||||
description: "Our AI models analyze context, respect your terminology, and even translate text inside images.",
|
||||
context: {
|
||||
title: "Industry Context",
|
||||
desc: "Describe your field and get tailored translations, not generic ones."
|
||||
},
|
||||
glossary: {
|
||||
title: "Industry Glossaries",
|
||||
desc: "Define your technical terms. CTA stay 'Air Handling Unit', never 'Call To Action'."
|
||||
},
|
||||
vision: {
|
||||
title: "Image Vision",
|
||||
desc: "Text embedded in images, diagrams and charts is detected and translated."
|
||||
},
|
||||
demo: {
|
||||
source: "Source (FR)",
|
||||
google: "Google Translate",
|
||||
ours: "Our AI"
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
title: "Your formatting,",
|
||||
title2: "perfectly preserved",
|
||||
subtitle: "Other translators break your layout. We don't.",
|
||||
points: [
|
||||
{ title: "SmartArt & Diagrams", desc: "Org charts, flowcharts, hierarchies — all translated identically." },
|
||||
{ title: "Tables of Contents", desc: "TOC entries, page numbers and cross-references updated correctly." },
|
||||
{ title: "Charts & Graphs", desc: "Titles, axis labels, legends and series names — everything is translated." },
|
||||
{ title: "Shapes & Text Boxes", desc: "Rectangles, rounded blocks, callouts — localized everywhere." },
|
||||
{ title: "Headers & Footers", desc: "Headers, footers and footnotes are never missed." },
|
||||
{ title: "130+ Languages", desc: "Google Translate, DeepL and professional-grade AI engines." }
|
||||
]
|
||||
},
|
||||
formats: {
|
||||
title: "Every format,",
|
||||
title2: "every element",
|
||||
subtitle: "We translate what others miss. Your business deserves irreproachable documentation.",
|
||||
word: {
|
||||
name: "Word",
|
||||
items: ["Paragraphs & headings", "Tables & charts", "SmartArt diagrams", "Table of contents", "Headers & footers", "Shapes & text boxes", "Footnotes & endnotes"]
|
||||
},
|
||||
excel: {
|
||||
name: "Excel",
|
||||
items: ["Cell values", "Sheet names", "Charts & labels", "Headers & footers", "Merged cells preserved"]
|
||||
},
|
||||
pptx: {
|
||||
name: "PowerPoint",
|
||||
items: ["Slide text & notes", "Charts & diagrams", "Shapes & text boxes", "Master layouts", "Animations preserved"]
|
||||
},
|
||||
pdf: {
|
||||
name: "PDF",
|
||||
items: ["Text-based PDFs", "Layout preserved", "Images kept in place", "Tables maintained", "Output as DOCX or PDF"]
|
||||
}
|
||||
},
|
||||
pricing: {
|
||||
title: "Simple, honest pricing",
|
||||
subtitle: "What you see is what you pay. No hidden fees.",
|
||||
monthly: "Monthly",
|
||||
annual: "Annual",
|
||||
bestValue: "Most Popular",
|
||||
month: "/month",
|
||||
footer: "What you see is what you pay. No hidden fees after translation.",
|
||||
starter: {
|
||||
name: "Starter",
|
||||
desc: "For individuals and small projects",
|
||||
features: ["50 documents / month", "Up to 50 pages per doc", "Google Translate + DeepL", "Files up to 10 MB"],
|
||||
cta: "Get Started"
|
||||
},
|
||||
pro: {
|
||||
name: "Pro",
|
||||
desc: "For demanding professionals",
|
||||
features: ["200 documents / month", "Up to 200 pages per doc", "AI-powered translation (DeepSeek)", "Google + DeepL included", "Custom glossaries & prompts", "Priority support"],
|
||||
cta: "Try Pro"
|
||||
},
|
||||
business: {
|
||||
name: "Business",
|
||||
desc: "For teams with high-volume needs",
|
||||
features: ["1,000 documents / month", "Up to 500 pages per doc", "Premium AI (Claude)", "All providers + API access", "Webhooks & automation", "5 team seats"],
|
||||
cta: "Contact Us"
|
||||
}
|
||||
},
|
||||
cta: {
|
||||
title: "Start translating in 30 seconds",
|
||||
subtitle: "No credit card required. Try for free now and bring your multilingual documents back to life.",
|
||||
button: "Create Free Account",
|
||||
secure: "Secured by AES-256 encryption"
|
||||
},
|
||||
footer: {
|
||||
desc: "Expert in intelligent document translation. We blend the art of layout with the science of contextual AI.",
|
||||
product: "Product",
|
||||
resources: "Resources",
|
||||
legal: "Legal",
|
||||
rights: "© 2026 Wordly.art — All rights reserved.",
|
||||
hosted: "Hosted in Europe",
|
||||
powered: "Powered by"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
38
wordly.art---traduction-de-documents/src/index.css
Normal file
38
wordly.art---traduction-de-documents/src/index.css
Normal file
@@ -0,0 +1,38 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&family=Space+Grotesk:wght@400;500;600;700&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: "Space Grotesk", sans-serif;
|
||||
|
||||
--color-primary: #003B5C;
|
||||
--color-secondary: #00D1C1;
|
||||
--color-accent: #C6F432;
|
||||
|
||||
--shadow-warm: 0 20px 50px -12px rgba(0, 59, 92, 0.12);
|
||||
--shadow-teal: 0 20px 50px -12px rgba(0, 209, 193, 0.15);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply font-sans selection:bg-secondary/30 text-primary;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
@apply font-display tracking-tight;
|
||||
}
|
||||
}
|
||||
|
||||
.glass {
|
||||
@apply bg-white/70 backdrop-blur-xl border border-white/20;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary via-secondary to-accent;
|
||||
}
|
||||
|
||||
.bento-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
11
wordly.art---traduction-de-documents/src/main.tsx
Normal file
11
wordly.art---traduction-de-documents/src/main.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import {StrictMode} from 'react';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
import './i18n';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
Reference in New Issue
Block a user