All jobs

Add a Second Language to an Astro Site

Yes. An agent adds a locale to your Astro site's i18n config, writes the UI strings, creates the localised routes and switcher, builds it and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent starts from the Linear issue, which names the language, then reads how far the site already goes. In a blog like satnaing/astro-paper, astro.config.ts already has an i18n block with locales: ["en"], defaultLocale: "en" and routing.prefixDefaultLocale: false, and astro-paper.config.ts sets site.lang to en, site.dir to ltr and a timezone. There is a src/i18n directory: types.ts declares a UIStrings interface with nav, post, pagination, home, footer, pages and a11y groups, lang/en.ts is the only implementation, and index.ts loads every file under lang/ with import.meta.glob and exports useTranslations(locale), which falls back to English. CHANGELOG.md records this as the i18n layer added in #631. So the shape exists; what is missing is a second locale and the routes that serve it.

The agent then reads where the locale is used, so the new one reaches every place. src/layouts/Layout.astro sets <html lang> from Astro.currentLocale ?? site.lang and builds the RSS link with getRelativeLocaleUrl; src/components/Header.astro calls useTranslations(locale), strips the locale prefix with stripLocale from src/utils/withBase.ts to compute the active nav item, and links home with getRelativeLocaleUrl(locale, ""); src/utils/getPostPaths.ts has getPostUrl(id, filePath, locale) that prefixes post URLs for a non-default locale; src/pages/index.astro reads t.home.featured and t.home.recentPosts. Posts live in src/content/posts as a glob collection whose schema in src/content.config.ts has title, description, pubDatetime, tags and no language field, and src/content/pages/about.md is the About page.

The change is in four parts. First, config: the new code goes into locales in astro.config.ts, and src/i18n/lang/<code>.ts implements UIStrings in full, so astro check fails the build if a key is missed rather than letting English leak through at runtime. Second, routes: with prefixDefaultLocale off, Astro serves a non-default locale from src/pages/<code>/, so the agent adds that directory with pages that import the same layouts and components as the English ones, for the index, posts/[...page], posts/[...slug], tags, archives, search, about and 404, each reading Astro.currentLocale. Third, content: it adds an optional lang field to the posts schema defaulting to site.lang, filters each localised listing to posts in its language, and adds a localised about.md so the pages collection has a translated entry. Fourth, discovery: a language switcher in Header.astro built from getRelativeLocaleUrl and the stripped current path, and the @astrojs/sitemap integration given i18n with the default locale and the locale map, so the sitemap carries alternate links.

Verification follows .github/workflows/ci.yml, which runs pnpm run lint, pnpm run format:check and pnpm run build on Node 24 with pnpm. build here is astro check && astro build && pagefind --site dist && cp -r dist/pagefind public/, so the type check, the static build of every localised page, the search index and its copy into public/ all have to pass. The agent opens dist/ in the sandbox browser through astro preview, loads the new locale's home page, a post and the About page, confirms <html lang> and the switcher, then opens a pull request that links the Linear issue.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Add Spanish (es) as a second language to this Astro site, as the linked issue asks. Read astro.config.ts, astro-paper.config.ts, src/i18n/types.ts, src/i18n/index.ts, src/i18n/lang/en.ts, src/layouts/Layout.astro, src/components/Header.astro, src/utils/getPostPaths.ts, src/content.config.ts and the pages under src/pages first. Add es to i18n.locales, write src/i18n/lang/es.ts implementing every key of UIStrings, add src/pages/es/ with localised versions of the index, posts, tags, archives, search, about and 404 pages that reuse the existing layouts and components, add an optional lang field to the posts schema that defaults to the site language and filter each localised listing by it, add a Spanish about page to the pages collection, add a language switcher to Header.astro using getRelativeLocaleUrl, and pass the locale map to the sitemap integration. Run pnpm run lint, pnpm run format:check and pnpm run build, which includes astro check and pagefind, then open the preview in the browser and load the Spanish home, a post and the about page. Open a pull request that links the issue and lists every file added or changed.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on its own branch that links the Linear issue. It adds src/i18n/lang/es.ts, the src/pages/es/ directory and a Spanish About page, and changes astro.config.ts, src/content.config.ts, src/components/Header.astro and whichever listing pages gained the language filter. The description lists each file, shows the pnpm run build output with astro check reporting no errors, and includes screenshots from the sandbox browser of the Spanish home page, a post and the About page with the switcher visible.

The session read names the repository and the pull request, and the screenshots are artifacts with download links that last an hour per read. The transcript at GET /o/{org}/sessions/{id}/messages keeps the lint, format and build runs and every page the browser loaded, so a reviewer who does not read Spanish can still see that every route renders and that <html lang="es"> is set.

Before merging, a reviewer checks the translated strings in es.ts for tone and terminology, since the agent writes them and a native speaker should read them, checks that the English site is unchanged at its unprefixed URLs, and confirms the sitemap now lists both locales. A post that should appear in both languages needs a translated copy with lang: es; the pull request says which posts are English-only after the change.

Run it on every Linear issue labelled i18n

Add an automation to the Project with this prompt and a linear trigger on linear.issue.labeled with i18n in its labels. Labelling an issue that names the language starts one run in the Project's repository; the trigger adds the issue after the prompt, so the agent reads the language code from it and replaces nothing in the prompt text. A change made by a Linear app integration never fires the trigger, and an automation starts at most max_runs_per_hour runs in an hour.

From your own server, the same run starts with this request and the issue's URL added to resources as https://linear.app/<workspace>/issue/<ID>, which puts the issue in the prompt through your Linear connection. Give the key a webhook URL and Rush POSTs run.completed with data.session_url when the turn ends, so your handler can post the pull request link back on the issue.

The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.

Why run it on Rush?

Adding a locale to an Astro site touches config, strings, a parallel page tree and content, and the only honest check is the full build with astro check and a browser on the output. The agent runs that loop in its own sandbox with Node 24 and pnpm as CI uses them, and loads every new route in a real browser before the pull request exists, which is more than most laptop attempts do before pushing.

What it did is kept with the result. The transcript holds each build, including any astro check error that catches a missing UIStrings key, and the screenshots are on the session. Several issues, one per language, run at once in separate sandboxes and arrive as separate pull requests.

Frequently asked questions

Does the English site change?

No. prefixDefaultLocale stays off, so English keeps its unprefixed URLs and the new locale lives under its own prefix. The existing src/pages files are untouched apart from the Header switcher and the listing filter, which defaults to the site language and so shows what it showed before.

Who writes the translations?

The agent writes src/i18n/lang/es.ts and the Spanish About page, and the pull request marks them as needing a native speaker's review. astro check guarantees the file is complete against UIStrings; it cannot judge the wording, which is why the description points a reviewer at that file first.

Do existing posts appear in the new locale?

Only posts with lang set to the new code appear in its listings. The schema field defaults to the site language so every existing post stays English, and the pull request lists the posts that have no Spanish copy. Translating a post is a copy with lang: es, which can be a follow-up on the same session.

What about search and the RSS feed?

Pagefind indexes the built dist/, so the Spanish pages are searchable once pnpm run build has run; the agent keeps the search page per locale. The RSS link in Layout.astro already goes through getRelativeLocaleUrl, and a localised rss.xml.ts under src/pages/es/ is added in the same pull request if the issue asks for a per-language feed.