πŸš€ How qz-l.com Delivers Multi-Language Blog Posts with Only One Markdown File

How we implemented automatic real-time translations using Google GenAI**, while preserving all Markdown formatting perfectly.

December 1, 2025β€’By qz-l team

πŸš€ 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:

  1. We detect the requested language
  2. If it's "en" β†’ deliver the original .md
  3. If it's "zh" or "fr" β†’ call our translation API
  4. The API asks Google GenAI to translate:
    • markdown
    • title
    • description
  5. Returned content is pure Markdown, structure preserved
  6. 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!

Related Posts

Building a Markdown-Based Blog System with Next.js

Learn how we built a simple, performant blog system for qz-l using markdown files and Next.js static generation

Tech Dive: Building the AI-Powered QR Scanner Tool on QZ-L.com

A deep dive into how QZ-L.com implemented the AI-powered QR Scanner tool with React, Tailwind CSS, and AI analysis.

Full Subscription Management Now Live on QZ-L.com

QZ-L.com now lets you upgrade, cancel, and track your subscription usage with ease. Manage your plans and enjoy full control over your short links and aliases.

πŸš€ How qz-l.com Delivers Multi-Language Blog Posts with Only One Markdown File | qz-l