i18next bold text in the middle of a translation

Viewed 12238

I have a static file called translations.json which includes my translations:

{
  "common": {
    "greeting": "We will see you at NEW YORK in the morning!"
  }
}

Within my react code, ive been use doing somethign along the lines of:

<p>{translate('common.greeting')}</p>

However, I want the word "NEW YORK" to be bold. Ive been doing some research and I see the the Trans component might be what im looking for but im having no luck. Im about to just split the translation into 3 parts... greetingIntro, grettingBold, and grettingEnd...

Can someone point me in the right direction?

4 Answers

Thanks to @adrai for noting the answer in a comment, thought i'd type it out here for future visitors;

Read the full docs at https://react.i18next.com/latest/trans-component

You can use the <Trans> component from react-i18next

import { Trans } from 'react-i18next'

I'm assuming that the 'NEW YORK' text should be dynamic (easily replaceable), if not you don't need the values part.

<Trans i18nkey='common.greeting' values={{ city: 'NEW YORK' }}>
  We will see you at <strong>NEW YORK</strong> in the morning!
</Trans>

Then, in the locale JSON file you can use <N> as a placeholder for the JSX tag at index number N used inside the <Trans> tag and {{key}} for any dynamic value (like city in this case)

{
  "common": {
    "greeting": "We will see you at <1>{{city}}</1> in the morning!"
  }
}

Why <1>? The string gets split into indexed parts, starting at 0. The first part (index 0) in this case will be the text "We will see you at " and the second part (index 1) will be the <strong> element.

The text inside the <Trans> component is only used as a fallback if no translation is found, but the <strong> tag will replace the <1> placeholder.

Update: According to the docs, since version 10.4.0 you can also use a few tags directly in the text: ['br', 'strong', 'i', 'p'] but never nested, and no attributes. Also see Aleksandar Sadzak's answer.

@adrai and @xec has already answered this question, but I think it's still too complicated.

So the simple version of this answer is... Use this in your code:

import { Trans } from 'react-i18next'

<Trans i18nKey="common.greeting" />

and this in your JSON:

{
  "common": {
    "greeting": "We will see you at <strong>NEW YORK</strong> in the morning!"
  }
}

Full docs available at https://react.i18next.com/latest/trans-component

My versions are:

"i18next": "^20.6.0",
"react-i18next": "^11.11.4",

Since v11.6 they have an alternate style which lists components:

They're example:

<Trans
  i18nKey="myKey" // optional -> fallbacks to defaults if not provided
  defaults="hello <italic>beautiful</italic> <bold>{{what}}</bold>" // optional defaultValue
  values={{ what: 'world'}}
  components={{ italic: <i />, bold: <strong /> }}
/>

This would come out something like:

<Trans 
  i18nkey='common.greeting' 
  defaults="We will see you at <bold>{{city}}</bold> in the morning!"
  values={{ city: 'NEW YORK' }}>
  components={{ bold: <strong /> }}  
/>

Splitting the sentence will defeat the purpose of having translations, what if another language requires the split parts in a different order?

Also I'm not fond of the Trans component since you need to maintain the translation in both the default JSON and in the React code.

I suggest using embedded HTML, but be careful if you have a user input in the translation since it should be manually escaped.

In your case since you don't have any user inputs just go with:

JSON:

{
  "common": {
    "greeting": "We will see you at <b>NEW YORK</b> in the morning!"
  }
}

React:

<div dangerouslySetInnerHTML={{__html: translate("common.greeting")}} />
Related