@crowdin/app-project-module 0.77.0 → 0.78.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.
@@ -21,7 +21,7 @@ const job_1 = require("../util/job");
21
21
  const files_1 = require("../util/files");
22
22
  function handle(config, integration) {
23
23
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
24
- var _a, _b;
24
+ var _a, _b, _c;
25
25
  const projectId = req.crowdinContext.jwtPayload.context.project_id || (req === null || req === void 0 ? void 0 : req.body.projectId);
26
26
  const uploadTranslations = req.query.uploadTranslations === 'true' || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.uploadTranslations);
27
27
  req.logInfo(`Updating crowdin project ${projectId}`);
@@ -33,6 +33,11 @@ function handle(config, integration) {
33
33
  if (((_b = config.api) === null || _b === void 0 ? void 0 : _b.default) && req.body.files) {
34
34
  req.body = req.body.files;
35
35
  }
36
+ const excludedTargetLanguages = yield (0, files_1.getExcludedTargetLanguages)({
37
+ client: req.crowdinApiClient,
38
+ projectId,
39
+ languages: ((_c = req.query.languages) === null || _c === void 0 ? void 0 : _c.split(',')) || [],
40
+ });
36
41
  yield (0, job_1.runAsJob)({
37
42
  integrationId: req.crowdinContext.clientId,
38
43
  crowdinId: req.crowdinContext.crowdinId,
@@ -40,13 +45,13 @@ function handle(config, integration) {
40
45
  title: 'Sync files to Crowdin',
41
46
  payload: req.body,
42
47
  res,
43
- projectId: projectId,
48
+ projectId,
44
49
  client: req.crowdinApiClient,
45
50
  jobType: types_1.JobClientType.MANUAL,
46
51
  jobStoreType: integration.jobStoreType,
47
52
  jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
48
- var _c;
49
- if (req.body && ((_c = req.body) === null || _c === void 0 ? void 0 : _c.length)) {
53
+ var _d;
54
+ if (req.body && ((_d = req.body) === null || _d === void 0 ? void 0 : _d.length)) {
50
55
  req.body = yield (0, files_1.expandFilesTree)(req.body, req, integration, job);
51
56
  req.body = (0, lodash_uniqby_1.default)(req.body, 'id');
52
57
  }
@@ -59,6 +64,7 @@ function handle(config, integration) {
59
64
  appSettings: req.integrationSettings,
60
65
  uploadTranslations,
61
66
  job,
67
+ excludedTargetLanguages: req.query.languages ? excludedTargetLanguages : undefined,
62
68
  });
63
69
  let message;
64
70
  if ((0, files_1.isExtendedResultType)(result)) {
@@ -19,7 +19,9 @@ function handle(config, integration) {
19
19
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
20
20
  var _a;
21
21
  req.logInfo('Updating integration data');
22
- const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
22
+ const client = req.crowdinApiClient;
23
+ const projectId = req.crowdinContext.jwtPayload.context.project_id;
24
+ const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, projectId);
23
25
  if (rootFolder) {
24
26
  req.logInfo(`Updating integration data for crowding root folder ${rootFolder.id}`);
25
27
  }
@@ -27,23 +29,35 @@ function handle(config, integration) {
27
29
  if (((_a = config.api) === null || _a === void 0 ? void 0 : _a.default) && req.body.files) {
28
30
  req.body = req.body.files;
29
31
  }
32
+ let payload = req.body;
33
+ if (integration.excludedTargetLanguages) {
34
+ let options = {};
35
+ if (rootFolder) {
36
+ options = {
37
+ directoryId: rootFolder.id,
38
+ recursion: 'true',
39
+ };
40
+ }
41
+ const files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options)).data.map((d) => d.data);
42
+ payload = (0, files_1.filterLanguages)(payload, files);
43
+ }
30
44
  yield (0, job_1.runAsJob)({
31
45
  integrationId: req.crowdinContext.clientId,
32
46
  crowdinId: req.crowdinContext.crowdinId,
33
47
  type: types_1.JobType.UPDATE_TO_INTEGRATION,
34
48
  title: 'Sync files to ' + config.name,
35
- payload: req.body,
49
+ payload,
36
50
  res,
37
- projectId: req.crowdinContext.jwtPayload.context.project_id,
38
- client: req.crowdinApiClient,
51
+ projectId,
52
+ client,
39
53
  jobType: types_1.JobClientType.MANUAL,
40
54
  jobStoreType: integration.jobStoreType,
41
55
  jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
42
56
  const result = yield integration.updateIntegration({
43
- projectId: req.crowdinContext.jwtPayload.context.project_id,
44
- client: req.crowdinApiClient,
57
+ projectId,
58
+ client,
45
59
  credentials: req.integrationCredentials,
46
- request: req.body,
60
+ request: payload,
47
61
  rootFolder,
48
62
  appSettings: req.integrationSettings,
49
63
  job,
@@ -94,6 +94,7 @@ function handle(config, integration) {
94
94
  options.integrationSearchListener = integration.integrationSearchListener;
95
95
  options.checkSubscription = !(0, subscription_1.isAppFree)(config);
96
96
  options.uploadTranslations = integration.uploadTranslations;
97
+ options.excludedTargetLanguages = integration.excludedTargetLanguages;
97
98
  options.sentryData = process.env.SENTRY_DSN
98
99
  ? {
99
100
  dsn: process.env.SENTRY_DSN,
@@ -34,7 +34,7 @@ export interface IntegrationLogic extends ModuleKey {
34
34
  /**
35
35
  * function to update crowdin files (e.g. pull integration data to crowdin source files)
36
36
  */
37
- updateCrowdin: ({ projectId, client, credentials, request, rootFolder, appSettings, uploadTranslations, job, }: {
37
+ updateCrowdin: ({ projectId, client, credentials, request, rootFolder, appSettings, uploadTranslations, job, excludedTargetLanguages, }: {
38
38
  projectId: number;
39
39
  client: Crowdin;
40
40
  credentials: any;
@@ -43,6 +43,7 @@ export interface IntegrationLogic extends ModuleKey {
43
43
  appSettings?: any;
44
44
  uploadTranslations?: boolean;
45
45
  job: JobClient;
46
+ excludedTargetLanguages?: string[];
46
47
  }) => Promise<void | ExtendedResult<void>>;
47
48
  /**
48
49
  * function to update integration content (e.g. load crowdin translations and push them to integration service)
@@ -125,6 +126,10 @@ export interface IntegrationLogic extends ModuleKey {
125
126
  * Enable the option to upload translations to crowdin that are already present in the integration.
126
127
  */
127
128
  uploadTranslations?: boolean;
129
+ /**
130
+ * Enable the option to upload file for translation into selected languages.
131
+ */
132
+ excludedTargetLanguages?: boolean;
128
133
  /**
129
134
  * function to get crowdin file translation progress
130
135
  */
@@ -299,6 +304,7 @@ export interface File {
299
304
  customContent?: string;
300
305
  labels?: LabelTreeElement[];
301
306
  failed?: boolean;
307
+ excludedTargetLanguages?: string[];
302
308
  }
303
309
  export interface Folder {
304
310
  id: string;
@@ -116,6 +116,7 @@ function applyIntegrationModuleDefaults(config, integration) {
116
116
  parentId: parentId ? parentId.toString() : undefined,
117
117
  name: e.title || e.name,
118
118
  type: e.type,
119
+ excludedTargetLanguages: e.excludedTargetLanguages,
119
120
  });
120
121
  });
121
122
  return res;
@@ -1,6 +1,7 @@
1
- import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem } from '../types';
1
+ import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem, UpdateIntegrationRequest } from '../types';
2
2
  import { JobClient } from './types';
3
3
  import Crowdin from '@crowdin/crowdin-api-client';
4
+ import { SourceFilesModel } from '@crowdin/crowdin-api-client';
4
5
  export declare function skipFilesByRegex(files: TreeItem[] | undefined, skipIntegrationNodes?: SkipIntegrationNodes): TreeItem[];
5
6
  export declare function expandFilesTree(nodes: IntegrationFile[], req: IntegrationRequest, integration: IntegrationLogic, job?: JobClient): Promise<IntegrationFile[]>;
6
7
  export declare function isExtendedResultType<T>(data?: T | ExtendedResult<T>): data is ExtendedResult<T>;
@@ -10,3 +11,9 @@ export declare function markUnsyncedFiles({ integrationId, crowdinId, client, fi
10
11
  client: Crowdin;
11
12
  files?: TreeItem[];
12
13
  }): Promise<TreeItem[]>;
14
+ export declare function getExcludedTargetLanguages({ client, projectId, languages, }: {
15
+ client: Crowdin;
16
+ projectId: number;
17
+ languages: string[];
18
+ }): Promise<string[]>;
19
+ export declare function filterLanguages(request: UpdateIntegrationRequest, files: SourceFilesModel.File[]): UpdateIntegrationRequest;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
12
+ exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
13
13
  const types_1 = require("./types");
14
14
  const storage_1 = require("../../../storage");
15
15
  const util_1 = require("../../../util");
@@ -98,3 +98,26 @@ function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
98
98
  });
99
99
  }
100
100
  exports.markUnsyncedFiles = markUnsyncedFiles;
101
+ function getExcludedTargetLanguages({ client, projectId, languages, }) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ const projectData = yield client.projectsGroupsApi.getProject(projectId);
104
+ const targetLanguages = projectData.data.targetLanguageIds;
105
+ return targetLanguages.filter((language) => !languages.includes(language));
106
+ });
107
+ }
108
+ exports.getExcludedTargetLanguages = getExcludedTargetLanguages;
109
+ function filterLanguages(request, files) {
110
+ const result = {};
111
+ for (const fileId in request) {
112
+ const file = files.find((f) => f.id === parseInt(fileId, 10));
113
+ if (file) {
114
+ const excludedLanguages = new Set((file === null || file === void 0 ? void 0 : file.excludedTargetLanguages) || []);
115
+ result[fileId] = request[fileId].filter((lang) => !excludedLanguages.has(lang));
116
+ }
117
+ else {
118
+ result[fileId] = request[fileId];
119
+ }
120
+ }
121
+ return result;
122
+ }
123
+ exports.filterLanguages = filterLanguages;
@@ -572,8 +572,8 @@ class MySQLStorage {
572
572
  yield this.dbPromise;
573
573
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
574
574
  const [rows] = yield connection.execute(`
575
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
576
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
575
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
576
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
577
577
  FROM job
578
578
  WHERE id = ?
579
579
  `, [id]);
@@ -586,8 +586,8 @@ class MySQLStorage {
586
586
  yield this.dbPromise;
587
587
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
588
588
  const [rows] = yield connection.execute(`
589
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
590
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
589
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
590
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
591
591
  FROM job
592
592
  WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL
593
593
  `, [integrationId, crowdinId]);
@@ -606,8 +606,8 @@ class MySQLStorage {
606
606
  yield this.dbPromise;
607
607
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
608
608
  const [rows] = yield connection.execute(`
609
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
610
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
609
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
610
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
611
611
  FROM job
612
612
  WHERE status IN (?, ?) AND finished_at is NULL
613
613
  `, [types_1.JobStatus.IN_PROGRESS, types_1.JobStatus.CREATED]);
@@ -629,7 +629,7 @@ class MySQLStorage {
629
629
  yield this.dbPromise;
630
630
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
631
631
  const [rows] = yield connection.execute(`
632
- SELECT integration_id as integrationId, crowdin_id as crowdinId, file_id as fileId, language_id as languageId, etag
632
+ SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag
633
633
  FROM translation_file_cache
634
634
  WHERE integration_id = ? AND crowdin_id = ? AND file_id = ?
635
635
  `, [integrationId, crowdinId, fileId]);
@@ -642,7 +642,7 @@ class MySQLStorage {
642
642
  yield this.dbPromise;
643
643
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
644
644
  const [rows] = yield connection.execute(`
645
- SELECT integration_id as integrationId, crowdin_id as crowdin_id, file_id as fileId, language_id as languageId
645
+ SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag
646
646
  FROM translation_file_cache
647
647
  WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? AND language_id = ?
648
648
  `, [integrationId, crowdinId, fileId, languageId]);
@@ -624,8 +624,8 @@ class PostgreStorage {
624
624
  yield this.dbPromise;
625
625
  return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
626
626
  const res = yield client.query(`
627
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
628
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
627
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
628
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
629
629
  FROM job
630
630
  WHERE id = $1
631
631
  `, [id]);
@@ -638,8 +638,8 @@ class PostgreStorage {
638
638
  yield this.dbPromise;
639
639
  return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
640
640
  const res = yield client.query(`
641
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
642
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
641
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
642
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
643
643
  FROM job
644
644
  WHERE integration_id = $1 AND crowdin_id = $2 AND finished_at is NULL
645
645
  `, [integrationId, crowdinId]);
@@ -658,8 +658,8 @@ class PostgreStorage {
658
658
  yield this.dbPromise;
659
659
  return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
660
660
  const res = yield client.query(`
661
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
662
- title, info, data, attempt, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
661
+ SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status,
662
+ title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt"
663
663
  FROM job
664
664
  WHERE status IN ($1, $2) AND finished_at is NULL
665
665
  `, [types_2.JobStatus.IN_PROGRESS, types_2.JobStatus.CREATED]);
@@ -673,7 +673,7 @@ class PostgreStorage {
673
673
  yield this.executeQuery((client) => client.query(`
674
674
  INSERT
675
675
  INTO translation_file_cache(integration_id, crowdin_id, file_id, language_id, etag)
676
- VALUES ($1, $2, $3, $4, $4)
676
+ VALUES ($1, $2, $3, $4, $5)
677
677
  `, [integrationId, crowdinId, fileId, languageId, etag]));
678
678
  });
679
679
  }
@@ -682,7 +682,7 @@ class PostgreStorage {
682
682
  yield this.dbPromise;
683
683
  return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
684
684
  const res = yield client.query(`
685
- SELECT integration_id as integrationId, crowdin_id as crowdinId, file_id as fileId, language_id as languageId, etag
685
+ SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag
686
686
  FROM translation_file_cache
687
687
  WHERE integration_id = $1 AND crowdin_id = $2 AND file_id = $3
688
688
  `, [integrationId, crowdinId, fileId]);
@@ -695,7 +695,7 @@ class PostgreStorage {
695
695
  yield this.dbPromise;
696
696
  return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
697
697
  const res = yield client.query(`
698
- SELECT integration_id as integrationId, crowdin_id as crowdinId, file_id as fileId, language_id as languageId
698
+ SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag
699
699
  FROM translation_file_cache
700
700
  WHERE integration_id = $1 AND crowdin_id = $2 AND file_id = $3 AND language_id = $4
701
701
  `, [integrationId, crowdinId, fileId, languageId]);
@@ -79,7 +79,15 @@
79
79
  integration-name="{{name}}"
80
80
  integration-logo="logo.png"
81
81
  {{#if uploadTranslations}}
82
- integration-button-menu-items='[{"label":"Sync translations", "title":"Sync translations to Crowdin", "action":"uploadTranslations"}]'
82
+ {{#if excludedTargetLanguages}}
83
+ integration-button-menu-items='[{"label":"Sync translations", "title":"Sync translations to Crowdin", "action":"uploadTranslations"}, {"label":"Select target languages", "title":"Upload file for translation into selected languages", "action":"excludedTargetLanguages"}]'
84
+ {{else}}
85
+ integration-button-menu-items='[{"label":"Sync translations", "title":"Sync translations to Crowdin", "action":"uploadTranslations"}]'
86
+ {{/if}}
87
+ {{else}}
88
+ {{#if excludedTargetLanguages}}
89
+ integration-button-menu-items='[{"label":"Select target languages", "title":"Upload file for translation into selected languages", "action":"excludedTargetLanguages"}]'
90
+ {{/if}}
83
91
  {{/if}}
84
92
  {{#if filtering.crowdinLanguages}}
85
93
  crowdin-filter
@@ -209,6 +217,15 @@
209
217
  </div>
210
218
  </crowdin-modal>
211
219
 
220
+ {{#if excludedTargetLanguages}}
221
+ <crowdin-modal
222
+ modal-width="50"
223
+ modal-title="Select Target Languages for Translation"
224
+ id="excluded-languages"
225
+ >
226
+ </crowdin-modal>
227
+ {{/if}}
228
+
212
229
  {{#if infoModal}}
213
230
  <crowdin-modal
214
231
  style="display: none;"
@@ -479,6 +496,7 @@
479
496
 
480
497
  let project = {};
481
498
  let crowdinData = [];
499
+ let fileToSync = [];
482
500
 
483
501
  getCrowdinData();
484
502
  getIntegrationData();
@@ -511,6 +529,7 @@
511
529
  item.type = e.type;
512
530
  item.node_type = fileType;
513
531
  item.failed = e.failed;
532
+ item.excludedTargetLanguages = e.excludedTargetLanguages;
514
533
  } else {
515
534
  item.node_type = e.nodeType || folderType;
516
535
  }
@@ -534,6 +553,8 @@
534
553
  languagesSorted.push({...project.inContextPseudoLanguage, inContext: true});
535
554
  }
536
555
 
556
+ setLanguagesForTranslation(languagesSorted);
557
+
537
558
  appComponent.setCrowdinLanguagesData(languagesSorted)
538
559
  })
539
560
  {{#or withCronSync webhooks}}
@@ -636,17 +657,21 @@
636
657
  .catch(e => catchRejection(e, 'Can\'t fetch file progress'));
637
658
  }
638
659
 
639
- function uploadFilesToCrowdin(event) {
660
+ function uploadFilesToCrowdin(event, languages = []) {
640
661
  let files = [];
641
662
  let uploadTranslations = false;
642
- if (event.detail.action === 'uploadTranslations') {
663
+ if (event.detail?.action === 'uploadTranslations') {
643
664
  files = event.detail.files;
644
665
  uploadTranslations = true;
666
+ } else if (event.detail?.action === 'excludedTargetLanguages') {
667
+ fileToSync = event.detail.files;
668
+ openLanguageModal();
669
+ return;
645
670
  } else {
646
671
  files = event.detail;
647
672
  }
648
673
 
649
- if (event.detail.length === 0) {
674
+ if (event.detail?.length === 0 || !event.detail) {
650
675
  showToast('Select templates which will be pushed to Crowdin');
651
676
  return;
652
677
  }
@@ -669,7 +694,7 @@
669
694
  }));
670
695
  {{/if}}
671
696
  checkOrigin()
672
- .then(restParams => fetch(`api/crowdin/update${restParams}&uploadTranslations=${uploadTranslations}`, {
697
+ .then(restParams => fetch(`api/crowdin/update${restParams}&uploadTranslations=${uploadTranslations}&languages=${languages}`, {
673
698
  method: 'POST',
674
699
  headers: { 'Content-Type': 'application/json' },
675
700
  body: JSON.stringify(req)
@@ -844,6 +869,56 @@
844
869
  .catch(e => catchRejection(e, 'Can\'t upload files to {{name}}'))
845
870
  }
846
871
 
872
+ function openLanguageModal() {
873
+ openModal(languageModal);
874
+ }
875
+
876
+ async function uploadFilesToCrowdinWithExcludedLanguages() {
877
+ const languageSelect = document.querySelector('#language-select');
878
+ const selectedLanguages = await languageSelect.getValue();
879
+ closeModal(languageModal);
880
+ uploadFilesToCrowdin({ detail: fileToSync }, selectedLanguages);
881
+ }
882
+
883
+ async function setLanguagesForTranslation(languages) {
884
+ const languageIds = languages.map(language => language.id);
885
+ if (languageModal) {
886
+ let modalContent = `
887
+ <div>
888
+ <crowdin-select
889
+ is-multi
890
+ is-searchable
891
+ is-position-fixed
892
+ close-on-select="false"
893
+ name="languages"
894
+ id="language-select"
895
+ label="Languages"
896
+ >`;
897
+
898
+ for (const language of languages) {
899
+ modalContent += `<option value="${language.id}">${language.name}</option>`;
900
+ }
901
+
902
+ modalContent += `
903
+ </crowdin-select>
904
+ </div>
905
+ <div slot="footer">
906
+ <crowdin-button id="upload-exclude-languages" outlined onclick="uploadFilesToCrowdinWithExcludedLanguages()">
907
+ <div class="integration-icon-button">
908
+ <span>Sync to</span>
909
+ <crowdin-logo is-icon/>
910
+ </div>
911
+ </crowdin-button>
912
+ </div>
913
+ `;
914
+
915
+ languageModal.innerHTML = modalContent;
916
+
917
+ const select = document.querySelector('#language-select')
918
+ await select.setValue(languageIds);
919
+ }
920
+ }
921
+
847
922
  {{#if configurationFields}}
848
923
  const settingsModal = document.getElementById('settings-modal');
849
924
  const settingsSaveBtn = document.getElementById('settings-save-btn');
@@ -1109,6 +1184,12 @@
1109
1184
 
1110
1185
  const permissions = document.getElementById('permissions-modal');
1111
1186
 
1187
+ {{#if excludedTargetLanguages}}
1188
+ const languageModal = document.getElementById('excluded-languages');
1189
+ {{else}}
1190
+ const languageModal = undefined;
1191
+ {{/if}}
1192
+
1112
1193
  {{#if infoModal}}
1113
1194
  const infoModal = document.getElementById('info-modal');
1114
1195
  {{else}}
@@ -1126,6 +1207,9 @@
1126
1207
  if (settingsModal) {
1127
1208
  closeModal(settingsModal);
1128
1209
  }
1210
+ if (languageModal) {
1211
+ closeModal(languageModal);
1212
+ }
1129
1213
  }
1130
1214
  });
1131
1215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.77.0",
3
+ "version": "0.78.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",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "@aws-sdk/client-s3": "^3.744.0",
23
23
  "@aws-sdk/s3-request-presigner": "^3.744.0",
24
- "@crowdin/crowdin-apps-functions": "^0.10.0",
24
+ "@crowdin/crowdin-apps-functions": "^0.11.0",
25
25
  "@crowdin/logs-formatter": "^2.1.7",
26
26
  "@godaddy/terminus": "^4.12.1",
27
27
  "@monaco-editor/react": "^4.6.0",