Skip to main content
Your translations, translation memory and glossary are years of work. Moving them should not be a week of exports, spreadsheets and re-imports.
One command reads a Crowdin project through Crowdin’s API and writes it into a TextSetu project. It runs on your machine with your own Crowdin token, so no third-party credential is ever stored by TextSetu, and nothing in Crowdin is modified — the migration only reads.
Run --dry-run first, always. It reads both sides in full and prints exactly what would land: which languages get added, how many keys and TM segments and glossary terms, every key that had to be renamed, and every loss as a number. It writes nothing and exits 0.

What moves

Before you start

In Crowdin, create a personal access token under Account Settings → API. It only needs read access. Note your project id — the number in the project URL. On Crowdin Enterprise, note your organization name too, the <name> in <name>.crowdin.com. In TextSetu:
  1. Create the project you are migrating into, and set its source language to match Crowdin’s. The command adds Crowdin’s source language if it is missing, but it cannot make it the project’s source — do that first, or your source strings arrive as just another target language.
  2. Sign in with textsetu login. A personal access token is required for the translation memory and glossary, because both belong to the organization; a project token can still do --only translations.
  3. Check your plan has head-room. Keys, languages, translation memories and glossaries are all metered. An organization over its limits is read-only, and the run stops.
Decide your key shape before you run. A Crowdin identifier is unique per file; a TextSetu key is unique per project. If common.json and errors.json both contain title, those are two Crowdin strings competing for one TextSetu key. The migration refuses and names them rather than silently letting one win. Either rename one in Crowdin, or run with --on-collision prefix, which namespaces every key by its file (common.title, errors.title).

Migrating

  1. Set your tokens. export CROWDIN_TOKEN=… for Crowdin (prefer the environment variable over the --crowdin-token flag, which leaves the secret in your shell history), and textsetu login for TextSetu.
  2. Dry-run, and read the plan. Section Reading the plan explains every line.
  3. Resolve anything it surfaced — a key collision, a language spelled differently in the two tools (--locale-map zh-CN=zh-Hans), renames you would rather make yourself in Crowdin. Then dry-run again.
  4. Run it for real. Languages are created first, then translations, then the translation memory, then the glossary.
  5. Verify. textsetu status shows completeness per language — compare it with Crowdin’s own. Then spot-check a plural key, a pt-BR/pt-PT pair and a string with placeholders.

Choosing what to bring

Reading the plan

Warnings

Warnings never stop a run. Each names a loss, with a count, so you can decide before anything is written.

When something fails

A migration is several write phases, and it is deliberately not a transaction. If one fails, the command says what already landed:
Fix the cause, then run the same command again.
Re-running is always safe, and is both the recovery path and the ordinary one. Imports update rather than duplicate, a language that already exists is skipped, a TM or glossary already carrying the Crowdin one’s name is reused, and descriptions and tags are only ever filled — so an edit you made in TextSetu in between survives the next run. There is no state file to clean up.
Common causes: To undo rather than repeat: every phase is recorded as a job in the import history, and each can be reverted from the UI. The migration never deletes anything.

What does not move

A migration is judged on what did not arrive, so each of these is a counted line in --dry-run rather than a surprise afterwards.
  • Approval state. Everything arrives as a value, or as a proposal on a project that requires approval. Crowdin’s approved/unapproved distinction is not carried.
  • History, authors, timestamps and comments. Only the current translation moves.
  • Deliberately-empty translations, which arrive as untranslated.
  • File and directory structure. Crowdin’s files become key namespaces under --on-collision prefix, and are otherwise discarded. If your file tree is the thing you want to keep, crowdin download followed by textsetu init and textsetu push --translations preserves it instead.
  • Screenshots. TextSetu stores one screenshot per key with no regions, so Crowdin’s many-to-many mapping with bounding boxes has nowhere to land.
  • maxLength, folded into the key description.
  • Glossary synonyms beyond the first per language.
  • A TM source text’s leading and trailing whitespace. The source is the TM’s lookup key and is stored trimmed. Translations keep theirs, and so do all ordinary translation values.
  • Project settings — QA checks, workflow steps, plural-rule overrides. Configure the TextSetu project first.

Large projects

Translations upload as one multi-language document, split only when they would exceed the 10 MB request limit. The translation memory is chunked at 500 segments per request. Writes are paced under the API’s rate limit, and Crowdin reads run six at a time against its 20-concurrent cap. A project of a few thousand keys takes a minute or two; if a run is interrupted, re-running it costs only the reads.

Next steps

Moving your file sync too? The CLI guide maps crowdin.yml onto textsetu.json field by field, including the placeholder and languages_mapping equivalents.