Translate
Overview
Translate lets each player pick a language and read your server content in it. You declare the languages in one configuration entry, write the texts in JSON files (the format is compatible with the Triton plugin, so existing files can be reused), and display them anywhere that accepts MiniMessage placeholders with <translate_key:your.key>. Each player sees the text in their own language, with fallback to another language when a text is missing.
Three optional features sit on top: translated signs (a sign shows its text in the language of whoever looks at it), a language menu and settings menu built as ordinary GUI pages, and AI chat translation, where chat messages are translated for viewers who speak a different language through an AI provider or a translation engine.
Player choices are stored in artifacts. The language is saved per player, as are the sign and chat translation opt-ins.
Features
- Language list — each language has a code, a banner flag, display name, client locales, fallbacks and actions to run when chosen.
- Triton-style JSON files — text and sign translations, with
%1,%2arguments, loaded from a folder and reloadable without a restart. - Fallback chain — requested language, then its
fallbackLanguages, then the main language. - Auto-detection — optionally switch to the language matching the client locale.
- Translated signs — place text at sign coordinates; players can opt out in the settings menu.
- Page-declared menus — build the language and settings menus as GUI pages with Translate button types.
- AI chat translation — OpenRouter, OpenAI, DeepSeek, Groq, Mistral, Ollama, Anthropic, Gemini, LibreTranslate, DeepL or any OpenAI-compatible server, with a cache.
- Language events and facts — trigger entries when a player changes language and test the current language in criteria.
Installation
- Install the GUI extension to use the language and settings menus (optional if you only use the command).
- Optionally install the MySQL extension: the AI translation cache is then saved in the database, otherwise it lives only in memory.
- Put the Translate JAR in
plugins/Typewriter/extensions/and restart the server. - Create the folder
plugins/Typewriter/Translate/(it is created automatically on first load) and put your.jsontranslation files in it. - Install MiniPlaceholders if you want to display translations through placeholders.
Getting started
- In the TypeWriter web panel, open a static page and add a Player Language artifact: without it the chosen language is not saved.
- Add a Translation Config entry. In
languages, adden_GBandfr_FR: for each setname,flag(a banner code),displayName(for example&3Francais),minecraftCodes(fr_FR,fr_CA) and optionallyfallbackLanguages. SetmainLanguagetoen_GB. - Create
plugins/Typewriter/Translate/messages.jsonwith this content, then publish or run/translate reload:
[
{
"type": "text",
"key": "welcome.message",
"languages": {
"en_GB": "&6Welcome to the server, %1!",
"fr_FR": "&6Bienvenue sur le serveur, %1 !"
}
}
]
- Use
<translate_key:welcome.message>in any MiniMessage text with placeholders (a dialogue, a hologram, a scoreboard). A player running/translate fr_FRnow sees the French text. - To let players choose from a menu, build a menu page with the GUI extension, place
translate_button:LANGUAGE#0,LANGUAGE#1... items on it, reference it inlanguageMenuof the config, and trigger Open Language Selector.
Text arguments %1, %2 are filled only by code that calls the translation API with arguments. The <translate_key:...> placeholder takes only a key, so %1 stays as written there.
How it works
A player's language is resolved in this order: the saved choice, then the language matching the client locale (minecraftCodes), then mainLanguage.
Translation files
Files are .json files directly in the translations folder (translationsFolder, Translate by default, inside plugins/Typewriter/). Each file is an array of items; a file can also be an object that contains such an array under translations, items, languages, data or entries. Items without a valid type and key are skipped, and a file that fails to parse is logged and ignored.
A text item has a key and a languages object. & color codes are converted. Key lookup is tolerant: exact key, then the key with the prefixes lang_, lang. or default., then case-insensitive, then a match on the part after the last _ or ..
A sign item has locations (world, x, y, z) and lines per language. Up to 8 lines are read, but only the first 4 (front side) are shown.
{
"type": "sign",
"key": "shop.sign",
"locations": [{"world": "world", "x": 100, "y": 64, "z": 200}],
"lines": {
"en_GB": ["&b[Shop]", "&fClick to open", "", ""],
"fr_FR": ["&b[Boutique]", "&fCliquez pour ouvrir", "", ""]
}
}
A sign is translated when a player places or edits it at a listed location, or right-clicks it; the player sees their language on the sign through a client-side update. Players who switched sign translation off in the settings menu see the original.
Menus
The language and settings menus are normal open_gui entries of the GUI extension. Translate provides button types to place on their items (the button type field of a GUI item):
| Button type | Behaviour |
|---|---|
translate_button:LANGUAGE#<n> | The n-th language of the config (from 0). Shows the code and a marker on the active one; left click selects it. Empty past the last language. |
translate_button:TOGGLE_CHAT | Shows and switches the player's AI chat translation (on by default). |
translate_button:TOGGLE_SIGN | Shows and switches the player's sign translation (on by default). |
Reference the pages in the Translation Config: languageMenu (and languageMenuViewId for a specific view) for the selector, settingsMenu (and settingsMenuViewId) for settings. Open them with Open Language Selector or Open Translation Settings. /translate menu opens the language menu. If openSelectorCommandOverride is set, that command is run by the console instead ({player} is replaced by the player name).
Language selection from the menu needs a Player Language artifact: without one the player is told the choice would not be saved. The toggles likewise need the Player AI Preference and Player Sign Preference artifacts.
AI chat translation
Configure aiTranslation in the Translation Config. Set enabled, choose a provider and give an apiKey. The base URL and model default to those of the provider unless apiBaseUrl or model is set; OPENAI_COMPATIBLE requires apiBaseUrl. Prefer env:VAR_NAME as apiKey to read the key from an environment variable instead of storing it in the page.
When a player chats, the message is translated into the language of each viewer whose language differs from the sender's and who has chat translation on. The translated text replaces the message in the viewer's chat, and hovering it shows the original. Chat waits at most chatTranslationTimeoutMs (800 ms by default) for translations, then shows the untranslated message to the viewers still waiting. Messages matching noTranslateRegex (commands and pure numbers by default) are not sent, text matching censorRegex is replaced by *** before sending, and enabledSections must contain CHAT.
After three failed requests in a row, AI translation disables itself until the next reload of translations. Results are cached by message and language, in the database if the MySQL extension is present and in memory otherwise.
Commands
The command is /translate, with the aliases /lang and /language. It can only be run by players.
| Command | Description |
|---|---|
/translate | Shows the help. |
/translate <language> | Sets your language by its configured name (for example fr_FR). |
/translate list | Lists the configured languages and marks yours. |
/translate menu | Opens the language selector. |
/translate toggle_ai | Switches AI chat translation for you. |
/translate sign set <key> | Marks the sign you look at (within 5 blocks) as translated with this key. Requires typewriter.translate.admin. |
/translate sign remove | Removes the mark from the sign. Requires typewriter.translate.admin. |
/translate sign info | Shows the key, location and translation of the sign. Requires typewriter.translate.admin. |
/translate reload | Reloads translation files. Requires typewriter.translate.admin. |
/translate sign set only writes a marker and the key on the sign. The actual text comes from a sign item in a JSON file with the sign's location.
Permissions
| Permission | Grants |
|---|---|
typewriter.translate.admin | /translate reload and /translate sign .... |
Placeholders
MiniPlaceholders expansion translate, usable as MiniMessage tags. Translated text may contain MiniMessage markup and nested placeholders.
| Placeholder | Returns |
|---|---|
<translate_key:some.key> | The text for this key in the viewer's language. If missing, [Translation not found: key] in red. |
<translate_lang> | The viewer's language code, such as fr_FR. |
<translate_lang_display> | The language display name. |
<translate_lang_flag> | The language banner flag code. |
<translate_count> | Number of loaded translations (texts plus signs). |
<translate_languages> | Number of configured languages. |
Entries
10 entries, generated from the extension source.
Actions4
- Open Language Selector ActionOpen the language selection menu
- Open Translation Settings ActionOpen the translation settings menu
- Reload Translations ActionReload translation files from disk
- Set Language ActionSet a player's translation language
Events1
- Player Language Change EventTriggered when a player changes their language
Facts1
- Player Language FactReturns the player's current language as a numeric value
Artifacts3
- Player Ai Preference ArtifactStores player AI translation preferences
- Player Language ArtifactStores player language preferences
- Player Sign Preference ArtifactStores player sign translation preferences
Manifests1
- Translation ConfigConfigure the multi-language translation system
Troubleshooting
[Translation not found: key] appears. The key is not in any loaded file, or the file failed to load. Check the server log for Failed to load translation file, validate the JSON, and run /translate reload. Make sure the file is directly in plugins/Typewriter/Translate/ (subfolders are not read) and has the .json extension.
The language resets at each login. No Player Language artifact is published, so the choice is not stored. Add one.
The menu does nothing. The log says No 'languageMenu' configured or No 'settingsMenu' configured: set the menu page in the Translation Config. For the language menu, openSelectorCommandOverride takes precedence when it is not empty.
The language does not follow the client language. Without a saved choice, the client locale is used automatically if it appears in minecraftCodes of a language. forceClientLocaleOnJoin additionally switches the player, and overwrites their saved choice, whenever the client reports a locale change that matches another language.
AI translation stopped working. Look for AUTOMATICALLY DISABLED in the log: three consecutive API failures turn it off. Fix the key, URL or model, then run /translate reload. apiKey references env var ... not set means the environment variable is missing.
A sign is not translated. The sign must be at exactly the coordinates listed in a sign item of a JSON file, in the right world name. The player must not have turned sign translation off, and the sign is translated when placed or right-clicked.
Related extensions
- TinyEssentials — its chat format is kept when AI translation wraps the chat renderer.
- MySQL — persistent AI translation cache.