13

Currently I have the following code to expose react-intl to non-components, but it throws an error for intl as undefined.

I have created a separate component as 'CurrentLocale' and inject-intl to it. The exporting function t will use intl formatMessage from CurrentLocale context.

import React from 'react';
import {injectIntl} from 'react-intl';
import PropTypes from 'prop-types';
import { flow } from 'lodash';

class CurrentLocale extends React.Component {


    constructor(props,context){

        super();
        console.log(context,props);
        console.log(this.formatMessage);
        const { intl } = this.context.intl;//this.props;

        this.formatMessage = intl.formatMessage;
    }

    render() {
        return false;
    }
}
CurrentLocale.contextTypes={
    intl:PropTypes.object,
};


injectIntl(CurrentLocale);

function intl() {
    return new CurrentLocale();
}

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


const t = opts => {
    const id = opts.id;
    const type = opts.type;
    const values = opts.values;
    let t;

    switch (type){

        case 'message':
        default:
            t = formatMessage(id, values);
    }

    return t;
}

export default t;

t is called as in another plain javascript file as,

import t from './locale/t';
t( { type: 'message', id:'button.Next'});

Following is the error message. enter image description here Thanks in advance.

Kanishka
  • 357
  • 1
  • 4
  • 16

5 Answers5

15

There's also another approach very simple I used for solving a similar problem: Provide access to the intl object for a non-component:

import { IntlProvider, addLocaleData } from 'react-intl';
import localeDataDE from 'react-intl/locale-data/de';
import localeDataEN from 'react-intl/locale-data/en';
import { formMessages } from '../../../store/i18n'; // I defined some messages here
import { Locale } from '../../../../utils'; //I set the locale fom here

addLocaleData([...localeDataEN, ...localeDataDE]);
const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
const intlProvider = new IntlProvider({ locale, messages });
const { intl } = intlProvider.getChildContext();

export const SCHEMA = {
  salutation: {
    label: intl.formatMessage(formMessages.salutationLabel),
    errormessages: {
      required: intl.formatMessage(formMessages.salutationError),
    },
  },
  academic_title_code: {
    label: intl.formatMessage(formMessages.academicTitleLabel),
  },
};

It's working like a charm!

UPDATE for v3.x

After migration to react-intl 3.x

import { createIntl, createIntlCache } from 'react-intl'
import { formMessages } from '../../../store/i18n'; // I defined some messages here
import { Locale } from '../../../../utils'; //I set the locale fom here

const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache();
const intl = createIntl({ locale, messages }, cache)

export const SCHEMA = {
  salutation: {
    label: intl.formatMessage(formMessages.salutationLabel),
    errormessages: {
      required: intl.formatMessage(formMessages.salutationError),
    },
  },
  academic_title_code: {
    label: intl.formatMessage(formMessages.academicTitleLabel),
  },
};
sergioviniciuss
  • 4,131
  • 2
  • 29
  • 44
  • In the new version of react-intl, this solution result in error which says `.getChildContext is not a function`. – saman.shahmohamadi Aug 19 '19 at 09:11
  • 1
    I update my libs once a month and will probably have this issue then next week when I do that. but last time I checked (last month), with 2.9.0 it works. Thanks for pointing the issue. I'll keep an eye on it once I update it as I use it A LOT and probably it might break my application. – sergioviniciuss Aug 19 '19 at 09:16
6

There's a new way to do it pretty easily with createIntl, it returns an object that you can use outside React components. Here's an example from the documentation.

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

// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache()

const intl = createIntl({
  locale: 'fr-FR',
  messages: {}
}, cache)

// Call imperatively
intl.formatNumber(20)

// Pass it to IntlProvider
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>

I personally store the intl object in Redux store so I can access it everywhere in my app.

michauwilliam
  • 734
  • 1
  • 9
  • 19
Valentin Duboscq
  • 875
  • 8
  • 19
3

This line: const { intl } = this.context.intl; should be const { intl } = this.context;

Here is a reference post of someone doing almost the exact same thing as you are: https://github.com/yahoo/react-intl/issues/983#issuecomment-342314143

In the above the author is creating essentially a singleton that is exported instead of creating a new instance each time like you have above. This might be something you want to consider as well.

mrjman
  • 126
  • 1
  • 2
1

There's also another way solving a similar problem to used react-intl formatMessage for non-components.

Create a LocaleStore.js store file.

 import _formatMessage from "format-message";

    export default class LocaleStore {

      formatMessage = (id, values) => {
        if (!(id in this.messages)) {
          console.warn("Id not found in intl list: " + id);
          return id;
        }
        return _formatMessage(this.messages[id], values);
      };
    }

import LocaleStore your CombinedStores.js

import LocaleStore from "./stores/LocaleStore";
import en from "./translations/en";
import de from "./translations/de";
import Global from "./stores/global"
const locale = new LocaleStore("en", {
  en,
  de
});
export default {
global:new Global(locale) 
}

now you can use this in your GlobalStore.js

    class GlobalStore {
    constructor(locale) {
     this.locale = locale;
    }

   formatMessage=(message_is,formatLanguage="en")=> {
     return  this.locale.formatMessage(message_id, formatLanguage);
    }
  }
durga patra
  • 191
  • 3
  • 10
0

If you can accept to use a function component I prefer to use the useIntl hook

https://reactjs.org/docs/components-and-props.html#function-and-class-components

I can then get values like this:

import { useIntl } from "react-intl";

const intl = useIntl()
intl.formatMessage({ id: 'myId' }),

https://formatjs.io/docs/react-intl/api/#useintl-hook

Ogglas
  • 38,157
  • 20
  • 203
  • 266