Localization Helper in 3.3.0
-
Hi,
Our template structure looks like this
Besides other functions we have a
translate
function in ourglobal-helper.js
since all our localization is located in a sub-folder translation, it''s quite handy in a report templates to write{{translate "text"}}
.const jsreport = require('jsreport-proxy'); function translate(key) { return jsreport.localization.localize(key, "translations"); }
Since the upgrade to jsreport 3.3.0 we face the following error message with our setup.
Report "reportB" render failed. Error when evaluating engine handlebars for template /reportB/reportB localize helper couldn't find asset with data at translations/en.json Error: localize helper couldn't find asset with data at translations/en.json at Object.localize (/app/node_modules/@jsreport/jsreport-localization/lib/worker.js:44:17) at async /app/node_modules/@jsreport/jsreport-core/lib/worker/render/executeEngine.js:133:40 at async Promise.all (index 0) at async executionFn (/app/node_modules/@jsreport/jsreport-core/lib/worker/render/executeEngine.js:132:9)
Are we doing something wrong? Was this changed from 3.2.0 to 3.3.0 and if so, is there a possibility to fix this? Is there another way to solve the situation?
The release notes of 3.3.0 state "fix not resolving relative path correctly when calling asset code from another asset". Is this maybe the reason?
You can find a setup here
Thanks in advance for any advice.
-
Yes, this was changed in 3.3.0 because we considered it as a bug. Now everything is resolved relative to the particular entity. We consider it as a better approach.
The following approach could work for you
const jsreport = require('jsreport-proxy'); async function translate(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'); }
https://playground.jsreport.net/w/anon/K78DqxIW
We are considering some improvements for the future versions:
- default to "translations" folder in localize function so the second argument won't be needed
- a one-liner function to resolve the current template path or folder
Thank you for the demo and the great description!
-
Thanks for the fast response, works well.
Just out of curiosity, are you considering a fallback to a default localization if no language is found? Currently an exception is thrown and the whole report won't build.
Edit: I added our fallback handling in the playground example from above.
-
The default language can be set on the template itself. See in the studio template properties -> localization -> Template language
Another option is to use a custom script, which can be scoped globaly or also to a particular folder.
https://jsreport.net/learn/scripts#script-scopeasync function beforeRender (req, res) { req.options.localization = req.options.localization || { language: 'en' } }
-
Thanks for the clarification.
What I meant was that we fallback for each translation individually. If not found in the given language we search in english and if we don't find it there we return the [key] instead of undefined. Outlined in reportB of the example.
https://playground.jsreport.net/w/anon/K78DqxIW
-
I see, thank you for the suggestion. It seems to me that the requirements can differ and not yet sure which approach should be provided from us by design. I think we would start by documenting how you can adapt the localization function and see in time what is the most common usage.
The current localization is missing explicit language specification. We will add it in the next release.
Then a complex use case like yours can be implemented like this.const jsreport = require('jsreport-proxy'); const defaultLanguage = 'cz' // add to helpers custom localization function async function translate(key, translationsPath) { if (typeof translationsPath !== 'string') { // default to specific translations folder const template = jsreport.req.template const templatePath = await jsreport.folders.resolveEntityPath(template, 'templates') const folderPath = templatePath.substring(0, templatePath.lastIndexOf('/')) translationsPath = folderPath + '/translations' } let localizedVal try { localizedVal = await jsreport.localization.localize(key, translationsPath) } catch (e) { // asset with requested language not found, try default language try { localizedVal = await jsreport.localization.localize({ key, folder: translationsPath, language: defaultLanguage }) } catch (e) { // asset for default language not found return `[${key}]` } } if (localizedVal == null) { // key not found in the asset, try search for key in the asset for default language try { localizedVal = await jsreport.localization.localize({ key, folder: translationsPath, language: defaultLanguage }) } catch (e) { // asset for default language not found return `[${key}]` } if (localizedVal == null) { return `[${key}]` } return localizedVal } return localizedVal }
Do you have some thoughts/comments on what we plan?
-
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.