react-intl-po
Advanced tools
Comparing version 2.0.2 to 2.1.0
@@ -6,2 +6,13 @@ # react-intl-po | ||
## [v2.1.0] | ||
> Jul 17, 2017 | ||
* feat(contexts): Allow user to specify msgctxt with `-c` arguments. ([@Sand1929](https://github.com/Sand1929)in [#84]) | ||
## [v2.0.2] | ||
> Mar 02, 2017 | ||
* fix(potFormater): Escape quotes in msgId ([@jonbretman](https://github.com/jonbretman) in [#70]) | ||
* chore(Jest): update jest to 19. | ||
## [v2.0.1] | ||
@@ -8,0 +19,0 @@ > Feb 12, 2017 |
@@ -10,6 +10,6 @@ #!/usr/bin/env node | ||
_commander2.default.command('json2pot <srcPatterns>').option('-o, --output <path>', 'The output pathname of `.pot` file to be translated').option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)').action(require('./extractAndWritePOTFromMessagesSync')); | ||
_commander2.default.command('json2pot <srcPatterns>').option('-o, --output <path>', 'The output pathname of `.pot` file to be translated').option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)').option('-c, --message-context [context]', 'Translation message context (defaults to no context)').action(require('./extractAndWritePOTFromMessagesSync')); | ||
_commander2.default.command('po2json <srcPatterns>').option('-m, --messages-pattern <path>', 'The pattern of *json* files extracted from *babel-plugin-react-intl*').option('-o, --output <path>', 'The output pathname of a file / directory').option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)').action(require('./filterPOAndWriteTranslateSync')); | ||
_commander2.default.command('po2json <srcPatterns>').option('-m, --messages-pattern <path>', 'The pattern of *json* files extracted from *babel-plugin-react-intl*').option('-o, --output <path>', 'The output pathname of a file / directory').option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)').option('-c, --message-context [context]', 'Translation message context (defaults to no context)').action(require('./filterPOAndWriteTranslateSync')); | ||
_commander2.default.parse(process.argv); |
@@ -49,3 +49,4 @@ 'use strict'; | ||
result += (0, _flowRight2.default)(_potFormater2.default, // 2. return formated string | ||
_readAllMessageAsObjectSync2.default)(srcPatterns, messageKey); | ||
_readAllMessageAsObjectSync2.default // 1. return messages object | ||
)(srcPatterns, messageKey); | ||
@@ -52,0 +53,0 @@ _fs2.default.writeFileSync(output, result); |
@@ -27,5 +27,5 @@ 'use strict'; | ||
var _flatten = require('lodash/flatten'); | ||
var _flattenDeep = require('lodash/flattenDeep'); | ||
var _flatten2 = _interopRequireDefault(_flatten); | ||
var _flattenDeep2 = _interopRequireDefault(_flattenDeep); | ||
@@ -58,5 +58,11 @@ var _flowRight = require('lodash/flowRight'); | ||
var getTranslationTableContext = function getTranslationTableContext(messageContext, message) { | ||
return messageContext ? message[messageContext] + '\x04' : ''; | ||
}; | ||
function filterPOAndWriteTranslateSync(srcPatterns, _ref) { | ||
var _ref$messageKey = _ref.messageKey, | ||
messageKey = _ref$messageKey === undefined ? 'defaultMessage' : _ref$messageKey, | ||
_ref$messageContext = _ref.messageContext, | ||
messageContext = _ref$messageContext === undefined ? '' : _ref$messageContext, | ||
messagesPattern = _ref.messagesPattern, | ||
@@ -66,5 +72,11 @@ output = _ref.output; | ||
var translationTable = (0, _readAllPOAsObjectSync2.default)(srcPatterns); | ||
var messageList = (0, _flowRight2.default)(_flatten2.default, // 3. return flatten object values | ||
_values2.default, // 2. return object values | ||
_readAllMessageAsObjectSync2.default)(messagesPattern, messageKey); | ||
var messageList = (0, _flowRight2.default)(_flattenDeep2.default, // 4. return flattened values | ||
function (objects) { | ||
return objects.map(function (o) { | ||
return (0, // 3. return values | ||
_values2.default)(o); | ||
}); | ||
}, _values2.default, // 2. return context objects | ||
_readAllMessageAsObjectSync2.default // 1. return message object | ||
)(messagesPattern, messageKey, messageContext); | ||
@@ -74,3 +86,3 @@ var locales = Object.keys(translationTable); | ||
return _defineProperty({}, locale, (0, _toObjectBy2.default)(messageList, function (message) { | ||
return _defineProperty({}, message.id, translationTable[locale][message[messageKey]]); | ||
return _defineProperty({}, message.id, translationTable[locale]['' + getTranslationTableContext(messageContext, message) + message[messageKey]]); | ||
})); | ||
@@ -77,0 +89,0 @@ }); |
@@ -61,2 +61,13 @@ 'use strict'; | ||
/** | ||
* Formatting POT contexts | ||
* @param {String} messageContext | ||
* @return {String} | ||
* | ||
* @author Sandy Suh | ||
*/ | ||
var potContextsFormater = function potContextsFormater(messageContext) { | ||
return messageContext ? 'msgctxt ' + JSON.stringify(messageContext) + '\n' : ''; | ||
}; | ||
/** | ||
* Formatting POT comments | ||
@@ -74,3 +85,5 @@ * @param {Object} messageObject | ||
.sort().map(function (id) { | ||
return potCommentsFormater(messageObject[id]) + 'msgid ' + JSON.stringify(id) + '\nmsgstr ""\n'; | ||
return Object.keys(messageObject[id]).map(function (context) { | ||
return '' + potCommentsFormater(messageObject[id][context]) + potContextsFormater(context) + 'msgid ' + JSON.stringify(id) + '\nmsgstr ""\n'; | ||
}).join('\n'); | ||
}).join('\n'); | ||
@@ -77,0 +90,0 @@ }; |
@@ -15,5 +15,5 @@ 'use strict'; | ||
var _mergeWith = require('lodash/mergeWith'); | ||
var _mergeWith2 = require('lodash/mergeWith'); | ||
var _mergeWith2 = _interopRequireDefault(_mergeWith); | ||
var _mergeWith3 = _interopRequireDefault(_mergeWith2); | ||
@@ -28,3 +28,3 @@ var _isArray = require('lodash/isArray'); | ||
var customizer = function customizer(accValue, objectValue) { | ||
var concatCustomizer = function concatCustomizer(accValue, objectValue) { | ||
if (!(0, _isArray2.default)(accValue)) return objectValue; | ||
@@ -35,4 +35,8 @@ | ||
var mergeCustomizer = function mergeCustomizer(accValue, objectValue) { | ||
return (0, _mergeWith3.default)(accValue, objectValue, concatCustomizer); | ||
}; | ||
// hint: Use defaultMessage as key by default | ||
var indexBy = function indexBy(messageKey) { | ||
var indexBy = function indexBy(messageKey, messageContext) { | ||
return function (_ref) { | ||
@@ -42,3 +46,3 @@ var messages = _ref.messages, | ||
return messages.reduce(function (acc, message) { | ||
return _extends({}, acc, _defineProperty({}, message[messageKey], acc[message[messageKey]] ? acc[message[messageKey]].concat([_extends({}, message, { filename: filename })]) : [_extends({}, message, { filename: filename })])); | ||
return _extends({}, acc, _defineProperty({}, message[messageKey], (0, _mergeWith3.default)(acc[message[messageKey]], _defineProperty({}, messageContext ? message[messageContext] : '', [_extends({}, message, { filename: filename })]), concatCustomizer))); | ||
}, {}); | ||
@@ -61,2 +65,3 @@ }; | ||
var messageKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'defaultMessage'; | ||
var messageContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; | ||
@@ -68,7 +73,7 @@ return (0, _glob.sync)(srcPatterns) | ||
}) | ||
// 2. convert message list to object by messageKey | ||
.map(indexBy(messageKey)) | ||
// 2. convert message list to nested objects by messageKey and messageContext | ||
.map(indexBy(messageKey, messageContext)) | ||
// 3. aggregate objects (merge and concat) | ||
.reduce(function (acc, object) { | ||
return (0, _mergeWith2.default)(acc, object, customizer); | ||
return (0, _mergeWith3.default)(acc, object, mergeCustomizer); | ||
}, {}); | ||
@@ -75,0 +80,0 @@ } |
{ | ||
"name": "react-intl-po", | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"description": "Extract POT from react-intl and convert back to json.", | ||
@@ -50,3 +50,3 @@ "author": "Michael Hsu", | ||
"babel-register": "^6.18.0", | ||
"codecov": "^1.0.1", | ||
"codecov": "^2.1.0", | ||
"eslint": "^3.13.0", | ||
@@ -53,0 +53,0 @@ "jest": "^19.0.2", |
@@ -31,4 +31,8 @@ # react-intl-po | ||
[peerDependency]: https://david-dm.org/evenchange4/react-intl-po#info=peerDependencies | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/evenchange4/react-intl-po.svg)](https://greenkeeper.io/) | ||
## Demo | ||
Standalone example based on Create-React-App: https://github.com/evenchange4/react-intl-po-example | ||
## Installation | ||
@@ -62,7 +66,8 @@ | ||
| **Arguments** | **Description** | | ||
| ------------------ | ---------------------------------------------------------------------- | | ||
| `srcPatterns` | The pattern of *.json* files extracted from *babel-plugin-react-intl* | | ||
| `output (-o)` | The output pathname of *.pot* file to be translated | | ||
| `message-key (-k)` | [Optional] Translation message key (default key is `defaultMessage`) | | ||
| **Arguments** | **Description** | | ||
| ------------------ | ---------------------------------------------------------------------- | | ||
| `srcPatterns` | The pattern of *.json* files extracted from *babel-plugin-react-intl* | | ||
| `output (-o)` | The output pathname of *.pot* file to be translated | | ||
| `message-key (-k)` | [Optional] Translation message key (default key is `defaultMessage`) | | ||
| `message-context (-c)` | [Optional] Translation message context (defaults to no context) | | ||
@@ -93,2 +98,3 @@ ### po2json | ||
| `message-key (-k)` | [Optional] Translation message key (default key is `defaultMessage`) | | ||
| `message-context (-c)` | [Optional] Translation message context (defaults to no context) | | ||
@@ -107,2 +113,20 @@ | ||
#### Option 1 (Recommended): | ||
Set the `message-context (-c)` to `'id'` of message object from *babel-plugin-react-intl* (there is no context by default). | ||
The advantage of this option over Option 2 (below) is that PO file editors that provide features such as translation suggestions or error-checking often expect the message key to be `defaultMessage`. | ||
``` | ||
$ rip po2json './node_modules/mcs-translation/po/mcs-public*.po' \ | ||
-m './_translations/src/**/*.json' \ | ||
-o './translations' \ | ||
-c 'id' | ||
$ rip po2json './node_modules/mcs-translation/po/mcs-public*.po' \` | ||
-m './_translations/src/**/*.json' \ | ||
-o './translations.json' \ | ||
-c 'id' | ||
``` | ||
#### Option 2: | ||
Set the `message-key (-k)` to `'id'` of message object from *babel-plugin-react-intl* (default key is `'defaultMessage'`). ([#41](https://github.com/evenchange4/react-intl-po/pull/41)) | ||
@@ -109,0 +133,0 @@ |
@@ -9,2 +9,3 @@ #!/usr/bin/env node | ||
.option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)') | ||
.option('-c, --message-context [context]', 'Translation message context (defaults to no context)') | ||
.action(require('./extractAndWritePOTFromMessagesSync')); | ||
@@ -20,4 +21,5 @@ | ||
.option('-k, --message-key [key]', 'Translation message key (default key is `defaultMessage`)') | ||
.option('-c, --message-context [context]', 'Translation message context (defaults to no context)') | ||
.action(require('./filterPOAndWriteTranslateSync')); | ||
program.parse(process.argv); |
@@ -7,3 +7,3 @@ /* eslint-disable no-console */ | ||
import values from 'lodash/values'; | ||
import flatten from 'lodash/flatten'; | ||
import flattenDeep from 'lodash/flattenDeep'; | ||
import flowRight from 'lodash/flowRight'; | ||
@@ -16,10 +16,14 @@ import toObjectBy from 'to-object-by'; | ||
const getTranslationTableContext = (messageContext, message) => | ||
(messageContext ? `${message[messageContext]}\u0004` : ''); | ||
function filterPOAndWriteTranslateSync(srcPatterns, | ||
{ messageKey = 'defaultMessage', messagesPattern, output }) { | ||
{ messageKey = 'defaultMessage', messageContext = '', messagesPattern, output }) { | ||
const translationTable = readAllPOAsObjectSync(srcPatterns); | ||
const messageList = flowRight( | ||
flatten, // 3. return flatten object values | ||
values, // 2. return object values | ||
readAllMessageAsObjectSync, // 1. return message object | ||
)(messagesPattern, messageKey); | ||
flattenDeep, // 4. return flattened values | ||
objects => objects.map(o => values(o)), // 3. return values | ||
values, // 2. return context objects | ||
readAllMessageAsObjectSync, // 1. return message object | ||
)(messagesPattern, messageKey, messageContext); | ||
@@ -29,3 +33,3 @@ const locales = Object.keys(translationTable); | ||
[locale]: toObjectBy(messageList, message => ({ | ||
[message.id]: translationTable[locale][message[messageKey]], | ||
[message.id]: translationTable[locale][`${getTranslationTableContext(messageContext, message)}${message[messageKey]}`], | ||
})), | ||
@@ -32,0 +36,0 @@ })); |
@@ -49,2 +49,12 @@ /** | ||
/** | ||
* Formatting POT contexts | ||
* @param {String} messageContext | ||
* @return {String} | ||
* | ||
* @author Sandy Suh | ||
*/ | ||
const potContextsFormater = messageContext => | ||
(messageContext ? `msgctxt ${JSON.stringify(messageContext)}\n` : ''); | ||
/** | ||
* Formatting POT comments | ||
@@ -62,5 +72,9 @@ * @param {Object} messageObject | ||
.sort() | ||
.map(id => `${potCommentsFormater(messageObject[id])}msgid ${JSON.stringify(id)}\nmsgstr ""\n`) | ||
.map(id => | ||
Object.keys(messageObject[id]) | ||
.map(context => `${potCommentsFormater(messageObject[id][context])}${potContextsFormater(context)}msgid ${JSON.stringify(id)}\nmsgstr ""\n`) | ||
.join('\n'), | ||
) | ||
.join('\n'); | ||
export default potFormater; |
@@ -6,3 +6,3 @@ import fs from 'fs'; | ||
const customizer = (accValue, objectValue) => { | ||
const concatCustomizer = (accValue, objectValue) => { | ||
if (!isArray(accValue)) return objectValue; | ||
@@ -13,9 +13,14 @@ | ||
const mergeCustomizer = (accValue, objectValue) => | ||
mergeWith(accValue, objectValue, concatCustomizer); | ||
// hint: Use defaultMessage as key by default | ||
const indexBy = messageKey => ({ messages, filename }) => | ||
const indexBy = (messageKey, messageContext) => ({ messages, filename }) => | ||
messages.reduce((acc, message) => ({ | ||
...acc, | ||
[message[messageKey]]: acc[message[messageKey]] | ||
? acc[message[messageKey]].concat([{ ...message, filename }]) | ||
: [{ ...message, filename }], | ||
[message[messageKey]]: mergeWith( | ||
acc[message[messageKey]], | ||
{ [messageContext ? message[messageContext] : '']: [{ ...message, filename }]}, | ||
concatCustomizer, | ||
), | ||
}), {}); | ||
@@ -34,12 +39,12 @@ | ||
function readAllMessageAsObjectSync(srcPatterns, messageKey = 'defaultMessage') { | ||
function readAllMessageAsObjectSync(srcPatterns, messageKey = 'defaultMessage', messageContext = '') { | ||
return globSync(srcPatterns) | ||
// 1. read messages | ||
.map(filename => ({ filename, messages: JSON.parse(fs.readFileSync(filename, 'utf8')) })) | ||
// 2. convert message list to object by messageKey | ||
.map(indexBy(messageKey)) | ||
// 2. convert message list to nested objects by messageKey and messageContext | ||
.map(indexBy(messageKey, messageContext)) | ||
// 3. aggregate objects (merge and concat) | ||
.reduce((acc, object) => mergeWith(acc, object, customizer), {}); | ||
.reduce((acc, object) => mergeWith(acc, object, mergeCustomizer), {}); | ||
} | ||
export default readAllMessageAsObjectSync; |
@@ -25,3 +25,3 @@ import fs from 'fs'; | ||
it('should optput currect filter merged file with id as messageKey', () => { | ||
it('should output correct filter merged file with id as messageKey', () => { | ||
const messagesPattern = './test/messages/**/*.json'; | ||
@@ -33,1 +33,9 @@ const output = './test/temp/translations-id.json'; | ||
}); | ||
it('should output correct filter merged file with id as messageContext', () => { | ||
const messagesPattern = './test/messages/**/*.json'; | ||
const output = './test/temp/translations-ctxt.json'; | ||
filterPOAndWriteTranslateSync('./test/po/mcs-ctxt.*.po', { messageContext: 'id', messagesPattern, output }); | ||
expect(JSON.parse(fs.readFileSync(output, 'utf8'))).toMatchSnapshot(); | ||
}); |
@@ -10,16 +10,18 @@ import potFormater from '../src/potFormater'; | ||
potFormater({ | ||
'Go to MCS website': [ | ||
{ | ||
id: 'App.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/App/App.json', | ||
}, | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
'Go to MCS website': { | ||
'': [ | ||
{ | ||
id: 'App.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/App/App.json', | ||
}, | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
}, | ||
}), | ||
@@ -32,9 +34,11 @@ ).toMatchSnapshot(); | ||
potFormater({ | ||
'Go to MCS website': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
'Go to MCS website': { | ||
'': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
}, | ||
}), | ||
@@ -47,10 +51,12 @@ ).toMatchSnapshot(); | ||
potFormater({ | ||
'NotFound.errorButton': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'My description\nis\nquite\nlong.', | ||
defaultMessage: 'This is\nmultiline', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
'NotFound.errorButton': { | ||
'': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'My description\nis\nquite\nlong.', | ||
defaultMessage: 'This is\nmultiline', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
}, | ||
}), | ||
@@ -63,12 +69,39 @@ ).toMatchSnapshot(); | ||
potFormater({ | ||
'This is "quoted"': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'My description\nis\nquite\nlong.', | ||
defaultMessage: 'This is "quoted"', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
'This is "quoted"': { | ||
'': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'My description\nis\nquite\nlong.', | ||
defaultMessage: 'This is "quoted"', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
}, | ||
}), | ||
).toMatchSnapshot(); | ||
}); | ||
it('should return pot formatted string, with message context', () => { | ||
expect( | ||
potFormater({ | ||
'Go to MCS website': { | ||
'App.errorButton': [ | ||
{ | ||
id: 'App.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/App/App.json', | ||
}, | ||
], | ||
'NotFound.errorButton': [ | ||
{ | ||
id: 'NotFound.errorButton', | ||
description: 'Click error Button', | ||
defaultMessage: 'Go to MCS website', | ||
filename: './messages/src/containers/NotFound/messages.json', | ||
}, | ||
], | ||
}, | ||
}), | ||
).toMatchSnapshot(); | ||
}); |
@@ -30,1 +30,11 @@ import readAllMessageAsObjectSync from '../src/readAllMessageAsObjectSync'; | ||
}); | ||
it('should return messages object with id as context', () => { | ||
expect( | ||
readAllMessageAsObjectSync( | ||
'./test/messages/**/*.json', | ||
undefined, | ||
'id', | ||
), | ||
).toMatchSnapshot(); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
72215
55
945
162