Skip to content

Commit

Permalink
Support a list of keys (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
omrilotan authored Dec 9, 2019
1 parent ac510c3 commit a479841
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 9 deletions.
37 changes: 29 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class I18n {
* @param {String} key
* @return {String} A default string for a missing key
*/
static getDefault(key = '') {
return key.split('.').pop().replace(/_/g, ' ');
static getDefault(key) {
return (key || '').split('.').pop().replace(/_/g, ' ');
}

/**
Expand Down Expand Up @@ -78,14 +78,17 @@ class I18n {
*/
translate(key, data) {

// Collect scopes
const scopes = [data || {}, this].map(({$scope}) => $scope).filter(Boolean);
const keys = Array.isArray(key) ? key : [ key ];

// Create key alternatives with prefixes
const alternatives = scopes.map((scope) => [scope, key].join('.'));
const alternatives = [].concat(
...keys.map(
(key) => this.alternatives(key, data)
)
);

// Find the first match
let result = this.find(...alternatives, key);
let result = this.find(...alternatives);

// Handle one,other translation structure
result = getOneOther(result, data);
Expand All @@ -101,11 +104,29 @@ class I18n {
}
break;
default:
this[MISSING].forEach((fn) => fn(key, this.$scope, this.translations));
return I18n.getDefault(key);
this[MISSING].forEach((fn) => fn(`${key}`, this.$scope, this.translations));
return I18n.getDefault(...keys);
}
}

/**
* Create key alternatives with prefixes according to instance scopes
* @param {string} key
* @param {object} data Object optionally containing '$scope' parameter
* @return {string[]}
*/
alternatives(key, data) {
return [data || {}, this].map(
({$scope}) => $scope
).filter(
Boolean
).map(
(scope) => [scope, key].join('.')
).concat(
key
);
}

/**
* find
* @param {...[String]} alternatives Different alternatives of strings to find
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fiverr/i18n",
"version": "1.5.8",
"version": "1.6.0",
"description": "Translation helper",
"keywords": [
"i18n",
Expand Down
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ const i18n = new I18n({
});
```

### Translate
```js
i18n.t('my.key'); // I'm a sentence
```

### Find alternatives
```js
i18n.t(['my.missing.key', 'my.key']); // I'm a sentence
```

### Add more translations after instantiation
```js
i18n.add({yet: {another: {key: 'I\'m here, too!'}}});
Expand Down
19 changes: 19 additions & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ describe('I18n', () => {
expect(i18n.translate('root.user.name')).to.equal('Martin');
});

it('i18n uses keys list to find an existing key', () => {
expect(i18n.translate([
'root_user_name',
'root.user.name'
])).to.equal('Martin');
});

it('translate function is bound to instance', () => {
const translate = i18n.translate;

Expand Down Expand Up @@ -118,6 +125,18 @@ describe('I18n', () => {
})).to.equal('I am in a different scope');
});

it('keys arrays performs lookup in scope', () => {
expect(i18n.translate(
[
'i.am.not.found',
'i.am.in.scope'
],
{
$scope: 'another_controller_name.action_name'
}
)).to.equal('I am in a different scope');
});

it('prefers contextual string to non contextual (found in scope)', () => {
const more = {
i: {
Expand Down
54 changes: 54 additions & 0 deletions tests/missing-key.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ const I18n = require('../');

describe('missing keys report', () => {
it('reports missing keys', () => {
let reported = false;
const i18n = new I18n({
$scope: 'some.scope',
missing: (key, scope, translations) => {
reported = true;
expect(scope).to.equal('some.scope');
expect(key).to.equal('a.missing.key');
expect(translations).to.be.an('object');
Expand All @@ -15,6 +17,58 @@ describe('missing keys report', () => {
});

i18n.translate('a.missing.key');
expect(reported).to.be.true;
});

it('reports missing key for undefined', () => {
let reported = false;
const i18n = new I18n({
$scope: 'some.scope',
missing: (key, scope, translations) => {
reported = true;
expect(scope).to.equal('some.scope');
expect(key).to.equal('undefined');
expect(translations).to.be.an('object');
expect(translations).to.be.empty;
}
});

i18n.translate();
expect(reported).to.be.true;
});

it('reports missing keys for arrays', () => {
let reported = false;
const i18n = new I18n({
$scope: 'some.scope',
missing: (key, scope, translations) => {
reported = true;
expect(scope).to.equal('some.scope');
expect(key).to.equal('a.missing.key,another.missing.key');
expect(translations).to.be.an('object');
expect(translations).to.be.empty;
}
});

i18n.translate(['a.missing.key', 'another.missing.key']);
expect(reported).to.be.true;
});

it('does not report missing key when one of the keys was found', () => {
let reported = false;
const i18n = new I18n({
$scope: 'some.scope',
missing: () => {
reported = true;
},
translations: {
fallback: 'falling back'
}
});

const translation = i18n.translate(['a.missing.key', 'fallback']);
expect(reported).to.be.false;
expect(translation).to.equal('falling back');
});

it('reports missing keys using "onmiss"', () => {
Expand Down

0 comments on commit a479841

Please sign in to comment.