gtx-cli 2.6.23 → 2.6.24
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/CHANGELOG.md +6 -0
- package/dist/api/downloadFileBatch.js +20 -1
- package/dist/formats/json/extractJson.js +13 -4
- package/dist/formats/json/mergeJson.js +21 -7
- package/dist/fs/config/downloadedVersions.d.ts +1 -0
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/workflow/download.js +13 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.6.24
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1036](https://github.com/generaltranslation/gt/pull/1036) [`5fc08d0`](https://github.com/generaltranslation/gt/commit/5fc08d0028b7936b5916a048786bedc3b13e0042) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Fix: Update composite JSONs when non-translatable content changes, fix bug where `save-local` update composite key index
|
|
8
|
+
|
|
3
9
|
## 2.6.23
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -9,6 +9,7 @@ import mergeYaml from '../formats/yaml/mergeYaml.js';
|
|
|
9
9
|
import { getDownloadedVersions, saveDownloadedVersions, ensureNestedObject, } from '../fs/config/downloadedVersions.js';
|
|
10
10
|
import { recordDownloaded } from '../state/recentDownloads.js';
|
|
11
11
|
import { recordWarning } from '../state/translateWarnings.js';
|
|
12
|
+
import { hashStringSync } from '../utils/hash.js';
|
|
12
13
|
import stringify from 'fast-json-stable-stringify';
|
|
13
14
|
/**
|
|
14
15
|
* Downloads multiple translation files in a single batch request
|
|
@@ -70,16 +71,32 @@ export async function downloadFileBatch(fileTracker, files, options, forceDownlo
|
|
|
70
71
|
// If a local translation already exists for the same source version, skip overwrite
|
|
71
72
|
const downloadedVersion = downloadedVersions.entries[branchId]?.[fileId]?.[versionId]?.[locale];
|
|
72
73
|
const fileExists = fs.existsSync(outputPath);
|
|
73
|
-
|
|
74
|
+
let sourceChanged = false;
|
|
75
|
+
if (downloadedVersion?.sourceHash) {
|
|
76
|
+
try {
|
|
77
|
+
const currentSourceContent = fs.readFileSync(inputPath, 'utf8');
|
|
78
|
+
const currentSourceHash = hashStringSync(currentSourceContent);
|
|
79
|
+
sourceChanged = currentSourceHash !== downloadedVersion.sourceHash;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
sourceChanged = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!forceDownload &&
|
|
86
|
+
!sourceChanged &&
|
|
87
|
+
fileExists &&
|
|
88
|
+
downloadedVersion) {
|
|
74
89
|
result.skipped.push(requestedFile);
|
|
75
90
|
continue;
|
|
76
91
|
}
|
|
77
92
|
let data = file.data;
|
|
93
|
+
let sourceContentHash;
|
|
78
94
|
if (options.options?.jsonSchema && locale) {
|
|
79
95
|
const jsonSchema = validateJsonSchema(options.options, inputPath);
|
|
80
96
|
if (jsonSchema) {
|
|
81
97
|
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
82
98
|
if (originalContent) {
|
|
99
|
+
sourceContentHash = hashStringSync(originalContent);
|
|
83
100
|
data = mergeJson(originalContent, inputPath, options.options, [
|
|
84
101
|
{
|
|
85
102
|
translatedContent: file.data,
|
|
@@ -94,6 +111,7 @@ export async function downloadFileBatch(fileTracker, files, options, forceDownlo
|
|
|
94
111
|
if (yamlSchema) {
|
|
95
112
|
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
96
113
|
if (originalContent) {
|
|
114
|
+
sourceContentHash = hashStringSync(originalContent);
|
|
97
115
|
data = mergeYaml(originalContent, inputPath, options.options, [
|
|
98
116
|
{
|
|
99
117
|
translatedContent: file.data,
|
|
@@ -135,6 +153,7 @@ export async function downloadFileBatch(fileTracker, files, options, forceDownlo
|
|
|
135
153
|
]);
|
|
136
154
|
downloadedVersions.entries[branchId][fileId][versionId][locale] = {
|
|
137
155
|
updatedAt: new Date().toISOString(),
|
|
156
|
+
...(sourceContentHash ? { sourceHash: sourceContentHash } : {}),
|
|
138
157
|
};
|
|
139
158
|
didUpdateDownloadedLock = true;
|
|
140
159
|
}
|
|
@@ -33,6 +33,9 @@ export function extractJson(localContent, inputPath, options, targetLocale, defa
|
|
|
33
33
|
const canonicalTargetLocale = useCanonicalLocaleKeys
|
|
34
34
|
? gt.resolveCanonicalLocale(targetLocale)
|
|
35
35
|
: targetLocale;
|
|
36
|
+
const canonicalDefaultLocale = useCanonicalLocaleKeys
|
|
37
|
+
? gt.resolveCanonicalLocale(defaultLocale)
|
|
38
|
+
: defaultLocale;
|
|
36
39
|
// Handle include-style schemas (simple path-based extraction)
|
|
37
40
|
if (jsonSchema.include) {
|
|
38
41
|
const extracted = flattenJsonWithStringFilter(localJson, jsonSchema.include);
|
|
@@ -58,16 +61,22 @@ export function extractJson(localContent, inputPath, options, targetLocale, defa
|
|
|
58
61
|
logger.warn(`No matching items found for locale ${targetLocale} at path: ${sourceObjectPointer}`);
|
|
59
62
|
continue;
|
|
60
63
|
}
|
|
64
|
+
// Also find default locale items
|
|
65
|
+
const matchingDefaultLocaleItems = findMatchingItemArray(canonicalDefaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
|
|
66
|
+
const defaultKeys = Object.keys(matchingDefaultLocaleItems);
|
|
67
|
+
const targetEntries = Object.entries(matchingTargetLocaleItems);
|
|
61
68
|
// Initialize the nested structure for this source object pointer
|
|
62
69
|
if (!compositeResult[sourceObjectPointer]) {
|
|
63
70
|
compositeResult[sourceObjectPointer] = {};
|
|
64
71
|
}
|
|
65
|
-
// For each
|
|
66
|
-
for (
|
|
72
|
+
// For each target item, use the default locale's key position
|
|
73
|
+
for (let i = 0; i < targetEntries.length; i++) {
|
|
74
|
+
const [, { sourceItem }] = targetEntries[i];
|
|
67
75
|
// Extract values at the include paths
|
|
68
76
|
const extractedValues = flattenJsonWithStringFilter(sourceItem, sourceObjectOptions.include);
|
|
69
|
-
//
|
|
70
|
-
|
|
77
|
+
// Use default locale key
|
|
78
|
+
const outputKey = i < defaultKeys.length ? defaultKeys[i] : targetEntries[i][0];
|
|
79
|
+
compositeResult[sourceObjectPointer][outputKey] = extractedValues;
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
82
|
else {
|
|
@@ -100,10 +100,25 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
100
100
|
? gt.resolveCanonicalLocale(target.targetLocale)
|
|
101
101
|
: target.targetLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
|
|
102
102
|
Object.values(targetItemsToRemove).forEach(({ index }) => indiciesToRemove.add(index));
|
|
103
|
-
//
|
|
103
|
+
// Remap mismatched positional keys to current source positions
|
|
104
|
+
const sourceKeys = [...matchingDefaultLocaleItemKeys];
|
|
105
|
+
const remappedTargetItems = {};
|
|
106
|
+
for (const [key, value] of Object.entries(targetItems)) {
|
|
107
|
+
if (matchingDefaultLocaleItemKeys.has(key)) {
|
|
108
|
+
remappedTargetItems[key] = value;
|
|
109
|
+
}
|
|
110
|
+
else if (sourceKeys.length === 1 &&
|
|
111
|
+
!(sourceKeys[0] in remappedTargetItems)) {
|
|
112
|
+
remappedTargetItems[sourceKeys[0]] = value;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
logger.warn(`Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Merge matchingDefaultLocaleItems and remapped targetItems
|
|
104
119
|
const mergedItems = {
|
|
105
120
|
...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),
|
|
106
|
-
...
|
|
121
|
+
...remappedTargetItems,
|
|
107
122
|
};
|
|
108
123
|
// 4. Validate that the mergedItems is not empty
|
|
109
124
|
if (Object.keys(mergedItems).length === 0) {
|
|
@@ -113,8 +128,8 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
113
128
|
for (const [sourceItemPointer, targetItem] of Object.entries(mergedItems)) {
|
|
114
129
|
// 5. Validate that all the array indecies are still present in the source json
|
|
115
130
|
if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {
|
|
116
|
-
logger.
|
|
117
|
-
|
|
131
|
+
logger.warn(`Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`);
|
|
132
|
+
continue;
|
|
118
133
|
}
|
|
119
134
|
// 6. Override the source item with the translated values
|
|
120
135
|
const defaultLocaleSourceItem = matchingDefaultLocaleItems[sourceItemPointer].sourceItem;
|
|
@@ -145,10 +160,9 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
145
160
|
itemsToAdd.push(mutatedSourceItem);
|
|
146
161
|
}
|
|
147
162
|
}
|
|
148
|
-
// 8. Check that items to add is >= items to remove
|
|
163
|
+
// 8. Check that items to add is >= items to remove
|
|
149
164
|
if (itemsToAdd.length < indiciesToRemove.size) {
|
|
150
|
-
logger.
|
|
151
|
-
return exitSync(1);
|
|
165
|
+
logger.warn(`Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`);
|
|
152
166
|
}
|
|
153
167
|
// 9. Remove all items for the target locale (they can be identified by the key)
|
|
154
168
|
const filteredSourceObjectValue = sourceObjectValue.filter((_, index) => !indiciesToRemove.has(index));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "2.6.
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.6.24";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// This file is auto-generated. Do not edit manually.
|
|
2
|
-
export const PACKAGE_VERSION = '2.6.
|
|
2
|
+
export const PACKAGE_VERSION = '2.6.24';
|
|
@@ -57,6 +57,7 @@ export async function downloadTranslations(fileVersionData, jobData, branchData,
|
|
|
57
57
|
skipped: new Map(),
|
|
58
58
|
};
|
|
59
59
|
// Step 1: Poll translation jobs if jobData exists
|
|
60
|
+
let pollTimedOut = false;
|
|
60
61
|
if (jobData) {
|
|
61
62
|
const pollStep = new PollTranslationJobsStep(gt);
|
|
62
63
|
const pollResult = await pollStep.run({
|
|
@@ -75,8 +76,15 @@ export async function downloadTranslations(fileVersionData, jobData, branchData,
|
|
|
75
76
|
recordWarning('failed_translation', value.fileName, `Failed to translate for locale ${value.locale}`);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
79
|
+
// Even if polling timed out, still download whatever completed successfully
|
|
78
80
|
if (!pollResult.success) {
|
|
79
|
-
|
|
81
|
+
pollTimedOut = true;
|
|
82
|
+
if (pollResult.fileTracker.completed.size > 0) {
|
|
83
|
+
logger.warn(chalk.yellow(`Timed out, but ${pollResult.fileTracker.completed.size} translation(s) completed successfully. Downloading completed files...`));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
90
|
else {
|
|
@@ -93,6 +101,10 @@ export async function downloadTranslations(fileVersionData, jobData, branchData,
|
|
|
93
101
|
forceDownload,
|
|
94
102
|
});
|
|
95
103
|
await downloadStep.wait();
|
|
104
|
+
// If polling timed out, report failure even though we downloaded what we could
|
|
105
|
+
if (pollTimedOut) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
96
108
|
return downloadResult;
|
|
97
109
|
}
|
|
98
110
|
/**
|