i18next bold text in the middle of a translation
Asked Answered
T

6

30

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?

Titanic answered 31/1, 2021 at 2:6 Comment(1)
Yes the Trans component is what you're looking for: react.i18next.com/latest/trans-componentMori
I
27

@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",
Intransigeance answered 11/1, 2022 at 11:32 Comment(2)
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... :(Ligroin
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 :)Tacklind
T
22

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.

Tacklind answered 20/4, 2021 at 10:26 Comment(3)
This should be accepted answer!Comose
@Titanic please accept this answer...Superlative
What about react native?Unsophisticated
C
12

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 /> }}  
/>
Cloy answered 7/2, 2022 at 19:50 Comment(0)
P
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.

Physiography answered 14/2, 2023 at 14:21 Comment(2)
I think the correct way to do the translation is with <Trans t={t} i18nKey=...Rubricate
Using t(...) inside of <Trans> kind of defeats the purpose of using the component in the first place (maybe unless you're using it on a JSX prop value? which is not the case here). You can probably either simply not use Trans here (<strong> and <i> should work without), or drop the t() and use the i18nKey and values prop instead. See examples in the official docs: react.i18next.com/latest/…Tacklind
C
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")}} />
Cubital answered 2/3, 2021 at 11:2 Comment(3)
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 sourceTacklind
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."Cubital
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.Tacklind
T
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>
Treacherous answered 2/9, 2023 at 19:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.