Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

i18next-parser

Package Overview
Dependencies
Maintainers
1
Versions
144
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

i18next-parser - npm Package Compare versions

Comparing version 1.0.0-beta14 to 1.0.0-beta16

dist/broccoli.js

2

bin/cli.js

@@ -5,3 +5,3 @@ #!/usr/bin/env node

var fs = require('fs')
var i18nTransform = require('../dist')
var i18nTransform = require('../dist/transform')
var path = require('path')

@@ -8,0 +8,0 @@ var pkg = require('../package.json')

# Changelog
## 1.0.0-beta14 - latest
## 1.0.0-beta16 - latest

@@ -5,0 +5,0 @@ - The changelog for the beta can be found in the [releases](https://github.com/i18next/i18next-parser/releases)

@@ -1,228 +0,4 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};var _createClass = function () {function defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}return function (Constructor, protoProps, staticProps) {if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};}();var _helpers = require('./helpers');
var _stream = require('stream');
var _lodash = require('lodash');var _lodash2 = _interopRequireDefault(_lodash);
var _eol = require('eol');var _eol2 = _interopRequireDefault(_eol);
var _fs = require('fs');var _fs2 = _interopRequireDefault(_fs);
var _parser = require('./parser');var _parser2 = _interopRequireDefault(_parser);
var _path = require('path');var _path2 = _interopRequireDefault(_path);
var _vinyl = require('vinyl');var _vinyl2 = _interopRequireDefault(_vinyl);
var _yamljs = require('yamljs');var _yamljs2 = _interopRequireDefault(_yamljs);
var _baseLexer = require('./lexers/base-lexer');var _baseLexer2 = _interopRequireDefault(_baseLexer);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call && (typeof call === "object" || typeof call === "function") ? call : self;}function _inherits(subClass, superClass) {if (typeof superClass !== "function" && superClass !== null) {throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;}var
i18nTransform = function (_Transform) {_inherits(i18nTransform, _Transform);
function i18nTransform() {var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};_classCallCheck(this, i18nTransform);
options.objectMode = true;var _this = _possibleConstructorReturn(this, (i18nTransform.__proto__ || Object.getPrototypeOf(i18nTransform)).call(this,
options));
_this.defaults = {
contextSeparator: '_',
createOldCatalogs: true,
defaultNamespace: 'translation',
defaultValue: '',
extension: '.json',
filename: '$NAMESPACE',
indentation: 2,
keepRemoved: false,
keySeparator: '.',
lexers: {},
lineEnding: 'auto',
locales: ['en', 'fr'],
namespaceSeparator: ':',
output: 'locales',
reactNamespace: false,
sort: false };
_this.options = _extends({}, _this.defaults, options);
if (_this.options.keySeparator === false) {
_this.options.keySeparator = '__!NO_KEY_SEPARATOR!__';
}
if (_this.options.namespaceSeparator === false) {
_this.options.namespaceSeparator = '__!NO_NAMESPACE_SEPARATOR!__';
}
_this.entries = [];
_this.parser = new _parser2.default(_this.options);
_this.parser.on('error', function (error) {return _this.emit('error', error);});
_this.parser.on('warning', function (warning) {return _this.emit('warning', warning);});
_this.localeRegex = /\$LOCALE/g;
_this.namespaceRegex = /\$NAMESPACE/g;return _this;
}_createClass(i18nTransform, [{ key: '_transform', value: function _transform(
file, encoding, done) {var _this2 = this;
var content = void 0;
if (file.isBuffer()) {
content = file.contents.toString('utf8');
} else
{
content = _fs2.default.readFileSync(file.path, encoding);
}
this.emit('reading', file);
var extension = _path2.default.extname(file.path).substring(1);
var entries = this.parser.parse(content, extension);
entries.forEach(function (entry) {
var key = entry.key;
var parts = key.split(_this2.options.namespaceSeparator);
// make sure we're not pulling a 'namespace' out of a default value
if (parts.length > 1 && key !== entry.defaultValue) {
entry.namespace = parts.shift();
} else
if (extension === 'jsx' || _this2.options.reactNamespace) {
entry.namespace = _this2.grabReactNamespace(content);
}
entry.namespace = entry.namespace || _this2.options.defaultNamespace;
key = parts.join(_this2.options.namespaceSeparator);
key = key.replace(/\\('|"|`)/g, '$1');
key = key.replace(/\\n/g, '\n');
key = key.replace(/\\r/g, '\r');
key = key.replace(/\\t/g, '\t');
key = key.replace(/\\\\/g, '\\');
entry.key = entry.namespace + _this2.options.keySeparator + key;
_this2.addEntry(entry);
});
done();
} }, { key: '_flush', value: function _flush(
done) {var _this3 = this;
var catalog = {};
if (this.options.sort) {
this.entries = this.entries.sort(function (a, b) {return a.key.localeCompare(b.key);});
}
this.entries.forEach(function (entry) {
catalog = (0, _helpers.dotPathToHash)(
entry,
catalog,
{
separator: _this3.options.keySeparator,
value: _this3.options.defaultValue });
});
this.options.locales.forEach(function (locale) {
var outputPath = _path2.default.resolve(_this3.options.output, locale);
Object.keys(catalog).forEach(function (namespace) {
var filename = _this3.options.filename;
filename = filename.replace(_this3.localeRegex, locale);
filename = filename.replace(_this3.namespaceRegex, namespace);
var extension = _this3.options.extension;
extension = extension.replace(_this3.localeRegex, locale);
extension = extension.replace(_this3.namespaceRegex, namespace);
var oldFilename = filename + '_old' + extension;
filename += extension;
var namespacePath = _path2.default.resolve(outputPath, filename);
var namespaceOldPath = _path2.default.resolve(outputPath, oldFilename);
var newCatalog = void 0;
var existingCatalog = _this3.getCatalog(namespacePath);
var oldCatalog = _this3.getCatalog(namespaceOldPath);
// merges existing translations with the new ones
var _mergeHashes = (0, _helpers.mergeHashes)(
existingCatalog,
catalog[namespace],
null,
_this3.options.keepRemoved),newKeys = _mergeHashes.new,oldKeys = _mergeHashes.old;
// restore old translations if the key is empty
newCatalog = (0, _helpers.populateHash)(oldCatalog, newKeys);
// add keys from the current catalog that are no longer used
oldCatalog = _lodash2.default.extend(oldCatalog, oldKeys);
// push files back to the stream
_this3.pushFile(namespacePath, newCatalog);
if (_this3.options.createOldCatalogs) {
_this3.pushFile(namespaceOldPath, oldCatalog);
}
});
});
done();
} }, { key: 'addEntry', value: function addEntry(
entry) {
var existing = this.entries.filter(function (x) {return x.key === entry.key;})[0];
if (!existing) {
this.entries.push(entry);
} else
{
existing = _extends({}, existing, entry);
}
if (entry.context) {
var contextEntry = Object.assign({}, entry);
delete contextEntry.context;
contextEntry.key += this.options.contextSeparator + entry.context;
this.addEntry(contextEntry);
}
} }, { key: 'getCatalog', value: function getCatalog(
path) {
var content = void 0;
try {
content = JSON.parse(_fs2.default.readFileSync(path));
}
catch (error) {
if (error.code !== 'ENOENT') {
this.emit('error', error);
}
content = {};
}
return content;
} }, { key: 'pushFile', value: function pushFile(
path, contents) {
var text = void 0;
if (path.endsWith('yml')) {
text = _yamljs2.default.stringify(contents, null, this.options.indentation);
} else
{
text = JSON.stringify(contents, null, this.options.indentation) + '\n';
}
if (this.options.lineEnding === 'auto') {
text = _eol2.default.auto(text);
} else
if (lineEnding === '\r\n' || lineEnding === 'crlf') {
text = _eol2.default.crlf(text);
} else
if (lineEnding === '\r' || lineEnding === 'cr') {
text = _eol2.default.cr(text);
} else
{
// Defaults to LF, aka \n
text = _eol2.default.lf(text);
}
var file = new _vinyl2.default({
path: path,
contents: Buffer.from(text) });
this.push(file);
} }, { key: 'grabReactNamespace', value: function grabReactNamespace(
content) {
var reactTranslateRegex = new RegExp(
'translate\\((?:\\s*\\[?\\s*)(' + _baseLexer2.default.stringPattern + ')');
var translateMatches = content.match(reactTranslateRegex);
if (translateMatches) {
return translateMatches[1].slice(1, -1);
}
} }]);return i18nTransform;}(_stream.Transform);exports.default = i18nTransform;module.exports = exports['default'];
'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _broccoli = require('./broccoli');Object.defineProperty(exports, 'broccoli', { enumerable: true, get: function get() {return _interopRequireDefault(_broccoli).default;} });var _parser = require('./parser');Object.defineProperty(exports, 'parser', { enumerable: true, get: function get() {return _interopRequireDefault(_parser).
default;} });var _transform = require('./transform');Object.defineProperty(exports, 'transform', { enumerable: true, get: function get() {return _interopRequireDefault(_transform).
default;} });Object.defineProperty(exports, 'gulp', { enumerable: true, get: function get() {return _interopRequireDefault(_transform).
default;} });function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}

@@ -64,3 +64,3 @@ 'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};var _createClass = function () {function defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}return function (Constructor, protoProps, staticProps) {if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};}();var _baseLexer = require('./base-lexer');var _baseLexer2 = _interopRequireDefault(_baseLexer);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call && (typeof call === "object" || typeof call === "function") ? call : self;}function _inherits(subClass, superClass) {if (typeof superClass !== "function" && superClass !== null) {throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;}var

var functionPattern = this.functionPattern();
var curlyPattern = '(?:{{)' + functionPattern + '\\s+(.*)(?:}})';
var curlyPattern = '(?:{{)' + functionPattern + '\\s+(.*?)(?:}})';
var parenthesisPattern = '(?:\\()' + functionPattern + '\\s+(.*)(?:\\))';

@@ -67,0 +67,0 @@ var pattern = curlyPattern + '|' + parenthesisPattern;

@@ -52,5 +52,16 @@ # Contribute

```
yarn global add gulp@next
cd test
gulp i18next
```
To test broccoli:
```
yarn global add broccoli-cli
cd test
rm -rf dist && broccoli build dist
```
## `0.x` vs `1.x`

@@ -57,0 +68,0 @@

@@ -5,3 +5,3 @@ {

"name": "i18next-parser",
"version": "1.0.0-beta14",
"version": "1.0.0-beta16",
"license": "MIT",

@@ -23,2 +23,3 @@ "main": "dist/index.js",

"acorn-jsx": "^4.1.1",
"broccoli-plugin": "^1.3.0",
"cheerio": "^1.0.0-rc.2",

@@ -31,2 +32,3 @@ "colors": "~1.2.0-rc0",

"lodash": "~4.17.4",
"rsvp": "^4.8.2",
"through2": "~2.0.3",

@@ -47,3 +49,6 @@ "vinyl": "~2.0.1",

"babel-register": "^6.26.0",
"broccoli": "^1.1.4",
"broccoli-funnel": "^2.0.1",
"chai": "^4.1.2",
"fs-extra": "^6.0.1",
"gulp": "^4.0.0",

@@ -50,0 +55,0 @@ "mocha": "^5.0.0"

@@ -50,3 +50,3 @@ # i18next Parser [![Build Status](https://travis-ci.org/i18next/i18next-parser.svg?branch=master)](https://travis-ci.org/i18next/i18next-parser)

yarn add -D i18next-parser@next
npm install --save-dev i18next-parser
npm install --save-dev i18next-parser@next
```

@@ -57,7 +57,7 @@

```javascript
const i18next = require('i18next-parser');
const i18nextParser = require('i18next-parser').gulp;
gulp.task('i18next', function() {
gulp.src('app/**')
.pipe(new i18next({
.pipe(new i18nextParser({
locales: ['en', 'de'],

@@ -72,3 +72,32 @@ output: 'locales'

### Broccoli
Save the package to your devDependencies:
```
yarn add -D i18next-parser@next
npm install --save-dev i18next-parser@next
```
[Broccoli.js](https://github.com/broccolijs/broccoli) defines itself as a fast, reliable asset pipeline, supporting constant-time rebuilds and compact build definitions.
```javascript
const Funnel = require('broccoli-funnel')
const i18nextParser = require('i18next-parser').broccoli;
const appRoot = 'broccoli'
let i18n = new Funnel(appRoot, {
files: ['handlebars.hbs', 'javascript.js'],
annotation: 'i18next-parser'
})
i18n = new i18nextParser([i18n], {
output: 'broccoli/locales'
})
module.exports = i18n
```
## Options

@@ -75,0 +104,0 @@

@@ -1,229 +0,4 @@

import { dotPathToHash, mergeHashes, populateHash } from './helpers'
import { Transform } from 'stream'
import _ from 'lodash'
import eol from 'eol'
import fs from 'fs'
import Parser from './parser'
import path from 'path'
import VirtualFile from 'vinyl'
import YAML from 'yamljs'
import BaseLexer from './lexers/base-lexer';
export default class i18nTransform extends Transform {
constructor(options = {}) {
options.objectMode = true
super(options)
this.defaults = {
contextSeparator: '_',
createOldCatalogs: true,
defaultNamespace: 'translation',
defaultValue: '',
extension: '.json',
filename: '$NAMESPACE',
indentation: 2,
keepRemoved: false,
keySeparator: '.',
lexers: {},
lineEnding: 'auto',
locales: ['en', 'fr'],
namespaceSeparator: ':',
output: 'locales',
reactNamespace: false,
sort: false
}
this.options = { ...this.defaults, ...options }
if (this.options.keySeparator === false) {
this.options.keySeparator = '__!NO_KEY_SEPARATOR!__'
}
if (this.options.namespaceSeparator === false) {
this.options.namespaceSeparator = '__!NO_NAMESPACE_SEPARATOR!__'
}
this.entries = []
this.parser = new Parser(this.options)
this.parser.on('error', error => this.emit('error', error))
this.parser.on('warning', warning => this.emit('warning', warning))
this.localeRegex = /\$LOCALE/g
this.namespaceRegex = /\$NAMESPACE/g
}
_transform(file, encoding, done) {
let content
if (file.isBuffer()) {
content = file.contents.toString('utf8')
}
else {
content = fs.readFileSync(file.path, encoding)
}
this.emit('reading', file)
const extension = path.extname(file.path).substring(1)
const entries = this.parser.parse(content, extension)
entries.forEach(entry => {
let key = entry.key
const parts = key.split(this.options.namespaceSeparator)
// make sure we're not pulling a 'namespace' out of a default value
if (parts.length > 1 && key !== entry.defaultValue) {
entry.namespace = parts.shift()
}
else if (extension === 'jsx' || this.options.reactNamespace) {
entry.namespace = this.grabReactNamespace(content)
}
entry.namespace = entry.namespace || this.options.defaultNamespace
key = parts.join(this.options.namespaceSeparator)
key = key.replace(/\\('|"|`)/g, '$1')
key = key.replace(/\\n/g, '\n')
key = key.replace(/\\r/g, '\r')
key = key.replace(/\\t/g, '\t')
key = key.replace(/\\\\/g, '\\')
entry.key = entry.namespace + this.options.keySeparator + key
this.addEntry(entry)
})
done()
}
_flush(done) {
let catalog = {}
if (this.options.sort) {
this.entries = this.entries.sort((a, b) => a.key.localeCompare(b.key))
}
this.entries.forEach(entry => {
catalog = dotPathToHash(
entry,
catalog,
{
separator: this.options.keySeparator,
value: this.options.defaultValue
}
)
})
this.options.locales.forEach(locale => {
const outputPath = path.resolve(this.options.output, locale)
Object.keys(catalog).forEach(namespace => {
let filename = this.options.filename
filename = filename.replace(this.localeRegex, locale)
filename = filename.replace(this.namespaceRegex, namespace)
let extension = this.options.extension
extension = extension.replace(this.localeRegex, locale)
extension = extension.replace(this.namespaceRegex, namespace)
const oldFilename = filename + '_old' + extension
filename += extension
const namespacePath = path.resolve(outputPath, filename)
const namespaceOldPath = path.resolve(outputPath, oldFilename)
let newCatalog
let existingCatalog = this.getCatalog(namespacePath)
let oldCatalog = this.getCatalog(namespaceOldPath)
// merges existing translations with the new ones
const { new: newKeys, old: oldKeys } = mergeHashes(
existingCatalog,
catalog[namespace],
null,
this.options.keepRemoved
)
// restore old translations if the key is empty
newCatalog = populateHash(oldCatalog, newKeys)
// add keys from the current catalog that are no longer used
oldCatalog = _.extend(oldCatalog, oldKeys)
// push files back to the stream
this.pushFile(namespacePath, newCatalog)
if (this.options.createOldCatalogs) {
this.pushFile(namespaceOldPath, oldCatalog)
}
})
})
done()
}
addEntry(entry) {
let existing = this.entries.filter(x => x.key === entry.key)[0]
if (!existing) {
this.entries.push(entry)
}
else {
existing = { ...existing, ...entry }
}
if (entry.context) {
const contextEntry = Object.assign({}, entry)
delete contextEntry.context
contextEntry.key += this.options.contextSeparator + entry.context
this.addEntry(contextEntry)
}
}
getCatalog(path) {
let content
try {
content = JSON.parse( fs.readFileSync( path ) )
}
catch (error) {
if (error.code !== 'ENOENT') {
this.emit('error', error)
}
content = {}
}
return content
}
pushFile(path, contents) {
let text
if (path.endsWith('yml')) {
text = YAML.stringify(contents, null, this.options.indentation)
}
else {
text = JSON.stringify(contents, null, this.options.indentation) + '\n'
}
if (this.options.lineEnding === 'auto') {
text = eol.auto(text)
}
else if (lineEnding === '\r\n' || lineEnding === 'crlf') {
text = eol.crlf(text)
}
else if (lineEnding === '\r' || lineEnding === 'cr') {
text = eol.cr(text)
}
else {
// Defaults to LF, aka \n
text = eol.lf(text)
}
const file = new VirtualFile({
path,
contents: Buffer.from(text)
})
this.push(file)
}
grabReactNamespace(content) {
const reactTranslateRegex = new RegExp(
'translate\\((?:\\s*\\[?\\s*)(' + BaseLexer.stringPattern + ')'
)
const translateMatches = content.match(reactTranslateRegex)
if (translateMatches) {
return translateMatches[1].slice(1, -1)
}
}
}
export { default as broccoli } from './broccoli'
export { default as parser } from './parser'
export { default as transform } from './transform'
export { default as gulp } from './transform'

@@ -64,3 +64,3 @@ import BaseLexer from './base-lexer'

const functionPattern = this.functionPattern()
const curlyPattern = '(?:{{)' + functionPattern + '\\s+(.*)(?:}})'
const curlyPattern = '(?:{{)' + functionPattern + '\\s+(.*?)(?:}})'
const parenthesisPattern = '(?:\\()' + functionPattern + '\\s+(.*)(?:\\))'

@@ -67,0 +67,0 @@ const pattern = curlyPattern + '|' + parenthesisPattern

{
"bar": "",
"foo": "",
"first": "Yo2!",
"second": "",
"fourth": "",
"second": "",
"bar": "",
"foo": "",
"third": {

@@ -12,3 +12,5 @@ "first": "Hello <1><0>{{name}}</0></1>, you have <3>{{count}}</3> unread message. <5>Go to messages</5>.",

},
"fifth": ""
"fifth": "",
"This should be part of the value and the key": "This should be part of the value and the key",
"don't split <1>{{on}}</1>": "don't split <1>{{on}}</1>"
}

@@ -12,3 +12,4 @@ {

"sixth": "",
"seventh": "defaultValue"
"seventh": "defaultValue",
"selfClosing": ""
}
{
"bar": "",
"foo": "",
"first": "",
"second": "",
"fourth": "",
"second": "",
"bar": "",
"foo": "",
"third": {

@@ -12,3 +12,5 @@ "first": "Hello <1><0>{{name}}</0></1>, you have <3>{{count}}</3> unread message. <5>Go to messages</5>.",

},
"fifth": ""
"fifth": "",
"This should be part of the value and the key": "This should be part of the value and the key",
"don't split <1>{{on}}</1>": "don't split <1>{{on}}</1>"
}

@@ -12,3 +12,4 @@ {

"sixth": "",
"seventh": "defaultValue"
"seventh": "defaultValue",
"selfClosing": ""
}

@@ -12,2 +12,9 @@ import { assert } from 'chai'

it('extracts multiple keys on a single line', (done) => {
const Lexer = new HandlebarsLexer()
const content = '<p>{{t "first"}} {{t "second"}}</p>'
assert.deepEqual(Lexer.extract(content), [{ key: 'first' }, { key: 'second' }])
done()
})
it('extracts the second argument as defaultValue', (done) => {

@@ -14,0 +21,0 @@ const Lexer = new HandlebarsLexer()

import { assert } from 'chai'
import Vinyl from 'vinyl'
import fs from 'fs'
import i18nTransform from '../src/index'
import i18nTransform from '../src/transform'
import path from 'path'

@@ -6,0 +6,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc