Code Monkey home page Code Monkey logo

Comments (23)

caridy avatar caridy commented on May 4, 2024 1

beautiful examples should not drive our api. 🍦

from formatjs.

ericf avatar ericf commented on May 4, 2024

/cc @caridy @pselden @mridgway @redonkulus @g13n @sebmarkbage

from formatjs.

caridy avatar caridy commented on May 4, 2024

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.

caridy avatar caridy commented on May 4, 2024

As for my proposal, I'm not sure between <FormatMessage value={this.something} /> vs <FormatMessage message={this.something} />

from formatjs.

ericf avatar ericf commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

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.

ericf avatar ericf commented on May 4, 2024

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.

ericf avatar ericf commented on May 4, 2024

@sebmarkbage I'm very interested in your thoughts around:

  1. Noun-y vs. Verb-y component names in React
  2. 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.

turadg avatar turadg commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

@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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

@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.

ericf avatar ericf commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

Note that for pluralization etc, we use a different one of these templates at runtime for each possible variation.

from formatjs.

ericf avatar ericf commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

Although since you're not actually embedding them as children, I'd probably still prefer the message={""} prop instead of JSX children.

from formatjs.

ericf avatar ericf commented on May 4, 2024

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.

sebmarkbage avatar sebmarkbage commented on May 4, 2024

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.

ericf avatar ericf commented on May 4, 2024

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.

ericf avatar ericf commented on May 4, 2024

Update

So it seems we're all converging on:

  1. Prefix React Intl Component exports with Formatted.
  2. 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.

ericf avatar ericf commented on May 4, 2024

#66 and #67 have been merged which implement decision made against the questions in this thread.

from formatjs.

necolas avatar necolas commented on May 4, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.