23

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?

Syn
  • 938
  • 1
  • 10
  • 21
  • 1
    Yes the Trans component is what you're looking for: https://react.i18next.com/latest/trans-component – adrai Feb 04 '21 at 14:08

6 Answers6

22

@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",
  • My version was outdated which is probably the reason why it didn't work for me. but now I can't take back the down-vote... :( – jones Mar 01 '22 at 07:49
  • This is perfect for the simplest use cases, as long you just need some bold text without any replaceable text or tag, this is the way to go, just make sure to update to 10.4 or newer :) – xec Mar 25 '22 at 08:58
16

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.

xec
  • 17,349
  • 3
  • 46
  • 54
5

Since v11.6 they have an alternate style which lists components - this means you can use names rather than numbers for the wrapping tags.

Their 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 /> }}
/>

When applied to your problem, 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 /> }}  
/>
icc97
  • 11,395
  • 8
  • 76
  • 90
5

For anyone tackling this with next-18next in 2023. I am using:

  "i18next": "^22.4.9",
  "next": "13.1.6",
  "next-i18next": "^13.1.5",
  "react": "18.2.0",
  "react-dom": "18.2.0",
  "react-i18next": "^12.1.5"

Translation file

public/locales/<locale>/common.json

{
  "replacedValue": "some replaced value", 
  "anotherReplacedValue": "another replaced value", 
  "sentence": "Sentence with <strong>{{replacedValue}}</strong>, and <i>{{anotherReplacedValue}}</i>."

}

Page file

pages/index.js

import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useTranslation, Trans  } from "next-i18next";

export default function Home() {
  const { t } = useTranslation("common");

  return (
    <p>
      <Trans>
        {t("sentence", {
          replacedValue: t('replacedValue'),
          anotherReplacedValue: t('anotherReplacedValue'),
        })}
      </Trans>
    </p>
  );
}

export async function getServerSideProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ["common"])),
    },
  };
}

Result

Sentence with some replaced value, and another replaced value.

Note ⚠️

Be sure to import Trans from next-i18next rather than react-i18next, otherwise you'll get hydration errors since the translations happens client-side instead of server-side.

HotFix
  • 157
  • 1
  • 11
1

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")}} />
Kazikal
  • 156
  • 7
  • `dangerouslySetInnerHTML` is named so for a reason, it opens your app up for xss attacks if you ever use this approach with a dynamic value or an untrusted translation source – xec Apr 20 '21 at 10:30
  • Useless comment since it was already stated "but be careful if you have a user input in the translation since it should be manually escaped." – Kazikal Apr 21 '21 at 11:08
  • I see you edited your answer, but I think you may have misunderstood what is going on. With Trans there is still only one place you need to maintain the translation, since you don't need to have both a fallback text and a translation for the same language. In any case I think it's a terrible idea to recommend a solution that is a potential security vulnerability when it is easily avoided. – xec Apr 22 '21 at 16:43
0

If you are using next and you have to pass NEW YORK as a param you can do this : in your json file :

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

and in your component : city = "new york" is a var in your component:

 <Trans i18nKey="greeting" components={{city}}></Trans>