npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@transglot/astro

v0.1.0

Published

SSR-aware Astro integration over @transglot/runtime: a per-request getLocaleBundle() (+ middleware) that returns a serializable snapshot for .astro server rendering, and a hydration-safe <TransglotIsland> (a React island wrapper of @transglot/react-i18n)

Downloads

136

Readme

@transglot/astro

SSR-aware Astro integration over @transglot/runtime. Fetch a locale's published bundle per request on the server, render translated .astro HTML from it, and hand the same snapshot to a React island so hydration has nothing to refetch and nothing to flash.

  • Server entry @transglot/astro has no React import, so it is safe in .astro frontmatter, a route handler, middleware, and astro.config.
  • Client entry @transglot/astro/island is the React island wrapper: TransglotIsland, plus useTranslations and <T> re-exported from @transglot/react-i18n.
  • Middleware entry @transglot/astro/middleware is the auto-wired onRequest the integration registers for you. You rarely import it directly.

astro is an optional peer dependency: the Astro types this package needs are declared structurally, so it type-checks and builds without Astro installed.

Install

npm install @transglot/astro @transglot/runtime

The island entry additionally needs React and Astro's React renderer:

npm install @astrojs/react react react-dom

Quickstart: the integration

Add transglot() to astro.config. It exposes your CDN coordinates to the rest of the build and registers a per-request middleware that seeds each request's bundle onto Astro.locals.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import { transglot } from '@transglot/astro';

export default defineConfig({
  integrations: [
    react(),
    transglot({
      baseUrl: 'https://app.example.com',
      project: 42,
      cdnKey: process.env.TRANSGLOT_CDN_KEY,
      defaultLocale: 'en',
    }),
  ],
});

| option | required | meaning | | --- | --- | --- | | baseUrl | yes | origin of the app / CDN, e.g. https://app.example.com | | project | yes | numeric project id (the CDN URL segment) | | cdnKey | yes | read-only taicdn_… key | | defaultLocale | no | locale the middleware loads when nothing else resolves one | | version | no | pin every request to an immutable …/v{n} instead of "latest" | | authIn | no | present the key in a header (default) or the query | | localsKey | no | the Astro.locals key for the snapshot (default transglot) | | middleware | no | set false to wire createTranslationMiddleware yourself |

Render translated .astro HTML

Read the per-request snapshot with readSnapshot, then build a synchronously seeded client from it. createSeededClient parses the bundle at construction, so client.t(...) returns translations on the very first (and only) server render.

---
import { readSnapshot, createSeededClient } from '@transglot/astro';

const snapshot = readSnapshot(Astro.locals);
const client = snapshot ? createSeededClient({ snapshots: snapshot }) : null;
---
<h1>{client?.t('home.title') ?? 'home.title'}</h1>
<p>{client?.t('cart.items', { count: 3 })}</p>

Not using the middleware? Load a bundle explicitly instead. getLocaleBundle returns the same serializable TranslateSnapshot:

---
import { getLocaleBundle } from '@transglot/astro';

const snapshot = await getLocaleBundle({
  baseUrl: 'https://app.example.com',
  project: 42,
  cdnKey: import.meta.env.TRANSGLOT_CDN_KEY,
  locale: Astro.currentLocale ?? 'en',
});
---

Hydrate an island from the same snapshot

Pass the snapshot as a prop to <TransglotIsland> and mark it with a client:* directive. Its first render, server-side and on hydration, is already translated, because it seeds from the snapshot the surrounding HTML rendered from.

---
import { readSnapshot } from '@transglot/astro';
import { TransglotIsland } from '@transglot/astro/island';
import Menu from '../components/Menu'; // a React island calling useTranslations()

const snapshot = readSnapshot(Astro.locals)!;
---
<TransglotIsland client:load snapshot={snapshot}>
  <Menu />
</TransglotIsland>
// src/components/Menu.tsx
import { useTranslations } from '@transglot/astro/island';

export default function Menu() {
  const { t, locale, setLocale } = useTranslations();

  return (
    <nav>
      <span>{t('nav.home')}</span>
      <button onClick={() => setLocale(locale === 'en' ? 'fr' : 'en')}>{locale}</button>
    </nav>
  );
}

Seed several locales at once with snapshots={[en, fr]} and switching between them is instant, with no network at all. To let a visitor switch to a locale you did not seed, give the island a loader (the CDN coordinates) and it fetches that locale on the client:

<TransglotIsland
  client:load
  snapshot={snapshot}
  loader={{ baseUrl, project: 42, cdnKey }}
/>

Without a loader the client is seed-only: switching to an unseeded locale is a no-op that keeps the current locale (and warns in dev) rather than rendering raw keys. Note that a client-side loader puts the read-only cdnKey in the browser; omit it if you would rather keep the key server-side and reload the page instead.

The client is built once per mount (a useState initializer). To swap the snapshot on navigation, remount the island with a React key.

API

Server (@transglot/astro)

  • transglot(options): the Astro integration. On astro:config:setup it publishes the resolved config through a virtual module and, unless middleware: false, registers the per-request middleware.
  • getLocaleBundle(options)Promise<TranslateSnapshot>: fetches and validates one locale's published bundle. It drives the runtime client for the entire wire contract (URL shape, key auth, Retry-After backoff, RFC 7807 errors, format detection and parse validation), so failures arrive as the runtime's typed RuntimeError.
  • createTranslationMiddleware(options): the middleware factory, if you want to wire it as your own src/middleware.ts. Takes resolveLocale(context) to pick the locale from a URL, cookie or header; falls back to Astro's context.currentLocale, then defaultLocale. Resolves nothing, does nothing.
  • readSnapshot(locals, localsKey?) → the snapshot the middleware stored, or undefined when it did not run or resolved no locale.
  • createSeededClient({ snapshots, locale?, loadSnapshot?, dev? }) → a RuntimeClient whose cache is filled synchronously from the snapshots. It has no background refresh and no persistent storage: hydrate reports whether a locale is already seeded, refresh re-runs loadLocale for the active locale, and stop has no timer to detach.
  • DEFAULT_LOCALS_KEY, VIRTUAL_CONFIG_ID, and the runtime re-exports createClient, RuntimeError, parseBundle, interpolate.

Client (@transglot/astro/island)

  • <TransglotIsland snapshot|snapshots locale? loader?>: seeds a client from the server snapshot and provides it to @transglot/react-i18n.
  • useTranslations(), <T keypath params>, TransglotProvider: re-exported from @transglot/react-i18n so an island file needs only one import.

The snapshot carries the raw bundle body plus its delivery format, version and ETag. It deliberately does not carry the cdnKey: seeding never refetches the seeded locale, so the key need not cross to the browser.

Missing keys return the key (never throw); network and HTTP errors surface as the runtime's typed RuntimeError. The full runtime client contract is documented in @transglot/runtime.