Why does defineMessages from react-intl throw an error when passing in a object reference?

4.2k Views Asked by At

I am trying to get my head around react-intl from the Yahoo! i18n project and I am running into a weird issue. My goal is to store the base string (English) in some sort of JSON files outside the components so they can be edited by non-devs.

It seems logical than I can just import them and then use the parts I need in the component, but the defineMessages function causes an error when I do this.

Edit: The issue seems to be around the babel-plugin-react-intl plugin and "exporting" the default strings. The app runs fine, but when the npm run build command is run the error occurs.

.babelrc:

{
    "presets": [
        "es2015",
        "react"
    ],
    "plugins": [
        ["react-intl", {
            "messagesDir": "./build/messages/"
        }]
    ]
}

webpack-config:

module.exports = {
  entry: './src/app.js',   // The startingpoint of the app
  output: {
    filename: 'bundle.js',  // Name of the "compiled" JavaScript.
    path: './dist',         // Which dir to put it on disk.
    publicPath: '/',        // Which relative path to fetch code from on the client.
  },
  module: {
    loaders:[
      {
        test: /\.jsx?$/,            // Convert ES2015/React-code into ES5.
        exclude: /node_modules/,
        loader: 'babel'
      },
      {
        test: /\.json$/,            // Load JSON-files into code base.
        exclude: /node_modules/,
        loader: 'json',
      },
    ]
  },
};

package.json:

{
  "name": "intl3",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "dependencies": {
    "babel-core": "^6.14.0",
    "babel-loader": "^6.2.5",
    "babel-plugin-react-intl": "^2.2.0",
    "babel-preset-es2015": "^6.14.0",
    "babel-preset-react": "^6.11.1",
    "eslint": "^3.3.1",
    "eslint-loader": "^1.5.0",
    "eslint-plugin-babel": "^3.3.0",
    "eslint-plugin-react": "^6.1.2",
    "json-loader": "^0.5.4",
    "react": "^15.3.2",
    "react-dom": "^15.3.2",
    "react-intl": "^2.1.5",
    "webpack": "^1.13.2",
    "webpack-dev-server": "^1.16.1"
  },
  "devDependencies": {
    "babel-plugin-react-intl": "^2.2.0",
    "babel-preset-react": "^6.16.0",
    "json-loader": "^0.5.4"
  },
  "scripts": {
    "start:dev": "webpack-dev-server --content-base ./ --config webpack.config.js",
    "prebuild": "cp index.html ./dist/index.html",
    "build": "webpack --config webpack.config.js",
    "start": "http-server dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Code that works:

import React from 'react';
import { FormattedMessage, defineMessages } from 'react-intl';

const strings = defineMessages({
    "title": {
        "id": "TITLE",
        "description": "Title of the app.",
        "defaultMessage": "Intl Company, Inc."
    },
    "menu": {
        "id": "MENU",
        "description": "Word for 'menu'.",
        "defaultMessage": "Menu"
    }
});

const Header = (props) => {
    return (
        <header>
            <div>
                <FormattedMessage {...strings.title} values={ { name: 'World' } } />
            </div>
        </header>
    );
};

export default Header;

Code that fails:

const headerStrings = {
    "title": {
        "id": "TITLE",
        "description": "Title of the app.",
        "defaultMessage": "Intl Company, Inc."
    },
    "menu": {
        "id": "MENU",
        "description": "Word for 'menu'.",
        "defaultMessage": "Menu"
    }
};

const strings = defineMessages(headerStrings);

Error message I get when trying to pass a reference instead of object directly:

./src/components/Header.js
Module build failed: SyntaxError: [React Intl] `defineMessages()` must be called with an object expression with values that are React Intl Message Descriptors, also defined as object expressions.

  17 | };
  18 | 
> 19 | const strings = defineMessages(headerStrings);
     |                 ^
  20 | 
  21 | const Header = (props) => {
  22 |     return (

BabelLoaderError: SyntaxError: [React Intl] `defineMessages()` must be called with an object expression with values that are React Intl Message Descriptors, also defined as object expressions.

  17 | };
  18 | 
> 19 | const strings = defineMessages(headerStrings);
     |                 ^
  20 | 
  21 | const Header = (props) => {
  22 |     return (
3

There are 3 best solutions below

0
On BEST ANSWER

The behavior of defineMessages is not a bug. This function is a hook to "scrape" the default messages from the component. If you want to include your strings from a JSON import, defineMessages is not necessary since it's purpose is to export your default messages to pass off to a translator.

0
On

Found this when searching for the same issue and thought I'd leave the answer that helped me the most (based on the title of this question). defineMessages is intended for use with babel-plugin-react-intl and I think this issue describes the intended functionality better and why passing in a previously defined object doesn't work and why you have to define the message in the call to defineMessages

To quote Eric's response:

Since Babel is a compiler and not a JavaScript interpreter or runtime you'll need to define the messages you want extracted at the defineMessages() call site. This means that defineMessages() should only be used where the message is actually defined, and you shouldn't use it where the message is referenced.

The whole point of this plugin is to extract the messages from source code and defineMessages() is a hook for this and it's expected that the message descriptor exists within the call site. defineMessages() is an identify function and returns the object passed in, so the most common use case is:

const messages = defineMessages({
    greeting: {
      id: 'home.greeting',
      defaultMessage: 'Hello, {name}!'
    }
});

<FormattedMessage {...messages.greeting}/>
5
On

If you are using babel-plugin-react-intl together with webpack, make sure you are only loading the babel plugin once, either through .babelrc or webpack.config.js, but not in both as it causes the plugin to be loaded multiple times, resulting in the exact same error when trying to run it through webpack.