Skip to content

Commit

Permalink
Support new plural suffixes with JSON v4 format (#252)
Browse files Browse the repository at this point in the history
* Support new plural suffixes

Fixes gh-228

Signed-off-by: Scott González <[email protected]>

* refactor: rename the `pluralVersion` option to `compatibilityJSON` to align with the conventions of `i18next`

* update test name

---------

Signed-off-by: Scott González <[email protected]>
Co-authored-by: cheton <[email protected]>
  • Loading branch information
scottgonzalez and cheton authored Aug 25, 2023
1 parent 9f9d300 commit 8daa6b4
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 43 deletions.
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 23,7 @@ into resource files
{
"Loading...": "Wird geladen...", // uses existing translation
"Backslashes in single quote: ' \\ '": "__NOT_TRANSLATED__", // returns a custom string
"This is a multiline string": "this is a multiline string", // returns the key as the default value
"This is a multiline string": "this is a multiline string", // returns the key as the default value
"car": "car",
"car_blue": "One blue car",
"car_blue_plural": "{{count}} blue cars",
Expand Down Expand Up @@ -263,7 263,7 @@ gulp.task('i18next', function() {
resource: {
// the source path is relative to current working directory
loadPath: 'assets/i18n/{{lng}}/{{ns}}.json',

// the destination path is relative to your `gulp.dest()` path
savePath: 'i18n/{{lng}}/{{ns}}.json'
}
Expand Down Expand Up @@ -307,7 307,7 @@ const Parser = require('i18next-scanner').Parser;
const parser = new Parser(options);

const code = "i18next.t('key'); ...";
parser.parseFuncFromString(code);
parser.parseFuncFromString(code);

const jsx = '<Trans i18nKey="some.key">Default text</Trans>';
parser.parseTransFromString(jsx);
Expand Down Expand Up @@ -381,7 381,7 @@ Get the value of a translation key or the whole i18n resource store
// Returns the whole i18n resource store
parser.get();

// Returns the resource store with the top-level keys sorted by alphabetical order
// Returns the resource store with the top-level keys sorted by alphabetical order
parser.get({ sort: true });

// Returns a value in fallback language (@see options.fallbackLng) with namespace and key
Expand Down Expand Up @@ -438,12 438,12 @@ For example:
const customTransform = function _transform(file, enc, done) {
const parser = this.parser;
const content = fs.readFileSync(file.path, enc);

parser.parseFuncFromString(content, { list: ['i18n.t'] }, function(key) {
const defaultValue = '__L10N__';
parser.set(key, defaultValue);
});

done();
};
```
Expand All @@ -455,13 455,13 @@ const hash = require('sha1');
const customTransform = function _transform(file, enc, done) {
const parser = this.parser;
const content = fs.readFileSync(file.path, enc);

parser.parseFuncFromString(content, { list: ['i18n._'] }, function(key) {
const value = key;
const defaultKey = hash(value);
parser.set(defaultKey, value);
});

done();
};
```
Expand All @@ -484,7 484,7 @@ const customFlush = function _flush(done) {
// add your code
});
});

done();
};

Expand All @@ -500,6 500,7 @@ Below are the configuration options with their default values:

```javascript
{
compatibilityJSON: 'v3', // One of: 'v1', 'v2', 'v3', 'v4
debug: false,
removeUnusedKeys: false,
sort: false,
Expand Down Expand Up @@ -553,6 554,14 @@ Below are the configuration options with their default values:
}
```

#### compatibilityJSON

Type: `String` Default: `'v3'`

The `compatibilityJSON` version to use for plural suffixes.

See https://https://www.i18next.com/misc/json-format for details.

#### debug

Type: `Boolean` Default: `false`
Expand Down Expand Up @@ -724,7 733,7 @@ Resource options:
```js
{ // Default
resource: {
// The path where resources get loaded from. Relative to current working directory.
// The path where resources get loaded from. Relative to current working directory.
loadPath: 'i18n/{{lng}}/{{ns}}.json',

// The path to store resources. Relative to the path specified by `gulp.dest(path)`.
Expand All @@ -745,7 754,7 @@ Resource options:
```js
{ // Default
resource: {
// The path where resources get loaded from. Relative to current working directory.
// The path where resources get loaded from. Relative to current working directory.
loadPath: function(lng, ns) {
return 'i18n/' lng '/' ns '.json';
},
Expand Down Expand Up @@ -857,7 866,7 @@ interpolation options

Type: `Object` Default: `{}`

This can be used to pass any additional information regarding the string.
This can be used to pass any additional information regarding the string.

#### allowDynamicKeys

Expand Down
1 change: 1 addition & 0 deletions examples/i18next-scanner.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 11,7 @@ module.exports = {
],
output: './',
options: {
compatibilityJSON: 'v3',
debug: true,
func: {
list: ['i18next.t', 'i18n.t'],
Expand Down
41 changes: 10 additions & 31 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 18,9 @@ import flattenObjectKeys from './flatten-object-keys';
import nodesToString from './nodes-to-string';
import omitEmptyObject from './omit-empty-object';

i18next.init({
compatibilityJSON: 'v3',
});

const defaults = {
compatibilityJSON: 'v3', // JSON format

debug: false, // verbose logging

sort: false, // sort keys in alphabetical order
Expand Down Expand Up @@ -224,32 222,6 @@ const normalizeOptions = (options) => {
return options;
};

// Get an array of plural suffixes for a given language.
// @param {string} lng The language.
// @param {string} pluralSeparator pluralSeparator, default '_'.
// @return {array} An array of plural suffixes.
const getPluralSuffixes = (lng, pluralSeparator = '_') => {
const rule = i18next.services.pluralResolver.getRule(lng);

if (!(rule && rule.numbers)) {
return []; // Return an empty array if lng is not supported
}

if (rule.numbers.length === 1) {
return [`${pluralSeparator}0`];
}

if (rule.numbers.length === 2) {
return ['', `${pluralSeparator}plural`];
}

const suffixes = rule.numbers.reduce((acc, n, i) => {
return acc.concat(`${pluralSeparator}${i}`);
}, []);

return suffixes;
};

/**
* Creates a new parser
* @constructor
Expand All @@ -272,14 244,21 @@ class Parser {
...options
});

const i18nextInstance = i18next.createInstance();
i18nextInstance.init({
compatibilityJSON: this.options.compatibilityJSON,
pluralSeparator: this.options.pluralSeparator,
});

const lngs = this.options.lngs;
const namespaces = this.options.ns;

lngs.forEach((lng) => {
this.resStore[lng] = this.resStore[lng] || {};
this.resScan[lng] = this.resScan[lng] || {};

this.pluralSuffixes[lng] = ensureArray(getPluralSuffixes(lng, this.options.pluralSeparator));
this.pluralSuffixes[lng] = i18nextInstance.services.pluralResolver.getSuffixes(lng);

if (this.pluralSuffixes[lng].length === 0) {
this.log(`No plural rule found for: ${lng}`);
}
Expand Down
27 changes: 27 additions & 0 deletions test/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 791,33 @@ describe('Plural', () => {
});
});

test('compatibilityJSON', () => {
const parser = new Parser({
compatibilityJSON: 'v4',
lngs: ['en'],
});
const content = fs.readFileSync(path.resolve(__dirname, 'fixtures/plural.js'), 'utf-8');
parser.parseFuncFromString(content, { propsFilter: props => props });
expect(parser.get()).toEqual({
en: {
translation: {
'key_one': '',
'key_other': '',
'keyWithCount_one': '',
'keyWithCount_other': '',
'keyWithVariable_one': '',
'keyWithVariable_other': '',
'keyWithCountAndDefaultValues_one': '{{count}} item',
'keyWithCountAndDefaultValues_other': '{{count}} item',
'keyWithDefaultValueAndCount_one': '{{count}} item',
'keyWithDefaultValueAndCount_other': '{{count}} item',
'keyWithDefaultValueAndVariable_one': '{{count}} item',
'keyWithDefaultValueAndVariable_other': '{{count}} item',
}
}
});
});

test('User defined function', () => {
const parser = new Parser({
plural: (lng, ns, key, options) => {
Expand Down

0 comments on commit 8daa6b4

Please sign in to comment.