handlebars-i18n 1.6.3 → 1.7.0

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,8 +16,14 @@
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);
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');
41
27
  }
42
28
  else if (typeof define === 'function' && define.amd)
43
29
  define(['Handlebars', 'i18next', 'Intl'], factory);
@@ -50,15 +36,24 @@
50
36
  return false;
51
37
  }
52
38
 
53
- })(this, function (handlebars, i18next, Intl) {
39
+ })(this, function (handlebars, i18next, Intl, RelativeTimePolyfill, isTest) {
54
40
 
55
41
  'use strict';
56
42
 
57
- var defaultConf = {
43
+ // the object to store
44
+ // configurations for specific languages (standard)
45
+ // and custom settings.
46
+ const defaultConf = {
58
47
  DateTimeFormat: {
59
48
  standard: {},
60
49
  custom: {}
61
50
  },
51
+ RelativeTimeFormat: {
52
+ standard: {
53
+ all: {unit: 'hours'}
54
+ },
55
+ custom: {}
56
+ },
62
57
  NumberFormat: {
63
58
  standard: {},
64
59
  custom: {}
@@ -71,8 +66,12 @@
71
66
  }
72
67
  };
73
68
 
74
- // make a copy of default object
75
- var optionsConf = JSON.parse(JSON.stringify(defaultConf));
69
+ // make a copy of default object to hold (optional)
70
+ // custom configuration to be defined by the user
71
+ let optionsConf = JSON.parse(JSON.stringify(defaultConf));
72
+
73
+ // object for holding polyfill languages in node environment
74
+ const polyfillLangs = {};
76
75
 
77
76
 
78
77
  /*************************************
@@ -87,21 +86,22 @@
87
86
  * @private
88
87
  */
89
88
  function __applyToConstructor(constructor, argArray) {
90
- var args = [null].concat(argArray);
91
- var factoryFunction = constructor.bind.apply(constructor, args);
89
+ let args = [null].concat(argArray);
90
+ let factoryFunction = constructor.bind.apply(constructor, args);
92
91
  return new factoryFunction();
93
92
  }
94
93
 
95
94
  /**
96
95
  *
97
96
  * @param hndlbrsOpts
97
+ * @param lang
98
98
  * @param OCFormat
99
99
  * @returns {*}
100
100
  * @private
101
101
  */
102
102
  function __configLookup(hndlbrsOpts, lang, OCFormat) {
103
103
 
104
- // check if an options object with .hash exists, and holds some content
104
+ // check if an options object with property hash exists, and holds some content
105
105
  if (typeof hndlbrsOpts === 'object'
106
106
  && typeof hndlbrsOpts.hash === 'object'
107
107
  && Object.keys(hndlbrsOpts.hash).length > 0) {
@@ -113,19 +113,19 @@
113
113
  if (typeof oh.format === 'undefined') {
114
114
  return oh;
115
115
  }
116
- // when custom format is given, check if the configuration was set
116
+ // when custom format is given, check if the configuration was set ...
117
117
  else if (typeof OCFormat.custom[oh.format] !== 'undefined'
118
118
  && typeof OCFormat.custom[oh.format][lang] !== 'undefined') {
119
119
  return OCFormat.custom[oh.format][lang];
120
120
  }
121
121
  }
122
122
 
123
- // when no options for custom formats given, first check whether
123
+ // ... when no options for custom formats given, first check whether
124
124
  // the specific language has a generic definition
125
125
  if (typeof OCFormat.standard[lang] !== 'undefined')
126
126
  return OCFormat.standard[lang];
127
127
 
128
- // then check if a universal format definition for all languages exist
128
+ // ... then check if a universal format definition for all languages exists
129
129
  if (typeof OCFormat.standard.all !== 'undefined')
130
130
  return OCFormat.standard.all;
131
131
 
@@ -139,6 +139,7 @@
139
139
  * @param lngShortcode
140
140
  * @param typeOfFormat
141
141
  * @param options
142
+ * @param customFormat
142
143
  * @returns {boolean}
143
144
  * @private
144
145
  */
@@ -150,12 +151,10 @@
150
151
  return false;
151
152
  }
152
153
 
153
- if (typeOfFormat !== 'DateTimeFormat'
154
- && typeOfFormat !== 'NumberFormat'
155
- && typeOfFormat !== 'PriceFormat') {
154
+ if (! ['DateTimeFormat', 'RelativeTimeFormat', 'NumberFormat', 'PriceFormat'].includes(typeOfFormat)) {
156
155
  console.error('@ handlebars-i18n.configure(): Invalid argument <' + typeOfFormat + '>. ' +
157
156
  'Second argument must be a string with the options key. ' +
158
- 'Use either "DateTimeFormat", "NumberFormat" or "PriceFormat".');
157
+ 'Use either "DateTimeFormat", "RelativeTimeFormat", "NumberFormat", or "PriceFormat".');
159
158
  return false;
160
159
  }
161
160
 
@@ -178,27 +177,132 @@
178
177
  /**
179
178
  *
180
179
  * @param lang
181
- * @param typeOfFormat
180
+ * @param formatType
182
181
  * @param options
183
182
  * @param customFormat
184
183
  * @returns {boolean}
185
184
  * @private
186
185
  */
187
- function __setArgs(lang, typeOfFormat, options, customFormat) {
186
+ function __setArgs(lang, formatType, options, customFormat) {
188
187
 
189
188
  if (typeof customFormat !== 'undefined' && customFormat !== null) {
190
189
  // 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] = {};
190
+ if (typeof optionsConf[formatType].custom[customFormat] === 'undefined')
191
+ optionsConf[formatType].custom[customFormat] = {};
193
192
 
194
- optionsConf[typeOfFormat].custom[customFormat][lang] = options;
193
+ optionsConf[formatType].custom[customFormat][lang] = options;
195
194
  }
196
195
  else
197
- optionsConf[typeOfFormat].standard[lang] = options;
196
+ optionsConf[formatType].standard[lang] = options;
198
197
 
199
198
  return true;
200
199
  }
201
200
 
201
+ /**
202
+ *
203
+ * @param input
204
+ * @returns {boolean}
205
+ * @private
206
+ */
207
+ function __isNumOrString(input) {
208
+ return typeof input === 'number' || (typeof input === 'string' && input !== '')
209
+ }
210
+
211
+ /**
212
+ *
213
+ * @param dateInput
214
+ * @returns {Date}
215
+ * @private
216
+ */
217
+ function __createDateObj(dateInput) {
218
+
219
+ let date;
220
+
221
+ if (typeof dateInput === 'number') {
222
+ // input as milliseconds since unix epoch, like: 1583922952743
223
+ date = new Date(dateInput);
224
+ }
225
+ else if (typeof dateInput === 'string') {
226
+
227
+ if (dateInput.charAt(0) === '[' && dateInput.slice(-1) === ']') {
228
+ // input as array represented as string such as "[2020, 11]"
229
+ dateInput = dateInput.substring(1, dateInput.length - 1).replace(/ /g, '');
230
+ let dateArr = dateInput.split(',');
231
+ let dateFactory = __applyToConstructor.bind(null, Date);
232
+ date = dateFactory(dateArr);
233
+ }
234
+ else if (dateInput.toLowerCase() === 'now' || dateInput.toLowerCase() === 'today') {
235
+ // input as word "now" or "today"
236
+ date = new Date();
237
+ }
238
+ else {
239
+ // input as date string such as "1995-12-17T03:24:00"
240
+ date = new Date(dateInput);
241
+ }
242
+ }
243
+ else {
244
+ // fallback: today’s date
245
+ date = new Date();
246
+ }
247
+
248
+ return date;
249
+ }
250
+
251
+ /**
252
+ *
253
+ * @param lang
254
+ * @param opts
255
+ * @returns {Intl.RelativeTimeFormat|*}
256
+ * @private
257
+ */
258
+ function __getRelDateFormatPolyfill(lang, opts) {
259
+ if (typeof Intl.RelativeTimeFormat === 'function')
260
+ return new Intl.RelativeTimeFormat(lang, opts);
261
+ else {
262
+ if (typeof polyfillLangs[lang] === 'undefined') {
263
+ try {
264
+ polyfillLangs[lang] = require(`relative-time-format/locale/${lang}`);
265
+ } catch (e) {
266
+ console.error(e);
267
+ }
268
+ }
269
+ RelativeTimePolyfill.addLocale(polyfillLangs[lang]);
270
+ return new RelativeTimePolyfill(lang, opts);
271
+ }
272
+ }
273
+
274
+ /**
275
+ *
276
+ * @param diff
277
+ * @param unit
278
+ * @returns {number}
279
+ * @private
280
+ */
281
+ function __getDateDiff(diff, unit) {
282
+
283
+ const divisions = {
284
+ second: 1e3,
285
+ seconds: 1e3,
286
+ minute: 6e4,
287
+ minutes: 6e4,
288
+ hour: 3.6e6,
289
+ hours: 3.6e6,
290
+ day: 8.64e7,
291
+ days: 8.64e7,
292
+ week: 6.048e8,
293
+ weeks: 6.048e8,
294
+ month: 2.629746e9,
295
+ months: 2.629746e9,
296
+ quarter: 78894e5,
297
+ quarters: 78894e5,
298
+ year: 3.15576e10,
299
+ years: 3.15576e10,
300
+ }
301
+
302
+ unit = unit || 'hour';
303
+ return Math.trunc(diff / divisions[unit]);
304
+ }
305
+
202
306
 
203
307
  /*************************************
204
308
  * PUBLIC INTERFACE
@@ -213,11 +317,11 @@
213
317
  * @param typeOfFormat : string - DateTimeFormat | NumberFormat | PriceFormat
214
318
  * @param options : object - the options object
215
319
  */
216
- configure: function (langOrArr, typeOfFormat, options, customFormatname = null) {
320
+ configure: function (langOrArr, typeOfFormat, options, customFormatName) {
217
321
 
218
322
  if (typeof langOrArr !== 'string' && !Array.isArray(langOrArr)) {
219
323
  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.');
324
+ 'First argument must be a string with language code such as "en" or an array with language parameters.');
221
325
  return false;
222
326
  }
223
327
 
@@ -235,8 +339,8 @@
235
339
  });
236
340
  }
237
341
  else {
238
- if (__validateArgs(langOrArr, typeOfFormat, options, customFormatname))
239
- __setArgs(langOrArr, typeOfFormat, options, customFormatname);
342
+ if (__validateArgs(langOrArr, typeOfFormat, options, customFormatName))
343
+ __setArgs(langOrArr, typeOfFormat, options, customFormatName);
240
344
  else
241
345
  return false;
242
346
  }
@@ -245,7 +349,7 @@
245
349
  },
246
350
 
247
351
  /**
248
- * resets the configuration to default state like it is before configure() is called
352
+ * resets the configuration to default state like it was before configure() has been called
249
353
  */
250
354
  reset: function () {
251
355
  optionsConf = JSON.parse(JSON.stringify(defaultConf));
@@ -269,14 +373,14 @@
269
373
  handlebars = overrideHndlbrs;
270
374
  else if (typeof overrideHndlbrs !== 'undefined' && overrideHndlbrs !== null)
271
375
  console.error('@ handlebars-i18n.init(): Invalid Argument [1] given for overrideHndlbrs. ' +
272
- 'Argument must be the Handlebars Object. Using handlebars object on module instead.');
376
+ 'Argument must be the Handlebars object. Using generic Handlebars object instead.');
273
377
 
274
378
  if (typeof overrideI18n === 'object' && overrideI18n !== null)
275
379
  i18next = overrideI18n;
276
380
 
277
381
  else if (typeof overrideI18n !== 'undefined' && overrideI18n !== null)
278
382
  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.');
383
+ 'Argument must be the i18next object. Using generic i18next object on module level instead.');
280
384
 
281
385
  handlebars.registerHelper('__',
282
386
  /**
@@ -316,7 +420,7 @@
316
420
  );
317
421
  handlebars.registerHelper('_date',
318
422
  /**
319
- * formats a given date by the give internationalization options
423
+ * formats a date according to the give internationalization options
320
424
  *
321
425
  * allows multiple input forms:
322
426
  * {{_date}}
@@ -341,40 +445,63 @@
341
445
  * @param options
342
446
  */
343
447
  function (dateInput, options) {
448
+ const date= __createDateObj(dateInput);
449
+ const opts = __configLookup(options, i18next.language, optionsConf.DateTimeFormat);
450
+ const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
451
+ return dateFormat.format(date);
452
+ }
453
+ );
454
+ handlebars.registerHelper('_dateRel',
455
+ /**
456
+ * returns a relative date formatted according the options
457
+ * a positive dateValue will address a future date, like 'in 1 day'
458
+ * a negative dateValue will relate to a past date, like '1 day ago'
459
+ *
460
+ * _dateRel uses a polyfill in node environment because node’s Intl
461
+ * does not support relative date formats.
462
+ *
463
+ * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
464
+ * @link https://www.npmjs.com/package/relative-time-format
465
+ *
466
+ * @param dateValue
467
+ * @param options
468
+ * @returns {string}
469
+ */
470
+ function (dateValue, options) {
471
+ const relDate= parseInt(dateValue);
472
+ const opts = __configLookup(options, i18next.language, optionsConf.RelativeTimeFormat);
473
+ const relDateFormat = __getRelDateFormatPolyfill(i18next.language, opts);
474
+ return relDateFormat.format(relDate, opts.unit);
475
+ }
476
+ );
477
+ handlebars.registerHelper('_dateDiff',
478
+ /**
479
+ *
480
+ * @param dateInputA
481
+ * @param dateInputB
482
+ * @param options
483
+ * @returns {string|null}
484
+ */
485
+ function (dateInputA, dateInputB, options) {
344
486
 
345
- var date;
487
+ let dateDiff;
346
488
 
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
- }
489
+ if (! __isNumOrString(dateInputA) && ! __isNumOrString(dateInputB))
490
+ return null;
491
+ else if (! __isNumOrString(dateInputB))
492
+ dateDiff = __createDateObj(dateInputA);
493
+ else if (! __isNumOrString(dateInputA))
494
+ dateDiff = __createDateObj(dateInputB);
369
495
  else {
370
- // fallback: today's date
371
- date = new Date();
496
+ const dateA= __createDateObj(dateInputA);
497
+ const dateB= __createDateObj(dateInputB);
498
+ dateDiff = dateA - dateB;
372
499
  }
373
500
 
374
- var opts = __configLookup(options, i18next.language, optionsConf.DateTimeFormat);
375
-
376
- const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
377
- return dateFormat.format(date);
501
+ const opts = __configLookup(options, i18next.language, optionsConf.RelativeTimeFormat);
502
+ const relDate = __getDateDiff(dateDiff, opts.unit);
503
+ const relDateFormat = __getRelDateFormatPolyfill(i18next.language, opts);
504
+ return relDateFormat.format(relDate, opts.unit);
378
505
  }
379
506
  );
380
507
  handlebars.registerHelper('_num',
@@ -392,7 +519,7 @@
392
519
  */
393
520
  function (number, options) {
394
521
 
395
- var opts = __configLookup(options, i18next.language, optionsConf.NumberFormat);
522
+ let opts = __configLookup(options, i18next.language, optionsConf.NumberFormat);
396
523
 
397
524
  const priceFormat = new Intl.NumberFormat(i18next.language, opts);
398
525
  return priceFormat.format(number);
@@ -413,7 +540,7 @@
413
540
  */
414
541
  function (price, options) {
415
542
 
416
- var opts = __configLookup(options, i18next.language, optionsConf.PriceFormat);
543
+ let opts = __configLookup(options, i18next.language, optionsConf.PriceFormat);
417
544
 
418
545
  // for convenience automatically add the object parameter style:'currency' if not given
419
546
  if (typeof opts['style'] !== 'string' && typeof opts['currency'] === 'string')
@@ -425,9 +552,23 @@
425
552
  );
426
553
 
427
554
  return handlebars;
428
- }
429
- }
430
- });
431
-
432
-
555
+ },
433
556
 
557
+ /**
558
+ * we conditionally export the helpers to be able to test against them;
559
+ * in production they are not exported.
560
+ */
561
+ ...(isTest) && {
562
+ private: {
563
+ applyToConstructor: __applyToConstructor,
564
+ configLookup: __configLookup,
565
+ validateArgs: __validateArgs,
566
+ setArgs: __setArgs,
567
+ isNumOrString: __isNumOrString,
568
+ createDateObj: __createDateObj,
569
+ getRelDateFormatPolyfill: __getRelDateFormatPolyfill,
570
+ getDateDiff: __getDateDiff
571
+ }
572
+ },
573
+ }
574
+ });
@@ -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;if(!f(e)&&!f(t))return null;if(f(t))if(f(e)){a=g(e)-g(t)}else a=g(t);else a=g(e);const o=l(n,r.language,i.RelativeTimeFormat),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}}}}));