google-spreadsheet-translation-sync 1.3.5 → 1.3.6

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/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "google-spreadsheet-translation-sync",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "A plugin to read and write i18n translationsfrom and to google spreadsheets ",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "mocha --reporter spec"
7
+ "test": "mocha --reporter spec",
8
+ "build": "grunt build"
8
9
  },
9
10
  "repository": {
10
11
  "type": "git",
package/src/handler.js CHANGED
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
-
4
3
  /**
5
4
  *
6
5
  * @param translationFormat
@@ -13,6 +12,8 @@ module.exports.getHandler = function (translationFormat) {
13
12
  switch (translationFormat) {
14
13
  default:
15
14
  throw new Error('No handler available for the translation format ' + translationFormat);
15
+ case TRANSLATION_FORMATS.JSON_STRUCTURE:
16
+ return require('./handlers/json_structure');
16
17
  case TRANSLATION_FORMATS.LOCALE_JSON:
17
18
  return require('./handlers/locale_json');
18
19
  case TRANSLATION_FORMATS.GETTEXT:
@@ -0,0 +1,116 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs');
4
+ const withoutError = require('../helpers').withoutError
5
+ const constraints = require('../util/constraints');
6
+
7
+ module.exports.loadTranslationFile = function (filePath, callback) {
8
+
9
+ fs.access(filePath, function (err) {
10
+
11
+ if (err) {
12
+ callback({}); // we return the empty json
13
+ } else {
14
+
15
+ fs.readFile(filePath, function (err, data) {
16
+ callback(err ? {} : JSON.parse(data));
17
+ })
18
+ }
19
+ });
20
+ }
21
+
22
+ /**
23
+ * A wrapper to return a flat key: value object structure, used for uploading
24
+ * @param {string} filePath
25
+ * @param {function} callback
26
+ */
27
+ module.exports.getTranslationKeys = function (filePath, callback) {
28
+ const result = {};
29
+
30
+ const keyFlattener = function(structure, prefix) {
31
+ prefix = prefix || '';
32
+
33
+ Object.keys(structure).forEach((key) => {
34
+
35
+ const subStructure = structure[key];
36
+
37
+ if (typeof subStructure === 'object' && subStructure !== null && subStructure !== undefined) {
38
+ keyFlattener(subStructure, prefix + key + '.');
39
+ } else {
40
+ result[prefix + key] = subStructure;
41
+ }
42
+ })
43
+
44
+ }
45
+
46
+ this.loadTranslationFile(filePath, (parsedObject) => {
47
+ keyFlattener(parsedObject);
48
+ callback(result);
49
+ });
50
+ }
51
+
52
+ module.exports.updateTranslations = function (translationData, translationRootFolder, options, callback) {
53
+ const path = require('path');
54
+ const async = require('async');
55
+ const mod = this;
56
+ const fileUtils = require('../util/file-utils');
57
+
58
+ if (! fs.existsSync(translationRootFolder)) {
59
+ throw new Error('The folder ' + translationRootFolder + ' does not exist');
60
+ }
61
+
62
+ async.each(Object.keys(translationData), function(locale, done) {
63
+
64
+ // is it a comment or a real translation?
65
+ if (locale.substr(0, constraints.commentCollumnName.length) !== constraints.commentCollumnName) {
66
+
67
+ async.each(Object.keys(translationData[locale]), function (namespace, done2) {
68
+
69
+ const localeFileName = fileUtils.buildTranslationFileName(constraints.TRANSLATION_FORMATS.JSON_STRUCTURE, namespace, locale, options);
70
+ const file = path.resolve(translationRootFolder + '/' + localeFileName);
71
+
72
+ mod.loadTranslationFile(file, function (translations) {
73
+ const potentiallyUpdatedTranslations = translationData[locale][namespace];
74
+
75
+ Object.keys(potentiallyUpdatedTranslations).forEach((key) => {
76
+ const parts = key.split('.');
77
+ let tree = translations;
78
+
79
+ while (parts.length > 0) {
80
+ const part = parts.shift();
81
+
82
+ if (parts.length === 0) {
83
+ tree[part] = potentiallyUpdatedTranslations[key];
84
+ } else {
85
+ if (!tree[part]) {
86
+ tree[part] = {}
87
+ }
88
+
89
+ tree = tree[part];
90
+ }
91
+ }
92
+ });
93
+
94
+ // now we write
95
+ fs.writeFile(file, JSON.stringify(translations, null, 4), function (err) {
96
+ if (withoutError(err)) {
97
+ // console.info('Updated translations of %o', localeFileName);
98
+ }
99
+ done2();
100
+ })
101
+ })
102
+ }, function () {
103
+ done();
104
+ })
105
+
106
+ } else {
107
+ done();
108
+ }
109
+ }, function (err) {
110
+ if (withoutError(err, callback)) {
111
+ callback(null);
112
+ }
113
+ });
114
+
115
+
116
+ }
@@ -1,5 +1,6 @@
1
1
  const formats = {
2
- LOCALE_JSON: 'locale_json',
2
+ LOCALE_JSON: 'locale_json', // just a plain array - lorem.ipsum keys will be {'lorem.ipsum': 'whatever'}
3
+ JSON_STRUCTURE: 'json_structure', // lorem.ipsum keys will be converted into a json structure: {lorem: {ipsum: 'whatever'}}
3
4
  GETTEXT: 'gettext',
4
5
  PROPERTIES: 'properties',
5
6
  YAML: 'yml'
@@ -19,6 +19,7 @@ module.exports.buildTranslationFileName = function (format, namespace, locale, o
19
19
  throw new Error('Unknown extension for translation format ' + format);
20
20
  break;
21
21
  case constraints.TRANSLATION_FORMATS.LOCALE_JSON:
22
+ case constraints.TRANSLATION_FORMATS.JSON_STRUCTURE:
22
23
  extension = 'json';
23
24
  break;
24
25
  case constraints.TRANSLATION_FORMATS.GETTEXT:
package/test/test.js CHANGED
@@ -21,6 +21,10 @@ const timeout = 20000;
21
21
  var tmpFile = tmp.fileSync({postfix: '.csv'});
22
22
 
23
23
  const csv = require('fast-csv');
24
+ const fileUtils = require("../src/util/file-utils");
25
+ const yamlHandler = require("../src/handlers/yaml");
26
+ const rimraf = require("rimraf");
27
+ const yaml = require("js-yaml");
24
28
  const testFile = tmpFile.name;
25
29
  const csvData = [
26
30
  ['key', 'default', 'de', 'it', 'fr', 'pl', 'hu'],
@@ -53,8 +57,8 @@ function ensureFolder (folder) {
53
57
  }
54
58
 
55
59
 
56
- // const testFor = 'all' // 'connect', 'upload', 'import', 'properties', 'locale_json', 'gettext', 'yml'
57
- // const testFor = 'yml';
60
+ // const testFor = 'all' // 'connect', 'upload', 'import', 'properties', 'locale_json', 'gettext', 'yml', 'json_structure'
61
+ // const testFor = 'json_structure';
58
62
  const testFor = 'all';
59
63
 
60
64
  const tests = [
@@ -514,6 +518,118 @@ const tests = [
514
518
  });
515
519
  }
516
520
  },
521
+
522
+ {
523
+ name: 'should upload changes in the json_structure test project',
524
+ run: ['upload', 'json_structure'],
525
+ fnc: function (done) {
526
+ this.timeout(timeout);
527
+
528
+ const baseName = 'messages';
529
+
530
+ const options = {
531
+ translationFormat: 'json_structure',
532
+ fileBaseName: baseName,
533
+ keyId: csvData[0][0],
534
+ gid: 0,
535
+ namespaceSeparator: '.',
536
+ spreadsheetId: testSheetId,
537
+ credentials: accessData
538
+ }
539
+
540
+ const files = [];
541
+
542
+ const tempFolder = tmp.dirSync({prefix: 'trans-dyn-to-update'});
543
+ const handler = require('../src/handlers/json_structure');
544
+ const fileUtils = require('../src/util/file-utils');
545
+
546
+ const data = {};
547
+
548
+ csvData[0].forEach( function (key, index) {
549
+
550
+ if (index > 0) {
551
+ data[key] = {default: {}}
552
+ csvData.forEach(function (lines, i) {
553
+ if (i > 0 && lines[index]) {
554
+ data[key].default[lines[0]] = lines[index];
555
+ }
556
+ });
557
+ files.push(tempFolder.name + '/' + fileUtils.buildTranslationFileName(options.translationFormat, null, key, options));
558
+ }
559
+ });
560
+
561
+ handler.updateTranslations(data, tempFolder.name, options, () => {
562
+
563
+ app.exportToSpreadsheet(files, options, function (err) {
564
+ const rimraf = require("rimraf");
565
+ expect(err).to.be.null;
566
+
567
+ if (!err) {
568
+ connector(options.spreadsheetId, options.gid, accessData, function (err, sheet) {
569
+ expect(err).to.be.null;
570
+ expect(sheet).to.be.an('object');
571
+
572
+ sheet.getRows({
573
+ offset: 0,
574
+ limit: csvData.length - 1
575
+ }).then(function (rows) {
576
+ expect(rows).to.have.lengthOf(csvData.length - 1);
577
+ expect(rows[0][options.keyId]).to.equal(csvData[1][0]);
578
+ expect(rows[0].pl).to.equal(csvData[1][5]);
579
+ expect(rows[0].default).to.equal(csvData[1][1]);
580
+ expect(rows[0].hu).to.equal('Elfogadom'); // this was not part of the upload and should not be overwrittem
581
+ expect(rows[1].default).to.equal(csvData[2][1]);
582
+ expect(rows[1].de).to.equal(csvData[2][2]);
583
+ expect(rows[1].key).to.equal(csvData[2][0]);
584
+ rimraf.sync(tempFolder.name);
585
+ done()
586
+ })
587
+ })
588
+ } else {
589
+ rimraf.sync(tempFolder.name);
590
+ done()
591
+ }
592
+ })
593
+ });
594
+ }
595
+ },
596
+
597
+ {
598
+ name: 'should import updated json_structure keys from the test project',
599
+ run: ['import', 'json_structure'],
600
+ fnc: function (done) {
601
+ this.timeout(timeout);
602
+
603
+ const baseName = 'messages';
604
+
605
+ const options = {
606
+ translationFormat: 'json_structure',
607
+ fileBaseName: baseName,
608
+ namespaceSeparator: '.',
609
+ spreadsheetId: testSheetId_yaml,
610
+ credentials: accessData
611
+ };
612
+
613
+ const translationRoot = ensureFolder(path.resolve('./test/translations/' + options.translationFormat + '/'));
614
+ const testFile = path.resolve(translationRoot + '/' + baseName + '.en.json');
615
+
616
+ app.importFromSpreadsheet(translationRoot, options, function (err) {
617
+ expect(err).to.be.null
618
+
619
+ if (!err) {
620
+ const fs = require('fs');
621
+
622
+ expect(fs.existsSync(testFile)).to.equal(true);
623
+
624
+ const translations = require(testFile);
625
+
626
+ expect(translations.add_address).to.equal("Add new address");
627
+ expect(translations.additional.news).to.equal("Additional News");
628
+ }
629
+ done();
630
+ });
631
+ }
632
+ },
517
633
  ];
518
634
 
519
635