Skip to content

Commit 42011bc

Browse files
committed
Example
Clipboard translate
1 parent 6f7fe75 commit 42011bc

7 files changed

Lines changed: 308 additions & 59 deletions

File tree

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,29 @@ $ npm install -g @wstaeblein/json-translator
4242

4343
## Usage and API
4444

45-
The app has 3 commands, plus you can execute it with no arguments or with --help to get on screen help. The commands and their arguments are explained below
45+
The app has 4 commands, plus you can execute it with no arguments or with --help to get on screen help. The commands and their arguments are explained below
4646

4747

4848
### 1- TRANSLATE
4949

5050
This command performs the automatic translation using Google Translator. The idea here is to get a fast translation without the need for a translator. It is a good idea to either revise the translation or have someone revise for you. That's why you can have your translation saved as a JSON or as a TSV.
5151

52+
You can also translate straight from the clipboard. Just copy ta valid JSON structure to the clipboard and run the translate command. The result will be copied to the clipboard when ready.
53+
5254
You'll need to pass 5 arguments as shown below, none are optional.
5355

5456
```sh
5557
$ jsontrans translate ./myfile-pt.json pt en json
58+
$ jsontrans translate clip pt en json
5659
```
57-
The first argument is the command translate, the second is a path that points to a JSON file with it's contents in your original language. From this file the translate command will generate a new file with the translation.
60+
The first argument is the command translate, the second is either a path that points to a JSON file with it's contents in your original language, or the text clip, in which case the contents of the clipboard will be used instead of a file. From this content the translate command will generate a new file or set the clipboard with the result of the translation.
5861

59-
The third argument is the ISO code of the language used in file passed in the previous argument and the forth is the language you wish your file translated into. Last but not least, the fifth argument is the output format. It can be either json or tsv.
62+
The third argument is the ISO code of the language used in file passed in the previous argument and the forth is the language you wish your file translated into. [Click here](https://cloud.google.com/translate/docs/languages) to see a list of supported languages. Last but not least, the fifth argument is the output format. It can be either json or tsv.
6063

6164
If JSON format is chosen, a JSON file with the translation is created. This file will have same structure as the one passed but with the contents translated into your language of choice. If TSV format is chosen, a TSV file with columns for and ID (internal use), the original language and the target language are created. This way you can provide a translator with a file that is already translated and need only a revision and possible corrections, making his/her job much faster and easier, perhaps even cheaper.
6265

66+
When using clipboard translation the last argument has no effect. The clipboard will always accept a JSON structure to be translated and will be set with the same structure after having it's content translated.
67+
6368
![TSV file with translation](./imgs/tsv-ready.png)
6469

6570
*When tsv is the format, a file with a translation is generated*
@@ -105,10 +110,21 @@ $ jsontrans transform ./myfile.tsv /myfile.json newfilename
105110
```
106111

107112
The 4 arguments are as follows. The first is the command transform, the second is the path to the TSV file, the third is the path to the original JSON file (the one used to create the TSV file for translation) and the last is the filename for the json file to be created.
113+
  
114+
  
115+
### 4- TRIM
116+
117+
This command just removes double underscores from a JSON file's keys. The idea being: You create a file with the original language's translation, usually your language or english. In this file you shall have keys that may not be for translation.
118+
119+
```sh
120+
$ jsontrans trim ./myfile.json
121+
```
122+
108123

109124

110125
### Observations
111126

127+
- In the [examples folder](/examples) you'll find a [JSON file in english](/examples/en.json), it's [translation into spanish](/examples/es.json) and a [TSV file](/examples/en-es.tsv) ready to be translated from english to spanish. All these files were generated using this application. The first was the original file used to produce the other 2 with the `translate` and `prepare` commands respectively.
112128
- The TSV extension indicates tab separated files, much like CSVs but instead of commas or semicolons TABs are used to separate cells.
113129
- Existing files will be overwritten without warning.
114130
- The translator must be instructed to make changes only to the third column with the appropriate translations and nothing else.

examples/en-es.tsv

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Internal ID en es
2+
title ACME Application
3+
desc This application does a lot of good things to many people
4+
mainbtntext Download
5+
form.waitmsg Please wait while we submit your form
6+
form.okmsg Form sent successfully
7+
form.errmsg An error has occurred!
8+
form.placeholders.name Name
9+
form.placeholders.email Email
10+
form.placeholders.msg Message
11+
bullets.0.title Simple and straightforward interface
12+
bullets.0.text Just drag one or more files over the screen to start the process.
13+
bullets.1.title Available in multiple languages
14+
bullets.1.text Currently the interface is available in english, portuguese and spanish.
15+
bullets.2.title Control over the process
16+
bullets.2.text You can choose what to do all along the process.
17+
bullets.3.title Control over usage
18+
bullets.3.text You know at all times how many times the application has been used.
19+
wait Wait

examples/en.json

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,29 @@
1010
"name": "Name",
1111
"email": "Email",
1212
"msg": "Message"
13-
}
13+
}
1414
},
1515
"bullets": [
16-
{ "title": "Simple and straightforward interface", "__icon": "drag", "text": "Just drag one or more files over the screen to start the process." },
17-
{ "title": "Available in multiple languages", "__icon": "globe", "text": "Currently the interface is available in english, portuguese and spanish." },
18-
{ "title": "Control over the process", "__icon": "file", "text": "You can choose what to do all along the process." },
19-
{ "title": "Control over usage", "__icon": "dash", "text": "You know at all times how many times the application has been used." }
16+
{
17+
"title": "Simple and straightforward interface",
18+
"text": "Just drag one or more files over the screen to start the process.",
19+
"__icon": "drag"
20+
},
21+
{
22+
"title": "Available in multiple languages",
23+
"text": "Currently the interface is available in english, portuguese and spanish.",
24+
"__icon": "globe"
25+
},
26+
{
27+
"title": "Control over the process",
28+
"text": "You can choose what to do all along the process.",
29+
"__icon": "file"
30+
},
31+
{
32+
"title": "Control over usage",
33+
"text": "You know at all times how many times the application has been used.",
34+
"__icon": "dash"
35+
}
2036
],
2137
"wait": "Wait"
22-
2338
}

examples/es.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@
1515
"bullets": [
1616
{
1717
"title": "Interfaz simple y directa",
18-
"__icon": "drag",
19-
"text": "Simplemente arrastre uno o más archivos sobre la pantalla para iniciar el proceso."
18+
"text": "Simplemente arrastre uno o más archivos sobre la pantalla para iniciar el proceso.",
19+
"icon": "drag"
2020
},
2121
{
2222
"title": "Disponible en varios idiomas",
23-
"__icon": "globe",
24-
"text": "Actualmente, la interfaz está disponible en inglés, portugués y español."
23+
"text": "Actualmente, la interfaz está disponible en inglés, portugués y español.",
24+
"icon": "globe"
2525
},
2626
{
2727
"title": "Control sobre el proceso",
28-
"__icon": "file",
29-
"text": "Puede elegir qué hacer a lo largo del proceso."
28+
"text": "Puede elegir qué hacer a lo largo del proceso.",
29+
"icon": "file"
3030
},
3131
{
3232
"title": "Control sobre el uso",
33-
"__icon": "dash",
34-
"text": "Sabes en todo momento cuántas veces se ha utilizado la aplicación."
33+
"text": "Sabes en todo momento cuántas veces se ha utilizado la aplicación.",
34+
"icon": "dash"
3535
}
3636
],
3737
"wait": "Esperar"

index.js

Lines changed: 104 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
//
66
// Prepare for and Translates JSON files
77
// *************************************
8-
98
(async function() {
109

1110
var googleTrans = require('@vitalets/google-translate-api');
@@ -14,22 +13,45 @@
1413
var version = require('./package.json').version || '';
1514
var os = require('os');
1615
var cp = require('chroma-palette');
16+
var clipboard = require('node-clipboardy');
1717
var args = process.argv.slice(2);
1818
var log = console.log;
1919

2020

21-
if (!args.length || args[0].toLowerCase() == '--help' || args[0].toLowerCase() == '-h') {
21+
if (!args.length || args[0].toLowerCase() == '--help' || args[0].toLowerCase() == 'help' || args[0].toLowerCase() == '-h') {
2222
help();
2323
} else {
2424
let op = args[0].toLowerCase();
2525
let tsvfile = '';
2626
let jsonfile = '';
2727
let currlang = '', outlang = '';
2828
let startTime = new Date().getTime();
29+
let clipBoardFlag = false;
2930

3031
switch (op) {
32+
case 'trim':
33+
if (args.length != 2) {
34+
endThis('Arguments are wrong or in the wrong number');
35+
}
36+
37+
try {
38+
let fn = args[1];
39+
let pth = makePath(fn);
40+
jsonfile = fs.readFileSync(pth, { encoding:'utf8' });
41+
let newJSON = JSON.parse(jsonfile, trimUnderscores);
42+
43+
fs.writeFileSync(pth, JSON.stringify(newJSON, null, 4), { encoding:'utf8' });
44+
45+
} catch (error) {
46+
log(error);
47+
endThis('An unexpected error has occurred!');
48+
}
49+
break;
50+
3151
case 'translate':
32-
if (args.length != 5) {
52+
let isclip = args[1].toLowerCase() == 'clip' || args[1].toLowerCase() == 'clipboard';
53+
54+
if (!(([4, 5].includes(args.length) && isclip) || args.length == 5)) {
3355
endThis('Arguments are wrong or in the wrong number');
3456
}
3557
let format = args[4].toLowerCase();
@@ -43,22 +65,44 @@
4365
try {
4466
let fn = args[1];
4567
let pth = makePath(fn);
46-
jsonfile = JSON.parse(fs.readFileSync(pth, { encoding:'utf8' }));
47-
let newJSON = await translate(jsonfile, currlang, outlang);
68+
if (isclip) {
69+
try {
70+
let cbtext = clipboard.readSync();
71+
if (!cbtext) {
72+
endThis('Clipboard empty');
73+
} else {
74+
jsonfile = JSON.parse(cbtext);
75+
}
76+
} catch (error) {
77+
log(error);
78+
endThis('Clipboard content could not be read and/or formatted');
79+
}
80+
clipBoardFlag = true;
81+
} else {
82+
jsonfile = JSON.parse(fs.readFileSync(pth, { encoding:'utf8' }));
83+
}
4884

49-
let parsePth = path.parse(pth);
50-
let newpath = path.join(parsePth.dir, (parsePth.name.toLowerCase() == currlang ? outlang : parsePth.name + '-' + outlang));
51-
52-
switch (format) {
53-
case 'json':
54-
fs.writeFileSync(newpath + '.json', JSON.stringify(newJSON, null, 4), { encoding:'utf8' });
55-
break;
56-
57-
case 'tsv':
58-
let emptyTSV = prepare(jsonfile, currlang, outlang); // Creates TSV ready to be translated
59-
let tsvOutPath = newpath + '.tsv'; // Creates path to save TSV
60-
let newTSV = fillTSV(emptyTSV, newJSON); // Translate TSV
61-
fs.writeFileSync(tsvOutPath, newTSV, { encoding:'utf8' }); // Saves TSV
85+
let newJSONText = JSON.stringify(await translate(jsonfile, currlang, outlang));
86+
let newJSON = JSON.parse(newJSONText, trimUnderscores);
87+
88+
if (clipBoardFlag) {
89+
clipboard.writeSync(JSON.stringify(newJSON, null, 4));
90+
log(`\n${cp.yellow.paint('MESSAGE: ')} Result copied to clipboard!`);
91+
} else {
92+
let parsePth = path.parse(pth);
93+
let newpath = path.join(parsePth.dir, (parsePth.name.toLowerCase() == currlang ? outlang : parsePth.name + '-' + outlang));
94+
95+
switch (format) {
96+
case 'json':
97+
fs.writeFileSync(newpath + '.json', JSON.stringify(newJSON, null, 4), { encoding:'utf8' });
98+
break;
99+
100+
case 'tsv':
101+
let emptyTSV = prepare(jsonfile, currlang, outlang); // Creates TSV ready to be translated
102+
let tsvOutPath = newpath + '.tsv'; // Creates path to save TSV
103+
let newTSV = fillTSV(emptyTSV, newJSON); // Translate TSV
104+
fs.writeFileSync(tsvOutPath, newTSV, { encoding:'utf8' }); // Saves TSV
105+
}
62106
}
63107
} catch (error) {
64108
log(error);
@@ -210,6 +254,14 @@
210254
// ------------------------------------------------
211255
function prepare(json, lang, outlang) {
212256
let arr = props2Array(json, lang, outlang);
257+
let index = arr.length;
258+
259+
while (index--) {
260+
let dotnot = arr[index].split('\t').shift();
261+
if (dotnot.includes('.__') || dotnot.substr(0, 2) == '__') {
262+
arr.splice(index, 1);
263+
}
264+
}
213265
let resp = arr.join(os.EOL);
214266
return resp;
215267
}
@@ -225,8 +277,9 @@
225277
let lineArr = tsv[i].split('\t');
226278

227279
if (lineArr.length >= 3) {
228-
let dots = lineArr[0], value = lineArr[2];
229-
dotNotation(json, dots, value);
280+
let dots = lineArr[0];
281+
let value = lineArr[2];
282+
if (value) { dotNotation(json, dots, value); }
230283
}
231284
}
232285
return json;
@@ -256,7 +309,7 @@
256309
// ----------------------------------------------------------------
257310
function dotNotation(obj, dotArr, value) {
258311
if (typeof dotArr == 'string') {
259-
// Tenta de novo com is como array
312+
// Tenta de novo com dotArr como array
260313
return dotNotation(obj, dotArr.split('.'), value);
261314

262315
} else if (dotArr.length == 1 && value !== undefined) {
@@ -266,10 +319,14 @@
266319
return obj[dotArr[0]] = value;
267320
} else {
268321
// Se já for um array, acrescenta o item, senão cria o array com o item
269-
if (Array.isArray(obj)) { obj[+dotArr[0]] = value; } else { obj = [value]; }
322+
if (Array.isArray(obj)) {
323+
obj[+dotArr[0]] = value;
324+
} else {
325+
obj = [value];
326+
}
270327
}
271328
} else if (dotArr.length==0) {
272-
// Se is for vazio, retorna o objeto
329+
// Se dotArr for vazio, retorna o objeto
273330
return obj;
274331
} else {
275332
// Gira a roda
@@ -283,7 +340,7 @@
283340
// --------------------------------------------------------------------------------------
284341
// Returns an array with the dotnotation of each property of the object passed in obj Ex:
285342
// obj = { a: { b: { c: 'hello' } }}
286-
// Retorns:
343+
// Returns:
287344
// a.b.c [TAB] hello [TAB]
288345
// With the following first line: 'ID' [TAB] originalLang code [TAB] targetLang code
289346
// --------------------------------------------------------------------------------------
@@ -332,6 +389,19 @@
332389
});
333390
}
334391

392+
393+
// ----------------------------------------------------
394+
// Remove leading underscores from a JSON property name
395+
// ----------------------------------------------------
396+
function trimUnderscores(key, value) {
397+
if (key.substr(0, 2) == '__') {
398+
this[key.slice(2)] = value;
399+
return;
400+
} else {
401+
return value;
402+
}
403+
}
404+
335405
// ----------------------
336406
// Provides onscreen help
337407
// ----------------------
@@ -345,8 +415,8 @@ ${cp.red.paint('(_| _) \\_/ | \\| | | \\ | | | \\| _) |_ | | | \\ / | \
345415
${cp.yellow.paint('by Walter Staeblein v. ' + version)}
346416
===========================================================
347417
348-
This app can translate almost any JSON file or prepare a JSON file for translation as a TSV file.
349-
You can use the following commands:
418+
This app can translate almost any JSON file or prepare a JSON file for translation
419+
as a TSV file. You can use the following commands:
350420
351421
${cp.red.paint('1- Automatic Translation')}
352422
------------------------
@@ -380,22 +450,17 @@ myfile.json) into a new JSON file with the translation in place. The name for
380450
new file is passed in newfilename and it's structure will be the same as myfile.json
381451
but the values will be replaced with the ones from myfile.tsv.
382452
383-
------------------------------------------------------------------------------------
384-
385-
${cp.green.paint('Observations:')}
386-
=============
387-
388-
- The TSV extension indicates tab separated files, much like CSVs but instead of
389-
commas or semicolons TABs are used to separate cells.
390-
391-
- Existing files will be overwritten without warning.
392453
393-
- The tsv files generated by this app must not be changed. The translator should
394-
only fill the third column with the appropriate translations and nothing else.
454+
${cp.red.paint('4- Trims the original JSON file of it\'s double underscores')}
455+
-----------------------------------------------------------
456+
${cp.yellow.paint('jsontrans trim ./myfile.json')}
395457
396-
- Transform command supposes TSV file's first line is a header and will be ignored.
458+
Trims the original JSON file, used to generate the TSV with command 2 and passed as
459+
myfile.json, of it's double underscores in key names. When you prepend 2 underscores
460+
to a key name, it's value won't be translated. This command is to remove those extra
461+
characters from the original file.
397462
398-
`);
463+
`);
399464
process.exit(0);
400465
}
401466

0 commit comments

Comments
 (0)