1056 lines
38 KiB
JavaScript
1056 lines
38 KiB
JavaScript
// i18next, v1.7.5
|
|
// Copyright (c)2014 Jan Mühlemann (jamuhl).
|
|
// Distributed under MIT license
|
|
// http://i18next.com
|
|
var i18n = require('../index')
|
|
, expect = require('expect.js')
|
|
, sinon = require('sinon');
|
|
|
|
describe('i18next.translate', function() {
|
|
|
|
var opts;
|
|
|
|
beforeEach(function() {
|
|
opts = {
|
|
lng: 'en-US',
|
|
fallbackLng: 'dev',
|
|
fallbackNS: [],
|
|
fallbackToDefaultNS: false,
|
|
fallbackOnNull: true,
|
|
fallbackOnEmpty: false,
|
|
load: 'all',
|
|
preload: [],
|
|
supportedLngs: [],
|
|
lowerCaseLng: false,
|
|
ns: 'translation',
|
|
resGetPath: 'test/locales/__lng__/__ns__.json',
|
|
resSetPath: 'test/locales/__lng__/new.__ns__.json',
|
|
saveMissing: false,
|
|
resStore: false,
|
|
returnObjectTrees: false,
|
|
interpolationPrefix: '__',
|
|
interpolationSuffix: '__',
|
|
postProcess: '',
|
|
parseMissingKey: '',
|
|
debug: false,
|
|
objectTreeKeyHandler: null,
|
|
lngWhitelist: null
|
|
};
|
|
});
|
|
|
|
describe('keys with non supported values', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
test: 'hi'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
|
|
it('it should not break on null key', function() {
|
|
expect(i18n.t(null)).to.be('');
|
|
});
|
|
|
|
it('it should not break on undefined key', function() {
|
|
expect(i18n.t(undefined)).to.be('');
|
|
});
|
|
|
|
});
|
|
|
|
describe('resource is missing', function() {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should return key', function() {
|
|
expect(i18n.t('missing')).to.be('missing');
|
|
});
|
|
|
|
it('it should return default value if set', function() {
|
|
expect(i18n.t('missing', { defaultValue: 'defaultOfMissing'})).to.be('defaultOfMissing');
|
|
});
|
|
|
|
describe('with namespaces', function() {
|
|
|
|
it('it should return key', function() {
|
|
expect(i18n.t('translate:missing')).to.be('translate:missing');
|
|
});
|
|
|
|
it('it should return default value if set', function() {
|
|
expect(i18n.t('translate:missing', { defaultValue: 'defaultOfMissing'})).to.be('defaultOfMissing');
|
|
});
|
|
|
|
describe('and function parseMissingKey set', function() {
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
parseMissingKey: function(key) {
|
|
var ret = key;
|
|
|
|
if (ret.indexOf(':')) {
|
|
ret = ret.substring(ret.lastIndexOf(':')+1, ret.length);
|
|
}
|
|
|
|
if (ret.indexOf('.')) {
|
|
ret = ret.substring(ret.lastIndexOf('.')+1, ret.length);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}), function(t) { done(); });
|
|
});
|
|
|
|
it('it should parse key', function() {
|
|
expect(i18n.t('translate:missing')).to.be('missing');
|
|
expect(i18n.t('translate:somenesting.missing')).to.be('missing');
|
|
});
|
|
|
|
it('it should return default value if set', function() {
|
|
expect(i18n.t('translate:missing', { defaultValue: 'defaultOfMissing'})).to.be('defaultOfMissing');
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('resource string is null', function() {
|
|
var resStore = {
|
|
dev: { translation: { key1: null, key2: { key3: null } } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, returnObjectTrees: true, fallbackOnNull: false }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate value', function() {
|
|
expect(i18n.t('key1')).to.be(null);
|
|
expect(i18n.t('key2')).to.eql({ key3: null });
|
|
});
|
|
|
|
describe('with option fallbackOnNull = true', function() {
|
|
var resStore = {
|
|
dev: { translation: { key1: 'fallbackKey1', key2: { key3: 'fallbackKey3' } } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { key1: null, key2: { key3: null } } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, fallbackOnNull: true}),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate to fallback value', function() {
|
|
expect(i18n.t('key1')).to.be('fallbackKey1');
|
|
expect(i18n.t('key2.key3')).to.eql('fallbackKey3');
|
|
});
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
describe('key with empty string value as valid option', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: '' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('');
|
|
});
|
|
|
|
describe('missing on unspecific', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: 'text' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { empty: '' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, lng: 'en' }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('text');
|
|
});
|
|
});
|
|
|
|
describe('on specific language', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: 'text' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { empty: '' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('key with empty string set to fallback if empty', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: '' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, fallbackOnEmpty: true }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('empty');
|
|
});
|
|
|
|
describe('missing on unspecific', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: 'text' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { empty: '' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, lng: 'en', fallbackOnEmpty: true }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('text');
|
|
});
|
|
});
|
|
|
|
describe('on specific language', function() {
|
|
var resStore = {
|
|
dev: { translation: { empty: 'text' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { empty: '' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore, fallbackOnEmpty: true }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate correctly', function() {
|
|
expect(i18n.t('empty')).to.be('text');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('resource key as array', function() {
|
|
var resStore = {
|
|
dev: { translation: { existing1: 'hello _name_', existing2: 'howdy __name__' } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
describe('when none of the keys exist', function() {
|
|
it('return the same value as translating the last non-existent key', function() {
|
|
expect(i18n.t(['nonexistent1', 'nonexistent2'], {name: "Joe"})).to.equal(i18n.t('nonexistent2', {name: "Joe"}));
|
|
});
|
|
});
|
|
|
|
describe('when one of the keys exist', function() {
|
|
it('return the same value as translating the one existing key', function() {
|
|
expect(i18n.t(['nonexistent1', 'existing2'], {name: "Joe"})).to.equal(i18n.t('existing2', {name: "Joe"}));
|
|
});
|
|
});
|
|
|
|
describe('when two or more of the keys exist', function() {
|
|
it('return the same value as translating the first existing key', function() {
|
|
expect(i18n.t(['nonexistent1', 'existing2', 'existing1'], {name: "Joe"})).to.equal(i18n.t('existing2', {name: "Joe"}));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('resource string as array', function() {
|
|
var resStore = {
|
|
dev: { translation: { testarray: ["title", "text"] } },
|
|
en: { translation: { } },
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate nested value', function() {
|
|
expect(i18n.t('testarray')).to.be('title\ntext');
|
|
});
|
|
});
|
|
|
|
describe('accessing tree values', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
test: { 'simple_en-US': 'ok_from_en-US' }
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore }
|
|
), function(t) { done(); });
|
|
});
|
|
|
|
it('it should return nested string as usual', function() {
|
|
expect(i18n.t('test.simple_en-US')).to.be('ok_from_en-US');
|
|
});
|
|
|
|
it('it should not fail silently on accessing an objectTree', function() {
|
|
expect(i18n.t('test')).to.be('key \'translation:test (en-US)\' returned an object instead of string.');
|
|
});
|
|
|
|
describe('optional return an objectTree for UI components,...', function() {
|
|
|
|
describe('with init flag', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: {
|
|
test_dev: { res_dev: 'added __replace__' }
|
|
}
|
|
},
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
test_en_US: { res_en_US: 'added __replace__' }
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
returnObjectTrees: true,
|
|
resStore: resStore }
|
|
), function(t) { done(); });
|
|
});
|
|
|
|
it('it should return objectTree applying options', function() {
|
|
expect(i18n.t('test_en_US', { replace: 'two' })).to.eql({ 'res_en_US': 'added two' });
|
|
expect(i18n.t('test_en_US', { replace: 'three' })).to.eql({ 'res_en_US': 'added three' });
|
|
expect(i18n.t('test_en_US', { replace: 'four' })).to.eql({ 'res_en_US': 'added four' });
|
|
|
|
// from fallback
|
|
expect(i18n.t('test_dev', { replace: 'two' })).to.eql({ 'res_dev': 'added two' });
|
|
});
|
|
|
|
});
|
|
|
|
describe('with flag in options', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
test: { res: 'added __replace__',
|
|
id: 0,
|
|
regex: /test/,
|
|
func: function () {},
|
|
template: '4',
|
|
title: 'About...',
|
|
text: 'Site description',
|
|
media: ['test']
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
returnObjectTrees: false,
|
|
resStore: resStore }),
|
|
function(t) { done(); } );
|
|
});
|
|
|
|
it('it should return objectTree', function() {
|
|
expect(i18n.t('test', { returnObjectTrees: true, replace: 'two' })).to.eql({
|
|
res: 'added two',
|
|
id: 0,
|
|
regex: resStore['en-US'].translation.test.regex,
|
|
func: resStore['en-US'].translation.test.func,
|
|
template: '4',
|
|
title: 'About...',
|
|
text: 'Site description',
|
|
media: ['test']
|
|
});
|
|
//expect(i18n.t('test', { returnObjectTrees: true, replace: 'three' })).to.eql({ 'res': 'added three' });
|
|
//expect(i18n.t('test', { returnObjectTrees: true, replace: 'four' })).to.eql({ 'res': 'added four' });
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('resource nesting', function() {
|
|
var resStore = {
|
|
dev: { translation: { nesting1: '1 $t(nesting2)' } },
|
|
en: { translation: { nesting2: '2 $t(nesting3)' } },
|
|
'en-US': { translation: { nesting3: '3' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate nested value', function() {
|
|
expect(i18n.t('nesting1')).to.be('1 2 3');
|
|
});
|
|
|
|
it('it should apply nested value on defaultValue', function() {
|
|
expect(i18n.t('nesting_default', {defaultValue: '0 $t(nesting1)'})).to.be('0 1 2 3');
|
|
});
|
|
|
|
describe('resource nesting syntax error', function() {
|
|
var resStore = {
|
|
dev: { translation: { nesting1: '1 $t(nesting2' } },
|
|
en: { translation: { nesting2: '2 $t(nesting3)' } },
|
|
'en-US': { translation: { nesting3: '3' } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate nested value', function() {
|
|
expect(i18n.t('nesting1')).to.be('');
|
|
});
|
|
|
|
});
|
|
|
|
describe('with setting new options', function() {
|
|
var resStore = {
|
|
dev: { translation: {
|
|
nesting1: '$t(nesting2, {"count": __girls__}) and __count__ boy',
|
|
nesting1_plural: '$t(nesting2, {"count": __girls__}) and __count__ boys'
|
|
} },
|
|
en: { translation: {
|
|
nesting2: '__count__ girl',
|
|
nesting2_plural: '__count__ girls'
|
|
} }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should translate nested value and set new options', function() {
|
|
expect(i18n.t('nesting1', {count: 2, girls: 3})).to.be('3 girls and 2 boys');
|
|
expect(i18n.t('nesting1', {count: 1, girls: 3})).to.be('3 girls and 1 boy');
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe('interpolation - replacing values inside a string', function() {
|
|
|
|
describe('default i18next way', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'added __toAdd__',
|
|
interpolationTest2: 'added __toAdd__ __toAdd__ twice',
|
|
interpolationTest3: 'added __child.one__ __child.two__',
|
|
interpolationTest4: 'added __child.grandChild.three__'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should replace passed in key/values', function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: 'something'})).to.be('added something');
|
|
expect(i18n.t('interpolationTest2', {toAdd: 'something'})).to.be('added something something twice');
|
|
expect(i18n.t('interpolationTest3', { child: { one: '1', two: '2'}})).to.be('added 1 2');
|
|
expect(i18n.t('interpolationTest4', { child: { grandChild: { three: '3'}}})).to.be('added 3');
|
|
});
|
|
|
|
it('it should replace passed in key/values in replace member', function() {
|
|
expect(i18n.t('interpolationTest1', { replace: {toAdd: 'something'} })).to.be('added something');
|
|
expect(i18n.t('interpolationTest2', { replace: {toAdd: 'something'} })).to.be('added something something twice');
|
|
expect(i18n.t('interpolationTest3', { replace: { child: { one: '1', two: '2'}} })).to.be('added 1 2');
|
|
expect(i18n.t('interpolationTest4', { replace: { child: { grandChild: { three: '3'}}} })).to.be('added 3');
|
|
});
|
|
|
|
it("it should not escape HTML", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '<html>'})).to.be('added <html>');
|
|
});
|
|
|
|
it('it should replace passed in key/values on defaultValue', function() {
|
|
expect(i18n.t('interpolationTest5', {defaultValue: 'added __toAdd__', toAdd: 'something'})).to.be('added something');
|
|
});
|
|
|
|
it("it should escape dollar signs in replacement values", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '$&'})).to.be('added $&');
|
|
});
|
|
|
|
});
|
|
|
|
describe('default i18next way - different prefix/suffix', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'added *toAdd*',
|
|
interpolationTest2: 'added *toAdd* *toAdd* twice',
|
|
interpolationTest3: 'added *child.one* *child.two*',
|
|
interpolationTest4: 'added *child.grandChild.three*'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore,
|
|
interpolationPrefix: '*',
|
|
interpolationSuffix: '*'
|
|
}), function(t) { done(); });
|
|
});
|
|
|
|
it('it should replace passed in key/values', function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: 'something'})).to.be('added something');
|
|
expect(i18n.t('interpolationTest2', {toAdd: 'something'})).to.be('added something something twice');
|
|
expect(i18n.t('interpolationTest3', { child: { one: '1', two: '2'}})).to.be('added 1 2');
|
|
expect(i18n.t('interpolationTest4', { child: { grandChild: { three: '3'}}})).to.be('added 3');
|
|
});
|
|
|
|
it('it should replace passed in key/values on defaultValue', function() {
|
|
expect(i18n.t('interpolationTest5', {defaultValue: 'added *toAdd*', toAdd: 'something'})).to.be('added something');
|
|
});
|
|
|
|
});
|
|
|
|
describe('default i18next way - different prefix/suffix via options', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'added *toAdd*',
|
|
interpolationTest2: 'added *toAdd* *toAdd* twice',
|
|
interpolationTest3: 'added *child.one* *child.two*',
|
|
interpolationTest4: 'added *child.grandChild.three*',
|
|
interpolationTest5: 'added *count*',
|
|
interpolationTest5_plural: 'added *count*'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore
|
|
}), function(t) { done(); });
|
|
});
|
|
|
|
it('it should replace passed in key/values', function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: 'something', interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added something');
|
|
expect(i18n.t('interpolationTest2', {toAdd: 'something', interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added something something twice');
|
|
expect(i18n.t('interpolationTest3', { child: { one: '1', two: '2'}, interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added 1 2');
|
|
expect(i18n.t('interpolationTest4', { child: { grandChild: { three: '3'}}, interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added 3');
|
|
expect(i18n.t('interpolationTest5', { count: 3, interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added 3');
|
|
});
|
|
|
|
it('it should replace passed in key/values on defaultValue', function() {
|
|
expect(i18n.t('interpolationTest6', {defaultValue: 'added *toAdd*', toAdd: 'something', interpolationPrefix: '*', interpolationSuffix: '*'})).to.be('added something');
|
|
});
|
|
|
|
});
|
|
|
|
describe('default i18next way - with escaping interpolated arguments per default', function () {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'added __toAdd__',
|
|
interpolationTest5: 'added __toAddHTML__',
|
|
interpolationTest6: 'added __child.oneHTML__',
|
|
interpolationTest7: 'added __toAddHTML__ __toAdd__'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore,
|
|
escapeInterpolation: true
|
|
}), function(t) { done(); });
|
|
});
|
|
|
|
it("it should escape HTML", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '<html>'})).to.be('added <html>');
|
|
});
|
|
|
|
it("it should not escape when HTML is suffixed", function() {
|
|
expect(i18n.t('interpolationTest5', {toAdd: '<html>'})).to.be('added <html>');
|
|
expect(i18n.t('interpolationTest6', { child: { one: '<1>'}})).to.be('added <1>');
|
|
});
|
|
|
|
it("it should support both escaping and not escaping HTML", function() {
|
|
expect(i18n.t('interpolationTest7', {toAdd: '<html>', escapeInterpolation: true})).to.be('added <html> <html>');
|
|
});
|
|
|
|
it("it should escape dollar signs in replacement values", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '$&'})).to.be('added $&');
|
|
});
|
|
|
|
});
|
|
|
|
describe('default i18next way - with escaping interpolated arguments per default via options', function () {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'added __toAdd__',
|
|
interpolationTest5: 'added __toAddHTML__',
|
|
interpolationTest6: 'added __child.oneHTML__',
|
|
interpolationTest7: 'added __toAddHTML__ __toAdd__'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore
|
|
}), function(t) { done(); });
|
|
});
|
|
|
|
it("it should escape HTML", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '<html>', escapeInterpolation: true})).to.be('added <html>');
|
|
});
|
|
|
|
it("it should not escape when HTML is suffixed", function() {
|
|
expect(i18n.t('interpolationTest5', {toAdd: '<html>', escapeInterpolation: true})).to.be('added <html>');
|
|
expect(i18n.t('interpolationTest6', { child: { one: '<1>', escapeInterpolation: true}})).to.be('added <1>');
|
|
});
|
|
|
|
it("it should support both escaping and not escaping HTML", function() {
|
|
expect(i18n.t('interpolationTest7', {toAdd: '<html>', escapeInterpolation: true})).to.be('added <html> <html>');
|
|
});
|
|
|
|
it("it should escape dollar signs in replacement values", function() {
|
|
expect(i18n.t('interpolationTest1', {toAdd: '$&'})).to.be('added $&');
|
|
});
|
|
|
|
});
|
|
|
|
describe('using sprintf', function() {
|
|
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: { } },
|
|
'en-US': {
|
|
translation: {
|
|
interpolationTest1: 'The first 4 letters of the english alphabet are: %s, %s, %s and %s',
|
|
interpolationTest2: 'Hello %(users[0].name)s, %(users[1].name)s and %(users[2].name)s',
|
|
interpolationTest3: 'The last letter of the english alphabet is %s'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should replace passed in key/values', function() {
|
|
expect(i18n.t('interpolationTest1', {postProcess: 'sprintf', sprintf: ['a', 'b', 'c', 'd']})).to.be('The first 4 letters of the english alphabet are: a, b, c and d');
|
|
expect(i18n.t('interpolationTest2', {postProcess: 'sprintf', sprintf: { users: [{name: 'Dolly'}, {name: 'Molly'}, {name: 'Polly'}] } })).to.be('Hello Dolly, Molly and Polly');
|
|
});
|
|
|
|
it('it should recognize the sprintf syntax and automatically add the sprintf processor', function() {
|
|
expect(i18n.t('interpolationTest1', 'a', 'b', 'c', 'd')).to.be('The first 4 letters of the english alphabet are: a, b, c and d');
|
|
expect(i18n.t('interpolationTest3', 'z')).to.be('The last letter of the english alphabet is z');
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('plural usage', function() {
|
|
|
|
describe('basic usage - singular and plural form', function() {
|
|
var resStore = {
|
|
dev: { 'ns.2': {
|
|
pluralTest: 'singular from ns.2',
|
|
pluralTest_plural: 'plural from ns.2',
|
|
pluralTestWithCount: '__count__ item from ns.2',
|
|
pluralTestWithCount_plural: '__count__ items from ns.2'
|
|
}},
|
|
en: { },
|
|
'en-US': {
|
|
'ns.1': {
|
|
pluralTest: 'singular',
|
|
pluralTest_plural: 'plural',
|
|
pluralTestWithCount: '__count__ item',
|
|
pluralTestWithCount_plural: '__count__ items'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore,
|
|
ns: { namespaces: ['ns.1', 'ns.2'], defaultNs: 'ns.1'}
|
|
}),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct plural or singular form', function() {
|
|
expect(i18n.t('pluralTest', {count: 0})).to.be('plural');
|
|
expect(i18n.t('pluralTest', {count: 1})).to.be('singular');
|
|
expect(i18n.t('pluralTest', {count: 2})).to.be('plural');
|
|
expect(i18n.t('pluralTest', {count: 7})).to.be('plural');
|
|
|
|
expect(i18n.t('pluralTestWithCount', {count: 0})).to.be('0 items');
|
|
expect(i18n.t('pluralTestWithCount', {count: 1})).to.be('1 item');
|
|
expect(i18n.t('pluralTestWithCount', {count: 7})).to.be('7 items');
|
|
});
|
|
|
|
it('it should provide correct plural or singular form for second namespace', function() {
|
|
expect(i18n.t('ns.2:pluralTest', {count: 0})).to.be('plural from ns.2');
|
|
expect(i18n.t('ns.2:pluralTest', {count: 1})).to.be('singular from ns.2');
|
|
expect(i18n.t('ns.2:pluralTest', {count: 2})).to.be('plural from ns.2');
|
|
expect(i18n.t('ns.2:pluralTest', {count: 7})).to.be('plural from ns.2');
|
|
|
|
expect(i18n.t('ns.2:pluralTestWithCount', {count: 1})).to.be('1 item from ns.2');
|
|
expect(i18n.t('ns.2:pluralTestWithCount', {count: 7})).to.be('7 items from ns.2');
|
|
});
|
|
|
|
});
|
|
|
|
describe('basic usage - singular and plural form on fallbacks', function() {
|
|
var resStore = {
|
|
'fr': {
|
|
'translation': {}
|
|
},
|
|
'en': {
|
|
'translation': {
|
|
pluralTest: 'singular',
|
|
pluralTest_plural: 'plural',
|
|
pluralTestWithCount: '__count__ item',
|
|
pluralTestWithCount_plural: '__count__ items'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore,
|
|
lng: 'fr',
|
|
fallbackLng: 'en'
|
|
}),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct plural or singular form', function() {
|
|
expect(i18n.t('pluralTest', {count: 0})).to.be('plural');
|
|
expect(i18n.t('pluralTest', {count: 1})).to.be('singular');
|
|
expect(i18n.t('pluralTest', {count: 2})).to.be('plural');
|
|
expect(i18n.t('pluralTest', {count: 7})).to.be('plural');
|
|
|
|
expect(i18n.t('pluralTestWithCount', {count: 0})).to.be('0 items');
|
|
expect(i18n.t('pluralTestWithCount', {count: 1})).to.be('1 item');
|
|
expect(i18n.t('pluralTestWithCount', {count: 7})).to.be('7 items');
|
|
});
|
|
|
|
});
|
|
|
|
|
|
describe('basic usage 2 - singular and plural form in french', function() {
|
|
var resStore = {
|
|
dev: { 'ns.2': {
|
|
pluralTest: 'singular from ns.2',
|
|
pluralTest_plural: 'plural from ns.2',
|
|
pluralTestWithCount: '__count__ item from ns.2',
|
|
pluralTestWithCount_plural: '__count__ items from ns.2'
|
|
}},
|
|
en: { },
|
|
'fr': {
|
|
'ns.1': {
|
|
pluralTest: 'singular',
|
|
pluralTest_plural: 'plural',
|
|
pluralTestWithCount: '__count__ item',
|
|
pluralTestWithCount_plural: '__count__ items'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
lng: 'fr',
|
|
resStore: resStore,
|
|
ns: { namespaces: ['ns.1', 'ns.2'], defaultNs: 'ns.1'}
|
|
}),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct plural or singular form', function() {
|
|
expect(i18n.t('pluralTest', {count: 0})).to.be('singular');
|
|
expect(i18n.t('pluralTest', {count: 1})).to.be('singular');
|
|
expect(i18n.t('pluralTest', {count: 2})).to.be('plural');
|
|
expect(i18n.t('pluralTest', {count: 7})).to.be('plural');
|
|
|
|
expect(i18n.t('pluralTestWithCount', {count: 0})).to.be('0 item');
|
|
expect(i18n.t('pluralTestWithCount', {count: 1})).to.be('1 item');
|
|
expect(i18n.t('pluralTestWithCount', {count: 7})).to.be('7 items');
|
|
});
|
|
});
|
|
|
|
describe('extended usage - multiple plural forms - ar', function() {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
ar: { translation: {
|
|
key: 'singular',
|
|
key_plural_0: 'zero',
|
|
key_plural_2: 'two',
|
|
key_plural_3: 'few',
|
|
key_plural_11: 'many',
|
|
key_plural_100: 'plural'
|
|
}
|
|
},
|
|
'ar-??': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { lng: 'ar', resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct plural forms', function() {
|
|
expect(i18n.t('key', {count: 0})).to.be('zero');
|
|
expect(i18n.t('key', {count: 1})).to.be('singular');
|
|
expect(i18n.t('key', {count: 2})).to.be('two');
|
|
expect(i18n.t('key', {count: 3})).to.be('few');
|
|
expect(i18n.t('key', {count: 4})).to.be('few');
|
|
expect(i18n.t('key', {count: 104})).to.be('few');
|
|
expect(i18n.t('key', {count: 11})).to.be('many');
|
|
expect(i18n.t('key', {count: 99})).to.be('many');
|
|
expect(i18n.t('key', {count: 199})).to.be('many');
|
|
expect(i18n.t('key', {count: 100})).to.be('plural');
|
|
});
|
|
});
|
|
|
|
describe('extended usage - multiple plural forms - ru', function() {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
ru: { translation: {
|
|
key: '1,21,31',
|
|
key_plural_2: '2,3,4',
|
|
key_plural_5: '0,5,6'
|
|
}
|
|
},
|
|
'ru-??': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { lng: 'ru', resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct plural forms', function() {
|
|
expect(i18n.t('key', {count: 0})).to.be('0,5,6');
|
|
expect(i18n.t('key', {count: 1})).to.be('1,21,31');
|
|
expect(i18n.t('key', {count: 2})).to.be('2,3,4');
|
|
expect(i18n.t('key', {count: 3})).to.be('2,3,4');
|
|
expect(i18n.t('key', {count: 4})).to.be('2,3,4');
|
|
expect(i18n.t('key', {count: 104})).to.be('2,3,4');
|
|
expect(i18n.t('key', {count: 11})).to.be('0,5,6');
|
|
expect(i18n.t('key', {count: 24})).to.be('2,3,4');
|
|
expect(i18n.t('key', {count: 25})).to.be('0,5,6');
|
|
expect(i18n.t('key', {count: 99})).to.be('0,5,6');
|
|
expect(i18n.t('key', {count: 199})).to.be('0,5,6');
|
|
expect(i18n.t('key', {count: 100})).to.be('0,5,6');
|
|
});
|
|
});
|
|
|
|
describe('extended usage - ask for a key in a language with a different plural form', function() {
|
|
var resStore = {
|
|
en: { translation: {
|
|
key:'singular_en',
|
|
key_plural:'plural_en'
|
|
}
|
|
},
|
|
zh: { translation: {
|
|
key: 'singular_zh'
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { lng: 'zh', resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide translation for passed in language with 1 item', function() {
|
|
expect(i18n.t('key', { lng: 'en', count:1 })).to.be('singular_en');
|
|
});
|
|
|
|
it('it should provide translation for passed in language with 2 items', function() {
|
|
expect(i18n.t('key', { lng: 'en', count:2 })).to.be('plural_en');
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe('context usage', function() {
|
|
|
|
describe('basic usage', function() {
|
|
var resStore = {
|
|
dev: { 'ns.2': {
|
|
friend_context: 'A friend from ns2',
|
|
friend_context_male: 'A boyfriend from ns2',
|
|
friend_context_female: 'A girlfriend from ns2'
|
|
}
|
|
},
|
|
en: { 'ns.1': {
|
|
friend_context: 'A friend',
|
|
friend_context_male: 'A boyfriend',
|
|
friend_context_female: 'A girlfriend'
|
|
}
|
|
},
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
resStore: resStore,
|
|
ns: { namespaces: ['ns.1', 'ns.2'], defaultNs: 'ns.1'}
|
|
}),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct context form', function() {
|
|
expect(i18n.t('friend_context')).to.be('A friend');
|
|
expect(i18n.t('friend_context', {context: ''})).to.be('A friend');
|
|
expect(i18n.t('friend_context', {context: 'male'})).to.be('A boyfriend');
|
|
expect(i18n.t('friend_context', {context: 'female'})).to.be('A girlfriend');
|
|
});
|
|
|
|
it('it should provide correct context form for second namespace', function() {
|
|
expect(i18n.t('ns.2:friend_context')).to.be('A friend from ns2');
|
|
expect(i18n.t('ns.2:friend_context', {context: ''})).to.be('A friend from ns2');
|
|
expect(i18n.t('ns.2:friend_context', {context: 'male'})).to.be('A boyfriend from ns2');
|
|
expect(i18n.t('ns.2:friend_context', {context: 'female'})).to.be('A girlfriend from ns2');
|
|
});
|
|
});
|
|
|
|
describe('extended usage - in combination with plurals', function() {
|
|
var resStore = {
|
|
dev: { translation: { } },
|
|
en: { translation: {
|
|
friend_context: '__count__ friend',
|
|
friend_context_male: '__count__ boyfriend',
|
|
friend_context_female: '__count__ girlfriend',
|
|
friend_context_plural: '__count__ friends',
|
|
friend_context_male_plural: '__count__ boyfriends',
|
|
friend_context_female_plural: '__count__ girlfriends'
|
|
}
|
|
},
|
|
'en-US': { translation: { } }
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, { resStore: resStore }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide correct context with plural forms', function() {
|
|
expect(i18n.t('friend_context', { count: 1 })).to.be('1 friend');
|
|
expect(i18n.t('friend_context', {context: '', count: 1 })).to.be('1 friend');
|
|
expect(i18n.t('friend_context', {context: 'male', count: 1 })).to.be('1 boyfriend');
|
|
expect(i18n.t('friend_context', {context: 'female', count: 1 })).to.be('1 girlfriend');
|
|
|
|
expect(i18n.t('friend_context', { count: 10 })).to.be('10 friends');
|
|
expect(i18n.t('friend_context', {context: '', count: 10 })).to.be('10 friends');
|
|
expect(i18n.t('friend_context', {context: 'male', count: 10 })).to.be('10 boyfriends');
|
|
expect(i18n.t('friend_context', {context: 'female', count: 10 })).to.be('10 girlfriends');
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('with passed in languages different from set one', function() {
|
|
|
|
beforeEach(function(done) {
|
|
i18n.init(i18n.functions.extend(opts, {
|
|
preload: ['de-DE'] }),
|
|
function(t) { done(); });
|
|
});
|
|
|
|
it('it should provide translation for passed in language', function() {
|
|
expect(i18n.t('simple_de', { lng: 'de-DE' })).to.be('ok_from_de');
|
|
});
|
|
|
|
describe('with language not preloaded', function() {
|
|
|
|
it('it should provide translation for passed in language after loading file sync', function() {
|
|
var expectedValue = i18n.clientVersion ? 'simple_fr' : 'ok_from_fr';
|
|
expect(i18n.t('simple_fr', { lng: 'fr' })).to.be(expectedValue);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}); |