mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-05-29 22:37:44 +02:00
feat: translate Curriculum Vitae in both English and French
This commit is contained in:
@ -9,9 +9,9 @@ export interface CurriculumVitaeProps {}
|
||||
|
||||
export const CurriculumVitae: React.FC<CurriculumVitaeProps> = () => {
|
||||
return (
|
||||
<main className="curriculum-vitae container-fluid">
|
||||
<div className="row main clearfix">
|
||||
<section className="col-md-3 card-wrapper profile-card-wrapper affix">
|
||||
<main className="curriculum-vitae mx-auto px-4 text-sm">
|
||||
<div className="-mx-4 p-2">
|
||||
<section className="col-md-3 card-wrapper relative">
|
||||
<CurriculumVitaeProfile />
|
||||
|
||||
<div className="card background-card">
|
||||
@ -20,14 +20,14 @@ export const CurriculumVitae: React.FC<CurriculumVitaeProps> = () => {
|
||||
|
||||
<hr />
|
||||
|
||||
<section className="section-separated">
|
||||
<section className="flex">
|
||||
<CurriculumVitaeEducation />
|
||||
<CurriculumVitaeSkills />
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section className="section-separated">
|
||||
<section className="flex">
|
||||
<CurriculumVitaeWork />
|
||||
<CurriculumVitaeInterests />
|
||||
</section>
|
||||
|
@ -9,13 +9,98 @@ export const CurriculumVitaeEducation: React.FC<
|
||||
> = () => {
|
||||
const t = useTranslations()
|
||||
|
||||
const educations = [
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2023-2024.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2023-2024.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.web"),
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.ci-cd"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2023-2024.courses.complexity-algorithms",
|
||||
),
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.no-sql"),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2022-2023.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2022-2023.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.web"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.tests"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.clean-code"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.systems-c"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2022-2023.courses.sql-security",
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2021-2022.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2021-2022.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.java"),
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.systems-c"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2021-2022.courses.windows-forms",
|
||||
),
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.sql"),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.lycee.years.2019-2021.title"),
|
||||
studyType: t("curriculum-vitae.education.lycee.study-type"),
|
||||
institution: t("curriculum-vitae.education.lycee.institution"),
|
||||
score: t("curriculum-vitae.education.lycee.score"),
|
||||
courses: [],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<CurriculumVitaeSection
|
||||
id="education"
|
||||
title={t("curriculum-vitae.education.title")}
|
||||
icon={<FaGraduationCap size={24} />}
|
||||
>
|
||||
<p>Test</p>
|
||||
<ul className="list-unstyled m-0">
|
||||
{educations.map((education) => {
|
||||
return (
|
||||
<li key={education.years} className="card card-nested">
|
||||
<div className="content">
|
||||
<p className="relative m-0">
|
||||
<strong>{education.studyType}</strong>
|
||||
</p>
|
||||
|
||||
<p className="relative m-0">
|
||||
<strong>{education.score}</strong>
|
||||
</p>
|
||||
|
||||
<p className="text-muted m-0">{education.institution}</p>
|
||||
|
||||
<p className="text-muted m-0">
|
||||
<small>{education.years}</small>
|
||||
</p>
|
||||
|
||||
{education.courses.length > 0 ? (
|
||||
<ul className="educational-courses">
|
||||
{education.courses.map((course) => {
|
||||
return <li key={course}>{course}</li>
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</CurriculumVitaeSection>
|
||||
)
|
||||
}
|
||||
|
@ -9,13 +9,28 @@ export const CurriculumVitaeInterests: React.FC<
|
||||
> = () => {
|
||||
const t = useTranslations()
|
||||
|
||||
const interests = [
|
||||
t("curriculum-vitae.interests.open-source"),
|
||||
t("curriculum-vitae.interests.high-tech"),
|
||||
]
|
||||
|
||||
return (
|
||||
<CurriculumVitaeSection
|
||||
id="interests"
|
||||
title={t("home.interests.title")}
|
||||
icon={<FaHeart size={24} />}
|
||||
>
|
||||
<p>Test</p>
|
||||
<ul className="list-unstyled m-0">
|
||||
{interests.map((interest) => {
|
||||
return (
|
||||
<li key={interest} className="card card-nested">
|
||||
<p>
|
||||
<strong>{interest}</strong>
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</CurriculumVitaeSection>
|
||||
)
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ export const CurriculumVitaeProfile: React.FC<
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<div className="card profile-card">
|
||||
<div className="card p-2">
|
||||
<div className="profile-pic-container">
|
||||
<div className="profile-pic">
|
||||
<Image
|
||||
className="media-object img-circle center-block"
|
||||
className="mx-auto block"
|
||||
alt={t("meta.title")}
|
||||
src="/images/logo_background.webp"
|
||||
width={800}
|
||||
@ -33,8 +33,8 @@ export const CurriculumVitaeProfile: React.FC<
|
||||
<h5 className="text-muted">{t("home.about.nationality.value")}</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div className="contact-details clearfix">
|
||||
<div className="detail">
|
||||
<div className="flex justify-center">
|
||||
<div className="relative px-3">
|
||||
<span className="info">
|
||||
<a className="link-disguise" href="mailto:contact@theoludwig.fr">
|
||||
contact@theoludwig.fr
|
||||
|
@ -14,7 +14,7 @@ export const CurriculumVitaeSection: React.FC<CurriculumVitaeSectionProps> = (
|
||||
<div className="icon">{icon}</div>
|
||||
|
||||
<div className="info">
|
||||
<h4 className="title text-uppercase">{title}</h4>
|
||||
<h4 className="font-semibold uppercase">{title}</h4>
|
||||
|
||||
<div className="content">{children}</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { useTranslations } from "next-intl"
|
||||
import { FaToolbox } from "react-icons/fa"
|
||||
import {
|
||||
SKILL_CATEGORIES,
|
||||
SKILL_NAMES_BY_CATEGORY,
|
||||
} from "../Home/Skills/skills"
|
||||
import { CurriculumVitaeSection } from "./CurriculumVitaeSection"
|
||||
|
||||
export interface CurriculumVitaeSkillsProps {}
|
||||
@ -9,13 +13,47 @@ export const CurriculumVitaeSkills: React.FC<
|
||||
> = () => {
|
||||
const t = useTranslations()
|
||||
|
||||
const skills = [
|
||||
...SKILL_CATEGORIES.map((category) => {
|
||||
const skillNames = SKILL_NAMES_BY_CATEGORY[category]
|
||||
return {
|
||||
category,
|
||||
skillNames,
|
||||
}
|
||||
}),
|
||||
{
|
||||
category: "others",
|
||||
skillNames: [t("locales.en-US"), t("home.skills.driving-license")],
|
||||
},
|
||||
] as const
|
||||
|
||||
return (
|
||||
<CurriculumVitaeSection
|
||||
id="skills"
|
||||
title={t("home.skills.title")}
|
||||
icon={<FaToolbox size={24} />}
|
||||
>
|
||||
<p>Test</p>
|
||||
<ul className="list-unstyled m-0">
|
||||
{skills.map(({ category, skillNames }) => {
|
||||
return (
|
||||
<li key={category} className="card card-nested relative">
|
||||
<div className="skill-info">
|
||||
<strong>{t(`home.skills.${category}`)}</strong>
|
||||
|
||||
<div className="labels mt-2">
|
||||
{skillNames.map((skillName) => {
|
||||
return (
|
||||
<p key={skillName} className="label label-keyword">
|
||||
{skillName}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</CurriculumVitaeSection>
|
||||
)
|
||||
}
|
||||
|
@ -7,13 +7,63 @@ export interface CurriculumVitaeWorkProps {}
|
||||
export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
||||
const t = useTranslations()
|
||||
|
||||
const workExperiences = [
|
||||
{
|
||||
summary: t("curriculum-vitae.work.ircad.summary"),
|
||||
website: "https://ircad.fr/",
|
||||
name: "IRCAD",
|
||||
location: "1 Place de l'Hôpital, FR-67000 Strasbourg",
|
||||
position: t("curriculum-vitae.work.ircad.position"),
|
||||
dates: "28/08/2023 - 02/09/2024",
|
||||
duration: t("curriculum-vitae.work.ircad.duration"),
|
||||
},
|
||||
{
|
||||
summary: t("curriculum-vitae.work.numerize.summary"),
|
||||
website: "https://numerize.com/",
|
||||
name: "Numerize",
|
||||
location: "4 Rue Sophie Germain, FR-67720 Hœrdt",
|
||||
position: t("curriculum-vitae.work.numerize.position"),
|
||||
dates: "11/04/2023 - 26/07/2023",
|
||||
duration: t("curriculum-vitae.work.numerize.duration"),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<CurriculumVitaeSection
|
||||
id="work-experience"
|
||||
title={t("curriculum-vitae.work.title")}
|
||||
icon={<MdWork size={24} />}
|
||||
>
|
||||
<p>Test</p>
|
||||
<ul className="list-unstyled m-0">
|
||||
{workExperiences.map((workExperience) => {
|
||||
return (
|
||||
<li key={workExperience.name} className="card card-nested">
|
||||
<p className="relative m-0">
|
||||
<strong>
|
||||
<a href={workExperience.website} target="_blank">
|
||||
{workExperience.name}
|
||||
</a>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p className="relative m-0">
|
||||
<strong>{workExperience.position}</strong>
|
||||
</p>
|
||||
|
||||
<p className="text-muted">
|
||||
<small>
|
||||
<span className="space-right">
|
||||
{workExperience.dates} ({workExperience.duration})
|
||||
</span>
|
||||
</small>
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<p>{workExperience.summary}</p>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</CurriculumVitaeSection>
|
||||
)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { useMemo } from "react"
|
||||
import { Link } from "../../Design/Link/Link"
|
||||
import { useTheme } from "../../Layout/Header/SwitchTheme"
|
||||
import type { SkillName } from "./skills"
|
||||
import { skills } from "./skills"
|
||||
import { SKILLS } from "./skills"
|
||||
|
||||
export interface SkillItemProps {
|
||||
skillName: SkillName
|
||||
@ -14,7 +14,7 @@ export interface SkillItemProps {
|
||||
export const SkillItem: React.FC<SkillItemProps> = (props) => {
|
||||
const { skillName } = props
|
||||
|
||||
const skill = skills[skillName]
|
||||
const skill = SKILLS[skillName]
|
||||
|
||||
const { theme } = useTheme()
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { useTranslations } from "next-intl"
|
||||
import { Section, SectionTitle } from "../../Layout/Section/Section"
|
||||
import { SkillItem } from "./SkillItem"
|
||||
import { SkillsSection } from "./SkillsSection"
|
||||
import { SKILL_CATEGORIES, SKILL_NAMES_BY_CATEGORY } from "./skills"
|
||||
|
||||
export interface SkillsProps {}
|
||||
|
||||
@ -12,34 +13,17 @@ export const Skills: React.FC<SkillsProps> = () => {
|
||||
<Section verticalSpacing horizontalSpacing id="skills">
|
||||
<SectionTitle>{t("home.skills.title")}</SectionTitle>
|
||||
|
||||
<SkillsSection title={t("home.skills.programming-languages")}>
|
||||
<SkillItem skillName="TypeScript" />
|
||||
<SkillItem skillName="Python" />
|
||||
<SkillItem skillName="C/C++" />
|
||||
<SkillItem skillName="PHP" />
|
||||
</SkillsSection>
|
||||
{SKILL_CATEGORIES.map((category) => {
|
||||
const skillNames = SKILL_NAMES_BY_CATEGORY[category]
|
||||
|
||||
<SkillsSection title={t("home.skills.frontend")}>
|
||||
<SkillItem skillName="HTML" />
|
||||
<SkillItem skillName="CSS" />
|
||||
<SkillItem skillName="Tailwind CSS" />
|
||||
<SkillItem skillName="React.js (+ Next.js)" />
|
||||
</SkillsSection>
|
||||
|
||||
<SkillsSection title={t("home.skills.backend")}>
|
||||
<SkillItem skillName="Laravel" />
|
||||
<SkillItem skillName="Node.js" />
|
||||
<SkillItem skillName="Fastify" />
|
||||
<SkillItem skillName="PostgreSQL" />
|
||||
</SkillsSection>
|
||||
|
||||
<SkillsSection title={t("home.skills.software-tools")}>
|
||||
<SkillItem skillName="GNU/Linux" />
|
||||
<SkillItem skillName="Arch Linux" />
|
||||
<SkillItem skillName="Visual Studio Code" />
|
||||
<SkillItem skillName="Git" />
|
||||
<SkillItem skillName="Docker" />
|
||||
</SkillsSection>
|
||||
return (
|
||||
<SkillsSection key={category} title={t(`home.skills.${category}`)}>
|
||||
{skillNames.map((skillName) => {
|
||||
return <SkillItem key={skillName} skillName={skillName} />
|
||||
})}
|
||||
</SkillsSection>
|
||||
)
|
||||
})}
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ export interface Skill {
|
||||
image: string | { [key: string]: string }
|
||||
}
|
||||
|
||||
export const skills = {
|
||||
export const SKILLS = {
|
||||
JavaScript: {
|
||||
link: "https://developer.mozilla.org/docs/Web/JavaScript",
|
||||
image: "/images/skills/JavaScript.webp",
|
||||
@ -112,4 +112,27 @@ export const skills = {
|
||||
},
|
||||
} as const
|
||||
|
||||
export type SkillName = keyof typeof skills
|
||||
export type SkillName = keyof typeof SKILLS
|
||||
|
||||
export const SKILL_CATEGORIES = [
|
||||
"programming-languages",
|
||||
"frontend",
|
||||
"backend",
|
||||
"software-tools",
|
||||
] as const
|
||||
export type SkillCategory = (typeof SKILL_CATEGORIES)[number]
|
||||
|
||||
export const SKILL_NAMES_BY_CATEGORY = {
|
||||
"programming-languages": ["TypeScript", "Python", "C/C++", "PHP"],
|
||||
frontend: ["HTML", "CSS", "Tailwind CSS", "React.js (+ Next.js)"],
|
||||
backend: ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
||||
"software-tools": [
|
||||
"GNU/Linux",
|
||||
"Arch Linux",
|
||||
"Visual Studio Code",
|
||||
"Git",
|
||||
"Docker",
|
||||
],
|
||||
} as const satisfies {
|
||||
[key in SkillCategory]: SkillName[]
|
||||
}
|
||||
|
Reference in New Issue
Block a user