When your blog has to exist in more than one language
Running a publication in several languages is mostly an exercise in moving the same post through a pipeline without breaking it. You have the original, you have a translation, and somewhere in between you have a Make scenario calling DeepL, a tag map, a slug convention, a set of images, and a tangle of internal links that all have to come out the other side intact. The hard part was never the translation itself — modern machine translation is good. The hard part is everything wrapped around the words: keeping the structure, the metadata, and the cross-references aligned across every language you publish, and then getting each version back into Ghost without flattening it on the way.
What actually breaks in a translation pipeline
When people describe their multilingual Ghost setup, the words that come up are brittle and multi-step. A typical flow fires a webhook on publish, ships the body to a translation API, stitches the result back together, and tries to recreate tags and a slug and a feature image on a second post or a second site. Every hop is a place where something quietly goes wrong. The translated post loses a tag, or gets a slug that collides with the original, or comes back as plain prose where a Ghost callout, toggle, or bookmark used to be. Internal links still point at the source-language URLs because nothing rewrote them. And because the whole thing runs through automation you only half-remember configuring, you usually find out weeks later when a reader or a search console does.
The deeper issue is that none of this lives anywhere you can see it all at once. Your canonical content is locked behind a web editor, your translations are scattered across automation logs and a second instance, and there is no single place where you can look at the source and its translations side by side and confirm they match.
A local control plane for every language
Specter gives you that single place. It is a native macOS app that does two-way sync between a Ghost blog and a folder of plain markdown files: pull every post down with its frontmatter — title, tags, status, feature image URL, excerpt — work on it locally, and push it back. Once your archive is a folder of .md files, your source language and your translations become files sitting next to each other on disk, and that folder becomes the control plane the pipeline never had.
From there the translation work changes shape. Instead of pushing one post at a time through a webhook, you can hand the whole archive — or a whole section of it — to the AI you already use, ask it to translate every body while leaving the structure and metadata alone, and read the result as files before any of it goes near Ghost. You bring your own model; Specter bundles none and charges for no tokens. The piece that makes this safe rather than reckless is the dry-run preview: before anything writes, Specter shows you exactly which posts would be created, updated, or flagged as a conflict, so you see the full scope of a translation pass instead of trusting an automation to have done the right thing. The longer walk-through of that workflow lives at translate your Ghost blog.
Why structure and Cards are the whole game here
Translation is where the fidelity question stops being abstract. Ghost posts are not really markdown — they are Lexical documents, and the markdown Specter gives you is a faithful projection of that, not the underlying truth. For ordinary prose the round-trip is clean. For posts built heavily out of Cards — galleries, embeds, toggles, bookmarks — a projection can lose detail on the way back, and you should know that going in rather than discover it on a published page. This is exactly why the dry-run exists and why it matters more in a translation flow than almost anywhere else: it is the moment to confirm a callout is still a callout before you ship the German version. How Specter handles Ghost Cards is worth reading before you translate a card-heavy archive, because it tells you honestly where the edges are.
The same local view is what makes the cross-reference work tractable. When your translated posts are files, rewriting internal links so the French version points at French URLs is a pass over a folder rather than a hand-edit per post — see bulk-fixing internal links. And because every language now exists as plain files, you have a real copy of all of it; pairing this with a proper backup of your Ghost blog means a translation gone wrong is something you can roll back, not something you have to re-translate.
What this does and does not solve
It is worth being precise about the boundary. Specter works on the content layer — the posts, their text, their metadata. It does not configure your multilingual site for you: the theme work, the URL routing between languages, the hreflang tags and canonical setup that tell search engines which version is which all live in your Ghost theme and infrastructure, and that is a separate job from anything Specter touches. What Specter handles is keeping the actual content of each language correct and in sync.
If your setup runs one Ghost instance per language — a common pattern — that maps onto Specter cleanly: each instance is its own synced folder. You connect each one with its Admin API key, and you end up with a folder per language, each a clean local mirror you can translate, review, and push back independently. The original and its translations stop being scattered across automations and second sites, and start being files you can see, diff, and trust.