# General Translation Knowledge Base: How do I migrate from Crowdin to General Translation?
URL: https://generaltranslation.com/en-GB/kb/migrate-from-crowdin.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Keep your resource files, i18n library, and reviewed translations. Turn on Skip untranslated strings, download your translations, stop Crowdin's Git integration from pushing translations, and import the files with npx gt upload before the first npx gt translate. General Translation then translates new and changed strings in your build, using your glossary from a Context Group.
Category: Workflow Tools
Last updated: 2026-11-12
Authors: Kevin Wu
Published: 2026-11-12

Crowdin holds your source files, their translations, a glossary, and a translation memory, and its Git integration returns translations as pull requests. Moving to General Translation keeps the resource files your app already reads: the `gt` CLI imports them with your existing translations, then translates new and changed strings in your build or CI. Your i18n library, keys, and file layout don&#39;t change; moving copy inline is a separate, optional step (see [migrating from react-i18next](/kb/migrate-from-react-i18next) or [from next-intl](/kb/migrate-from-next-intl)). Your glossary becomes a Context Group that your app, docs, and other projects can share.

## What carries over from Crowdin?

| In Crowdin                                                 | In General Translation                                                                                                                                                      |
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `crowdin.yml` `files` entries                              | `files` in `gt.config.json`, with `[locale]` in place of `%locale%`                                                                                                         |
| Translated files                                           | Imported with [`npx gt upload`](/docs/cli/reference/commands/upload) and kept until their source changes                                                                    |
| Glossary                                                   | A [Context Group](/docs/platform/dashboard/guides/defining-context-for-translations) Glossary, imported from CSV                                                            |
| Style guide and AI prompts                                 | [Custom Prompts](/docs/platform/dashboard/guides/defining-context-for-translations), globally or per locale                                                                 |
| String context and max length                              | Comments in your source Android and iOS files, which General Translation reads as context, or a [keyed metadata](/docs/cli/reference/keyed-metadata) file for JSON and YAML |
| Translation memory                                         | Your uploaded translations: unchanged strings keep them, and only new and changed strings are translated. TMX files aren&#39;t imported                                     |
| GitHub or GitLab integration (`l10n_` branches by default) | [`npx gt translate`](/docs/cli/reference/commands/translate) in CI, or [Locadex](/docs/platform/locadex/quickstart) pull requests                                           |
| Proofreading and approval                                  | Editing in the [Dashboard](/docs/platform/dashboard/guides/reviewing-translations), with approval on the Enterprise plan                                                    |
| Over-the-air (OTA) content delivery for mobile apps        | Translations ship in your resource files with each app release                                                                                                              |

The CLI reads JSON, YAML, `.pot` and `.po`, iOS `.strings`, `.stringsdict`, and `.xcstrings`, Android `strings.xml`, `.resx`, HTML, Markdown and MDX, and more. (See the [supported formats](/docs/cli/reference/config#files).) It doesn&#39;t read XLIFF, ARB, Java `.properties`, or CSV and XLSX string files.

## Step 1: Stop Crowdin from filling gaps with source text

By default, Crowdin [exports untranslated strings as source text](https://support.crowdin.com/project-settings/export/) so files are never empty. Imported as-is, that English would count as finished translations. Before you download, turn on **Skip untranslated strings** under **Settings &gt; Export** in the Crowdin project (it has no effect while **Skip untranslated files** is on), or set [`skip_untranslated_strings`](https://crowdin.github.io/crowdin-cli/advanced) in `crowdin.yml`:

```yaml title="crowdin.yml"
files:
  - source: /locales/en/**/*.json
    translation: /locales/%locale%/**/%original_file_name%
    skip_untranslated_strings: true
```

What &quot;skip&quot; means depends on the format. Crowdin removes untranslated strings from flat JSON, YAML, Android XML, iOS `.strings` and `.stringsdict`, and `.resx`. It exports them as empty values in nested JSON, i18next JSON, gettext PO, and TS or JS files, so search those files for empty strings and remove them before importing. Add `export_only_approved: true` if you want to carry over only proofread translations.

## Step 2: Download your translations

If the source files are already in your repository, download the translations with the [Crowdin CLI](https://crowdin.github.io/crowdin-cli/commands/crowdin-download). Preview the paths first:

```bash
crowdin download --dryrun
crowdin download --skip-untranslated-strings
```

Without the CLI, use **Translations &gt; Build &amp; Download** in the Crowdin project, which follows the project&#39;s export settings. Crowdin Enterprise users set `base_url` to their organisation&#39;s API address in `crowdin.yml`.

Check the locale folder names. `%locale%` produces language-and-region codes such as `uk-UA`, while `%two_letters_code%` produces language-only codes such as `uk`. List the same codes in `gt.config.json`, or rename them on export with Crowdin&#39;s [`languages_mapping`](https://support.crowdin.com/developer/configuration-file/) option.

## Step 3: Stop Crowdin from pushing translations

Crowdin&#39;s GitHub and GitLab integrations [sync every hour by default](https://support.crowdin.com/github-integration/) and open pull requests (merge requests on GitLab) from `l10n_` branches. Before General Translation writes any files, clear the integration&#39;s **Sync Schedule** and save, so Crowdin stops sending translations to your repository; only the member who set it up can edit it. Close any open `l10n_` pull requests, and remove Crowdin steps from your CI. Once General Translation manages the files, you can remove the integration entirely. If two tools write the same files, whichever writes last wins.

## Step 4: Point the CLI at the same files

Install the `gt` CLI and translate your `crowdin.yml` entries into `gt.config.json`. Each `translation` pattern becomes an `include` pattern with `[locale]`:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["de-DE", "pt-BR", "uk-UA"],
  "files": {
    "json": { "include": ["locales/[locale]/**/*.json"] }
  }
}
```

Sign in with [`npx gt login`](/docs/cli/reference/commands/login) and set `GT_PROJECT_ID`, or set `projectId` in the config. In CI, use `GT_API_KEY` with a project API key that has **Files &gt; Write**, **Translation queue &gt; Enabled**, and **Context &gt; Write** permissions.

## Step 5: Bring over your glossary and string context

A project owner or manager can download the glossary as CSV from Crowdin&#39;s [Glossaries](https://support.crowdin.com/glossary/) page, or with the CLI (`crowdin glossary list` shows the ID):

```bash
crowdin glossary download <glossary-id> --format csv --to ./glossary.csv
```

Crowdin names columns by language. Rename `Term [en]` to `term`, `Description [en]` to `definition`, and each target column, such as `Term [de]`, to `translation_de`; delete the remaining columns and save the file. General Translation&#39;s importer reads that layout directly and interprets other layouts with AI. In the Dashboard, create a [Context Group](/docs/platform/dashboard/guides/defining-context-for-translations), click **Import**, choose the file, and assign the group to your project before the first translation run, so new translations use your terminology from the start.

String context tells the translator what a string means, whether a developer wrote it or Crowdin generated it. Comments above each string in your source Android `strings.xml` and iOS `.strings` files already reach General Translation as context. For context stored only in Crowdin, get each string&#39;s `context` and `maxLength` from Crowdin&#39;s [source strings API](https://support.crowdin.com/developer/api/v2/#tag/Source-Strings/operation/api.projects.strings.getMany). Add the context as comments in Android and iOS source files, or, for JSON and YAML, write both to a [keyed metadata](/docs/cli/reference/keyed-metadata) file next to the source file:

```json title="locales/en/common.metadata.json"
{
  "checkout": {
    "save": { "context": "Button that saves the shipping address", "maxChars": 12 }
  }
}
```

Recreate your style guide and AI prompt instructions as [Custom Prompts](/docs/platform/dashboard/guides/defining-context-for-translations). To keep an archive of your translation memory, download it as TMX with `crowdin tm download <tm-id>`.

## Step 6: Upload before you translate

Run [`npx gt upload`](/docs/cli/reference/commands/upload) first. It sends each source file and the matching translated file for every locale, and stores those translations as completed:

```bash
npx gt upload
npx gt translate
```

[`npx gt translate`](/docs/cli/reference/commands/translate) then translates only what&#39;s missing, such as a new locale or a file with no translation, and writes the results to the same paths. Running [`npx gt translate`](/docs/cli/reference/commands/translate) before [`npx gt upload`](/docs/cli/reference/commands/upload) generates new translations that overwrite the downloaded files. General Translation translates strings Crowdin hadn&#39;t translated the next time their source file changes. To ship them sooner, finish them in Crowdin before you download.

## Step 7: Translate new copy automatically

From now on, run [`npx gt translate`](/docs/cli/reference/commands/translate) in your [build or CI](/kb/translations-in-ci), or let [Locadex](/docs/platform/locadex/quickstart) open pull requests with translations. Commit `gt-lock.json`, which records the translations the CLI has already written so it doesn&#39;t download them again. If your team edits translated files in the repository, run [`npx gt save-local`](/docs/cli/reference/commands/save-local) before the next translation so those edits are kept.

## When should you stay on Crowdin?

Stay when an open-source project relies on Crowdin&#39;s free licence and community translators, when you order human translation through its vendor marketplace, when you exchange XLIFF or Office files, or when native mobile apps depend on over-the-air updates. (See [General Translation vs. Crowdin](/kb/general-translation-vs-crowdin).)

## Common questions

### Will I lose my proofread translations?

No, if you skip untranslated strings on export and run [`npx gt upload`](/docs/cli/reference/commands/upload) before [`npx gt translate`](/docs/cli/reference/commands/translate). Uploaded translations stay in place until their source text changes, and your team can correct any translation in the [Dashboard](/docs/platform/dashboard/guides/reviewing-translations).

### Can I keep using Crowdin for some content?

Yes. Limit `include` and `exclude` in `gt.config.json` to the files General Translation manages, and keep Crowdin syncing everything else. Don&#39;t let both tools write the same files.

## Key takeaways

* Turn on Skip untranslated strings before exporting, or Crowdin fills gaps with source text that would be imported as translations.
* Stop Crowdin&#39;s Git integration from pushing translations before General Translation writes the same files.
* Mirror `crowdin.yml` in `gt.config.json`, import the glossary into a Context Group, and carry string context stored in Crowdin over as per-key context before translating.
* Run [`npx gt upload`](/docs/cli/reference/commands/upload) before the first [`npx gt translate`](/docs/cli/reference/commands/translate), then translate new copy in your build or with Locadex.

## Related questions

- [Crowdin alternatives: General Translation vs. Crowdin](https://generaltranslation.com/en-GB/kb/general-translation-vs-crowdin.mdx): Choose General Translation to keep copy in your components and generate translations with your build. Crowdin manages translation projects around resource files, translators, and review workflows, which engineers connect to the app and maintain as the product changes. Crowdin remains a strong fit for open-source community translation and vendor-led programmes.
- [How do I migrate from Lokalise to General Translation?](https://generaltranslation.com/en-GB/kb/migrate-from-lokalise.mdx): Keep your resource files, i18n library, and reviewed translations. Export from Lokalise without empty translations, turn off its GitHub sync, and import the files with npx gt upload before the first npx gt translate. General Translation then translates new and changed strings in your build, with your glossary in a Context Group and key descriptions as per-key context.
- [How do I migrate from Phrase to General Translation?](https://generaltranslation.com/en-GB/kb/migrate-from-phrase.mdx): Keep your locale files, i18n library, and verified translations. Pull from Phrase Strings without untranslated keys or fallbacks, deactivate Repo Sync, and import the files with npx gt upload before the first npx gt translate. General Translation then translates new and changed strings in your build, with your term base in a Context Group and key descriptions as per-key context.
- [How do I keep translations up to date on every deploy?](https://generaltranslation.com/en-GB/kb/translations-in-ci.mdx): Run translation as a pipeline step before each production build, so new copy ships translated with the deploy that adds it. General Translation's gt CLI does this with npx gt translate, using a project API key and project ID stored as CI secrets.
- [What is translation memory, and do you still need it with AI?](https://generaltranslation.com/en-GB/kb/what-is-translation-memory.mdx): Translation memory (TM) is a database of earlier source and target segment pairs that a translation tool searches for exact, in-context, and fuzzy matches. With AI, you still want approved wording reused and past translations available as context. General Translation reuses matching translations from earlier file versions and keeps team edits, without a separate TM to maintain.

## Previous and next questions

- Previous question: [Build time, run time, or translation proxy: how should you deliver translations?](https://generaltranslation.com/en-GB/kb/build-time-vs-runtime-translation.mdx)
- Next question: [How do I migrate from Lokalise to General Translation?](https://generaltranslation.com/en-GB/kb/migrate-from-lokalise.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/en-GB/kb/sitemap.md) for every article.
