Tutorial

Auto-Translate Ghost Blog Posts into 8 Languages for SEO

Published September 25, 2026

Auto-Translate Ghost Blog Posts into 8 Languages for SEO

Auto‑Translate Ghost Blog Posts into 8 Languages for SEO

Published on 2026‑09‑25 • 8‑minute read


Why multilingual Ghost posts?

Ghost is a popular, headless CMS for publishing. By default it serves content in a single language, which limits organic traffic from non‑English markets. Translating each article manually is time‑consuming and error‑prone. With SiteLocaleAI you can:

  • Translate on the fly using your own LLM API key (Claude, GPT‑4o‑mini, etc.).
  • Localize prices with psychological rounding per currency.
  • Pre‑render translated pages so Google and Bing index the full HTML, boosting international SEO.
  • Stay self‑hosted, keeping data and keys under your control.

Below is a step‑by‑step technical guide to turn a standard Ghost installation into a multilingual, SEO‑ready publishing engine.


Prerequisites

Item Minimum version
Ghost 5.0+ (self‑hosted or Ghost(Pro) with custom theme)
Node.js 18.x
npm / yarn latest
LLM API key Claude, GPT‑4o‑mini, or any OpenAI‑compatible endpoint
SiteLocaleAI package @sitelocaleai/translate (npm)

1. Install the SiteLocaleAI JS library

From your Ghost theme directory (e.g., content/themes/my‑theme), run:

npm install @sitelocaleai/translate

The library is framework‑agnostic, so you can import it in any front‑end script.


2. Create a tiny translation helper

Add a new file src/translate.js (or any location you prefer) with the following code:

// src/translate.js
import { LocaleAI } from '@sitelocaleai/translate';

// Pull your LLM API key from an environment variable (never hard‑code!).
const localeAI = new LocaleAI({
  apiKey: process.env.SITELOCALEAI_API_KEY,
  provider: 'openai', // or 'anthropic' for Claude
  model: 'gpt-4o-mini', // adjust as needed
});

/**
 * Translate raw HTML content into the target language.
 * @param {string} html   The original Ghost post HTML.
 * @param {string} lang   ISO‑639‑1 language code (e.g., 'de', 'fr').
 * @returns {Promise<string>} Translated HTML.
 */
export async function translatePost(html, lang) {
  // Preserve HTML tags while translating the textual nodes.
  const translated = await localeAI.translateHTML(html, {
    targetLang: lang,
    // Enable price localisation – the library will detect $/£/€ etc.
    priceLocalization: true,
    // Psychological rounding (e.g., $9.99 → $9.95)
    rounding: true,
  });
  return translated;
}

This helper will be called from a Ghost webhook (next section).


3. Set up a Ghost webhook to trigger translation

In the Ghost Admin panel, go to Integrations → Webhooks → Add webhook.

  • Event: post.published
  • URL: https://your‑domain.com/api/translate
  • Method: POST

Create a lightweight Express endpoint (or any serverless function) that receives the post payload, runs the translation, and stores the results back into Ghost via its Content API.

// server/translate-webhook.js
import express from 'express';
import bodyParser from 'body-parser';
import { translatePost } from '../src/translate.js';
import GhostContentAPI from '@tryghost/content-api';

const app = express();
app.use(bodyParser.json());

const ghost = new GhostContentAPI({
  url: process.env.GHOST_URL,
  key: process.env.GHOST_ADMIN_KEY,
  version: 'v5.0',
});

// Target languages – eight of them
const LANGUAGES = ['de', 'fr', 'es', 'ja', 'zh', 'pt', 'it', 'nl'];

app.post('/api/translate', async (req, res) => {
  const { post } = req.body;
  const originalHtml = post.html;
  const slug = post.slug;

  // Translate in parallel
  const translations = await Promise.all(
    LANGUAGES.map(async (lang) => {
      const translatedHtml = await translatePost(originalHtml, lang);
      // Create a new Ghost post for the language version
      const newPost = await ghost.posts.add({
        title: `${post.title} (${lang.toUpperCase()})`,
        html: translatedHtml,
        status: 'published',
        // Use a language‑specific slug for SEO
        slug: `${slug}-${lang}`,
        // Add a custom tag so we can filter later
        tags: [{ name: `lang:${lang}` }],
      });
      return newPost;
    })
  );

  console.log(`✅ Translated post ${slug} into ${LANGUAGES.length} languages`);
  res.status(200).json({ success: true, translations });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`🚀 Translation webhook listening on ${PORT}`));

Tip: Deploy this endpoint on the same host as your Ghost installation or on a cheap VPS. Because the library is self‑hosted, you only pay for the LLM calls.


4. Pre‑render translated pages for SEO

Search engines struggle with client‑side translations. SiteLocaleAI ships a CLI that crawls your site, fetches each language version, and writes static HTML files that can be served directly.

# Install the CLI globally (optional)
npm i -g @sitelocaleai/cli

# Run the pre‑render for the eight languages
sitelocaleai prerender \
  --site https://your‑domain.com \
  --langs de,fr,es,ja,zh,pt,it,nl \
  --output ./static-multilingual

The CLI performs the following:
1. Requests each /slug‑lang page.
2. Executes the same LLM translation pipeline on the server side (ensuring the same price localisation).
3. Saves the fully rendered HTML to ./static-multilingual/<lang>/<slug>.html.

You can now configure your web server (NGINX, Apache, or a CDN) to serve these static files to crawlers based on the Accept‑Language header or a URL prefix (/de/, /fr/, …). This gives you full SEO indexing without sacrificing the dynamic nature of Ghost for human visitors.


5. Verify multilingual SEO

Use Google Search Console → URL Inspection to fetch each language URL. You should see the translated <title> and <meta description> tags.

<title>Cómo preparar café perfecto – Ghost Blog</title>
<meta name="description" content="Aprende a preparar café de calidad profesional con estos pasos sencillos.">

Make sure the hreflang tags are present in the <head> of every page. You can inject them via a Ghost helper:

{{!-- in your default.hbs layout --}}
{{#foreach languages}}
  <link rel="alternate" hreflang="{{this}}" href="{{siteUrl}}/{{this}}/{{slug}}" />
{{/foreach}}

The languages array can be populated from the LANGUAGES constant you used earlier.


6. Optional: WordPress‑style plugin for non‑technical editors

If your editorial team prefers a UI, SiteLocaleAI also offers a WordPress‑style plugin that runs entirely in the browser—no Node.js required. You can embed the same translatePost function into a Ghost admin custom integration, exposing a “Translate” button that triggers the same workflow.

For details, see the official docs: https://sitelocaleai.com/docs/plugin‑integration


7. Monitoring costs and performance

Because you provide the LLM API key, you control the cost per token. A typical 800‑word blog post translates to ~1200 tokens per language. With GPT‑4o‑mini at $0.0005 per 1k tokens, the cost for eight languages is roughly $0.0048 per post—well under a cent.

You can log token usage via the localeAI.on('usage', ...) event and feed it into your analytics dashboard.


8. Wrap‑up

With just a few lines of JavaScript, a webhook, and a one‑time CLI run, your Ghost blog now:
- Serves eight fully translated, SEO‑friendly pages.
- Localizes prices with psychological rounding for each market.
- Pre‑renders static HTML that search engines love.
- Keeps data private by using your own LLM API keys.

Ready to boost your international traffic? Try SiteLocaleAI today and watch your Ghost blog go global.


🚀 Try SiteLocaleAI now – head over to https://sitelocaleai.com/docs to get started and see the full documentation.