config i18n
This commit is contained in:
parent
16f07bf63b
commit
373a4af4b1
@ -47,7 +47,14 @@ export default function Footer(props: Props) {
|
||||
variant="link"
|
||||
className="flex flex-col justify-center"
|
||||
>
|
||||
<a target="_blank" href="https://juancman.dev/rss.xml">
|
||||
<a
|
||||
target="_blank"
|
||||
href={
|
||||
props.lang == "en"
|
||||
? "https://juancman.dev/feed.xml"
|
||||
: "https://juancman.dev/es/feed.xml"
|
||||
}
|
||||
>
|
||||
<RssIcon className="w-6" />
|
||||
RSS feed
|
||||
</a>
|
||||
|
@ -21,7 +21,7 @@ const locales = {
|
||||
top: "Arriba",
|
||||
navigation: "Navevación",
|
||||
},
|
||||
} as const;
|
||||
};
|
||||
|
||||
const { lang } = Astro.props;
|
||||
---
|
||||
@ -34,7 +34,7 @@ const { lang } = Astro.props;
|
||||
>
|
||||
<section class="flex max-w-max">
|
||||
<LinkButton
|
||||
href="/"
|
||||
href={lang === "en" ? "/" : "/es"}
|
||||
size="icon"
|
||||
variant="link"
|
||||
className="rounded-full px-0"
|
||||
|
@ -8,75 +8,38 @@ import {
|
||||
Info,
|
||||
Mail,
|
||||
} from "lucide-react";
|
||||
import { useTranslations, useTranslatedPath } from "@/i18n/utils";
|
||||
|
||||
type TNavItem = {
|
||||
to: string;
|
||||
child: React.ReactNode;
|
||||
type: string;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
export const navItems: TNavItem[] = [
|
||||
{
|
||||
to: "/blog",
|
||||
child: (
|
||||
<>
|
||||
<NotebookText />
|
||||
Blog
|
||||
</>
|
||||
),
|
||||
type: "blog",
|
||||
icon: <NotebookText />,
|
||||
},
|
||||
{ type: "portfolio", icon: <BriefcaseBusiness /> },
|
||||
{
|
||||
type: "videos",
|
||||
icon: <MonitorPlay />,
|
||||
},
|
||||
{
|
||||
to: "/portfolio",
|
||||
child: (
|
||||
<>
|
||||
<BriefcaseBusiness />
|
||||
Portfolio
|
||||
</>
|
||||
),
|
||||
type: "microblog",
|
||||
icon: <Newspaper />,
|
||||
},
|
||||
{
|
||||
to: "es/videos",
|
||||
child: (
|
||||
<>
|
||||
<MonitorPlay />
|
||||
Videos
|
||||
</>
|
||||
),
|
||||
type: "resources",
|
||||
icon: <PocketKnife />,
|
||||
},
|
||||
{
|
||||
to: "/microblog",
|
||||
child: (
|
||||
<>
|
||||
<Newspaper />
|
||||
Microblog
|
||||
</>
|
||||
),
|
||||
type: "about",
|
||||
icon: <Info />,
|
||||
},
|
||||
{
|
||||
to: "/resources",
|
||||
child: (
|
||||
<>
|
||||
<PocketKnife />
|
||||
Resources
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: "/about",
|
||||
child: (
|
||||
<>
|
||||
<Info />
|
||||
About
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: "/contact",
|
||||
child: (
|
||||
<>
|
||||
<Mail />
|
||||
Contact
|
||||
</>
|
||||
),
|
||||
type: "contact",
|
||||
icon: <Mail />,
|
||||
},
|
||||
];
|
||||
|
||||
@ -84,19 +47,46 @@ type Props = {
|
||||
lang: "en" | "es";
|
||||
};
|
||||
|
||||
const locales = {
|
||||
en: {
|
||||
navigation: "Navigation",
|
||||
blog: { label: "Blog", to: "/blog" },
|
||||
portfolio: { label: "Portfolio", to: "/portfolio" },
|
||||
videos: { label: "Videos", to: "/es/videos" },
|
||||
microblog: { label: "Microblog", to: "/microblog" },
|
||||
resources: { label: "Resources", to: "/resources" },
|
||||
about: { label: "About", to: "/about" },
|
||||
contact: { label: "Contact", to: "/contact" },
|
||||
},
|
||||
es: {
|
||||
navigation: "Navegación",
|
||||
blog: { label: "Blog", to: "/es/blog" },
|
||||
portfolio: { label: "Portfolio", to: "/es/portfolio" },
|
||||
videos: { label: "Videos", to: "/es/videos" },
|
||||
microblog: { label: "Microblog", to: "/microblog" },
|
||||
resources: { label: "Recursos", to: "/es/recursos" },
|
||||
about: { label: "Acerca de", to: "/es/acerca-de" },
|
||||
contact: { label: "Contacto", to: "/es/contacto" },
|
||||
},
|
||||
} as const;
|
||||
|
||||
export default function Navigation(props: Props) {
|
||||
const t = useTranslations(props.lang);
|
||||
const translatePath = useTranslatedPath(props.lang);
|
||||
|
||||
return (
|
||||
<nav className="px-4 sm:px-0 max-w-[65ch] mx-auto prose prose-invert pt-5 pb-20">
|
||||
<h2 id="navigation">Navigation</h2>
|
||||
<h2 id="navigation">{locales[props.lang].navigation}</h2>
|
||||
<ul className="list-none p-0 flex flex-wrap gap-4">
|
||||
{navItems.map((navItem, index) => (
|
||||
<li key={index} className="m-0 p-0">
|
||||
<LinkButton
|
||||
variant="link"
|
||||
href={navItem.to}
|
||||
href={locales[props.lang][navItem.type].to}
|
||||
className="p-0 text-base gap-1"
|
||||
>
|
||||
{navItem.child}
|
||||
{navItem.icon}
|
||||
{locales[props.lang][navItem.type].label}
|
||||
</LinkButton>
|
||||
</li>
|
||||
))}
|
||||
|
@ -1,6 +1,9 @@
|
||||
---
|
||||
title: About
|
||||
description: This website was first created as a portfolio, but learning about how the personal website is the digital form of the house tree, I like the idea of going that way instead of a generic landing with my social media.
|
||||
description:
|
||||
This website was first created as a portfolio, but learning about how the
|
||||
personal website is the digital form of the house tree, I like the idea of
|
||||
going that way instead of a generic landing with my social media.
|
||||
---
|
||||
|
||||
# About
|
||||
@ -9,11 +12,8 @@ This website was first created as a portfolio, but learning about how the
|
||||
personal website is the digital form of the house tree, I like the idea of going
|
||||
that way instead of a generic landing with my social media.
|
||||
|
||||
I'm trying to expand my skills, as I'm a Frontend Developer (with knowledge on
|
||||
Backend), but skills like writing are important.
|
||||
|
||||
This website is in English to reach more people and put it into practice, but
|
||||
Spanish is my mother tongue.
|
||||
This website is in English to reach more people; and in Spanish, because is my
|
||||
mother tongue.
|
||||
|
||||
All content written here is without AI; I don't use it for generating ideas; the
|
||||
only exception is [LanguageTool](https://languagetool.org/) for validating my
|
||||
|
22
src/content/pages/es/acerca-de.mdx
Normal file
22
src/content/pages/es/acerca-de.mdx
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Acerca de
|
||||
description:
|
||||
Este website fue creado en un principio como portfolio, pero luego de aprender
|
||||
como los websites personales son la forma difgital de la casa del árbol, me
|
||||
gustó más esa idea en lugar de una página genérica con mis redes sociales.
|
||||
---
|
||||
|
||||
# Acerca de
|
||||
|
||||
Este website fue creado en un principio como portfolio, pero luego de aprender
|
||||
como los websites personales son la forma difgital de la casa del árbol, me
|
||||
gustó más esa idea en lugar de una página genérica con mis redes sociales.
|
||||
|
||||
Este website está en Inglés para llegar a más gente; y en Español, ya que es mi
|
||||
lengua materna.
|
||||
|
||||
Todo el contenido escrito aquí es sin AI; no la uso para generar ideas; la única
|
||||
excepción es [LanguageTool](https://languagetool.org/) para validar mi
|
||||
gramática.
|
||||
|
||||
[](https://notbyai.fyi/)
|
36
src/content/pages/es/contacto.mdx
Normal file
36
src/content/pages/es/contacto.mdx
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Contact
|
||||
description: You can contact me if you want me to work, or just say hello.
|
||||
---
|
||||
|
||||
# Contacto
|
||||
|
||||
You can contact me if:
|
||||
|
||||
- You want me to work
|
||||
- Just say hello
|
||||
|
||||
Please consider that **I don't**:
|
||||
|
||||
- Work for free
|
||||
- Work on your startup idea and just get equity in return (I can't pay my bills
|
||||
with lottery tickets)
|
||||
- Work for you and get "exposure" (I can't pay my bills with exposure)
|
||||
- Communicate via phone number; all communication must be via email (we can use
|
||||
Discord, Slack, etc. once you hire me).
|
||||
|
||||
## My email
|
||||
|
||||
Just change `[at]` for `@` and `[dot]` for `.`. This is for preventing web
|
||||
crawlers from getting my email:
|
||||
|
||||
```
|
||||
contact[at]juancman[dot]dev
|
||||
```
|
||||
|
||||
## Social media
|
||||
|
||||
You can send me a direct message:
|
||||
|
||||
- [LinkedIn](https://www.linkedin.com/in/juancmandev)
|
||||
- [GitHub](https://github.com/juancmandev)
|
61
src/content/pages/es/recursos.mdx
Normal file
61
src/content/pages/es/recursos.mdx
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Resources
|
||||
description:
|
||||
Here you can find websites, YouTube channels, courses and more stuff that I
|
||||
consume or find interesting.
|
||||
---
|
||||
|
||||
# Recursos
|
||||
|
||||
Here you can find **websites**, **YouTube channels**, **courses** and **more**
|
||||
stuff that I consume or find interesting.
|
||||
|
||||
## Courses and Documentation
|
||||
|
||||
- [fireship.io](https://fireship.io)
|
||||
|
||||
- [MDN Web Docs](https://developer.mozilla.org/en-US)
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- [Astro](https://astro.build/) - Tool for building websites, that's how I built
|
||||
this one, really useful when you want a static website, but you can do Server
|
||||
Side Rendering too
|
||||
|
||||
- [Next.js](https://nextjs.org) - Dynamic and flexible React meta-framework,
|
||||
previously used on this Website
|
||||
|
||||
- [PocketBase](https://pocketbase.io/) - Fast and light database.
|
||||
|
||||
- [Supabase](https://supabase.com) - Open Source Backend as a Service
|
||||
alternative for Firebase, uses PostgreSQL and is really good if you're working
|
||||
alone or you want a solid backend without investing to much
|
||||
|
||||
- [TailwindCSS](https://tailwindcss.com) - Best way to write CSS
|
||||
|
||||
- [shadcn/ui](https://ui.shadcn.com) - Best components for React, sinergy with
|
||||
TailwindCSS
|
||||
|
||||
## YouTube channels
|
||||
|
||||
- [Mental Outlaw](https://www.youtube.com/channel/UC7YOGHUfC1Tb6E4pudI9STA)
|
||||
|
||||
- [Eric Murphy](https://www.youtube.com/channel/UC5KDiSAFxrDWhmysBcNqtMA)
|
||||
|
||||
- [Luke Smith](https://www.youtube.com/channel/UC2eYFnH61tmytImy1mTYvhA)
|
||||
|
||||
## Personal Websites
|
||||
|
||||
- [Eric Murphy](https://ericmurphy.xyz)
|
||||
|
||||
- [Luke Smith](https://lukesmith.xyz/)
|
||||
|
||||
## Favorite Blogs
|
||||
|
||||
- [Why I Will Never Join Mastodon (or the rest of the Fediverse)](https://ericmurphy.xyz/blog/mastodon)
|
||||
|
||||
- [Create More, Consume Less](https://www.bikobatanari.art/posts/2020/create-more) -
|
||||
_Currently offline_
|
||||
|
||||
- [My Website is a Personal Museum](https://www.bikobatanari.art/posts/2020/personal-museum) -
|
||||
_Currently offline_
|
16
src/i18n/ui.ts
Normal file
16
src/i18n/ui.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export const languages = {
|
||||
en: 'English',
|
||||
es: 'Español',
|
||||
};
|
||||
|
||||
export const defaultLang = 'en';
|
||||
export const showDefaultLang = false;
|
||||
|
||||
export const ui = {
|
||||
en: {
|
||||
'hello': 'Hello',
|
||||
},
|
||||
es: {
|
||||
'hello': 'Hola',
|
||||
},
|
||||
} as const;
|
19
src/i18n/utils.ts
Normal file
19
src/i18n/utils.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { ui, defaultLang, showDefaultLang } from './ui';
|
||||
|
||||
export function getLangFromUrl(url: URL) {
|
||||
const [, lang] = url.pathname.split('/');
|
||||
if (lang in ui) return lang as keyof typeof ui;
|
||||
return defaultLang;
|
||||
}
|
||||
|
||||
export function useTranslations(lang: keyof typeof ui) {
|
||||
return function t(key: keyof typeof ui[typeof defaultLang]) {
|
||||
return ui[lang][key] || ui[defaultLang][key];
|
||||
}
|
||||
}
|
||||
|
||||
export function useTranslatedPath(lang: keyof typeof ui) {
|
||||
return function translatePath(path: string, l: string = lang) {
|
||||
return !showDefaultLang && l === defaultLang ? path : `/${l}${path}`
|
||||
}
|
||||
}
|
@ -3,17 +3,18 @@ import Header from "@/components/header.astro";
|
||||
import Navigation from "@/components/navigation";
|
||||
import Footer from "@/components/footer";
|
||||
import "@/styles/globals.css";
|
||||
import { getLangFromUrl } from "../i18n/utils";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
lang: "en" | "es";
|
||||
}
|
||||
|
||||
const { title, description, lang } = Astro.props;
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={lang}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -30,7 +31,13 @@ const { title, description, lang } = Astro.props;
|
||||
rel="alternate"
|
||||
title="juancmandev"
|
||||
type="application/rss+xml"
|
||||
href={new URL("rss.xml", Astro.site)}
|
||||
href={new URL("feed.xml", Astro.site)}
|
||||
/>
|
||||
<link
|
||||
rel="alternate"
|
||||
title="juancmandev"
|
||||
type="application/rss+xml"
|
||||
href={new URL("feed.xml", `${Astro.site}/es/`)}
|
||||
/>
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
@ -1,14 +1,9 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import LinkButton from "@/components/link-button";
|
||||
---
|
||||
|
||||
<Layout lang="en" title="juancmandev" description="Error 404. Not found.">
|
||||
<Layout title="Not found" description="Error 404: Not found.">
|
||||
<div class="prose prose-invert">
|
||||
<h1 class="">Error 404: Not found</h1>
|
||||
<p>Do not worry, you can <strong>go back to home</strong>.</p>
|
||||
<LinkButton variant="default" href="/" className="no-underline"
|
||||
>Home</LinkButton
|
||||
>
|
||||
</div>
|
||||
</Layout>
|
||||
|
@ -21,7 +21,7 @@ const { page } = Astro.props;
|
||||
const { Content } = await page.render();
|
||||
---
|
||||
|
||||
<Layout lang="en" title={page.data.title} description={page.data.description}>
|
||||
<Layout {...page.data}>
|
||||
<article class="prose prose-invert">
|
||||
<Content components={{ ...components }} />
|
||||
</article>
|
||||
|
@ -25,7 +25,7 @@ const { post } = Astro.props;
|
||||
const { Content } = await post.render();
|
||||
---
|
||||
|
||||
<Layout lang="en" title={post.data.title} description={post.data.description}>
|
||||
<Layout title={post.data.title} description={post.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{post.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
|
@ -13,7 +13,7 @@ const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
|
||||
sortContentByDate(allPosts);
|
||||
---
|
||||
|
||||
<Layout {...pageData} lang="en">
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
|
28
src/pages/es/[...slug].astro
Normal file
28
src/pages/es/[...slug].astro
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
|
||||
interface Props {
|
||||
page: CollectionEntry<"pages">;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allPages = await getCollection("pages");
|
||||
|
||||
return allPages.map((page: CollectionEntry<"pages">) => ({
|
||||
params: { slug: page.slug },
|
||||
props: { page },
|
||||
}));
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
const { Content } = await page.render();
|
||||
---
|
||||
|
||||
<Layout {...page.data}>
|
||||
<article class="prose prose-invert">
|
||||
<Content components={{ ...components }} />
|
||||
</article>
|
||||
</Layout>
|
129
src/pages/es/feed.xml.ts
Normal file
129
src/pages/es/feed.xml.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import rss from "@astrojs/rss";
|
||||
import type { RSSFeedItem } from "@astrojs/rss";
|
||||
import { getCollection } from "astro:content";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { parse as htmlParser } from "node-html-parser";
|
||||
import { getImage } from "astro:assets";
|
||||
import type { ImageMetadata } from "astro";
|
||||
|
||||
const markdownParser = new MarkdownIt();
|
||||
|
||||
const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}",
|
||||
);
|
||||
const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}",
|
||||
);
|
||||
|
||||
export async function GET(context: any) {
|
||||
const items: RSSFeedItem[] = [];
|
||||
|
||||
const blog = await getCollection(
|
||||
"blog",
|
||||
({ data }) => data.draft !== true && data.rss === true,
|
||||
);
|
||||
const portfolio = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true && data.rss === true,
|
||||
);
|
||||
|
||||
for await (const post of blog) {
|
||||
const body = markdownParser.render(post.body);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesBlog[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default,
|
||||
);
|
||||
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", ""),
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
}
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date,
|
||||
description: post.data.description,
|
||||
link: `/blog/${post.slug}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
for await (const project of portfolio) {
|
||||
const body = markdownParser.render(project.body);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default,
|
||||
);
|
||||
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", ""),
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
// images starting with `/images/` is the public dir
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
}
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: project.data.title,
|
||||
pubDate: project.data.date,
|
||||
description: project.data.description,
|
||||
link: `/portfolio/${project.slug}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return rss({
|
||||
xmlns: { atom: "http://www.w3.org/2005/Atom" },
|
||||
title: "juancmandev",
|
||||
description: "Bienvenido a mi dominio, extraño.",
|
||||
site: `${context.site}es/`,
|
||||
customData: [
|
||||
"<language>es-mx</language>",
|
||||
`<image>
|
||||
<url>https://juancman.dev/logo.png</url>
|
||||
<title>juancmandev</title>
|
||||
<link>https://juancman.dev</link>
|
||||
</image>`,
|
||||
`<atom:link href="${context.site}es/feed.xml" rel="self" type="application/rss+xml"/>`,
|
||||
].join(""),
|
||||
items,
|
||||
trailingSlash: false,
|
||||
});
|
||||
}
|
@ -3,7 +3,6 @@ import Layout from "@/layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout
|
||||
lang="es"
|
||||
title="juancmandev"
|
||||
description="Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad."
|
||||
>
|
||||
@ -16,5 +15,10 @@ import Layout from "@/layouts/Layout.astro";
|
||||
>privacidad.</strong
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Este es mi <strong>website</strong>, un pedazo de Internet al que puedo
|
||||
llamar <strong>hogar</strong>. Aquí comparto mi pasión por proyectos open
|
||||
source y otros temas.
|
||||
</p>
|
||||
</div>
|
||||
</Layout>
|
||||
|
@ -25,11 +25,7 @@ const { project } = Astro.props;
|
||||
const { Content } = await project.render();
|
||||
---
|
||||
|
||||
<Layout
|
||||
lang="es"
|
||||
title={project.data.title}
|
||||
description={project.data.description}
|
||||
>
|
||||
<Layout title={project.data.title} description={project.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{project.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
|
@ -16,7 +16,7 @@ const allVideos = await getCollection(
|
||||
sortContentByDate(allVideos);
|
||||
---
|
||||
|
||||
<Layout {...pageData} lang="es">
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
|
@ -121,7 +121,7 @@ export async function GET(context: any) {
|
||||
<title>juancmandev</title>
|
||||
<link>https://juancman.dev</link>
|
||||
</image>`,
|
||||
`<atom:link href="${context.site}rss.xml" rel="self" type="application/rss+xml"/>`,
|
||||
`<atom:link href="${context.site}feed.xml" rel="self" type="application/rss+xml"/>`,
|
||||
].join(""),
|
||||
items,
|
||||
trailingSlash: false,
|
@ -18,7 +18,6 @@ const last3Projects = allProjects.slice(0, 3);
|
||||
---
|
||||
|
||||
<Layout
|
||||
lang="en"
|
||||
title="juancmandev"
|
||||
description="Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender."
|
||||
>
|
||||
|
@ -11,7 +11,6 @@ const data = await pb.collection("microblogs").getFullList({
|
||||
---
|
||||
|
||||
<Layout
|
||||
lang="en"
|
||||
title="Microblog"
|
||||
description="Short-format writing. Instead of using shitty social media."
|
||||
>
|
||||
|
@ -25,11 +25,7 @@ const { project } = Astro.props;
|
||||
const { Content } = await project.render();
|
||||
---
|
||||
|
||||
<Layout
|
||||
lang="en"
|
||||
title={project.data.title}
|
||||
description={project.data.description}
|
||||
>
|
||||
<Layout title={project.data.title} description={project.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{project.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
|
@ -16,7 +16,7 @@ const allProjects = await getCollection(
|
||||
sortContentByDate(allProjects);
|
||||
---
|
||||
|
||||
<Layout {...pageData} lang="en">
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
|
Loading…
x
Reference in New Issue
Block a user