Skip to content
Skip to Content

Reference / CLI

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.

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, 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.

Synopsis

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

# 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

FlagArgumentDefaultEffect
--locales<list>every configured target localeimport or export only this comma-separated subset of target locales
--dry-runnoneoffimport only: validate and report without changing the memory; an export refuses it with INVALID_DIRECTION
--overwritenoneoffimport only: let an imported unit replace a translation the memory already holds; an export refuses it with INVALID_DIRECTION

Also takes the 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.

Behavior

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 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.

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

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

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.

Warning

--overwrite replaces translations the memory already holds, and later runs reuse the replacements. Run it with --dry-run first to see the overwritten count.

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

An accepted unit's source text joins the index fuzzy reuse 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

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.

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.

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

Schema: tmx-envelope.json validates the whole envelope; Schemas lists the others.

With --json, the envelope carries the import or export result:

// 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

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:

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.

Exit codes

CodeMeaning
0the file was read or written; skipped or refused units are reported, not a failure
2could 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.

Edit on GitHub

On this page