Skip to content
Contractsv1.9.0

Translating your content

The localization system covers the plugin’s own messages. This page covers the other half: the content you author. Contract titles and descriptions, category names and descriptions, objective and reward text, and preset display names can all carry per-language translations, stored right in your data files.

Default content is English only

All default content is written in English and is not translated on purpose to reduce the file size and maintenance burden for newcomers. If you want to translate the default content without too much effort, you can do so by dumping the data files to an LLM of your choice and asking it to translate them. (point them to this page to explain the translation format and fallback behavior)

Every content text field has a matching *Translations sibling: title has titleTranslations, description has descriptionTranslations, a category’s name has nameTranslations, and a preset’s displayName has displayNameTranslations. Each one maps a language code to the translated text, or is null when there are no translations:

{
"title": "Big Game Hunter",
"titleTranslations": {
"fr": "Chasse au gros gibier",
"es": "Cazador de caza mayor"
},
"description": "Take down the island's most dangerous predators.",
"descriptionTranslations": null
}

When a player opens the menu, each field resolves independently:

  1. Look up the player’s language in the field’s translations, matching the language code case-insensitively.
  2. If there is no entry for that language, or the entry is blank, show the authored field instead.

There is no region fallback: a player on zh-CN gets the zh-CN entry or the authored text, never a zh entry. The player’s language is the one their framework tracks, the same one used for message localization.

The plain field (title, description, name, displayName) is not “the English text”. It is the fallback every player without a matching translation sees, in whatever language you wrote it. If you author your content in French, French is the fallback and an en entry is just another translation. Write the authored text in the language most of your players speak, then add translations for the rest.

Like message localization, partial coverage is fine. A missing or blank entry falls back per field, so you can translate the contracts that matter first and finish over time.

Presets carry a displayName that requirement chips show instead of the raw key, and it is the translatable part. The key stays the identifier that @ references resolve against, so renaming what players see never breaks your contracts. See Presets for the full shape.