Translations
Phrase files are the only copy of a plugin’s user-facing strings. Operators reword or recolour a message by editing JSON — no rebuild, no fork. Keys are checked at build against the files that plugin actually loads.
Load, then reply#
Nothing is loaded automatically, including the shared common set. s2s build reads translations.load(...) to work out which keys cmd.replyT and Translations.translate accept; a key from a file you did not load is a compile error.
import { command, ADMFLAG, Chat, Translations, translations, HookResult } from '@s2script/sdk';
export function OnPluginStart(): void {
translations.load('basecomm', 'common');
command.admin('sm_gag', ADMFLAG.CHAT, (cmd) => {
cmd.replyT('Usage Gag');
Chat.toSlot(cmd.callerSlot, Translations.translate(cmd.callerSlot, 'Gagged Player', 1));
return HookResult.Handled;
});
}translations.load is load-window only — it throws after settle. Order is significant: translate takes the first hit within each of its two passes (the client’s language, then English). List your own set before any shared one if you want to override a shared phrase.
A plugin that loads nothing widens PhraseKey to string and compiles unchecked.
Phrase files#
One file per set, at translations/<set>.phrases.json in the addon (for example addons/s2script/translations/basecomm.phrases.json). Flat key → English template. {1}, {2}, … are positional slots; {green}, {default}, … are colour tags expanded on output.
{
"Usage Gag": "Usage: sm_gag <target>",
"Gagged Player": "{green}[SM]{default} Gagged {1} player."
}Keys are human-readable English on purpose: an unresolved key renders as the key itself.
A translator adds translations/<code>/<set>.phrases.json — no code change, no rebuild. English (the root file) is the fallback when a language file omits a key.
A missing phrase file renders raw keys. Translations.load warns, naming the set, when a seedless load finds no file.
Colour tags#
On CS2, tags match ChatColors names in lowercase ({green}, {default}, {lightred}, …). Expansion happens at output: chat keeps the control byte; console expands then strips, so tags never leak as literal {green}. Unknown tags are deleted (players never see stray braces) and warned once each to the server console.
{1} arguments cannot inject tags — braces are stripped from substituted values.
Chat.toAll is not per-client localized; it uses the operator-settable server default (Translations.setDefaultLanguage). cmd.replyT and Translations.translate(slot, …) resolve for that caller’s language.
Lower-level load#
Prefer translations.load(...) — that is what the build reads. Translations.load(name, seed?) is the lower-level form, for a name computed at runtime or a set with an in-code default. A seed is starting content; the file overrides it.
See the translations module and commands.