@vocab/phrase 1.2.0 → 1.2.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/LICENSE +21 -0
- package/dist/declarations/src/csv.d.ts +10 -10
- package/dist/declarations/src/file.d.ts +4 -4
- package/dist/declarations/src/index.d.ts +2 -2
- package/dist/declarations/src/logger.d.ts +3 -3
- package/dist/declarations/src/phrase-api.d.ts +12 -12
- package/dist/declarations/src/pull-translations.d.ts +7 -7
- package/dist/declarations/src/push-translations.d.ts +11 -11
- package/dist/vocab-phrase.cjs.dev.js +24 -59
- package/dist/vocab-phrase.cjs.prod.js +24 -59
- package/dist/vocab-phrase.esm.js +20 -55
- package/package.json +4 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
### MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 SEEK
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,10 +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
|
+
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,4 +1,4 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { promises as fs } from 'fs';
|
|
3
|
-
export declare const mkdir: typeof fs.mkdir;
|
|
4
|
-
export declare const writeFile: typeof fs.writeFile;
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
export declare const mkdir: typeof fs.mkdir;
|
|
4
|
+
export declare const writeFile: typeof fs.writeFile;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { pull } from './pull-translations';
|
|
2
|
-
export { push } from './push-translations';
|
|
1
|
+
export { pull } from './pull-translations';
|
|
2
|
+
export { push } from './push-translations';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import debug from 'debug';
|
|
2
|
-
export declare const trace: debug.Debugger;
|
|
3
|
-
export declare const log: (...params: unknown[]) => void;
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
export declare const trace: debug.Debugger;
|
|
3
|
+
export declare const log: (...params: unknown[]) => void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { TranslationsByLanguage } from '@vocab/types';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
export declare function callPhrase<T = any>(relativePath: string, options?: Parameters<typeof fetch>[1]): Promise<T>;
|
|
4
|
-
export declare function pullAllTranslations(branch: string): Promise<TranslationsByLanguage>;
|
|
5
|
-
export declare function pushTranslations(translationsByLanguage: TranslationsByLanguage, { devLanguage, branch }: {
|
|
6
|
-
devLanguage: string;
|
|
7
|
-
branch: string;
|
|
8
|
-
}): Promise<{
|
|
9
|
-
uploadId: string;
|
|
10
|
-
}>;
|
|
11
|
-
export declare function deleteUnusedKeys(uploadId: string, branch: string): Promise<void>;
|
|
12
|
-
export declare function ensureBranch(branch: string): Promise<void>;
|
|
1
|
+
import type { TranslationsByLanguage } from '@vocab/types';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
export declare function callPhrase<T = any>(relativePath: string, options?: Parameters<typeof fetch>[1]): Promise<T>;
|
|
4
|
+
export declare function pullAllTranslations(branch: string): Promise<TranslationsByLanguage>;
|
|
5
|
+
export declare function pushTranslations(translationsByLanguage: TranslationsByLanguage, { devLanguage, branch }: {
|
|
6
|
+
devLanguage: string;
|
|
7
|
+
branch: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
uploadId: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function deleteUnusedKeys(uploadId: string, branch: string): Promise<void>;
|
|
12
|
+
export declare function ensureBranch(branch: string): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { UserConfig } from '@vocab/types';
|
|
2
|
-
interface PullOptions {
|
|
3
|
-
branch?: string;
|
|
4
|
-
deleteUnusedKeys?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare function pull({ branch }: PullOptions, config: UserConfig): Promise<void>;
|
|
7
|
-
export {};
|
|
1
|
+
import type { UserConfig } from '@vocab/types';
|
|
2
|
+
interface PullOptions {
|
|
3
|
+
branch?: string;
|
|
4
|
+
deleteUnusedKeys?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function pull({ branch }: PullOptions, config: UserConfig): Promise<void>;
|
|
7
|
+
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { UserConfig } from '@vocab/types';
|
|
2
|
-
interface PushOptions {
|
|
3
|
-
branch: string;
|
|
4
|
-
deleteUnusedKeys?: boolean;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
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.
|
|
9
|
-
*/
|
|
10
|
-
export declare function push({ branch, deleteUnusedKeys }: PushOptions, config: UserConfig): Promise<void>;
|
|
11
|
-
export {};
|
|
1
|
+
import { UserConfig } from '@vocab/types';
|
|
2
|
+
interface PushOptions {
|
|
3
|
+
branch: string;
|
|
4
|
+
deleteUnusedKeys?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
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.
|
|
9
|
+
*/
|
|
10
|
+
export declare function push({ branch, deleteUnusedKeys }: PushOptions, config: UserConfig): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -22,16 +22,16 @@ var debug__default = /*#__PURE__*/_interopDefault(debug);
|
|
|
22
22
|
const mkdir = fs.promises.mkdir;
|
|
23
23
|
const writeFile = fs.promises.writeFile;
|
|
24
24
|
|
|
25
|
-
const trace = debug__default[
|
|
25
|
+
const trace = debug__default["default"](`vocab:phrase`);
|
|
26
26
|
const log = (...params) => {
|
|
27
27
|
// eslint-disable-next-line no-console
|
|
28
|
-
console.log(chalk__default[
|
|
28
|
+
console.log(chalk__default["default"].yellow('Vocab'), ...params);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
function translationsToCsv(translations, devLanguage) {
|
|
32
32
|
const languages = Object.keys(translations);
|
|
33
|
-
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
34
|
-
|
|
33
|
+
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
34
|
+
// Ensure languages are ordered for locale mapping
|
|
35
35
|
const orderedLanguages = [devLanguage, ...altLanguages];
|
|
36
36
|
const devLanguageTranslations = translations[devLanguage];
|
|
37
37
|
const csv = Object.entries(devLanguageTranslations).map(([key, {
|
|
@@ -41,7 +41,6 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
41
41
|
}]) => {
|
|
42
42
|
const altTranslationMessages = altLanguages.map(language => {
|
|
43
43
|
var _translations$languag, _translations$languag2;
|
|
44
|
-
|
|
45
44
|
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
45
|
});
|
|
47
46
|
return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
@@ -49,8 +48,9 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
49
48
|
const csvString = sync.stringify(csv, {
|
|
50
49
|
delimiter: ',',
|
|
51
50
|
header: false
|
|
52
|
-
});
|
|
51
|
+
});
|
|
53
52
|
|
|
53
|
+
// Column indices start at 1
|
|
54
54
|
const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
|
|
55
55
|
const keyIndex = orderedLanguages.length + 1;
|
|
56
56
|
const commentIndex = keyIndex + 1;
|
|
@@ -65,15 +65,13 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/* eslint-disable no-console */
|
|
68
|
-
|
|
69
68
|
function _callPhrase(path, options = {}) {
|
|
70
69
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
71
|
-
|
|
72
70
|
if (!phraseApiToken) {
|
|
73
71
|
throw new Error('Missing PHRASE_API_TOKEN');
|
|
74
72
|
}
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
return fetch__default["default"](path, {
|
|
74
|
+
...options,
|
|
77
75
|
headers: {
|
|
78
76
|
Authorization: `token ${phraseApiToken}`,
|
|
79
77
|
// Provide identification via User Agent as requested in https://developers.phrase.com/api/#overview--identification-via-user-agent
|
|
@@ -82,30 +80,26 @@ function _callPhrase(path, options = {}) {
|
|
|
82
80
|
}
|
|
83
81
|
}).then(async response => {
|
|
84
82
|
console.log(`${path}: ${response.status} - ${response.statusText}`);
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
const secondsUntilLimitReset = Math.ceil(Number.parseFloat(response.headers.get('X-Rate-Limit-Reset') || '0') - Date.now() / 1000);
|
|
84
|
+
console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${secondsUntilLimitReset} seconds remaining)`);
|
|
85
|
+
trace('\nLink:', response.headers.get('Link'), '\n');
|
|
86
|
+
// Print All Headers:
|
|
87
87
|
// console.log(Array.from(r.headers.entries()));
|
|
88
88
|
|
|
89
89
|
try {
|
|
90
90
|
var _response$headers$get;
|
|
91
|
-
|
|
92
91
|
const result = await response.json();
|
|
93
92
|
trace(`Internal Result (Length: ${result.length})\n`);
|
|
94
|
-
|
|
95
93
|
if ((!options.method || options.method === 'GET') && (_response$headers$get = response.headers.get('Link')) !== null && _response$headers$get !== void 0 && _response$headers$get.includes('rel=next')) {
|
|
96
94
|
var _response$headers$get2, _response$headers$get3;
|
|
97
|
-
|
|
98
95
|
const [, nextPageUrl] = (_response$headers$get2 = (_response$headers$get3 = response.headers.get('Link')) === null || _response$headers$get3 === void 0 ? void 0 : _response$headers$get3.match(/<([^>]*)>; rel=next/)) !== null && _response$headers$get2 !== void 0 ? _response$headers$get2 : [];
|
|
99
|
-
|
|
100
96
|
if (!nextPageUrl) {
|
|
101
97
|
throw new Error("Can't parse next page URL");
|
|
102
98
|
}
|
|
103
|
-
|
|
104
99
|
console.log('Results received with next page: ', nextPageUrl);
|
|
105
100
|
const nextPageResult = await _callPhrase(nextPageUrl, options);
|
|
106
101
|
return [...result, ...nextPageResult];
|
|
107
102
|
}
|
|
108
|
-
|
|
109
103
|
return result;
|
|
110
104
|
} catch (e) {
|
|
111
105
|
console.error('Unable to parse response as JSON', e);
|
|
@@ -113,19 +107,15 @@ function _callPhrase(path, options = {}) {
|
|
|
113
107
|
}
|
|
114
108
|
});
|
|
115
109
|
}
|
|
116
|
-
|
|
117
110
|
async function callPhrase(relativePath, options = {}) {
|
|
118
111
|
const projectId = process.env.PHRASE_PROJECT_ID;
|
|
119
|
-
|
|
120
112
|
if (!projectId) {
|
|
121
113
|
throw new Error('Missing PHRASE_PROJECT_ID');
|
|
122
114
|
}
|
|
123
|
-
|
|
124
115
|
return _callPhrase(`https://api.phrase.com/v2/projects/${projectId}/${relativePath}`, options).then(result => {
|
|
125
116
|
if (Array.isArray(result)) {
|
|
126
117
|
console.log('Result length:', result.length);
|
|
127
118
|
}
|
|
128
|
-
|
|
129
119
|
return result;
|
|
130
120
|
}).catch(error => {
|
|
131
121
|
console.error(`Error calling phrase for ${relativePath}:`, error);
|
|
@@ -135,24 +125,21 @@ async function callPhrase(relativePath, options = {}) {
|
|
|
135
125
|
async function pullAllTranslations(branch) {
|
|
136
126
|
const phraseResult = await callPhrase(`translations?branch=${branch}&per_page=100`);
|
|
137
127
|
const translations = {};
|
|
138
|
-
|
|
139
128
|
for (const r of phraseResult) {
|
|
140
129
|
if (!translations[r.locale.code]) {
|
|
141
130
|
translations[r.locale.code] = {};
|
|
142
131
|
}
|
|
143
|
-
|
|
144
132
|
translations[r.locale.code][r.key.name] = {
|
|
145
133
|
message: r.content
|
|
146
134
|
};
|
|
147
135
|
}
|
|
148
|
-
|
|
149
136
|
return translations;
|
|
150
137
|
}
|
|
151
138
|
async function pushTranslations(translationsByLanguage, {
|
|
152
139
|
devLanguage,
|
|
153
140
|
branch
|
|
154
141
|
}) {
|
|
155
|
-
const formData = new FormData__default[
|
|
142
|
+
const formData = new FormData__default["default"]();
|
|
156
143
|
const {
|
|
157
144
|
csvString,
|
|
158
145
|
localeMapping,
|
|
@@ -168,11 +155,9 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
168
155
|
formData.append('file_format', 'csv');
|
|
169
156
|
formData.append('branch', branch);
|
|
170
157
|
formData.append('update_translations', 'true');
|
|
171
|
-
|
|
172
158
|
for (const [locale, index] of Object.entries(localeMapping)) {
|
|
173
159
|
formData.append(`locale_mapping[${locale}]`, index);
|
|
174
160
|
}
|
|
175
|
-
|
|
176
161
|
formData.append('format_options[key_index]', keyIndex);
|
|
177
162
|
formData.append('format_options[comment_index]', commentIndex);
|
|
178
163
|
formData.append('format_options[tag_column]', tagColumn);
|
|
@@ -183,7 +168,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
183
168
|
body: formData
|
|
184
169
|
});
|
|
185
170
|
trace('Upload result:\n', result);
|
|
186
|
-
|
|
187
171
|
if (result && 'id' in result) {
|
|
188
172
|
log('Upload ID:', result.id, '\n');
|
|
189
173
|
log('Successfully Uploaded\n');
|
|
@@ -192,7 +176,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
192
176
|
log('Response:', result);
|
|
193
177
|
throw new Error('Error uploading');
|
|
194
178
|
}
|
|
195
|
-
|
|
196
179
|
return {
|
|
197
180
|
uploadId: result.id
|
|
198
181
|
};
|
|
@@ -236,65 +219,56 @@ async function pull({
|
|
|
236
219
|
trace(`Pulling translations from Phrase for languages ${config.devLanguage} and ${alternativeLanguages.join(', ')}`);
|
|
237
220
|
const phraseLanguages = Object.keys(allPhraseTranslations);
|
|
238
221
|
trace(`Found Phrase translations for languages ${phraseLanguages.join(', ')}`);
|
|
239
|
-
|
|
240
222
|
if (!phraseLanguages.includes(config.devLanguage)) {
|
|
241
223
|
throw new Error(`Phrase did not return any translations for the configured development language "${config.devLanguage}".\nPlease ensure this language is present in your Phrase project's configuration.`);
|
|
242
224
|
}
|
|
243
|
-
|
|
244
225
|
const allVocabTranslations = await core.loadAllTranslations({
|
|
245
226
|
fallbacks: 'none',
|
|
246
227
|
includeNodeModules: false,
|
|
247
228
|
withTags: true
|
|
248
229
|
}, config);
|
|
249
|
-
|
|
250
230
|
for (const loadedTranslation of allVocabTranslations) {
|
|
251
231
|
const devTranslations = loadedTranslation.languages[config.devLanguage];
|
|
252
|
-
|
|
253
232
|
if (!devTranslations) {
|
|
254
233
|
throw new Error('No dev language translations loaded');
|
|
255
234
|
}
|
|
256
|
-
|
|
257
|
-
|
|
235
|
+
const defaultValues = {
|
|
236
|
+
...devTranslations
|
|
258
237
|
};
|
|
259
238
|
const localKeys = Object.keys(defaultValues);
|
|
260
|
-
|
|
261
239
|
for (const key of localKeys) {
|
|
262
|
-
defaultValues[key] = {
|
|
240
|
+
defaultValues[key] = {
|
|
241
|
+
...defaultValues[key],
|
|
263
242
|
...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
|
|
264
243
|
};
|
|
265
|
-
}
|
|
266
|
-
|
|
244
|
+
}
|
|
267
245
|
|
|
246
|
+
// Only write a `_meta` field if necessary
|
|
268
247
|
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
269
248
|
defaultValues._meta = loadedTranslation.metadata;
|
|
270
249
|
}
|
|
271
|
-
|
|
272
250
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
273
|
-
|
|
274
251
|
for (const alternativeLanguage of alternativeLanguages) {
|
|
275
252
|
if (alternativeLanguage in allPhraseTranslations) {
|
|
276
|
-
const altTranslations = {
|
|
253
|
+
const altTranslations = {
|
|
254
|
+
...loadedTranslation.languages[alternativeLanguage]
|
|
277
255
|
};
|
|
278
256
|
const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
|
|
279
|
-
|
|
280
257
|
for (const key of localKeys) {
|
|
281
258
|
var _phraseAltTranslation;
|
|
282
|
-
|
|
283
259
|
const phraseKey = core.getUniqueKey(key, loadedTranslation.namespace);
|
|
284
260
|
const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
|
|
285
|
-
|
|
286
261
|
if (!phraseTranslationMessage) {
|
|
287
262
|
trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
|
|
288
263
|
continue;
|
|
289
264
|
}
|
|
290
|
-
|
|
291
|
-
|
|
265
|
+
altTranslations[key] = {
|
|
266
|
+
...altTranslations[key],
|
|
292
267
|
message: phraseTranslationMessage
|
|
293
268
|
};
|
|
294
269
|
}
|
|
295
|
-
|
|
296
270
|
const altTranslationFilePath = core.getAltLanguageFilePath(loadedTranslation.filePath, alternativeLanguage);
|
|
297
|
-
await mkdir(path__default[
|
|
271
|
+
await mkdir(path__default["default"].dirname(altTranslationFilePath), {
|
|
298
272
|
recursive: true
|
|
299
273
|
});
|
|
300
274
|
await writeFile(altTranslationFilePath, `${JSON.stringify(altTranslations, null, 2)}\n`);
|
|
@@ -321,48 +295,39 @@ async function push({
|
|
|
321
295
|
await ensureBranch(branch);
|
|
322
296
|
trace(`Pushing translations to phrase for languages ${allLanguages.join(', ')}`);
|
|
323
297
|
const phraseTranslations = {};
|
|
324
|
-
|
|
325
298
|
for (const loadedTranslation of allLanguageTranslations) {
|
|
326
299
|
for (const language of allLanguages) {
|
|
327
300
|
const localTranslations = loadedTranslation.languages[language];
|
|
328
|
-
|
|
329
301
|
if (!localTranslations) {
|
|
330
302
|
continue;
|
|
331
303
|
}
|
|
332
|
-
|
|
333
304
|
if (!phraseTranslations[language]) {
|
|
334
305
|
phraseTranslations[language] = {};
|
|
335
306
|
}
|
|
336
|
-
|
|
337
307
|
const {
|
|
338
308
|
metadata: {
|
|
339
309
|
tags: sharedTags = []
|
|
340
310
|
}
|
|
341
311
|
} = loadedTranslation;
|
|
342
|
-
|
|
343
312
|
for (const localKey of Object.keys(localTranslations)) {
|
|
344
313
|
const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
|
|
345
314
|
const {
|
|
346
315
|
tags = [],
|
|
347
316
|
...localTranslation
|
|
348
317
|
} = localTranslations[localKey];
|
|
349
|
-
|
|
350
318
|
if (language === config.devLanguage) {
|
|
351
319
|
localTranslation.tags = [...tags, ...sharedTags];
|
|
352
320
|
}
|
|
353
|
-
|
|
354
321
|
phraseTranslations[language][phraseKey] = localTranslation;
|
|
355
322
|
}
|
|
356
323
|
}
|
|
357
324
|
}
|
|
358
|
-
|
|
359
325
|
const {
|
|
360
326
|
uploadId
|
|
361
327
|
} = await pushTranslations(phraseTranslations, {
|
|
362
328
|
devLanguage: config.devLanguage,
|
|
363
329
|
branch
|
|
364
330
|
});
|
|
365
|
-
|
|
366
331
|
if (deleteUnusedKeys$1) {
|
|
367
332
|
await deleteUnusedKeys(uploadId, branch);
|
|
368
333
|
}
|
|
@@ -22,16 +22,16 @@ var debug__default = /*#__PURE__*/_interopDefault(debug);
|
|
|
22
22
|
const mkdir = fs.promises.mkdir;
|
|
23
23
|
const writeFile = fs.promises.writeFile;
|
|
24
24
|
|
|
25
|
-
const trace = debug__default[
|
|
25
|
+
const trace = debug__default["default"](`vocab:phrase`);
|
|
26
26
|
const log = (...params) => {
|
|
27
27
|
// eslint-disable-next-line no-console
|
|
28
|
-
console.log(chalk__default[
|
|
28
|
+
console.log(chalk__default["default"].yellow('Vocab'), ...params);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
function translationsToCsv(translations, devLanguage) {
|
|
32
32
|
const languages = Object.keys(translations);
|
|
33
|
-
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
34
|
-
|
|
33
|
+
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
34
|
+
// Ensure languages are ordered for locale mapping
|
|
35
35
|
const orderedLanguages = [devLanguage, ...altLanguages];
|
|
36
36
|
const devLanguageTranslations = translations[devLanguage];
|
|
37
37
|
const csv = Object.entries(devLanguageTranslations).map(([key, {
|
|
@@ -41,7 +41,6 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
41
41
|
}]) => {
|
|
42
42
|
const altTranslationMessages = altLanguages.map(language => {
|
|
43
43
|
var _translations$languag, _translations$languag2;
|
|
44
|
-
|
|
45
44
|
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
45
|
});
|
|
47
46
|
return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
@@ -49,8 +48,9 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
49
48
|
const csvString = sync.stringify(csv, {
|
|
50
49
|
delimiter: ',',
|
|
51
50
|
header: false
|
|
52
|
-
});
|
|
51
|
+
});
|
|
53
52
|
|
|
53
|
+
// Column indices start at 1
|
|
54
54
|
const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
|
|
55
55
|
const keyIndex = orderedLanguages.length + 1;
|
|
56
56
|
const commentIndex = keyIndex + 1;
|
|
@@ -65,15 +65,13 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/* eslint-disable no-console */
|
|
68
|
-
|
|
69
68
|
function _callPhrase(path, options = {}) {
|
|
70
69
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
71
|
-
|
|
72
70
|
if (!phraseApiToken) {
|
|
73
71
|
throw new Error('Missing PHRASE_API_TOKEN');
|
|
74
72
|
}
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
return fetch__default["default"](path, {
|
|
74
|
+
...options,
|
|
77
75
|
headers: {
|
|
78
76
|
Authorization: `token ${phraseApiToken}`,
|
|
79
77
|
// Provide identification via User Agent as requested in https://developers.phrase.com/api/#overview--identification-via-user-agent
|
|
@@ -82,30 +80,26 @@ function _callPhrase(path, options = {}) {
|
|
|
82
80
|
}
|
|
83
81
|
}).then(async response => {
|
|
84
82
|
console.log(`${path}: ${response.status} - ${response.statusText}`);
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
const secondsUntilLimitReset = Math.ceil(Number.parseFloat(response.headers.get('X-Rate-Limit-Reset') || '0') - Date.now() / 1000);
|
|
84
|
+
console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${secondsUntilLimitReset} seconds remaining)`);
|
|
85
|
+
trace('\nLink:', response.headers.get('Link'), '\n');
|
|
86
|
+
// Print All Headers:
|
|
87
87
|
// console.log(Array.from(r.headers.entries()));
|
|
88
88
|
|
|
89
89
|
try {
|
|
90
90
|
var _response$headers$get;
|
|
91
|
-
|
|
92
91
|
const result = await response.json();
|
|
93
92
|
trace(`Internal Result (Length: ${result.length})\n`);
|
|
94
|
-
|
|
95
93
|
if ((!options.method || options.method === 'GET') && (_response$headers$get = response.headers.get('Link')) !== null && _response$headers$get !== void 0 && _response$headers$get.includes('rel=next')) {
|
|
96
94
|
var _response$headers$get2, _response$headers$get3;
|
|
97
|
-
|
|
98
95
|
const [, nextPageUrl] = (_response$headers$get2 = (_response$headers$get3 = response.headers.get('Link')) === null || _response$headers$get3 === void 0 ? void 0 : _response$headers$get3.match(/<([^>]*)>; rel=next/)) !== null && _response$headers$get2 !== void 0 ? _response$headers$get2 : [];
|
|
99
|
-
|
|
100
96
|
if (!nextPageUrl) {
|
|
101
97
|
throw new Error("Can't parse next page URL");
|
|
102
98
|
}
|
|
103
|
-
|
|
104
99
|
console.log('Results received with next page: ', nextPageUrl);
|
|
105
100
|
const nextPageResult = await _callPhrase(nextPageUrl, options);
|
|
106
101
|
return [...result, ...nextPageResult];
|
|
107
102
|
}
|
|
108
|
-
|
|
109
103
|
return result;
|
|
110
104
|
} catch (e) {
|
|
111
105
|
console.error('Unable to parse response as JSON', e);
|
|
@@ -113,19 +107,15 @@ function _callPhrase(path, options = {}) {
|
|
|
113
107
|
}
|
|
114
108
|
});
|
|
115
109
|
}
|
|
116
|
-
|
|
117
110
|
async function callPhrase(relativePath, options = {}) {
|
|
118
111
|
const projectId = process.env.PHRASE_PROJECT_ID;
|
|
119
|
-
|
|
120
112
|
if (!projectId) {
|
|
121
113
|
throw new Error('Missing PHRASE_PROJECT_ID');
|
|
122
114
|
}
|
|
123
|
-
|
|
124
115
|
return _callPhrase(`https://api.phrase.com/v2/projects/${projectId}/${relativePath}`, options).then(result => {
|
|
125
116
|
if (Array.isArray(result)) {
|
|
126
117
|
console.log('Result length:', result.length);
|
|
127
118
|
}
|
|
128
|
-
|
|
129
119
|
return result;
|
|
130
120
|
}).catch(error => {
|
|
131
121
|
console.error(`Error calling phrase for ${relativePath}:`, error);
|
|
@@ -135,24 +125,21 @@ async function callPhrase(relativePath, options = {}) {
|
|
|
135
125
|
async function pullAllTranslations(branch) {
|
|
136
126
|
const phraseResult = await callPhrase(`translations?branch=${branch}&per_page=100`);
|
|
137
127
|
const translations = {};
|
|
138
|
-
|
|
139
128
|
for (const r of phraseResult) {
|
|
140
129
|
if (!translations[r.locale.code]) {
|
|
141
130
|
translations[r.locale.code] = {};
|
|
142
131
|
}
|
|
143
|
-
|
|
144
132
|
translations[r.locale.code][r.key.name] = {
|
|
145
133
|
message: r.content
|
|
146
134
|
};
|
|
147
135
|
}
|
|
148
|
-
|
|
149
136
|
return translations;
|
|
150
137
|
}
|
|
151
138
|
async function pushTranslations(translationsByLanguage, {
|
|
152
139
|
devLanguage,
|
|
153
140
|
branch
|
|
154
141
|
}) {
|
|
155
|
-
const formData = new FormData__default[
|
|
142
|
+
const formData = new FormData__default["default"]();
|
|
156
143
|
const {
|
|
157
144
|
csvString,
|
|
158
145
|
localeMapping,
|
|
@@ -168,11 +155,9 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
168
155
|
formData.append('file_format', 'csv');
|
|
169
156
|
formData.append('branch', branch);
|
|
170
157
|
formData.append('update_translations', 'true');
|
|
171
|
-
|
|
172
158
|
for (const [locale, index] of Object.entries(localeMapping)) {
|
|
173
159
|
formData.append(`locale_mapping[${locale}]`, index);
|
|
174
160
|
}
|
|
175
|
-
|
|
176
161
|
formData.append('format_options[key_index]', keyIndex);
|
|
177
162
|
formData.append('format_options[comment_index]', commentIndex);
|
|
178
163
|
formData.append('format_options[tag_column]', tagColumn);
|
|
@@ -183,7 +168,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
183
168
|
body: formData
|
|
184
169
|
});
|
|
185
170
|
trace('Upload result:\n', result);
|
|
186
|
-
|
|
187
171
|
if (result && 'id' in result) {
|
|
188
172
|
log('Upload ID:', result.id, '\n');
|
|
189
173
|
log('Successfully Uploaded\n');
|
|
@@ -192,7 +176,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
192
176
|
log('Response:', result);
|
|
193
177
|
throw new Error('Error uploading');
|
|
194
178
|
}
|
|
195
|
-
|
|
196
179
|
return {
|
|
197
180
|
uploadId: result.id
|
|
198
181
|
};
|
|
@@ -236,65 +219,56 @@ async function pull({
|
|
|
236
219
|
trace(`Pulling translations from Phrase for languages ${config.devLanguage} and ${alternativeLanguages.join(', ')}`);
|
|
237
220
|
const phraseLanguages = Object.keys(allPhraseTranslations);
|
|
238
221
|
trace(`Found Phrase translations for languages ${phraseLanguages.join(', ')}`);
|
|
239
|
-
|
|
240
222
|
if (!phraseLanguages.includes(config.devLanguage)) {
|
|
241
223
|
throw new Error(`Phrase did not return any translations for the configured development language "${config.devLanguage}".\nPlease ensure this language is present in your Phrase project's configuration.`);
|
|
242
224
|
}
|
|
243
|
-
|
|
244
225
|
const allVocabTranslations = await core.loadAllTranslations({
|
|
245
226
|
fallbacks: 'none',
|
|
246
227
|
includeNodeModules: false,
|
|
247
228
|
withTags: true
|
|
248
229
|
}, config);
|
|
249
|
-
|
|
250
230
|
for (const loadedTranslation of allVocabTranslations) {
|
|
251
231
|
const devTranslations = loadedTranslation.languages[config.devLanguage];
|
|
252
|
-
|
|
253
232
|
if (!devTranslations) {
|
|
254
233
|
throw new Error('No dev language translations loaded');
|
|
255
234
|
}
|
|
256
|
-
|
|
257
|
-
|
|
235
|
+
const defaultValues = {
|
|
236
|
+
...devTranslations
|
|
258
237
|
};
|
|
259
238
|
const localKeys = Object.keys(defaultValues);
|
|
260
|
-
|
|
261
239
|
for (const key of localKeys) {
|
|
262
|
-
defaultValues[key] = {
|
|
240
|
+
defaultValues[key] = {
|
|
241
|
+
...defaultValues[key],
|
|
263
242
|
...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
|
|
264
243
|
};
|
|
265
|
-
}
|
|
266
|
-
|
|
244
|
+
}
|
|
267
245
|
|
|
246
|
+
// Only write a `_meta` field if necessary
|
|
268
247
|
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
269
248
|
defaultValues._meta = loadedTranslation.metadata;
|
|
270
249
|
}
|
|
271
|
-
|
|
272
250
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
273
|
-
|
|
274
251
|
for (const alternativeLanguage of alternativeLanguages) {
|
|
275
252
|
if (alternativeLanguage in allPhraseTranslations) {
|
|
276
|
-
const altTranslations = {
|
|
253
|
+
const altTranslations = {
|
|
254
|
+
...loadedTranslation.languages[alternativeLanguage]
|
|
277
255
|
};
|
|
278
256
|
const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
|
|
279
|
-
|
|
280
257
|
for (const key of localKeys) {
|
|
281
258
|
var _phraseAltTranslation;
|
|
282
|
-
|
|
283
259
|
const phraseKey = core.getUniqueKey(key, loadedTranslation.namespace);
|
|
284
260
|
const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
|
|
285
|
-
|
|
286
261
|
if (!phraseTranslationMessage) {
|
|
287
262
|
trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
|
|
288
263
|
continue;
|
|
289
264
|
}
|
|
290
|
-
|
|
291
|
-
|
|
265
|
+
altTranslations[key] = {
|
|
266
|
+
...altTranslations[key],
|
|
292
267
|
message: phraseTranslationMessage
|
|
293
268
|
};
|
|
294
269
|
}
|
|
295
|
-
|
|
296
270
|
const altTranslationFilePath = core.getAltLanguageFilePath(loadedTranslation.filePath, alternativeLanguage);
|
|
297
|
-
await mkdir(path__default[
|
|
271
|
+
await mkdir(path__default["default"].dirname(altTranslationFilePath), {
|
|
298
272
|
recursive: true
|
|
299
273
|
});
|
|
300
274
|
await writeFile(altTranslationFilePath, `${JSON.stringify(altTranslations, null, 2)}\n`);
|
|
@@ -321,48 +295,39 @@ async function push({
|
|
|
321
295
|
await ensureBranch(branch);
|
|
322
296
|
trace(`Pushing translations to phrase for languages ${allLanguages.join(', ')}`);
|
|
323
297
|
const phraseTranslations = {};
|
|
324
|
-
|
|
325
298
|
for (const loadedTranslation of allLanguageTranslations) {
|
|
326
299
|
for (const language of allLanguages) {
|
|
327
300
|
const localTranslations = loadedTranslation.languages[language];
|
|
328
|
-
|
|
329
301
|
if (!localTranslations) {
|
|
330
302
|
continue;
|
|
331
303
|
}
|
|
332
|
-
|
|
333
304
|
if (!phraseTranslations[language]) {
|
|
334
305
|
phraseTranslations[language] = {};
|
|
335
306
|
}
|
|
336
|
-
|
|
337
307
|
const {
|
|
338
308
|
metadata: {
|
|
339
309
|
tags: sharedTags = []
|
|
340
310
|
}
|
|
341
311
|
} = loadedTranslation;
|
|
342
|
-
|
|
343
312
|
for (const localKey of Object.keys(localTranslations)) {
|
|
344
313
|
const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
|
|
345
314
|
const {
|
|
346
315
|
tags = [],
|
|
347
316
|
...localTranslation
|
|
348
317
|
} = localTranslations[localKey];
|
|
349
|
-
|
|
350
318
|
if (language === config.devLanguage) {
|
|
351
319
|
localTranslation.tags = [...tags, ...sharedTags];
|
|
352
320
|
}
|
|
353
|
-
|
|
354
321
|
phraseTranslations[language][phraseKey] = localTranslation;
|
|
355
322
|
}
|
|
356
323
|
}
|
|
357
324
|
}
|
|
358
|
-
|
|
359
325
|
const {
|
|
360
326
|
uploadId
|
|
361
327
|
} = await pushTranslations(phraseTranslations, {
|
|
362
328
|
devLanguage: config.devLanguage,
|
|
363
329
|
branch
|
|
364
330
|
});
|
|
365
|
-
|
|
366
331
|
if (deleteUnusedKeys$1) {
|
|
367
332
|
await deleteUnusedKeys(uploadId, branch);
|
|
368
333
|
}
|
package/dist/vocab-phrase.esm.js
CHANGED
|
@@ -18,8 +18,8 @@ const log = (...params) => {
|
|
|
18
18
|
|
|
19
19
|
function translationsToCsv(translations, devLanguage) {
|
|
20
20
|
const languages = Object.keys(translations);
|
|
21
|
-
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
22
|
-
|
|
21
|
+
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
22
|
+
// Ensure languages are ordered for locale mapping
|
|
23
23
|
const orderedLanguages = [devLanguage, ...altLanguages];
|
|
24
24
|
const devLanguageTranslations = translations[devLanguage];
|
|
25
25
|
const csv = Object.entries(devLanguageTranslations).map(([key, {
|
|
@@ -29,7 +29,6 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
29
29
|
}]) => {
|
|
30
30
|
const altTranslationMessages = altLanguages.map(language => {
|
|
31
31
|
var _translations$languag, _translations$languag2;
|
|
32
|
-
|
|
33
32
|
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;
|
|
34
33
|
});
|
|
35
34
|
return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
@@ -37,8 +36,9 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
37
36
|
const csvString = stringify(csv, {
|
|
38
37
|
delimiter: ',',
|
|
39
38
|
header: false
|
|
40
|
-
});
|
|
39
|
+
});
|
|
41
40
|
|
|
41
|
+
// Column indices start at 1
|
|
42
42
|
const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
|
|
43
43
|
const keyIndex = orderedLanguages.length + 1;
|
|
44
44
|
const commentIndex = keyIndex + 1;
|
|
@@ -53,15 +53,13 @@ function translationsToCsv(translations, devLanguage) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/* eslint-disable no-console */
|
|
56
|
-
|
|
57
56
|
function _callPhrase(path, options = {}) {
|
|
58
57
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
59
|
-
|
|
60
58
|
if (!phraseApiToken) {
|
|
61
59
|
throw new Error('Missing PHRASE_API_TOKEN');
|
|
62
60
|
}
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
return fetch(path, {
|
|
62
|
+
...options,
|
|
65
63
|
headers: {
|
|
66
64
|
Authorization: `token ${phraseApiToken}`,
|
|
67
65
|
// Provide identification via User Agent as requested in https://developers.phrase.com/api/#overview--identification-via-user-agent
|
|
@@ -70,30 +68,26 @@ function _callPhrase(path, options = {}) {
|
|
|
70
68
|
}
|
|
71
69
|
}).then(async response => {
|
|
72
70
|
console.log(`${path}: ${response.status} - ${response.statusText}`);
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const secondsUntilLimitReset = Math.ceil(Number.parseFloat(response.headers.get('X-Rate-Limit-Reset') || '0') - Date.now() / 1000);
|
|
72
|
+
console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${secondsUntilLimitReset} seconds remaining)`);
|
|
73
|
+
trace('\nLink:', response.headers.get('Link'), '\n');
|
|
74
|
+
// Print All Headers:
|
|
75
75
|
// console.log(Array.from(r.headers.entries()));
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
78
|
var _response$headers$get;
|
|
79
|
-
|
|
80
79
|
const result = await response.json();
|
|
81
80
|
trace(`Internal Result (Length: ${result.length})\n`);
|
|
82
|
-
|
|
83
81
|
if ((!options.method || options.method === 'GET') && (_response$headers$get = response.headers.get('Link')) !== null && _response$headers$get !== void 0 && _response$headers$get.includes('rel=next')) {
|
|
84
82
|
var _response$headers$get2, _response$headers$get3;
|
|
85
|
-
|
|
86
83
|
const [, nextPageUrl] = (_response$headers$get2 = (_response$headers$get3 = response.headers.get('Link')) === null || _response$headers$get3 === void 0 ? void 0 : _response$headers$get3.match(/<([^>]*)>; rel=next/)) !== null && _response$headers$get2 !== void 0 ? _response$headers$get2 : [];
|
|
87
|
-
|
|
88
84
|
if (!nextPageUrl) {
|
|
89
85
|
throw new Error("Can't parse next page URL");
|
|
90
86
|
}
|
|
91
|
-
|
|
92
87
|
console.log('Results received with next page: ', nextPageUrl);
|
|
93
88
|
const nextPageResult = await _callPhrase(nextPageUrl, options);
|
|
94
89
|
return [...result, ...nextPageResult];
|
|
95
90
|
}
|
|
96
|
-
|
|
97
91
|
return result;
|
|
98
92
|
} catch (e) {
|
|
99
93
|
console.error('Unable to parse response as JSON', e);
|
|
@@ -101,19 +95,15 @@ function _callPhrase(path, options = {}) {
|
|
|
101
95
|
}
|
|
102
96
|
});
|
|
103
97
|
}
|
|
104
|
-
|
|
105
98
|
async function callPhrase(relativePath, options = {}) {
|
|
106
99
|
const projectId = process.env.PHRASE_PROJECT_ID;
|
|
107
|
-
|
|
108
100
|
if (!projectId) {
|
|
109
101
|
throw new Error('Missing PHRASE_PROJECT_ID');
|
|
110
102
|
}
|
|
111
|
-
|
|
112
103
|
return _callPhrase(`https://api.phrase.com/v2/projects/${projectId}/${relativePath}`, options).then(result => {
|
|
113
104
|
if (Array.isArray(result)) {
|
|
114
105
|
console.log('Result length:', result.length);
|
|
115
106
|
}
|
|
116
|
-
|
|
117
107
|
return result;
|
|
118
108
|
}).catch(error => {
|
|
119
109
|
console.error(`Error calling phrase for ${relativePath}:`, error);
|
|
@@ -123,17 +113,14 @@ async function callPhrase(relativePath, options = {}) {
|
|
|
123
113
|
async function pullAllTranslations(branch) {
|
|
124
114
|
const phraseResult = await callPhrase(`translations?branch=${branch}&per_page=100`);
|
|
125
115
|
const translations = {};
|
|
126
|
-
|
|
127
116
|
for (const r of phraseResult) {
|
|
128
117
|
if (!translations[r.locale.code]) {
|
|
129
118
|
translations[r.locale.code] = {};
|
|
130
119
|
}
|
|
131
|
-
|
|
132
120
|
translations[r.locale.code][r.key.name] = {
|
|
133
121
|
message: r.content
|
|
134
122
|
};
|
|
135
123
|
}
|
|
136
|
-
|
|
137
124
|
return translations;
|
|
138
125
|
}
|
|
139
126
|
async function pushTranslations(translationsByLanguage, {
|
|
@@ -156,11 +143,9 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
156
143
|
formData.append('file_format', 'csv');
|
|
157
144
|
formData.append('branch', branch);
|
|
158
145
|
formData.append('update_translations', 'true');
|
|
159
|
-
|
|
160
146
|
for (const [locale, index] of Object.entries(localeMapping)) {
|
|
161
147
|
formData.append(`locale_mapping[${locale}]`, index);
|
|
162
148
|
}
|
|
163
|
-
|
|
164
149
|
formData.append('format_options[key_index]', keyIndex);
|
|
165
150
|
formData.append('format_options[comment_index]', commentIndex);
|
|
166
151
|
formData.append('format_options[tag_column]', tagColumn);
|
|
@@ -171,7 +156,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
171
156
|
body: formData
|
|
172
157
|
});
|
|
173
158
|
trace('Upload result:\n', result);
|
|
174
|
-
|
|
175
159
|
if (result && 'id' in result) {
|
|
176
160
|
log('Upload ID:', result.id, '\n');
|
|
177
161
|
log('Successfully Uploaded\n');
|
|
@@ -180,7 +164,6 @@ async function pushTranslations(translationsByLanguage, {
|
|
|
180
164
|
log('Response:', result);
|
|
181
165
|
throw new Error('Error uploading');
|
|
182
166
|
}
|
|
183
|
-
|
|
184
167
|
return {
|
|
185
168
|
uploadId: result.id
|
|
186
169
|
};
|
|
@@ -224,63 +207,54 @@ async function pull({
|
|
|
224
207
|
trace(`Pulling translations from Phrase for languages ${config.devLanguage} and ${alternativeLanguages.join(', ')}`);
|
|
225
208
|
const phraseLanguages = Object.keys(allPhraseTranslations);
|
|
226
209
|
trace(`Found Phrase translations for languages ${phraseLanguages.join(', ')}`);
|
|
227
|
-
|
|
228
210
|
if (!phraseLanguages.includes(config.devLanguage)) {
|
|
229
211
|
throw new Error(`Phrase did not return any translations for the configured development language "${config.devLanguage}".\nPlease ensure this language is present in your Phrase project's configuration.`);
|
|
230
212
|
}
|
|
231
|
-
|
|
232
213
|
const allVocabTranslations = await loadAllTranslations({
|
|
233
214
|
fallbacks: 'none',
|
|
234
215
|
includeNodeModules: false,
|
|
235
216
|
withTags: true
|
|
236
217
|
}, config);
|
|
237
|
-
|
|
238
218
|
for (const loadedTranslation of allVocabTranslations) {
|
|
239
219
|
const devTranslations = loadedTranslation.languages[config.devLanguage];
|
|
240
|
-
|
|
241
220
|
if (!devTranslations) {
|
|
242
221
|
throw new Error('No dev language translations loaded');
|
|
243
222
|
}
|
|
244
|
-
|
|
245
|
-
|
|
223
|
+
const defaultValues = {
|
|
224
|
+
...devTranslations
|
|
246
225
|
};
|
|
247
226
|
const localKeys = Object.keys(defaultValues);
|
|
248
|
-
|
|
249
227
|
for (const key of localKeys) {
|
|
250
|
-
defaultValues[key] = {
|
|
228
|
+
defaultValues[key] = {
|
|
229
|
+
...defaultValues[key],
|
|
251
230
|
...allPhraseTranslations[config.devLanguage][getUniqueKey(key, loadedTranslation.namespace)]
|
|
252
231
|
};
|
|
253
|
-
}
|
|
254
|
-
|
|
232
|
+
}
|
|
255
233
|
|
|
234
|
+
// Only write a `_meta` field if necessary
|
|
256
235
|
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
257
236
|
defaultValues._meta = loadedTranslation.metadata;
|
|
258
237
|
}
|
|
259
|
-
|
|
260
238
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
261
|
-
|
|
262
239
|
for (const alternativeLanguage of alternativeLanguages) {
|
|
263
240
|
if (alternativeLanguage in allPhraseTranslations) {
|
|
264
|
-
const altTranslations = {
|
|
241
|
+
const altTranslations = {
|
|
242
|
+
...loadedTranslation.languages[alternativeLanguage]
|
|
265
243
|
};
|
|
266
244
|
const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
|
|
267
|
-
|
|
268
245
|
for (const key of localKeys) {
|
|
269
246
|
var _phraseAltTranslation;
|
|
270
|
-
|
|
271
247
|
const phraseKey = getUniqueKey(key, loadedTranslation.namespace);
|
|
272
248
|
const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
|
|
273
|
-
|
|
274
249
|
if (!phraseTranslationMessage) {
|
|
275
250
|
trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
|
|
276
251
|
continue;
|
|
277
252
|
}
|
|
278
|
-
|
|
279
|
-
|
|
253
|
+
altTranslations[key] = {
|
|
254
|
+
...altTranslations[key],
|
|
280
255
|
message: phraseTranslationMessage
|
|
281
256
|
};
|
|
282
257
|
}
|
|
283
|
-
|
|
284
258
|
const altTranslationFilePath = getAltLanguageFilePath(loadedTranslation.filePath, alternativeLanguage);
|
|
285
259
|
await mkdir(path.dirname(altTranslationFilePath), {
|
|
286
260
|
recursive: true
|
|
@@ -309,48 +283,39 @@ async function push({
|
|
|
309
283
|
await ensureBranch(branch);
|
|
310
284
|
trace(`Pushing translations to phrase for languages ${allLanguages.join(', ')}`);
|
|
311
285
|
const phraseTranslations = {};
|
|
312
|
-
|
|
313
286
|
for (const loadedTranslation of allLanguageTranslations) {
|
|
314
287
|
for (const language of allLanguages) {
|
|
315
288
|
const localTranslations = loadedTranslation.languages[language];
|
|
316
|
-
|
|
317
289
|
if (!localTranslations) {
|
|
318
290
|
continue;
|
|
319
291
|
}
|
|
320
|
-
|
|
321
292
|
if (!phraseTranslations[language]) {
|
|
322
293
|
phraseTranslations[language] = {};
|
|
323
294
|
}
|
|
324
|
-
|
|
325
295
|
const {
|
|
326
296
|
metadata: {
|
|
327
297
|
tags: sharedTags = []
|
|
328
298
|
}
|
|
329
299
|
} = loadedTranslation;
|
|
330
|
-
|
|
331
300
|
for (const localKey of Object.keys(localTranslations)) {
|
|
332
301
|
const phraseKey = getUniqueKey(localKey, loadedTranslation.namespace);
|
|
333
302
|
const {
|
|
334
303
|
tags = [],
|
|
335
304
|
...localTranslation
|
|
336
305
|
} = localTranslations[localKey];
|
|
337
|
-
|
|
338
306
|
if (language === config.devLanguage) {
|
|
339
307
|
localTranslation.tags = [...tags, ...sharedTags];
|
|
340
308
|
}
|
|
341
|
-
|
|
342
309
|
phraseTranslations[language][phraseKey] = localTranslation;
|
|
343
310
|
}
|
|
344
311
|
}
|
|
345
312
|
}
|
|
346
|
-
|
|
347
313
|
const {
|
|
348
314
|
uploadId
|
|
349
315
|
} = await pushTranslations(phraseTranslations, {
|
|
350
316
|
devLanguage: config.devLanguage,
|
|
351
317
|
branch
|
|
352
318
|
});
|
|
353
|
-
|
|
354
319
|
if (deleteUnusedKeys$1) {
|
|
355
320
|
await deleteUnusedKeys(uploadId, branch);
|
|
356
321
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vocab/phrase",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"main": "dist/vocab-phrase.cjs.js",
|
|
5
5
|
"module": "dist/vocab-phrase.esm.js",
|
|
6
6
|
"author": "SEEK",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@vocab/core": "^1.2.
|
|
9
|
+
"@vocab/core": "^1.2.4",
|
|
10
10
|
"@vocab/types": "^1.1.2",
|
|
11
11
|
"chalk": "^4.1.0",
|
|
12
12
|
"csv-stringify": "^6.2.3",
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
"node-fetch": "^2.6.1"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@types/debug": "^4.1.5",
|
|
18
19
|
"@types/node-fetch": "^2.5.7"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"dist"
|
|
22
23
|
]
|
|
23
|
-
}
|
|
24
|
+
}
|