SayKit
Reference

CLI

The saykit command-line interface, every command and flag

The saykit CLI extracts messages from your source files. It's installed with @saykit/config and exposes two commands: extract and clean.

saykit --help
saykit help [command]

saykit extract

Walk every bucket in saykit.config.ts, extract messages from the files matched by include, and write the source locale catalogue. Other locales are never edited; a locale with no file yet is created empty so a TMS can register it.

saykit extract
saykit extract --watch
saykit extract --verbose
saykit extract --quiet

Options

Prop

Type

What it does

For each bucket:

  1. Globs include (and excludes exclude).
  2. Asks each transformer to parse each file and return a list of messages.
  3. Merges entries with the same text + context, unioning their references.
  4. Hashes ids for messages without a custom id.
  5. Writes the source locale catalogue via the bucket's formatter, and creates an empty placeholder for any locale that has no file yet. Existing non-source files are left untouched.
  6. Writes a {locale}.d.{extension}.ts declaration next to each catalogue.

Exit codes

CodeMeaning
0Success.
1Failure during config load, extraction, or write.

The CLI throws on unrecoverable errors (missing config, invalid config, formatter parse failure). Use --verbose to see the full stack.

saykit clean

Remove dead entries from every non-source locale file. clean only ever subtracts, it never writes new keys into a locale, so running it can only shrink your locale files.

saykit clean
saykit clean --verbose
saykit clean --quiet

For each bucket, and each locale after the source, clean:

  • drops keys that no longer exist in the source catalogue (orphans),
  • drops entries with an empty translation, which resolve through fallback anyway,
  • leaves every remaining entry byte-for-byte as it is on disk.

Keys that exist in the source but not in a locale file are deliberately left alone, adding them is your translation management system's job.

Options

Prop

Type

clean is optional and never required for correctness. If your TMS already prunes orphaned keys, you may never need it, extraction plus load-time fallback is enough on its own.

Configuration discovery

The CLI searches for saykit.config.{ts,mts,cts,js,mjs,cjs} starting from the current working directory and walking up the tree. The first match wins.

TypeScript configs are read by the runtime directly, which needs Node 22.18+ or a runtime that loads TypeScript itself (Bun, Deno, tsx). Nothing is written to disk, and a config can import from alongside it as normal — using the real extension, ./formatter.ts rather than ./formatter.js.

Running from package.json

A typical setup wires the CLI into npm scripts so you don't have to remember the binary path:

package.json
{
  "scripts": {
    "extract": "saykit extract",
    "extract:watch": "saykit extract --watch"
  }
}
pnpm extract
pnpm extract:watch

CI usage

The most common CI check is: extraction has been run, and translations are up-to-date with code:

- run: pnpm install
- run: pnpm saykit extract
- run: git diff --exit-code -- 'src/locales/*'

This fails the build if anyone forgot to run extract after changing or adding messages.

Future commands

The CLI is intentionally small today. New commands (compile, lint, stats) may land before 1.0. Anything published outside extract and clean will go through the same saykit <command> --help discovery.

On this page