For me it would be fine to stay with your current implementation and document the use-case somewhere in a how-to.

Currently our complex case is implemented like this.

async function translateWithFallback(key) { const template = jsreport.req.template const templatePath = await jsreport.folders.resolveEntityPath(template, 'templates') const folderPath = templatePath.substring(0, templatePath.lastIndexOf('/')) return jsreport.localization.localize(key, folderPath + '/translations') .then(translation => { return !translation ? fallbackTranslation(key) : translation }) .catch((e) => fallbackTranslation(key)); } function fallbackTranslation(key) { const defaultDataPath = "translations/en.json"; return jsreport.folders.resolveEntityFromPath(defaultDataPath, 'assets') .then(function(resolvedValue) { return JSON.parse(resolvedValue.entity.content.toString()); }) .then(function(data) { var v = data[key] return !v ? "[" + key + "]" : "*" + v; }) }

Of course neither capable of special translation folders nor any other fallback language then English.