Skip to content
Engineering

Block-based CMS with AI translation: our stack and trade-offs

We built a headless CMS with block editor, AI translation to 10 languages, and SEO tooling on PostgreSQL. Here's why we rejected SaaS and what we'd do differently.

AAIVCJ 5 min read

We built a block-based headless CMS with AI translation because no off-the-shelf platform gave us both control and simplicity. This is the engine behind aivcj.com—and we're sharing what we learned about trading SaaS convenience for architectural ownership.

What we built and why

The core problem: we publish technical content for Indian manufacturers, distributors, and HR heads in 10 languages. We needed editors to compose pages without touching code, translate content in one click, and ensure every language version ranked in search. A SaaS headless CMS would have meant stitching together Contentful + a translation API + an SEO plugin, with per-seat costs and API call overheads.

Instead, we built a single system on PostgreSQL with three layers:

  1. Block editor: Non-technical editors compose pages from pre-built, on-brand sections (hero, testimonial, comparison table, form).
  2. AI translation pipeline: One click sends English content to Claude, which translates to 10 languages with terminology consistency and brand voice.
  3. SEO suite: Automatic hreflang tags, JSON-LD schema, sitemaps, and llms.txt generation for each language.

The stack: Next.js frontend, NestJS backend, PostgreSQL database, Claude for translation. We self-host everything.

How it works

Block composition: Editors open the visual builder and drag sections onto a page. Each section is a React component with predefined props (headline, body, image, CTA button). No custom HTML. Blocks are stored as JSON in PostgreSQL, so they're queryable and versionable.

Translation flow: Editor clicks "Translate to all languages." The system extracts all text blocks, sends them to Claude with a structured prompt that includes:

  • Terminology glossary (e.g., "AIVCJ ERP" stays untranslated in all languages)
  • Brand voice guidelines (formal, confident, specific—no generic phrasing)
  • Context (is this a product page, blog post, or case study?)
  • Target language and audience (Indian market context for Hindi, German business norms for German)

Claude returns translations as JSON. We store them as draft versions, never auto-publishing. An editor reviews each language, can edit inline, and approves before going live. This human-in-the-loop step prevents hallucination and cultural mismatches.

SEO automation: When a page is published in English, the system generates:

  • A canonical URL (e.g., /en/blog/erp-for-credit-based-distribution-businesses)
  • hreflang links pointing to /hi/, /de/, /fr/ versions
  • JSON-LD structured data with language metadata
  • Sitemap entries for each language
  • llms.txt file listing all pages and their translations

We submit sitemaps via IndexNow to notify Google of new content immediately, rather than waiting for crawl.

Key decisions and trade-offs

PostgreSQL over MongoDB: We needed strong consistency for role-based access control (an editor in India shouldn't see HR content for a US client). PostgreSQL's JSONB type lets us store blocks as JSON while enforcing relational constraints on users, roles, and permissions. A document database would have meant building our own consistency layer.

Block editor over markdown: Blocks give non-technical users (ops leads, HR heads) a visual, drag-and-drop interface. Markdown is faster to write and version-control friendly, but requires coding skill. We chose blocks because our audience doesn't code. We still support markdown inside text blocks for power users who want to write faster.

AI translation with human review, not auto-publish: Claude is powerful but not perfect. We could auto-publish translations, but that risks brand damage and SEO penalties if the model misunderstands context. This is a deliberate slowdown for safety.

Role-based access over open editing: We use PostgreSQL's row-level security to ensure editors see only content they're assigned to. We added 2FA for admin accounts. This is more complex than "everyone can edit everything," but necessary for multi-client, multi-language teams.

Self-hosting over SaaS: We pay for infrastructure (database, compute, storage) but avoid per-seat licensing and API call overheads. For a small team, this is cheaper. For a large organization with many concurrent editors, SaaS might be more cost-effective. We chose self-hosting because we wanted data residency control and the ability to embed Claude directly without API call limits.

Build vs. SaaS: our decision matrix
FactorSaaS headless CMSOur PostgreSQL build
AI translationNoAPI calls, extra costYesNative, Claude integrated
SEO toolingPartlyLimited, plugins requiredYesFull control, hreflang, JSON-LD, llms.txt
Role-based accessPartlyPer-seat pricing, limited rolesYesCustom roles, 2FA, audit logs
Data residencyNoUS/EU only, compliance riskYesSelf-hosted, full control
Setup timeYesDays, minimal engineeringNoMonths, dedicated team
Scaling costNoPer-user or per-request feesYesInfra cost only, predictable

Why we chose to build on PostgreSQL instead of adopting a headless CMS platform.

What we measured

We track:

  • Translation latency: Time from "Translate" click to draft ready for review. This depends on Claude's response time, not our code.
  • Editor workflow: Pages published per week, average time to publish (including review). We aim for <2 hours from draft to live across all languages.
  • Uptime: The CMS is live at /work/aivcj-cms for you to test.

We don't publish latency or accuracy numbers because they depend on Claude's performance, which we don't control.

What we would do differently

Batch translation scheduling: Right now, translating a page is synchronous—you click, wait for Claude, then review. For high-volume content (e.g., 50 pages at once), we'd queue translations and send batch requests to Claude, reducing API calls and cost.

Translation memory: We could store all past translations and reuse them for similar phrases, reducing API calls and ensuring consistency. We haven't built this yet because our content volume is still small, but it's on the roadmap.

Block versioning UI: Editors can't yet see a visual diff between block versions. We store versions in PostgreSQL, but the UI only shows "revert to version X." A side-by-side diff would help editors understand what changed.

Multilingual SEO insights: We generate hreflang and JSON-LD automatically, but we don't yet show editors which keywords rank in each language or which translations drive traffic. Integrating Google Search Console data would help editors optimize for each market.

Try it yourself

The CMS is live and open to explore. Visit /work/aivcj-cms to see the block editor, translation pipeline, and SEO suite in action. You can see how pages are composed, how translations are managed, and how the system handles 10 languages.

If you're building a multilingual platform for manufacturers, distributors, or HR teams, this architecture scales. The same engine powers our CMS & Content Platforms offering, which includes block builder, AI translation, SEO tooling, role-based publishing, and prompt management.

We've also documented related challenges: how to build a company chatbot that cites its sources, when RAG actually makes sense, and how to safely embed AI into HR workflows.

AI translation workflow
  1. Step 1: Editor writes in EnglishComposes blocks, sets metadata, saves draft
  2. Step 2: Trigger translationOne-click request to translate to 10 languages
  3. Step 3: Claude processesStructured prompt with terminology, brand voice, context
  4. Step 4: Translations stored as draftVersioned, not yet live, ready for review
  5. Step 5: Editor reviews & approvesCan edit, flag issues, or request retranslation
  6. Step 6: Publish to all languagesScheduled or immediate, with hreflang & JSON-LD

Content flows from editor through Claude, with manual review before publishing to each language.

Sources

Primary sources this article relies on. Rules and rates change — check the source before you act.

  1. 01Google: localized versions (hreflang)developers.google.com · accessed Oct 7, 2026
  2. 02Google: FAQ structured datadevelopers.google.com · accessed Oct 7, 2026
  3. 03llms.txt proposalllmstxt.org · accessed Oct 7, 2026
  4. 04IndexNowindexnow.org · accessed Oct 7, 2026
  5. 05Next.js internationalizationnextjs.org · accessed Oct 7, 2026
  6. 06W3C: declaring language in HTMLw3.org · accessed Oct 7, 2026
  • #cms
  • #ai translation
  • #architecture
  • #postgresql
  • #next js
LinkedInWhatsApp
Frequently asked questions

Frequently asked questions

01Why did you build your own CMS instead of using Contentful or Strapi?
We needed tight integration with AI translation, SEO tooling, and role-based publishing without vendor lock-in or per-seat costs. A SaaS headless CMS would have meant custom API calls for every feature we wanted to own. Building on PostgreSQL gave us control over data residency, cost scaling, and the ability to embed Claude directly into the translation pipeline.
02How does the AI translation pipeline avoid hallucination?
We use Claude with a structured prompt that enforces terminology consistency across languages and requires the model to flag ambiguous source text. Each translation is versioned and can be manually reviewed before publishing. We don't auto-publish translated content; editors always have a review step.
03What's the trade-off between block-based and markdown?
Blocks give non-technical editors a visual, drag-and-drop experience and enforce consistent styling. Markdown is faster to write and version-control friendly, but requires technical skill. We chose blocks because our audience includes ops leads and HR heads who don't code. We still support markdown inside text blocks for power users.
04How do you handle SEO for 10 language versions?
We use hreflang tags to tell Google which page is the canonical version in each language, generate JSON-LD schema for each language variant, and submit sitemaps via IndexNow to speed up indexing. Each language gets its own URL structure (e.g., /en/, /hi/, /de/) with proper lang attributes.
05Why PostgreSQL and not a document database?
PostgreSQL's JSONB type gives us the flexibility of document storage with the safety of relational constraints. We needed strong consistency for role-based access control, version history, and audit trails. Document databases would have meant building our own consistency layer. PostgreSQL also scales better for our query patterns: filtering by language, status, and role.
Need this built?

CMS & Content Platforms

Your team edits everything. Developers touch nothing.

Keep reading