Code Monkey home page Code Monkey logo

typescript-react-intl's Introduction

typescript-react-intl

Extracts string messages from TypeScript React components or ts files that use React Intl. You can use it in React Native too.

Build Status

Usage

If you have many files to processes,you can get contents use node-glob with fs module Custom component support since version 0.3.0. checkout tests/index.js

npm i typescript-react-intl -D
var parse = require("typescript-react-intl").default;

// results is an array
// contents is your tsx file
var results = parse(contents);

// or if you want support custom components
var results = parse(contents, {
  tagNames: ["MyComponent", "StyledText"],
});

React-intl

Only support <FormattedMessage/> and defineMessages We don't use <FormattedHtmlMessage/>

Examples

var fs = require("fs");
var glob = require("glob");
var parser = require("typescript-react-intl").default;

function runner(pattern, cb) {
  var results = [];
  pattern = pattern || "src/**/*.@(tsx|ts)";
  glob(pattern, function(err, files) {
    if (err) {
      throw new Error(err);
    }
    files.forEach((f) => {
      var contents = fs.readFileSync(f).toString();
      var res = parser(contents);
      results = results.concat(res);
    });

    cb && cb(results);
  });
}

// demo
runner(null, function(res) {
  var locale = {};

  res.forEach((r) => {
    locale[r.id] = r.defaultMessage;
  });

  var locales = {
    en: locale,
  };

  // save file to disk。you can save as a json file,just change the ext and contents as you want.
  fs.writeFileSync(
    `src/translations/all.ts`,
    `export default ${JSON.stringify(locales, null, 2)}\r`,
  );
});

typescript-react-intl's People

Contributors

9renpoto avatar baktun14 avatar bang88 avatar dependabot[bot] avatar ilan-schemoul avatar joelshepherd avatar jwulf avatar nathanloisel avatar seanf 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

Watchers

 avatar  avatar  avatar

typescript-react-intl's Issues

Support for FormattedHTMLMessage

Hi, I'm curious as to why you don't support FormattedHTMLMessage (as mentioned in your README).

I'm using this library in my project which uses FormattedHTMLMessage in a few places, so I've had to modify this library to support it, which only took a minor change and it seems to work well. I'd be happy to submit a PR for this if you're interested.

Support formatMessage api

Hi, thanks for putting this together!

I think it would be great to add formatMessage into the mix since it's basically the same as FormattedMessage.

Fails to Extract Concatenated Strings in defineMessages

When I use a concatenated string in defineMessages for defaultMessage, that message isn't picked up by the plugin.

eg

const messages = defineMessages({
    message: {
        id: "mesage.id",
        defaultMessage: "Message over"
            + " multiple lines",
    },
});

Expose the TS typings

It will be great if package.json contains the following so that the project can be flawlessly utilized from TypeScript:

"typings": "lib/index.d.ts"

Not correctly parsing Typescript output?

Hi there, first of all - thanks for putting this together!

I'm having an issue getting it to work in my TypeScript React project.

I'm using the example script, and I get empty objects back for all my files.

I added some console.log statements to your code to see what's going on.

The first is in findFirstJsxOpeningLikeElementWithName:

[ NodeObject {
    pos: 4020,
    end: 4147,
    flags: 0,
    transformFlags: undefined,
    parent: undefined,
    kind: 247,
    tagName:
     IdentifierObject {
       pos: 4021,
       end: 4037,
       flags: 0,
       parent: undefined,
       text: 'FormattedMessage' },
    attributes: [ [Object], [Object], pos: 4037, end: 4140 ] } ]
[]

The next is in main in the elements.attributes.forEach function:

attr:
NodeObject {
  pos: 4037,
  end: 4075,
  flags: 0,
  transformFlags: undefined,
  parent: undefined,
  kind: 250,
  name: IdentifierObject { pos: 4037, end: 4046, flags: 0, parent: undefined, text: 'id' },
  initializer:
   NodeObject {
     pos: 4047,
     end: 4075,
     flags: 0,
     transformFlags: undefined,
     parent: undefined,
     kind: 252,
     expression:
      TokenObject {
        pos: 4048,
        end: 4073,
        flags: 0,
        parent: undefined,
        kind: 9,
        text: 'Magikcraft.NotLoggedIn' } } }
attr:
NodeObject {
  pos: 4075,
  end: 4140,
  flags: 0,
  transformFlags: undefined,
  parent: undefined,
  kind: 250,
  name:
   IdentifierObject {
     pos: 4075,
     end: 4095,
     flags: 0,
     parent: undefined,
     text: 'defaultMessage' },
  initializer:
   NodeObject {
     pos: 4097,
     end: 4140,
     flags: 0,
     transformFlags: undefined,
     parent: undefined,
     kind: 252,
     expression:
      TokenObject {
        pos: 4098,
        end: 4138,
        flags: 0,
        parent: undefined,
        kind: 9,
        text: 'Go to Minecraft and type \'/spellbook\'' } } }

And finally in main just before return res.concat(dm);. This is res:

[ { id: undefined, defaultMessage: undefined } ]

It looks like elements.attributes.forEach is parsing the wrong properties of the returned nodes. Could this be a TypeScript compiler version issue?

You are requiring typescript, but the package.json doesn't specify a version. The version in my project is 2.1.4.

0.15 changes Typescript version

Hi, love your work.

In 0.14, you installed typescript as a peer dependency like this:

 "typescript": "^2.0.10"

In 0.15, you install it like this:

 "typescript": "2.0.10"

This breaks a bunch of my stuff in weird ways. I get errors about yield being a keyword.

I'm working around it by installing typescript explicitly at 2.2.0. I need 0.15 of this package for the fix to issue #4.

Could you unlock the typescript peer dependency version in 0.15?

Not parsing FormattedMessage if no defaultMessage is defined

As the title says a component with both id and defaultMessage is picked up, if only attr id is defined the component is ignored. defaultMessage is not mandatory by any mean, and I don't see the point of putting one as it will just be the same as what I write in the english locale file.

Replace tslint.json with tslint.js or tslint.yaml

I don't like config formats which don't support comments, and although tslint supports comments in the configuration file tslint.json, it's not strictly JSON and so github highlights comments in all red. So I'd like to use another option.

Option 1, JavaScript: tslint.js:

module.exports = {
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {
        "no-any": true,
        // TODO perhaps we *should* use the 'I' prefix?
        "interface-name": [true, "never-prefix"],
        "no-unused-expression": [true, "allow-fast-null-checks"]
    },
    "rulesDirectory": []
}

Or option 2, YAML: tslint.yaml:

defaultSeverity: error
extends:
    - 'tslint:recommended'
jsRules:
rules:
    no-any: true
    # TODO perhaps we *should* use the 'I' prefix?
    interface-name: [true, never-prefix]
    no-unused-expression: [true, allow-fast-null-checks]
rulesDirectory: []

I prefer option 2, but I know YAML can be a bit of an adjustment, so let me know what you think, @BANG88 .

EDIT: yaml uses # for comments, not //

react-intl-translations-manager integration example

Hi,

I just wanted to share an example integration with react-intl-translations-manager.

While it's not rocket science, I think it might be useful for others that are figuring out how to integrate
react-intl in their TypeScript project. A lot of guides mention babel-plugin-react-intl, but I can't use that as I'm not using Babel, or compiling to ES6. Using typescript-react-intl is a lot easier. I still want to manage my translations with react-intl-translations-manager though.

In short, the following approach is used:

  1. all messages are extracted using typescript-react-intl, and written to a temporary allMessages.json file.
  2. react-intl-translations-manager uses this allMessages.json file to manage the translations in src/translations.
  3. The temp file is cleaned up.

Dependencies:

The script (scripts/manageTranslations.js):

const DEFAULT_LANGUAGE = 'en-US'; // for the default language, everything is automatically whitelisted
const LANGUAGES = ['nl-BE', 'fr-BE']; // translations to generate---don't include the default language
const TARGET_DIRECTORY = 'src/translations';
const EXTRACT_MESSAGE_FILE_PATTERN = 'src/**/*.@(tsx|ts)';

const TEMP_DIR = './temp';
const TEMP_MESSAGE_FILENAME = 'allMessages.json';

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const rimraf = require('rimraf');
const parser = require('typescript-react-intl').default;
const manageTranslations = require('react-intl-translations-manager').default;
const {readMessageFiles, getDefaultMessages} = require('react-intl-translations-manager');

function extractTranslations(pattern, cb) {
  let results = [];
  pattern = pattern || 'src/**/*.@(tsx|ts)';
  glob(pattern, function (err, files) {
    if (err) {
      throw new Error(err);
    }
    files.forEach(function(f) {
      const contents = fs.readFileSync(f).toString();
      const res = parser(contents);
      results = results.concat(res);
    });

    cb && cb(results);
  });
}

if (!fs.existsSync(TEMP_DIR)){
  fs.mkdirSync(TEMP_DIR);
}

const tempMessageFilePath = path.join(TEMP_DIR, TEMP_MESSAGE_FILENAME);

extractTranslations(EXTRACT_MESSAGE_FILE_PATTERN, function (messages) {
  fs.writeFileSync(tempMessageFilePath, JSON.stringify(messages, null, 2));

  manageTranslations({
    messagesDirectory: TEMP_DIR,
    translationsDirectory: TARGET_DIRECTORY,
    languages: [DEFAULT_LANGUAGE, ...LANGUAGES],
    // avoid reporting translation issues with default language - https://github.com/GertjanReynaert/react-intl-translations-manager/issues/76
    overrideCoreMethods: {
      provideWhitelistFile: (language) => {
        // Avoid reporting untranslated stuff in defaultLanguage
        if (language.lang === DEFAULT_LANGUAGE) {
          const messageFiles = readMessageFiles(TEMP_DIR);
          const messages = getDefaultMessages(messageFiles).messages;
          return Object.keys(messages);
        } else {
          if (!fs.existsSync(language.whitelistFilepath)) {
            return [];
          }
          return JSON.parse(fs.readFileSync(language.whitelistFilepath, 'utf-8'));
        }
      }
    }
  });

  rimraf.sync(TEMP_DIR);
});

For convenience, you can add this to your npm scripts:

"manage:translations": "node scripts/manageTranslations.js",

Hope this helps someone!

JS Templates stopped working in v0.2.2 or higher

FormattedMessage tags formatted like this are not being picked up anymore

<FormattedMessage
    id="configure.info"
    defaultMessage={`The installer has detected {numDrives, number} {numDrives, plural,
      one {drive}
      other {drives}
    } and determined {numDrives, plural,
      one {its}
      other {their}
    } best configuration.
    If this is not the intended use of {numDrives, plural,
      one {this drive}
      other {these drives}
    }, please change the configuration to your preferences.`}
    values={{ numDrives: writableVolumes.length }}
  />

It used to output:

{ "configure.info": "The installer has detected {numDrives, number} {numDrives, plural,\n                one {drive}\n                other {drives}\n              } and determined {numDrives, plural,\n                one {its}\n                other {their}\n              } best configuration.\n              If this is not the intended use of {numDrives, plural,\n                one {this drive}\n                other {these drives}\n              }, please change the configuration to your preferences." }

typescript-react-intl doesn't parse defaultMessage derived with react-intl hooks

typescript-react-intl is able to parse and derive defaultMessage from jsx but not those from hooks.

JSX version(works):

<FormattedMessage
      id="taskTypeId.generalWork"
      defaultMessage="General Work"
 />

Hooks version(doesn't work):

intl = useIntl()
intl.formatMessage({
      id:"taskTypeId.generalWork"
      defaultMessage:"General Work"
})

Messages defined inside standalone objects are not picked up.

Messages which are defined using defineMessages inside standalone objects are not extracted by the parser. For example:

enum ItemStatus {
  OPENED = 'opened',
  CLOSED = 'closed',
}

const standaloneMessages: {[key in ItemStatus]: Message} = {
  [ItemStatus.OPENED]: {
    defaultMessage: 'Opened',
    id: 'app.itemStatus.Opened',
  },
  [ItemStatus.CLOSED]: {
    defaultMessage: 'Closed',
    id: 'app.itemStatus.Closed',
  },
};

const messages = defineMessages(standaloneMessages);

The problem here is that the parser will not extract the messages, but the code is still valid TypeScript, so there is no way how I can restrict the usage of this pattern. The fix in code, to get it to work is simple, but should not be both approaches supported?

// This will be extracted by parser
const messages = defineMessages<ItemStatus>({
  [ItemStatus.OPENED]: {
    defaultMessage: 'Opened',
    id: 'app.itemStatus.Opened',
  },
  [ItemStatus.CLOSED]: {
    defaultMessage: 'Closed',
    id: 'app.itemStatus.Closed',
  },
});

(using typescript-react-intl 0.4.0)

Fails on stateless functions.

cloned the repo and ran agains this file:

/* tslint:disable: variable-name */
import * as React from 'react';
import { defineMessages, FormattedMessage } from 'react-intl';
import 'react-toolbox/lib/tooltip/theme.scss';
import { AppBar, Navigation, Link } from 'react-toolbox';

const { Grid, Row, Col } = require('react-flexbox-grid');

const translations = defineMessages({
  predefinedTranslation: {
    id: 'PREDEFINED',
    defaultMessage: 'Hello {name}, I am predefined',
    description: 'This translation is defined in a defineMessage.'
  }
});

interface IPageProps { }
const Page: React.SFC<IPageProps> = ({ children }) => {
  return (
    <Grid>
      <Row>
        <Col xs={ 6 } md={ 3 }>
          <AppBar fixed flat>
          <Navigation type='vertical'>
            <Link href='http://' label='Inbox' icon='inbox' />
            <Link href='http://' active label='Profile' icon='person' />
          </Navigation>
            <a href='/home'>React Toolbox Docs</a>
            This is from Main Page
            <FormattedMessage
              { ...translations.predefinedTranslation }
              values={ { name: 'World' } }
            />
          </AppBar>
          { children }
        </Col>
      </Row>
    </Grid>
  );
};

Page.displayName = 'Mobile-page-Main';
export { Page };

Tried test of the file will fail:

  1. <FormattedMessage/>
  TypeError: Cannot read property 'text' of undefined
    index.ts:94:8
    index.ts:92:28
    main (index.ts:90:24)
    Test.t [as fn] (test/index.js:28:15)

findFirstJsxOpeningLikeElementWithName will yield on the stateless function:

{
    "pos": 1003,
    "end": 1059,
    "flags": 0,
    "kind": 251,
    "expression": {
        "pos": 1023,
        "end": 1057,
        "flags": 0,
        "kind": 177,
        "expression": {
            "pos": 1023,
            "end": 1035,
            "flags": 0,
            "text": "translations"
        },
        "name": {
            "pos": 1036,
            "end": 1057,
            "flags": 0,
            "text": "predefinedTranslation"
        }
    }
}

And thus will fail.

typescript-react-intl doesn't work with TypeScript 2.3.4

$ npm test

> [email protected] test C:\Users\mihai.dinculescu\Desktop\typescript-react-intl
> ava --verbose


  × <FormattedMessage/> Error: element.attributes.forEach is not a function
  √ defineMessages() should only work with variable declaration
  × <FormattedMessage/> should work with StatelessComponent Error: element.attributes.forEach is not a function
  √ It should return empty Array when not found

  2 tests failed [15:25:20]


  1. <FormattedMessage/>
  TypeError: element.attributes.forEach is not a function
    index.ts:90:28
    main (index.ts:88:24)
    Test.t [as fn] (test/index.js:10:13)


  2. <FormattedMessage/> should work with StatelessComponent
  TypeError: element.attributes.forEach is not a function
    index.ts:90:28
    main (index.ts:88:24)
    Test.t [as fn] (test/index.js:60:13)

element.forEach is not a function

When upgrade TypeScript to 2.2.0 I get the following error.

bash-3.2$ ts-node utility/extractStrings.ts
TypeError: element.forEach is not a function
    at find (/Users/mdentremont/Projects/REACT/ApolloX.Builder/node_modules/typescript-react-intl/index.ts:48:25)
    at findFirstJsxOpeningLikeElementWithName (/Users/mdentremont/Projects/REACT/ApolloX.Builder/node_modules/typescript-react-intl/index.ts:38:5)
    at main (/Users/mdentremont/Projects/REACT/ApolloX.Builder/node_modules/typescript-react-intl/index.ts:84:14)
    at /Users/mdentremont/Projects/REACT/ApolloX.Builder/utility/extractStrings.ts:29:25
    at Array.forEach (native)
    at /Users/mdentremont/Projects/REACT/ApolloX.Builder/utility/extractStrings.ts:27:15
    at f (/Users/mdentremont/Projects/REACT/ApolloX.Builder/node_modules/once/once.js:25:25)
    at Glob.<anonymous> (/Users/mdentremont/Projects/REACT/ApolloX.Builder/node_modules/glob/glob.js:151:7)
    at emitOne (events.js:96:13)
    at Glob.emit (events.js:188:7)

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.