Automatic chat translation for Minecraft (Paper 1.21+). When a player sends a chat message, every other player receives it translated into their own language — based on their Minecraft client language, or a language they pick manually.
- 🌍 Automatic — uses each player's Minecraft locale, no setup needed per player
- 🎛️ Per-player control — anyone can turn translation off, or switch language without changing their Minecraft language
- ⚡ Optimized — fully asynchronous, cached, and de-duplicated; the main server thread is never blocked
- 🔌 Pluggable engines — self-hosted LibreTranslate, Google Gemini, or Groq
- 💾 SQLite storage — per-player settings stored in an embedded database, scales to large player counts
- Java 21+
- Minecraft 1.21+, any server in the Bukkit/Spigot family
- A translation engine (see below). The easiest free options are Groq (API key) or self-hosted LibreTranslate (Docker/Python).
One jar covers 1.21.0 → 26.2 (the latest release) — no per-version builds. This works because TranslateEra deliberately avoids NMS/version-specific code and uses only stable API. It is compiled against the 1.21.0 API floor (so every method exists on 1.21.0 and every later release) and has been verified to compile cleanly against the whole range, including the 1.21 → 26 major-version transition: 1.21.1, 1.21.4, 1.21.8, 1.21.11, 26.1 and 26.2. Newer releases are expected to keep working as long as this stable API remains.
Minecraft moved to a calendar-based versioning scheme in 2026 (after 1.21.11 came 26.1, 26.2, ...). The plugin keeps
api-version: '1.21'so it loads on the widest range — from 1.21.0 up through the newest 26.x servers.
One jar runs on the whole plugin family — it uses only cross-compatible API
(AsyncPlayerChatEvent, the BungeeCord Chat API, getLocale()):
| Server | Supported |
|---|---|
| Bukkit / CraftBukkit | ✅ |
| Spigot | ✅ |
| Paper | ✅ |
| Purpur | ✅ |
| Folia | ✅ (regionized-thread safe; folia-supported: true) |
Modded platforms (Forge, NeoForge, Fabric/Quilt) are not supported directly — they run mods, not Bukkit plugins. To use TranslateEra there, run a hybrid server such as Mohist/Arclight (Forge/NeoForge) or Banner/Cardboard (Fabric/Quilt), which load Bukkit plugins on a modded server.
- Download
TranslateEra-<version>.jar(or build it, see Building). - Drop it into your server's
plugins/folder. - Start the server once to generate
plugins/TranslateEra/config.yml. - Pick and configure a translation engine (below), then run
/lang reloador restart.
On the console you should see: Translation provider: <name>.
Set the provider field in config.yml to libretranslate, gemini, or groq.
| Provider | Quality | Speed | Cost | Notes |
|---|---|---|---|---|
| Groq | Good | Very fast | Free tier | Generous limits, great for chat volume |
| Gemini | High | Fast | Free tier | ~15 requests/min on the free tier |
| LibreTranslate | Medium | Fast (local) | Free | Self-hosted, unlimited, fully offline-capable |
If the selected provider's API key is empty, the plugin safely falls back to LibreTranslate.
- Go to https://console.groq.com/keys and sign in.
- Click Create API Key and copy it (starts with
gsk_). - In
config.yml:provider: groq groq: api-key: "gsk_your_key_here" model: "llama-3.1-8b-instant"
- Run
/lang reload.
- Go to https://aistudio.google.com/app/apikey and sign in.
- Click Create API key and copy it (starts with
AIza). - In
config.yml:provider: gemini gemini: api-key: "AIza_your_key_here" model: "gemini-2.0-flash"
- Run
/lang reload.
Run your own translation server. Two ways:
docker run -d --name libretranslate -p 5000:5000 \
-v lt-models:/home/libretranslate/.local \
libretranslate/libretranslate --load-only en,tr,de,es,fr,ru,ar--load-only en,tr,...— only these languages are downloaded (faster startup, less RAM). Add whatever languages your players use.-v lt-models:...— persists the downloaded models so they aren't re-downloaded.- First startup downloads the models and can take a few minutes.
Make it start automatically with Docker:
docker update --restart unless-stopped libretranslateVerify it works:
curl http://localhost:5000/languages
curl -s -X POST http://localhost:5000/translate \
-H "Content-Type: application/json" \
-d '{"q":"Hello world","source":"en","target":"tr"}'Note: the container needs internet access on first run to download models. If you see
Temporary failure in name resolutionor anIndexErrorabout languages, the container couldn't reach the internet — check Docker's DNS and restart.
pip install libretranslate
libretranslate --load-only en,tr,de --host 0.0.0.0 --port 5000provider: libretranslate
libretranslate:
url: "http://localhost:5000" # change if LibreTranslate runs on another host
api-key: "" # only if your instance requires oneThen run /lang reload.
| Command | Description | Permission |
|---|---|---|
/lang on |
Enable translation for yourself | translateera.use (default) |
/lang off |
Disable translation; see original messages | translateera.use |
/lang set <code> |
Pick a language manually (e.g. tr, en, de) |
translateera.use |
/lang reset |
Go back to your Minecraft client language | translateera.use |
/lang status |
Show your current settings | translateera.use |
/lang list |
List supported languages | translateera.use |
/lang reload |
Reload the configuration | translateera.admin (op) |
Aliases: /translate, /dil.
Hover over a translated message to see the original text.
provider: libretranslate # libretranslate | gemini | groq
libretranslate:
url: "http://localhost:5000"
api-key: ""
gemini:
api-key: ""
model: "gemini-2.0-flash"
groq:
api-key: ""
model: "llama-3.1-8b-instant"
# If the primary provider fails, these are tried in order (empty-key ones are skipped).
fallback:
order: [groq, gemini, libretranslate]
# Chat line format. Other chat plugins' formatting no longer applies (we re-send the
# message), so reproduce yours here. Supports {player}, {message}, and %PlaceholderAPI%.
format:
chat: "{player}: {message}"
translated-tag: "" # optional tag before translated messages, e.g. "&7[T] "
# Content that should not be translated; masked before translation and restored after.
protect:
urls: true
coordinates: true
mentions: true # @name mentions
translation:
enabled-by-default: true # is translation ON for new players?
cache:
max-size: 50000 # max cached translations
expire-after-write-minutes: 360
max-message-length: 256 # longer messages are delivered untranslated
cooldown-ms: 0 # per-player anti-spam; 0 disables
metrics: true # anonymous bStats stats; false to opt out- Custom messages: all plugin text lives in
plugins/TranslateEra/messages.yml— edit it to reword or localize (supports&colour codes). - PlaceholderAPI: install it to use
%...%placeholders (e.g.%luckperms_prefix%) informat.chat. - Hover: hovering a translated message shows the original text.
- Cross-platform delivery: messages can't be rewritten per-recipient, so the
AsyncPlayerChatEventis cancelled and each recipient is sent the translation individually via the BungeeCord Chat API — available on every Bukkit-family server. - Off the main thread: the chat event already runs async, and the HTTP calls use
the JDK's async
HttpClienton a dedicated worker pool — the main thread never blocks. - De-duplication: recipients are grouped by target language, so each language is translated once and delivered to everyone who needs it.
- Caching: a Caffeine cache keyed by
(text, language)means repeated phrases cost nothing; concurrent requests for the same pair are coalesced into one provider call. - Skip when unnecessary: recipients who share the sender's language, or who turned translation off, receive the original with no translation call.
- Storage: per-player settings live in an embedded SQLite database
(
plugins/TranslateEra/players.db) with single-row upserts — no full-file rewrites.
Requires JDK 21 and Maven.
mvn clean packageThe shaded plugin jar is produced at target/TranslateEra-<version>.jar.
MIT LICENSE


-1.jpg)



