diff --git a/.eslintrc b/.eslintrc
index cfbf631..9a44ac7 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -3,7 +3,9 @@
"rules": {
"func-names": 0,
"no-tabs": 0,
- "react/jsx-filename-extension": 0,
- "indent": ["error", "tab"]
+ "max-len": 1,
+ "class-methods-use-this": 0,
+ "indent": [2, "tab"],
+ "react/jsx-filename-extension": 0
}
}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..cd30b24
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - "6"
+script:
+ - "npm test"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f9de2f4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+# Change Log
+
+## v0.3.0 (2017-06-26)
+
+**Implemented enhancements:**
+
+- Added nxgettext function which allows to translate plural strings with context.
+- Extracted HOC into a separate component and updated it to inherit from that standalone component.
\ No newline at end of file
diff --git a/README.md b/README.md
index c8e71f2..3a80524 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,21 @@
-# react-gettext
+# react-gettext 0.3.0
+
+[![Build Status](https://travis-ci.org/eugene-manuilov/react-gettext.svg?branch=master)](https://travis-ci.org/eugene-manuilov/react-gettext)
Tiny React library for implementing gettext localization in your application. It provides HOC function to enhance your application by exposing gettext functions in the context scope.
## Instalation
-React Gettext requires **React 15.0 or later**.
+React Gettext requires **React 15.0 or later**. You can add this package using following commands:
```
npm install react-gettext --save
```
+```
+yarn add react-gettext
+```
+
## Usage
Let's assume you have following React application:
@@ -55,7 +61,7 @@ To make it translatable you need to update your `app.js` file to use HOC functio
```diff
// app.js
import React, { Component } from 'react';
-+ import Textdomain from 'react-gettext';
++ import withGettext from 'react-gettext';
import Header from './Header';
import Footer from './Footer';
@@ -64,15 +70,16 @@ To make it translatable you need to update your `app.js` file to use HOC functio
...
}
-+ export default Textdomain({...}, 'n != 1')(App);
++ export default withGettext({...}, 'n != 1')(App);
```
-After doing it you can start using `gettext`, `ngettext` and `xgettext` functions in your descending components:
+After doing it you can start using `gettext`, `ngettext`, `xgettext` and `nxgettext` functions in your descending components:
```diff
// Header.js
- import React, { Component } from 'react';
-+ import React, { Component, PropTypes } from 'react';
++ import React, { Component } from 'react';
++ import PropTypes from 'prop-types';
export default class Header extends Component {
@@ -88,13 +95,14 @@ After doing it you can start using `gettext`, `ngettext` and `xgettext` function
+ Header.contextTypes = {
+ gettext: PropTypes.func.isRequired,
+ ngettext: PropTypes.func.isRequired,
-+ xgettext: PropTypes.func.isRequired
++ xgettext: PropTypes.func.isRequired,
++ nxgettext: PropTypes.func.isRequired,
+ };
```
## Documentation
-### Textdomain(translations, pluralForms)
+### withGettext(translations, pluralForms)
Higher-order function which is exported by default from `react-gettext` package. It accepts two arguments and returns function to create higher-order component.
@@ -111,7 +119,7 @@ const translations = {
const pluralForms = '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'; // 3 plural forms for Russian, Belarusian, Bosnian, Croatian, Serbian, Ukrainian, etc.
-const HOC = Textdomain(translations, pluralForms)(App);
+const HOC = withGettext(translations, pluralForms)(App);
```
```javascript
@@ -126,9 +134,31 @@ function getPluralForms(n) {
return n > 1 ? 1 : 0;
}
-const HOC = Textdomain(getTranslations, getPluralForms)(App);
+const HOC = withGettext(getTranslations, getPluralForms)(App);
```
+As an alternative you can pass translations and plural form as properties to higher-order-component, like this:
+
+```javascript
+function getTranslations() {
+ return {
+ 'Some text': 'Some translated text',
+ ...
+ };
+}
+
+function getPluralForms(n) {
+ return n > 1 ? 1 : 0;
+}
+
+const HOC = withGettext()(App);
+
+...
+
+ReactDOM.render(..., ...);
+```
+
+
### gettext(message)
The function to translate a string. Accepts original message and returns translation if it exists, otherwise original message.
@@ -171,14 +201,49 @@ Example:
this.context.xgettext('some text', 'context where this message is used');
```
+### nxgettext(singular, plural, n, context)
+
+The function to translate plural string based on a specific context. Accepts singular and plural messages along with a number to calculate plural form against and context string. Returns translated message based on plural form if it exists, otherwise original message based on **n** value.
+
+- **singular**: a string to be translated when count is not plural
+- **plural**: a string to be translated when count is plural
+- **n**: a number to count plural form
+- **context**: A context to search translation in.
+
+Example:
+
+```javascript
+// somewhere in your jsx component
+this.context.nxgettext('day ago', 'days ago', numberOfDays, 'Article publish date');
+```
+
## Poedit
-If you use Poedit app to translate your messages, then you can use `gettext;ngettext:1,2;xgettext:1,2c` as keywords list to properly parse and extract strings from your javascript files.
+If you use Poedit app to translate your messages, then you can use `gettext;ngettext:1,2;xgettext:1,2c;nxgettext:1,2,4c` as keywords list to properly parse and extract strings from your javascript files.
+
+Here is an example of a **POT** file which you can start with:
+
+```
+msgid ""
+msgstr ""
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Basepath: ./src\n"
+"X-Poedit-KeywordsList: gettext;ngettext:1,2;xgettext:1,2c;nxgettext:1,2,4c\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+```
## Contribute
-What to help or have a suggestion? Open a [new ticket](https://github.com/eugene-manuilov/react-gettext/issues/new) and we can discuss it or submit pull request. Please, make sure you run `npm run test` before submitting a pull request.
+What to help or have a suggestion? Open a [new ticket](https://github.com/eugene-manuilov/react-gettext/issues/new) and we can discuss it or submit pull request. Please, make sure you run `npm test` before submitting a pull request.
## License
-MIT
\ No newline at end of file
+MIT
diff --git a/__tests__/__snapshots__/context.js.snap b/__tests__/__snapshots__/context.js.snap
deleted file mode 100644
index 2383584..0000000
--- a/__tests__/__snapshots__/context.js.snap
+++ /dev/null
@@ -1,30 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Child component can use context functions 1`] = `
-
- -
-
- gettext:
-
-
- 7[|UyK-%T\`CKw-%j $1/6XTzdd(8+cY,/JCngNVv+wQO6NAv:+^=4GA[,+G@^GI<
-
-
- -
-
- xgettext:
-
-
- T6%(h7m%J*O(Bm6!FiRk0 9;V]r\`kPz-ROW7E8*t.Aki9}j=hU9xfU8X|.7wEuZ0
-
-
- -
-
- ngettext:
-
-
- ZgcaUXD7<qhRY#>A~@-\`y2%xm@t{TLsN1LvUL\`6zamv|e}G|rY\`ms-|aMxn#_2f{
-
-
-
-`;
diff --git a/__tests__/context.js b/__tests__/context.js
index a0ff835..a34deba 100644
--- a/__tests__/context.js
+++ b/__tests__/context.js
@@ -1,68 +1,149 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
+import { mount } from 'enzyme';
import faker from 'faker';
-import renderer from 'react-test-renderer';
-
-import Textdomain from '../lib/index';
-
-class App extends Component {
- render() {
- return ;
- }
-};
-
-class C1 extends Component {
- render() {
- return (
-
- -
-
gettext:
- {this.context.gettext(this.props.gettext)}
-
- -
-
xgettext:
- {this.context.xgettext(this.props.xgettext, this.props.context)}
-
- -
-
ngettext:
- {this.context.ngettext(this.props.singular, this.props.plural, this.props.num)}
-
-
- );
- }
-};
-
-C1.contextTypes = {
- gettext: PropTypes.func.isRequired,
- xgettext: PropTypes.func.isRequired,
- ngettext: PropTypes.func.isRequired
-};
-
-test('Child component can use context functions', () => {
- const po = {};
- const sentence1 = faker.lorem.sentence();
- const sentence2 = faker.lorem.sentence();
- const sentence3 = faker.lorem.sentence();
- const sentence4 = faker.lorem.sentence();
- const sentence5 = faker.lorem.sentence();
-
- po[sentence1] = '7[|UyK-%T`CKw-%j $1/6XTzdd(8+cY,/JCngNVv+wQO6NAv:+^=4GA[,+G@^GI<';
- po[`${sentence3}\u0004${sentence2}`] = 'T6%(h7m%J*O(Bm6!FiRk0 9;V]r`kPz-ROW7E8*t.Aki9}j=hU9xfU8X|.7wEuZ0';
- po[sentence4] = [
- 'ZgcaUXD7A~@-`y2%xm@t{TLsN1LvUL`6zamv|e}G|rY`ms-|aMxn#_2f{',
- 'v.,rfN>-w1>j#KBX%eRO[nm@|MgGv,8E.o8-|`t.9Sz{9n#k=+V?W.#k@/tjWq|>'
+
+import withGettext from '../lib/index';
+
+describe('Higher-order-component translates', () => {
+ const pluralForm = 'n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2';
+ const catalog = {};
+
+ const gettext1 = faker.lorem.sentence();
+ const gettext2 = faker.lorem.sentence();
+ catalog[gettext1] = faker.lorem.sentence();
+
+ const single1 = faker.lorem.sentence();
+ const plural1 = faker.lorem.sentence();
+ catalog[single1] = [
+ faker.lorem.sentence(),
+ faker.lorem.sentence(),
+ faker.lorem.sentence(),
];
- const hoc = Textdomain(po, 'n!=1')(App);
- const component = renderer.create(React.createElement(hoc, {
- gettext: sentence1,
- xgettext: sentence2,
- context: sentence3,
- singular: sentence4,
- plural: sentence5,
- num: 2
- }, []));
-
- let tree = component.toJSON();
- expect(tree).toMatchSnapshot();
-});
\ No newline at end of file
+ const single2 = faker.lorem.sentence();
+ const plural2 = faker.lorem.sentence();
+
+ const context1 = faker.lorem.sentence();
+ const gettext3 = faker.lorem.sentence();
+ const contextKey1 = `${context1}\u0004${gettext3}`;
+ catalog[contextKey1] = faker.lorem.sentence();
+
+ const context2 = faker.lorem.sentence();
+ const gettext4 = faker.lorem.sentence();
+
+ const single3 = faker.lorem.sentence();
+ const plural3 = faker.lorem.sentence();
+ const context3 = faker.lorem.sentence();
+ const contextKey2 = `${context3}\u0004${single3}`;
+ catalog[contextKey2] = [
+ faker.lorem.sentence(),
+ faker.lorem.sentence(),
+ faker.lorem.sentence(),
+ ];
+
+ const single4 = faker.lorem.sentence();
+ const plural4 = faker.lorem.sentence();
+ const context4 = faker.lorem.sentence();
+
+ class baseComponent extends Component {
+ render() {
+ const { type, num } = this.props;
+
+ let message = '';
+ switch (type) {
+ case 'gettext1':
+ message = this.context.gettext(gettext1);
+ break;
+ case 'gettext2':
+ message = this.context.gettext(gettext2);
+ break;
+ case 'ngettext1':
+ message = this.context.ngettext(single1, plural1, num);
+ break;
+ case 'ngettext2':
+ message = this.context.ngettext(single2, plural2, num);
+ break;
+ case 'xgettext1':
+ message = this.context.xgettext(gettext3, context1);
+ break;
+ case 'xgettext2':
+ message = this.context.xgettext(gettext4, context2);
+ break;
+ case 'nxgettext1':
+ message = this.context.nxgettext(single3, plural3, num, context3);
+ break;
+ case 'nxgettext2':
+ message = this.context.nxgettext(single4, plural4, num, context4);
+ break;
+ }
+
+ return {message}
;
+ }
+ };
+
+ baseComponent.contextTypes = {
+ gettext: PropTypes.func.isRequired,
+ xgettext: PropTypes.func.isRequired,
+ ngettext: PropTypes.func.isRequired,
+ nxgettext: PropTypes.func.isRequired,
+ };
+
+ const Textdomain = withGettext(catalog, pluralForm)(baseComponent);
+
+ test('gettext', () => {
+ let wrapper;
+
+ // check when translation exists
+ wrapper = mount();
+ expect(wrapper.text()).toBe(catalog[gettext1]);
+
+ // check when translation exists
+ wrapper = mount();
+ expect(wrapper.text()).toBe(gettext2);
+ });
+
+ test('ngettext', () => {
+ let wrapper;
+
+ // check when translation exists
+ for (let i = 0; i < 3; i++) {
+ wrapper = mount();
+ expect(wrapper.text()).toBe(catalog[single1][i]);
+ }
+
+ // check fallbacks when a translation doesn't exist
+ for (let i = 0; i < 2; i++) {
+ wrapper = mount();
+ expect(wrapper.text()).toBe(i == 0 ? single2 : plural2);
+ }
+ });
+
+ test('xgettext', () => {
+ let wrapper;
+
+ // check when translation exists
+ wrapper = mount();
+ expect(wrapper.text()).toBe(catalog[contextKey1]);
+
+ // check when translation exists
+ wrapper = mount();
+ expect(wrapper.text()).toBe(gettext4);
+ });
+
+ test('ngettext', () => {
+ let wrapper;
+
+ // check when translation exists
+ for (let i = 0; i < 3; i++) {
+ wrapper = mount();
+ expect(wrapper.text()).toBe(catalog[contextKey2][i]);
+ }
+
+ // check fallbacks when a translation doesn't exist
+ for (let i = 0; i < 2; i++) {
+ wrapper = mount();
+ expect(wrapper.text()).toBe(i == 0 ? single4 : plural4);
+ }
+ });
+});
diff --git a/__tests__/default-export.js b/__tests__/default-export.js
deleted file mode 100644
index 6e241d9..0000000
--- a/__tests__/default-export.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import Textdomain from '../lib/index';
-
-test('Test default export', () => {
- expect(typeof Textdomain).toBe('function');
-});
-
-test('Test HOF returns HOC function', () => {
- expect(typeof Textdomain({}, '')).toBe('function');
-});
\ No newline at end of file
diff --git a/__tests__/higher-order-component.js b/__tests__/higher-order-component.js
deleted file mode 100644
index 46b3025..0000000
--- a/__tests__/higher-order-component.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import faker from 'faker';
-
-import Textdomain from '../lib/index';
-
-const getComponent = () => class extends Component {
- render() {
- return React.Children.only(this.props.children);
- }
-};
-
-describe('Higher-order component', () => {
- const name = faker.lorem.word();
-
- const baseComponent = getComponent();
- baseComponent.displayName = name;
-
- const hoc = Textdomain({}, '')(baseComponent);
-
- test('is React Component', () => {
- expect(Component.isPrototypeOf(hoc)).toBeTruthy();
- });
-
- test('has proper display name', () => {
- expect(hoc.displayName).toBe(`WithGettext(${name})`);
- });
-
- test('has childContextTypes', () => {
- expect(hoc.childContextTypes).toEqual({
- gettext: PropTypes.func,
- ngettext: PropTypes.func,
- xgettext: PropTypes.func
- });
- });
-
- test('has static functions for context types', () => {
- expect(typeof hoc.gettext).toBe('function');
- expect(typeof hoc.ngettext).toBe('function');
- expect(typeof hoc.xgettext).toBe('function');
- });
-});
-
-describe('Higher-order component instance', () => {
- const baseComponent = getComponent();
- const hoc = Textdomain({}, '')(baseComponent);
-
- test('returns child context', () => {
- const hocObject = new hoc({});
- expect(hocObject.getChildContext()).toEqual({
- gettext: hoc.gettext,
- ngettext: hoc.ngettext,
- xgettext: hoc.xgettext
- });
- });
-});
\ No newline at end of file
diff --git a/__tests__/hoc.js b/__tests__/hoc.js
new file mode 100644
index 0000000..807eeb6
--- /dev/null
+++ b/__tests__/hoc.js
@@ -0,0 +1,43 @@
+import { Component } from 'react';
+import PropTypes from 'prop-types';
+import faker from 'faker';
+
+import withGettext from '../lib/index';
+
+describe('Higher-order-component', () => {
+ class baseComponent extends Component {
+ render() {
+ return this.props.children;
+ }
+ };
+
+ const name = faker.lorem.word();
+ baseComponent.displayName = name;
+
+ const plural = 'n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2';
+ const Textdomain = withGettext({}, plural)(baseComponent);
+
+ test('is React component', () => {
+ expect(Component.isPrototypeOf(Textdomain)).toBeTruthy();
+ });
+
+ test('has proper displayName', () => {
+ expect(Textdomain.displayName).toBe(`withGettext(${name})`);
+ });
+
+ test('has proper childContextType', () => {
+ expect(Textdomain.childContextTypes).toEqual({
+ gettext: PropTypes.func,
+ ngettext: PropTypes.func,
+ xgettext: PropTypes.func,
+ nxgettext: PropTypes.func,
+ });
+ });
+
+ test('properly calculates plural form', () => {
+ const instance = new Textdomain({plural});
+ expect(instance.getPluralForm(1)).toBe(0);
+ expect(instance.getPluralForm(2)).toBe(1);
+ expect(instance.getPluralForm(5)).toBe(2);
+ });
+});
diff --git a/__tests__/translations.js b/__tests__/translations.js
deleted file mode 100644
index 97e0cf4..0000000
--- a/__tests__/translations.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React, { Component } from 'react';
-import faker from 'faker';
-
-import Textdomain from '../lib/index';
-
-const getComponent = () => class extends Component {
- render() {
- return null;
- }
-};
-
-test('Default values when translations not found', () => {
- const baseComponent = getComponent();
- const hoc = Textdomain({}, '')(baseComponent);
-
- let sentence = '';
-
- sentence = faker.lorem.sentence();
- expect(hoc.gettext(sentence)).toBe(sentence);
-
- sentence = faker.lorem.sentence();
- expect(hoc.xgettext(sentence, 'context')).toBe(sentence);
-
- const sentence1 = faker.lorem.sentence();
- const sentence2 = faker.lorem.sentence();
- const number = faker.random.number({min: 1, max: 3});
- const expected = number > 1 ? sentence2 : sentence1;
- expect(hoc.ngettext(sentence1, sentence2, number)).toBe(expected);
-});
-
-test('Test gettext function', () => {
- const sentence = faker.lorem.sentence();
- const translation = faker.lorem.sentence();
-
- const po = {};
- po[sentence] = translation;
-
- const baseComponent = getComponent();
- const hoc = Textdomain(po, '')(baseComponent);
-
- expect(hoc.gettext(sentence)).toBe(translation);
-});
-
-test('Test xgettext function', () => {
- const sentence = faker.lorem.sentence();
- const context = faker.lorem.sentence();
- const translation = faker.lorem.sentence();
-
- const po = {};
- po[`${context}\u0004${sentence}`] = translation;
-
- const baseComponent = getComponent();
- const hoc = Textdomain(po, '')(baseComponent);
-
- expect(hoc.xgettext(sentence, context)).toBe(translation);
- expect(hoc.xgettext(sentence, faker.lorem.sentence())).not.toBe(translation);
-});
-
-test('Test ngettext function', () => {
- const sentence = faker.lorem.sentence();
- const translation1 = faker.lorem.sentence();
- const translation2 = faker.lorem.sentence();
- const translation3 = faker.lorem.sentence();
-
- const po = {};
- po[sentence] = [translation1, translation2, translation3];
-
- const baseComponent = getComponent();
- const hoc = Textdomain(po, 'n % 3 == 2 ? 2 : n % 2 == 1 ? 1 : 0')(baseComponent);
-
- const n = faker.random.number();
- const translation = po[sentence][n % 3 == 2 ? 2 : n % 2 == 1 ? 1 : 0];
- expect(hoc.ngettext(sentence, faker.lorem.sentence(), n)).toBe(translation);
-});
-
-test('Function callbacks for translations and plural forms', () => {
- const sentence1 = faker.lorem.sentence();
- const sentence2 = faker.lorem.sentence();
- const context = faker.lorem.sentence();
-
- const sentence3 = faker.lorem.sentence();
- const translation1 = faker.lorem.sentence();
- const translation2 = faker.lorem.sentence();
- const translation3 = faker.lorem.sentence();
-
- const getPO = () => {
- const po = {};
-
- po[sentence1] = translation1;
- po[`${context}\u0004${sentence2}`] = translation2;
- po[sentence3] = [translation1, translation2, translation3];
-
- return po;
- };
-
- const pluralForm = n => n % 3 == 2 ? 2 : n % 2 == 1 ? 1 : 0;
-
- const baseComponent = getComponent();
- const hoc = Textdomain(getPO, pluralForm)(baseComponent);
-
- expect(hoc.gettext(sentence1)).toBe(translation1);
- expect(hoc.xgettext(sentence2, context)).toBe(translation2);
-
- const n = faker.random.number();
- const translation = getPO()[sentence3][n % 3 == 2 ? 2 : n % 2 == 1 ? 1 : 0];
- expect(hoc.ngettext(sentence3, faker.lorem.sentence(), n)).toBe(translation);
-})
\ No newline at end of file
diff --git a/package.json b/package.json
index e0e0c20..44abffc 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"bugs": {
"url": "https://github.com/eugene-manuilov/react-gettext/issues"
},
- "version": "0.2.0",
+ "version": "0.3.0",
"main": "lib/index",
"files": [
"*.md",
@@ -42,6 +42,7 @@
"babel-preset-env": "^1.4.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
+ "enzyme": "^2.9.1",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-loader": "^1.7.1",
@@ -49,7 +50,9 @@
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.3",
"faker": "^4.1.0",
+ "hoist-non-react-statics": "^1.2.0",
"jest": "^19.0.2",
+ "jest-enzyme": "^3.3.0",
"prop-types": "^15.5.8",
"react": "^15.5.4",
"react-dom": "^15.5.4",
@@ -57,10 +60,8 @@
"webpack": "^2.4.1"
},
"peerDependencies": {
+ "hoist-non-react-statics": "^1.2.0",
"prop-types": "^15.0.0-0 || ^16.0.0-0",
"react": "^15.0.0-0 || ^16.0.0-0"
- },
- "dependencies": {
- "hoist-non-react-statics": "^1.2.0"
}
}
diff --git a/src/Textdomain.js b/src/Textdomain.js
new file mode 100644
index 0000000..c6c51c3
--- /dev/null
+++ b/src/Textdomain.js
@@ -0,0 +1,133 @@
+import { Component } from 'react';
+import PropTypes from 'prop-types';
+
+class Textdomain extends Component {
+
+ getChildContext() {
+ const self = this;
+
+ return {
+ gettext: self.gettext.bind(self),
+ xgettext: self.xgettext.bind(self),
+ ngettext: self.ngettext.bind(self),
+ nxgettext: self.nxgettext.bind(self),
+ };
+ }
+
+ getTranslations() {
+ const { translations } = this.props;
+ return typeof translations === 'function' ? translations() : translations;
+ }
+
+ getPluralForm(n) {
+ const { plural } = this.props;
+
+ // return 0 if n is not integer
+ if (isNaN(parseInt(n, 10))) {
+ return 0;
+ }
+
+ // if pluralForm is function, use it to get plural form index
+ if (typeof plural === 'function') {
+ return plural(n);
+ }
+
+ // if pluralForm is string and contains only "n", "0-9", " ", "!=?:%+-/*><&|()"
+ // characters, then we can "eval" it to calculate plural form
+ if (typeof plural === 'string' && !plural.match(/[^n0-9 !=?:%+-/*><&|()]/i)) {
+ /* eslint-disable no-new-func */
+ const calcPlural = Function('n', `return ${plural}`);
+ /* eslint-enable no-new-func */
+ return +calcPlural(n);
+ }
+
+ return 0;
+ }
+
+ getDelimiter() {
+ return '\u0004'; // End of Transmission (EOT)
+ }
+
+ gettext(message) {
+ const messages = this.getTranslations();
+ return Object.prototype.hasOwnProperty.call(messages, message)
+ ? messages[message]
+ : message;
+ }
+
+ ngettext(singular, plural, n) {
+ const self = this;
+ const messages = self.getTranslations();
+ const pluralIndex = self.getPluralForm(n);
+ const defaultValue = n > 1 ? plural : singular;
+
+ return Object.prototype.hasOwnProperty.call(messages, singular)
+ && Array.isArray(messages[singular])
+ && messages[singular].length > pluralIndex
+ && pluralIndex >= 0
+ ? messages[singular][pluralIndex]
+ : defaultValue;
+ }
+
+ xgettext(message, context) {
+ const self = this;
+ const EOT = self.getDelimiter();
+ const messages = self.getTranslations();
+ const key = context + EOT + message;
+
+ return Object.prototype.hasOwnProperty.call(messages, key)
+ ? messages[key]
+ : message;
+ }
+
+ nxgettext(singular, plural, n, context) {
+ const self = this;
+ const messages = self.getTranslations();
+ const pluralIndex = self.getPluralForm(n);
+ const defaultValue = n > 1 ? plural : singular;
+ const EOT = self.getDelimiter();
+ const key = context + EOT + singular;
+
+ return Object.prototype.hasOwnProperty.call(messages, key)
+ && Array.isArray(messages[key])
+ && messages[key].length > pluralIndex
+ && pluralIndex >= 0
+ ? messages[key][pluralIndex]
+ : defaultValue;
+ }
+
+ render() {
+ return this.props.children;
+ }
+
+}
+
+Textdomain.propTypes = {
+ translations: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.objectOf(PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.arrayOf(PropTypes.string),
+ ])),
+ ]),
+ plural: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.string,
+ ]),
+ children: PropTypes.arrayOf(PropTypes.node),
+};
+
+Textdomain.defaultProps = {
+ translations: {},
+ plural: 'n != 1',
+ children: [],
+};
+
+Textdomain.childContextTypes = {
+ gettext: PropTypes.func,
+ ngettext: PropTypes.func,
+ xgettext: PropTypes.func,
+ nxgettext: PropTypes.func,
+};
+
+export default Textdomain;
diff --git a/src/index.js b/src/index.js
index 25ec5db..22eff52 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,85 +1,24 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';
+import Textdomain from './Textdomain';
-export default function textdomain(translations, pluralForm) {
- const EOT = '\u0004'; // End of Transmission
-
- const getTranslations = function () {
- // if translations is function, call it to get translations object,
- // otherwise just use incoming translations object
- return typeof translations === 'function' ? translations() : translations;
- };
-
- const getPluralForm = (n) => {
- // return 0 if n is not integer
- if (isNaN(parseInt(n, 10))) {
- return 0;
- }
-
- // if pluralForm is function, use it to get plural form index
- if (typeof pluralForm === 'function') {
- return pluralForm(n);
- }
-
- // if pluralForm is string and contains only "n", "0-9", " ", "=?:%+-/*><&|"
- // characters, then we can eval it to calculate plural form
- if (typeof pluralForm === 'string' && !pluralForm.match(/[^n0-9 =?:%+-/*><&|]/i)) {
- /* eslint-disable no-eval */
- return +eval(pluralForm.toLowerCase().split('n').join(n));
- /* eslint-enable no-eval */
- }
-
- return 0;
- };
-
- // return HOC function
+export default function withGettext(translations = {}, pluralForm = 'n != 1') {
return (WrappedComponent) => {
- class WithGettext extends Component {
- static gettext(message) {
- const messages = getTranslations();
-
- return messages[message] ? messages[message] : message;
- }
-
- static ngettext(singular, plural, n) {
- const messages = getTranslations();
- const pluralIndex = getPluralForm(n);
- const defaultValue = n > 1 ? plural : singular;
-
- return Array.isArray(messages[singular]) && messages[singular][pluralIndex]
- ? messages[singular][pluralIndex]
- : defaultValue;
- }
-
- static xgettext(message, context) {
- const messages = getTranslations();
- const key = context + EOT + message;
-
- return messages[key] ? messages[key] : message;
- }
-
- getChildContext() {
- return {
- gettext: WithGettext.gettext,
- ngettext: WithGettext.ngettext,
- xgettext: WithGettext.xgettext,
- };
- }
+ class WithGettext extends Textdomain {
render() {
return React.createElement(WrappedComponent, this.props);
}
- }
- WithGettext.displayName = `WithGettext(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
+ }
- WithGettext.childContextTypes = {
- gettext: PropTypes.func,
- ngettext: PropTypes.func,
- xgettext: PropTypes.func,
+ WithGettext.defaultProps = {
+ translations,
+ plural: pluralForm,
};
+ WithGettext.displayName = `withGettext(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
+
return hoistNonReactStatic(WithGettext, WrappedComponent);
};
}
diff --git a/yarn.lock b/yarn.lock
index e5b9411..2208739 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,10 @@
# yarn lockfile v1
+"@types/react@^15.0.22":
+ version "15.0.31"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-15.0.31.tgz#21dfc5d41ee1600ff7d7b738ad21a9502aa3ecf2"
+
abab@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
@@ -875,6 +879,10 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
+boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+
boom@2.x.x:
version "2.10.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
@@ -1047,6 +1055,27 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
+cheerio@^0.22.0:
+ version "0.22.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash.assignin "^4.0.9"
+ lodash.bind "^4.1.4"
+ lodash.defaults "^4.0.1"
+ lodash.filter "^4.4.0"
+ lodash.flatten "^4.2.0"
+ lodash.foreach "^4.3.0"
+ lodash.map "^4.4.0"
+ lodash.merge "^4.4.0"
+ lodash.pick "^4.2.1"
+ lodash.reduce "^4.4.0"
+ lodash.reject "^4.4.0"
+ lodash.some "^4.4.0"
+
chokidar@^1.4.3, chokidar@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2"
@@ -1230,6 +1259,19 @@ crypto-browserify@^3.11.0:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
+css-select@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "2.1"
+ domutils "1.5.1"
+ nth-check "~1.0.1"
+
+css-what@2.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
@@ -1276,6 +1318,12 @@ decamelize@^1.0.0, decamelize@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+deep-equal-ident@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz#06f4b89e53710cd6cea4a7781c7a956642de8dc9"
+ dependencies:
+ lodash.isequal "^3.0"
+
deep-extend@~0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
@@ -1356,10 +1404,38 @@ doctrine@^2.0.0:
esutils "^2.0.2"
isarray "^1.0.0"
+dom-serializer@0, dom-serializer@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+ dependencies:
+ domelementtype "~1.1.1"
+ entities "~1.1.1"
+
domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+domelementtype@1, domelementtype@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@^2.3.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
+ dependencies:
+ domelementtype "1"
+
+domutils@1.5.1, domutils@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@@ -1405,6 +1481,43 @@ enhanced-resolve@^3.0.0:
object-assign "^4.0.1"
tapable "^0.2.5"
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+enzyme-matchers@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-3.3.0.tgz#cec99edfcb6117ed7d405b3d9b98d1ee3ccdbc47"
+ dependencies:
+ deep-equal-ident "^1.1.1"
+
+enzyme-to-json@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-1.5.1.tgz#e34f4d126bb3f4696ce3800b51f9ed83df708799"
+ dependencies:
+ lodash.filter "^4.6.0"
+ lodash.isnil "^4.0.0"
+ lodash.isplainobject "^4.0.6"
+ lodash.omitby "^4.5.0"
+ lodash.range "^3.2.0"
+ object-values "^1.0.0"
+ object.entries "^1.0.3"
+
+enzyme@^2.9.1:
+ version "2.9.1"
+ resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.9.1.tgz#07d5ce691241240fb817bf2c4b18d6e530240df6"
+ dependencies:
+ cheerio "^0.22.0"
+ function.prototype.name "^1.0.0"
+ is-subset "^0.1.1"
+ lodash "^4.17.4"
+ object-is "^1.0.1"
+ object.assign "^4.0.4"
+ object.entries "^1.0.4"
+ object.values "^1.0.4"
+ prop-types "^15.5.10"
+ uuid "^3.0.1"
+
"errno@>=0.1.1 <0.2.0-0", errno@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -1417,7 +1530,7 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
-es-abstract@^1.7.0:
+es-abstract@^1.6.1, es-abstract@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
dependencies:
@@ -1878,6 +1991,14 @@ function-bind@^1.0.2, function-bind@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
+function.prototype.name@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.0.0.tgz#5f523ca64e491a5f95aba80cc1e391080a14482e"
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.0"
+ is-callable "^1.1.2"
+
gauge@~2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.3.tgz#1c23855f962f17b3ad3d0dc7443f304542edfe09"
@@ -2051,6 +2172,17 @@ html-encoding-sniffer@^1.0.1:
dependencies:
whatwg-encoding "^1.0.1"
+htmlparser2@^3.9.1:
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+ dependencies:
+ domelementtype "^1.3.0"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^2.0.2"
+
http-signature@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
@@ -2154,7 +2286,7 @@ is-builtin-module@^1.0.0:
dependencies:
builtin-modules "^1.0.0"
-is-callable@^1.1.1, is-callable@^1.1.3:
+is-callable@^1.1.1, is-callable@^1.1.2, is-callable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
@@ -2267,6 +2399,10 @@ is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+is-subset@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+
is-symbol@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
@@ -2439,6 +2575,14 @@ jest-environment-node@^19.0.2:
jest-mock "^19.0.0"
jest-util "^19.0.2"
+jest-enzyme@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/jest-enzyme/-/jest-enzyme-3.3.0.tgz#c5b50682e336bf30ed5ff335e89c61a50e64bd04"
+ dependencies:
+ "@types/react" "^15.0.22"
+ enzyme-matchers "^3.3.0"
+ enzyme-to-json "^1.5.0"
+
jest-file-exists@^19.0.0:
version "19.0.0"
resolved "https://registry.yarnpkg.com/jest-file-exists/-/jest-file-exists-19.0.0.tgz#cca2e587a11ec92e24cfeab3f8a94d657f3fceb8"
@@ -2738,11 +2882,118 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
+lodash._baseisequal@^3.0.0:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1"
+ dependencies:
+ lodash.isarray "^3.0.0"
+ lodash.istypedarray "^3.0.0"
+ lodash.keys "^3.0.0"
+
+lodash._bindcallback@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
+
+lodash._getnative@^3.0.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash.assignin@^4.0.9:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+
+lodash.bind@^4.1.4:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
+
lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
-lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.3.0:
+lodash.defaults@^4.0.1:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+
+lodash.filter@^4.4.0, lodash.filter@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+
+lodash.flatten@^4.2.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+
+lodash.foreach@^4.3.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+
+lodash.isarguments@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
+lodash.isarray@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+
+lodash.isequal@^3.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-3.0.4.tgz#1c35eb3b6ef0cd1ff51743e3ea3cf7fdffdacb64"
+ dependencies:
+ lodash._baseisequal "^3.0.0"
+ lodash._bindcallback "^3.0.0"
+
+lodash.isnil@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c"
+
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+
+lodash.istypedarray@^3.0.0:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62"
+
+lodash.keys@^3.0.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+ dependencies:
+ lodash._getnative "^3.0.0"
+ lodash.isarguments "^3.0.0"
+ lodash.isarray "^3.0.0"
+
+lodash.map@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
+
+lodash.merge@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
+lodash.omitby@^4.5.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.omitby/-/lodash.omitby-4.6.0.tgz#5c15ff4754ad555016b53c041311e8f079204791"
+
+lodash.pick@^4.2.1:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
+
+lodash.range@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.range/-/lodash.range-3.2.0.tgz#f461e588f66683f7eadeade513e38a69a565a15d"
+
+lodash.reduce@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
+
+lodash.reject@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
+
+lodash.some@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -2750,7 +3001,7 @@ longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
-loose-envify@^1.0.0, loose-envify@^1.1.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -2949,6 +3200,12 @@ npmlog@^4.0.2:
gauge "~2.7.1"
set-blocking "~2.0.0"
+nth-check@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
+ dependencies:
+ boolbase "~1.0.0"
+
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -2969,10 +3226,18 @@ object-hash@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.1.8.tgz#28a659cf987d96a4dabe7860289f3b5326c4a03c"
+object-is@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
+
object-keys@^1.0.10, object-keys@^1.0.8:
version "1.0.11"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+object-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/object-values/-/object-values-1.0.0.tgz#72af839630119e5b98c3b02bb8c27e3237158105"
+
object.assign@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc"
@@ -2981,6 +3246,15 @@ object.assign@^4.0.4:
function-bind "^1.1.0"
object-keys "^1.0.10"
+object.entries@^1.0.3, object.entries@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.6.1"
+ function-bind "^1.1.0"
+ has "^1.0.1"
+
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -2988,6 +3262,15 @@ object.omit@^2.0.0:
for-own "^0.1.4"
is-extendable "^0.1.1"
+object.values@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a"
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.6.1"
+ function-bind "^1.1.0"
+ has "^1.0.1"
+
once@^1.3.0, once@^1.3.3, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -3202,7 +3485,14 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
-prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
+prop-types@^15.5.10, prop-types@^15.5.7, prop-types@~15.5.7:
+ version "15.5.10"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
+ dependencies:
+ fbjs "^0.8.9"
+ loose-envify "^1.3.1"
+
+prop-types@^15.5.8:
version "15.5.8"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
dependencies:
@@ -3868,7 +4158,7 @@ util@0.10.3, util@^0.10.3:
dependencies:
inherits "2.0.1"
-uuid@^3.0.0:
+uuid@^3.0.0, uuid@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"