@crowdin/app-project-module 0.99.0 → 0.100.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/out/index.js CHANGED
@@ -210,6 +210,7 @@ function addCrowdinEndpoints(app, clientConfig) {
210
210
  fileProcessingApps.registerFilePostImport({ config, app });
211
211
  fileProcessingApps.registerFilePreExport({ config, app });
212
212
  fileProcessingApps.registerFilePostExport({ config, app });
213
+ fileProcessingApps.registerFileTranslationsAlignmentExport({ config, app });
213
214
  apiApp.register({ config, app });
214
215
  aiProvider.register({ config, app });
215
216
  aiPromptProvider.register({ config, app });
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { Config, CrowdinClientRequest } from '../../../types';
4
+ import { TranslationsAlignmentLogic } from '../types';
5
+ export default function handle(baseConfig: Config, config: TranslationsAlignmentLogic, folderName: string): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const util_1 = require("../../../util");
18
+ const files_1 = require("../util/files");
19
+ function handle(baseConfig, config, folderName) {
20
+ const folderPath = config.filesFolder || baseConfig.dbFolder;
21
+ if (!fs_1.default.existsSync(path_1.default.join(folderPath, folderName))) {
22
+ fs_1.default.mkdirSync(path_1.default.join(folderPath, folderName), { recursive: true });
23
+ }
24
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
25
+ var _a;
26
+ const response = {};
27
+ const baseFilesUrl = `${baseConfig.baseUrl}/file/download/${folderName}`;
28
+ const body = req.body;
29
+ // Skip assets (e.g., images, videos, or other media files) as they typically don't need to be processed
30
+ if (((_a = body === null || body === void 0 ? void 0 : body.file) === null || _a === void 0 ? void 0 : _a.type) === 'assets' && !('processAssets' in config && config.processAssets)) {
31
+ res.sendStatus(415);
32
+ return;
33
+ }
34
+ const fileSourceStringsContent = body.sourceStringsUrl
35
+ ? (yield (0, files_1.getFileStrings)(body.sourceStringsUrl))
36
+ : body.sourceStrings;
37
+ const fileTranslationsContent = body.translationStringsUrl
38
+ ? (yield (0, files_1.getFileStrings)(body.translationStringsUrl))
39
+ : body.translationStrings;
40
+ const fileProcessResult = yield config.fileProcess(body, {
41
+ sourceStrings: fileSourceStringsContent,
42
+ translations: fileTranslationsContent,
43
+ }, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id);
44
+ const { strings, error: processingErrorMessage } = fileProcessResult;
45
+ if (!strings) {
46
+ response.translations = [];
47
+ res.send({
48
+ data: response,
49
+ error: processingErrorMessage ? { message: processingErrorMessage } : undefined,
50
+ });
51
+ return;
52
+ }
53
+ const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
54
+ const bufferData = Buffer.from(stringsNDJson, 'utf-8');
55
+ if (Buffer.byteLength(bufferData) < files_1.MAX_BODY_SIZE) {
56
+ response.translations = strings;
57
+ }
58
+ else {
59
+ let url;
60
+ if (config.storeFile) {
61
+ url = yield config.storeFile(bufferData);
62
+ }
63
+ else {
64
+ const storedFile = yield (0, files_1.storeFile)(bufferData, path_1.default.join(folderPath, folderName));
65
+ url = `${baseFilesUrl}?file=${storedFile}`;
66
+ }
67
+ response.translationsUrl = url;
68
+ }
69
+ res.send({ data: response, error: processingErrorMessage ? { message: processingErrorMessage } : undefined });
70
+ }));
71
+ }
72
+ exports.default = handle;
@@ -20,3 +20,7 @@ export declare function registerFilePostExport({ config, app }: {
20
20
  config: Config;
21
21
  app: Express;
22
22
  }): void;
23
+ export declare function registerFileTranslationsAlignmentExport({ config, app }: {
24
+ config: Config;
25
+ app: Express;
26
+ }): void;
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerFilePostExport = exports.registerFilePreExport = exports.registerFilePostImport = exports.registerFilePreImport = exports.registerCustomFileFormat = void 0;
6
+ exports.registerFileTranslationsAlignmentExport = exports.registerFilePostExport = exports.registerFilePreExport = exports.registerFilePostImport = exports.registerFilePreImport = exports.registerCustomFileFormat = void 0;
7
7
  const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
8
8
  const custom_file_format_1 = __importDefault(require("./handlers/custom-file-format"));
9
9
  const file_download_1 = __importDefault(require("./handlers/file-download"));
10
10
  const pre_post_process_1 = __importDefault(require("./handlers/pre-post-process"));
11
+ const translations_alignment_1 = __importDefault(require("./handlers/translations-alignment"));
11
12
  const types_1 = require("./types");
12
13
  const defaults_1 = require("./util/defaults");
13
14
  function registerCustomFileFormat({ config, app }) {
@@ -87,3 +88,17 @@ function registerFilePostExport({ config, app }) {
87
88
  app.get(`/file/download/${types_1.ProcessFileJobType.POST_EXPORT}`, (0, file_download_1.default)(config, config.filePostExport, types_1.ProcessFileJobType.POST_EXPORT));
88
89
  }
89
90
  exports.registerFilePostExport = registerFilePostExport;
91
+ function registerFileTranslationsAlignmentExport({ config, app }) {
92
+ if (!config.fileTranslationsAlignmentExport) {
93
+ return;
94
+ }
95
+ (0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.fileTranslationsAlignmentExport);
96
+ app.post('/translations-alignment', (0, crowdin_client_1.default)({
97
+ config,
98
+ optional: false,
99
+ checkSubscriptionExpiration: true,
100
+ moduleKey: config.fileTranslationsAlignmentExport.key,
101
+ }), (0, translations_alignment_1.default)(config, config.fileTranslationsAlignmentExport, types_1.ProcessFileJobType.TRANSLATIONS_ALIGNMENT));
102
+ app.get(`/file/download/${types_1.ProcessFileJobType.POST_EXPORT}`, (0, file_download_1.default)(config, config.fileTranslationsAlignmentExport, types_1.ProcessFileJobType.TRANSLATIONS_ALIGNMENT));
103
+ }
104
+ exports.registerFileTranslationsAlignmentExport = registerFileTranslationsAlignmentExport;
@@ -60,9 +60,17 @@ export interface CustomFileFormatLogic extends FileProcessLogic {
60
60
  }
61
61
  export type FileImportExportLogic = FilePreImportLogic | FilePostImportLogic | FilePreExportLogic | FilePostExportLogic;
62
62
  export type FileImportExportContent = ProcessFileString[] | Buffer | undefined;
63
+ export type TranslationAlignmentContent = TranslationAlignmentString[] | Buffer | undefined;
64
+ export interface FileTranslationAlignmentContent {
65
+ sourceStrings: TranslationAlignmentContent;
66
+ translations: TranslationAlignmentContent;
67
+ }
63
68
  export interface BaseFileProcessLogic<T> {
64
69
  fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<T>;
65
70
  }
71
+ export interface TranslationAlignmentProcessLogin<T> {
72
+ fileProcess: (req: ProcessTranslationAlignmentRequest, content: FileTranslationAlignmentContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<T>;
73
+ }
66
74
  export interface FilePreImportLogic extends FileProcessLogic, BaseFileProcessLogic<ContentFileResponse> {
67
75
  /**
68
76
  * Set to `true` to enable asset processing in the application.
@@ -79,6 +87,19 @@ export interface FilePostExportLogic extends FileProcessLogic, BaseFileProcessLo
79
87
  */
80
88
  processAssets?: boolean;
81
89
  }
90
+ export interface TranslationsAlignmentLogic extends FileProcessLogic, TranslationAlignmentProcessLogin<TranslationAlignmentResponse> {
91
+ }
92
+ export interface ProcessTranslationAlignmentRequest {
93
+ jobType: ProcessFileJobType;
94
+ file: ProcessFileRecord;
95
+ sourceLanguage: LanguagesModel.Language;
96
+ targetLanguages: LanguagesModel.Language[];
97
+ sourceStrings: TranslationAlignmentString[];
98
+ sourceStringsUrl: string;
99
+ translationStrings: TranslationAlignmentString[];
100
+ translationStringsUrl: string;
101
+ getRawContent?: (encoding: BufferEncoding) => Promise<string | Buffer>;
102
+ }
82
103
  export interface ProcessFileRequest {
83
104
  jobType: ProcessFileJobType;
84
105
  file: ProcessFileRecord;
@@ -102,7 +123,8 @@ export declare enum ProcessFileJobType {
102
123
  PRE_IMPORT = "pre-import-file",
103
124
  POST_IMPORT = "post-import-file",
104
125
  PRE_EXPORT = "pre-export-file",
105
- POST_EXPORT = "post-export-file"
126
+ POST_EXPORT = "post-export-file",
127
+ TRANSLATIONS_ALIGNMENT = "translations-alignment-file"
106
128
  }
107
129
  export interface ParseFileResponse {
108
130
  previewFile?: Buffer;
@@ -121,6 +143,14 @@ export interface StringsFileResponse extends ParseFileResponse {
121
143
  export interface ContentFileResponse extends BuildFileResponse {
122
144
  notModified?: boolean;
123
145
  }
146
+ export interface TranslationAlignmentResponse {
147
+ strings?: TranslationAlignmentResult[];
148
+ error?: string;
149
+ }
150
+ export interface TranslationAlignmentResult {
151
+ sourceStringId: number;
152
+ text: string | SourceStringsModel.PluralText;
153
+ }
124
154
  export interface ProcessFileString {
125
155
  previewId?: number;
126
156
  id: number;
@@ -135,6 +165,11 @@ export interface ProcessFileString {
135
165
  translations?: StringTranslations;
136
166
  uniqId?: string;
137
167
  }
168
+ export interface TranslationAlignmentString {
169
+ id: number | null;
170
+ text: string | SourceStringsModel.PluralText;
171
+ context: string;
172
+ }
138
173
  export interface StringTranslations {
139
174
  [language: string]: {
140
175
  text: string | SourceStringsModel.PluralText;
@@ -9,4 +9,5 @@ var ProcessFileJobType;
9
9
  ProcessFileJobType["POST_IMPORT"] = "post-import-file";
10
10
  ProcessFileJobType["PRE_EXPORT"] = "pre-export-file";
11
11
  ProcessFileJobType["POST_EXPORT"] = "post-export-file";
12
+ ProcessFileJobType["TRANSLATIONS_ALIGNMENT"] = "translations-alignment-file";
12
13
  })(ProcessFileJobType = exports.ProcessFileJobType || (exports.ProcessFileJobType = {}));
@@ -86,6 +86,17 @@ function handle(config) {
86
86
  },
87
87
  ];
88
88
  }
89
+ if (config.fileTranslationsAlignmentExport) {
90
+ // prevent possible overrides of the other modules
91
+ config.fileTranslationsAlignmentExport = Object.assign(Object.assign({}, config.fileTranslationsAlignmentExport), { key: config.identifier + '-ftae' });
92
+ modules['file-translations-alignment'] = [
93
+ {
94
+ key: config.fileTranslationsAlignmentExport.key,
95
+ signaturePatterns: config.fileTranslationsAlignmentExport.signaturePatterns,
96
+ url: '/translations-alignment',
97
+ },
98
+ ];
99
+ }
89
100
  if (config.customMT) {
90
101
  // prevent possible overrides of the other modules
91
102
  config.customMT = Object.assign(Object.assign({}, config.customMT), { key: config.identifier + '-mt' });
package/out/types.d.ts CHANGED
@@ -5,7 +5,7 @@ import { ContextContent } from './modules/context-menu/types';
5
5
  import { CustomMTLogic } from './modules/custom-mt/types';
6
6
  import { CustomSpellcheckerModule } from './modules/custom-spell-check/types';
7
7
  import { EditorPanels } from './modules/editor-right-panel/types';
8
- import { CustomFileFormatLogic, FilePostExportLogic, FilePostImportLogic, FilePreExportLogic, FilePreImportLogic } from './modules/file-processing/types';
8
+ import { CustomFileFormatLogic, FilePostExportLogic, FilePostImportLogic, FilePreExportLogic, FilePreImportLogic, TranslationsAlignmentLogic } from './modules/file-processing/types';
9
9
  import { IntegrationLogic } from './modules/integration/types';
10
10
  import { Storage } from './storage';
11
11
  import { MySQLStorageConfig } from './storage/mysql';
@@ -203,6 +203,7 @@ export interface ClientConfig extends ImagePath {
203
203
  filePostImport?: FilePostImportLogic;
204
204
  filePreExport?: FilePreExportLogic;
205
205
  filePostExport?: FilePostExportLogic;
206
+ fileTranslationsAlignmentExport?: TranslationsAlignmentLogic;
206
207
  /**
207
208
  * Disable formatting logs
208
209
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.99.0",
3
+ "version": "0.100.1",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",
@@ -13,7 +13,8 @@
13
13
  "prettier": "prettier --config .prettierrc src/**/*.ts --write",
14
14
  "lint-ci": "eslint \"{src,tests}/**/*.{js,ts}\"",
15
15
  "test-coverage": "jest --ci --reporters=jest-junit --reporters=default --coverage --coverageReporters=cobertura --coverageReporters=html",
16
- "test": "jest"
16
+ "test": "jest",
17
+ "postinstall": "patch-package"
17
18
  },
18
19
  "files": [
19
20
  "out/**/*"
@@ -89,7 +90,8 @@
89
90
  "rollup": "^3.29.4",
90
91
  "rollup-plugin-delete": "^2.1.0",
91
92
  "ts-jest": "^29.2.5",
92
- "typescript": "^4.9.5"
93
+ "typescript": "^4.9.5",
94
+ "patch-package": "^8.0.0"
93
95
  },
94
96
  "repository": {
95
97
  "type": "git",