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
145
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-beta1 to 1.0.0-beta10

dist/lexers/jsx-lexer.js

24

CHANGELOG.md
# Changelog
## 1.0.0-beta10 - latest
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta10)
## 1.0.0-beta9
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta9)
## 1.0.0-beta8
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta8)
## 1.0.0-beta7
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta7)
## 1.0.0-beta2
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta2)
## 1.0.0-beta1
- See [release](https://github.com/i18next/i18next-parser/releases/tag/1.0.0-beta1)
## 0.13.0
## 0.13.0 - latest
- Support `defaultValue` option along the translation key (#68)

@@ -10,0 +30,0 @@

29

dist/helpers.js

@@ -1,8 +0,11 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.populateHash = exports.mergeHashes = exports.dotPathToHash = undefined;var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {return typeof obj;} : function (obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;};var _lodash = require('lodash');var _lodash2 = _interopRequireDefault(_lodash);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.ParsingError = exports.populateHash = exports.mergeHashes = exports.dotPathToHash = undefined;var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {return typeof obj;} : function (obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;};var _lodash = require('lodash');var _lodash2 = _interopRequireDefault(_lodash);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;}
// Takes a `path` of the form 'foo.bar' and
// turn it into a hash {foo: {bar: ""}}.
// The generated hash can be attached to an
// optional `hash`.
function dotPathToHash(path) {var separator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '.';var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';var target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
// Turn an entry for the Parser and turn in into a hash,
// turning the key path 'foo.bar' into an hash {foo: {bar: ""}}
// The generated hash can be attached to an optional `target`.
function dotPathToHash(entry) {var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var path = entry.key;
var separator = options.separator || '.';
var value = entry.defaultValue || options.value || '';
if (path.endsWith(separator)) {

@@ -16,2 +19,5 @@ path = path.slice(0, -separator.length);

segments.reduce(function (hash, segment, index) {
if (!segment) {
return hash;
} else
if (index === segments.length - 1) {

@@ -101,8 +107,15 @@ hash[segment] = value;

return target;
}exports.
}var
ParsingError = function (_Error) {_inherits(ParsingError, _Error);
function ParsingError(message) {_classCallCheck(this, ParsingError);var _this = _possibleConstructorReturn(this, (ParsingError.__proto__ || Object.getPrototypeOf(ParsingError)).call(this,
message));
_this.name = 'ParsingError';return _this;
}return ParsingError;}(Error);exports.
dotPathToHash = dotPathToHash;exports.
mergeHashes = mergeHashes;exports.
populateHash = populateHash;
populateHash = populateHash;exports.
ParsingError = ParsingError;

@@ -9,3 +9,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 _vinyl = require('vinyl');var _vinyl2 = _interopRequireDefault(_vinyl);
var _yamljs = require('yamljs');var _yamljs2 = _interopRequireDefault(_yamljs);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 _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

@@ -19,3 +20,3 @@ i18nTransform = function (_Transform) {_inherits(i18nTransform, _Transform);

contextSeparator: '_',
createOldLibraries: true,
createOldCatalogs: true,
defaultNamespace: 'translation',

@@ -33,2 +34,3 @@ defaultValue: '',

output: 'locales',
reactNamespace: false,
sort: false };

@@ -38,5 +40,11 @@

_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.lexers);
_this.parser = new _parser2.default(_this.options);
_this.parser.on('error', function (error) {return _this.emit('error', error);});

@@ -52,3 +60,3 @@ _this.parser.on('warning', function (warning) {return _this.emit('warning', warning);});

if (file.isBuffer()) {
content = file.contents;
content = file.contents.toString('utf8');
} else

@@ -61,4 +69,4 @@ {

var extenstion = _path2.default.extname(file.path).substring(1);
var entries = this.parser.parse(content, extenstion);
var extension = _path2.default.extname(file.path).substring(1);
var entries = this.parser.parse(content, extension);

@@ -72,5 +80,6 @@ entries.forEach(function (entry) {

} else
{
entry.namespace = _this2.options.defaultNamespace;
if (extension === 'jsx' || _this2.options.reactNamespace) {
entry.namespace = _this2.grabReactNamespace(content);
}
entry.namespace = entry.namespace || _this2.options.defaultNamespace;

@@ -100,7 +109,9 @@ key = parts.join(_this2.options.namespaceSeparator);

catalog = (0, _helpers.dotPathToHash)(
entry.key,
_this3.options.keySeparator,
entry.defaultValue || _this3.options.defaultValue,
catalog);
entry,
catalog,
{
separator: _this3.options.keySeparator,
value: _this3.options.defaultValue });
});

@@ -146,3 +157,3 @@

_this3.pushFile(namespacePath, newCatalog);
if (_this3.options.createOldLibraries) {
if (_this3.options.createOldCatalogs) {
_this3.pushFile(namespaceOldPath, oldCatalog);

@@ -215,2 +226,12 @@ }

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;

@@ -18,3 +18,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 regex = new RegExp(
'<([A-Z][A-Z0-9]*)([^>]*\\s' + this.attr + '[^>]*)>(?:(.*?)<\\/\\1>)?',
'<([A-Z][A-Z0-9]*)([^>]*\\s' + this.attr + '[^>]*)>(?:((?:\\s|.)*?)<\\/\\1>)?',
'gi');var _loop = function _loop() {

@@ -21,0 +21,0 @@

@@ -5,2 +5,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 _events = require('events');var _events2 = _interopRequireDefault(_events);

var _javascriptLexer = require('./lexers/javascript-lexer');var _javascriptLexer2 = _interopRequireDefault(_javascriptLexer);
var _jsxLexer = require('./lexers/jsx-lexer');var _jsxLexer2 = _interopRequireDefault(_jsxLexer);
var _path = require('path');var _path2 = _interopRequireDefault(_path);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;}

@@ -16,2 +17,3 @@

js: ['JavascriptLexer'],
jsx: ['JavascriptLexer', 'JsxLexer'],
mjs: ['JavascriptLexer'],

@@ -25,3 +27,4 @@

HTMLLexer: _htmlLexer2.default,
JavascriptLexer: _javascriptLexer2.default };var
JavascriptLexer: _javascriptLexer2.default,
JsxLexer: _jsxLexer2.default };var

@@ -32,3 +35,9 @@

options));
_this.lexers = _extends({}, lexers, options);return _this;
_this.options = options;
if (options.reactNamespace) {
lexers.js = lexers.jsx;
}
_this.lexers = _extends({}, lexers, options.lexers);return _this;
}_createClass(Parser, [{ key: 'parse', value: function parse(

@@ -35,0 +44,0 @@

@@ -38,3 +38,3 @@ # Contribute

```
mocha --require babel-register --require babel-polyfill test/**/*.js
yarn test
```

@@ -58,1 +58,9 @@

I will not maintain the old version but will welcome bug fixes as PRs.
## Deploy
- `yarn watch`
- update `package.json` version
- create commit and add version tag
- `npm pulish --tag next` (skip next tag if not in beta)
- create a github release

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

"name": "i18next-parser",
"version": "1.0.0-beta1",
"version": "1.0.0-beta10",
"license": "MIT",
"main": "src/index.js",
"main": "dist/index.js",
"bin": {

@@ -13,4 +13,4 @@ "i18next": "./bin/cli.js"

"scripts": {
"test": "mocha --require babel-register --require babel-polyfill test/**/*.test.js",
"watch": "babel src -d dist -w"
"test": "mocha --require babel-register --require babel-polyfill 'test/**/*.test.js'",
"watch": "./node_modules/babel-cli/bin/babel.js src -d dist -w"
},

@@ -22,2 +22,3 @@ "repository": {

"dependencies": {
"acorn-jsx": "^4.1.1",
"colors": "~1.2.0-rc0",

@@ -24,0 +25,0 @@ "commander": "~2.9.0",

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

## `1.x`
## DISCLAMER: `1.0.0-beta`
`1.x` is currently in beta. It is a deep rewrite of this package that solves many issues, the main one being that it was slowly becoming unmaintainable. The [migration](docs/migration.md) contains all the breaking changes. If you rely on a `0.x.x` version, you can still find the old documentation on its dedicated [branch](https://github.com/i18next/i18next-parser/tree/0.x.x).
`1.x` is currently in beta. You can follow the pre-releases [here](https://github.com/i18next/i18next-parser/releases). It is a deep rewrite of this package that solves many issues, the main one being that it was slowly becoming unmaintainable. The [migration](docs/migration.md) contains all the breaking changes. Everything that follows is related to `1.x`. If you rely on a `0.x.x` version, you can still find the old documentation on its dedicated [branch](https://github.com/i18next/i18next-parser/tree/0.x.x).

@@ -35,4 +35,4 @@

```
yarn global add i18next-parser
npm install -g i18next-parser
yarn global add i18next-parser@next
npm install -g i18next-parser@next
i18next 'app/**/*.{js,hbs}' 'lib/**/*.{js,hbs}' [-oc]

@@ -52,3 +52,3 @@ ```

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

@@ -77,23 +77,25 @@ ```

Option | Description | Default
Option | Description | Default
---------------------- | ----------------------------------------------------- | ---
**contextSeparator** | Key separator used in your translation keys | `_`
**createOldLibraries** | Save the \_old files | `true`
**defaultNamespace** | Default namespace used in your i18next config | `translation`
**defaultValue** | Default value to give to empty keys | `''`
**extension** | Edit the extension of the locale files | `.json`
**filename** | Edit the filename of the locale files | `'$NAMESPACE'`
**indentation** | Indentation of the catalog files | `2`
**keepRemoved** | Keep keys from the catalog that are no longer in code | `false`
**keySeparator** | Key separator used in your translation keys | `.`
**lexers** | See below for details | `{}`
**lineEnding** | Control the line ending. See options at [eol](https://github.com/ryanve/eol) | `auto`
**locales** | An array of the locales in your applications | `['en','fr']`
**namespaceSeparator** | Namespace separator used in your translation keys | `:`
**output** | Where to write the locale files relative to the base | `locales`
**sort** | Whether or not to sort the catalog | `false`
**contextSeparator** | Key separator used in your translation keys | `_`
**createOldCatalogs** | Save the \_old files | `true`
**defaultNamespace** | Default namespace used in your i18next config | `translation`
**defaultValue** | Default value to give to empty keys | `''`
**extension** <sup>1<sup>| Extenstion of the catalogs | `.json`
**filename** <sup>1<sup>| Filename of the catalogs | `'$NAMESPACE'`
**indentation** | Indentation of the catalog files | `2`
**keepRemoved** | Keep keys from the catalog that are no longer in code | `false`
**keySeparator** <sup>2<sup>| Key separator used in your translation keys | `.`
**lexers** | See below for details | `{}`
**lineEnding** | Control the line ending. See options at [eol](https://github.com/ryanve/eol) | `auto`
**locales** | An array of the locales in your applications | `['en','fr']`
**namespaceSeparator** <sup>2<sup>| Namespace separator used in your translation keys | `:`
**output** | Where to write the locale files relative to the base | `locales`
**reactNamespace** <sup>3<sup>| For react file, extract the [defaultNamespace](https://react.i18next.com/components/translate-hoc.html) | `false`
**sort** | Whether or not to sort the catalog | `false`
### Catalog filenames
1. Both `filename` and `extension` options support injection of `$LOCALE` and `$NAMESPACE` variables. The file output is JSON by default, if you want YAML, the `extension` must end with `yml`.
2. If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
3. If the file being parsed has a `.jsx` extension, this option is ignored and the namespace is being extracted.
Both `filename` and `extension` options support injection of `$LOCALE` and `$NAMESPACE` variables.

@@ -104,3 +106,3 @@ ### Lexers

```
```js
{

@@ -115,2 +117,3 @@ lexers: {

js: ['JavascriptLexer'],
jsx: ['JavascriptLexer', 'JsxLexer'],
mjs: ['JavascriptLexer'],

@@ -125,3 +128,3 @@

```
```js
{

@@ -135,3 +138,3 @@ lexers: {

],
...
// ...
}

@@ -160,4 +163,10 @@ }

**`JsxLexer` options**
Option | Description | Default
------------- | ---------------------- | -------
**attr** | Attribute for the keys | `i18nKey`
## Events

@@ -164,0 +173,0 @@

import _ from 'lodash'
// Takes a `path` of the form 'foo.bar' and
// turn it into a hash {foo: {bar: ""}}.
// The generated hash can be attached to an
// optional `hash`.
function dotPathToHash(path, separator = '.', value = '', target = {}) {
// Turn an entry for the Parser and turn in into a hash,
// turning the key path 'foo.bar' into an hash {foo: {bar: ""}}
// The generated hash can be attached to an optional `target`.
function dotPathToHash(entry, target = {}, options = {}) {
let path = entry.key
const separator = options.separator || '.'
const value = entry.defaultValue || options.value || ''
if (path.endsWith(separator)) {

@@ -16,3 +19,6 @@ path = path.slice(0, -separator.length)

segments.reduce((hash, segment, index) => {
if (index === segments.length - 1) {
if (!segment) {
return hash
}
else if (index === segments.length - 1) {
hash[segment] = value

@@ -103,2 +109,8 @@ }

class ParsingError extends Error {
constructor(message) {
super(message);
this.name = 'ParsingError';
}
}

@@ -108,3 +120,4 @@ export {

mergeHashes,
populateHash
populateHash,
ParsingError
}

@@ -10,2 +10,3 @@ import { dotPathToHash, mergeHashes, populateHash } from './helpers'

import YAML from 'yamljs'
import BaseLexer from './lexers/base-lexer';

@@ -19,3 +20,3 @@ export default class i18nTransform extends Transform {

contextSeparator: '_',
createOldLibraries: true,
createOldCatalogs: true,
defaultNamespace: 'translation',

@@ -33,2 +34,3 @@ defaultValue: '',

output: 'locales',
reactNamespace: false,
sort: false

@@ -38,5 +40,11 @@ }

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.lexers)
this.parser = new Parser(this.options)
this.parser.on('error', error => this.emit('error', error))

@@ -52,3 +60,3 @@ this.parser.on('warning', warning => this.emit('warning', warning))

if (file.isBuffer()) {
content = file.contents
content = file.contents.toString('utf8')
}

@@ -61,4 +69,4 @@ else {

const extenstion = path.extname(file.path).substring(1)
const entries = this.parser.parse(content, extenstion)
const extension = path.extname(file.path).substring(1)
const entries = this.parser.parse(content, extension)

@@ -72,5 +80,6 @@ entries.forEach(entry => {

}
else {
entry.namespace = this.options.defaultNamespace
else if (extension === 'jsx' || this.options.reactNamespace) {
entry.namespace = this.grabReactNamespace(content)
}
entry.namespace = entry.namespace || this.options.defaultNamespace

@@ -100,6 +109,8 @@ key = parts.join(this.options.namespaceSeparator)

catalog = dotPathToHash(
entry.key,
this.options.keySeparator,
entry.defaultValue || this.options.defaultValue,
catalog
entry,
catalog,
{
separator: this.options.keySeparator,
value: this.options.defaultValue
}
)

@@ -146,3 +157,3 @@ })

this.pushFile(namespacePath, newCatalog)
if (this.options.createOldLibraries) {
if (this.options.createOldCatalogs) {
this.pushFile(namespaceOldPath, oldCatalog)

@@ -216,2 +227,12 @@ }

}
grabReactNamespace(content) {
const reactTranslateRegex = new RegExp(
'translate\\((?:\\s*\\[?\\s*)(' + BaseLexer.stringPattern + ')'
)
const translateMatches = content.match(reactTranslateRegex)
if (translateMatches) {
return translateMatches[1].slice(1, -1)
}
}
}

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

const regex = new RegExp(
'<([A-Z][A-Z0-9]*)([^>]*\\s' + this.attr + '[^>]*)>(?:(.*?)<\\/\\1>)?',
'<([A-Z][A-Z0-9]*)([^>]*\\s' + this.attr + '[^>]*)>(?:((?:\\s|.)*?)<\\/\\1>)?',
'gi'

@@ -21,0 +21,0 @@ )

@@ -5,2 +5,3 @@ import EventEmitter from 'events'

import JavascriptLexer from './lexers/javascript-lexer'
import JsxLexer from './lexers/jsx-lexer'
import path from 'path'

@@ -16,2 +17,3 @@

js: ['JavascriptLexer'],
jsx: ['JavascriptLexer', 'JsxLexer'],
mjs: ['JavascriptLexer'],

@@ -25,3 +27,4 @@

HTMLLexer,
JavascriptLexer
JavascriptLexer,
JsxLexer
}

@@ -32,3 +35,9 @@

super(options)
this.lexers = { ...lexers, ...options }
this.options = options
if (options.reactNamespace) {
lexers.js = lexers.jsx
}
this.lexers = { ...lexers, ...options.lexers }
}

@@ -35,0 +44,0 @@

import { assert } from 'chai'
import { dotPathToHash } from '../../src/helpers'
describe('dotPathToHash helper function', function () {
it('creates an object from a string path', function (done) {
const res = dotPathToHash('one')
describe('dotPathToHash helper function', () => {
it('creates an object from a string path', (done) => {
const res = dotPathToHash({ key: 'one' })
assert.deepEqual(res, { one: '' })

@@ -11,4 +11,8 @@ done()

it('ignores trailing separator', function (done) {
const res = dotPathToHash('one..', '..')
it('ignores trailing separator', (done) => {
const res = dotPathToHash(
{ key: 'one.' },
{},
{ separator: '.' }
)
assert.deepEqual(res, { one: '' })

@@ -18,4 +22,6 @@ done()

it('ignores duplicated separator', function (done) {
const res = dotPathToHash('one..two', '..')
it('ignores duplicated separator', (done) => {
const res = dotPathToHash(
{ key: 'one..two' }
)
assert.deepEqual(res, { one: { two: '' } })

@@ -25,10 +31,17 @@ done()

it('use provided default value', function (done) {
const res = dotPathToHash('one', null, 'myDefaultValue')
assert.deepEqual(res, { one: 'myDefaultValue' })
it('handles a target hash', (done) => {
const res = dotPathToHash(
{ key: 'one.two.three' },
{ one: { twenty: '' } }
)
assert.deepEqual(res, { one: { two: { three: '' }, twenty: '' } })
done()
})
it('use provided default value', function (done) {
const res = dotPathToHash('one', null, 'myDefaultValue')
it('handles a `defaultValue` option', (done) => {
const res = dotPathToHash(
{ key: 'one' },
{},
{ value: 'myDefaultValue' }
)
assert.deepEqual(res, { one: 'myDefaultValue' })

@@ -38,10 +51,8 @@ done()

it('handles a target hash', function (done) {
const res = dotPathToHash('one.two.three', '.', '', { one: { twenty: '' } })
assert.deepEqual(res, { one: { two: { three: '' }, twenty: '' } })
done()
})
it('handles a different separator', function (done) {
const res = dotPathToHash('one_two_three.', '_')
it('handles a `separator` option', (done) => {
const res = dotPathToHash(
{ key: 'one_two_three.' },
{},
{ separator: '_' }
)
assert.deepEqual(res, { one: { two: { 'three.': '' } } })

@@ -48,0 +59,0 @@ done()

import { assert } from 'chai'
import { mergeHashes } from '../../src/helpers'
describe('mergeHashes helper function', function () {
it('replaces empty `target` keys with `source`', function (done) {
describe('mergeHashes helper function', () => {
it('replaces empty `target` keys with `source`', (done) => {
const source = { key1: 'value1' }

@@ -15,3 +15,3 @@ const target = { key1: '' }

it('does not replaces empty `target` keys with `source` if it is a hash', function (done) {
it('does not replaces empty `target` keys with `source` if it is a hash', (done) => {
const source = { key1: { key11: 'value1'} }

@@ -24,5 +24,5 @@ const target = { key1: '' }

done()
})
})
it('keeps `target` keys not in `source`', function (done) {
it('keeps `target` keys not in `source`', (done) => {
const source = { key1: 'value1' }

@@ -37,3 +37,3 @@ const target = { key1: '', key2: '' }

it('stores into `old` the keys from `source` that are not in `target`', function (done) {
it('stores into `old` the keys from `source` that are not in `target`', (done) => {
const source = { key1: 'value1', key2: 'value2' }

@@ -48,3 +48,3 @@ const target = { key1: '' }

it('copies `source` keys to `target` regardless of presence when keepRemoved is enabled', function (done) {
it('copies `source` keys to `target` regardless of presence when keepRemoved is enabled', (done) => {
const source = { key1: 'value1', key2: 'value2' }

@@ -59,3 +59,3 @@ const target = { key1: '', key3: '' }

it('restores plural keys when the singular one exists', function (done) {
it('restores plural keys when the singular one exists', (done) => {
const source = { key1: '', key1_plural: 'value1' }

@@ -70,3 +70,3 @@ const target = { key1: '' }

it('does not restores plural keys when the singular one does not', function (done) {
it('does not restores plural keys when the singular one does not', (done) => {
const source = { key1: '', key1_plural: 'value1' }

@@ -81,3 +81,3 @@ const target = { key2: '' }

it('restores context keys when the singular one exists', function (done) {
it('restores context keys when the singular one exists', (done) => {
const source = { key1: '', key1_context: 'value1' }

@@ -92,3 +92,3 @@ const target = { key1: '' }

it('does not restores context keys when the singular one does not', function (done) {
it('does not restores context keys when the singular one does not', (done) => {
const source = { key1: '', key1_context: 'value1' }

@@ -103,3 +103,3 @@ const target = { key2: '' }

it('works with deep objects', function (done) {
it('works with deep objects', (done) => {
const source = {

@@ -158,3 +158,3 @@ key1: 'value1',

it('leaves arrays of values (multiline) untouched', function (done) {
it('leaves arrays of values (multiline) untouched', (done) => {
const source = { key1: ['Line one.', 'Line two.'] }

@@ -161,0 +161,0 @@ const target = { key1: '' }

import { assert } from 'chai'
import { populateHash } from '../../src/helpers'
describe('populateHash helper function', function () {
it('replaces `target` empty keys with `source` ones', function (done) {
describe('populateHash helper function', () => {
it('replaces `target` empty keys with `source` ones', (done) => {
const source = { key1: 'value1' }

@@ -14,3 +14,3 @@ const target = { key1: '' }

it('leaves untouched `target` keys that are not empty', function (done) {
it('leaves untouched `target` keys that are not empty', (done) => {
const source = { key1: 'value1' }

@@ -24,3 +24,3 @@ const target = { key1: 'value2' }

it('leaves untouched `target` keys not in `source`', function (done) {
it('leaves untouched `target` keys not in `source`', (done) => {
const source = { key1: 'value1' }

@@ -34,3 +34,3 @@ const target = { key1: '', key2: '' }

it('works with deep objects', function (done) {
it('works with deep objects', (done) => {
const source = {

@@ -37,0 +37,0 @@ key1: 'value1',

@@ -7,4 +7,6 @@ import { assert } from 'chai'

const enLibraryPath = path.normalize('en/translation.json')
describe('parser', () => {
it('parses globally on multiple lines', done => {
it('parses globally on multiple lines', (done) => {
let result

@@ -19,3 +21,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.once('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -31,3 +33,3 @@ }

it('parses multiline function calls', done => {
it('parses multiline function calls', (done) => {
let result

@@ -43,3 +45,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -56,3 +58,3 @@ }

it('creates context keys', done => {
it('creates context keys', (done) => {
let result

@@ -66,3 +68,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -82,3 +84,3 @@ }

it('parses html files', done => {
it('parses html files', (done) => {
let result

@@ -102,3 +104,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -114,3 +116,3 @@ }

it('parses handlebars files', done => {
it('parses handlebars files', (done) => {
let result

@@ -139,3 +141,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -152,3 +154,3 @@ }

it('parses javascript files', done => {
it('parses javascript files', (done) => {
let result

@@ -170,3 +172,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -183,3 +185,40 @@ }

it('creates two files per namespace and per locale', done => {
it('parses react files', (done) => {
let result
const i18nextParser = new i18nTransform()
const fakeFile = new Vinyl({
contents: fs.readFileSync(
path.resolve(__dirname, 'templating/react.jsx')
),
path: 'react.jsx'
})
const expected = {
first: '',
second: '',
third: {
first: 'Hello <1><0>{{name}}</0></1>, you have <3>{{count}}</3> unread message. <5>Go to messages</5>.',
second: ' <1>Hello,</1> this shouldn\'t be trimmed.',
third: '<0>Hello,</0>this should be trimmed.<2> and this shoudln\'t</2>'
},
fourth: '',
fifth: '',
bar: '',
foo: ''
}
i18nextParser.on('data', file => {
// support for a default Namespace
if (file.relative.endsWith(path.normalize('en/react.json'))) {
result = JSON.parse(file.contents)
}
})
i18nextParser.on('end', () => {
assert.deepEqual(result, expected)
done()
})
i18nextParser.end(fakeFile)
})
it('creates two files per namespace and per locale', (done) => {
let results = []

@@ -198,3 +237,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
results.push(file.relative.replace('locales/', ''))
results.push(file.relative.replace(/locales[\//\\]/, ''))
})

@@ -225,3 +264,3 @@ i18nextParser.on('end', () => {

expectedFiles.forEach(filename => {
assert.include(results, filename)
assert.include(results, path.normalize(filename))
if (!--length) done()

@@ -234,3 +273,3 @@ })

it('handles escaped single and double quotes', done => {
it('handles escaped single and double quotes', (done) => {
let result

@@ -246,3 +285,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -261,3 +300,3 @@ }

it('handles escaped characters', done => {
it('handles escaped characters', (done) => {
let result

@@ -273,3 +312,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -287,3 +326,3 @@ }

it('returns buffers', done => {
it('returns buffers', (done) => {
const i18nextParser = new i18nTransform()

@@ -305,3 +344,3 @@ const fakeFile = new Vinyl({

it('retrieves values in existing catalog', done => {
it('retrieves values in existing catalog', (done) => {
let result

@@ -315,3 +354,3 @@ const i18nextParser = new i18nTransform({ output: 'test/locales' })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_merge.json')) {
if (file.relative.endsWith(path.normalize('en/test_merge.json'))) {
result = JSON.parse(file.contents)

@@ -328,3 +367,3 @@ }

it('does not leak values between locales', done => {
it('does not leak values between locales', (done) => {
let resultEN

@@ -339,6 +378,6 @@ let resultFR

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_leak.json')) {
if (file.relative.endsWith(path.normalize('en/test_leak.json'))) {
resultEN = JSON.parse(file.contents)
}
if (file.relative.endsWith('fr/test_leak.json')) {
if (file.relative.endsWith(path.normalize('fr/test_leak.json'))) {
resultFR = JSON.parse(file.contents)

@@ -356,3 +395,3 @@ }

it('retrieves context values in existing catalog', done => {
it('retrieves context values in existing catalog', (done) => {
let result

@@ -372,3 +411,3 @@ const i18nextParser = new i18nTransform({ output: 'test/locales' })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_context.json')) {
if (file.relative.endsWith(path.normalize('en/test_context.json'))) {
result = JSON.parse(file.contents)

@@ -385,3 +424,3 @@ }

it('retrieves plural values in existing catalog', done => {
it('retrieves plural values in existing catalog', (done) => {
let result

@@ -405,3 +444,3 @@ const i18nextParser = new i18nTransform({ output: 'test/locales' })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_plural.json')) {
if (file.relative.endsWith(path.normalize('en/test_plural.json'))) {
result = JSON.parse(file.contents)

@@ -418,3 +457,3 @@ }

it('retrieves plural and context values in existing catalog', done => {
it('retrieves plural and context values in existing catalog', (done) => {
let result

@@ -434,3 +473,3 @@ const i18nextParser = new i18nTransform({ output: 'test/locales' })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_context_plural.json')) {
if (file.relative.endsWith(path.normalize('en/test_context_plural.json'))) {
result = JSON.parse(file.contents)

@@ -448,3 +487,3 @@ }

describe('options', () => {
it('handles filename and extension with $LOCALE and $NAMESPACE var', done => {
it('handles filename and extension with $LOCALE and $NAMESPACE var', (done) => {
let results = []

@@ -463,3 +502,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
results.push(file.relative.replace('locales/', ''))
results.push(file.relative.replace(/locales[\\\/]/, ''))
})

@@ -474,3 +513,3 @@ i18nextParser.on('end', () => {

expectedFiles.forEach(filename => {
assert.include(results, filename)
assert.include(results, path.normalize(filename))
if (!--length) done()

@@ -483,3 +522,3 @@ })

it('handles custom namespace and key separators', done => {
it('handles custom namespace and key separators', (done) => {
let result

@@ -498,3 +537,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/test_separators.json')) {
if (file.relative.endsWith(path.normalize('en/test_separators.json'))) {
result = JSON.parse(file.contents)

@@ -511,5 +550,29 @@ }

it('supports a defaultValue', done => {
it('handles disabling namespace and key separators', (done) => {
let result
const i18nextParser = new i18nTransform({
namespaceSeparator: false,
keySeparator: false
})
const fakeFile = new Vinyl({
contents: Buffer.from("asd t('Status: loading...')"),
path: 'file.js'
})
i18nextParser.on('data', file => {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)
}
})
i18nextParser.once('end', () => {
assert.deepEqual(result, { 'Status: loading...': '' })
done()
})
i18nextParser.end(fakeFile)
})
it('supports a defaultValue', (done) => {
let result
const i18nextParser = new i18nTransform({
defaultValue: 'NOT_TRANSLATED'

@@ -523,3 +586,3 @@ })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -536,5 +599,44 @@ }

it('supports outputing to yml', done => {
it('parses Trans if reactNamespace is true', (done) => {
let result
const i18nextParser = new i18nTransform({
reactNamespace: true
})
const fakeFile = new Vinyl({
contents: fs.readFileSync(
path.resolve(__dirname, 'templating/react.jsx')
),
path: 'react.js'
})
const expected = {
first: '',
second: '',
third: {
first: 'Hello <1><0>{{name}}</0></1>, you have <3>{{count}}</3> unread message. <5>Go to messages</5>.',
second: ' <1>Hello,</1> this shouldn\'t be trimmed.',
third: '<0>Hello,</0>this should be trimmed.<2> and this shoudln\'t</2>'
},
fourth: '',
fifth: '',
bar: '',
foo: ''
}
i18nextParser.on('data', file => {
// support for a default Namespace
if (file.relative.endsWith(path.normalize('en/react.json'))) {
result = JSON.parse(file.contents)
}
})
i18nextParser.on('end', () => {
assert.deepEqual(result, expected)
done()
})
i18nextParser.end(fakeFile)
})
it('supports outputing to yml', (done) => {
let result
const i18nextParser = new i18nTransform({
extension: '.yml'

@@ -548,3 +650,3 @@ })

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.yml')) {
if (file.relative.endsWith(path.normalize('en/translation.yml'))) {
result = file.contents.toString('utf8')

@@ -554,3 +656,3 @@ }

i18nextParser.once('end', () => {
assert.equal(result, 'first: ""\n')
assert.equal(result.replace(/\r\n/g, '\n'), 'first: ""\n')
done()

@@ -562,3 +664,3 @@ })

it('supports an indentation option', done => {
it('supports an indentation option', (done) => {
let result

@@ -574,3 +676,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = file.contents.toString('utf8')

@@ -580,3 +682,3 @@ }

i18nextParser.once('end', () => {
assert.deepEqual(result.split('\n')[1], ' "first": ""')
assert.deepEqual(result.replace(/\r\n/g, '\n').split('\n')[1], ' "first": ""')
done()

@@ -588,12 +690,11 @@ })

it('handles skipping the old catalog with createOldLibraries=false', done => {
it('handles skipping the old catalog with createOldCatalogs=false', (done) => {
let results = []
const i18nextParser = new i18nTransform({
locales: ['en', 'de', 'fr'],
defaultNamespace: 'default',
createOldLibraries: false
createOldCatalogs: false
})
const fakeFile = new Vinyl({
contents: Buffer.from(
"asd t('ns1:first') t('second') \n asd t('ns2:third') ad t('fourth')"
"asd t('ns1:first') t('second') \n asd ad t('fourth')"
),

@@ -604,3 +705,3 @@ path: 'file.js'

i18nextParser.on('data', file => {
results.push(file.relative.replace('locales/', ''))
results.push(file.relative.replace(/locales[\\\/]/, ''))
})

@@ -611,14 +712,10 @@ i18nextParser.on('end', () => {

'en/ns1.json',
'en/ns2.json',
'de/default.json',
'de/ns1.json',
'de/ns2.json',
'fr/default.json',
'fr/ns1.json',
'fr/ns2.json'
'fr/ns1.json'
]
let length = expectedFiles.length
assert.equal(results.length, expectedFiles.length)
expectedFiles.forEach(filename => {
assert.include(results, filename)
assert.include(results, path.normalize(filename))
if (!--length) done()

@@ -632,3 +729,3 @@ })

describe('lexers', () => {
it('support custom lexers options', done => {
it('support custom lexers options', (done) => {
let result

@@ -651,3 +748,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -666,3 +763,3 @@ }

describe('sort', () => {
it('does not sort by default', done => {
it('does not sort by default', (done) => {
let result

@@ -678,3 +775,3 @@ const i18nextParser = new i18nTransform()

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -692,3 +789,3 @@ }

it('supports sort as an option', done => {
it('supports sort as an option', (done) => {
let result

@@ -706,3 +803,3 @@ const i18nextParser = new i18nTransform({

i18nextParser.on('data', file => {
if (file.relative.endsWith('en/translation.json')) {
if (file.relative.endsWith(enLibraryPath)) {
result = JSON.parse(file.contents)

@@ -723,3 +820,3 @@ }

describe('events', () => {
it('emits a `reading` event', done => {
it('emits a `reading` event', (done) => {
let result

@@ -742,3 +839,3 @@ const i18nextParser = new i18nTransform()

it('emits a `error` event if the catalog is not valid json', done => {
it('emits a `error` event if the catalog is not valid json', (done) => {
const i18nextParser = new i18nTransform({ output: 'test/locales' })

@@ -751,3 +848,3 @@ const fakeFile = new Vinyl({

i18nextParser.on('error', error => {
assert.equal(error.message, 'Unexpected token / in JSON at position 0')
assert.equal(error.message.startsWith('Unexpected token /'), true)
done()

@@ -758,3 +855,3 @@ })

it('emits an `error` if a lexer does not exist', done => {
it('emits an `error` if a lexer does not exist', (done) => {
const results = []

@@ -774,3 +871,3 @@ const i18nextParser = new i18nTransform({ lexers: { js: ['fakeLexer'] } })

it('emits a `warning` event if a key contains a variable', done => {
it('emits a `warning` event if a key contains a variable', (done) => {
const i18nextParser = new i18nTransform({ output: 'test/locales' })

@@ -777,0 +874,0 @@ const fakeFile = new Vinyl({

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