gtx-cli 2.6.5 → 2.6.7

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.6.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#975](https://github.com/generaltranslation/gt/pull/975) [`18d6672`](https://github.com/generaltranslation/gt/commit/18d6672c45d1cafe93817aec67fa60c70a1c7567) Thanks [@brian-lou](https://github.com/brian-lou)! - Add branch config options
8
+
9
+ - [#978](https://github.com/generaltranslation/gt/pull/978) [`73238cf`](https://github.com/generaltranslation/gt/commit/73238cf4d55e8b42c10c870c6a525df9ff36c338) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Fixing `save-local` behavior for composite JSONs
10
+
11
+ ## 2.6.6
12
+
13
+ ### Patch Changes
14
+
15
+ - [#974](https://github.com/generaltranslation/gt/pull/974) [`ba07f07`](https://github.com/generaltranslation/gt/commit/ba07f078079a4ae0d07231fb98fee1a4668a5a39) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Fixing `save-local` behavior for composite JSONs
16
+
3
17
  ## 2.6.5
4
18
 
5
19
  ### Patch Changes
@@ -7,6 +7,7 @@ import { gt } from '../utils/gt.js';
7
7
  import os from 'node:os';
8
8
  import { randomUUID } from 'node:crypto';
9
9
  import { hashStringSync } from '../utils/hash.js';
10
+ import { extractJson } from '../formats/json/extractJson.js';
10
11
  const findLatestDownloadedVersion = (downloadedVersions, branchId, fileId, locale) => {
11
12
  const versionsForFile = downloadedVersions.entries?.[branchId]?.[fileId] ?? undefined;
12
13
  if (!versionsForFile)
@@ -125,7 +126,17 @@ export async function collectAndSendUserEditDiffs(files, settings) {
125
126
  }
126
127
  catch { }
127
128
  if (diff && diff.trim().length > 0) {
128
- const localContent = await fs.promises.readFile(c.outputPath, 'utf8');
129
+ const rawLocalContent = await fs.promises.readFile(c.outputPath, 'utf8');
130
+ // For JSON files with jsonSchema config, extract to composite format
131
+ let localContent = rawLocalContent;
132
+ if (c.fileName.endsWith('.json') &&
133
+ settings.options?.jsonSchema &&
134
+ c.locale !== settings.defaultLocale) {
135
+ const extractedContent = extractJson(rawLocalContent, c.fileName, settings.options, c.locale, settings.defaultLocale);
136
+ if (extractedContent) {
137
+ localContent = extractedContent;
138
+ }
139
+ }
129
140
  collectedDiffs.push({
130
141
  fileName: c.fileName,
131
142
  locale: c.locale,
package/dist/cli/flags.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import findFilepath from '../fs/findFilepath.js';
2
+ import { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';
2
3
  const DEFAULT_TIMEOUT = 600;
3
4
  export function attachSharedFlags(command) {
4
5
  command
@@ -35,7 +36,8 @@ export function attachTranslateFlags(command) {
35
36
  .option('--experimental-clear-locale-dirs', 'Clear locale directories before downloading new translations', false)
36
37
  .option('--branch <branch>', 'Specify a custom branch to use for translations')
37
38
  .option('--disable-branch-detection', 'Disable additional branch detection and optimizations and use the manually specified branch', false)
38
- .option('--enable-branching', 'Enable branching for the project', false); // disabled by default for now
39
+ .option('--enable-branching', 'Enable branching for the project', false) // disabled by default for now
40
+ .option('--remote-name <name>', 'Specify a custom remote name to use for branch detection', DEFAULT_GIT_REMOTE_NAME);
39
41
  return command;
40
42
  }
41
43
  export function attachAdditionalReactTranslateFlags(command) {
@@ -5,7 +5,7 @@ import fs from 'node:fs';
5
5
  import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
6
6
  import { resolveFiles } from '../fs/config/parseFilesConfig.js';
7
7
  import { validateSettings } from './validateSettings.js';
8
- import { GT_DASHBOARD_URL } from '../utils/constants.js';
8
+ import { DEFAULT_GIT_REMOTE_NAME, GT_DASHBOARD_URL, } from '../utils/constants.js';
9
9
  import { resolveProjectId } from '../fs/utils.js';
10
10
  import path from 'node:path';
11
11
  import chalk from 'chalk';
@@ -175,13 +175,17 @@ export async function generateSettings(flags, cwd = process.cwd()) {
175
175
  ];
176
176
  // Add branch options if not provided
177
177
  const branchOptions = mergedOptions.branchOptions || {};
178
- branchOptions.enabled = flags.enableBranching ?? false;
178
+ branchOptions.enabled =
179
+ flags.enableBranching ?? gtConfig.branchOptions?.enabled ?? false;
179
180
  branchOptions.currentBranch =
180
181
  flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;
181
182
  branchOptions.autoDetectBranches = flags.disableBranchDetection
182
183
  ? false
183
- : true;
184
- branchOptions.remoteName = gtConfig.branchOptions?.remoteName ?? 'origin';
184
+ : (gtConfig.branchOptions?.autoDetectBranches ?? true);
185
+ branchOptions.remoteName =
186
+ flags.remoteName ??
187
+ gtConfig.branchOptions?.remoteName ??
188
+ DEFAULT_GIT_REMOTE_NAME;
185
189
  mergedOptions.branchOptions = branchOptions;
186
190
  // if there's no existing config file, creates one
187
191
  // does not include the API key to avoid exposing it
@@ -0,0 +1,15 @@
1
+ import { AdditionalOptions } from '../../types/index.js';
2
+ /**
3
+ * Extracts translated values from a full JSON file back into composite JSON format.
4
+ * This is the inverse of mergeJson - it takes a merged/reconstructed JSON file
5
+ * and extracts the values for a specific locale into the composite structure
6
+ * that the server expects.
7
+ *
8
+ * @param localContent - The full JSON content from the user's local file
9
+ * @param inputPath - The path to the file (used for matching jsonSchema)
10
+ * @param options - Additional options containing jsonSchema config
11
+ * @param targetLocale - The locale to extract values for
12
+ * @param defaultLocale - The default/source locale
13
+ * @returns The composite JSON string, or null if no extraction needed
14
+ */
15
+ export declare function extractJson(localContent: string, inputPath: string, options: AdditionalOptions, targetLocale: string, defaultLocale: string): string | null;
@@ -0,0 +1,92 @@
1
+ import { logger } from '../../console/logger.js';
2
+ import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, validateJsonSchema, } from './utils.js';
3
+ import { flattenJsonWithStringFilter } from './flattenJson.js';
4
+ import { gt } from '../../utils/gt.js';
5
+ /**
6
+ * Extracts translated values from a full JSON file back into composite JSON format.
7
+ * This is the inverse of mergeJson - it takes a merged/reconstructed JSON file
8
+ * and extracts the values for a specific locale into the composite structure
9
+ * that the server expects.
10
+ *
11
+ * @param localContent - The full JSON content from the user's local file
12
+ * @param inputPath - The path to the file (used for matching jsonSchema)
13
+ * @param options - Additional options containing jsonSchema config
14
+ * @param targetLocale - The locale to extract values for
15
+ * @param defaultLocale - The default/source locale
16
+ * @returns The composite JSON string, or null if no extraction needed
17
+ */
18
+ export function extractJson(localContent, inputPath, options, targetLocale, defaultLocale) {
19
+ const jsonSchema = validateJsonSchema(options, inputPath);
20
+ if (!jsonSchema) {
21
+ // No schema
22
+ return null;
23
+ }
24
+ let localJson;
25
+ try {
26
+ localJson = JSON.parse(localContent);
27
+ }
28
+ catch {
29
+ logger.error(`Invalid JSON file: ${inputPath}`);
30
+ return null;
31
+ }
32
+ const useCanonicalLocaleKeys = options?.experimentalCanonicalLocaleKeys ?? false;
33
+ const canonicalTargetLocale = useCanonicalLocaleKeys
34
+ ? gt.resolveCanonicalLocale(targetLocale)
35
+ : targetLocale;
36
+ // Handle include-style schemas (simple path-based extraction)
37
+ if (jsonSchema.include) {
38
+ const extracted = flattenJsonWithStringFilter(localJson, jsonSchema.include);
39
+ return JSON.stringify(extracted, null, 2);
40
+ }
41
+ if (!jsonSchema.composite) {
42
+ logger.error('No include or composite property found in JSON schema');
43
+ return null;
44
+ }
45
+ // Handle composite schemas
46
+ const compositeResult = {};
47
+ // Generate source object pointers from the local JSON
48
+ const sourceObjectPointers = generateSourceObjectPointers(jsonSchema.composite, localJson);
49
+ for (const [sourceObjectPointer, { sourceObjectValue, sourceObjectOptions },] of Object.entries(sourceObjectPointers)) {
50
+ if (sourceObjectOptions.type === 'array') {
51
+ if (!Array.isArray(sourceObjectValue)) {
52
+ logger.warn(`Source object value is not an array at path: ${sourceObjectPointer}`);
53
+ continue;
54
+ }
55
+ // Find the matching items for the target locale
56
+ const matchingTargetLocaleItems = findMatchingItemArray(canonicalTargetLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
57
+ if (!Object.keys(matchingTargetLocaleItems).length) {
58
+ logger.warn(`No matching items found for locale ${targetLocale} at path: ${sourceObjectPointer}`);
59
+ continue;
60
+ }
61
+ // Initialize the nested structure for this source object pointer
62
+ if (!compositeResult[sourceObjectPointer]) {
63
+ compositeResult[sourceObjectPointer] = {};
64
+ }
65
+ // For each matching item, extract the included values
66
+ for (const [itemPointer, { sourceItem }] of Object.entries(matchingTargetLocaleItems)) {
67
+ // Extract values at the include paths
68
+ const extractedValues = flattenJsonWithStringFilter(sourceItem, sourceObjectOptions.include);
69
+ // Store under the item pointer (e.g., "/0")
70
+ compositeResult[sourceObjectPointer][itemPointer] = extractedValues;
71
+ }
72
+ }
73
+ else {
74
+ // Object type
75
+ if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {
76
+ logger.warn(`Source object value is not an object at path: ${sourceObjectPointer}`);
77
+ continue;
78
+ }
79
+ // Find the matching item for the target locale
80
+ const matchingTargetItem = findMatchingItemObject(canonicalTargetLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
81
+ if (!matchingTargetItem.sourceItem) {
82
+ logger.warn(`No matching item found for locale ${targetLocale} at path: ${sourceObjectPointer}`);
83
+ continue;
84
+ }
85
+ // Extract values at the include paths
86
+ const extractedValues = flattenJsonWithStringFilter(matchingTargetItem.sourceItem, sourceObjectOptions.include);
87
+ // Store the extracted values
88
+ compositeResult[sourceObjectPointer] = extractedValues;
89
+ }
90
+ }
91
+ return JSON.stringify(compositeResult, null, 2);
92
+ }
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.6.5";
1
+ export declare const PACKAGE_VERSION = "2.6.7";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.6.5';
2
+ export const PACKAGE_VERSION = '2.6.7';
@@ -2,3 +2,4 @@ export declare const GT_DASHBOARD_URL = "https://dash.generaltranslation.com";
2
2
  export declare const GT_CONFIG_SCHEMA_URL = "https://assets.gtx.dev/config-schema.json";
3
3
  export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
4
4
  export declare const TEMPLATE_FILE_ID: string;
5
+ export declare const DEFAULT_GIT_REMOTE_NAME = "origin";
@@ -3,3 +3,4 @@ export const GT_DASHBOARD_URL = 'https://dash.generaltranslation.com';
3
3
  export const GT_CONFIG_SCHEMA_URL = 'https://assets.gtx.dev/config-schema.json';
4
4
  export const TEMPLATE_FILE_NAME = '__INTERNAL_GT_TEMPLATE_NAME__';
5
5
  export const TEMPLATE_FILE_ID = hashStringSync(TEMPLATE_FILE_NAME);
6
+ export const DEFAULT_GIT_REMOTE_NAME = 'origin';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.6.5",
3
+ "version": "2.6.7",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [