Write messages in code. Ship translations with almost no runtime baggage.
SayKit is a framework-agnostic i18n toolkit built around compile-time extraction, typed configuration, and small runtime primitives. It keeps authoring ergonomic for developers and translation flows clean for everyone else.
import { Say } from '@saykit/react'; export function Inbox({ count }: { count: number }) { return ( <section> <h1><Say>Inbox</Say></h1> <p> <Say.Plural _={count} zero="You have no unread messages" one="You have 1 unread message" other="You have # unread messages" /> </p> </section> ); }
Why SayKit
- Compile-time extraction from JS, TS, JSX, and TSX
- ICU MessageFormat for plurals, ordinals, and select
- Framework-agnostic core, with adapters for React and Carbon
- Translation files are plain modules, importable directly
- Typed config, fast CLI, watch mode for instant feedback
Terminal
$ saykit extract
✓ 12 messages extracted
→ src/locales/en.po, fr.po
End-to-end workflow
A translation pipeline that stays close to your codebase
From authoring to deployment, without pushing app teams onto a heavyweight platform or a third-party service.
Define
Author messages inline with tagged templates or React components.
Extract
Scan source files and collect ICU-ready messages into translation files.
Translate
Hand translators structured files with comments, context, and stable identifiers.
Deploy
Import translation files directly. No compile step, no extra build-time transform, no shipped extractor.
Ready to try it?
Start with the docs, then adapt the example closest to your stack.
The repo includes working examples for Next.js, TanStack Start, and Carbon. The core runtime stays useful even outside framework-specific adapters.