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

Getting formatted message without JSX or react just the formatted string for titles, aria-labels etc. #157

Closed
andreimc opened this issue Sep 5, 2015 · 14 comments

Comments

@andreimc
Copy link

andreimc commented Sep 5, 2015

Hey guys,

I have a react-intl/redux project and I am just wondering how can I format the string just without react component?

I need this for titles, aria-lable etc.

I currently have my own getIntlMessage(path) method I am just wondering how to apply the format to it as well.

for example:

const en = {
  'greeting': 'Hey ${name}'
}

const formattedMsg = someMagicMethod(getIntlMessage('greeting'), {name: 'Bob'})

should be Hey Bob 

Regards,
Andrei

@andreimc
Copy link
Author

andreimc commented Sep 5, 2015

I found a solution using intl-messageformat something like:

function formattedMessage(message, locale, values) {
  const messageFormat = new IntlMessageFormat(message, locale);
  return messageFormat.format(values);
}

any better ways of doing this?

@ericf
Copy link
Collaborator

ericf commented Sep 8, 2015

This has been addressed on the v2-dev branch.

@ericf
Copy link
Collaborator

ericf commented Sep 11, 2015

See: #162

@ericf ericf closed this as completed Sep 11, 2015
@bartvanandel
Copy link

How exactly is this addressed in v2-dev? I see that you can use formatMessage from within a React component, but I could not find an obvious way to do the same thing without JSX or React. As an example, I'm trying to create an internationalized data container, which by itself does not render anything and thus does not extend from React.Component. How to do this?

@ericf
Copy link
Collaborator

ericf commented Feb 26, 2016

@bartvanandel you're looking in the wrong place then. This lib is React bindings to the Intl built-ins plus a few extensions.

See: http://formatjs.io/github/

@bartvanandel
Copy link

@ericf I know what this lib is doing. We are using it in a React app with internationalized components. This app however also uses classes which are not visual components (i.e. data managers), which also have the ability to generate messages. These should be internationalized as well, preferably using the same approach of calls to defineMessages and formatMessage. I'm currently investigating for a way to do this and I was hoping this library could help us with that as well.

Edit: I see now that there was some discussion on this subject in the comments of #162, but I did not find a (good) solution there. Maybe using Redux could help us solve the issue, using something like mentioned in the "deprecation" message here: https://github.com/overblog/react-redux-intl. Will investigate.

@ericf
Copy link
Collaborator

ericf commented Feb 29, 2016

In order to use this lib, any formatting must happen within the context of an <IntlProvider>. Formatting happens by either using the imperative API or the components (which use the imperative API under the hood.)

I'm trying to create an internationalized data container

You'd have to provide more details about what you mean here.

@bartvanandel
Copy link

Fair enough.

What I'm trying to do here is making sure that non-visual components don't end up as React Components, which are essentially made to be rendered. Example: we have a set of classes which parse configuration files which are read from a server. Obviously such a configuration parser, and the configuration data itself, are not visual components and should therefore not derive from React Component in my opinion. But when it comes to strings, we are providing some defaults (fallback only, using getters) which should be translated to the language of the user. Hence, internationalization outside of React Components.

Currently I've written a workaround which basically relies on the application being initialized (which it always is). Inside our main app I'm including a CurrentLocale component, which upon initialization populates a static variable with itself. Functions like formatMessage(...args) are exported from the same file, which essentially simply forward all arguments to CurrentLocale.instance.props.intl.formatMessage(...args). This works, but it's not the most elegant solution. Now if react-intl could do this for us...

@ericf
Copy link
Collaborator

ericf commented Mar 1, 2016

Now if react-intl could do this for us...

Do what exactly?

@bartvanandel
Copy link

Do what exactly?

Provide a way to use at least some part of the functionality outside of React Components. Like defineMessages for instance, which admittedly doesn't do much (I know it mostly acts as an anchor which is easily findable by babel-plugin-react-intl).

Our current workaround:

CurrentLocale.js, which is rendered once in the App main:

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

// Does not actually render anything visible. 
// We need it to provide access to internationalization for classes
// which are not a React component
class CurrentLocale extends React.Component {
    static instance: CurrentLocale = null;

    componentWillMount() {
        if (!CurrentLocale.instance)
            CurrentLocale.instance = this;
    }

    render() {
        return false;
    }
}

export default injectIntl(CurrentLocale);

export function intl() {
    return CurrentLocale.instance.props.intl;
}

export function formatMessage(...args) {
    return intl().formatMessage(...args);
}

SomeClass.js, which is not a React component and does not even use React:

import {defineMessages} from 'react-intl';
import {formatMessage} from './../intl/CurrentLocale.js';

const messages = defineMessages({
    name: {
        id: "MyClass.name",
        description: "Default name",
        defaultMessage: "Name"
    }
});

export default class SomeClass {
    _name: string;
    nameIntl: string;

    constructor({name=undefined, nameIntl="MyClass.name", ...args}) {
        this.name = name;
        this.nameIntl = nameIntl;
    }

    // ...

    get name():string {
        return this._name || (this.nameIntl ? formatMessage({id: this.nameIntl}) : null);
    }
}

Note: we're using Webpack to bundle everything together.

@ericf
Copy link
Collaborator

ericf commented Mar 2, 2016

How is SomeClass.name used in your example above? Where does this formatted string get displayed?

React Intl uses the Intl.NumberFormat and Intl.DateTimeFormat built into JavaScript, along with intl-messageformat and intl-relativeformat which are lower-level libraries we created. If you can't refactor your code to move more things into React components, then you'll want to use these lower-level formatting APIs directly.

@bartvanandel
Copy link

How is SomeClass.name used in your example above? Where does this formatted string get displayed?

That depends. One use is that we are creating some OpenLayers layers, which by default get a localized name (using the nameIntl id), but the name is overridable by the configuration.

It's not like we cannot refactor everything to be a React component, it's just that it does not make sense so I don't want it.

Can we still rely on the defineMessages calls using these lower level libraries?

@ericf
Copy link
Collaborator

ericf commented Mar 3, 2016

That depends. One use is that we are creating some OpenLayers layers, which by default get a localized name (using the nameIntl id), but the name is overridable by the configuration.

If the map is being initialized in React via componentDidMount then you can use injectIntl() to provide React Intl's imperative API to your component, and access this API inside of componentDidMount where you can have side-effects like rendering a map while also localizing numbers, dates, and strings.

Can we still rely on the defineMessages calls using these lower level libraries?

Yes, this method just echoes its input; it's a hook for babel-plugin-react-intl to extract default messages from the app's source code.

@gbeltranl
Copy link

For anyone looking for a solution of this, with the hook useIntl they made it possible.

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

No branches or pull requests

4 participants