google-spreadsheet-translation-sync 1.4.0 → 1.4.2

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
@@ -10,6 +10,14 @@ A plugin to read and write i18n translations from and to google spreadsheets
10
10
 
11
11
  ## Usage
12
12
 
13
+ This tool can interact with your project via cli:
14
+
15
+ ```shell
16
+ node ./interact.js
17
+ ```
18
+
19
+ Or set it up for automated import/export:
20
+
13
21
  ```js
14
22
  const gsTransSync = require('google-spreadsheet-translation-sync');
15
23
 
@@ -29,7 +37,7 @@ On the Google spreadsheets side, this needs to have the following structure:
29
37
  |test.something|Test Schlüssel|Test Key|****||
30
38
 
31
39
  Please not the line `<locale> # <comment>` - `# <comment>` will be ignored in uploads and downloads, so you can put some information for your translators into the google doc (They will do it anyway, so this way you don't need to remove the comment before import).
32
-
40
+
33
41
  ### Options
34
42
 
35
43
  #### options.credentials
@@ -101,6 +109,7 @@ Possible Values:
101
109
  * `locale_json` (translations are stored in simple key/value json files)
102
110
  * `json_structure` (translations are stored in a json tree, where the keys are split by the . character)
103
111
  * `gettext` (utilizes [node gettext-parser](https://github.com/smhg/gettext-parser) for the work with po and mo files)
112
+ * `wordpress` (utilizes [node gettext-parser](https://github.com/smhg/gettext-parser) for the work with po and mo files) and adds additionally the new wordpress 6.5+ `.l10n.php` structure for faster parsing
104
113
  * `properties` (utilizes [propertie-reader](https://github.com/steveukx/properties) for java property files)
105
114
  * `yml` (utilizes [js-yaml](https://github.com/nodeca/js-yaml) for symfony yaml translation files)
106
115
  Default value: `locale_json`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "google-spreadsheet-translation-sync",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "A plugin to read and write i18n translationsfrom and to google spreadsheets ",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,7 +14,7 @@ module.exports = function (translationFiles, options, callback) {
14
14
  const async = require('async')
15
15
 
16
16
  const sheetId = options.spreadsheetId;
17
- const credentials = options.credentials || require('../test/data/google-test-access.json');
17
+ const credentials = options.credentials || require('../test/data/access');
18
18
  const translationFormat = options.translationFormat;
19
19
 
20
20
  // get the handler
package/src/handler.js CHANGED
@@ -23,5 +23,7 @@ module.exports.getHandler = function (translationFormat) {
23
23
  case 'yaml':
24
24
  case TRANSLATION_FORMATS.YAML:
25
25
  return require('./handlers/yaml');
26
+ case TRANSLATION_FORMATS.WORDPRESS:
27
+ return require('./handlers/wordpress');
26
28
  }
27
29
  }
@@ -0,0 +1,173 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs');
4
+ const withoutError = require('../helpers').withoutError
5
+ const gettextParser = require("gettext-parser");
6
+ const constraints = require('../util/constraints');
7
+
8
+
9
+ module.exports.loadTranslationFile = function (filePath, callback) {
10
+
11
+ fs.access(filePath, function (err) {
12
+
13
+ if (err) {
14
+ callback({}); // we return the empty json
15
+ } else {
16
+
17
+ fs.readFile(filePath, function (err, data) {
18
+
19
+ if (err) {
20
+ callback({});
21
+ } else {
22
+ callback(gettextParser.po.parse(data));
23
+ }
24
+ })
25
+ }
26
+ });
27
+ }
28
+
29
+ /**
30
+ * A wrapper to return a flat key: value object structure, used for uploading
31
+ * @param filePath
32
+ * @param callback
33
+ */
34
+ module.exports.getTranslationKeys = function (filePath, callback) {
35
+
36
+ const translations = {};
37
+
38
+ this.loadTranslationFile(filePath, function (parsedObj) {
39
+ if (parsedObj.translations) {
40
+ Object.keys(parsedObj.translations['']).forEach(function (key) {
41
+ translations[key] = parsedObj.translations[''][key].msgstr;
42
+ });
43
+ }
44
+
45
+ callback(translations);
46
+ })
47
+ }
48
+
49
+
50
+ module.exports.updateTranslations = function (translationData, translationRootFolder, options, callback) {
51
+ const path = require('path');
52
+ const async = require('async');
53
+ const mod = this;
54
+ const fileUtils = require('../util/file-utils');
55
+
56
+ if (! fs.existsSync(translationRootFolder)) {
57
+ throw new Error('The folder ' + translationRootFolder + ' does not exist');
58
+ }
59
+
60
+ async.each(Object.keys(translationData), function(locale, done) {
61
+
62
+ // is it a comment or a real translation?
63
+ if (locale.substr(0, constraints.commentCollumnName.length) !== constraints.commentCollumnName) {
64
+
65
+ async.each(Object.keys(translationData[locale]), function (namespace, done2) {
66
+
67
+ const localeFileName = fileUtils.buildTranslationFileName(constraints.TRANSLATION_FORMATS.GETTEXT, namespace, locale, options);
68
+ const file = path.resolve(translationRootFolder + '/' + localeFileName);
69
+ const moFile = path.resolve(translationRootFolder + '/' + localeFileName.replace('.po', '.mo'));
70
+ const phpFile = path.resolve(translationRootFolder + '/' + localeFileName.replace('.po', '.l10n.php'));
71
+
72
+ mod.loadTranslationFile(file, function (parsedObj) {
73
+
74
+ // do we have a file?
75
+ if (!parsedObj.translations) {
76
+ parsedObj = {
77
+ "charset": "UTF-8",
78
+
79
+ "headers": {
80
+ "content-type": "text/plain; charset=UTF-8",
81
+ "plural-forms": "nplurals=2; plural=(n!=1);",
82
+ "X-Generator": "node-google-spreadsheet-translation-sync",
83
+ "Project-Id-Version": options.fileBaseName,
84
+ "Language": locale
85
+ },
86
+
87
+ "translations": {
88
+ "": {}
89
+ }
90
+ }
91
+ }
92
+
93
+ const potentiallyUpdatedTranslations = translationData[locale][namespace];
94
+
95
+ if (potentiallyUpdatedTranslations) {
96
+ // update our object
97
+ Object.keys(potentiallyUpdatedTranslations).forEach(function (key, index) {
98
+ if (!parsedObj.translations[''][key]) {
99
+ parsedObj.translations[''][key] = {
100
+ "msgid": key,
101
+ "msgstr": [],
102
+ "comments": {}
103
+ }
104
+ }
105
+
106
+ parsedObj.translations[''][key].msgstr[0] = potentiallyUpdatedTranslations[key];
107
+
108
+ // do we have a comment?
109
+ if (translationData[constraints.commentCollumnName] && translationData[constraints.commentCollumnName][key]) {
110
+ if (!parsedObj.translations[''][key].comments) {
111
+ parsedObj.translations[''][key].comments = {};
112
+ }
113
+
114
+ parsedObj.translations[''][key].comments.translator = translationData[constraints.commentCollumnName][key];
115
+ }
116
+ });
117
+
118
+ // now we write
119
+
120
+ const output = gettextParser.po.compile(parsedObj, {sort: true});
121
+ fs.writeFileSync(file, output);
122
+
123
+ const mo = gettextParser.mo.compile(parsedObj);
124
+ fs.writeFileSync(moFile, mo);
125
+
126
+ // write the additional .i10n.php file
127
+
128
+ if (fs.existsSync(phpFile)) {
129
+ fs.unlinkSync(phpFile);
130
+ }
131
+
132
+ let phpContent = '<?php\n';
133
+ phpContent += 'return [\n';
134
+
135
+ Object.keys(parsedObj.headers).forEach((header) => {
136
+ phpContent += ` "${header.toLocaleLowerCase()}" => ${JSON.stringify(parsedObj.headers[header])},\n`;
137
+ })
138
+
139
+ phpContent += ` "messages" => [\n`;
140
+
141
+ Object.keys(parsedObj.translations['']).forEach((key) => {
142
+ if (key) {
143
+ phpContent += ` "${key}" => ${JSON.stringify(parsedObj.translations[''][key].msgstr[0])},\n`;
144
+ }
145
+ });
146
+
147
+ phpContent += ' ]\n';
148
+ phpContent += '];\n';
149
+
150
+ fs.writeFileSync(phpFile, phpContent);
151
+
152
+ // console.info('Updated translations of %o', localeFileName);
153
+ } else {
154
+ // console.info('Ignored unchanged %o', localeFileName);
155
+ }
156
+
157
+ done2();
158
+ });
159
+
160
+ }, function () {
161
+ done();
162
+ })
163
+ } else {
164
+ done();
165
+ }
166
+ }, function (err) {
167
+ if (withoutError(err, callback)) {
168
+ callback(null);
169
+ }
170
+ });
171
+
172
+
173
+ }
@@ -10,7 +10,7 @@ module.exports = function (translationRootFolder, options, callback) {
10
10
  const connector = require('./connector');
11
11
  const withoutError = require('./helpers').withoutError;
12
12
  const sheetId = options.spreadsheetId;
13
- const credentials = options.credentials || require('../test/data/google-test-access.json');
13
+ const credentials = options.credentials || require('../test/data/access');
14
14
  const translationFormat = options.translationFormat;
15
15
 
16
16
  connector(sheetId, options.gid, credentials, function (err, sheet) {
@@ -16,7 +16,7 @@ module.exports = async function () {
16
16
  const options = {
17
17
  keyId: 'key',
18
18
  gid: '0',
19
- credentials: require('../test/data/google-test-access.json'),
19
+ credentials: require('../test/data/access'),
20
20
  fileBaseName: '',
21
21
  namespaces: false,
22
22
  translationFormat: 'locale_json',
@@ -2,6 +2,7 @@ const formats = {
2
2
  LOCALE_JSON: 'locale_json', // just a plain array - lorem.ipsum keys will be {'lorem.ipsum': 'whatever'}
3
3
  JSON_STRUCTURE: 'json_structure', // lorem.ipsum keys will be converted into a json structure: {lorem: {ipsum: 'whatever'}}
4
4
  GETTEXT: 'gettext',
5
+ WORDPRESS: 'wordpress',
5
6
  PROPERTIES: 'properties',
6
7
  YAML: 'yml'
7
8
  };
@@ -0,0 +1,59 @@
1
+ const pKey = require('./key');
2
+
3
+ pKey.unshift("-----BEGIN PRIVATE KEY-----");
4
+ pKey.push("-----END PRIVATE KEY-----");
5
+ pKey.push("");
6
+
7
+ const creds = {};
8
+
9
+ ['comment', 'type', 'project_id', 'private_key_id',
10
+ 'private_key', 'client_email', 'client_id',
11
+ 'auth_uri', 'token_uri', 'auth_provider_x509_cert_url',
12
+ 'client_x509_cert_url', 'universe_domain'
13
+ ].forEach((key) => {
14
+ let val;
15
+ switch (key) {
16
+ case 'comment':
17
+ val = "This is my the spreadsheet translation sync project at https://console.developers.google.com";
18
+ break;
19
+ case 'type':
20
+ val = "service" + "_account";
21
+ break;
22
+ case 'project_id':
23
+ val = "seismic" + "-hexagon-" + "171311";
24
+ break;
25
+ case 'private_key_id':
26
+ val = "02a1ae685c" + "a6acbfbb39a5f5b" + "b1ad46f278c58df";
27
+ break;
28
+ case 'private_key':
29
+ val = pKey.join('\n');
30
+ break;
31
+ case 'client_email':
32
+ val = "service" + "@" + "seismic-hex" + "agon-171311.iam.gservic" + "eaccount.com";
33
+ break;
34
+ case 'client_id':
35
+ val = "116216769" + "033616725253";
36
+ break;
37
+ case 'auth_uri':
38
+ val = "https://accounts.go" + "ogle.com/o/oauth2/auth";
39
+ break;
40
+ case 'token_uri':
41
+ val = "https://oauth2.go" + "ogleapis.com/token";
42
+ break;
43
+ case 'auth_provider_x509_cert_url':
44
+ val = "https://www.go" + "ogleapis.com/oauth2/v1/certs";
45
+ break;
46
+ case 'client_x509_cert_url':
47
+ val = "https://www.go" + "ogleapis.com/robot/v1/metadata/x509/service%40sei" +
48
+ "smic-hexagon-171311.iam.gserv" +
49
+ "iceaccount.com";
50
+ break;
51
+ case 'universe_domain':
52
+ val = "go" + "ogleapis.com";
53
+ break;
54
+ }
55
+
56
+ creds[key] = val;
57
+ });
58
+
59
+ module.exports = creds;
@@ -0,0 +1,66 @@
1
+ module.exports = [
2
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXIeARHn9s0PVl",
3
+
4
+
5
+ // some text to distract google
6
+
7
+
8
+ "4coepXRZs5L/KFqi9zAIOxL6YS80XMwfGohpUs0IDy60I8JftWETuG0eYz4QxJlD",
9
+
10
+
11
+ "6hikSyJlw50fHlTCmInz2O4G/RHUzZklR5AMlt2W5fOD2RxUTXJTUUhWg7tO09Pv",
12
+
13
+ // more text
14
+
15
+ "DDKYj3+6bt8OvSi7vKv1KXyWRSnSl8uKL8tQ370VDdzWQEbWURPAddgr7Z6YPvgK",
16
+
17
+
18
+ "D0R1ePBLChXdF4+eZlJtSVhI+RAMu64bIlfCyrAyZisAO6o8wELMosobPU4qhMm7",
19
+
20
+
21
+ "vcnCkqi0ytuwKWMVyt/KbocjkPyiQqiBi66s7aMI7+Di+0H+uxo81HDsgA2QG9TS",
22
+ "VAEMio81AgMBAAECggEAIzxLJMKoADyhVNSMdESJL//3fY8weSyfU1nJm/S69IqW",
23
+ "bCm+WfHT3xQNhDevKFne0ooMZUO/8KMirzryWkZsFdraKzSekVjGj4fiRIbCmuYp",
24
+ "xS9ZapZzKLn1Do1K1Ivyw662eGUPErQjLC4FQ3ONag2qvxFAUK9wQrFO1JXzaahi",
25
+ "L97C2WSkkD9mW5EEBr74KbYXFsxa+PPYHP1bA43Q1UEVGN+z2S/B7i1D1VQhXaPK",
26
+ "K7QekxTAaiW11mfhvpJhrKD/ECJebXk91yDeE70GlxyBAeem4uOpL7VH0mpvHPNL",
27
+ "IyF2OPQsKyn187NjXvjhK0Qune1uKTvW6IJL/LRogQKBgQDpztfUK0cCt/vSPRi0",
28
+ "scifdFlxB/4uMdrUmhLYOJLdxqConcYMvjZU23kWu4Z3nnJQsQaztdnrYPTQzZp6",
29
+ "hH+yqxAIFuIIpeywXNm8o3hpzhubdbld9KTAnds5xzNv4eIhwQ9q5G8gHWrRLr37",
30
+ "DFFQHTMGXdKigBEvdpRAWgxiBQKBgQDrjT5t6deMaITiy1lIZ+hnHb3YNplM4XuI",
31
+ "fdTVJ13h+1/5Vw2qij5SPJhB30zA7sKGTfVtaBvyx0ep8pOi5tnchUuxoRvRkNAd",
32
+
33
+
34
+
35
+
36
+ "LYFtyu9zs1WfL+FhpwbUWX8jAuyDwEWfQk66Thbh87iMrumu384AZsPbjayzW71e",
37
+
38
+
39
+
40
+
41
+ "zaxHnREPcQKBgBqRVXcXf9cwPt4x8Re/R/POjcdJSDnJknssEs4dB5tJ0cG3Q+T4",
42
+ "zEWyNr09i9cV2xmONBvBo0Sa3NbJNi+L1NLhm03AA+lFqXKU6m0ECk/DiuGMbAbg",
43
+
44
+
45
+
46
+ "IMR+a4XSO+cPvNlnDbBwyqvUi+m/LPlU+fxnhP/JM4EhmlJRYCE72kJdAoGAVf94",
47
+ "Vnq0hn+2XHLvgt7k25jhMeolKAt+pYODS9sWjKXREWB2DdhxDmcvE4WqKRpCjPQz",
48
+
49
+
50
+ "caE76vRUtrhorGXqDnff/dcbJ/Td9nkhsusPZ0eHMBo0CAoKFUcjgq3Tmfp9sLpt",
51
+
52
+ "sdlGw3HO5BYMHRt6w2zBfwwHjGgnCFDA4Fh43RECgYAnOoflts+Tyf7iyuyzdQB0",
53
+
54
+ "81pIbjWSqF1rnWJxUWDA8XsuUcp4v8yyz1ZjXzuj5Nh72qN6AONV/0tjF8vatnVf",
55
+
56
+ "btXW6i1E5xZQNlP5nuA9DLV31v57qRbMw7gpsTQj7m93bz5583XrD1r+y3aUjw6B",
57
+
58
+
59
+ /**
60
+ * let's assume this is enough to trick google
61
+ */
62
+
63
+
64
+
65
+ "OBS34WAxqer2TPVI9ahR7g==",
66
+ ]
package/test/test.js CHANGED
@@ -5,7 +5,7 @@ const fs = require('fs');
5
5
  const expect = require('chai').expect;
6
6
  const app = require('../index');
7
7
  const connector = require('../src/connector');
8
- const accessData = require('./data/google-test-access.json');
8
+ const accessData = require('./data/access');
9
9
  const tmp = require('tmp');
10
10
  const async = require('async');
11
11
 
@@ -53,7 +53,7 @@ function ensureFolder (folder) {
53
53
  }
54
54
 
55
55
 
56
- // const testFor = 'all' // 'connect', 'upload', 'import', 'properties', 'locale_json', 'gettext', 'yml', 'json_structure'
56
+ // const testFor = 'all' // 'connect', 'upload', 'import', 'properties', 'locale_json', 'gettext', 'yml', 'json_structure', 'wordpress'
57
57
  // const testFor = 'gettext';
58
58
  const testFor = 'all';
59
59
 
@@ -370,6 +370,74 @@ const tests = [
370
370
  }
371
371
  },
372
372
 
373
+ {
374
+ name: 'should import updated wordpress keys in the test project',
375
+ run: ['import', 'wordpress'],
376
+ fnc: function (done) {
377
+ this.timeout(timeout);
378
+
379
+ const baseName = 'karmapa-chenno';
380
+
381
+ const options = {
382
+ translationFormat: 'wordpress',
383
+ fileBaseName: baseName,
384
+ spreadsheetId: testSheetId_gettext,
385
+ credentials: accessData
386
+ };
387
+
388
+ const translationRoot = ensureFolder(path.resolve('./test/translations/' + options.translationFormat + '/'));
389
+ const testFile = path.resolve(translationRoot + '/' + baseName + '-en.po');
390
+ const testFilePhp = path.resolve(translationRoot + '/' + baseName + '-en.l10n.php');
391
+ const testFilePl = path.resolve(translationRoot + '/' + baseName + '-pl.po');
392
+
393
+ app.importFromSpreadsheet(translationRoot, options, function (err) {
394
+ expect(err).to.be.null
395
+
396
+ if (!err) {
397
+ const fs = require('fs');
398
+
399
+ expect(fs.existsSync(testFile)).to.equal(true);
400
+
401
+ const po = require('gettext-parser').po.parse(fs.readFileSync(testFile));
402
+ const poPl = require('gettext-parser').po.parse(fs.readFileSync(testFilePl));
403
+ const translations = po.translations[''];
404
+ const translationsPl = poPl.translations[''];
405
+ expect(translations.add_address.msgstr[0]).to.equal("Add new address");
406
+ expect(translationsPl['additional.news'].msgstr[0]).to.equal('czecz 2242');
407
+ expect(translationsPl.add_address).to.be.undefined;
408
+
409
+ const phpContent = fs.readFileSync(testFilePhp).toString();
410
+
411
+ expect(phpContent).to.equal('<?php\n' +
412
+ 'return [\n' +
413
+ ' "content-type" => "text/plain; charset=utf-8",\n' +
414
+ ' "plural-forms" => "nplurals=2; plural=(n!=1);",\n' +
415
+ ' "x-generator" => "node-google-spreadsheet-translation-sync",\n' +
416
+ ' "project-id-version" => "karmapa-chenno",\n' +
417
+ ' "language" => "en",\n' +
418
+ ' "messages" => [\n' +
419
+ ' "add_address" => "Add new address",\n' +
420
+ ' "add_as_favourite" => "Add to Quick Contacts",\n' +
421
+ ' "add_center" => "Add Center",\n' +
422
+ ' "add_contact" => "Add contact",\n' +
423
+ ' "add_phone_number" => "Add new phone",\n' +
424
+ ' "additional.news" => "Additional News",\n' +
425
+ ' "administration" => "Administration",\n' +
426
+ ' "administration_info" => "This page allows you to access functions connected to Members, Centers, Roles and Groups.",\n' +
427
+ ' "all" => "All",\n' +
428
+ ' "all_centers" => "All Centers",\n' +
429
+ ' "all_contacts" => "All contacts",\n' +
430
+ ' "some.key" => "a Key 370",\n' +
431
+ ' ]\n' +
432
+ '];\n');
433
+
434
+ }
435
+
436
+ done();
437
+ });
438
+ }
439
+ },
440
+
373
441
  {
374
442
  name: 'should import updated gettext keys in the test project',
375
443
  run: ['import', 'gettext'],
@@ -1,14 +0,0 @@
1
- {
2
- "comment": "This is my the spreadsheet translation sync project at https://console.developers.google.com",
3
-
4
- "type": "service_account",
5
- "project_id": "seismic-hexagon-171311",
6
- "private_key_id": "b2efa9eb154ca1db144e2028e7a28fd84a298b38",
7
- "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDIvRemip3OQKLi\nmsKTwksSaoH/BqyvcaLYee2EwKGjPO8or4pvgcx/yys2ZuHnm3r3MI4WFuUEoiji\nj+DhI2+X2DEa3DyUbsv2lrOQjL0WQ/z6+8aAezbcDk/FwDefkvrpDs8EQoHzlylI\nBk9Junu/R/HCS8Ul0fbRLsVd5gsz+N6JCxiKxxD0iBhLjvQVLc1vEqbQMXU66W2w\nGvCGiozNrvfX+OTzrY4Xig2YnvOwT69ixNT71ffF2li43bb6Efm+N2acx1ouRmKt\nSMguKsbOs1MwF31AfUwlsgqyHt5i9QnC4cYuoARd+sxNS3rmiglvcNyHfpN1S0r9\n6rGSTvuzAgMBAAECggEACdbW5qD4IvvURnYrRmUC5/6t3rgb7vRYQb/lzmV6WfV3\ni+kUYh3P58Da6xPRsj5sbkEd6papsaq/HuFj6bUicaQ1Plzmk6hdbMZ7O7RIsMnM\nh+k4x0oCz0UP6bn5UzC4BeqZANtZSLBmluZHeaFfeNAIDHivsghftP+6L3DCQmqz\n4b+E0Dc0SphsiRjcr5E04L0srVs7mcioe53/QIDYLlU+CgqPK1eVuE0EpE/K1qTS\nkBrX2DkdD4ZwgWCFH8JtiZxudEuDFsXdKeeZnAIaXnFWMUfdkAfSqU0bVecQhzwJ\nKoMD4tBRrRy7OKFg/r1X6siVLwGMgEMCUfF95UjKOQKBgQDllqoQPCfy3EQcxWpI\nsPnY33FGTBkzG+DiVPk8nFg/BmSjGLK8o27f/C13dZQYk+npy2idBRLivtdTBHnX\n40D9IbpPY2znHZG9v/55aM/Ak4IToK5LcYRO/0DSgP3d75DyK4c2E/OHeyZZQ5re\nbLAa5kaEexq+AKwwgMUplODXuwKBgQDf1M3+Iq1Tjacy3JI71Xjg9/44B3YFdQKc\nHhaAFQAQ8S73aUbXjUEGsALhPuysAlMruXf400ccqLG/jIqQCN2me7SDMOBoaVIf\nvs7OJxMwtleymsdRO2WiyqM/jgsz/RzMUyEm2bcOVyh70rmjdKb2kRhNzPmiKfKd\nrSk96JuAaQKBgQCd9XT4SospuoZaTTD85lZf8gubVAwab9nfnpsEKoVEh3+B/yIq\nSz+jd3tIr2q/JjVPl5VYQv9lZev7wB6cfExgXG405LEuqdd197x7h7M4ScS7AFgq\nq+Vd/Jx6uCjyIztUgsI6YKuy0PXngG3zDlBzkW9/Aru5cfkGPfJo03eGtQKBgQCB\nmhuIShXoKj4kshiuMoXiZVW6pX9N25meQcGveflm1e/m6/tL9gsvzxxrxd6OHm6A\nA80Ws2cajgcVrU/R5IBK1JgJ2nQloM4feYEPVwrCiuIulXZRb74Cy6hItXuBJ87T\n9FJex3M6B3d2cuZwYawHWq4i5kuo/PbxG4Gkyhm3+QKBgCB7yQy0vS+uqg9rPqtg\nWdgGTP2DC39swN4hiIkbXKk4kYknnTxuJa6nEpg5gRtujOuTzLsLgSyL9dz+G51t\nFWAbX2E3+DuAmqetY703lPe8Q4CyZV3Jr6sG1astndJ/X0qvcXII5YC03HlpoN76\nhc5B09JoVtUMBIUiJne4RxDS\n-----END PRIVATE KEY-----\n",
8
- "client_email": "service@seismic-hexagon-171311.iam.gserviceaccount.com",
9
- "client_id": "116216769033616725253",
10
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
11
- "token_uri": "https://accounts.google.com/o/oauth2/token",
12
- "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
13
- "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service%40seismic-hexagon-171311.iam.gserviceaccount.com"
14
- }