Empty translated blog posts with manifest-based deployment
The Problem
After deploying to Cloudflare, translated blog posts (Spanish, German, French, etc.) randomly loaded with empty content. Sometimes they worked fine, sometimes not. The page rendered, but the content was missing.
What is a Manifest File?
My blog posts are stored as JSON files in the codebase, not in a database. This lets me write content in my editor and track everything in Git. Simple.
A manifest file is a pre-generated JSON file that contains all blog post data. Cloudflare Workers don't have access to a traditional filesystem at runtime, they can only use files bundled during the build. So instead of reading individual blog post files at runtime, the build process creates a single manifest with all posts.
The manifest is generated at build time and included in the deployment. At runtime, the code reads from this manifest instead of the filesystem.
The Cause
My manifest structure looks like this:
{
"slug": "my-post",
"translatedSlugs": {
"en": "my-post",
"es": "mi-publicacion"
},
"localized": {
"es": { "title": "...", "content": "..." },
"de": { "title": "...", "content": "..." }
}
}
Notice: English is NOT in the localized
object. English content lives at the base level.
My getPostByTranslatedSlug
function was doing this:
// Get the post with English locale to access translatedSlugs
const post = getPostBySlug(slug, 'en');
// Check if translatedSlugs match...
if (post.translatedSlugs[someLocale] === translatedSlug) {
return getPostBySlug(slug, requestedLocale);
}
When I called getPostBySlug(slug, 'en')
, it tried to access manifest.localized.en
which doesn't exist, so it returned empty content.
The Fix
Instead of calling getPostBySlug(slug, 'en')
, I changed it to use getAllPosts()
which returns all posts with base metadata including translatedSlugs
:
export function getPostByTranslatedSlug(translatedSlug: string, locale: string): BlogPost | null {
// Get all posts to access base metadata
const allPosts = getAllPosts();
for (const post of allPosts) {
if (post.translatedSlugs) {
for (const [lang, slugValue] of Object.entries(post.translatedSlugs)) {
if (slugValue === translatedSlug) {
// Found it! Now get the localized version
return getPostBySlug(post.slug, locale);
}
}
}
}
return null;
}
Is Using a Manifest File a Good Practice?
It depends on your deployment target:
For Cloudflare Workers / Edge platforms: Yes, it's necessary. These platforms don't have access to a traditional filesystem, so you need to bundle all data at build time.
For traditional Node.js servers (Vercel, AWS, etc.): Not required. You can read from the filesystem at runtime, which is simpler and doesn't require a build step to generate the manifest.
The tradeoff: manifest files add complexity to your build process and can cause bugs like this one if not structured carefully. But they enable deployment to edge platforms which are faster and cheaper.
Lesson Learned
When using a manifest for static exports:
- Don't assume the base locale exists in
localized
- Base metadata (like
translatedSlugs
) lives at the root level - Only non-base locales are in
localized