Tuesday, October 18, 2022

trivial javascript templating with reduce()

 At work we have a translation system that has strings like this:

"Welcome home, ${name}"

"Bienvenido a casa, ${name}"

"willkommen zu hause ${name}"

etc. 

Now those look like javascript template strings, but since it's at run time not compile, you can't just jam it in with backticks....(and also you probably want to have a little map of key value pairs to use)

Apparently other systems we use have a template system for the i18n stuff - you could just include a maplike object of key/value substitutions to make - but ours didn't have that and it wasn't clear it would be easy to include, given all the gatsby etc environmental weirdness it has to handle

Being a properly lazy programmer, I found this example of code to possibly steal. It can even handle stuff like 'Example with math: ${2 +2}' and get you that 4. But that seemed like overkill plus triggered my 'possible security risk' spider sense - that whole "new Function()" bit made me think I was too lazy or not smart enough to know if it was safe to use.

Really, I just had a Javascript 101 / interview question on my hand... "how can I replace all ${FOO} instances in a string with the corresponding values from a maplike object?". But one insight was, I shouldn't treat it like a fancy regex for every instance of ${ }.... I can just run against the keys of maplike thing that has the substitutions I care about.

Interestingly (to me) the best candidate array helper function was "reduce()", against the array of keys from the maplike - the initial value is the template, then the function takes in the template (with applied substitutions) as the previous value, and the current value is actually the key to do a replaceAll with... thus...

const templater = (message,replacements) =>
Object.keys(replacements).reduce((stringSoFar, key) =>
stringSoFar.replaceAll(`\${${key}}`, replacements[key]), message);

And with a little tester 

const translation = "${FOO} x ${FOO} y ${BAR} z ${FOO} omega FOO BAR";
const replace = {"FOO":"foofoo","BAR": "barbar"};

console.log(templater(translation, replace));

which gave me

foofoo x foofoo y barbar z foofoo omega FOO BAR

as expected

No comments:

Post a Comment