Reputation: 3643
I have a react based application and use i18next's Trans component for translations.
Let's say we have a user registration form and we want to confirm that a new account has been created by displaying the following text:
User with name NAME has been created.
where NAME is provided by user and is displayed in bold.
What I have now is the following code in react:
<Trans i18n={i18next} i18nKey="userCreated" values={{name: 'Tom'}}>
User with name <strong>{{name}}</strong> has been created.
</Trans>
and the following translation string
"userCreated": "User with name <1>{{name}}</1> has been created.",
For a simple case with name: 'Tom'
it works fine and displays
User with name Tom has been created.
However I would like to let users use special characters in their names. When I change name to Tom&Jerry
I'm getting
User with name
Tom&Jerry
has been created.
After doing some research I thought that the issue is that both i18next and react escape their inputs and so the name is escaped twice. I turned off the i18next escaping in i18next.init
by adding the following option
interpolation: {escapeValue: false},
That fixes the Tom&Jerry case:
User with name Tom&Jerry has been created.
but there are some inputs that break it completely, for example setting name: 'Tom<'
renders like this (everything after name is bold):
User with name Tom has been created.
and setting name: '<Tom>'
gives me:
User with name has been created.
(no name displayed at all). I would like it to render like this:
User with name
<Tom>
has been created.
Is there a way to make it work?
Upvotes: 5
Views: 14907
Reputation: 5463
It's possible to make this work while setting interpolation: { escapeValue: false }
globally (so that t
still works as expected), by setting both tOptions={{ interpolation: { escapeValue: true } }}
(so that values are escaped before interpolation) and shouldUnescape
(so the whole translation string is unescaped after interpolation) on every Trans
component:
<Trans
i18nKey="userCreated"
values={{ name: 'Tom&Jerry <Tom><' }}
tOptions={{ interpolation: { escapeValue: true } }}
shouldUnescape
>
User with name <strong>{{ name }}</strong> has been created.
</Trans>
Upvotes: 3
Reputation: 41
If you want to use t()
of useTranslation()
hook instead of <Trans>
component,
const Component = () => {
const { t } = useTranslation();
const label = t('this.is.your.key',
'You {{ampersand}} Me',
{ ampersand: '&', interpolation: { escapeValue: false } }
);
return (<span>{label}</span>);
};
Upvotes: 4
Reputation: 200
There is more elegant way to achieve this.
You can use property "shouldUnescape" of Trans component. In my example the value for "welcome" is "<b>Special characters:</b> {{ characters }}"
import React from "react";
import { Trans } from "react-i18next";
import { escape } from "lodash";
export default function App() {
return (
<div className="App">
<Trans
i18nKey="welcome"
shouldUnescape={true}
components={{ b: <b /> }}
values={{ characters: escape("<, >, &") }}
/>
</div>
);
}
https://codesandbox.io/s/react-i18next-forked-tn9c3k?file=/src/app.js
Upvotes: 0
Reputation: 1975
There is no elegant solution to this problem. The root of the problem is in the source code of the Trans
component. You either need to create a patch for this file changing this behavior or implement a cheaty trick by yourself. If you're going to use a cheaty solution, you'd create a component UnescapedTrans
which will unescape HTML entities using lodash:
import React, { Suspense } from "react";
import { Trans } from "react-i18next";
import { unescape } from "lodash";
const getUnescapeChildrenRef = ref =>
Array.from(ref.childNodes).forEach(node => {
if (!node.innerText) {
return node;
}
node.innerText = unescape(node.innerText);
});
const UnescapedTrans = props => (
<div ref={getUnescapeChildrenRef}>
<Trans {...props} tOptions={{ interpolation: { escapeValue: true } }} />
</div>
);
Upvotes: 5