Socket
Socket
Sign inDemoInstall

i18next-scanner

Package Overview
Dependencies
8
Maintainers
1
Versions
92
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 0.2.0

test/output/i18n/de/translation.savedMissing.json

10

gulpfile.js

@@ -64,7 +64,5 @@ var _ = require('lodash');

key = hash(value); // returns a hash value as default key
parser.parseValue(value, key);
return;
}
parser.parseKey(key, value);
parser.parse(key, value);
});

@@ -97,3 +95,3 @@ }());

key = hash(value); // returns a hash value as default key
parser.parseValue(value, key);
parser.parse(key, value);
});

@@ -120,3 +118,3 @@ }());

key = hash(value); // returns a hash value as default key
parser.parseValue(value, key);
parser.parse(key, value);
}

@@ -123,0 +121,0 @@ });

4

index.js

@@ -33,3 +33,3 @@ var _ = require('lodash');

var keys = _.trim(r[1], '\'"');
parser.parseKeys(keys);
parser.parseAttrs(keys);
}

@@ -52,3 +52,3 @@ });

var key = _.str.trim(r[1], '\'"');
parser.parseKey(key);
parser.parse(key);
}

@@ -55,0 +55,0 @@ });

@@ -104,44 +104,7 @@ var fs = require('fs');

/**
* Parses translation keys from `data-i18n` attribute in HTML
* @param {string} attrs A semicolon-separated list of attributes
*/
Parser.prototype.parseAttrs = function(attrs) {
var _self = this;
var keys = [];
if (attrs.indexOf(';') <= attrs.length-1) {
keys = attrs.split(';');
} else {
keys = [ attrs ];
}
_.each(keys, function(key) {
var attr = 'text';
key = _.trim(key);
if (key.length === 0) {
return;
}
if (key.indexOf('[') === 0) {
var parts = key.split(']');
key = parts[1];
attr = parts[0].substr(1, parts[0].length-1);
}
if (key.indexOf(';') === key.length-1) {
key = key.substr(0, key.length-2);
}
_self.parseKey(key);
});
};
/**
* Parses a key string and stores the key-value pairs to i18n resource store
* Parses a translation key and stores the key-value pairs to i18n resource store
* @param {string} key The translation key
* @param {string} [defaultValue] The key's value
*/
Parser.prototype.parseKey = function(key, defaultValue) {
Parser.prototype.parse = function(key, defaultValue) {
var options = this.options;

@@ -199,29 +162,35 @@ var resStore = this.resStore;

/**
* Parses a value string and stores the key-value pairs to i18n resource store
* @param {string} value The key's value
* @param {string} defaultKey The translation key
* Parses translation keys from `data-i18n` attribute in HTML
* @param {string} attrs A semicolon-separated list of attributes
*/
Parser.prototype.parseValue = function(value, defaultKey) {
var options = this.options;
var resStore = this.resStore;
Parser.prototype.parseAttrs = function(attrs) {
var _self = this;
var ns = _.isString(options.ns) ? options.ns : options.ns.defaultNs;
console.assert(_.isString(ns) && !!ns.length, 'ns is not a valid string', ns);
var keys = [];
if (attrs.indexOf(';') <= attrs.length-1) {
keys = attrs.split(';');
} else {
keys = [ attrs ];
}
var key = defaultKey;
console.assert(_.isString(key) && !!key.length, 'defaultKey is not a valid string', key);
_.each(keys, function(key) {
var attr = 'text';
_.each(options.lngs, function(lng) {
var lookupKey = '[' + lng + '][' + ns + '][' + key + ']';
key = _.trim(key);
// Verify existing value
var _value = map(resStore, lng + '.' + ns + '.' + key);
if (_.isUndefined(_value)) {
map(resStore, lng + '.' + ns + '.' + key, value);
debuglog('[+] resStore' + lookupKey + '="' + map(resStore, lookupKey) + '"');
} else if (_value === value) {
debuglog('[v] resStore' + lookupKey + '="' + map(resStore, lookupKey) + '"');
} else {
console.error('[x] Key conflict in ' + lookupKey + '="' + _value + '"\n' + 'sha1' + '("' + value + '")=' + key);
if (key.length === 0) {
return;
}
if (key.indexOf('[') === 0) {
var parts = key.split(']');
key = parts[1];
attr = parts[0].substr(1, parts[0].length-1);
}
if (key.indexOf(';') === key.length-1) {
key = key.substr(0, key.length-2);
}
_self.parseKey(key);
});

@@ -231,3 +200,4 @@ };

/**
* Parses hash arguments
* Parses hash arguments for Handlebars block helper
* @see [Hash Arguments]{@http://code.demunskin.com/other/Handlebars/block_helpers.html#hash-arguments}
* @see [Regular expression for parsing name value pairs]{@link http://stackoverflow.com/questions/168171/regular-expression-for-parsing-name-value-pairs}

@@ -238,7 +208,8 @@ * @example <caption>Example usage:</caption>

* str.match(/([^=,\s]*)\s*=\s*((?:"(?:\\.|[^"\\]+)*"|'(?:\\.|[^'\\]+)*')|[^'"\s]*)/igm) || [];
* @param [string] hashArguments A string representation of hash arguments
* @param [string] str A string representation of hash arguments
* @return {object}
*/
Parser.prototype.parseHashArguments = function(hashArguments) {
Parser.prototype.parseHashArguments = function(str) {
var hash = {};
var params = hashArguments.match(/([^=,\s]*)\s*=\s*((?:"(?:\\.|[^"\\]+)*"|'(?:\\.|[^'\\]+)*')|[^'"\s]*)/igm) || [];
var params = str.match(/([^=,\s]*)\s*=\s*((?:"(?:\\.|[^"\\]+)*"|'(?:\\.|[^'\\]+)*')|[^'"\s]*)/igm) || [];
var unquote = function(str, quoteChar) {

@@ -278,2 +249,3 @@ quoteChar = quoteChar || '"';

});
return hash;

@@ -280,0 +252,0 @@ };

{
"name": "i18next-scanner",
"version": "0.1.0",
"description": "A text scanner for i18next",
"version": "0.2.0",
"description": "i18next-scanner is a transfrom stream that can scan your code, extract translation keys/values, and merge them into i18n resource files.",
"homepage": "https://github.com/cheton/i18next-scanner",

@@ -6,0 +6,0 @@ "author": "Cheton Wu <cheton@gmail.com>",

@@ -5,30 +5,47 @@ # i18next-scanner [![build status](https://travis-ci.org/cheton/i18next-scanner.svg?branch=master)](https://travis-ci.org/cheton/i18next-scanner)

i18next-scanner is available as both gulp and grunt plugins that can scan your code, extracts translation keys/values, and merges them into i18n resource files.
i18next-scanner is a transfrom stream that can scan your code, extract translation keys/values, and merge them into i18n resource files.
It's available as both Gulp and Grunt plugins.
## Features
* Fully compatible with [i18next](https://github.com/i18next/i18next) - a full-featured i18n javascript library for translating your webapplication.
* Support [i18next-text](https://github.com/cheton/i18next-text) to write your code without the need to maintain i18n keys.
* A transform stream that works with both Gulp and Grunt task runner.
* Support custom transform and flush functions.
## Installation
```
npm install --save-dev i18next-scanner
```
`npm install i18next-scanner`
## Usage
The main entry function of [i18next-scanner](https://github.com/cheton/i18next-scanner) is a transform stream. You can use [vinyl-fs](https://github.com/wearefractal/vinyl) to create a readable stream, pipe the stream through [i18next-scanner](https://github.com/cheton/i18next-scanner) to transform your code into an i18n resource object, and write to a destination folder.
Here is a simple example showing how that works:
```javascript
var i18next = require('i18next-scanner');
var vfs = require('vinyl-fs');
vfs.src(['path/to/src'])
.pipe(i18next())
.pipe(vfs.dest('path/to/dest');
```
## Gulp Usage
Now you are ready to set up a minimal configuration, and get started with Gulp.
For example:
```javascript
gulp.task('i18next-scanner', function() {
var i18next = require('i18next-scanner');
var gulp = require('gulp');
var i18next = require('i18next-scanner');
return gulp.src(['src/**/*.{js,html}'], {base: 'src'})
gulp.task('i18next', function() {
return gulp.src(['src/**/*.{js,html}'])
.pipe(i18next({
// Provides a list of supported languages by setting the lngs option.
lngs: ['en', 'de'],
// Sorts the keys in ascending order.
sort: true, // default: false
// Provides a default value if a value is not specified.
defaultValue: '', // default: ''
// The resGetPath is your source i18n path, it is relative to current working directory.
resGetPath: 'assets/i18n/__lng__/__ns__.json', // default: 'i18n/__lng__/__ns__.json'
// The resSetPath is your target i18n path, it is relative to your gulp.dest path.
resSetPath: 'i18n/__lng__/__ns__.json', // default: 'i18n/__lng__/__ns__.json'
// Default namespace is 'translation'.
ns: 'translation'
// a list of supported languages
lngs: ['en', 'de'],
// the source path is relative to current working directory
resGetPath: 'assets/i18n/__lng__/__ns__.json',
// the destination path is relative to your `gulp.dest()` path
resSetPath: 'i18n/__lng__/__ns__.json'
})

@@ -39,152 +56,345 @@ .pipe(gulp.dest('assets'));

## Grunt Usage
Once you've finished the installation, add this line to your project's Gruntfile:
```javascript
grunt.loadNpmTasks('i18next-scanner');
```
## Usage with i18next-text
In your project's Gruntfile, add a section named `i18next` to the data object passed into `grunt.initConfig()`, like so:
```javascript
grunt.initConfig({
i18next: {
dev: {
src: 'src/**/*.{js,html}',
dest: 'assets',
options: {
lngs: ['en', 'de'],
resGetPath: 'assets/i18n/__lng__/__ns__.json',
resSetPath: 'i18n/__lng__/__ns__.json'
}
}
}
});
````
### Parses the i18n._() method
## Advanced Usage
### Customize transform and flush functions
As mentioned in the [Usage](#usage) section, the main entry function returns a [through2](https://github.com/rvagg/through2) object stream, you can pass in your `transform` and `flush` functions:
```javascript
i18next(options[, customTransform[, customFlush]])
```
gulp.task('i18next-scanner', function() {
var i18next = require('i18next-scanner');
var hash = require('i18next-text').hash['sha1'];
var options = {
lngs: ['en', 'de'],
defaultValue: '__STRING_NOT_TRANSLATED__',
resGetPath: 'assets/i18n/__lng__/__ns__.json',
resSetPath: 'i18n/__lng__/__ns__.json',
ns: 'translation'
};
var customTransform = function(file, enc, done) {
var parser = this.parser;
var extname = path.extname(file.path);
var content = fs.readFileSync(file.path, enc);
### Usage with i18next-text
/*
* Supports i18next-text's _() method for i18next:
*
* i18n._('This is text value');
* i18n._("text"); // result matched
* i18n._('text'); // result matched
* i18n._("text", { count: 1 }); // result matched
* i18n._("text" + str); // skip run-time variables
*/
(function() {
var results = content.match(/i18n\._\(("[^"]*"|'[^']*')\s*[\,\)]/igm) || '';
_.each(results, function(result) {
var key, value;
var r = result.match(/i18n\._\(("[^"]*"|'[^']*')/);
#### Example of parsing strings
You might want to find all occurrences of the `i18n._()` function in your code.
For example:
```javascript
i18n._('This is text value');
i18n._("text");
i18n._('text');
i18n._("text", { count: 1 });
i18n._("text" + str); // skip run-time variables
```
if (r) {
value = _.trim(r[1], '\'"');
key = hash(value); // returns a SHA-1 hash value as default key
parser.parseValue(value, key);
}
});
}());
The content can be parsed with a regular expression, like below:
```javascript
i18n\._\(("[^"]*"|'[^']*')\s*[\,\)]
```
done();
};
The code might look like this:
```javascript
var _ = require('lodash');
var hash = require('i18next-text').hash['sha1'];
var customTransform = function(file, enc, done) {
var parser = this.parser;
var extname = path.extname(file.path);
var content = fs.readFileSync(file.path, enc);
return gulp.src(['src/**/*.js'], {base: 'src'})
.pipe(i18next(options))
.pipe(gulp.dest('assets'));
});
(function() {
var results = content.match(/i18n\._\(("[^"]*"|'[^']*')\s*[\,\)]/igm) || '';
_.each(results, function(result) {
var key, value;
var r = result.match(/i18n\._\(("[^"]*"|'[^']*')/);
if (r) {
value = _.trim(r[1], '\'"');
key = hash(value); // returns a hash value as its default key
parser.parse(key, value);
}
});
}());
done();
};
```
### Handlebars i18n helper with block expressions
#### Handlebars i18n helper
**i18n function helper**
```hbs
{{i18n 'bar'}}
{{i18n 'bar' defaultKey='foo'}}
{{i18n 'baz' defaultKey='locale:foo'}}
{{i18n defaultKey='noval'}}
```
Using the regular expression for the above:
```javascript
{{i18n\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')?([^}]*)}}
```
**i18n block helper**
```hbs
{{#i18n}}Some text{{/i18n}}
{{#i18n this}}Description: {{description}}{{/i18n}}
{{#i18n this last-name=lastname}}{{firstname}} ${last-name}{{/i18n}}
```
gulp.task('i18next-scanner', function() {
var i18next = require('i18next-scanner');
var hash = require('i18next-text').hash['sha1'];
var options = {
lngs: ['en', 'de'],
defaultValue: '__STRING_NOT_TRANSLATED__',
resGetPath: 'assets/i18n/__lng__/__ns__.json',
resSetPath: 'i18n/__lng__/__ns__.json',
ns: 'translation'
};
Using the regular expression for the above:
```javascript
{{#i18n\s*([^}]*)}}((?:(?!{{\/i18n}})(?:.|\n))*){{\/i18n}}
```
var customTransform = function(file, enc, done) {
var parser = this.parser;
var extname = path.extname(file.path);
var content = fs.readFileSync(file.path, enc);
**Sample code**
/*
* Supports Handlebars i18n helper
*
* {{i18n 'bar'}}
* {{i18n 'bar' defaultKey='foo'}}
* {{i18n 'baz' defaultKey='locale:foo'}}
* {{i18n defaultKey='noval'}}
*/
(function() {
var results = content.match(/{{i18n\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')?([^}]*)}}/gm) || [];
_.each(results, function(result) {
var key, value;
var r = result.match(/{{i18n\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')?([^}]*)}}/m) || [];
The sample code might look like this:
```javascript
var _ = require('lodash');
var hash = require('i18next-text').hash['sha1'];
var customTransform = function(file, enc, done) {
var parser = this.parser;
var extname = path.extname(file.path);
var content = fs.readFileSync(file.path, enc);
if ( ! _.isUndefined(r[1])) {
value = _.trim(r[1], '\'"');
}
// i18n function helper
(function() {
var results = content.match(/{{i18n\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')?([^}]*)}}/gm) || [];
_.each(results, function(result) {
var key, value;
var r = result.match(/{{i18n\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')?([^}]*)}}/m) || [];
var params = parser.parseHashArguments(r[2]);
if (_.has(params, 'defaultKey')) {
key = params['defaultKey'];
}
if (_.isUndefined(key) && _.isUndefined(value)) {
return;
}
if ( ! _.isUndefined(r[1])) {
value = _.trim(r[1], '\'"');
}
if (_.isUndefined(key)) {
key = hash(value); // returns a SHA-1 hash value as default key
parser.parseValue(value, key);
return;
}
var params = parser.parseHashArguments(r[2]);
if (_.has(params, 'defaultKey')) {
key = params['defaultKey'];
}
parser.parseKey(key, value);
});
}());
if (_.isUndefined(key) && _.isUndefined(value)) {
return;
}
/*
* Supports Handlebars i18n helper with block expressions
*
* {{#i18n}}Some text{{/i18n}}
* {{#i18n this}}Description: {{description}}{{/i18n}}
* {{#i18n this last-name=lastname}}{{firstname}} ${last-name}{{/i18n}}
*
* http://stackoverflow.com/questions/406230/regular-expression-to-match-string-not-containing-a-wordo
*/
(function() {
var results = content.match(/{{#i18n\s*([^}]*)}}((?:(?!{{\/i18n}})(?:.|\n))*){{\/i18n}}/gm) || [];
_.each(results, function(result) {
var key, value;
var r = result.match(/{{#i18n\s*([^}]*)}}((?:(?!{{\/i18n}})(?:.|\n))*){{\/i18n}}/m) || [];
if (_.isUndefined(key)) {
key = hash(value); // returns a hash value as its default key
}
if ( ! _.isUndefined(r[2])) {
value = _.trim(r[2], '\'"');
}
parser.parse(key, value);
});
}());
if (_.isUndefined(value)) {
return;
}
// i18n block helper
(function() {
var results = content.match(/{{#i18n\s*([^}]*)}}((?:(?!{{\/i18n}})(?:.|\n))*){{\/i18n}}/gm) || [];
_.each(results, function(result) {
var key, value;
var r = result.match(/{{#i18n\s*([^}]*)}}((?:(?!{{\/i18n}})(?:.|\n))*){{\/i18n}}/m) || [];
key = hash(value); // returns a SHA-1 hash value as default key
parser.parseValue(value, key);
});
}());
if ( ! _.isUndefined(r[2])) {
value = _.trim(r[2], '\'"');
}
done();
};
if (_.isUndefined(value)) {
return;
}
return gulp.src(['src/**/*.hbs'], {base: 'src'})
.pipe(i18next(options))
.pipe(gulp.dest('assets'));
});
key = hash(value); // returns a hash value as its default key
parser.parse(key, value);
});
}());
done();
};
```
## Options
## API
```
function(options[, customTransform[, customFlush]])
```
### options
```javascript
{
lngs: ['en'],
sort: false,
defaultValue: '',
resGetPath: 'i18n/__lng__/__ns__.json',
resSetPath: 'i18n/__lng__/__ns__.json',
nsseparator: ':',
keyseparator: '.',
interpolationPrefix: '__',
interpolationSuffix: '__',
ns: {
namespaces: [],
defaultNs: 'translation'
}
}
```
#### lngs
Type: `Array` Default: `['en']`
Provides a list of supported languages.
#### sort
Type: `Boolean` Default: `false`
Set to `true` if you want to sort translation keys in ascending order.
#### defaultValue
Type: `String` Default: `''`
Provides a default value if a value is not specified.
#### resGetPath
Type: `String` Default: `'i18n/__lng__/__ns__.json'`
The source path of resource files. The `resGetPath` is relative to current working directory.
#### resSetPath
Type: `String` Default: `'i18n/__lng__/__ns__.json'`
The target path of resource files. The `resSetPath` is relative to current working directory or your `gulp.dest()` path.
#### nsseparator
Type: `String` Default: `':'`
The namespace separator.
#### keyseparator
Type: `String` Default: `'.'`
The key separator.
#### interpolationPrefix
Type: `String` Default: `'__'`
The prefix for variables.
#### interpolationSuffix
Type: `String` Default: `'__'`
The suffix for variables.
#### ns
Type: `Object` or `String`
If an `Object` is supplied, you can either specify a list of namespaces, or override the default namespace.
For example:
```javascript
{
ns: {
namespaces: [ // Default: []
'resource',
'locale'
],
defaultNs: 'resource' // Default: 'translation'
}
}
```
If a `String` is supplied instead, it will become the default namespace.
For example:
```javascript
{
ns: 'resource' // Default: 'translation'
}
```
### customTransform
The optional `customTransform` function is provided as the 2nd argument. It must have the following signature: `function (file, encoding, done) {}`. A minimal implementation should call the `done()` function to indicate that the transformation is done, even if that transformation means discarding the file.
For example:
```javascript
var customTransform = function _transform(file, enc, done) {
var parser = this.parser;
var extname = path.extname(file.path);
var content = fs.readFileSync(file.path, enc);
// add custom code
done();
};
```
To parse a translation key, call `parser.parse(key, defaultValue)` to assign the key with an optional `defaultValue`.
For example:
```javascript
var _ = require('lodash');
var customTransform = function _transform(file, enc, done) {
var parser = this.parser;
var content = fs.readFileSync(file.path, enc);
var results = [];
// parse the content and loop over the results
_.each(results, function(result) {
var key = result.key;
var value = result.defaultValue || '';
parser.parse(key, value);
});
};
```
Alternatively, you may call `parser.parse(defaultKey, value)` to assign the value with a default key. The `defaultKey` should be unique string and can never be `null`, `undefined`, or empty.
For example:
```javascript
var _ = require('lodash');
var hash = require('i18next-text').hash['sha1'];
var customTransform = function _transform(file, enc, done) {
var parser = this.parser;
var content = fs.readFileSync(file.path, enc);
var results = [];
// parse the content and loop over the results
_.each(results, function(result) {
var key = result.defaultKey || hash(result.value);
var value = result.value;
parser.parse(key, value);
});
};
```
### customFlush
The optional `customFlush` function is provided as the last argument, it is called just prior to the stream ending. You can implement your `customFlush` function to override the default `flush` function. When everything's done, call the `done()` function to indicate the stream is finished.
For example:
```javascript
var _ = require('lodash');
var customFlush = function _flush(done) {
var that = this;
var resStore = parser.toObject({
sort: !!parser.options.sort
});
// loop over the resStore
_.each(resStore, function(namespaces, lng) {
_.each(namespaces, function(obj, ns) {
// add custom code
});
});
done();
};
```
## License

@@ -191,0 +401,0 @@

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc