Character Vault
Any Concept / Any System
Compendium
Your System Come To Life
Roll20 for Android
Streamlined for your Tablet
Roll20 for iPad
Streamlined for your Tablet

Personal tools

Difference between revisions of "Character Sheet i18n"

From Roll20 Wiki

Jump to: navigation, search
(formatting)
Line 5: Line 5:
  
 
Character Sheet i18n, or internationalization, will allow you to design your [[Building Character Sheets|character sheet]] in such a way that our community of translators will be able to translate your sheet into their language, making that language available to anyone on Roll20. If your sheet has been translated into another language, say French, and someone using your sheet has their account language set to French your sheet will show up with the translated texts for that user.
 
Character Sheet i18n, or internationalization, will allow you to design your [[Building Character Sheets|character sheet]] in such a way that our community of translators will be able to translate your sheet into their language, making that language available to anyone on Roll20. If your sheet has been translated into another language, say French, and someone using your sheet has their account language set to French your sheet will show up with the translated texts for that user.
 +
 +
Some of the advanced translation-methods uses [[Sheet Worker Scripts|sheetworkers]].
  
 
== Designing Your Sheet ==
 
== Designing Your Sheet ==
Setting up your [[Building Character Sheets|sheet]] to be able to respond to the i18n service is very simple, it just can take some time, especially if you are working with a large pre-existing sheet. There are 2 steps you must take to allow your sheet to be translated. Step one, mark the elements that you want to contain the translated text. Step two, create a translations file that contains all of the strings that will be translated. This file will be fed to our translation service, Crowdin, where our volunteers can go string-by-string and translate them into another language. This will generate the same translation file in all of the other languages, which we will then use accordingly when someone has that language selected on Roll20.
+
Setting up your [[Building Character Sheets|sheet]] to be able to respond to the <code>i18n</code> service is very simple, it just can take some time, especially if you are working with a large, pre-existing sheet. There are 2 steps you must take to allow your sheet to be translated.
 +
 
 +
* '''Step one''', mark the elements that you want to contain the translated text.
 +
* '''Step two''', create a translations file that contains all of the strings that will be translated.
 +
 
 +
This file will be fed to our translation service, [https://crowdin.com/ CrowdIn], where our volunteers can go string-by-string and translate them into another language. This will generate the same translation file in all of the other languages, which we will then use accordingly when someone has that language selected on Roll20.
  
Since creating one single design that can fit multiple sizes of translated text can be hard, we've added a class to the parent "charsheet" element to let you have separate css for individual languages. The class is "lang-[2 char language code]", so if the language is English "lang-en" or French "lang-fr". This will allow you, after your sheet has been translated, to change a style, specifically for one language, if the new text for that language doesn't fit your current design.
+
Since creating one single design that can fit multiple sizes of translated text can be hard, we've added a class to the parent <code>.charsheet</code> element to let you have separate css for individual languages. The class is <code>lang-[2 char language code]</code>, so if the language is English(<code>lang-en</code>) or French(<code>lang-fr</code>). This will allow you, after your sheet has been translated, to change a style, specifically for one language, if the new text for that language doesn't fit your current design.
  
 
=== Step One, Sheet Formatting ===
 
=== Step One, Sheet Formatting ===
You will want to add the "data-i18n=[unique-key]" attribute to every element that has text in it that you want translated. Keep all of the text that is already there in the element, because it can be used to generate your translation file for you when you are done (explained later). '''It is especially important that the text stays there while the sheet-i18n code is only on Dev, otherwise your sheet will no longer work on the main site until sheet-i18n makes it to main.''' If you want further examples of how the i18n translations work, check out the [https://github.com/Roll20/roll20-character-sheets/tree/master/5th%20Edition%20OGL%20by%20Roll20 5th Edition OGL by Roll20] sheet which has already been formatted with the i18n text.
+
You will want to add the <code>data-i18n=[unique-key]</code>-attribute to every element that has text in it that you want translated. Keep all of the text that is already there in the element, because it can be used to generate your translation file for you when you are done (explained later). '''It is especially important that the text stays there while the sheet-i18n code is only on Dev, otherwise your sheet will no longer work on the main site until sheet-i18n makes it to main.''' If you want further examples of how the i18n translations work, check out the [https://github.com/Roll20/roll20-character-sheets/tree/master/5th%20Edition%20OGL%20by%20Roll20 5th Edition OGL by Roll20] sheet which has already been formatted with the i18n text.
  
 
====Standard Text====
 
====Standard Text====
Line 43: Line 50:
 
</div>
 
</div>
 
</pre>
 
</pre>
Notice how every span in this code block has the data-i18n attribute with a key that references the text contained in the span. Also notice that there is HTML within the span, this is totally fine. The HTML will be marked by Crowdin so it is easy for them to copy and paste the HTML into their translation. While HTML is okay inside of the translation, try and keep it as simple as possible because the translators are most likely not code-savvy and may have issues copying the code if it's not simple. It is very important that every unique string has a unique key; notice that since this text is in all caps I'm using a -u at the end of each key. That is because these words show up later in the sheet, but are cased normally.
+
Notice how every <code><nowiki><span></nowiki></code> in this code-block has the <code>data-i18n</code>-attribute with a key that references the text contained in the <code><nowiki><span></nowiki></code>. Also notice that there is HTML within the <code><nowiki><span></nowiki></code>, this is totally fine. The HTML will be marked by Crowdin so it is easy for them to copy and paste the HTML into their translation. While HTML is okay inside of the translation, try and keep it as simple as possible because the translators are most likely not code-savvy and may have issues copying the code if it's not simple. It is very important that every unique string has a unique key; notice that since this text is in all caps I'm using a -u at the end of each key. That is because these words show up later in the sheet, but are cased normally.
  
 
There are some cases where you will want to add extra context to the key that the translator might not get from the the text alone, like with abbreviations.
 
There are some cases where you will want to add extra context to the key that the translator might not get from the the text alone, like with abbreviations.
Line 58: Line 65:
 
</div>
 
</div>
 
</pre>
 
</pre>
Notice the keys for the V, S, and M texts spells out exactly what the single character is referring to. Otherwise the translator would have no idea what "V" means. This way the translator can change V to the first letter of whatever "Verbal" is in their language, if it is different.
+
Notice the keys for the <code>V</code>, <code>S</code>, and <code>M</code>-texts spells out exactly what the single character is referring to. Otherwise the translator would have no idea what <code>V</code> means. This way the translator can change <code>V</code> to the first letter of whatever <code>Verbal</code> is in their language, if it is different.
  
 
====Element Attribute Text====
 
====Element Attribute Text====
Many times you will have text inside of an element attribute that needs translated, for example placeholder and titles. To replace the text in these attributes you can use "data-i18n-[attribute]='key'" to tell the parser that the key you are supplying is to be used to replace the text within the given attribute. For example if you were replacing the text in a placeholder attribute:
+
Many times you will have text inside of an element attribute that needs translated, for example placeholder and titles. To replace the text in these attributes you can use <code>data-i18n-[attribute]='key'</code> to tell the parser that the key you are supplying is to be used to replace the text within the given attribute. For example if you were replacing the text in a placeholder attribute:
  
 
<pre data-language="html"><input name="attr_weapon_name" data-i18n-placeholder="weapon-name" placeholder="Hand Axe" />
 
<pre data-language="html"><input name="attr_weapon_name" data-i18n-placeholder="weapon-name" placeholder="Hand Axe" />
 
</pre>
 
</pre>
  
The supported attributes are: title, alt, aria-label, label, placeholder
+
The supported attributes are: <code>title</code>, <code>alt</code>, <code>aria-label</code>, <code>label</code>, <code>placeholder</code>
  
 
====Variable Replacement====
 
====Variable Replacement====
One special case is if you have text within a string that you do not want translated you can use special variables and the "data-i18n-vars" attribute.
+
One special case is if you have text within a string that you do not want translated you can use special variables and the <code>data-i18n-vars</code>-attribute.
 
<pre data-language="html">
 
<pre data-language="html">
 
<span data-i18n="var-test" data-i18n-vars="will not|be translated">This text {{0}} {{1}}</span>
 
<span data-i18n="var-test" data-i18n-vars="will not|be translated">This text {{0}} {{1}}</span>
 
</pre>
 
</pre>
The text inside of the data-i18n-vars attribute will be divided by the "|" character and placed in an array. You can then reference that array using {{[index]}} to reference the specific row of the array.
+
The text inside of the data-i18n-vars attribute will be divided by the <code>|</code> character and placed in an array. You can then reference that array using <code>{{[index]}}</code> to reference the specific row of the array.
  
 
====Dynamic Key Replacement====
 
====Dynamic Key Replacement====
There are some situations where you need to fetch a translation based off of a changing key. For example, you have a "class selection" dropdown menu where the user can select their class. The class names that show up in this list can be translated using the normal tools. But, you cannot translate the value inside of the option in the select box. (we do this so that if you change languages, the sheet data doesn't have to be translated. The translation system stays purely in the front-end and not in the data layer) Instead, where you want to display this class name, translated, you mark the span with the attribute "data-i18n-dynamic". This will tell the parser to use the value that would normally show up in the span as the key to fetch a translation.
+
There are some situations where you need to fetch a translation based off of a changing key. For example, you have a "class selection" dropdown menu where the user can select their class. The class names that show up in this list can be translated using the normal tools. But, you cannot translate the value inside of the option in the select box. (we do this so that if you change languages, the sheet data doesn't have to be translated. The translation system stays purely in the front-end and not in the data layer) Instead, where you want to display this class name, translated, you mark the span with the attribute <code>data-i18n-dynamic</code>. This will tell the parser to use the value that would normally show up in the span as the key to fetch a translation.
  
 
<pre data-language="html" style="overflow:auto; width:auto;">
 
<pre data-language="html" style="overflow:auto; width:auto;">
Line 97: Line 104:
 
</pre>
 
</pre>
  
Now, with the above code, when you select "Bárbaro" from the select box, "Barbarian" will be saved under attr_class. But when the sheet refreshes and the span with name="attr_class" would normally be filled in with "Barbarian", instead "Barbarian" will be used as the key to reference the translation JSON and display "Bárbaro" instead.
+
Now, with the above code, when you select "Bárbaro" from the select box, "Barbarian" will be saved under attr_class. But when the sheet refreshes and the span with <code>name="attr_class"</code> would normally be filled in with "Barbarian", instead "Barbarian" will be used as the key to reference the translation JSON and display "Bárbaro" instead.
  
 
====Roll Queries====
 
====Roll Queries====
Line 120: Line 127:
 
</pre>
 
</pre>
  
The <nowiki><span></nowiki> tag will not appear, but it will cause the key "ask-bonus" to be included in the generated translation file. The script will create an attribute called "bonusmacro" with the value "What is your bonus?", and the roll button will use that value to send the command, <nowiki>"[[d20 + ?{What is your bonus?|0}]]"</nowiki>. If the sheet is opened while another translation file is active, it will set the attribute to the new translated value for "bonusmacro". This technique can be used to substitute individual keys, or a single attribute can be used to store a complex query, with the Sheet Worker Script inserting translated keys into the query code as needed.
+
The <code><nowiki><span></nowiki></code> tag will not appear, but it will cause the key <code>ask-bonus</code> to be included in the generated translation file. The script will create an attribute called <code>bonusmacro</code> with the value <code>What is your bonus?</code>, and the roll button will use that value to send the command, <code><nowiki>"[[d20 + ?{What is your bonus?|0}]]"</nowiki></code>. If the sheet is opened while another translation file is active, it will set the attribute to the new translated value for <code>bonusmacro</code>. This technique can be used to substitute individual keys, or a single attribute can be used to store a complex query, with the Sheet Worker Script inserting translated keys into the query code as needed.
  
 
<pre data-language="javascript" style="overflow:auto; width:auto;">
 
<pre data-language="javascript" style="overflow:auto; width:auto;">
Line 148: Line 155:
  
 
====Roll Templates====
 
====Roll Templates====
Static HTML inside of the sheet's roll template definition can use all of the above tools, including Dynamic Keys. If it is in the sheet's rolltemplate definition, code like <span data-i18n-dynamic><nowiki>{{strength-key}}</nowiki></span> will give the translated value of whatever is the value of <nowiki>{{strength-key}}</nowiki>. If you would like to send dynamically translated values inside of a roll function, use the tool below.
+
Static HTML inside of the sheet's roll template definition can use all of the above tools, including Dynamic Keys. If it is in the sheet's rolltemplate definition, code like <code><span data-i18n-dynamic><nowiki>{{strength-key}}</nowiki></span></code> will give the translated value of whatever is the value of <code><nowiki>{{strength-key}}</nowiki></code>. If you would like to send dynamically translated values inside of a roll function, use the tool below.
  
Inside of a roll function, using a roll template, you can wrap any text in "^{ [key] }" to denote the text as a translation key. This text will be replaced with the translated version associated with that key. This step happens after character sheet values are swapped in, so "^{ @{ability_key} }" where ability_key = STRENGTH will be parsed as "^{STRENGTH}" and will display whatever translation value you have for "STRENGTH". This also works on the key, for the key/value pairs used in the allprops() function. For example: "{{^{TEXT}=@{content} }}" will have the translation value for "TEXT" as "<nowiki>{{key}}</nowiki>" inside of the allprops() function.
+
Inside of a roll function, using a roll template, you can wrap any text in <code>^{ [key] }</code> to denote the text as a translation key. This text will be replaced with the translated version associated with that key. This step happens after character sheet values are swapped in, so <code>^{ @{ability_key} }</code> where <code>ability_key = STRENGTH</code> will be parsed as <code>^{STRENGTH}</code> and will display whatever translation value you have for "STRENGTH". This also works on the key, for the key/value pairs used in the <code>allprops()</code> function. '''Example:''' <code>{{^{TEXT}=@{content} }}</code> will have the translation value for <code>TEXT</code> as <code><nowiki>{{key}}</nowiki></code> inside of the <code>allprops()</code> function.
  
 
====List Ordering====
 
====List Ordering====
Line 156: Line 163:
  
 
Identify the container. All list items must be inside of the container, or they will be ignored:
 
Identify the container. All list items must be inside of the container, or they will be ignored:
  <nowiki>data-i18n-list="list-key"</nowiki>
+
  <code><nowiki>data-i18n-list="list-key"</nowiki></code>
  
 
Identify the individual items in the list. These items can be a block of HTML. When they are reordered, the whole block will be moved.
 
Identify the individual items in the list. These items can be a block of HTML. When they are reordered, the whole block will be moved.
  <nowiki>data-i18n-list-item="item-key"</nowiki>
+
  <code><nowiki>data-i18n-list-item="item-key"</nowiki></code>
  
 
(Optional) The "starting" order of the list is assumed to be read top-down in the HTML. If your order is different, you can manually number the items (starting at 1) to set the order.
 
(Optional) The "starting" order of the list is assumed to be read top-down in the HTML. If your order is different, you can manually number the items (starting at 1) to set the order.
  <nowiki>data-i18n-list-item-num="1"</nowiki>
+
  <code><nowiki>data-i18n-list-item-num="1"</nowiki></code>
  
 
(Special) This is not a tag that you will "use", it is a tag that will be created if there is an error with your formatting. If there is an error with your List Order code, the list will be ignored and this tag will be created on the list container element with the error. If you are not seeing the list be reordered, like you expect, check the parent element for an error.
 
(Special) This is not a tag that you will "use", it is a tag that will be created if there is an error with your formatting. If there is an error with your List Order code, the list will be ignored and this tag will be created on the list container element with the error. If you are not seeing the list be reordered, like you expect, check the parent element for an error.
  <nowiki>data-i18n-error="Error Message"</nowiki>
+
  <code><nowiki>data-i18n-error="Error Message"</nowiki></code>
  
 
Add the key to your translation file. This will contain a comma-delimited list of all of the item keys, in the language of the original translation. Reordering these keys will reorder the items. The list key can be used for several lists, if all of the items being sorted are the "same" items. The HTML doesn't have to be the same, only the words that are being sorted. For example, a list of "Skills" in D&D may show up in several places on a sheet with totally different HTML but they can all use the same list key. I will use the example code below so generate my list key.
 
Add the key to your translation file. This will contain a comma-delimited list of all of the item keys, in the language of the original translation. Reordering these keys will reorder the items. The list key can be used for several lists, if all of the items being sorted are the "same" items. The HTML doesn't have to be the same, only the words that are being sorted. For example, a list of "Skills" in D&D may show up in several places on a sheet with totally different HTML but they can all use the same list key. I will use the example code below so generate my list key.
  <nowiki>"skills-list":"acrobatics,arcana,sleight-of-hand,survival"</nowiki>
+
  <code><nowiki>"skills-list":"acrobatics,arcana,sleight-of-hand,survival"</nowiki></code>
  
 
To reorder this list for the Czech language, the list key value would look like:
 
To reorder this list for the Czech language, the list key value would look like:
  <nowiki>"skills-list":"acrobatics,sleight-of-hand,arcana,survival"</nowiki>
+
  <code><nowiki>"skills-list":"acrobatics,sleight-of-hand,arcana,survival"</nowiki></code>
  
 
Example:
 
Example:
Line 226: Line 233:
  
 
==Step Two, Generating the Translation File==
 
==Step Two, Generating the Translation File==
Once you have formatted your sheet, following the instructions above, you can very easily create your translation file. After loading your sheet within the editor,''' go into the game,''' and there open the Developer Tools for your browser and go to the console. At the very bottom of the console, where you can enter code, type "i18nOutput". (it will most likely start to auto-complete once you start typing) Hit enter to retrieve this object, and it will dump a JSON string containing all of the keys you have used with the corresponding text from within the element/placeholder. You should be able to then copy this text into your favorite JSON formatter (optional, but recommended)  and then into the "Translation" tab in the Game Settings Custom Sheet area.
+
Once you have formatted your sheet, following the instructions above, you can very easily create your translation file. After loading your sheet within the editor,''' go into the game,''' and there open the Developer Tools for your browser and go to the console. At the very bottom of the console, where you can enter code, type <code>i18nOutput</code>. (it will most likely start to auto-complete once you start typing) Hit enter to retrieve this object, and it will dump a JSON string containing all of the keys you have used with the corresponding text from within the element/placeholder. You should be able to then copy this text into your favorite JSON formatter (optional, but recommended)  and then into the "Translation" tab in the Game Settings Custom Sheet area.
  
 
If you wish to create the translation.json manually, you can use the following format:
 
If you wish to create the translation.json manually, you can use the following format:
Line 240: Line 247:
 
If you see any red text like [translation-key] this is a key that you are telling the system to use, but have not entered into the translation JSON. If you use the JSON generated by the system, you should not see any of these.
 
If you see any red text like [translation-key] this is a key that you are telling the system to use, but have not entered into the translation JSON. If you use the JSON generated by the system, you should not see any of these.
  
Once you have a working translation file, you can upload this with the rest of your sheet code to the root of your character sheet folder as "translation.json". This will automatically be picked up by our system and uploaded to Crowdin, where it can then be translated.
+
Once you have a working translation file, you can upload this with the rest of your sheet code to the root of your character sheet folder as <code>translation.json</code>. This will automatically be picked up by our system and uploaded to Crowdin, where it can then be translated.
  
 
== Changing your Translation ==
 
== Changing your Translation ==
If you wish to change your translation after it has already been generated, you can edit your translation.json file, and the changes will be propagated to the language files the next time translations are updated. However, certain changes will not cause translation values to be overwritten, even if the values haven't been translated yet. In other words, sometimes changes to the translation.json will not show up in your sheet.
+
If you wish to change your translation after it has already been generated, you can edit your <code>translation.json</code> file, and the changes will be propagated to the language files the next time translations are updated. However, certain changes will not cause translation values to be overwritten, even if the values haven't been translated yet. In other words, sometimes changes to the <code>translation.json</code> will not show up in your sheet.
  
 
Changes that will not trigger an update include changes where the only difference is white space or special characters, and changes that only remove characters and do not change or add new characters.
 
Changes that will not trigger an update include changes where the only difference is white space or special characters, and changes that only remove characters and do not change or add new characters.
Line 252: Line 259:
  
 
== See Also ==
 
== See Also ==
 +
* [[Sheet Worker Scripts|Sheetworkers]]
 
* [https://github.com/Anduh/acsi ACSI - Automated Character Sheet Internationalizer] A linux tool that can save some time while inserting and creating the language tags
 
* [https://github.com/Anduh/acsi ACSI - Automated Character Sheet Internationalizer] A linux tool that can save some time while inserting and creating the language tags
 
<br />
 
<br />
Line 257: Line 265:
  
 
[[Category:Docs]]
 
[[Category:Docs]]
 +
[[Category:Sheetworker]]
 
[[Category:Character Sheet Creation]]
 
[[Category:Character Sheet Creation]]

Revision as of 14:30, 4 April 2020

Attention: Roll20 is no longer maintaining this document on the community wiki. For the most up-to-date information please visit this page on our Help Center for assistance: Here. For more information you can email us at Team@roll20.net

Character Sheet i18n, or internationalization, will allow you to design your character sheet in such a way that our community of translators will be able to translate your sheet into their language, making that language available to anyone on Roll20. If your sheet has been translated into another language, say French, and someone using your sheet has their account language set to French your sheet will show up with the translated texts for that user.

Some of the advanced translation-methods uses sheetworkers.

Contents

Designing Your Sheet

Setting up your sheet to be able to respond to the i18n service is very simple, it just can take some time, especially if you are working with a large, pre-existing sheet. There are 2 steps you must take to allow your sheet to be translated.

  • Step one, mark the elements that you want to contain the translated text.
  • Step two, create a translations file that contains all of the strings that will be translated.

This file will be fed to our translation service, CrowdIn, where our volunteers can go string-by-string and translate them into another language. This will generate the same translation file in all of the other languages, which we will then use accordingly when someone has that language selected on Roll20.

Since creating one single design that can fit multiple sizes of translated text can be hard, we've added a class to the parent .charsheet element to let you have separate css for individual languages. The class is lang-[2 char language code], so if the language is English(lang-en) or French(lang-fr). This will allow you, after your sheet has been translated, to change a style, specifically for one language, if the new text for that language doesn't fit your current design.

Step One, Sheet Formatting

You will want to add the data-i18n=[unique-key]-attribute to every element that has text in it that you want translated. Keep all of the text that is already there in the element, because it can be used to generate your translation file for you when you are done (explained later). It is especially important that the text stays there while the sheet-i18n code is only on Dev, otherwise your sheet will no longer work on the main site until sheet-i18n makes it to main. If you want further examples of how the i18n translations work, check out the 5th Edition OGL by Roll20 sheet which has already been formatted with the i18n text.

Standard Text

<div class="col">
    <div class="row">
        <span data-i18n="acrobatics-u">ACROBATICS <span>(Dex)</span></span>
        <input class="num" type="text" name="attr_npc_acrobatics" placeholder="0">
    </div>
    <div class="row">
        <span data-i18n="animal-handling-u">ANIMAL HANDLING</span>
        <input class="num" type="text" name="attr_npc_animal_handling" placeholder="0">
    </div>
    <div class="row">
        <span data-i18n="arcana-u">ARCANA</span>
        <input class="num" type="text" name="attr_npc_arcana" placeholder="0">
    </div>
    <div class="row">
        <span data-i18n="athletics-u">ATHLETICS</span>
        <input class="num" type="text" name="attr_npc_athletics" placeholder="0">
    </div>
    <div class="row">
        <span data-i18n="deception-u">DECEPTION</span>
        <input class="num" type="text" name="attr_npc_deception" placeholder="0">
    </div>
    <div class="row">
        <span data-i18n="history-u">HISTORY</span>
        <input class="num" type="text" name="attr_npc_history" placeholder="0">
    </div>
</div>

Notice how every <span> in this code-block has the data-i18n-attribute with a key that references the text contained in the <span>. Also notice that there is HTML within the <span>, this is totally fine. The HTML will be marked by Crowdin so it is easy for them to copy and paste the HTML into their translation. While HTML is okay inside of the translation, try and keep it as simple as possible because the translators are most likely not code-savvy and may have issues copying the code if it's not simple. It is very important that every unique string has a unique key; notice that since this text is in all caps I'm using a -u at the end of each key. That is because these words show up later in the sheet, but are cased normally.

There are some cases where you will want to add extra context to the key that the translator might not get from the the text alone, like with abbreviations.

<div class="row">
    <span data-i18n="components:-u">COMPONENTS:</span>
    <input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked">
    <span data-i18n="spell-component-verbal">V</span>
    <input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked">
    <span data-i18n="spell-component-somatic">S</span>
    <input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked">
    <span data-i18n="spell-component-material">M</span>
    <input type="text" name="attr_spellcomp_materials" accept="Material" style="margin-left: 17px; width: 215px;" placeholder="ruby dust worth 50gp" data-i18n-place="ruby-dust-place">
</div>

Notice the keys for the V, S, and M-texts spells out exactly what the single character is referring to. Otherwise the translator would have no idea what V means. This way the translator can change V to the first letter of whatever Verbal is in their language, if it is different.

Element Attribute Text

Many times you will have text inside of an element attribute that needs translated, for example placeholder and titles. To replace the text in these attributes you can use data-i18n-[attribute]='key' to tell the parser that the key you are supplying is to be used to replace the text within the given attribute. For example if you were replacing the text in a placeholder attribute:

<input name="attr_weapon_name" data-i18n-placeholder="weapon-name" placeholder="Hand Axe" />

The supported attributes are: title, alt, aria-label, label, placeholder

Variable Replacement

One special case is if you have text within a string that you do not want translated you can use special variables and the data-i18n-vars-attribute.

<span data-i18n="var-test" data-i18n-vars="will not|be translated">This text {{0}} {{1}}</span>

The text inside of the data-i18n-vars attribute will be divided by the | character and placed in an array. You can then reference that array using {{[index]}} to reference the specific row of the array.

Dynamic Key Replacement

There are some situations where you need to fetch a translation based off of a changing key. For example, you have a "class selection" dropdown menu where the user can select their class. The class names that show up in this list can be translated using the normal tools. But, you cannot translate the value inside of the option in the select box. (we do this so that if you change languages, the sheet data doesn't have to be translated. The translation system stays purely in the front-end and not in the data layer) Instead, where you want to display this class name, translated, you mark the span with the attribute data-i18n-dynamic. This will tell the parser to use the value that would normally show up in the span as the key to fetch a translation.

// This part doesn't have to change
<select class="hiding" name="attr_class" style="width: 64px;">
	<option value="Barbarian" data-i18n="barbarian">Bárbaro</option>
	<option value="Bard" data-i18n="bard">Bardo</option>
	<option value="Cleric" data-i18n="cleric">Clerigo</option>
	<option value="Druid" data-i18n="druid">Druida</option>
</select>
...
<span data-i18n="class-options">Class Options</span> (<span name="attr_class" data-i18n-dynamic></span>)
...
{
	"Barbarian":"Bárbaro",
	"Bard":"Bardo",
	"Cleric":"Clerigo",
	"Druid":"Druida"
}

Now, with the above code, when you select "Bárbaro" from the select box, "Barbarian" will be saved under attr_class. But when the sheet refreshes and the span with name="attr_class" would normally be filled in with "Barbarian", instead "Barbarian" will be used as the key to reference the translation JSON and display "Bárbaro" instead.

Roll Queries

Text in roll queries cannot be translated in the character sheet HTML, however using Sheet Worker Scripts and attributes, the text can be translated in JavaScript and substituted into the roll query.

<script type="text/worker">

on("sheet:opened", function(eventInfo){
    
    setAttrs({
        bonusmacro: getTranslationByKey("ask-bonus")
    });     
    
});

</script>
...
<span style="display: none" data-i18n="ask-bonus">What is your bonus?</span>
<button type='roll' value='[[d20 + ?{@{bonusmacro}|0}]]' ></button>

The <span> tag will not appear, but it will cause the key ask-bonus to be included in the generated translation file. The script will create an attribute called bonusmacro with the value What is your bonus?, and the roll button will use that value to send the command, "[[d20 + ?{What is your bonus?|0}]]". If the sheet is opened while another translation file is active, it will set the attribute to the new translated value for bonusmacro. This technique can be used to substitute individual keys, or a single attribute can be used to store a complex query, with the Sheet Worker Script inserting translated keys into the query code as needed.

<script type="text/worker">
on("sheet:opened", function(eventInfo){
    
    setAttrs({
        rollquery: "?{" + getTranslationByKey("advantageordisadvantage") +"|" + getTranslationByKey("neither") + ",d20|" + getTranslationByKey("advantage") + ",2d20kh1[" + getTranslationByKey("withadvantage") + "]|" + getTranslationByKey("disadvantage") + ",2d20kl1[" + getTranslationByKey("withdisadvantage") + "]}",
    });
    
});

</script>
...
<span style="display: none" data-i18n="advantageordisadvantage">Are you rolling with Advantage or Disadvantage?</span>
<span style="display: none" data-i18n="neither">Neither</span>
<span style="display: none" data-i18n="advantage">Advantage</span>
<span style="display: none" data-i18n="withadvantage">with Advantage</span>
<span style="display: none" data-i18n="disadvantage">Disadvantage</span>
<span style="display: none" data-i18n="withdisadvantage">with Disadvantage</span>
<button type='roll' value='[[@{rollquery}]]' ></button>

This example will create the following roll using the default translation file

[[?{Are you rolling with Advantage or Disadvantage?|Neither,d20|Advantage,2d20kh1[with Advantage]|Disadvantage,2d20kl1[with Disadvantage]}]]

Roll Templates

Static HTML inside of the sheet's roll template definition can use all of the above tools, including Dynamic Keys. If it is in the sheet's rolltemplate definition, code like {{strength-key}} will give the translated value of whatever is the value of {{strength-key}}. If you would like to send dynamically translated values inside of a roll function, use the tool below.

Inside of a roll function, using a roll template, you can wrap any text in ^{ [key] } to denote the text as a translation key. This text will be replaced with the translated version associated with that key. This step happens after character sheet values are swapped in, so ^{ @{ability_key} } where ability_key = STRENGTH will be parsed as ^{STRENGTH} and will display whatever translation value you have for "STRENGTH". This also works on the key, for the key/value pairs used in the allprops() function. Example: {{^{TEXT}=@{content} }} will have the translation value for TEXT as {{key}} inside of the allprops() function.

List Ordering

If you have a list of items in a container, that in English is alphabetically ordered, and you want to be able to change the order in other languages you can use this tool. The re-ordering will respect all current formatting in the list, so even if your list spans several sub-containers, everything should remain formatted the same way.

Identify the container. All list items must be inside of the container, or they will be ignored:

data-i18n-list="list-key"

Identify the individual items in the list. These items can be a block of HTML. When they are reordered, the whole block will be moved.

data-i18n-list-item="item-key"

(Optional) The "starting" order of the list is assumed to be read top-down in the HTML. If your order is different, you can manually number the items (starting at 1) to set the order.

data-i18n-list-item-num="1"

(Special) This is not a tag that you will "use", it is a tag that will be created if there is an error with your formatting. If there is an error with your List Order code, the list will be ignored and this tag will be created on the list container element with the error. If you are not seeing the list be reordered, like you expect, check the parent element for an error.

data-i18n-error="Error Message"

Add the key to your translation file. This will contain a comma-delimited list of all of the item keys, in the language of the original translation. Reordering these keys will reorder the items. The list key can be used for several lists, if all of the items being sorted are the "same" items. The HTML doesn't have to be the same, only the words that are being sorted. For example, a list of "Skills" in D&D may show up in several places on a sheet with totally different HTML but they can all use the same list key. I will use the example code below so generate my list key.

"skills-list":"acrobatics,arcana,sleight-of-hand,survival"

To reorder this list for the Czech language, the list key value would look like:

"skills-list":"acrobatics,sleight-of-hand,arcana,survival"

Example:

<div data-i18n-list="skills-list"> // Label your list container and point it to the key it will use to set the order
    <div> // notice that the items are in a sub-container, these columns will still exist after the reordering happens
        <div data-i18n-list-item="acrobatics"> // Label your list items, these are the blocks that will be reordered
            <span data-i18n="acrobatics-u">ACROBATICS</span>
            <input type="text" name="attr_npc_acrobatics" placeholder="0">
        </div>
        <div data-i18n-list-item="arcana">
            <span data-i18n="arcana-u">ARCANA</span>
            <input type="text" name="attr_npc_arcana" placeholder="0">
        </div>
    </div>
    <div>
        <div data-i18n-list-item="sleight-of-hand">
            <span data-i18n="sleight-of-hand-u">SLEIGHT OF HAND</span>
            <input type="text" name="attr_npc_sleight_of_hand" placeholder="0">
        </div>
        <div data-i18n-list-item="survival">
            <span data-i18n="survival-u">SURVIVAL</span>
            <input type="text" name="attr_npc_survival" placeholder="0">
        </div>
    </div>
</div>

The same example only, rather than having the list go down the columns then across, we're going to have the list go across the columns then down:

<div data-i18n-list="skills-list">
    <div>
        <div data-i18n-list-item="acrobatics" data-i18n-list-item-num="1">
            <span data-i18n="acrobatics-u">ACROBATICS</span>
            <input type="text" name="attr_npc_acrobatics" placeholder="0">
        </div>
        <div data-i18n-list-item="sleight-of-hand" data-i18n-list-item-num="3">
            <span data-i18n="sleight-of-hand-u">SLEIGHT OF HAND</span>
            <input type="text" name="attr_npc_sleight_of_hand" placeholder="0">
        </div>
    </div>
    <div>
        <div data-i18n-list-item="arcana" data-i18n-list-item-num="2">
            <span data-i18n="arcana-u">ARCANA</span>
            <input type="text" name="attr_npc_arcana" placeholder="0">
        </div>
        <div data-i18n-list-item="survival" data-i18n-list-item-num="4">
            <span data-i18n="survival-u">SURVIVAL</span>
            <input type="text" name="attr_npc_survival" placeholder="0">
        </div>
    </div>
</div>

Step Two, Generating the Translation File

Once you have formatted your sheet, following the instructions above, you can very easily create your translation file. After loading your sheet within the editor, go into the game, and there open the Developer Tools for your browser and go to the console. At the very bottom of the console, where you can enter code, type i18nOutput. (it will most likely start to auto-complete once you start typing) Hit enter to retrieve this object, and it will dump a JSON string containing all of the keys you have used with the corresponding text from within the element/placeholder. You should be able to then copy this text into your favorite JSON formatter (optional, but recommended) and then into the "Translation" tab in the Game Settings Custom Sheet area.

If you wish to create the translation.json manually, you can use the following format:

{
    "key": "string",
    "key-2": "Another string."
}

Once you have your formatted sheet and the translation JSON saved for your custom sheet, it should look exactly like it did before you translated everything, assuming everything was done correctly.

If you see any red text like [translation-key] this is a key that you are telling the system to use, but have not entered into the translation JSON. If you use the JSON generated by the system, you should not see any of these.

Once you have a working translation file, you can upload this with the rest of your sheet code to the root of your character sheet folder as translation.json. This will automatically be picked up by our system and uploaded to Crowdin, where it can then be translated.

Changing your Translation

If you wish to change your translation after it has already been generated, you can edit your translation.json file, and the changes will be propagated to the language files the next time translations are updated. However, certain changes will not cause translation values to be overwritten, even if the values haven't been translated yet. In other words, sometimes changes to the translation.json will not show up in your sheet.

Changes that will not trigger an update include changes where the only difference is white space or special characters, and changes that only remove characters and do not change or add new characters.

Submitting your changes to Roll20

See Beginner's Guide to GitHub

See Also