🚀 How qz-l.com Delivers Multi-Language Blog Posts with Only One Markdown File

At qz-l.com, we believe great developer experiences should be fast, simple, and intelligent.
When building our new blog system, we wanted a solution that:

Keeps a single English Markdown file as the source of truth

Automatically serves blog content in English, Chinese, or French

Avoids manually maintaining multiple .md translations

Uses modern AI responsibly and efficiently

Integrates seamlessly with our Next.js App Router

This post covers exactly how we implemented automatic real-time translations using Google GenAI, while preserving all Markdown formatting perfectly.

🎯 Problem: Multi-Language Blog Without Multi-File Headaches

Traditionally, multi-language blogs maintain:

/posts/my-post.en.md
/posts/my-post.zh.md
/posts/my-post.fr.md

This quickly becomes:

❌ Hard to maintain

❌ Error-prone

❌ Difficult to update consistently

❌ Expensive (writer/translator cost)

We wanted:

✔ Only one file

✔ Translated automatically

✔ SEO-friendly

✔ Fully Markdown-preserving

✔ No hallucinated structure changes

✔ No risk of broken code blocks or formatting

So we built a translator API endpoint around:

@google/genai
gemini-2.5-flash-lite

🧠 The Core Idea

When a user views a blog post:

We detect the requested language

If it's "en" → deliver the original .md

If it's "zh" or "fr" → call our translation API

The API asks Google GenAI to translate:

markdown

title

description

Returned content is pure Markdown, structure preserved

We render it exactly as if it were a local .md file

📡 API Design

Our translation endpoint supports three fields:

type TranslateRequest = {
markdown?: string;
targetLanguage: string;
title?: string;
description?: string;
};

And returns:

type TranslateResponse = {
translatedMarkdown?: string;
translatedTitle?: string;
translatedDescription?: string;
};

🤖 System Prompt (The Secret Sauce)

We built a robust system prompt to guarantee:

No broken Markdown

Table, lists, headings, inline code preserved

Only the text is translated

JSON response guaranteed

Here is the exact SYSTEM_PROMPT:

You are a professional translator and Markdown expert.
Your task is to translate the input content into the TARGET_LANGUAGE specified in the user request.
Preserve all Markdown formatting, code blocks, headings, lists, links, tables, emojis, and any other Markdown syntax exactly for the 'markdown' field.
Also translate 'title' and 'description' text accurately.
Return only the translated content in the same structure as input.
If the TARGET_LANGUAGE is English, return the original content unmodified.

🛠 Implementation (Next.js API Route)

Below is the core logic powering our blog translation:

import { GoogleGenAI } from "@google/genai";

const apiKey = process.env.GOOGLE_AI_KEY!;
const ai = new GoogleGenAI({ apiKey });

export async function POST(req: NextRequest) {
const { markdown, targetLanguage, title, description } = await req.json();

const SYSTEM_PROMPT = `...`;

const userContent =
`TARGET_LANGUAGE: ${targetLanguage}\n\n` +
`markdown:\n${markdown}\n` +
(title ? `\ntitle:\n${title}\n` : "") +
(description ? `\ndescription:\n${description}\n` : "");

const aiRes = await ai.models.generateContent({
model: "gemini-2.5-flash-lite",
contents: [{ role: "user", parts: [{ text: userContent }] }],
config: {
temperature: 0,
systemInstruction: {
role: "system",
parts: [{ text: SYSTEM_PROMPT }],
},
},
});

const textOutput = aiRes.candidates?.[0]?.content?.parts?.[0]?.text ?? "";
let cleanText = textOutput
.trim()
.replace(/^```json\s*/i, "")
.replace(/```$/, "");

const aiJson = JSON.parse(cleanText);

return NextResponse.json({
translatedMarkdown: aiJson.markdown || aiJson.markdown?.content,
translatedTitle: aiJson.title,
translatedDescription: aiJson.description,
});
}

🚦 When Does Translation Happen?

Inside the blog loader:

let posts = await getBlogPostSummaries();
const lang = await getCurrentLanguage();

if (lang !== "en") {
posts = await Promise.all(
posts.map((post) => translatePost(post, lang))
);
}

This keeps everything fast and idiomatic in Next.js.

💾 Should We Cache Translations?

Yes — and we will.
Google GenAI is fast but not free.

Planned improvements:

Redis caching

File-based .cache.json

ETag header invalidation

Blog content doesn't change often, so per-post caching is ideal.

🌍 Result: True Multi-Language Without Multi-File

Our system now supports:

🇬🇧 English

🇨🇳 Chinese

🇫🇷 French

All from one single Markdown file.

No duplication.
No editing in multiple languages.
No human translation cost.

Just:

Great UX

Clean architecture

Low maintenance

Perfect Markdown fidelity

🚀 What’s Next?

We are expanding internationalization with:

Per-paragraph caching

Translator cost analytics

Inline code fence safety mode

Translated SEO fields

Automatic hreflang sitemap support

🙌 Final Thoughts

This system lets us write once and publish globally.
If you're building a multilingual blog without translator overhead, this architecture is clean, scalable, and future-proof.

If you’d like the full example repo or template, just ask!