-
Notifications
You must be signed in to change notification settings - Fork 77
React Bindings Discussion
This page lists a number of ideas for improvements and future plans for fluent-react
.
The text content of the wrapped element isn't currently used and serves as visual help for the developer. In the future it might be used to extract the source copy. It must be a simple string expression. For simple translations without interpolations, the following two components yield the same result:
<Localized id="hello">
<p>Hello, world!</p>
</Localized>
<Localized id="hello">
<p>{'Hello, world!'}</p>
</Localized>
For translations with interpolated arguments, only the latter syntax is valid:
<Localized id="hello" $username={name}>
<p>{'Hello, { $username }'}</p>
</Localized>
By putting the text in a simple string expression we're saving React's cycles: <p>Hello, { name }</p>
would be evaluated by React before being passed to Localized
where it would be replaced by the translation. It also isn't the proper Fluent syntax for this message as external arguments need to be prefixed with a $
in FTL. Lastly there's a naming mismatch between the JSX variable name
and the Fluent argument username
.
Because the text content of the <p>
element isn't used at runtime, the following is also valid:
<Localized id="hello" $username={name}>
<p />
</Localized>
As an alternative, there could be an orig
or source
prop which takes the source copy:
<Localized id="hello" $username={name} orig="Hello, {$name}">
<p />
</Localized>
Is there a way to use regular JSX as localized contents and make the extraction smarter? Something like the following would look more familiar to React developers:
<Localized id="hello" $username={name}>
<p>Hello, { name }</p>
</Localized>
Putting <p>Hello, { name }</p>
as the content looks good on paper and it makes it such that this content can be used as an ultimate fallback, but it has a number of shortcomings:
-
{ name }
is evaluated before the localization logic happens. If you have<p>Hello, { name.toUpperCase() }</p>
,toUpperCase
will be called before the element makes it to<Localized>
.<Localized>
will then replace the content with the translation and hand it off to React for rendering. So the rendering happens only once but the descendant elements are evaluated nonetheless. -
{ name }
is not the proper Fluent syntax for this message; it should be{ $username }
. It's a coincidence that JSX also uses braces for interpolated expressions. We could probably solve this with additional logic in the extraction tool. -
If the message needs plurals in the source language, you can't express them in JSX, and you need the Fluent syntax. Same with variables which need to be passed through Fluent's number or date formatting. These could be special-cases that require the use of simple string expressions.
-
It may be misleading: it's regular JSX so developers might be tempted to put more logic in it; think
{items.map(item => <MyItem>{item}</MyItem>) }
.
<Localized>
can be wrapped by the users of the library to create element-specific wrappers:
<LocalizedP id="hello" />
{'Hello, world!'}
</LocalizedP>
<LocalizedSpan id="hello">
{'Hello, world!'}
</LocalizedSpan>
This could be used to easily define allowed and forbidden attributes for custom components.
- In order to support async fallback we need
messages
to be an async iterable which in turn requires a different syntax ingetMessageContext
:for await (… of …)
. We might end up with a separate class,AsyncLocalization
in order to support this.
- Can it cache its
message
? - Can it request a fallback from the provider if the
message
doesn't exist? Async? - Perhaps format the message in
componentWillReceiveProps
and store it in the<Localized>
's state?