Skip to content

Overview

You can create Crowdin apps that extend the functionality of Crowdin by adding new modules to the Crowdin UI. Modules are displayed in the Crowdin UI and can be used to add new features or modify existing ones.

Supported Modules

Other Supported Modules

The SDK supports all the other modules that are available in Crowdin:

Sample

Example of profile-resources-menu module:

index.js
const crowdinModule = require('@crowdin/app-project-module');
const configuration = {
baseUrl: 'https://123.ngrok.io',
clientId: 'clientId',
clientSecret: 'clientSecret',
name: 'Sample App',
identifier: 'sample-app',
description: 'Sample App description',
dbFolder: __dirname,
imagePath: __dirname + '/' + 'logo.png',
profileResourcesMenu: {
fileName: 'setup.html',
uiPath: __dirname + '/' + 'public',
environments: 'crowdin-enterprise'
}
};
crowdinModule.createApp(configuration);

Configuration

ParameterDescriptionExample
imagePathPath to the module logo (can be different image than the app logo).__dirname + '/' + 'reports.png'
fileNameOptional, only needed if file is not index.html.'reports.html'
uiPathFolder where UI of the module is located (js, html, css files).__dirname + '/' + 'public'
environmentsEnvironment where the module should render.crowdin / enterprise

Combining Modules

Each module can work as an extension to other modules, being something like a configuration UI for them:

index.js
const crowdinModule = require('@crowdin/app-project-module');
const app = crowdinModule.express();
const configuration = {
baseUrl: 'https://123.ngrok.io',
clientId: 'clientId',
clientSecret: 'clientSecret',
name: 'Sample App',
identifier: 'sample-app',
description: 'Sample App description',
dbFolder: __dirname,
imagePath: __dirname + '/' + 'logo.png',
profileResourcesMenu: {
fileName: 'setup.html',
uiPath: __dirname + '/' + 'public',
environments: 'crowdin'
},
customMT: {
translate
},
onUninstall: cleanup
};
const crowdinApp = crowdinModule.addCrowdinEndpoints(app, configuration);
const metadataStore = crowdinModule.metadataStore;
async function cleanup(organization, allCredentials) {
// Cleanup logic
await crowdinApp.deleteMetadata(organization);
}
async function translate(crowdinClient, context, projectId, source, target, strings) {
const organization = context.jwtPayload.domain || context.jwtPayload.context.organization_id;
const metadata = await metadataStore.getMetadata(organization);
// do translation based on metadata
const translations = ['hello', 'world'];
return translations;
}
// extra endpoints for resources UI
app.post('/metadata', async (req, res) => {
const { context } = await crowdinApp.establishCrowdinConnection(req.query.jwt);
const id = `${context.jwtPayload.context.organization_id}_${context.jwtPayload.context.project_id}`;
const organization = context.jwtPayload.domain || context.jwtPayload.context.organization_id;
const metadata = await metadataStore.getMetadata(organization);
await crowdinApp.saveMetadata(id, req.body, organization);
res.status(204).end();
});
app.get('/metadata', async (req, res) => {
const { context } = await crowdinApp.establishCrowdinConnection(req.query.jwt);
const id = `${context.jwtPayload.context.organization_id}_${context.jwtPayload.context.project_id}`;
const metadata = await metadataStore.getMetadata(id) || {};
res.status(200).send(metadata);
});
app.listen(3000, () => console.log('Crowdin app started'));

Read more about App Metadata.