Academia de programacion con contenido gratuito, enfoque practico y cero aburrimiento.
En la era de la inteligencia artificial, aprender a programar no perdio relevancia: se volvio mas importante que nunca. Herramientas como ChatGPT, Copilot o Claude pueden escribir codigo, pero si no entendes los fundamentos, no sabes que pedirle a la IA, no podes evaluar si lo que te devuelve esta bien, y mucho menos depurarlo cuando falla.
Programierds nace para ensenar los fundamentos de la programacion de forma clara y directa. No se trata de seguir tutoriales sin pensar, sino de entender los conceptos que te permiten dirigir a la IA como una herramienta, no depender de ella como una muleta.
Somos Tony Stark, la IA es Jarvis. Nosotros dirigimos, ella ejecuta. Pero para dirigir, hay que saber.
| Tecnologia | Version | Uso |
|---|---|---|
| Astro | 5.x | Framework web, SSG, Content Collections |
| TypeScript | strict | Tipado estatico en todo el proyecto |
| Tailwind CSS | 3.x | Styling utility-first con dark mode |
| Vitest | 4.x | Testing unitario de funciones de utilidad |
| CodeMirror 6 | 6.x | Editor de codigo en el Playground |
| Zod | via Astro | Validacion de schemas en Content Collections |
@tailwindcss/typography— clasesprosepara renderizado de Markdown@tailwindcss/forms— estilos base para formularios
- Space Grotesk — tipografia principal (
@fontsource/space-grotesk) - Fira Code — tipografia monospace para bloques de codigo (
@fontsource/fira-code)
| Token | Valor | Uso |
|---|---|---|
primary |
#0df259 |
Verde acento |
background-dark |
#102216 |
Fondo oscuro |
background-light |
#f5f8f6 |
Fondo claro |
accent-blue |
#3b82f6 |
Acentos azules |
accent-brown |
#78350f |
Acentos marrones |
editor-bg |
#0d1b11 |
Fondo del editor de codigo |
terminal-bg |
#050a06 |
Fondo de la terminal |
programierds/
├── src/
│ ├── assets/ # Assets estaticos (SVGs, imagenes)
│ ├── components/ # Componentes Astro reutilizables
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ ├── Icon.astro # Sistema de iconos SVG por nombre
│ │ ├── Icons.astro # Sprite sheet de iconos
│ │ ├── Welcome.astro # Componente de bienvenida
│ │ └── test/
│ │ └── TestRunner.astro # Motor de quizzes interactivos
│ ├── content/
│ │ ├── es/ # Contenido en espanol (fuente de verdad)
│ │ │ ├── courses/ # Cursos (c, git, java, javascript)
│ │ │ └── tests/ # Quizzes
│ │ └── en/ # Contenido en ingles (traducciones, fallback a es)
│ │ ├── courses/
│ │ └── tests/
│ ├── content.config.ts # Schemas Zod (courses_es, courses_en, tests_es, tests_en...)
│ ├── i18n/ # Sistema bilingue
│ │ ├── es.ts # Diccionario de strings en espanol
│ │ ├── en.ts # Diccionario de strings en ingles
│ │ ├── types.ts # Locale, DEFAULT_LOCALE, LOCALES
│ │ ├── index.ts # useTranslations, getLocaleFromPathname, withLocale
│ │ └── i18n.test.ts # Tests del helper
│ ├── layouts/
│ │ └── Layout.astro # Layout base: fonts, meta, dark mode, <html lang>
│ ├── pages/
│ │ ├── index.astro # Redirect a /es/
│ │ ├── 404.astro # Pagina de error (detecta locale desde URL)
│ │ └── [lang]/ # Todo el sitio vive bajo /[lang]/
│ │ ├── index.astro # Home
│ │ ├── playground.astro
│ │ ├── cursos/
│ │ │ ├── index.astro
│ │ │ └── [course]/
│ │ │ ├── index.astro
│ │ │ └── [lesson].astro
│ │ ├── presentaciones/
│ │ │ ├── index.astro
│ │ │ └── *.astro (22 slides)
│ │ └── test/
│ │ ├── index.astro
│ │ └── [slug].astro
│ ├── styles/
│ │ └── global.css # Tailwind directives + estilos globales
│ ├── templates/ # Mockups HTML de referencia (no se buildean)
│ └── utils/
│ ├── paths.ts # getRelativePath, getLocalizedRelativePath
│ ├── paths.test.ts # Tests unitarios de paths.ts
│ ├── content.ts # Helpers bilingues: getCoursesForLocale, etc.
│ ├── tests.ts # Logica pura de quizzes
│ └── tests.test.ts # Tests unitarios
├── public/ # Assets estaticos (imagenes, favicons)
├── docs/ # Documentacion del proyecto
├── astro.config.mjs # Configuracion Astro (site, base path)
├── tailwind.config.mjs # Configuracion Tailwind con tokens
├── tsconfig.json # TypeScript strict mode
└── package.json
El sitio es bilingue (es / en) con prefijo de locale obligatorio. La raiz / hace redirect a /es/.
| Ruta | Descripcion |
|---|---|
/ |
Redirect a /es/ (locale default) |
/[lang]/ |
Landing principal (lang = es o en) |
/[lang]/cursos |
Listado de cursos |
/[lang]/cursos/[course] |
Detalle del curso |
/[lang]/cursos/[course]/[lesson] |
Leccion individual en Markdown |
/[lang]/playground |
Editor de codigo interactivo |
/[lang]/presentaciones |
Listado de presentaciones |
/[lang]/presentaciones/[nombre] |
Slide interactiva individual |
/[lang]/test |
Listado de quizzes |
/[lang]/test/[slug] |
Quiz interactivo individual |
/404 |
Pagina de error (detecta locale desde URL) |
El sitio soporta espanol (es, default) e ingles (en). El selector en el header son las banderas 🇦🇷 / 🇺🇸.
- Config:
astro.config.mjsdefinei18ncondefaultLocale: 'es',prefixDefaultLocale: true,fallback: { en: 'es' }. - Diccionarios UI:
src/i18n/es.tsysrc/i18n/en.ts— objetos planos con todas las strings de interfaz. - Helper:
src/i18n/index.tsexponeuseTranslations(locale),getLocaleFromPathname,withLocale, etc. - Paths:
src/utils/paths.tsexponegetLocalizedRelativePath(locale, path)— usalo SIEMPRE para links internos, en lugar degetRelativePath. - Content collections: hay seis colecciones en total —
courses_es,courses_en,chapters_es,chapters_en,tests_es,tests_en. Los helpersgetCoursesForLocale,getChaptersForLocale,getTestsForLocaledesrc/utils/content.tshacen merge con fallback a espanol cuando falta la traduccion en ingles.
- Agrega la clave y su valor en
src/i18n/es.ts(fuente de verdad, define el tipoTranslationKey). - Agrega la traduccion equivalente en
src/i18n/en.ts(TypeScript te obliga a cubrirla). - En el componente, leela con:
---
import { useTranslations, type Locale } from '../i18n';
const { lang } = Astro.params as { lang: Locale };
const t = useTranslations(lang);
---
<h1>{t('mi.nueva.clave')}</h1>Si la clave falta en en.ts (traduccion parcial), useTranslations hace fallback automatico a espanol.
La estructura es espejada por idioma. El curso de C en espanol vive en src/content/es/courses/c/ y su version en ingles deberia vivir en src/content/en/courses/c/.
Para traducir una leccion existente:
- Copiar
src/content/es/courses/c/03-variables-y-constantes.mdasrc/content/en/courses/c/03-variables-y-constantes.md(mismo nombre de archivo). - Traducir el
titledel frontmatter y el cuerpo en Markdown. - Al buildear, la version en ingles aparece en
/en/cursos/c/03-variables-y-constantes.
Si una leccion NO existe en en/, la ruta /en/cursos/c/... renderiza la version en espanol con un banner "traduccion pendiente". Esto permite traducir de forma incremental sin romper el sitio.
Para traducir un curso entero, tambien hay que crear su index.md en src/content/en/courses/<curso>/index.md con los campos description, technology, difficulty.
Si una IA tiene que seguir traduciendo contenido faltante, este es el flujo correcto:
- Elegir un bloque coherente: terminar un curso o una tanda consecutiva de lecciones (
01-04,05-08, etc.). Evitar traducir archivos sueltos al azar. - Verificar que realmente falte: revisar si el archivo ya existe en
src/content/en/...antes de crearlo. - Mantener la ruta espejada: mismo nombre de archivo, misma carpeta, mismo slug.
- Traducir solo contenido: frontmatter + cuerpo Markdown. No cambiar nombres de archivo, slugs, estructura del curso ni schemas.
- Conservar identificadores:
- en cursos/lecciones: conservar filename y ruta relativa
- en tests: conservar
slug,idde preguntas,kind, y estructuraquestions/algorithm
- No usar
getCollection()directo para “probar” si aparece. La resolucion oficial siempre pasa porsrc/utils/content.ts. - Validar despues de cada tanda:
npm run astro checknpm run test
- Si una traduccion nueva no aparece en
localhostpero el archivo existe, sospechar de dev server stale. Reiniciarastro devantes de asumir que la traduccion esta mal.
- No inventar slugs nuevos en tests traducidos.
- No renombrar archivos al pasar de
esaen. - No mezclar varios cursos en una sola tanda si todavia no cerraste el bloque actual.
- Preferir consistencia pedagogica sobre literalidad absoluta: si el original usa tono didactico simple, la traduccion tambien.
- Mantener snippets de codigo intactos salvo strings visibles al usuario cuando tenga sentido traducirlas.
- No tocar contenido fuente en
es/salvo que el trabajo sea explicitamente corregir errores del original.
Cuando una ruta /en/... muestra espanol, no asumas automaticamente que “la pagina esta rota”.
- Si existe el archivo en
src/content/en/...y aun asi ves espanol, puede haber cache/dev server stale. - Si no existe el archivo en
src/content/en/..., entonces el helper esta haciendo fallback intencional al contenido en espanol. - El comportamiento de fallback vive en
src/utils/content.ts.
Hay un problema de contenido preexistente en tests de C con id/slug repetido:
src/content/es/tests/input-c-01.mdsrc/content/es/tests/ingreso-de-datos-en-c.md
Antes de traducir tests, verificar duplicados para no replicar el problema en en/.
Mismo patron: copiar src/content/es/tests/mi-test.md a src/content/en/tests/mi-test.md y traducir title, description, prompt, options, explanation. El slug se mantiene (es el identificador).
Las presentaciones viven en src/pages/[lang]/presentaciones/*.astro. Cada archivo tiene getStaticPaths que genera /es/presentaciones/... y /en/presentaciones/.... Hoy el contenido interno esta en espanol.
Para soportar EN en una presentacion:
- Leer el locale desde
Astro.params.lang. - Envolver los textos con
useTranslations(lang)o usar un condicional{lang === 'en' ? '...' : '...'}para los fragmentos. - Cuando aun no este traducida, el Layout muestra el banner "untranslatedBanner" automaticamente al detectar
lang === 'en'en el listing.
El fallback opera a dos niveles:
- UI strings: si
en.tsno tiene una clave,useTranslations('en')devuelve el valor dees.ts. - Content: si una leccion/test no existe en
src/content/en/..., los helpers deutils/content.tsdevuelven el entry en espanol y marcanfallbackUsed: true. Las paginas muestran un banner.
Definidas en src/content.config.ts con Zod. Hay seis colecciones (tres por idioma):
| Collection | Patron | Campos obligatorios |
|---|---|---|
courses_es / courses_en |
{es,en}/courses/**/index.md |
description, technology, difficulty |
chapters_es / chapters_en |
{es,en}/courses/**/*.md (sin index) |
title |
tests_es / tests_en |
{es,en}/tests/**/*.md |
title, description, slug, category, kind, y segun kind: questions[] o algorithm |
Importante: nunca llames getCollection('courses_es') directamente en paginas — usa los helpers getCoursesForLocale(lang), getChaptersForCourse(courseId, lang), getTestsForLocale(lang) de src/utils/content.ts. Estos hacen el merge con fallback a espanol cuando falta la traduccion.
Hay dos tipos soportados. Si no se especifica kind, se usa multiple-choice.
kind |
Descripcion |
|---|---|
multiple-choice (default) |
Varias preguntas con opciones, feedback al final |
code-ordering |
Un unico algoritmo con lineas mezcladas que hay que reordenar, contra tiempo |
---
title: "Fundamentos de JavaScript"
description: "Descripcion breve (max 180 chars)"
slug: "fundamentos-javascript"
category: "JavaScript"
difficulty: "beginner" # beginner | intermediate | advanced (opcional)
timeEstimate: 10 # minutos (opcional)
kind: "multiple-choice" # opcional, este es el default
questions:
- id: "q1"
prompt: "¿Cuanto es 2 + 2?"
code: | # snippet de codigo (opcional)
const x = 2 + 2;
language: "js" # lenguaje del snippet (opcional)
options:
- "3"
- "4"
- "5"
correctAnswer: 1 # indice 0-based
explanation: "2 + 2 = 4." # explicacion (opcional)
---
Contenido introductorio del quiz en Markdown (opcional).Un unico algoritmo con lineas en el orden correcto. El runner las mezcla en el cliente al cargar y el usuario las reordena arrastrando o con botones de flecha, contra tiempo.
---
title: "Ordenar: variables y constantes en C"
description: "Arrastra las lineas para reconstruir un programa en C."
slug: "ordenar-variables-c"
category: "C"
difficulty: "beginner"
timeEstimate: 5 # minutos (activa el timer)
kind: "code-ordering" # OBLIGATORIO para este tipo
algorithm:
prompt: "Ordena las lineas para armar un programa en C."
language: "c"
lines: # orden correcto, minimo 2 lineas
- "#include <stdio.h>"
- "int main() {"
- " int edad = 18;"
- " return 0;"
- "}"
explanation: "Por que este orden es correcto..." # opcional
---
Contenido introductorio en Markdown (opcional).Notas importantes para code-ordering:
algorithm.linesva en el orden correcto. El shuffle se hace en el cliente.- Cada linea tiene que ser un string NO vacio (
.min(1)en el schema). - Para escapar comillas dobles en una linea:
\". Para representar\nen unprintf:\\nen el YAML. - Se recomienda
timeEstimate— este tipo de test esta pensado como "programa con tiempo".
- Crear una carpeta en
src/content/es/courses/con el nombre del curso (ej:python/) - Agregar
index.mdcon el frontmatter requerido:
---
description: "Descripcion del curso (max 120 caracteres)"
technology: "Python"
difficulty: "Beginner" # Beginner | Intermediate | Advanced
---- Agregar lecciones como archivos Markdown numerados:
---
title: "Titulo de la leccion"
---
# Titulo de la leccion
Contenido en Markdown...- Agregar un
logo.svgpara el icono del curso - Registrar el icono en
src/components/Icon.astrocon el nombre en minuscula de la technology
Las paginas se generan automaticamente. No hay que tocar rutas ni configuracion.
Para publicar el curso en ingles, duplicar los mismos archivos en src/content/en/courses/<curso>/. Si no existen en ingles, /en/cursos/<curso> hace fallback al contenido en espanol automaticamente.
- Crear un archivo
.mdensrc/content/es/tests/(ej:mi-quiz.md) — la version en ingles va ensrc/content/en/tests/con el mismo nombre. - Elegir el
kind(default:multiple-choice). Para tests de ordenar codigo:kind: "code-ordering" - El slug en el frontmatter debe ser unico — se valida en runtime
- Usar el schema definido en
content.config.ts - El quiz queda disponible automaticamente en
/test/[slug]
# crear archivo en espanol (default)
touch src/content/es/tests/ordenar-mi-algoritmo.md
# (opcional) version en ingles
touch src/content/en/tests/ordenar-mi-algoritmo.md# frontmatter
---
title: "Ordenar: mi algoritmo"
description: "Descripcion breve."
slug: "ordenar-mi-algoritmo"
category: "C"
difficulty: "beginner"
timeEstimate: 5
kind: "code-ordering"
algorithm:
prompt: "Descripcion de lo que hay que ordenar."
language: "c"
lines:
- "linea 1"
- "linea 2"
- "linea 3"
explanation: "Opcional."
---Los tests de logica pura viven en src/utils/tests.test.ts y cubren src/utils/tests.ts.
npm run test # Corre todos los tests una vez
npm run test:watch # Modo watch (desarrollo)Una tarea no esta completa hasta completar este circuito:
- Test primero — escribir el test que falla (Rojo)
- Implementacion minima — hacer que el test pase (Verde)
- Validacion estatica —
npm run astro check(TypeScript + Astro) - Sin regresiones —
npm run test(todos los tests pasan)
npm run dev # Servidor de desarrollo (localhost:4321)
npm run build # Build de produccion -> ./dist/
npm run preview # Preview del build de produccion
npm run astro check # Validacion TypeScript y Astro
npm run test # Tests con Vitest (single run)
npm run test:watch # Tests con Vitest (watch mode)El sitio se despliega automaticamente a GitHub Pages cuando se hace push a la rama main. El desarrollo se hace en la rama desarrollo y se mergea a main via Pull Request.
- URL de produccion: https://facundouferer.github.io/programierds
Facundo Uferer — Senior Full Stack Engineer | AI-Driven Architect | Tech Lead