handlebars-i18n-cli 2.0.2 → 2.0.4

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/README.md CHANGED
@@ -25,7 +25,7 @@ If you do not link the package, you may run into the error `bash: i18n-collect:
25
25
 
26
26
  ## General Use
27
27
 
28
- ### 1. Language Key Extraction: _i18n-collect_
28
+ ### 1. Language Key Extraction: i18n-collect
29
29
 
30
30
  #### Syntax:
31
31
 
@@ -39,10 +39,10 @@ Generate a file `translations.json` holding the translations for `de`, `fr`, and
39
39
  names intended for i18next translation from all html files in your project:
40
40
 
41
41
  ```shell
42
- i18n-collect my-project/**/*.html my-project/translations.json --lng de,en,fr
42
+ i18n-collect src/**/*.hbs src/translations.json --lng de,en,fr
43
43
  ```
44
44
 
45
- From a very simple template like this …
45
+ From a very simple handlebars template like this …
46
46
 
47
47
  ```html
48
48
  <!DOCTYPE html>
@@ -56,7 +56,7 @@ From a very simple template like this …
56
56
  </html>
57
57
  ```
58
58
 
59
- … the generated translations.json would be:
59
+ … the generated `translations.json` would be:
60
60
 
61
61
  ```json
62
62
  {
@@ -83,7 +83,7 @@ From a very simple template like this …
83
83
  }
84
84
  ```
85
85
 
86
- ### 2. Automatic Translation via DeepL API: _i18n-deepl_
86
+ ### 2. Automatic Translation via DeepL API: i18n-deepl
87
87
 
88
88
  #### Syntax:
89
89
 
@@ -97,7 +97,7 @@ i18n-deepl translate <source> <target> <targetLang> <options...>
97
97
  i18n-deepl translate en.json fi.json fi
98
98
  ```
99
99
 
100
- Will run the file en.json against DeepL API. From this file
100
+ Runs the file `en.json` against DeepL API. From this
101
101
 
102
102
  ```
103
103
  {
@@ -107,7 +107,7 @@ Will run the file en.json against DeepL API. From this file
107
107
  }
108
108
  ```
109
109
 
110
- will be generated the Finish translation fi.json:
110
+ … the Finish translation `fi.json` will be generated:
111
111
 
112
112
  ```
113
113
  {
@@ -130,11 +130,10 @@ template, the carry to the according language JSON is done by the CLI. You then
130
130
  In case a translation string expects variables for replacement, these variables will be added to your json template.
131
131
 
132
132
  If you are not using [handlebars-i18n](https://github.com/fwalzel/handlebars-i18n.git) for translation but a custom integration of i18next into handlebars.js, you
133
- might be able to appropriate this cli by using the option --translFunc (
134
- see below).
133
+ might be able to appropriate this cli by using the option --translFunc (see below).
135
134
 
136
- Also `handlebars-i18n-cli` allows you to **auto-translate** a JSON file with an existing translation to another language
137
- via the DeepL API, while the original key names and JSOn structure are kept.
135
+ Also `handlebars-i18n-cli` allows you to **auto-translate** a JSON file with an existing translation to another language
136
+ via the DeepL API, while the original key names and JSON structure are kept.
138
137
 
139
138
  ## Example
140
139
 
@@ -164,7 +163,7 @@ You can use `handlebars-i18n-cli` in a programmatical way too. For import and in
164
163
 
165
164
  * The source files can be passed in as [glob](https://www.npmjs.com/package/glob) pattern.
166
165
  * i18n-collect is agnostic against the data type of the template(s) you want to extract translations keys from. It works
167
- with `.html` as well as `.js` files.
166
+ with `.hbs`, `.html` as well as `.js` files.
168
167
 
169
168
  `<target>`
170
169
 
@@ -177,36 +176,22 @@ i18next.init({
177
176
  });
178
177
  ```
179
178
 
180
- ### Usage options
179
+ ### Options
181
180
 
182
- `--alphabetical` or `-a`
181
+ * `--alphabetical` or `-a`: Order the keys to the translation strings alphabetically in the generated json file(s). When the flag
182
+ is not set, the keys appear in order as within the template(s).
183
+ * `--dryRun` or `-dr`: For simulation: Logs the result to console, but does not write out the file(s) to disk.
184
+ * `--empty` or `-e`: Create empty value strings for the translations in the json files(s). When the flag is not set, the
185
+ value strings contain current language and key name. Example:
183
186
 
184
- This will order the keys to the translation strings alphabetically in the generated json file(s). When the flag
185
- --alphabetical is not set the keys appear in order as within the template(s).
186
-
187
- ---
188
-
189
- `--dryRun` or `-dr`
190
-
191
- For simulation: Logs the result to console, but does not write out the file(s) to disk.
192
-
193
- ---
194
-
195
- `--empty` or `-e`
196
-
197
- Create empty value strings for the translations in the json files(s). When the flag --empty is not set the
198
- value strings contain current language and key name.
199
-
200
- Example:
201
-
202
- The template
187
+ The template
203
188
 
204
189
  ```html
205
190
  <h1>{{__ headline userName="Frank"}}</h1>
206
191
  <p>{{__ paragraph}}</p>
207
192
  ```
208
193
 
209
- would become
194
+ would become
210
195
 
211
196
  ```json
212
197
  {
@@ -219,7 +204,7 @@ would become
219
204
  }
220
205
  ```
221
206
 
222
- instead of
207
+ instead of:
223
208
 
224
209
  ```json
225
210
  {
@@ -232,71 +217,43 @@ instead of
232
217
  }
233
218
  ```
234
219
 
235
- ---
236
-
237
- `--lng language1,language2,...languageN`
238
-
239
- The list of language shortcodes you want to be generated with an own set in the json. Arguments are comma separated (no
240
- blank space between, no quotation marks around).
241
- If no language is defined, "en" is the default.
242
-
243
- ---
244
-
245
- `--log` or `-l`
246
-
247
- Logs the final result that is written to the json files(s) into the console as well.
248
220
 
249
- ---
250
-
251
- `--separateLngFiles` or `-sf`
252
-
253
- Write each language in a separate json file instead of a single one.
221
+ * `--lng <languges>`: The list of language shortcodes you want to be generated with an own set in the json. Arguments are comma separated (no
222
+ blank space between, no quotation marks around). Example: `--lng de,fi,en,es`. If no language is defined, `en` is the default.
223
+ * `--log` or `-l`: Logs the final result that is written to the json files(s) into the console as well.
224
+ * `--separateLngFiles` or `-sf`: Write each language in a separate json file instead of a single one. (By default,
225
+ all translations are written to a single json file). This will generate three json files
226
+ *translation.de.json*, *translation.en.json*, and *translation.fn.json*, each
227
+ holding only the translation for their respective language:
254
228
 
255
229
  ```bash
256
- i18n-collect my-project/template.html my-project/translation.json --lng de,en,fr --separateLngFiles
230
+ i18n-collect template.hbs translation.json --lng de,en,fn --separateLngFiles
257
231
  ```
258
-
259
- Will generate three json files: **translation.de.json**, **translation.en.json**, and **translation.fn.json** each
260
- holding only the translation for their respective language. By default all translations are written to a single json file.
261
-
262
- ---
263
-
264
- `--translFunc=yourCustomFunctionName`
265
-
266
- If you are not using handlebars-i18n for translations but a custom handlebars helper, you might be able to use
267
- i18n-collect as well.Say your translation function has the name *t* instead of handlebars-i18n’s *__* (double
268
- underscore) and your template usage would look like
232
+ * `--translFunc <yourCustomFunctionName>`: Substitutes the query name for the handlebars function, performing the translation.
233
+ Say your translation function has the name *t* instead of handlebars-i18n’s *__* (double underscore) and your template
234
+ usage would look like:
269
235
 
270
236
  ```html
271
- {{t myKeyNameToTranslation}}
237
+ <p> {{t myKeyNameToTranslation}} </p>
272
238
  ```
273
239
 
274
- you can do
240
+ You can then do
275
241
 
276
242
  ```bash
277
- i18n-collect my-project/template.html my-project/translation.json --translFunc=t
243
+ i18n-collect my-project/template.html my-project/translation.json --translFunc t
278
244
  ```
279
245
 
280
- --translFunc=t then substitutes the default *__* with a search for t.
246
+ this substitutes the default *__* with a search for the handlebars function named *t*.
281
247
 
282
- ---
283
-
284
- `--update` or `-u`
285
-
286
- Update an existing .json file with new translations. All keys in the existing .json are kept, new ones from the template
287
- will be added.
288
-
289
- Works also with the option --separateLngFiles:
248
+ * `--update` or `-u`: Update an existing .json file with new translations. All keys in the existing .json are kept, new ones from the template
249
+ will be added. Works also with the option `--separateLngFiles`. Leave out the language ending and json file extension and give only the base name for <target>. In this example case
250
+ handlebars-i18n-cli would look for *translation.de.json*, *translation.en.json*, and *translation.en.json* to update
251
+ them. A language file that does not exist yet will be generated.
290
252
 
291
253
  ```bash
292
254
  i18n-collect my-project/**/*.html my-project/translation --update --lng de,en,fr --separateLngFiles
293
255
  ```
294
256
 
295
- Leave out the language ending and json file extension and give only the base name for <target>. In this example case
296
- handlebars-i18n-cli would look for *translation.de.json*, *translation.en.json*, and *translation.en.json* to update
297
- them. A language file that does not exist yet will be generated.
298
-
299
-
300
257
  ## Detailed Description for _i18n-deepl_ Commands
301
258
 
302
259
  i18n-deepl is a command-line tool to translate i18next JSON files using the DeepL API. Below is a detailed guide to its
@@ -403,10 +360,10 @@ Translation complete. See ./output.json for your results.
403
360
  ## Run tests
404
361
 
405
362
  ```bash
406
- npm run test
363
+ npm test
407
364
  ```
408
365
 
409
366
  ## License
410
367
 
411
- Copyright (c) 2022-24 Florian Walzel,
412
- MIT License
368
+ MIT License, Copyright (c) 2022–25 Florian Walzel
369
+
@@ -26,10 +26,10 @@ existing keys, and generating separate files for each language.
26
26
 
27
27
  **Positional Arguments**
28
28
 
29
- | **Name** | **Type** | **Description** | **Example** |
30
- |-----------|---------------|-----------------------------------------|---------------------------|
31
- | `source` | `string` | Path to the source files. | `./src/template.html` |
32
- | `target` | `string` | Path to the target files or directory. | `./src/translations.json` |
29
+ | **Name** | **Type** | **Description** | **Example** |
30
+ |-----------|---------------|------------------------------------------|---------------------------|
31
+ | `source` | `string` | Path to the source file(s). Glob allowed | `./src/**/*.html` |
32
+ | `target` | `string` | Path to the target file(s) or directory. | `./src/translations.json` |
33
33
 
34
34
  **Optional `options` Object Properties**
35
35
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "handlebars-i18n-cli",
3
- "version": "2.0.2",
4
- "description": "Extracts translation keys from handlebars templates and forms JSON from it; generates automatic translations via DeepL.",
3
+ "version": "2.0.4",
4
+ "description": "Extract translation keys from handlebars templates and form JSON from it, generate automatic translations via DeepL",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "types": "types/index.d.ts",
@@ -41,21 +41,21 @@
41
41
  "homepage": "https://github.com/fwalzel/handlebars-i18n-cli#readme",
42
42
  "dependencies": {
43
43
  "async-file-tried": "^1.2.1",
44
- "axios": "^1.7.2",
45
- "commander": "^12.1.0",
46
- "deepl-node": "^1.13.0",
47
- "dotenv": "^16.4.5",
48
- "glob": "^11.0.0"
44
+ "axios": "^1.12.2",
45
+ "commander": "^14.0.1",
46
+ "deepl-node": "^1.19.1",
47
+ "dotenv": "^17.2.2",
48
+ "glob": "^11.0.3"
49
49
  },
50
50
  "devDependencies": {
51
- "c8": "^10.1.2",
52
- "chai": "^5.1.1",
53
- "chai-as-promised": "^8.0.0",
54
- "coveralls-next": "^4.2.1",
55
- "mocha": "^10.1.0",
56
- "sinon": "^18.0.0",
57
- "sinon-chai": "^4.0.0",
51
+ "c8": "^10.1.3",
52
+ "chai": "^5.3.3",
53
+ "chai-as-promised": "^8.0.2",
54
+ "coveralls-next": "^5.0.0",
55
+ "mocha": "^11.7.2",
56
+ "sinon": "^21.0.0",
57
+ "sinon-chai": "^4.0.1",
58
58
  "test-console": "^2.0.0",
59
- "typescript": "^5.7.2"
59
+ "typescript": "^5.9.2"
60
60
  }
61
61
  }
@@ -117,7 +117,7 @@ describe('i18n-deepl getSupportedLanguages', function () {
117
117
  * translateTexts
118
118
  ****************************************/
119
119
  describe('i18n-deepl translateTexts', () => {
120
- let authKey, texts, sourceLang, targetLang, options, translatorStub;
120
+ let authKey, texts, sourceLang, targetLang, options, translateTextStub;
121
121
 
122
122
  beforeEach(() => {
123
123
  authKey = 'valid-auth-key';
@@ -126,50 +126,53 @@ describe('i18n-deepl translateTexts', () => {
126
126
  targetLang = 'FR';
127
127
  options = {formality: 'informal'};
128
128
 
129
- // Mock deepl.Translator
130
- translatorStub = sinon.stub(new deepl.Translator(authKey));
131
- sinon.stub(deepl, 'Translator').returns(translatorStub);
129
+ // Stub the 'translateText' method on the prototype
130
+ // This will affect all future instances of deepl.Translator
131
+ translateTextStub = sinon.stub(deepl.Translator.prototype, 'translateText');
132
132
  });
133
133
 
134
134
  afterEach(() => {
135
- sinon.restore(); // Reset all stubs/mocks/spies between tests
135
+ sinon.restore(); // This correctly restores the original prototype method
136
136
  });
137
137
 
138
138
  it('[C-1] translateTexts should throw an error if authKey is not a string', async () => {
139
- try {
140
- await translateTexts(12345, texts, sourceLang, targetLang, options);
141
- } catch (error) {
142
- expect(error.message).to.equal('Invalid argument authKey provided.');
143
- }
139
+ // This test doesn't even need the stub, but the setup is now correct
140
+ await expect(translateTexts(12345, texts, sourceLang, targetLang, options))
141
+ .to.be.rejectedWith('Invalid argument authKey provided.');
144
142
  });
145
143
 
146
- it('[C-2] translateTexts should call deepl.Translator with correct authKey', async () => {
147
- translatorStub.translateText.resolves(['Bonjour', 'Monde']);
148
-
149
- await translateTexts(authKey, texts, sourceLang, targetLang, options);
144
+ it('[C-2] should call the translator once with all texts and return the aggregated results', async () => {
145
+ // The expected result from your function
146
+ const expectedTranslations = ['Bonjour', 'Monde'];
150
147
 
151
- expect(deepl.Translator).to.have.been.calledWith(authKey);
152
- expect(translatorStub.translateText).to.have.been.calledWith(texts, sourceLang, targetLang, options);
153
- });
148
+ // This is what the deepl-node library would return for a batch request
149
+ const mockApiResult = [
150
+ 'Bonjour', 'Monde'
151
+ ];
154
152
 
155
- it('[C-3] translateTexts should return translated texts from deepl.Translator', async () => {
156
- const translatedTexts = ['Bonjour', 'Monde'];
157
- translatorStub.translateText.resolves(translatedTexts);
153
+ // CORRECT: Configure the stub to resolve ONCE with an array of result objects.
154
+ translateTextStub.resolves(mockApiResult);
158
155
 
159
156
  const result = await translateTexts(authKey, texts, sourceLang, targetLang, options);
160
157
 
161
- expect(result).to.deep.equal(translatedTexts);
158
+ // CORRECT: Verify the stub was called only ONCE.
159
+ // The first argument should be the entire `texts` array.
160
+ // The `options` object is also being passed, so we include it in the check.
161
+ expect(translateTextStub).to.have.been.calledOnceWith(texts, sourceLang, targetLang, options);
162
+
163
+ // The assertion for the final output remains the same and should now pass.
164
+ expect(result).to.deep.equal(expectedTranslations);
162
165
  });
163
166
 
164
- it('[C-4] translateTexts should throw an error when deepl.Translator.translateText fails', async () => {
165
- const error = new Error('API Error');
166
- translatorStub.translateText.rejects(error);
167
+ it('[C-3] should throw an error when the translation API fails', async () => {
168
+ const apiError = new Error('API Error');
167
169
 
168
- try {
169
- await translateTexts(authKey, texts, sourceLang, targetLang, options);
170
- } catch (err) {
171
- expect(err.message).to.equal('API Error');
172
- }
170
+ // CORRECT: Configure the stub to reject the promise directly.
171
+ translateTextStub.rejects(apiError);
172
+
173
+ // Use chai-as-promised for cleaner async error checking.
174
+ await expect(translateTexts(authKey, texts, sourceLang, targetLang, options))
175
+ .to.be.rejectedWith(apiError);
173
176
  });
174
177
  });
175
178
 
@@ -381,9 +384,9 @@ describe('i18n-collect', () => {
381
384
  }
382
385
  inspect.restore();
383
386
  assert.deepEqual(inspect.output, [
384
- `Now processing test/test-assets/simple.html\n`,
385
- `{\n \"translations\": {\n \"en\": {\n \"myKey\": \"en of myKey with variables {{myVar}}\"\n }\n }\n}\n`
386
- ],
387
+ `Now processing test/test-assets/simple.html\n`,
388
+ `{\n \"translations\": {\n \"en\": {\n \"myKey\": \"en of myKey with variables {{myVar}}\"\n }\n }\n}\n`
389
+ ],
387
390
  'The logged output did not match the expected result.');
388
391
  });
389
392
 
@@ -459,8 +462,8 @@ describe('i18n-collect', () => {
459
462
  const inspect = stdout.inspect();
460
463
  try {
461
464
  await i18nCollect(templSimple, `test/test-generated/test-${fileNo}.json`, {update: true, log: true});
462
- } catch(e) {
463
- console.log (e)
465
+ } catch (e) {
466
+ console.log(e)
464
467
  }
465
468
  inspect.restore();
466
469
  assert.deepEqual(inspect.output, [