Skip to main content
A bot that speaks several languages does not put an if on the language in every file: it declares its texts once per language, and every function receives t, which picks the right one. This page gives your bot two languages, and makes /ping answer with t('pong').

One declaration per language

A language is declared with language(): locale says which one, texts holds the texts of the bot by key. Create one file per language in src/messages/:
src/messages/en-US.ts
src/messages/fr.ts
  • locale is the language, as Discord writes it: 'en-US', 'en-GB', 'fr', 'de', 'es-ES', 'pt-BR', 'ja'… A word Discord does not know is underlined, and refused when the file loads, with the list.
  • default: true marks the language used when the one asked for is not there. Exactly one file says so: with chapterjs dev running, two files saying so are both underlined in the editor.
  • A placeholder is a name between braces: {name}, {count}. It takes a text or a number, and a number is written the way the language writes numbers (1,234 in English, 1 234 in French).
  • A plural has one form per quantity (one, other, and zero, two, few, many for the languages that need them). count picks the form by the rules of the language.
The default language has every text, and the other languages may have only some of them: what is missing comes from the default. A text another language has but the default lacks, or a {name} that became {nom}, is reported on that file, and that language is left out until it matches.

t: a text in the right language

Every function of your project (commands, events, buttons, forms, tasks) receives t. Change the welcome of the events chapter to use it:
src/events/hello.ts
Type t(' in your editor: it offers pong, welcome, next, members, the keys of the default language. Pick welcome, and the second argument asks for name and count: the placeholders of the text, read from the file. Forget count, and the line is underlined. The types come from the language file itself, on every keystroke, with nothing generated from its content. A plural takes its count the same way:
src/commands/members.ts

Which language t speaks

Here is the subtle part, and the reason t exists rather than an if (interaction.locale === 'fr'). A message is sent once, and everyone in the channel reads the same text: Discord does not translate it per reader. So t speaks the language of those who will read what you send, not of the person who used the command: Discord only knows the language of a Community server (a setting of the server). Elsewhere it says en-US whatever the members speak, so a public message there uses your default language. A bot in one language puts that language as the default and never thinks about it again; a bot in several follows the big servers, which set theirs. t.locale says which language was taken, and t.in('fr', 'welcome', { ... }) gives a text in another language, for a message to someone else. When the exact language is not in your files, t takes the closest one (en-GB reads the en-US texts), and the default otherwise.
t is given to your function before it runs, for the audience the file declares. A command that is not ephemeral but answers with ephemeral: true at the call keeps the language of the server in that answer. When a command’s answers are for the person, put ephemeral: true on the command: t then speaks their language, and it is the default of its answers too.

The texts of buttons, menus and forms

A label is written in the file of the component, where there is no t yet: the message is not sent. To translate it, make it a function of t and the data; it runs when the message is sent, for who will read it:
src/components/next-page.ts
This works for the label of a button, the placeholder of a menu and its options, the title of a form and the texts of its fields.

What the bot answers by itself

When the bot can’t run what was asked (a command used in the wrong place, a button of an old message, a run that threw), it answers by itself, in English. These phrases are translated in framework, with the words for a command, a button, a menu and a form, which {what} stands for:
src/messages/fr.ts
A phrase is answered in the language of the person; one a file does not translate stays in English. Every phrase, with its English text, is in the Reference.

The descriptions of commands

What Discord shows of a command before it is used (its name, its description, its options) is registered, not sent by the bot, so it can’t go through t. It is the one thing translated by data. A command can leave description out of its file and have it in the language files instead, by the name of the command:
src/messages/en-US.ts
The default language gives the description everyone sees; the other languages give their translations (name too, when the command should have another name in that language). A command has its texts in one place: with description in its file, a language file that mentions it is an error; without it, the file must not have locales. Move the description of /ping there too, and remove it from its file: the tickets, music, community and moderation templates do that for every command.

Points of attention

  • Keys are typed from the default file. Add a text there first, then in the others; the editor and the loader both check it.
  • Braces written as is are doubled: '{{name}}' shows {name}.
  • The language of a server is en-US unless it is a Community server. Do not be surprised when a public message in a French server comes out in the default language.
  • Without a language, there is no t: it does not exist, in the types nor at runtime. Skip the language files if your bot speaks one language and you prefer plain texts.

Recap

  • language({ locale, texts }) holds the texts of one language by key; one of them is default: true.
  • t('key', { placeholders }) gives the text in the language of who will read it; the editor checks keys and placeholders.
  • A label of a component is a function of t; the phrases of the bot go in framework; the descriptions of commands in commands.

Organising a growing project

Share code, store data, and keep files short.

Guide: Translated messages

The same, in short.