gtx-cli 2.0.15 → 2.0.17-alpha.1
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/checkFileTranslations.js +4 -2
- package/dist/api/downloadFile.d.ts +1 -1
- package/dist/api/downloadFile.js +2 -2
- package/dist/api/downloadFileBatch.d.ts +1 -0
- package/dist/api/downloadFileBatch.js +6 -4
- package/dist/api/uploadFiles.d.ts +26 -0
- package/dist/api/uploadFiles.js +46 -0
- package/dist/cli/base.d.ts +8 -0
- package/dist/cli/base.js +42 -0
- package/dist/formats/files/fileMapping.d.ts +10 -0
- package/dist/formats/files/fileMapping.js +76 -0
- package/dist/formats/files/translate.d.ts +0 -4
- package/dist/formats/files/translate.js +6 -43
- package/dist/formats/files/upload.d.ts +13 -0
- package/dist/formats/files/upload.js +192 -0
- package/dist/formats/json/mergeJson.d.ts +1 -1
- package/dist/formats/json/mergeJson.js +4 -27
- package/dist/formats/utils.d.ts +2 -0
- package/dist/formats/utils.js +24 -0
- package/dist/fs/config/parseFilesConfig.js +5 -3
- package/dist/types/index.d.ts +6 -5
- package/dist/utils/flattenJsonFiles.js +2 -2
- package/dist/utils/localizeStaticImports.js +2 -2
- package/dist/utils/localizeStaticUrls.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.0.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#512](https://github.com/generaltranslation/gt/pull/512) [`7c01ee8`](https://github.com/generaltranslation/gt/commit/7c01ee8af1e882d222fc3b0224b17f459ec5243b) Thanks [@brian-lou](https://github.com/brian-lou)! - Add new CLI command 'upload', add additional transform options for file translations
|
|
8
|
+
|
|
3
9
|
## 2.0.15
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -180,6 +180,7 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
180
180
|
const outputPath = resolveOutputPath(fileName, locale);
|
|
181
181
|
return {
|
|
182
182
|
translationId,
|
|
183
|
+
inputPath: fileName,
|
|
183
184
|
outputPath,
|
|
184
185
|
locale,
|
|
185
186
|
fileLocale: `${fileName}:${locale}`,
|
|
@@ -187,9 +188,10 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
187
188
|
});
|
|
188
189
|
// Use batch download if there are multiple files
|
|
189
190
|
if (batchFiles.length > 1) {
|
|
190
|
-
const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, locale }) => ({
|
|
191
|
+
const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, inputPath, locale }) => ({
|
|
191
192
|
translationId,
|
|
192
193
|
outputPath,
|
|
194
|
+
inputPath,
|
|
193
195
|
locale,
|
|
194
196
|
})), options);
|
|
195
197
|
// Process results
|
|
@@ -206,7 +208,7 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
206
208
|
else if (batchFiles.length === 1) {
|
|
207
209
|
// For a single file, use the original downloadFile method
|
|
208
210
|
const file = batchFiles[0];
|
|
209
|
-
const result = await downloadFile(file.translationId, file.outputPath, file.locale, options);
|
|
211
|
+
const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
|
|
210
212
|
if (result) {
|
|
211
213
|
downloadStatus.downloaded.add(file.fileLocale);
|
|
212
214
|
}
|
|
@@ -6,4 +6,4 @@ import { Settings } from '../types/index.js';
|
|
|
6
6
|
* @param maxRetries - The maximum number of retries to attempt
|
|
7
7
|
* @param retryDelay - The delay between retries in milliseconds
|
|
8
8
|
*/
|
|
9
|
-
export declare function downloadFile(translationId: string, outputPath: string, locale: string, options: Settings, maxRetries?: number, retryDelay?: number): Promise<boolean>;
|
|
9
|
+
export declare function downloadFile(translationId: string, outputPath: string, inputPath: string, locale: string, options: Settings, maxRetries?: number, retryDelay?: number): Promise<boolean>;
|
package/dist/api/downloadFile.js
CHANGED
|
@@ -12,7 +12,7 @@ import { TextDecoder } from 'node:util';
|
|
|
12
12
|
* @param maxRetries - The maximum number of retries to attempt
|
|
13
13
|
* @param retryDelay - The delay between retries in milliseconds
|
|
14
14
|
*/
|
|
15
|
-
export async function downloadFile(translationId, outputPath, locale, options, maxRetries = 3, retryDelay = 1000) {
|
|
15
|
+
export async function downloadFile(translationId, outputPath, inputPath, locale, options, maxRetries = 3, retryDelay = 1000) {
|
|
16
16
|
let retries = 0;
|
|
17
17
|
while (retries <= maxRetries) {
|
|
18
18
|
try {
|
|
@@ -29,7 +29,7 @@ export async function downloadFile(translationId, outputPath, locale, options, m
|
|
|
29
29
|
if (jsonSchema) {
|
|
30
30
|
const originalContent = fs.readFileSync(outputPath, 'utf8');
|
|
31
31
|
if (originalContent) {
|
|
32
|
-
data = mergeJson(originalContent,
|
|
32
|
+
data = mergeJson(originalContent, inputPath, options.options, [
|
|
33
33
|
{
|
|
34
34
|
translatedContent: data,
|
|
35
35
|
targetLocale: locale,
|
|
@@ -17,6 +17,7 @@ export async function downloadFileBatch(files, options, maxRetries = 3, retryDel
|
|
|
17
17
|
const result = { successful: [], failed: [] };
|
|
18
18
|
// Create a map of translationId to outputPath for easier lookup
|
|
19
19
|
const outputPathMap = new Map(files.map((file) => [file.translationId, file.outputPath]));
|
|
20
|
+
const inputPathMap = new Map(files.map((file) => [file.translationId, file.inputPath]));
|
|
20
21
|
const localeMap = new Map(files.map((file) => [file.translationId, file.locale]));
|
|
21
22
|
while (retries <= maxRetries) {
|
|
22
23
|
try {
|
|
@@ -28,8 +29,9 @@ export async function downloadFileBatch(files, options, maxRetries = 3, retryDel
|
|
|
28
29
|
try {
|
|
29
30
|
const translationId = file.id;
|
|
30
31
|
const outputPath = outputPathMap.get(translationId);
|
|
32
|
+
const inputPath = inputPathMap.get(translationId);
|
|
31
33
|
const locale = localeMap.get(translationId);
|
|
32
|
-
if (!outputPath) {
|
|
34
|
+
if (!outputPath || !inputPath) {
|
|
33
35
|
logWarning(`No output path found for file: ${translationId}`);
|
|
34
36
|
result.failed.push(translationId);
|
|
35
37
|
continue;
|
|
@@ -41,11 +43,11 @@ export async function downloadFileBatch(files, options, maxRetries = 3, retryDel
|
|
|
41
43
|
}
|
|
42
44
|
let data = file.data;
|
|
43
45
|
if (options.options?.jsonSchema && locale) {
|
|
44
|
-
const jsonSchema = validateJsonSchema(options.options,
|
|
46
|
+
const jsonSchema = validateJsonSchema(options.options, inputPath);
|
|
45
47
|
if (jsonSchema) {
|
|
46
|
-
const originalContent = fs.readFileSync(
|
|
48
|
+
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
47
49
|
if (originalContent) {
|
|
48
|
-
data = mergeJson(originalContent,
|
|
50
|
+
data = mergeJson(originalContent, inputPath, options.options, [
|
|
49
51
|
{
|
|
50
52
|
translatedContent: file.data,
|
|
51
53
|
targetLocale: locale,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Settings } from '../types/index.js';
|
|
2
|
+
import { DataFormat, FileFormat } from '../types/data.js';
|
|
3
|
+
export type FileUpload = {
|
|
4
|
+
content: string;
|
|
5
|
+
fileName: string;
|
|
6
|
+
fileFormat: FileFormat;
|
|
7
|
+
dataFormat?: DataFormat;
|
|
8
|
+
locale: string;
|
|
9
|
+
};
|
|
10
|
+
export type UploadData = {
|
|
11
|
+
data: {
|
|
12
|
+
source: FileUpload;
|
|
13
|
+
translations: FileUpload[];
|
|
14
|
+
}[];
|
|
15
|
+
sourceLocale: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Uploads multiple files to the API
|
|
19
|
+
* @param files - Array of file objects to upload
|
|
20
|
+
* @param options - The options for the API call
|
|
21
|
+
* @returns The uploaded content or version ID
|
|
22
|
+
*/
|
|
23
|
+
export declare function uploadFiles(files: {
|
|
24
|
+
source: FileUpload;
|
|
25
|
+
translations: FileUpload[];
|
|
26
|
+
}[], options: Settings): Promise<Response>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { createSpinner, exit, logMessage } from '../console/logging.js';
|
|
3
|
+
/**
|
|
4
|
+
* Uploads multiple files to the API
|
|
5
|
+
* @param files - Array of file objects to upload
|
|
6
|
+
* @param options - The options for the API call
|
|
7
|
+
* @returns The uploaded content or version ID
|
|
8
|
+
*/
|
|
9
|
+
export async function uploadFiles(files, options) {
|
|
10
|
+
logMessage(chalk.cyan('Files to upload:') +
|
|
11
|
+
'\n' +
|
|
12
|
+
files
|
|
13
|
+
.map((file) => ` - ${chalk.bold(file.source.fileName)} -> ${file.translations
|
|
14
|
+
.map((t) => t.locale)
|
|
15
|
+
.join(', ')}`)
|
|
16
|
+
.join('\n'));
|
|
17
|
+
const spinner = createSpinner('dots');
|
|
18
|
+
spinner.start(`Uploading ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation...`);
|
|
19
|
+
const uploadData = {
|
|
20
|
+
data: files.map((file) => ({
|
|
21
|
+
source: file.source,
|
|
22
|
+
translations: file.translations,
|
|
23
|
+
})),
|
|
24
|
+
sourceLocale: options.defaultLocale,
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(`${options.baseUrl}/v1/project/files/upload`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: JSON.stringify(uploadData),
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
'x-gt-api-key': options.apiKey,
|
|
33
|
+
'x-gt-project-id': options.projectId,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(`Failed to upload files: ${response.statusText} (${response.status})`);
|
|
38
|
+
}
|
|
39
|
+
spinner.stop(chalk.green('Files uploaded successfully'));
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
spinner.stop(chalk.red('An unexpected error occurred while uploading files'));
|
|
44
|
+
exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/cli/base.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { Settings, SupportedLibraries, SetupOptions } from '../types/index.js';
|
|
3
|
+
export type UploadOptions = {
|
|
4
|
+
config?: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
projectId?: string;
|
|
7
|
+
defaultLocale?: string;
|
|
8
|
+
};
|
|
3
9
|
export type TranslateOptions = {
|
|
4
10
|
config?: string;
|
|
5
11
|
defaultLocale?: string;
|
|
@@ -23,10 +29,12 @@ export declare class BaseCLI {
|
|
|
23
29
|
init(): void;
|
|
24
30
|
execute(): void;
|
|
25
31
|
protected setupGTCommand(): void;
|
|
32
|
+
protected setupUploadCommand(): void;
|
|
26
33
|
protected setupLoginCommand(): void;
|
|
27
34
|
protected setupInitCommand(): void;
|
|
28
35
|
protected setupConfigureCommand(): void;
|
|
29
36
|
protected setupSetupCommand(): void;
|
|
37
|
+
protected handleUploadCommand(settings: Settings & UploadOptions): Promise<void>;
|
|
30
38
|
protected handleGenericTranslate(settings: Settings & TranslateOptions): Promise<void>;
|
|
31
39
|
protected handleSetupReactCommand(options: SetupOptions): Promise<void>;
|
|
32
40
|
protected handleInitCommand(ranReactSetup: boolean): Promise<void>;
|
package/dist/cli/base.js
CHANGED
|
@@ -18,6 +18,7 @@ import localizeStaticUrls from '../utils/localizeStaticUrls.js';
|
|
|
18
18
|
import flattenJsonFiles from '../utils/flattenJsonFiles.js';
|
|
19
19
|
import localizeStaticImports from '../utils/localizeStaticImports.js';
|
|
20
20
|
import copyFile from '../fs/copyFile.js';
|
|
21
|
+
import { upload } from '../formats/files/upload.js';
|
|
21
22
|
export class BaseCLI {
|
|
22
23
|
library;
|
|
23
24
|
additionalModules;
|
|
@@ -30,6 +31,7 @@ export class BaseCLI {
|
|
|
30
31
|
this.setupInitCommand();
|
|
31
32
|
this.setupConfigureCommand();
|
|
32
33
|
this.setupSetupCommand();
|
|
34
|
+
this.setupUploadCommand();
|
|
33
35
|
this.setupLoginCommand();
|
|
34
36
|
}
|
|
35
37
|
// Init is never called in a child class
|
|
@@ -65,6 +67,22 @@ export class BaseCLI {
|
|
|
65
67
|
endCommand('Done!');
|
|
66
68
|
});
|
|
67
69
|
}
|
|
70
|
+
setupUploadCommand() {
|
|
71
|
+
this.program
|
|
72
|
+
.command('upload')
|
|
73
|
+
.description('Upload source files and translations to the General Translation platform')
|
|
74
|
+
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
75
|
+
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
76
|
+
.option('--project-id <id>', 'Project ID for the translation service')
|
|
77
|
+
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
78
|
+
.action(async (initOptions) => {
|
|
79
|
+
displayHeader('Starting upload...');
|
|
80
|
+
const settings = await generateSettings(initOptions);
|
|
81
|
+
const options = { ...initOptions, ...settings };
|
|
82
|
+
await this.handleUploadCommand(options);
|
|
83
|
+
endCommand('Done!');
|
|
84
|
+
});
|
|
85
|
+
}
|
|
68
86
|
setupLoginCommand() {
|
|
69
87
|
this.program
|
|
70
88
|
.command('auth')
|
|
@@ -155,6 +173,30 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
155
173
|
endCommand("Done! Take advantage of all of General Translation's features by signing up for a free account! https://generaltranslation.com/signup");
|
|
156
174
|
});
|
|
157
175
|
}
|
|
176
|
+
async handleUploadCommand(settings) {
|
|
177
|
+
// dataFormat for JSONs
|
|
178
|
+
let dataFormat;
|
|
179
|
+
if (this.library === 'next-intl') {
|
|
180
|
+
dataFormat = 'ICU';
|
|
181
|
+
}
|
|
182
|
+
else if (this.library === 'i18next') {
|
|
183
|
+
if (this.additionalModules.includes('i18next-icu')) {
|
|
184
|
+
dataFormat = 'ICU';
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
dataFormat = 'I18NEXT';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
dataFormat = 'JSX';
|
|
192
|
+
}
|
|
193
|
+
if (!settings.files) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const { resolvedPaths: sourceFiles, placeholderPaths, transformPaths, } = settings.files;
|
|
197
|
+
// Process all file types at once with a single call
|
|
198
|
+
await upload(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
|
|
199
|
+
}
|
|
158
200
|
async handleGenericTranslate(settings) {
|
|
159
201
|
// dataFormat for JSONs
|
|
160
202
|
let dataFormat;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ResolvedFiles, TransformFiles } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a mapping between source files and their translated counterparts for each locale
|
|
4
|
+
* @param filePaths - Resolved file paths for different file types
|
|
5
|
+
* @param placeholderPaths - Placeholder paths for translated files
|
|
6
|
+
* @param transformPaths - Transform paths for file naming
|
|
7
|
+
* @param locales - List of locales to create a mapping for
|
|
8
|
+
* @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths
|
|
9
|
+
*/
|
|
10
|
+
export declare function createFileMapping(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, targetLocales: string[], defaultLocale: string): Record<string, Record<string, string>>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { SUPPORTED_FILE_EXTENSIONS } from '../files/supportedFiles.js';
|
|
2
|
+
import { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getRelative } from '../../fs/findFilepath.js';
|
|
5
|
+
import { getLocaleProperties } from 'generaltranslation';
|
|
6
|
+
import { replaceLocalePlaceholders } from '../utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Creates a mapping between source files and their translated counterparts for each locale
|
|
9
|
+
* @param filePaths - Resolved file paths for different file types
|
|
10
|
+
* @param placeholderPaths - Placeholder paths for translated files
|
|
11
|
+
* @param transformPaths - Transform paths for file naming
|
|
12
|
+
* @param locales - List of locales to create a mapping for
|
|
13
|
+
* @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths
|
|
14
|
+
*/
|
|
15
|
+
export function createFileMapping(filePaths, placeholderPaths, transformPaths, targetLocales, defaultLocale) {
|
|
16
|
+
const fileMapping = {};
|
|
17
|
+
for (const locale of targetLocales) {
|
|
18
|
+
const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);
|
|
19
|
+
const localeMapping = {};
|
|
20
|
+
// Process each file type
|
|
21
|
+
for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {
|
|
22
|
+
if (!filePaths[typeIndex] || !translatedPaths[typeIndex])
|
|
23
|
+
continue;
|
|
24
|
+
const sourcePaths = filePaths[typeIndex];
|
|
25
|
+
let translatedFiles = translatedPaths[typeIndex];
|
|
26
|
+
if (!translatedFiles)
|
|
27
|
+
continue;
|
|
28
|
+
const transformPath = transformPaths[typeIndex];
|
|
29
|
+
if (transformPath) {
|
|
30
|
+
if (typeof transformPath === 'string') {
|
|
31
|
+
translatedFiles = translatedFiles.map((filePath) => {
|
|
32
|
+
const directory = path.dirname(filePath);
|
|
33
|
+
const fileName = path.basename(filePath);
|
|
34
|
+
const baseName = fileName.split('.')[0];
|
|
35
|
+
const transformedFileName = transformPath
|
|
36
|
+
.replace('*', baseName)
|
|
37
|
+
.replace('[locale]', locale);
|
|
38
|
+
return path.join(directory, transformedFileName);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// transformPath is an object
|
|
43
|
+
const targetLocaleProperties = getLocaleProperties(locale);
|
|
44
|
+
const defaultLocaleProperties = getLocaleProperties(defaultLocale);
|
|
45
|
+
if (!transformPath.replace ||
|
|
46
|
+
typeof transformPath.replace !== 'string') {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Replace all locale property placeholders
|
|
50
|
+
const replaceString = replaceLocalePlaceholders(transformPath.replace, targetLocaleProperties);
|
|
51
|
+
translatedFiles = translatedFiles.map((filePath) => {
|
|
52
|
+
let relativePath = getRelative(filePath);
|
|
53
|
+
if (transformPath.match &&
|
|
54
|
+
typeof transformPath.match === 'string') {
|
|
55
|
+
// Replace locale placeholders in the match string using defaultLocale properties
|
|
56
|
+
let matchString = transformPath.match;
|
|
57
|
+
matchString = replaceLocalePlaceholders(matchString, defaultLocaleProperties);
|
|
58
|
+
relativePath = relativePath.replace(new RegExp(matchString, 'g'), replaceString);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
relativePath = replaceString;
|
|
62
|
+
}
|
|
63
|
+
return path.resolve(relativePath);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < sourcePaths.length; i++) {
|
|
68
|
+
const sourceFile = getRelative(sourcePaths[i]);
|
|
69
|
+
const translatedFile = getRelative(translatedFiles[i]);
|
|
70
|
+
localeMapping[sourceFile] = translatedFile;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
fileMapping[locale] = localeMapping;
|
|
74
|
+
}
|
|
75
|
+
return fileMapping;
|
|
76
|
+
}
|
|
@@ -11,7 +11,3 @@ import { TranslateOptions } from '../../cli/base.js';
|
|
|
11
11
|
* @returns Promise that resolves when translation is complete
|
|
12
12
|
*/
|
|
13
13
|
export declare function translateFiles(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, dataFormat: DataFormat | undefined, options: Settings & TranslateOptions): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
* Creates a mapping between source files and their translated counterparts for each locale
|
|
16
|
-
*/
|
|
17
|
-
export declare function createFileMapping(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, locales: string[]): Record<string, Record<string, string>>;
|
|
@@ -2,15 +2,14 @@ import { checkFileTranslations } from '../../api/checkFileTranslations.js';
|
|
|
2
2
|
import { sendFiles } from '../../api/sendFiles.js';
|
|
3
3
|
import { noSupportedFormatError, noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
|
|
4
4
|
import { logErrorAndExit, createSpinner, logError, logSuccess, } from '../../console/logging.js';
|
|
5
|
-
import { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';
|
|
6
5
|
import { getRelative, readFile } from '../../fs/findFilepath.js';
|
|
7
|
-
import path from 'node:path';
|
|
8
6
|
import chalk from 'chalk';
|
|
9
7
|
import { downloadFile } from '../../api/downloadFile.js';
|
|
10
8
|
import { downloadFileBatch } from '../../api/downloadFileBatch.js';
|
|
11
9
|
import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
12
10
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
13
11
|
import { parseJson } from '../json/parseJson.js';
|
|
12
|
+
import { createFileMapping } from './fileMapping.js';
|
|
14
13
|
const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
15
14
|
/**
|
|
16
15
|
* Sends multiple files to the API for translation
|
|
@@ -95,7 +94,7 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
95
94
|
});
|
|
96
95
|
const { data, locales, translations } = response;
|
|
97
96
|
// Create file mapping for all file types
|
|
98
|
-
const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales);
|
|
97
|
+
const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales, options.defaultLocale);
|
|
99
98
|
// Process any translations that were already completed and returned with the initial response
|
|
100
99
|
const downloadStatus = await processInitialTranslations(translations, fileMapping, options);
|
|
101
100
|
// Check for remaining translations
|
|
@@ -106,44 +105,6 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
106
105
|
logErrorAndExit(`Error translating files: ${error}`);
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Creates a mapping between source files and their translated counterparts for each locale
|
|
111
|
-
*/
|
|
112
|
-
export function createFileMapping(filePaths, placeholderPaths, transformPaths, locales) {
|
|
113
|
-
const fileMapping = {};
|
|
114
|
-
for (const locale of locales) {
|
|
115
|
-
const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);
|
|
116
|
-
const localeMapping = {};
|
|
117
|
-
// Process each file type
|
|
118
|
-
for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {
|
|
119
|
-
if (!filePaths[typeIndex] || !translatedPaths[typeIndex])
|
|
120
|
-
continue;
|
|
121
|
-
const sourcePaths = filePaths[typeIndex];
|
|
122
|
-
let translatedFiles = translatedPaths[typeIndex];
|
|
123
|
-
if (!translatedFiles)
|
|
124
|
-
continue;
|
|
125
|
-
const transformPath = transformPaths[typeIndex];
|
|
126
|
-
if (transformPath) {
|
|
127
|
-
translatedFiles = translatedFiles.map((filePath) => {
|
|
128
|
-
const directory = path.dirname(filePath);
|
|
129
|
-
const fileName = path.basename(filePath);
|
|
130
|
-
const baseName = fileName.split('.')[0];
|
|
131
|
-
const transformedFileName = transformPath
|
|
132
|
-
.replace('*', baseName)
|
|
133
|
-
.replace('[locale]', locale);
|
|
134
|
-
return path.join(directory, transformedFileName);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
for (let i = 0; i < sourcePaths.length; i++) {
|
|
138
|
-
const sourceFile = getRelative(sourcePaths[i]);
|
|
139
|
-
const translatedFile = getRelative(translatedFiles[i]);
|
|
140
|
-
localeMapping[sourceFile] = translatedFile;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
fileMapping[locale] = localeMapping;
|
|
144
|
-
}
|
|
145
|
-
return fileMapping;
|
|
146
|
-
}
|
|
147
108
|
/**
|
|
148
109
|
* Processes translations that were already completed and returned with the initial API response
|
|
149
110
|
* @returns Set of downloaded file+locale combinations
|
|
@@ -171,6 +132,7 @@ async function processInitialTranslations(translations = [], fileMapping, option
|
|
|
171
132
|
}
|
|
172
133
|
return {
|
|
173
134
|
translationId: id,
|
|
135
|
+
inputPath: fileName,
|
|
174
136
|
outputPath,
|
|
175
137
|
fileLocale: `${fileName}:${locale}`,
|
|
176
138
|
locale,
|
|
@@ -182,9 +144,10 @@ async function processInitialTranslations(translations = [], fileMapping, option
|
|
|
182
144
|
}
|
|
183
145
|
// Use batch download if there are multiple files
|
|
184
146
|
if (batchFiles.length > 1) {
|
|
185
|
-
const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, locale }) => ({
|
|
147
|
+
const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, inputPath, locale }) => ({
|
|
186
148
|
translationId,
|
|
187
149
|
outputPath,
|
|
150
|
+
inputPath,
|
|
188
151
|
locale,
|
|
189
152
|
})), options);
|
|
190
153
|
// Process results
|
|
@@ -201,7 +164,7 @@ async function processInitialTranslations(translations = [], fileMapping, option
|
|
|
201
164
|
else if (batchFiles.length === 1) {
|
|
202
165
|
// For a single file, use the original downloadFile method
|
|
203
166
|
const file = batchFiles[0];
|
|
204
|
-
const result = await downloadFile(file.translationId, file.outputPath, file.locale, options);
|
|
167
|
+
const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
|
|
205
168
|
if (result) {
|
|
206
169
|
downloadStatus.downloaded.add(file.fileLocale);
|
|
207
170
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ResolvedFiles, Settings, TransformFiles } from '../../types/index.js';
|
|
2
|
+
import { DataFormat } from '../../types/data.js';
|
|
3
|
+
import { UploadOptions } from '../../cli/base.js';
|
|
4
|
+
/**
|
|
5
|
+
* Sends multiple files to the API for translation
|
|
6
|
+
* @param filePaths - Resolved file paths for different file types
|
|
7
|
+
* @param placeholderPaths - Placeholder paths for translated files
|
|
8
|
+
* @param transformPaths - Transform paths for file naming
|
|
9
|
+
* @param dataFormat - Format of the data within the files
|
|
10
|
+
* @param options - Translation options including API settings
|
|
11
|
+
* @returns Promise that resolves when translation is complete
|
|
12
|
+
*/
|
|
13
|
+
export declare function upload(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, dataFormat: DataFormat | undefined, options: Settings & UploadOptions): Promise<void>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { noSupportedFormatError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
|
|
2
|
+
import { logErrorAndExit, createSpinner, logError, } from '../../console/logging.js';
|
|
3
|
+
import { getRelative, readFile } from '../../fs/findFilepath.js';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { downloadFile } from '../../api/downloadFile.js';
|
|
6
|
+
import { downloadFileBatch } from '../../api/downloadFileBatch.js';
|
|
7
|
+
import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
8
|
+
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
9
|
+
import { parseJson } from '../json/parseJson.js';
|
|
10
|
+
import { uploadFiles } from '../../api/uploadFiles.js';
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { createFileMapping } from './fileMapping.js';
|
|
13
|
+
const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
14
|
+
/**
|
|
15
|
+
* Sends multiple files to the API for translation
|
|
16
|
+
* @param filePaths - Resolved file paths for different file types
|
|
17
|
+
* @param placeholderPaths - Placeholder paths for translated files
|
|
18
|
+
* @param transformPaths - Transform paths for file naming
|
|
19
|
+
* @param dataFormat - Format of the data within the files
|
|
20
|
+
* @param options - Translation options including API settings
|
|
21
|
+
* @returns Promise that resolves when translation is complete
|
|
22
|
+
*/
|
|
23
|
+
export async function upload(filePaths, placeholderPaths, transformPaths, dataFormat = 'JSX', options) {
|
|
24
|
+
// Collect all files to translate
|
|
25
|
+
const allFiles = [];
|
|
26
|
+
const additionalOptions = options.options || {};
|
|
27
|
+
// Process JSON files
|
|
28
|
+
if (filePaths.json) {
|
|
29
|
+
if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
|
|
30
|
+
logErrorAndExit(noSupportedFormatError);
|
|
31
|
+
}
|
|
32
|
+
const jsonFiles = filePaths.json.map((filePath) => {
|
|
33
|
+
const content = readFile(filePath);
|
|
34
|
+
const parsedJson = parseJson(content, filePath, additionalOptions, options.defaultLocale);
|
|
35
|
+
const relativePath = getRelative(filePath);
|
|
36
|
+
return {
|
|
37
|
+
content: parsedJson,
|
|
38
|
+
fileName: relativePath,
|
|
39
|
+
fileFormat: 'JSON',
|
|
40
|
+
dataFormat,
|
|
41
|
+
locale: options.defaultLocale,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
allFiles.push(...jsonFiles);
|
|
45
|
+
}
|
|
46
|
+
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
47
|
+
if (fileType === 'json')
|
|
48
|
+
continue;
|
|
49
|
+
if (filePaths[fileType]) {
|
|
50
|
+
const files = filePaths[fileType].map((filePath) => {
|
|
51
|
+
const content = readFile(filePath);
|
|
52
|
+
const sanitizedContent = sanitizeFileContent(content);
|
|
53
|
+
const relativePath = getRelative(filePath);
|
|
54
|
+
return {
|
|
55
|
+
content: sanitizedContent,
|
|
56
|
+
fileName: relativePath,
|
|
57
|
+
fileFormat: fileType.toUpperCase(),
|
|
58
|
+
dataFormat,
|
|
59
|
+
locale: options.defaultLocale,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
allFiles.push(...files);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (allFiles.length === 0) {
|
|
66
|
+
logError('No files to upload were found. Please check your configuration and try again.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!options.defaultLocale) {
|
|
70
|
+
logErrorAndExit(noDefaultLocaleError);
|
|
71
|
+
}
|
|
72
|
+
if (!options.apiKey) {
|
|
73
|
+
logErrorAndExit(noApiKeyError);
|
|
74
|
+
}
|
|
75
|
+
if (options.apiKey.startsWith('gtx-dev-')) {
|
|
76
|
+
logErrorAndExit(devApiKeyError);
|
|
77
|
+
}
|
|
78
|
+
if (!options.projectId) {
|
|
79
|
+
logErrorAndExit(noProjectIdError);
|
|
80
|
+
}
|
|
81
|
+
const locales = options.locales || [];
|
|
82
|
+
// Create file mapping for all file types
|
|
83
|
+
const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales, options.defaultLocale);
|
|
84
|
+
// construct object
|
|
85
|
+
const uploadData = allFiles.map((file) => {
|
|
86
|
+
const encodedContent = Buffer.from(file.content).toString('base64');
|
|
87
|
+
const sourceFile = {
|
|
88
|
+
content: encodedContent,
|
|
89
|
+
fileName: file.fileName,
|
|
90
|
+
fileFormat: file.fileFormat,
|
|
91
|
+
dataFormat: file.dataFormat,
|
|
92
|
+
locale: file.locale,
|
|
93
|
+
};
|
|
94
|
+
const translations = [];
|
|
95
|
+
for (const locale of locales) {
|
|
96
|
+
const translatedFileName = fileMapping[locale][file.fileName];
|
|
97
|
+
if (translatedFileName && existsSync(translatedFileName)) {
|
|
98
|
+
const translatedContent = readFileSync(translatedFileName, 'utf8');
|
|
99
|
+
const encodedTranslatedContent = Buffer.from(translatedContent).toString('base64');
|
|
100
|
+
translations.push({
|
|
101
|
+
content: encodedTranslatedContent,
|
|
102
|
+
fileName: translatedFileName,
|
|
103
|
+
fileFormat: file.fileFormat,
|
|
104
|
+
dataFormat: file.dataFormat,
|
|
105
|
+
locale,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
source: sourceFile,
|
|
111
|
+
translations,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
// Send all files in a single API call
|
|
116
|
+
const response = await uploadFiles(uploadData, options);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
logErrorAndExit(`Error uploading files: ${error}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Processes translations that were already completed and returned with the initial API response
|
|
124
|
+
* @returns Set of downloaded file+locale combinations
|
|
125
|
+
*/
|
|
126
|
+
async function processInitialTranslations(translations = [], fileMapping, options) {
|
|
127
|
+
const downloadStatus = {
|
|
128
|
+
downloaded: new Set(),
|
|
129
|
+
failed: new Set(),
|
|
130
|
+
};
|
|
131
|
+
if (!translations || translations.length === 0) {
|
|
132
|
+
return downloadStatus;
|
|
133
|
+
}
|
|
134
|
+
// Filter for ready translations
|
|
135
|
+
const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
|
|
136
|
+
if (readyTranslations.length > 0) {
|
|
137
|
+
const spinner = createSpinner('dots');
|
|
138
|
+
spinner.start('Downloading translations...');
|
|
139
|
+
// Prepare batch download data
|
|
140
|
+
const batchFiles = readyTranslations
|
|
141
|
+
.map((translation) => {
|
|
142
|
+
const { locale, fileName, id } = translation;
|
|
143
|
+
const outputPath = fileMapping[locale][fileName];
|
|
144
|
+
if (!outputPath) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
translationId: id,
|
|
149
|
+
outputPath,
|
|
150
|
+
inputPath: fileName,
|
|
151
|
+
fileLocale: `${fileName}:${locale}`,
|
|
152
|
+
locale,
|
|
153
|
+
};
|
|
154
|
+
})
|
|
155
|
+
.filter(Boolean);
|
|
156
|
+
if (batchFiles.length === 0 || batchFiles[0] === null) {
|
|
157
|
+
return downloadStatus;
|
|
158
|
+
}
|
|
159
|
+
// Use batch download if there are multiple files
|
|
160
|
+
if (batchFiles.length > 1) {
|
|
161
|
+
const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, inputPath, locale }) => ({
|
|
162
|
+
translationId,
|
|
163
|
+
outputPath,
|
|
164
|
+
inputPath,
|
|
165
|
+
locale,
|
|
166
|
+
})), options);
|
|
167
|
+
// Process results
|
|
168
|
+
batchFiles.forEach((file) => {
|
|
169
|
+
const { translationId, fileLocale } = file;
|
|
170
|
+
if (batchResult.successful.includes(translationId)) {
|
|
171
|
+
downloadStatus.downloaded.add(fileLocale);
|
|
172
|
+
}
|
|
173
|
+
else if (batchResult.failed.includes(translationId)) {
|
|
174
|
+
downloadStatus.failed.add(fileLocale);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else if (batchFiles.length === 1) {
|
|
179
|
+
// For a single file, use the original downloadFile method
|
|
180
|
+
const file = batchFiles[0];
|
|
181
|
+
const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
|
|
182
|
+
if (result) {
|
|
183
|
+
downloadStatus.downloaded.add(file.fileLocale);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
downloadStatus.failed.add(file.fileLocale);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
spinner.stop(chalk.green('Downloaded cached translations'));
|
|
190
|
+
}
|
|
191
|
+
return downloadStatus;
|
|
192
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';
|
|
2
|
-
export declare function mergeJson(originalContent: string,
|
|
2
|
+
export declare function mergeJson(originalContent: string, inputPath: string, options: AdditionalOptions, targets: {
|
|
3
3
|
translatedContent: string;
|
|
4
4
|
targetLocale: string;
|
|
5
5
|
}[], defaultLocale: string): string[];
|
|
@@ -3,8 +3,9 @@ import { exit, logError, logWarning } from '../../console/logging.js';
|
|
|
3
3
|
import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, getSourceObjectOptionsArray, validateJsonSchema, } from './utils.js';
|
|
4
4
|
import { JSONPath } from 'jsonpath-plus';
|
|
5
5
|
import { getLocaleProperties } from 'generaltranslation';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { replaceLocalePlaceholders } from '../utils.js';
|
|
7
|
+
export function mergeJson(originalContent, inputPath, options, targets, defaultLocale) {
|
|
8
|
+
const jsonSchema = validateJsonSchema(options, inputPath);
|
|
8
9
|
if (!jsonSchema) {
|
|
9
10
|
return targets.map((target) => target.translatedContent);
|
|
10
11
|
}
|
|
@@ -13,7 +14,7 @@ export function mergeJson(originalContent, filePath, options, targets, defaultLo
|
|
|
13
14
|
originalJson = JSON.parse(originalContent);
|
|
14
15
|
}
|
|
15
16
|
catch {
|
|
16
|
-
logError(`Invalid JSON file: ${
|
|
17
|
+
logError(`Invalid JSON file: ${inputPath}`);
|
|
17
18
|
exit(1);
|
|
18
19
|
}
|
|
19
20
|
// Handle include
|
|
@@ -204,30 +205,6 @@ export function mergeJson(originalContent, filePath, options, targets, defaultLo
|
|
|
204
205
|
}
|
|
205
206
|
return [JSON.stringify(mergedJson, null, 2)];
|
|
206
207
|
}
|
|
207
|
-
// helper function to replace locale placeholders in a string
|
|
208
|
-
// with the corresponding locale properties
|
|
209
|
-
// ex: {locale} -> will be replaced with the locale code
|
|
210
|
-
// ex: {localeName} -> will be replaced with the locale name
|
|
211
|
-
function replaceLocalePlaceholders(string, localeProperties) {
|
|
212
|
-
return string.replace(/\{(\w+)\}/g, (match, property) => {
|
|
213
|
-
// Handle common aliases
|
|
214
|
-
if (property === 'locale' || property === 'localeCode') {
|
|
215
|
-
return localeProperties.code;
|
|
216
|
-
}
|
|
217
|
-
if (property === 'localeName') {
|
|
218
|
-
return localeProperties.name;
|
|
219
|
-
}
|
|
220
|
-
if (property === 'localeNativeName') {
|
|
221
|
-
return localeProperties.nativeName;
|
|
222
|
-
}
|
|
223
|
-
// Check if the property exists in localeProperties
|
|
224
|
-
if (property in localeProperties) {
|
|
225
|
-
return localeProperties[property];
|
|
226
|
-
}
|
|
227
|
-
// Return the original placeholder if property not found
|
|
228
|
-
return match;
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
208
|
/**
|
|
232
209
|
* Apply transformations to the sourceItem in-place
|
|
233
210
|
* @param sourceItem - The source item to apply transformations to
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// helper function to replace locale placeholders in a string
|
|
2
|
+
// with the corresponding locale properties
|
|
3
|
+
// ex: {locale} -> will be replaced with the locale code
|
|
4
|
+
// ex: {localeName} -> will be replaced with the locale name
|
|
5
|
+
export function replaceLocalePlaceholders(string, localeProperties) {
|
|
6
|
+
return string.replace(/\{(\w+)\}/g, (match, property) => {
|
|
7
|
+
// Handle common aliases
|
|
8
|
+
if (property === 'locale' || property === 'localeCode') {
|
|
9
|
+
return localeProperties.code;
|
|
10
|
+
}
|
|
11
|
+
if (property === 'localeName') {
|
|
12
|
+
return localeProperties.name;
|
|
13
|
+
}
|
|
14
|
+
if (property === 'localeNativeName') {
|
|
15
|
+
return localeProperties.nativeName;
|
|
16
|
+
}
|
|
17
|
+
// Check if the property exists in localeProperties
|
|
18
|
+
if (property in localeProperties) {
|
|
19
|
+
return localeProperties[property];
|
|
20
|
+
}
|
|
21
|
+
// Return the original placeholder if property not found
|
|
22
|
+
return match;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -39,9 +39,11 @@ export function resolveFiles(files, locale, cwd) {
|
|
|
39
39
|
}
|
|
40
40
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
41
41
|
// ==== TRANSFORMS ==== //
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const transform = files[fileType]?.transform;
|
|
43
|
+
if (transform &&
|
|
44
|
+
!Array.isArray(transform) &&
|
|
45
|
+
(typeof transform === 'string' || typeof transform === 'object')) {
|
|
46
|
+
transformPaths[fileType] = transform;
|
|
45
47
|
}
|
|
46
48
|
// ==== PLACEHOLDERS ==== //
|
|
47
49
|
if (files[fileType]?.include) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -60,8 +60,12 @@ export type ResolvedFiles = {
|
|
|
60
60
|
} & {
|
|
61
61
|
gt?: string;
|
|
62
62
|
};
|
|
63
|
+
export type TransformOption = {
|
|
64
|
+
match?: string;
|
|
65
|
+
replace: string;
|
|
66
|
+
};
|
|
63
67
|
export type TransformFiles = {
|
|
64
|
-
[K in SupportedFileExtension]?: string;
|
|
68
|
+
[K in SupportedFileExtension]?: TransformOption | string;
|
|
65
69
|
};
|
|
66
70
|
export type FilesOptions = {
|
|
67
71
|
[K in SupportedFileExtension]?: {
|
|
@@ -119,8 +123,5 @@ export type SourceObjectOptions = {
|
|
|
119
123
|
transform?: TransformOptions;
|
|
120
124
|
};
|
|
121
125
|
export type TransformOptions = {
|
|
122
|
-
[transformPath: string]:
|
|
123
|
-
match?: string;
|
|
124
|
-
replace: string;
|
|
125
|
-
};
|
|
126
|
+
[transformPath: string]: TransformOption;
|
|
126
127
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createFileMapping } from '../formats/files/
|
|
1
|
+
import { createFileMapping } from '../formats/files/fileMapping.js';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
export default async function flattenJsonFiles(settings) {
|
|
4
4
|
if (!settings.files ||
|
|
@@ -7,7 +7,7 @@ export default async function flattenJsonFiles(settings) {
|
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
9
|
const { resolvedPaths: sourceFiles } = settings.files;
|
|
10
|
-
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales);
|
|
10
|
+
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, settings.defaultLocale);
|
|
11
11
|
await Promise.all(Object.values(fileMapping).map(async (filesMap) => {
|
|
12
12
|
const targetFiles = Object.values(filesMap).filter((path) => path.endsWith('.json'));
|
|
13
13
|
await Promise.all(targetFiles.map(async (file) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import { createFileMapping } from '../formats/files/
|
|
2
|
+
import { createFileMapping } from '../formats/files/fileMapping.js';
|
|
3
3
|
import { logError } from '../console/logging.js';
|
|
4
4
|
/**
|
|
5
5
|
* Localizes static imports in content files.
|
|
@@ -21,7 +21,7 @@ export default async function localizeStaticImports(settings) {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
const { resolvedPaths: sourceFiles } = settings.files;
|
|
24
|
-
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales);
|
|
24
|
+
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, settings.defaultLocale);
|
|
25
25
|
// Process all file types at once with a single call
|
|
26
26
|
await Promise.all(Object.entries(fileMapping).map(async ([locale, filesMap]) => {
|
|
27
27
|
// Get all files that are md or mdx
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import { createFileMapping } from '../formats/files/
|
|
2
|
+
import { createFileMapping } from '../formats/files/fileMapping.js';
|
|
3
3
|
/**
|
|
4
4
|
* Localizes static urls in content files.
|
|
5
5
|
* Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
|
|
@@ -20,7 +20,7 @@ export default async function localizeStaticUrls(settings) {
|
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const { resolvedPaths: sourceFiles } = settings.files;
|
|
23
|
-
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales);
|
|
23
|
+
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, settings.defaultLocale);
|
|
24
24
|
// Process all file types at once with a single call
|
|
25
25
|
await Promise.all(Object.entries(fileMapping).map(async ([locale, filesMap]) => {
|
|
26
26
|
// Get all files that are md or mdx
|