-
Notifications
You must be signed in to change notification settings - Fork 12
LocalizationSystem
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)
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
.jsonfile 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).
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 | 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 |
-
ConfigManager.getServerLanguage()readslocalization.languagefromconfig.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 fromen_usas fallback. - If the deployed file's
_langVersionis older than the current JAR version, new keys are merged in without overwriting existing values. -
overrides.jsonis loaded. Overrides take top priority over all files.
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)
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.
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_langVersionvalue) -
MessageUtil.java→CURRENT_LANG_VERSIONconstant
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>
|
| 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 |
| 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.
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 andstorage.autoMigrateis 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.
-
Generate a template:
/language template zh_twThis creates
neoessentials/languages/templates/zh_tw_template.json. -
Translate all values in the template (replace
[TRANSLATE] English textwith 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:
{ "_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:"localization": { "language": "zh_tw" }
-
Run
/neoessentials reload. -
Verify coverage with
/language validate zh_tw.
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 |
- 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
_langVersioninen_us.jsonandCURRENT_LANG_VERSIONinMessageUtil.java. - On the next server start, the new key is automatically merged into all deployed language files.
commands.neoessentials.<category>.<action> (command feedback messages)
neoessentials.<category>.<action> (system / non-command messages)
Examples:
commands.neoessentials.teleport.home.not_foundcommands.neoessentials.moderation.ban.successneoessentials.moderation.ban_messageneoessentials.error.no_server
// 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);| 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