I have a repeating attribute "ability_type2", that is a datalist input. <input type="text" name="attr_ability_type2" list="feats" class="show-feats" title="@{repeating_ability_$X_ability_type2}" placeholder="General" data-i18n-placeholder="general" value="" accept="Feat Type" /> <datalist id="feats" class="hidden"> <option value="Achievement" data-i18n="achievement">Achievement</option> <option value="Animal Companion" data-i18n="animalcompanion">Animal Companion</option> <option value="Animal Familiar" data-i18n="animalfamiliar">Animal/Familiar</option> </datalist> I have added the key=value pairs to the translation.json and used the appropriate syntax in a macro ie ^{@{ability_type2}} But nothing from the datalist is being translated in chat when called using a macro with the sheet's roll template. ie [red text within square brackets] I found that datalist translation may be problematic on the wiki: <a href="https://wiki.roll20.net/Character_Sheet_Translation#Datalist" rel="nofollow">https://wiki.roll20.net/Character_Sheet_Translation#Datalist</a> Of course it is... lol so I tried to modify the wiki example to match my sheet; substituted "repeating_inventory" with "repeating_ability" and "name" with "ability_type2". I wasn't sure how to deal with the "GLOBAL__INVENTORY_INPUTS" or "GLOBAL__INVENTORY" variables since my code doesn't have either. I only need to translate the ability_type2 attribute. So I carefully removed them with a battle axe. ;-) Here's the wiki example along with the GLOBAL__INVENTORY_INPUTS and GLOBAL__INVENTORY shortened samples from the ARC sheet. const GLOBAL__INVENTORY_INPUTS = [ "name", "type", "description" ]; const GLOBAL__INVENTORY = { combat_scowling_face: { type: "damage_and_defense", complexity: 0, }, combat_pitchfork: { type: "damage_and_defense", complexity: 0, }, techniques_doompause: { type: "spells_and_techniques", complexity: 0, }, }; on("change:repeating_inventory:name", (eventInfo) => { const id = eventInfo.sourceAttribute.split("_")[2]; const updateAttrs = {}; const name = eventInfo.newValue.trim(); let translation = getTranslationByKey(name) if (translation) { updateAttrs[`repeating_inventory_${id}_name`] = translation; const searchInputs = _.difference(GLOBAL__INVENTORY_INPUTS, ["name"]); _.each(searchInputs, (key) => { const attr = `repeating_inventory_${id}_${key}`; const i18n = `${name}_${key}`; // check if property exists in global if (GLOBAL__INVENTORY[name][key]) { updateAttrs[attr] = GLOBAL__INVENTORY[name][key]; } // check if translation exists else if (getTranslationByKey(i18n)) { updateAttrs[attr] = getTranslationByKey(i18n); } }); log(updateAttrs) setAttrs(updateAttrs, { silent: true }); } }); And here's how I've tried to adjust the example to work for me. Console log shows when an unknown translation has been entered for ability_type2, but I'm still seeing untranslated text in chat. on("change:repeating_ability:ability_type2", (eventInfo) => { const id = eventInfo.sourceAttribute.split("_")[2]; const updateAttrs = {}; const featName = eventInfo.newValue.replace(/\s/g, '').toLowerCase(); let translation = getTranslationByKey(featName) if (translation) { updateAttrs[`repeating_ability_${id}_ability_type2`] = translation; const attr = `repeating_ability_${id}_ability_type2`; const i18n = `${featName}`; if (getTranslationByKey(i18n)) { updateAttrs[attr] = getTranslationByKey(i18n); } TAS.debug(updateAttrs); setAttrs(updateAttrs, { silent: true }); } }); Thanks in advanced for any help/suggestions.