Skip to content

User Interface

You can specify your own UI by specifying the uiPath property (and optionally fileName) or by using the React JSON schema forms.

Only the Project Integration apps use the crowdin-simple-integration component provided by the Crowdin UI Kit by default.

React JSON Schema Forms

For all modules that have UI (except projectIntegration), you can use React JSON Schema forms as frontend.

To achieve this, you can specify the formSchema property in the module definition:

index.js
configuration.projectMenu = {
formSchema: {
"title": "A registration form",
"description": "A simple form example.",
"type": "object",
"required": [
"firstName",
"lastName"
],
"properties": {
"firstName": {
"type": "string",
"title": "First name",
"default": "Chuck"
},
"lastName": {
"type": "string",
"title": "Last name"
}
}
},
formUiSchema: {
"ui:submitButtonOptions": {
"submitText": "Confirm Details"
},
"lastName": {
"ui:help": "Hint: Choose cool lastname!"
}
}
};

By default, form data will be stored in app metadata. To retrieve data you can use this code:

crowdinApp.getMetadata(`${organizationId}-${projectId}`);

To customize this behavior you can provide formGetDataUrl and formPostDataUrl properties that will contain URL to custom actions in your app. This actions should support GET request for fetching data (formGetDataUrl) and POST request to save new data (formPostDataUrl).

Response structure for GET request example:

res.status(200).send({
formData: {
firstName: 'First Name'
}
}).end();

Also, you can provide the following properties:

  • formSchema - new schema to replace active schema;
  • formUiSchema - new uiSchema to replace current;
  • message - custom message that will be displayed in toasts;
  • redirect - url for redirect (useful for files download).

Custom HTML Field

You can add a piece of custom HTML to your form. To do this, you will need:

  1. Add field with type string to the form with additional uiSchema properties:

    const exampleConfig = {
    formSchema: {
    bio: {
    type: 'string'
    }
    },
    formUiSchema: {
    bio: {
    'ui:widget': 'htmlWidget'
    }
    }
    };
  2. Provide form value using one of options - ui:options content or formData (formData value have higher priority):

    const exampleConfig = {
    formSchema: {
    bio: {
    type: 'string',
    },
    },
    formUiSchema: {
    bio: {
    'ui:widget': 'htmlWidget',
    },
    },
    formData: {
    bio: '<h2>HTML formatted description</h2>',
    },
    };

Custom Widgets

You can extend the functionality of your form by using custom widgets. Below are two examples of custom widgets that can be used to enhance your forms: crowdinFilesWidget and monacoEditorWidget.

Crowdin Files Widget

The crowdinFilesWidget widget, part of the Crowdin UI Kit, allows users to select files. To use the crowdinFilesWidget, add a property to your formSchema with the type string and provide the custom widget in the formUiSchema as follows:

index.js
formSchema: {
properties: {
files: {
title: 'Crowdin Files',
type: 'string',
},
},
},
formUiSchema: {
files: {
"ui:widget": 'crowdinFilesWidget',
},
}

Monaco Editor Widget

The monacoEditorWidget allows you to embed a powerful code editor into your form. It is useful for scenarios where users need to provide or modify code snippets. The widget is based on the Monaco Editor, which powers Visual Studio Code, and supports syntax highlighting, auto-completion, and other advanced editor features.

index.js
formSchema: {
properties: {
code: {
type: 'string',
language: 'javascript',
height: 500,
minimap: true,
lineNumbers: 'on',
},
},
},
formUiSchema: {
code: {
"ui:widget": 'monacoEditorWidget',
"ui:help": 'Provide your custom JavaScript code here',
},
}

You can specify various editor options such as language, height, lineNumbers, and minimap visibility or provide custom options with different settings. You can find the full list of available options in the Monaco Editor documentation. For example:

index.js
options: {
minimap: {
enabled: true,
side: 'left',
},
lineNumbers: 'on',
}

Manipulate Form from JavaScript

You may want to manipulate the form from JavaScript. For example, you may want to change the form schema or uiSchema from a custom code widget.

To do this, you can use the global renderForm function and the formSchema, formUiSchema and formData variables.

index.js
const updatedSchema = {
...window.currentFormSchema,
properties: {
...window.currentFormSchema.properties,
lastName: {
type: 'string',
title: 'New title for last name field',
default: 'Jon Doe'
}
}
};
window.renderForm(window.currentFormData, updatedSchema, window.currentFormUiSchema);

Listening to Data Change Events

You can also listen for form data changes:

index.js
function setInputValue (target, value) {
const setter = Object.getOwnPropertyDescriptor(target, "value").set
const prototype = Object.getPrototypeOf(target)
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set
if (setter && setter !== prototypeValueSetter) {
prototypeValueSetter.call(target, value)
} else {
setter.call(target, value)
}
const event = new Event("input", {bubbles: true})
target.dispatchEvent(event);
}
document.addEventListener('formDataUpdated', (event) => {
if (event?.detail?.type === '1') {
setInputValue(document.getElementById('root_selector'), 'value');
}
});

Masking Credentials

The maskKey is designed to mask a given string, revealing only the last three characters while replacing the preceding characters with a masking symbol (by default, an asterisk *).

index.js
const crowdinModule = require("@crowdin/app-project-module");
// ...
crowdinModule.maskKey('ABCD-ABCD-ABCD'); // Output: "***********BCD"

You should mask input values if you use form to store credentials, api keys, passwords etc. To achieve this add middlewares before functions used to display and store form data. Also use password widget in form schema.

Example:

index.js
const moduleConfig = {
formSchema: {
title: "Service Setup Form",
type: "object",
required: ["key"],
properties: {
key: {
type: "string",
title: "API Key"
}
}
},
formUiSchema: {
key: {
"ui:widget": "password"
}
},
formPostDataUrl: '/form',
// temp field, will be removed
maskPasswords: true
};
index.js
const crowdinModule = require("@crowdin/app-project-module");
// ...
app.post('/save-form-data', crowdinModule.postRequestCredentialsMasker(settingsForm), async (req, res) => {
const { client, context } = await crowdinApp.establishCrowdinConnection(req.query.jwtToken);
const formData = req.body.data;
});
// if your application uses a non-default action to retrieve data, you should also mask it.
app.get('/get-form-data', crowdinModule.getRequestCredentialsMasker({ moduleConfig: crowdinModule }), async (req, res) => {
const { client, context } = await crowdinApp.establishCrowdinConnection(req.query.jwtToken);
const data = (await storage.getStorage().getMetadata(id)) || {};
return res.send({
formData: data
});
});

With such setup API key will be masked for front-end. You can use it in your code as usual.

Skipping Toast After Form Submit

To skip default toast you can return message: '', or message: null in response.

req.send({ message: null });

Theming

For applications using the UI Kit, the theme will be automatically selected based on the theme chosen in Crowdin. Additionally, for those using React JSON Schema, the light or dark theme will be automatically set.

Manual Theme Selection for Other Applications

For all other cases, you can use the following App JS method:

AP.getTheme(function (theme) {
if (theme === 'dark') {
// set dark styles
} else {
// set light styles
}
});

You can also retrieve the list of CSS variables and use them for custom styling:

AP.getCssVariables(style => {
// apply custom styling
});

Alternatively, you can use one of the predefined CSS classes:

Text colors
crdn-text:primary
crdn-text:title
crdn-text:body
crdn-text:muted
crdn-text:disabled
crdn-text:info
crdn-text:success
crdn-text:warning
crdn-text:danger
Backgrounds
crdn-bg:lvl-0
crdn-bg:lvl-1
crdn-bg:lvl-2
crdn-bg:lvl-2:05
crdn-bg:lvl-3
crdn-bg:primary
crdn-bg:success
crdn-bg:warning
crdn-bg:danger
Borders
crdn-border:primary
crdn-border:danger
crdn-border:info
crdn-border:success
crdn-border:warning