How I built a massive multilingual pSEO architecture without losing my mind

Vibe coded a multilingual pSEO monster lately. The single file system became too large, so it had to be refactored.

The structure needed to be modular, scalable, and actually maintainable. Most programmatic SEO projects fall apart because of janky folder spaghetti or dumping junk content and getting blacklisted. I wasn’t going to do either.


1. Brands as a source of truth

All supported brands live in one clean brands.ts file. It’s a TypeScript array where each brand has its metadata (slug, image path, etc.). You update it onceboom, all systems know.

export const brands = [
{
name: 'Amazon',
slug: 'amazon',
logo: '/logos/amazon.svg',
// ...
},
// ...
] as const

This file drives the content structure, the index building, the variation logic - everything.


2. Content modularized by Type + Language

Each content type (info, how to buy, where to buy) lives in its own directory under src/data/content. Inside each, you’ll find:

  • types.ts for typing
  • index.ts to stitch everything together
  • Language files (en.ts, ru.ts, es.ts, cn.ts, etc.)

Each language file is just a big object mapping brand names to their language-specific content. Like this:

export const giftCardContentEN = {
Amazon: {
intro: "Buy Amazon gift cards easily...",
steps: [...],
tips: [...],
},
// ...
}

The index.ts then composes all of these into a single object per brand:

export const giftCardContent = {
Amazon: {
en: giftCardContentEN.Amazon,
ru: giftCardContentRU.Amazon,
// ...
},
// ...
}

This way, every brand always has a defined slot for each content type and language. No gaps and if-hell.


3. Variation system that doesn't suck

To avoid templated content hell, I built a variation engine with dynamic section generators. Located in src/lib/howToBuyVariations/ and whereToBuyVariations/, each language has its own variation file. That means intros, tips, and step-by-step guides can be randomized or swapped without compromising clarity or intent.

So instead of dumping 1000 low-effort pages into Google and hoping for the best, this structure actually gives the user something valuable while staying unique enough to avoid penalties.


4. Multilingual blog architecture that’s clean as hell

Every blog lives under src/app/[locale]/blog/[slug]/page.tsx. Example:

src/app/en/blog/how-to-use-steam-gift-card/page.tsx
src/app/es/blog/how-to-use-steam-gift-card/page.tsx

Each blog is fully localized with its own title, meta, and content per language. Want to know if a blog is “complete”? Just check if the slug exists in all language folders. That’s it.


5. Translation coverage with TypeScript safety

Thanks to static typing, I can’t forget translations even if I wanted to. Every brand and language gets checked at build time. If a field is missing, it fails. That forces discipline and makes the whole setup feel more like a product than a content dump.


6. Adding a new Brand or Language is fast

  • New brand? Add to brands.ts, then drop its content into each language file.
  • New language? Create the language file in each content type, wire it into the index, and done.

The whole system is driven by structure, not guesswork.


7. Results (So Far)

Launched in a hypercompetitive niche. Within a few weeks:

  • ~11,000 impressions
  • ~1500/day growth rate
  • Still early, but already indexing cleanly

Programmatic SEO doesn’t have to be spam. You just need to treat it like product development. If it’s actually helpful, long-tail, and well-linked, it ranks. If it’s just scale for the sake of scale, it dies.

This setup can scale up to hundreds of brands and dozens of languages without imploding.