localize.dev

Docs / Astro

Astro i18n, automated

Localize an Astro site: .astro pages get t() from the URL’s language, every static page gets a copy per language for search engines, and React, Vue or Svelte islands follow along.

How it works

Text and expressions in .astro files become {t('key')}, with t reading the language from the URL (/es/about). Each page gets a copy per language, so every language is a real, indexable page; dynamic routes keep their getStaticPaths and props.

React, Vue and Svelte islands are localized in the same run, each with its own library, following the page’s language.

Before and after

The engine’s real output for this file. In a real run, the hosted model names the keys from context.

src/pages/products/tea.astro · before
---
import Layout from '../../layouts/Layout.astro';
const product = { name: 'Jasmine tea', stock: 12 };
---
<Layout title="Our teas">
  <h1>{product.name}, picked this spring</h1>
  <p>{product.stock > 0 ? 'In stock' : 'Sold out'}</p>
</Layout>
src/pages/products/tea.astro · after
---
import Layout from '../../layouts/Layout.astro';
import { getLangFromUrl, useTranslations } from '../i18n';
const product = { name: 'Jasmine tea', stock: 12 };
const t = useTranslations(getLangFromUrl(Astro.url));
---
<Layout title={t('tea.our_teas')}>
  <h1>{t('tea.picked_this_spring', { name: product.name })}</h1>
  <p>{product.stock > 0 ? t('tea.in_stock') : t('tea.sold_out')}</p>
</Layout>
locales/en.json
{
  "tea": {
    "our_teas": "Our teas",
    "picked_this_spring": "{{name}}, picked this spring",
    "in_stock": "In stock",
    "sold_out": "Sold out"
  }
}

What the setup adds

The first run sets up a generated src/i18n module (islands: their own libraries) if the project doesn’t have it yet:

  • src/i18n with t, getLangFromUrl and localizePath
  • A copy of each page under src/pages/<lang>/, dynamic routes included
  • <html lang dir> set from the URL in layouts

Run it

The CLI needs Node.js 22 or later.

npm install --save-dev localize-dev
npx localize-dev login

# Preview, then write the changes
npx localize-dev run --dry-run -l es,fr,de
npx localize-dev run -l es,fr,de

# Or open a pull request instead
npx localize-dev pr -l es,fr,de

Or add the GitHub Action and get a pull request on every push. Try it on your own code first in the playground.