feat: add language context and translation support
- Introduced LanguageProvider to manage language state and translations. - Updated components (Header, Hero, About, Skills, Projects, Education, Contact) to utilize translations. - Added language toggle button in the header for switching between French and English. - Enhanced styling for language toggle button in the header.
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { User, Heart, Target, Coffee } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const About = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const stats = [
|
||||
{ icon: <User size={24} />, value: "3ème", label: "Année d'études" },
|
||||
{ icon: <Heart size={24} />, value: "100%", label: "Passion" },
|
||||
{ icon: <Target size={24} />, value: "∞", label: "Objectifs" },
|
||||
{ icon: <Coffee size={24} />, value: "☕", label: "Fuel quotidien" }
|
||||
{ icon: <User size={24} />, value: t('about.stats.year'), label: t('about.stats.yearLabel') },
|
||||
{ icon: <Heart size={24} />, value: t('about.stats.passion'), label: t('about.stats.passionLabel') },
|
||||
{ icon: <Target size={24} />, value: t('about.stats.goals'), label: t('about.stats.goalsLabel') },
|
||||
{ icon: <Coffee size={24} />, value: t('about.stats.fuel'), label: t('about.stats.fuelLabel') }
|
||||
];
|
||||
|
||||
const containerVariants = {
|
||||
@@ -42,9 +45,9 @@ const About = () => {
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="section-title">À propos de moi</h2>
|
||||
<h2 className="section-title">{t('about.title')}</h2>
|
||||
<p className="section-subtitle">
|
||||
Découvrez qui je suis et ce qui me passionne
|
||||
{t('about.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -57,32 +60,23 @@ const About = () => {
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<motion.div className="about-card" variants={itemVariants}>
|
||||
<h3>Mon parcours</h3>
|
||||
<h3>{t('about.journey.title')}</h3>
|
||||
<p>
|
||||
Actuellement en 3ème année de <strong>Technologies de l'Informatique</strong> à la
|
||||
HELHa de Tournai, je me passionne pour le développement d'applications et les
|
||||
nouvelles technologies. Mon parcours m'a permis d'acquérir une solide base
|
||||
technique et une approche méthodique du développement.
|
||||
{t('about.journey.content')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div className="about-card" variants={itemVariants}>
|
||||
<h3>Ma passion</h3>
|
||||
<h3>{t('about.passion.title')}</h3>
|
||||
<p>
|
||||
Ce qui m'anime le plus, c'est la création de solutions innovantes qui résolvent
|
||||
des problèmes réels. J'aime particulièrement le développement mobile avec
|
||||
<strong> Flutter</strong> et le développement web moderne avec
|
||||
<strong> React</strong> et <strong> TypeScript</strong>.
|
||||
{t('about.passion.content')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div className="about-card" variants={itemVariants}>
|
||||
<h3>Mes objectifs</h3>
|
||||
<h3>{t('about.goals.title')}</h3>
|
||||
<p>
|
||||
Je cherche constamment à améliorer mes compétences et à rester à jour avec
|
||||
les dernières tendances technologiques. Mon objectif est de devenir un
|
||||
développeur full-stack polyvalent et de contribuer à des projets qui ont
|
||||
un impact positif.
|
||||
{t('about.goals.content')}
|
||||
</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
@@ -123,13 +117,11 @@ const About = () => {
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<div className="highlight-content">
|
||||
<h3>En quelques mots</h3>
|
||||
<h3>{t('about.quote.title')}</h3>
|
||||
<p>
|
||||
"La technologie n'est rien. Ce qui est important, c'est d'avoir la foi en les gens,
|
||||
qu'ils soient fondamentalement bons et intelligents, et si vous leur donnez des outils,
|
||||
ils feront des choses merveilleuses avec."
|
||||
"{t('about.quote.content')}"
|
||||
</p>
|
||||
<cite>- Steve Jobs</cite>
|
||||
<cite>- {t('about.quote.author')}</cite>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,10 @@ import { motion } from 'framer-motion';
|
||||
import { Mail, Phone, MapPin, Send, Github, Linkedin, MessageCircle, CheckCircle, AlertCircle } from 'lucide-react';
|
||||
import { sendContactEmail } from '../services/emailService';
|
||||
import type { ContactFormData } from '../services/emailService';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const Contact = () => {
|
||||
const { t } = useLanguage();
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
@@ -93,7 +95,7 @@ const Contact = () => {
|
||||
{
|
||||
icon: <Linkedin size={24} />,
|
||||
name: "LinkedIn",
|
||||
url: "https://linkedin.com/in/dayronvanleemput", // Remplacez par votre profil
|
||||
url: "https://www.linkedin.com/in/dayron-van-leemput-992a94398", // Remplacez par votre profil
|
||||
color: "#0077B5"
|
||||
}
|
||||
];
|
||||
@@ -131,9 +133,9 @@ const Contact = () => {
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="section-title">Contactez-moi</h2>
|
||||
<h2 className="section-title">{t('contact.title')}</h2>
|
||||
<p className="section-subtitle">
|
||||
Une question, un projet ou simplement envie d'échanger ? N'hésitez pas à me contacter !
|
||||
{t('contact.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -149,12 +151,10 @@ const Contact = () => {
|
||||
<div className="contact-intro">
|
||||
<h3>
|
||||
<MessageCircle size={24} />
|
||||
Restons en contact
|
||||
{t('contact.stayInTouch')}
|
||||
</h3>
|
||||
<p>
|
||||
Je suis toujours intéressé par de nouveaux projets, des collaborations
|
||||
ou simplement des discussions autour de la technologie. N'hésitez pas
|
||||
à me contacter !
|
||||
{t('contact.intro')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -189,7 +189,7 @@ const Contact = () => {
|
||||
</div>
|
||||
|
||||
<div className="social-links">
|
||||
<h4>Retrouvez-moi aussi sur :</h4>
|
||||
<h4>{t('contact.findMeOn')}</h4>
|
||||
<div className="social-grid">
|
||||
{socialLinks.map((social, index) => (
|
||||
<motion.a
|
||||
@@ -223,7 +223,7 @@ const Contact = () => {
|
||||
|
||||
{/* Formulaire de contact */}
|
||||
<motion.div className="contact-form-container" variants={itemVariants}>
|
||||
<h3>Envoyez-moi un message</h3>
|
||||
<h3>{t('contact.sendMessage')}</h3>
|
||||
|
||||
{isSubmitted && (
|
||||
<motion.div
|
||||
@@ -233,7 +233,7 @@ const Contact = () => {
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
>
|
||||
<CheckCircle size={20} />
|
||||
Message envoyé avec succès ! Je vous répondrai bientôt.
|
||||
{t('contact.success')}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
@@ -258,7 +258,7 @@ const Contact = () => {
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<label htmlFor="name">Nom complet</label>
|
||||
<label htmlFor="name">{t('contact.form.name')}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
@@ -266,7 +266,7 @@ const Contact = () => {
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre nom"
|
||||
placeholder={t('contact.form.name')}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
@@ -277,7 +277,7 @@ const Contact = () => {
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<label htmlFor="email">Email</label>
|
||||
<label htmlFor="email">{t('contact.form.email')}</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
@@ -297,7 +297,7 @@ const Contact = () => {
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<label htmlFor="subject">Sujet</label>
|
||||
<label htmlFor="subject">{t('contact.form.subject')}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
@@ -305,7 +305,7 @@ const Contact = () => {
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Objet de votre message"
|
||||
placeholder={t('contact.form.subject')}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
@@ -316,7 +316,7 @@ const Contact = () => {
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<label htmlFor="message">Message</label>
|
||||
<label htmlFor="message">{t('contact.form.message')}</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
@@ -324,7 +324,7 @@ const Contact = () => {
|
||||
onChange={handleChange}
|
||||
required
|
||||
rows={6}
|
||||
placeholder="Votre message..."
|
||||
placeholder={t('contact.form.message')}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
@@ -342,12 +342,12 @@ const Contact = () => {
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<div className="loading-spinner" />
|
||||
Envoi en cours...
|
||||
{t('contact.form.sending')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send size={20} />
|
||||
Envoyer le message
|
||||
{t('contact.form.send')}
|
||||
</>
|
||||
)}
|
||||
</motion.button>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { GraduationCap, Calendar, MapPin, Award, BookOpen, Target } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const Education = () => {
|
||||
const { t } = useLanguage();
|
||||
const education = [
|
||||
{
|
||||
id: 1,
|
||||
degree: "Bachelier en Technologies de l'Informatique",
|
||||
school: "HELHa - Haute École Louvain en Hainaut",
|
||||
location: "Tournai, Belgique",
|
||||
period: "2023 - 2026",
|
||||
currentYear: "3ème année",
|
||||
status: "En cours",
|
||||
description: "Formation complète en développement logiciel, programmation, bases de données, réseaux et gestion de projets informatiques.",
|
||||
degree: t('education.degree'),
|
||||
school: t('education.school'),
|
||||
location: t('education.location'),
|
||||
period: t('education.period'),
|
||||
currentYear: t('education.currentYear'),
|
||||
status: t('education.status'),
|
||||
description: t('education.description'),
|
||||
highlights: [
|
||||
"Programmation orientée objet (Java, C#)",
|
||||
"Développement web (HTML, CSS, JavaScript, React)",
|
||||
"Développement mobile (Flutter, Dart)",
|
||||
"Bases de données et SQL",
|
||||
"Gestion de projets",
|
||||
"Réseaux et systèmes"
|
||||
t('education.highlights.0'),
|
||||
t('education.highlights.1'),
|
||||
t('education.highlights.2'),
|
||||
t('education.highlights.3'),
|
||||
t('education.highlights.4'),
|
||||
t('education.highlights.5')
|
||||
],
|
||||
color: "#4CAF50",
|
||||
icon: <GraduationCap size={24} />
|
||||
@@ -27,16 +29,16 @@ const Education = () => {
|
||||
|
||||
const certifications = [
|
||||
{
|
||||
title: "Développement Mobile Flutter",
|
||||
provider: "Formation autodidacte",
|
||||
date: "2024",
|
||||
title: t('education.cert1.title'),
|
||||
provider: t('education.cert1.provider'),
|
||||
date: t('education.cert1.date'),
|
||||
skills: ["Dart", "Flutter", "Firebase", "API REST"],
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
title: "React & TypeScript",
|
||||
provider: "Projets personnels",
|
||||
date: "2024",
|
||||
title: t('education.cert2.title'),
|
||||
provider: t('education.cert2.provider'),
|
||||
date: t('education.cert2.date'),
|
||||
skills: ["React", "TypeScript", "Hooks", "Context API"],
|
||||
color: "#FF9800"
|
||||
}
|
||||
@@ -75,9 +77,9 @@ const Education = () => {
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="section-title">Formation & Apprentissage</h2>
|
||||
<h2 className="section-title">{t('education.title')}</h2>
|
||||
<p className="section-subtitle">
|
||||
Mon parcours académique et mes apprentissages continus
|
||||
{t('education.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -178,7 +180,7 @@ const Education = () => {
|
||||
>
|
||||
<h3 className="certifications-title">
|
||||
<Award size={24} />
|
||||
Formations complémentaires & Autodidacte
|
||||
{t('education.certifications.title')}
|
||||
</h3>
|
||||
<div className="certifications-grid">
|
||||
{certifications.map((cert, index) => (
|
||||
@@ -224,13 +226,13 @@ const Education = () => {
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h3>Objectifs d'apprentissage 2025</h3>
|
||||
<h3>{t('education.learningGoals2025')}</h3>
|
||||
<div className="goals-grid">
|
||||
{[
|
||||
{ goal: "Maîtriser Firebase et les services cloud", progress: 60 },
|
||||
{ goal: "Approfondir Spring Boot pour le backend", progress: 30 },
|
||||
{ goal: "Apprendre Docker et les conteneurs", progress: 20 },
|
||||
{ goal: "Développer mes compétences en UI/UX", progress: 45 }
|
||||
{ goal: t('education.goal1'), progress: 60 },
|
||||
{ goal: t('education.goal2'), progress: 30 },
|
||||
{ goal: t('education.goal3'), progress: 20 },
|
||||
{ goal: t('education.goal4'), progress: 45 }
|
||||
].map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Menu, X, Sun, Moon } from 'lucide-react';
|
||||
import { Menu, X, Sun, Moon, Globe } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
interface HeaderProps {
|
||||
darkMode: boolean;
|
||||
@@ -9,14 +10,15 @@ interface HeaderProps {
|
||||
|
||||
const Header = ({ darkMode, toggleDarkMode }: HeaderProps) => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { language, setLanguage, t } = useLanguage();
|
||||
|
||||
const menuItems = [
|
||||
{ name: 'Accueil', href: '#hero' },
|
||||
{ name: 'À propos', href: '#about' },
|
||||
{ name: 'Compétences', href: '#skills' },
|
||||
{ name: 'Projets', href: '#projects' },
|
||||
{ name: 'Formation', href: '#education' },
|
||||
{ name: 'Contact', href: '#contact' }
|
||||
{ name: t('nav.home'), href: '#hero' },
|
||||
{ name: t('nav.about'), href: '#about' },
|
||||
{ name: t('nav.skills'), href: '#skills' },
|
||||
{ name: t('nav.projects'), href: '#projects' },
|
||||
{ name: t('nav.education'), href: '#education' },
|
||||
{ name: t('nav.contact'), href: '#contact' }
|
||||
];
|
||||
|
||||
const scrollToSection = (href: string) => {
|
||||
@@ -27,6 +29,10 @@ const Header = ({ darkMode, toggleDarkMode }: HeaderProps) => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'fr' ? 'en' : 'fr');
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
initial={{ y: -100 }}
|
||||
@@ -68,13 +74,26 @@ const Header = ({ darkMode, toggleDarkMode }: HeaderProps) => {
|
||||
</ul>
|
||||
|
||||
<div className="nav-controls">
|
||||
{/* Toggle langue */}
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
onClick={toggleLanguage}
|
||||
className="language-toggle"
|
||||
aria-label={t('btn.changeLang')}
|
||||
title={t('btn.changeLang')}
|
||||
>
|
||||
<Globe size={18} />
|
||||
<span className="language-text">{language === 'fr' ? 'EN' : 'FR'}</span>
|
||||
</motion.button>
|
||||
|
||||
{/* Toggle thème */}
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
onClick={toggleDarkMode}
|
||||
className="theme-toggle"
|
||||
aria-label="Changer de thème"
|
||||
aria-label={t('btn.changeTheme')}
|
||||
>
|
||||
{darkMode ? <Sun size={20} /> : <Moon size={20} />}
|
||||
</motion.button>
|
||||
@@ -85,7 +104,7 @@ const Header = ({ darkMode, toggleDarkMode }: HeaderProps) => {
|
||||
whileTap={{ scale: 0.9 }}
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="menu-toggle"
|
||||
aria-label="Menu"
|
||||
aria-label={t('btn.menu')}
|
||||
>
|
||||
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</motion.button>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Download, Github, Linkedin, Mail } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const Hero = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const handleDownloadCV = () => {
|
||||
// Ici, vous pouvez ajouter le lien vers votre CV
|
||||
const link = document.createElement('a');
|
||||
@@ -25,7 +28,7 @@ const Hero = () => {
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
>
|
||||
Dayron Van Leemput
|
||||
{t('hero.title')}
|
||||
</motion.h1>
|
||||
|
||||
<motion.h2
|
||||
@@ -34,7 +37,7 @@ const Hero = () => {
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
>
|
||||
Étudiant en Technologies de l'Informatique
|
||||
{t('hero.subtitle')}
|
||||
</motion.h2>
|
||||
|
||||
<motion.p
|
||||
@@ -43,8 +46,7 @@ const Hero = () => {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.8 }}
|
||||
>
|
||||
Bac 3 à la HELHa de Tournai | Jeune développeur passionné par les nouvelles technologies
|
||||
et le développement d'applications innovantes
|
||||
{t('hero.description')}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
@@ -60,7 +62,7 @@ const Hero = () => {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<Download size={20} />
|
||||
Télécharger mon CV
|
||||
{t('btn.downloadCV')}
|
||||
</motion.button>
|
||||
|
||||
<motion.a
|
||||
@@ -74,7 +76,7 @@ const Hero = () => {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<Mail size={20} />
|
||||
Me contacter
|
||||
{t('btn.contactMe')}
|
||||
</motion.a>
|
||||
</motion.div>
|
||||
|
||||
@@ -96,7 +98,7 @@ const Hero = () => {
|
||||
</motion.a>
|
||||
|
||||
<motion.a
|
||||
href="https://linkedin.com/in/dayronvanleemput" // Remplacez par votre profil LinkedIn
|
||||
href="https://www.linkedin.com/in/dayron-van-leemput-992a94398" // Remplacez par votre profil LinkedIn
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="social-link"
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { ExternalLink, Github, MapPin } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const Projects = () => {
|
||||
const { t } = useLanguage();
|
||||
const projects = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Travel Mate",
|
||||
description: "Application mobile conçue pour simplifier l'organisation de voyages de groupe. Elle permet de centraliser toutes les informations importantes d'un voyage : planification, gestion des dépenses, découverte d'activités et coordination entre les participants.",
|
||||
status: "Bientôt disponible sur App Store et Play Store",
|
||||
title: t('projects.travelMate.title'),
|
||||
description: t('projects.travelMate.description'),
|
||||
status: t('projects.status.available'),
|
||||
technologies: ["Dart", "Flutter", "Firebase"],
|
||||
features: [
|
||||
"Planification de voyage collaborative",
|
||||
"Gestion des dépenses partagées",
|
||||
"Découverte d'activités locales",
|
||||
"Coordination en temps réel"
|
||||
t('projects.travelMate.feature1'),
|
||||
t('projects.travelMate.feature2'),
|
||||
t('projects.travelMate.feature3'),
|
||||
t('projects.travelMate.feature4')
|
||||
],
|
||||
color: "#4CAF50",
|
||||
icon: <MapPin size={24} />,
|
||||
@@ -25,15 +27,15 @@ const Projects = () => {
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Portfolio Web",
|
||||
description: "Site web personnel moderne et responsive développé avec React et TypeScript. Inclut des animations fluides, un mode sombre/clair et une architecture modulaire.",
|
||||
status: "Projet actuel",
|
||||
title: t('projects.portfolio.title'),
|
||||
description: t('projects.portfolio.description'),
|
||||
status: t('projects.status.current'),
|
||||
technologies: ["React", "TypeScript", "Framer Motion", "CSS3"],
|
||||
features: [
|
||||
"Design responsive",
|
||||
"Animations fluides",
|
||||
"Mode sombre/clair",
|
||||
"Performance optimisée"
|
||||
t('projects.portfolio.feature1'),
|
||||
t('projects.portfolio.feature2'),
|
||||
t('projects.portfolio.feature3'),
|
||||
t('projects.portfolio.feature4')
|
||||
],
|
||||
color: "#2196F3",
|
||||
icon: <ExternalLink size={24} />,
|
||||
@@ -77,9 +79,9 @@ const Projects = () => {
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="section-title">Mes Projets</h2>
|
||||
<h2 className="section-title">{t('projects.title')}</h2>
|
||||
<p className="section-subtitle">
|
||||
Découvrez les projets sur lesquels j'ai travaillé et qui me tiennent à cœur
|
||||
{t('projects.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -138,7 +140,7 @@ const Projects = () => {
|
||||
</div>
|
||||
|
||||
<div className="project-features">
|
||||
<h4>Fonctionnalités principales :</h4>
|
||||
<h4>{t('projects.features')}</h4>
|
||||
<ul>
|
||||
{project.features.map((feature, featureIndex) => (
|
||||
<motion.li
|
||||
@@ -170,7 +172,7 @@ const Projects = () => {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<Github size={20} />
|
||||
Code
|
||||
{t('projects.btn.code')}
|
||||
</motion.a>
|
||||
)}
|
||||
{project.links.demo !== "#" && (
|
||||
@@ -183,7 +185,7 @@ const Projects = () => {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<ExternalLink size={20} />
|
||||
Voir le projet
|
||||
{t('projects.btn.viewProject')}
|
||||
</motion.a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Code, Database, Smartphone, Globe, Server, Wrench } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const Skills = () => {
|
||||
const { t } = useLanguage();
|
||||
const skillCategories = [
|
||||
{
|
||||
icon: <Smartphone size={32} />,
|
||||
title: "Mobile",
|
||||
title: t('skills.category.mobile'),
|
||||
color: "#4FC3F7",
|
||||
skills: [
|
||||
{ name: "Dart", level: 85, color: "#0175C2" },
|
||||
@@ -14,7 +16,7 @@ const Skills = () => {
|
||||
},
|
||||
{
|
||||
icon: <Globe size={32} />,
|
||||
title: "Frontend",
|
||||
title: t('skills.category.frontend'),
|
||||
color: "#42A5F5",
|
||||
skills: [
|
||||
{ name: "React", level: 75, color: "#61DAFB" },
|
||||
@@ -24,7 +26,7 @@ const Skills = () => {
|
||||
},
|
||||
{
|
||||
icon: <Server size={32} />,
|
||||
title: "Backend",
|
||||
title: t('skills.category.backend'),
|
||||
color: "#66BB6A",
|
||||
skills: [
|
||||
{ name: "Java", level: 75, color: "#ED8B00" },
|
||||
@@ -33,7 +35,7 @@ const Skills = () => {
|
||||
},
|
||||
{
|
||||
icon: <Database size={32} />,
|
||||
title: "Outils & Autres",
|
||||
title: t('skills.category.tools'),
|
||||
color: "#AB47BC",
|
||||
skills: [
|
||||
{ name: "Git", level: 70, color: "#F05032" },
|
||||
@@ -76,9 +78,9 @@ const Skills = () => {
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="section-title">Compétences & Technologies</h2>
|
||||
<h2 className="section-title">{t('skills.title')}</h2>
|
||||
<p className="section-subtitle">
|
||||
Les technologies que je maîtrise et avec lesquelles j'aime travailler
|
||||
{t('skills.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -157,13 +159,13 @@ const Skills = () => {
|
||||
transition={{ duration: 0.8, delay: 0.3 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h3 className="soft-skills-title">Autres compétences</h3>
|
||||
<h3 className="soft-skills-title">{t('skills.otherSkills')}</h3>
|
||||
<div className="soft-skills-grid">
|
||||
{[
|
||||
{ name: "Résolution de problèmes", icon: <Wrench size={20} /> },
|
||||
{ name: "Travail en équipe", icon: <Code size={20} /> },
|
||||
{ name: "Apprentissage continu", icon: <Database size={20} /> },
|
||||
{ name: "Communication", icon: <Globe size={20} /> }
|
||||
{ name: t('skills.problemSolving'), icon: <Wrench size={20} /> },
|
||||
{ name: t('skills.teamwork'), icon: <Code size={20} /> },
|
||||
{ name: t('skills.continuousLearning'), icon: <Database size={20} /> },
|
||||
{ name: t('skills.communication'), icon: <Globe size={20} /> }
|
||||
].map((softSkill, index) => (
|
||||
<motion.div
|
||||
key={softSkill.name}
|
||||
|
||||
Reference in New Issue
Block a user