Localization
The plugin supports multiple languages with full translation coverage.
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
| Language | Code | Status |
|---|---|---|
| English | en-US | ✅ Complete |
| German | de-DE | ✅ Complete |
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
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.
Custom translation files (custom_*.json) are NEVER overwritten by plugin updates. Use these files for your own translations to preserve them across updates.
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
Custom translation files allow you to add or override translations without losing them during plugin updates.
How Custom Files Work
- Create
custom_en-US.json(copy fromcustom_en-US.json.example) - Add only the keys you want to override or add
- Custom translations are merged on top of standard translations
- Custom files are NEVER overwritten by updates
Load Order
Translations are loaded in this order:
localization/{language}.json(e.g.,en-US.json) - Standard translationslocalization/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
All lootbag names, descriptions, and rarities are now fully localizable using translation keys.
Lootbag Translation Keys
| Key Pattern | Description |
|---|---|
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"
}
Using custom files (custom_*.json) ensures your translations are preserved during plugin updates!
Fallback Behavior
If a translation is missing:
- Uses the achievement
idformatted as display name my_custom_achievement→My Custom Achievement- 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.
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:
- Automatic Migration - The plugin automatically extracts new JSON files on first start
- Old Format - The old
.langfiles inServer/Languages/are no longer used - 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"
}