Reputation: 3811
I'm using i18next in a project and can't get around including html tags in translation files and having them properly rendered.
An example of my .json
translation file:
"en": {
"product": {
"header": "Welcome, <strong>User!</strong>"
}
}
There is an excellent answer to this question, but relating to JQuery. I'm not using JQuery, my project is React and here is the setup that I have:
import i18next from 'i18next';
import en from 'locales/en';
i18next.
init({
lng: 'en',
fallbackLng: false,
resources: en,
debug: false,
interpolation: {
escapeValue: false
}
});
export default i18next.t.bind(i18next);
In component I have:
import t from 'i18n';
t('product.header')
Html that I want:
Welcome, <strong>User!</strong>
Html I'm getting:
Welcome, <strong>User!</strong>
Thanks
Upvotes: 42
Views: 52347
Reputation: 6738
An approach I prefer is to use markdown
in your translation and then use react-markdown
to convert it to HTML
.
You would set your translations like this:
"en": {
"product": {
"header": "Welcome, **User!**"
}
}
And in your component
const MyComponent = () => {
const { t } = useTranslationWithMarkdown();
return (
<h1>
{t("product.header")}
</h1>
);
};
I build this custom hook for that:
import React from "react";
import { useTranslation } from "react-i18next";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
export function useTranslationWithMarkdown() {
const { t, ...rest } = useTranslation();
const tMarkdown = React.useCallback(
(key: string, options?: Record<string, unknown>) => {
return <Markdown remarkPlugins={[remarkGfm]}>{t(key, options)}</Markdown>;
},
[t],
);
return { t: tMarkdown, ...rest };
}
Upvotes: 0
Reputation: 1012
Now you can use Trans
without converting every tag to number tag:
"en": {
"product": {
"header": "Welcome, <strong>User!</strong>"
}
}
"strong" tags in translation file will be replaced with components' the value of "strong" key
<Trans i18nKey="en.product.header" components={{ strong: <strong /> }} />
The result is "Welcome, <strong>User!</strong>" as html as you expected.
Upvotes: 5
Reputation: 27
You could try and use the property dangerouslySetInnerHTML. This lets you use HTML in your JSON that will be rendered in the tag.
Note that you can't have both, this property and children
Example of usage
<p dangerouslySetInnerHTML={{__html: t(`users_desc`) }} />
in the json you can write something like this
{
...
"users_desc": "Users are <b> Amazing </b>"
...
}
This will render a tag
with a tag as child.
Upvotes: -1
Reputation: 1
First you need to write your json file like this:
"en":{
"Welcome":"Welcome",
"User":"User"
}
Then write your HTML code as follows:
<header>{t('Welcome')},<strong>{t('User')}!<strong>
<header>
Upvotes: -4
Reputation: 466
Since [email protected]
, you can use tags inside your translation string and replace it with components
prop in Trans
component or t
property from useTranslation
hook:
https://react.i18next.com/latest/trans-component#using-with-react-components
Example of usage:
<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 /> }}
/>
Upvotes: 17
Reputation: 1012
Don't put the HTML tags in the translation. It's a bad idea anyway. Separation of concerns guys will be all whiny about it.
Use the <Trans>
component if react-i18next https://react.i18next.com/latest/trans-component
Do like so:
// Component.js
<Trans>Welcome, <strong>User!</strong>, here's your <strong>broom</strong></Trans>
And the corresponding translation file:
// your locales/starwars_en.js file
translations: {
"Welcome, <1>User!</1>, here's your <3>broom</3>": "Welcome, <1>young padawan!</1>, here's your <3>light saber</3>",
}
These numbers <1> and <3> will strike you as random but wait for it:
Trans.children = [
'Welcome, ', // index 0
'<strong>User!</strong>' // index 1
', please don't be ', // index 2
'<strong>weak</strong>', // index 3
' unread messages. ', // index 4
]
SIDE NOTE (Can be considered a hack but saves tons of time): The guys at react.i18next.com, don't have this in their docs, but you can use the base language as a key (English in this case). It saves you time, not to double translate like they showed in their docs and I quote:
// Component file
import React from 'react';
import { Trans } from 'react-i18next'
function MyComponent({ person, messages }) {
const { name } = person;
const count = messages.length;
return (
<Trans i18nKey="userMessagesUnread" count={count}>
Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.
</Trans>
);
}
// translation file
"userMessagesUnread": "Hello <1>{{name}}</1>, you have {{count}} unread message. <5>Go to message</5>.",
"userMessagesUnread_plural": "Hello <1>{{name}}</1>, you have {{count}} unread messages. <5>Go to messages</5>.",
Anyway "Kudos!" to the i18next team! You are awesome, guys!
Here - go nuts!
Upvotes: 19
Reputation: 4498
not a problem of react-i18next - you just can't add html into a react element. react will safe you from xss vulnerability and escape the content:
more detail and possible solution: https://facebook.github.io/react/tips/dangerously-set-inner-html.html
but be aware if you put user content there not escaped you open your site to xss attacks.
more secure reading the react-i18next readme: https://github.com/i18next/react-i18next#interpolate-component
which makes you split content
so the "best" solution - at least what we do - is using markdown in the translations and use eg: https://github.com/acdlite/react-remarkable to convert that to formatted content.
Upvotes: 7