I18n.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. 'use strict';
  2. var common = require('../common');
  3. describe('I18n', function () {
  4. describe('translate', function () {
  5. this.timeout(30000);
  6. before(function () {
  7. $.PrivateBin.I18n.reset();
  8. });
  9. jsc.property(
  10. 'returns message ID unchanged if no translation found',
  11. 'string',
  12. function (messageId) {
  13. messageId = messageId.replace(/%(s|d)/g, '%%');
  14. var plurals = [messageId, messageId + 's'],
  15. fake = [messageId],
  16. result = $.PrivateBin.I18n.translate(messageId);
  17. $.PrivateBin.I18n.reset();
  18. var alias = $.PrivateBin.I18n._(messageId);
  19. $.PrivateBin.I18n.reset();
  20. var pluralResult = $.PrivateBin.I18n.translate(plurals);
  21. $.PrivateBin.I18n.reset();
  22. var pluralAlias = $.PrivateBin.I18n._(plurals);
  23. $.PrivateBin.I18n.reset();
  24. var fakeResult = $.PrivateBin.I18n.translate(fake);
  25. $.PrivateBin.I18n.reset();
  26. var fakeAlias = $.PrivateBin.I18n._(fake);
  27. $.PrivateBin.I18n.reset();
  28. if (messageId.indexOf('<a') === -1) {
  29. messageId = $.PrivateBin.Helper.htmlEntities(messageId);
  30. } else {
  31. messageId = DOMPurify.sanitize(
  32. messageId, {
  33. ALLOWED_TAGS: ['a', 'i', 'span'],
  34. ALLOWED_ATTR: ['href', 'id']
  35. }
  36. );
  37. }
  38. return messageId === result && messageId === alias &&
  39. messageId === pluralResult && messageId === pluralAlias &&
  40. messageId === fakeResult && messageId === fakeAlias;
  41. }
  42. );
  43. jsc.property(
  44. 'replaces %s in strings with first given parameter, encoding all, when no link is in the messageID',
  45. 'string',
  46. '(small nearray) string',
  47. 'string',
  48. function (prefix, params, postfix) {
  49. prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
  50. params[0] = params[0].replace(/%(s|d)/g, '%%');
  51. postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
  52. const translation = $.PrivateBin.Helper.htmlEntities(prefix + params[0] + postfix);
  53. params.unshift(prefix + '%s' + postfix);
  54. const result = $.PrivateBin.I18n.translate.apply(this, params);
  55. $.PrivateBin.I18n.reset();
  56. const alias = $.PrivateBin.I18n._.apply(this, params);
  57. $.PrivateBin.I18n.reset();
  58. return translation === result && translation === alias;
  59. }
  60. );
  61. jsc.property(
  62. 'replaces %s in strings with first given parameter, encoding params only, when a link is part of the messageID',
  63. 'string',
  64. '(small nearray) string',
  65. 'string',
  66. function (prefix, params, postfix) {
  67. prefix = prefix.replace(/%(s|d)/g, '%%');
  68. params[0] = params[0].replace(/%(s|d)/g, '%%');
  69. postfix = postfix.replace(/%(s|d)/g, '%%');
  70. const translation = DOMPurify.sanitize(
  71. prefix + '<a href="' + params[0] + '"></a>' + postfix, {
  72. ALLOWED_TAGS: ['a', 'i', 'span'],
  73. ALLOWED_ATTR: ['href', 'id']
  74. }
  75. );
  76. params.unshift(prefix + '<a href="%s"></a>' + postfix);
  77. const result = $.PrivateBin.I18n.translate.apply(this, params);
  78. $.PrivateBin.I18n.reset();
  79. const alias = $.PrivateBin.I18n._.apply(this, params);
  80. $.PrivateBin.I18n.reset();
  81. return translation === result && translation === alias;
  82. }
  83. );
  84. jsc.property(
  85. 'replaces %s in strings with first given parameter into an element, encoding all, when no link is in the messageID',
  86. 'string',
  87. '(small nearray) string',
  88. 'string',
  89. function (prefix, params, postfix) {
  90. prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
  91. params[0] = params[0].replace(/%(s|d)/g, '%%');
  92. postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
  93. const translation = $('<textarea>').text((prefix + params[0] + postfix)).text();
  94. let args = Array.prototype.slice.call(params);
  95. args.unshift(prefix + '%s' + postfix);
  96. let clean = jsdom();
  97. $('body').html('<div id="i18n"></div>');
  98. args.unshift($('#i18n'));
  99. $.PrivateBin.I18n.translate.apply(this, args);
  100. const result = $('#i18n').text();
  101. $.PrivateBin.I18n.reset();
  102. clean();
  103. clean = jsdom();
  104. $('body').html('<div id="i18n"></div>');
  105. args[0] = $('#i18n');
  106. $.PrivateBin.I18n._.apply(this, args);
  107. const alias = $('#i18n').text();
  108. $.PrivateBin.I18n.reset();
  109. clean();
  110. return translation === result && translation === alias;
  111. }
  112. );
  113. jsc.property(
  114. 'replaces %s in strings with first given parameter into an element, encoding params only, when a link is part of the messageID inserted',
  115. 'string',
  116. '(small nearray) string',
  117. 'string',
  118. function (prefix, params, postfix) {
  119. prefix = prefix.replace(/%(s|d)/g, '%%').trim();
  120. params[0] = params[0].replace(/%(s|d)/g, '%%').trim();
  121. postfix = postfix.replace(/%(s|d)/g, '%%').trim();
  122. const translation = DOMPurify.sanitize(
  123. prefix + '<a href="' + params[0] + '"></a>' + postfix, {
  124. ALLOWED_TAGS: ['a', 'i', 'span'],
  125. ALLOWED_ATTR: ['href', 'id']
  126. }
  127. );
  128. let args = Array.prototype.slice.call(params);
  129. args.unshift(prefix + '<a href="%s"></a>' + postfix);
  130. let clean = jsdom();
  131. $('body').html('<div id="i18n"></div>');
  132. args.unshift($('#i18n'));
  133. $.PrivateBin.I18n.translate.apply(this, args);
  134. const result = $('#i18n').html();
  135. $.PrivateBin.I18n.reset();
  136. clean();
  137. clean = jsdom();
  138. $('body').html('<div id="i18n"></div>');
  139. args[0] = $('#i18n');
  140. $.PrivateBin.I18n._.apply(this, args);
  141. const alias = $('#i18n').html();
  142. $.PrivateBin.I18n.reset();
  143. clean();
  144. return translation === result && translation === alias;
  145. }
  146. );
  147. });
  148. describe('getPluralForm', function () {
  149. before(function () {
  150. $.PrivateBin.I18n.reset();
  151. });
  152. jsc.property(
  153. 'returns valid key for plural form',
  154. common.jscSupportedLanguages(),
  155. 'integer',
  156. function(language, n) {
  157. $.PrivateBin.I18n.reset(language);
  158. var result = $.PrivateBin.I18n.getPluralForm(n);
  159. // arabic seems to have the highest plural count with 6 forms
  160. return result >= 0 && result <= 5;
  161. }
  162. );
  163. });
  164. // loading of JSON via AJAX needs to be tested in the browser, this just mocks it
  165. // TODO: This needs to be tested using a browser.
  166. describe('loadTranslations', function () {
  167. this.timeout(30000);
  168. before(function () {
  169. $.PrivateBin.I18n.reset();
  170. });
  171. jsc.property(
  172. 'downloads and handles any supported language',
  173. common.jscSupportedLanguages(),
  174. function(language) {
  175. // cleanup
  176. var clean = jsdom('', {cookie: ['lang=en']});
  177. $.PrivateBin.I18n.reset('en');
  178. $.PrivateBin.I18n.loadTranslations();
  179. clean();
  180. // mock
  181. clean = jsdom('', {cookie: ['lang=' + language]});
  182. // eslint-disable-next-line global-require
  183. $.PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json'));
  184. var loadedLang = $.PrivateBin.I18n.getLanguage(),
  185. result = $.PrivateBin.I18n.translate('Never'),
  186. alias = $.PrivateBin.I18n._('Never');
  187. clean();
  188. return language === loadedLang && result === alias;
  189. }
  190. );
  191. jsc.property(
  192. 'should default to en',
  193. function() {
  194. var clean = jsdom('', {url: 'https://privatebin.net/'});
  195. // when navigator.userLanguage is undefined and no default language
  196. // is specified, it would throw an error
  197. [ 'language', 'userLanguage' ].forEach(function (key) {
  198. Object.defineProperty(navigator, key, {
  199. value: undefined,
  200. writeable: false
  201. });
  202. });
  203. $.PrivateBin.I18n.reset('en');
  204. $.PrivateBin.I18n.loadTranslations();
  205. var result = $.PrivateBin.I18n.translate('Never'),
  206. alias = $.PrivateBin.I18n._('Never');
  207. clean();
  208. return 'Never' === result && 'Never' === alias;
  209. }
  210. );
  211. });
  212. });