Carbon
Use SayKit with Carbon to localise commands, components, and replies
SayKit includes a Carbon integration via @saykit/carbon.
The Carbon integration does three things:
- registers a shared
Sayinstance with your Carbon app - helps command and component classes expose translated metadata
- gives interactions and guilds a locale-aware
sayhelper
How It Works
@saykit/carbon is built around two pieces:
SayPlugin, which installs your sharedSayinstance into CarbonwithSay(), which helps you attach translated properties to Carbon classes
Once the plugin is registered:
- interactions get
interaction.locale-awareinteraction.say - guilds get
guild.preferredLocale-awareguild.say
Register the Plugin
Create your Say instance once, then register SayPlugin when you create the Carbon client.
import { Client } from '@buape/carbon'
import { SayPlugin } from '@saykit/carbon'
import say from './i18n'
const client = new Client(
{ ... },
{ ... },
[new SayPlugin(say)],
)If your app registers commands, components, or modals separately, keep using the same shared say instance for all of them.
Localising Commands
For commands, withSay() accepts your shared Say instance and a mapping function. That mapping function runs for each locale and is used to build Carbon's localisation data.
import { Command, type CommandInteraction } from '@buape/carbon';
import { withSay } from '@saykit/carbon';
import type { Say } from 'saykit';
export class PingCommand extends withSay(Command) {
constructor(say: Say) {
super(say, (say) => ({
name: say`ping`,
description: say`Ping the bot!`,
}));
}
async run(interaction: CommandInteraction) {
await interaction.reply({
content: interaction.say`Pong!`,
});
}
}SayKit looks for the say identifier when transforming messages. To have a string extracted and
compiled, write it with say, such as say`ping` or
interaction.say`Pong!`.
Once your commands are defined, register them with Carbon passing your shared say instance as needed.
// ...
const client = new Client(
{ ... },
{ commands: [new PingCommand(say), ...] },
[new SayPlugin(say)],
)
// ...Carbon still owns registration and execution. SayKit just provides the translated strings and locale-aware formatting.
Mental Model
A simple way to think about the integration is:
- define translated metadata with
say - reply to the current Discord context with
interaction.sayorguild.say - pass one shared
Sayinstance through the parts of your app that Carbon registers