Skip to content

Overview

Project integration is one of the most common application types. It allows you to create and add a new integration into your Crowdin project. You can find it in the integrations section of the project page. It is available to project members with Manager (or higher) permissions.

It uses the crowdin-simple-integration component provided by the Crowdin UI Kit.

Sample App

index.js
const crowdinModule = require('@crowdin/app-project-module');
const crowdinAppFunctions = require('@crowdin/crowdin-apps-functions');
const axios = require('axios').default;
const configuration = {
baseUrl: 'https://123.ngrok.io',
clientId: 'clientId',
clientSecret: 'clientSecret',
port: 8080,
scopes: [
crowdinModule.Scope.PROJECTS
],
name: 'Sample App',
identifier: 'sample-app',
description: 'Sample App description',
dbFolder: __dirname,
imagePath: __dirname + '/' + 'logo.png',
projectIntegration: {
withRootFolder: true,
excludedTargetLanguages: true,
getIntegrationFiles: async (
credentials,
appSettings,
parentId,
search,
page
11 collapsed lines
) => {
const res = [
{
id: '12',
name: 'File from integration',
type: 'json',
parentId: '10'
}
];
return {
data: res
};
},
updateCrowdin: async ({
projectId,
client,
credentials,
request,
rootFolder,
appSettings,
uploadTranslations,
job,
52 collapsed lines
excludedTargetLanguages,
}) => {
if (job.type !== 'cron') {
await job.update({
info: 'Preparing to upload'
});
}
const directories = await client.sourceFilesApi
.withFetchAll()
.listProjectDirectories(projectId);
const { folder, files } = await crowdinAppFunctions.getOrCreateFolder({
directories: directories.data.map((d) => d.data),
client,
projectId,
directoryName: 'Folder from integration',
parentDirectory: rootFolder
});
const result = await job.update({
progress: 5,
info: 'Uploading files'
});
if (result?.isCanceled) {
return;
}
const fileId = await crowdinAppFunctions.updateOrCreateFile({
client,
projectId,
name: 'integration.json',
title: 'Sample file from integration',
type: 'json',
directoryId: folder.id,
data: '<file_content>',
file: files.find((f) => f.name === 'integration.json'),
excludedTargetLanguages,
});
await job.update({
progress: 95
});
if (uploadTranslations) {
await crowdinAppFunctions.uploadTranslations(
client,
projectId,
fileId,
'es',
'integration.json',
{ title: 'Hola Mundo' },
);
}
},
updateIntegration: async ({
projectId,
client,
credentials,
request,
rootFolder,
27 collapsed lines
appSettings,
job,
}) => {
const directories = await client.sourceFilesApi
.withFetchAll()
.listProjectDirectories(projectId);
const { files } = await crowdinAppFunctions.getFolder({
directories: directories.data.map((d) => d.data),
client,
projectId,
directoryName: 'Folder from integration',
parentDirectory: rootFolder
});
const file = files.find((f) => f.name === 'integration.json');
if (file) {
const link = await client.translationsApi.buildProjectFileTranslation(
projectId,
file.id,
{ targetLanguageId: 'uk' },
);
if (!link) {
return;
}
const response = await axios.get(link.data.url);
}
},
onLogout: async (projectId, client, credentials, appSettings) => {
// cleanup logic
}
}
};
crowdinModule.createApp(configuration);

Configuration

Parameter
DescriptionDefault
withRootFolderWhether to create a root integration folder in Crowdin.false
integrationPaginationEnable pagination on integration files list.false
integrationOneLevelFetchingTurn on request when opening a directory and pass its id.false
integrationSearchListenerTurn on search listener and pass search string.false
uploadTranslationsenable the option to download translations from integration. In case it enabled, you need to correctly handle it in the updateCrowdin function.false
excludedTargetLanguagesEnable the option to upload file for translation into selected languages.false

getIntegrationFiles Function

This function is used to retrieve files and folders from the integration.

Parameters
  • credentials - Crowdin API credentials.
  • appSettings - Application settings.
  • parentId - Parent folder identifier.
  • search - Search string.
  • page - Page number.
Return Value

Should retrieve files and folders from the integration and return them in the following format:

{
data: [
{
id: string,
name: string,
type?: string,
parentId?: string,
nodeType?: string,
labels?: [
{
text: string,
type: string,
color: string
}
],
customContent?: string
}
],
stopPagination?: boolean,
message?: string
}

Only the id and name fields are required. The type field is required for files.

Description:

  • id - unique identifier of the file or folder.
  • name - name of the file or folder.
  • type- type of the file (json, xml, etc.). If you skip this, the item will be considered as a folder.
  • parentId - identifier of the parent folder.
  • nodeType - type of the node (0 - folder, 1 - file, 2 - branch).
  • labels - custom labels for the file entry.
    • text - label text.
    • type - label type (primary | secondary | success | warning | info | danger | dark | light).
    • color - label color.
  • customContent - custom HTML content for the file entry.
  • stopPagination - stop pagination if there is no more data to return in case you use pagination for the integration files.
  • message - custom message.

updateCrowdin Function

This function is used to update the Crowdin project with the data from the integration. Here you need to get data from the integration and upload it to Crowdin.

Parameters
  • projectId - Crowdin project ID.
  • client - Crowdin API client.
  • credentials - Crowdin API credentials.
  • request - Request object.
  • rootFolder - Root folder identifier.
  • appSettings - Application settings.
  • uploadTranslations - Boolean value indicating whether the user has enabled the option to upload existing translations from the integration to Crowdin.
  • job - Job object.
  • excludedTargetLanguages - Array of language IDs to exclude from uploading translations to Crowdin.
Return Value

By default, the function should return nothing. If you want to display a custom message after executing the update process, you can return an object with the message field:

return {
message: 'Some message'
};

updateIntegration Function

This function is used to update the integration with the data from Crowdin. Here you need to get translations from Crowdin and upload them to the integration.

Parameters
  • projectId - Crowdin project ID.
  • client - Crowdin API client.
  • credentials - Crowdin API credentials.
  • request - Request object.
  • rootFolder - Root folder identifier.
  • appSettings - Application settings.
  • job - Job object.

Upload Only New Translations

You can configure the integration to upload only new translations. To do this, you need to use the job.fetchTranslation method to get the link to download the translation file. After uploading the translation to the integration, you need to call the job.translationUploaded method to notify Crowdin that the translation has been uploaded.

index.js
updateIntegration: async ({
projectId,
client,
credentials,
request,
rootFolder,
appSettings,
job,
}) => {
const directories = await client.sourceFilesApi
.withFetchAll()
.listProjectDirectories(projectId);
const { files } = await crowdinAppFunctions.getFolder({
directories: directories.data.map((d) => d.data),
client,
projectId,
directoryName: 'Folder from integration',
parentDirectory: rootFolder
});
const file = files.find((f) => f.name === 'integration.json');
if (file) {
// Fetches only the new translations
const link = job.fetchTranslation({
fileId: file.id,
languageId: 'uk'
});
if (!link) {
return;
}
const response = await axios.get(link.data.url);
// upload translation logic
// If using job.fetchTranslation, call this method after successfully uploading the translation to the service
await job.translationUploaded({
fileId: file.id,
translationParams: [{
languageId: 'uk',
etag: link.data.etag
}]
});
}
}

Unsynced Files

Files with synchronization problems can be marked as unsynced. This helps users easily identify and manage problematic files.

configuration.projectIntegration.updateIntegration: async ({
projectId,
client,
credentials,
request,
rootFolder,
appSettings,
job,
}) => {
const unsyncedFiles = [];
const file = files.find((f) => f.name === 'integration.json');
try {
if (file) {
// upload translation logic
}
} catch(e) {
if (e.response && e.response.status === 404) {
unsyncedFiles.push(file.id);
}
}
// Marks the specified files as unsynced, indicating they failed to synchronize properly
await job.markFilesAsUnsynced({ fileIds: unsyncedFiles });
}

getFileProgress Function

Is possible to customize the translation progress for each file by using the getFileProgress function.

Parameters
  • projectId - Crowdin project ID.
  • client - Crowdin API client.
  • fileId - File ID.
Return Value

Should return the translation progress for the file in the following format:

{
[fileId]: [
{
languageId: string,
eTag: string,
words: {
total: number,
translated: number,
approved: number
},
phrases: {
total: number,
translated: number,
approved: number
},
translationProgress: number,
approvalProgress: number
}
]
}

onLogout Function

This function is called when the user logs out from the integration. You can use it to clean up any resources or perform other necessary actions.

Parameters
  • projectId - Crowdin project ID.
  • client - Crowdin API client.
  • credentials - Crowdin API credentials.
  • appSettings - Application settings.

User Errors Retention Period

Configure the retention period for user errors, with automatic daily deletion:

index.js
configuration.projectIntegration.userErrorLifetimeDays = 7

The default value is 30 days.

Info Window

You can define a section with some information notes or a help section for your application:

index.js
configuration.projectIntegration.infoModal = {
title: 'Info',
content: `
<h1>This is your app help section</h1>
</br>
<h2>This is just an example</h2>
`
}

Notice

You can also set notifications at the top of the screen with important information for your application:

index.js
configuration.projectIntegration.notice = {
title: 'Upgrade Alert',
content: `We recommend using a <a target="_blank" href="https://store.crowdin.com/">newer version</a> as this application is no longer supported.`,
type: 'info',
icon: true,
close: false
}

Possible notification types: info | warning | danger | success | error | dataLostWarning.

Filters

To disable language filtering, add the following configuration:

index.js
configuration.projectIntegration.filtering = {
crowdinLanguages: false
}

If you need to filter files, to skip some of them, you can use the following configuration:

index.js
configuration.projectIntegration.skipIntegrationNodes = {
fileNamePattern: '\\([\\w]{2}[-|_][\\w]{2,3}\\)$',
folderNamePattern: '\\([\\w]{2}[-|_][\\w]{2,3}\\)$'
}

Description:

  • fileNamePattern - regular expression to skip files. Optional.
  • folderNamePattern - regular expression to skip folders. Optional.

Testing

Crowdin Apps SDK provides a set of testing utilities to help you test your integration app.

See Testing for more information.

API

The Project Integration module implements API methods. For a full list of available endpoints, visit the Default Endpoints for Project Integration.