How to add and use messages
All user-facing strings go through MessageService (MiniMessage-based i18n).
Steps
1. Add keys to both lang files
Keys must be identical in both src/main/resources/lang/de.yml and lang/en.yml.
LangParityTest fails the build if the sets diverge.
Naming convention: <module-id>.<action> (dot-separated, kebab-case segments).
my-feature: action-done: "<prefix> <green>Done: <result>" not-found: "<prefix> <red>Could not find <name>.</red>"# lang/de.yml — same keys, translated valuesmy-feature: action-done: "<prefix> <green>Fertig: <result>" not-found: "<prefix> <red><name> wurde nicht gefunden.</red>"<prefix> is a built-in placeholder resolved to the value of the prefix key at the
root of the active lang file (e.g. <gray>[<gold>Kreiscraft</gold>]</gray>). You do not
need to pass it manually.
2. Use MiniMessage tags in the string value
Standard MiniMessage formatting tags work: <green>, <red>, <gradient:#aaa:#bbb>,
<click:...>, etc. User-supplied runtime values are inserted via named placeholders:
<result>, <name>, etc. — these are resolved at render time from the varargs you pass
(see step 3).
3. Send a message to a CommandSender
// No runtime placeholders:ctx.messages().send(sender, "my-feature.not-found");
// With runtime placeholders (key/value pairs, varargs):ctx.messages().send(sender, "my-feature.action-done", "result", "some value");ctx.messages().send(sender, "my-feature.not-found", "name", playerName);Signature: void send(CommandSender sender, String key, Object... placeholders)
Placeholders are interleaved name, value pairs. Every <name> tag in the message
string is replaced with the corresponding value. Values are converted with
String.valueOf().
4. Render as Component (without sending)
Component c = ctx.messages().render("my-feature.action-done", "result", "42");Signature: Component render(String key, Object... placeholders)
5. Broadcast to all online players
ctx.messages().broadcast("core.startup.summary", "enabled", 5, "disabled", 2, "failed", 0);6. Built-in %token% placeholders (PlaceholderResolver)
These are not MiniMessage tags. They use the %token% pattern and are resolved by
PlaceholderResolver for raw string templates (e.g. tablist header/footer strings). They
are not available inside the lang YAML files via MessageService.
| Token | Value |
|---|---|
%tps% |
TPS (first sample), capped at 20.0 and rounded to at most two decimals |
%server_players% |
Online player count |
%system_time% |
Current server time HH:mm:ss |
%player_name% |
Player’s display name (requires Player context) |
%ping% |
Player’s ping in ms (requires Player context) |
%playtime% |
Player’s playtime as HH:MM:SS (requires Player context) |
%plugin_prefix% |
Literal [Kreiscraft] |
Access via ctx.services().get(PlaceholderResolver.class).resolveString(template, player).
7. Run tests
./gradlew testLangParityTest — both lang files must have identical key sets.
MessageKeyReferenceTest — keys referenced in code must exist in the lang files.