Локализация
Выбор языка сервера, свои переводы и переопределение отдельных строк.
Команды 1#
Примеры#
Переключить сервер на русский#
Язык задаётся одним кодом; игрокам ничего ставить не нужно.
/language list
/language ru_ru
/language stats
list печатает коды из джарника. /language ru_ru то же, что /language set ru_ru, и пишет localization.language в конфиг. stats показывает, сколько ключей закрыто относительно en_us. Неизвестный код не ломает чат: недостающие строки берутся из en_us.
Поправить одну строку, не трогая jar#
Своя формулировка кита или бана, которая переживёт обновление мода.
/language override set commands.neoessentials.kits.claimed Вы получили кит {0}
/language override get commands.neoessentials.kits.claimed
/language override list
Override кладётся в DataStore (language_overrides), не в файл языка. get печатает текущий оверрайд или что ключа нет. Кавычку ' в значении с {0} удваивайте, иначе MessageFormat съест подстановку.
Свой полный перевод#
Начать файл в languages/custom/, а не копировать джарник руками.
/language template ru_ru
/language validate ru_ru
/language reload
template пишет каркас в languages/custom/. Это наложение: ключей, которых в файле нет, мод по-прежнему берёт из jar. validate проверяет плейсхолдеры и §-коды. После ручной правки файла — reload, не рестарт.
Тонкие места#
OP 4 и neoessentials.language — разные вещи#
Brigadier проверяет hasPermission(4). Узел из PermissionRegistry на эту команду не вешается. Игрок с neoessentials.language в LuckPerms, но без OP 4, команду не увидит.
Что делать: Либо выдайте OP 4 / консоль, либо поймите, что внешний провайдер эту команду по узлу не откроет.
reload команд не перерегистрирует#
/language reload перечитывает каталоги и оверрайды. Секция commands.language читается при старте: выключенная команда после reload не появится.
Что делать: Включение самой /language — рестарт сервера.
Известные проблемы#
Одиночная кавычка в строке с {0} печатает литерал {0}#
Это MessageFormat: ' начинает экранированный кусок и глотает плейсхолдер. В en_us/ru_ru это уже учтено удвоением; свой override легко сломать.
Что делать: Пишите '' вместо '. После правки — /language override reload или /language reload.
Подробное руководство#
Раздел ниже — исходная документация проекта (docs/Wiki/LocalizationSystem.md). Ссылки на другие страницы вики переписаны на этот сайт; текст самих руководств не редактируется, чтобы синк с апстримом не давал конфликтов.
Version: 1.0.4+build.16 · Last verified: 2026-07-23
Added / overhauled in build.62 · Language config setting added in build.187 (build numbers below build.16 predate the NeoForge 26.1 port's build-counter reset)
Overview#
NeoEssentials ships a fully featured, server-side localization system. Every in-game message is driven by translation keys stored in language JSON files. Server admins can:
- Set the active language with one line in
config.json — no file renaming needed.
- Override individual messages without editing any bundled file.
- Add or swap entire languages by dropping a
.json file in the custom language directory.
- Validate coverage of any language file against the English base.
- Regenerate a language file from the JAR while preserving user edits.
All tooling is exposed through the /language in-game command (OP level 4).
Choosing the Active Language#
In-game (recommended): /language set <code> (or the bare shortcut /language <code>) validates the code against the currently deployed language files, writes it to config.json, and reloads translations immediately — no restart or separate reload step needed.
/language set fr_fr
Manually: Set the server language in config.json under the localization section:
"localization": {
"language": "fr_fr"
}
Reload in-game after a manual edit: /neoessentials reload
Available built-in language codes:
| Code |
Language |
en_us |
English (US) — default |
fr_fr |
French |
de_de |
German |
es_es |
Spanish |
pt_br |
Portuguese (Brazil) |
zh_cn |
Chinese (Simplified) |
nl_nl |
Dutch |
pl_pl |
Polish |
ru_ru |
Russian |
Fallback: Any key not present in the configured language file automatically falls back to en_us. You will never see a raw translation key in-game.
File Locations#
| File |
Purpose |
neoessentials/languages/custom/<language>.json |
Active language file — auto-deployed from JAR when first selected, auto-merged on updates (e.g. fr_fr.json when "language": "fr_fr") |
neoessentials/languages/custom/en_us.json |
Fallback English file (always present) |
neoessentials/languages/custom/<code>.json |
Any additional custom / community language files |
neoessentials/languages/overrides.json |
Legacy admin message overrides file — only consulted for a one-time migration into the DataStore on first boot (see note below) |
neoessentials/languages/templates/<code>_template.json |
Auto-generated templates for translators |
How the System Works#
Startup / Boot#
ConfigManager.getServerLanguage() reads localization.language from config.json (default: en_us).
MessageUtil.loadTranslations() (private, called internally on startup) resolves the configured language code. MessageUtil.reloadTranslations() (public) is the equivalent used by /language reload / /language set / /neoessentials reload.
- The language file is looked for at
neoessentials/languages/custom/<code>.json.
- If not found (first run), it is deployed from the JAR's bundled
<code>.json.
- If the
fr_fr (or other) JAR file does not cover 100% of keys, missing keys are filled from en_us as fallback.
- If the deployed file's
_langVersion is older than the current JAR version, new keys are merged in without overwriting existing values.
overrides.json is loaded. Overrides take top priority over all files.
Key Resolution Order (highest → lowest priority)#
1. Admin overrides (overrides.json)
2. Configured language file (e.g. fr_fr.json when "language": "fr_fr")
3. en_us fallback (for keys not covered by the configured language)
4. Human-readable fallback (generated from the key name)
Human-Readable Fallback#
If a key is not found in any file, instead of showing the raw key string (commands.neoessentials.home.not_found), the system strips common prefixes and converts dots/underscores to spaces:
commands.neoessentials.home.not_found → "Home not found"
neoessentials.moderation.player_only_command → "Moderation player only command"
This ensures players always see readable English even if a key is missing.
Version Tracking#
Each language file contains a _langVersion metadata key:
{
"_langVersion": "23",
...
}
MessageUtil.java maintains a constant CURRENT_LANG_VERSION. On startup, if the deployed file's version is lower, new keys are merged automatically. The file version is then bumped to the current value and saved.
Current version: 23 (MessageUtil.CURRENT_LANG_VERSION, matches the shipped en_us.json's own _langVersion).
When you add new translation keys to en_us.json, increment _langVersion by 1 in both:
- src/main/resources/data/lang/en_us.json (the _langVersion value)
- MessageUtil.java → CURRENT_LANG_VERSION constant
/language Command Reference#
All subcommands require OP level 4.
| Command |
Description |
/language list |
List all custom language files loaded |
/language reload |
Reload all language files and overrides from disk |
/language stats |
Show statistics (loaded languages, missing keys tracked, overrides) |
/language info |
Show full command reference |
/language set <code> |
Set the active server language and reload translations immediately |
/language <code> |
Shortcut for /language set <code> |
Templates & Missing Keys#
| Command |
Description |
/language template <code> |
Generate a translator-ready template for language <code> |
/language exportmissing |
Export all runtime-tracked missing keys to a JSON template file |
/language clearmissing |
Reset the missing-keys tracker |
Validation & Regeneration#
| Command |
Description |
/language validate <code> |
Compare <code>.json against en_us.json; shows coverage %, missing keys, and extra keys |
/language regenerate <code> |
Re-deploy <code>.json from JAR, merging user translations, auto-backup to .bak |
/language validate example output:
═══ Language Validation: fr_fr ═══
Total base keys: 1058
Translated keys: 987
Coverage: §e93%
Missing keys: 71
Extra keys (not in base): 3
First 10 missing key(s):
- commands.neoessentials.teleport.request.sent
- commands.neoessentials.teleport.request.received
...
Run /language regenerate fr_fr to update the file from JAR.
Admin Overrides#
Override any message key permanently. Overrides are persisted through the active DataStore (see Storage Backend) as a single document — with the default json backend that's neoessentials/store/language_overrides.json; with sqlite/mysql it's a table in the configured database. Overrides survive reloads.
Storage note: neoessentials/languages/overrides.json (the old flat file) is now only read for a one-time legacy migration — if the DataStore has no override document yet and storage.autoMigrate is enabled, any overrides found in that file are imported once and the DataStore becomes authoritative from then on.
| Command |
Description |
/language override set <key> <value> |
Set a message override |
/language override get <key> |
Show the current override for a key |
/language override remove <key> |
Remove a single override |
/language override list |
List all active overrides |
/language override clear |
Remove all overrides |
/language override reload |
Reload overrides from disk |
Example — customize the server's welcome message:
/language override set neoessentials.server.welcome §aWelcome to MyServer! Type /help to get started.
Creating a Custom Language#
-
Generate a template:
/language template zh_tw
This creates neoessentials/languages/templates/zh_tw_template.json.
-
Translate all values in the template (replace [TRANSLATE] English text with your translation). Keep {0}, {1} placeholders exactly as-is — they are substituted at runtime with player names, counts, etc.
-
Add metadata at the top of your file:
json
{
"_nativeName": "繁體中文",
"_englishName": "Traditional Chinese",
"_languageCode": "zh_tw",
"_author": "YourName",
"_version": "1.0",
...
}
-
Save the file as neoessentials/languages/custom/zh_tw.json.
-
Set it active in config.json:
json
"localization": {
"language": "zh_tw"
}
-
Run /neoessentials reload.
-
Verify coverage with /language validate zh_tw.
Bundled Language Files#
The following languages are bundled in the JAR and can be activated via config.json:
| Code |
Language |
en_us |
English (base) |
fr_fr |
French |
de_de |
German |
es_es |
Spanish |
pt_br |
Portuguese (Brazil) |
zh_cn |
Chinese (Simplified) |
nl_nl |
Dutch |
pl_pl |
Polish |
ru_ru |
Russian |
Developer Notes#
Adding a New Translation Key#
- Add the key + English value to
src/main/resources/data/lang/en_us.json.
- Use the key via
MessageUtil.localize("your.new.key", arg0, arg1...) in Java.
- Increment
_langVersion in en_us.json and CURRENT_LANG_VERSION in MessageUtil.java.
- On the next server start, the new key is automatically merged into all deployed language files.
Key Naming Convention#
commands.neoessentials.<category>.<action> (command feedback messages)
neoessentials.<category>.<action> (system / non-command messages)
Examples:
- commands.neoessentials.teleport.home.not_found
- commands.neoessentials.moderation.ban.success
- neoessentials.moderation.ban_message
- neoessentials.error.no_server
MessageUtil API#
// Simple lookup with args
String msg = MessageUtil.localize("commands.neoessentials.home.not_found", homeName);
// With explicit English fallback (use when you're certain of the English text)
String msg = MessageUtil.localize("commands.neoessentials.home.not_found", "Home ''{0}'' not found.", homeName);
// As a Component (for sendSuccess / sendFailure)
Component comp = MessageUtil.component("commands.neoessentials.home.not_found", homeName);
// Convenience coloured components
Component success = MessageUtil.success("commands.neoessentials.home.set", name);
Component error = MessageUtil.error("commands.neoessentials.home.not_found", name);
Component warning = MessageUtil.warning("commands.neoessentials.home.limit_reached", max);
Component info = MessageUtil.info("commands.neoessentials.home.list_header", count);
Troubleshooting#
| Problem |
Fix |
| Language not changing after config edit |
Run /neoessentials reload — language is re-read on every reload. |
Message shows raw key (e.g. commands.neoessentials.xyz) |
The key is missing from en_us.json. Add it and bump _langVersion. |
| Message shows human-readable fallback instead of proper text |
Same as above — key is missing. The humanizer is a safety net, not the final output. |
| Bundled language file not updating |
Run /language regenerate <code>. |
| Override not showing in-game |
Run /language override reload or /language reload. Overrides live in the DataStore now — use /language override set/get/remove rather than hand-editing overrides.json (which is legacy-import-only). |
| Language file corrupted |
Delete it and run /language regenerate <code> to redeploy from JAR. |
| Language falls back to English for some messages |
That key isn't translated in the language file yet. Run /language validate <code> to see missing keys. |
Last updated: 1.0.4+build.16 — 2026-07-23