Translations
Translations
This page explains SkinEngine's translation system.
Folder structure
Translations live in:
/plugins/SkinEngine/translations/<language>/
A language folder contains these files:
language.ymlmessages.ymlguis.yml
Example:
/plugins/SkinEngine/translations/default/
├ language.yml
├ messages.yml
└ guis.yml
What each file does
language.yml
This file describes the language itself.
Current format:
language:
name: English
author: "SomeAuthorName"
client:
- en_us
- en_gb
What the fields mean:
language.name: human-readable language name shown by the pluginlanguage.author: translator or source namelanguage.client: Minecraft client locales that should map to this translation
How it is used:
settings.language.typeselects the active base languagesettings.language.switch.auto-detect-client-languageuseslanguage.clientto match player locales/se language <name>uses the available language folders loaded fromtranslations/
Notes:
- folder ids are lowercase ids like
default,serbian,german - locale values are normalized like
en_us,de_de,sr_rs - if a translation folder is missing
language.ymlor contains invalid metadata, it should not be treated as a valid standalone language
messages.yml
This file contains chat messages, prefixes, reusable colors, and command feedback.
Use it for:
- command responses
- error and success messages
- category labels
- reusable string fragments
- reusable lists for lore or multiline messages
guis.yml
This file contains translated GUI text.
Use it for:
- GUI titles
- built-in button names and lore
- pack entry lore
- skin entry names and lore
- skin filter labels and entry styles
guis.yml is translation data. It is not the same file as the root guis.yml layout config documented on GUI Configuration.
The skin filter keeps its option labels and selected states separate from the lore layout:
items:
filter:
name: <#fdab20>Skin Filter</#fdab20>
entries:
selected: '<#fdab20>▶</#fdab20> <#ffc157>{filter}</#ffc157>'
unselected: '<gray>▶</gray> <#d6e3cc>{filter}</#d6e3cc>'
options:
all: Show All
unlocked: Show Unlocked
locked: Show Locked
lore:
- '{filter_all_entry}'
- '{filter_unlocked_entry}'
- '{filter_locked_entry}'
Change entries.selected or entries.unselected to restyle a complete option row. Keep {filter} in both entries so the matching label from options is displayed.
Translation loading behavior
SkinEngine loads:
- the configured base language (defined in
config.yml) - languages that your players have manually selected with
/se language <name>or client languages
You may create unlimited languages. However, if your custom languages are missing some required message keys, they will fall back to the default language (English).
Supported formatting and tokens
MiniMessage
Both messages.yml and guis.yml support MiniMessage.
Examples:
prefix_success: "<green>[SE]</green> "
items:
close:
name: "<red>Cancel</red>"
Plain {token} placeholders
SkinEngine will look for placeholders and replace them wherever it's possible.
[!WARNING] We won't go into detail about where
{token}placeholders are parsed. Keep in mind that they are not global and may work in one, but won't work in another message!
Some valid examples that we currently have:
{skin}{skin_name}{skin_description}{category}{permission}{cost}{balance}{difference}{pack}{count}{player}{language}{effective_language}{status}{file}{storage}{reason}{text}{usage}{latest_version}{current_version}{seconds}{mode}{target}{targets}{sent}{skipped}{size_mb}{players}
Internal {$token:*} tags
SkinEngine also supports internal tags with the {$token:*} format.
Current supported tags are:
{$msg:key}{$msglist:key}{$sound:...}{$image:pack_image}{$offset:number}
{$msg:key}
Inserts another single message value from messages.yml.
Example:
text_error: "#ffa79e"
blocked_item: '{$msg:prefix_error}<{$msg:text_error}>Blocked.</{$msg:text_error}>'
{$msglist:key}
Inserts a list value, mainly for lore or multiline output.
Example:
shared_lore:
- "<gray>Line 1</gray>"
- "<gray>Line 2</gray>"
lore: "{$msglist:shared_lore}"
{$sound:...}
Plays a sound when the message is sent, or when a GUI open-action is processed.
Example:
prefix_success: '{$sound:"block.note_block.bell":1:1.1}<green>[SE]</green> '
Rules:
- Sounds must be placed at the very beggining of the string, before all text or other tags/placeholders
- Sounds can be namespaced. This means that sounds like
{$sound:"my_namespace:custom_sound":1:1}will work - If the namespace is not present, the plugin will treat the sound as a
minecraft:sound - You may have noticed the optional
1:1part at the end of the sound tag. The first value is volume, the second is pitch. Decimals allowed - GUI
open-actionsuse the same sound syntax
{$image:pack_image}
Inserts a managed image character from configs/images.yml.
Example:
titles:
packs: "<white>{$image:skinengine_gui_packs}"
{$offset:number}
Inserts a managed font offset string.
Example:
titles:
start: "<white>{$offset:-48}{$image:skinengine_gui_start}"
Where each token type works
In messages.yml
Supported:
- MiniMessage
- plain
{token}placeholders for that message context {$msg:key}{$msglist:key}{$sound:...}- PlaceholderAPI placeholders, if PlaceholderAPI is installed
{$image:...}and{$offset:...}after font token replacement
In guis.yml inside translations/<language>/
Supported:
- MiniMessage
- plain
{token}placeholders for that GUI context {$msg:key}{$msglist:key}- PlaceholderAPI placeholders, if PlaceholderAPI is installed
{$image:...}{$offset:...}
For GUI titles specifically, {$image:...} and {$offset:...} are first-class parts of the title rendering flow.
In language.yml
Do not treat language.yml like a message file.
Use plain metadata only:
- no MiniMessage formatting
- no runtime placeholders
- no internal
{$token:*}tags
Translation guidelines
Keep ids stable
- do not rename message keys unless the code changed
- do not rename GUI translation keys unless the code changed
- do not rename language folder ids casually once players are using them
Preserve placeholders exactly
- keep
{token}names unchanged - keep
{$msg:key}and{$msglist:key}references unchanged unless you also update the referenced keys - keep
{$image:...}keys exact - keep
{$offset:...}numeric format exact
Prefer overlay translations
If you are making a non-default translation:
- copy only the keys you want to override
- let missing keys fall back to
default
That makes updates easier.
Be careful with multiline lore
Some GUI entries expect lists, not single strings. If the default file uses a YAML list, keep it as a list.
Test client locale matching
If you rely on automatic locale detection:
- verify the values in
language.client - use normalized locale ids like
en_us,de_de,sr_rs
Watch for context-specific placeholders
{pack} may work in one GUI title but not in an unrelated chat message. Reusing text blindly across contexts can leave literal placeholder text visible in-game.
Practical examples
Reusable prefix in messages.yml
prefix_error: "<red>[SE]</red> "
no_permission: '{$msg:prefix_error}<white>You do not have permission.</white>'
GUI title in translated guis.yml
titles:
packs: "<white>{$offset:-48}{$image:skinengine_gui_packs}{$offset:-218}{$image:skinengine_title_packs}"
Language metadata
language:
name: Serbian
author: "YourName"
client:
- sr_rs