@vocab/phrase 0.0.0-delete-unused-keys-20228144520 → 0.0.0-feature-ignore-flag-push-20241014224750

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.
@@ -1,10 +1,9 @@
1
1
  import { promises } from 'fs';
2
2
  import path from 'path';
3
3
  import { getAltLanguages, loadAllTranslations, getUniqueKey, getAltLanguageFilePath } from '@vocab/core';
4
- import FormData from 'form-data';
5
- import fetch from 'node-fetch';
6
- import chalk from 'chalk';
4
+ import pc from 'picocolors';
7
5
  import debug from 'debug';
6
+ import { stringify } from 'csv-stringify/sync';
8
7
 
9
8
  const mkdir = promises.mkdir;
10
9
  const writeFile = promises.writeFile;
@@ -12,49 +11,90 @@ const writeFile = promises.writeFile;
12
11
  const trace = debug(`vocab:phrase`);
13
12
  const log = (...params) => {
14
13
  // eslint-disable-next-line no-console
15
- console.log(chalk.yellow('Vocab'), ...params);
14
+ console.log(pc.yellow('Vocab'), ...params);
16
15
  };
17
16
 
17
+ function translationsToCsv(translations, devLanguage) {
18
+ const languages = Object.keys(translations);
19
+ const altLanguages = languages.filter(language => language !== devLanguage);
20
+ const devLanguageTranslations = translations[devLanguage];
21
+ const csvFilesByLanguage = Object.fromEntries(languages.map(language => [language, []]));
22
+ Object.entries(devLanguageTranslations).map(([key, {
23
+ message,
24
+ description,
25
+ tags
26
+ }]) => {
27
+ const sharedData = [key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
28
+ const devLanguageRow = [...sharedData, message];
29
+ csvFilesByLanguage[devLanguage].push(devLanguageRow);
30
+ altLanguages.map(language => {
31
+ var _translations$languag;
32
+ 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;
33
+ if (altTranslationMessage) {
34
+ csvFilesByLanguage[language].push([...sharedData, altTranslationMessage]);
35
+ }
36
+ });
37
+ });
38
+ const csvFileStrings = Object.fromEntries(Object.entries(csvFilesByLanguage)
39
+ // Ensure CSV files are only created if the language has at least 1 translation
40
+ .filter(([_, csvFile]) => csvFile.length > 0).map(([language, csvFile]) => {
41
+ const csvFileString = stringify(csvFile, {
42
+ delimiter: ',',
43
+ header: false
44
+ });
45
+ return [language, csvFileString];
46
+ }));
47
+
48
+ // Column indices start at 1
49
+ const keyIndex = 1;
50
+ const commentIndex = keyIndex + 1;
51
+ const tagColumn = commentIndex + 1;
52
+ const messageIndex = tagColumn + 1;
53
+ return {
54
+ csvFileStrings,
55
+ keyIndex,
56
+ messageIndex,
57
+ commentIndex,
58
+ tagColumn
59
+ };
60
+ }
61
+
62
+ /* eslint-disable no-console */
18
63
  function _callPhrase(path, options = {}) {
19
64
  const phraseApiToken = process.env.PHRASE_API_TOKEN;
20
-
21
65
  if (!phraseApiToken) {
22
66
  throw new Error('Missing PHRASE_API_TOKEN');
23
67
  }
24
-
25
- return fetch(path, { ...options,
68
+ return fetch(path, {
69
+ ...options,
26
70
  headers: {
27
71
  Authorization: `token ${phraseApiToken}`,
28
72
  // Provide identification via User Agent as requested in https://developers.phrase.com/api/#overview--identification-via-user-agent
29
- 'User-Agent': 'SEEK Demo Candidate App (jhope@seek.com.au)',
73
+ 'User-Agent': 'Vocab Client (https://github.com/seek-oss/vocab)',
30
74
  ...options.headers
31
75
  }
32
76
  }).then(async response => {
33
77
  console.log(`${path}: ${response.status} - ${response.statusText}`);
34
- console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${response.headers.get('X-Rate-Limit-Reset')} seconds remaining})`);
35
- console.log('\nLink:', response.headers.get('Link'), '\n'); // Print All Headers:
78
+ const secondsUntilLimitReset = Math.ceil(Number.parseFloat(response.headers.get('X-Rate-Limit-Reset') || '0') - Date.now() / 1000);
79
+ console.log(`Rate Limit: ${response.headers.get('X-Rate-Limit-Remaining')} of ${response.headers.get('X-Rate-Limit-Limit')} remaining. (${secondsUntilLimitReset} seconds remaining)`);
80
+ trace('\nLink:', response.headers.get('Link'), '\n');
81
+ // Print All Headers:
36
82
  // console.log(Array.from(r.headers.entries()));
37
83
 
38
84
  try {
39
85
  var _response$headers$get;
40
-
41
86
  const result = await response.json();
42
- console.log(`Internal Result (Length: ${result.length})\n`);
43
-
87
+ trace(`Internal Result (Length: ${result.length})\n`);
44
88
  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')) {
45
89
  var _response$headers$get2, _response$headers$get3;
46
-
47
90
  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 : [];
48
-
49
91
  if (!nextPageUrl) {
50
- throw new Error('Cant parse next page URL');
92
+ throw new Error("Can't parse next page URL");
51
93
  }
52
-
53
- console.log('Results recieved with next page: ', nextPageUrl);
94
+ console.log('Results received with next page: ', nextPageUrl);
54
95
  const nextPageResult = await _callPhrase(nextPageUrl, options);
55
96
  return [...result, ...nextPageResult];
56
97
  }
57
-
58
98
  return result;
59
99
  } catch (e) {
60
100
  console.error('Unable to parse response as JSON', e);
@@ -62,19 +102,15 @@ function _callPhrase(path, options = {}) {
62
102
  }
63
103
  });
64
104
  }
65
-
66
105
  async function callPhrase(relativePath, options = {}) {
67
106
  const projectId = process.env.PHRASE_PROJECT_ID;
68
-
69
107
  if (!projectId) {
70
108
  throw new Error('Missing PHRASE_PROJECT_ID');
71
109
  }
72
-
73
110
  return _callPhrase(`https://api.phrase.com/v2/projects/${projectId}/${relativePath}`, options).then(result => {
74
111
  if (Array.isArray(result)) {
75
112
  console.log('Result length:', result.length);
76
113
  }
77
-
78
114
  return result;
79
115
  }).catch(error => {
80
116
  console.error(`Error calling phrase for ${relativePath}:`, error);
@@ -84,57 +120,79 @@ async function callPhrase(relativePath, options = {}) {
84
120
  async function pullAllTranslations(branch) {
85
121
  const phraseResult = await callPhrase(`translations?branch=${branch}&per_page=100`);
86
122
  const translations = {};
87
-
88
123
  for (const r of phraseResult) {
89
124
  if (!translations[r.locale.code]) {
90
125
  translations[r.locale.code] = {};
91
126
  }
92
-
93
127
  translations[r.locale.code][r.key.name] = {
94
128
  message: r.content
95
129
  };
96
130
  }
97
-
98
131
  return translations;
99
132
  }
100
- async function pushTranslationsByLocale(contents, locale, branch) {
101
- const formData = new FormData();
102
- const fileContents = Buffer.from(JSON.stringify(contents));
103
- formData.append('file', fileContents, {
104
- contentType: 'application/json',
105
- filename: `${locale}.json`
106
- });
107
- formData.append('file_format', 'json');
108
- formData.append('locale_id', locale);
109
- formData.append('branch', branch);
110
- formData.append('update_translations', 'true');
111
- trace('Starting to upload:', locale);
133
+ async function pushTranslations(translationsByLanguage, {
134
+ devLanguage,
135
+ branch
136
+ }) {
112
137
  const {
113
- id
114
- } = await callPhrase(`uploads`, {
115
- method: 'POST',
116
- body: formData
117
- });
118
- log('Upload ID:', id, '\n');
119
- log('Successfully Uploaded:', locale, '\n');
138
+ csvFileStrings,
139
+ keyIndex,
140
+ commentIndex,
141
+ tagColumn,
142
+ messageIndex
143
+ } = translationsToCsv(translationsByLanguage, devLanguage);
144
+ let devLanguageUploadId = '';
145
+ for (const [language, csvFileString] of Object.entries(csvFileStrings)) {
146
+ const formData = new FormData();
147
+ formData.append('file', new Blob([csvFileString], {
148
+ type: 'text/csv'
149
+ }), `${language}.translations.csv`);
150
+ formData.append('file_format', 'csv');
151
+ formData.append('branch', branch);
152
+ formData.append('update_translations', 'true');
153
+ formData.append('update_descriptions', 'true');
154
+ formData.append(`locale_mapping[${language}]`, messageIndex.toString());
155
+ formData.append('format_options[key_index]', keyIndex.toString());
156
+ formData.append('format_options[comment_index]', commentIndex.toString());
157
+ formData.append('format_options[tag_column]', tagColumn.toString());
158
+ formData.append('format_options[enable_pluralization]', 'false');
159
+ log(`Uploading translations for language ${language}`);
160
+ const result = await callPhrase(`uploads`, {
161
+ method: 'POST',
162
+ body: formData
163
+ });
164
+ trace('Upload result:\n', result);
165
+ if (result && 'id' in result) {
166
+ log('Upload ID:', result.id, '\n');
167
+ log('Successfully Uploaded\n');
168
+ } else {
169
+ log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
170
+ log('Response:', result);
171
+ throw new Error('Error uploading');
172
+ }
173
+ if (language === devLanguage) {
174
+ devLanguageUploadId = result.id;
175
+ }
176
+ }
120
177
  return {
121
- uploadId: id
178
+ devLanguageUploadId
122
179
  };
123
180
  }
124
- async function deleteUnusedKeys(uploadId, locale, branch) {
181
+ async function deleteUnusedKeys(uploadId, branch) {
125
182
  const query = `unmentioned_in_upload:${uploadId}`;
126
- const result = await callPhrase('keys', {
183
+ const {
184
+ records_affected
185
+ } = await callPhrase('keys', {
127
186
  method: 'DELETE',
128
187
  headers: {
129
188
  'Content-Type': 'application/json'
130
189
  },
131
190
  body: JSON.stringify({
132
191
  branch,
133
- locale_id: locale,
134
192
  q: query
135
193
  })
136
194
  });
137
- log('Successfully deleted', result.records_affected, 'unused keys from branch', branch);
195
+ log('Successfully deleted', records_affected, 'unused keys from branch', branch);
138
196
  }
139
197
  async function ensureBranch(branch) {
140
198
  await callPhrase(`branches`, {
@@ -146,63 +204,72 @@ async function ensureBranch(branch) {
146
204
  name: branch
147
205
  })
148
206
  });
149
- trace('Created branch:', branch);
207
+ log('Created branch:', branch);
150
208
  }
151
209
 
152
210
  async function pull({
153
- branch = 'local-development'
211
+ branch = 'local-development',
212
+ errorOnNoGlobalKeyTranslation
154
213
  }, config) {
155
214
  trace(`Pulling translations from branch ${branch}`);
156
215
  await ensureBranch(branch);
157
216
  const alternativeLanguages = getAltLanguages(config);
158
217
  const allPhraseTranslations = await pullAllTranslations(branch);
159
218
  trace(`Pulling translations from Phrase for languages ${config.devLanguage} and ${alternativeLanguages.join(', ')}`);
219
+ const phraseLanguages = Object.keys(allPhraseTranslations);
220
+ trace(`Found Phrase translations for languages ${phraseLanguages.join(', ')}`);
221
+ if (!phraseLanguages.includes(config.devLanguage)) {
222
+ 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.`);
223
+ }
160
224
  const allVocabTranslations = await loadAllTranslations({
161
225
  fallbacks: 'none',
162
- includeNodeModules: false
226
+ includeNodeModules: false,
227
+ withTags: true
163
228
  }, config);
164
-
165
229
  for (const loadedTranslation of allVocabTranslations) {
166
230
  const devTranslations = loadedTranslation.languages[config.devLanguage];
167
-
168
231
  if (!devTranslations) {
169
232
  throw new Error('No dev language translations loaded');
170
233
  }
171
-
172
- const defaultValues = { ...devTranslations
234
+ const defaultValues = {
235
+ ...devTranslations
173
236
  };
174
237
  const localKeys = Object.keys(defaultValues);
175
-
176
238
  for (const key of localKeys) {
177
- defaultValues[key] = { ...defaultValues[key],
178
- ...allPhraseTranslations[config.devLanguage][getUniqueKey(key, loadedTranslation.namespace)]
239
+ var _defaultValues$key$gl;
240
+ defaultValues[key] = {
241
+ ...defaultValues[key],
242
+ ...allPhraseTranslations[config.devLanguage][(_defaultValues$key$gl = defaultValues[key].globalKey) !== null && _defaultValues$key$gl !== void 0 ? _defaultValues$key$gl : getUniqueKey(key, loadedTranslation.namespace)]
179
243
  };
180
244
  }
181
245
 
246
+ // Only write a `_meta` field if necessary
247
+ if (Object.keys(loadedTranslation.metadata).length > 0) {
248
+ defaultValues._meta = loadedTranslation.metadata;
249
+ }
182
250
  await writeFile(loadedTranslation.filePath, `${JSON.stringify(defaultValues, null, 2)}\n`);
183
-
184
251
  for (const alternativeLanguage of alternativeLanguages) {
185
252
  if (alternativeLanguage in allPhraseTranslations) {
186
- const altTranslations = { ...loadedTranslation.languages[alternativeLanguage]
253
+ const altTranslations = {
254
+ ...loadedTranslation.languages[alternativeLanguage]
187
255
  };
188
256
  const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
189
-
190
257
  for (const key of localKeys) {
191
- var _phraseAltTranslation;
192
-
193
- const phraseKey = getUniqueKey(key, loadedTranslation.namespace);
258
+ var _defaultValues$key$gl2, _phraseAltTranslation;
259
+ const phraseKey = (_defaultValues$key$gl2 = defaultValues[key].globalKey) !== null && _defaultValues$key$gl2 !== void 0 ? _defaultValues$key$gl2 : getUniqueKey(key, loadedTranslation.namespace);
194
260
  const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
195
-
196
261
  if (!phraseTranslationMessage) {
197
262
  trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
263
+ if (errorOnNoGlobalKeyTranslation && defaultValues[key].globalKey) {
264
+ throw new Error(`Missing translation for global key ${key} in language ${alternativeLanguage}`);
265
+ }
198
266
  continue;
199
267
  }
200
-
201
- altTranslations[key] = { ...altTranslations[key],
268
+ altTranslations[key] = {
269
+ ...altTranslations[key],
202
270
  message: phraseTranslationMessage
203
271
  };
204
272
  }
205
-
206
273
  const altTranslationFilePath = getAltLanguageFilePath(loadedTranslation.filePath, alternativeLanguage);
207
274
  await mkdir(path.dirname(altTranslationFilePath), {
208
275
  recursive: true
@@ -214,51 +281,63 @@ async function pull({
214
281
  }
215
282
 
216
283
  /**
217
- * Uploading to the Phrase API for each language. Adding a unique namespace to each key using file path they key came from
284
+ * Uploads translations to the Phrase API for each language.
285
+ * A unique namespace is appended to each key using the file path the key came from.
218
286
  */
219
287
  async function push({
220
288
  branch,
221
- deleteUnusedKeys: deleteUnusedKeys$1
289
+ deleteUnusedKeys: deleteUnusedKeys$1,
290
+ ignore
222
291
  }, config) {
223
292
  const allLanguageTranslations = await loadAllTranslations({
224
293
  fallbacks: 'none',
225
- includeNodeModules: false
226
- }, config);
294
+ includeNodeModules: false,
295
+ withTags: true
296
+ }, {
297
+ ...config,
298
+ ignore: [...(config.ignore || []), ...(ignore || [])]
299
+ });
227
300
  trace(`Pushing translations to branch ${branch}`);
228
301
  const allLanguages = config.languages.map(v => v.name);
229
302
  await ensureBranch(branch);
230
303
  trace(`Pushing translations to phrase for languages ${allLanguages.join(', ')}`);
231
304
  const phraseTranslations = {};
232
-
233
305
  for (const loadedTranslation of allLanguageTranslations) {
234
306
  for (const language of allLanguages) {
235
307
  const localTranslations = loadedTranslation.languages[language];
236
-
237
308
  if (!localTranslations) {
238
309
  continue;
239
310
  }
240
-
241
311
  if (!phraseTranslations[language]) {
242
312
  phraseTranslations[language] = {};
243
313
  }
244
-
314
+ const {
315
+ metadata: {
316
+ tags: sharedTags = []
317
+ }
318
+ } = loadedTranslation;
245
319
  for (const localKey of Object.keys(localTranslations)) {
246
- const phraseKey = getUniqueKey(localKey, loadedTranslation.namespace);
247
- phraseTranslations[language][phraseKey] = localTranslations[localKey];
320
+ const {
321
+ tags = [],
322
+ ...localTranslation
323
+ } = localTranslations[localKey];
324
+ if (language === config.devLanguage) {
325
+ localTranslation.tags = [...tags, ...sharedTags];
326
+ }
327
+ const globalKey = loadedTranslation.languages[config.devLanguage][localKey].globalKey;
328
+ const phraseKey = globalKey !== null && globalKey !== void 0 ? globalKey : getUniqueKey(localKey, loadedTranslation.namespace);
329
+ phraseTranslations[language][phraseKey] = localTranslation;
248
330
  }
249
331
  }
250
332
  }
251
-
252
- for (const language of allLanguages) {
253
- if (phraseTranslations[language]) {
254
- const {
255
- uploadId
256
- } = await pushTranslationsByLocale(phraseTranslations[language], language, branch);
257
-
258
- if (deleteUnusedKeys$1) {
259
- await deleteUnusedKeys(uploadId, language, branch);
260
- }
261
- }
333
+ const {
334
+ devLanguageUploadId
335
+ } = await pushTranslations(phraseTranslations, {
336
+ devLanguage: config.devLanguage,
337
+ branch
338
+ });
339
+ if (deleteUnusedKeys$1) {
340
+ await deleteUnusedKeys(devLanguageUploadId, branch);
262
341
  }
263
342
  }
264
343
 
package/package.json CHANGED
@@ -1,19 +1,29 @@
1
1
  {
2
2
  "name": "@vocab/phrase",
3
- "version": "0.0.0-delete-unused-keys-20228144520",
3
+ "version": "0.0.0-feature-ignore-flag-push-20241014224750",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/seek-oss/vocab.git",
7
+ "directory": "packages/phrase"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
4
12
  "main": "dist/vocab-phrase.cjs.js",
5
13
  "module": "dist/vocab-phrase.esm.js",
6
14
  "author": "SEEK",
7
15
  "license": "MIT",
8
16
  "dependencies": {
9
- "@vocab/core": "^1.0.0",
10
- "@vocab/types": "^1.0.0",
11
- "chalk": "^4.1.0",
17
+ "csv-stringify": "^6.2.3",
12
18
  "debug": "^4.3.1",
13
- "form-data": "^3.0.0",
14
- "node-fetch": "^2.6.1"
19
+ "picocolors": "^1.0.0",
20
+ "@vocab/core": "^1.6.2"
15
21
  },
16
22
  "devDependencies": {
17
- "@types/node-fetch": "^2.5.7"
18
- }
19
- }
23
+ "@types/debug": "^4.1.5",
24
+ "@types/node": "^18.11.9"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ]
29
+ }
package/CHANGELOG.md DELETED
@@ -1,128 +0,0 @@
1
- # @vocab/phrase
2
-
3
- ## 0.0.0-delete-unused-keys-20228144520
4
-
5
- ### Minor Changes
6
-
7
- - [`fcd1482`](https://github.com/seek-oss/vocab/commit/fcd1482753e74274935f00ac0fc3afe1bf7f1989) Thanks [@askoufis](https://github.com/askoufis)! - Add an optional `deleteUnusedKeys` flag to the `push` function. If set to `true`, unused keys will be deleted from Phrase after translations are pushed.
8
-
9
- ## 1.0.1
10
-
11
- ### Patch Changes
12
-
13
- - [`20eec77`](https://github.com/seek-oss/vocab/commit/20eec770705d05048ad8b32575cb92720b887f5b) [#76](https://github.com/seek-oss/vocab/pull/76) Thanks [@askoufis](https://github.com/askoufis)! - `vocab pull` no longer errors when phrase returns no translations for a configured language
14
-
15
- ## 1.0.0
16
-
17
- ### Major Changes
18
-
19
- - [`3031054`](https://github.com/seek-oss/vocab/commit/303105440851db6126f0606e1607745b27dd981c) [#51](https://github.com/seek-oss/vocab/pull/51) Thanks [@jahredhope](https://github.com/jahredhope)! - Release v1.0.0
20
-
21
- Release Vocab as v1.0.0 to signify a stable API and support future [semver versioning](https://semver.org/) releases.
22
-
23
- Vocab has seen a lot of iteration and changes since it was first published on 20 November 2020. We are now confident with the API and believe Vocab is ready for common use.
24
-
25
- ### Patch Changes
26
-
27
- - Updated dependencies [[`0074382`](https://github.com/seek-oss/vocab/commit/007438273ef70f5d5ded45777933651ad8df36f6), [`3031054`](https://github.com/seek-oss/vocab/commit/303105440851db6126f0606e1607745b27dd981c)]:
28
- - @vocab/core@1.0.0
29
- - @vocab/types@1.0.0
30
-
31
- ## 0.0.11
32
-
33
- ### Patch Changes
34
-
35
- - Updated dependencies [[`5b1fdc0`](https://github.com/seek-oss/vocab/commit/5b1fdc019522b12e7ef94b2fec57b54a9310d41c)]:
36
- - @vocab/core@0.0.11
37
- - @vocab/types@0.0.9
38
-
39
- ## 0.0.10
40
-
41
- ### Patch Changes
42
-
43
- - Updated dependencies [[`7c96a14`](https://github.com/seek-oss/vocab/commit/7c96a142f602132d38c1df1a47a1f4657dc5c94c)]:
44
- - @vocab/core@0.0.10
45
-
46
- ## 0.0.9
47
-
48
- ### Patch Changes
49
-
50
- - Updated dependencies [[`3034bd3`](https://github.com/seek-oss/vocab/commit/3034bd3de610a9d1f3bfbd8caefa27064dee2710), [`c110745`](https://github.com/seek-oss/vocab/commit/c110745b79df1a8ade6b1d8a49e798b04a7b95e1)]:
51
- - @vocab/core@0.0.9
52
-
53
- ## 0.0.8
54
-
55
- ### Patch Changes
56
-
57
- - Updated dependencies [[`f2fca67`](https://github.com/seek-oss/vocab/commit/f2fca679c66ae65405a0aa24f0a0e472026aad0d)]:
58
- - @vocab/core@0.0.8
59
- - @vocab/types@0.0.8
60
-
61
- ## 0.0.7
62
-
63
- ### Patch Changes
64
-
65
- - [`283bcad`](https://github.com/seek-oss/vocab/commit/283bcada06e622ab14ed891743ed3f55cf09e245) [#33](https://github.com/seek-oss/vocab/pull/33) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Move all vocab files to single directory with configurable suffix
66
-
67
- - Updated dependencies [[`283bcad`](https://github.com/seek-oss/vocab/commit/283bcada06e622ab14ed891743ed3f55cf09e245), [`ad0d240`](https://github.com/seek-oss/vocab/commit/ad0d2404545ded8e11621eae8f29467ff3352366), [`f3992ef`](https://github.com/seek-oss/vocab/commit/f3992efbf08939ebf853fac650a49cc46dc51dfb), [`f3992ef`](https://github.com/seek-oss/vocab/commit/f3992efbf08939ebf853fac650a49cc46dc51dfb)]:
68
- - @vocab/core@0.0.7
69
- - @vocab/types@0.0.7
70
-
71
- ## 0.0.6
72
-
73
- ### Patch Changes
74
-
75
- - [`80a46c0`](https://github.com/seek-oss/vocab/commit/80a46c01a55408675f5822c3618519f80136c3ab) [#27](https://github.com/seek-oss/vocab/pull/27) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Add `ignore` config for ignoring files/folders from cli scripts
76
-
77
- * [`80a46c0`](https://github.com/seek-oss/vocab/commit/80a46c01a55408675f5822c3618519f80136c3ab) [#27](https://github.com/seek-oss/vocab/pull/27) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Ignore node_modules from push, pull and compile scripts
78
-
79
- * Updated dependencies [[`80a46c0`](https://github.com/seek-oss/vocab/commit/80a46c01a55408675f5822c3618519f80136c3ab), [`80a46c0`](https://github.com/seek-oss/vocab/commit/80a46c01a55408675f5822c3618519f80136c3ab)]:
80
- - @vocab/core@0.0.6
81
- - @vocab/types@0.0.6
82
-
83
- ## 0.0.5
84
-
85
- ### Patch Changes
86
-
87
- - Updated dependencies [[`371ed16`](https://github.com/seek-oss/vocab/commit/371ed16a232a04dab13afa7e2b352dfb6724eea4), [`c222d68`](https://github.com/seek-oss/vocab/commit/c222d68a3c0c24723a338eccb959798881f6a118)]:
88
- - @vocab/core@0.0.5
89
-
90
- ## 0.0.4
91
-
92
- ### Patch Changes
93
-
94
- - [`5f5c581`](https://github.com/seek-oss/vocab/commit/5f5c581a65bff28729ee19e1ec0bdea488a9d6c2) [#19](https://github.com/seek-oss/vocab/pull/19) Thanks [@jahredhope](https://github.com/jahredhope)! - Compile useable TypeScript importable files with `vocab compile`.
95
-
96
- The new `vocab compile` step replaces `vocab generate-types` in creating a fully functional **translations.ts** file.
97
-
98
- This allows vocab to be used **without the Webpack Plugin**, however use of the plugin is still heavily advised to ensure optimal loading of translation content on the web.
99
-
100
- Support for unit testing is now better than ever! The newly created **translations.ts** means your unit test code will see the same code as available while rendering.
101
-
102
- See the [documentation](https://github.com/seek-oss/vocab) for further usage details.
103
-
104
- * [`02f943c`](https://github.com/seek-oss/vocab/commit/02f943ca892913b41f9e4720a72400777cf14b3d) [#17](https://github.com/seek-oss/vocab/pull/17) Thanks [@jahredhope](https://github.com/jahredhope)! - Add additional debug traces
105
-
106
- * Updated dependencies [[`5f5c581`](https://github.com/seek-oss/vocab/commit/5f5c581a65bff28729ee19e1ec0bdea488a9d6c2), [`02f943c`](https://github.com/seek-oss/vocab/commit/02f943ca892913b41f9e4720a72400777cf14b3d)]:
107
- - @vocab/core@0.0.4
108
- - @vocab/types@0.0.5
109
-
110
- ## 0.0.3
111
-
112
- ### Patch Changes
113
-
114
- - [`08de30d`](https://github.com/seek-oss/vocab/commit/08de30d338c2a5ebdcf14da7c736dddf22e7ca9e) [#14](https://github.com/seek-oss/vocab/pull/14) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Add ability to override files namespace with \$namespace
115
-
116
- * [`26b52f4`](https://github.com/seek-oss/vocab/commit/26b52f4878ded440841e08c858bdc9e685500c2a) [#16](https://github.com/seek-oss/vocab/pull/16) Thanks [@jahredhope](https://github.com/jahredhope)! - Enable debugging with DEBUG environment variable
117
-
118
- * Updated dependencies [[`08de30d`](https://github.com/seek-oss/vocab/commit/08de30d338c2a5ebdcf14da7c736dddf22e7ca9e), [`ed6cf40`](https://github.com/seek-oss/vocab/commit/ed6cf408973f2e9c4d07a71fcb52f40294ebaf65), [`26b52f4`](https://github.com/seek-oss/vocab/commit/26b52f4878ded440841e08c858bdc9e685500c2a), [`b5a5a05`](https://github.com/seek-oss/vocab/commit/b5a5a05a5bb87b48e6e9160af75f555728143ea2)]:
119
- - @vocab/core@0.0.3
120
- - @vocab/types@0.0.4
121
-
122
- ## 0.0.2
123
-
124
- ### Patch Changes
125
-
126
- - Updated dependencies [[`4710f34`](https://github.com/seek-oss/vocab/commit/4710f341f2827643e3eff69ef7e26d44ec6e8a2b), [`4710f34`](https://github.com/seek-oss/vocab/commit/4710f341f2827643e3eff69ef7e26d44ec6e8a2b)]:
127
- - @vocab/types@0.0.3
128
- - @vocab/core@0.0.2
@@ -1,4 +0,0 @@
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,3 +0,0 @@
1
- import debug from 'debug';
2
- export declare const trace: debug.Debugger;
3
- export declare const log: (...params: unknown[]) => void;
@@ -1,10 +0,0 @@
1
- import { TranslationsByKey } from './../../types/src/index';
2
- import type { TranslationsByLanguage } from '@vocab/types';
3
- import fetch from 'node-fetch';
4
- export declare function callPhrase<T = any>(relativePath: string, options?: Parameters<typeof fetch>[1]): Promise<T>;
5
- export declare function pullAllTranslations(branch: string): Promise<TranslationsByLanguage>;
6
- export declare function pushTranslationsByLocale(contents: TranslationsByKey, locale: string, branch: string): Promise<{
7
- uploadId: string;
8
- }>;
9
- export declare function deleteUnusedKeys(uploadId: string, locale: string, branch: string): Promise<void>;
10
- export declare function ensureBranch(branch: string): Promise<void>;
package/src/file.ts DELETED
@@ -1,4 +0,0 @@
1
- import { promises as fs } from 'fs';
2
-
3
- export const mkdir = fs.mkdir;
4
- export const writeFile = fs.writeFile;
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { pull } from './pull-translations';
2
- export { push } from './push-translations';
package/src/logger.ts DELETED
@@ -1,9 +0,0 @@
1
- import chalk from 'chalk';
2
- import debug from 'debug';
3
-
4
- export const trace = debug(`vocab:phrase`);
5
-
6
- export const log = (...params: unknown[]) => {
7
- // eslint-disable-next-line no-console
8
- console.log(chalk.yellow('Vocab'), ...params);
9
- };