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) [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) [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) [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) [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) [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) [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) [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, the app developed for iOS and Android platforms share most of the source strings for iOS and Android files. So once you select the **Hide** option, the system will detect the duplicate strings for both types of files (iOS and Android) and hide them while keeping visible only the strings in the files uploaded first. Once the string that was uploaded first is translated, the hidden duplicate will get this translation automatically due to the selected **Hide** option. This way, translators will translate only the unique visible texts. When the translation of the visible string is updated, its hidden duplicates will also get 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. ## [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 | **Strongly recommended.** A Base64-URL-encoded SHA-256 hash of the `code_verifier`. Used to secure the authorization flow with PKCE. Required if `code_challenge_method` is provided. 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`. Required if `code_challenge` is provided. | 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 ``` ## [Users Are Redirected Back to Your Site by Crowdin](#users-are-redirected-back-to-your-site-by-crowdin) [Section titled “Users Are Redirected Back to Your Site by Crowdin”](#users-are-redirected-back-to-your-site-by-crowdin) 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.** You receive Client Secret for the app when you register it. This parameter is not required if you’re using PKCE (i.e., providing a `code_verifier`). | | `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 | **Strongly recommended.** The original random string used to generate the `code_challenge`. Required if a `code_challenge` was sent in the initial authorization request. | 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 [JWT Token Structure](/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.** You receive Client Secret for the app when you register it. | | `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 Enterprise. 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 Enterprise 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 translations are committed the default message is shown `New translations {fileName} ({languageName})`. You can use the `commit_message` parameter to add Git tags (e.g., to skip builds). crowdin.yml ```yml "commit_message": "[ci skip]" ``` To replace the default commit message, use the `append_commit_message` parameter with the `false` value. You can also add two optional placeholders: `%original_file_name%` and `%language%` to use the appropriate file name and language variables accordingly. crowdin.yml ```yml "commit_message": "Fix: New translations %original_file_name% from Crowdin" "append_commit_message": false ``` ### [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/export/) * [Language mapping in Crowdin Enterprise](/enterprise/project-settings/export/) ## [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 ``` * Same as source string: ```graphql count of languages summary where (has translation as source) > 0 ``` * Modified source String: ```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 ``` * Has translations after approval: ```graphql count of languages summary where (has translation after approval) > 0 ``` #### [TM and MT](#tm-and-mt) [Section titled “TM and MT”](#tm-and-mt) * 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 TM or MT: ```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 * Verbal Expression Scope: 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 android 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 pre-translation. | | `provider` | **Type:** `string`**Allowed values:** `tm`, `global_tm`, `google`, `google_automl`, `microsoft`, `crowdin`, `deepl`, `modernmt`, `amazon`, `watson`, `custom_mt`**Description:** Translation provided via translation memory or machine translation engine. | | `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 or MT. | | `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 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 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 pre-translation. | | `provider` | **Type:** `string`**Allowed values:** `tm`, `global_tm`, `google`, `google_automl`, `microsoft`, `crowdin`, `deepl`, `modernmt`, `amazon`, `watson`, `custom_mt`**Description:** Translation provided via translation memory or machine translation engine. | | `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
> Join the growing localization management platform! Build apps for all the teams already using Crowdin or Crowdin Enterprise to customize and extend localization experience.
 3M+ Registered users 250k+ Projects 15k+ Active project owners By creating Crowdin apps, developers can integrate existing services with Crowdin, add new features, upload and manage content. Crowdin apps are web applications that function remotely via HTTP. To an end user, an app appears as a fully integrated part of Crowdin. Once your app is installed, its features are delivered straight to the Crowdin UI. You can develop a Crowdin app using any of the preferred programming languages and web frameworks, and deploy it in many different ways. From massive SaaS services to static apps served right from a code repo, Crowdin apps are designed to let you connect anything to Crowdin.  ## [Creating Crowdin Apps](#creating-crowdin-apps) [Section titled “Creating Crowdin Apps”](#creating-crowdin-apps) The development of Crowdin App starts with creating an app descriptor. The app descriptor is a JSON file that describes the interaction of the app with Crowdin. The descriptor includes general information for the app, as well as the modules that the app will be using or extending. Basically, the descriptor is a middle ground between the remote app and Crowdin. When a Crowdin account owner installs an app, what they are really doing is installing this descriptor file, which contains pointers to your app. Read more about [App Descriptor](/developer/crowdin-apps-app-descriptor/). The next step would be to implement the app functionality according to the app descriptor which implies the following steps: Step 1 Event listeners implementation – the usage of webhooks which are triggered by Crowdin to perform certain actions on the app side (installation of the app, app removal, etc). Step 2 Modules implementation – module usage in the Crowdin apps. Modules are the functional parts integrated into the apps with help of which apps extend Crowdin and interact with it. Read more about [UI Modules](/developer/crowdin-apps-modules-ui/) and [File Processing Modules](/developer/crowdin-apps-modules-file-processing/). ## [Using Crowdin APIs in Crowdin Apps](#using-crowdin-apis-in-crowdin-apps) [Section titled “Using Crowdin APIs in Crowdin Apps”](#using-crowdin-apis-in-crowdin-apps) Crowdin Apps communicate with Crowdin using our RESTful APIs. You can use the Crowdin APIs in Crowdin apps you develop for Crowdin, as well as in scripts, API clients, or other methods of making calls. Our APIs allow you to manage Crowdin TMs, glossaries, source content (files and strings), translations, branches, etc. You can use the APIs to upload source files, export translations, as well as for user management, generating reports, and more. [API Overview ](/developer/api/)Explore the Crowdin API documentation to learn more about the available endpoints and how to use them. Need Help? We support all developers who help us improve our product and add interesting developments to our community. [Contact Support ](https://crowdin.com/contacts) ## [Using JS API in Crowdin Apps](#using-js-api-in-crowdin-apps) [Section titled “Using JS API in Crowdin Apps”](#using-js-api-in-crowdin-apps) For improved interaction between the Crowdin app and Crowdin, you can use our library that provides cross-window communication. The library simplifies the interaction with the Crowdin interface, allows you to get additional information from the page where the application was opened, or manipulate certain UI elements of the page directly from the application. Read more about [Crowdin Apps JS](/developer/crowdin-apps-js/). ## [Publishing Your App](#publishing-your-app) [Section titled “Publishing Your App”](#publishing-your-app) After creating and testing your app, the next thing you need to do is to publish it into the cloud or any public server so that it’s always accessible to Crowdin and other users. Read more about [Publishing Your App](/developer/crowdin-apps-publishing/). When you’re ready to share your app you can submit your app to the [Crowdin Store](https://store.crowdin.com). This allows other users to install and run your app(s). ## [Examples of Crowdin Apps](#examples-of-crowdin-apps) [Section titled “Examples of Crowdin Apps”](#examples-of-crowdin-apps) Before you start developing your own Crowdin apps, you can take a look at a few examples. They showcase the integration between Crowdin Enterprise and external services like Mailchimp and SendGrid. Read more about [Crowdin Mailchimp Example](https://github.com/crowdin-community/crowdin-mailchimp-example) and [Crowdin SendGrid Example](https://github.com/crowdin-community/crowdin-sendgrid-example). ## [Assistance from Our Team](#assistance-from-our-team) [Section titled “Assistance from Our Team”](#assistance-from-our-team) Our team is ready to help you with the technical implementation of your app. Once your app is ready we’ll discuss how we can help you with exposure to our customers. For any guidance from our team contact us at [](mailto:support@crowdin.com). ## [Case Study](#case-study) [Section titled “Case Study”](#case-study) [Play](https://youtube.com/watch?v=V1tS-GeriIA)
# App Descriptor
> Learn how to define the app descriptor
The app descriptor is one of the essential building blocks of Crowdin apps. The app descriptor is a JSON file (for example, `manifest.json`) that includes general information for the app, as well as the modules that the app wants to operate with or extend. It describes how the application will work, what resources will be used, etc. ## [App Descriptor Structure](#app-descriptor-structure) [Section titled “App Descriptor Structure”](#app-descriptor-structure) The app descriptor is a JSON object with the following structure: manifest.json ```json { "identifier": "your-application-identifier", "name": "Your Application", "description": "Application description", "logo": "/assets/logos/app-logo.png", "baseUrl": "http://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" ] } ] } } ``` | Property | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `identifier` | **Type:** `string` (`^[a-z0-9-._]+$`)**Required:** yes**Description:** A unique key to identify the app. This identifier must be <= 255 characters.CautionDon’t use uppercase in the app identifier. | | `name` | **Type:** `string`**Required:** yes**Description:** The human-readable name of the app. | | `baseUrl` | **Type:** `string` (`uri`)**Required:** yes**Description:** The base URL of the remote app, which is used for all communications back to the app instance. Once the app is installed in a workspace, the app’s baseUrl can’t be changed without uninstalling the app beforehand.**This is important:** choose your baseUrl wisely before making your app public. The baseUrl must start with `https://` to ensure that all data is sent securely between our cloud instances and your app. | | `authentication` | **Type:** `Authentication`**Required:** yes**Description:** Specifies the authentication type to use when signing requests between the host application and the Crowdin app. | | `description` | **Type:** `string`**Description:** The human-readable description of what the app does. The description will be visible in the Crowdin UI. | | `logo` | **Type:** `string` (`relativeUri`)**Description:** The image URL relative to the app’s base URL which will be displayed in the Crowdin UI. | | `events` | **Type:** `Events`**Description:** Allow the app to register for app event notifications. | | `scopes` | **Type:** \[`string`, … ]**Description:** Set of [scopes](/developer/understanding-scopes/) requested by this app.```json "scopes": [ "project", "tm" ] ``` | | `modules` | **Type:** `object`**Description:** The list of modules this app provides. | | `environments` | **Type:** \[`string`, … ]**Allowed values:** `crowdin`, `crowdin-enterprise`**Description:** Set of environments where a module could be installed.```json "environments": [ "crowdin-enterprise" ] ``` | ## [Authentication](#authentication) [Section titled “Authentication”](#authentication) Specifies the authentication type to use when signing requests from the host application to the Crowdin app. Crowdin Apps support two types of authentication: * using OAuth app (`crowdin_app` value) * without OAuth app (`none` value) In case your Crowdin app requires access to Crowdin API at any time, it’s recommended to use the `crowdin_app`, in other cases feel free to use the `none`. The authentication type `none` grants access to Crowdin API as well as the `crowdin_app`, but only when the Crowdin app is executed on the user side, for example, when the iframe opens. Example: manifest.json ```json { "authentication": { "type": "crowdin_app", "clientId": "your-client-id" } } ``` | Property | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `type` | **Type:** `string`**Defaults to:** `none`**Allowed values:** `none`, `crowdin_app`**Description:** The type of authentication to use. | | `clientId` | **Type:** `string`**Description:** OAuth client id for authorization via the `crowdin_app` type. | ## [Modules](#modules) [Section titled “Modules”](#modules) Modules are how apps extend Crowdin and interact with it. Using modules your app can do the following things: * Extend the Crowdin UI. * Create integrations with external services. * Add support for new custom file formats. * Customize processing for supported file formats. Read more about [UI Modules](/developer/crowdin-apps-modules-ui/) and [File Processing Modules](/developer/crowdin-apps-modules-file-processing/). ## [Events](#events) [Section titled “Events”](#events) Allow an app to register callbacks for events that occur in the workspace. When an event is fired, a POST request will be made to the appropriate URL registered for the event. The installed callback is an integral part of the installation process of an app, whereas the remaining events are essentially webhooks. Each property within this object is a URL relative to the app’s base URL. Example: manifest.json ```json { "events": { "installed": "/hook/installed", "uninstall": "/hook/uninstall" } } ``` | Property | Description | | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `installed` | **Type:** `string`**Description:** The event that is sent to an app after a user installed the app in Crowdin.This event is required if you use `crowdin_app`. Read more about [Authentication](#authentication). | | `uninstall` | **Type:** `string`**Description:** The event that is sent to an app before the app uninstallation from Crowdin. | ### [Installed Event Payload](#installed-event-payload) [Section titled “Installed Event Payload”](#installed-event-payload) The Installed event is sent from Crowdin to the remote app when a user installs the app in Crowdin. The Installed event contains the information about the Crowdin workspace or profile the Crowdin App was installed to, the information about the app itself, as well as the credentials 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: | Property | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `appId` | **Type:** `string`**Description:** The identifier of the app that is declared in the app descriptor file. | | `appSecret` | **Type:** `string`**Description:** The unique secret used for authorization of your Crowdin app. | | `clientId` | **Type:** `string`**Description:** The OAuth client identifier that is declared in the app descriptor file. | | `userId` | **Type:** `integer`**Description:** The numeric identifier of the user that installed the app in Crowdin Enterprise. | | `organizationId` | **Type:** `integer`**Description:** The numeric identifier of the organization the app was installed to. | | `domain` | **Type:** `string`**Description:** The name of the organization in Crowdin Enterprise the app was installed to. For Crowdin the domain value is always null | | `baseUrl` | **Type:** `string`**Description:** The `baseUrl` of the organization in Crowdin Enterprise the app was installed to. For Crowdin the `baseUrl` value is always `https://crowdin.com` | ### [Uninstall Event Payload](#uninstall-event-payload) [Section titled “Uninstall Event Payload”](#uninstall-event-payload) The uninstall event is sent from Crowdin Enterprise to the remote Crowdin app when a user uninstalls the app from Crowdin Enterprise. The Uninstall event, like the install event, contains the information about the Crowdin workspace or account the Crowdin App was installed to, and the information about the app itself. After receiving the uninstall event, it’s necessary to find and remove all of the data related to the Crowdin workspace or account the app is removed from. 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: | Property | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `appId` | **Type:** `string`**Description:** The identifier of the app that is declared in the app descriptor file. | | `clientId` | **Type:** `string`**Description:** The OAuth client identifier that is declared in the app descriptor file. | | `organizationId` | **Type:** `integer`**Description:** The numeric identifier of the organization the app uninstalled from. | | `domain` | **Type:** `string`**Description:** The name of the organization in Crowdin Enterprise the app uninstalled from. For Crowdin the domain value is always null | | `baseUrl` | **Type:** `string`**Description:** The baseUrl of the organization in Crowdin Enterprise the app uninstalled from. For Crowdin the `baseUrl` 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 or not. ## [Installation in Crowdin](#installation-in-crowdin) [Section titled “Installation in Crowdin”](#installation-in-crowdin) To install the 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. In the appeared dialog, paste in the Crowdin app Manifest URL and click **Install**. 3. In the **Install Application** dialog, configure preferred permissions and click **Install**. ## [Installation in Crowdin Enterprise](#installation-in-crowdin-enterprise) [Section titled “Installation in Crowdin Enterprise”](#installation-in-crowdin-enterprise) To install the 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. In the appeared dialog, paste in the Crowdin app Manifest URL and click **Install**. 3. In the **Install Application** dialog, configure preferred permissions and 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 the installation process. This step allows you to define who can access and use the app across its various modules and specifying 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 will not be able to communicate via the API with projects that are not included in the selected list. Also, the app will only be displayed in the UI of the selected projects. This ensures that the app’s functionality and access are precisely tailored to the specific needs and security requirements of your organization. You can configure these access permissions at the time of installation or adjust them at any time for already installed apps. This flexibility allows you to respond to changes in your project requirements or security policies by updating the access settings to either expand or restrict the app’s functionality and visibility within your Crowdin projects. ### [User Access Categories](#user-access-categories) [Section titled “User Access Categories”](#user-access-categories) You can define which user categories are allowed to use the app. This setting is applied to each app module independently. Available options for Crowdin: * Only me (i.e., project owner) * Me, project managers and developers * All project members * Custom Access (selected users) * Guests (unauthenticated users)  Available options for Crowdin Enterprise: * Only organization admins * Organization admins, project managers and developers * All project members * Custom Access (selected users) * Guests (unauthenticated users)  ### [Project Access Configuration](#project-access-configuration) [Section titled “Project Access Configuration”](#project-access-configuration) In addition to user access, you can also specify the projects in which the app can be used (these settings apply across all app modules). Project access options: * Projects you own (for Crowdin) or All projects (for Crowdin Enterprise) * Selected projects This targeted approach allows for enhanced security and customization, ensuring that the app is only used where it’s really needed. ## [Installed Event Communication Flow](#installed-event-communication-flow) [Section titled “Installed Event Communication Flow”](#installed-event-communication-flow) When a Crowdin App is installed in the Account Settings the authorization flow takes place during which Crowdin and Crowdin App exchange the authorization data (the authorization code is being exchanged for an access token). In the following illustration, you can see the events that take place during this process.  Let’s examine in detail each step that happens in the illustration: 1. Installation of the Manifest URL - the user pastes in the Manifest URL in the *Account Settings* > *Crowdin Applications* and clicks **Install**. 2. Fetching content from the Manifest URL - the request is sent to Crowdin App. 3. Response: manifest JSON - Crowdin App returns the Manifest JSON that contains the data about the app. 4. Manifest Data Validation - the received content is validated according to the structure and data of the Manifest JSON. 5. Prompt to install - the information about the Crowdin App, as well as the list of permissions and the **Install** button is displayed 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 for API token generation. 8. Token request - Crowdin App sends the request for API token acquiring: ```plaintext POST https://accounts.crowdin.com/oauth/token ``` 9. Access Token - authorization service returns the API access token and the refresh token. 10. Success Code Response - Crowdin App returns the success status code (2xx) which confirms that the application installation was successfully finished. In case the status code is different, the application will be removed from the Crowdin account. **Token request parameters (step 8):** | | | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `grant_type: crowdin_app` | **Type:** `string`**Required:** yes**Description:** The parameter is used for the flow specification of an OAuth app. | | `client_id` | **Type:** `string`**Required:** yes**Description:** Client ID for the app is received when the app is registered. | | `client_secret` | **Type:** `string`**Required:** yes**Description:** 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 for authorization of 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 `