---
title: "verbatra tmx"
description: "Import another tool's TMX translation memory so later runs reuse its strings instead of paying for them, or export this project's memory as TMX."
type: "reference"
---
# verbatra tmx

Import another tool's TMX translation memory so later runs reuse its strings instead of paying for them, or export this project's memory as TMX.

<AvailableFrom version="0.11.0" />

Move a translation memory in or out in TMX, the interchange format the mainstream translation platforms use. `tmx import` lands another tool's memory in [this project's translation memory](https://verbatra.kreitz-webdev.de/docs/the-cache.md), so later runs reuse those strings instead of paying for them again; `tmx export` writes the memory back out. Neither direction calls a provider, reads an API key, or touches the network. A real import also adds missing entries to an existing `.gitignore`, see [.gitignore entries](https://verbatra.kreitz-webdev.de/docs/cli.md#gitignore-entries).

## Synopsis [#synopsis]

```bash
verbatra tmx <direction> [file] [flags]
```

- `<direction>`: `import` or `export`; anything else is refused with `INVALID_DIRECTION`.
- `[file]`: the TMX file to read or write, `verbatra-memory.tmx` in the project root by default.

## Examples [#examples]

```bash
# land another tool's memory in this project
verbatra tmx import legacy.tmx

# report what would land, change nothing
verbatra tmx import legacy.tmx --dry-run

# let the file win where it disagrees with the memory
verbatra tmx import legacy.tmx --overwrite

# write the memory to verbatra-memory.tmx
verbatra tmx export

# only German, to a chosen path
verbatra tmx export out/memory.tmx --locales de
```

## Flags [#flags]

| Flag | Argument | Default | Effect |
| --- | --- | --- | --- |
| `--locales` | `<list>` | every configured target locale | import or export only this comma-separated subset of target locales |
| `--dry-run` | none | off | import only: validate and report without changing the memory; an export refuses it with `INVALID_DIRECTION` |
| `--overwrite` | none | off | import only: let an imported unit replace a translation the memory already holds; an export refuses it with `INVALID_DIRECTION` |

Also takes the [global flags](https://verbatra.kreitz-webdev.de/docs/cli.md#global-flags) `--cwd`, `--config` and `--json`. Here the config search and a relative `[file]` you pass resolve from `--cwd`, while the memory and the default `verbatra-memory.tmx` resolve from the [project root](https://verbatra.kreitz-webdev.de/docs/config-file.md#the-working-directory-and-env).

## Behavior [#behavior]

### What an imported unit has to satisfy [#what-an-imported-unit-has-to-satisfy]

A file from another tool is untrusted input, and a unit in the memory can be reused without anyone being asked again. So every candidate translation faces the same [integrity gate](https://verbatra.kreitz-webdev.de/docs/translation-safety.md) as provider output, checked when it enters the memory:

- its placeholders match those of its own source segment,
- its inline HTML or XML tags match those of its source segment, so an injected tag such as `<img onerror>` is refused,
- it is a valid ICU message under the configured format's adapter,
- it has not collapsed into runaway output,
- it is not blank where the source has text.

A unit with a blank source segment identifies no string and is refused too. Inline markup (`bpt`, `ept`, `ph`, `it`) is flattened to its text, so the placeholder check catches markup that carried something that mattered; the text of a `sub` element is a separate flow (a tooltip, alternative text) and is left out. Segment properties, including the `x-origin` and `x-review` a verbatra export writes, are ignored: the memory records no origin.

Nothing is dropped quietly: every refusal, unusable unit, unit with no segment in your source locale, flattened unit, and dropped `sub` text is counted by reason.

<AvailableFrom version="0.12.0" />

Each refused unit is also listed with its ordinal, its reason, and what is wrong when the check can name it, for example `unit 12: icu ({count} plural: missing arm "few" required by the target language)`.

### Matching language tags [#matching-language-tags]

A TMX file spells languages the way its author's tool did. The matching rule never guesses:

1. Both sides are lowercased and underscores become hyphens, so `pt_BR`, `pt-br` and `pt-BR` are one locale. An exact match wins.
2. Otherwise a configured locale that is a subtag prefix of the tag matches, the longest one first (RFC 4647 lookup): with `de` and `de-AT` configured, `de-AT-1996` lands on `de-AT` and `de-CH-1901` on `de`.
3. A shorter tag also reaches a configured locale that only adds a region or variant, never a script: `de` lands on `de-DE` and `pt` on `pt-BR`, but `sr` never on `sr-Latn`.
4. Tags whose script or region differ never match: `zh-CN` is never stored as `zh-TW`, and `de-AT` never as `de-CH`.
5. A tag is ambiguous only when the locales it could reach do not lie on one prefix chain, such as `pt` with `pt-BR` and `pt-AO` configured.

Every segment is resolved against the source locale and all configured targets in one pass, even under `--locales`, so a `pt-BR` target in a project whose source is `pt` is never read as the source. Source segments are ranked like target segments: an exact tag outranks a prefix match, equal values agree, and equal-standing segments with different values (`en-US` "Color" and `en-GB` "Colour") refuse the unit. A config whose source and a target normalize to the same tag is refused outright.

Two target segments that reach the same locale never let the last one win: an exact tag outranks a prefix match, and two equal-standing segments with different values store nothing for that locale and count the unit as conflicting. Unmatched tags are counted per tag, `tu` elements outside the first `body` are counted, and a header `srclang` that is not your source locale is reported.

### When the file and the memory disagree [#when-the-file-and-the-memory-disagree]

The project's memory wins: an imported translation that differs from one already held is refused and counted as kept, unless you pass `--overwrite`. Within one file, the first unit for a source wins and later repeats count as duplicates, so importing the same file twice writes nothing the second time.

<Callout type="warn">
`--overwrite` replaces translations the memory already holds, and later runs reuse the replacements. Run it with `--dry-run` first to see the `overwritten` count.
</Callout>

Imported units are stored under the project's current configuration fingerprint, so they are reused exactly while the provider, model, tone, the glossary terms verbatra would send and `localeMap` stay the same. Re-import the file after changing them. A memory file written by a newer verbatra is left untouched; the counts are still reported, as on a dry run.

### Imported units and fuzzy reuse [#imported-units-and-fuzzy-reuse]

An accepted unit's source text joins the index [fuzzy reuse](https://verbatra.kreitz-webdev.de/docs/the-cache.md) scores changed strings against, so an imported source that differs from one of your strings can be served for it. Such a reuse still passes the integrity gate against your real entry and carries a `FUZZY_CACHE_REUSE` review flag. An imported source identical to one of your strings but hashing differently (your entry has a description or meaning TMX cannot carry) is never reused. A plural form is matched by its text: the import takes the plural flag from your source file, so `cart.items_one` and `cart.items_other` fill from the memory like any other key. Leave `fuzzyCache` out of your config to allow exact matches only.

### What export writes [#what-export-writes]

One `tu` per distinct source string, with the source segment and one target segment per exported locale, under a TMX 1.4b header (`srclang`, `creationtool`, `creationtoolversion`, `segtype`, `o-tmf`). Only entries under the current configuration fingerprint are written; entries stored under an earlier provider, model, tone or glossary configuration are left out and counted as `otherConfiguration`, with a line of their own in the summary. Language tags are written in BCP 47 form (`en_US` becomes `en-US`), characters XML 1.0 cannot represent are removed and counted, and an empty memory produces a valid, empty file. Entries without stored source text, carried forward from an older cache, are left out and counted. The summary counts `tu` elements in its total and target segments per locale, so the per-locale counts add up to more than the total whenever a string is translated into several locales.

<AvailableFrom version="0.12.0" />

Every target segment carries `<prop type="x-origin">` (`machine`, `human`, `import` or `unknown`) from `verbatra.provenance.json`, and a `machine` segment also `<prop type="x-review">` (`approved`, `unreviewed` or `rejected`). When the provenance or lock file cannot be read, no property is written and the result says `provenanceMarkers: "unavailable"`. See [Machine-translation evidence](https://verbatra.kreitz-webdev.de/docs/provenance-evidence.md#tmx).

The file is written only inside the project root. A path that names no file, climbs out of the project root, or names a file the project depends on (a locale file, the lock, provenance or cache file, a config file, the glossary file) is refused with `TMX_OUTPUT_CONFLICT` before the memory is read, also after resolving symbolic links. A write that fails on disk stops with `TMX_UNWRITABLE`, naming the file and the file-system code.

### JSON output [#json-output]

Schema: [`tmx-envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/tmx-envelope.json) validates the whole envelope; [Schemas](https://verbatra.kreitz-webdev.de/docs/cli/output.md#schemas) lists the others.

With `--json`, the [envelope](https://verbatra.kreitz-webdev.de/docs/cli/output.md#json-output) carries the import or export result:

```ts
// tmx import
{ dryRun: boolean; file: string; units: number; locales: ImportTmxLocaleResult[];
  unmatchedLanguages: TmxLanguageReport[]; ambiguousLanguages: TmxLanguageReport[];
  memoryWritable: boolean; /* plus the skipped, unmatched and conflicting unit counts */ }

// tmx export
{ path: string; units: number; locales: { locale: string; units: number }[];
  withoutSource: number; otherConfiguration?: number; illegalCharactersRemoved: number;
  provenanceMarkers: "written" | "unavailable" }
```

Each import locale reports `added`, `unchanged`, `overwritten`, `kept`, `duplicates`, `conflicting`, `rejected` counts by reason, and the `refusals` list.

### Terminal feedback [#terminal-feedback]

<AvailableFrom version="0.12.0" />

Both directions show one progress line on stderr. `tmx import` then suggests `verbatra translate`, or the real import after a dry run; that suggestion keeps the file, `--overwrite`, `--locales`, `--cwd` and `--config`. `tmx export` suggests nothing:

```text output
verbatra: importing legacy.tmx into the memory... done (0.2s)
next: verbatra translate (reuses the imported memory before calling a provider)
```

`--quiet` drops these lines. See [Terminal output](https://verbatra.kreitz-webdev.de/docs/cli/output.md#terminal-output).

## Exit codes [#exit-codes]

| Code | Meaning |
| --- | --- |
| `0` | the file was read or written; skipped or refused units are reported, not a failure |
| `2` | could not run: a config error, a missing or unreadable TMX file (`SOURCE_UNREADABLE`), an oversized or unparseable one, or a source locale file that cannot be parsed (`SOURCE_INVALID`), a refused export path (`TMX_OUTPUT_CONFLICT`), a failed write (`TMX_UNWRITABLE`, `MEMORY_UNWRITABLE`), or a usage error |

An invalid file's message names the line, column and unit where it failed; entity declarations and internal DTD subsets are refused outright, and an external doctype is discarded, never fetched. See [the exit-code contract](https://verbatra.kreitz-webdev.de/docs/cli/output.md#exit-codes).

## Related [#related]

- [The cache](https://verbatra.kreitz-webdev.de/docs/the-cache.md) explains the translation memory and how it is keyed.
- [Manual translation](https://verbatra.kreitz-webdev.de/docs/manual-translation.md) covers the xlsx, CSV and TSV handoff of pending strings to a person.
- [Translation safety](https://verbatra.kreitz-webdev.de/docs/translation-safety.md) explains the checks an imported unit passes.
- [`tmxErrorLocation`](https://verbatra.kreitz-webdev.de/docs/sdk/exchange.md#tmxerrorlocation) reads the place of a TMX error from the SDK.
