gtx-cli 2.0.22 → 2.0.23

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,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.0.23
4
+
5
+ ### Patch Changes
6
+
7
+ - [#539](https://github.com/generaltranslation/gt/pull/539) [`b88e468`](https://github.com/generaltranslation/gt/commit/b88e4684be9cd82a7d23e38fc893c2a8b7f0165f) Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - feat: add ability to exclude import paths and url paths from localization
8
+
3
9
  ## 2.0.22
4
10
 
5
11
  ### Patch Changes
@@ -17,6 +17,8 @@ export type TranslateOptions = {
17
17
  experimentalHideDefaultLocale?: boolean;
18
18
  experimentalFlattenJsonFiles?: boolean;
19
19
  experimentalLocalizeStaticImports?: boolean;
20
+ excludeStaticUrls?: string[];
21
+ excludeStaticImports?: string[];
20
22
  };
21
23
  export type LoginOptions = {
22
24
  keyType?: 'development' | 'production';
@@ -109,10 +109,14 @@ export async function generateSettings(options, cwd = process.cwd()) {
109
109
  : undefined;
110
110
  mergedOptions = {
111
111
  ...mergedOptions,
112
- experimentalLocalizeStaticImports: gtConfig.options?.experimentalLocalizeStaticImports,
113
- experimentalLocalizeStaticUrls: gtConfig.options?.experimentalLocalizeStaticUrls,
114
- experimentalHideDefaultLocale: gtConfig.options?.experimentalHideDefaultLocale,
115
- experimentalFlattenJsonFiles: gtConfig.options?.experimentalFlattenJsonFiles,
112
+ experimentalLocalizeStaticImports: gtConfig.options?.experimentalLocalizeStaticImports ||
113
+ options.experimentalLocalizeStaticImports,
114
+ experimentalLocalizeStaticUrls: gtConfig.options?.experimentalLocalizeStaticUrls ||
115
+ options.experimentalLocalizeStaticUrls,
116
+ experimentalHideDefaultLocale: gtConfig.options?.experimentalHideDefaultLocale ||
117
+ options.experimentalHideDefaultLocale,
118
+ experimentalFlattenJsonFiles: gtConfig.options?.experimentalFlattenJsonFiles ||
119
+ options.experimentalFlattenJsonFiles,
116
120
  };
117
121
  // Add additional options if provided
118
122
  if (mergedOptions.options) {
@@ -2,8 +2,8 @@ import { getLocaleProperties } from 'generaltranslation';
2
2
  import { exit, logError } from '../../console/logging.js';
3
3
  import { JSONPath } from 'jsonpath-plus';
4
4
  import { flattenJson } from './flattenJson.js';
5
- import micromatch from 'micromatch';
6
5
  import path from 'node:path';
6
+ import micromatch from 'micromatch';
7
7
  const { isMatch } = micromatch;
8
8
  // Find the matching source item in an array
9
9
  // where the key matches the identifying locale property
@@ -108,6 +108,8 @@ export type AdditionalOptions = {
108
108
  };
109
109
  docsUrlPattern?: string;
110
110
  docsImportPattern?: string;
111
+ excludeStaticUrls?: string[];
112
+ excludeStaticImports?: string[];
111
113
  docsHideDefaultLocaleImport?: boolean;
112
114
  copyFiles?: string[];
113
115
  experimentalLocalizeStaticImports?: boolean;
@@ -1,6 +1,8 @@
1
1
  import * as fs from 'fs';
2
2
  import { createFileMapping } from '../formats/files/fileMapping.js';
3
3
  import { logError } from '../console/logging.js';
4
+ import micromatch from 'micromatch';
5
+ const { isMatch } = micromatch;
4
6
  /**
5
7
  * Localizes static imports in content files.
6
8
  * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
@@ -31,38 +33,58 @@ export default async function localizeStaticImports(settings) {
31
33
  // Get file content
32
34
  const fileContent = await fs.promises.readFile(filePath, 'utf8');
33
35
  // Localize the file
34
- const localizedFile = localizeStaticImportsForFile(fileContent, settings.defaultLocale, locale, settings.options?.docsHideDefaultLocaleImport || false, settings.options?.docsImportPattern);
36
+ const localizedFile = localizeStaticImportsForFile(fileContent, settings.defaultLocale, locale, settings.options?.docsHideDefaultLocaleImport || false, settings.options?.docsImportPattern, settings.options?.excludeStaticImports);
35
37
  // Write the localized file to the target path
36
38
  await fs.promises.writeFile(filePath, localizedFile);
37
39
  }));
38
40
  }));
39
41
  }
40
42
  // Naive find and replace, in the future, construct an AST
41
- function localizeStaticImportsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]' // eg /docs/[locale] or /[locale]
42
- ) {
43
+ function localizeStaticImportsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
44
+ exclude = []) {
43
45
  if (!pattern.startsWith('/')) {
44
46
  pattern = '/' + pattern;
45
47
  }
46
48
  // 1. Search for all instances of:
47
49
  const patternHead = pattern.split('[locale]')[0];
50
+ // Escape special regex characters and remove trailing slash if present
51
+ const escapedPatternHead = patternHead
52
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
53
+ .replace(/\/$/, '');
48
54
  let regex;
49
55
  if (hideDefaultLocale) {
50
- const trimmedPatternHead = patternHead.endsWith('/')
51
- ? patternHead.slice(0, -1)
52
- : patternHead;
53
- // Match complete markdown links: `import { Foo } from '@/docs/[locale]/foo.md'`
54
- regex = new RegExp(`import\\s+(.*?)\\s+from\\s+(["'])${trimmedPatternHead}(.*?)\\2`, 'g');
56
+ // Match complete import statements: `import { Foo } from '/docs/foo.md'`
57
+ regex = new RegExp(`import\\s+(.*?)\\s+from\\s+(["'])${escapedPatternHead}(?:/([^"']*?))?\\2`, 'g');
55
58
  }
56
59
  else {
57
- // Match complete markdown links with default locale: `import { Foo } from '@/docs/${defaultLocale}/foo.md'`
58
- regex = new RegExp(`import\\s+(.*?)\\s+from\\s+(["'])${patternHead}${defaultLocale}(.*?)\\2`, 'g');
60
+ // Match complete import statements with default locale: `import { Foo } from '/docs/${defaultLocale}/foo.md'`
61
+ regex = new RegExp(`import\\s+(.*?)\\s+from\\s+(["'])${escapedPatternHead}/${defaultLocale}(?:/([^"']*?))?\\2`, 'g');
59
62
  }
60
63
  const matches = file.match(regex);
61
64
  if (!matches) {
62
65
  return file;
63
66
  }
67
+ exclude = exclude.map((pattern) => pattern.replace(/\[locale\]/g, defaultLocale));
64
68
  // 2. Replace the default locale with the target locale in all matched instances
65
69
  const localizedFile = file.replace(regex, (match, bindings, quoteType, pathContent) => {
70
+ // Check if this path should be excluded from localization
71
+ if (exclude.length > 0) {
72
+ let matchPath = '';
73
+ // let matchPath = patternHead;
74
+ if (pathContent) {
75
+ matchPath = hideDefaultLocale
76
+ ? `${patternHead}${pathContent}`
77
+ : `${patternHead}${defaultLocale}/${pathContent}`;
78
+ }
79
+ else {
80
+ matchPath = hideDefaultLocale
81
+ ? `${patternHead}`
82
+ : `${patternHead}${defaultLocale}`;
83
+ }
84
+ if (exclude.some((pattern) => isMatch(matchPath, pattern))) {
85
+ return match; // Don't localize excluded paths
86
+ }
87
+ }
66
88
  // get the quote type
67
89
  quoteType = match.match(/["']/)?.[0] || '"';
68
90
  if (!quoteType) {
@@ -81,12 +103,12 @@ function localizeStaticImportsForFile(file, defaultLocale, targetLocale, hideDef
81
103
  if (!pathContent || pathContent === '') {
82
104
  return `import ${bindings} from ${quoteType}${patternHead}${targetLocale}${quoteType}`;
83
105
  }
84
- return `import ${bindings} from ${quoteType}${patternHead}${targetLocale}${pathContent}${quoteType}`;
106
+ return `import ${bindings} from ${quoteType}${patternHead}${targetLocale}/${pathContent}${quoteType}`;
85
107
  }
86
108
  else {
87
109
  // For non-hideDefaultLocale, replace defaultLocale with targetLocale
88
110
  // pathContent contains everything after the default locale (no leading slash if present)
89
- return `import ${bindings} from ${quoteType}${patternHead}${targetLocale}${pathContent}${quoteType}`;
111
+ return `import ${bindings} from ${quoteType}${patternHead}${targetLocale}${pathContent ? '/' + pathContent : ''}${quoteType}`;
90
112
  }
91
113
  });
92
114
  return localizedFile;
@@ -1,5 +1,7 @@
1
1
  import * as fs from 'fs';
2
2
  import { createFileMapping } from '../formats/files/fileMapping.js';
3
+ import micromatch from 'micromatch';
4
+ const { isMatch } = micromatch;
3
5
  /**
4
6
  * Localizes static urls in content files.
5
7
  * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
@@ -30,16 +32,16 @@ export default async function localizeStaticUrls(settings) {
30
32
  // Get file content
31
33
  const fileContent = await fs.promises.readFile(filePath, 'utf8');
32
34
  // Localize the file
33
- const localizedFile = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern);
34
- const localizedFileHrefs = localizeStaticHrefsForFile(localizedFile, settings.defaultLocale, locale, settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern);
35
+ const localizedFile = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls);
36
+ const localizedFileHrefs = localizeStaticHrefsForFile(localizedFile, settings.defaultLocale, locale, settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls);
35
37
  // Write the localized file to the target path
36
38
  await fs.promises.writeFile(filePath, localizedFileHrefs);
37
39
  }));
38
40
  }));
39
41
  }
40
42
  // Naive find and replace, in the future, construct an AST
41
- function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]' // eg /docs/[locale] or /[locale]
42
- ) {
43
+ function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
44
+ exclude = []) {
43
45
  if (!pattern.startsWith('/')) {
44
46
  pattern = '/' + pattern;
45
47
  }
@@ -62,8 +64,26 @@ function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaul
62
64
  if (!matches) {
63
65
  return file;
64
66
  }
67
+ exclude = exclude.map((pattern) => pattern.replace(/\[locale\]/g, defaultLocale));
65
68
  // 2. Replace the default locale with the target locale in all matched instances
66
69
  const localizedFile = file.replace(regex, (match, pathContent) => {
70
+ if (exclude.length > 0) {
71
+ // Check if this path should be excluded from localization
72
+ let matchPath = '';
73
+ if (pathContent) {
74
+ matchPath = hideDefaultLocale
75
+ ? `${patternHead}${pathContent}`
76
+ : `${patternHead}${defaultLocale}/${pathContent}`;
77
+ }
78
+ else {
79
+ matchPath = hideDefaultLocale
80
+ ? `${patternHead}`
81
+ : `${patternHead}${defaultLocale}`;
82
+ }
83
+ if (exclude.some((pattern) => isMatch(matchPath, pattern))) {
84
+ return match; // Don't localize excluded paths
85
+ }
86
+ }
67
87
  if (hideDefaultLocale) {
68
88
  // For hideDefaultLocale, check if path already has target locale
69
89
  if (pathContent) {
@@ -81,13 +101,14 @@ function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaul
81
101
  else {
82
102
  // For non-hideDefaultLocale, replace defaultLocale with targetLocale
83
103
  // pathContent contains everything after the default locale (no leading slash if present)
84
- return `](${patternHead}${targetLocale}${pathContent ? '/' + pathContent : ''})`;
104
+ const result = `](${patternHead}${targetLocale}${pathContent ? '/' + pathContent : ''})`;
105
+ return result;
85
106
  }
86
107
  });
87
108
  return localizedFile;
88
109
  }
89
- function localizeStaticHrefsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]' // eg /docs/[locale] or /[locale]
90
- ) {
110
+ function localizeStaticHrefsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
111
+ exclude = []) {
91
112
  if (!pattern.startsWith('/')) {
92
113
  pattern = '/' + pattern;
93
114
  }
@@ -110,8 +131,21 @@ function localizeStaticHrefsForFile(file, defaultLocale, targetLocale, hideDefau
110
131
  if (!matches) {
111
132
  return file;
112
133
  }
134
+ exclude = exclude.map((pattern) => pattern.replace(/\[locale\]/g, defaultLocale));
113
135
  // 2. Replace the default locale with the target locale in all matched instances
114
136
  const localizedFile = file.replace(regex, (match, pathContent) => {
137
+ // Check if this path should be excluded from localization
138
+ if (exclude.length > 0) {
139
+ let matchPath = patternHead;
140
+ if (pathContent) {
141
+ matchPath = hideDefaultLocale
142
+ ? `${patternHead}${pathContent}`
143
+ : `${patternHead}${defaultLocale}/${pathContent}`;
144
+ }
145
+ if (exclude.some((pattern) => isMatch(matchPath, pattern))) {
146
+ return match; // Don't localize excluded paths
147
+ }
148
+ }
115
149
  if (hideDefaultLocale) {
116
150
  // For hideDefaultLocale, check if path already has target locale
117
151
  if (pathContent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.0.22",
3
+ "version": "2.0.23",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [