# Custom File Format

Custom File Format Apps allow you to add support for new custom file formats. It's implemented by delegating a source file parsing to an app with a custom file format module.

[Custom File Format Module](https://support.crowdin.com/developer/crowdin-apps-module-custom-file-format/)

## Sample

```js ins="customFileFormat" title="index.js"
    import crowdinModule from '@crowdin/app-project-module';

    const generatePreviewFile = (fileContent, strings) => {
      // preview file generation logic
      return Buffer.from('');
    };

    const configuration = {
      baseUrl: 'https://123.ngrok.io',
      clientId: 'clientId',
      clientSecret: 'clientSecret',
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      customFileFormat: [
        {
          filesFolder: import.meta.dirname,
          type: 'type-xyz',
          signaturePatterns: {
            fileName: '^.+\\.xml$'
          },
          parseFile: async ({ fileContent: file, request, client, context, projectId }) => {
            // parse logic
            const strings = [];

            return {
              strings,
              previewFile: generatePreviewFile(file, strings),
            };
          },
          buildFile: async ({ fileContent, request, strings, client, context, projectId }) => {
            // build logic

            const contentFile = Buffer.from('<content>');
            return { contentFile }
          }
        }
      ]
    };

    crowdinModule.createApp(configuration);
    ```
  ```ts ins="customFileFormat" title="index.ts"
    import crowdinModule from '@crowdin/app-project-module';
    import type { ClientConfig } from '@crowdin/app-project-module';
    import type { ProcessFileString } from '@crowdin/app-project-module/modules/file-processing';

    const generatePreviewFile = (fileContent: Buffer, strings: ProcessFileString[]): Buffer => {
      // preview file generation logic
      return Buffer.from('');
    };

    const configuration: ClientConfig = {
      baseUrl: 'https://123.ngrok.io',
      clientId: 'clientId',
      clientSecret: 'clientSecret',
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      customFileFormat: [
        {
          filesFolder: import.meta.dirname,
          type: 'type-xyz',
          signaturePatterns: {
            fileName: '^.+\\.xml$'
          },
          parseFile: async ({ fileContent: file, request, client, context, projectId }) => {
            // parse logic
            const strings: ProcessFileString[] = [];

            return {
              strings,
              previewFile: generatePreviewFile(file, strings),
            };
          },
          buildFile: async ({ fileContent, request, strings, client, context, projectId }) => {
            // build logic

            const contentFile = Buffer.from('<content>');
            return { contentFile }
          }
        }
      ]
    };

    crowdinModule.createApp(configuration);
    ```
  Also, the custom file format module can support string export:

```js {15, 20-24} title="index.js"
    import crowdinModule from '@crowdin/app-project-module';

    const configuration = {
      baseUrl: 'https://123.ngrok.io',
      clientId: 'clientId',
      clientSecret: 'clientSecret',
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      customFileFormat: [
        {
          filesFolder: import.meta.dirname,
          type: 'type-xyz',
          stringsExport: true,
          multilingualExport: true,
          extensions: [
            '.resx'
          ],
          exportStrings: async ({ request, strings, client, context, projectId }) => {
            const file = request.file;
            // export logic
            return { contentFile: Buffer.from('') }
          }
        }
      ]
    };

    crowdinModule.createApp(configuration);
    ```
  ```ts {16, 21-25} title="index.ts"
    import crowdinModule from '@crowdin/app-project-module';
    import type { ClientConfig } from '@crowdin/app-project-module';

    const configuration: ClientConfig = {
      baseUrl: 'https://123.ngrok.io',
      clientId: 'clientId',
      clientSecret: 'clientSecret',
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      customFileFormat: [
        {
          filesFolder: import.meta.dirname,
          type: 'type-xyz',
          stringsExport: true,
          multilingualExport: true,
          extensions: [
            '.resx'
          ],
          exportStrings: async ({ request, strings, client, context, projectId }) => {
            const file = request.file;
            // export logic
            return { contentFile: Buffer.from('') }
          }
        }
      ]
    };

    crowdinModule.createApp(configuration);
    ```
  Read more about [Strings Array Structure](https://support.crowdin.com/developer/crowdin-apps-module-custom-file-format/#strings-array-structure).

## Configuration

| Parameter<div class="w-48"/> | Description                                                                                                                             | Default value |
|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `type`                       | The type parameter value for a custom file format. Used for a custom format file upload via API.                                        | -             |
| `filesFolder`                | Folder where large files are temporarily stored.                                                                                        | -             |
| `multilingual`               | Used to combine the content of multiple languages into one request when uploading and downloading translations in your Crowdin project. | `false`       |
| `autoUploadTranslations`     | Defines whether to upload translations automatically.                                                                                   | `false`       |
| `signaturePatterns`          | Contains `fileName` and/or `fileContent` regular expressions used to detect file type when uploading a new source file via UI or API.   | -             |
| `customSrxSupported`         | Enable custom SRX segmentation.                                                                                                         | `false`       |
| `stringsExport`              | Enable strings export.                                                                                                                  | `false`       |
| `extensions`                 | File extensions (used for strings export).                                                                                              | `[]`          |
| `multilingualExport`         | Enable multi-language strings export (for file formats that support multiple languages in one file).                                    | `false`       |

### Multiple File Formats

You can support multiple custom file formats in a single app by providing an array:

```js title="index.js"
const configuration = {
  // ... other configuration
  customFileFormat: [
    {
      name: 'Format A',
      type: 'type-a',
      signaturePatterns: { fileName: '^.+\\.xml$' },
      parseFile: async ({ fileContent }) => {
        // parse logic for format A
        return { strings: [] };
      },
      buildFile: async ({ strings }) => {
        return { contentFile: '' };
      }
    },
    {
      name: 'Format B',
      type: 'type-b',
      signaturePatterns: { fileName: '^.+\\.csv$' },
      parseFile: async ({ fileContent }) => {
        // parse logic for format B
        return { strings: [] };
      },
      buildFile: async ({ strings }) => {
        return { contentFile: '' };
      }
    }
  ]
};
```

### `parseFile` Function

This function is used to parse the content of the source file.

##### Parameters

- `fileContent` - File content.
- `request` - Request object.
- `client` - Crowdin API client.
- `context` - [Context object](/app-project-module/reference/context/).
- `projectId` - Crowdin project ID.

##### Return Value

Returns an object with the following structure:

```yml
{
  strings: [],
  previewFile?: Buffer,
  error: 'Some error message'
}
```

**Description:**

- `strings` - the processed strings.
- `previewFile` - optional, content that will be displayed as a preview in the Crowdin Editor.
- `error` - optional, and if it's present, the file will be marked as failed.

### `buildFile` Function

This function is used to build the content of the translation file.

##### Parameters

- `fileContent` - File content.
- `request` - Request object.
- `strings` - Strings array.
- `client` - Crowdin API client.
- `context` - [Context object](/app-project-module/reference/context/).
- `projectId` - Crowdin project ID.

##### Return Value

Returns an object with the following structure:

```yml
{
  contentFile: '<content>'
}
```

### `exportStrings` Function

This function is used to export string translations in a format different from the source file format.

##### Parameters

- `request` - the request object.
- `strings` - the strings array.
- `client` - the Crowdin API client.
- `context` - the context object.
- `projectId` - Crowdin project ID.

##### Return Value

Returns an object with the following structure:

```yml
{
  contentFile: '<content>'
}
```