Artículos
Utilice una interfaz pura para crear un blog multilingüe "dinámico + artículo"
Convertí mi sitio web en un blog multilingüe "dinámico + artículo": el contenido se administra con Markdown, la traducción se completa al publicar, se generan páginas estáticas durante la construcción y el navegador es responsable de leer y cambiar de idioma. Este artículo desglosa el modelo de contenido, los canales compartidos, las compensaciones entre la carga bajo demanda y el SEO, y qué ahorra exactamente el front-end puro.
· 9 min de lectura
Espero que mi sitio web pueda usarse para anotar un párrafo de pensamientos o para escribir claramente un problema técnico completo. Pon "dinámica" en el corto y "artículo" en el largo. Los dos pueden vincularse entre sí y, cuando el visitante cambia de idioma, el contenido también debe cambiarse al mismo tiempo.
Este requisito parece muy común, pero si agrega una base de datos, un backend y una API de contenido desde el principio, y luego maneja la versión de cada idioma, pronto se convertirá en otro producto que requerirá mantenimiento a largo plazo.
Primero elegí limitar los requisitos a lo que realmente puedo usar: ** Un autor, ingresa el contenido en Git y reconstruye el sitio web después de la publicación. El lector sólo necesita archivos estáticos y un navegador. ** Esta es la estructura actual "dinámica + artículo" de stephen.taipei.
El front-end puro se refiere al final de la lectura.#
Primero dejemos claros los límites. Este proyecto utiliza React, Vite y Tailwind. Node.js todavía es necesario para el procesamiento de contenido, la conversión de imágenes y la creación de scripts; La traducción también recurrirá a servicios externos durante la etapa de publicación. Después de la implementación, Nginx proporciona los archivos estáticos.
Entonces, lo que quiero decir con front-end puro es que cuando los lectores abren el sitio web, no necesitan un back-end de blog especial para buscar información, agrupar páginas o traducir en tiempo real. Tiene un proceso de compilación y un host, pero no una API de aplicación persistente ni una base de datos para leer artículos.
Si el trabajo se realiza con antelación, el final de la lectura será mucho más sencillo.
Noticias y artículos comparten la misma fuente, cada uno tiene su propio modelo#
El contenido se concentra en content/ y el texto original es todo Markdown. Los dos contenidos no están calzados en el mismo formato:
content/
tags.yaml
tags.locales.yaml
notes/2026/20261004-0944.md
notes/2026/20261004-0944.locales.yaml
articles/frontend-only-multilingual-blog/
index.md
locales.yaml
cover.webp
La dinámica son cartas cortas. El nombre del archivo utiliza el minuto de la hora de Taipei como ID. Se puede marcar con ideas, aprendizaje, en línea, trampas o lectura. Se puede establecer simplemente escribiendo un párrafo sin tener que pensar en un título cada vez. Aparecerá un recordatorio si supera las 500 palabras y deberá convertirse en artículo si supera las 1000 palabras.
Los artículos tienen un slug, título, resumen, fecha fijos y también pueden tener una portada y una hora de actualización. La URL no se cambiará después de que se haga pública para evitar que el enlace compartido original deje de ser válido.
Los dos están relacionados usando article:. Para obtener actualizaciones, puede escribir primero la pregunta y luego vincularla al artículo completo; la página del artículo enumerará las actualizaciones que hacen referencia a él al revés. El contenido breve conserva el punto de partida actual y el contenido largo es responsable de finalizar la derivación. No es necesario publicar el mismo texto completo en ambas caras.
Permitir que las reglas de contenido bloqueen los errores antes de compilar#
Si Markdown simplemente se incluyera en escena, muchos problemas no se verían hasta después de su publicación: fechas incorrectas, artículos no conectados, imágenes sin alternativas o el mismo tema escrito con tres etiquetas similares.
Puse estas reglas en el canal de contenido compartido. loadContent Lea el archivo, verifique los campos con Zod y luego procese Markdown, imágenes, idioma y relaciones de contenido. Tanto la inspección CLI como la construcción de Vite utilizan esta canalización para reducir la brecha entre "la máquina local dice que funciona, pero el entorno oficial no".
La etiqueta solo acepta tags.yaml claves registradas; deben existir artículos referenciados dinámicamente y otras dinámicas; La vista previa local puede leer borradores y la versión oficial excluirá los borradores. Estas reglas no se basan en recordarlas al publicar, sino que realmente bloquean las inspecciones.
Markdown también tiene límites de salida claros: el raw HTML no se emite directamente, el HTML generado se sanea y los bloques de código se resaltan durante la compilación. Las imágenes de contenido referenciadas mediante rutas relativas locales se convierten a WebP con un ancho máximo de 1600px y se les elimina la información EXIF, incluidos los datos GPS. Los artículos, las tarjetas y las vistas previas para compartir usan las imágenes procesadas. Las imágenes remotas y las referenciadas mediante rutas absolutas desde la raíz no pasan por este proceso de conversión local.
El costo del sistema multilingüe se pagará al publicar el artículo.#
El sitio web actualmente admite 22 idiomas. El texto original en chino tradicional se escribe una vez y los títulos, resúmenes, descripciones de texto e imágenes de los otros 21 idiomas se almacenan en el locales.yaml al lado.
Aquí hay una decisión que me importa: **Los lectores no tienen que esperar la traducción. ** content:localize Generar traducciones al publicar. La versión oficial solo lee los archivos existentes y no vuelve a llamar al servicio de traducción cada vez que se implementa.
Utilice el resumen fuente para comparar el texto original y el texto traducido. Si se modifica el texto original, la traducción anterior ya no podrá considerarse la última versión; Si falta una familia de idiomas o la traducción no está actualizada, content:check fallará. El proceso de traducción protege el código, el código en línea y los destinos de enlace de la traducción de identificadores técnicos o URL.
Esto no significa que la traducción automática tenga la calidad de la edición humana. Aborda la integridad y la coherencia actualizada, y el lenguaje y el tono profesionales aún requieren revisión. El texto original que no se haya modificado conservará la traducción existente y el texto modificado manualmente no se sobrescribirá al volver a ejecutarlo.
Lea el resumen en la lista, haga clic para cargar el artículo#
Una vez aumentado el contenido, si la página de inicio carga el texto completo de cada artículo y cada familia de idiomas, la interfaz pura será muy lenta.
El plugin de Vite divide el contenido en tres puntos de entrada: virtual:content/latest para la página de inicio, virtual:content/index para las páginas de contenido y un virtual:content/article/<slug> independiente para cada artículo. La página de inicio muestra las tres notas más recientes y tres tarjetas de artículos. Los datos de cada nota siguen incluyendo el texto completo y sus traducciones; la tarjeta solo recorta lo que muestra. Las tarjetas de artículos largos llevan únicamente resúmenes y datos relacionados, mientras que el cuerpo completo se carga por artículo mediante dynamic import.
El suspenso de React es responsable de esperar el texto y el límite de error exterior es responsable de mostrar el mensaje de error de carga. Cuando falla la solicitud de texto, el título, el resumen y la navegación de la página permanecen.
Aquí también hay compensaciones: actualmente, hay un fragmento de texto por artículo, que contiene traducciones de cada familia de idiomas, y aún no se ha dividido en un fragmento por familia de idiomas. El texto completo dinámico y las traducciones se ingresan en el índice de contenido. A medida que el volumen de contenido sigue aumentando, el tamaño del índice y el número de descargas de texto multilingüe son los siguientes pasos a medir.
Debe haber conmutación frontal y una entrada para lectura estática.#
Solo hacemos SPA y, cuando lo compartimos, a menudo solo vemos el título y la imagen en la página de inicio. Esto no es suficiente para el artículo.
Por lo tanto, al configurar, cada URL dinámica, de artículo y de etiqueta tiene su propio shell HTML, que contiene título, descripción, canónico, Open Graph y JSON-LD, y el texto original legible se coloca en noscript. Puede obtener el contenido y la portada exclusiva de este artículo sin ejecutar JavaScript.
El mismo contenido también producirá feed.xml, sitemap-content.xml, la versión Markdown del artículo y el índice del artículo para llms.txt. Estos son productos creados, no otro conjunto de contenido que requiere sincronización manual.
Sin embargo, los límites del SEO multilingüe también deben quedar claros: **Actualmente, las actualizaciones y los artículos comparten la misma URL en cada idioma, y el idioma se cambia mediante la interfaz; Las páginas de contenido estático, los feeds y las versiones Markdown mantienen el texto original. ** La página de inicio también tiene renderizado previo para el idioma y los modos claro y oscuro. Esto no significa que cada artículo tenga también 22 páginas de idiomas indexables de forma independiente.
Si necesita permitir que los motores de búsqueda incluyan cada familia de idiomas en el futuro, deberá agregar el enrutamiento de la familia de idiomas, las páginas estáticas correspondientes y hreflang. Ése es otro requisito que este conjunto aún no cumple.
La publicación también es un proceso de entrega.#
Mi proceso de publicación es: escribir el texto original, generar la traducción, verificar el contenido, enviarlo a Git y luego fusionarlo en main. Las GitHub Actions existentes reconstruirán el sitio web y lo implementarán.
npm run content:localize
npm run content:check
npm run test:content
npm run build
Si faltan traducciones, primero completo los idiomas pendientes. Los artículos en borrador se quedan en la vista previa local. La salida de producción también comprueba que no existan páginas de borradores y busca sus marcas en los principales archivos públicos, como el feed, el sitemap, la página de inicio y los bundles de JavaScript. El contenido, igual que el código, tiene diffs e historial de versiones, y se puede revertir junto con él.
Este método sacrifica las actualizaciones en tiempo real: si cambia un fragmento de texto, debe esperar una implementación. Pero para mi sitio web técnico, estoy dispuesto a cambiar esta espera a cambio de un backend menos, una dependencia del período de ejecución menos y un registro de cambios que sea más fácil de rastrear.
¿Cuándo es apropiado hacer esto?#
Si es autor, puede reconstruir la frecuencia de publicación, el contenido es principalmente texto y está acostumbrado a Git, esta arquitectura es muy adecuada. Las actualizaciones breves reducen el umbral de escritura, el artículo conserva la derivación completa y el multilenguaje permite que más personas lean el mismo contenido.
Si los requisitos se convierten en colaboración en tiempo real entre varias personas, gestión de permisos, comentarios de lectores, inicio de sesión o suscripción, entonces es necesario reevaluar el CMS y el backend. El front-end puro no hará que estas necesidades desaparezcan automáticamente, pero no las he incluido en el alcance de este blog por el momento.
Los principios de diseño esta vez son muy similares a lo que escribí en Patrón arquitectónico para múltiples layouts y temas: primero separe las responsabilidades claramente y luego deje que las reglas compartidas mantengan la coherencia.
Para este sitio web, el punto clave es permitirme seguir escribiendo y saber qué contenido se entrega realmente cada vez que publico.