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:
- A self‑hosted translation layer that pulls content from Claude or GPT‑4o‑mini.
- Prices automatically rounded to the most persuasive values per currency.
- Fully rendered HTML snapshots for each locale, ready for Google, Bing, and Baidu.
- 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
translatormiddleware. - [x ** Use
translatePostfor dynamic content. - [x ** Apply
formatPricefor localized pricing. - [x ** Configure
sitelocale.config.mjsand run the CLI. - [x ** Deploy the static
dist/folder.
9. Next Steps & Resources
- Dive deeper into custom prompts and tone control in the docs: https://sitelocaleai.com/docs.
- Learn how to integrate the CLI with CI/CD pipelines: https://sitelocaleai.com/docs/cli.
- Explore the WordPress plugin if you ever need a non‑JS solution.
🎉 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!