Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use as static methods #41

Closed
klimashkin opened this issue Dec 9, 2014 · 37 comments
Closed

Use as static methods #41

klimashkin opened this issue Dec 9, 2014 · 37 comments

Comments

@klimashkin
Copy link

Hello!
Is it possible to use format methods in static component methods?

@caridy
Copy link
Collaborator

caridy commented Dec 9, 2014

can you elaborate more?

@pselden
Copy link

pselden commented Dec 10, 2014

I'm not sure if this is what @klimashkin is asking for, but I've found myself wanting to use the this.formatMessage, this.getIntlMessage, etc... methods from outside of a React component. It seems like we'll need to also include intl-messageformat directly and rebuild much of logic for caching/getting messages that react-intl already does.

For instance, when writing a validator for a form which returns error messages, I would want to have my validation logic outside of my component and pass back an internationalized/localized error message that my component could then display.

@klimashkin
Copy link
Author

Yes, @pselden understood me correctly. Sometimes I need to format message outside of component instance

@ericf
Copy link
Collaborator

ericf commented Dec 10, 2014

You guys can use the IntlMessageFormat API directly by depending on intl-messageformat as well as react-intl.

@ericf
Copy link
Collaborator

ericf commented Dec 17, 2014

Does using intl-messageformat directly work for you guys?

Our original vision is that we'd build up APIs like IntlMessageFormat and IntlRelativeFormat that look at work similar to the built-in Intl.NumberFormat and Intl.DateTimeFormat APIs. So when you wanted to format data in pure JavaScript code, you'd use these APIs directly. And the high-level integrations like React Intl provide a declarative sugar layer over top of the low level APIs.

@deviousdodo
Copy link

@ericf Is there any particular advantage of using this.formatMessage instead of something like t()?
What I'm thinking:

var Intl = require('intl');
Intl.t('my.string'); // this can be used anywhere

This approach would get rid of the mixin and having to pass messages as props. You can also reuse it from anywhere.

Yes, it can be built using IntlMessageFormat (ie. a new adapter), but I don't see why you guys have built a mixin instead of providing some generic functionality.

@klimashkin
Copy link
Author

Yep, intl-messageformat good, but in this way react-intl became redundant in project

@caridy
Copy link
Collaborator

caridy commented Dec 17, 2014

guys, let's not be opinionated here. we do offer:

  1. The React way by providing react components (e.g.: <ReactIntl.Number style="percent">{0.95}</ReactIntl.Number>)
  2. A React Mixin: ReactIntl.Mixin that allow you to add new methods to your existing components (e.g.: this.getIntlMessage('SHORT') to look up for message SHORT, but also low level methods to format numbers, dates and messages with the corresponding cache layer on top of react.
  3. A low level API to access the helper methods to programmatically call them when needed, without relying on React components lifecycle (e.g.: require('intl-messageformat')).

If none of those 3 options fulfill your needs, just provide more information and we can evaluate what else can we do to help you.

@caridy caridy closed this as completed Dec 17, 2014
@ericf
Copy link
Collaborator

ericf commented Dec 17, 2014

@ericf Is there any particular advantage of using this.formatMessage instead of something like t()?
What I'm thinking:

var Intl = require('intl');
Intl.t('my.string'); // this can be used anywhere

@deviousdodo We don't plan on extending the built-in Intl object since that could be future hostile, that's why we provide message formatting via the intl-messageformat package at IntlMessageFormat.

Here are the advantages that the React Intl Mixin and upcoming React Intl Components provide over our core intl-* libraries:

  1. A declarative way to format data in templates.
  2. Caching of Intl* instances.
  3. Automatic lookup of locales, messages, and formats within the React component hierarchy.
  4. Convenience for custom named formats.

Yes, it can be built using IntlMessageFormat (ie. a new adapter), but I don't see why you guys have built a mixin instead of providing some generic functionality.

This is not either or, we provide both a set of core libraries and a set of high level integration libraries built on the core.

@ericf ericf removed the needs info label Dec 17, 2014
@deviousdodo
Copy link

@ericf Thank you for the answer!

However, it seems my comment wasn't clear enough. When I said require('intl') I wasn't referring to the built-in object, but to a "generic" adapter. I do agree that having an adapter has advantages over using the intl-* libraries and I was in fact advocating an adapter that's not tied to a specific library. So my code would probably be more clear if I write it like this:

var i18n = require('generic-intl-adapter'); // sugar package, just like react-intl, without the react integration
i18n.formatMessage('my.example.message'); // same API as the react component, just without the "this." prefix
i18n.formatNumber(1000, { style   : 'currency', currency: 'USD' }); // etc

I believe this approach would have the same benefits you outlined above, plus you wouldn't have to propagate the message, formats, etc throughout the react component hierarchy (they would just be added to an i18n object used everywhere) and you wouldn't need to add a mixin to every react component.

@caridy Right now we're using react-intl as is and it serves our needs. My comment was more of a question rather than a statement, sorry if it came out differently. Given the low-level API we'll be able to build anything we want, if need be.

@ericf
Copy link
Collaborator

ericf commented Dec 18, 2014

@deviousdodo Okay I see, that's more clear now.

@caridy and I have been debating this. We're trying to determine if we want people to just use the low-level APIs like Intl.NumberFormat, IntlMessageFormat, etc. or provide a higher-level library for use in JavaScript — probably called Format.js 😄

I've also been investigating how we can support ES6 template strings, as the way to provide a declarative way to format messages in pure JavaScript code. And this is something we'll likely be supporting since the implementation is trivial to do so:

var num = 1000;
var msg = formatMessage`{${num}, number, integer} photos`
console.log(msg); // => "1,000 photos"

So for now, our answer is use the Intl* apis, if you want caching support you can also use the intl-format-cache package on npm which memoizes the Intl* constructors. And know that we're actively working on creating a possible Format.js lib that's higher level, and we could use that as the base for all our higher level template libs integrations as well.

@deviousdodo
Copy link

@ericf A Format.js lib would be awesome, both standalone and as the base for the lib adapters (since it would make writing other adapters easier).

Thanks again for the detailed reply!

@steffenmllr
Copy link

I'm also struggling with this - is there a way to access the translations outside of the component? Without the mixin? I'm trying get some model validation messages translated that don't live inside a component and couldn't find something in the docs

@steida
Copy link

steida commented Mar 4, 2015

@steffenmllr Have you succeeded? I suppose for formating outside component, we can bypass whole react-intl.

@DmitryOlkhovoi
Copy link

Oh i'm stuck... how i can use it outside component? I need translate message on action creator

export const fetchNotificationsFailure = () => ({
  type: appConstants.SHOW_ERROR,
  message: 'Please try to the refresh page. Error code: #n1.',
});

@steida
Copy link

steida commented Apr 12, 2016

It's easy. First, you have to realize everything happen in component, at least in app component
https://github.com/este/este/blob/master/src/common/app/start.js

You will render message on UI sooner or later, directly or with side effects helper. Check Este https://github.com/este/este/blob/master/src/browser/auth/LoginError.react.js

@zabojad
Copy link

zabojad commented Jun 20, 2016

Not being able to translate messages outside of components is just a pain, I have to move controller code to views just because of that... And for things that do not concern views at all like generating emails, etc...

We should definitively be able to translate things outside of components but with the same intl instance as the one used by our components (so that we do not have to provide twice the messages, etc...)...

@ericf
Copy link
Collaborator

ericf commented Jun 20, 2016

@zabojad React Intl builds on a set of core libraries: http://formatjs.io/github/#core

@zabojad
Copy link

zabojad commented Jun 20, 2016

@ericf I know that. I've tried to used Intl MessageFormat outside of my react components but when I do so, I have to give it all the app messages and locale data as it's another instance of intl than the one used by my IntlProvider at the root of my App component... IMO, there should be a way to use the same intl instance for both...

@ericf
Copy link
Collaborator

ericf commented Jun 20, 2016

@zabojad

Everything goes through IntlProvider have you tried creating an instance of that?

import {IntlProvider} from 'react-intl';

const {intl} = new IntlProvider({
  locale: 'en',
  messages: {},
}, {}).getChildContext();

intl.formatNumber(1000); // "1,000"

@lizhenhua86
Copy link

@ericf your solution is work for me. thank you very much.

@cwtuan
Copy link

cwtuan commented Jun 14, 2017

@klimashkin, Try https://www.npmjs.com/package/react-intl-universal, it Internationalize React apps not only for React.Component but also for Vanilla JS. It's just the right tool for your question.

import intl from 'react-intl-universal';
class App extends React.Component {
  render() {
     return <div> intl.get('key') </div>
  }
}

@mschipperheyn
Copy link
Contributor

mschipperheyn commented Oct 16, 2017

If you use React 16, this is now possible simply with a little sugar. The reason it is possible is that React 16 allows you to return text directly from a component now. The difference with FormatMessage is that FormatMessage returns a <span>Result text</span> around your result text.

import React from 'react';
import { injectIntl } from 'react-intl';

const GetMessage = ({ id, params, intl }) => intl.formatMessage({ id, params });

export default injectIntl(GetMessage);

<GetMessage id="ui.key"/>

Unfortunately, the result is still a React Element. Just without the extra span.

@MarcoNicolodi
Copy link

MarcoNicolodi commented Feb 7, 2018

Hey guys.

Its been three years since this issue has been opened.

Considering the fact that the current user language is stored in the redux store, have you guys come up with common pratices to vanilla js intl with react-intl ?

For example i have this validation library which my file-uploader component uses to validate a given file:

const maxSize = (file, size) => {
    if (file.size > size) {
        return 'File size is bigger than the max allowed';
    }
}

This is not the whole code, but should i just return a messageId, which makes my client code dependent on react-intl, or should i turn this .js file into a .jsx file and return a FormattedMessage? Both of them dont look any good, but which approach have you guys done lately?

We also have the case which a alert message component listens to notification actions emmited by the store. If my component only accepts a message id, i would lose features provided by FormattedMessage such as plularization and arguments. This is an exemple of an action that dispatches a alert message:

.catch(() => {
    showDangerNotification(<FormattedMessage id="AnErrorOcurred" />);
});

@adyz
Copy link

adyz commented Feb 27, 2018

@ericf - do you think that creating a function out of your code like this

import { FormattedMessage, IntlProvider } from 'react-intl';
import { language, messages } from '../../i18nSetup';

const {intl} = new IntlProvider({locale: language, messages}, {}).getChildContext();

const translate = (id: string, values?: {}) => {
    return intl.formatMessage({id}, values);
};

export default translate;

and using it all over a react app

// a react component
import translate from '../translate';

// a react class
<h1>{translate('key.id')}</h1>

would create any performance issues compared with the recommended way <FormatMessage id="">?

@veddermatic
Copy link

@adyz : Have you been using your solution, and if so, how does it perform?

@mtcanada
Copy link

@adyz Any update on the performance of this code?

@longlho
Copy link
Member

longlho commented Mar 27, 2020

this issue is very old, what r you looking for specifically?

@mtcanada
Copy link

mtcanada commented Mar 27, 2020

@longlho I have 2 questions.
First thing is for using React-Intl API outside of React components. I know we can now use 'createIntl' to achieve this, and I'm wondering whether it is okay to make a util file with the custom functions like the following and use it everywhere. I did not want to call createIntl() and getLocale, message again and again everywhere.

import { createIntl, createIntlCache } from 'react-intl';

const locale = getLocale(); // this is my func
const messages = getMessage(); // this is my func 
const cache = createIntlCache();
const intl = createIntl({locale, messages}, cache);

export function formatMessage(message) {
    intl.formatMessage(message);
}

@mtcanada
Copy link

mtcanada commented Mar 27, 2020

Also, secondly, I know for the class component, we need to call injectIntl() to wrap the class component and preferably I do not want to wrap so many components just to have the access to the props of Intl. So is there any way to being able to use Intl without calling injectIntl()?

OR if the solution above is feasible, why don't we just call the custom function created above to use Intl in the class component?

I'm guessing this is not possible, otherwise, there wouldn't be so much discussion around InjectIntl() when there is a simple solution like this. Thank you in advance.

@longlho
Copy link
Member

longlho commented Mar 27, 2020

@longlho I have 2 questions.
First thing is for using React-Intl API outside of React components. I know we can now use 'createIntl' to achieve this, and I'm wondering whether it is okay to make a util file with the custom functions like the following and use it everywhere. I did not want to call createIntl() and getLocale, message again and again everywhere.

import { createIntl, createIntlCache } from 'react-intl';

const locale = getLocale(); // this is my func
const messages = getMessage(); // this is my func 
const cache = createIntlCache();
const intl = createIntl({locale, messages}, cache);

export function formatMessage(message) {
    intl.formatMessage(message);
}

Yes you can do that, we're doing at at Dropbox.

@mtcanada
Copy link

@longlho Sorry the discussion is kind of everywhere. Yes, I would like to use this custom formatMessage in react class component as well, and it seemed like its working fine.

Do you see any potential issue with this approach of creating the util file like above rather than calling InjectIntl() for every class component?

@mtcanada
Copy link

The discussion is related to #1609

@mtcanada
Copy link

@longlho just before closing this old thread and moving to the new one, could you answer the question about the potential issue with using this custom function in class components when you get chance? Thank you so much

@longlho
Copy link
Member

longlho commented Mar 27, 2020

Since locale & messages are global you'd have to make sure your code operate in a runtime where those variables never change, which is generally the correct assumption for code in browser (but not SSR). Other than that it's ok.

@mtcanada
Copy link

@longlho sorry i'm a bit confused by 'operate in a runtime where those variables never change'. Why they should not change when you use this approach?

We allow users to choose a language on a setting page and we set locale to the chosen language so locale does change sometimes.

@longlho
Copy link
Member

longlho commented Mar 27, 2020

Every time you change locale or messages or config you need to re-create intl object. Based on your code snippet above it looks like you don't?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests