CLI referenceverbatra tmxnew

verbatra tmx

Import a TMX translation memory from another tool, or export this project's memory as TMX.

Available from 0.11.0

This needs verbatra 0.11.0 or newer. Earlier releases do not have it, so check your installed version with verbatra --version and upgrade if it is older.

TMX is the interchange format the mainstream translation platforms produce and consume. verbatra tmx import lands a memory one of them exported into this project's translation memory, so a later run reuses those strings instead of paying for them again. verbatra tmx export writes this project's memory back out in the same format, so the work can leave as easily as it arrived.

Neither direction calls a provider, reads an API key, or makes a network request. Every value comes from the file or from the memory already on disk.

Synopsis

verbatra tmx <direction> [file] [flags]

<direction> is import or export. [file] is the TMX file to read or write, and defaults to verbatra-memory.tmx in the working directory.

Flags

FlagArgumentDefaultEffect
--cwd<path>current directoryresolve config and the memory from this directory
--config<path>search for oneload this config file instead of searching for one
--locales<list>all configuredcomma-separated subset of target locales
--dry-runnoneoffimport only, refused on an export: validate and report without changing the memory
--overwritenoneoffimport only, refused on an export: let an imported unit replace a translation the memory already holds
--jsonnoneoffprint one JSON envelope on stdout carrying the result, or the error code on a failed run; the human-readable error line still goes to stderr

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 what the project already holds
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

What an imported unit has to satisfy

A file from another tool is untrusted input, and a unit that reaches the memory can be reused without anyone being asked again. So nothing is stored until it has earned it. Every candidate translation faces the same integrity gate that provider output and a filled translator handoff already face, checked when the record enters the memory rather than left to the run that later reads it:

  • its placeholders have to match the placeholders of its own source segment,
  • its inline HTML or XML tags have to match the tags of its own source segment, so a tag the source never had (such as an injected <img onerror>) is refused,
  • it has to be a valid ICU message under the configured format's adapter,
  • it must not have collapsed into runaway output rather than a translation,
  • it must not be blank where the source has text.

On top of that, a unit whose source segment is blank identifies no string at all, so it is refused too. Inline markup (bpt, ept, ph, it) is flattened to its text, which the placeholder check then catches if the markup was carrying something that mattered. A sub element inside that markup is a separate flow of text, such as a tooltip or alternative text, not part of the segment's own string, so its text is left out rather than flattened in.

Nothing is dropped quietly. Every refusal is counted by reason, and so is every unit the reader could not use, every unit with no segment in your source locale, every unit whose markup was flattened, and every unit whose sub text was left out.

Matching language tags

A TMX file spells its languages however its author's tool spelled them, which is rarely how your config spells them. The rule is explicit, and it never guesses:

  1. Both sides are lowercased and underscores are folded to hyphens, so pt_BR, pt-br and pt-BR are one locale. An exact match at that point wins.
  2. Otherwise, the tag can reach a configured locale that is a subtag prefix of it, so a file written in en-US lands in a project configured for en. When several configured locales are prefixes of the tag, the longest one wins, as in RFC 4647 lookup: in a project configured for de and de-AT, a file written in de-AT-1996 lands on de-AT, and one written in de-CH-1901 lands on de.
  3. A shorter tag can also reach a configured locale that begins with it, but only when every subtag the locale adds is a region or a variant, never a script. A file written in de lands in a project configured only for de-DE, and pt in one configured only for pt-BR, but sr never lands on sr-Latn and zh never on zh-Hant-TW, because a script subtag names a different writing system. A configured locale that is a prefix of the tag always takes precedence, and among several longer ones the nearest wins.
  4. Two tags whose script or region subtags differ never match. zh-CN is never stored as zh-TW, sr-Latn never as sr-Cyrl, and de-AT never as de-CH: such a tag is reported as matching no configured locale, not guessed at.
  5. A tag is only reported as ambiguous when the configured locales it could reach do not lie on one prefix chain. pt in a project configured for pt-BR and pt-AO is reported as ambiguous, not assigned to whichever came first.

Every segment is resolved against the source locale and all of your configured target locales in one pass, not against the source first and the targets afterwards. That is what stops a regional target being taken for the bare source tag it extends: in a project whose source is pt and whose targets include pt-BR, the pt-BR segment is that target, never a second reading of the source. It still holds when you narrow a run with --locales, because the resolution sees every configured locale either way.

A unit's source segments are ranked the same way as its target segments, described below: an exact tag outranks a segment that only reaches your source locale by subtag prefix, and two segments carrying the same value agree, so en "Save" beside en-US "Save" in a project configured for en imports normally. A unit whose source segments of equal standing carry different values, such as en-US "Color" and en-GB "Colour", is refused and counted rather than attributed to whichever came last. A config whose source locale and one of its target locales are the same tag once case and separators are normalized is refused outright, because no segment could be attributed to either.

Two target segments that resolve to the same configured locale never let the last one silently win. An exact tag always outranks a segment that only reaches the locale by subtag prefix, so in a project configured for de, a unit carrying de "Straße" and de-CH "Strasse" stores "Straße". If two segments of equal standing carry different values, such as de-CH "Strasse" and de-AT "Straße", nothing is stored for that locale from that unit, and the unit is counted as conflicting for that locale in the summary and in the --json result. The unit's other locales still land, and two segments carrying the same value are not a conflict.

Units the reader could not use, units with no source segment, units whose markup was flattened, and tu elements sitting outside the file's first body are all counted too. If the header declares a srclang that is not your source locale, that is reported rather than ignored.

Tags that match nothing configured are counted per tag and reported, so a file full of languages you do not translate tells you so rather than looking like an empty import.

When the file and the memory disagree

The project's own memory wins. If the memory already holds a different translation for the same source and locale, the imported one is refused and counted as kept. Pass --overwrite to reverse that and let the file win.

Within one file, the first unit for a given source wins and later repeats are counted as duplicates.

Importing the same file twice therefore changes nothing the second time, and writes no file at all.

Imported units and your configuration

Imported units are stored under the project's current configuration fingerprint, the same key a real run writes. That is deliberate: an imported memory is reused exactly when the configuration that would consume it matches, and stops matching when the provider, model, tone, or glossary changes, which is the protection the fingerprint layer exists to give. Re-import the file after such a change.

If the memory file was written by a newer verbatra than the one you are running, import leaves it untouched and says so, rather than writing a file the newer build would then have to distrust.

Imported units and fuzzy reuse

One consequence is worth stating plainly, because no check at import time can police it. An accepted unit's source text is written into the memory's source index, which is exactly what fuzzy reuse scores a changed string against. An imported source that differs from a string in your project can therefore be served for that string, which is what resemblance means.

An imported source that is identical to one of your strings but hashes differently is not reachable at all. Fuzzy reuse discards any candidate whose normalized source equals the string being looked up, so if your entry carries a description, a meaning, or a plural flag that a TMX unit cannot carry, the hashes differ, the identical text disqualifies the fuzzy candidate, and the string goes to the provider as usual.

A fuzzy reuse is still held to the integrity gate against your real entry, and it is reported as a FUZZY_CACHE_REUSE review flag on the run summary, so it is visible rather than silent. If you would rather an imported memory were not reachable that way at all, leave fuzzyCache out of your config: without it, only an exact content-hash match is ever reused.

What export writes

One tu element per distinct source string, carrying the source segment and one target segment per exported locale, under a TMX 1.4b header naming srclang, creationtool, creationtoolversion, segtype, and o-tmf. Only entries stored under the current configuration fingerprint are written, which is the same set a run would reuse.

Language tags are written in BCP 47 form, however your config spells them: underscores become hyphens, the language is lowercase, a script is title case, and a region is uppercase. A project configured for en_US and pt_BR writes srclang="en-US" and xml:lang="pt-BR", which another tool accepts and which imports back into the same project unchanged. How verbatra keys its own memory does not change.

A character XML 1.0 cannot represent at all, such as a control character or a lone surrogate, is removed from the segment text so the file stays readable by a conformant parser. How many were removed is reported in the summary and in the --json result.

An empty memory produces a valid, empty TMX file rather than an error.

An entry the memory holds no source text for cannot be written, because TMX has no unit without a source segment. That happens only for entries carried forward from a cache written before the memory stored source text; they are left out and counted, and they fill in as later runs touch those strings again.

Exit codes

CodeMeaning
0the file was read or written
2could not run: a config error, a missing or unreadable file, a file that is not valid TMX, or a usage error

A file that is oversized, malformed, not a TMX document, or that declares an XML entity is refused with a structured error naming what failed. When the problem has a place in the file, the message also names its line and column and, inside a translation unit, which unit it was (counting from 1), so line 9, column 29, unit 2 points you at the second tu. With --json the same message is in the error envelope, and an SDK caller reads the same place as structured data with tmxErrorLocation(error). Entity declarations and internal DTD subsets are refused outright, so neither an external entity reference nor an unbounded expansion is ever reached, and the plain external doctype that real writers emit is discarded rather than fetched.

Units that were skipped or refused do not fail the run. They are reported, because a real-world memory almost always carries some of both.

  • The cache explains what the translation memory is and how it is keyed.
  • Manual translation covers the xlsx, CSV, and TSV handoff, which is a different thing: a per-locale slice of the pending delta for a human translator, not the whole memory.
  • Translation safety explains the checks an imported unit has to pass.
Edit on GitHub