From a2a7e6795486c41fcdd11e76d65890886f65b327 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 11:14:57 +0200 Subject: [PATCH 01/17] Generate localized .tex files using content data and i18n files --- .gitignore | 3 +- app/[locale]/layout.tsx | 7 -- components/About.tsx | 5 +- components/Banner.tsx | 3 +- components/Footer.tsx | 3 +- content/about.json | 10 ++ cv/cv_hendrik_schmitz_de.tex | 212 ----------------------------------- cv/cv_hendrik_schmitz_en.tex | 208 ---------------------------------- cv/generate-tex-files.ts | 180 +++++++++++++++++++++++++++++ messages/de.json | 5 + messages/en.json | 5 + package-lock.json | 212 ++++++++++++++++++++++++++++++++--- package.json | 7 +- tsconfig.node.json | 6 + util/date-time.ts | 26 +++++ 15 files changed, 445 insertions(+), 447 deletions(-) create mode 100644 content/about.json delete mode 100644 cv/cv_hendrik_schmitz_de.tex delete mode 100644 cv/cv_hendrik_schmitz_en.tex create mode 100644 cv/generate-tex-files.ts create mode 100644 tsconfig.node.json diff --git a/.gitignore b/.gitignore index 7c01ed155d..72ec9b221b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,6 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -# ignore generated pdf files +# ignore generated latex and pdf files +*.tex *.pdf \ No newline at end of file diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index eb3cd32343..b9c25bdcb6 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,15 +1,8 @@ -import type { Metadata } from "next"; import "bootstrap/dist/css/bootstrap.min.css"; import "font-awesome/css/font-awesome.min.css"; import { supportedLocales } from "@/util/i18n"; import "../globals.scss"; -// export const metadata: Metadata = { -// title: "Hendrik Schmitz | Portfolio", -// description: -// "Experienced Fullstack Software Engineer with a strong focus on Frontend Development. Explore my portfolio showcasing innovative web solutions and cutting-edge technology expertise.", -// }; - //function to generate the routes for all the locales export async function generateStaticParams() { return supportedLocales.map((locale) => ({ locale })); diff --git a/components/About.tsx b/components/About.tsx index 7ee261cb74..30132013a8 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -1,11 +1,12 @@ import Image from "next-export-optimize-images/image"; import experience from "@/content/experience.json"; +import about from "@/content/about.json"; import profilePic from "@/cv/profile_picture.jpg"; import styles from "./About.module.scss"; import { formatDate } from "@/util/date-time"; import { Locale, Messages } from "@/util/i18n"; -const birthDate = new Date("1998-06-30"); +const birthDate = new Date(about.birthday); const currentDate = new Date(); const difference = currentDate.getTime() - birthDate.getTime(); const ageDate = new Date(difference); @@ -38,7 +39,7 @@ export default function About({ Hendrik Schmitz
diff --git a/components/Banner.tsx b/components/Banner.tsx index 787a8ee993..35fe698e37 100644 --- a/components/Banner.tsx +++ b/components/Banner.tsx @@ -1,4 +1,5 @@ import { Locale, Messages } from "@/util/i18n"; +import about from "@/content/about.json"; import styles from "./Banner.module.scss"; export default function Banner({ @@ -11,7 +12,7 @@ export default function Banner({ return (
-

Hendrik Schmitz

+

{about.name}

{messages.banner.jobTitle}

-

Copyright © {currentYear} Hendrik Schmitz

+

Copyright © {currentYear} {about.name}

diff --git a/content/about.json b/content/about.json new file mode 100644 index 0000000000..5e5e02e86d --- /dev/null +++ b/content/about.json @@ -0,0 +1,10 @@ +{ + "name": "Hendrik Schmitz", + "birthday": "1998-06-30", + "phoneNumber": "+49 151 70013949", + "address": "Pontstr. 137, 52062 Aachen", + "website": "smtz.dev", + "linkedIn": "hendriksmtz", + "github": "drik98", + "mail": "hendrik@smtz.dev" +} \ No newline at end of file diff --git a/cv/cv_hendrik_schmitz_de.tex b/cv/cv_hendrik_schmitz_de.tex deleted file mode 100644 index e791fbb764..0000000000 --- a/cv/cv_hendrik_schmitz_de.tex +++ /dev/null @@ -1,212 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Twenty One Seconds Resume/CV -% LaTeX Template -% Version 1.2 (2024/06/26) -% -% This template has been downloaded from: -% http://www.LaTeXTemplates.com -% -% -% License -% Original author: -% Carmine Spagnuolo (cspagnuolo@unisa.it) with major modifications by -% Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) -%% Copyright 2022-now Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) -% -% This work may be distributed and/or modified under the -% conditions of the LaTeX Project Public License, either version 1.3 -% of this license or (at your option) any later version. -% The latest version of this license is in -% http://www.latex-project.org/lppl.txt -% and version 1.3 or later is part of all distributions of LaTeX -% version 2005/12/01 or later. -% -% This work has the LPPL maintenance status `maintained'. -% -% The Current Maintainer of this work is Alessandro Trinca Tornidor -% -% This work consists of the files template.tex and twentyonesecondcv.cls -% and the derived file twentyonesecondcv.pdf -% -% see https://github.com/trincadev/cv-latex-twentyoneseconds/ for updates - -%---------------------------------------------------------------------------------------- -% PACKAGES AND OTHER DOCUMENT CONFIGURATIONS -%---------------------------------------------------------------------------------------- - -\documentclass[letterpaper]{twentyonesecondcv} % a4paper for A4 - -\usepackage[shortlabels]{enumitem} - -\profilepic{profile_picture.jpg} % Profile picture -\cvjobtitle{Software Engineer} % Job title/career -\cvname{Hendrik Schmitz} % Your name - -%% Mandatory informations used by \makeinfoprofile. To hide these fields leave the contents of the macro empty (e.g. '\cvsitepersonal{}' instead than '\cvsitepersonal{en.wikipedia.org}') -%% See https://tex.stackexchange.com/a/692525/109031 -\cvbirthdate{30. Juni 1998} -\cvnumberphone{+49 151 70013949} -\cvaddressurl{} -\cvaddress{Pontstr. 137, 52062 Aachen} % Short address/location, use \newline if more than 1 line is required -\cvsitepersonal{smtz.dev} % personal site -\cvstackoverflow{} -\cvlinkedin{hendriksmtz} -\cvskypeurlbase{} % Skype -\cvskypeurl{} -\cvgithub{drik98} -\cvmail{hendrik@smtz.dev} - -\textfootersidenote{} - -\pagenumber{Seite}{von} - -\begin{document} -\sidesection{ - \makeheaderprofile - % \makeheaderprofilenoimg - \makeinfoprofile - % \aboutme{Short section about myself.} - % \customsidesection{Header profile section}{It's possible to hide the profile picture using \textbackslash\texttt{makeheaderprofilenoimg} instead than \textbackslash\texttt{makeheaderprofile} (you could remove \textbackslash\texttt{profilepic\{image.png\}} then).} - %\customsidesection{Info profile section}{The command \textbackslash\texttt{makeinfoprofile} doesn't use empty macros \textbackslash\texttt{cvsite*\{\}} (e.g. \textbackslash\texttt{cvsitepersonal\{\}} instead than \textbackslash\texttt{cvsitepersonal\{example.com\}}).} - %\customsidesection{About the info profile commands}{If not empty the \textbackslash\texttt{cvaddressurl\{\}} command create a custom hyperlink containing the \textbackslash\texttt{cvaddress\{Address, Nation\}} text. Same for \textbackslash\texttt{cvskypeurlbase\{join.skype.com/...\}} and \textbackslash\texttt{cvskypeurl\{skype-username\}} (visit \colorhrefcustom{https://support.skype.com/en/faq/FA34802/}{skype FAQs}) to create an invite url.} - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \customskills{Sprachen}{{Französisch (Konversationssicher)/3.5},{Englisch (Verhandlungssicher)/5},{Deutsch (Muttersprache)/6}}{} - - \customskills{Programmiersprachen}{{Shell/4},{Python/4},{Java\char`\/Kotlin/5.5},{CSS\char`\/SCSS/5.5},{JavaScript\char`\/Typescript/6}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} - - \makefootersidenodevfill - -} -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% for some reason it's impossible to have a new line here... -\mainsection{ - - \section{Über mich} - - In meiner achtjährigen beruflichen Laufbahn habe ich umfangreiche Erfahrungen in der Frontend-Entwicklung gesammelt, wobei ich mich auf Vue.js spezialisiert habe. Darüber hinaus verfüge ich über umfangreiche Kenntnisse im Back-End, da ich zuvor hauptsächlich mit Java und Kotlin gearbeitet habe. Diese Erfahrung ermöglicht es mir, sowohl bei der Frontend- als auch bei der Backend-Entwicklung effektiv zusammenzuarbeiten und nahtlose Integrationen zu gewährleisten. - \newline Dank meines fundierten Fachwissens und meiner umfangreichen Erfahrung bin ich in der Lage, auf effektive Weise mit verschiedenen Akteuren in der Softwareentwicklung zusammenzuarbeiten, darunter Product Owner, Designer, BackendEntwickler und DevOps-Experten. - - \section{Beruflicher Werdegang} - - \begin{twenty} - \twentyitem - {seit 2023} - {Senior Software Engineer} - {\newline itemis AG} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Schlüsselrolle bei der Gestaltung der Threat Analysis and Risk Assessment (TARA) durch itemis SECURE, mit dem Ziel der strikten Einhaltung der ISO 21434 Standards. - \item Vorantreiben der Mission, TARA mit einem benutzerfreundlichen Ansatz unter Verwendung von Vue.js in Verbindung mit modelix ins Web zu bringen. - \item Durchsetzung hoher Qualitätsstandards für sauberen Code und Best Practices in der Softwareentwicklung. - \item Weiterentwicklung des Tech-Stacks durch die Einführung neuer und fortschrittlicher Technologien. - \item Verbesserung der Entwicklungserfahrung durch die Optimierung von Workflows. - \item Entwicklung von APIs und Etablierung der Kommunikation zwischen Diensten mit Kotlin. - \end{itemize} - } - \twentyitem - {2020-2023} - {Software Engineer} - {\newline aixigo AG} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Leitung der Entwicklung von Micro Frontends, hauptsächlich unter Verwendung von Vue.js. - \item Federführung bei der Entwicklung und Konzeption von Fintech-Microservices mit Vue.js und Java EE. - \item Zentrale Rolle bei der Modernisierung des Tech-Stacks durch die Einführung von TypeScript und Storybook. - \item Einsatz für hochwertige Softwareentwicklung und Betonung von Clean Code. - \item Aktive Mitwirkung an der Verbesserung des CI/CD-Prozesses. - \item Verantwortung für die Einarbeitung neuer Teammitglieder, Bereitstellung von Anleitung und Coaching für eine reibungslose Integration. - \item Teilnahme an einem Mentorenprogramm, um sich als Führungskraft weiterzuentwickeln. - \end{itemize} - } - \twentyitem - {2019-2020} - {Software Engineer (Studentische Hilfskraft)} - {\newline Werkzeugmaschinenlabor WZL der RWTH Aachen} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Förderung der Modernisierung des Tech-Stacks durch Proof of Concepts und die Einführung von Frameworks wie Vue.js und Spring Boot. - \item Etablierung eines standardisierten CI/CD-Prozesses. - \item Einarbeitung, Unterstützung und Mentoring für neue Auszubildende und Kollegen. - \end{itemize} - } - \end{twenty} - -%% end main section -} -% \newpage - -\clearpage % mandatory to make it work the command '\pagenumber' - -% \noindent -\sidesection{ - \makeheaderprofilenoimg - - - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \customskills{Frameworks \& Technologien}{{React/4},{Docker\char`\/Kubernetes/4},{Nuxt/4.5},{CI\char`\/CD/5},{Quarkus\char`\/Spring/5.5},{Vue.js/6}}{} - - \customskills{Agile Entwicklung}{{Product Owner/2},{Kanban/3},{Scrum Master/4},{Scrum/5}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} - - \makefooterprofile{} - - \makefootersidenode - -} -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% for some reason there is no way to have a new line here... -\mainsection{ - - \section{Beruflicher Werdegang} - - \begin{twenty} - \twentyitem - {2016-2019} - {Duales Studium Mathematisch-technischer Softwareentwickler} - {\newline Werkzeugmaschinenlabor WZL der RWTH Aachen} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Entwicklung von Java-Webanwendungen für industrielle Anwendungen, mit Schwerpunkt auf Frontend-Entwicklung von Software-Demonstratoren in Industrie 4.0. - \item Nutzung von Technologien wie JavaScript, HTML, CSS, Java, PostgreSQL und Hibernate. - \item Erfassung von Anforderungen und Konzeption von Anwendungen. - \item Betreuung von Auszubildenden. - \end{itemize} - } - \end{twenty} - - \section{Ausbildung und Studium} - - \begin{twentymid} % Environment for a list with descriptions - \twentyitem - {2016-2020} - {B.Sc. Scientific Programming} - {\newline FH Aachen - University of Applied Sciences } - {Thesis: Entwicklung von Bewertungsansätzen für ausgewählte Process Mining Algorithmen in der Auftragsabwicklung} - \vspace{-3mm} - \twentyitem - {2016-2019} - {Ausbildung zum Mathematisch-technischem Softwareentwickler} - {\newline Industrie- und Handelskammer (IHK) Aachen} - {} - \vspace{-3mm} - \twentyitem - {2016} - {Abitur} - {\newline Dalton Gymnasium Alsdorf} - {} - %\vspace{-12mm} - - \end{twentymid} - -} -\end{document} \ No newline at end of file diff --git a/cv/cv_hendrik_schmitz_en.tex b/cv/cv_hendrik_schmitz_en.tex deleted file mode 100644 index baa2c27406..0000000000 --- a/cv/cv_hendrik_schmitz_en.tex +++ /dev/null @@ -1,208 +0,0 @@ - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Twenty One Seconds Resume/CV -% LaTeX Template -% Version 1.2 (2024/06/26) -% -% This template has been downloaded from: -% http://www.LaTeXTemplates.com -% -% -% License -% Original author: -% Carmine Spagnuolo (cspagnuolo@unisa.it) with major modifications by -% Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) -%% Copyright 2022-now Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) -% -% This work may be distributed and/or modified under the -% conditions of the LaTeX Project Public License, either version 1.3 -% of this license or (at your option) any later version. -% The latest version of this license is in -% http://www.latex-project.org/lppl.txt -% and version 1.3 or later is part of all distributions of LaTeX -% version 2005/12/01 or later. -% -% This work has the LPPL maintenance status `maintained'. -% -% The Current Maintainer of this work is Alessandro Trinca Tornidor -% -% This work consists of the files template.tex and twentyonesecondcv.cls -% and the derived file twentyonesecondcv.pdf -% -% see https://github.com/trincadev/cv-latex-twentyoneseconds/ for updates - -%---------------------------------------------------------------------------------------- -% PACKAGES AND OTHER DOCUMENT CONFIGURATIONS -%---------------------------------------------------------------------------------------- - -\documentclass[letterpaper]{twentyonesecondcv} % a4paper for A4 - -\usepackage[shortlabels]{enumitem} - -\profilepic{profile_picture.jpg} % Profile picture -\cvjobtitle{Software Engineer} % Job title/career -\cvname{Hendrik Schmitz} % Your name - -%% Mandatory informations used by \makeinfoprofile. To hide these fields leave the contents of the macro empty (e.g. '\cvsitepersonal{}' instead than '\cvsitepersonal{en.wikipedia.org}') -%% See https://tex.stackexchange.com/a/692525/109031 -\cvbirthdate{30 June 1998} -\cvnumberphone{+49 151 70013949} -\cvaddressurl{} -\cvaddress{Pontstr. 137, 52062 Aachen} % Short address/location, use \newline if more than 1 line is required -\cvsitepersonal{smtz.dev} % personal site -\cvstackoverflow{} -\cvlinkedin{hendriksmtz} -\cvskypeurlbase{} % Skype -\cvskypeurl{} -\cvgithub{drik98} -\cvmail{hendrik@smtz.dev} - -\textfootersidenote{} - -\pagenumber{Page}{of} - -\begin{document} -\sidesection{ - \makeheaderprofile - % \makeheaderprofilenoimg - \makeinfoprofile - % \aboutme{Short section about myself.} - % \customsidesection{Header profile section}{It's possible to hide the profile picture using \textbackslash\texttt{makeheaderprofilenoimg} instead than \textbackslash\texttt{makeheaderprofile} (you could remove \textbackslash\texttt{profilepic\{image.png\}} then).} - %\customsidesection{Info profile section}{The command \textbackslash\texttt{makeinfoprofile} doesn't use empty macros \textbackslash\texttt{cvsite*\{\}} (e.g. \textbackslash\texttt{cvsitepersonal\{\}} instead than \textbackslash\texttt{cvsitepersonal\{example.com\}}).} - %\customsidesection{About the info profile commands}{If not empty the \textbackslash\texttt{cvaddressurl\{\}} command create a custom hyperlink containing the \textbackslash\texttt{cvaddress\{Address, Nation\}} text. Same for \textbackslash\texttt{cvskypeurlbase\{join.skype.com/...\}} and \textbackslash\texttt{cvskypeurl\{skype-username\}} (visit \colorhrefcustom{https://support.skype.com/en/faq/FA34802/}{skype FAQs}) to create an invite url.} - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \customskills{Languages}{{French (Intermediate)/3.5},{English (Mastery)/5},{German (Native)/6}}{} - - \customskills{Programming Languages}{{Shell/4},{Python/4},{Java\char`\/Kotlin/5.5},{CSS\char`\/SCSS/5.5},{JavaScript\char`\/Typescript/6}}{Scale: 0 (basic skills) - 6 (expert)} - - \makefootersidenodevfill - -} -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% for some reason it's impossible to have a new line here... -\mainsection{ - - \section{About} - - In my eight-year professional career, I have gained extensive experience in frontend development, specializing in Vue.js. In addition, I have extensive knowledge of the back-end, having primarily worked with Java and Kotlin before. This experience allows me to collaborate effectively on both front-end and back-end development and ensure seamless integrations. - \newline Thanks to my in-depth expertise and extensive experience, I am able to work effectively with various stakeholders in software development, including product owners, designers, backend developers and DevOps experts. - - \section{Experiences} - - \begin{twenty} - \twentyitem - {since 2023} - {Senior Software Engineer} - {\newline itemis AG} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Key role in shaping the trajectory of Threat Analysis and Risk Assessment (TARA) through itemis SECURE, aiming for strict adherence to ISO 21434 standards. - \item Driving the mission to bring TARA to the web with a user-friendly approach using Vue.js in conjunction with modelix. - \item Enforcing high-quality standards of clean code and best practices in software development. - \item Evolving the tech stack by introducing newer and more advanced technologies. - \item Enhancing the development experience by streamlining workflows for fellow developers. - \item Developing APIs and establishing cross-service communication using Kotlin. - \end{itemize} - } - \twentyitem - {2020-2023} - {Software Engineer} - {\newline aixigo AG} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Led the development of Micro Frontends primarily using Vue.js. - \item Spearheaded the development and conceptualization of Fintech Microservices using Vue.js and Java EE. - \item Played a central role in modernizing the tech stack by introducing TypeScript and Storybook. - \item Advocated for high-quality software development, emphasizing code excellence. - \item Actively contributed to enhancing the CI/CD process. - \item Took charge of onboarding new team members, providing guidance and coaching for smooth integration. - \item Participation in mentoring program to develop as a leader. - \end{itemize} - } - \twentyitem - {2019-2020} - {Software Engineer (Student Assistant)} - {\newline Werkzeugmaschinenlabor WZL der RWTH Aachen} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Promoted modernization of the tech stack through proof of concepts and introducing frameworks like Vue.js and Spring Boot. - \item Established a standardized CI/CD process. - \item Provided training, teaching, and support to new apprentices and colleagues. - \end{itemize} - } - \twentyitem - {2016-2019} - {Dual Study Mathematical-technical software developer} - {\newline Werkzeugmaschinenlabor WZL der RWTH Aachen} - { - \vspace{-3mm} - \begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] - \item Contributed to the development of Java web applications for industrial applications, focusing on frontend development of software demonstrators in Industry 4.0. - \item Utilized technologies such as JavaScript, HTML, CSS, Java, PostgreSQL and Hibernate. - \item Recording of requirements and conception of applications. - \item Supervision of trainees - \end{itemize} - } - \end{twenty} - -%% end main section -} -% \newpage - -\clearpage % mandatory to make it work the command '\pagenumber' - -% \noindent -\sidesection{ - \makeheaderprofilenoimg - - - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \customskills{Frameworks \& Technologies}{{React/4},{Docker\char`\/Kubernetes/4},{Nuxt/4.5},{CI\char`\/CD/5},{Quarkus\char`\/Spring/5.5},{Vue.js/6}}{} - - \customskills{Agile Development}{{Product Owner/2},{Kanban/3},{Scrum Master/4},{Scrum/5}}{Scale: 0 (basic skills) - 6 (expert)} - - \makefooterprofile{} - - \makefootersidenode - -} -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% for some reason there is no way to have a new line here... -\mainsection{ - - \section{Other Education and certifications} - - \begin{twentymid} % Environment for a list with descriptions - \twentyitem - {2016-2020} - {B.Sc. Scientific Programming} - {\newline FH Aachen - University of Applied Sciences } - {Thesis: Development of evaluation approaches for selected process mining algorithms in order processing.} - \vspace{-3mm} - \twentyitem - {2016-2019} - {Training as mathematical-technical software developer} - {\newline Industrie- und Handelskammer (IHK) Aachen} - {} - \vspace{-3mm} - \twentyitem - {2016} - {Abitur} - {\newline Dalton Gymnasium Alsdorf} - {} - %\vspace{-12mm} - - \end{twentymid} - -} -\end{document} \ No newline at end of file diff --git a/cv/generate-tex-files.ts b/cv/generate-tex-files.ts new file mode 100644 index 0000000000..07899a95a6 --- /dev/null +++ b/cv/generate-tex-files.ts @@ -0,0 +1,180 @@ +import fs from 'fs'; +import messages from '@/messages/de.json' +import about from "@/content/about.json" +import experience from "@/content/experience.json" +import education from "@/content/education.json" +import { formatDateRangeCV } from '@/util/date-time'; +import { getMultilingualContent, supportedLocales } from '@/util/i18n'; + +for(const locale of supportedLocales) { + const birthday = new Intl.DateTimeFormat(locale, { + dateStyle: "long" + }).format(new Date(about.birthday)); + + const localizedTemplate = String.raw` +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Twenty One Seconds Resume/CV +% LaTeX Template +% Version 1.2 (2024/06/26) +% +% This template has been downloaded from: +% http://www.LaTeXTemplates.com +% +% +% License +% Original author: +% Carmine Spagnuolo (cspagnuolo@unisa.it) with major modifications by +% Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) +%% Copyright 2022-now Alessandro Trinca Tornidor (alessandro at trinca dot tornidor dot com) +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status 'maintained'. +% +% The Current Maintainer of this work is Alessandro Trinca Tornidor +% +% This work consists of the files template.tex and twentyonesecondcv.cls +% and the derived file twentyonesecondcv.pdf +% +% see https://github.com/trincadev/cv-latex-twentyoneseconds/ for updates + +%---------------------------------------------------------------------------------------- +% PACKAGES AND OTHER DOCUMENT CONFIGURATIONS +%---------------------------------------------------------------------------------------- + +\documentclass[letterpaper]{twentyonesecondcv} % a4paper for A4 + +\usepackage[shortlabels]{enumitem} + +\profilepic{profile_picture.jpg} % Profile picture +\cvjobtitle{${messages.banner.jobTitle}} % Job title/career +\cvname{${about.name}} % Your name + +%% Mandatory informations used by \makeinfoprofile. To hide these fields leave the contents of the macro empty (e.g. '\cvsitepersonal{}' instead than '\cvsitepersonal{en.wikipedia.org}') +%% See https://tex.stackexchange.com/a/692525/109031 +\cvbirthdate{${birthday}} +\cvnumberphone{${about.phoneNumber}} +\cvaddressurl{} +\cvaddress{${about.address}} % Short address/location, use \newline if more than 1 line is required +\cvsitepersonal{${about.website}} % personal site +\cvstackoverflow{} +\cvlinkedin{${about.linkedIn}} +\cvskypeurlbase{} % Skype +\cvskypeurl{} +\cvgithub{${about.github}} +\cvmail{${about.mail}} + +\textfootersidenote{} + +\pagenumber{${messages.cv.pageNumber}}{${messages.cv.pageOf}} + +\begin{document} +\sidesection{ + \makeheaderprofile + % \makeheaderprofilenoimg + \makeinfoprofile + % \aboutme{Short section about myself.} + % \customsidesection{Header profile section}{It's possible to hide the profile picture using \textbackslash\texttt{makeheaderprofilenoimg} instead than \textbackslash\texttt{makeheaderprofile} (you could remove \textbackslash\texttt{profilepic\{image.png\}} then).} + %\customsidesection{Info profile section}{The command \textbackslash\texttt{makeinfoprofile} doesn't use empty macros \textbackslash\texttt{cvsite*\{\}} (e.g. \textbackslash\texttt{cvsitepersonal\{\}} instead than \textbackslash\texttt{cvsitepersonal\{example.com\}}).} + %\customsidesection{About the info profile commands}{If not empty the \textbackslash\texttt{cvaddressurl\{\}} command create a custom hyperlink containing the \textbackslash\texttt{cvaddress\{Address, Nation\}} text. Same for \textbackslash\texttt{cvskypeurlbase\{join.skype.com/...\}} and \textbackslash\texttt{cvskypeurl\{skype-username\}} (visit \colorhrefcustom{https://support.skype.com/en/faq/FA34802/}{skype FAQs}) to create an invite url.} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \customskills{Sprachen}{{Französisch (Konversationssicher)/3.5},{Englisch (Verhandlungssicher)/5},{Deutsch (Muttersprache)/6}}{} + + \customskills{Programmiersprachen}{{Shell/4},{Python/4},{Java\/Kotlin/5.5},{CSS\/SCSS/5.5},{JavaScript\/Typescript/6}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} + + \makefootersidenodevfill + +} +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% for some reason it's impossible to have a new line here... +\mainsection{ + + \section{${messages.header.sections.about}} + + ${messages.about.carrer.join('\\newline ')} + + \section{${messages.header.sections.experience}} + + \begin{twenty}${experience.map(exp => ( +` + \\twentyitem + {${formatDateRangeCV(new Date(exp.startDate), exp.endDate ? new Date(exp.endDate) : undefined, locale, messages)}} + {${getMultilingualContent(exp.title, locale)}} + {\\newline ${getMultilingualContent(exp.company, locale)}} + { + \\vspace{-3mm} + \\begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] +${getMultilingualContent(exp.keyPoints, locale).map(item => +` \\item ${item}`).join("\n")} + \\end{itemize} + }` +)).join("")} + \end{twenty} + +%% end main section +} +% \newpage + +\clearpage % mandatory to make it work the command '\pagenumber' + +% \noindent +\sidesection{ + \makeheaderprofilenoimg + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \customskills{Frameworks \& Technologien}{{React/4},{Docker\/Kubernetes/4},{Nuxt/4.5},{CI\/CD/5},{Quarkus\/Spring/5.5},{Vue.js/6}}{} + + \customskills{Agile Entwicklung}{{Product Owner/2},{Kanban/3},{Scrum Master/4},{Scrum/5}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} + + \makefooterprofile{} + + \makefootersidenode + +} +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% for some reason there is no way to have a new line here... +\mainsection{ + + \section{${messages.header.sections.education}} + + \begin{twentymid}${education.map(ed => ( +` + \\twentyitem + {${formatDateRangeCV(new Date(ed.startDate), ed.endDate ? new Date(ed.endDate) : undefined, locale, messages)}} + {${getMultilingualContent(ed.title, locale)}} + {\\newline ${getMultilingualContent(ed.educator, locale)}} + { + \\vspace{-3mm} + \\begin{itemize}[leftmargin=5mm,noitemsep,topsep=0pt] +${getMultilingualContent(ed.keyPoints, locale).map(item => +` \\item ${item}`).join("\n")} + \\end{itemize} + }` + )).join("")} + \end{twentymid} +} +\end{document} +`; + +const filePath = `./cv/cv_${about.name.toLocaleLowerCase().split(/\s+/).join("_")}_${locale}.tex`; +try { + fs.writeFileSync(filePath, localizedTemplate, 'utf8'); + console.log('File has been written to ' + filePath); +} catch (err) { + console.error('Error writing file: ' + filePath, err); +} +} diff --git a/messages/de.json b/messages/de.json index 4bbe21ee95..e7ffe1b0f4 100644 --- a/messages/de.json +++ b/messages/de.json @@ -41,5 +41,10 @@ "email": "E-Mail", "message": "Nachricht", "submit": "Absenden" + }, + "cv": { + "pageNumber": "Seite", + "pageOf": "von", + "since": "seit" } } \ No newline at end of file diff --git a/messages/en.json b/messages/en.json index 37389bece2..483aa1e4a7 100644 --- a/messages/en.json +++ b/messages/en.json @@ -41,5 +41,10 @@ "email": "E-Mail", "message": "Message", "submit": "Send" + }, + "cv": { + "pageNumber": "Page", + "pageOf": "of", + "since": "since" } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7153285b9b..9820bcbf36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,21 @@ "eslint-config-next": "14.2.5", "next-export-optimize-images": "^4.5.0", "serve": "^14.2.3", - "typescript": "^5" + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.5.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" } }, "node_modules/@emnapi/runtime": { @@ -604,6 +618,31 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@next/env": { "version": "14.2.5", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", @@ -818,6 +857,30 @@ "tslib": "^2.4.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1031,6 +1094,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1776,6 +1851,12 @@ "node": ">= 0.6" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1959,6 +2040,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2402,6 +2492,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2411,6 +2513,18 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", @@ -3750,15 +3864,15 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonfile": { @@ -3872,6 +3986,12 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5390,16 +5510,67 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", + "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/tslib": { @@ -5566,6 +5737,12 @@ "punycode": "^2.1.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5793,6 +5970,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5806,4 +5992,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index f6f81a1922..4bd651f43f 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "dev": "next dev", + "prebuild": "ts-node --project ./tsconfig.node.json -r tsconfig-paths/register ./cv/generate-tex-files.ts", "build": "next build && next-export-optimize-images", "start": "serve out", "lint": "next lint" @@ -27,6 +28,8 @@ "eslint-config-next": "14.2.5", "next-export-optimize-images": "^4.5.0", "serve": "^14.2.3", - "typescript": "^5" + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.5.4" } -} \ No newline at end of file +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000000..55a0181250 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + } +} diff --git a/util/date-time.ts b/util/date-time.ts index 6f1e4707c9..f515270dd1 100644 --- a/util/date-time.ts +++ b/util/date-time.ts @@ -23,3 +23,29 @@ export function formatDateRange( .map((date) => formatDate(date, locale, messages)) .join(" - "); } + +function formatDateCV( + date?: Date, + locale = defaultLocale +): string | null{ + if (!date) return null; + return new Intl.DateTimeFormat(locale, { + year: "numeric", + }).format(date); +} + +export function formatDateRangeCV( + startDate?: Date, + endDate?: Date, + locale = defaultLocale, + messages = defaultMessages +): string { + const [formattedStartDate, formattedEndDate] = [startDate, endDate].map((date) => formatDateCV(date, locale)); + if(!formattedStartDate && !formattedEndDate) { + return ""; + } + if(!formattedEndDate) { + return `${messages.cv.since} ${formattedStartDate}`; + } + return `${formattedStartDate}-${formattedEndDate}`; +} \ No newline at end of file From fa6a84cbe0916f12c808b9a914b4e3dc5f84dc54 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 11:19:39 +0200 Subject: [PATCH 02/17] dynamically generate pdfs for all locales --- .github/workflows/build-and-deploy.yml | 22 +++++++++++----------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 697ce0f344..53e01f838e 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -6,21 +6,23 @@ jobs: steps: - name: Set up Git repository uses: actions/checkout@v4 - - name: Compile LaTeX document (de) - uses: xu-cheng/latex-action@v3 + - name: Install dependencies + run: npm install + - name: Generate localized LaTeX templates + run: npm run generate-latex + - name: Upload generated LaTeX templates + uses: actions/upload-artifact@v4 with: - root_file: cv/cv_hendrik_schmitz_de.tex - continue_on_error: "true" - env: - TEXINPUTS: ".:./cv//:" - - name: Compile LaTeX document (en) + name: latex-templates + path: cv/cv_hendrik_schmitz_*.tex + - name: Compile LaTeX documents uses: xu-cheng/latex-action@v3 with: - root_file: cv/cv_hendrik_schmitz_en.tex + root_file: cv/cv_hendrik_schmitz_*.tex continue_on_error: "true" env: TEXINPUTS: ".:./cv//:" - - name: Upload PDF files + - name: Upload generated PDF files uses: actions/upload-artifact@v4 with: name: cvs @@ -29,8 +31,6 @@ jobs: run: | mkdir -p public cp -R *.pdf public/ - - name: Install dependencies - run: npm install - name: Build Next.js app run: npm run build - name: Deploy to Netlify diff --git a/package.json b/package.json index 4bd651f43f..1b40f3f104 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "private": true, "scripts": { "dev": "next dev", - "prebuild": "ts-node --project ./tsconfig.node.json -r tsconfig-paths/register ./cv/generate-tex-files.ts", "build": "next build && next-export-optimize-images", "start": "serve out", - "lint": "next lint" + "lint": "next lint", + "generate-latex": "ts-node --project ./tsconfig.node.json -r tsconfig-paths/register ./cv/generate-tex-files.ts" }, "dependencies": { "bootstrap": "^3.3.7", From 9e50d5f38ccdf03cf35f523ec4e5d4f486210f7f Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 11:38:18 +0200 Subject: [PATCH 03/17] =?UTF-8?q?Fix=20broken=20unicode=20=C3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/education.json | 2 +- content/experience.json | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/content/education.json b/content/education.json index 9508b1d04b..be426cbff0 100644 --- a/content/education.json +++ b/content/education.json @@ -36,7 +36,7 @@ "image": "/images/experience/wzl.png", "keyPoints": { "de": [ - "Entwicklung von Java-Webanwendungen für industrielle Anwendungen, mit Schwerpunkt auf Frontend-Entwicklung von Software- Demonstratoren in Industrie 4.0", + "Entwicklung von Java-Webanwendungen für industrielle Anwendungen, mit Schwerpunkt auf Frontend-Entwicklung von Software- Demonstratoren in Industrie 4.0", "Nutzung von Technologien wie JavaScript, HTML, CSS, Java, PostgreSQL und Hibernate", "Erfassung von Anforderungen und Konzeption von Anwendungen", "Betreuung von Auszubildenden" diff --git a/content/experience.json b/content/experience.json index 906899c0d8..bfe73d011b 100644 --- a/content/experience.json +++ b/content/experience.json @@ -10,10 +10,10 @@ "image": "/images/experience/itemis.svg", "keyPoints": { "de": [ - "Schlüsselrolle bei der Gestaltung der Threat Analysis and Risk Assessment (TARA) durch itemis SECURE, mit dem Ziel der strikten Einhaltung der ISO 21434 Standards", + "Schlüsselrolle bei der Gestaltung der Threat Analysis and Risk Assessment (TARA) durch itemis SECURE, mit dem Ziel der strikten Einhaltung der ISO 21434 Standards", "Vorantreiben der Mission, TARA mit einem benutzerfreundlichen Ansatz unter Verwendung von Vue.js in Verbindung mit modelix ins Web zu bringen", - "Durchsetzung hoher Qualitätsstandards für Clean Code und Best Practices in der Softwareentwicklung", - "Weiterentwicklung des Tech-Stacks durch die Einführung neuer und fortschrittlicher Technologien", + "Durchsetzung hoher Qualitätsstandards für Clean Code und Best Practices in der Softwareentwicklung", + "Weiterentwicklung des Tech-Stacks durch die Einführung neuer und fortschrittlicher Technologien", "Verbesserung der Entwicklungserfahrung durch die Optimierung von Workflows", "Entwicklung von APIs und Etablierung der Kommunikation zwischen Diensten mit Kotlin" ], @@ -40,12 +40,12 @@ "keyPoints": { "de": [ "Leitung der Entwicklung von Micro Frontends, hauptsächlich unter Verwendung von Vue.js", - "Federführung bei der Entwicklung und Konzeption von Fintech-Microservices mit Vue.js und Java EE", - "Zentrale Rolle bei der Modernisierung des Tech-Stacks durch die Einführung von TypeScript und Storybook", - "Einsatz für hochwertige Softwareentwicklung und Betonung von Clean Code", + "Federführung bei der Entwicklung und Konzeption von Fintech-Microservices mit Vue.js und Java EE", + "Zentrale Rolle bei der Modernisierung des Tech-Stacks durch die Einführung von TypeScript und Storybook", + "Einsatz für hochwertige Softwareentwicklung und Betonung von Clean Code", "Aktive Mitwirkung an der Verbesserung des CI/CD-Prozesses", - "Verantwortung für die Einarbeitung neuer Teammitglieder, Bereitstellung von Anleitung und Coaching für eine reibungslose Integration", - "Teilnahme an einem Mentorenprogramm, um sich als Führungskraft weiterzuentwickeln" + "Verantwortung für die Einarbeitung neuer Teammitglieder, Bereitstellung von Anleitung und Coaching für eine reibungslose Integration", + "Teilnahme an einem Mentorenprogramm, um sich als Führungskraft weiterzuentwickeln" ], "en": [ "Led the development of Micro Frontends primarily using Vue.js.", @@ -73,9 +73,9 @@ "image": "/images/experience/wzl.png", "keyPoints": { "de": [ - "Förderung der Modernisierung des Tech-Stacks durch Proof of Concepts und die Einführung von Frameworks wie Vue.js und Spring Boot", + "Förderung der Modernisierung des Tech-Stacks durch Proof of Concepts und die Einführung von Frameworks wie Vue.js und Spring Boot", "Etablierung eines standardisierten CI/CD-Prozesses", - "Einarbeitung, Unterstützung und Mentoring für neue Auszubildende und Kollegen" + "Einarbeitung, Unterstützung und Mentoring für neue Auszubildende und Kollegen" ], "en": [ "Promoted modernization of the tech stack through proof of concepts and introducing frameworks like Vue.js and Spring Boot.", From 3d4aa4d50f8198e5f4cbfb67bdf7ec366c86d11c Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 11:58:13 +0200 Subject: [PATCH 04/17] Fix missing translation for about section --- app/[locale]/page.tsx | 11 +---------- content/experience.json | 5 ++++- cv/generate-tex-files.ts | 9 +++++++-- util/i18n.ts | 9 +++++++++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 129d7e50b7..69fdbb14f1 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -1,6 +1,5 @@ -import { notFound } from "next/navigation"; import { Metadata } from "next"; -import { Locale, Messages } from "@/util/i18n"; +import { Locale, getMessages } from "@/util/i18n"; import About from "@/components/About"; import Banner from "@/components/Banner"; @@ -44,11 +43,3 @@ export default async function Home({ ); } - -async function getMessages(locale: string): Promise { - try { - return (await import(`../../messages/${locale}.json`)).default; - } catch (error) { - notFound(); - } -} diff --git a/content/experience.json b/content/experience.json index bfe73d011b..f88dbdfef2 100644 --- a/content/experience.json +++ b/content/experience.json @@ -59,7 +59,10 @@ } }, { - "company": "Werkzeugmaschinenlabor WZL der RWTH Aachen", + "company": { + "de": "Werkzeugmaschinenlabor WZL der RWTH Aachen", + "en": "Laboratory for Machine Tools and Production Engineering (WZL) of RWTH Aachen University" + }, "url": { "de": "https://www.wzl.rwth-aachen.de/", "en": "https://www.wzl.rwth-aachen.de/cms/~sijq/wzl/?lidx=1" diff --git a/cv/generate-tex-files.ts b/cv/generate-tex-files.ts index 07899a95a6..1742466c07 100644 --- a/cv/generate-tex-files.ts +++ b/cv/generate-tex-files.ts @@ -1,12 +1,17 @@ import fs from 'fs'; -import messages from '@/messages/de.json' import about from "@/content/about.json" import experience from "@/content/experience.json" import education from "@/content/education.json" import { formatDateRangeCV } from '@/util/date-time'; -import { getMultilingualContent, supportedLocales } from '@/util/i18n'; +import { Locale, getMessages, getMultilingualContent, supportedLocales } from '@/util/i18n'; for(const locale of supportedLocales) { + generateTemplate(locale); +} + +async function generateTemplate(locale: Locale) { + const messages = await getMessages(locale); + const birthday = new Intl.DateTimeFormat(locale, { dateStyle: "long" }).format(new Date(about.birthday)); diff --git a/util/i18n.ts b/util/i18n.ts index 1ace54cacd..a554095c03 100644 --- a/util/i18n.ts +++ b/util/i18n.ts @@ -1,4 +1,5 @@ import defaultMessages from "@/messages/de.json"; +import { notFound } from "next/navigation"; export type Messages = typeof defaultMessages; export enum Locale { @@ -38,3 +39,11 @@ export function getMultilingualContent( } return content; } + +export async function getMessages(locale: string): Promise { + try { + return (await import(`@/messages/${locale}.json`)).default; + } catch (error) { + notFound(); + } +} From f050075d82528ca1b110f5efc1e80e622579822d Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 12:02:18 +0200 Subject: [PATCH 05/17] Fix typing issues --- components/About.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/About.tsx b/components/About.tsx index 30132013a8..e098f9bc70 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -4,7 +4,7 @@ import about from "@/content/about.json"; import profilePic from "@/cv/profile_picture.jpg"; import styles from "./About.module.scss"; import { formatDate } from "@/util/date-time"; -import { Locale, Messages } from "@/util/i18n"; +import { Locale, Messages, getMultilingualContent } from "@/util/i18n"; const birthDate = new Date(about.birthday); const currentDate = new Date(); @@ -28,7 +28,7 @@ export default function About({ formatDate(new Date(currentJob.startDate), locale, messages) ) .replace("{jobTitle}", String(currentJob.title)) - .replace("{company}", currentJob.company); + .replace("{company}", getMultilingualContent(currentJob.company, locale)); return (
From 71097b811860d2ad856975aecbb732150481408f Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 12:07:30 +0200 Subject: [PATCH 06/17] Fix typing issues for job title --- components/About.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/About.tsx b/components/About.tsx index e098f9bc70..ede7178882 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -27,7 +27,7 @@ export default function About({ "{startDate}", formatDate(new Date(currentJob.startDate), locale, messages) ) - .replace("{jobTitle}", String(currentJob.title)) + .replace("{jobTitle}",getMultilingualContent(currentJob.title, locale)) .replace("{company}", getMultilingualContent(currentJob.company, locale)); return ( From d2eb61dcb8c31c69e19f52224c48f1f584e0df08 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 12:11:54 +0200 Subject: [PATCH 07/17] Use information from about-content to fill out social links --- components/About.tsx | 2 +- components/Footer.tsx | 64 ++++++++++++++++++++++++------------------- content/about.json | 1 + 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/components/About.tsx b/components/About.tsx index ede7178882..6f0ccdbaec 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -27,7 +27,7 @@ export default function About({ "{startDate}", formatDate(new Date(currentJob.startDate), locale, messages) ) - .replace("{jobTitle}",getMultilingualContent(currentJob.title, locale)) + .replace("{jobTitle}", getMultilingualContent(currentJob.title, locale)) .replace("{company}", getMultilingualContent(currentJob.company, locale)); return ( diff --git a/components/Footer.tsx b/components/Footer.tsx index 3abc4ee4e3..1bf71b3ed7 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -9,7 +9,9 @@ export default function Footer() {
diff --git a/content/about.json b/content/about.json index 5e5e02e86d..372c9a7b85 100644 --- a/content/about.json +++ b/content/about.json @@ -5,6 +5,7 @@ "address": "Pontstr. 137, 52062 Aachen", "website": "smtz.dev", "linkedIn": "hendriksmtz", + "xing": "Hendrik_Schmitz29", "github": "drik98", "mail": "hendrik@smtz.dev" } \ No newline at end of file From 05d89fea72b37748be0891ccf50cf561518d1408 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 12:21:24 +0200 Subject: [PATCH 08/17] escape backticks --- cv/generate-tex-files.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cv/generate-tex-files.ts b/cv/generate-tex-files.ts index 1742466c07..0fbcb2480f 100644 --- a/cv/generate-tex-files.ts +++ b/cv/generate-tex-files.ts @@ -94,7 +94,7 @@ async function generateTemplate(locale: Locale) { %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \customskills{Sprachen}{{Französisch (Konversationssicher)/3.5},{Englisch (Verhandlungssicher)/5},{Deutsch (Muttersprache)/6}}{} - \customskills{Programmiersprachen}{{Shell/4},{Python/4},{Java\/Kotlin/5.5},{CSS\/SCSS/5.5},{JavaScript\/Typescript/6}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} + \customskills{Programmiersprachen}{{Shell/4},{Python/4},{Java\char${"`"}\/Kotlin/5.5},{CSS\char${"`"}\/SCSS/5.5},{JavaScript\char${"`"}\/Typescript/6}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} \makefootersidenodevfill @@ -141,7 +141,7 @@ ${getMultilingualContent(exp.keyPoints, locale).map(item => %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%Skill bar section, each skill must have a value between 0 an 6 (float)%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \customskills{Frameworks \& Technologien}{{React/4},{Docker\/Kubernetes/4},{Nuxt/4.5},{CI\/CD/5},{Quarkus\/Spring/5.5},{Vue.js/6}}{} + \customskills{Frameworks \& Technologien}{{React/4},{Docker\char${"`"}\/Kubernetes/4},{Nuxt/4.5},{CI\char${"`"}\/CD/5},{Quarkus\char${"`"}\/Spring/5.5},{Vue.js/6}}{} \customskills{Agile Entwicklung}{{Product Owner/2},{Kanban/3},{Scrum Master/4},{Scrum/5}}{Skala: 0 (Grundkenntnisse) - 6 (Experte)} From 335b0cb8e423e99e56a3254371acd9ece02e2a54 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Thu, 1 Aug 2024 13:32:20 +0200 Subject: [PATCH 09/17] Generate skill sections --- app/[locale]/page.tsx | 2 +- components/Skills.tsx | 19 ++++- content/skill-categories.json | 51 +++++++++++++ content/skills.json | 134 ++++++++++++++++++++++------------ cv/generate-tex-files.ts | 41 +++++++++-- 5 files changed, 193 insertions(+), 54 deletions(-) create mode 100644 content/skill-categories.json diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 69fdbb14f1..0e718398f0 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -37,7 +37,7 @@ export default async function Home({ - +
);