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
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, page11 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 | Description | Default |
---|---|---|
withRootFolder | Whether to create a root integration folder in Crowdin. | false |
integrationPagination | Enable pagination on integration files list. | false |
integrationOneLevelFetching | Turn on request when opening a directory and pass its id. | false |
integrationSearchListener | Turn on search listener and pass search string. | false |
uploadTranslations | enable the option to download translations from integration. In case it enabled, you need to correctly handle it in the updateCrowdin function. | false |
excludedTargetLanguages | Enable 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.
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:
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:
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:
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:
configuration.projectIntegration.filtering = { crowdinLanguages: false}
If you need to filter files, to skip some of them, you can use the following configuration:
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.