Universally Documentation

Step-by-step guides, multilingual SEO tips, and best practices to help you translate and scale your WordPress website.

Astro integration options

This page lists every option you can pass to universally({...}) in astro.config.mjs, the environment variable that sets each one, and the format of the push endpoint.

Options

Option Environment variable Default Notes
mode UNIVERSALLY_MODE 'sdk' 'sdk' or 'proxy', case insensitive. Any other value throws when the config runs. See Astro with language subdomains.
apiKey UNIVERSALLY_API_KEY none, required The 64 character key from the project's Setup page. An sk_ key also authenticates. A pk_ key fails the languages call and the build.
apiUrl UNIVERSALLY_API_URL https://api.universally.com Where languages and catalogs are read from.
translatorUrl UNIVERSALLY_TRANSLATOR_URL https://translator.universally.com Where new strings are reported.
revalidateSeconds UNIVERSALLY_REVALIDATE_SECONDS 60 Smallest gap between two checks for dashboard edits, per language. 0 or a negative number turns polling off. A non numeric environment value is ignored and the default applies.
hreflang.format none 'variant' 'variant', 'lang', or a function. Any other string behaves as 'variant'.
hreflang.xDefault none true Adds an x-default link to the source page. Only false disables it.
switcher none true Renders the language switcher script in <UniversallyHead />. Only false disables it.
scriptsUrl UNIVERSALLY_SCRIPTS_URL https://scripts.universally.com Origin the switcher script loads from.

A missing key throws: "set UNIVERSALLY_API_KEY in .env or pass apiKey in the integration options. The key is the 64 character site key from your project Setup page."

Where values come from

Each value is resolved in this order, first match wins:

  1. The option in universally({...})
  2. The process environment
  3. .env.local in the Astro project root, next to astro.config.mjs
  4. .env in the same folder

An empty value counts as missing. .env.production and other mode files are not read. Reading the files needs Node 20.12 or newer; on older Node they are skipped without an error. The key is resolved at build time and compiled into the server bundle, never the client bundle, so it has to be present where the build runs. See Deploy an Astro site with Universally.

hreflang.format

For a source of English (US) with French (France) under /fr/ and Spanish (Mexico) under /es/, the page /fr/about renders:

<!-- format: 'variant', the default -->
<link rel="alternate" hreflang="en-us" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es-419" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />

<!-- format: 'lang' -->
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />

The source comes first, then targets in the order Universally returns them, then x-default. Query strings and hashes are dropped from the URLs. See hreflang tags.

A function receives one language row, with fields such as lang, variant, region, and url, and returns the value. It runs only when the config runs:

universally({
  hreflang: {
    format: (language) => language.variant.toUpperCase()
  }
})

Examples

// Check for dashboard edits every five minutes
universally({ revalidateSeconds: 300 })

// Place the switcher script yourself
universally({ switcher: false })

// Serve through language subdomains
universally({ mode: 'proxy' })

Types and exports

import universally, {
  REVALIDATE_PATH,            // '/_universally/revalidate'
  DEFAULT_REVALIDATE_SECONDS, // 60
  DEFAULT_SCRIPTS_URL         // 'https://scripts.universally.com'
} from '@universally-sdk/astro';
import type {
  HreflangOptions,
  ResolvedHreflang,
  UniversallyMode,
  UniversallyOptions,
  ResolvedUniversallyOptions
} from '@universally-sdk/astro';
import { UniversallyHead } from '@universally-sdk/astro/components';

Astro.locals is typed by /// <reference types="@universally-sdk/astro/env" /> in src/env.d.ts:

declare namespace App {
  interface Locals {
    lang?: string;
    t: (source: string) => string;
    href: (path: string) => string;
  }
}

Push endpoint

POST /_universally/revalidate
X-Universally-Signature: v1=<hex>
Content-Type: application/json

{ "siteId": "site_123", "cacheEpoch": 12, "langs": ["fr"], "ts": 1750000000 }
Field Meaning
X-Universally-Signature HMAC-SHA256 of the raw body, keyed with the private half of the site key (its last 32 hex characters), as lowercase hex
langs Variant codes or URL prefixes. null or [] means every language.
ts Unix seconds, within 5 minutes of the receiver's clock

A trailing slash on the path is accepted.

Status When
200 { "ok": true, "reloaded": [...] }, listing only languages whose cache epoch changed
401 { "ok": false }: malformed body, missing or wrong signature, expired ts, or a key with no private half
404 Proxy mode, where the endpoint does not exist
405 Any method other than POST
413 Body over 16 KiB, checked before the signature
Was this helpful?