SayKit
ReferenceAPI

@saykit/carbon

API reference for the Carbon integration

import { createWithSay, SayPlugin } from '@saykit/carbon';

SayPlugin

A Carbon Plugin that registers a shared Catalogue globally and applies the interaction- and guild-level extensions.

import { Client } from '@buape/carbon';
import { SayPlugin } from '@saykit/carbon';

const client = new Client({/* ... */}, {/* ... */}, [new SayPlugin(catalogue)]);

Constructor

Prop

Type

SayPlugin does three things on construction:

  1. Stores the Catalogue in a global slot keyed by an internal symbol.
  2. Applies the BaseInteraction.prototype.say extension.
  3. Applies the Guild.prototype.say extension.

The plugin has no other lifecycle methods, once you've constructed it and passed it to the Client, you can forget about it.

createWithSay(catalogue)

Binds a withSay to a catalogue, normally once beside the catalogue itself, so commands do not have to take the catalogue themselves.

src/i18n.ts
export const withSay = createWithSay(catalogue);

withSay(BaseClass)

A higher-order class that decorates a Carbon class with SayKit support. The exact behaviour depends on what you pass in.

For BaseCommand subclasses

When the base class extends BaseCommand (Command, CommandWithSubcommands, …), the resulting constructor accepts a mapping function:

class PingCommand extends withSay(Command) {
  constructor() {
    super((say) => ({
      name: say`ping`,
      description: say`Ping the bot!`,
    }));
  }
}

The mapping function runs once per locale, receiving that locale's view. SayKit then combines those results into Carbon's per-locale command metadata so Discord can render the command in each user's locale.

Localisable keys:

  • name
  • description
  • label
  • title
  • placeholder
  • content
  • options (with their own name / description / placeholder)
  • components
  • subcommands
  • subcommandGroups

For BaseComponent and Modal subclasses

For components (Button, StringSelect, …) and modals, withSay produces a constructor that takes the translated props directly, no per-locale mapping, since Discord doesn't localise component metadata per-user the way it does commands:

class RollAgainButton extends withSay(Button) {
  customId = 'roll-again';
  constructor(say: View) {
    super({ label: say`Roll Again` });
  }
}

Caching

A bound withSay() caches its output class per base class, calling withSay(Command) twice returns the same generated class, so instanceof and identity checks still work.

Extensions

When SayPlugin is registered, these extensions are applied to Carbon's prototypes:

BaseInteraction.prototype.say

interface BaseInteraction<T> {
  get say(): View;
}

Returns the view for the interaction's locale. Views are memoised on the catalogue, so repeated access costs nothing and no cloning is involved.

Guild.prototype.say

interface Guild {
  get say(): View;
}

Returns the view for the guild's preferred_locale, memoised on the catalogue the same way.

Type imports

The package also re-exports type augmentations for completeness:

import type {} from '@saykit/carbon'; // augments @buape/carbon types

You don't need this for normal use, it's automatic when you import any value from the package.

Next

On this page