@vocab/phrase 1.1.0 → 1.2.0
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 +66 -5
- package/dist/declarations/src/csv.d.ts +10 -0
- package/dist/declarations/src/phrase-api.d.ts +5 -3
- package/dist/declarations/src/push-translations.d.ts +2 -1
- package/dist/vocab-phrase.cjs.dev.js +113 -28
- package/dist/vocab-phrase.cjs.prod.js +113 -28
- package/dist/vocab-phrase.esm.js +113 -28
- package/package.json +8 -4
- package/CHANGELOG.md +0 -145
- package/src/file.ts +0 -4
- package/src/index.ts +0 -2
- package/src/logger.ts +0 -9
- package/src/phrase-api.ts +0 -181
- package/src/pull-translations.test.ts +0 -214
- package/src/pull-translations.ts +0 -114
- package/src/push-translations.test.ts +0 -168
- package/src/push-translations.ts +0 -67
package/README.md
CHANGED
|
@@ -422,20 +422,81 @@ Or to re-run the compiler when files change use:
|
|
|
422
422
|
$ vocab compile --watch
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
-
## External
|
|
425
|
+
## External Translation Tooling
|
|
426
426
|
|
|
427
427
|
Vocab can be used to synchronize your translations with translations from a remote translation platform.
|
|
428
428
|
|
|
429
|
-
| Platform
|
|
430
|
-
|
|
|
431
|
-
| [Phrase]
|
|
429
|
+
| Platform | Environment Variables |
|
|
430
|
+
| -------- | ----------------------------------- |
|
|
431
|
+
| [Phrase] | PHRASE_PROJECT_ID, PHRASE_API_TOKEN |
|
|
432
432
|
|
|
433
433
|
```bash
|
|
434
434
|
$ vocab push --branch my-branch
|
|
435
|
-
$ vocab push --branch my-branch --delete-unused-keys
|
|
436
435
|
$ vocab pull --branch my-branch
|
|
437
436
|
```
|
|
438
437
|
|
|
438
|
+
### [Phrase] Platform Features
|
|
439
|
+
|
|
440
|
+
#### Delete Unused keys
|
|
441
|
+
|
|
442
|
+
When uploading translations, Phrase identifies keys that exist in the Phrase project, but were not
|
|
443
|
+
referenced in the upload. These keys can be deleted from Phrase by providing the
|
|
444
|
+
`---delete-unused-keys` flag to `vocab push`. E.g.
|
|
445
|
+
|
|
446
|
+
```sh
|
|
447
|
+
$ vocab push --branch my-branch --delete-unused-keys
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
[phrase]: https://developers.phrase.com/api/
|
|
451
|
+
|
|
452
|
+
#### [Tags]
|
|
453
|
+
|
|
454
|
+
`vocab push` supports uploading [tags] to Phrase.
|
|
455
|
+
|
|
456
|
+
Tags can be added to an individual key via the `tags` property:
|
|
457
|
+
|
|
458
|
+
```jsonc
|
|
459
|
+
// translations.json
|
|
460
|
+
{
|
|
461
|
+
"Hello": {
|
|
462
|
+
"message": "Hello",
|
|
463
|
+
"tags": ["greeting", "home_page"]
|
|
464
|
+
},
|
|
465
|
+
"Goodbye": {
|
|
466
|
+
"message": "Goodbye",
|
|
467
|
+
"tags": ["home_page"]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Tags can also be added under a top-level `_meta` field. This will result in the tags applying to all
|
|
473
|
+
keys specified in the file:
|
|
474
|
+
|
|
475
|
+
```jsonc
|
|
476
|
+
// translations.json
|
|
477
|
+
{
|
|
478
|
+
"_meta": {
|
|
479
|
+
"tags": ["home_page"]
|
|
480
|
+
},
|
|
481
|
+
"Hello": {
|
|
482
|
+
"message": "Hello",
|
|
483
|
+
"tags": ["greeting"]
|
|
484
|
+
},
|
|
485
|
+
"Goodbye": {
|
|
486
|
+
"message": "Goodbye"
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
In the above example, both the `Hello` and `Goodbye` keys would have the `home_page` tag attached to
|
|
492
|
+
them, but only the `Hello` key would have the `usage_greeting` tag attached to it.
|
|
493
|
+
|
|
494
|
+
**NOTE**: Only tags specified on keys in your [`devLanguage`][configuration] will be uploaded.
|
|
495
|
+
Tags on keys in other languages will be ignored.
|
|
496
|
+
|
|
497
|
+
[tags]: https://support.phrase.com/hc/en-us/articles/5822598372252-Tags-Strings-
|
|
498
|
+
[configuration]: #Configuration
|
|
499
|
+
|
|
439
500
|
## Troubleshooting
|
|
440
501
|
|
|
441
502
|
### Problem: Passed locale is being ignored or using en-US instead
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TranslationsByLanguage } from '@vocab/types';
|
|
2
|
+
export declare function translationsToCsv(translations: TranslationsByLanguage, devLanguage: string): {
|
|
3
|
+
csvString: string;
|
|
4
|
+
localeMapping: {
|
|
5
|
+
[k: string]: number;
|
|
6
|
+
};
|
|
7
|
+
keyIndex: number;
|
|
8
|
+
commentIndex: number;
|
|
9
|
+
tagColumn: number;
|
|
10
|
+
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { TranslationsByKey } from './../../types/src/index';
|
|
2
1
|
import type { TranslationsByLanguage } from '@vocab/types';
|
|
3
2
|
import fetch from 'node-fetch';
|
|
4
3
|
export declare function callPhrase<T = any>(relativePath: string, options?: Parameters<typeof fetch>[1]): Promise<T>;
|
|
5
4
|
export declare function pullAllTranslations(branch: string): Promise<TranslationsByLanguage>;
|
|
6
|
-
export declare function
|
|
5
|
+
export declare function pushTranslations(translationsByLanguage: TranslationsByLanguage, { devLanguage, branch }: {
|
|
6
|
+
devLanguage: string;
|
|
7
|
+
branch: string;
|
|
8
|
+
}): Promise<{
|
|
7
9
|
uploadId: string;
|
|
8
10
|
}>;
|
|
9
|
-
export declare function deleteUnusedKeys(uploadId: string,
|
|
11
|
+
export declare function deleteUnusedKeys(uploadId: string, branch: string): Promise<void>;
|
|
10
12
|
export declare function ensureBranch(branch: string): Promise<void>;
|
|
@@ -4,7 +4,8 @@ interface PushOptions {
|
|
|
4
4
|
deleteUnusedKeys?: boolean;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Uploads translations to the Phrase API for each language.
|
|
8
|
+
* A unique namespace is appended to each key using the file path the key came from.
|
|
8
9
|
*/
|
|
9
10
|
export declare function push({ branch, deleteUnusedKeys }: PushOptions, config: UserConfig): Promise<void>;
|
|
10
11
|
export {};
|
|
@@ -9,6 +9,7 @@ var FormData = require('form-data');
|
|
|
9
9
|
var fetch = require('node-fetch');
|
|
10
10
|
var chalk = require('chalk');
|
|
11
11
|
var debug = require('debug');
|
|
12
|
+
var sync = require('csv-stringify/sync');
|
|
12
13
|
|
|
13
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
14
15
|
|
|
@@ -27,6 +28,44 @@ const log = (...params) => {
|
|
|
27
28
|
console.log(chalk__default['default'].yellow('Vocab'), ...params);
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
function translationsToCsv(translations, devLanguage) {
|
|
32
|
+
const languages = Object.keys(translations);
|
|
33
|
+
const altLanguages = languages.filter(language => language !== devLanguage); // Ensure languages are ordered for locale mapping
|
|
34
|
+
|
|
35
|
+
const orderedLanguages = [devLanguage, ...altLanguages];
|
|
36
|
+
const devLanguageTranslations = translations[devLanguage];
|
|
37
|
+
const csv = Object.entries(devLanguageTranslations).map(([key, {
|
|
38
|
+
message,
|
|
39
|
+
description,
|
|
40
|
+
tags
|
|
41
|
+
}]) => {
|
|
42
|
+
const altTranslationMessages = altLanguages.map(language => {
|
|
43
|
+
var _translations$languag, _translations$languag2;
|
|
44
|
+
|
|
45
|
+
return (_translations$languag = translations[language]) === null || _translations$languag === void 0 ? void 0 : (_translations$languag2 = _translations$languag[key]) === null || _translations$languag2 === void 0 ? void 0 : _translations$languag2.message;
|
|
46
|
+
});
|
|
47
|
+
return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
48
|
+
});
|
|
49
|
+
const csvString = sync.stringify(csv, {
|
|
50
|
+
delimiter: ',',
|
|
51
|
+
header: false
|
|
52
|
+
}); // Column indices start at 1
|
|
53
|
+
|
|
54
|
+
const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
|
|
55
|
+
const keyIndex = orderedLanguages.length + 1;
|
|
56
|
+
const commentIndex = keyIndex + 1;
|
|
57
|
+
const tagColumn = commentIndex + 1;
|
|
58
|
+
return {
|
|
59
|
+
csvString,
|
|
60
|
+
localeMapping,
|
|
61
|
+
keyIndex,
|
|
62
|
+
commentIndex,
|
|
63
|
+
tagColumn
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* eslint-disable no-console */
|
|
68
|
+
|
|
30
69
|
function _callPhrase(path, options = {}) {
|
|
31
70
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
32
71
|
|
|
@@ -109,31 +148,56 @@ async function pullAllTranslations(branch) {
|
|
|
109
148
|
|
|
110
149
|
return translations;
|
|
111
150
|
}
|
|
112
|
-
async function
|
|
151
|
+
async function pushTranslations(translationsByLanguage, {
|
|
152
|
+
devLanguage,
|
|
153
|
+
branch
|
|
154
|
+
}) {
|
|
113
155
|
const formData = new FormData__default['default']();
|
|
114
|
-
const
|
|
156
|
+
const {
|
|
157
|
+
csvString,
|
|
158
|
+
localeMapping,
|
|
159
|
+
keyIndex,
|
|
160
|
+
commentIndex,
|
|
161
|
+
tagColumn
|
|
162
|
+
} = translationsToCsv(translationsByLanguage, devLanguage);
|
|
163
|
+
const fileContents = Buffer.from(csvString);
|
|
115
164
|
formData.append('file', fileContents, {
|
|
116
|
-
contentType: '
|
|
117
|
-
filename:
|
|
165
|
+
contentType: 'text/csv',
|
|
166
|
+
filename: 'translations.csv'
|
|
118
167
|
});
|
|
119
|
-
formData.append('file_format', '
|
|
120
|
-
formData.append('locale_id', locale);
|
|
168
|
+
formData.append('file_format', 'csv');
|
|
121
169
|
formData.append('branch', branch);
|
|
122
170
|
formData.append('update_translations', 'true');
|
|
123
|
-
|
|
124
|
-
const {
|
|
125
|
-
|
|
126
|
-
}
|
|
171
|
+
|
|
172
|
+
for (const [locale, index] of Object.entries(localeMapping)) {
|
|
173
|
+
formData.append(`locale_mapping[${locale}]`, index);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
formData.append('format_options[key_index]', keyIndex);
|
|
177
|
+
formData.append('format_options[comment_index]', commentIndex);
|
|
178
|
+
formData.append('format_options[tag_column]', tagColumn);
|
|
179
|
+
formData.append('format_options[enable_pluralization]', 'false');
|
|
180
|
+
log('Uploading translations');
|
|
181
|
+
const result = await callPhrase(`uploads`, {
|
|
127
182
|
method: 'POST',
|
|
128
183
|
body: formData
|
|
129
184
|
});
|
|
130
|
-
|
|
131
|
-
|
|
185
|
+
trace('Upload result:\n', result);
|
|
186
|
+
|
|
187
|
+
if (result && 'id' in result) {
|
|
188
|
+
log('Upload ID:', result.id, '\n');
|
|
189
|
+
log('Successfully Uploaded\n');
|
|
190
|
+
} else {
|
|
191
|
+
log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
|
|
192
|
+
log('Response:', result);
|
|
193
|
+
throw new Error('Error uploading');
|
|
194
|
+
}
|
|
195
|
+
|
|
132
196
|
return {
|
|
133
|
-
uploadId: id
|
|
197
|
+
uploadId: result.id
|
|
134
198
|
};
|
|
135
199
|
}
|
|
136
|
-
async function deleteUnusedKeys(uploadId,
|
|
200
|
+
async function deleteUnusedKeys(uploadId, branch) {
|
|
137
201
|
const query = `unmentioned_in_upload:${uploadId}`;
|
|
138
202
|
const {
|
|
139
203
|
records_affected
|
|
@@ -144,7 +208,6 @@ async function deleteUnusedKeys(uploadId, locale, branch) {
|
|
|
144
208
|
},
|
|
145
209
|
body: JSON.stringify({
|
|
146
210
|
branch,
|
|
147
|
-
locale_id: locale,
|
|
148
211
|
q: query
|
|
149
212
|
})
|
|
150
213
|
});
|
|
@@ -180,7 +243,8 @@ async function pull({
|
|
|
180
243
|
|
|
181
244
|
const allVocabTranslations = await core.loadAllTranslations({
|
|
182
245
|
fallbacks: 'none',
|
|
183
|
-
includeNodeModules: false
|
|
246
|
+
includeNodeModules: false,
|
|
247
|
+
withTags: true
|
|
184
248
|
}, config);
|
|
185
249
|
|
|
186
250
|
for (const loadedTranslation of allVocabTranslations) {
|
|
@@ -198,6 +262,11 @@ async function pull({
|
|
|
198
262
|
defaultValues[key] = { ...defaultValues[key],
|
|
199
263
|
...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
|
|
200
264
|
};
|
|
265
|
+
} // Only write a `_meta` field if necessary
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
269
|
+
defaultValues._meta = loadedTranslation.metadata;
|
|
201
270
|
}
|
|
202
271
|
|
|
203
272
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
@@ -235,7 +304,8 @@ async function pull({
|
|
|
235
304
|
}
|
|
236
305
|
|
|
237
306
|
/**
|
|
238
|
-
*
|
|
307
|
+
* Uploads translations to the Phrase API for each language.
|
|
308
|
+
* A unique namespace is appended to each key using the file path the key came from.
|
|
239
309
|
*/
|
|
240
310
|
async function push({
|
|
241
311
|
branch,
|
|
@@ -243,7 +313,8 @@ async function push({
|
|
|
243
313
|
}, config) {
|
|
244
314
|
const allLanguageTranslations = await core.loadAllTranslations({
|
|
245
315
|
fallbacks: 'none',
|
|
246
|
-
includeNodeModules: false
|
|
316
|
+
includeNodeModules: false,
|
|
317
|
+
withTags: true
|
|
247
318
|
}, config);
|
|
248
319
|
trace(`Pushing translations to branch ${branch}`);
|
|
249
320
|
const allLanguages = config.languages.map(v => v.name);
|
|
@@ -263,23 +334,37 @@ async function push({
|
|
|
263
334
|
phraseTranslations[language] = {};
|
|
264
335
|
}
|
|
265
336
|
|
|
337
|
+
const {
|
|
338
|
+
metadata: {
|
|
339
|
+
tags: sharedTags = []
|
|
340
|
+
}
|
|
341
|
+
} = loadedTranslation;
|
|
342
|
+
|
|
266
343
|
for (const localKey of Object.keys(localTranslations)) {
|
|
267
344
|
const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
|
|
268
|
-
|
|
345
|
+
const {
|
|
346
|
+
tags = [],
|
|
347
|
+
...localTranslation
|
|
348
|
+
} = localTranslations[localKey];
|
|
349
|
+
|
|
350
|
+
if (language === config.devLanguage) {
|
|
351
|
+
localTranslation.tags = [...tags, ...sharedTags];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
phraseTranslations[language][phraseKey] = localTranslation;
|
|
269
355
|
}
|
|
270
356
|
}
|
|
271
357
|
}
|
|
272
358
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
359
|
+
const {
|
|
360
|
+
uploadId
|
|
361
|
+
} = await pushTranslations(phraseTranslations, {
|
|
362
|
+
devLanguage: config.devLanguage,
|
|
363
|
+
branch
|
|
364
|
+
});
|
|
278
365
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
}
|
|
366
|
+
if (deleteUnusedKeys$1) {
|
|
367
|
+
await deleteUnusedKeys(uploadId, branch);
|
|
283
368
|
}
|
|
284
369
|
}
|
|
285
370
|
|
|
@@ -9,6 +9,7 @@ var FormData = require('form-data');
|
|
|
9
9
|
var fetch = require('node-fetch');
|
|
10
10
|
var chalk = require('chalk');
|
|
11
11
|
var debug = require('debug');
|
|
12
|
+
var sync = require('csv-stringify/sync');
|
|
12
13
|
|
|
13
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
14
15
|
|
|
@@ -27,6 +28,44 @@ const log = (...params) => {
|
|
|
27
28
|
console.log(chalk__default['default'].yellow('Vocab'), ...params);
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
function translationsToCsv(translations, devLanguage) {
|
|
32
|
+
const languages = Object.keys(translations);
|
|
33
|
+
const altLanguages = languages.filter(language => language !== devLanguage); // Ensure languages are ordered for locale mapping
|
|
34
|
+
|
|
35
|
+
const orderedLanguages = [devLanguage, ...altLanguages];
|
|
36
|
+
const devLanguageTranslations = translations[devLanguage];
|
|
37
|
+
const csv = Object.entries(devLanguageTranslations).map(([key, {
|
|
38
|
+
message,
|
|
39
|
+
description,
|
|
40
|
+
tags
|
|
41
|
+
}]) => {
|
|
42
|
+
const altTranslationMessages = altLanguages.map(language => {
|
|
43
|
+
var _translations$languag, _translations$languag2;
|
|
44
|
+
|
|
45
|
+
return (_translations$languag = translations[language]) === null || _translations$languag === void 0 ? void 0 : (_translations$languag2 = _translations$languag[key]) === null || _translations$languag2 === void 0 ? void 0 : _translations$languag2.message;
|
|
46
|
+
});
|
|
47
|
+
return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
48
|
+
});
|
|
49
|
+
const csvString = sync.stringify(csv, {
|
|
50
|
+
delimiter: ',',
|
|
51
|
+
header: false
|
|
52
|
+
}); // Column indices start at 1
|
|
53
|
+
|
|
54
|
+
const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
|
|
55
|
+
const keyIndex = orderedLanguages.length + 1;
|
|
56
|
+
const commentIndex = keyIndex + 1;
|
|
57
|
+
const tagColumn = commentIndex + 1;
|
|
58
|
+
return {
|
|
59
|
+
csvString,
|
|
60
|
+
localeMapping,
|
|
61
|
+
keyIndex,
|
|
62
|
+
commentIndex,
|
|
63
|
+
tagColumn
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* eslint-disable no-console */
|
|
68
|
+
|
|
30
69
|
function _callPhrase(path, options = {}) {
|
|
31
70
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
32
71
|
|
|
@@ -109,31 +148,56 @@ async function pullAllTranslations(branch) {
|
|
|
109
148
|
|
|
110
149
|
return translations;
|
|
111
150
|
}
|
|
112
|
-
async function
|
|
151
|
+
async function pushTranslations(translationsByLanguage, {
|
|
152
|
+
devLanguage,
|
|
153
|
+
branch
|
|
154
|
+
}) {
|
|
113
155
|
const formData = new FormData__default['default']();
|
|
114
|
-
const
|
|
156
|
+
const {
|
|
157
|
+
csvString,
|
|
158
|
+
localeMapping,
|
|
159
|
+
keyIndex,
|
|
160
|
+
commentIndex,
|
|
161
|
+
tagColumn
|
|
162
|
+
} = translationsToCsv(translationsByLanguage, devLanguage);
|
|
163
|
+
const fileContents = Buffer.from(csvString);
|
|
115
164
|
formData.append('file', fileContents, {
|
|
116
|
-
contentType: '
|
|
117
|
-
filename:
|
|
165
|
+
contentType: 'text/csv',
|
|
166
|
+
filename: 'translations.csv'
|
|
118
167
|
});
|
|
119
|
-
formData.append('file_format', '
|
|
120
|
-
formData.append('locale_id', locale);
|
|
168
|
+
formData.append('file_format', 'csv');
|
|
121
169
|
formData.append('branch', branch);
|
|
122
170
|
formData.append('update_translations', 'true');
|
|
123
|
-
|
|
124
|
-
const {
|
|
125
|
-
|
|
126
|
-
}
|
|
171
|
+
|
|
172
|
+
for (const [locale, index] of Object.entries(localeMapping)) {
|
|
173
|
+
formData.append(`locale_mapping[${locale}]`, index);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
formData.append('format_options[key_index]', keyIndex);
|
|
177
|
+
formData.append('format_options[comment_index]', commentIndex);
|
|
178
|
+
formData.append('format_options[tag_column]', tagColumn);
|
|
179
|
+
formData.append('format_options[enable_pluralization]', 'false');
|
|
180
|
+
log('Uploading translations');
|
|
181
|
+
const result = await callPhrase(`uploads`, {
|
|
127
182
|
method: 'POST',
|
|
128
183
|
body: formData
|
|
129
184
|
});
|
|
130
|
-
|
|
131
|
-
|
|
185
|
+
trace('Upload result:\n', result);
|
|
186
|
+
|
|
187
|
+
if (result && 'id' in result) {
|
|
188
|
+
log('Upload ID:', result.id, '\n');
|
|
189
|
+
log('Successfully Uploaded\n');
|
|
190
|
+
} else {
|
|
191
|
+
log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
|
|
192
|
+
log('Response:', result);
|
|
193
|
+
throw new Error('Error uploading');
|
|
194
|
+
}
|
|
195
|
+
|
|
132
196
|
return {
|
|
133
|
-
uploadId: id
|
|
197
|
+
uploadId: result.id
|
|
134
198
|
};
|
|
135
199
|
}
|
|
136
|
-
async function deleteUnusedKeys(uploadId,
|
|
200
|
+
async function deleteUnusedKeys(uploadId, branch) {
|
|
137
201
|
const query = `unmentioned_in_upload:${uploadId}`;
|
|
138
202
|
const {
|
|
139
203
|
records_affected
|
|
@@ -144,7 +208,6 @@ async function deleteUnusedKeys(uploadId, locale, branch) {
|
|
|
144
208
|
},
|
|
145
209
|
body: JSON.stringify({
|
|
146
210
|
branch,
|
|
147
|
-
locale_id: locale,
|
|
148
211
|
q: query
|
|
149
212
|
})
|
|
150
213
|
});
|
|
@@ -180,7 +243,8 @@ async function pull({
|
|
|
180
243
|
|
|
181
244
|
const allVocabTranslations = await core.loadAllTranslations({
|
|
182
245
|
fallbacks: 'none',
|
|
183
|
-
includeNodeModules: false
|
|
246
|
+
includeNodeModules: false,
|
|
247
|
+
withTags: true
|
|
184
248
|
}, config);
|
|
185
249
|
|
|
186
250
|
for (const loadedTranslation of allVocabTranslations) {
|
|
@@ -198,6 +262,11 @@ async function pull({
|
|
|
198
262
|
defaultValues[key] = { ...defaultValues[key],
|
|
199
263
|
...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
|
|
200
264
|
};
|
|
265
|
+
} // Only write a `_meta` field if necessary
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
269
|
+
defaultValues._meta = loadedTranslation.metadata;
|
|
201
270
|
}
|
|
202
271
|
|
|
203
272
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
@@ -235,7 +304,8 @@ async function pull({
|
|
|
235
304
|
}
|
|
236
305
|
|
|
237
306
|
/**
|
|
238
|
-
*
|
|
307
|
+
* Uploads translations to the Phrase API for each language.
|
|
308
|
+
* A unique namespace is appended to each key using the file path the key came from.
|
|
239
309
|
*/
|
|
240
310
|
async function push({
|
|
241
311
|
branch,
|
|
@@ -243,7 +313,8 @@ async function push({
|
|
|
243
313
|
}, config) {
|
|
244
314
|
const allLanguageTranslations = await core.loadAllTranslations({
|
|
245
315
|
fallbacks: 'none',
|
|
246
|
-
includeNodeModules: false
|
|
316
|
+
includeNodeModules: false,
|
|
317
|
+
withTags: true
|
|
247
318
|
}, config);
|
|
248
319
|
trace(`Pushing translations to branch ${branch}`);
|
|
249
320
|
const allLanguages = config.languages.map(v => v.name);
|
|
@@ -263,23 +334,37 @@ async function push({
|
|
|
263
334
|
phraseTranslations[language] = {};
|
|
264
335
|
}
|
|
265
336
|
|
|
337
|
+
const {
|
|
338
|
+
metadata: {
|
|
339
|
+
tags: sharedTags = []
|
|
340
|
+
}
|
|
341
|
+
} = loadedTranslation;
|
|
342
|
+
|
|
266
343
|
for (const localKey of Object.keys(localTranslations)) {
|
|
267
344
|
const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
|
|
268
|
-
|
|
345
|
+
const {
|
|
346
|
+
tags = [],
|
|
347
|
+
...localTranslation
|
|
348
|
+
} = localTranslations[localKey];
|
|
349
|
+
|
|
350
|
+
if (language === config.devLanguage) {
|
|
351
|
+
localTranslation.tags = [...tags, ...sharedTags];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
phraseTranslations[language][phraseKey] = localTranslation;
|
|
269
355
|
}
|
|
270
356
|
}
|
|
271
357
|
}
|
|
272
358
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
359
|
+
const {
|
|
360
|
+
uploadId
|
|
361
|
+
} = await pushTranslations(phraseTranslations, {
|
|
362
|
+
devLanguage: config.devLanguage,
|
|
363
|
+
branch
|
|
364
|
+
});
|
|
278
365
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
}
|
|
366
|
+
if (deleteUnusedKeys$1) {
|
|
367
|
+
await deleteUnusedKeys(uploadId, branch);
|
|
283
368
|
}
|
|
284
369
|
}
|
|
285
370
|
|