aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--astro.config.mjs22
-rw-r--r--src/data/blog/mapa-facil-protomaps.md72
-rw-r--r--src/i18n/en.json89
-rw-r--r--src/i18n/es.json89
-rw-r--r--src/i18n/index.ts34
-rw-r--r--src/layouts/ContactPageLayout.astro99
-rw-r--r--src/layouts/HomePageLayout.astro62
-rw-r--r--src/layouts/Layout.astro28
-rw-r--r--src/layouts/PortfolioPageLayout.astro113
-rw-r--r--src/layouts/TrajectoryPageLayout.astro81
-rw-r--r--src/pages/blog.xml.ts (renamed from src/pages/blog.xml.js)38
-rw-r--r--src/pages/en/contact.astro5
-rw-r--r--src/pages/en/index.astro5
-rw-r--r--src/pages/en/portfolio.astro5
-rw-r--r--src/pages/en/trajectory.astro5
-rw-r--r--src/pages/index.astro68
-rw-r--r--src/pages/portfolio/index.astro145
-rw-r--r--src/pages/trajectory.astro110
-rw-r--r--src/partials/Footer.astro26
-rw-r--r--src/partials/Header.astro17
-rw-r--r--styles/shared.scss3
21 files changed, 716 insertions, 400 deletions
diff --git a/astro.config.mjs b/astro.config.mjs
index 843c997..12285a1 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -2,16 +2,30 @@ import sitemap from '@astrojs/sitemap';
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
+import { DEFAULT_LANGUAGE, LANGUAGE_CODES } from './src/i18n';
export default defineConfig({
compressHTML: true,
site: "https://www.costas.dev",
- integrations: [sitemap({
- priority: 0.5,
- changefreq: 'weekly'
- }), mdx()],
+ i18n: {
+ defaultLocale: DEFAULT_LANGUAGE,
+ locales: LANGUAGE_CODES,
+ routing: {
+ prefixDefaultLocale: false,
+ fallbackType: 'redirect',
+ redirectToDefaultLocale: true
+ }
+ },
+ integrations: [
+ sitemap({
+ priority: 0.5,
+ changefreq: 'weekly'
+ }),
+ mdx()
+ ],
build: {
assets: 'assets',
inlineStylesheets: 'never',
},
+ scopedStyleStrategy: 'where'
}); \ No newline at end of file
diff --git a/src/data/blog/mapa-facil-protomaps.md b/src/data/blog/mapa-facil-protomaps.md
index 43f5309..acdc13b 100644
--- a/src/data/blog/mapa-facil-protomaps.md
+++ b/src/data/blog/mapa-facil-protomaps.md
@@ -46,46 +46,46 @@ Si estás usando tecnologías JavaScript (ya sea Vite con algún framework, o As
```html
<!DOCTYPE html>
<html lang="es">
- <head>
- <!-- ... -->
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.0/dist/maplibre-gl.min.css" />
- <style>
- #map {
- height: 100vh;
- width: 100vw;
- }
- </style>
- </head>
-
- <body>
- <div id="map"></div>
+ <head>
+ <!-- ... -->
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.0/dist/maplibre-gl.min.css" />
+ <style>
+ #map {
+ height: 100vh;
+ width: 100vw;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="map"></div>
- <script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.0/dist/maplibre-gl.min.js"></script>
- <script src="https://cdn.jsdelivr.net/npm/pmtiles-protocol@1.0.4/index.min.js"></script>
- <script>
- /*
- * Si estás usando paquetes de npm o un importmap o similar, puedes importar los módulos así:
- * import maplibregl from "maplibre-gl";
- * import * as pmtiles from "pmtiles";
- */
+ <script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.0/dist/maplibre-gl.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/pmtiles-protocol@1.0.4/index.min.js"></script>
+ <script>
+ /*
+ * Si estás usando paquetes de npm o un importmap o similar, puedes importar los módulos así:
+ * import maplibregl from "maplibre-gl";
+ * import * as pmtiles from "pmtiles";
+ */
- // Añadir el protocolo de pmtiles a MapLibre
- let protocol = new pmtiles.Protocol();
- maplibregl.addProtocol('pmtiles', protocol.tile);
+ // Añadir el protocolo de pmtiles a MapLibre
+ let protocol = new pmtiles.Protocol();
+ maplibregl.addProtocol('pmtiles', protocol.tile);
- // Crear el mapa
- const map = new maplibregl.Map({
- container: 'map',
- style: '/miestilo.json',
- bounds: [
- [2.0428702462744956, 41.263970142291704],
- [2.243836537167097, 41.48757944350839]
- ]
- });
- </script>
- </body>
+ // Crear el mapa
+ const map = new maplibregl.Map({
+ container: 'map',
+ style: '/miestilo.json',
+ bounds: [
+ [2.0428702462744956, 41.263970142291704],
+ [2.243836537167097, 41.48757944350839]
+ ]
+ });
+ </script>
+ </body>
</html>
Y luego el archivo `miestilo.json` puedes descargarlo de [maps.protomaps.com](https://maps.protomaps.com/#map=0.89/0/0&theme=light&lang=en&tiles=https://demo-bucket.protomaps.com/v4.pmtiles&local_sprites=true) clicando en "Get style JSON" y guardándolo en tu servidor. Cambias la ruta a tu pmtiles y ya. Y puedes usar cualquier otro estilo para MapLibre, como los de [OpenMapTiles](https://openmaptiles.org/styles/) mismamente, o hacer uno propio con algo como [Maputnik](https://maputnik.github.io/editor/).
-Y con esto, ya tienes un mapa vectorial personalizado de Barcelona, servido desde tu servidor estático, sin tener que preocuparte de configurar un servidor de mapas ni costes adicionales. Y puedes personalizarlo todo lo que quieras, desde los colores hasta los datos que se muestran. Sin cargar pesados SDKs desde CDNs, sin depender de terceros, y sin el riesgo de una factura deorbitada porque te pasaste de peticiones. \ No newline at end of file
+Y con esto, ya tienes un mapa vectorial personalizado de Barcelona, servido desde tu servidor estático, sin tener que preocuparte de configurar un servidor de mapas ni costes adicionales. Y puedes personalizarlo todo lo que quieras, desde los colores hasta los datos que se muestran. Sin cargar pesados SDKs desde CDNs, sin depender de terceros, y sin el riesgo de una factura deorbitada porque te pasaste de peticiones.
diff --git a/src/i18n/en.json b/src/i18n/en.json
new file mode 100644
index 0000000..1c4c1b1
--- /dev/null
+++ b/src/i18n/en.json
@@ -0,0 +1,89 @@
+{
+ "header": {
+ "home": "Home",
+ "trajectory": "Trajectory",
+ "portfolio": "Portfolio",
+ "blog": "Blog",
+ "contact": "Contact"
+ },
+ "footer": {
+ "copyright": "All rights reserved.",
+ "contentLicencedUnder": "Unless otherwise stated, the content of this site is licensed under",
+ "sourceCodeAvailableOn": "Source code for this site is available on",
+ "andIsLicencedUnder": "and is licenced under",
+ "eupl": "European Union Public Licence"
+ },
+ "contactPage": {
+ "title": "Contact",
+ "description": "The ways to contact me, either by email, phone or social networks.",
+ "headline": "Get in touch with me!",
+ "enableJs": "JavaScript must be enabled to view the email address and phone number. This is done to prevent scrapers and spam.",
+ "intro": "The easiest way to contact me is through my email address: <a href=\"#\" id=\"email-addr\">Enable JS</a>. You can also use <a href=\"https://wa.me/message/W7T7L4EZAELQI1\">WhatsApp</a> with the phone number <a href=\"#\" id=\"phone-number\">Enable JS</a>.",
+ "socialMedia": "You can also find me on some social networks:"
+ },
+ "homePage": {
+ "title": "Home",
+ "description": "Homepage of my website",
+ "welcome": "Welcome to my website. My name is Ariel, and here you will find information about me and my projects.",
+ "whoAmI": "Who am I?",
+ "whoAmIDesc": "I am a web developer who likes to learn new things and share knowledge. I enjoy programming, web design, and creativity. I love creating new things and learning from others.",
+ "moreAboutMe": "More about me",
+ "whatIDo": "What do I do?",
+ "whatIDoDesc": "I currently work as a software developer and Cloud administrator at a technology company. I develop web applications in PHP and deploy them in the cloud securely and efficiently.",
+ "myPortfolio": "My portfolio",
+ "latestBlogPosts": "Latest blog posts",
+ "viewAllPosts": "View all posts"
+ },
+ "trajectoryPage": {
+ "title": "Trajectory",
+ "description": "My trajectory as a software developer, with information about my education, work experience and projects I have worked on.",
+ "headline": "My trajectory as a developer",
+ "intro": "I am a software developer living in Vigo, Spain. I love technology and I enjoy learning new things. I am professionally dedicated to software development in all its aspects: from architecture design, implementation and production deployment, to other aspects such as user experience, accessibility and security.",
+ "techTitle": "Technologies I master",
+ "techDescription1": "I mainly master the Microsoft ecosystem: .NET (C#), ASP.NET Core, SQL Server, Azure and Azure DevOps. I also have experience with other languages and technologies, such as PHP, Python and Java. Additionally, I have frontend knowledge with HTML5, CSS3, JavaScript and TypeScript.",
+ "techDescription2": "I also have experience with DevOps tools such as Docker, Kubernetes, Terraform and GitHub Actions; as well as the Azure Cloud ecosystem.",
+ "educationTitle": "Education and credentials",
+ "efsetCert1": "Official EF SET C2 Proficient Certificate",
+ "efsetCert2": ": certifies my C2 English level, the highest according to the Common European Framework of Reference for Languages.",
+ "viewCertificate": "View certificate",
+ "azureDeveloperCert1": "Microsoft Certified: Azure Developer Associate",
+ "azureDeveloperCert2": ": certifies my knowledge in Azure application development.",
+ "viewCredential": "View credential",
+ "azureDevOpsCert1": "Microsoft Certified: Azure DevOps Engineer Expert",
+ "azureDevOpsCert2": ": certifies my knowledge in implementing DevOps methodologies in Azure with Azure DevOps and GitHub.",
+ "higherTechCert1": "Higher Technical Certificate in Multiplatform Application Development",
+ "higherTechCert2": ": higher vocational training degree, obtained at",
+ "higherTechCert3": "IES de Teis",
+ "higherTechCert4": "in Vigo.",
+ "experienceTitle": "Work experience",
+ "estelaria1": "Estelaria Solutions (Q3 2023 - present)",
+ "estelaria2": "Full-stack application development, mainly in PHP with Symfony, MongoDB and Vanilla JavaScript. In addition, I implemented a considerable number of improvements in usability, accessibility and performance.",
+ "estelaria3": "I am also responsible for managing the infrastructure on AWS, and implementing collaboration tools such as Jira and GitHub.",
+ "polygon1": "Internship at Polygon-E (Q2 2023)",
+ "polygon2": "I did my professional training internship at Polygon-E, where I developed several internal management applications with ASP.NET Core and Blazor, deploying on on-premise environments with Windows Server, IIS and SQL Server.",
+ "projectsTitle": "Projects",
+ "projectsDescription": "You can find the (public) projects I have worked on in <a href=\"/portfolio\">my portfolio</a> and on my <a href=\"https://github.com/arielcostas\">GitHub profile</a>."
+ },
+ "portfolioPage": {
+ "title": "Portfolio",
+ "description": "A list of projects I have worked on in recent years, with the technologies used.",
+ "headline": "My portfolio",
+ "intro": "In this section, you will find a list of the projects I have worked on, both for third parties and my own. If you would like more information about any of them, please do not hesitate to <a href=\"/contact\">contact me</a>.",
+ "freelanceTitle": "Projects for third parties (freelance)",
+ "freelanceDesc": "I have developed projects for third parties by commission, the most notable being the following:",
+ "orderExtractorTitle": "Online Order Purchase Extractor",
+ "orderExtractorDesc": "Desktop application that extracts data about online orders from various platforms (such as WooCommerce, Amazon, and eBay). <a href=\"/portfolio/order-extractor\">More information</a>.",
+ "touristInfoTitle": "Tourist Information Point on Mobile Devices",
+ "touristInfoDesc": "Web application for tourist information, with QR codes, 360° content, and YouTube embedding. <a href=\"/portfolio/dynamic-tourist-info\">More information</a>.",
+ "wpConsultingTitle": "WordPress Consulting",
+ "wpConsultingDesc": "Maintenance, optimisation, and migration work for WordPress websites and WooCommerce online shops. <a href=\"/portfolio/wp-consulting\">More information</a>. <a href=\"/contact\">Contact</a>.",
+ "ownProjectsTitle": "My own projects",
+ "ownProjectsDesc": "Additionally, I have several personal projects that I have developed in my own time, some of which are active and open source.",
+ "personalWebTitle": "Personal Website",
+ "personalWebDesc": "Developed with Astro, a static site generator that lets you write content in Markdown and publish to the web with excellent performance. Deployed on Azure Static Web Apps.",
+ "mientrenoTitle": "MiEntreno (end-of-course project)",
+ "mientrenoDesc": "Web application for managing sports training, with a simple and easy-to-use interface. Developed with ASP.NET Core, Razor Pages, and SQL Server. <a href=\"/portfolio/mientreno\">More information</a>. <a href=\"https://github.com/arielcostas/mientreno\">Source code</a>.",
+ "vigo360Title": "Vigo 360",
+ "vigo360Desc": "Blog about Vigo and its surroundings, mainly focused on mobility and toponymy. Developed in Go, with a MySQL database and deployed on a VPS managed by myself. <a href=\"/portfolio/vigo-360\">More information</a>. <a href=\"https://github.com/arielcostas/vigo360\">Source code</a>. <a href=\"https://vigo360.es\">Website</a>."
+ }
+} \ No newline at end of file
diff --git a/src/i18n/es.json b/src/i18n/es.json
new file mode 100644
index 0000000..0e6e8bb
--- /dev/null
+++ b/src/i18n/es.json
@@ -0,0 +1,89 @@
+{
+ "header": {
+ "home": "Inicio",
+ "trajectory": "Trayectoria",
+ "portfolio": "Portafolio",
+ "blog": "Blog",
+ "contact": "Contacto"
+ },
+ "footer": {
+ "copyright": "Todos los derechos reservados.",
+ "contentLicencedUnder": "Salvo que se indique lo contrario, el contenido de este sitio está bajo licencia",
+ "sourceCodeAvailableOn": "El código fuente de este sitio está disponible en",
+ "andIsLicencedUnder": "y se ofrece bajo licencia",
+ "eupl": "Licencia Pública de la Unión Europea"
+ },
+ "contactPage": {
+ "title": "Contacto",
+ "description": "Las formas de ponerte en contacto conmigo, ya sea por correo electrónico, teléfono o redes sociales.",
+ "headline": "¡Ponte en contacto conmigo!",
+ "enableJs": "Es necesario activar JavaScript para ver la dirección de correo electrónico y el número de teléfono. Esto se hace para evitar scrapers y spam.",
+ "intro": "La forma más sencilla de contactar conmigo es a través de mi dirección de correo electrónico: <a href=\"#\" id=\"email-addr\">Activa JS</a>. También puedes usar <a href=\"https://wa.me/message/W7T7L4EZAELQI1\">WhatsApp</a> con el número de teléfono <a href=\"#\" id=\"phone-number\">Activa JS</a>.",
+ "socialMedia": "También puedes encontrarme en algunas redes sociales:"
+ },
+ "homePage": {
+ "title": "Inicio",
+ "description": "Página de inicio de mi web",
+ "welcome": "Te doy la bienvenida a mi web. Me llamo Ariel, y aquí encontrarás información sobre mí y mis proyectos.",
+ "whoAmI": "¿Quién soy?",
+ "whoAmIDesc": "Soy un desarrollador web que le gusta aprender cosas nuevas y compartir su conocimiento. Me gusta la programación, el diseño web y la creatividad. Me encanta crear cosas nuevas y aprender de los demás.",
+ "moreAboutMe": "Más información sobre mí",
+ "whatIDo": "¿Qué hago?",
+ "whatIDoDesc": "Actualmente trabajo como desarrollador de software y administrador Cloud en una empresa de tecnología. Me encargo de desarrollar aplicaciones web en PHP y desplegarlas en la nube de forma segura y eficiente.",
+ "myPortfolio": "Mi portfolio",
+ "latestBlogPosts": "Últimas entradas del blog",
+ "viewAllPosts": "Ver todas las entradas"
+ },
+ "trajectoryPage": {
+ "title": "Trayectoria",
+ "description": "Mi trayectoria como desarrollador de software, con información sobre mi educación, experiencia laboral y proyectos en los que he trabajado.",
+ "headline": "Mi trayectoria como desarrollador",
+ "intro": "Soy un desarrollador de software que vive en Vigo, España. Me gusta mucho la tecnología, y me gusta aprender cosas nuevas. Me dedico profesionalmente al desarrollo de software en todos sus ámbitos: desde el diseño de la arquitectura, la implementación y el despliegue en producción, pasando por otros aspectos como la experiencia de usuario, la accesibilidad y la seguridad.",
+ "techTitle": "Tecnologías que domino",
+ "techDescription1": "Domino principalmente el ecosistema de Microsoft: .NET (C#), ASP.NET Core, SQL Server, Azure y Azure DevOps. También tengo experiencia con otros lenguajes y tecnologías, como PHP, Python y Java. Además, tengo conocimientos de frontend con HTML5, CSS3, JavaScript y TypeScript.",
+ "techDescription2": "También tengo experiencia con herramientas de DevOps como Docker, Kubernetes, Terraform y GitHub Actions; así como el ecosistema Cloud de Azure.",
+ "educationTitle": "Educación y credenciales",
+ "efsetCert1": "Certificado oficial EF SET C2 Proficient",
+ "efsetCert2": ": acredita mi nivel de inglés C2, el más alto según el Marco Común Europeo de Referencia para las Lenguas.",
+ "viewCertificate": "Ver certificado",
+ "azureDeveloperCert1": "Microsoft Certified: Azure Developer Associate",
+ "azureDeveloperCert2": ": acredita mis conocimientos en el desarrollo de aplicaciones en Azure.",
+ "viewCredential": "Ver credencial",
+ "azureDevOpsCert1": "Microsoft Certified: Azure DevOps Engineer Expert",
+ "azureDevOpsCert2": ": acredita mis conocimientos en la implementación de metodologías de DevOps en Azure con Azure DevOps y GitHub.",
+ "higherTechCert1": "Técnico Superior en Desarrollo de Aplicaciones Multiplataforma",
+ "higherTechCert2": ": título de formación profesional de grado superior, obtenido en el",
+ "higherTechCert3": "IES de Teis",
+ "higherTechCert4": "en Vigo.",
+ "experienceTitle": "Experiencia laboral",
+ "estelaria1": "Estelaria Solutions (Q3 2023 - actualidad)",
+ "estelaria2": "Desarrollo de aplicaciones full-stack, principalmente en PHP con Symfony, MongoDB y JavaScript Vanilla. Además, implementé una cantidad considerable de mejoras en usabilidad, accesibilidad y rendimiento.",
+ "estelaria3": "También me encargo de la gestión de la infraestructura en AWS, y de la implementación de herramientas de colaboración como Jira y GitHub.",
+ "polygon1": "FCT en Polygon-E (Q2 2023)",
+ "polygon2": "Realicé mis prácticas de formación profesional en la empresa Polygon-E, donde desarrollé varias aplicaciones de gestión interna con ASP.NET Core y Blazor, desplegando sobre entornos on-premise con Windows Server, IIS y SQL Server.",
+ "projectsTitle": "Proyectos",
+ "projectsDescription": "Puedes encontrar los proyectos (públicos) en los que he trabajado en <a href=\"/portfolio\">mi portfolio</a> y en mi <a href=\"https://github.com/arielcostas\">perfil de GitHub</a>."
+ },
+ "portfolioPage": {
+ "title": "Portfolio",
+ "description": "Un listado de los proyectos en los que he trabajado en los últimos años, con las tecnologías utilizadas.",
+ "headline": "Mi portfolio",
+ "intro": "En esta sección encontrarás una lista de los proyectos en los que he trabajado, tanto para terceros como propios. Si quieres más información sobre alguno de ellos, no dudes en <a href=\"/contact\">contactar conmigo</a>.",
+ "freelanceTitle": "Proyectos para terceros (freelance)",
+ "freelanceDesc": "He realizado desarrollos de proyectos para terceros por encargo, siendo los más destacados los siguientes:",
+ "orderExtractorTitle": "Extractor de pedidos compra online",
+ "orderExtractorDesc": "Aplicación de escritorio que extrae los datos sobre los pedidos on-line de diversas plataformas (como WooCommerce, Amazon y Ebay). <a href=\"/portfolio/order-extractor\">Más información</a>.",
+ "touristInfoTitle": "Punto de información turística en móviles",
+ "touristInfoDesc": "Aplicación web de información turística, con QR, contenido en 360º e incrustado de YouTube. <a href=\"/portfolio/dynamic-tourist-info\">Más información</a>.",
+ "wpConsultingTitle": "Consultoría WordPress",
+ "wpConsultingDesc": "Trabajos de mantenimiento, optimización y migración de sitios web WordPress y tiendas online WooCommerce. <a href=\"/portfolio/wp-consulting\">Más información</a>. <a href=\"/contact\">Contactar</a>.",
+ "ownProjectsTitle": "Proyectos propios",
+ "ownProjectsDesc": "Además, tengo varios proyectos propios que he desarrollado en mi tiempo, estando algunos de ellos en activo, y como código abierto.",
+ "personalWebTitle": "Web personal",
+ "personalWebDesc": "Desarrollado con Astro, un generador de sitios web estáticos que permite escribir contenido en Markdown y publicar en la web con un rendimiento excelente. Desplegado sobre Azure Static Web Apps.",
+ "mientrenoTitle": "MiEntreno (proyecto fin de ciclo)",
+ "mientrenoDesc": "Aplicación web para la gestión de entrenamientos deportivos, con una interfaz sencilla y fácil de usar. Desarrollado con ASP.NET Core, Razor Pages y SQL Server. <a href=\"/portfolio/mientreno\">Más información</a>. <a href=\"https://github.com/arielcostas/mientreno\">Código fuente</a>.",
+ "vigo360Title": "Vigo 360",
+ "vigo360Desc": "Blog sobre Vigo y su entorno, orientado principalmente a hablar de movilidad y toponimia. Desarrollado en Go, con base de datos MySQL y desplegado sobre VPS administrado por mí mismo. <a href=\"/portfolio/vigo-360\">Más información</a>. <a href=\"https://github.com/arielcostas/vigo360\">Código fuente</a>. <a href=\"https://vigo360.es\">Web</a>."
+ }
+} \ No newline at end of file
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 0000000..4ed1a52
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,34 @@
+import Spanish from './es.json';
+import English from './en.json';
+
+export const SHOW_DEFAULT_LANGUAGE = false;
+export const DEFAULT_LANGUAGE = "es";
+
+export type LanguageKeys = "es" | "en";
+
+export const languages: Record<LanguageKeys, { code: string, name: string }> = {
+ es: {
+ code: "es",
+ name: "Español"
+ },
+ en: {
+ code: "en",
+ name: "English"
+ }
+}
+
+export const LANGUAGE_CODES = Object.keys(languages);
+
+export const useTranslations = (lang: string | undefined) => {
+ switch (lang) {
+ case languages.en.code:
+ return English;
+ case languages.es.code:
+ default:
+ return Spanish;
+ }
+}
+
+export function getUrlWithoutLocale(url: string) {
+ return url.replace(/\/[a-z]{2}\//, "/");
+} \ No newline at end of file
diff --git a/src/layouts/ContactPageLayout.astro b/src/layouts/ContactPageLayout.astro
new file mode 100644
index 0000000..3b3c278
--- /dev/null
+++ b/src/layouts/ContactPageLayout.astro
@@ -0,0 +1,99 @@
+---
+import { useTranslations } from "../i18n";
+import Layout from "./Layout.astro";
+
+const t = useTranslations(Astro.currentLocale);
+
+const schema = {
+ "@context": "https://schema.org",
+ "@type": "ContactPage",
+ url: "https://www.costas.dev/contact",
+ headline: t.contactPage.headline,
+};
+---
+
+<Layout title={t.contactPage.title} description={t.contactPage.description}>
+ <script
+ is:inline
+ type="application/ld+json"
+ slot="head-jsonld"
+ set:html={JSON.stringify(schema)}
+ />
+
+ <h1>{t.contactPage.headline}</h1>
+
+ <noscript>
+ <div role="alert" class="warning">
+ {t.contactPage.enableJs}
+ </div>
+ </noscript>
+
+ <p set:html={t.contactPage.intro} />
+
+ <p>{t.contactPage.socialMedia}</p>
+
+ <dl>
+ <dt>GitHub</dt>
+ <dd><a href="https://github.com/arielcostas">@arielcostas</a></dd>
+ <dt>LinkedIn</dt>
+ <dd>
+ <a href="https://www.linkedin.com/in/ariel-costas/"
+ >/in/ariel-costas</a>
+ </dd>
+ <dt>BlueSky</dt>
+ <dd><a href="https://bsky.app/profile/costas.dev">@costas.dev</a></dd>
+ </dl>
+</Layout>
+
+<script>
+ const encryptedEmail = "LygNLiMmFRo/GlQZaFIWBA==";
+ const encryptedPhoneNumber = "ZWlQfX1QT0Z+XgVd";
+ const key = "NZdKOfvuLn5jF6sryF0Q";
+
+ const emailAddrLink = document.getElementById(
+ "email-addr",
+ ) as HTMLAnchorElement;
+ const phoneNumberLink = document.getElementById(
+ "phone-number",
+ ) as HTMLAnchorElement;
+
+ (() => {
+ if (emailAddrLink == null || phoneNumberLink == null) {
+ return;
+ }
+
+ const emailAddress = xorData(encryptedEmail, key);
+ const phoneNumber = xorData(encryptedPhoneNumber, key);
+
+ emailAddrLink.href = `mailto:${emailAddress}`;
+ emailAddrLink.textContent = emailAddress;
+
+ phoneNumberLink.href = `tel:${phoneNumber}`;
+ phoneNumberLink.textContent = phoneNumber;
+ })();
+
+ function xorData(data: string, key: string): string {
+ let actualData = atob(data);
+ let actualKey = key;
+ const keyLength = key.length;
+ const dataLength = actualData.length;
+ const result = new Array(dataLength);
+
+ // If the key is 12 characters but the data is 30 characters, the key should be repeated 3 times and truncated to 30 characters
+ if (keyLength < dataLength) {
+ actualKey = key
+ .repeat(Math.ceil(dataLength / keyLength))
+ .substring(0, dataLength);
+ } else if (keyLength > dataLength) {
+ actualKey = key.substring(0, dataLength);
+ }
+
+ for (let i = 0; i < dataLength; i++) {
+ result[i] = String.fromCharCode(
+ actualData.charCodeAt(i) ^ actualKey.charCodeAt(i),
+ );
+ }
+
+ return result.join("");
+ }
+</script>
diff --git a/src/layouts/HomePageLayout.astro b/src/layouts/HomePageLayout.astro
new file mode 100644
index 0000000..146eaec
--- /dev/null
+++ b/src/layouts/HomePageLayout.astro
@@ -0,0 +1,62 @@
+---
+import { getCollection } from "astro:content";
+import Layout from "./Layout.astro";
+import { useTranslations } from "../i18n";
+
+const blogCollection = (await getCollection("blog")).sort((a, b) => {
+ return b.data.publishedAt.getTime() - a.data.publishedAt.getTime();
+});
+
+const t = useTranslations(Astro.currentLocale);
+
+const schema = {
+ "@context": "http://schema.org",
+ "@type": "WebSite",
+ id: "https://www.costas.dev/",
+ url: "https://www.costas.dev/",
+ headline: t.homePage.title,
+};
+---
+
+<Layout title={t.homePage.title} description={t.homePage.description}>
+ <script
+ is:inline
+ type="application/ld+json"
+ slot="head-jsonld"
+ set:html={JSON.stringify(schema)}
+ />
+
+ <h1>{t.homePage.title}</h1>
+ <p>{t.homePage.welcome}</p>
+
+ <h2>{t.homePage.whoAmI}</h2>
+ <p>{t.homePage.whoAmIDesc}</p>
+ <a href="/trajectory">{t.homePage.moreAboutMe}</a>
+
+ <h2>{t.homePage.whatIDo}</h2>
+ <p>{t.homePage.whatIDoDesc}</p>
+ <a href="/portfolio">{t.homePage.myPortfolio}</a>
+
+ <h2>{t.homePage.latestBlogPosts}</h2>
+ <ul>
+ {
+ blogCollection.slice(0, 5).map((p) => {
+ const date = Intl.DateTimeFormat(Astro.currentLocale, {
+ day: "2-digit",
+ month: "short",
+ year: "numeric",
+ }).format(p.data.publishedAt);
+ return (
+ <li>
+ <time datetime={p.data.publishedAt.toISOString()}>
+ {date}
+ </time>
+ <a href={`/blog/${p.id}`}>{p.data.title}</a>
+ </li>
+ );
+ })
+ }
+ </ul>
+ <a href="/blog">{t.homePage.viewAllPosts}</a>
+
+</Layout>
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index 4952d55..eda1716 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -13,7 +13,7 @@ const { title, description } = Astro.props;
---
<!doctype html>
-<html lang="es">
+<html lang={Astro.currentLocale}>
<head>
<meta charset="UTF-8" />
<meta name="description" content={description} />
@@ -53,6 +53,7 @@ const { title, description } = Astro.props;
<style is:global lang="scss">
@use "../../styles/shared.scss" as *;
+ @use "sass:color";
html,
body {
@@ -82,7 +83,7 @@ const { title, description } = Astro.props;
}
*::selection {
- background-color: transparentize($accent, 0.85);
+ background-color: color.adjust($accent, $alpha: -0.85);
}
li > time {
@@ -93,12 +94,17 @@ const { title, description } = Astro.props;
padding: 1rem;
margin-block: 1rem;
border-radius: 0.5rem;
-
- box-shadow: 0 0 0 1px $accent;
-
+
&.note {
background-color: $noteBackground;
color: $noteText;
+ box-shadow: 0 0 0 1px $noteText;
+ }
+
+ &.warning {
+ background-color: $warningBackground;
+ color: $warningText;
+ box-shadow: 0 0 0 1px $warningText;
}
}
@@ -132,10 +138,15 @@ const { title, description } = Astro.props;
}
}
- main a, footer a {
+ main a {
color: $accentDark;
- transition: color 0.2s ease-in-out,
- box-shadow 0.2s ease-in-out;
+ }
+
+ footer a {
+ color: $accentLight;
+ }
+
+ main a, footer a {
text-decoration: none;
padding: 0.1rem;
box-shadow: 0 1px $accent;
@@ -145,6 +156,7 @@ const { title, description } = Astro.props;
}
&:focus {
+ color: $accentDark;
outline: none;
background-color: $secondary;
box-shadow: 0 4px #0b0c0c;
diff --git a/src/layouts/PortfolioPageLayout.astro b/src/layouts/PortfolioPageLayout.astro
new file mode 100644
index 0000000..0ade4ba
--- /dev/null
+++ b/src/layouts/PortfolioPageLayout.astro
@@ -0,0 +1,113 @@
+---
+import Layout from "./Layout.astro";
+import { useTranslations } from "../i18n";
+import TechnologyBadge from "../partials/TechnologyBadge.astro";
+
+const t = useTranslations(Astro.currentLocale);
+
+const schema = {
+ "@context": "https://schema.org",
+ "@type": "WebPage",
+ url: "https://www.costas.dev/portfolio",
+ headline: t.portfolioPage.headline,
+};
+---
+
+<Layout title={t.portfolioPage.title} description={t.portfolioPage.description}>
+ <script
+ is:inline
+ type="application/ld+json"
+ slot="head-jsonld"
+ set:html={JSON.stringify(schema)}
+ ></script>
+
+ <h1>{t.portfolioPage.headline}</h1>
+
+ <p set:html={t.portfolioPage.intro} />
+
+ <h2>{t.portfolioPage.freelanceTitle}</h2>
+
+ <p>{t.portfolioPage.freelanceDesc}</p>
+
+ <section>
+ <article>
+ <h3>{t.portfolioPage.orderExtractorTitle}</h3>
+
+ <p set:html={t.portfolioPage.orderExtractorDesc} />
+
+ <TechnologyBadge code="java" />
+ <TechnologyBadge code="windows" />
+ </article>
+
+ <article>
+ <h3>{t.portfolioPage.touristInfoTitle}</h3>
+
+ <p set:html={t.portfolioPage.touristInfoDesc} />
+
+ <TechnologyBadge code="php" />
+ <TechnologyBadge code="mysql" />
+ </article>
+
+ <article>
+ <h3>{t.portfolioPage.wpConsultingTitle}</h3>
+
+ <p set:html={t.portfolioPage.wpConsultingDesc} />
+
+ <TechnologyBadge code="php" />
+ </article>
+ </section>
+
+ <h2>{t.portfolioPage.ownProjectsTitle}</h2>
+
+ <p>{t.portfolioPage.ownProjectsDesc}</p>
+
+ <section>
+ <article>
+ <h3>{t.portfolioPage.personalWebTitle}</h3>
+
+ <p>{t.portfolioPage.personalWebDesc}</p>
+
+ <TechnologyBadge code="astro" />
+ <TechnologyBadge code="azure" />
+ </article>
+
+ <article>
+ <h3>{t.portfolioPage.mientrenoTitle}</h3>
+
+ <p set:html={t.portfolioPage.mientrenoDesc} />
+
+ <TechnologyBadge code="dotnet" />
+ <TechnologyBadge code="sqlserver" />
+ <TechnologyBadge code="azure" />
+ <TechnologyBadge code="rabbitmq" />
+ </article>
+
+ <article>
+ <h3>{t.portfolioPage.vigo360Title}</h3>
+
+ <p set:html={t.portfolioPage.vigo360Desc} />
+
+ <TechnologyBadge code="go" />
+ <TechnologyBadge code="mysql" />
+ <TechnologyBadge code="linux" />
+ </article>
+ </section>
+</Layout>
+
+<style>
+ section {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
+ gap: 1rem;
+ }
+
+ article {
+ padding: 1rem;
+ border: 1px solid var(--accent);
+ border-radius: 0.5rem;
+ }
+
+ article h3 {
+ margin-top: 0;
+ }
+</style> \ No newline at end of file
diff --git a/src/layouts/TrajectoryPageLayout.astro b/src/layouts/TrajectoryPageLayout.astro
new file mode 100644
index 0000000..1739a32
--- /dev/null
+++ b/src/layouts/TrajectoryPageLayout.astro
@@ -0,0 +1,81 @@
+---
+import { useTranslations } from "../i18n";
+import Layout from "./Layout.astro";
+
+const t = useTranslations(Astro.currentLocale);
+
+const schema = {
+ "@context": "https://schema.org",
+ "@type": "WebPage",
+ url: "https://www.costas.dev/trajectory",
+ headline: t.trajectoryPage.headline,
+};
+---
+
+<Layout title={t.trajectoryPage.title} description={t.trajectoryPage.description}>
+ <script
+ is:inline
+ type="application/ld+json"
+ slot="head-jsonld"
+ set:html={JSON.stringify(schema)}
+ />
+
+ <h1>{t.trajectoryPage.headline}</h1>
+
+ <p>{t.trajectoryPage.intro}</p>
+
+ <h2>{t.trajectoryPage.techTitle}</h2>
+
+ <p>{t.trajectoryPage.techDescription1}</p>
+
+ <p>{t.trajectoryPage.techDescription2}</p>
+
+ <h2>{t.trajectoryPage.educationTitle}</h2>
+
+ <ul>
+ <li>
+ <strong>{t.trajectoryPage.efsetCert1}</strong>
+ {t.trajectoryPage.efsetCert2} <a
+ href="https://cert.efset.org/es/Yxzc9L"
+ >{t.trajectoryPage.viewCertificate}</a
+ >
+ </li>
+
+ <li>
+ <strong>{t.trajectoryPage.azureDeveloperCert1}</strong>
+ {t.trajectoryPage.azureDeveloperCert2} <a
+ href="https://learn.microsoft.com/api/credentials/share/en-us/ariel-costas/E15072607CCF2DA9?sharingId=149A1CD9C13790F4"
+ >{t.trajectoryPage.viewCredential}</a
+ >.
+ </li>
+
+ <li>
+ <strong>{t.trajectoryPage.azureDevOpsCert1}</strong>
+ {t.trajectoryPage.azureDevOpsCert2} <a
+ href="https://learn.microsoft.com/api/credentials/share/en-us/ariel-costas/5FB94876A1701595?sharingId=149A1CD9C13790F4"
+ >{t.trajectoryPage.viewCredential}</a
+ >
+ </li>
+
+ <li>
+ <strong>{t.trajectoryPage.higherTechCert1}</strong>
+ {t.trajectoryPage.higherTechCert2} <a href="https://iesteis.es/">{t.trajectoryPage.higherTechCert3}</a> {t.trajectoryPage.higherTechCert4}
+ </li>
+ </ul>
+
+ <h2>{t.trajectoryPage.experienceTitle}</h2>
+
+ <h3>{t.trajectoryPage.estelaria1}</h3>
+
+ <p>{t.trajectoryPage.estelaria2}</p>
+
+ <p>{t.trajectoryPage.estelaria3}</p>
+
+ <h3>{t.trajectoryPage.polygon1}</h3>
+
+ <p>{t.trajectoryPage.polygon2}</p>
+
+ <h2>{t.trajectoryPage.projectsTitle}</h2>
+
+ <p set:html={t.trajectoryPage.projectsDescription} />
+</Layout>
diff --git a/src/pages/blog.xml.js b/src/pages/blog.xml.ts
index b4fbe38..f165475 100644
--- a/src/pages/blog.xml.js
+++ b/src/pages/blog.xml.ts
@@ -1,19 +1,19 @@
-import rss from '@astrojs/rss';
-import { getCollection } from 'astro:content';
-
-
-export async function GET(context) {
- const collection = await getCollection('blog');
-
- return rss({
- title: "Blog de Ariel Costas",
- description: "Artículos del blog de Ariel Costas",
- site: context.site,
- items: collection.map((post) => ({
- title: post.data.title,
- link: `${context.site}blog/${post.slug}`,
- description: post.data.metaDescription,
- pubDate: post.data.publishedAt
- }))
- })
-}
+import rss from '@astrojs/rss';
+import { getCollection } from 'astro:content';
+
+
+export async function GET(context: any) {
+ const collection = await getCollection('blog');
+
+ return rss({
+ title: "Blog de Ariel Costas",
+ description: "Artículos del blog de Ariel Costas",
+ site: context.site,
+ items: collection.map((post: any) => ({
+ title: post.data.title,
+ link: `${context.site}blog/${post.slug}`,
+ description: post.data.metaDescription,
+ pubDate: post.data.publishedAt
+ }))
+ })
+}
diff --git a/src/pages/en/contact.astro b/src/pages/en/contact.astro
new file mode 100644
index 0000000..ef69d6e
--- /dev/null
+++ b/src/pages/en/contact.astro
@@ -0,0 +1,5 @@
+---
+import ContactPageLayout from "../../layouts/ContactPageLayout.astro";
+---
+
+<ContactPageLayout /> \ No newline at end of file
diff --git a/src/pages/en/index.astro b/src/pages/en/index.astro
new file mode 100644
index 0000000..8c5945a
--- /dev/null
+++ b/src/pages/en/index.astro
@@ -0,0 +1,5 @@
+---
+import HomePageLayout from "../../layouts/HomePageLayout.astro";
+---
+
+<HomePageLayout /> \ No newline at end of file
diff --git a/src/pages/en/portfolio.astro b/src/pages/en/portfolio.astro
new file mode 100644
index 0000000..63840f5
--- /dev/null
+++ b/src/pages/en/portfolio.astro
@@ -0,0 +1,5 @@
+---
+import PortfolioPageLayout from "../../layouts/PortfolioPageLayout.astro";
+---
+
+<PortfolioPageLayout /> \ No newline at end of file
diff --git a/src/pages/en/trajectory.astro b/src/pages/en/trajectory.astro
new file mode 100644
index 0000000..3f5ff77
--- /dev/null
+++ b/src/pages/en/trajectory.astro
@@ -0,0 +1,5 @@
+---
+import TrajectoryPageLayout from "../../layouts/TrajectoryPageLayout.astro";
+---
+
+<TrajectoryPageLayout /> \ No newline at end of file
diff --git a/src/pages/index.astro b/src/pages/index.astro
index fcef172..8ac64a6 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,69 +1,5 @@
---
-import { getCollection } from "astro:content";
-import Layout from "../layouts/Layout.astro";
-
-const blogCollection = (await getCollection("blog")).sort((a, b) => {
- return b.data.publishedAt.getTime() - a.data.publishedAt.getTime();
-});
-
-const schema = {
- "@context": "http://schema.org",
- "@type": "WebSite",
- id: "https://www.costas.dev/",
- url: "https://www.costas.dev/",
-};
+import HomePageLayout from "../layouts/HomePageLayout.astro";
---
-<Layout title="Inicio" description="Página de inicio de mi web">
- <script
- is:inline
- type="application/ld+json"
- slot="head-jsonld"
- set:html={JSON.stringify(schema)}
- />
-
- <h1>Inicio</h1>
- <p>
- Te doy la bienvenida a mi web. Me llamo Ariel, y aquí encontrarás
- información sobre mí y mis proyectos.
- </p>
-
- <h2>¿Quién soy?</h2>
- <p>
- Soy un desarrollador web que le gusta aprender cosas nuevas y compartir
- su conocimiento. Me gusta la programación, el diseño web y la
- creatividad. Me encanta crear cosas nuevas y aprender de los demás.
- </p>
- <a href="/trajectory">Más información sobre mí</a>
-
- <h2>¿Qué hago?</h2>
- <p>
- Actualmente trabajo como desarrollador de software y admistrador
- Cloud en una empresa de tecnología. Me encargo de desarrollar
- aplicaciones web en PHP y desplegarlas en la nube de forma segura y eficiente.
- </p>
- <a href="/portfolio">Mi portfolio</a>
-
- <h2>Últimas entradas del blog</h2>
- <ul>
- {
- blogCollection.slice(0, 5).map((p) => {
- const date = Intl.DateTimeFormat("es-ES", {
- day: "2-digit",
- month: "short",
- year: "numeric",
- }).format(p.data.publishedAt);
- return (
- <li>
- <time datetime={p.data.publishedAt.toISOString()}>
- {date}
- </time>
- <a href={`/blog/${p.id}`}>{p.data.title}</a>
- </li>
- );
- })
- }
- </ul>
- <a href="/blog">Ver todas las entradas</a>
-
-</Layout>
+<HomePageLayout /> \ No newline at end of file
diff --git a/src/pages/portfolio/index.astro b/src/pages/portfolio/index.astro
index 43f776b..63840f5 100644
--- a/src/pages/portfolio/index.astro
+++ b/src/pages/portfolio/index.astro
@@ -1,146 +1,5 @@
---
-import Layout from "../../layouts/Layout.astro";
-import TechnologyBadge from "../../partials/TechnologyBadge.astro";
-
-const schema = {
- "@context": "https://schema.org",
- "@type": "WebPage",
- url: "https://www.costas.dev/portfolio",
- headline: "Mi portfolio de proyectos",
-};
+import PortfolioPageLayout from "../../layouts/PortfolioPageLayout.astro";
---
-<Layout title="Portfolio" description="Un listado de los proyectos en los que he trabajado en los últimos años, con las tecnologías utilizadas.">
- <script
- is:inline
- type="application/ld+json"
- slot="head-jsonld"
- set:html={JSON.stringify(schema)}
- ></script>
-
- <h1>Mi portfolio</h1>
-
- <p>
- En esta sección encontrarás una lista de los proyectos en los que he
- trabajado, tanto para terceros como propios. Si quieres más información
- sobre alguno de ellos, no dudes en
- <a href="/contact">contactar conmigo</a>.
- </p>
-
- <h2>Proyectos para terceros (freelance)</h2>
-
- <p>
- He realizado desarrollos de proyectos para terceros por encargo, siendo los más destacados los siguientes:
- </p>
-
- <section>
- <article>
- <h3>Extractor de pedidos compra online</h3>
-
- <p>
- Aplicación de escritorio que extrae los datos sobre los pedidos
- on-line de diversas plataformas (como WooCommerce, Amazon y
- Ebay). <a href="/portfolio/order-extractor">Más información</a>.
- </p>
-
- <TechnologyBadge code="java" />
- <TechnologyBadge code="windows" />
- </article>
-
- <article>
- <h3>Punto de información turística en móviles</h3>
-
- <p>
- Aplicación web de información turística, con QR, contenido en 360º e incrustado
- de YouTube. <a href="/portfolio/dynamic-tourist-info">Más información</a>.
- </p>
-
- <TechnologyBadge code="php" />
- <TechnologyBadge code="mysql" />
- </article>
-
- <article>
- <h3>Consultoría WordPress</h3>
-
- <p>
- Trabajos de mantenimiento, optimización y migración de sitios web WordPress y tiendas online WooCommerce.
- <a href="/portfolio/wp-consulting">Más información</a>.
- <a href="/contact">Contactar</a>.
- </p>
-
- <TechnologyBadge code="php" />
- </article>
- </section>
-
- <h2>Proyectos propios</h2>
-
- <p>
- Además, tengo varios proyectos propios que he desarrollado en mi tiempo, estando algunos de ellos en activo, y como código abierto.
- </p>
-
- <section>
- <article>
- <h3>Web personal</h3>
-
- <p>
- Desarrollado con Astro, un generador de sitios web estáticos que
- permite escribir contenido en Markdown y publicar en la web con un
- rendimiento excelente. Desplegado sobre Azure Static Web Apps.
- </p>
-
- <TechnologyBadge code="astro" />
- <TechnologyBadge code="azure" />
- </article>
-
- <article>
- <h3>MiEntreno (proyecto fin de ciclo)</h3>
-
- <p>
- Aplicación web para la gestión de entrenamientos deportivos, con
- una interfaz sencilla y fácil de usar. Desarrollado con ASP.NET Core,
- Razor Pages y SQL Server. <a href="/portfolio/mientreno">Más información</a>.
- <a href="https://github.com/arielcostas/mientreno">Código fuente</a>.
- </p>
-
- <TechnologyBadge code="dotnet" />
- <TechnologyBadge code="sqlserver" />
- <TechnologyBadge code="azure" />
- <TechnologyBadge code="rabbitmq" />
- </article>
-
- <article>
- <h3>Vigo 360</h3>
-
- <p>
- Blog sobre Vigo y su entorno, orientado principalmente a hablar de
- movilidad y toponimia. Desarrollado en Go, con base de datos MySQL
- y desplegado sobre VPS administrado por mí mismo.
- <a href="/portfolio/vigo-360">Más información</a>.
- <a href="https://github.com/arielcostas/vigo360">Código fuente</a>.
- <a href="https://vigo360.es">Web</a>.
- </p>
-
- <TechnologyBadge code="go" />
- <TechnologyBadge code="mysql" />
- <TechnologyBadge code="linux" />
- </article>
- </section>
-</Layout>
-
-<style>
- section {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
- gap: 1rem;
- }
-
- article {
- padding: 1rem;
- border: 1px solid var(--accent);
- border-radius: 0.5rem;
- }
-
- article h3 {
- margin-top: 0;
- }
-</style> \ No newline at end of file
+<PortfolioPageLayout /> \ No newline at end of file
diff --git a/src/pages/trajectory.astro b/src/pages/trajectory.astro
index 87557de..b1366e6 100644
--- a/src/pages/trajectory.astro
+++ b/src/pages/trajectory.astro
@@ -1,111 +1,5 @@
---
-import Layout from "../layouts/Layout.astro";
-
-const schema = {
- "@context": "https://schema.org",
- "@type": "WebPage",
- "url": "https://www.costas.dev/trajectory",
- "headline": "Mi trayectoria como desarrollador"
-};
+import TrajectoryPageLayout from "../layouts/TrajectoryPageLayout.astro";
---
-<Layout title="Trayectoria" description="Mi trayectoria como desarrollador de software, con información sobre mi educación, experiencia laboral y proyectos en los que he trabajado.">
- <script is:inline type="application/ld+json" slot="head-jsonld" set:html={JSON.stringify(schema)}></script>
-
- <h1>Mi trayectoria como desarrollador</h1>
-
- <p>
- Soy un desarrollador de software que vive en Vigo, España. Me gusta
- mucho la tecnología, y me gusta aprender cosas nuevas. Me dedico
- profesionalmente al desarrollo de software en todos sus ámbitos: desde
- el diseño de la arquitectura, la implementación y el despliegue en
- producción, pasando por otros aspectos como la experiencia de usuario,
- la accesibilidad y la seguridad.
- </p>
-
- <h2>Tecnologías que domino</h2>
-
- <p>
- Domino principalmente el ecosistema de Microsoft: .NET (C#), ASP.NET
- Core, SQL Server, Azure y Azure DevOps. También tengo experiencia con
- otros lenguajes y tecnologías, como PHP, Python y Java. Además, tengo
- conocimientos de frontend con
- <abbr title="Hypertext Markup Language">HTML5</abbr>,
- <abbr title="Cascading Style Sheets">CSS3</abbr>, JavaScript y
- TypeScript.
- </p>
-
- <p>
- También tengo experiencia con herramientas de DevOps como Docker,
- Kubernetes, Terraform y GitHub Actions; así como el ecosistema Cloud de
- Azure.
- </p>
-
- <h2>Educación y credenciales</h2>
-
- <ul>
- <li>
- <strong>Certificado oficial EF SET C2 Proficient</strong>:
- acredita mi nivel de inglés C2, el más alto según el Marco Común
- Europeo de Referencia para las Lenguas. <a
- href="https://cert.efset.org/es/Yxzc9L"
- >Ver certificado</a>
- </li>
-
- <li>
- <strong>Microsoft Certified: Azure Developer Associate</strong>:
- acredita mis conocimientos en el desarrollo de aplicaciones en
- Azure. <a
- href="https://learn.microsoft.com/api/credentials/share/en-us/ariel-costas/E15072607CCF2DA9?sharingId=149A1CD9C13790F4"
- >Ver credencial</a
- >.
- </li>
-
- <li>
- <strong>Microsoft Certified: Azure DevOps Engineer Expert</strong>:
- acredita mis conocimientos en la implementación de metodologías de
- DevOps en Azure con Azure DevOps y GitHub. <a
- href="https://learn.microsoft.com/api/credentials/share/en-us/ariel-costas/5FB94876A1701595?sharingId=149A1CD9C13790F4"
- >Ver credencial</a
- >
- </li>
-
- <li>
- <strong
- >Técnico Superior en Desarrollo de Aplicaciones Multiplataforma</strong
- >: título de formación profesional de grado superior, obtenido en el
- <a href="https://iesteis.es/">IES de Teis</a> en Vigo.
- </li>
- </ul>
-
- <h2>Experiencia laboral</h2>
-
- <h3>Estelaria Solutions (Q3 2023 - actualidad)</h3>
-
- <p>
- Desarrollo de aplicaciones full-stack, principalmente en PHP con
- Symfony, MongoDB y JavaScript Vanilla. Además, implementé una cantidad
- considerable de mejoras en usabilidad, accesibilidad y rendimiento.
- </p>
-
- <p>
- También me encargo de la gestión de la infraestructura en AWS, y de la
- implementación de herramientas de colaboración como Jira y GitHub.
- </p>
-
- <h3>FCT en Polygon-E (Q2 2023)</h3>
-
- <p>
- Realicé mis prácticas de formación profesional en la empresa Polygon-E,
- donde desarrollé varias aplicaciones de gestión interna con ASP.NET Core
- y Blazor, desplegando sobre entornos <i>on-premise</i> con Windows Server,
- IIS y SQL Server.
- </p>
-
- <h2>Proyectos</h2>
-
- <p>
- Puedes encontrar los proyectos (públicos) en los que he trabajado en <a href="/portfolio">mi portfolio</a>
- y en mi <a href="https://github.com/arielcostas">perfil de GitHub</a>.
- </p>
-</Layout>
+<TrajectoryPageLayout />
diff --git a/src/partials/Footer.astro b/src/partials/Footer.astro
index dae78b7..d5ca5cb 100644
--- a/src/partials/Footer.astro
+++ b/src/partials/Footer.astro
@@ -1,19 +1,23 @@
+---
+import { useTranslations } from "../i18n"
+
+const t = useTranslations(Astro.currentLocale);
+---
+
<footer>
<p>
- Copyright &copy; 2023-{new Date().getFullYear()} Ariel Costas Guerrero. Todos
- los derechos reservados.
+ Copyright &copy; 2023-{new Date().getFullYear()} Ariel Costas Guerrero. {t.footer.copyright}
</p>
+
<p>
- Contenido cedido bajo licencia <a
- href="https://creativecommons.org/licenses/by-sa/4.0/deed.es"
- >CC BY-SA 4.0</a
- > salvo que se indique lo contrario. El código fuente de este sitio está
- disponible en <a href="https://github.com/arielcostas/costasdev"
- >GitHub</a
- >, bajo los términos de la <a
+ {t.footer.contentLicencedUnder} <a
+ href={`https://creativecommons.org/licenses/by-sa/4.0/deed.${Astro.currentLocale}`}
+ >CC BY-SA 4.0</a>.
+
+ {t.footer.sourceCodeAvailableOn} <a href="https://github.com/arielcostas/costasdev">GitHub</a>
+ {t.footer.andIsLicencedUnder} <a
href="https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12"
- >Licencia Pública de la Unión Europea</a
- >.
+ >{t.footer.eupl} 1.2</a
</p>
</footer>
diff --git a/src/partials/Header.astro b/src/partials/Header.astro
index 35a39ae..bd8c9d6 100644
--- a/src/partials/Header.astro
+++ b/src/partials/Header.astro
@@ -1,9 +1,16 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { useTranslations } from "../i18n"
+
+const t = useTranslations(Astro.currentLocale);
+---
+
<header>
- <a href="/">Inicio</a>
- <a href="/trajectory">Trayectoria</a>
- <a href="/portfolio">Portfolio</a>
- <a href="/blog">Blog</a>
- <a href="/contact">Contacto</a>
+ <a href={getRelativeLocaleUrl(Astro.currentLocale!, "")}>{t.header.home}</a>
+ <a href={getRelativeLocaleUrl(Astro.currentLocale!, "trajectory")}>{t.header.trajectory}</a>
+ <a href={getRelativeLocaleUrl(Astro.currentLocale!, "portfolio")}>{t.header.portfolio}</a>
+ <a href={getRelativeLocaleUrl("es", "blog")}>{t.header.blog}</a>
+ <a href={getRelativeLocaleUrl(Astro.currentLocale!, "contact")}>{t.header.contact}</a>
</header>
<style lang="scss">
diff --git a/styles/shared.scss b/styles/shared.scss
index d36696f..202651c 100644
--- a/styles/shared.scss
+++ b/styles/shared.scss
@@ -15,6 +15,9 @@ $lightAlt: hsl(0, 0%, 100%);
$noteBackground: hsl(210, 40%, 96%);
$noteText: hsl(210, 40%, 40%);
+$warningBackground: hsl(40, 100%, 90%);
+$warningText: hsl(40, 100%, 30%);
+
$shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15);
$floatingRadius: 0.5rem;