SayKit
ReferenceAPI

@saykit/react

API reference for the React integration

@saykit/react is split across three entry points:

import { Say } from '@saykit/react'; // shared (server + client)
import { SayProvider, useSay } from '@saykit/react/client'; // client only
import { createWithSay, getSay, setSay } from '@saykit/react/server'; // server only

The default @saykit/react import uses the react-server export condition to swap between the server and client implementations of <Say> automatically.

@saykit/react

<Say>

The primary translation component. Author content as children.

<Say>Hello, {name}!</Say>

Props:

Prop

Type

<Say> is a macro, the build-tool plugin reads the JSX, extracts the message, and rewrites the element to render say.call(...).

<Say.Plural>

<Say.Plural _={count} one="1 item" other={<>{count} items</>} />

Props:

Prop

Type

<Say.Ordinal>

<Say.Ordinal _={position} _1="1st" _2="2nd" _3="3rd" other={<>{position}th</>} />

Same props as <Say.Plural>, CLDR plural categories plus exact-number branches.

<Say.Select>

<Say.Select _={gender} male="He liked it" female="She liked it" other="They liked it" />

Props:

Prop

Type

<Say.Number>

<Say>You have <Say.Number _={items.length} /> items</Say>
<Say.Number _={total} style="::currency/EUR" />

Formats a number the way the active locale writes one. Usually a fragment inside a <Say>; on its own it is formatted but not extracted, since there is nothing to translate.

Prop

Type

<Say.Date>

<Say>Published <Say.Date _={post.publishedAt} style="medium" /></Say>
<Say.Date _={post.publishedAt} style="::yMMMM" />

Formats the date portion of a Date or timestamp.

Prop

Type

<Say.Time>

<Say>
  Doors open at <Say.Time _={opensAt} style="short" />
</Say>

Formats the time portion of a Date or timestamp. Same props as <Say.Date>; skeletons look like ::Hm.

@saykit/react/client

<SayProvider>

Wraps a client tree with the view its descendants resolve against. Required before any client-side use of <Say> or useSay().

It takes either a Store, or a locale and its messages. A store owns a catalogue and can switch locale; it is a live object, so it belongs to an application that holds its catalogue on the client. A locale and its messages are plain data and are what a server can send across the boundary; the provider builds a single-locale store over them, which has nothing to switch to.

<SayProvider store={store}>{children}</SayProvider>

<SayProvider locale={locale} messages={messages}>
  {children}
</SayProvider>

Props:

Prop

Type

Throws if given neither a store nor a locale and its messages. In a server component the props are filled in from the view established for the request, so it is written <SayProvider> with none.

useSay()

Returns the current View from the nearest <SayProvider>. Throws if no provider is in the tree.

Subscribes to the provider's store with useSyncExternalStore, so the component re-renders when the locale changes.

function Title({ name }: { name: string }) {
  const say = useSay();
  return <h1 title={say`Hello, ${name}!`}>{say.locale}</h1>;
}

There is no hook for the store behind it. A store is a module-scope value you already hold, so switching is store.set('fr') on the one you built; a provider given a locale and its messages has no catalogue to switch through anyway.

@saykit/react/server

createWithSay()

Binds a withSay to a catalogue. withSay(Component, getLocale) wraps a server component so its view is negotiated with match(), loaded, and put into React's per-request cache() before the component renders, so a concurrent request rendering another locale reads its own.

Wrap every route segment that renders messages: a framework may render a page before the layout above it, and a parent that has not run yet has established nothing.

export const withSay = createWithSay(catalogue);

export default withSay(Page, (props) => props.params.then((params) => params.locale));

Arguments:

Prop

Type

setSay()

Establishes a View for the request, for a caller that resolved one already. This is what withSay does once it has loaded the view.

setSay(await catalogue.load('fr'));

One view per request: a second view for another locale takes over for everything rendered after it, including the messages <SayProvider> serialises. Development warns when it happens.

getSay()

Returns the View established for the request. Throws if nothing established one. The server counterpart of useSay().

const say = getSay();
say`Hello, ${name}!`;
say.locale;

The view is held in React's per-request cache(), so a concurrent request rendering another locale reads its own.

Next

On this page