Git Product home page Git Product logo

babel-plugin-react-intl's Introduction

This repo was migrated to the monorepo

babel-plugin-react-intl

travis

Extracts string messages for translation from modules that use React Intl.

Dependencies

React Intl

This Babel plugin works with React Intl v2.x

Babel

  • 3.x of this plugin works with Babel 7
  • 2.x works with Babel 6
  • 1.x works with Babel 5

Installation

$ npm install babel-plugin-react-intl

Usage

This Babel plugin only visits ES6 modules which import React Intl.

The default message descriptors for the app's default language will be extracted from: defineMessages(), <FormattedMessage>, and <FormattedHTMLMessage>; all of which are named exports of the React Intl package.

If a message descriptor has a description, it'll be removed from the source after it's extracted to save bytes since it isn't used at runtime.

Via .babelrc (Recommended)

.babelrc

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

Options

  • messagesDir: The target location where the plugin will output a .json file corresponding to each component from which React Intl messages were extracted. If not provided, the extracted message descriptors will only be accessible via Babel's API.

  • enforceDescriptions: Whether message declarations must contain a description to provide context to translators. Defaults to: false.

  • extractSourceLocation: Whether the metadata about the location of the message in the source file should be extracted. If true, then file, start, and end fields will exist for each extracted message descriptors. Defaults to false.

  • moduleSourceName: The ES6 module source name of the React Intl package. Defaults to: "react-intl", but can be changed to another name/path to React Intl.

  • overrideIdFn: A function with the signature (id: string, defaultMessage: string, description: string|object) => string which allows you to override the ID both in the extracted javascript and messages.

  • removeDefaultMessage: Remove defaultMessage field in generated js after extraction.

  • additionalComponentNames: Additional component names to extract messages from, e.g: ['FormattedFooBarMessage']. NOTE: By default we check for the fact that FormattedMessage & FormattedHTMLMessage are imported from moduleSourceName to make sure variable alias works. This option does not do that so it's less safe.

Via Node API

The extract message descriptors are available via the metadata property on the object returned from Babel's transform() API:

require('@babel/core').transform('code', {
  plugins: ['react-intl']
}) // => { code, map, ast, metadata['react-intl'].messages };

babel-plugin-react-intl's People

Contributors

bj00rn avatar c089 avatar ericf avatar houfio avatar joual avatar longlho avatar mattkime avatar okuryu avatar sanichkotikov avatar strml avatar swernerx avatar toadzky avatar ykzts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

babel-plugin-react-intl's Issues

Delete JSON file with extracted messages if component with message definitions was deleted

Is there a way to delete automatically JSON file with extracted from React component messages if that component was deleted/renamed?

This is a bit annoying to go and remove JSON files manually every time I remove or rename a component. And it's easy to forget to do that.

I thought that the problem was in my Babel configuration saying to cache output into the file system but it looks like that's not the case.

Has anybody encountered this problem?

Suggestions for a XLIFF converter?

I guess this is not the ideal place to ask such question but it kinda relates to this project I guess.

So after using this plugin we get the JSON files with the extracted messages, which is great. We can also then easily aggregate / transform all those messages into a single file or any other format by running scripts like this one.

In my company, but I guess in general, I'm evaluating the usage of the XLIFF format as it seems to be the preferred choice of translator agencies.
I was wondering if you know any tool to help with that (so JSON -> XLIFF converter) or if you had some experiences with that. If yes, could you provide some hints which xml elements should be used based on the information extracted by the plugin (the XLIFF 2.0 spec)?

Thanks in advance!

Why does this create multiple files?

Tried this out and it creates a directory structure mirroring the source code structure.

Why not just create a single file? Translators don't care about the source code structure lol.

Also now I have concat every file on the frontend before sending it down to the IntlProvider. A single file would be much better imo.

Edit: What I'm envisioning is, instead of creating an array of objects like this:

[
{ id: 'whatever', defaultMessage: 'message' }
{ id: 'whatever2', defaultMessage: 'another message' }
...
]

babel-plugin-react-intl would automatically generate a flat file for the "en" messages

{ whatever: 'message', whatever2: 'another message' }

So when I'm including messages to pass to I can just import the file generated by babel-plugin-react-intl

import enMessages from './enMessages'
<IntlProvider messages={enMessages} />

Translation files aren't being created

Hi,

I don't know if I'm implementing this wrong.
But I've set a .babelrc file, and I'm calling a FormattedMessage in the app, wrapped with a IntlProvider.
As I understand this should create the translation file in de defined folder?
Is there anything I'm forgetting?

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

The parseError thrown by printICUMessage is not captured properly by babel

When I define my messages with some invalid message syntax which can't be properly parsed by PrintICUMessage, then a parseError is thrown, and then re-thrown here:

This gets swallowed by babel, and a very different error is thrown, making it hard to understand what is going on...

Module build failed: TypeError: C:/Workspace/src/app/messages.js: Error is not a function
    at File.buildCodeFrameError (C:\Workspace\node_modules\babel-core\lib\transformation\file\index.js:407:15)
    at NodePath.buildCodeFrameError (C:\Workspace\node_modules\babel-traverse\lib\path\index.js:143:26)
    at C:\Workspace\node_modules\babel-preset-playlyfe\lib\plugins\react-intl.js:75:37

Apparently removing the second parameter from this line fixes this problem.

PS: I'm using this plugin within my webpack build.

Where do you call defineMessages ?

Hello

Does the defineMessages function need to be called FROM a react component for the babel-plugin-react-intl to work ?

For example, can I call defineMessages in a simple JS file ?

Best regards

Getting an error when iterating over messages

Hello. I am writing an application using react-intl and babel-plugin-react-intl for extracting messages, as it is intended.
However, the code below breaks build with an error "[React Intl] Messages must be statically evaluate-able for extraction."

const keys = [ 'some', 'other', 'thing' ];

const messages = keys.map((item, key) => {
    return (<FormattedMessage key={key}
        description="Sample description"
        id={item}
        defaultMessage={item}
    />);
});

Whilst code like this - doesn't

const key = 'some';

<FormattedMessage
    description="Sample description"
    id={key}
    defaultMessage={key}
/>

I am very confused with such behaviour and would like to hear some possible solutions for this. Thanks.

Module importing syntax

Hello.
I found a restriction. I have 2 react components, one of them using 'Import' syntax:

import {FormattedMessage} from 'react-intl';

And another one using 'require' syntax:

const FormattedMessage = require('react-intl').FormattedMessage;

So, plugin extracts messages only from component with 'Import' syntax. Of course it's not a core, but might be useful for some cases.

Does this plugin work with ES5 code?

Trying to figure out why no messages are being extracted. Debugging through the plugin source, it seems it wants to parse the "imports" from the source file, and finds none. I think this might be because we are still using ES5 syntax. Do we need to move to ES6 in order to use this plugin?

`selectordinal` pretty-prints as `plural`

The pretty-printer is not taking into account the ordinal: true value in the parsed message AST when printing it back out, therefore it's replacing selectordinal with plural in the printed message:

{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} Place
{rank, plural, one {#st} two {#nd} few {#rd} other {#th}} Place

Enable Customization of IMPORTED_NAMES

First, after a few tries at i18n in various systems I have to say that this auto extraction plugin is an AWESOME IDEA. I'm very excited to have a translation toolchain rather than just a bunch of message properties files.

I'd like to request (and will happily PR) a small new feature: User defined extensions to IMPORTED_NAMES.


Basically the use case is this: I have an app with both locale specific translations and tenant specific messages. For example in one tenant a message might read: "Hello and welcome to Brand1" and the other might be "Hello and welcome to Brand2". This means that translated files will be the number of supported tenants * number of supported locales which makes this a 2 dimensional lookup (brand and locale) rather than a 1 dimensional one (locale).

brand1-en-US.json
brand1-en-CA.json
brand2-en-US.json
brand2-en-CA.json

I have been doing a lot of thinking about how to effectively manage/extract this information without a huge burden on the developers and here is what I've come up with:

// Define co-located brand overrides by prefixing the ID with the brand name.
// Sorting/filtering will be a post processing step
var tenantOverrides = defineMessages({
  id: "brand2.my-message"
  default: "Hooray I'm in Brand 2!"
})

// Any use of formatMessage etc will be for the base brand
var message = formatMessage({
  id: "my-message"
  default: "Hooray I'm in Brand 1!"
})

If I had this feature, I would add defineBrandOverrides to use in place of defineMessages when that is what I am doing. This allows me to let developers continue to co-locate all untranslated messages and keep using the message extractor as is. It also means that if there are no overrides there is no additional work.

Add options to ignore non statically evaluate-able keys and values

In my project I often use the following formatted message:

<FormattedMessage id="id" defaultMessage="{value1} {value2}" values={{
  value1: (<FormattedMessage id={`common.${type}`} />),
  value2: (<FormattedMessage id={`common.${value}`} />),
}}/>

And during the messages extraction I'm getting [React Intl] Messages must be statically evaluate-able for extraction..

Can we have some option to ignore these messages and continue to extract?

FormattedMessage evaluated twice, description is removed and enforceDescriptions fails

Hi,

I'm using this message which the extraction plugin (v 2.0) parses twice.
I also attempted using defineMessages();

          <FormattedMessage
              defaultMessage= {'Hello 2, {name}! How are you today?'}
              description= 'Welcome greeting to the user'
              id={'greeting'}
              values={{'name': 'Fox'}}
          />

For some reason the description attribute is being removed and the final .json does not contain the description of the message. If mandatory descriptions are required, the parsing will stop since this message is invalid.

Why do you remove it at index.js#178?

                    if (descriptor.defaultMessage) {
                        storeMessage(descriptor, path, state);

                        attributes.filter(function (attr) {
                            var keyPath = attr.get('name');
                            var key = getMessageDescriptorKey(keyPath);
                            return key === 'description';
                        }).forEach(function (attr) {
                            return attr.remove();
                        }); 
                    }

TypeScript

It would be very nice, if the tool also worked together with TypeScript.

FormattedPlural support

Not sure if this is my misunderstanding or design issue. Right now this plugin doesn't seem to work with FormattedPlural tag from react-intl. Are there any plans to support it or will it be dropped in next versions of react-intl?

messagesObj.get(...).map is not a function AGAIN

It seems to me there is some fishy parsing going on....

I don't think #16 is fixed, BTW.

.babelrc

{
    "plugins": [
        ["react-intl", {
            "messagesDir": "./dist/i18n/",
            "enforceDescriptions": true
        }]
    ]
}

If I do this:

import {defineMessages} from 'react-intl';

const messages = defineMessages({
    anotherText: {
        id: 'anotherText',
        description: 'Some Text',
        defaultMessage: 'Some Text with {title}!'
    },
    yetAnotherText: {
        id: 'yetAnotherText',
        description: 'Yet Another Text',
        defaultMessage: 'Yet Another Text with {title}!'
    }        
});

console.log(messages);

everything works absolutely fine.

If I do this:

import {defineMessages} from 'react-intl';

const messages = {
    anotherText: {
        id: 'anotherText',
        description: 'Some Text',
        defaultMessage: 'Some Text with {title}!'
    },
    yetAnotherText: {
        id: 'yetAnotherText',
        description: 'Yet Another Text',
        defaultMessage: 'Yet Another Text with {title}!'
    }        
};

console.log(defineMessages(messages));

then babel-plugin-react-intl throws this:

messagesObj.get(...).map is not a function
    at PluginPass.CallExpression (<ABRIDGED>\node_modules\babel-plugin-react-intl\lib\index.js:208:51)

Direct copy/paste from package.json

{
  "devDependencies": {
    "babel-core": "^6.7.6",
    "babel-plugin-react-intl": "^2.1.2",
    "babel-preset-react": "^6.5.0",   
    "react-intl": "^2.1.2"
}

I am a bit puzzled.

Unkown options

using this setup in .babelrc

  "babel": {
    "presets": [
      "es2015",
      "react",
      "stage-2"
    ],
    "plugins": [
      "react-intl", {
          "messagesDir": "messages/"
      }]
  },

I got the following error:

ReferenceError: [BABEL] app/App.jsx: Unknown option: /Users/benjaminDreux/depot/front-2/.babelrc.presets

directory gets created outside of messagesDir

I have an app that links to an SDK using npm link. This all works fine, except for babel-plugin-react-intl generating a directory outside of messagesDir.

So the app.json gets created in the correct location:

build/messages/app.json

but the .json files of the SDK (which is in node_modules/boundlessgeo-sdk) get generated in sdk in the root of the repo:


Barts-MacBook-Air:tabbed bartvandeneijnden$ ls -l sdk/js/components/
total 112
-rw-r--r--  1 bartvandeneijnden  staff  1265 Dec  4 09:06 AddLayer.json
-rw-r--r--  1 bartvandeneijnden  staff   157 Dec  4 09:06 Chart.json
-rw-r--r--  1 bartvandeneijnden  staff  1417 Dec  4 09:06 FeatureTable.json
-rw-r--r--  1 bartvandeneijnden  staff   178 Dec  4 09:06 Geocoding.json
-rw-r--r--  1 bartvandeneijnden  staff   154 Dec  4 09:06 GeocodingResults.json
-rw-r--r--  1 bartvandeneijnden  staff   314 Dec  4 09:06 Geolocation.json
-rw-r--r--  1 bartvandeneijnden  staff   124 Dec  4 09:06 HomeButton.json
-rw-r--r--  1 bartvandeneijnden  staff   275 Dec  4 09:06 ImageExport.json
-rw-r--r--  1 bartvandeneijnden  staff  2010 Dec  4 09:06 LayerListItem.json
-rw-r--r--  1 bartvandeneijnden  staff   568 Dec  4 09:06 Measure.json
-rw-r--r--  1 bartvandeneijnden  staff   152 Dec  4 09:06 QGISLegend.json
-rw-r--r--  1 bartvandeneijnden  staff  1044 Dec  4 09:06 QGISPrint.json
-rw-r--r--  1 bartvandeneijnden  staff  1347 Dec  4 09:06 QueryBuilder.json
-rw-r--r--  1 bartvandeneijnden  staff   285 Dec  4 09:06 Select.son

Any pointers to as why this might be happening? This is only when using npm link between app and SDK.

How to create global message keys

According to the example An error will be thrown if there are messages in different components that use the same "id".

If i understand this correctly it's not possible to use global translation messages in multiple components, right? For example, if a button with the word Save is used in 10 different components, one would need to have 10 different translations for it due to the id restriction.

Is there a way around this somehow?

Plugin 0 specified in .... provided an invalid property of "addLocaleData"

Getting this error when I add the plugin to do extraction

Module build failed: Error: Plugin 0 specified in "/...../web-react/.babelrc" provided an invalid property of "addLocaleData"

My .babelrc currently looks like this:

{
    presets: ['react', 'es2015', 'react-hmre', "stage-0"],
    "plugins": [
        ["react-intl", {
            "messagesDir": "./build/lang/",
            "enforceDescriptions": true
            }
        ]
    ]
}

Messages are not always picked up

I managed to get this plugin work a first time.

Add translation was good, the messages keep poping in my './messages/' folder.
Then i decided to remove the folder, and let babel recreate the messages for me.
In this scenario, the messages are not recreated.
I had to go through all the component, make a change in order for the message to be extracted.

I guess this issue depends on the way I use babel any advice on where to look.
If we found something, it could be added as a warning in the readme/relevant docs

Use the plugin with the babel API

require('babel-core').transform("code", { 
       plugins: ["react-intl"], 
        modules: "amd", 
        filename: './temp/components/admin_console/admin_sidebar.jsx'
    });

If i use that code the plugin doesn't seem to work, the file.metadata.modules.imports on line 186 is an empty array so mightHaveReactIntlMessages var is always false.

@ericf were you able to actually run it without the babel-cli?

thanks.

Path must be a string. Received undefined

Hi,

I've tried to install react-intl@next for my react starter project and after I've installed npm3 on my system, I've started to experience the following error which goes away if I remove babel-plugin-react-intl from my project.

Jans-MacBook-Pro-2:learning2016 janvorcak$ npm3 start

> [email protected] start /Users/janvorcak/learning2016
> node src/server/index.js

/Users/janvorcak/learning2016/node_modules/babel-core/lib/transformation/file/index.js:548
      throw err;
      ^

TypeError: /Users/janvorcak/learning2016/src/client/counter/Counter.js: Path must be a string. Received undefined
    at assertPath (path.js:8:11)
    at Object.posix.join (path.js:479:5)
    at PluginPass.exit (/Users/janvorcak/learning2016/node_modules/babel-plugin-react-intl/lib/index.js:155:46)
    at newFn (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/visitors.js:293:19)
    at NodePath._call (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/path/context.js:74:18)
    at NodePath.call (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/path/context.js:46:17)
    at NodePath.visit (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/path/context.js:116:8)
    at TraversalContext.visitQueue (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/context.js:153:16)
    at TraversalContext.visitSingle (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/context.js:113:19)
    at TraversalContext.visit (/Users/janvorcak/learning2016/node_modules/babel-traverse/lib/context.js:197:19)

When I remove a plugin from my setup, everything works fine. Could it be a bug?
You can reproduce it by

git clone https://github.com/jvorcak/universal-react-kit.git
# upgrade babel-plugin-react-intl to latest version
cd universal-react-kit
npm3 install
npm3 start

Thanks for the work you're doing with this plugin.

Description strangely missing using babel 6

I'm in the process of upgrading to babel 6 and noticed this plugin throws an error because of missing description field (with enforceDescriptions: true).

All my messages have the description field, so this is not the problem (also it was working with babel 5).

For example, given this message

const messages = defineMessages({
  builtWith: {
    id: 'footer.builtWith',
    description: 'Mention how the site is built with',
    defaultMessage: 'This site is built with <3 using {link}'
  }
})

I get the following error

SyntaxError: lib/components/Footer.js: [React Intl] Message must have a `description`.
  2 | import { defineMessages, FormattedMessage } from 'react-intl'
  3 |
> 4 | const messages = defineMessages({
    |                  ^
  5 |   builtWith: {
  6 |     id: 'footer.builtWith',
  7 |     description: 'Mention how the site is built with',

If I log the descriptor argument and state.opts of the storeMessage function I see that it's called multiple times

{ id: 'footer.builtWith',
  description: 'Mention how the site is built with',
  defaultMessage: 'This site is built with <3 using {link}' } {}
{ id: 'footer.builtWith',
  defaultMessage: 'This site is built with <3 using {link}' } { messagesDir: './_translations', enforceDescriptions: true }

As you can see there are 2 differences here:

  • the first time description is present, but state.opts is empty
  • the second time description is somehow missing (thus throwing the error) and state.opts is the options defined for the plugin in .babelrc

PS: my .babelrc

{
  "presets": [ "es2015", "react", "stage-0" ],
  "plugins": [
    [ "react-intl", {
      "messagesDir": "./_translations",
      "enforceDescriptions": true
    }]
  ]
}

Any idea what's going on here? Thanks

Workflow for this plugin

Thanks for this plugin, it does what it says (that is extracting translations into message files), but i'm a bit confused of how to actually use this in a production setup. Here's what i found out so far:

  1. The plugin will extract each <FormattedMessage /> etc to a file which resembles the path of the file containing the message, e.g. src/components/Menu.json will contain translations for src/components/Menu.jsx.
  2. Next step is (and this is up to the user of the plugin) to collect all extracted messages into a structure which can be loaded by the application, basically a json object in the form { messageKey: translation}

What i'm wondering is: How to i a) generate message files for different languages and b) when i run the generation again, all changes are overwritten.

I would have assumed that the process looks something like this:

  1. Generate translation files
  2. Give these translation files to a translator who translates the them
  3. Use the translated files to generate the single message file for your application

The problem is: when something in the app changes, let's say i add 2 new pages which need to be translated - all existing translations would be overwritten by the default messages.

I guess i'm missing something here?

Customize output format other than JSON

How do I output like a .yaml file like the below? Setting key, title, and value to the values of react-intl, id, description, defaultMessage respectively.

- translation:
    key: "some.path.to.component"
    title: "Lorem ipsum"
    value: "Blah blah blah"

Does this plugin work?

THIS IS A PREVIEW RELEASE THAT WORKS WITH A FUTURE, UNRELEASED VERSION OF REACT INTL.

Does that mean react-intl v2.0.0-pr-1? Or some other unreleased version?

Nothing I do seems to make this plugin work on react-intl v2.0.0-pr-1.

Required defaultMessage

What is the reason behind requiring the "defaultMessage" field when extracting the strings?
React-Intl can work perfectly fine without this attribute.

Would it be feasible to have an option/parameter for plugin not to require it if specified?
I could do it, and create a PR.

Why separate JSON files per component that uses FormattedMessage

React Intl v2.0.0-pr-1 uses a flat message system, where <FormattedMessage id="greeting" ...> in header.jsx and <FormattedMessage id="greeting" ...> in footer.jsx refers to the same message.

In my application, I have this in header.jsx:

<FormattedMessage
  id="greeting"
  description="Welcome greeting to the user"
  defaultMessage="Hello, {name}! How are you today?"
  values={{name: 'this.props.name'}}
/>

In footer.jsx I have this:

<FormattedMessage
  id="greeting"
  values={{name: 'this.props.name'}}
/>

This works fine, although the [React Intl] Line 16: Message "greeting" is missing a 'defaultMessage' and will not be extracted. warning is a bit annoying since this is intentional.

babel-plugin-react-intl creates these two JSON message files:

  • ./build/messages/app/components/header/header.json
  • ./build/messages/app/components/footer/footer.json (this is empty)

Because of the flat message system, I don't see the point in separating the messages based on the owner components. I would expect that babel-plugin-react-intl instead created just one file: ./build/messages.json

The problem here is, if I later decide to remove the message from header.jsx (which will require me to move description and defaultMessage to footer.jsx), the JSON file I need to give translators for translations jumped from ./build/messages/app/components/header/header.json to ./build/messages/app/components/footer/footer.json

I suppose I could always create a secondary step where I combine the various message JSON files myself, I just don't understand the header.json vs footer.json use case. I'd like at least an option to just create a single messages.json file.

How to use this plugin?

I am not sure what I'm doing wrong, but I cannot get it to work.

I am using gulp and babelify. I have checked that babelify uses the last version of babel-core and I have configure it to load my .babelrc file as follows:

bundler.transform(babelify.configure({
  sourceMapRelative: 'app/js',
  babelrc: '../../.babelrc'
}))

My .babelrc file contains the following:

{
  "plugins": ["react-intl"],
  "extra": {
    "react-intl": {
      "messagesDir": "./",
      "enforceDescriptions": true
    }
  }
}

Do you have an idea of why it's not working?

Unable to use with a higher order component (HOC)

I find the react-intl a bit too verbose for my usage so I'm looking at options.

HOC workaround

To start, I made a really simple shortcut component to shorten usage:

// @flow
import React from 'react'
import {FormattedMessage} from 'react-intl'
import pure from 'recompose/pure'

type MProps = {v?:Object, id: string, m:string}
const M = pure((props:MProps):Element<any> => {
  const { v, m, ...rest } = props
  return <FormattedMessage values={v} defaultMessage={m} {...rest} />
})

export {
  M
}

This takes:

<FormattedMessage id="greeting"
      defaultMessage="Hello, {name}! How are you today?"
      values={{name: this.props.name}}
      />

and shortens it to:

<M id='greeting' v={{name: this.props.name}} m='Hello, {name}! How are you today?' />

Certainly shorter (even if crytpic) but significantly reduces the size of my page component markup by allowing me to use a single line in many cases.

Error

The problem as you may have already guessed is that this babel plugin will not pickup the messages and generate the json file, it yields:

client?80c2:47 ./ui/src/lib/ui/i18n.js
Module build failed: Error: /Users/kross/projects/af_core/ui/src/lib/ui/i18n.js: [React Intl] Messages must be statically evaluate-able for extraction.
SyntaxError: /Users/kross/projects/af_core/ui/src/lib/ui/i18n.js: [React Intl] Messages must be statically evaluate-able for extraction.
  15 | const M = pure((props:MProps):Element<any> => {
  16 |   const { v, m, ...rest } = props
> 17 |   return <FormattedMessage values={v} defaultMessage={m} {...rest} />
     |                                                       ^
  18 | })
  19 | 
  20 | export {
    at File.buildCodeFrameError (/Users/kross/projects/af_core/node_modules/babel-core/lib/transformation/file/index.js:446:15)
    at NodePath.buildCodeFrameError (/Users/kross/projects/af_core/node_modules/babel-traverse/lib/path/index.js:143:26)
    at getMessageDescriptorValue (/Users/kross/projects/af_core/node_modules/babel-plugin-react-intl/lib/index.js:55:20)
    at /Users/kross/projects/af_core/node_modules/babel-plugin-react-intl/lib/index.js:76:25
    at Array.reduce (native)
    at createMessageDescriptor (/Users/kross/projects/af_core/node_modules/babel-plugin-react-intl/lib/index.js:64:26)
    at PluginPass.JSXOpeningElement (/Users/kross/projects/af_core/node_modules/babel-plugin-react-intl/lib/index.js:189:38)
    at newFn (/Users/kross/projects/af_core/node_modules/babel-traverse/lib/visitors.js:301:19)
    at NodePath._call (/Users/kross/projects/af_core/node_modules/babel-traverse/lib/path/context.js:76:18)
    at NodePath.call (/Users/kross/projects/af_core/node_modules/babel-traverse/lib/path/context.js:48:17)
    at /Users/kross/projects/af_core/node_modules/happypack/lib/HappyLoader.js:41:23
    at /Users/kross/projects/af_core/node_modules/happypack/lib/HappyPlugin.js:314:7
    at /Users/kross/projects/af_core/node_modules/happypack/lib/HappyWorker.js:51:5
    at /Users/kross/projects/af_core/node_modules/happypack/lib/applyLoaders.js:270:18
    at /Users/kross/projects/af_core/node_modules/babel-loader/index.js:83:25
    at /Users/kross/projects/af_core/node_modules/babel-loader/lib/fs-cache.js:147:16
    at ReadFileContext.callback (/Users/kross/projects/af_core/node_modules/babel-loader/lib/fs-cache.js:28:23)
    at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:359:13)
 @ ./ui/src/components/SignInForm.js 63:12-37

Any thoughts or strategies to shorten the syntax but get message generation working?

Add an option to include original line numbers within extracted message information

Now that this plugin helps me extract the messages I need to translate (and it works great, thanks!), I am trying to generate a list of message-related issues (untranslated messages, etc.) further in my process. This is why I could use an additional "originalLineNumber" field in the extracted JSON data (or maybe "sourceLineNumber", or any other self-explaining name, of course). That information would let me find more quickly where, in the source code, the message is declared. Of course, the original file name would be useful too, but I can guess it from the JSON file path. And since not everybody is interested in the information, it could be made optional (maybe an "includeOriginalLineNumbers" option?).

Do you think it would be both possible and relevant?

A quick look at the plugin source code makes me think that the function storeMessage() could get the information from the property path.node.loc.start.line, when available. But then I may not be aware of some Babel subtleties.

Move 'printICUMessage(ast)' into separate project

The printICUMessage(ast) function which was suggested to me in formatjs/intl-messageformat#118 works great and is very useful as a standalone tool to convert an ICU AST into an ICU string pattern. Would you consider moving this into its own separate project so that I could depend on it directly in my package.json? Would be better so that I don't have to copy-paste the code and keep it in sync as updates are made upstream. Could be called for example something like icuAstToPattern.

Cannot use backslash('\') in jsx value string

When I'm trying to use backslash in my values (defaultMessage) in JSX, like this:

<FormattedMessage id="someId" defaultMessage="My message (one\\two\\three)"/> 

Plugin raises:

Message failed to parse: SyntaxError: Expected "\\#", "\\u", "\\{", "\\}", "{", [^{}\\\0-\x1F� \t\n\r], end of input or whitespace but "\\" found. See: http://formatjs.io/guides/message-syntax/

So it's looks like I cannot use \ symbol in JSX value. Any suggestions?

Infinite mkdirp loop when coming from another Webpack loader

In my setup, I have ts-loader set to run before babel-loader. When I add a reference to FormattedMessage to a file (say Page.tsx), it looks like it assumes the filename (specifically file.opts.filename, which I found by placing some strategic console.logs in the exit() method in this plugin) is [project dir]/node_modules/ts-loader/index.js![project dir]/src/pages/asset/Page.tsx'. The plugin then proceeds to try to create a folder at that nonsense URL and maxes out the stack.

Does it make sense for this plugin to be aware of the Webpack/loaders case (I think JSPM has a similar syntax)? Or maybe babel-loader should be passing more realistic filenames and I should go create an issue on that project? Either way, this is a blocker to me being able to use this plugin, which had been extremely useful before I switched to TypeScript!

Does not pick up messages defined in a `format*` method

This should probably be behind a config since it could cause a lot of bad data if not used carefully but so far this plugin only picks up messages in a defineMessages block and in the react components. It would be nice to be able to use format*() for a11y without having to revert to defineMessages.

Happy to PR this 😄

Overriding messages

Hi,

When ever I run babel plugin it actually overrides the existing translations(in messages folder), in my app I am translating messages from english to german. I might missing some point how to use this.

I am using some script to get all the messages from messages folder and write into a flat json file that I am using for intialising the

Please help me here how to get rid of this problem

Not compatible with babel 6?

I tried to upgrade babel to the latest major version (6) on my project, and it seems like this plugin is not compatible (?) Maybe this is obvious, but I don't see any compatibility note anywhere. Just wondering...

Error: Plugin 5 specified in ... an invalid property of "messagesDir"

Error: Plugin 5 specified in "/home/f/dm/.babelrc" provided an invalid property of "messagesDir"
    at Plugin.init (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/plugin.js:127:13)
    at Function.normalisePlugin (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:169:12)
    at /usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:199:30
    at Array.map (native)
    at Function.normalisePlugins (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:175:20)
    at OptionManager.mergeOptions (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:273:36)
    at OptionManager.addConfig (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:223:10)
    at OptionManager.findConfigs (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:366:16)
    at OptionManager.init (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:410:12)
    at File.initOptions (/usr/local/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/index.js:191:75)

.babelrc

{
  "presets": ["es2015-node5", "stage-3", "react"],
  "plugins": [
    "transform-object-rest-spread",
    "transform-class-properties",
    "transform-decorators",
    "transform-runtime",
    "react-intl", {
      "messagesDir": "./build/messages"
    }
  ],
  "sourceMaps": "both"
}
> npm ls | grep intl
├─┬ [email protected]
│ ├── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]

> node -v
v5.4.0

Error while using with webpack

When I run webpack I get the following error.

ERROR in ./app/components/Home.js
Module build failed: SyntaxError: /Users/nmaves/projects/maves-software/intensity/app/components/Home.js: [React Intl] Message must have a `description`.
  2 | import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl'
  3 | 
> 4 | const messages = defineMessages({
    |                  ^
  5 |   greeting: {
  6 |       id: 'greeting',
  7 |       description: 'Welcome greeting to the user',

Here is my webpack.config.js

module.exports = {
    entry: "./app/App.js",
    output: {
        filename: "public/bundle.js"
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel',
                query: {
                    presets: ['react', 'es2015'],
                    plugins: ['react-intl']
                }
            }
        ]
    }
}

Here is my test component.

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

const messages = defineMessages({
    greeting: {
        id: 'greeting',
        description: 'Welcome greeting to the user',
        defaultMessage: 'Hello, {name}! How are you today?'
    }
});

class Home extends React.Component {

    constructor(props) {
        super(props);
        this.state = {}
    }

    render() {
        return (
            <div>
                <FormattedMessage {...messages.greeting} />
            </div>
        )
    }
}

export default Home;

No extraction without warning

If I define a message like this

<FormattedMessage id="hello">

The message will be used in the app, but the babel plugin will not warn about anything (which make defaultMessage not required) this comportement is not necessary bad in my opinion, because i always use the same value for id and defaultMessage.

What is a bug, is that the babel plugin will see the message but ignore it and will not extract the message.

messagesObj.get(...).map is not a function

Hello,

I try to test [email protected] with babel-plugin-react-intl

I defined

import {defineMessages, injectIntl} from 'react-intl';
...
render(){
        const {formatMessage} = this.props.intl;

}
...
export default injectIntl(Serie)
formatMessage(messages.aa)
...
const messages = defineMessages({
    aa: {
        id: 'serie.aa',
        defaultMessage: 'travail',
    },
    bb: {
        id: 'serie.bb',
        defaultMessage: 'Temps',
    },
    cc: {
        id: 'serie.cc',
        defaultMessage: 'cycle',
    },
});

I use this .babelrc

{
  "plugins": ["react-intl"],
  "extra": {
    "react-intl": {
        "messagesDir": "./app/langs/",
        "enforceDescriptions": false
    }
  }
}

when I try to build I have this message:

messagesObj.get(...).map is not a function
    at NodePath.CallExpression (/keep/Repo/burner-front/node_modules/babel-plugin-react-intl/lib/index.js:300:51)
....

sorry if it my bad it's the first time I use react-intl
cheers

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.