Changes

Jump to navigation Jump to search
update for unique string IDs and {{ModId}}
Line 7: Line 7:  
==Add a custom language==
 
==Add a custom language==
 
===Data format===
 
===Data format===
You can add custom languages by editing the <tt>Data/AdditionalLanguages</tt> asset. Each entry consists of an object with these fields:
+
You can add custom languages by editing the <samp>Data/AdditionalLanguages</samp> asset. Each entry consists of an object with these fields:
    
{| class="wikitable"
 
{| class="wikitable"
Line 14: Line 14:  
! description
 
! description
 
|-
 
|-
| <tt>ID</tt>
+
| <samp>ID</samp>
| A unique ID value. This is not shown in-game.
+
| A [[Modding:Common data field types#Unique string ID|unique string ID]] for your language. This isn't shown in-game.
 
|-
 
|-
| <tt>LanguageCode</tt>
+
| <samp>LanguageCode</samp>
| The language code for this localization. This should ideally be a [[wikipedia:List of ISO 639-1 codes|ISO 639-1 code]]. You should avoid commas for compatibility with Content Patcher packs checking the <tt><nowiki>{{Language}}</nowiki></tt> token.
+
| The language code for this localization. This should ideally be an [[wikipedia:List of ISO 639-1 codes|ISO 639-1 code]], with only letters and hyphens. You should avoid commas for compatibility with Content Patcher packs checking the <samp><nowiki>{{Language}}</nowiki></samp> token.
 
|-
 
|-
| <tt>ButtonTexture</tt>
+
| <samp>ButtonTexture</samp>
 
| The asset name for a 174x78 pixel texture containing the button of the language for language selection menu. The top half of the sprite is the default state, while the bottom half is the hover state.
 
| The asset name for a 174x78 pixel texture containing the button of the language for language selection menu. The top half of the sprite is the default state, while the bottom half is the hover state.
 
|-
 
|-
| <tt>TimeFormat</tt>
+
| <samp>TimeFormat</samp>
 
| A string which describes the in-game time format, with tokens replaced by in-game values. For example, <code>[HOURS_12]:[MINUTES] [AM_PM]</code> would show <code>12:00 PM</code> at noon.
 
| A string which describes the in-game time format, with tokens replaced by in-game values. For example, <code>[HOURS_12]:[MINUTES] [AM_PM]</code> would show <code>12:00 PM</code> at noon.
   Line 34: Line 34:  
* <code>[AM_PM]</code>: the localized text for "am" or "pm" (taken from <code>Strings\\StringsFromCSFiles:DayTimeMoneyBox.cs.10370</code> and <code>DayTimeMoneyBox.cs.10371</code> respectively). The game shows "pm" between noon and 11:59pm inclusively; it shows "am" otherwise.
 
* <code>[AM_PM]</code>: the localized text for "am" or "pm" (taken from <code>Strings\\StringsFromCSFiles:DayTimeMoneyBox.cs.10370</code> and <code>DayTimeMoneyBox.cs.10371</code> respectively). The game shows "pm" between noon and 11:59pm inclusively; it shows "am" otherwise.
 
|-
 
|-
| <tt>ClockTimeFormat</tt>
+
| <samp>ClockTimeFormat</samp>
| A string which describes the in-game time format. Equivalent to <tt>TimeFormat</tt>, but used for the in-game clock.
+
| A string which describes the in-game time format. Equivalent to <samp>TimeFormat</samp>, but used for the in-game clock.
 
|-
 
|-
| <tt>ClockDateFormat</tt>
+
| <samp>ClockDateFormat</samp>
 
| A string which describes the in-game date format as shown in the in-game clock, with tokens replaced by in-game values. For example, <code>[DAY_OF_WEEK]. [DAY_OF_MONTH]</code> would show <code>Mon. 1</code>.
 
| A string which describes the in-game date format as shown in the in-game clock, with tokens replaced by in-game values. For example, <code>[DAY_OF_WEEK]. [DAY_OF_MONTH]</code> would show <code>Mon. 1</code>.
    
The valid tokens are:
 
The valid tokens are:
* <code>[DAY_OF_WEEK]</code>: the abbreviated day of week as returned by <code>Game1.shortDayDisplayNameFromDayOfSeason</code> (like <tt>Mon</tt> for Monday).
+
* <code>[DAY_OF_WEEK]</code>: the abbreviated day of week as returned by <code>Game1.shortDayDisplayNameFromDayOfSeason</code> (like <samp>Mon</samp> for Monday).
 
* <code>[DAY_OF_MONTH]</code>: the numerical day of the month.
 
* <code>[DAY_OF_MONTH]</code>: the numerical day of the month.
 
|-
 
|-
| <tt>UseLatinFont</tt>
+
| <samp>FontApplyYOffset</samp>
| Whether the language uses the game's default fonts. If set to <tt>false</tt>, you must set the <tt>FontFile</tt> field.
+
| ''(optional)'' Whether to shift the font up by four pixels (multiplied by the <samp>FontPixelZoom</samp>), to better align languages with larger characters like Chinese and Japanese. Default false.
 
|-
 
|-
| <tt>FontFile</tt>
+
| <samp>NumberComma</samp>
| ''(optional)'' The asset name for the font file to use (if <tt>UseLatinFont</tt> is <tt>false</tt>). See [[#Custom font|''custom font'']] below.
+
| ''(optional)'' The string to use as the thousands separator (like <code>","</code> for <code>5,000,000</code>). Defaults to a comma.
 
|-
 
|-
| <tt>FontPixelZoom</tt>
+
| <samp>SmallFontLineSpacing</samp>
| ''(optional)'' A factor by while to multiply the font size.
+
| ''(optional)'' The line spacing value used by <samp>smallFont</samp>. Defaults to 26.
 
|-
 
|-
| <tt>FontApplyYOffset</tt>
+
| <samp>UseGenderedCharacterTranslations</samp>
| ''(optional)'' Whether to shift the font up by four pixels (multiplied by the <tt>FontPixelZoom</tt>), to better align languages with larger characters like Chinese and Japanese.
  −
|-
  −
| <tt>NumberComma</tt>
  −
| ''(optional)'' The string to use as the thousands separator (e.g. <code>","</code> for <code>5,000,000</code>). Defaults to a comma.
  −
|-
  −
| <tt>SmallFontLineSpacing</tt>
  −
| ''(optional)'' The line spacing value used by <tt>smallFont</tt>. Defaults to 26.
  −
|-
  −
| <tt>UseGenderedCharacterTranslations</tt>
   
| ''(optional)'' Whether the social tab and gift log will use gender-specific translations (like the vanilla Portuguese language). Defaults to false.
 
| ''(optional)'' Whether the social tab and gift log will use gender-specific translations (like the vanilla Portuguese language). Defaults to false.
   −
Specifically, this affects the <tt>Strings\StringsFromCSFiles:SocialPage.cs.11635</tt> translation ("''(Single)''"). When enabled, it can contain male and female translations separated by <code>/</code>, like the vanilla Portuguese translation: "''(solteiro)/(solteira)''".
+
Specifically, this affects the <samp>Strings\StringsFromCSFiles:SocialPage.cs.11635</samp> translation ("''(Single)''"). When enabled, it can contain male and female translations separated by <code>/</code>, like the vanilla Portuguese translation: "''(solteiro)/(solteira)''".
 +
|-
 +
| ''custom font fields''
 +
| See ''[[#Add a custom font|add a custom font]]'' below.
 
|}
 
|}
    
===Example===
 
===Example===
This Content Patcher pack would add Esperanto to the game. '''You should change <tt>Pathoschild.Esperanto</tt> to your mod's actual ID.'''
+
This Content Patcher pack would add Esperanto to the game. (<code><nowiki>{{ModId}}</nowiki></code> is a token, which will be replaced with your mod ID automatically.)
 
{{#tag:syntaxhighlight|
 
{{#tag:syntaxhighlight|
 
{
 
{
     "Format": "{{Content Patcher version}}",
+
     "Format": "{{Content Patcher version}}",<nowiki>
 
     "Changes": [
 
     "Changes": [
 
         // define language
 
         // define language
Line 79: Line 73:  
             "Target": "Data/AdditionalLanguages",
 
             "Target": "Data/AdditionalLanguages",
 
             "Entries": {
 
             "Entries": {
                 "Pathoschild.Esperanto": { // for technical reasons, you need to specify the ID here *and* in the "ID" field
+
                 "{{ModId}}_Esperanto": { // for technical reasons, you need to specify the ID here *and* in the "ID" field
                     "ID": "Pathoschild.Esperanto",
+
                     "ID": "{{ModId}}_Esperanto",
 
                     "LanguageCode": "eo",
 
                     "LanguageCode": "eo",
                     "ButtonTexture": "Mods/Pathoschild.Esperanto/Button",
+
                     "ButtonTexture": "Mods/{{ModId}}/Button",
 
                     "UseLatinFont": true,
 
                     "UseLatinFont": true,
 
                     "TimeFormat": "[HOURS_24_00]:[MINUTES]",
 
                     "TimeFormat": "[HOURS_24_00]:[MINUTES]",
Line 94: Line 88:  
         {
 
         {
 
             "Action": "Load",
 
             "Action": "Load",
             "Target": "Mods/Pathoschild.Esperanto/Button",
+
             "Target": "Mods/{{ModId}}/Button",
 
             "FromFile": "assets/button.png"
 
             "FromFile": "assets/button.png"
 
         }
 
         }
 
     ]
 
     ]
}|lang=javascript}}
+
}</nowiki>|lang=javascript}}
    
Once the language is defined, you can add translations to the game by patching game assets like usual, and use the language code you specified above. For example:
 
Once the language is defined, you can add translations to the game by patching game assets like usual, and use the language code you specified above. For example:
Line 116: Line 110:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
==Custom font==
+
==Add a custom font==
If you set <tt>UseLatinFont: false</tt> in the language data, you can provide your own Bitmap font with the <tt>FontFile</tt> field. This lets you map arbitrary Unicode characters to sprites in your font texture. You can [[Modding:Editing XNB files#Unpack game files|unpack your game's <tt>Content</tt> folder]] and look at the Chinese, Japanese, Korean, and Russian fonts in the <tt>Fonts</tt> folder for some examples.
+
You can provide your own Bitmap font for your custom language, which maps arbitrary Unicode characters to sprites in your font texture. You can [[Modding:Editing XNB files#Unpack game files|unpack your game's <samp>Content</samp> folder]] and look at the Chinese, Japanese, Korean, and Russian fonts in the <samp>Fonts</samp> folder for examples of how this looks in practice.
 +
 
 +
===Data format===
 +
To enable a custom font, add these three extra fields to [[#Add a custom language|your <samp>Data/AdditionalLanguages</samp> entry]]:
 +
 
 +
{| class="wikitable"
 +
|-
 +
! field
 +
! description
 +
|-
 +
| <samp>UseLatinFont</samp>
 +
| Whether the language uses the game's default fonts. '''Set to <samp>false</samp>''' to enable a custom font.
 +
|-
 +
| <samp>FontFile</samp>
 +
| The asset name for your <samp>.fnt</samp> font data file (see [[#Font files|''font files'']] below). This must be the asset's name in the game's <samp>Content</samp> folder, not the file path in your content pack; see [[#Example 2|the example below]].
 +
|-
 +
| <samp>FontPixelZoom</samp>
 +
| A factor by which to multiply the font size. The recommended baseline is 1.5, but you can adjust it to make your text smaller or bigger in-game.
 +
|}
   −
For example, here's the <tt>Content/Fonts/Japanese.fnt</tt> file (with most of the characters stripped out for brevity):
+
===Font files===
 +
Note: if your language has a TrueType font available, you can use the [https://www.angelcode.com/products/bmfont/ Bitmap font generator] to generate these files from it.
   −
<syntaxhighlight lang="xml">
+
; Font data
 +
: Each font must have a text (XML) file with the <samp>.fnt</samp> extension which describes the font.
 +
 
 +
: For example, here's the <samp>Content/Fonts/Japanese.fnt</samp> file (with most of the characters stripped out for brevity):
 +
 
 +
: <syntaxhighlight lang="xml">
 
<?xml version="1.0"?>
 
<?xml version="1.0"?>
 
<font>
 
<font>
Line 138: Line 156:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
See the [https://www.angelcode.com/products/bmfont/doc/file_format.html official format documentation] to understand all the options, but at a high level:
+
: See the [https://www.angelcode.com/products/bmfont/doc/file_format.html official format documentation] to understand all the options, but at a high level:
   −
{| class="wikitable"
+
: {| class="wikitable"
 
|-
 
|-
 
! field
 
! field
 
! description
 
! description
 
|-
 
|-
| <tt>info</tt>
+
| <samp>info</samp>
 
| Describes the font itself: the name, TrueType size, padding and spacing, etc.
 
| Describes the font itself: the name, TrueType size, padding and spacing, etc.
 
|-
 
|-
| <tt>common</tt>
+
| <samp>common</samp>
 
| Provides common info which applies to all of the characters, like the line height.
 
| Provides common info which applies to all of the characters, like the line height.
 
|-
 
|-
| <tt>pages</tt>
+
| <samp>pages</samp>
| Lists the sprite textures that are part of the font. In the above example, the Japanese character sprites are split into two images: <tt>Japanese_0.png</tt> and <tt>Japanese_1.png</tt>. Each character in <tt>chars</tt> specifies which page it's on.
+
| Lists the sprite textures that are part of the font. In the above example, the Japanese character sprites are split into two images: <samp>Japanese_0.png</samp> and <samp>Japanese_1.png</samp>. Each character in <samp>chars</samp> specifies which page it's on.
 
|-
 
|-
| <tt>chars</tt>
+
| <samp>chars</samp>
| Maps each Unicode character you'll use in-game to the sprite font. The example above defines one character with these <tt>char</tt> fields:
+
| Maps each Unicode character you'll use in-game to the sprite font. The example above defines one character with these <samp>char</samp> fields:
   −
{| class="wikitable"
+
: {| class="wikitable"
 
|-
 
|-
 
! char attribute
 
! char attribute
 
! explanation
 
! explanation
 
|-
 
|-
| <tt>id</tt>
+
| <samp>id</samp>
| The decimal Unicode ID for the character (e.g. 37347 in the example above is 釣). You can search characters in [https://www.unicodepedia.com/unicode/cjk-unified-ideographs/91e3/cjk-unified-ideograph-91e3/ Unicodepedia] to find their Unicode IDs.
+
| The decimal Unicode ID for the character (''e.g.,'' 37347 in the example above is 釣). You can search characters in [https://www.unicodepedia.com/unicode/cjk-unified-ideographs/91e3/cjk-unified-ideograph-91e3/ Unicodepedia] to find their Unicode IDs.
 
|-
 
|-
| <tt>x</tt><br /><tt>y</tt><br /><tt>width</tt><br /><tt>height</tt>
+
| <samp>x</samp><br /><samp>y</samp><br /><samp>width</samp><br /><samp>height</samp>
 
| The top-left position and size of the character sprite in the page image, measured in pixels.
 
| The top-left position and size of the character sprite in the page image, measured in pixels.
 
|-
 
|-
| <tt>xoffset</tt><br /><tt>yoffset</tt>
+
| <samp>xoffset</samp><br /><samp>yoffset</samp>
 
| A pixel offset to apply to the character when it's drawn to the screen.
 
| A pixel offset to apply to the character when it's drawn to the screen.
 
|-
 
|-
| <tt>xadvance</tt>
+
| <samp>xadvance</samp>
 
| When drawing multiple characters to the screen, how many pixels drawing this character should advance the cursor.
 
| When drawing multiple characters to the screen, how many pixels drawing this character should advance the cursor.
 
|-
 
|-
| <tt>page</tt>
+
| <samp>page</samp>
| The ID of the page image which contains the sprite, as defined in the <tt>pages</tt> field.
+
| The ID of the page image which contains the sprite, as defined in the <samp>pages</samp> field.
 
|-
 
|-
| <tt>chnl</tt>
+
| <samp>chnl</samp>
| The color channel for which the sprite has data. (This is always <tt>15</tt> for all channels in Stardew Valley's current fonts.)
+
| The color channel for which the sprite has data. (This is always <samp>15</samp> for all channels in Stardew Valley's current fonts.)
 
|}
 
|}
 
|}
 
|}
 +
 +
; Font images
 +
: Each font also needs one or more images which contain the character sprites (white on a transparent background). The file names and sprite positions are defined in the above font data file.
 +
 +
: For example, here's the <samp>Content/Fonts/Japanese_0</samp> file (with a black background so the sprites are visible):
 +
: <div style="display: inline-block; background:#000;">[[File:Modding - example font texture.png|300px]]</div>
 +
 +
===Example===
 +
If you're using [[Modding:Content Patcher|Content Patcher]], your content pack should look something like this with the files described above:
 +
<pre>
 +
📁 Your Mod Name/
 +
  🗎 content.json
 +
  🗎 manifest.json
 +
  📁 assets/
 +
    🗎 YourLanguage.fnt
 +
    🗎 YourLanguage_0.png
 +
</pre>
 +
 +
Now you just need to make them available through the game's <samp>Content/Fonts</samp> folder. Make sure the target for the <samp>.fnt</samp> file matches what you specified via <samp>FontFile</samp> in the [[#Add a custom language|language data]], and the target for the image matches what you specified via <samp>pages</samp> in the [[#Font files|font data]].
 +
 +
For example, here's the previous Esperanto example with a custom font (note the <samp>UseLatinFont</samp> and <samp>FontFile</samp> fields in the language data, and the two new patches at the bottom):
 +
{{#tag:syntaxhighlight|
 +
{
 +
    "Format": "{{Content Patcher version}}",<nowiki>
 +
    "Changes": [
 +
        // define language
 +
        {
 +
            "Action": "EditData",
 +
            "Target": "Data/AdditionalLanguages",
 +
            "Entries": {
 +
                "{{ModId}}_Esperanto": { // for technical reasons, you need to specify the ID here *and* in the "ID" field
 +
                    "ID": "{{ModId}}_Esperanto",
 +
                    "LanguageCode": "eo",
 +
                    "ButtonTexture": "Mods/{{ModId}}/Button",
 +
                    "UseLatinFont": false,
 +
                    "FontFile": "Fonts/{{ModId}}/Esperanto",
 +
                    "TimeFormat": "[HOURS_24_00]:[MINUTES]",
 +
                    "ClockTimeFormat": "[HOURS_24_00]:[MINUTES]",
 +
                    "ClockDateFormat": "[DAY_OF_WEEK] [DAY_OF_MONTH]"
 +
                }
 +
            }
 +
        },
 +
 +
        // load button texture
 +
        {
 +
            "Action": "Load",
 +
            "Target": "Mods/{{ModId}}/Button",
 +
            "FromFile": "assets/button.png"
 +
        }
 +
 +
        // load font files
 +
        {
 +
            "Action": "Load",
 +
            "Target": "Fonts/{{ModId}}/Esperanto",
 +
            "FromFile": "assets/Esperanto.fnt"
 +
        },
 +
        {
 +
            "Action": "Load",
 +
            "Target": "Fonts/{{ModId}}/Esperanto_0",
 +
            "FromFile": "assets/Esperanto_0.png"
 +
        }
 +
    ]
 +
}</nowiki>|lang=javascript}}
 +
 +
==Limitations==
 +
Custom languages must be available very early in the game startup, and won't be handled correctly if they're added later. That means:
 +
* For Content Patcher packs, they must be added without <samp>When</samp> conditions (or only using immutable conditions like config or <samp>HasMod</samp>).
 +
* For C# mods, they should be added in [[Modding:Modder Guide/APIs/Events#Game loop|<samp>GameLaunched</samp>]] or earlier.
    
==See also==
 
==See also==
translators
8,437

edits

Navigation menu