react-i18next: Error occurs on render when using tOptions.interpolation

🐛 Bug Report

With the merge of my PR https://github.com/i18next/react-i18next/pull/1204, when I try to pass interpolation to tOptions, an error occurs. When I remove the tOptions prop that I passed in, then it works, which is very weird 🤔.

In the console React on the first few lines says:

The above error occurred in the <br> component:
    in br (at dashboard-greeting/index.tsx:40)
    in Trans (at dashboard-greeting/index.tsx:34)

In the very last line of console React says React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

To Reproduce

To reproduce this you would have to use Trans like this:

<Trans
        i18nKey="home:UserGreetingHeadline"
        defaults="Hallo {{ firstName }},<br/>
        willkommen auf <bold>meine</bold>tonies, hier kannst du ganz zentral
        Deine Toniesammlung und Tonieboxen verwalten."
        values={{ firstName: 'Tiger<no>' }}
        components={{ br: <br />, bold: <strong /> }}
        tOptions={{ interpolation: { escapeValue: true } }}
      />

As for the i18nKey, you do not have to specifically put it into a home.json file 👍.

Expected behavior

I do expect this to at least be able to render, with or without tOptions prop. I still find it very strange that it is working when I do not use the tOptions prop.

PS. I am happy to work on whatever has to be fixed in case this happens because of either my last PR or if anything else has to be changed 💕.

Pictures

Picture of the error:

Screenshot from 2020-12-03 07-50-13

Picture when not using tOptions prop, then it works apparently 🤔 :

Here the message is also broken because of the HTML tags in firstName 😢. Screenshot from 2020-12-03 07-51-35

Your Environment

  • runtime version: i.e. node v14.15.0
  • i18next version: i.e. 11.7.4
  • os: Linux, Ubuntu

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 42 (37 by maintainers)

Most upvoted comments

just set

    react: {
      transSupportBasicHtmlNodes: false
    },

https://codesandbox.io/s/react-i18next-forked-0opfv and results are the same

i18n.init({
   // all your options

   react: {
     transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p', 'no']
   }
})

and yes…you can just use the other tags (strong, br, …) as is in the translations…

Yes, the HTML.parse call is the problem.

@jamuhl Regarding this issue, of Trans not escaping properly, perhaps this could be something to fix in the future 😄, I am also down to heavily contribute as mentioned 🥰. I think it’d be great if we could fix this, then not just our, but other companies hopefully won’t encounter the same bug 🎉. This would also be great, as more and more companies start using react-i18next 🙌.

Also @jamuhl, something I forgot to ask regarding fixing this, do you have any ideas on how we could possibly fix this, or if it even is possible 🤔?

Thanks 🙏 💕

Analyze html-parse-stringify2 and propose a fix there or find an alternative.

That looks good to me, too: https://codesandbox.io/s/react-i18next-forked-1hfow?file=/src/app.js

I think we’ll set this in our global config then, to avoid the escaping issue globally.

I wonder if this should be mentioned in the documentation. I’m sure @tigerabrodi would be happy to do a PR for that 😃

@tigerabrodi would guess so…

Yes, HTML.parse takes the tag and converts it to something valid -> <no3> -> assumes non self-closing tag…

But one can also see: https://codesandbox.io/s/react-i18next-forked-eig23 works for
(which is in the transKeepBasicHtmlNodesFor list)

Main difference:

t always gives you a string

Trans tries its best to create a valid react component tree from the translation and node children it gets

well if it works with <no> you would have to define what you accept…allowing HTML inside user input is always a bit risky as it opens the door for injecting malicious scripts…

@adrai Hmm, seems like html-parse-stringify2 was last updated three years ago https://github.com/rayd/html-parse-stringify2

Actually this is still broken 😕 When adding an unclosed tag, it again breaks the rendering badly: https://codesandbox.io/s/react-i18next-forked-1hfow?file=/src/app.js:381-437 Despite using transSupportBasicHtmlNodes: false

To me it looks like a bug that <Trans> doesn’t escape values like t() does.

Since setting escapeValue: true breaks it in a different way, that doesn’t seem like a good “not a bug, its a feature” explanation.

If it is a bug, could you reopen the issue? Or should we (@tigerabrodi or me) file a new one? We could dig into the implementation to see if we can come up with a solution.

The br in components props does not work as https://react.i18next.com/latest/trans-component#alternative-usage-v-11-6-0 (see the warning: Existing self-closing HTML tag names are reserved keys).

The br in the translation will be converted to valid react element thanks to: https://react.i18next.com/latest/trans-component#using-for-less-than-br-greater-than-and-other-simple-html-elements-in-translations-v-10-4-0

Not had the time to create a reproducible sample but my guess is that setting { escapeValue: true } conflicts with interpolating react elements into the result.

to get Tigern<no> working adding it to the https://react.i18next.com/latest/trans-component#using-for-less-than-br-greater-than-and-other-simple-html-elements-in-translations-v-10-4-0 might already work - but not tested myself.

-> i18next.options.react: { ..., transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p', 'no'] }

If you watch the greeting text above Hallo Tigern&lt;no&gt;,, is there a way we could instead display the name properly like Tigern<no> 🤔 ?

This is what @jamuhl tried to explain here: https://github.com/i18next/react-i18next/pull/1204#issuecomment-737062765

So while this PR fixes an issue - I guess, it will not solve the initial problem you liked to solve. You might open an issue with the content you like to get translated and we might help out with some options on how to use the Trans component for it.

Does it work if you remove the “< br >” from the components prop? like: components={{ bold: <strong /> }}