language(), one declaration per language, in the files you like (the templates keep them in a messages.ts next to what they translate). Every function of your project then receives t, which gives a text in the right language.
src/messages/en-US.ts
src/messages/fr.ts
src/commands/hello.ts
t knows the keys of your files and the {placeholders} of each text: a key that does not exist, or a missing {count}, is underlined in your editor.
Everything a language file declares, and the type of
t, is in the
reference.The declarations
localeis the language, as theLocalevalues write it:'en-US','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: truemarks the language used when the one of a person or of a server is not there. Exactly one file says so; with a single file, it goes without saying. When two files say so, both are underlined in the editor, on theirdefault: true, oncechapterjs devorchapterjs buildran the files: hover it to read which other file says it.- A language that does not have a text answers with the default language’s. A text the default language does not have either comes from the language that has it (
en-USfirst, then the others in order): a module written in German and Spanish works in a bot whose default language is French, andtoffers its keys all the same. A{name}that became{nom}is reported on that declaration, with the key and the language the text comes from: the declaration is left out until it matches again, and the others keep working. - A placeholder is a name between braces, made of letters, digits and
_:{name},{count}. It takes a text or a number. A number is written the way the language writes numbers (1,234in English,1 234in French,1.234in German): give it as a text to keep it as is, like a year or an id. - A brace written as is is doubled:
'{{name}}'shows{name}. - A language may be declared in several places. The texts of a feature can live next to it, in a second
language({ locale: 'en-US', ... }): itstexts,commandsandframeworkare added to the others of that language,toffers them all, anddefault: truein any of them decides for the whole language. The same key in two places is reported on the later one, with the first:
src/moderation/messages.ts
Plurals
A text that depends on a number has one form per quantity, likemembers above. Its count picks the form, following the rules of the language: English says “1 member” and “0 members”, French says “0 membre”, Russian and Arabic have more forms.
src/commands/count.ts
zero, one, two, few, many and other. other is always there; write the forms your language uses, the others fall back to other. zero, when written, is used for 0 in every language ({ zero: 'no apple', one: '{count} apple', other: '{count} apples' }). count is a number, and {count} in a form is that number; a form may have other placeholders, given the same way. A plural in one language can be a plain text in another ('{count} membre(s)'), as long as the placeholders match.
Which language t speaks
A message is sent once, and everyone in the channel reads the same text: Discord does not translate it per reader. Sot 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. Elsewhere it says
en-US whatever the server speaks, so a public message there uses your default language: a French bot whose fr declaration says default: true speaks French in every server, and a bot in many languages follows the big servers that set theirs.
When that language is not in the file, t takes one of the same language in another region (en-GB reads the 'en-US' texts, es-419 the 'es-ES' ones, zh-TW the 'zh-CN' ones: add the file when that is not what you want), and the default one otherwise. t.locale says which one was taken, and t.in(locale, key, params) gives a text in another language, to write to someone else:
src/events/notify.ts
interaction.locale, guild.preferredLocale) is taken as is: t picks the closest one declared, as above. To write several texts in that language, t.in(locale) alone is the same t for it: const say = t.in(interaction.locale); say('pong').
The texts of a button, a menu, a form
A label, a placeholder or a title is written in the file of the component. To translate it, make it a function: it receivest, in the language of who will read the message, and the data the component carries. It runs when the message is sent.
src/components/next.ts
label of a button, the placeholder of a menu and the label and description of its options, the title of a form and the label, description, placeholder and content of its fields. A text written as is stays as it is. What the function returns is checked against the limits of Discord when the message is sent.
What the bot answers by itself
When the bot can’t run what was asked, it answers by itself: a command used where it does not work, a button of an old message, arun that threw. These phrases are in English. Translate the ones you want in framework, with the words for a command, a button, a menu and a form, which {what} stands for:
src/messages/fr.ts
FrameworkTexts. A phrase is answered in the language of the person, from the file that translates it; a phrase a file does not translate stays in English. A phrase without the placeholders of the English one is refused when the file loads.
The names and descriptions of commands
What Discord shows of a command before it is used (its name, its description, its options and their choices) is registered once, in every language: Discord picks the language. Give them in thecommands of each language file, by the name of the command:
src/messages/fr.ts
description in their file: a command that does not exist, or one its file describes, is underlined (once chapterjs dev or chapterjs build ran the command files). An option or a choice that does not exist is reported when the file loads.
A command has its texts in one place. Without description in its file, the language files describe it: the default language gives the description Discord shows to everyone (and the description of each option that has none in the file), the other languages their translations. With description in its file, the file owns its texts, with its own locales for translations, and a language file that mentions it is an error:
In development
Inchapterjs dev, saving a language file changes its texts at once, and the commands are registered again when what Discord shows of them changed. A file that does not load, or no longer matches the default language, is reported with what is wrong, and that language keeps its last texts that worked. When the first language is added, the last one removed, or another file becomes the default one, the types of t follow (and two files saying default: true are both underlined):
language() reference
Every field of a language file, and the type of t.
Command translations
The name and the description of a command in each language.