Search code examples
javascriptreactjsinternationalizationreact-intlformatjs

react-intl render react component as placeholder in formatMessage


I have a component, that uses the injectIntl-HOC, and returns a message

...
return (
  <Message>
    {intl.formatMessage({
      id: 'page.checkout.hint'
    }, {
      link: <b>{intl.formatMessage({ id: 'page.checkout.hint.hyperlink' })}</b>
    })}
  </Message>
)
...

and my language file looks like this:

...
"page.checkout.hint": "You're going to be redirected automatically. If nothing happens, please click {link}",
"page.checkout.hint.hyperlink": "here",
...

This results in: You're going to be redirected automatically. If nothing happens, please click [object Object].

if i use <FormattedMessage id="page.checkout.hint" values={{ link: <b>{intl.formatMessage({ id: 'page.checkout.hint.hyperlink' })}</b> }}> instead renders correctly.

Does Anybody has a clue?


Solution

  • The object is because your interpolated link value is actually a React b component, but is expecting a string so it simply does toString on the object and outputs into you interpolated link

    FormattedMessage does some work behind the scenes if you pass it a react component as a value, to keep it as such in the rendered output, taking advantage of all the benefits of being a react component.

    However the injectIntl version doesn't do that work on your behalf.

    While it isn't the recommended approach as it mixes paradigms and takes all the benefits of React away from that point onwards, if you have real reasons for using HTML string value in injectIntl over a react component in value FormattedMessage you can as a last resort use:

    this.props.intl.formatHTMLMessage(
      {id: 'page.checkout.hint'}, 
      {link: `<b>${this.props.intl.formatMessage({ id: 'page.checkout.hint.hyperlink' })}</b>`}
    )
    

    Thats the reason for the difference, but allowing HTML string directly is only really there for legacy support, so if possible it should be avoided