handlebars-i18n 1.8.3 → 1.10.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.
- package/dist/handlebars-i18n.js +30 -8
- package/dist/handlebars-i18n.min.js +1 -1
- package/examples/browser-example/index.html +25 -4
- package/package.json +2 -2
- package/readme.md +153 -108
- package/test/handlebars-i18n.test.js +68 -3
package/dist/handlebars-i18n.js
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* handlebars-i18n.js
|
|
3
3
|
*
|
|
4
4
|
* @author: Florian Walzel
|
|
5
|
-
* @date: 2024-08
|
|
6
5
|
*
|
|
7
6
|
* handlebars-i18n adds features for localization/
|
|
8
7
|
* internationalization to handlebars.js
|
|
9
8
|
*
|
|
10
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-25 Florian Walzel, MIT License
|
|
11
10
|
*
|
|
12
11
|
*********************************************************************/
|
|
13
12
|
|
|
@@ -381,12 +380,34 @@
|
|
|
381
380
|
* use like: {{__ "key_name"}}
|
|
382
381
|
* or with attributes: {{__ "key_with_count" count=7}}
|
|
383
382
|
*
|
|
384
|
-
* @param
|
|
385
|
-
* @param
|
|
383
|
+
* @param key
|
|
384
|
+
* @param options
|
|
386
385
|
* @returns {*}
|
|
387
386
|
*/
|
|
388
|
-
function (
|
|
389
|
-
|
|
387
|
+
function (key, options) {
|
|
388
|
+
const hash = options.hash || {};
|
|
389
|
+
|
|
390
|
+
// Force object/array return if needed
|
|
391
|
+
const result = (typeof i18next !== "undefined")
|
|
392
|
+
? i18next.t(key, {...hash, returnObjects: true})
|
|
393
|
+
: key;
|
|
394
|
+
|
|
395
|
+
if (typeof result === "string") {
|
|
396
|
+
return new handlebars.SafeString(result);
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
handlebars.registerHelper('keyExists',
|
|
402
|
+
/**
|
|
403
|
+
* checks if a translation key exists
|
|
404
|
+
* use like: {{#if (keyExists "myKey")}} {{__ "myKey"}} {{/if}}
|
|
405
|
+
*
|
|
406
|
+
* @param {string} key - The translation key to check
|
|
407
|
+
* @returns {boolean}
|
|
408
|
+
*/
|
|
409
|
+
function (key) {
|
|
410
|
+
return i18next.exists(key);
|
|
390
411
|
}
|
|
391
412
|
);
|
|
392
413
|
handlebars.registerHelper('_locale',
|
|
@@ -502,7 +523,7 @@
|
|
|
502
523
|
break;
|
|
503
524
|
default:
|
|
504
525
|
throw new Error('@ handlebars-i18n: invalid argument "unit" was given for _dateAdd.' +
|
|
505
|
-
'Unit must be
|
|
526
|
+
'Unit must be one of "second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year".');
|
|
506
527
|
}
|
|
507
528
|
|
|
508
529
|
const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
|
|
@@ -534,6 +555,7 @@
|
|
|
534
555
|
);
|
|
535
556
|
handlebars.registerHelper('_dateDiff',
|
|
536
557
|
/**
|
|
558
|
+
* Outputs the relative time difference between two given dates in the requested unit.
|
|
537
559
|
*
|
|
538
560
|
* @param dateInputA
|
|
539
561
|
* @param dateInputB
|
|
@@ -629,4 +651,4 @@
|
|
|
629
651
|
}
|
|
630
652
|
},
|
|
631
653
|
}
|
|
632
|
-
});
|
|
654
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){if("object"==typeof exports&&"object"==typeof module){const e=require("handlebars"),r=require("i18next"),n=require("intl"),a=require("relative-time-format");module.exports=t(e,r,n,a,"TEST"===process?.env?.NODE_ENV)}else if("function"==typeof define&&define.amd)define(["Handlebars","i18next","Intl"],t);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=t(e.Handlebars,e.i18next,e.Intl)}}(this,(function(e,t,r,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 s={};function u(e,t){let r=[null].concat(t);return new(e.bind.apply(e,r))}function l(e,t,r){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!==r.custom[n.format]&&void 0!==r.custom[n.format][t])return r.custom[n.format][t]}return void 0!==r.standard[t]?r.standard[t]:void 0!==r.standard.all?r.standard.all:{}}function c(e,t,r,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(t)?"object"!=typeof r?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+r+"> 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 <"+t+'>. Second argument must be a string with the options key. Use either "DateTimeFormat", "RelativeTimeFormat", "NumberFormat", or "PriceFormat".'),!1)}function m(e,t,r,n){return null!=n?(void 0===i[t].custom[n]&&(i[t].custom[n]={}),i[t].custom[n][e]=r):i[t].standard[e]=r,!0}function g(e){return"number"==typeof e||"string"==typeof e&&""!==e}function
|
|
1
|
+
!function(e,t){if("object"==typeof exports&&"object"==typeof module){const e=require("handlebars"),r=require("i18next"),n=require("intl"),a=require("relative-time-format");module.exports=t(e,r,n,a,"TEST"===process?.env?.NODE_ENV)}else if("function"==typeof define&&define.amd)define(["Handlebars","i18next","Intl"],t);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=t(e.Handlebars,e.i18next,e.Intl)}}(this,(function(e,t,r,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 s={};function u(e,t){let r=[null].concat(t);return new(e.bind.apply(e,r))}function l(e,t,r){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!==r.custom[n.format]&&void 0!==r.custom[n.format][t])return r.custom[n.format][t]}return void 0!==r.standard[t]?r.standard[t]:void 0!==r.standard.all?r.standard.all:{}}function c(e,t,r,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(t)?"object"!=typeof r?(console.error("@ handlebars-i18n.configure(): Invalid argument <"+r+"> 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 <"+t+'>. Second argument must be a string with the options key. Use either "DateTimeFormat", "RelativeTimeFormat", "NumberFormat", or "PriceFormat".'),!1)}function m(e,t,r,n){return null!=n?(void 0===i[t].custom[n]&&(i[t].custom[n]={}),i[t].custom[n][e]=r):i[t].standard[e]=r,!0}function g(e){return"number"==typeof e||"string"==typeof e&&""!==e}function f(e){let t;if("number"==typeof e)t=new Date(e);else if("string"==typeof e)if("["===e.charAt(0)&&"]"===e.slice(-1)){let r=(e=e.substring(1,e.length-1).replace(/ /g,"")).split(",");t=u.bind(null,Date)(r)}else t="now"===e.toLowerCase()||"today"===e.toLowerCase()?new Date:new Date(e);else t=new Date;return t}function d(e,t){if("function"==typeof r.RelativeTimeFormat)return new r.RelativeTimeFormat(e,t);if(void 0===s[e])try{s[e]=require(`relative-time-format/locale/${e}`)}catch(e){console.error(e)}return n.addLocale(s[e]),new n(e,t)}function b(e,t){return t=t||"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}[t])}return{configure:function(e,t,r,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,t,r,n))return!1;m(e,t,r,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?t=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(r,n){const a=n.hash||{},o=void 0!==t?t.t(r,{...a,returnObjects:!0}):r;return"string"==typeof o?new e.SafeString(o):o})),e.registerHelper("keyExists",(function(e){return t.exists(e)})),e.registerHelper("_locale",(function(){return t.language})),e.registerHelper("localeIs",(function(e){return t.language===e})),e.registerHelper("_date",(function(e,n){const a=f(e),o=l(n,t.language,i.DateTimeFormat);return new r.DateTimeFormat(t.language,o).format(a)})),e.registerHelper("_dateAdd",(function(e,n,a){if("number"!=typeof e&&"string"!=typeof e)throw new Error('@ handlebars-i18n: invalid first argument "dateInput" was given for _dateAdd.');if("number"!=typeof n)throw new Error('@ handlebars-i18n: invalid second argument "offset" was given for _dateAdd.');const o=f(e),s=l(a,t.language,i.DateTimeFormat);switch(s.unit=s.unit||"hour",s.unit){case"second":case"seconds":o.setSeconds(o.getSeconds()+n);break;case"minute":case"minutes":o.setMinutes(o.getMinutes()+n);break;case"hour":case"hours":o.setHours(o.getHours()+n);break;case"day":case"days":o.setDate(o.getDate()+n);break;case"week":case"weeks":o.setDate(o.getDate()+7*n);break;case"month":case"months":o.setMonth(o.getMonth()+n);break;case"quarter":case"quarters":o.setMonth(o.getMonth()+3*n);break;case"year":case"years":o.setFullYear(o.getFullYear()+n);break;default:throw new Error('@ handlebars-i18n: invalid argument "unit" was given for _dateAdd.Unit must be one of "second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year".')}return new r.DateTimeFormat(t.language,s).format(o)})),e.registerHelper("_dateRel",(function(e,r){const n=parseInt(e),a=l(r,t.language,i.RelativeTimeFormat);return d(t.language,a).format(n,a.unit)})),e.registerHelper("_dateDiff",(function(e,r,n){let a,o=l(n,t.language,i.RelativeTimeFormat);if(!g(e))return console.error("@ handlebars-i18n: invalid first argument dateInputA was given for _dateDiff."),null;r=r||"now",a=f(e)-f(r);const s=b(a,o.unit);return d(t.language,o).format(s,o.unit)})),e.registerHelper("_num",(function(e,n){let a=l(n,t.language,i.NumberFormat);return new r.NumberFormat(t.language,a).format(e)})),e.registerHelper("_price",(function(e,n){let a=l(n,t.language,i.PriceFormat);"string"!=typeof a.style&&"string"==typeof a.currency&&(a.style="currency");return new r.NumberFormat(t.language,a).format(e)})),e},...a&&{private:{applyToConstructor:u,configLookup:l,validateArgs:c,setArgs:m,isNumOrString:g,createDateObj:f,getRelDateFormatPolyfill:d,getDateDiff:b}}}}));
|
|
@@ -53,7 +53,12 @@
|
|
|
53
53
|
'key2': '{{what}} is good.',
|
|
54
54
|
'key3WithCount': '{{count}} item',
|
|
55
55
|
'key3WithCount_plural': '{{count}} items',
|
|
56
|
-
'key4': 'Selected Language is:'
|
|
56
|
+
'key4': 'Selected Language is:',
|
|
57
|
+
'fruits': ['Apple', 'Banana', 'Cherry'],
|
|
58
|
+
'steps': [
|
|
59
|
+
{ 'title': 'Step 1', 'text': 'Open the app' },
|
|
60
|
+
{ 'title': 'Step 2', text: 'Click start' }
|
|
61
|
+
]
|
|
57
62
|
}
|
|
58
63
|
},
|
|
59
64
|
'de' : {
|
|
@@ -63,12 +68,18 @@
|
|
|
63
68
|
'key2': '{{what}} ist gut.',
|
|
64
69
|
'key3WithCount': '{{count}} Gegenstand',
|
|
65
70
|
'key3WithCount_plural': '{{count}} Gegenstände',
|
|
66
|
-
'key4': 'Die ausgewählte Sprache ist:'
|
|
71
|
+
'key4': 'Die ausgewählte Sprache ist:',
|
|
72
|
+
'fruits': ['Apfel', 'Banane', 'Kirsche'],
|
|
73
|
+
'steps': [
|
|
74
|
+
{ 'title': 'Schritt 1', 'text': 'App öffnen' },
|
|
75
|
+
{ 'title': 'Schritt 2', 'text': 'Klicke auf Start' }
|
|
76
|
+
]
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
79
|
},
|
|
70
80
|
lng : 'en',
|
|
71
|
-
compatibilityJSON: 'v2'
|
|
81
|
+
compatibilityJSON: 'v2', // important: defines the JSON standard for variable replacement
|
|
82
|
+
returnObjects: true // set to true if you want to loop over an array or objects of properties
|
|
72
83
|
});
|
|
73
84
|
|
|
74
85
|
// -- Handlebars' example data object
|
|
@@ -130,6 +141,16 @@
|
|
|
130
141
|
<code>{{{{raw}}}} {{__ "key1" lng="de"}} {{{{/raw}}}}</code>
|
|
131
142
|
<p>→ {{__ "key1" lng="de"}}</p>
|
|
132
143
|
|
|
144
|
+
<h4>Loop over Array of Translations:</h4>
|
|
145
|
+
<code>
|
|
146
|
+
{{{{raw}}}} {{#each (__ "fruits")}} {{this}} {{/each}} {{{{/raw}}}}
|
|
147
|
+
</code>
|
|
148
|
+
<p><ul>
|
|
149
|
+
{{#each (__ "fruits")}}
|
|
150
|
+
<li>{{this}}</li>
|
|
151
|
+
{{/each}}
|
|
152
|
+
</ul></p>
|
|
153
|
+
|
|
133
154
|
<h3>Output selected language</h3>
|
|
134
155
|
|
|
135
156
|
<button onclick="changeLang()">{{__ "key0"}} {{#if (localeIs "en")}}German {{else}}Englisch {{/if}}</button>
|
|
@@ -267,4 +288,4 @@
|
|
|
267
288
|
|
|
268
289
|
</script>
|
|
269
290
|
</body>
|
|
270
|
-
</html>
|
|
291
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "handlebars-i18n",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "handlebars-i18n adds internationalization to handlebars.js using i18next and Intl.",
|
|
5
5
|
"main": "dist/handlebars-i18n.js",
|
|
6
6
|
"scripts": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@commitlint/cli": "17.6.1",
|
|
46
46
|
"@commitlint/config-conventional": "16.2.1",
|
|
47
47
|
"@types/intl": "^1.2.2",
|
|
48
|
-
"@types/node": "^24.
|
|
48
|
+
"@types/node": "^24.7.0",
|
|
49
49
|
"c8": "^10.1.3",
|
|
50
50
|
"chai": "4.3.6",
|
|
51
51
|
"coveralls-next": "^4.2.1",
|
package/readme.md
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
# handlebars-i18n
|
|
2
2
|
|
|
3
|
-
`handlebars-i18n` adds the
|
|
3
|
+
*What it is about:* `handlebars-i18n` adds the translation features of [i18next](https://www.i18next.com/)
|
|
4
4
|
to [handlebars.js](https://handlebarsjs.com/). It also provides **date**, **number**, and **currency formatting**
|
|
5
|
-
via [Intl](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl). Use as node module or in
|
|
6
|
-
the web browser. Supports Typescript.
|
|
7
|
-
|
|
8
|
-
Handlebars-i18n is listed amongst i18next’s [framework helpers](https://www.i18next.com/overview/supported-frameworks).
|
|
5
|
+
via [Intl](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl). Use as node module or in the web browser. `handlebars-i18n` is listed amongst i18next’s official [framework helpers](https://www.i18next.com/overview/supported-frameworks).
|
|
9
6
|
|
|
10
7
|
[](https://opensource.org/licenses/MIT)
|
|
11
8
|

|
|
@@ -17,16 +14,22 @@ Handlebars-i18n is listed amongst i18next’s [framework helpers](https://www.i1
|
|
|
17
14
|

|
|
18
15
|

|
|
19
16
|
|
|
20
|
-
## License
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
## Key Features & Advantages
|
|
19
|
+
|
|
20
|
+
- handlebars-i18n comes lightweight, well tested, and with detailed [examples](#detailed-examples)
|
|
21
|
+
- allows granular custom [presets](#generic-language-format-settings) per language
|
|
22
|
+
- supports Typescript
|
|
23
|
+
- has an optional [CLI](#additional-cli-helper-for-handlebars-i18n-available) for automatic Translations via DeepL
|
|
24
|
+
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
## Please Support
|
|
25
27
|
|
|
26
|
-
If you use handlebars-i18n
|
|
28
|
+
`handlebars-i18n` is free but not free of value. If you make serious use of handlebars-i18n, I’d be delighted if you
|
|
27
29
|
|
|
28
30
|
[](https://www.buymeacoffee.com/fwalzel)
|
|
29
31
|
|
|
32
|
+
|
|
30
33
|
## Install
|
|
31
34
|
|
|
32
35
|
```sh
|
|
@@ -35,17 +38,17 @@ npm i handlebars-i18n
|
|
|
35
38
|
|
|
36
39
|
## Import
|
|
37
40
|
|
|
38
|
-
Import
|
|
41
|
+
Import with ES6 syntax:
|
|
39
42
|
|
|
40
|
-
```
|
|
41
|
-
|
|
43
|
+
```typescript
|
|
44
|
+
import HandlebarsI18n from "handlebars-i18n";
|
|
42
45
|
HandlebarsI18n.init();
|
|
43
46
|
```
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
As commonJS within node environment:
|
|
46
49
|
|
|
47
|
-
```
|
|
48
|
-
|
|
50
|
+
```javascript
|
|
51
|
+
const HandlebarsI18n = require("handlebars-i18n");
|
|
49
52
|
HandlebarsI18n.init();
|
|
50
53
|
```
|
|
51
54
|
|
|
@@ -61,12 +64,12 @@ Usage in web browser (old school):
|
|
|
61
64
|
</script>
|
|
62
65
|
```
|
|
63
66
|
|
|
64
|
-
Via
|
|
67
|
+
Via CDN:
|
|
65
68
|
```javascript
|
|
66
|
-
<script src="https://cdn.jsdelivr.net/npm/handlebars-i18n@1.
|
|
69
|
+
<script src="https://cdn.jsdelivr.net/npm/handlebars-i18n@1.10.0/dist/handlebars-i18n.min.js"></script>
|
|
67
70
|
```
|
|
68
71
|
|
|
69
|
-
## Quick
|
|
72
|
+
## Quick Example
|
|
70
73
|
|
|
71
74
|
Initialize i18next with your language strings and default settings:
|
|
72
75
|
|
|
@@ -88,7 +91,8 @@ i18next.init({
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
},
|
|
91
|
-
lng: "en"
|
|
94
|
+
lng: "en",
|
|
95
|
+
compatibilityJSON: 'v2'
|
|
92
96
|
});
|
|
93
97
|
```
|
|
94
98
|
|
|
@@ -100,7 +104,6 @@ let data = {
|
|
|
100
104
|
myPrice: 1200.99,
|
|
101
105
|
myDate: "2020-03-11T03:24:00"
|
|
102
106
|
}
|
|
103
|
-
|
|
104
107
|
```
|
|
105
108
|
|
|
106
109
|
Initialize handlebars-i18n:
|
|
@@ -120,66 +123,66 @@ HandlebarsI18n.configure([
|
|
|
120
123
|
|
|
121
124
|
Finally use in template:
|
|
122
125
|
|
|
123
|
-
```
|
|
126
|
+
```hbs
|
|
124
127
|
<p> {{__ "phrase1"}} </p>
|
|
125
128
|
```
|
|
126
129
|
|
|
127
|
-
*
|
|
130
|
+
* output: en → **What is good?** | de → **Was ist gut?**
|
|
128
131
|
|
|
129
|
-
```
|
|
132
|
+
```hbs
|
|
130
133
|
<p> {{__ "phrase2" thing=myItem}} </p>
|
|
131
134
|
```
|
|
132
135
|
|
|
133
|
-
*
|
|
136
|
+
* output: en → **handlebars-i18n is good.** | de → **handlebars-i18n ist gut.**
|
|
134
137
|
|
|
135
|
-
```
|
|
138
|
+
```hbs
|
|
136
139
|
<p> {{_date myDate}} </p>
|
|
137
140
|
```
|
|
138
141
|
|
|
139
|
-
*
|
|
142
|
+
* output: en → **March 11, 2020 at 3:24 AM** | de → **11.3.2020, 03:24**
|
|
140
143
|
|
|
141
|
-
```
|
|
144
|
+
```hbs
|
|
142
145
|
<p> {{_price myPrice}} </p>
|
|
143
146
|
```
|
|
144
147
|
|
|
145
|
-
*
|
|
148
|
+
* output: en → **\$1,200.99** | de → **1.200,99 $**
|
|
146
149
|
|
|
147
|
-
## Detailed
|
|
150
|
+
## Detailed Examples
|
|
148
151
|
|
|
149
152
|
:point_right: See the *examples folder* in the repo for more use cases and details.
|
|
150
153
|
|
|
151
|
-
- Open `examples/browser-example/index.html` in your
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
+
- Open `examples/browser-example/index.html` in your web browser to see an implementation with a simple UI.
|
|
155
|
+
- Prompt `npm run example:js` from the root of this repo to log a very basic node example to console.
|
|
156
|
+
- Prompt `npm run example:ts` to compile and log a typescript example.
|
|
154
157
|
|
|
155
|
-
## Additional CLI Helper for Handlebars-i18n available
|
|
158
|
+
## Additional CLI Helper for Handlebars-i18n available
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
:metal: `handlebars-i18n` has its own command line
|
|
158
161
|
interface [handlebars-i18n-cli](https://www.npmjs.com/package/handlebars-i18n-cli).
|
|
159
162
|
|
|
160
163
|
```sh
|
|
161
164
|
npm i handlebars-i18n-cli --save-dev
|
|
162
165
|
```
|
|
163
166
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
* programmatically extract/ update translation strings from handlebars templates and generate i18next conform
|
|
168
|
+
JSON files from it
|
|
169
|
+
* automatic translation of i18next JSON via [DeepL’s](https://www.deepl.com/en/pro-api/) free API
|
|
167
170
|
|
|
168
|
-
##
|
|
171
|
+
## Template Functions
|
|
169
172
|
|
|
170
|
-
### __
|
|
173
|
+
### `__`
|
|
171
174
|
|
|
172
|
-
Returns the phrase associated with the given key for the selected language. __ will take all options
|
|
175
|
+
Returns the phrase associated with the given key for the selected language. `__` will take all options
|
|
173
176
|
i18next’s [t-function](https://www.i18next.com/overview/api#t) would take.
|
|
174
|
-
The
|
|
177
|
+
The key can be passed hard encoded in the template when written in quotes:
|
|
175
178
|
|
|
176
|
-
```
|
|
179
|
+
```hbs
|
|
177
180
|
{{__ "keyToTranslationPhrase"}}
|
|
178
181
|
```
|
|
179
182
|
|
|
180
183
|
… or it can be referenced via a handlebars variable:
|
|
181
184
|
|
|
182
|
-
```
|
|
185
|
+
```hbs
|
|
183
186
|
{{__ keyFromHandlebarsData}}
|
|
184
187
|
```
|
|
185
188
|
|
|
@@ -187,7 +190,7 @@ The primary key can be passed hard encoded in the template when written in quote
|
|
|
187
190
|
|
|
188
191
|
Template usage:
|
|
189
192
|
|
|
190
|
-
```
|
|
193
|
+
```hbs
|
|
191
194
|
{{__ "whatIsWhat" a="Everything" b="fine"}}
|
|
192
195
|
```
|
|
193
196
|
|
|
@@ -201,9 +204,11 @@ The i18next resource:
|
|
|
201
204
|
}
|
|
202
205
|
```
|
|
203
206
|
|
|
207
|
+
* output: en → **Everything is fine.**
|
|
208
|
+
|
|
204
209
|
**Plurals**
|
|
205
210
|
|
|
206
|
-
```
|
|
211
|
+
```hbs
|
|
207
212
|
{{__ "keyWithCount" count=8}}
|
|
208
213
|
```
|
|
209
214
|
|
|
@@ -213,194 +218,226 @@ The i18next resource:
|
|
|
213
218
|
"keyWithCount" : "{{count}} item",
|
|
214
219
|
"keyWithCount_plural" : "{{count}} items"
|
|
215
220
|
}
|
|
216
|
-
},
|
|
221
|
+
}, ...
|
|
217
222
|
```
|
|
218
223
|
|
|
219
|
-
**Override globally selected language**
|
|
224
|
+
**Override the globally selected language**
|
|
220
225
|
|
|
221
|
-
```
|
|
226
|
+
```hbs
|
|
222
227
|
{{__ "key1" lng="de"}}
|
|
223
228
|
```
|
|
224
229
|
|
|
225
|
-
Will output the contents for
|
|
230
|
+
Will output the contents for `de` even though a different language is globally set.
|
|
231
|
+
|
|
232
|
+
**Looping over an array (or object) of translations**
|
|
233
|
+
|
|
234
|
+
```hbs
|
|
235
|
+
<ul>
|
|
236
|
+
{{#each (__ "fruits")}}
|
|
237
|
+
<li>{{this}}</li>
|
|
238
|
+
{{/each}}
|
|
239
|
+
</ul>
|
|
240
|
+
```
|
|
241
|
+
In this case the key `fruits` would contain an array of translation strings, like:
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
{
|
|
245
|
+
en: {
|
|
246
|
+
translation: {
|
|
247
|
+
fruits: ["Apple", "Banana", "Cherry"]
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
returnObjects: true
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
It is recommended to set `returnObjects` actively to `true` in the `i18next.init` object if you want to loop over
|
|
255
|
+
an array or objects of properties.
|
|
226
256
|
|
|
227
257
|
---
|
|
228
258
|
|
|
229
|
-
###
|
|
259
|
+
### `keyExists`
|
|
230
260
|
|
|
231
|
-
|
|
261
|
+
Checks if a i18next translation key exists. Returns `true` or `false`.
|
|
232
262
|
|
|
263
|
+
```hbs
|
|
264
|
+
{{#if (keyExists "myKey")}} {{__ "myKey"}} {{/if}}
|
|
233
265
|
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### `_locale`
|
|
270
|
+
|
|
271
|
+
Returns the shortcode of i18next’s currently selected language such as `en`, `de`, `ja` … etc.
|
|
272
|
+
|
|
273
|
+
```hbs
|
|
234
274
|
{{_locale}}
|
|
235
275
|
```
|
|
236
276
|
|
|
237
277
|
---
|
|
238
278
|
|
|
239
|
-
### localeIs
|
|
279
|
+
### `localeIs`
|
|
240
280
|
|
|
241
|
-
Checks a string against i18next’s currently selected language. Returns
|
|
281
|
+
Checks a string against i18next’s currently selected language. Returns `true` or `false`.
|
|
242
282
|
|
|
243
|
-
```
|
|
283
|
+
```hbs
|
|
244
284
|
{{#if (localeIs "en")}} ... {{/if}}
|
|
245
285
|
```
|
|
246
286
|
|
|
247
287
|
---
|
|
248
288
|
|
|
249
|
-
### _date
|
|
289
|
+
### `_date`
|
|
250
290
|
|
|
251
291
|
Outputs a formatted date according to the language specific conventions.
|
|
252
292
|
|
|
253
|
-
```
|
|
293
|
+
```hbs
|
|
254
294
|
{{_date}}
|
|
255
295
|
```
|
|
256
296
|
|
|
257
297
|
If called without argument the current date is returned. Any other input date can be passed as a conventional date
|
|
258
|
-
string, a number (timestamp in milliseconds), or a date array. _date accepts all arguments
|
|
298
|
+
string, a number (timestamp in milliseconds), or a date array. `_date` accepts all arguments
|
|
259
299
|
Javascript’s [new Date()](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Date)
|
|
260
300
|
constructor would accept.
|
|
261
301
|
|
|
262
302
|
**Date argument given as date string:**
|
|
263
303
|
|
|
264
|
-
```
|
|
304
|
+
```hbs
|
|
265
305
|
{{_date "2020-03-11T03:24:00"}}
|
|
266
306
|
```
|
|
267
307
|
|
|
268
308
|
or
|
|
269
309
|
|
|
270
|
-
```
|
|
310
|
+
```hbs
|
|
271
311
|
{{_date "December 17, 1995 03:24:00"}}
|
|
272
312
|
```
|
|
273
313
|
|
|
274
314
|
**Date argument given as number (milliseconds since begin of unix epoch):**
|
|
275
315
|
|
|
276
|
-
```
|
|
316
|
+
```hbs
|
|
277
317
|
{{_date 1583922952743}}
|
|
278
318
|
```
|
|
279
319
|
|
|
280
320
|
**Date argument given as javascript date array** [year, monthIndex [, day [, hour [, minutes [,
|
|
281
321
|
seconds [, milliseconds]]]]]]:
|
|
282
322
|
|
|
283
|
-
```
|
|
323
|
+
```hbs
|
|
284
324
|
{{_date "[2012, 11, 20, 3, 0, 0]"}}
|
|
285
325
|
```
|
|
286
326
|
|
|
287
|
-
**Additional arguments for formatting
|
|
327
|
+
**Additional arguments for formatting:**
|
|
288
328
|
|
|
289
329
|
You can add multiple arguments for individual formatting.
|
|
290
330
|
See [Intl DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
|
|
291
331
|
for your option arguments. Alternatively check this repo’s TS types
|
|
292
332
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
293
333
|
|
|
294
|
-
```
|
|
334
|
+
```hbs
|
|
295
335
|
{{_date 1583922952743 year="2-digit" day="2-digit" timeZone="America/Los_Angeles"}}
|
|
296
336
|
```
|
|
297
337
|
|
|
298
338
|
---
|
|
299
339
|
|
|
300
|
-
### _dateAdd
|
|
340
|
+
### `_dateAdd`
|
|
301
341
|
|
|
302
342
|
Adds a time offset in a given unit to a date, returns the modified date.
|
|
303
343
|
|
|
304
|
-
```
|
|
344
|
+
```hbs
|
|
305
345
|
{{_dateAdd "1996-12-17" 24 unit="hour"}}
|
|
306
346
|
```
|
|
307
347
|
|
|
308
|
-
|
|
348
|
+
* output: en → **12/18/1996**
|
|
309
349
|
|
|
310
|
-
The first argument is a date (see function
|
|
350
|
+
The first argument is a date (see function `_date` for valid date inputs). The second argument is a time amount given
|
|
311
351
|
as number. The option **unit** specifies the time amount. Possible units
|
|
312
352
|
are `"second"` | `"minute"` | `"hour"` | `"day"` | `"week"` | `"month"` | `"quarter"` |`"year"` (default is `"hour"`).
|
|
313
|
-
Further options as for function
|
|
353
|
+
Further options as for function `_date` can be applied.
|
|
314
354
|
|
|
315
355
|
---
|
|
316
356
|
|
|
317
|
-
### _dateDiff
|
|
357
|
+
### `_dateDiff`
|
|
318
358
|
|
|
319
|
-
Outputs the relative time difference between two given dates.
|
|
359
|
+
Outputs the relative time difference between two given dates in the requested unit.
|
|
320
360
|
|
|
361
|
+
```hbs
|
|
362
|
+
{{_dateDiff "2000-12-17" "2001-12-17" unit="year"}}
|
|
321
363
|
```
|
|
322
|
-
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Will output for "en" → **in 1 year**
|
|
364
|
+
* output: en → **in 1 year**
|
|
326
365
|
|
|
327
366
|
The second date argument is subtracted from the first. If the difference is a positive value, a future event statement
|
|
328
|
-
is made. A negative value refers to a past date. (If no second argument is given, the default date is the present moment).
|
|
329
|
-
Allowed date input formats are similar to
|
|
367
|
+
is made. A negative value refers to a past date. (If no second argument is given, the default date is the present moment).
|
|
368
|
+
Allowed date input formats are similar to `_date`, options equal `_dateRel`. Default unit is `"hour"`.
|
|
330
369
|
|
|
331
370
|
---
|
|
332
371
|
|
|
333
|
-
### _dateRel
|
|
372
|
+
### `_dateRel`
|
|
334
373
|
|
|
335
374
|
Outputs a string with a relative date statement, formatted according to the language specific conventions.
|
|
336
375
|
|
|
337
|
-
```
|
|
376
|
+
```hbs
|
|
338
377
|
{{_dateRel 7 unit="hour"}}
|
|
339
378
|
```
|
|
340
379
|
|
|
341
|
-
|
|
380
|
+
* output: en → **in 7 hours**
|
|
342
381
|
|
|
343
|
-
```
|
|
382
|
+
```hbs
|
|
344
383
|
{{_dateRel -7 unit="hour"}}
|
|
345
384
|
```
|
|
346
385
|
|
|
347
|
-
|
|
386
|
+
* output: en → **7 hours ago**
|
|
348
387
|
|
|
349
388
|
A positive number argument leads to a future event statement, a negative refers to a past date. Possible units
|
|
350
|
-
are `"second"` | `"minute"` | `"hour"` | `"day"` | `"week"` | `"month"` | `"quarter"` |`"year"` (default is `"hour"`).
|
|
389
|
+
are `"second"` | `"minute"` | `"hour"` | `"day"` | `"week"` | `"month"` | `"quarter"` |`"year"` (default is `"hour"`).
|
|
351
390
|
For a complete set of options (such as `numberingSystem` or `localeMatcher`)
|
|
352
391
|
see [Intl.RelativeTimeFormat Constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat).
|
|
353
392
|
Alternatively check this repo’s TS types in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
354
393
|
|
|
355
394
|
---
|
|
356
395
|
|
|
357
|
-
### _num
|
|
396
|
+
### `_num`
|
|
358
397
|
|
|
359
|
-
Outputs a formatted number according to the language specific conventions of number representation
|
|
360
|
-
**4,100,000.8314** for "**en**", but **4.100.000,8314** for "**de**".
|
|
398
|
+
Outputs a formatted number according to the language specific conventions of number representation.
|
|
361
399
|
|
|
362
|
-
```
|
|
363
|
-
{{_num
|
|
400
|
+
```hbs
|
|
401
|
+
{{_num 3.14159}}
|
|
364
402
|
```
|
|
365
403
|
|
|
366
|
-
**Additional arguments for formatting
|
|
404
|
+
**Additional arguments for formatting:**
|
|
367
405
|
|
|
368
406
|
You can add multiple arguments for individual formatting.
|
|
369
407
|
See [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)
|
|
370
408
|
for your option arguments. Alternatively check this repo’s TS types
|
|
371
409
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
372
410
|
|
|
373
|
-
```
|
|
411
|
+
```hbs
|
|
374
412
|
{{_num 3.14159 maximumFractionDigits=2}}
|
|
375
413
|
```
|
|
376
414
|
|
|
377
|
-
|
|
415
|
+
* output: en → **3.14** | de → **3,14**
|
|
378
416
|
|
|
379
417
|
---
|
|
380
418
|
|
|
381
|
-
### _price
|
|
419
|
+
### `_price`
|
|
382
420
|
|
|
383
|
-
Outputs a formatted currency string according to the language specific conventions of price representation
|
|
384
|
-
**€9,999.99** for "**en**", but **9.999,99 €** for "**de**".
|
|
421
|
+
Outputs a formatted currency string according to the language specific conventions of price representation.
|
|
385
422
|
|
|
386
|
-
```
|
|
423
|
+
```hbs
|
|
387
424
|
{{_price 9999.99}}
|
|
388
425
|
```
|
|
389
426
|
|
|
390
|
-
**Additional arguments for formatting
|
|
427
|
+
**Additional arguments for formatting:**
|
|
391
428
|
|
|
392
429
|
You can add multiple arguments for individual currency formatting.
|
|
393
430
|
See [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)
|
|
394
431
|
for your option arguments. Alternatively check this repo’s TS types
|
|
395
432
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
396
433
|
|
|
397
|
-
```
|
|
434
|
+
```hbs
|
|
398
435
|
{{_price 1000 currency="JPY" minimumFractionDigits=2}}
|
|
399
436
|
```
|
|
400
437
|
|
|
401
438
|
---
|
|
402
439
|
|
|
403
|
-
## How to use HandlebarsI18n.configure method
|
|
440
|
+
## How to use `HandlebarsI18n.configure` method
|
|
404
441
|
|
|
405
442
|
### Generic language format settings
|
|
406
443
|
|
|
@@ -411,11 +448,11 @@ all languages or only for specific languages.
|
|
|
411
448
|
HandlebarsI18n.configure("all", "DateTimeFormat", {timeZone: "America/Los_Angeles"});
|
|
412
449
|
```
|
|
413
450
|
|
|
414
|
-
First argument is the language shortcode or "
|
|
451
|
+
First argument is the language shortcode or `"all"` for all languages. Second is the format option you want to
|
|
415
452
|
address (`DateTimeFormat`, `RelativeTimeFormat`, `NumberFormat`, or `PriceFormat`). Third argument is the options object
|
|
416
453
|
with the specific settings.
|
|
417
454
|
|
|
418
|
-
Examples for generic settings
|
|
455
|
+
**Examples for generic settings:**
|
|
419
456
|
|
|
420
457
|
```javascript
|
|
421
458
|
HandlebarsI18n.configure("all", "RelativeTimeFormat", {style: "long", unit: "second"});
|
|
@@ -433,9 +470,9 @@ HandlebarsI18n.configure("all", "PriceFormat", {currency: "HKD", currencyDisplay
|
|
|
433
470
|
|
|
434
471
|
You can also define specific subsets to be used in the template, i.e. if you want the date in different formats such as:
|
|
435
472
|
|
|
436
|
-
- **2020** (year-only)
|
|
437
|
-
- **11.3.2020** (standard-date)
|
|
438
|
-
- **7:24:02** (time-only)
|
|
473
|
+
- **2020** (`year-only`)
|
|
474
|
+
- **11.3.2020** (`standard-date`)
|
|
475
|
+
- **7:24:02** (`time-only`)
|
|
439
476
|
|
|
440
477
|
To do this, define a 4th parameter with a custom name:
|
|
441
478
|
|
|
@@ -447,9 +484,9 @@ HandlebarsI18n.configure([
|
|
|
447
484
|
]);
|
|
448
485
|
```
|
|
449
486
|
|
|
450
|
-
Call a subset in template with the parameter format="custom-name"
|
|
487
|
+
Call a subset in template with the parameter `format="custom-name"`, like:
|
|
451
488
|
|
|
452
|
-
```
|
|
489
|
+
```hbs
|
|
453
490
|
{{_date myDate format="year-only"}}
|
|
454
491
|
```
|
|
455
492
|
|
|
@@ -502,14 +539,14 @@ it instead of the generic Handlebars object like so:
|
|
|
502
539
|
```javascript
|
|
503
540
|
const HandlebarsModified = require("handlebars");
|
|
504
541
|
HandlebarsModified.registerHelper("foo", function () {
|
|
505
|
-
return "
|
|
542
|
+
return "bar"
|
|
506
543
|
});
|
|
507
544
|
HandlebarsI18n.init(HandlebarsModified);
|
|
508
545
|
```
|
|
509
546
|
|
|
510
|
-
HandlebarsI18n will have your previously defined method
|
|
547
|
+
HandlebarsI18n will have your previously defined method `foo` by now.
|
|
511
548
|
|
|
512
|
-
The same can be done for a custom instance of i18next. Pass it as the second argument to the init function.
|
|
549
|
+
The same can be done for a custom instance of i18next. Pass it as the second argument to the `init` function.
|
|
513
550
|
|
|
514
551
|
```javascript
|
|
515
552
|
const i18nextCustom = require("i18next");
|
|
@@ -523,12 +560,20 @@ HandlebarsI18n.init(null, i18nextCustom);
|
|
|
523
560
|
npm test
|
|
524
561
|
```
|
|
525
562
|
|
|
563
|
+
## License
|
|
564
|
+
|
|
565
|
+
MIT License, Copyright (c) 2020–25 Florian Walzel
|
|
566
|
+
|
|
567
|
+
## Contributing
|
|
568
|
+
|
|
569
|
+
Contributions are welcome! Please open an issue or submit a pull request on GitHub.
|
|
570
|
+
|
|
526
571
|
## Merci à vous
|
|
527
572
|
|
|
528
573
|
For your contribution, I would like to
|
|
529
574
|
thank [@MickL](https://github.com/MickL), [@dargmuesli](https://github.com/dargmuesli), and [@DiefBell](DiefBell).
|
|
530
575
|
|
|
531
|
-
## Note
|
|
576
|
+
## Note on alternatives
|
|
532
577
|
|
|
533
578
|
There is a *different* package named [handlebars-i18next](https://www.npmjs.com/package/handlebars-i18next)
|
|
534
579
|
by [@jgonggrijp](https://github.com/jgonggrijp) which might also suit your needs. Cheers!
|
|
@@ -19,13 +19,23 @@ describe('handlebars-i18n Tests', function () {
|
|
|
19
19
|
'en': {
|
|
20
20
|
translation: {
|
|
21
21
|
'key1': 'What is good?',
|
|
22
|
-
'key2': '{{what}} is {{adverb}}.'
|
|
22
|
+
'key2': '{{what}} is {{adverb}}.',
|
|
23
|
+
'fruits': ["Apple", "Banana", "Cherry"],
|
|
24
|
+
'steps': [
|
|
25
|
+
{ 'title': 'Step 1', 'text': 'Open the app' },
|
|
26
|
+
{ 'title': 'Step 2', text: 'Click start' }
|
|
27
|
+
]
|
|
23
28
|
}
|
|
24
29
|
},
|
|
25
30
|
'de': {
|
|
26
31
|
translation: {
|
|
27
32
|
'key1': 'Was ist gut?',
|
|
28
|
-
'key2': '{{what}} ist {{adverb}}.'
|
|
33
|
+
'key2': '{{what}} ist {{adverb}}.',
|
|
34
|
+
'fruits': ["Apfel", "Banane", "Kirsche"],
|
|
35
|
+
'steps': [
|
|
36
|
+
{ 'title': 'Schritt 1', 'text': 'App öffnen' },
|
|
37
|
+
{ 'title': 'Schritt 2', 'text': 'Klicke auf Start' }
|
|
38
|
+
]
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
},
|
|
@@ -177,6 +187,61 @@ describe('handlebars-i18n Tests', function () {
|
|
|
177
187
|
assert.equal("handlebarsI18next ist gut.", res.string);
|
|
178
188
|
});
|
|
179
189
|
|
|
190
|
+
it("__ should loop over array of strings with #each", () => {
|
|
191
|
+
i18next.changeLanguage('en');
|
|
192
|
+
const template = Handlebars.compile(`
|
|
193
|
+
<ul>
|
|
194
|
+
{{#each (__ "fruits")}}
|
|
195
|
+
<li>{{this}}</li>
|
|
196
|
+
{{/each}}
|
|
197
|
+
</ul>
|
|
198
|
+
`);
|
|
199
|
+
|
|
200
|
+
const output = template({});
|
|
201
|
+
console.log(output);
|
|
202
|
+
expect(output).to.contain("Apple");
|
|
203
|
+
expect(output).to.contain("Banana");
|
|
204
|
+
expect(output).to.contain("Cherry");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("__ should loop over array of objects with #each", () => {
|
|
208
|
+
const template = Handlebars.compile(`
|
|
209
|
+
<ol>
|
|
210
|
+
{{#each (__ "steps")}}
|
|
211
|
+
<li><strong>{{title}}</strong>: {{text}}</li>
|
|
212
|
+
{{/each}}
|
|
213
|
+
</ol>
|
|
214
|
+
`);
|
|
215
|
+
|
|
216
|
+
const output = template({});
|
|
217
|
+
expect(output).to.contain("<strong>Step 1</strong>: Open the app");
|
|
218
|
+
expect(output).to.contain("<strong>Step 2</strong>: Click start");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
/****************************************
|
|
223
|
+
Tests against function keyExists
|
|
224
|
+
****************************************/
|
|
225
|
+
|
|
226
|
+
it('should return true for an existing translation key', function() {
|
|
227
|
+
const exists = hI18n.helpers.keyExists('key1');
|
|
228
|
+
assert.isTrue(exists);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should return false for a non-existing translation key', function() {
|
|
232
|
+
const exists = hI18n.helpers.keyExists('nonExistentKey');
|
|
233
|
+
assert.isFalse(exists);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should return false when called with no parameters', function() {
|
|
237
|
+
const exists = hI18n.helpers.keyExists();
|
|
238
|
+
assert.isFalse(exists);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should return false when called with null or undefined', function() {
|
|
242
|
+
assert.isFalse(hI18n.helpers.keyExists(null));
|
|
243
|
+
assert.isFalse(hI18n.helpers.keyExists(undefined));
|
|
244
|
+
});
|
|
180
245
|
|
|
181
246
|
/****************************************
|
|
182
247
|
Tests against function _date
|
|
@@ -1165,4 +1230,4 @@ describe('handlebars-i18n Private helper Function Tests (in production not expor
|
|
|
1165
1230
|
expect(result).to.deep.equal({});
|
|
1166
1231
|
});
|
|
1167
1232
|
|
|
1168
|
-
});
|
|
1233
|
+
});
|