Mission
To pass in a React element and format the message based on its content; e.g. we want these to render with different plural/singular text depending on what the value of X is, with the value of X being bolded:
- where X == 0, it would read as: “0 Examples!!!”
- where X == 1, it would read as: “1 Example”
- where X > 1, it would read as: “2 Examples”
Problem
@atlassian/wrm-react-i18n
internally works by swapping out any React elements with placeholders and then delegates to wrm/format
which formats those place holders in to the message. @atlassian/wrm-react-i18n
takes the formatted message, and then replaces the placeholders with the provided React elements.
It’s exactly these placeholders that prevents wrm/format
from being able to pick the correct choice because it doesn’t know the true values.
A bad solution
This should not be used, because:
- We forever have a risk that there’s an XSS or it’s one day introduced by accident
- Even if in some specific scenario the chance is ranked as low, we do not want linters and security scanners causing fatigue
- It introduces a new bug type where the HTML is not as intended due to having gone through the translation process
Putting the HTML into the formatted string:
some.i18n.key={0,choice,0#<b>0</b> Examples!!!!!|1#<b>1</b> Example|1#<b>{0,number}</b> Examples}
Then allowing using dangerouslySetInnerHTML
(assuming this is a modern codebase where React is used):
<p dangerouslySetInnerHTML={{__html: I18n.getText('some.i18n.key', numberOfExamples)}}/>
A better solution
→ Pass the value in again without being wrapped in any React elements.
We’d make the I18n string:
some.i18n.key={1,choice,0#{0,number} Examples!!!!!|1#{0,number} Example|1<{0,number} Examples}
Then use it like:
import {I18n} from '@atlassian/wrm-react-i18n';
I18n.getText(
'some.i18n.key',
<b>{numberOfExamples}</b>,
numberOfExamples,
)