# FormatJS > Localize FormatJS & react-intl messages — no custom formatter required. URL: https://store.crowdin.com/formatjs Category: File Formats Type: App Author: Awesome Crowdin FormatJS's `formatjs extract` command outputs messages in a standard JSON shape (`{ "id": { "defaultMessage", "description" } }`) that Crowdin doesn't handle natively — so today you have to run `formatjs extract --format crowdin` to convert it first. This app reads the standard output directly, removing that extra step. ## How It Works ### Step 1: Upload extracted messages Upload the JSON produced by `formatjs extract`. The app detects FormatJS-shaped catalogs (any `.json` containing `defaultMessage`) and leaves other JSON to Crowdin's native parser. ### Step 2: Extract translatable content Each message becomes a Crowdin string: `defaultMessage` is the source text and `description` is the translator context. ICU placeholders, plurals, and react-intl rich-text tags are kept verbatim. ### Step 3: Translate in Crowdin Translators work with the extracted strings, with the message id and description for context. ### Step 4: Export translated files Exports mirror the original structure, replacing only each `defaultMessage` with the translation while preserving `description`, key order, and any other fields — so the file is ready for `formatjs compile`. Untranslated messages keep their source text. The app also supports Crowdin bundles, generating a translated catalog for any target language. ## File Format Details | Attribute | Value | | --------- | ----- | | File Extension | .json | | [API Type](https://developer.crowdin.com/api/v2/#operation/api.projects.files.post) | formatjs-messages | | Source can be edited in Crowdin | No | | Generating translated files via [Bundles](https://support.crowdin.com/bundles/) | [Search Crowdin Store](https://store.crowdin.com/tags/string-exporter) | | Supports pluralization | Yes | | WYSIWYG Preview in the editor | No | | Supported types of contextual information | Text comments, Screenshots | | Custom attributes | Yes | | Existing translations import possibility | Key-value mapping | ## Sample File ```json { "with.description": { "defaultMessage": "Save changes", "description": "primary action button" }, "no.description": { "defaultMessage": "Cancel" }, "empty.message": { "defaultMessage": "", "description": "intentionally empty, should be skipped" }, "bare.string": "Logged compiled value still translatable", "object.description": { "defaultMessage": "Notifications", "description": { "context": "nav label", "component": "Sidebar" } } } ```