Line 1: |
Line 1: |
| {{../../header}} | | {{../../header}} |
| | | |
− | ===Translation===
| + | 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. |
− | The translation API lets you translate your mod into the player's current language, accounting for locale fallback automatically (e.g. if a translation isn't available in <tt>pt-BR.json</tt>, SMAPI will check <tt>pt.json</tt> and <tt>default.json</tt>). Translations can be a simple string, or they can include tokens to inject values into the translation. | |
| | | |
− | <dl> | + | ==Overview== |
− | <dt>File structure</dt> | + | 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 <samp>pt-BR.json</samp>, SMAPI will check <samp>pt.json</samp> and <samp>default.json</samp>). Translations can be a simple string, or they can include tokens to inject values into the translation. |
− | <dd>SMAPI reads translations from JSON files in a structure like this: | + | |
| + | ==i18n folder== |
| + | ===File structure=== |
| + | SMAPI reads translations from JSON files in an <samp>i18n</samp> subfolder in your mod folder: |
| <pre> | | <pre> |
| YourMod/ | | YourMod/ |
Line 17: |
Line 19: |
| </pre> | | </pre> |
| | | |
− | The <tt>default.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: | + | The <samp>default.json</samp> 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: |
− | <source lang="javascript">
| |
− | {
| |
− | // example translations
| |
− | "item-type.label": "Item type",
| |
− | "item-type.fruit-tree": "{{fruitName}} tree",
| |
− | }
| |
− | </source>
| |
− | | |
− | You can then add translation files for each language you want to support, by copying the <tt>default.json</tt> file and translating its values. Each translation file should have one of these file names:
| |
| | | |
| {| class="wikitable" | | {| class="wikitable" |
Line 34: |
Line 27: |
| |- | | |- |
| | Chinese | | | Chinese |
− | | <tt>zh.json</tt> | + | | <samp>zh.json</samp> |
| + | |- |
| + | | French |
| + | | <samp>fr.json</samp> |
| |- | | |- |
| | German | | | German |
− | | <tt>de.json</tt> | + | | <samp>de.json</samp> |
| + | |- |
| + | | Hungarian |
| + | | <samp>hu.json</samp> |
| + | |- |
| + | | Italian |
| + | | <samp>it.json</samp> |
| |- | | |- |
| | Japanese | | | Japanese |
− | | <tt>ja.json</tt> | + | | <samp>ja.json</samp> |
| + | |- |
| + | | Korean |
| + | | <samp>ko.json</samp> |
| |- | | |- |
| | Portuguese | | | Portuguese |
− | | <tt>pt.json</tt> | + | | <samp>pt.json</samp> |
| |- | | |- |
| | Russian | | | Russian |
− | | <tt>ru.json</tt> | + | | <samp>ru.json</samp> |
| |- | | |- |
| | Spanish | | | Spanish |
− | | <tt>es.json</tt> | + | | <samp>es.json</samp> |
− | |}</dd> | + | |- |
| + | | Turkish |
| + | | <samp>tr.json</samp> |
| + | |} |
| | | |
− | <dt>Reading translations</dt> | + | For a [[Modding:Custom languages|custom language]], use its <samp>LanguageCode</samp> value in the filename. |
− | <dd>Once your i18n files are set up, you can read translations for the current locale: | + | |
− | <source lang="c#"> | + | ===File format=== |
| + | Each <samp>.json</samp> 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 |
| + | "item-type.label": "Item type", |
| + | "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). |
| + | |
| + | ===Tips for translators=== |
| + | * Save i18n files with UTF-8 encoding to avoid broken symbols in-game. |
| + | * Type <code>reload_i18n</code> 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: |
| + | <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 60: |
Line 87: |
| // 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" }); |
− | </source> | + | </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, simply assign it to a string: | + | The <samp>helper.Translate(…)</samp> 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: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| // use fluent chain | | // use fluent chain |
− | string text = helper.Translate(key).Tokens(tokens).Tokens(moreTokens).Assert(); | + | string text = helper.Translation.Get(key).Tokens(tokens).Tokens(moreTokens).Default("missing translation?"); |
− | </source> | + | </syntaxhighlight> |
| + | |
| + | ===Strongly-typed API=== |
| + | The built-in API doesn't have build-time validation. For example, if you set a <code>fruitName</code> argument but the translation actually uses <code><nowiki>{{fruit}}</nowiki></code>, 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 [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 text = I18n.ItemType_FruitTree(fruitName: "apple"); |
| + | </syntaxhighlight> |
| | | |
− | If your code has a lot of translation calls, you can make it less verbose by aliasing the translation helper: | + | If a translation breaks, you'll know immediately since it won't compile. |
− | <source lang="c#">
| |
− | var i18n = helper.Translation;
| |
| | | |
− | i18n.Get("item-type.fruit-tree", new { fruitName = i18n.Get("apple") });
| + | ==See also== |
− | </source></dd>
| + | [https://gist.github.com/Pathoschild/4e0af42158583983a4206d4d734bfc0b LINQ script] to format an event for translations |
| | | |
− | <dt>Tips for translators</dt>
| + | [[zh:模组:制作指南/APIs/Translation]] |
− | <dd>
| |
− | * Save i18n files with UTF-8 encoding to avoid broken symbols in-game.
| |
− | * Type <code>reload_i18n</code> 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.)
| |
− | </dd>
| |
− | </dl>
| |