Skip to main content

Localization

The plugin supports multiple languages with full translation coverage.

Version 1.6.0+

Since version 1.6.0, language files are stored externally as JSON files, allowing server operators to customize translations without modifying the JAR.

Supported Languages

LanguageCodeStatus
Englishen-US✅ Complete
Germande-DE✅ Complete
Custom Languages

Additional languages can be added manually. Simply create a <locale>.json file in the localization folder and set the language code in config.json.

Configuration

Set the language in config.json:

{
"language": "en-US"
}

Language Files

Language files are located in the plugin's data folder:

KyuubiAchievements/
└── localization/
├── en-US.json # English (standard)
├── de-DE.json # German (standard)
├── custom_en-US.json # Custom English overrides
├── custom_de-DE.json # Custom German overrides
├── custom_en-US.json.example # Template for custom translations
└── custom_de-DE.json.example # Template for custom translations
Auto-Extraction

On first start, the plugin automatically extracts the standard language files from the JAR. Standard files may be updated during plugin updates to include new translations.

Version 1.8.6+

Custom translation files (custom_*.json) are NEVER overwritten by plugin updates. Use these files for your own translations to preserve them across updates.

Custom Languages

Any language can be added manually! Simply create a <locale>.json file (e.g., es-ES.json, pt-BR.json, ja-JP.json) in the localization folder and set "language": "<locale>" in config.json. No code changes required!

File Format

Language files use JSON format:

{
"achievements.ui.gallery": "Achievement Gallery",
"achievements.ui.search": "Search...",
"achievements.name.first_blood": "First Blood",
"achievements.desc.first_blood": "Kill your first enemy"
}

Benefits of JSON Format

  • Syntax Highlighting - Better editor support
  • Validation - JSON schema validation possible
  • No Escaping Issues - Special characters handled automatically
  • Standard Format - Widely supported and understood

Key Structure

Achievement Names

{
"achievements.name.first_blood": "First Blood",
"achievements.name.spider_hunter_1": "Spider Hunter I",
"achievements.name.spider_hunter_2": "Spider Hunter II"
}

Achievement Descriptions

{
"achievements.desc.first_blood": "Kill your first enemy",
"achievements.desc.spider_hunter_1": "Kill 1 spider",
"achievements.desc.spider_hunter_2": "Kill 10 spiders"
}

Title Names

{
"achievements.title.spider_slayer": "Spider Slayer",
"achievements.title.monster_hunter": "Monster Hunter",
"achievements.title.veteran": "Veteran"
}

UI Text

achievements.ui.gallery=Achievement Gallery
achievements.ui.search=Search...
achievements.ui.all=All
achievements.ui.combat=Combat
achievements.ui.progression=Progression
achievements.ui.exploration=Exploration
achievements.ui.social=Social
achievements.ui.completed=Completed
achievements.ui.locked=Locked
achievements.ui.unlocked=Unlocked
achievements.ui.progress=Progress
achievements.ui.requires=Requires
achievements.ui.reward=Reward
achievements.ui.title=Title
achievements.ui.prev=Previous
achievements.ui.next=Next
achievements.ui.page=Page

Trigger Descriptions

achievements.trigger.blocks_mined=Mine {0} blocks
achievements.trigger.blocks_placed=Place {0} blocks
achievements.trigger.kills=Kill {0} {1}
achievements.trigger.items_crafted=Craft {0} {1}
achievements.trigger.playtime_minutes=Play for {0} minutes
achievements.trigger.distance_walked=Walk {0} blocks
achievements.trigger.chat_messages=Send {0} chat messages
achievements.trigger.zones_discovered=Discover {0} zones

Command Messages

achievements.cmd.grant.success=Granted {0} to {1}
achievements.cmd.grant.already=Player already has this achievement
achievements.cmd.revoke.success=Revoked {0} from {1}
achievements.cmd.revoke.not_unlocked=Player doesn't have this achievement
achievements.cmd.list.header={0}'s Achievements ({1}/{2}):
achievements.cmd.list.empty=No achievements unlocked

Broadcast Messages

achievements.broadcast.unlock={0} unlocked: {1}
achievements.broadcast.title={0} earned the title: {1}

Custom Translation Files

Version 1.8.6+

Custom translation files allow you to add or override translations without losing them during plugin updates.

How Custom Files Work

  1. Create custom_en-US.json (copy from custom_en-US.json.example)
  2. Add only the keys you want to override or add
  3. Custom translations are merged on top of standard translations
  4. Custom files are NEVER overwritten by updates

Load Order

Translations are loaded in this order:

  1. localization/{language}.json (e.g., en-US.json) - Standard translations
  2. localization/custom_{language}.json (e.g., custom_en-US.json) - Custom overrides

Keys in the custom file will override keys from the standard file.

Example: Custom Translation File

localization/custom_en-US.json:

{
"achievements.name.first_blood": "My First Kill",
"achievements.desc.first_blood": "Defeat your very first enemy!",
"lootbag.name.my_custom_bag": "Treasure Chest",
"lootbag.desc.my_custom_bag": "Contains rare treasures!"
}

Lootbag Localization

Version 1.8.5+

All lootbag names, descriptions, and rarities are now fully localizable using translation keys.

Lootbag Translation Keys

Key PatternDescription
lootbag.name.<id>Display name of the lootbag
lootbag.desc.<id>Description text
lootbag.rarity.<rarity>Rarity display name (common, uncommon, rare, epic, legendary)

Example: Lootbag Translations

localization/custom_en-US.json:

{
"lootbag.name.starter_pack": "Starter Pack",
"lootbag.desc.starter_pack": "A bag of essential items for new adventurers.",
"lootbag.name.epic_treasure": "Epic Treasure Chest",
"lootbag.desc.epic_treasure": "Contains powerful weapons and rare materials!",
"lootbag.rarity.common": "Common",
"lootbag.rarity.uncommon": "Uncommon",
"lootbag.rarity.rare": "Rare",
"lootbag.rarity.epic": "Epic",
"lootbag.rarity.legendary": "Legendary"
}

localization/custom_de-DE.json:

{
"lootbag.name.starter_pack": "Starterpaket",
"lootbag.desc.starter_pack": "Eine Tasche mit wichtigen Gegenständen für neue Abenteurer.",
"lootbag.name.epic_treasure": "Epische Schatzkiste",
"lootbag.desc.epic_treasure": "Enthält mächtige Waffen und seltene Materialien!",
"lootbag.rarity.common": "Gewöhnlich",
"lootbag.rarity.uncommon": "Ungewöhnlich",
"lootbag.rarity.rare": "Selten",
"lootbag.rarity.epic": "Episch",
"lootbag.rarity.legendary": "Legendär"
}

Adding Custom Translations

For Custom Achievements

When creating custom achievements, add translations to the custom JSON files:

custom/custom_achievements.json:

{
"achievements": [
{
"id": "my_custom_achievement",
"category": "progression",
"trigger": { "type": "blocks_mined", "target": "any", "count": 100 }
}
]
}

localization/custom_en-US.json:

{
"achievements.name.my_custom_achievement": "My Custom Achievement",
"achievements.desc.my_custom_achievement": "Mine 100 blocks of any type"
}

localization/custom_de-DE.json:

{
"achievements.name.my_custom_achievement": "Mein Eigenes Achievement",
"achievements.desc.my_custom_achievement": "Baue 100 Blöcke beliebiger Art ab"
}
tip

Using custom files (custom_*.json) ensures your translations are preserved during plugin updates!

Fallback Behavior

If a translation is missing:

  1. Uses the achievement id formatted as display name
  2. my_custom_achievementMy Custom Achievement
  3. Underscores become spaces, words capitalized

Placeholders

Numbered Placeholders

# {0} = first parameter, {1} = second, etc.
achievements.trigger.kills=Kill {0} {1}
# Result: "Kill 50 Spiders"

Named Placeholders (in code)

achievements.broadcast.unlock={player} unlocked: {achievement}

Adding a New Language

Step 1: Create Language File

Create a new JSON file in the localization folder:

KyuubiAchievements/
└── localization/
├── en-US.json
├── de-DE.json
├── fr-FR.json
└── es-ES.json ← New language file

Step 2: Copy English File

Copy en-US.json and rename it to your language code (e.g., fr-FR.json).

Step 3: Translate

Translate all values (keep keys unchanged):

{
"achievements.ui.gallery": "Galerie des Succès",
"achievements.ui.search": "Rechercher...",
"achievements.name.first_blood": "Premier Sang"
}

Step 4: Configure

Set the language in config.json:

{
"language": "fr-FR"
}

Translation Guidelines

Consistency

  • Use consistent terminology throughout
  • Match game terminology where applicable
  • Keep placeholder positions logical

Length

  • UI elements have limited space
  • Test translations in-game
  • Abbreviate if necessary

Special Characters

  • Umlauts and accents are supported (ä, ö, ü, é, etc.)
  • Unicode characters work
  • Emoji support depends on client font

Example Translations

Achievement Chain (English)

{
"achievements.name.miner_1": "Novice Miner",
"achievements.name.miner_2": "Apprentice Miner",
"achievements.name.miner_3": "Journeyman Miner",
"achievements.name.miner_4": "Master Miner",
"achievements.desc.miner_1": "Mine 100 blocks",
"achievements.desc.miner_2": "Mine 1,000 blocks",
"achievements.desc.miner_3": "Mine 10,000 blocks",
"achievements.desc.miner_4": "Mine 100,000 blocks"
}

Achievement Chain (German)

{
"achievements.name.miner_1": "Bergbau-Anfänger",
"achievements.name.miner_2": "Bergbau-Lehrling",
"achievements.name.miner_3": "Bergbau-Geselle",
"achievements.name.miner_4": "Bergbau-Meister",
"achievements.desc.miner_1": "Baue 100 Blöcke ab",
"achievements.desc.miner_2": "Baue 1.000 Blöcke ab",
"achievements.desc.miner_3": "Baue 10.000 Blöcke ab",
"achievements.desc.miner_4": "Baue 100.000 Blöcke ab"
}

Hot Reload

Language changes require a server restart to take effect.

Development

During development, consider creating a reload command to test translations without restarting.


Migration from v1.5.x

If you're upgrading from v1.5.x or earlier:

  1. Automatic Migration - The plugin automatically extracts new JSON files on first start
  2. Old Format - The old .lang files in Server/Languages/ are no longer used
  3. Custom Translations - Copy your custom translations to the new JSON format in localization/

Converting .lang to JSON

Old format:

achievements.name.my_achievement=My Achievement
achievements.desc.my_achievement=Do something cool

New format:

{
"achievements.name.my_achievement": "My Achievement",
"achievements.desc.my_achievement": "Do something cool"
}