@vocab/phrase 0.0.0-compiled-translation-import-order-20230328231631 → 0.0.0-global-key-support-20231025223328

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,10 @@
1
- import type { TranslationsByLanguage } from '@vocab/types';
1
+ import type { TranslationsByLanguage } from '@vocab/core';
2
2
  export declare function translationsToCsv(translations: TranslationsByLanguage, devLanguage: string): {
3
- csvString: string;
4
- localeMapping: {
5
- [k: string]: number;
3
+ csvFileStrings: {
4
+ [k: string]: string;
6
5
  };
7
6
  keyIndex: number;
7
+ messageIndex: number;
8
8
  commentIndex: number;
9
9
  tagColumn: number;
10
10
  };
@@ -1,4 +1,4 @@
1
- import type { TranslationsByLanguage } from '@vocab/types';
1
+ import type { TranslationsByLanguage } from '@vocab/core';
2
2
  import fetch from 'node-fetch';
3
3
  export declare function callPhrase<T = any>(relativePath: string, options?: Parameters<typeof fetch>[1]): Promise<T>;
4
4
  export declare function pullAllTranslations(branch: string): Promise<TranslationsByLanguage>;
@@ -6,7 +6,7 @@ export declare function pushTranslations(translationsByLanguage: TranslationsByL
6
6
  devLanguage: string;
7
7
  branch: string;
8
8
  }): Promise<{
9
- uploadId: string;
9
+ devLanguageUploadId: string;
10
10
  }>;
11
11
  export declare function deleteUnusedKeys(uploadId: string, branch: string): Promise<void>;
12
12
  export declare function ensureBranch(branch: string): Promise<void>;
@@ -1,7 +1,8 @@
1
- import type { UserConfig } from '@vocab/types';
1
+ import type { UserConfig } from '@vocab/core';
2
2
  interface PullOptions {
3
3
  branch?: string;
4
4
  deleteUnusedKeys?: boolean;
5
+ errorOnNoGlobalKeyTranslation?: boolean;
5
6
  }
6
- export declare function pull({ branch }: PullOptions, config: UserConfig): Promise<void>;
7
+ export declare function pull({ branch, errorOnNoGlobalKeyTranslation }: PullOptions, config: UserConfig): Promise<void>;
7
8
  export {};
@@ -1,4 +1,4 @@
1
- import { UserConfig } from '@vocab/types';
1
+ import type { UserConfig } from '@vocab/core';
2
2
  interface PushOptions {
3
3
  branch: string;
4
4
  deleteUnusedKeys?: boolean;
@@ -31,34 +31,43 @@ const log = (...params) => {
31
31
  function translationsToCsv(translations, devLanguage) {
32
32
  const languages = Object.keys(translations);
33
33
  const altLanguages = languages.filter(language => language !== devLanguage);
34
- // Ensure languages are ordered for locale mapping
35
- const orderedLanguages = [devLanguage, ...altLanguages];
36
34
  const devLanguageTranslations = translations[devLanguage];
37
- const csv = Object.entries(devLanguageTranslations).map(([key, {
35
+ const csvFilesByLanguage = Object.fromEntries(languages.map(language => [language, []]));
36
+ Object.entries(devLanguageTranslations).map(([key, {
38
37
  message,
39
38
  description,
40
39
  tags
41
40
  }]) => {
42
- const altTranslationMessages = altLanguages.map(language => {
41
+ const sharedData = [key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
42
+ const devLanguageRow = [...sharedData, message];
43
+ csvFilesByLanguage[devLanguage].push(devLanguageRow);
44
+ altLanguages.map(language => {
43
45
  var _translations$languag, _translations$languag2;
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
+ const altTranslationMessage = (_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;
47
+ if (altTranslationMessage) {
48
+ csvFilesByLanguage[language].push([...sharedData, altTranslationMessage]);
49
+ }
45
50
  });
46
- return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
47
- });
48
- const csvString = sync.stringify(csv, {
49
- delimiter: ',',
50
- header: false
51
51
  });
52
+ const csvFileStrings = Object.fromEntries(Object.entries(csvFilesByLanguage)
53
+ // Ensure CSV files are only created if the language has at least 1 translation
54
+ .filter(([_, csvFile]) => csvFile.length > 0).map(([language, csvFile]) => {
55
+ const csvFileString = sync.stringify(csvFile, {
56
+ delimiter: ',',
57
+ header: false
58
+ });
59
+ return [language, csvFileString];
60
+ }));
52
61
 
53
62
  // Column indices start at 1
54
- const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
55
- const keyIndex = orderedLanguages.length + 1;
63
+ const keyIndex = 1;
56
64
  const commentIndex = keyIndex + 1;
57
65
  const tagColumn = commentIndex + 1;
66
+ const messageIndex = tagColumn + 1;
58
67
  return {
59
- csvString,
60
- localeMapping,
68
+ csvFileStrings,
61
69
  keyIndex,
70
+ messageIndex,
62
71
  commentIndex,
63
72
  tagColumn
64
73
  };
@@ -139,45 +148,50 @@ async function pushTranslations(translationsByLanguage, {
139
148
  devLanguage,
140
149
  branch
141
150
  }) {
142
- const formData = new FormData__default["default"]();
143
151
  const {
144
- csvString,
145
- localeMapping,
152
+ csvFileStrings,
146
153
  keyIndex,
147
154
  commentIndex,
148
- tagColumn
155
+ tagColumn,
156
+ messageIndex
149
157
  } = translationsToCsv(translationsByLanguage, devLanguage);
150
- const fileContents = Buffer.from(csvString);
151
- formData.append('file', fileContents, {
152
- contentType: 'text/csv',
153
- filename: 'translations.csv'
154
- });
155
- formData.append('file_format', 'csv');
156
- formData.append('branch', branch);
157
- formData.append('update_translations', 'true');
158
- for (const [locale, index] of Object.entries(localeMapping)) {
159
- formData.append(`locale_mapping[${locale}]`, index);
160
- }
161
- formData.append('format_options[key_index]', keyIndex);
162
- formData.append('format_options[comment_index]', commentIndex);
163
- formData.append('format_options[tag_column]', tagColumn);
164
- formData.append('format_options[enable_pluralization]', 'false');
165
- log('Uploading translations');
166
- const result = await callPhrase(`uploads`, {
167
- method: 'POST',
168
- body: formData
169
- });
170
- trace('Upload result:\n', result);
171
- if (result && 'id' in result) {
172
- log('Upload ID:', result.id, '\n');
173
- log('Successfully Uploaded\n');
174
- } else {
175
- log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
176
- log('Response:', result);
177
- throw new Error('Error uploading');
158
+ let devLanguageUploadId = '';
159
+ for (const [language, csvFileString] of Object.entries(csvFileStrings)) {
160
+ const formData = new FormData__default["default"]();
161
+ const fileContents = Buffer.from(csvFileString);
162
+ formData.append('file', fileContents, {
163
+ contentType: 'text/csv',
164
+ filename: `${language}.translations.csv`
165
+ });
166
+ formData.append('file_format', 'csv');
167
+ formData.append('branch', branch);
168
+ formData.append('update_translations', 'true');
169
+ formData.append('update_descriptions', 'true');
170
+ formData.append(`locale_mapping[${language}]`, messageIndex);
171
+ formData.append('format_options[key_index]', keyIndex);
172
+ formData.append('format_options[comment_index]', commentIndex);
173
+ formData.append('format_options[tag_column]', tagColumn);
174
+ formData.append('format_options[enable_pluralization]', 'false');
175
+ log(`Uploading translations for language ${language}`);
176
+ const result = await callPhrase(`uploads`, {
177
+ method: 'POST',
178
+ body: formData
179
+ });
180
+ trace('Upload result:\n', result);
181
+ if (result && 'id' in result) {
182
+ log('Upload ID:', result.id, '\n');
183
+ log('Successfully Uploaded\n');
184
+ } else {
185
+ log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
186
+ log('Response:', result);
187
+ throw new Error('Error uploading');
188
+ }
189
+ if (language === devLanguage) {
190
+ devLanguageUploadId = result.id;
191
+ }
178
192
  }
179
193
  return {
180
- uploadId: result.id
194
+ devLanguageUploadId
181
195
  };
182
196
  }
183
197
  async function deleteUnusedKeys(uploadId, branch) {
@@ -210,7 +224,8 @@ async function ensureBranch(branch) {
210
224
  }
211
225
 
212
226
  async function pull({
213
- branch = 'local-development'
227
+ branch = 'local-development',
228
+ errorOnNoGlobalKeyTranslation
214
229
  }, config) {
215
230
  trace(`Pulling translations from branch ${branch}`);
216
231
  await ensureBranch(branch);
@@ -237,9 +252,10 @@ async function pull({
237
252
  };
238
253
  const localKeys = Object.keys(defaultValues);
239
254
  for (const key of localKeys) {
255
+ var _defaultValues$key$gl;
240
256
  defaultValues[key] = {
241
257
  ...defaultValues[key],
242
- ...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
258
+ ...allPhraseTranslations[config.devLanguage][(_defaultValues$key$gl = defaultValues[key].globalKey) !== null && _defaultValues$key$gl !== void 0 ? _defaultValues$key$gl : core.getUniqueKey(key, loadedTranslation.namespace)]
243
259
  };
244
260
  }
245
261
 
@@ -255,11 +271,14 @@ async function pull({
255
271
  };
256
272
  const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
257
273
  for (const key of localKeys) {
258
- var _phraseAltTranslation;
259
- const phraseKey = core.getUniqueKey(key, loadedTranslation.namespace);
274
+ var _defaultValues$key$gl2, _phraseAltTranslation;
275
+ const phraseKey = (_defaultValues$key$gl2 = defaultValues[key].globalKey) !== null && _defaultValues$key$gl2 !== void 0 ? _defaultValues$key$gl2 : core.getUniqueKey(key, loadedTranslation.namespace);
260
276
  const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
261
277
  if (!phraseTranslationMessage) {
262
278
  trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
279
+ if (errorOnNoGlobalKeyTranslation && defaultValues[key].globalKey) {
280
+ throw new Error(`Missing translation for global key ${key} in language ${alternativeLanguage}`);
281
+ }
263
282
  continue;
264
283
  }
265
284
  altTranslations[key] = {
@@ -310,7 +329,7 @@ async function push({
310
329
  }
311
330
  } = loadedTranslation;
312
331
  for (const localKey of Object.keys(localTranslations)) {
313
- const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
332
+ var _globalKey;
314
333
  const {
315
334
  tags = [],
316
335
  ...localTranslation
@@ -318,18 +337,23 @@ async function push({
318
337
  if (language === config.devLanguage) {
319
338
  localTranslation.tags = [...tags, ...sharedTags];
320
339
  }
340
+ let globalKey;
341
+ if (loadedTranslation.languages[config.devLanguage][localKey].globalKey) {
342
+ globalKey = loadedTranslation.languages[config.devLanguage][localKey].globalKey;
343
+ }
344
+ const phraseKey = (_globalKey = globalKey) !== null && _globalKey !== void 0 ? _globalKey : core.getUniqueKey(localKey, loadedTranslation.namespace);
321
345
  phraseTranslations[language][phraseKey] = localTranslation;
322
346
  }
323
347
  }
324
348
  }
325
349
  const {
326
- uploadId
350
+ devLanguageUploadId
327
351
  } = await pushTranslations(phraseTranslations, {
328
352
  devLanguage: config.devLanguage,
329
353
  branch
330
354
  });
331
355
  if (deleteUnusedKeys$1) {
332
- await deleteUnusedKeys(uploadId, branch);
356
+ await deleteUnusedKeys(devLanguageUploadId, branch);
333
357
  }
334
358
  }
335
359
 
@@ -31,34 +31,43 @@ const log = (...params) => {
31
31
  function translationsToCsv(translations, devLanguage) {
32
32
  const languages = Object.keys(translations);
33
33
  const altLanguages = languages.filter(language => language !== devLanguage);
34
- // Ensure languages are ordered for locale mapping
35
- const orderedLanguages = [devLanguage, ...altLanguages];
36
34
  const devLanguageTranslations = translations[devLanguage];
37
- const csv = Object.entries(devLanguageTranslations).map(([key, {
35
+ const csvFilesByLanguage = Object.fromEntries(languages.map(language => [language, []]));
36
+ Object.entries(devLanguageTranslations).map(([key, {
38
37
  message,
39
38
  description,
40
39
  tags
41
40
  }]) => {
42
- const altTranslationMessages = altLanguages.map(language => {
41
+ const sharedData = [key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
42
+ const devLanguageRow = [...sharedData, message];
43
+ csvFilesByLanguage[devLanguage].push(devLanguageRow);
44
+ altLanguages.map(language => {
43
45
  var _translations$languag, _translations$languag2;
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
+ const altTranslationMessage = (_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;
47
+ if (altTranslationMessage) {
48
+ csvFilesByLanguage[language].push([...sharedData, altTranslationMessage]);
49
+ }
45
50
  });
46
- return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
47
- });
48
- const csvString = sync.stringify(csv, {
49
- delimiter: ',',
50
- header: false
51
51
  });
52
+ const csvFileStrings = Object.fromEntries(Object.entries(csvFilesByLanguage)
53
+ // Ensure CSV files are only created if the language has at least 1 translation
54
+ .filter(([_, csvFile]) => csvFile.length > 0).map(([language, csvFile]) => {
55
+ const csvFileString = sync.stringify(csvFile, {
56
+ delimiter: ',',
57
+ header: false
58
+ });
59
+ return [language, csvFileString];
60
+ }));
52
61
 
53
62
  // Column indices start at 1
54
- const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
55
- const keyIndex = orderedLanguages.length + 1;
63
+ const keyIndex = 1;
56
64
  const commentIndex = keyIndex + 1;
57
65
  const tagColumn = commentIndex + 1;
66
+ const messageIndex = tagColumn + 1;
58
67
  return {
59
- csvString,
60
- localeMapping,
68
+ csvFileStrings,
61
69
  keyIndex,
70
+ messageIndex,
62
71
  commentIndex,
63
72
  tagColumn
64
73
  };
@@ -139,45 +148,50 @@ async function pushTranslations(translationsByLanguage, {
139
148
  devLanguage,
140
149
  branch
141
150
  }) {
142
- const formData = new FormData__default["default"]();
143
151
  const {
144
- csvString,
145
- localeMapping,
152
+ csvFileStrings,
146
153
  keyIndex,
147
154
  commentIndex,
148
- tagColumn
155
+ tagColumn,
156
+ messageIndex
149
157
  } = translationsToCsv(translationsByLanguage, devLanguage);
150
- const fileContents = Buffer.from(csvString);
151
- formData.append('file', fileContents, {
152
- contentType: 'text/csv',
153
- filename: 'translations.csv'
154
- });
155
- formData.append('file_format', 'csv');
156
- formData.append('branch', branch);
157
- formData.append('update_translations', 'true');
158
- for (const [locale, index] of Object.entries(localeMapping)) {
159
- formData.append(`locale_mapping[${locale}]`, index);
160
- }
161
- formData.append('format_options[key_index]', keyIndex);
162
- formData.append('format_options[comment_index]', commentIndex);
163
- formData.append('format_options[tag_column]', tagColumn);
164
- formData.append('format_options[enable_pluralization]', 'false');
165
- log('Uploading translations');
166
- const result = await callPhrase(`uploads`, {
167
- method: 'POST',
168
- body: formData
169
- });
170
- trace('Upload result:\n', result);
171
- if (result && 'id' in result) {
172
- log('Upload ID:', result.id, '\n');
173
- log('Successfully Uploaded\n');
174
- } else {
175
- log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
176
- log('Response:', result);
177
- throw new Error('Error uploading');
158
+ let devLanguageUploadId = '';
159
+ for (const [language, csvFileString] of Object.entries(csvFileStrings)) {
160
+ const formData = new FormData__default["default"]();
161
+ const fileContents = Buffer.from(csvFileString);
162
+ formData.append('file', fileContents, {
163
+ contentType: 'text/csv',
164
+ filename: `${language}.translations.csv`
165
+ });
166
+ formData.append('file_format', 'csv');
167
+ formData.append('branch', branch);
168
+ formData.append('update_translations', 'true');
169
+ formData.append('update_descriptions', 'true');
170
+ formData.append(`locale_mapping[${language}]`, messageIndex);
171
+ formData.append('format_options[key_index]', keyIndex);
172
+ formData.append('format_options[comment_index]', commentIndex);
173
+ formData.append('format_options[tag_column]', tagColumn);
174
+ formData.append('format_options[enable_pluralization]', 'false');
175
+ log(`Uploading translations for language ${language}`);
176
+ const result = await callPhrase(`uploads`, {
177
+ method: 'POST',
178
+ body: formData
179
+ });
180
+ trace('Upload result:\n', result);
181
+ if (result && 'id' in result) {
182
+ log('Upload ID:', result.id, '\n');
183
+ log('Successfully Uploaded\n');
184
+ } else {
185
+ log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
186
+ log('Response:', result);
187
+ throw new Error('Error uploading');
188
+ }
189
+ if (language === devLanguage) {
190
+ devLanguageUploadId = result.id;
191
+ }
178
192
  }
179
193
  return {
180
- uploadId: result.id
194
+ devLanguageUploadId
181
195
  };
182
196
  }
183
197
  async function deleteUnusedKeys(uploadId, branch) {
@@ -210,7 +224,8 @@ async function ensureBranch(branch) {
210
224
  }
211
225
 
212
226
  async function pull({
213
- branch = 'local-development'
227
+ branch = 'local-development',
228
+ errorOnNoGlobalKeyTranslation
214
229
  }, config) {
215
230
  trace(`Pulling translations from branch ${branch}`);
216
231
  await ensureBranch(branch);
@@ -237,9 +252,10 @@ async function pull({
237
252
  };
238
253
  const localKeys = Object.keys(defaultValues);
239
254
  for (const key of localKeys) {
255
+ var _defaultValues$key$gl;
240
256
  defaultValues[key] = {
241
257
  ...defaultValues[key],
242
- ...allPhraseTranslations[config.devLanguage][core.getUniqueKey(key, loadedTranslation.namespace)]
258
+ ...allPhraseTranslations[config.devLanguage][(_defaultValues$key$gl = defaultValues[key].globalKey) !== null && _defaultValues$key$gl !== void 0 ? _defaultValues$key$gl : core.getUniqueKey(key, loadedTranslation.namespace)]
243
259
  };
244
260
  }
245
261
 
@@ -255,11 +271,14 @@ async function pull({
255
271
  };
256
272
  const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
257
273
  for (const key of localKeys) {
258
- var _phraseAltTranslation;
259
- const phraseKey = core.getUniqueKey(key, loadedTranslation.namespace);
274
+ var _defaultValues$key$gl2, _phraseAltTranslation;
275
+ const phraseKey = (_defaultValues$key$gl2 = defaultValues[key].globalKey) !== null && _defaultValues$key$gl2 !== void 0 ? _defaultValues$key$gl2 : core.getUniqueKey(key, loadedTranslation.namespace);
260
276
  const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
261
277
  if (!phraseTranslationMessage) {
262
278
  trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
279
+ if (errorOnNoGlobalKeyTranslation && defaultValues[key].globalKey) {
280
+ throw new Error(`Missing translation for global key ${key} in language ${alternativeLanguage}`);
281
+ }
263
282
  continue;
264
283
  }
265
284
  altTranslations[key] = {
@@ -310,7 +329,7 @@ async function push({
310
329
  }
311
330
  } = loadedTranslation;
312
331
  for (const localKey of Object.keys(localTranslations)) {
313
- const phraseKey = core.getUniqueKey(localKey, loadedTranslation.namespace);
332
+ var _globalKey;
314
333
  const {
315
334
  tags = [],
316
335
  ...localTranslation
@@ -318,18 +337,23 @@ async function push({
318
337
  if (language === config.devLanguage) {
319
338
  localTranslation.tags = [...tags, ...sharedTags];
320
339
  }
340
+ let globalKey;
341
+ if (loadedTranslation.languages[config.devLanguage][localKey].globalKey) {
342
+ globalKey = loadedTranslation.languages[config.devLanguage][localKey].globalKey;
343
+ }
344
+ const phraseKey = (_globalKey = globalKey) !== null && _globalKey !== void 0 ? _globalKey : core.getUniqueKey(localKey, loadedTranslation.namespace);
321
345
  phraseTranslations[language][phraseKey] = localTranslation;
322
346
  }
323
347
  }
324
348
  }
325
349
  const {
326
- uploadId
350
+ devLanguageUploadId
327
351
  } = await pushTranslations(phraseTranslations, {
328
352
  devLanguage: config.devLanguage,
329
353
  branch
330
354
  });
331
355
  if (deleteUnusedKeys$1) {
332
- await deleteUnusedKeys(uploadId, branch);
356
+ await deleteUnusedKeys(devLanguageUploadId, branch);
333
357
  }
334
358
  }
335
359
 
@@ -19,34 +19,43 @@ const log = (...params) => {
19
19
  function translationsToCsv(translations, devLanguage) {
20
20
  const languages = Object.keys(translations);
21
21
  const altLanguages = languages.filter(language => language !== devLanguage);
22
- // Ensure languages are ordered for locale mapping
23
- const orderedLanguages = [devLanguage, ...altLanguages];
24
22
  const devLanguageTranslations = translations[devLanguage];
25
- const csv = Object.entries(devLanguageTranslations).map(([key, {
23
+ const csvFilesByLanguage = Object.fromEntries(languages.map(language => [language, []]));
24
+ Object.entries(devLanguageTranslations).map(([key, {
26
25
  message,
27
26
  description,
28
27
  tags
29
28
  }]) => {
30
- const altTranslationMessages = altLanguages.map(language => {
29
+ const sharedData = [key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
30
+ const devLanguageRow = [...sharedData, message];
31
+ csvFilesByLanguage[devLanguage].push(devLanguageRow);
32
+ altLanguages.map(language => {
31
33
  var _translations$languag, _translations$languag2;
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
+ const altTranslationMessage = (_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;
35
+ if (altTranslationMessage) {
36
+ csvFilesByLanguage[language].push([...sharedData, altTranslationMessage]);
37
+ }
33
38
  });
34
- return [message, ...altTranslationMessages, key, description, tags === null || tags === void 0 ? void 0 : tags.join(',')];
35
- });
36
- const csvString = stringify(csv, {
37
- delimiter: ',',
38
- header: false
39
39
  });
40
+ const csvFileStrings = Object.fromEntries(Object.entries(csvFilesByLanguage)
41
+ // Ensure CSV files are only created if the language has at least 1 translation
42
+ .filter(([_, csvFile]) => csvFile.length > 0).map(([language, csvFile]) => {
43
+ const csvFileString = stringify(csvFile, {
44
+ delimiter: ',',
45
+ header: false
46
+ });
47
+ return [language, csvFileString];
48
+ }));
40
49
 
41
50
  // Column indices start at 1
42
- const localeMapping = Object.fromEntries(orderedLanguages.map((language, index) => [language, index + 1]));
43
- const keyIndex = orderedLanguages.length + 1;
51
+ const keyIndex = 1;
44
52
  const commentIndex = keyIndex + 1;
45
53
  const tagColumn = commentIndex + 1;
54
+ const messageIndex = tagColumn + 1;
46
55
  return {
47
- csvString,
48
- localeMapping,
56
+ csvFileStrings,
49
57
  keyIndex,
58
+ messageIndex,
50
59
  commentIndex,
51
60
  tagColumn
52
61
  };
@@ -127,45 +136,50 @@ async function pushTranslations(translationsByLanguage, {
127
136
  devLanguage,
128
137
  branch
129
138
  }) {
130
- const formData = new FormData();
131
139
  const {
132
- csvString,
133
- localeMapping,
140
+ csvFileStrings,
134
141
  keyIndex,
135
142
  commentIndex,
136
- tagColumn
143
+ tagColumn,
144
+ messageIndex
137
145
  } = translationsToCsv(translationsByLanguage, devLanguage);
138
- const fileContents = Buffer.from(csvString);
139
- formData.append('file', fileContents, {
140
- contentType: 'text/csv',
141
- filename: 'translations.csv'
142
- });
143
- formData.append('file_format', 'csv');
144
- formData.append('branch', branch);
145
- formData.append('update_translations', 'true');
146
- for (const [locale, index] of Object.entries(localeMapping)) {
147
- formData.append(`locale_mapping[${locale}]`, index);
148
- }
149
- formData.append('format_options[key_index]', keyIndex);
150
- formData.append('format_options[comment_index]', commentIndex);
151
- formData.append('format_options[tag_column]', tagColumn);
152
- formData.append('format_options[enable_pluralization]', 'false');
153
- log('Uploading translations');
154
- const result = await callPhrase(`uploads`, {
155
- method: 'POST',
156
- body: formData
157
- });
158
- trace('Upload result:\n', result);
159
- if (result && 'id' in result) {
160
- log('Upload ID:', result.id, '\n');
161
- log('Successfully Uploaded\n');
162
- } else {
163
- log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
164
- log('Response:', result);
165
- throw new Error('Error uploading');
146
+ let devLanguageUploadId = '';
147
+ for (const [language, csvFileString] of Object.entries(csvFileStrings)) {
148
+ const formData = new FormData();
149
+ const fileContents = Buffer.from(csvFileString);
150
+ formData.append('file', fileContents, {
151
+ contentType: 'text/csv',
152
+ filename: `${language}.translations.csv`
153
+ });
154
+ formData.append('file_format', 'csv');
155
+ formData.append('branch', branch);
156
+ formData.append('update_translations', 'true');
157
+ formData.append('update_descriptions', 'true');
158
+ formData.append(`locale_mapping[${language}]`, messageIndex);
159
+ formData.append('format_options[key_index]', keyIndex);
160
+ formData.append('format_options[comment_index]', commentIndex);
161
+ formData.append('format_options[tag_column]', tagColumn);
162
+ formData.append('format_options[enable_pluralization]', 'false');
163
+ log(`Uploading translations for language ${language}`);
164
+ const result = await callPhrase(`uploads`, {
165
+ method: 'POST',
166
+ body: formData
167
+ });
168
+ trace('Upload result:\n', result);
169
+ if (result && 'id' in result) {
170
+ log('Upload ID:', result.id, '\n');
171
+ log('Successfully Uploaded\n');
172
+ } else {
173
+ log(`Error uploading: ${result === null || result === void 0 ? void 0 : result.message}\n`);
174
+ log('Response:', result);
175
+ throw new Error('Error uploading');
176
+ }
177
+ if (language === devLanguage) {
178
+ devLanguageUploadId = result.id;
179
+ }
166
180
  }
167
181
  return {
168
- uploadId: result.id
182
+ devLanguageUploadId
169
183
  };
170
184
  }
171
185
  async function deleteUnusedKeys(uploadId, branch) {
@@ -198,7 +212,8 @@ async function ensureBranch(branch) {
198
212
  }
199
213
 
200
214
  async function pull({
201
- branch = 'local-development'
215
+ branch = 'local-development',
216
+ errorOnNoGlobalKeyTranslation
202
217
  }, config) {
203
218
  trace(`Pulling translations from branch ${branch}`);
204
219
  await ensureBranch(branch);
@@ -225,9 +240,10 @@ async function pull({
225
240
  };
226
241
  const localKeys = Object.keys(defaultValues);
227
242
  for (const key of localKeys) {
243
+ var _defaultValues$key$gl;
228
244
  defaultValues[key] = {
229
245
  ...defaultValues[key],
230
- ...allPhraseTranslations[config.devLanguage][getUniqueKey(key, loadedTranslation.namespace)]
246
+ ...allPhraseTranslations[config.devLanguage][(_defaultValues$key$gl = defaultValues[key].globalKey) !== null && _defaultValues$key$gl !== void 0 ? _defaultValues$key$gl : getUniqueKey(key, loadedTranslation.namespace)]
231
247
  };
232
248
  }
233
249
 
@@ -243,11 +259,14 @@ async function pull({
243
259
  };
244
260
  const phraseAltTranslations = allPhraseTranslations[alternativeLanguage];
245
261
  for (const key of localKeys) {
246
- var _phraseAltTranslation;
247
- const phraseKey = getUniqueKey(key, loadedTranslation.namespace);
262
+ var _defaultValues$key$gl2, _phraseAltTranslation;
263
+ const phraseKey = (_defaultValues$key$gl2 = defaultValues[key].globalKey) !== null && _defaultValues$key$gl2 !== void 0 ? _defaultValues$key$gl2 : getUniqueKey(key, loadedTranslation.namespace);
248
264
  const phraseTranslationMessage = (_phraseAltTranslation = phraseAltTranslations[phraseKey]) === null || _phraseAltTranslation === void 0 ? void 0 : _phraseAltTranslation.message;
249
265
  if (!phraseTranslationMessage) {
250
266
  trace(`Missing translation. No translation for key ${key} in phrase as ${phraseKey} in language ${alternativeLanguage}.`);
267
+ if (errorOnNoGlobalKeyTranslation && defaultValues[key].globalKey) {
268
+ throw new Error(`Missing translation for global key ${key} in language ${alternativeLanguage}`);
269
+ }
251
270
  continue;
252
271
  }
253
272
  altTranslations[key] = {
@@ -298,7 +317,7 @@ async function push({
298
317
  }
299
318
  } = loadedTranslation;
300
319
  for (const localKey of Object.keys(localTranslations)) {
301
- const phraseKey = getUniqueKey(localKey, loadedTranslation.namespace);
320
+ var _globalKey;
302
321
  const {
303
322
  tags = [],
304
323
  ...localTranslation
@@ -306,18 +325,23 @@ async function push({
306
325
  if (language === config.devLanguage) {
307
326
  localTranslation.tags = [...tags, ...sharedTags];
308
327
  }
328
+ let globalKey;
329
+ if (loadedTranslation.languages[config.devLanguage][localKey].globalKey) {
330
+ globalKey = loadedTranslation.languages[config.devLanguage][localKey].globalKey;
331
+ }
332
+ const phraseKey = (_globalKey = globalKey) !== null && _globalKey !== void 0 ? _globalKey : getUniqueKey(localKey, loadedTranslation.namespace);
309
333
  phraseTranslations[language][phraseKey] = localTranslation;
310
334
  }
311
335
  }
312
336
  }
313
337
  const {
314
- uploadId
338
+ devLanguageUploadId
315
339
  } = await pushTranslations(phraseTranslations, {
316
340
  devLanguage: config.devLanguage,
317
341
  branch
318
342
  });
319
343
  if (deleteUnusedKeys$1) {
320
- await deleteUnusedKeys(uploadId, branch);
344
+ await deleteUnusedKeys(devLanguageUploadId, branch);
321
345
  }
322
346
  }
323
347
 
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@vocab/phrase",
3
- "version": "0.0.0-compiled-translation-import-order-20230328231631",
3
+ "version": "0.0.0-global-key-support-20231025223328",
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": "0.0.0-compiled-translation-import-order-20230328231631",
10
- "@vocab/types": "^1.1.2",
9
+ "@vocab/core": "0.0.0-global-key-support-20231025223328",
11
10
  "chalk": "^4.1.0",
12
11
  "csv-stringify": "^6.2.3",
13
12
  "debug": "^4.3.1",