handlebars-i18n 1.6.4 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,32 +2,12 @@
2
2
  * handlebars-i18n.js
3
3
  *
4
4
  * @author: Florian Walzel
5
- * @date: 2021-10
5
+ * @date: 2024-05
6
6
  *
7
7
  * handlebars-i18n adds features for localization/
8
8
  * internationalization to handlebars.js
9
9
  *
10
- * Copyright (c) 2020 Florian Walzel
11
- *
12
- * Permission is hereby granted, free of charge, to any person
13
- * obtaininga copy of this software and associated documentation
14
- * files (the "Software"), to deal in the Software without restriction,
15
- * including without limitation the rights to use, copy, modify, merge,
16
- * publish, distribute, sublicense, and/or sell copies of the Software,
17
- * and to permit persons to whom the Software is furnished to do so,
18
- * subject to the following conditions:
19
- *
20
- * The above copyright notice and this permission notice shall be
21
- * included in all copies or substantial portions of the Software.
22
- *
23
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
25
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
- * THE SOFTWARE.
10
+ * Copyright (c) 2020-24 Florian Walzel, MIT License
31
11
  *
32
12
  *********************************************************************/
33
13
 
@@ -36,10 +16,15 @@
36
16
  if (typeof exports === 'object' && typeof module === 'object') {
37
17
  const Handlebars = require('handlebars'),
38
18
  i18next = require('i18next'),
39
- Intl = require('intl');
40
- module.exports = factory(Handlebars, i18next, Intl);
41
- }
42
- else if (typeof define === 'function' && define.amd)
19
+ Intl = require('intl'),
20
+ RelativeTimeFormat = require('relative-time-format');
21
+ module.exports = factory(
22
+ Handlebars,
23
+ i18next,
24
+ Intl,
25
+ RelativeTimeFormat,
26
+ process?.env?.NODE_ENV === 'TEST');
27
+ } else if (typeof define === 'function' && define.amd)
43
28
  define(['Handlebars', 'i18next', 'Intl'], factory);
44
29
  else if (typeof root.Handlebars === 'object'
45
30
  && typeof root.i18next === 'object'
@@ -50,15 +35,24 @@
50
35
  return false;
51
36
  }
52
37
 
53
- })(this, function (handlebars, i18next, Intl) {
38
+ })(this, function (handlebars, i18next, Intl, RelativeTimePolyfill, isTest) {
54
39
 
55
40
  'use strict';
56
41
 
57
- var defaultConf = {
42
+ // the object to store
43
+ // configurations for specific languages (standard)
44
+ // and custom settings.
45
+ const defaultConf = {
58
46
  DateTimeFormat: {
59
47
  standard: {},
60
48
  custom: {}
61
49
  },
50
+ RelativeTimeFormat: {
51
+ standard: {
52
+ all: {unit: 'hours'}
53
+ },
54
+ custom: {}
55
+ },
62
56
  NumberFormat: {
63
57
  standard: {},
64
58
  custom: {}
@@ -71,8 +65,12 @@
71
65
  }
72
66
  };
73
67
 
74
- // make a copy of default object
75
- var optionsConf = JSON.parse(JSON.stringify(defaultConf));
68
+ // make a copy of default object to hold (optional)
69
+ // custom configuration to be defined by the user
70
+ let optionsConf = JSON.parse(JSON.stringify(defaultConf));
71
+
72
+ // object for holding polyfill languages in node environment
73
+ const polyfillLangs = {};
76
74
 
77
75
 
78
76
  /*************************************
@@ -87,21 +85,22 @@
87
85
  * @private
88
86
  */
89
87
  function __applyToConstructor(constructor, argArray) {
90
- var args = [null].concat(argArray);
91
- var factoryFunction = constructor.bind.apply(constructor, args);
88
+ let args = [null].concat(argArray);
89
+ let factoryFunction = constructor.bind.apply(constructor, args);
92
90
  return new factoryFunction();
93
91
  }
94
92
 
95
93
  /**
96
94
  *
97
95
  * @param hndlbrsOpts
96
+ * @param lang
98
97
  * @param OCFormat
99
98
  * @returns {*}
100
99
  * @private
101
100
  */
102
101
  function __configLookup(hndlbrsOpts, lang, OCFormat) {
103
102
 
104
- // check if an options object with .hash exists, and holds some content
103
+ // check if an options object with property hash exists, and holds some content
105
104
  if (typeof hndlbrsOpts === 'object'
106
105
  && typeof hndlbrsOpts.hash === 'object'
107
106
  && Object.keys(hndlbrsOpts.hash).length > 0) {
@@ -113,19 +112,19 @@
113
112
  if (typeof oh.format === 'undefined') {
114
113
  return oh;
115
114
  }
116
- // when custom format is given, check if the configuration was set
115
+ // when custom format is given, check if the configuration was set ...
117
116
  else if (typeof OCFormat.custom[oh.format] !== 'undefined'
118
117
  && typeof OCFormat.custom[oh.format][lang] !== 'undefined') {
119
118
  return OCFormat.custom[oh.format][lang];
120
119
  }
121
120
  }
122
121
 
123
- // when no options for custom formats given, first check whether
122
+ // ... when no options for custom formats given, first check whether
124
123
  // the specific language has a generic definition
125
124
  if (typeof OCFormat.standard[lang] !== 'undefined')
126
125
  return OCFormat.standard[lang];
127
126
 
128
- // then check if a universal format definition for all languages exist
127
+ // ... then check if a universal format definition for all languages exists
129
128
  if (typeof OCFormat.standard.all !== 'undefined')
130
129
  return OCFormat.standard.all;
131
130
 
@@ -139,6 +138,7 @@
139
138
  * @param lngShortcode
140
139
  * @param typeOfFormat
141
140
  * @param options
141
+ * @param customFormat
142
142
  * @returns {boolean}
143
143
  * @private
144
144
  */
@@ -150,12 +150,10 @@
150
150
  return false;
151
151
  }
152
152
 
153
- if (typeOfFormat !== 'DateTimeFormat'
154
- && typeOfFormat !== 'NumberFormat'
155
- && typeOfFormat !== 'PriceFormat') {
153
+ if (!['DateTimeFormat', 'RelativeTimeFormat', 'NumberFormat', 'PriceFormat'].includes(typeOfFormat)) {
156
154
  console.error('@ handlebars-i18n.configure(): Invalid argument <' + typeOfFormat + '>. ' +
157
155
  'Second argument must be a string with the options key. ' +
158
- 'Use either "DateTimeFormat", "NumberFormat" or "PriceFormat".');
156
+ 'Use either "DateTimeFormat", "RelativeTimeFormat", "NumberFormat", or "PriceFormat".');
159
157
  return false;
160
158
  }
161
159
 
@@ -178,27 +176,127 @@
178
176
  /**
179
177
  *
180
178
  * @param lang
181
- * @param typeOfFormat
179
+ * @param formatType
182
180
  * @param options
183
181
  * @param customFormat
184
182
  * @returns {boolean}
185
183
  * @private
186
184
  */
187
- function __setArgs(lang, typeOfFormat, options, customFormat) {
185
+ function __setArgs(lang, formatType, options, customFormat) {
188
186
 
189
187
  if (typeof customFormat !== 'undefined' && customFormat !== null) {
190
188
  // create object node with name of the configuration if not already existing
191
- if (typeof optionsConf[typeOfFormat].custom[customFormat] === 'undefined')
192
- optionsConf[typeOfFormat].custom[customFormat] = {};
189
+ if (typeof optionsConf[formatType].custom[customFormat] === 'undefined')
190
+ optionsConf[formatType].custom[customFormat] = {};
193
191
 
194
- optionsConf[typeOfFormat].custom[customFormat][lang] = options;
195
- }
196
- else
197
- optionsConf[typeOfFormat].standard[lang] = options;
192
+ optionsConf[formatType].custom[customFormat][lang] = options;
193
+ } else
194
+ optionsConf[formatType].standard[lang] = options;
198
195
 
199
196
  return true;
200
197
  }
201
198
 
199
+ /**
200
+ *
201
+ * @param input
202
+ * @returns {boolean}
203
+ * @private
204
+ */
205
+ function __isNumOrString(input) {
206
+ return typeof input === 'number' || (typeof input === 'string' && input !== '')
207
+ }
208
+
209
+ /**
210
+ *
211
+ * @param dateInput
212
+ * @returns {Date}
213
+ * @private
214
+ */
215
+ function __createDateObj(dateInput) {
216
+
217
+ let date;
218
+
219
+ if (typeof dateInput === 'number') {
220
+ // input as milliseconds since unix epoch, like: 1583922952743
221
+ date = new Date(dateInput);
222
+ } else if (typeof dateInput === 'string') {
223
+
224
+ if (dateInput.charAt(0) === '[' && dateInput.slice(-1) === ']') {
225
+ // input as array represented as string such as "[2020, 11]"
226
+ dateInput = dateInput.substring(1, dateInput.length - 1).replace(/ /g, '');
227
+ let dateArr = dateInput.split(',');
228
+ let dateFactory = __applyToConstructor.bind(null, Date);
229
+ date = dateFactory(dateArr);
230
+ } else if (dateInput.toLowerCase() === 'now' || dateInput.toLowerCase() === 'today') {
231
+ // input as word "now" or "today"
232
+ date = new Date();
233
+ } else {
234
+ // input as date string such as "1995-12-17T03:24:00"
235
+ date = new Date(dateInput);
236
+ }
237
+ } else {
238
+ // fallback: today’s date
239
+ date = new Date();
240
+ }
241
+
242
+ return date;
243
+ }
244
+
245
+ /**
246
+ *
247
+ * @param lang
248
+ * @param opts
249
+ * @returns {Intl.RelativeTimeFormat|*}
250
+ * @private
251
+ */
252
+ function __getRelDateFormatPolyfill(lang, opts) {
253
+ if (typeof Intl.RelativeTimeFormat === 'function')
254
+ return new Intl.RelativeTimeFormat(lang, opts);
255
+ else {
256
+ if (typeof polyfillLangs[lang] === 'undefined') {
257
+ try {
258
+ polyfillLangs[lang] = require(`relative-time-format/locale/${lang}`);
259
+ } catch (e) {
260
+ console.error(e);
261
+ }
262
+ }
263
+ RelativeTimePolyfill.addLocale(polyfillLangs[lang]);
264
+ return new RelativeTimePolyfill(lang, opts);
265
+ }
266
+ }
267
+
268
+ /**
269
+ *
270
+ * @param diff
271
+ * @param unit
272
+ * @returns {number}
273
+ * @private
274
+ */
275
+ function __getDateDiff(diff, unit) {
276
+
277
+ const divisions = {
278
+ second: 1e3,
279
+ seconds: 1e3,
280
+ minute: 6e4,
281
+ minutes: 6e4,
282
+ hour: 3.6e6,
283
+ hours: 3.6e6,
284
+ day: 8.64e7,
285
+ days: 8.64e7,
286
+ week: 6.048e8,
287
+ weeks: 6.048e8,
288
+ month: 2.629746e9,
289
+ months: 2.629746e9,
290
+ quarter: 78894e5,
291
+ quarters: 78894e5,
292
+ year: 3.15576e10,
293
+ years: 3.15576e10,
294
+ }
295
+
296
+ unit = unit || 'hour';
297
+ return Math.trunc(diff / divisions[unit]);
298
+ }
299
+
202
300
 
203
301
  /*************************************
204
302
  * PUBLIC INTERFACE
@@ -213,11 +311,11 @@
213
311
  * @param typeOfFormat : string - DateTimeFormat | NumberFormat | PriceFormat
214
312
  * @param options : object - the options object
215
313
  */
216
- configure: function (langOrArr, typeOfFormat, options, customFormatname = null) {
314
+ configure: function (langOrArr, typeOfFormat, options, customFormatName) {
217
315
 
218
316
  if (typeof langOrArr !== 'string' && !Array.isArray(langOrArr)) {
219
317
  console.error('@ handlebars-i18n.configure(): Invalid argument <' + langOrArr + '> ' +
220
- 'First argument must be a string with language code such as "en" or an array with parameters.');
318
+ 'First argument must be a string with language code such as "en" or an array with language parameters.');
221
319
  return false;
222
320
  }
223
321
 
@@ -233,10 +331,9 @@
233
331
  else
234
332
  return false;
235
333
  });
236
- }
237
- else {
238
- if (__validateArgs(langOrArr, typeOfFormat, options, customFormatname))
239
- __setArgs(langOrArr, typeOfFormat, options, customFormatname);
334
+ } else {
335
+ if (__validateArgs(langOrArr, typeOfFormat, options, customFormatName))
336
+ __setArgs(langOrArr, typeOfFormat, options, customFormatName);
240
337
  else
241
338
  return false;
242
339
  }
@@ -245,7 +342,7 @@
245
342
  },
246
343
 
247
344
  /**
248
- * resets the configuration to default state like it is before configure() is called
345
+ * resets the configuration to default state like it was before configure() has been called
249
346
  */
250
347
  reset: function () {
251
348
  optionsConf = JSON.parse(JSON.stringify(defaultConf));
@@ -269,14 +366,14 @@
269
366
  handlebars = overrideHndlbrs;
270
367
  else if (typeof overrideHndlbrs !== 'undefined' && overrideHndlbrs !== null)
271
368
  console.error('@ handlebars-i18n.init(): Invalid Argument [1] given for overrideHndlbrs. ' +
272
- 'Argument must be the Handlebars Object. Using handlebars object on module instead.');
369
+ 'Argument must be the Handlebars object. Using generic Handlebars object instead.');
273
370
 
274
371
  if (typeof overrideI18n === 'object' && overrideI18n !== null)
275
372
  i18next = overrideI18n;
276
373
 
277
374
  else if (typeof overrideI18n !== 'undefined' && overrideI18n !== null)
278
375
  console.error('@ handlebars-i18n.init(): Invalid Argument [2] given for overrideI18n. ' +
279
- 'Argument must be the i18next Object. Using i18next object on module level instead.');
376
+ 'Argument must be the i18next object. Using generic i18next object on module level instead.');
280
377
 
281
378
  handlebars.registerHelper('__',
282
379
  /**
@@ -289,7 +386,7 @@
289
386
  * @returns {*}
290
387
  */
291
388
  function (str, attributes) {
292
- return new handlebars.SafeString((typeof(i18next) !== 'undefined' ? i18next.t(str, attributes.hash) : str));
389
+ return new handlebars.SafeString((typeof (i18next) !== 'undefined' ? i18next.t(str, attributes.hash) : str));
293
390
  }
294
391
  );
295
392
  handlebars.registerHelper('_locale',
@@ -316,7 +413,7 @@
316
413
  );
317
414
  handlebars.registerHelper('_date',
318
415
  /**
319
- * formats a given date by the give internationalization options
416
+ * formats a date according to the give internationalization options
320
417
  *
321
418
  * allows multiple input forms:
322
419
  * {{_date}}
@@ -341,40 +438,63 @@
341
438
  * @param options
342
439
  */
343
440
  function (dateInput, options) {
441
+ const date = __createDateObj(dateInput);
442
+ const opts = __configLookup(options, i18next.language, optionsConf.DateTimeFormat);
443
+ const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
444
+ return dateFormat.format(date);
445
+ }
446
+ );
447
+ handlebars.registerHelper('_dateRel',
448
+ /**
449
+ * returns a relative date formatted according the options
450
+ * a positive dateValue will address a future date, like 'in 1 day'
451
+ * a negative dateValue will relate to a past date, like '1 day ago'
452
+ *
453
+ * _dateRel uses a polyfill in node environment because node’s Intl
454
+ * does not support relative date formats.
455
+ *
456
+ * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
457
+ * @link https://www.npmjs.com/package/relative-time-format
458
+ *
459
+ * @param dateValue
460
+ * @param options
461
+ * @returns {string}
462
+ */
463
+ function (dateValue, options) {
464
+ const relDate = parseInt(dateValue);
465
+ const opts = __configLookup(options, i18next.language, optionsConf.RelativeTimeFormat);
466
+ const relDateFormat = __getRelDateFormatPolyfill(i18next.language, opts);
467
+ return relDateFormat.format(relDate, opts.unit);
468
+ }
469
+ );
470
+ handlebars.registerHelper('_dateDiff',
471
+ /**
472
+ *
473
+ * @param dateInputA
474
+ * @param dateInputB
475
+ * @param options
476
+ * @returns {string|null}
477
+ */
478
+ function (dateInputA, dateInputB, options) {
344
479
 
345
- var date;
480
+ let dateDiff;
481
+ let opts = __configLookup(options, i18next.language, optionsConf.RelativeTimeFormat);
346
482
 
347
- if (typeof dateInput === 'number') {
348
- // input as milliseconds since unix epoch, like: 1583922952743
349
- date = new Date(dateInput);
350
- }
351
- else if (typeof dateInput === 'string') {
352
-
353
- if (dateInput.charAt(0) === '[' && dateInput.slice(-1) === ']') {
354
- // input as array represented as string such as "[2020, 11]"
355
- dateInput = dateInput.substring(1, dateInput.length - 1).replace(/ /g, '');
356
- var dateArr = dateInput.split(',');
357
- var dateFactory = __applyToConstructor.bind(null, Date);
358
- date = dateFactory(dateArr);
359
- }
360
- else if (dateInput.toLowerCase() === 'now' || dateInput.toLowerCase() === 'today') {
361
- // input as word "now" or "today"
362
- date = new Date();
363
- }
364
- else {
365
- // input as date string such as "1995-12-17T03:24:00"
366
- date = new Date(dateInput);
367
- }
368
- }
369
- else {
370
- // fallback: today's date
371
- date = new Date();
483
+ if (!__isNumOrString(dateInputA)) {
484
+ console.error('@ handlebars-i18n: invalid first argument dateInputA was given for _dateDiff.');
485
+ return null;
372
486
  }
373
487
 
374
- var opts = __configLookup(options, i18next.language, optionsConf.DateTimeFormat);
488
+ dateInputB = dateInputB || 'now';
375
489
 
376
- const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
377
- return dateFormat.format(date);
490
+ let dateA = __createDateObj(dateInputA);
491
+ let dateB = __createDateObj(dateInputB);
492
+
493
+ dateDiff = dateA - dateB;
494
+
495
+ const relDate = __getDateDiff(dateDiff, opts.unit);
496
+ const relDateFormat = __getRelDateFormatPolyfill(i18next.language, opts);
497
+ return relDateFormat.format(relDate, opts.unit);
378
498
  }
379
499
  );
380
500
  handlebars.registerHelper('_num',
@@ -392,7 +512,7 @@
392
512
  */
393
513
  function (number, options) {
394
514
 
395
- var opts = __configLookup(options, i18next.language, optionsConf.NumberFormat);
515
+ let opts = __configLookup(options, i18next.language, optionsConf.NumberFormat);
396
516
 
397
517
  const priceFormat = new Intl.NumberFormat(i18next.language, opts);
398
518
  return priceFormat.format(number);
@@ -413,7 +533,7 @@
413
533
  */
414
534
  function (price, options) {
415
535
 
416
- var opts = __configLookup(options, i18next.language, optionsConf.PriceFormat);
536
+ let opts = __configLookup(options, i18next.language, optionsConf.PriceFormat);
417
537
 
418
538
  // for convenience automatically add the object parameter style:'currency' if not given
419
539
  if (typeof opts['style'] !== 'string' && typeof opts['currency'] === 'string')
@@ -425,9 +545,23 @@
425
545
  );
426
546
 
427
547
  return handlebars;
428
- }
429
- }
430
- });
431
-
432
-
548
+ },
433
549
 
550
+ /**
551
+ * we conditionally export the helpers to be able to test against them;
552
+ * in production they are not exported.
553
+ */
554
+ ...(isTest) && {
555
+ private: {
556
+ applyToConstructor: __applyToConstructor,
557
+ configLookup: __configLookup,
558
+ validateArgs: __validateArgs,
559
+ setArgs: __setArgs,
560
+ isNumOrString: __isNumOrString,
561
+ createDateObj: __createDateObj,
562
+ getRelDateFormatPolyfill: __getRelDateFormatPolyfill,
563
+ getDateDiff: __getDateDiff
564
+ }
565
+ },
566
+ }
567
+ });
@@ -1 +1 @@
1
- !function(e,r){if("object"==typeof exports&&"object"==typeof module){let n=require("handlebars"),t=require("i18next"),a=require("intl");module.exports=r(n,t,a)}else"function"==typeof define&&define.amd?define(["Handlebars","i18next","Intl"],r):"object"==typeof e.Handlebars&&"object"==typeof e.i18next&&"object"==typeof e.Intl?e.HandlebarsI18n=r(e.Handlebars,e.i18next,e.Intl):console.error("@ handlebars-i18n: One or more dependencies are missing. Check for Handlebars, i18next and Intl.")}(this,function(e,r,n){"use strict";var t={DateTimeFormat:{standard:{},custom:{}},NumberFormat:{standard:{},custom:{}},PriceFormat:{standard:{all:{style:"currency",currency:"EUR"}},custom:{}}},a=JSON.parse(JSON.stringify(t));function o(e,r){var n=[null].concat(r);return new(e.bind.apply(e,n))}function i(e,r,n){if("object"==typeof e&&"object"==typeof e.hash&&Object.keys(e.hash).length>0){let t=e.hash;if(void 0===t.format)return t;if(void 0!==n.custom[t.format]&&void 0!==n.custom[t.format][r])return n.custom[t.format][r]}return void 0!==n.standard[r]?n.standard[r]:void 0!==n.standard.all?n.standard.all:{}}function u(e,r,n,t){return"string"!=typeof e?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+e+'> First argument must be a string with language code such as "en".'),!1):"DateTimeFormat"!==r&&"NumberFormat"!==r&&"PriceFormat"!==r?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+r+'>. Second argument must be a string with the options key. Use either "DateTimeFormat", "NumberFormat" or "PriceFormat".'),!1):"object"!=typeof n?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+n+"> Third argument must be an object containing the configuration parameters."),!1):(null==t||"string"==typeof t)&&""!==t&&" "!==t||(console.error("@ handlebars-i18n.configure(): Invalid argument <"+t+"> Fourth argument (optional) must be a string naming your custom format configuration."),!1)}function s(e,r,n,t){return null!=t?(void 0===a[r].custom[t]&&(a[r].custom[t]={}),a[r].custom[t][e]=n):a[r].standard[e]=n,!0}return{configure:function(e,r,n,t=null){if("string"!=typeof e&&!Array.isArray(e))return console.error("@ handlebars-i18n.configure(): Invalid argument <"+e+'> First argument must be a string with language code such as "en" or an array with parameters.'),!1;if(Array.isArray(e)){if(e.length<1)return console.log("@ handlebars-i18n.configure(): You passed an empty array, no parameters taken."),!1;e.forEach(e=>{if(!u(e[0],e[1],e[2],e[3]))return!1;s(e[0],e[1],e[2],e[3])})}else{if(!u(e,r,n,t))return!1;s(e,r,n,t)}return!0},reset:function(){return a=JSON.parse(JSON.stringify(t)),!0},init:function(t,u){return"object"==typeof t&&null!==t?e=t:null!=t&&console.error("@ handlebars-i18n.init(): Invalid Argument [1] given for overrideHndlbrs. Argument must be the Handlebars Object. Using handlebars object on module instead."),"object"==typeof u&&null!==u?r=u:null!=u&&console.error("@ handlebars-i18n.init(): Invalid Argument [2] given for overrideI18n. Argument must be the i18next Object. Using i18next object on module level instead."),e.registerHelper("__",function(n,t){return new e.SafeString(void 0!==r?r.t(n,t.hash):n)}),e.registerHelper("_locale",function(){return r.language}),e.registerHelper("localeIs",function(e){return r.language===e}),e.registerHelper("_date",function(e,t){if("number"==typeof e)u=new Date(e);else if("string"==typeof e){if("["===e.charAt(0)&&"]"===e.slice(-1)){var u,s=(e=e.substring(1,e.length-1).replace(/ /g,"")).split(",");u=o.bind(null,Date)(s)}else u="now"===e.toLowerCase()||"today"===e.toLowerCase()?new Date:new Date(e)}else u=new Date;var l=i(t,r.language,a.DateTimeFormat);let c=new n.DateTimeFormat(r.language,l);return c.format(u)}),e.registerHelper("_num",function(e,t){var o=i(t,r.language,a.NumberFormat);let u=new n.NumberFormat(r.language,o);return u.format(e)}),e.registerHelper("_price",function(e,t){var o=i(t,r.language,a.PriceFormat);"string"!=typeof o.style&&"string"==typeof o.currency&&(o.style="currency");let u=new n.NumberFormat(r.language,o);return u.format(e)}),e}}});
1
+ !function(e,r){if("object"==typeof exports&&"object"==typeof module){const e=require("handlebars"),t=require("i18next"),n=require("intl"),a=require("relative-time-format");module.exports=r(e,t,n,a,"TEST"===process?.env?.NODE_ENV)}else if("function"==typeof define&&define.amd)define(["Handlebars","i18next","Intl"],r);else{if("object"!=typeof e.Handlebars||"object"!=typeof e.i18next||"object"!=typeof e.Intl)return console.error("@ handlebars-i18n: One or more dependencies are missing. Check for Handlebars, i18next and Intl."),!1;e.HandlebarsI18n=r(e.Handlebars,e.i18next,e.Intl)}}(this,(function(e,r,t,n,a){"use strict";const o={DateTimeFormat:{standard:{},custom:{}},RelativeTimeFormat:{standard:{all:{unit:"hours"}},custom:{}},NumberFormat:{standard:{},custom:{}},PriceFormat:{standard:{all:{style:"currency",currency:"EUR"}},custom:{}}};let i=JSON.parse(JSON.stringify(o));const u={};function s(e,r){let t=[null].concat(r);return new(e.bind.apply(e,t))}function l(e,r,t){if("object"==typeof e&&"object"==typeof e.hash&&Object.keys(e.hash).length>0){let n=e.hash;if(void 0===n.format)return n;if(void 0!==t.custom[n.format]&&void 0!==t.custom[n.format][r])return t.custom[n.format][r]}return void 0!==t.standard[r]?t.standard[r]:void 0!==t.standard.all?t.standard.all:{}}function c(e,r,t,n){return"string"!=typeof e?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+e+'> First argument must be a string with language code such as "en".'),!1):["DateTimeFormat","RelativeTimeFormat","NumberFormat","PriceFormat"].includes(r)?"object"!=typeof t?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+t+"> Third argument must be an object containing the configuration parameters."),!1):(null==n||"string"==typeof n)&&""!==n&&" "!==n||(console.error("@ handlebars-i18n.configure(): Invalid argument <"+n+"> Fourth argument (optional) must be a string naming your custom format configuration."),!1):(console.error("@ handlebars-i18n.configure(): Invalid argument <"+r+'>. Second argument must be a string with the options key. Use either "DateTimeFormat", "RelativeTimeFormat", "NumberFormat", or "PriceFormat".'),!1)}function m(e,r,t,n){return null!=n?(void 0===i[r].custom[n]&&(i[r].custom[n]={}),i[r].custom[n][e]=t):i[r].standard[e]=t,!0}function f(e){return"number"==typeof e||"string"==typeof e&&""!==e}function g(e){let r;if("number"==typeof e)r=new Date(e);else if("string"==typeof e)if("["===e.charAt(0)&&"]"===e.slice(-1)){let t=(e=e.substring(1,e.length-1).replace(/ /g,"")).split(",");r=s.bind(null,Date)(t)}else r="now"===e.toLowerCase()||"today"===e.toLowerCase()?new Date:new Date(e);else r=new Date;return r}function d(e,r){if("function"==typeof t.RelativeTimeFormat)return new t.RelativeTimeFormat(e,r);if(void 0===u[e])try{u[e]=require(`relative-time-format/locale/${e}`)}catch(e){console.error(e)}return n.addLocale(u[e]),new n(e,r)}function b(e,r){return r=r||"hour",Math.trunc(e/{second:1e3,seconds:1e3,minute:6e4,minutes:6e4,hour:36e5,hours:36e5,day:864e5,days:864e5,week:6048e5,weeks:6048e5,month:2629746e3,months:2629746e3,quarter:78894e5,quarters:78894e5,year:315576e5,years:315576e5}[r])}return{configure:function(e,r,t,n){if("string"!=typeof e&&!Array.isArray(e))return console.error("@ handlebars-i18n.configure(): Invalid argument <"+e+'> First argument must be a string with language code such as "en" or an array with language parameters.'),!1;if(Array.isArray(e)){if(e.length<1)return console.log("@ handlebars-i18n.configure(): You passed an empty array, no parameters taken."),!1;e.forEach((e=>{if(!c(e[0],e[1],e[2],e[3]))return!1;m(e[0],e[1],e[2],e[3])}))}else{if(!c(e,r,t,n))return!1;m(e,r,t,n)}return!0},reset:function(){return i=JSON.parse(JSON.stringify(o)),!0},init:function(n,a){return"object"==typeof n&&null!==n?e=n:null!=n&&console.error("@ handlebars-i18n.init(): Invalid Argument [1] given for overrideHndlbrs. Argument must be the Handlebars object. Using generic Handlebars object instead."),"object"==typeof a&&null!==a?r=a:null!=a&&console.error("@ handlebars-i18n.init(): Invalid Argument [2] given for overrideI18n. Argument must be the i18next object. Using generic i18next object on module level instead."),e.registerHelper("__",(function(t,n){return new e.SafeString(void 0!==r?r.t(t,n.hash):t)})),e.registerHelper("_locale",(function(){return r.language})),e.registerHelper("localeIs",(function(e){return r.language===e})),e.registerHelper("_date",(function(e,n){const a=g(e),o=l(n,r.language,i.DateTimeFormat);return new t.DateTimeFormat(r.language,o).format(a)})),e.registerHelper("_dateRel",(function(e,t){const n=parseInt(e),a=l(t,r.language,i.RelativeTimeFormat);return d(r.language,a).format(n,a.unit)})),e.registerHelper("_dateDiff",(function(e,t,n){let a,o=l(n,r.language,i.RelativeTimeFormat);if(!f(e))return console.error("@ handlebars-i18n: invalid first argument dateInputA was given for _dateDiff."),null;t=t||"now",a=g(e)-g(t);const u=b(a,o.unit);return d(r.language,o).format(u,o.unit)})),e.registerHelper("_num",(function(e,n){let a=l(n,r.language,i.NumberFormat);return new t.NumberFormat(r.language,a).format(e)})),e.registerHelper("_price",(function(e,n){let a=l(n,r.language,i.PriceFormat);"string"!=typeof a.style&&"string"==typeof a.currency&&(a.style="currency");return new t.NumberFormat(r.language,a).format(e)})),e},...a&&{private:{applyToConstructor:s,configLookup:l,validateArgs:c,setArgs:m,isNumOrString:f,createDateObj:g,getRelDateFormatPolyfill:d,getDateDiff:b}}}}));