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.yml
  • messages.yml
  • guis.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 plugin
  • language.author: translator or source name
  • language.client: Minecraft client locales that should map to this translation

How it is used:

  • settings.language.type selects the active base language
  • settings.language.switch.auto-detect-client-language uses language.client to match player locales
  • /se language <name> uses the available language folders loaded from translations/

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.yml or 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:

  1. the configured base language (defined in config.yml)
  2. 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:1 part at the end of the sound tag. The first value is volume, the second is pitch. Decimals allowed
  • GUI open-actions use 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