handlebars-i18n 1.8.3 → 1.9.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 +17 -8
- package/dist/handlebars-i18n.min.js +1 -1
- package/examples/browser-example/index.html +25 -4
- package/package.json +1 -1
- package/readme.md +131 -98
- package/test/handlebars-i18n.test.js +44 -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,22 @@
|
|
|
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;
|
|
390
399
|
}
|
|
391
400
|
);
|
|
392
401
|
handlebars.registerHelper('_locale',
|
|
@@ -502,7 +511,7 @@
|
|
|
502
511
|
break;
|
|
503
512
|
default:
|
|
504
513
|
throw new Error('@ handlebars-i18n: invalid argument "unit" was given for _dateAdd.' +
|
|
505
|
-
'Unit must be
|
|
514
|
+
'Unit must be one of "second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year".');
|
|
506
515
|
}
|
|
507
516
|
|
|
508
517
|
const dateFormat = new Intl.DateTimeFormat(i18next.language, opts);
|
|
@@ -629,4 +638,4 @@
|
|
|
629
638
|
}
|
|
630
639
|
},
|
|
631
640
|
}
|
|
632
|
-
});
|
|
641
|
+
});
|
|
@@ -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("_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
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 import 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.9.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
|
-
Handlebars-i18n has its own command line
|
|
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
|
+
## Public 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
177
|
The primary 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
|
|
|
@@ -203,7 +206,7 @@ The i18next resource:
|
|
|
203
206
|
|
|
204
207
|
**Plurals**
|
|
205
208
|
|
|
206
|
-
```
|
|
209
|
+
```hbs
|
|
207
210
|
{{__ "keyWithCount" count=8}}
|
|
208
211
|
```
|
|
209
212
|
|
|
@@ -213,74 +216,99 @@ The i18next resource:
|
|
|
213
216
|
"keyWithCount" : "{{count}} item",
|
|
214
217
|
"keyWithCount_plural" : "{{count}} items"
|
|
215
218
|
}
|
|
216
|
-
},
|
|
219
|
+
}, ...
|
|
217
220
|
```
|
|
218
221
|
|
|
219
|
-
**Override globally selected language**
|
|
222
|
+
**Override the globally selected language**
|
|
220
223
|
|
|
221
|
-
```
|
|
224
|
+
```hbs
|
|
222
225
|
{{__ "key1" lng="de"}}
|
|
223
226
|
```
|
|
224
227
|
|
|
225
|
-
Will output the contents for
|
|
228
|
+
Will output the contents for `de` even though a different language is globally set.
|
|
229
|
+
|
|
230
|
+
**Looping over an array (or object) of translations**
|
|
231
|
+
|
|
232
|
+
```hbs
|
|
233
|
+
<ul>
|
|
234
|
+
{{#each (__ "fruits")}}
|
|
235
|
+
<li>{{this}}</li>
|
|
236
|
+
{{/each}}
|
|
237
|
+
</ul>
|
|
238
|
+
```
|
|
239
|
+
In this case the key `fruits` would contain an array of translation strings, like:
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
{
|
|
243
|
+
en: {
|
|
244
|
+
translation: {
|
|
245
|
+
fruits: ["Apple", "Banana", "Cherry"]
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
returnObjects: true
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
It is recommended to set `returnObjects` actively to `true` in the `i18next.init` object if you want to loop over
|
|
253
|
+
an array or objects of properties.
|
|
226
254
|
|
|
227
255
|
---
|
|
228
256
|
|
|
229
|
-
### _locale
|
|
257
|
+
### `_locale`
|
|
230
258
|
|
|
231
|
-
Returns the shortcode of i18next’s currently selected language such as
|
|
259
|
+
Returns the shortcode of i18next’s currently selected language such as `en`, `de`, `ja` … etc.
|
|
232
260
|
|
|
233
|
-
```
|
|
261
|
+
```hbs
|
|
234
262
|
{{_locale}}
|
|
235
263
|
```
|
|
236
264
|
|
|
237
265
|
---
|
|
238
266
|
|
|
239
|
-
### localeIs
|
|
267
|
+
### `localeIs`
|
|
240
268
|
|
|
241
269
|
Checks a string against i18next’s currently selected language. Returns **true** or **false**.
|
|
242
270
|
|
|
243
|
-
```
|
|
271
|
+
```hbs
|
|
244
272
|
{{#if (localeIs "en")}} ... {{/if}}
|
|
245
273
|
```
|
|
246
274
|
|
|
247
275
|
---
|
|
248
276
|
|
|
249
|
-
### _date
|
|
277
|
+
### `_date`
|
|
250
278
|
|
|
251
279
|
Outputs a formatted date according to the language specific conventions.
|
|
252
280
|
|
|
253
|
-
```
|
|
281
|
+
```hbs
|
|
254
282
|
{{_date}}
|
|
255
283
|
```
|
|
256
284
|
|
|
257
285
|
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
|
|
286
|
+
string, a number (timestamp in milliseconds), or a date array. `_date` accepts all arguments
|
|
259
287
|
Javascript’s [new Date()](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Date)
|
|
260
288
|
constructor would accept.
|
|
261
289
|
|
|
262
290
|
**Date argument given as date string:**
|
|
263
291
|
|
|
264
|
-
```
|
|
292
|
+
```hbs
|
|
265
293
|
{{_date "2020-03-11T03:24:00"}}
|
|
266
294
|
```
|
|
267
295
|
|
|
268
296
|
or
|
|
269
297
|
|
|
270
|
-
```
|
|
298
|
+
```hbs
|
|
271
299
|
{{_date "December 17, 1995 03:24:00"}}
|
|
272
300
|
```
|
|
273
301
|
|
|
274
302
|
**Date argument given as number (milliseconds since begin of unix epoch):**
|
|
275
303
|
|
|
276
|
-
```
|
|
304
|
+
```hbs
|
|
277
305
|
{{_date 1583922952743}}
|
|
278
306
|
```
|
|
279
307
|
|
|
280
308
|
**Date argument given as javascript date array** [year, monthIndex [, day [, hour [, minutes [,
|
|
281
309
|
seconds [, milliseconds]]]]]]:
|
|
282
310
|
|
|
283
|
-
```
|
|
311
|
+
```hbs
|
|
284
312
|
{{_date "[2012, 11, 20, 3, 0, 0]"}}
|
|
285
313
|
```
|
|
286
314
|
|
|
@@ -291,60 +319,59 @@ See [Intl DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScrip
|
|
|
291
319
|
for your option arguments. Alternatively check this repo’s TS types
|
|
292
320
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
293
321
|
|
|
294
|
-
```
|
|
322
|
+
```hbs
|
|
295
323
|
{{_date 1583922952743 year="2-digit" day="2-digit" timeZone="America/Los_Angeles"}}
|
|
296
324
|
```
|
|
297
325
|
|
|
298
326
|
---
|
|
299
327
|
|
|
300
|
-
### _dateAdd :tada: new in 1.8
|
|
328
|
+
### `_dateAdd` :tada: new in 1.8
|
|
301
329
|
|
|
302
330
|
Adds a time offset in a given unit to a date, returns the modified date.
|
|
303
331
|
|
|
304
|
-
```
|
|
332
|
+
```hbs
|
|
305
333
|
{{_dateAdd "1996-12-17" 24 unit="hour"}}
|
|
306
334
|
```
|
|
307
335
|
|
|
308
|
-
|
|
336
|
+
* output: en → **12/18/1996**
|
|
309
337
|
|
|
310
|
-
The first argument is a date (see function
|
|
338
|
+
The first argument is a date (see function `_date` for valid date inputs). The second argument is a time amount given
|
|
311
339
|
as number. The option **unit** specifies the time amount. Possible units
|
|
312
340
|
are `"second"` | `"minute"` | `"hour"` | `"day"` | `"week"` | `"month"` | `"quarter"` |`"year"` (default is `"hour"`).
|
|
313
|
-
Further options as for function
|
|
341
|
+
Further options as for function `_date` can be applied.
|
|
314
342
|
|
|
315
343
|
---
|
|
316
344
|
|
|
317
|
-
### _dateDiff
|
|
345
|
+
### `_dateDiff`
|
|
318
346
|
|
|
319
347
|
Outputs the relative time difference between two given dates.
|
|
320
348
|
|
|
349
|
+
```hbs
|
|
350
|
+
{{_dateDiff "2000-12-17" "2001-12-17" unit="year"}}
|
|
321
351
|
```
|
|
322
|
-
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Will output for "en" → **in 1 year**
|
|
352
|
+
* output: en → **in 1 year**
|
|
326
353
|
|
|
327
354
|
The second date argument is subtracted from the first. If the difference is a positive value, a future event statement
|
|
328
355
|
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
|
|
356
|
+
Allowed date input formats are similar to `_date`, options equal `_dateRel`. Default unit is `"hour"`.
|
|
330
357
|
|
|
331
358
|
---
|
|
332
359
|
|
|
333
|
-
### _dateRel
|
|
360
|
+
### `_dateRel`
|
|
334
361
|
|
|
335
362
|
Outputs a string with a relative date statement, formatted according to the language specific conventions.
|
|
336
363
|
|
|
337
|
-
```
|
|
364
|
+
```hbs
|
|
338
365
|
{{_dateRel 7 unit="hour"}}
|
|
339
366
|
```
|
|
340
367
|
|
|
341
|
-
|
|
368
|
+
* output: en → **in 7 hours**
|
|
342
369
|
|
|
343
|
-
```
|
|
370
|
+
```hbs
|
|
344
371
|
{{_dateRel -7 unit="hour"}}
|
|
345
372
|
```
|
|
346
373
|
|
|
347
|
-
|
|
374
|
+
* output: en → **7 hours ago**
|
|
348
375
|
|
|
349
376
|
A positive number argument leads to a future event statement, a negative refers to a past date. Possible units
|
|
350
377
|
are `"second"` | `"minute"` | `"hour"` | `"day"` | `"week"` | `"month"` | `"quarter"` |`"year"` (default is `"hour"`).
|
|
@@ -354,12 +381,11 @@ Alternatively check this repo’s TS types in [handlebars-i18n.d.ts](./dist/hand
|
|
|
354
381
|
|
|
355
382
|
---
|
|
356
383
|
|
|
357
|
-
### _num
|
|
384
|
+
### `_num`
|
|
358
385
|
|
|
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**".
|
|
386
|
+
Outputs a formatted number according to the language specific conventions of number representation.
|
|
361
387
|
|
|
362
|
-
```
|
|
388
|
+
```hbs
|
|
363
389
|
{{_num 4100000.8314 }}
|
|
364
390
|
```
|
|
365
391
|
|
|
@@ -370,20 +396,19 @@ See [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/
|
|
|
370
396
|
for your option arguments. Alternatively check this repo’s TS types
|
|
371
397
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
372
398
|
|
|
373
|
-
```
|
|
399
|
+
```hbs
|
|
374
400
|
{{_num 3.14159 maximumFractionDigits=2}}
|
|
375
401
|
```
|
|
376
402
|
|
|
377
|
-
|
|
403
|
+
* output: en → **3.14** | de → **3,14**
|
|
378
404
|
|
|
379
405
|
---
|
|
380
406
|
|
|
381
|
-
### _price
|
|
407
|
+
### `_price`
|
|
382
408
|
|
|
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**".
|
|
409
|
+
Outputs a formatted currency string according to the language specific conventions of price representation.
|
|
385
410
|
|
|
386
|
-
```
|
|
411
|
+
```hbs
|
|
387
412
|
{{_price 9999.99}}
|
|
388
413
|
```
|
|
389
414
|
|
|
@@ -394,13 +419,13 @@ See [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/
|
|
|
394
419
|
for your option arguments. Alternatively check this repo’s TS types
|
|
395
420
|
in [handlebars-i18n.d.ts](./dist/handlebars-i18n.d.ts).
|
|
396
421
|
|
|
397
|
-
```
|
|
422
|
+
```hbs
|
|
398
423
|
{{_price 1000 currency="JPY" minimumFractionDigits=2}}
|
|
399
424
|
```
|
|
400
425
|
|
|
401
426
|
---
|
|
402
427
|
|
|
403
|
-
## How to use HandlebarsI18n.configure method
|
|
428
|
+
## How to use `HandlebarsI18n.configure` method
|
|
404
429
|
|
|
405
430
|
### Generic language format settings
|
|
406
431
|
|
|
@@ -411,7 +436,7 @@ all languages or only for specific languages.
|
|
|
411
436
|
HandlebarsI18n.configure("all", "DateTimeFormat", {timeZone: "America/Los_Angeles"});
|
|
412
437
|
```
|
|
413
438
|
|
|
414
|
-
First argument is the language shortcode or "
|
|
439
|
+
First argument is the language shortcode or `"all"` for all languages. Second is the format option you want to
|
|
415
440
|
address (`DateTimeFormat`, `RelativeTimeFormat`, `NumberFormat`, or `PriceFormat`). Third argument is the options object
|
|
416
441
|
with the specific settings.
|
|
417
442
|
|
|
@@ -433,9 +458,9 @@ HandlebarsI18n.configure("all", "PriceFormat", {currency: "HKD", currencyDisplay
|
|
|
433
458
|
|
|
434
459
|
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
460
|
|
|
436
|
-
- **2020** (year-only)
|
|
437
|
-
- **11.3.2020** (standard-date)
|
|
438
|
-
- **7:24:02** (time-only)
|
|
461
|
+
- **2020** (`year-only`)
|
|
462
|
+
- **11.3.2020** (`standard-date`)
|
|
463
|
+
- **7:24:02** (`time-only`)
|
|
439
464
|
|
|
440
465
|
To do this, define a 4th parameter with a custom name:
|
|
441
466
|
|
|
@@ -447,9 +472,9 @@ HandlebarsI18n.configure([
|
|
|
447
472
|
]);
|
|
448
473
|
```
|
|
449
474
|
|
|
450
|
-
Call a subset in template with the parameter format="custom-name"
|
|
475
|
+
Call a subset in template with the parameter `format="custom-name"`, like:
|
|
451
476
|
|
|
452
|
-
```
|
|
477
|
+
```hbs
|
|
453
478
|
{{_date myDate format="year-only"}}
|
|
454
479
|
```
|
|
455
480
|
|
|
@@ -507,9 +532,9 @@ HandlebarsModified.registerHelper("foo", function () {
|
|
|
507
532
|
HandlebarsI18n.init(HandlebarsModified);
|
|
508
533
|
```
|
|
509
534
|
|
|
510
|
-
HandlebarsI18n will have your previously defined method
|
|
535
|
+
HandlebarsI18n will have your previously defined method `foo` by now.
|
|
511
536
|
|
|
512
|
-
The same can be done for a custom instance of i18next. Pass it as the second argument to the init function.
|
|
537
|
+
The same can be done for a custom instance of i18next. Pass it as the second argument to the `init` function.
|
|
513
538
|
|
|
514
539
|
```javascript
|
|
515
540
|
const i18nextCustom = require("i18next");
|
|
@@ -523,12 +548,20 @@ HandlebarsI18n.init(null, i18nextCustom);
|
|
|
523
548
|
npm test
|
|
524
549
|
```
|
|
525
550
|
|
|
551
|
+
## License
|
|
552
|
+
|
|
553
|
+
MIT License, Copyright (c) 2020–25 Florian Walzel
|
|
554
|
+
|
|
555
|
+
## Contributing
|
|
556
|
+
|
|
557
|
+
Contributions are welcome! Please open an issue or submit a pull request on GitHub.
|
|
558
|
+
|
|
526
559
|
## Merci à vous
|
|
527
560
|
|
|
528
561
|
For your contribution, I would like to
|
|
529
562
|
thank [@MickL](https://github.com/MickL), [@dargmuesli](https://github.com/dargmuesli), and [@DiefBell](DiefBell).
|
|
530
563
|
|
|
531
|
-
## Note
|
|
564
|
+
## Note on alternatives
|
|
532
565
|
|
|
533
566
|
There is a *different* package named [handlebars-i18next](https://www.npmjs.com/package/handlebars-i18next)
|
|
534
567
|
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,37 @@ 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
|
+
|
|
180
221
|
|
|
181
222
|
/****************************************
|
|
182
223
|
Tests against function _date
|
|
@@ -1165,4 +1206,4 @@ describe('handlebars-i18n Private helper Function Tests (in production not expor
|
|
|
1165
1206
|
expect(result).to.deep.equal({});
|
|
1166
1207
|
});
|
|
1167
1208
|
|
|
1168
|
-
});
|
|
1209
|
+
});
|