0%
Back to Showcase
Site builder · Next.js, TypeScript

GoSite

Every module on a GoSite site has to exist in every language it publishes. I rebuilt the translation pipeline that makes that affordable.

GoSite

Project Info

Client
My own product
Timeline
Ongoing
Role
Founder. Design and engineering.

Methodology

Next.jsTypeScriptFirestoreAutomated testing

Summary

GoSite is a site builder I own. Pages are assembled from content modules, and each one has to exist in every published language. I rebuilt the multilingual translation pipeline behind it: batching, chunk packing, caching and reinjection, in Next.js and TypeScript over Firestore.

The Challenge

The first version translated one module per model call. It worked, and it could not get cheaper: cost scaled with the number of modules, and the model saw a fragment instead of a page, so it lost the context that decides how a sentence reads.

The goal: batch blocks into single requests without ever putting translated text back into the wrong module.

The constraint

GoSite builds sites out of content modules, and every module has to exist in every language a site publishes. The first version translated module by module: one model call per block. It worked, and it had two problems that got worse as sites grew. Publishing was slow and expensive, and the model saw a fragment rather than a page, so it lost the context that decides how a sentence should read.

This is my own product, so the architecture decisions here are mine to make and mine to live with.

What I decided

Batch blocks into one request instead of paying per block.The per-block version was the cheaper thing to write and structurally could not get better: the cost scaled with the number of modules, forever. Batching had a ceiling worth chasing, and it bought back the page context the model was missing.

Accept two hard problems in exchange.Packing blocks up to a token budget, and putting each translated fragment back into the exact module it came from. Reinjection is the one that bites: a batch that returns slightly out of shape will quietly write the wrong text into the wrong block, and the page still renders.

Cache instead of re-requesting.Sites are republished far more often than their content changes, so paying to translate unchanged blocks on every publish is the largest avoidable cost in the system.

What I built

Next.js and TypeScript on the server side, with Firestore for storage. I owned this end to end: the frontend integration, the server-side TypeScript, the debugging, the tests and the production rollout.

  • A chunk packer that fills each request up to a token budget rather than sending whatever happens to be next.
  • A reinjection layer that maps every translated fragment back onto its source module.
  • A cache layer, so republishing unchanged content does not re-translate it.
  • An automated test suite over the packing and reinjection paths, which is where the production bugs actually were.

What went wrong, and what I changed

Reinjection bugs reached production before the tests did. Content came back attached to the wrong module, which is the failure mode of this design and the one I should have written tests against first. Packing and reassembly is exactly the kind of logic that is cheaper to test than to debug in a live system, and I did it in the wrong order.

The suite that exists now covers those paths, and it exists because of what they cost.

Where it stands

The pipeline runs in production at gosite.dev, with token usage per publish materially below the per-block version it replaced.

© 2026 Hossein Hashemi. All rights reserved.