Skip to main content

Formatting

Variables​

A variable is written between two %. Its value is passed in the second argument of t():

locales/en-US.json
{
"greeting": "Hello, %name%!"
}
ctx.t('greeting', {name: 'Alice'}); // "Hello, Alice!"

To write a literal %, escape it: \%. In JSON, the backslash itself has to be doubled:

{
"discount": "%value%\\% off"
}
Required values

A string with variables requires a values object, and every variable it uses must be in it. Otherwise, t() throws an error.

Expressions​

An expression is written %variable:expression(parameters)%. Multiple parameters are separated by |.

if: conditional text​

%variable:if(text)% outputs the text when the variable is true, nothing otherwise. The variable must be a boolean.

{
"weather": "It is %temp% degrees outside. %isHot:if(Wow, that's hot...)%"
}
ctx.t('weather', {temp: 30, isHot: true});
// "It is 30 degrees outside. Wow, that's hot..."

either: one or the other​

%variable:either(if true|if false)% picks one of two texts based on a boolean. It takes exactly two parameters.

{
"status": "The raid mode is %enabled:either(on|off)%."
}

plural: plurals​

%variable:plural(singular|plural|zero)% picks based on a number:

  • 1 gives the singular;
  • 0 gives the third parameter when provided, the plural otherwise;
  • any other value gives the plural.
{
"inbox": "You have %count% %count:plural(message|messages|no messages)%"
}
ctx.t('inbox', {count: 1}); // "You have 1 message"
ctx.t('inbox', {count: 5}); // "You have 5 messages"

The variable must be a number.

date: dates​

%variable:date(format)% formats a Date object. Available tokens:

  • YYYY: 4-digit year;
  • YY: 2-digit year;
  • MM: 2-digit month;
  • DD: 2-digit day;
  • hh: 2-digit hour;
  • mm: 2-digit minutes;
  • ss: 2-digit seconds.
{
"joined": "Member since %date:date(DD/MM/YYYY)%"
}
ctx.t('joined', {date: new Date(2026, 8, 24)}); // "Member since 24/09/2026"

Each token is replaced only once per format.

switch: multiple cases​

%variable:switch(case:text|case:text)% returns the text of the first matching case. A case can be:

  • an exact text: %role:switch(admin:Administrator|mod:Moderator)%;
  • an exact number: %level:switch(1:Beginner|2:Advanced)%;
  • a min-max range, bounds excluded by default. Add ! after a bound to include it, and use -inf or inf for an open range.
{
"size": "%members:switch(-inf-50:A small server|50!-1000:A growing server|1000!-inf:A large server)%"
}
ctx.t('size', {members: 50}); // "A growing server"

If no case matches, t() throws an error: make sure your ranges cover every possible value. Don't mix text cases with a numeric value: a text case compared to a number also throws. Finally, the text of a case cannot contain a colon (:).