Comments (23)
beautiful examples should not drive our api. 🍦
from formatjs.
/cc @caridy @pselden @mridgway @redonkulus @g13n @sebmarkbage
from formatjs.
two notes:
Prefix
Format* seems better (as we have discussed offline), paving the road for formatjs library in the future.
Self-Closing
for <FormatMessage count={1000}>{"Visitor {count, number}"}</FormatMessage>
, It is very confusing, two main reasons:
a) you will, most likely, never have a hardcoded ICU message in the component, it will be a value provided somehow thru props or by using the lookup mechanism, it will really be something like: <FormatMessage count={1000}>{this.props.myMessages.VISITOR}</FormatMessage>
, which is really not that diffirent from: <FormatMessage count={1000} message={this.props.myMessages.VISITOR} />
.
b) people might get confused thinking that what you put inside <FormatMessage>
or <FormatNumber>
can be any markup and/or components, while the reality is very different.
In resume, I will vote for an strict Self-Closing component prefixed with "Format*".
from formatjs.
As for my proposal, I'm not sure between <FormatMessage value={this.something} />
vs <FormatMessage message={this.something} />
from formatjs.
for
<FormatMessage count={1000}>{"Visitor {count, number}"}</FormatMessage>
, It is very confusing
I was just trying to keep the example simple, and show there's a string as the child of the component, that's all. I agree that in practice it's bad.
from formatjs.
One thing to consider. At FB, with our equivalent of FormatMessage, we:
A) Always write messages inline and extract/replace them with a transform for translation.
B) Allow complex components to be nested within messages.
<FormatMessage>
A user named <ProfilePicture pic={user.pic} /> {user.name} visited
<FormatNumber value={count} /> times.
</FormatMessage>
from formatjs.
beautiful examples should not drive our api.
@caridy I updated the examples to show how we normally do it with the message strings being external.
from formatjs.
@sebmarkbage I'm very interested in your thoughts around:
- Noun-y vs. Verb-y component names in React
- Self-closing components vs. components with non-component children
The way we want to handle the example message you gave would be to still stick with the ICU Message format since it can be understood by professional translators, and the string is externalized from the source and can be used by other client apps. Using external ICU Message strings limits how "rich" a single string message can be, i.e. you wouldn't be able to mix JSX inside the message string, but you could use HTML — although using JSX in messages was brought up in #26. This would lead us to handling the example like this:
var intlData = {
locales: ['en-US'],
messages: {
VISITED: '{name} visited {count, plural, one{# time} other{# times}}.'
}
};
<App {...intlData}>
<ProfilePicture pic={user.pic} />
<IntlMessage name={user.name} count={count}>
{this.getIntlMessage('VISTED')}
</IntlMessage>
</App>
The count
will be formatted automatically by Intl.NumberFormat
and the "time(s)"
label will have the correct pluralization. But I had to change the messages to move <ProfilePicture>
outside.
from formatjs.
The noun form does feel more React-y, but ReactInt.IntlMessage
does feel odd. What if it were FormattedMessage
(a noun) and then abbreviated to FMessage
? Or for that matter, LMessage
for "localized message". That really is the noun that the instantiated component is representing, a displayable entity that is not just parameterized to be used internationally but that has in this instance been localized to the current locale.
In the self-closing case of Message, for readability it would be nice to be able to pass just the key:
<LMessage key="VISITED" name={user.name} count={count}>
Naive implementation:
var $$components$message$$IntlMessage = $$react$$default.createClass({
displayName: 'IntlMessage',
mixins : [$$mixin$$default],
render: function () {
var props = this.props;
var message = this.props.key ?
this.getIntlMessage(this.props.key) :
this.props.message;
return $$react$$default.DOM.span(null, this.formatMessage(message, props));
}
});
from formatjs.
@ericf Moving the component outside of the message is not always possible. E.g. with emoticons that needs to be embedded into the message or something like that. Even if you prefer the message source to be externalized, it seems like I should be able to pass a component as one of the parameters.
var intlData = {
locales: ['en-US'],
messages: {
VISITED: 'A user named {picture} {name} visited {count, plural, one{# time} other{# times}}.'
}
};
<IntlMessage name={user.name} count={count} picture={<ProfilePicture pic={user.pic} />}>
{this.getIntlMessage('VISITED')}
</IntlMessage>
Regarding Noun-y vs. Verb-y names. I'm not sure if it's really that important to preserve the noun-y names. You can also change the prefix to "Formatted". E.g. <FormattedDate />
.
Technically children are really just considered data so a component can put any requirements they want on it. We might want to turn it into a special data type to make it easier to work with iterables of children that preserve key information in a set, but for now it's just data.
In general, I prefer to put even components as props. Named props can convey more information by being explicitly named than arbitrary children can.
Besides, for now, you can avoid the extra curlies by putting it into a JSX attribute. That's a nice feature.
<FormattedMessage message="foo {count}" />
The distinction between <FormattedMessage>foo {count}</FormattedMessage>
and <FormattedMessage>{"foo {count}"}</FormattedMessage>
is very subtle.
I guess someone could come along and want to make curlies in JSX attribute concatenate the value of the scope but I doubt that will change.
from formatjs.
@turadg Note that as of React 0.12, key
is a reserved property name in JSX and the value is not available user code so your example doesn't work without renaming it to something else.
from formatjs.
Even if you prefer the message source to be externalized, it seems like I should be able to pass a component as one of the parameters.
@sebmarkbage the only ["safe"] way I could think to support this is to update the HTMLMessage component to check props
for values that pass React.isValidElement()
, then call React.renderToString()
on them. This way I can pass along the string representation as the value for the {picture}
placeholder in the message string.
From there the underlying IntlMessageFormat
library will interpolate the string and substitute the value, and the fully formatted message string injected into the DOM via dangerouslySetInnerHTML
.
It seems like this will wreak havoc on perf though :( Another option would be to insert have IntlMessageFormat#format()
use string identifiers for the values that are represented as React elements, then split the resulting string into multiple children and replace the identifier strings with references to the React elements. I just worry about the safety of this approach.
from formatjs.
We have our precompiler split the string up into its pieces ahead of time. Then we fill in the blanks at runtime at the right place in the set, which may of course vary by language. We also place a key each item so that we can preserve nested component's state in case the sentence is updated or reordered.
Effectively, you pass something like this to React:
new Map([
['partial0', 'A user named '],
['picture', <ProfilePicture pic={user.pic} />],
['partial1', ' '],
['name', name],
['partial2', ' visited '],
['count', count],
['partial3', ' times']
])
from formatjs.
Note that for pluralization etc, we use a different one of these templates at runtime for each possible variation.
from formatjs.
Here's what I have so far to support React Elements inside of formatted messages:
https://gist.github.com/31996579873a92e08d3c
It can take something like this:
<FormatMessage name={<b>Eric</b>}>
{"Hello, {name}!"}
</FormatMessage>
And render this:
<span data-reactid=".1lpa6dq7ncw" data-react-checksum="1647393318"><span data-reactid=".1lpa6dq7ncw.0">Hello, </span><b data-reactid=".1lpa6dq7ncw.1">Eric</b><span data-reactid=".1lpa6dq7ncw.2">!</span></span>
So this seems like a step in the right direction, especially when looking at an example that uses relative time formatting since relative
currently isn't part of the ICU Message grammar:
<FormatMessage
ago={<FormatRelative value={post.date} />}
favs={post.favs}>
{"Posted {ago}, has {favs, number, integer}"}
</FormatMessage>
from formatjs.
Nice! Definitely a step in the right direction. Your keys will be implied which screws up any stateful components in case of updated sentences but that can be fixed later.
from formatjs.
Although since you're not actually embedding them as children, I'd probably still prefer the message={""}
prop instead of JSX children.
from formatjs.
Your keys will be implied which screws up any stateful components in case of updated sentences but that can be fixed later.
I don't think I follow what you're getting at here.
I'd probably still prefer the
message={""}
prop instead of JSX children
Yeah, the only tradeoff with this is that the ICU Message string cannot contain a message
variable; e.g.:
var ERR_MESSAGE = "Error {code}: {message}";
Since in React-land the message
prop would be special, the dev would need to update the above string to something like:
var ERR_MESSAGE = "Error {errCode}: {errMessage}";
Then when they use React Intl things would work properly:
<FormatMessage message={ERR_MESSAGE} errCode={404} errMessage="Not Found." />
So that's one tradeoff I'm wrestling with, and as @caridy mentioned earlier, we want to encourage people to externalize their strings since they will be different depending on the current user's locale.
from formatjs.
Your keys will be implied which screws up any stateful components in case of updated sentences but that can be fixed later.
I don't think I follow what you're getting at here.
I mean React keys. Normally you would do something like <AnimatedEmoticon key="the-smiley" />
to indicate that this particular item should retain state even if its position within the set changes. E.g. switching between two different messages or sentence order.
Since in React-land the message prop would be special
In React-land you will also have the same trade-off with the children
prop regardless. You can't do:
<FormatMessage children="something">{"something else {children}"}</FormatMessage>
Because the child position in React's JSX is just an alias for the children
prop.
from formatjs.
Because the child position in React's JSX is just an alias for the
children
prop.
Yeah, good point. Saying it this way makes that much clearer to me 😄
from formatjs.
Update
So it seems we're all converging on:
- Prefix React Intl Component
exports
withFormatted
. - React Intl Components should be self-closing and not use nesting. #66
Examples
import React from 'react';
import {
IntlMixin,
FormattedDate,
FormattedMessage,
FormattedNumber,
FormattedRelative
} from 'react-intl';
var intlData = {
locales: ['en-US'],
formats: {
number: {
USD: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2
}
}
},
messages: {
POST: 'Posted {ago}, {likes, plural, one{# like} other{# likes}}'
}
};
var App = React.createClass({
mixins: [IntlMixin],
render: function () {
return (
<FormattedNumber value={4200} locales="fr-FR" />
<FormattedNumber value={0.9} style="percent" />
<FormattedNumber value={99.95} format="USD" />
<FormattedDate value={new Date()} />
<FormattedRelative value={Date.now() - 2000} />
<FormattedMessage message={this.getIntlMessage('POST')}
ago={<b><FormattedRelative value={post.date} /></b>}
likes={post.likes} />
);
}
});
Read.render(
<App {...intlData} />,
document.getElementById('container')
);
from formatjs.
#66 and #67 have been merged which implement decision made against the questions in this thread.
from formatjs.
@sebmarkbage the approach you're using at facebook would be my preferred option. that makes it really easy to read the source code, and understand the format of the message from within the component itself! reminds me of the soy template msg
command. do you have any longer-term plans to open source any of that?
from formatjs.
Related Issues (20)
- @formatjs/intl-numberformat @ 8.9.0 -> 8.10.0 breaks Chrome 104 HOT 1
- cli-lib: onMsgExtracted never gets called HOT 1
- Update translation of 'last year' for Swedish locale HOT 1
- @formatjs/cli-lib is not an ESM module HOT 1
- formatMessage with ICU behave different then formatNumber HOT 2
- [@formatjs/intl Error MISSING_DATA] should be a warning not an error HOT 3
- [@formatjs/intl-durationformat] package.json deprecation warning HOT 2
- @formatjs/intl-messageformat, 10.5.0: Plural rules aren't applied correctly HOT 2
- @formatjs/intl-numberformat/polyfill: roundingIncrement seems to be a no-op HOT 3
- [docs] Rich Text Formatting example doesn't work HOT 2
- Polyfill.io has been sold to a weird Chinese company and should no longer be recommended HOT 1
- chore: update tzdata to 2024a
- INVALID_TAG when parsing regular HTML tag with attribute HOT 1
- Can't catch error HOT 1
- Feature request: allow units greater than `day` in `FormattedRelativeTime` with `updateIntervalInSeconds` to be picked HOT 2
- It's incredibly slow on lower end Android devices HOT 2
- Intl.Segmenter: isWordLike does not match Node.js and browsers HOT 2
- @formatjs/intl-locale/polyfill-force TypeError: Error uninitialized locale at Locale2.getWeekInfo HOT 3
- .formatMessage is not working when provided messages has object with `type: 1` HOT 8
- Extract does not work on vue template since vue 3.4.0 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from formatjs.