Translating Lists and Queries

The methods and descriptions in this post are heavily influenced by the roll20 wiki. The real difference here is the way the information is presented. That place is still a good resource for raw information.

Translating Lists

Let’s imagine you have a dropdown list (a select) that lets the player choose a stat. That might look like this.

<select name="attr_stat">
   <option value="strength" data-i18n="strength">Strength</option>
   <option value="dexterity" data-i18n="dexterity">Dexterity</option>
   <option value="constitution" data-i18n="constitution">Constitution</option>
   <option value="intelligence" data-i18n="intelligence">Intelligence</option>
   <option value="wisdom" data-i18n="wisdom">Wisdom</option>
   <option value="charisma" data-i18n="charisma">Charisma</option>
<select>Code language: HTML, XML (xml)

Here we have a simple select. When the user selects an item in the list, its translated value will be shown. All seems to be well… But what if you then try to display that result elsewhere, like this:

<span>The stat chosen was: </span>
<span name="attr_stat"></span>Code language: HTML, XML (xml)

The value appearing in the second span here is the option value. the problem is, that isn’t the translated value. The underlying value is still the untranslated value, and it is then dynamically altered only on display. The raw data is unchanged.

So if we want to display the translated value, we have to translate it. This is where the dynamic tag comes in:

<span>The stat chosen was: </span>
<span name="attr_stat" data-i18n-dynamic></span>Code language: HTML, XML (xml)

Adding this tag lets Roll20 know that the value of this attribute is set elsewhere and a translation already exists there. Use the same method for selects and datalists.

Translating Queries

Queries present a special problem. The text in a query is separate from the character sheet and cant be translated dynamically. Lets say you have a button that rolls an attack and asks for a modifier. Something like this:

<button type="roll" name="roll_attack" value="&{template:default} {{name=Generic Attack Roll}} {{Attack Roll=[[1d20+?{Attack Modifier|0}]] }}">Attack</button>Code language: HTML, XML (xml)

Imagine what happens when the player triggers this button. They’ll first be presented with a query popup that asks for the attack modifier, and then the roll template will appear with the title and row in the wrong language.

Translations cannot be directly included in a query, but the roll button value can draw from an attribute value. So the trick here is to use a sheet worker to update attributes when the sheet opens.

Roll20 has a sheet worker function, getTranslationByKey. Give this a translation key, and it will return the translated value, like this:

const translated_strength = getTranslationByKey('strength');Code language: JavaScript (javascript)

There are probably plenty of ways to use this, but remember, this is a sheet worker function – it won’t work in ordinary HTML.

For our poses, remember we want to translate that query text. We first need to have some translations existing in the sheet already. They might look like this:

<span class="hidden" data-18n="template-title">Generic Attack Roll</span>
<span class="hidden" data-18n="template-attack-label">Attack Roll</span>
<span class="hidden" data-18n="button-attack-modifier">Attack Modifier</span>
<span class="hidden" data-18n="button-label">Attack</span>Code language: HTML, XML (xml)

Now we have the four bits of data we want translated, we need to place them in the button text to be output. This is a sheet worker and so goes in your sheet’s script block.

on('sheet:opened', () => {
   const attack_button_query = "&{template:default} {{name=${getTranslationByKey('template-title')} }} {{ ${getTranslationByKey('template-attack-roll')}=[[1d20+?{${getTranslationByKey('button-attack-modifier')}|0} }}
       attack_button_query: attack_button_query 
});Code language: JavaScript (javascript)

This uses the template literal synax. It might be easier to understand like this (it is exactly the same outcome):

on('sheet:opened', () => {
   var template_title = getTranslationByKey('template-title');
   var attack_roll_title = getTranslationByKey('template-attack-roll');
   var button_attack_modifier = getTranslationByKey('button-attack-modifier');

   var query = "&{template:default} {{name=" + template_title + "}} {{" + attack_roll_title + "=[[1d20+?{" + button_attack_modifier + "|0} }}";

       attack_button_query: query 
});Code language: JavaScript (javascript)

This looks like a complex sheet worker, but when broken down this way, I hope you can see it not that complex. It runs evetry time the sheet is opened, and updates the translations. There is one final step to bring it all together. You need to pdate the button html. The original button looked like this:

<button type="roll" name="roll_attack" value="&{template:default} {{name=Generic Attack Roll}} {{Attack Roll=[[1d20+?{Attack Modifier|0}]] }}">Attack</button>Code language: HTML, XML (xml)

This includes the old query, But now the sheet worker creates the button text. We also need to show the button label. So the new button looks like this:

<button type="roll" name="roll_attack" value="@{attack_button_query}" date-i18n="button-label">Attack</button>Code language: HTML, XML (xml)

Whew. Translating queries is complexated. But here are the itemised steps:

  1. Create the original button. Examine what parts need to be translated.
  2. Create translations for those parts. Don’t forget the button label.
  3. Create a sheet:opened sheet worker, to create the new query
  4. Change the button’s value, and add a data-18n tag for its label.

It’s complicated, but the prcedure is straightforward and easily repeated.

The getTranslationByKey() function

As noted above, this is a sheet worker function, but this means you now have a way to include translated values in your sheet workers. Don’t forget about this, there are many situations you might want to use it.

Series Navigation<< Ordering Lists In TranslationTranslating rollTemplates >>

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.