'use strict'; const common = require('../common'); const fc = require('fast-check'); describe('I18n', function () { describe('translate', function () { this.timeout(30000); before(function () { PrivateBin.I18n.reset(); }); it('returns message ID unchanged if no translation found', () => { fc.assert(fc.property( fc.string(), function (messageId) { messageId = messageId.replace(/%(s|d)/g, '%%'); var plurals = [messageId, messageId + 's'], fake = [messageId], result = PrivateBin.I18n.translate(messageId); PrivateBin.I18n.reset(); var alias = PrivateBin.I18n._(messageId); PrivateBin.I18n.reset(); var pluralResult = PrivateBin.I18n.translate(plurals); PrivateBin.I18n.reset(); var pluralAlias = PrivateBin.I18n._(plurals); PrivateBin.I18n.reset(); var fakeResult = PrivateBin.I18n.translate(fake); PrivateBin.I18n.reset(); var fakeAlias = PrivateBin.I18n._(fake); PrivateBin.I18n.reset(); if (messageId.indexOf(' { fc.assert(fc.property( fc.string(), fc.array(fc.string(), {minLength: 1}), fc.string(), function (prefix, params, postfix) { prefix = prefix.replace(/%(s|d)/g, '%%').replace(/ { fc.assert(fc.property( fc.string(), fc.array(fc.string(), {minLength: 1}), fc.string(), function (prefix, params, postfix) { prefix = prefix.replace(/%(s|d)/g, '%%'); params[0] = params[0].replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%'); const translation = DOMPurify.sanitize( prefix + '' + postfix, { ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i, ALLOWED_TAGS: ['a', 'i', 'span', 'kbd'], ALLOWED_ATTR: ['href', 'id'] } ); params.unshift(prefix + '' + postfix); const result = PrivateBin.I18n.translate.apply(this, params); PrivateBin.I18n.reset(); const alias = PrivateBin.I18n._.apply(this, params); PrivateBin.I18n.reset(); return translation === result && translation === alias; } )); }); it('replaces %s in strings with first given parameter into an element, encoding all, when no link is in the messageID', () => { fc.assert(fc.property( fc.string(), fc.array(fc.string(), {minLength: 1}), fc.string(), function (prefix, params, postfix) { prefix = prefix.replace(/%(s|d)/g, '%%').replace(/ { fc.assert(fc.property( fc.string(), fc.array(fc.string(), {minLength: 1}), fc.string(), function (prefix, params, postfix) { prefix = prefix.replace(/%(s|d)/g, '%%').trim(); params[0] = params[0].replace(/%(s|d)/g, '%%').trim(); postfix = postfix.replace(/%(s|d)/g, '%%').trim(); const translation = DOMPurify.sanitize( prefix + '' + postfix, { ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i, ALLOWED_TAGS: ['a', 'i', 'span', 'kbd'], ALLOWED_ATTR: ['href', 'id'] } ); let args = Array.prototype.slice.call(params); args.unshift(prefix + '' + postfix); let clean = globalThis.cleanup(); document.body.innerHTML = '
'; const i18nElement2 = document.getElementById('i18n'); args.unshift(i18nElement2); PrivateBin.I18n.translate.apply(this, args); const result = i18nElement2.innerHTML; PrivateBin.I18n.reset(); clean(); clean = globalThis.cleanup(); document.body.innerHTML = '
'; args[0] = document.getElementById('i18n'); PrivateBin.I18n._.apply(this, args); const alias = document.getElementById('i18n').innerHTML; PrivateBin.I18n.reset(); clean(); return translation === result && translation === alias; } )); }); }); describe('getPluralForm', function () { before(function () { PrivateBin.I18n.reset(); }); it('returns valid key for plural form', () => { fc.assert(fc.property( common.fcSupportedLanguages(), fc.integer(), function(language, n) { PrivateBin.I18n.reset(language); var result = PrivateBin.I18n.getPluralForm(n); // arabic seems to have the highest plural count with 6 forms return result >= 0 && result <= 5; } )); }); }); // loading of JSON via AJAX needs to be tested in the browser, this just mocks it // TODO: This needs to be tested using a browser. describe('loadTranslations', function () { this.timeout(30000); before(function () { PrivateBin.I18n.reset(); }); it('downloads and handles any supported language', () => { fc.assert(fc.property( common.fcSupportedLanguages(), function(language) { // cleanup var clean = globalThis.cleanup('', {cookie: ['lang=en']}); PrivateBin.I18n.reset('en'); PrivateBin.I18n.loadTranslations(); clean(); // mock clean = globalThis.cleanup('', {cookie: ['lang=' + language]}); // eslint-disable-next-line global-require PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json')); var loadedLang = PrivateBin.I18n.getLanguage(), result = PrivateBin.I18n.translate('Never'), alias = PrivateBin.I18n._('Never'); clean(); return language === loadedLang && result === alias; } )); }); it('should default to en', () => { var clean = globalThis.cleanup('', {url: 'https://privatebin.net/'}); // when navigator.userLanguage is undefined and no default language // is specified, it would throw an error [ 'language', 'userLanguage' ].forEach(function (key) { Object.defineProperty(navigator, key, { value: undefined, configurable: true, enumerable: true, writable: false }); }); PrivateBin.I18n.reset('en'); PrivateBin.I18n.loadTranslations(); var result = PrivateBin.I18n.translate('Never'), alias = PrivateBin.I18n._('Never'); clean(); return 'Never' === result && 'Never' === alias; }); }); });