google-spreadsheet-translation-sync 1.4.0 → 1.4.1
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 +1 -0
- package/package.json +1 -1
- package/src/handler.js +2 -0
- package/src/handlers/wordpress.js +173 -0
- package/src/util/constraints.js +1 -0
- package/test/test.js +69 -1
package/README.md
CHANGED
|
@@ -101,6 +101,7 @@ Possible Values:
|
|
|
101
101
|
* `locale_json` (translations are stored in simple key/value json files)
|
|
102
102
|
* `json_structure` (translations are stored in a json tree, where the keys are split by the . character)
|
|
103
103
|
* `gettext` (utilizes [node gettext-parser](https://github.com/smhg/gettext-parser) for the work with po and mo files)
|
|
104
|
+
* `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
105
|
* `properties` (utilizes [propertie-reader](https://github.com/steveukx/properties) for java property files)
|
|
105
106
|
* `yml` (utilizes [js-yaml](https://github.com/nodeca/js-yaml) for symfony yaml translation files)
|
|
106
107
|
Default value: `locale_json`
|
package/package.json
CHANGED
package/src/handler.js
CHANGED
|
@@ -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
|
+
}
|
package/src/util/constraints.js
CHANGED
|
@@ -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
|
};
|
package/test/test.js
CHANGED
|
@@ -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'],
|