Developer Portal: Developer Portal documentation
# API
> Learn how to use Crowdin's API to integrate localization into your process
Crowdin’s API is a full-featured RESTful API that helps you integrate localization into your development process. The endpoints we use allow you to easily make calls to retrieve information and perform necessary actions. Most of the functionality of Crowdin is available through the API. It allows you to create projects for translations, add and update files, download translations, and much more. In this way, you can script the complex actions that your situation requires. [Crowdin API Reference](/developer/api/v2/)File-based projects [Enterprise API Reference](/developer/enterprise/api/v2/)File-based projects [Crowdin API Reference](/developer/api/v2/string-based/)String-based projects [Enterprise API Reference](/developer/enterprise/api/v2/string-based/)String-based projects [GraphQL API](/developer/graphql-api/)GraphQL API is a tool that allows you to retrieve exactly the data you need using more specific and flexible queries. ## [API Clients](#api-clients) [Section titled “API Clients”](#api-clients) The Crowdin API clients are lightweight, open-source interfaces developed for the Crowdin API. They offer common services for making API requests. [Official](https://github.com/crowdin/crowdin-api-client-js) [](https://github.com/crowdin/crowdin-api-client-js) ##### [Crowdin JavaScript client](https://github.com/crowdin/crowdin-api-client-js) [13967](https://github.com/crowdin/crowdin-api-client-js) [View and Install](https://github.com/crowdin/crowdin-api-client-js) [Official](https://github.com/crowdin/crowdin-api-client-php) [](https://github.com/crowdin/crowdin-api-client-php) ##### [Crowdin PHP client](https://github.com/crowdin/crowdin-api-client-php) [6235](https://github.com/crowdin/crowdin-api-client-php) [View and Install](https://github.com/crowdin/crowdin-api-client-php) [Official](https://github.com/crowdin/crowdin-api-client-java) [](https://github.com/crowdin/crowdin-api-client-java) ##### [Crowdin Java client](https://github.com/crowdin/crowdin-api-client-java) [9074](https://github.com/crowdin/crowdin-api-client-java) [View and Install](https://github.com/crowdin/crowdin-api-client-java) [Official](https://github.com/crowdin/crowdin-api-client-python) [](https://github.com/crowdin/crowdin-api-client-python) ##### [Crowdin Python client](https://github.com/crowdin/crowdin-api-client-python) [6938](https://github.com/crowdin/crowdin-api-client-python) [View and Install](https://github.com/crowdin/crowdin-api-client-python) [Official](https://github.com/crowdin/crowdin-api-client-ruby) [](https://github.com/crowdin/crowdin-api-client-ruby) ##### [Crowdin Ruby client](https://github.com/crowdin/crowdin-api-client-ruby) [5922](https://github.com/crowdin/crowdin-api-client-ruby) [View and Install](https://github.com/crowdin/crowdin-api-client-ruby) [Official](https://github.com/crowdin/crowdin-api-client-dotnet) [](https://github.com/crowdin/crowdin-api-client-dotnet) ##### [Crowdin .NET client](https://github.com/crowdin/crowdin-api-client-dotnet) [6240](https://github.com/crowdin/crowdin-api-client-dotnet) [View and Install](https://github.com/crowdin/crowdin-api-client-dotnet) [Official](https://github.com/crowdin/crowdin-api-client-go) [](https://github.com/crowdin/crowdin-api-client-go) ##### [Crowdin Go client](https://github.com/crowdin/crowdin-api-client-go) [1018](https://github.com/crowdin/crowdin-api-client-go) [View and Install](https://github.com/crowdin/crowdin-api-client-go) ## [AI Agents and API](#ai-agents-and-api) [Section titled “AI Agents and API”](#ai-agents-and-api) AI agents can integrate with Crowdin via the REST API or MCP (Model Context Protocol). AI-focused integration surfaces include LLM-friendly API specs (`llms.txt`) and MCP tool access to Crowdin capabilities. [API specs (llms.txt)](/_llms-txt/api.txt)LLM-friendly index of Crowdin API endpoints. [Crowdin MCP Server](/developer/crowdin-mcp-server/)MCP tools that expose Crowdin operations to AI clients. ## [See Also](#see-also) [Section titled “See Also”](#see-also) [Language Codes](/developer/language-codes/)A list of language codes used in Crowdin and Crowdin Enterprise.
# Mobile Apps Localization
> Manage localization for iOS and Android apps without translating the same strings twice
Effectively manage localization for iOS and Android apps without translating the same strings twice. ## [Localization Management within a Single Project](#localization-management-within-a-single-project) [Section titled “Localization Management within a Single Project”](#localization-management-within-a-single-project) Translate iOS and Android files within one Crowdin project. Use one of the options suggested below, depending on your project specifications. Recommendations: * Upload iOS (iOS Strings, iOS XLIFF) and Android (Android XML) app files into different locations (folders) in the Crowdin project. * Connect both Android and iOS repos to your Crowdin project to automate the synchronization of the source and translation files. * Use the **Duplicate Strings** option to manage duplicates. * Use the **Unify Placeholders** option to manage Android and iOS placeholders. ### [Hide Duplicates](#hide-duplicates) [Section titled “Hide Duplicates”](#hide-duplicates) Upload iOS and Android app files to one Crowdin project and select the **Hide** option in the project’s **Settings > Import > Source Strings**. Usually, iOS and Android apps share most of the source strings between iOS and Android files. Once you select the **Hide** option, the system detects duplicate strings across both file types and hides them, keeping visible only the strings from the files uploaded first. After the master string is translated, hidden duplicates inherit that translation due to the selected **Hide** option. This way, translators work only with unique visible texts. When the master string translation is updated, hidden duplicates inherit the updated translation. Read more about [Duplicate Strings](/project-settings/import/#duplicate-strings). ### [Unify Placeholders](#unify-placeholders) [Section titled “Unify Placeholders”](#unify-placeholders) If some source strings for iOS and Android are the same but differ only in placeholders, we recommend selecting the **Unify Placeholders** option in the project’s **Settings > Import > Source Strings**. For example, you added the iOS string `Hello, %@!` and a similar one to Android `Hello, %s!`. The **Unify Placeholders** option will convert both to `Hello, [%s]!`, so translation from the Android file can migrate to iOS. On export, you will get translations with the original placeholders. It’s also possible to add/modify new strings online in the project for Android XML and iOS Strings files via the **Strings** section of the project. Read more about [String Editing](/string-management/#string-editing). ## [Localization Management within a Single Project with Bundles](#localization-management-within-a-single-project-with-bundles) [Section titled “Localization Management within a Single Project with Bundles”](#localization-management-within-a-single-project-with-bundles) Recommendations: * Upload either iOS (iOS Strings, iOS XLIFF) or Android (Android XML) app files to your Crowdin project. * Configure bundles to be able to export translations in the needed file format. Read more about [Bundles](/bundles/). ### [Translation Export using Bundles](#translation-export-using-bundles) [Section titled “Translation Export using Bundles”](#translation-export-using-bundles) Localize the resources of just one application within Crowdin, and download different file formats for both your Android and iOS apps. For example, you can upload an XML file to Crowdin for [Android localization](https://crowdin.com/blog/2022/08/10/android-app-localization-tutorial) and receive two files on export: XML for Android and Strings for iOS. You might need to make some slight adjustments to the exported files (translation keys will remain the same as in the Android file, so they might need adjustments for the iOS file). However, the localization time and expenses for translation services can be significantly reduced using this approach. ## [Next Steps in Mobile Apps Localization](#next-steps-in-mobile-apps-localization) [Section titled “Next Steps in Mobile Apps Localization”](#next-steps-in-mobile-apps-localization) Here are the next steps you might consider while localizing your mobile apps. As an alternative to a more traditional approach when dealing with source files, you can send strings for translation directly from your design tools with the help of Crowdin plugins. Another good option is to use CDN Distributions to update translated strings of your mobile apps instantly without a need to roll out a new version on the App Store or Google Play. Read more about [Android](https://crowdin.com/blog/2022/08/10/android-app-localization-tutorial) or iOS [mobile app localization](https://crowdin.com/blog/2021/02/11/smart-ways-to-approach-mobile-app-localization) on our blog.
# Authorizing OAuth Apps
> Learn how to enable organization members to authorize your OAuth app
You can enable organization members to authorize your OAuth app. When you build an OAuth app, implement the web application flow described below to obtain an authorization code and then exchange it for a token. You choose the application type when you register it. A confidential application authorizes with a client secret, which suits apps with a trusted backend that can keep the secret private. A public application authorizes with PKCE and has no client secret, which suits single-page (SPA), mobile, and CLI apps. ## [Request Authorization Code](#request-authorization-code) [Section titled “Request Authorization Code”](#request-authorization-code) You should redirect the user to the `/oauth/authorize` endpoint with the following GET parameters:
```bash
https://accounts.crowdin.com/oauth/authorize
```
This will ask the user to approve the app access to their account based on the scopes specified in `REQUESTED_SCOPES` and then redirect back to the `REDIRECT_URI` you provided when creating an app. ### [Parameters](#request-authorization-code-parameters) [Section titled “Parameters”](#request-authorization-code-parameters) | Name | Value | Description | | ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `client_id` | string | **Required.** You receive Client ID for the app when you register it. | | `redirect_uri` | string | **Required.** The URL in your application where users will be sent after authorization. | | `response_type:``code` | string | **Required.** The parameter is used for the flow specification of an OAuth app. | | `scope` | string | **Required.** Select the access your app requires from the list of scopes available. You can add multiple [scopes](/developer/understanding-scopes/) separated by spaces (no need to use quotation marks). | | `state` | string | **Strongly recommended.** An unguessable random string. Use it for extra protection against cross-site request forgery attacks. | | `code_challenge` | string | **Required for public applications**, strongly recommended for confidential ones. A Base64-URL-encoded SHA-256 hash of the `code_verifier`. Used to secure the authorization flow with PKCE. A public application that omits it receives an `invalid_request` error instead of an authorization code. Read more about [PKCE RFC](https://datatracker.ietf.org/doc/html/rfc7636). | | `code_challenge_method` | string | **Strongly recommended.** The method used to derive the `code_challenge`. Crowdin supports `S256` and `plain`. Send `S256`. When this parameter is omitted, the method defaults to `plain`, which transmits the verifier unhashed. | The following Authorization Url will be created:
```bash
https://accounts.crowdin.com/oauth/authorize?client_id=m50YenPpqac8u5D4dnK&redirect_uri=https://impact-mobile.com/auth/crowdin&response_type=code&scope=project+tm&state=d131dd02c5e6eec4
```
After successful authorization users are redirected back to your site:
```bash
https://impact-mobile.com/auth/crowdin/?code=def50200df1fbb5ebac05f9288850d9e...0835bd3cf42&state=d131dd02c5e6eec4
```
If authorization has been declined, users are redirected to your website with an error:
```bash
https://impact-mobile.com/auth/crowdin/?error=access_denied&state=d131dd02c5e6eec4
```
## [Exchange the Code for an Access Token](#exchange-the-code-for-an-access-token) [Section titled “Exchange the Code for an Access Token”](#exchange-the-code-for-an-access-token) If a user authorizes the app, Crowdin redirects back to your site and you can exchange the code received for an access token:
```bash
POST https://accounts.crowdin.com/oauth/token
```
### [Parameters](#redirect-parameters) [Section titled “Parameters”](#redirect-parameters) | Name | Value | Description | | ---------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `grant_type:` `authorization_code` | string | **Required.** The parameter is used for the flow specification of an OAuth app. | | `client_id` | string | **Required.** You receive Client ID for the app when you register it. | | `client_secret` | string | **Required for confidential applications.** You receive Client Secret for the app when you register it. Sending a `code_verifier` does not replace it. Public applications have no client secret and omit this parameter. | | `redirect_uri` | string | **Required.** The URL in your application where users will be sent after authorization. | | `code` | string | **Required.** Code received from the callback query string. | | `code_verifier` | string | **Required if the authorization request included a `code_challenge`.** The original random string used to generate the `code_challenge`. Both confidential and public applications send it. | For example, a request in curl takes the following form:
```bash
curl -X POST \
https://accounts.crowdin.com/oauth/token \
-H "content-type: application/json" \
-d "{
\"grant_type\":\"authorization_code\",
\"client_id\":\"m50YenPpqac8u5D4dnK\",
\"client_secret\":\"yz35kYtjox...YE9Am\",
\"redirect_uri\":\"https://impact-mobile.com/auth/crowdin\",
\"code\":\"def50200df1fbb5ebac05f9288850d9e...0835bd3cf42\"
}"
```
### [Response](#redirect-response) [Section titled “Response”](#redirect-response) By default, the response takes the following form:
```json
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJS...lag1e_Zk4EdJ5diYfz0",
"token_type":"bearer",
"expires_in": 7200,
"refresh_token": "b213c684ccaa7db1217e946e6ad...fff7ae"
}
```
## [Make Requests to the API with the Access Token Returned](#make-requests-to-the-api-with-the-access-token-returned) [Section titled “Make Requests to the API with the Access Token Returned”](#make-requests-to-the-api-with-the-access-token-returned) The access token now allows you to make requests to Crowdin API on behalf of the authorized user. For example, in curl you can set the following Authorization header:
```bash
curl -H "Authorization: Bearer ACCESS_TOKEN" https://api.crowdin.com/api/v2/projects
```
Crowdin Enterprise:
```bash
curl -H "Authorization: Bearer ACCESS_TOKEN" https://.api.crowdin.com/api/v2/projects
```
Read more about [Security for Crowdin Apps](/developer/crowdin-apps-security/). ## [Refresh Token](#refresh-token) [Section titled “Refresh Token”](#refresh-token) The access token received after a user authorizes the app has an expiration time. The access token expires in the number of seconds defined in the response. To refresh a token without requiring the user to be redirected, send a POST request with the following body parameters to the authorization server:
```bash
POST https://accounts.crowdin.com/oauth/token
```
### [Parameters](#refresh-token-parameters) [Section titled “Parameters”](#refresh-token-parameters) | Name | Value | Description | | ----------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `grant_type:` `refresh_token` | string | **Required.** The parameter is used for the flow specification of an OAuth app. | | `client_id` | string | **Required.** You receive Client ID for the app when you register it. | | `client_secret` | string | **Required for confidential applications.** You receive Client Secret for the app when you register it. Public applications omit this parameter. | | `refresh_token` | string | **Required.** Refresh token received from the last authorization response. | For example, a request in curl takes the following form:
```bash
curl -X POST \
https://accounts.crowdin.com/oauth/token \
-H "content-type: application/json" \
-d "{
\"grant_type\":\"refresh_token\",
\"client_id\":\"m50YenPpqac8u5D4dnK\",
\"client_secret\":\"yz35kYtjox...YE9Am\",
\"refresh_token\":\"b213c684ccaa7db1217e946e6ad...fff7ae\"
}"
```
### [Response](#refresh-token-response) [Section titled “Response”](#refresh-token-response) By default, the response takes the following form:
```json
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJS...ZjFkMWI4OWFlIiwiaWF",
"token_type":"bearer",
"expires_in": 7200,
"refresh_token": "ea506ea4c37aa152f0a91ed2482...4a0c567"
}
```
## [Redirect URLs](#redirect-urls) [Section titled “Redirect URLs”](#redirect-urls) You can register one or more redirect URLs when you create an OAuth Application on Crowdin. For security reasons, if the URL is not included in the Application info, you won’t be able to redirect users to this URL after authorization.
# Automating Screenshot Management
> Learn how to automate screenshot creation and updates with Crowdin In-Context and Playwright
This guide explains how to automate generating and updating screenshots in Crowdin [In-Context](/developer/in-context-localization/). By automating screenshot creation, translators gain accurate and up-to-date visual context, improving the localization process and translation quality. The guide covers prerequisites, setup instructions, capturing screenshots across web pages, and uploading them to Crowdin via API. ## [Prerequisites](#prerequisites) [Section titled “Prerequisites”](#prerequisites) Before proceeding, ensure that your website integrates [Crowdin In-Context functionality](/developer/in-context-localization/#integration). ### [Account Setup](#account-setup) [Section titled “Account Setup”](#account-setup) To configure your account for automation to function properly, follow these steps: 1. **Enable 2FA in Crowdin**: Open your project and go to **Settings > Privacy & collaboration > Privacy** to set up Two-factor authentication for login. 2. **Disable Device Verification**: Device verification ensures account security but can interrupt automated workflows. Disabling it for testing environments ensures uninterrupted automation. * For Crowdin: Go to **Account Settings > Account > New device verification** and disable the setting. * For Crowdin Enterprise: Go to **Account Settings > Security > Device Verification** and disable the setting. 3. **Generate a Secret Key**: Obtain the secret key for generating 2FA tokens. This key is required for programmatically creating one-time passwords (OTPs) using the `otplib` library. ### [Dependencies](#dependencies) [Section titled “Dependencies”](#dependencies) This guide uses the following dependencies for browser automation and OTP generation: * [Playwright](https://playwright.dev/): A modern testing framework for browser automation, ideal for navigating and interacting with web pages. * [`otplib`](https://www.npmjs.com/package/otplib): A library for generating one-time passwords (OTPs) programmatically, essential for bypassing 2FA in automated workflows. Run the following command to install the dependencies: * npm
```bash
npm install -D @playwright/test otplib
```
* Yarn
```bash
yarn add -D @playwright/test otplib
```
* pnpm
```bash
pnpm add -D @playwright/test otplib
```
## [Capturing Screenshots with Crowdin In-Context](#capturing-screenshots-with-crowdin-in-context) [Section titled “Capturing Screenshots with Crowdin In-Context”](#capturing-screenshots-with-crowdin-in-context) Crowdin provides the `window.jipt.capture_screenshot(name: string, options: object | null)` method to automate screenshot generation. In addition to screenshots, this method collects metadata to provide translators with detailed and accurate context for their work. The following sections explain how to implement this functionality using Playwright. ### [Code Example](#code-example) [Section titled “Code Example”](#code-example) The following script illustrates navigating a website, logging in, capturing screenshots, and uploading them to Crowdin for translators’ reference:
```js
// @ts-check
const { test, expect } = require('@playwright/test');
const { authenticator } = require('otplib');
test('Capture Crowdin Screenshots', async ({ page }) => {
const siteUrl = 'https://example.com';
// Navigate to the application
await page.goto(siteUrl);
// Log in
await page.locator('#jipt-login-panel iframe').contentFrame().getByRole('button', { name: 'Log In' }).click();
await page.getByLabel('Email or Username').fill('username');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Log In' }).click();
// Handle Two-Factor Authentication (if applicable)
const token = authenticator.generate('KEY'); // Replace 'KEY' with your 2FA secret
await page.getByLabel('Verification Code').fill(token);
await page.getByRole('button', { name: 'Log In' }).click();
// Confirm login and start capturing screenshots
await page.getByRole('button', { name: 'Keep Me Logged In' }).click();
// Capture the first screenshot
await page.goto(siteUrl);
await page.locator('#crowdin-jipt-mask').click();
await expect(page.locator('h1')).toContainText('Crowdin HTML Sample');
await page.evaluate(() => {
return new Promise((resolve, reject) => {
window.jipt.capture_screenshot('HTML Sample File', { success: resolve, error: reject });
});
});
// Capture a second screenshot on another page
await page.goto(`${siteUrl}/second`);
await expect(page.locator('h1')).toContainText('Second Crowdin HTML Sample');
await page.evaluate(() => {
return new Promise((resolve, reject) => {
window.jipt.capture_screenshot('Second HTML Sample File', { override: false, success: resolve, error: reject });
});
});
});
```
### [Key Code Implementation Details](#key-code-implementation-details) [Section titled “Key Code Implementation Details”](#key-code-implementation-details) * **Navigating Pages**: Use `page.goto(url)` to navigate to specific pages in your application. * **Logging In**: Simulate user actions, such as filling out forms and clicking buttons, using Playwright’s locators like `getByRole()` and `getByLabel()`. * **Capturing Screenshots**: Use `window.jipt.capture_screenshot()` to generate and upload screenshots to Crowdin. The method accepts the following parameters: * `name: string`: The name of the screenshot. * `options: object | null`: Custom settings: * `override: boolean`: The `override` parameter determines how Crowdin handles screenshots with duplicate names. Use `true` (default) to overwrite the first matching screenshot or `false` to create a new one, even if the name matches. * `success: function`: A callback function triggered on successful upload. It receives an object with `{msg_type: 'make_screenshot_data', screenshot_name: string}`, which provides the type of the message and the name of the uploaded screenshot. * `error: function`: A callback function triggered on upload failure. It receives an object with `{msg_type: 'make_screenshot_error', response: object}` or an `Error`, containing details about the failure. * **Two-Factor Authentication**: Use the `otplib` library to programmatically generate OTP tokens when 2FA is enabled. Replace `'KEY'` with your project’s secret key for valid OTP generation.
# Configuration File
> Explore the possibilities of the crowdin.yml configuration file
The various Crowdin tools use a `crowdin.yml` configuration file that specifies the resources to be managed, including the files to be uploaded to Crowdin and the locations of the corresponding translations. [VCS Integrations](/integrations/#vcs-integrations)GitHub, GitLab, Bitbucket, Azure Repos. [Console Client (CLI)](https://crowdin.github.io/crowdin-cli/)Cross-platform command-line tool. [Visual Studio Code Plugin](https://marketplace.visualstudio.com/items?itemName=Crowdin.vscode-crowdin) [Android Studio Plugin](https://github.com/crowdin/android-studio-plugin#readme) ## [Configuration File Structure](#configuration-file-structure) [Section titled “Configuration File Structure”](#configuration-file-structure) A valid `crowdin.yml` configuration file has the following structure, so please ensure that you fill out all the needed information: * Head of the file with project credentials, preferences, and access information. * One files array element that describes a set of source and translation files you will manage. * Required fields in the files array: `source` that defines filters for the source files and `translation` with instructions on where to store the translated files or where to look for translations you already have if you want to upload them while setting up the CLI. ## [Writing a Simple Configuration File](#writing-a-simple-configuration-file) [Section titled “Writing a Simple Configuration File”](#writing-a-simple-configuration-file) A typical configuration file looks similar to the following: crowdin.yml
```yml
"project_id": "projectId"
"api_token": "personal-access-token"
"base_path": "."
"base_url": "https://api.crowdin.com"
"preserve_hierarchy": true
"files": [
{
"source": "/locale/en/folder1/[0-2].txt",
"translation": "/locale/%two_letters_code%/folder1/%original_file_name%"
},
]
```
| Name | Description | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `project_id` | Crowdin Project numeric ID | | `api_token` | Crowdin Personal Access Token. The token owner should have at least Manager permissions in the project | | `base_path`optional | Path to your project directory on a local machine relative to the `crowdin.yml` configuration file | | `base_url` optional | Crowdin API base URL. Optional for crowdin.com. For Crowdin Enterprise use the `https://{organization-name}.api.crowdin.com` | | `preserve_hierarchy`optional | If set to `true`, the directory structure will be preserved on the server. If set to `false`, the directory structure will be flattened. | | `source` | Filter for the source files. Wildcards are supported. | | `translation` | Path to store the translated files. Wildcards are supported. | ## [API Credentials from Environment Variables](#api-credentials-from-environment-variables) [Section titled “API Credentials from Environment Variables”](#api-credentials-from-environment-variables) You could load the API Credentials from an environment variable, for example: crowdin.yml
```yml
"project_id_env": "CROWDIN_PROJECT_ID"
"api_token_env": "CROWDIN_PERSONAL_TOKEN"
"base_path_env": "CROWDIN_BASE_PATH"
"base_url_env": "CROWDIN_BASE_URL"
```
If mixed, *api\_token* and *project\_id* are prioritized: crowdin.yml
```yml
"project_id_env": "CROWDIN_PROJECT_ID" # Low priority
"api_token_env": "CROWDIN_PERSONAL_TOKEN" # Low priority
"base_path_env": "CROWDIN_BASE_PATH" # Low priority
"base_url_env": "CROWDIN_BASE_PATH" # Low priority
"project_id": "projectId" # High priority
"api_token": "personal-access-token" # High priority
"base_path": "." # High priority
"base_url": "https://api.crowdin.com" # High priority
```
## [General Configuration](#general-configuration) [Section titled “General Configuration”](#general-configuration) The sample configuration provided above has source and translation attributes containing standard wildcards (also known as globbing patterns) to make it easier to work with multiple files. Here are patterns you can use: *** **\*** (asterisk) Matches any character in the file or directory name. If you specify `*.json`, it will include all files like `messages.json`, `about_us.json`, and anything that ends with `.json`. *** **\*\*** (doubled asterisk) Matches any string recursively (including subdirectories). Note that you can use `**` in both the source and translation patterns. When you use `**` in the translation pattern, it always includes a sub-path from the source for a given file. For example, you can use source: `/en/**/*.po` to recursively upload all `*.po` files to Crowdin. The translation pattern will be `/%two_letters_code%/**/%original_file_name%`. *** **?** (question mark) Matches any single character. *** **\[set]** Matchesany single character in a set. Behaves exactly like character sets in Regexp, including set negation (`[^a-z]`). *** **\\** (backslash) Escapes the next metacharacter. ## [Placeholders](#placeholders) [Section titled “Placeholders”](#placeholders) Crowdin CLI allows to use the following placeholders to put appropriate variables into the resulting file name: | **Name** | **Description** | | -------------------------- | ------------------------------------------------------------------------------------------ | | `%original_file_name%` | Original file name | | `%original_path%` | Take parent folder names in the Crowdin project to build file path in the resulting bundle | | `%file_extension%` | Original file extension | | `%file_name%` | File name without extension | | `%language%` | Language name (e.g., Ukrainian) | | `%two_letters_code%` | Language code ISO 639-1 (e.g., uk) | | `%three_letters_code%` | Language code ISO 639-2/T (e.g., ukr) | | `%locale%` | Locale (e.g., uk-UA) | | `%locale_with_underscore%` | Locale (e.g., uk\_UA) | | `%android_code%` | Android Locale identifier used to name “values-” directories | | `%osx_code%` | OS X Locale identifier used to name “.lproj” directories | | `%osx_locale%` | OS X locale used to name translation resources (e.g., uk, zh-Hans, zh\_HK) | You can also define the path for files in the resulting archive by putting a slash (`/`) at the beginning of the pattern. ## [Usage of Wildcards](#usage-of-wildcards) [Section titled “Usage of Wildcards”](#usage-of-wildcards) Structure of files and directories on the local machine: Example 1. Usage of wildcards in the source path: crowdin.yml
```yml
"files": [
{
"source": "/**/?[0-9].txt", # upload a1.txt, folder/a1.txt
"translation": "/**/%two_letters_code%_%original_file_name%"
},
{
"source": "/**/*\\?*.txt", # upload crowdin?test.txt, folder/crowdin?test.txt
"translation": "/**/%two_letters_code%_%original_file_name%"
},
{
"source": "/**/[^0-2].txt", # upload 3.txt, folder/3.txt, a.txt, folder/a.txt (ignore 1.txt, folder/1.txt)
"translation": "/**/%two_letters_code%_%original_file_name%"
}
]
```
Example 2. Usage of wildcards for ignoring files: crowdin.yml
```yml
"files": [
{
"source": "/**/*.*", #upload all files that the base_path contains
"translation": "/**/%two_letters_code%_%original_file_name%",
"ignore": [
"/**/%two_letters_code%_%original_file_name%", #ignore the translated files
"/**/?.txt", #ignore 1.txt, a.txt, folder/1.txt, folder/a.txt
"/**/[0-9].txt", #ignore 1.txt, folder/1.txt
"/**/*\\?*.txt", #ignore crowdin?test.txt, folder/crowdin?test.txt
"/**/[0-9][0-9][0-9].txt", #ignore 123.txt , folder/123.txt
"/**/[0-9]*_*.txt" #ignore 123_test.txt, folder/123_test.txt
]
}
]
```
## [Renaming Translations File](#renaming-translations-file) [Section titled “Renaming Translations File”](#renaming-translations-file) If you need to rename a file with translations or replace parts of the path after export, you can use the `translation_replace` parameter. For example, if the file is named `strings_en.po`, it can be renamed to `strings.po`, or you can replace folder names in the translation path: crowdin.yml
```yml
"files": [
{
"source": "/locale/**/*_en.po",
"translation": "/locale/**/%original_file_name%_%two_letters_code%",
"translation_replace": {
"_en": "",
"de/locale": "de/lokalisierung",
"fr/locale": "fr/paramètres"
}
}
]
```
In this case, `_en` will be erased from the file name, and the folder names `de/locale` and `fr/locale` will be replaced with `de/lokalisierung` and `fr/paramètres` respectively. ## [Ignoring Files and Directories](#ignoring-files-and-directories) [Section titled “Ignoring Files and Directories”](#ignoring-files-and-directories) From time to time, there are files and directories you don’t need to translate in Crowdin. In such cases, local per-file rules can be added to the config file in your project. crowdin.yml
```yml
"files": [
{
"source": "/**/*.properties",
"translation": "/**/%file_name%_%two_letters_code%.%file_extension%",
"ignore": [
"/test/file.properties",
"/example.properties"
]
},
{
"source": "/locale/en/**/*.po",
"translation": "/locale/%two_letters_code%/**/%original_file_name%",
"ignore": [
"/locale/en/templates",
"/locale/en/workflow"
]
}
]
```
You can also use [wildcards](#usage-of-wildcards) to ignore files. ## [Excluding Target Languages for Source Files](#excluding-target-languages-for-source-files) [Section titled “Excluding Target Languages for Source Files”](#excluding-target-languages-for-source-files) By default, the source files are available for translation into all target languages of the project. If some source files shouldn’t be translated into specific target languages, you can exclude them with the help of the parameter `excluded_target_languages`. Configuration file example: crowdin.yml
```yml
"files": [
{
"source": "/resources/en/*.json",
"translation": "/resources/%two_letters_code%/%original_file_name%",
"excluded_target_languages": [
"uk",
"fr"
]
}
]
```
## [Export Languages](#export-languages) [Section titled “Export Languages”](#export-languages) By default, all the languages are exported. If you need to export some specific languages, use the `export_languages` parameter to specify them. crowdin.yml
```yml
"export_languages": [
"uk",
"ja"
]
```
## [Multilingual Spreadsheet](#multilingual-spreadsheet) [Section titled “Multilingual Spreadsheet”](#multilingual-spreadsheet) If a CSV or XLS/XLSX file contains the translations for all target languages, you should specify appropriate language codes in the scheme. CSV file example:
```csv
identifier,source_phrase,context,Ukrainian,Spanish (Mexico),French
ident1,Source 1,Context 1,,,
ident2,Source 2,Context 2,,,
ident3,Source 3,Context 3,,,
```
Configuration file example: crowdin.yml
```yml
"files": [
{
"source": "multicolumn.csv",
"translation": "multicolumn.csv",
"first_line_contains_header": true,
"scheme": "identifier,source_phrase,context,uk,es-MX,fr"
}
]
```
If your CSV or XLS/XLSX file contains columns that should be skipped on import, use `none` for such columns in the scheme, for example: crowdin.yml
```yml
"scheme" : "identifier,source_phrase,context,uk,none,es-MX,none,fr"
```
##### [Scheme Constants](#scheme-constants) [Section titled “Scheme Constants”](#scheme-constants) To form the scheme for your CSV or XLS/XLSX file, use the following constants: * `identifier` – Column contains string identifiers. * `source_phrase` – Column contains source strings. * `source_or_translation` – Column contains source strings, but the same column will be filled with translations when the file is exported. When uploading existing translations, the values from this column will be used as translations. * `translation` – Column contains translations. * `context` – Column contains comments or context information for the source strings. * `max_length` – Column contains max.length limit values for the translations of the strings. * `labels` – Column contains labels for the source strings. * `none` – Column that will be skipped on import. ## [Saving Directory Structure on Server](#saving-directory-structure-on-server) [Section titled “Saving Directory Structure on Server”](#saving-directory-structure-on-server) You can use the `preserve_hierarchy` option to preserve or flatten the directory structure of your source files in the Crowdin project. Example of the configuration file using the `preserve_hierarchy` option: crowdin.yml
```yml
"preserve_hierarchy": true
"files": [
{
"source": "/locale/en/**/*.po",
"translation": "/locale/%two_letters_code%/**/%original_file_name%"
}
]
```
Let’s say the file/folder structure on your machine looks like this: If you don’t use the `"preserve_hierarchy": true` option in your configuration file at all or use it with the `false` value, all shared directories will be skipped, and the file structure in Crowdin will be represented as follows: Using the `"preserve_hierarchy": true` option, the file structure in Crowdin will be represented as follows: The directories that don’t contain any files for translation won’t be created in Crowdin (i.e., as the `emails` directory in the example above). ## [Uploading Files to Specified Path with Specified Type](#uploading-files-to-specified-path-with-specified-type) [Section titled “Uploading Files to Specified Path with Specified Type”](#uploading-files-to-specified-path-with-specified-type) This feature adds support for two optional parameters in the yml file section: `dest` and `type`. It’s typically used for projects where the uploaded name must be different so that Crowdin can detect the type correctly. The `dest` parameter allows you to specify a file name in Crowdin. It works for multiple files at once and supports the following placeholders: `%original_file_name%`, `%original_path%`, `%file_extension%`, `%file_name%`. Caution If you use the `dest` parameter, the configuration file should include the `preserve_hierarchy` parameter with the `true` value. Example of the configuration file with both parameters: crowdin.yml
```yml
"files": [
{
"source": "/conf/**/*.txt",
"dest": "/conf/**/%file_name%.properties",
"translation": "/conf/**/%two_letters_code%/%file_name%.properties",
"type": "properties"
},
{
"source": "/app/*.txt",
"dest": "/app/%file_name%.xml",
"translation": "/res/values-%android_code%/%original_file_name%",
"type": "android"
}
]
```
## [Changed Strings Update](#changed-strings-update) [Section titled “Changed Strings Update”](#changed-strings-update) You can use the `update_option` parameter to preserve translations for changed strings during the file update. If it is not set, translations for changed strings will be lost. Useful for typo fixes and minor changes in source strings. Depending on the value, `update_option` is used to preserve translations and preserve/remove validations of changed strings during file update. Acceptable values: * `update_as_unapproved` - preserve translations of changed strings and remove validations of those translations if they exist * `update_without_changes` - preserve translations and validations of changed strings Example of the configuration with the `update_option` parameter: crowdin.yml
```yml
"files": [
{
"source": "/*.csv",
"translation": "/%three_letters_code%/%file_name%.csv",
"first_line_contains_header": true,
"scheme": "identifier,source_phrase,translation,context",
"update_option": "update_as_unapproved"
},
{
"source": "/**/*.xlsx",
"translation": "/%three_letters_code%/folder/%file_name%.xlsx",
"update_option": "update_without_changes"
}
]
```
## [Custom Segmentation](#custom-segmentation) [Section titled “Custom Segmentation”](#custom-segmentation) Upload your XML, HTML, MD, or any other source files without a key-value structure with your own segmentation rules. If not specified, the pre-defined segmentation rules (SRX 2.0) are used for automatic content segmentation. Example of the configuration file custom segmentation: crowdin.yml
```yml
"files": [
{
"source": "/emails/sample1.html",
"translation": "/emails/%locale%/%original_file_name%",
"custom_segmentation": "/rules/sample.srx.xml"
}
]
```
## [Import Options](#import-options) [Section titled “Import Options”](#import-options) You can use additional parameters to customize the import process for specific file types. ### [XML Files Import Options](#xml-files-import-options) [Section titled “XML Files Import Options”](#xml-files-import-options) | Option | Type | Description | | -------------------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `translate_content` optional | bool | Defines whether to translate texts placed inside the tags. Acceptable values are 0 or 1. Default is 1. | | `translate_attributes`optional | bool | Defines whether to translate tags’ attributes. Acceptable values are 0 or 1. Default is 1. | | `content_segmentation`optional | bool | Defines whether to split long texts into smaller text segments. Acceptable values are 0 or 1. Default is 1. **Note!** When Content segmentation is enabled, the translation upload is handled by an experimental machine learning technology. | | `translatable_elements` optional | array | This is an array of strings, where each item is the XPaths to DOM element that should be imported. Sample path: `/path/to/node` or `/path/to/attribute[@attr]` **Note!** If defined, the parameters `translate_content` and `translate_attributes` are not taken into account while importing. | Example of the configuration with additional parameters: crowdin.yml
```yml
"files": [
{
"source": "/app/sample1.xml",
"translation": "/app/%locale%/%original_file_name%",
"translate_attributes": 1,
"translate_content": 0
},
{
"source": "/app/sample2.xml",
"translation": "/app/%locale%/%original_file_name%",
"translatable_elements": [
"/content/text", # translatable texts are stored in 'text' nodes of parent node 'content'
"/content/text[@value]" # translatable texts are stored in 'value' attribute of 'text' nodes
]
}
]
```
### [Other Files Import Options](#other-files-import-options) [Section titled “Other Files Import Options”](#other-files-import-options) | Option | Type | Description | | ------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `content_segmentation` optional | bool | Defines whether to split long texts into smaller text segments. Only for TXT, DOCX, DITA, IDML, MEDIAWIKI, HTML, Front Matter HTML, Markdown, Front Matter Markdown, XML, XLIFF, XLIFF 2.0 Acceptable values are 0 or 1. Default is 1. **Note:** When Content segmentation is enabled, the translation upload is handled by an experimental machine learning technology. | Example of the configuration with additional parameters: crowdin.yml
```yml
"files": [
{
"source": "/emails/sample1.html",
"translation": "/emails/%locale%/%original_file_name%",
"content_segmentation": 1
}
]
```
## [Export Options](#export-options) [Section titled “Export Options”](#export-options) You can use additional parameters to customize the export process for specific file types. ### [Java .properties File Format](#java-properties-file-format) [Section titled “Java .properties File Format”](#java-properties-file-format) ##### [Escape Quotes](#escape-quotes) [Section titled “Escape Quotes”](#escape-quotes) Defines whether a single quote should be escaped by another single quote or backslash in exported translations. You can add the `escape_quotes` per-file option. Acceptable values: * `0` - do not escape single quote * `1` - escape single quote with another single quote * `2` - escape single quote with a backslash * `3` - escape single quote with another single quote only in strings containing variables (default) ##### [Escape Special Characters](#escape-special-characters) [Section titled “Escape Special Characters”](#escape-special-characters) Defines whether any special characters (`=`, `:`, `!` and `#`) should be escaped by a backslash in exported translations. You can add the `escape_special_characters` per-file option. Acceptable values: * `0` - do not escape special characters * `1` - escape special characters by a backslash (default) Example of the configuration: crowdin.yml
```yml
"files": [
{
"source": "/en/strings.properties",
"translation": "/%two_letters_code%/%original_file_name%",
"escape_quotes": 1,
"escape_special_characters": 0
}
]
```
## [Configuration File for VCS Integrations](#configuration-file-for-vcs-integrations) [Section titled “Configuration File for VCS Integrations”](#configuration-file-for-vcs-integrations) VCS integrations require the same configuration file as the CLI tool, meaning the same structure is supported. The only difference is that you should not store the project credentials in the file header for security reasons. Also, you can use a few additional parameters. ### [Pull Request Title and Labels](#pull-request-title-and-labels) [Section titled “Pull Request Title and Labels”](#pull-request-title-and-labels) The default pull request title is `New Crowdin updates`. To specify your custom pull request title and add labels to the pull request, you can use the following parameters in the configuration file: `pull_request_title`, `pull_request_labels`. crowdin.yml
```yml
"pull_request_title": "Custom PR title"
"pull_request_labels": [
"crowdin",
"l10n"
]
```
Limitations Pull request labels are not supported by the Bitbucket integration. ### [Commit Message](#commit-message) [Section titled “Commit Message”](#commit-message) Each time the integration commits translations, it uses the following default commit message:
```plaintext
New translations {fileName} ({languageName})
[ci skip]
```
The `[ci skip]` tag is added by default so that syncing many files at once doesn’t trigger a separate CI build for each one. Your CI still runs as expected once these changes are merged into your main branch through a pull request. To add your own text to the commit message, use the `commit_message` parameter. By default, your text is appended below the default message (equivalent to setting `append_commit_message` to `true`): crowdin.yml
```yml
"commit_message": "Fix: New translations from Crowdin"
```
The resulting commit message looks like this:
```plaintext
New translations {fileName} ({languageName})
[ci skip]
Fix: New translations from Crowdin
```
To use only your own message instead of the default one, set `append_commit_message` to `false`: crowdin.yml
```yml
"commit_message": "Fix: New translations %original_file_name% from Crowdin"
"append_commit_message": false
```
You can use the `%original_file_name%` and `%language%` placeholders in your commit message to insert the file name and language. Caution When you replace the default message (`append_commit_message: false`), the `[ci skip]` tag is no longer added automatically. Include it in your custom message if you still want to skip intermediate CI builds. ### [Pull Request Assignee](#pull-request-assignee) [Section titled “Pull Request Assignee”](#pull-request-assignee) If you need to assign a pull request to particular users, use the `pull_request_assignees` parameter to specify them. **GitHub/GitHub Server:** crowdin.yml
```yml
"pull_request_assignees": [
"login1",
"login2"
]
```
**GitLab/GitLab Self-Managed:** crowdin.yml
```yml
"base_path": "."
"pull_request_assignees": [
"user_id1", # numeric ID
"user_id2" # numeric ID
]
```
Limitations Pull request assignee parameter is not supported by the Bitbucket, Bitbucket Server, and Azure Repos integrations. ### [Pull Request Reviewer](#pull-request-reviewer) [Section titled “Pull Request Reviewer”](#pull-request-reviewer) If you need to request a pull request review from particular users, use the `pull_request_reviewers` parameter to specify them. **GitHub/GitHub Server:** crowdin.yml
```yml
"pull_request_reviewers": [
"login1",
"login2"
]
```
**GitLab/GitLab Self-Managed:** crowdin.yml
```yml
"pull_request_reviewers": [
"user_id1", # numeric ID
"user_id2" # numeric ID
]
```
**Bitbucket:** crowdin.yml
```yml
"pull_request_reviewers": [
"uuid1", # user uuid
"uuid2" # user uuid
]
```
**Bitbucket Server:** crowdin.yml
```yml
"pull_request_reviewers": [
"username1",
"username2"
]
```
**Azure Repos:** crowdin.yml
```yml
"pull_request_reviewers": [
"guid1", # user ID
"guid2" # user ID
]
```
## [Adding Labels to Source Strings](#adding-labels-to-source-strings) [Section titled “Adding Labels to Source Strings”](#adding-labels-to-source-strings) To add existing or new labels to the source strings, use the `labels` parameter. Labels will be added to the source strings only during the initial upload to the Crowdin project. The strings uploaded to the Crowdin project before the use of the `labels` parameter won’t be labeled. If you remove the label added during the initial upload directly in Crowdin, it won’t be re-added on the next syncs. Example: crowdin.yml
```yml
"files": [
{
"source" : "/resources/en/*.json",
"translation" : "/resources/%two_letters_code%/%original_file_name%",
"labels" : [
"android",
"emails"
]
}
]
```
Limitations Label names can contain any special character except `,`. Read more about [Labels](/project-settings/labels/). ## [Language Mapping](#language-mapping) [Section titled “Language Mapping”](#language-mapping) Often software projects have custom names for locale directories. Crowdin allows you to map your own languages to be recognizable in your projects. Let’s say your locale directories are named `en`, `uk`, `fr`, `de`. All of them can be represented by the `%two_letters_code%` placeholder. Still, you have one directory named `zh_CH`. You can also override language codes for other placeholders like `%android_code%`, `%locale%`, etc. Read more about [Language Mapping configuration for CLI](https://crowdin.github.io/crowdin-cli/advanced#languages-mapping-configuration). To make it work with Crowdin without changes in your project, you can set up Language Mapping via UI. * [Language mapping in Crowdin](/project-settings/languages/#adding-custom-language-codes) * [Language mapping in Crowdin Enterprise](/enterprise/project-settings/languages/#language-mapping) ## [Using One Configuration File for VCS Integrations and CLI](#using-one-configuration-file-for-vcs-integrations-and-cli) [Section titled “Using One Configuration File for VCS Integrations and CLI”](#using-one-configuration-file-for-vcs-integrations-and-cli) There are cases when it’s necessary to use VCS integration and CLI for one project. Mostly, in this kind of situation, you’d need to have two separate configuration files, one for VCS integration and another for CLI. However, you can use a single configuration file for both cases. Since the VCS integration configuration file doesn’t contain `project_id` and `api_token` credentials required for CLI, you can pass them directly in the command using the following parameters: `-i/--project-id`, `-T/--token`. As a result, your command for downloading translations via CLI will look like the following:
```shell
crowdin download -i {your-project-id} -T {your-token}
```
Alternatively, you may use [Environment Variables](#api-credentials-from-environment-variables) or [Split Project Configuration and API Credentials](https://crowdin.github.io/crowdin-cli/configuration#split-project-configuration-and-api-credentials). ## [Example Configurations](#example-configurations) [Section titled “Example Configurations”](#example-configurations) ##### [Uploading CSV files](#uploading-csv-files) [Section titled “Uploading CSV files”](#uploading-csv-files) crowdin.yml
```yml
"project_id": "projectId"
"api_token": "personal-access-token"
"base_path": "."
"base_url": "https://api.crowdin.com"
"files": [
{
"source": "/*.csv",
"translation": "/%two_letters_code%/%original_file_name%",
# Specifies whether first line should be imported or it contains columns headers
"first_line_contains_header": true,
# Used only when uploading CSV file to define data columns mapping
"scheme": "identifier,source_phrase,translation,context,max_length"
}
]
```
##### [GetText Project](#gettext-project) [Section titled “GetText Project”](#gettext-project) crowdin.yml
```yml
"project_id": "projectId"
"api_token": "personal-access-token"
"base_path": "."
"base_url": "https://api.crowdin.com"
"files" : [
{
"source" : "/locale/en/**/*.po",
"translation" : "/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%",
"languages_mapping" : {
"two_letters_code" : {
"zh-CN" : "zh_CH",
"fr-QC": "fr"
}
}
}
]
```
##### [Android Project](#android-project) [Section titled “Android Project”](#android-project) crowdin.yml
```yml
"project_id": "projectId"
"api_token": "personal-access-token"
"base_path": "."
"base_url": "https://api.crowdin.com"
"files" : [
{
"source" : "/res/values/*.xml",
"translation" : "/res/values-%android_code%/%original_file_name%",
"languages_mapping" : {
"android_code" : {
"de" : "de"
}
}
}
]
```
# Crowdin Query Language (CroQL)
> Retrieve needed localization resources based on specific conditions
Crowdin Query Language (CroQL) is a tool for Crowdin Editor and Crowdin Enterprise Editor and Crowdin and Crowdin Enterprise API that allows you to retrieve needed localization resources based on specific conditions. Using CroQL, you can filter source strings and their translations for a specific target language, TM segments, and Glossary Terms. You can use CroQL with the following API methods: * Crowdin * [List Strings](/developer/api/v2/#tag/Source-Strings/operation/api.projects.strings.getMany) * [List Strings](/developer/api/v2/string-based/#tag/Source-Strings/operation/api.projects.strings.getMany) String-based * [List Language Translations](/developer/api/v2/#tag/StringAsset-Translations/operation/api.projects.languages.translations.getMany) * [List Language Translations](/developer/api/v2/string-based/#tag/String-Translations/operation/api.projects.languages.translations.getMany) String-based * [List TM Segments](/developer/api/v2/#tag/Translation-Memory/operation/api.tms.segments.getMany) * [List TM Segments](/developer/api/v2/string-based/#tag/Translation-Memory/operation/api.tms.segments.getMany) String-based * [List Terms](/developer/api/v2/#tag/Glossaries/operation/api.glossaries.terms.getMany) * [List Terms](/developer/api/v2/string-based/#tag/Glossaries/operation/api.glossaries.terms.getMany) String-based * Crowdin Enterprise * [List Strings](/developer/enterprise/api/v2/#tag/Source-Strings/operation/api.projects.strings.getMany) * [List Strings](/developer/enterprise/api/v2/string-based/#tag/Source-Strings/operation/api.projects.strings.getMany) String-based * [List Language Translations](/developer/enterprise/api/v2/#tag/StringAsset-Translations/operation/api.projects.languages.translations.getMany) * [List Language Translations](/developer/enterprise/api/v2/string-based/#tag/String-Translations/operation/api.projects.languages.translations.getMany) String-based * [List TM Segments](/developer/enterprise/api/v2/#tag/Translation-Memory/operation/api.tms.segments.getMany) * [List TM Segments](/developer/enterprise/api/v2/string-based/#tag/Translation-Memory/operation/api.tms.segments.getMany) String-based * [List Terms](/developer/enterprise/api/v2/#tag/Glossaries/operation/api.glossaries.terms.getMany) * [List Terms](/developer/enterprise/api/v2/string-based/#tag/Glossaries/operation/api.glossaries.terms.getMany) String-based ## [Operators](#operators) [Section titled “Operators”](#operators) Main CroQL operators are listed below. Use and combine them to set specific conditions for retrieving the needed content from Crowdin. To form your CroQL query, you can use the elements from the tables below. ### [Arithmetic Operators](#arithmetic-operators) [Section titled “Arithmetic Operators”](#arithmetic-operators) The arithmetic operators are used to perform mathematic operations with any numeric data types. | Name | Symbol | Example | | -------------- | ------ | ------- | | Addition | + | 1 + 9 | | Subtraction | - | 11 - 1 | | Division | / | 20 / 2 | | Multiplication | \* | 2 \* 5 | | Negation | - | -10 | ### [Comparison Operators](#comparison-operators) [Section titled “Comparison Operators”](#comparison-operators) The comparison operators are used to compare values and return `true` or `false`. | Name | Symbol | Aliases | Example | | ---------------- | ---------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | | Between | `{{expression}} between {{expression}} and {{expression}}` | | 5 between 1 and 10 | | Equal | = | | 10 = 10 | | Not equal | != | ≠ | 1 != 10; 1 ≠ 10 | | Greater | > | | 10 > 1 | | Greater or equal | >= | ≥ | 10 >= 1; 10 ≥ 1 | | Less | < | | 1 < 10 | | Less or equal | <= | ≤ | 1 <= 10; 1 ≤ 10 | | Contains | `{{string}} contains {{string}}` | | `"Hello World" contains "Hello"`; `"Hello World" contains text`; `text contains "Hello World"`; `context contains text` | ### [Logical Operators](#logical-operators) [Section titled “Logical Operators”](#logical-operators) The logical operators are used to combine multiple boolean expressions or values and provide a single boolean output. | Name | Symbol | Example | | ---- | ------ | ----------------- | | And | and | 1 < 10 and 10 > 1 | | Or | or | 1 < 10 or 10 > 1 | | Xor | xor | 1 < 10 xor 10 > 1 | | Not | not | not 1 < 10 | ### [Filtration Operators](#filtration-operators) [Section titled “Filtration Operators”](#filtration-operators) The filtration operators are used to filter the objects based on the specified condition. | Name | Symbol | Example | | ------ | ------------------------------------ | ----------------------------------------- | | Filter | `{{collection}} where {{predicate}}` | `translations where (count of votes > 0)` | | Match | `{{object}} with {{predicate}}` | `user with (login = "crowdin")` | ### [Conditional (Ternary) Operator](#conditional-ternary-operator) [Section titled “Conditional (Ternary) Operator”](#conditional-ternary-operator) The ternary operator is used to check a condition specified in the first value, and if it’s `true` it returns the second value, but if it’s `false` it returns the third value. | Name | Symbol | Example | | ------- | ---------------------------------------------------------- | -------------------------------------- | | Ternary | `If {{condition}} then {{expression}} else {{expression}}` | `If 1 < 10 then "less" else "greater"` | ### [Fetch Operators](#fetch-operators) [Section titled “Fetch Operators”](#fetch-operators) The fetch operators are used for retrieving data from the objects. | Name | Symbol | Example | | ---------- | ------------------------------------------ | ----------------------------------- | | Mention | `@user:{{string}}`; `@language:{{string}}` | `@user:"crowdin"`; `@language:"en"` | | Member | `{{member}} of {{object}}` | `count of translations` | | Identifier | `{{identifier}}` | `text`; `identifier` | ### [Scalar Operators](#scalar-operators) [Section titled “Scalar Operators”](#scalar-operators) The scalar operators are used to declare values for further processing. | Name | Symbol | Example | | -------- | -------------- | ------------------------------ | | Integer | `{{integer}}` | 10 | | Float | `{{float}}` | 10.01 | | String | `{{string}}` | “crowdin” | | Datetime | `{{datetime}}` | ‘today’; ‘2021-03-16 00:00:00’ | ### [Group Operator](#group-operator) [Section titled “Group Operator”](#group-operator) The group operator is used to determine the execution order of operators. | Name | Symbol | Example | | ----- | ------ | ------------------------------ | | Group | ( ) | 1 < 10 and (20 > 10 or 10 > 5) | ## [CroQL Query Examples](#croql-query-examples) [Section titled “CroQL Query Examples”](#croql-query-examples) In this section, you can find practical examples of CroQL queries that will help you understand and use the querying capabilities within Crowdin. These examples can help you learn how to create your own queries to retrieve specific sets of data based on various criteria, such as translation status, user activity, and string properties. [CroQL Tester - CroQL expression editor and tester](https://store.crowdin.com/croql-tester)Try out your CroQL queries in the CroQL Tester. ### [Strings Queries](#strings-queries) [Section titled “Strings Queries”](#strings-queries) These queries are used to retrieve information about source strings. * Strings that have no Ukrainian translations with approvals or votes:
```graphql
count of translations where ( language = @language:"uk" and ( count of approvals > 0 or count of votes > 0 ) ) = 0
```
* Strings that have only one translation:
```graphql
count of translations = 1
```
* Strings that have translations from only one specific user:
```graphql
count of translations > 0 and count of translations = count of translations where (user = @user:"crowdin")
```
* Strings that have at least one translation not from specific users:
```graphql
count of translations where (user != @user:"crowdin") > 0
```
* Strings that have all translations not from specific users:
```graphql
count of translations > 0 and count of translations = count of translations where (user != @user:"crowdin")
```
* Strings filtered by identifier and numeric id of a file in your Crowdin project:
```graphql
identifier = "key" and id of file = 777
```
* Strings that have unresolved issues filtered by numeric id of a file in your Crowdin project:
```graphql
id of file = 777 and count of comments where (has unresolved issue) > 0
```
* Hidden strings that are not duplicates and have one or more translations:
```graphql
is hidden and not is duplicate and count of translations > 0
```
* Strings that have one or more approvals:
```graphql
count of languages summary where (approvalsCount >= 1) > 0
```
* Strings that have comments made by the user with a `crowdin` username:
```graphql
count of comments where (user = @user:"crowdin") > 0
```
* Strings that contain “ABC” in the source text but don’t contain “ABC” in their translations:
```graphql
text contains "ABC" and (count of translations where (text contains "ABC") = 0)
```
#### [Field Queries](#field-queries) [Section titled “Field Queries”](#field-queries) These queries are used to retrieve information about source strings based on the [Fields](/enterprise/fields/) assigned to them. * Strings with a specific field name and an exact field value: *Use case: Find strings where the “Priority” field is set to “High”.*
```graphql
count of fields where (name = "Priority" and value = "High") > 0
```
* Strings with a specific field slug and a value containing a specific word: *Use case: Find strings where the “category” slug contains the word “mobile”.*
```graphql
count of fields where (slug = "field-category" and value contains "mobile") > 0
```
* Strings that have a specific field applied by its name: *Use case: Find all strings that have a “Department” field assigned, regardless of the value.*
```graphql
count of fields where (name = "Department") > 0
```
* Strings that have a specific field applied by its slug: *Use case: Find strings using a specific system identifier for an “Owner” field.*
```graphql
count of fields where (slug = "field-internal-owner") > 0
```
* Strings that have a field whose name contains a specific word: *Use case: Find strings that have any “Status” related fields (e.g., Legal Status, Review Status).*
```graphql
count of fields where (name contains "Status") > 0
```
* Strings that have any field with a non-empty value: *Use case: Find strings that have any metadata filled in.*
```graphql
count of fields where (value != "") > 0
```
* Strings that have a text-type field with a non-empty value: *Use case: Useful for ensuring metadata comments or descriptions are actually filled in.*
```graphql
count of fields where (type = "text" and value != "") > 0
```
* Strings matching multiple specific field names where the value contains a specific word: *Use case: Check if either “Platform” or “Environment” fields contain the word “Production”.*
```graphql
count of fields where ((name = "Platform" or name = "Environment") and value contains "Production") > 0
```
### [Translation Queries](#translation-queries) [Section titled “Translation Queries”](#translation-queries) These queries are used to retrieve information about translations. * Translations made by the user with a `crowdin` username or ones with ≥ 100 upvotes:
```graphql
user = @user:"crowdin" or count of votes where ( is up ) >= 100
```
### [Translation Memory Segment Queries](#translation-memory-segment-queries) [Section titled “Translation Memory Segment Queries”](#translation-memory-segment-queries) These queries are used to retrieve information about TM segments. * Translation memory segments containing at least one record used one or more times:
```graphql
count of records where (usageCount > 0) > 0
```
* Translation memory segments containing at least one record created by the user with a `crowdin` username:
```graphql
count of records where (createdBy = @user:"crowdin") > 0
```
### [Glossary Term Queries](#glossary-term-queries) [Section titled “Glossary Term Queries”](#glossary-term-queries) These queries are used to retrieve information about glossary terms. * Terms created by the user with a `crowdin` username for Ukrainian:
```graphql
user = @user:"crowdin" and language = @language:"uk"
```
* Terms that contain “ABC” and have part of speech set to noun:
```graphql
text contains "ABC" and partOfSpeech = "noun"
```
* Terms marked as not recommended or obsolete:
```graphql
status = "not_recommended" or status = "obsolete"
```
* Terms that are of type abbreviation and have a note:
```graphql
type = "abbreviation" and note contains "tooltip"
```
* Terms added after a specific date:
```graphql
createdAt > '2024-12-01 00:00:00'
```
* Terms with lemma equal to “cancel” and in English:
```graphql
lemma = "cancel" and language = @language:"en"
```
* Terms that include a reference URL:
```graphql
url contains "https://"
```
### [Examples based on the Editor Advanced Filter Options](#examples-based-on-the-editor-advanced-filter-options) [Section titled “Examples based on the Editor Advanced Filter Options”](#examples-based-on-the-editor-advanced-filter-options) #### [Strings](#strings) [Section titled “Strings”](#strings) * Strings added:
```graphql
added between '2023-12-06 13:44:14' and '2023-12-07 13:44:14'
```
* Strings updated:
```graphql
updated between '2023-12-06 13:44:14' and '2023-12-07 13:44:14'
```
* String Type:
```graphql
type is plain or type is icu
```
* Comments:
```graphql
count of comments > 0
```
* Screenshots:
```graphql
count of screenshots > 0
```
* QA Issues:
```graphql
count of languages summary where (has qa issues) > 0
```
#### [Translations](#translations) [Section titled “Translations”](#translations) * Untranslated:
```graphql
count of languages summary = 0
```
* Partially translated (plurals):
```graphql
count of languages summary where (is partially translated) > 0
```
* Translated:
```graphql
count of languages summary where (is translated) > 0
```
* Translated by:
```graphql
count of translations where (user = @user:"crowdin") > 0
```
* Not Translated by:
```graphql
count of translations where (user != @user:"crowdin") > 0
```
* Translation Equals Source String:
```graphql
count of languages summary where (has translation as source) > 0
```
* Source Changed After Translation:
```graphql
count of languages summary where (is source changed after translation) > 0
```
* Translations updated:
```graphql
count of languages summary where ( translation updated between '2023-12-06 13:44:14' and '2023-12-07 13:44:14') > 0
```
* Votes:
```graphql
count of languages summary where (rating > 0) > 0
```
#### [Duplicates](#duplicates) [Section titled “Duplicates”](#duplicates) * Master strings:
```graphql
not is duplicate
```
* Duplicates only:
```graphql
is duplicate
```
* Duplicates with shared translations:
```graphql
is duplicate and count of languages summary where (has shared translation) > 0
```
* Duplicates with own translations:
```graphql
is duplicate and count of languages summary where (not has shared translation and is translated) > 0
```
#### [Approvals](#approvals) [Section titled “Approvals”](#approvals) * Translated, not approved:
```graphql
count of languages summary where (is translated and not is approved) > 0
```
* Partially approved (plurals):
```graphql
count of languages summary where (is partially approved) > 0
```
* Approved:
```graphql
count of languages summary where (is approved) > 0
```
* Approved by:
```graphql
count of translations where (count of approvals where (user = @user:"crowdin") > 0) > 0
```
* Not Approved by:
```graphql
count of translations where (count of approvals where (user != @user:"crowdin") > 0) > 0
```
* Translation Added After Approval:
```graphql
count of languages summary where (has translation after approval) > 0
```
#### [AI, MT, and TM](#ai-mt-and-tm) [Section titled “AI, MT, and TM”](#ai-mt-and-tm) * Translated by AI:
```graphql
count of languages summary where (is translated by ai) > 0
```
* Translated by MT:
```graphql
count of languages summary where (is translated by mt) > 0
```
* Translated by TM:
```graphql
count of languages summary where (is translated by tm) > 0
```
* Translated by AI, MT, or TM:
```graphql
count of languages summary where (is auto translated) > 0
```
#### [Other](#other) [Section titled “Other”](#other) * Pre translation: * Used: Not available * Not used: Not available * Sort by: Not available * Verbal Expressions: Not available * Search in: Not available ## [Context](#context) [Section titled “Context”](#context) CroQL can be used in the following contexts: source string context, translation context, and translation memory (TM) segment context. Use the following examples as a basis for building your CroQL queries. ### [Source String Context](#source-string-context) [Section titled “Source String Context”](#source-string-context)
```json
{
"type is plain": true,
"type is plural": false,
"type is icu": false,
"type is asset": false,
"text": "Quick Start",
"identifier": "quick_start",
"context": "quick_start",
"max length": 0,
"is visible": true,
"is hidden": false,
"is duplicate": false,
"isPassedWorkflow": true,
"fields": {
"priority": "High",
"legal-status": "approved"
},
"file": {
"id": 32,
"name": "sample.csv",
"title": "Sample",
"type": "csv",
"context": "Some useful context information"
},
"branch": {
"id": 7,
"name": "main",
"title": "Main"
},
"comments": [
{
"has issue": false,
"has unresolved issue": false,
"user": 1
}
],
"added": "2021-04-09 13:44:14"
}
],
"updated": "2021-04-09 10:23:17"
}
"has ftl syntax qa issues": false,
"has android syntax qa issues": false,
"has numbers mismatch qa issues": false,
"has ai qa issues": false,
"has outdated translation qa issues": false,
"has mdx syntax qa issues": false,
"has custom qa issues": false,
"rating": 1,
"approvalsCount": 1
}
],
"labels": [
{
"id": 1,
"title": "label title",
"is system": false
}
],
"added": "2021-04-08 12:33:27",
"updated": "2021-04-08 12:33:27"
}
```
| | | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type is plain` | **Type:** `boolean`**Description:** The source string with plain text. | | `type is plural` | **Type:** `boolean`**Description:** The source string with plural forms. | | `type is icu` | **Type:** `boolean`**Description:** The source string with ICU. | | `type is asset` | **Type:** `boolean`**Description:** The source string is an asset. | | `text` | **Type:** `string`**Description:** The source string text. | | `identifier` | **Type:** `string`**Description:** The source string identifier (key). | | `context` | **Type:** `string`**Description:** The source string context. | | `max length` | **Type:** `integer`**Description:** The source string max.length. | | `is visible` | **Type:** `boolean`**Description:** The source string is visible. | | `is hidden` | **Type:** `boolean`**Description:** The source string is hidden. | | `is duplicate` | **Type:** `boolean`**Description:** The source string is duplicate. | | `isPassedWorkflow` | **Type:** `boolean`**Description:** Crowdin Enterprise only. The source string passed through a project workflow. | | `fields` | **Type:** `object`**Description:** Crowdin Enterprise only. An object containing custom properties ([Fields](/enterprise/fields/)) assigned to a string. You can filter these by `name`, `slug`, `value`, and `type`. | | `file` | **Type:** `object`**Description:** The source string file. | | `branch` | **Type:** `object`**Description:** The source string branch. | | `comments` | **Type:** `array`**Description:** The source string comments. | | `has issue` | **Type:** `boolean`**Description:** The source string has an issue. | | `has unresolved issue` | **Type:** `boolean`**Description:** The source string has an unresolved issue. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added a comment. | | `screenshots` | **Type:** `array`**Description:** The source string screenshots. | | `translations` | **Type:** `array`**Description:** Translations of the source string. | | `text` | **Type:** `string`**Description:** Translation text. | | `plural form` | **Type:** `string`**Description:** Translation plural form. | | `is pre translated` | **Type:** `boolean`**Description:** Translation added via auto-translation. | | `provider` | **Type:** `string`**Allowed values:** `tm`, `global_tm`, `google`, `google_automl`, `microsoft`, `crowdin`, `deepl`, `modernmt`, `amazon`, `watson`, `custom_mt`, `ai`**Description:** Translation provided via translation memory, machine translation engine, or AI. | | `language` | **Type:** `string`**Description:** Language identifier specified as a string. Use the [language codes](/developer/language-codes/), for example, `“uk”` for Ukrainian. To specify in queries, use the format: `@language:“uk”`. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added a translation. | | `votes` | **Type:** `array`**Description:** Array of the votes added to the translation. | | `is up` | **Type:** `boolean`**Description:** Upvote. | | `is down` | **Type:** `boolean`**Description:** Downvote. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added a vote for translation. | | `added` | **Type:** `datetime`**Description:** Date when a vote for translation was added. | | `approvals` | **Type:** `array`**Description:** Array of the added translation approvals. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who approved a translation. | | `added` | **Type:** `datetime`**Description:** Date when a translation approval was added. | | `updated` | **Type:** `datetime`**Description:** Date when a translation was updated. | | `languages summary` | **Type:** `array`**Description:** The source string top translations (the translations with the highest priority). | | `language` | **Type:** `string`**Description:** Language identifier specified as a string. Use the [language codes](/developer/language-codes/), for example, `“uk”` for Ukrainian. To specify in queries, use the format: `@language:“uk”`. | | `is translated` | **Type:** `boolean`**Description:** The source string is translated. | | `is partially translated` | **Type:** `boolean`**Description:** The source string is partially translated. | | `is approved` | **Type:** `boolean`**Description:** The source string is approved. | | `is partially approved` | **Type:** `boolean`**Description:** The source string is partially approved. | | `translation updated` | **Type:** `boolean`**Description:** The source string translation is updated. | | `is auto translated` | **Type:** `boolean`**Description:** The source string is translated by TM, MT, or AI. | | `is translated by tm` | **Type:** `boolean`**Description:** The source string is translated by TM. | | `is translated by mt` | **Type:** `boolean`**Description:** The source string is translated by MT. | | `is translated by ai` | **Type:** `boolean`**Description:** The source string is translated by AI. | | `is source changed after translation` | **Type:** `boolean`**Description:** The source string changed after translation. | | `has translation as source` | **Type:** `boolean`**Description:** The source string has translation equal to source text. | | `has translation after approval` | **Type:** `boolean`**Description:** The source string has translation after approval. | | `has shared translation` | **Type:** `boolean`**Description:** The source string duplicate has shared translations from a master string. | | `has qa issues` | **Type:** `boolean`**Description:** The source string has QA issues. | | `has empty translation qa issues` | **Type:** `boolean`**Description:** The source string has empty translation QA issues. | | `has translation length qa issues` | **Type:** `boolean`**Description:** The source string has translation length QA issues. | | `has tags mismatch qa issues` | **Type:** `boolean`**Description:** The source string has tags mismatch QA issues. | | `has spaces mismatch qa issues` | **Type:** `boolean`**Description:** The source string has spaces mismatch QA issues. | | `has variables mismatch qa issues` | **Type:** `boolean`**Description:** The source string has variables mismatch QA issues. | | `has punctuation mismatch qa issues` | **Type:** `boolean`**Description:** The source string has punctuation mismatch QA issues. | | `has character case mismatch qa issues` | **Type:** `boolean`**Description:** The source string has character case mismatch QA issues. | | `has special characters mismatch qa issues` | **Type:** `boolean`**Description:** The source string has special characters mismatch QA issues. | | `has incorrect translation qa issues` | **Type:** `boolean`**Description:** The source string has incorrect translation QA issues. | | `has spelling qa issues` | **Type:** `boolean`**Description:** The source string has spelling QA issues. | | `has icu syntax qa issues` | **Type:** `boolean`**Description:** The source string has ICU syntax QA issues. | | `has terms qa issues` | **Type:** `boolean`**Description:** The source string has terms QA issues. | | `has duplicate translation qa issues` | **Type:** `boolean`**Description:** The source string has duplicate translation QA issues. | | `has ftl syntax qa issues` | **Type:** `boolean`**Description:** The source string has FTL syntax QA issues. | | `has android syntax qa issues` | **Type:** `boolean`**Description:** The source string has Android syntax QA issues. | | `has numbers mismatch qa issues` | **Type:** `boolean`**Description:** The source string has numbers mismatch QA issues. | | `has ai qa issues` | **Type:** `boolean`**Description:** The source string has AI QA issues. | | `has outdated translation qa issues` | **Type:** `boolean`**Description:** The source string has outdated translation QA issues. | | `has mdx syntax qa issues` | **Type:** `boolean`**Description:** The source string has MDX syntax QA issues. | | `has custom qa issues` | **Type:** `boolean`**Description:** The source string has Custom QA issues. | | `rating` | **Type:** `integer`**Description:** The source string translation rating. | | `approvalsCount` | **Type:** `integer`**Description:** The number of translation approvals. | | `labels` | **Type:** `array`**Description:** The source string labels. | | `id` | **Type:** `integer`**Description:** Numeric identifier of the label. | | `title` | **Type:** `string`**Description:** Label title. | | `is system` | **Type:** `boolean`**Description:** System label (label with source file name that is automatically added to strings in string-based projects). | | `added` | **Type:** `datetime`**Description:** Date when a source string was added. | | `updated` | **Type:** `datetime`**Description:** Date when a source string was updated. | ### [Translation Context](#translation-context) [Section titled “Translation Context”](#translation-context)
```json
{
"text": "Швидкий старт",
"plural form": "none",
"is pre translated": true,
"provider": "tm",
"user": 1,
"votes": [
{
"is up": true,
"is down": false,
"user": 2,
"added": "2021-04-09 13:44:14"
}
],
"approvals": [
{
"user": 2,
"added": "2021-04-09 13:44:14"
}
],
"updated": "2021-04-09 10:23:17"
}
```
| | | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `text` | **Type:** `boolean`**Description:** Translation text. | | `plural form` | **Type:** `string`**Description:** Translation plural form. | | `is pre translated` | **Type:** `boolean`**Description:** Translation added via auto-translation. | | `provider` | **Type:** `string`**Allowed values:** `tm`, `global_tm`, `google`, `google_automl`, `microsoft`, `crowdin`, `deepl`, `modernmt`, `amazon`, `watson`, `custom_mt`, `ai`**Description:** Translation provided via translation memory, machine translation engine, or AI. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added a translation. | | `votes` | **Type:** `array`**Description:** Array of the votes added to the translation. | | `is up` | **Type:** `boolean`**Description:** Upvote. | | `is down` | **Type:** `boolean`**Description:** Downvote. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added a vote for translation. | | `added` | **Type:** `datetime`**Description:** Date when a vote for translation was added. | | `approvals` | **Type:** `array`**Description:** Array of the added translation approvals. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who approved a translation. | | `added` | **Type:** `datetime`**Description:** Date when a translation approval was added. | | `updated` | **Type:** `datetime`**Description:** Date when a translation was updated. | ### [Translation Memory (TM) Segment Context](#translation-memory-tm-segment-context) [Section titled “Translation Memory (TM) Segment Context”](#translation-memory-tm-segment-context)
```json
{
"records": [
{
"id": 1,
"text": "Перекладений текст",
"usageCount": 77,
"createdBy": 1,
"updatedBy": 1,
"createdAt": "2027-09-16T13:48:04+00:00",
"updatedAt": "2027-09-16T13:48:04+00:00"
}
]
}
```
| | | | ------------ | ----------------------------------------------------------------------------------------------------------- | | `records` | **Type:** `array`**Description:** Array of translation memory segment records. | | `id` | **Type:** `integer`**Description:** Numeric identifier of a record. | | `text` | **Type:** `string`**Description:** Translation text of a record. | | `usageCount` | **Type:** `integer`**Description:** The number of times a translation memory record has been used. | | `createdBy` | **Type:** `integer`**Description:** Numeric identifier of the user who created a translation memory record. | | `updatedBy` | **Type:** `integer`**Description:** Numeric identifier of the user who updated a translation memory record. | | `createdAt` | **Type:** `datetime`**Description:** Date when a translation memory record was created. | | `updatedAt` | **Type:** `datetime`**Description:** Date when a translation memory record was updated. | ### [Glossary Concept Context](#glossary-concept-context) [Section titled “Glossary Concept Context”](#glossary-concept-context)
```json
{
"id": 101,
"user": 12,
"subject": "User Interfact",
"definition": "A command used to save user progress in the application.",
"url": "https://example.com/concept/save",
"note": "Commonly used in forms and toolbars.",
"translatable": true,
"createdAt": "2027-09-16T13:48:04+00:00",
"updatedAt": "2027-09-16T13:48:04+00:00"
}
```
| | | | -------------- | -------------------------------------------------------------------------------------------------------------------------- | | `id` | **Type:** `integer`**Description:** Numeric identifier of the glossary concept. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who created the concept. | | `subject` | **Type:** `string`**Description:** Domain or area of knowledge the concept belongs to (e.g., UI, development, healthcare). | | `definition` | **Type:** `string`**Description:** General explanation or definition of the concept. | | `url` | **Type:** `string`**Description:** URL linking to a resource with more information about the concept. | | `note` | **Type:** `string`**Description:** Additional information or clarification for translators. | | `translatable` | **Type:** `boolean`**Description:** Indicates whether the concept can be translated into other languages. | | `createdAt` | **Type:** `datetime`**Description:** Date and time when the concept was created. | | `updatedAt` | **Type:** `datetime`**Description:** Date and time when the concept was last updated. | ### [Glossary Term Context](#glossary-term-context) [Section titled “Glossary Term Context”](#glossary-term-context)
```json
{
"id": 301,
"text": "Cancel",
"description": "A command to stop or abort an operation.",
"language": "en",
"user": 42,
"partOfSpeech": "verb",
"status": "admitted",
"type": "abbreviation",
"gender": "neuter",
"note": "Often used in confirmation dialogs.",
"lemma": "cancel",
"url": "https://example.com/term/cancel",
"concept": 101,
"createdAt": "2027-09-16T13:48:04+00:00",
"updatedAt": "2027-09-16T13:48:04+00:00"
}
```
| | | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | **Type:** `integer`**Description:** Numeric identifier of the glossary term. | | `text` | **Type:** `string`**Description:** The term itself, in the specified language. | | `description` | **Type:** `string`**Description:** Additional explanation or meaning of the term. | | `language` | **Type:** `string`**Description:** Language identifier specified as a string. Use the [language codes](/developer/language-codes/), for example, `“uk”` for Ukrainian. To specify in queries, use the format: `@language:“uk”`. | | `user` | **Type:** `integer`**Description:** Numeric identifier of the user who added the term. | | `partOfSpeech` | **Type:** `string`**Allowed values:** `noun`, `verb`, `adj`, `pron`, `propn`, `det`, `adv`, `adp`, `cconj`, `sconj`, `num`, `intj`, `aux`, `prt`, `sym`, `x`**Description:** The grammatical category of the term. | | `status` | **Type:** `string`**Allowed values:** `preferred`, `admitted`, `not_recommended`, `obsolete`, `draft`**Description:** Indicates the approval or usage level of the term. | | `type` | **Type:** `string`**Allowed values:** `full_form`, `acronym`, `abbreviation`, `short_form`, `phrase`, `variant`**Description:** Classification of the term by structure or usage. | | `gender` | **Type:** `string`**Allowed values:** `masculine`, `feminine`, `neuter`, `common`, `other`**Description:** Grammatical gender of the term, if applicable. | | `note` | **Type:** `string`**Description:** Additional translator guidance or term-related notes. | | `lemma` | **Type:** `string`**Description:** The base form of the term. | | `url` | **Type:** `string`**Description:** Reference link for the term. | | `concept` | **Type:** `integer`**Description:** ID of the concept the term belongs to. | | `createdAt` | **Type:** `datetime`**Description:** Date and time the term was created. | | `updatedAt` | **Type:** `datetime`**Description:** Date and time the term was last updated. |
# About Crowdin Apps
> Extend Crowdin and Crowdin Enterprise with your own apps: add features to the interface, connect external services, and process content.
 Build a Crowdin app with your AI coding agent:
```text
Help me build a Crowdin app. Do the following:
1. Install the Crowdin plugin with npx plugins add crowdin/skills.
2. Ask me what the app should do, then use the create-app skill to scaffold, develop, and publish it.
3. After publishing, open a preview so I can see the app inside Crowdin.
```
The agent signs in to Crowdin in your browser and asks you to confirm before it registers the app in your organization. The published app is installed in your account, and the manifest sets who else can use it. You change that later in [Crowdin Apps Permission Configuration](/developer/crowdin-apps-installation/#crowdin-apps-permission-configuration). Read more about [Crowdin Skills](/developer/crowdin-skills/). To an end user, an installed app appears as a fully integrated part of Crowdin, with its features delivered straight to the UI. ## [Choose How to Build](#choose-how-to-build) [Section titled “Choose How to Build”](#choose-how-to-build) There are two ways to build a Crowdin app. Serverless apps run on Crowdin infrastructure: you write the UI, and Crowdin hosts and runs the app. Self-hosted apps run on a server you operate, which lets them use every module type, including those with server-side logic. Neither approach replaces the other. Pick the one that matches what your app does. Serverless Apps Build dashboards, panels, and other UI modules that run on Crowdin infrastructure. You scaffold, preview, and publish the app from the CLI, with no server to deploy or maintain. Choose this path when your app is all interface: it displays data, extends the Crowdin UI, and calls the Crowdin API on behalf of the person using it. [Get started with the Serverless Apps SDK](/developer/crowdin-apps-serverless/) Self-Hosted Apps Build an app backed by your own server, using any language and framework. This path supports every module type, including file processing, AI providers, and custom MT engines. Choose this path when your app needs server-side logic: it processes files, reacts to webhooks, connects external services, or acts with the permissions of the account that installed it. [Get started with the Crowdin Apps SDK](/developer/crowdin-apps-sdk/) | | Serverless Apps | Self-Hosted Apps | | --------- | ----------------------------------------------------------- | ------------------------------------------ | | Hosting | Crowdin runs your app | You deploy and run your app | | Modules | UI modules | All module types | | API calls | Made as the user working in the app | Made as the account that installed the app | | Stack | TypeScript, with React and the Crowdin UI Kit preconfigured | Any language and framework | If your app combines an interface with server-side logic, build it as a self-hosted app. Self-hosted apps can include UI modules as well. To build a self-hosted app in a language other than JavaScript or TypeScript, follow the [App Descriptor](/developer/crowdin-apps-app-descriptor/) and [Security for Crowdin Apps](/developer/crowdin-apps-security/) references. ## [Explore the Docs](#explore-the-docs) [Section titled “Explore the Docs”](#explore-the-docs) [Serverless Apps SDK](/developer/crowdin-apps-serverless/)The toolkit for UI apps that Crowdin hosts and runs. [Crowdin Apps SDK](/developer/crowdin-apps-sdk/)The framework that builds a self-hosted app around your configuration. [UI Modules](/developer/crowdin-apps-modules-ui/)Panels, dashboards, menus, and other placements in the Crowdin UI. [AI Modules](/developer/crowdin-apps-modules-ai/)Custom AI providers, prompt providers, and processors for AI requests. [File Processing Modules](/developer/crowdin-apps-modules-file-processing/)Custom file formats, import and export processing, and bundle generators. [App Installation](/developer/crowdin-apps-installation/)How apps are installed and who in your account can use them. [Crowdin API](/developer/api/)Endpoints for files and strings, translations, TMs, glossaries, and reports. [App Descriptor](/developer/crowdin-apps-app-descriptor/)The JSON file that declares what your app is and which modules it adds. ## [Examples of Crowdin Apps](#examples-of-crowdin-apps) [Section titled “Examples of Crowdin Apps”](#examples-of-crowdin-apps) Start from a template instead of an empty project: * [Serverless Apps Starter Kit](https://github.com/crowdin-community/serverless-apps-starter-kit): The templates the serverless CLI scaffolds, and a complete serverless app to read end to end. * [Crowdin AI Provider Template](https://github.com/crowdin-community/crowdin-ai-provider-template): A self-hosted app that adds a custom AI provider, built with the Crowdin Apps SDK. * [Crowdin Custom MT Template](https://github.com/crowdin-community/crowdin-custom-mt-template): A self-hosted app that adds a custom machine translation engine. * [Crowdin Custom File Format Boilerplate](https://github.com/crowdin-community/crowdin-file-format-template): A file processing app that adds INI, CONF, and CFG support, with a preview for translators. ## [Publishing to the Crowdin Store](#publishing-to-the-crowdin-store) [Section titled “Publishing to the Crowdin Store”](#publishing-to-the-crowdin-store) When the app is ready to share, submit it to the [Crowdin Store](https://store.crowdin.com), where other users can find and install it. Read more about [Publishing Your App](/developer/crowdin-apps-publishing/). 3M+ Registered users 250k+ Projects 15k+ Active project owners Need Help? Our team can help with the technical side of your app, and once it is ready, with getting it in front of Crowdin customers. [Contact Support](https://crowdin.com/contacts) ## [Case Study](#case-study) [Section titled “Case Study”](#case-study) See how the LexiQA integration was built as a Crowdin app: [Play](https://youtube.com/watch?v=V1tS-GeriIA)
# App Descriptor
> Declare your app and its modules in a JSON file
The app descriptor is a JSON file, usually `manifest.json`, that tells Crowdin what the app is and which modules it adds.  ## [App Descriptor Structure](#app-descriptor-structure) [Section titled “App Descriptor Structure”](#app-descriptor-structure) The app descriptor is a JSON object. A self-hosted app serves it from its own server, and a serverless app keeps it in the project and pushes it to Crowdin with the CLI. ### [Self-Hosted App](#self-hosted-app) [Section titled “Self-Hosted App”](#self-hosted-app) manifest.json
```json
{
"identifier": "your-application-identifier",
"name": "Your Application",
"description": "Application description",
"logo": "/assets/logos/app-logo.png",
"baseUrl": "https://example.com",
"authentication": {
"type": "crowdin_app",
"clientId": "your-client-id"
},
"events": {
"installed": "/hooks/installed"
},
"scopes": [
"project"
],
"modules": {
"project-integrations": [
{
"key": "your-module-key",
"name": "Module Name",
"description": "Module description",
"logo": "/assets/logos/module-logo.png",
"url": "/page/integration",
"environments": [
"crowdin", "crowdin-enterprise"
]
}
]
}
}
```
### [Serverless App](#serverless-app) [Section titled “Serverless App”](#serverless-app) A serverless descriptor has no `baseUrl`, declares UI modules without a `url`, and leaves out `events`, which point at URLs on a server of your own. manifest.json
```json
{
"identifier": "your-application-identifier",
"name": "Your Application",
"description": "Application description",
"bundle": {
"mode": "internal"
},
"scopes": [
"project"
],
"modules": {
"project-menu": [
{
"key": "your-module-key",
"name": "Module name"
}
]
}
}
```
### [Fields](#fields) [Section titled “Fields”](#fields) | Property | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `identifier` | **Type:** `string`**Required:** yes**Description:** A unique key to identify the app, 255 characters or fewer. | | `name` | **Type:** `string`**Required:** yes**Description:** The human-readable name of the app. | | `baseUrl` | **Type:** `string` (`uri`)**Required:** yes (self-hosted apps only)**Description:** The base URL of the remote app, used for all communication back to it. A descriptor without a `baseUrl` describes a serverless app, which Crowdin hosts and runs. | | `bundle` | **Type:** `object`**Description:** Serverless apps only. Where Crowdin serves the app from. Set `mode` to `internal` for the bundle uploaded to Crowdin, or to `external` with a `url` that points to a local development server. | | `authentication` | **Type:** `Authentication`**Description:** The authentication type used when signing requests between Crowdin and the app. Defaults to `none`, which is the type serverless apps use. | | `description` | **Type:** `string`**Description:** The human-readable description of what the app does. It is visible in the Crowdin UI. | | `logo` | **Type:** `string` (`relativeUri`)**Description:** The image URL relative to the app’s base URL, displayed in the Crowdin UI. | | `events` | **Type:** `Events`**Description:** Callbacks for app event notifications (self-hosted apps only). | | `scopes` | **Type:** \[`string`, … ]**Required:** yes**Description:** Set of [scopes](/developer/understanding-scopes/) requested by this app. | | `modules` | **Type:** `object`**Required:** yes**Description:** The list of modules this app provides. Serverless apps provide UI modules. | | `default_permissions` | **Type:** `object`**Description:** Who can use the app right after installation. `user` accepts `owner`, `managers`, `all`, or `guests`, and `project` accepts `own` or `restricted`. | | `stringBasedAvailable` | **Type:** `boolean`**Description:** Whether the app’s modules are available in string-based projects. In file-based projects they are always available. | The `baseUrl` must start with `https://`, and it cannot change after the app is installed without uninstalling the app first, so choose it before you make the app public. All other URLs in the manifest, such as `logo`, `events`, and module URLs, are relative to `baseUrl`. Start each of them with `/`. Crowdin removes a trailing `/` from `baseUrl`, so `https://example.com/` and `https://example.com` work the same. ## [Authentication](#authentication) [Section titled “Authentication”](#authentication) Specifies the authentication type Crowdin uses when it signs requests to the app: * **`crowdin_app`**: Through an OAuth app. Use it when the app calls the Crowdin API on its own, at any time. * **`none`**: Without an OAuth app. The app still reaches the API, but only while it runs on the user side, for example while its iframe is open. Serverless apps use this type. * **`crowdin_agent`**: Through a dedicated bot user that Crowdin creates when the app is installed, so the app keeps working in projects afterwards, for example on a custom workflow step. The app’s API calls are authenticated as that user. Required by the [Workflow Step Type](/developer/crowdin-apps-module-workflow-step-type/#authentication) module. Read more about [Security for Crowdin Apps](/developer/crowdin-apps-security/). Example: manifest.json
```json
{
"authentication": {
"type": "crowdin_app",
"clientId": "your-client-id"
}
}
```
| Field | Type | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------- | | `type` | `string` | The type of authentication to use: `none` (the default), `crowdin_app`, or `crowdin_agent`. | | `clientId` | `string` | OAuth client id for authorization via the `crowdin_app` type. | ## [Modules](#modules) [Section titled “Modules”](#modules) Modules are how an app extends Crowdin. Each type declares where the app appears in the interface or what it processes: * **UI modules**: Panels, dashboards, menus, and dialogs inside Crowdin. * **AI modules**: Custom AI providers, prompt providers, and processors for AI requests. * **File processing modules**: Custom file formats, and processing before or after import and export. * **Other modules**: Custom machine translation engines, webhooks, workflow steps, QA checks, and spellcheckers. Read more about [UI Modules](/developer/crowdin-apps-modules-ui/), [AI Modules](/developer/crowdin-apps-modules-ai/), and [File Processing Modules](/developer/crowdin-apps-modules-file-processing/). ## [Events](#events) [Section titled “Events”](#events) A self-hosted app registers callbacks for events that happen in the workspace. Each property in this object is a URL relative to the app’s `baseUrl`, and Crowdin sends a POST request to it when the event fires. The `installed` callback is part of the installation itself, and the rest behave like webhooks. Example: manifest.json
```json
{
"events": {
"installed": "/hooks/installed",
"uninstall": "/hooks/uninstall"
}
}
```
| Event | Type | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `installed` | `string` | Sent to the app after a user installs it in Crowdin. Required with the `crowdin_app` and `crowdin_agent` [authentication](#authentication) types. | | `uninstall` | `string` | Sent to the app before it is uninstalled from Crowdin. | ### [Installed Event Payload](#installed-event-payload) [Section titled “Installed Event Payload”](#installed-event-payload) Crowdin sends the `installed` event to the app when a user installs it. The payload describes the workspace or profile the app was installed to, the app itself, and the credentials the app needs to fetch an API token. Read more about [Installed Event Flow](/developer/crowdin-apps-installation/#installed-event-communication-flow). Payload example:
```json
{
"appId": "your-application-identifier",
"appSecret": "dbfg....asdffgg",
"clientId": "your-client-id",
"userId": 1,
"organizationId": 1,
"domain": null,
"baseUrl": "https://crowdin.com"
}
```
```json
{
"appId": "your-application-identifier",
"appSecret": "dbfg....asdffgg",
"clientId": "your-client-id",
"userId": 1,
"organizationId": 1,
"domain": "{domain}",
"baseUrl": "https://{domain}.crowdin.com"
}
```
Properties: | Field | Type | Description | | ---------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `appId` | `string` | The identifier of the app that is declared in the app descriptor file. | | `appSecret` | `string` | The unique secret used for authorization of your Crowdin app. | | `clientId` | `string` | The OAuth client identifier that is declared in the app descriptor file. | | `userId` | `integer` | The numeric identifier of the user that installed the app. | | `organizationId` | `integer` | The numeric identifier of the organization the app was installed to. | | `domain` | `string` | The name of the organization in Crowdin Enterprise the app was installed to. In Crowdin the value is always `null`. | | `baseUrl` | `string` | The `baseUrl` of the organization in Crowdin Enterprise the app was installed to. In Crowdin the value is always `https://crowdin.com`. | | `agentId` | `integer` | The numeric identifier of the agent user created for the app. Sent only for apps with the `crowdin_agent` authentication type. | ### [Uninstall Event Payload](#uninstall-event-payload) [Section titled “Uninstall Event Payload”](#uninstall-event-payload) Crowdin sends the `uninstall` event to the app before the app is removed. Like the `installed` event, the payload describes the workspace or account the app was installed to and the app itself. Use it to remove the data you stored for that workspace or account. Payload example:
```json
{
"appId": "your-application-identifier",
"clientId": "your-client-id",
"organizationId": 1,
"domain": null,
"baseUrl": "https://crowdin.com"
}
```
```json
{
"appId": "your-application-identifier",
"clientId": "your-client-id",
"organizationId": 1,
"domain": "{domain}",
"baseUrl": "https://{domain}.crowdin.com"
}
```
Properties: | Field | Type | Description | | ---------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `appId` | `string` | The identifier of the app that is declared in the app descriptor file. | | `clientId` | `string` | The OAuth client identifier that is declared in the app descriptor file. | | `organizationId` | `integer` | The numeric identifier of the organization the app was uninstalled from. | | `domain` | `string` | The name of the organization in Crowdin Enterprise the app was uninstalled from. In Crowdin the value is always `null`. | | `baseUrl` | `string` | The `baseUrl` of the organization in Crowdin Enterprise the app was uninstalled from. In Crowdin the value is always `https://crowdin.com`. |
# App Installation
> Learn how to install and configure Crowdin Apps from the Crowdin Store or manually
You can install Crowdin Apps either from the [Crowdin Store](https://store.crowdin.com) or manually, depending on whether the app is already published. ## [Installation in Crowdin](#installation-in-crowdin) [Section titled “Installation in Crowdin”](#installation-in-crowdin) To install an app that is already published on the Crowdin Store, follow these steps: 1. Open your profile home page and select **Store** on the left sidebar. 2. Click **Install** on the needed app.  3. In the appeared dialog, configure preferred permissions and click **Install**. To install a private app, follow these steps: 1. Go to **Account Settings > Apps** and click **Install Private App**. 2. Select how you want to install the app and click **Install**: * **From a manifest URL** – for self-hosted apps that run on your own server. Paste in the app’s **Manifest URL**. * **From a manifest file** – for [serverless apps](/developer/crowdin-apps-serverless/) that Crowdin hosts. Upload the app’s manifest file (`.json`). 3. Configure preferred permissions. If you chose **From a manifest file**, also select the **App source**: * **Production bundle** – upload the app’s build as a `.zip` bundle. Crowdin hosts it and serves it to your users. * **Development URL** – load the app from a URL while you develop it, for example from a local dev server. 4. Click **Install**. ## [Installation in Crowdin Enterprise](#installation-in-crowdin-enterprise) [Section titled “Installation in Crowdin Enterprise”](#installation-in-crowdin-enterprise) To install an app that is already published on the Crowdin Store, follow these steps: 1. Open your organization’s **Workspace** and select **Store** on the left sidebar. 2. Click **Install** on the needed app.  3. In the appeared dialog, configure preferred permissions and click **Install**. To install a private app, follow these steps: 1. Go to **Organization Settings > Apps** and click **Install Private App**. 2. Select how you want to install the app and click **Install**: * **From a manifest URL** – for self-hosted apps that run on your own server. Paste in the app’s **Manifest URL**. * **From a manifest file** – for [serverless apps](/developer/crowdin-apps-serverless/) that Crowdin hosts. Upload the app’s manifest file (`.json`). 3. Configure preferred permissions. If you chose **From a manifest file**, also select the **App source**: * **Production bundle** – upload the app’s build as a `.zip` bundle. Crowdin hosts it and serves it to your users. * **Development URL** – load the app from a URL while you develop it, for example from a local dev server. 4. Click **Install**. ## [Crowdin Apps Permission Configuration](#crowdin-apps-permission-configuration) [Section titled “Crowdin Apps Permission Configuration”](#crowdin-apps-permission-configuration) Configure preferred permissions for each app during installation. In this step, you define who can access and use the app across its modules, and in which projects of your Crowdin account (for Crowdin) or Crowdin organization (for Crowdin Enterprise) it can be used. If you restrict access to certain projects by using the **Selected projects** option, the app cannot communicate via the API with projects outside that list. The app is also displayed only in the UI of the selected projects. You can configure these access permissions during installation or adjust them at any time for already installed apps: go to **Account Settings > Apps** (for Crowdin) or **Organization Settings > Apps** (for Crowdin Enterprise) and select **Edit** on the app. ### [User Access Categories](#user-access-categories) [Section titled “User Access Categories”](#user-access-categories) You can define which user categories can use the app. Configure this setting separately for each app module, so a module can have different access from the others. Available options for Crowdin: * Only me – the user who installed the app. * Me, project managers and developers / translation requestors * All project members * All users, including guests (unauthenticated users) * Custom Access – the users you pick with **Select users**. The module switches to this option automatically once you pick users. Available options for Crowdin Enterprise: * Only admins * Admins, managers and developers / translation requestors * All project members * All users, including guests (unauthenticated users) * Custom Access – the users you pick with **Select users**. The module switches to this option automatically once you pick users. ### [Project Access Configuration](#project-access-configuration) [Section titled “Project Access Configuration”](#project-access-configuration) You can also specify the projects in which the app can be used (these settings apply across all app modules). Project access options: * All projects I have access to (for Crowdin) or All projects (for Crowdin Enterprise) * Selected projects ## [Installed Event Communication Flow](#installed-event-communication-flow) [Section titled “Installed Event Communication Flow”](#installed-event-communication-flow) This flow applies to self-hosted apps, which register the `installed` event in their app descriptor. When a Crowdin App is installed in the Account Settings, an authorization flow takes place, during which Crowdin and the Crowdin App exchange the authorization data (the authorization code is exchanged for an access token). The following illustration shows the events in this process.  Let’s examine each step in the illustration in detail: 1. Installation of the Manifest URL - the user pastes in the Manifest URL in **Account Settings > Apps** and clicks **Install**. 2. Fetching content from the Manifest URL - the request is sent to the Crowdin App. 3. Response: manifest JSON - the Crowdin App returns the Manifest JSON that contains data about the app. 4. Manifest Data Validation - the received content is validated against the structure and data of the Manifest JSON. 5. Prompt to install - Crowdin displays the information about the Crowdin App, the list of permissions, and the **Install** button to the user. 6. Confirmation of the installation - the user confirms the installation of the Crowdin App. 7. The Installed event - Crowdin sends the Installed event with the authorization code to the Crowdin App so it can generate an API token. 8. Token request - the Crowdin App sends a request to acquire an API token:
```plaintext
POST https://accounts.crowdin.com/oauth/token
```
9. Access Token - the authorization service returns the API access token and the refresh token. 10. Success Code Response - the Crowdin App returns the success status code (2xx), which confirms that the app installation finished. If the status code is different, the app will be removed from the Crowdin account. **Token request parameters (step 8):** | | | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `grant_type: crowdin_app` | **Type:** `string`**Required:** yes**Description:** Specifies the flow for an OAuth app. | | `client_id` | **Type:** `string`**Required:** yes**Description:** The Client ID for the app is received when the app is registered. | | `client_secret` | **Type:** `string`**Required:** yes**Description:** The Client Secret for the app is received when the app is registered. | | `app_id` | **Type:** `string`**Required:** yes**Description:** Crowdin app identifier from the app descriptor. | | `app_secret` | **Type:** `string`**Required:** yes**Description:** The unique secret used to authorize your Crowdin app. This value is retrieved from the installed event. | | `domain` | **Type:** `string\|null`**Required:** yes**Description:** The name of the organization from which the app is accessed. This value is retrieved from the installed event. | | `user_id` | **Type:** `integer`**Required:** yes**Description:** The identifier of the user who installed the app. This value is retrieved from the installed event. |
# Crowdin Apps JS
> Utilize the Crowdin Apps JS library to interact with Crowdin
The Crowdin Apps JS is a JavaScript library that enables communication between your app and the Crowdin UI. Since all Crowdin Apps run inside a sandboxed `