Tutorial

Translate & SEO‑Optimize an Astro Site for 10 Locales with SiteLocaleAI

Published October 17, 2026

Translate & SEO‑Optimize an Astro Site for 10 Locales with SiteLocaleAI

Translate & SEO‑Optimize an Astro Site for 10 Locales with SiteLocaleAI

Published on SiteLocaleAI.com • 2026‑10‑17


Introduction

Astro is a fantastic framework for building fast, static‑site generators. However, when you want to reach a global audience you need translations, price localization, and search‑engine‑friendly URLs. SiteLocaleAI’s drop‑in JavaScript library makes this painless: it works with any front‑end framework, runs on your own LLM API keys, and even offers a CLI that pre‑renders translated pages for crawlers.

In this tutorial we’ll take a simple Astro blog and turn it into a 10‑locale, SEO‑optimized site (English, Spanish, French, German, Italian, Portuguese, Dutch, Japanese, Korean, and Chinese). By the end you’ll have:

  1. A self‑hosted translation layer that pulls content from Claude or GPT‑4o‑mini.
  2. Prices automatically rounded to the most persuasive values per currency.
  3. Fully rendered HTML snapshots for each locale, ready for Google, Bing, and Baidu.
  4. A clean, framework‑agnostic setup that can be reused on any future Astro project.

Prerequisites

Requirement Why it matters
Node.js 18+ Astro’s build pipeline runs on Node.
Astro 4.x Latest features and edge‑runtime support.
SiteLocaleAI npm package Provides the translation & price‑localization helpers.
LLM API key (Claude, GPT‑4o‑mini, etc.) SiteLocaleAI is self‑hosted – you supply the key.
Git For version control and deployment.

Make sure you have an LLM API key stored in an environment variable called LLM_API_KEY.


1. Install SiteLocaleAI

Open a terminal in your Astro project root and run:

npm i @sitelocaleai/core

The package is framework‑agnostic, so you can import it directly in any .astro or component file.


2. Define Your Locales

Create a locales folder at the project root. Inside, add a JSON file for each language. Below is an example for English (en.json) and Spanish (es.json).

// locales/en.json
{
  "siteTitle": "My Astro Blog",
  "description": "Thoughts on web development, AI, and more.",
  "priceFormat": "${{amount}}",
  "currency": "USD"
}
// locales/es.json
{
  "siteTitle": "Mi Blog Astro",
  "description": "Ideas sobre desarrollo web, IA y más.",
  "priceFormat": "{{amount}}€",
  "currency": "EUR"
}

Repeat for the remaining eight locales. Keep the keys identical across files – SiteLocaleAI will swap values based on the active locale.


3. Add the Translation Middleware

Create a new file src/lib/siteLocale.ts:

// src/lib/siteLocale.ts
import { createTranslator } from "@sitelocaleai/core";
import type { AstroGlobal } from "astro";

// Load locale JSON at build time (Vite can import JSON directly)
const locales = {
  en: await import("../../locales/en.json"),
  es: await import("../../locales/es.json"),
  // ...add the other 8 locales here
};

export const translator = createTranslator({
  apiKey: process.env.LLM_API_KEY!,
  locales,
  defaultLocale: "en",
  // Optional: custom prompt for tone/style
  prompt: "Translate the given text preserving HTML tags and keep a friendly, professional tone.",
});

/**
 * Astro hook – runs on every request (SSR) or at build time (SSG).
 */
export async function onRequest({ request }: AstroGlobal) {
  const url = new URL(request.url);
  const locale = url.pathname.split("/")[1] || "en"; // /es/… → es
  const data = await translator.loadLocale(locale);
  return { locale, data };
}

In astro.config.mjs register the hook:

// astro.config.mjs
import { defineConfig } from "astro/config";
import { onRequest } from "./src/lib/siteLocale";

export default defineConfig({
  integrations: [],
  server: {
    middleware: [onRequest],
  },
});

Now every page can access locale and data via Astro.locals.


4. Translate Content in Components

Here’s a simple Header.astro that pulls the site title from the locale file:

---
import { site } from "astro";
const { data } = Astro.locals;
---
<header class="p-4 bg-gray-100">
  <h1 class="text-2xl font-bold">{data.siteTitle}</h1>
  <p class="text-sm text-gray-600">{data.description}</p>
</header>

For dynamic blog posts you can ask the LLM to translate the body on‑the‑fly:

// src/lib/translatePost.ts
import { translator } from "./siteLocale";

export async function translatePost(content: string, locale: string) {
  // The library caches translations per locale to avoid repeated API calls.
  return await translator.translate(content, locale);
}

Use it in a page:

---
import { translatePost } from "../lib/translatePost";
import { getCollection } from "astro:";

const post = await getCollection("posts", (p) => p.slug === Astro.params.slug);
const { locale } = Astro.locals;
const translated = await translatePost(post.body, locale);
---
<article class="prose mx-auto">
  <h2>{post.title}</h2>
  {@html translated}
</article>

5. Price Localization & Psychological Rounding

SiteLocaleAI ships a helper that rounds numbers to the most persuasive values per currency (e.g., $9.99 → $9.95, €19.99 → €19.95). Create a tiny utility:

// src/lib/price.ts
import { roundPrice } from "@sitelocaleai/core";

export function formatPrice(amount: number, localeData: any) {
  const rounded = roundPrice(amount, localeData.currency);
  return localeData.priceFormat.replace("{{amount}}", rounded.toFixed(2));
}

Use it in a component:

---
import { formatPrice } from "../lib/price";
const price = 12.99;
const { data } = Astro.locals;
---
<p class="font-medium">{formatPrice(price, data)}</p>

The rounding tables are built‑in for USD, EUR, GBP, JPY, KRW, CNY, etc., and you can extend them via translator.setRoundingRules() if needed.


6. SEO Pre‑Rendering CLI

Search engines prefer fully rendered HTML. SiteLocaleAI’s CLI crawls your routes, renders each locale, and writes static snapshots to dist/.

First, add the CLI as a dev dependency:

npm i -D @sitelocaleai/cli

Then create a config file sitelocale.config.mjs:

export default {
  locales: ["en", "es", "fr", "de", "it", "pt", "nl", "ja", "ko", "zh"],
  outputDir: "dist",
  // Optional: custom meta tags per locale
  meta: {
    title: (locale) => `${locale.toUpperCase()} | My Astro Blog`,
    description: (locale) => `Read ${locale} articles about web dev and AI.`,
  },
};

Add a script to package.json:

"scripts": {
  "build": "astro build",
  "locale:pre": "sitelocale prerender"
}

Run the full pipeline:

npm run build && npm run locale:pre

The CLI will:
1. Spin up a temporary server.
2. Visit each route (/en/, /es/, …).
3. Capture the rendered HTML with translated text and rounded prices.
4. Write the snapshots to dist/en/, dist/es/, etc.

Now Google can index /es/blog/my‑post/ with the actual Spanish content, not a JavaScript‑only fallback.


7. Deploy

Because the site is fully static after pre‑rendering, you can host it on any CDN (Vercel, Netlify, Cloudflare Pages). Just push the dist/ folder.

If you ever add new locales or change translations, re‑run npm run locale:pre and redeploy.


8. Quick Checklist

  • [x ** Install @sitelocaleai/core.
  • [x ** Create locale JSON files for all 10 languages.
  • [x ** Wire up the translator middleware.
  • [x ** Use translatePost for dynamic content.
  • [x ** Apply formatPrice for localized pricing.
  • [x ** Configure sitelocale.config.mjs and run the CLI.
  • [x ** Deploy the static dist/ folder.

9. Next Steps & Resources


🎉 Ready to Go Global?

SiteLocaleAI gives you the power of LLM‑driven translation, price psychology, and SEO‑ready static rendering—all while you keep full control of your API keys and hosting. Try it today on your Astro project and watch your international traffic soar!

Start your free trial →