Publicaciones de blog traducidas vacías con despliegue basado en manifiesto
El Problema
Después de desplegar en Cloudflare, las publicaciones de blog traducidas (español, alemán, francés, etc.) se cargaban aleatoriamente con contenido vacío. A veces funcionaban bien, a veces no. La página se renderizaba, pero faltaba el contenido.
¿Qué es un Archivo Manifiesto?
Mis publicaciones de blog se almacenan como archivos JSON en la base de código, no en una base de datos. Esto me permite escribir contenido en mi editor y seguir todo en Git. Simple.
Un archivo manifiesto es un archivo JSON pregenerado que contiene todos los datos de las publicaciones del blog. Cloudflare Workers no tienen acceso a un sistema de archivos tradicional en tiempo de ejecución, solo pueden usar los archivos incluidos durante la compilación. Por lo tanto, en lugar de leer archivos de publicaciones individuales en tiempo de ejecución, el proceso de compilación crea un único manifiesto con todas las publicaciones.
El manifiesto se genera en el momento de la compilación y se incluye en el despliegue. En tiempo de ejecución, el código lee de este manifiesto en lugar del sistema de archivos.
La Causa
La estructura de mi manifiesto es la siguiente:
{
"slug": "mi-publicacion",
"translatedSlugs": {
"en": "my-post",
"es": "mi-publicacion"
},
"localized": {
"es": { "title": "...", "content": "..." },
"de": { "title": "...", "content": "..." }
}
}
Observa: El inglés NO está en el objeto localized
. El contenido en inglés reside en el nivel base.
Mi función getPostByTranslatedSlug
hacía esto:
// Obtener la publicación con la localización en inglés para acceder a translatedSlugs
const post = getPostBySlug(slug, 'en');
// Comprobar si translatedSlugs coincide...
if (post.translatedSlugs[someLocale] === translatedSlug) {
return getPostBySlug(slug, requestedLocale);
}
Cuando llamaba a getPostBySlug(slug, 'en')
, intentaba acceder a manifest.localized.en
, que no existe, por lo que devolvía contenido vacío.
La Solución
En lugar de llamar a getPostBySlug(slug, 'en')
, cambié para usar getAllPosts()
, que devuelve todas las publicaciones con metadatos base, incluidos translatedSlugs
:
export function getPostByTranslatedSlug(translatedSlug: string, locale: string): BlogPost | null {
// Obtener todas las publicaciones para acceder a los metadatos base
const allPosts = getAllPosts();
for (const post of allPosts) {
if (post.translatedSlugs) {
for (const [lang, slugValue] of Object.entries(post.translatedSlugs)) {
if (slugValue === translatedSlug) {
// ¡Encontrado! Ahora obtener la versión localizada
return getPostBySlug(post.slug, locale);
}
}
}
}
return null;
}
¿Es una Buena Práctica Usar un Archivo Manifiesto?
Depende de tu objetivo de despliegue:
Para Cloudflare Workers / plataformas Edge: Sí, es necesario. Estas plataformas no tienen acceso a un sistema de archivos tradicional, por lo que necesitas empaquetar todos los datos en el momento de la compilación.
Para servidores Node.js tradicionales (Vercel, AWS, etc.): No es necesario. Puedes leer desde el sistema de archivos en tiempo de ejecución, lo cual es más simple y no requiere un paso de compilación para generar el manifiesto.
La contrapartida: los archivos manifiesto añaden complejidad a tu proceso de compilación y pueden causar errores como este si no se estructuran cuidadosamente. Pero permiten el despliegue en plataformas edge, que son más rápidas y económicas.
Lección Aprendida
Al usar un manifiesto para exportaciones estáticas:
- No asumas que la localización base existe en
localized
- Los metadatos base (como
translatedSlugs
) residen en el nivel raíz - Solo las localizaciones que no son la base están en
localized