Difference between revisions of "Modding:Modder Guide/APIs/Translation"
Pathoschild (talk | contribs) (→Reading translations: copyedit, remove unneeded bit, + strongly-typed API) |
m (Replace deprecated <source> tags with <syntaxhighlight> tags) |
||
Line 62: | Line 62: | ||
===File format=== | ===File format=== | ||
Each <tt>.json</tt> file should contain a flat key→value lookup with your default text. Each key is case-insensitive, and can contain alphanumeric, underscore, hyphen, and dot characters. Feel free to add JavaScript comments to organise or document your translations. For example: | Each <tt>.json</tt> file should contain a flat key→value lookup with your default text. Each key is case-insensitive, and can contain alphanumeric, underscore, hyphen, and dot characters. Feel free to add JavaScript comments to organise or document your translations. For example: | ||
− | < | + | <syntaxhighlight lang="javascript"> |
{ | { | ||
// example translations | // example translations | ||
Line 68: | Line 68: | ||
"item-type.fruit-tree": "{{fruitName}} tree", | "item-type.fruit-tree": "{{fruitName}} tree", | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
The <code><nowiki>{{fruitName}}</nowiki></code> in the above example is a token. You can add any tokens you want (each having only letters in the name), and replace them with a different value in code (see [[#Reading translations|''reading translations'']] below). | The <code><nowiki>{{fruitName}}</nowiki></code> in the above example is a token. You can add any tokens you want (each having only letters in the name), and replace them with a different value in code (see [[#Reading translations|''reading translations'']] below). | ||
Line 79: | Line 79: | ||
===Built-in API=== | ===Built-in API=== | ||
Once your i18n files are set up, you can read translations for the current locale: | Once your i18n files are set up, you can read translations for the current locale: | ||
− | < | + | <syntaxhighlight lang="c#"> |
// read a simple translation | // read a simple translation | ||
string label = helper.Translation.Get("item-type.label"); | string label = helper.Translation.Get("item-type.label"); | ||
Line 85: | Line 85: | ||
// read a translation which uses tokens (accepts an anonymous object, dictionary, or model) | // read a translation which uses tokens (accepts an anonymous object, dictionary, or model) | ||
string text = helper.Translation.Get("item-type.fruit-tree", new { fruitName = "apple" }); | string text = helper.Translation.Get("item-type.fruit-tree", new { fruitName = "apple" }); | ||
− | </ | + | </syntaxhighlight> |
The <tt>helper.Translate(…)</tt> method returns a fluent interface — you can keep calling methods on its return value to customise the translation. (See IntelliSense for a description of the available methods.) To get the text, just assign it to a string: | The <tt>helper.Translate(…)</tt> method returns a fluent interface — you can keep calling methods on its return value to customise the translation. (See IntelliSense for a description of the available methods.) To get the text, just assign it to a string: | ||
− | < | + | <syntaxhighlight lang="c#"> |
// use fluent chain | // use fluent chain | ||
string text = helper.Translation.Get(key).Tokens(tokens).Tokens(moreTokens).Default("missing translation?"); | string text = helper.Translation.Get(key).Tokens(tokens).Tokens(moreTokens).Default("missing translation?"); | ||
− | </ | + | </syntaxhighlight> |
===Strongly-typed API=== | ===Strongly-typed API=== | ||
Line 97: | Line 97: | ||
The optional [https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder#readme SMAPI mod translation class builder] package lets you generate a strongly-typed class to read translations like this instead: | The optional [https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder#readme SMAPI mod translation class builder] package lets you generate a strongly-typed class to read translations like this instead: | ||
− | < | + | <syntaxhighlight lang="c#"> |
string label = I18n.ItemType_Label(); | string label = I18n.ItemType_Label(); | ||
string text = I18n.ItemType_FruitTree(fruitName: "apple"); | string text = I18n.ItemType_FruitTree(fruitName: "apple"); | ||
− | </ | + | </syntaxhighlight> |
If a translation breaks, you'll know immediately since it won't compile. | If a translation breaks, you'll know immediately since it won't compile. |
Revision as of 18:27, 19 February 2021
- Get started
- Game fundamentals
- Test & troubleshoot
- Release
- API reference
- Basic SMAPI APIs:
- Advanced SMAPI APIs:
- Specific guides
The translation API lets you translate your mod, and include all languages in one release package for SMAPI to use automatically based on the game language.
Overview
SMAPI reads translations from files in your mod folder. When you request a translation, it will automatically handle locale fallback (e.g. if a translation isn't available in pt-BR.json, SMAPI will check pt.json and default.json). Translations can be a simple string, or they can include tokens to inject values into the translation.
i18n folder
File structure
SMAPI reads translations from JSON files in an i18n subfolder in your mod folder:
YourMod/ i18n/ default.json es.json pt.json manifest.json YourMod.dll
The default.json file includes the default text that will be shown if a more specific translation isn't available, and you create a separate file for each language. Each translation file should have one of these file names:
Language | File name |
---|---|
Chinese | zh.json |
French | fr.json |
German | de.json |
Hungarian | hu.json |
Italian | it.json |
Japanese | ja.json |
Korean | ko.json |
Portuguese | pt.json |
Russian | ru.json |
Spanish | es.json |
Turkish | tr.json |
File format
Each .json file should contain a flat key→value lookup with your default text. Each key is case-insensitive, and can contain alphanumeric, underscore, hyphen, and dot characters. Feel free to add JavaScript comments to organise or document your translations. For example:
{
// example translations
"item-type.label": "Item type",
"item-type.fruit-tree": "{{fruitName}} tree",
}
The {{fruitName}}
in the above example is a token. You can add any tokens you want (each having only letters in the name), and replace them with a different value in code (see reading translations below).
Tips for translators
- Save i18n files with UTF-8 encoding to avoid broken symbols in-game.
- Type
reload_i18n
into the SMAPI console and hit enter to reload translations without exiting the game. (If a mod internally cached a translation, it may not be updated.)
Reading translations
Built-in API
Once your i18n files are set up, you can read translations for the current locale:
// read a simple translation
string label = helper.Translation.Get("item-type.label");
// read a translation which uses tokens (accepts an anonymous object, dictionary, or model)
string text = helper.Translation.Get("item-type.fruit-tree", new { fruitName = "apple" });
The helper.Translate(…) method returns a fluent interface — you can keep calling methods on its return value to customise the translation. (See IntelliSense for a description of the available methods.) To get the text, just assign it to a string:
// use fluent chain
string text = helper.Translation.Get(key).Tokens(tokens).Tokens(moreTokens).Default("missing translation?");
Strongly-typed API
The built-in API doesn't have build-time validation. For example, if you set a fruitName
argument but the translation actually uses {{fruit}}
, you won't know until you test each translation in-game and see the broken message. That's fine for most mods, but can be an issue with larger mods that have hundreds of translations across many different UI flows.
The optional SMAPI mod translation class builder package lets you generate a strongly-typed class to read translations like this instead:
string label = I18n.ItemType_Label();
string text = I18n.ItemType_FruitTree(fruitName: "apple");
If a translation breaks, you'll know immediately since it won't compile.