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:
- 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.
- 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. - 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.
Migrating
- Set your tokens.
export CROWDIN_TOKEN=…for Crowdin (prefer the environment variable over the--crowdin-tokenflag, which leaves the secret in your shell history), andtextsetu loginfor TextSetu. - Dry-run, and read the plan. Section Reading the plan explains every line.
- 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. - Run it for real. Languages are created first, then translations, then the translation memory, then the glossary.
- Verify.
textsetu statusshows completeness per language — compare it with Crowdin’s own. Then spot-check a plural key, apt-BR/pt-PTpair 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: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.
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 downloadfollowed bytextsetu initandtextsetu push --translationspreserves 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.- Import & Export — for anything the migration deliberately leaves out.
- Translation Memory and Glossary — what those assets do once they arrive.
- Approval workflow — if your values landed as proposals.

