@vocab/phrase 0.0.0-delete-unused-keys-20228144520 → 0.0.0-master-20240711232538
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/index.d.ts +2 -2
- package/dist/declarations/src/pull-translations.d.ts +8 -7
- package/dist/declarations/src/push-translations.d.ts +11 -10
- package/dist/vocab-phrase.cjs.d.ts +1 -0
- package/dist/vocab-phrase.cjs.dev.js +167 -94
- package/dist/vocab-phrase.cjs.prod.js +167 -94
- package/dist/vocab-phrase.esm.js +164 -89
- package/package.json +19 -9
- package/CHANGELOG.md +0 -128
- package/dist/declarations/src/file.d.ts +0 -4
- package/dist/declarations/src/logger.d.ts +0 -3
- package/dist/declarations/src/phrase-api.d.ts +0 -10
- package/src/file.ts +0 -4
- package/src/index.ts +0 -2
- package/src/logger.ts +0 -9
- package/src/phrase-api.ts +0 -173
- package/src/pull-translations.test.ts +0 -169
- package/src/pull-translations.ts +0 -103
- package/src/push-translations.test.ts +0 -150
- package/src/push-translations.ts +0 -67
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,2 +1,2 @@
|
|
|
1
|
-
export { pull } from
|
|
2
|
-
export { push } from
|
|
1
|
+
export { pull } from "./pull-translations.js";
|
|
2
|
+
export { push } from "./push-translations.js";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
interface PullOptions {
|
|
3
|
-
branch?: string;
|
|
4
|
-
deleteUnusedKeys?: boolean;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {}
|
|
1
|
+
import { type UserConfig } from '@vocab/core';
|
|
2
|
+
interface PullOptions {
|
|
3
|
+
branch?: string;
|
|
4
|
+
deleteUnusedKeys?: boolean;
|
|
5
|
+
errorOnNoGlobalKeyTranslation?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function pull({ branch, errorOnNoGlobalKeyTranslation }: PullOptions, config: UserConfig): Promise<void>;
|
|
8
|
+
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { UserConfig } from '@vocab/
|
|
2
|
-
interface PushOptions {
|
|
3
|
-
branch: string;
|
|
4
|
-
deleteUnusedKeys?: boolean;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {}
|
|
1
|
+
import { type UserConfig } from '@vocab/core';
|
|
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 +1,2 @@
|
|
|
1
1
|
export * from "./declarations/src/index";
|
|
2
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidm9jYWItcGhyYXNlLmNqcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi9kZWNsYXJhdGlvbnMvc3JjL2luZGV4LmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEifQ==
|
|
@@ -5,68 +5,106 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
var core = require('@vocab/core');
|
|
8
|
-
var
|
|
9
|
-
var fetch = require('node-fetch');
|
|
10
|
-
var chalk = require('chalk');
|
|
8
|
+
var pc = require('picocolors');
|
|
11
9
|
var debug = require('debug');
|
|
10
|
+
var sync = require('csv-stringify/sync');
|
|
12
11
|
|
|
13
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
14
13
|
|
|
15
14
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
16
|
-
var
|
|
17
|
-
var fetch__default = /*#__PURE__*/_interopDefault(fetch);
|
|
18
|
-
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
15
|
+
var pc__default = /*#__PURE__*/_interopDefault(pc);
|
|
19
16
|
var debug__default = /*#__PURE__*/_interopDefault(debug);
|
|
20
17
|
|
|
21
18
|
const mkdir = fs.promises.mkdir;
|
|
22
19
|
const writeFile = fs.promises.writeFile;
|
|
23
20
|
|
|
24
|
-
const trace = debug__default[
|
|
21
|
+
const trace = debug__default["default"](`vocab:phrase`);
|
|
25
22
|
const log = (...params) => {
|
|
26
23
|
// eslint-disable-next-line no-console
|
|
27
|
-
console.log(
|
|
24
|
+
console.log(pc__default["default"].yellow('Vocab'), ...params);
|
|
28
25
|
};
|
|
29
26
|
|
|
27
|
+
function translationsToCsv(translations, devLanguage) {
|
|
28
|
+
const languages = Object.keys(translations);
|
|
29
|
+
const altLanguages = languages.filter(language => language !== devLanguage);
|
|
30
|
+
const devLanguageTranslations = translations[devLanguage];
|
|
31
|
+
const csvFilesByLanguage = Object.fromEntries(languages.map(language => [language, []]));
|
|
32
|
+
Object.entries(devLanguageTranslations).map(([key, {
|
|
33
|
+
message,
|
|
34
|
+
description,
|
|
35
|
+
tags
|
|
36
|
+
}]) => {
|
|
37
|
+
const sharedData = [key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
|
|
38
|
+
const devLanguageRow = [...sharedData, message];
|
|
39
|
+
csvFilesByLanguage[devLanguage].push(devLanguageRow);
|
|
40
|
+
altLanguages.map(language => {
|
|
41
|
+
var _translations$languag;
|
|
42
|
+
const altTranslationMessage = (_translations$languag = translations[language]) === null || _translations$languag === void 0 || (_translations$languag = _translations$languag[key]) === null || _translations$languag === void 0 ? void 0 : _translations$languag.message;
|
|
43
|
+
if (altTranslationMessage) {
|
|
44
|
+
csvFilesByLanguage[language].push([...sharedData, altTranslationMessage]);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
const csvFileStrings = Object.fromEntries(Object.entries(csvFilesByLanguage)
|
|
49
|
+
// Ensure CSV files are only created if the language has at least 1 translation
|
|
50
|
+
.filter(([_, csvFile]) => csvFile.length > 0).map(([language, csvFile]) => {
|
|
51
|
+
const csvFileString = sync.stringify(csvFile, {
|
|
52
|
+
delimiter: ',',
|
|
53
|
+
header: false
|
|
54
|
+
});
|
|
55
|
+
return [language, csvFileString];
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
// Column indices start at 1
|
|
59
|
+
const keyIndex = 1;
|
|
60
|
+
const commentIndex = keyIndex + 1;
|
|
61
|
+
const tagColumn = commentIndex + 1;
|
|
62
|
+
const messageIndex = tagColumn + 1;
|
|
63
|
+
return {
|
|
64
|
+
csvFileStrings,
|
|
65
|
+
keyIndex,
|
|
66
|
+
messageIndex,
|
|
67
|
+
commentIndex,
|
|
68
|
+
tagColumn
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* eslint-disable no-console */
|
|
30
73
|
function _callPhrase(path, options = {}) {
|
|
31
74
|
const phraseApiToken = process.env.PHRASE_API_TOKEN;
|
|
32
|
-
|
|
33
75
|
if (!phraseApiToken) {
|
|
34
76
|
throw new Error('Missing PHRASE_API_TOKEN');
|
|
35
77
|
}
|
|
36
|
-
|
|
37
|
-
|
|
78
|
+
return fetch(path, {
|
|
79
|
+
...options,
|
|
38
80
|
headers: {
|
|
39
81
|
Authorization: `token ${phraseApiToken}`,
|
|
40
82
|
// Provide identification via User Agent as requested in https://developers.phrase.com/api/#overview--identification-via-user-agent
|
|
41
|
-
'User-Agent': '
|
|
83
|
+
'User-Agent': 'Vocab Client (https://github.com/seek-oss/vocab)',
|
|
42
84
|
...options.headers
|
|
43
85
|
}
|
|
44
86
|
}).then(async response => {
|
|
45
87
|
console.log(`${path}: ${response.status} - ${response.statusText}`);
|
|
46
|
-
|
|
47
|
-
console.log(
|
|
88
|
+
const secondsUntilLimitReset = Math.ceil(Number.parseFloat(response.headers.get('X-Rate-Limit-Reset') || '0') - Date.now() / 1000);
|
|
89
|
+
console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${secondsUntilLimitReset} seconds remaining)`);
|
|
90
|
+
trace('\nLink:', response.headers.get('Link'), '\n');
|
|
91
|
+
// Print All Headers:
|
|
48
92
|
// console.log(Array.from(r.headers.entries()));
|
|
49
93
|
|
|
50
94
|
try {
|
|
51
95
|
var _response$headers$get;
|
|
52
|
-
|
|
53
96
|
const result = await response.json();
|
|
54
|
-
|
|
55
|
-
|
|
97
|
+
trace(`Internal Result (Length: ${result.length})\n`);
|
|
56
98
|
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')) {
|
|
57
99
|
var _response$headers$get2, _response$headers$get3;
|
|
58
|
-
|
|
59
100
|
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 : [];
|
|
60
|
-
|
|
61
101
|
if (!nextPageUrl) {
|
|
62
|
-
throw new Error('
|
|
102
|
+
throw new Error("Can't parse next page URL");
|
|
63
103
|
}
|
|
64
|
-
|
|
65
|
-
console.log('Results recieved with next page: ', nextPageUrl);
|
|
104
|
+
console.log('Results received with next page: ', nextPageUrl);
|
|
66
105
|
const nextPageResult = await _callPhrase(nextPageUrl, options);
|
|
67
106
|
return [...result, ...nextPageResult];
|
|
68
107
|
}
|
|
69
|
-
|
|
70
108
|
return result;
|
|
71
109
|
} catch (e) {
|
|
72
110
|
console.error('Unable to parse response as JSON', e);
|
|
@@ -74,19 +112,15 @@ function _callPhrase(path, options = {}) {
|
|
|
74
112
|
}
|
|
75
113
|
});
|
|
76
114
|
}
|
|
77
|
-
|
|
78
115
|
async function callPhrase(relativePath, options = {}) {
|
|
79
116
|
const projectId = process.env.PHRASE_PROJECT_ID;
|
|
80
|
-
|
|
81
117
|
if (!projectId) {
|
|
82
118
|
throw new Error('Missing PHRASE_PROJECT_ID');
|
|
83
119
|
}
|
|
84
|
-
|
|
85
120
|
return _callPhrase(`https://api.phrase.com/v2/projects/${projectId}/${relativePath}`, options).then(result => {
|
|
86
121
|
if (Array.isArray(result)) {
|
|
87
122
|
console.log('Result length:', result.length);
|
|
88
123
|
}
|
|
89
|
-
|
|
90
124
|
return result;
|
|
91
125
|
}).catch(error => {
|
|
92
126
|
console.error(`Error calling phrase for ${relativePath}:`, error);
|
|
@@ -96,57 +130,79 @@ async function callPhrase(relativePath, options = {}) {
|
|
|
96
130
|
async function pullAllTranslations(branch) {
|
|
97
131
|
const phraseResult = await callPhrase(`translations?branch=${branch}&per_page=100`);
|
|
98
132
|
const translations = {};
|
|
99
|
-
|
|
100
133
|
for (const r of phraseResult) {
|
|
101
134
|
if (!translations[r.locale.code]) {
|
|
102
135
|
translations[r.locale.code] = {};
|
|
103
136
|
}
|
|
104
|
-
|
|
105
137
|
translations[r.locale.code][r.key.name] = {
|
|
106
138
|
message: r.content
|
|
107
139
|
};
|
|
108
140
|
}
|
|
109
|
-
|
|
110
141
|
return translations;
|
|
111
142
|
}
|
|
112
|
-
async function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
contentType: 'application/json',
|
|
117
|
-
filename: `${locale}.json`
|
|
118
|
-
});
|
|
119
|
-
formData.append('file_format', 'json');
|
|
120
|
-
formData.append('locale_id', locale);
|
|
121
|
-
formData.append('branch', branch);
|
|
122
|
-
formData.append('update_translations', 'true');
|
|
123
|
-
trace('Starting to upload:', locale);
|
|
143
|
+
async function pushTranslations(translationsByLanguage, {
|
|
144
|
+
devLanguage,
|
|
145
|
+
branch
|
|
146
|
+
}) {
|
|
124
147
|
const {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
csvFileStrings,
|
|
149
|
+
keyIndex,
|
|
150
|
+
commentIndex,
|
|
151
|
+
tagColumn,
|
|
152
|
+
messageIndex
|
|
153
|
+
} = translationsToCsv(translationsByLanguage, devLanguage);
|
|
154
|
+
let devLanguageUploadId = '';
|
|
155
|
+
for (const [language, csvFileString] of Object.entries(csvFileStrings)) {
|
|
156
|
+
const formData = new FormData();
|
|
157
|
+
formData.append('file', new Blob([csvFileString], {
|
|
158
|
+
type: 'text/csv'
|
|
159
|
+
}), `${language}.translations.csv`);
|
|
160
|
+
formData.append('file_format', 'csv');
|
|
161
|
+
formData.append('branch', branch);
|
|
162
|
+
formData.append('update_translations', 'true');
|
|
163
|
+
formData.append('update_descriptions', 'true');
|
|
164
|
+
formData.append(`locale_mapping[${language}]`, messageIndex.toString());
|
|
165
|
+
formData.append('format_options[key_index]', keyIndex.toString());
|
|
166
|
+
formData.append('format_options[comment_index]', commentIndex.toString());
|
|
167
|
+
formData.append('format_options[tag_column]', tagColumn.toString());
|
|
168
|
+
formData.append('format_options[enable_pluralization]', 'false');
|
|
169
|
+
log(`Uploading translations for language ${language}`);
|
|
170
|
+
const result = await callPhrase(`uploads`, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
body: formData
|
|
173
|
+
});
|
|
174
|
+
trace('Upload result:\n', result);
|
|
175
|
+
if (result && 'id' in result) {
|
|
176
|
+
log('Upload ID:', result.id, '\n');
|
|
177
|
+
log('Successfully Uploaded\n');
|
|
178
|
+
} else {
|
|
179
|
+
log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
|
|
180
|
+
log('Response:', result);
|
|
181
|
+
throw new Error('Error uploading');
|
|
182
|
+
}
|
|
183
|
+
if (language === devLanguage) {
|
|
184
|
+
devLanguageUploadId = result.id;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
132
187
|
return {
|
|
133
|
-
|
|
188
|
+
devLanguageUploadId
|
|
134
189
|
};
|
|
135
190
|
}
|
|
136
|
-
async function deleteUnusedKeys(uploadId,
|
|
191
|
+
async function deleteUnusedKeys(uploadId, branch) {
|
|
137
192
|
const query = `unmentioned_in_upload:${uploadId}`;
|
|
138
|
-
const
|
|
193
|
+
const {
|
|
194
|
+
records_affected
|
|
195
|
+
} = await callPhrase('keys', {
|
|
139
196
|
method: 'DELETE',
|
|
140
197
|
headers: {
|
|
141
198
|
'Content-Type': 'application/json'
|
|
142
199
|
},
|
|
143
200
|
body: JSON.stringify({
|
|
144
201
|
branch,
|
|
145
|
-
locale_id: locale,
|
|
146
202
|
q: query
|
|
147
203
|
})
|
|
148
204
|
});
|
|
149
|
-
log('Successfully deleted',
|
|
205
|
+
log('Successfully deleted', records_affected, 'unused keys from branch', branch);
|
|
150
206
|
}
|
|
151
207
|
async function ensureBranch(branch) {
|
|
152
208
|
await callPhrase(`branches`, {
|
|
@@ -158,65 +214,74 @@ async function ensureBranch(branch) {
|
|
|
158
214
|
name: branch
|
|
159
215
|
})
|
|
160
216
|
});
|
|
161
|
-
|
|
217
|
+
log('Created branch:', branch);
|
|
162
218
|
}
|
|
163
219
|
|
|
164
220
|
async function pull({
|
|
165
|
-
branch = 'local-development'
|
|
221
|
+
branch = 'local-development',
|
|
222
|
+
errorOnNoGlobalKeyTranslation
|
|
166
223
|
}, config) {
|
|
167
224
|
trace(`Pulling translations from branch ${branch}`);
|
|
168
225
|
await ensureBranch(branch);
|
|
169
226
|
const alternativeLanguages = core.getAltLanguages(config);
|
|
170
227
|
const allPhraseTranslations = await pullAllTranslations(branch);
|
|
171
228
|
trace(`Pulling translations from Phrase for languages ${config.devLanguage} and ${alternativeLanguages.join(', ')}`);
|
|
229
|
+
const phraseLanguages = Object.keys(allPhraseTranslations);
|
|
230
|
+
trace(`Found Phrase translations for languages ${phraseLanguages.join(', ')}`);
|
|
231
|
+
if (!phraseLanguages.includes(config.devLanguage)) {
|
|
232
|
+
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.`);
|
|
233
|
+
}
|
|
172
234
|
const allVocabTranslations = await core.loadAllTranslations({
|
|
173
235
|
fallbacks: 'none',
|
|
174
|
-
includeNodeModules: false
|
|
236
|
+
includeNodeModules: false,
|
|
237
|
+
withTags: true
|
|
175
238
|
}, config);
|
|
176
|
-
|
|
177
239
|
for (const loadedTranslation of allVocabTranslations) {
|
|
178
240
|
const devTranslations = loadedTranslation.languages[config.devLanguage];
|
|
179
|
-
|
|
180
241
|
if (!devTranslations) {
|
|
181
242
|
throw new Error('No dev language translations loaded');
|
|
182
243
|
}
|
|
183
|
-
|
|
184
|
-
|
|
244
|
+
const defaultValues = {
|
|
245
|
+
...devTranslations
|
|
185
246
|
};
|
|
186
247
|
const localKeys = Object.keys(defaultValues);
|
|
187
|
-
|
|
188
248
|
for (const key of localKeys) {
|
|
189
|
-
|
|
190
|
-
|
|
249
|
+
var _defaultValues$key$gl;
|
|
250
|
+
defaultValues[key] = {
|
|
251
|
+
...defaultValues[key],
|
|
252
|
+
...allPhraseTranslations[config.devLanguage][(_defaultValues$key$gl = defaultValues[key].globalKey) !== null && _defaultValues$key$gl !== void 0 ? _defaultValues$key$gl : core.getUniqueKey(key, loadedTranslation.namespace)]
|
|
191
253
|
};
|
|
192
254
|
}
|
|
193
255
|
|
|
256
|
+
// Only write a `_meta` field if necessary
|
|
257
|
+
if (Object.keys(loadedTranslation.metadata).length > 0) {
|
|
258
|
+
defaultValues._meta = loadedTranslation.metadata;
|
|
259
|
+
}
|
|
194
260
|
await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
|
|
195
|
-
|
|
196
261
|
for (const alternativeLanguage of alternativeLanguages) {
|
|
197
262
|
if (alternativeLanguage in allPhraseTranslations) {
|
|
198
|
-
const altTranslations = {
|
|
263
|
+
const altTranslations = {
|
|
264
|
+
...loadedTranslation.languages[alternativeLanguage]
|
|
199
265
|
};
|
|
200
266
|
const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
|
|
201
|
-
|
|
202
267
|
for (const key of localKeys) {
|
|
203
|
-
var _phraseAltTranslation;
|
|
204
|
-
|
|
205
|
-
const phraseKey = core.getUniqueKey(key, loadedTranslation.namespace);
|
|
268
|
+
var _defaultValues$key$gl2, _phraseAltTranslation;
|
|
269
|
+
const phraseKey = (_defaultValues$key$gl2 = defaultValues[key].globalKey) !== null && _defaultValues$key$gl2 !== void 0 ? _defaultValues$key$gl2 : core.getUniqueKey(key, loadedTranslation.namespace);
|
|
206
270
|
const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
|
|
207
|
-
|
|
208
271
|
if (!phraseTranslationMessage) {
|
|
209
272
|
trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
|
|
273
|
+
if (errorOnNoGlobalKeyTranslation && defaultValues[key].globalKey) {
|
|
274
|
+
throw new Error(`Missing translation for global key ${key} in language ${alternativeLanguage}`);
|
|
275
|
+
}
|
|
210
276
|
continue;
|
|
211
277
|
}
|
|
212
|
-
|
|
213
|
-
|
|
278
|
+
altTranslations[key] = {
|
|
279
|
+
...altTranslations[key],
|
|
214
280
|
message: phraseTranslationMessage
|
|
215
281
|
};
|
|
216
282
|
}
|
|
217
|
-
|
|
218
283
|
const altTranslationFilePath = core.getAltLanguageFilePath(loadedTranslation.filePath, alternativeLanguage);
|
|
219
|
-
await mkdir(path__default[
|
|
284
|
+
await mkdir(path__default["default"].dirname(altTranslationFilePath), {
|
|
220
285
|
recursive: true
|
|
221
286
|
});
|
|
222
287
|
await writeFile(altTranslationFilePath, `${JSON.stringify(altTranslations, null, 2)}\n`);
|
|
@@ -226,7 +291,8 @@ async function pull({
|
|
|
226
291
|
}
|
|
227
292
|
|
|
228
293
|
/**
|
|
229
|
-
*
|
|
294
|
+
* Uploads translations to the Phrase API for each language.
|
|
295
|
+
* A unique namespace is appended to each key using the file path the key came from.
|
|
230
296
|
*/
|
|
231
297
|
async function push({
|
|
232
298
|
branch,
|
|
@@ -234,43 +300,50 @@ async function push({
|
|
|
234
300
|
}, config) {
|
|
235
301
|
const allLanguageTranslations = await core.loadAllTranslations({
|
|
236
302
|
fallbacks: 'none',
|
|
237
|
-
includeNodeModules: false
|
|
303
|
+
includeNodeModules: false,
|
|
304
|
+
withTags: true
|
|
238
305
|
}, config);
|
|
239
306
|
trace(`Pushing translations to branch ${branch}`);
|
|
240
307
|
const allLanguages = config.languages.map(v => v.name);
|
|
241
308
|
await ensureBranch(branch);
|
|
242
309
|
trace(`Pushing translations to phrase for languages ${allLanguages.join(', ')}`);
|
|
243
310
|
const phraseTranslations = {};
|
|
244
|
-
|
|
245
311
|
for (const loadedTranslation of allLanguageTranslations) {
|
|
246
312
|
for (const language of allLanguages) {
|
|
247
313
|
const localTranslations = loadedTranslation.languages[language];
|
|
248
|
-
|
|
249
314
|
if (!localTranslations) {
|
|
250
315
|
continue;
|
|
251
316
|
}
|
|
252
|
-
|
|
253
317
|
if (!phraseTranslations[language]) {
|
|
254
318
|
phraseTranslations[language] = {};
|
|
255
319
|
}
|
|
256
|
-
|
|
320
|
+
const {
|
|
321
|
+
metadata: {
|
|
322
|
+
tags: sharedTags = []
|
|
323
|
+
}
|
|
324
|
+
} = loadedTranslation;
|
|
257
325
|
for (const localKey of Object.keys(localTranslations)) {
|
|
258
|
-
const
|
|
259
|
-
|
|
326
|
+
const {
|
|
327
|
+
tags = [],
|
|
328
|
+
...localTranslation
|
|
329
|
+
} = localTranslations[localKey];
|
|
330
|
+
if (language === config.devLanguage) {
|
|
331
|
+
localTranslation.tags = [...tags, ...sharedTags];
|
|
332
|
+
}
|
|
333
|
+
const globalKey = loadedTranslation.languages[config.devLanguage][localKey].globalKey;
|
|
334
|
+
const phraseKey = globalKey !== null && globalKey !== void 0 ? globalKey : core.getUniqueKey(localKey, loadedTranslation.namespace);
|
|
335
|
+
phraseTranslations[language][phraseKey] = localTranslation;
|
|
260
336
|
}
|
|
261
337
|
}
|
|
262
338
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
await deleteUnusedKeys(uploadId, language, branch);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
339
|
+
const {
|
|
340
|
+
devLanguageUploadId
|
|
341
|
+
} = await pushTranslations(phraseTranslations, {
|
|
342
|
+
devLanguage: config.devLanguage,
|
|
343
|
+
branch
|
|
344
|
+
});
|
|
345
|
+
if (deleteUnusedKeys$1) {
|
|
346
|
+
await deleteUnusedKeys(devLanguageUploadId, branch);
|
|
274
347
|
}
|
|
275
348
|
}
|
|
276
349
|
|