Skip to content

About

ATRIUM project in-place translation of XML (ALTO/AMCR) files into English language

Topics

Resources

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Latest commit

 

History

425 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ATRIUM translator: XML documents translated in place, ALTO pages and AMČR metadata

The translator translates the text inside an XML document and writes it back into the same document: every tag, namespace and attribute stays, and in an ALTO page every word box keeps its coordinates. It reads ALTO pages (the OCR text of scanned documents) and the free-text fields of AMČR metadata records, mainly Czech to English. Use it when the result must stay the same document in another language. It reads XML only, not plain text or PDF.

Releases · MIT licence · ATRIUM documentation · SSH Open Marketplace ("Translator of XML files - ATRIUM")

Inputs and outputs

Input Output
ALTO XML pages (an <alto> root, or --alto) the same pages, each word box's text translated, or given an <ALTERNATIVE> with the translation
any other XML, with a list of XPaths (--xpaths): AMČR records, OAI-PMH responses, TEI the same documents, the named fields translated, or followed by an xml:lang-marked translated sibling
a file, a folder, or a .txt list of URLs (such as OAI-PMH GetRecord requests) per document, <name>_log.csv: the source and the translation side by side, with a status each
optionally, an ATRIUM document record to add to, and a vocabulary of Czech–English terms per document, the record with its translations block; per run, the paradata

ALTO mode translates the full text of OCR pages. Metadata mode translates the fields that a list of XPaths names, in AMČR records (bare or inside OAI-PMH envelopes) or in any other XML. The pilot use is Czech to English: the free-text fields of AMČR metadata records, translated in replace mode and checked against the AMČR 2.2 schema. The target language is English by default; any other language the backend offers can be chosen.

Two output modes: replace, the default, overwrites the source text; append keeps it and adds the translation beside it. The record follows the hub's document contract. Every file a run writes: docs/reference/outputs.md.

Quick start

What you need: Docker, or Python 3.11 and a checkout. Network access to LINDAT (lindat.mff.cuni.cz: the translation service, and UDPipe when a vocabulary is loaded) and, when --xsd names AMČR's schema as below, to api.aiscr.cz. With --source_lang auto, the language-identification model: the images carry it, and a checkout downloads it once from Hugging Face. No GPU: the translation is done by a remote service, or by a server you run.

The production shape of AMČR's run, on the 15 AMČR records in data_samples/my_documents, from a checkout with Python 3.11:

git clone https://github.com/ufal/atrium-translator.git && cd atrium-translator
pip install -r requirements.txt
python main.py data_samples/my_documents --xpaths amcr-fields.txt --formats xml \
    --source_lang cs --target_lang en --output-mode replace \
    --xsd https://api.aiscr.cz/schema/amcr/2.2/amcr.xsd -o out

The same run with the published batch image, which carries the records:

mkdir -p out
docker run --rm --user "$(id -u):0" -v "$PWD/out:/data/output" ghcr.io/ufal/atrium-translator:<version> \
    data_samples/my_documents --xpaths amcr-fields.txt --formats xml \
    --source_lang cs --target_lang en --output-mode replace \
    --xsd https://api.aiscr.cz/schema/amcr/2.2/amcr.xsd -o /data/output    # <version>: the release without its v

Expected: exit code 0, and the log ends with XSD: 15/15 record(s) valid. For each record, out/ holds <doc>_en.xml with the free-text fields in English, <doc>.document.json, and <doc>_log.csv, one row per translated field. A row looks like this (an invented record, for illustration):

file,page_num,line_num,text_cs,text_en,status
C-TX-000000000,,//amcr:amcr/amcr:dokument/amcr:popis,"Záchranný výzkum při stavbě rodinného domu.","Rescue excavation during the construction of a family house.",ok

out/paradata/ holds the run's record. The ALTO page in the same folder is skipped, because this is a metadata run. The output of such a run, made on 2026-10-19, is in data_samples/in-place_translated_files/xml/ for comparison.

What the run calls. LINDAT's translation service, and its UDPipe service too, because the shipped config.txt names the bundled vocabulary (terms are matched by lemma); api.aiscr.cz for the schema. Without LINDAT, each request is retried (LINDAT_MAX_RETRIES, 4 by default), then the document fails, the batch goes on, and the run exits 3. Without UDPipe, the terms of a passage go unprotected and the run says so. Without the schema, the run stops before translating anything, with exit code 1.

As a service, the ghcr.io/ufal/atrium-translator-api image answers POST /translate with the translated document. It reads the same inputs the same way: the root element decides ALTO or metadata unless is_alto is sent, and on request it returns the record (include_record), the log (include_log) and an XSD verdict (XSD_PATH). What stays different, and why: docs/usage.md. Its start, endpoints and errors are in service/README.md.

How it works

The default backend is the LINDAT translation service (CUBBITT). Two alternatives can be selected: a language model behind an OpenAI-compatible API (the recommended self-hosted one is Qwen3.8-27B), and a self-hosted CTranslate2 model (ct2). Each document is translated in pieces: an ALTO text block, or a metadata field. Unless the source language is given, FastText identifies it per piece, and a guess is used only when it is confident and names a language the backend can translate. Long texts are split at sentence boundaries. When a vocabulary is loaded, its terms keep their agreed English: the LINDAT backend masks them before translation and restores them afterwards; the language models get them as a glossary. Every reply is checked against its source. A reply that is empty, runaway, cut short or looping is requested again, re-run at the end of the document, and, if it never recovers, the source text is kept and logged untranslated. An ALTO block is translated whole for fluency and line by line as a ruler; the fluent translation is then divided among the original lines and word boxes. Details: docs/how-it-works.md.

Where it fits in ATRIUM

In the AMČR pilot the translator gives AMČR records their English free-text fields. In the scanned-document pipeline it reads the per-page ALTO that ocr-postprocess writes. Its output is an end product, an edition for readers: no later stage reads it, and the document record refers to it as derived_from.translated_xml. The whole workflow: the hub's workflow page.

Limits and maturity

  • A language pair the backend cannot translate is refused. Nothing is translated first: the command line exits 1, and the service answers 422 unsupported_language_pair. Into English, LINDAT translates from Czech, German, French, Polish, Russian and Ukrainian (how a pair is checked).
  • LINDAT is a public research service without a service-level agreement. One of its replicas once answered every other request with a loop. The output guard recovers such replies at the cost of time, and a daily check watches the endpoint (what was found).
  • append output is not valid AMČR 2.2. The schema allows neither xml:lang on the free-text fields nor a second copy of them. Where a record must validate, use replace, the default. ALTO output is valid ALTO 3.1 in either mode (the decision).
  • Word positions in translated ALTO are approximate. Line boundaries are exact; how a line's words are spread over its word boxes is constructed (how the words are placed).
  • ct2 on a GPU needs CUDA 12's cuBLAS (12.8 or newer on a Blackwell card). EuroLLM on ct2 drops short segments, and the published images do not include the ct2 dependencies (what was measured).
  • Translation quality is not measured against references. The backend comparison of 2026-10-19 is reference-less and small, and no human has scored the translations yet (the bake-off). The tests check behaviour, not translation quality.
  • The service has no authentication or rate limiting. Deploy it behind a gateway or on a trusted network (known limits).

More: the service's Known limits.

Documentation

For To Read
using it try it on the sample records Quick start
set it up, translate ALTO and metadata, choose a mode docs/usage.md
look up the command line, outputs, paradata, the vocabulary cli, outputs, paradata, vocabulary
set every option: config.txt and the environment docs/configuration.md
running the service deploy and operate it, its errors and limits service/README.md
see the API contract service/openapi.json
judging the results see the limits before relying on the output Limits and maturity
see what was measured: the backends, LINDAT's replies backends, LINDAT's replies
see the components of a run and their licences docs/reference/licences.md
changing the code understand languages, the guard, the ALTO reconstruction docs/how-it-works.md
read why it is built this way backend choice, replace or append, history
contribute, test, release; the release history docs/development.md, CONTRIBUTING.md

Terms used across the tools are explained in the ATRIUM glossary. The documentation follows the ATRIUM documentation style.

Licence, citation and acknowledgements

The code is MIT (LICENSE). Each run records the components it used, each with its own licence or terms. The LINDAT translation and UDPipe services are offered under the LINDAT Terms of Use, and their CUBBITT and UDPipe models are CC BY-NC-SA 4.0. The FastText language-identification model is CC BY-NC 4.0 and is used only when the source language is detected. The AMČR and TEATER vocabularies are CC0. A self-hosted model has its own licence; Qwen3.8-27B, for example, is Apache-2.0. None of these is the licence of the translation. The rights in an output depend on the source document, and are stated by whoever holds them (Licences and paradata). The components, when each is used, and the inventory in para_config.txt: docs/reference/licences.md. Cite with CITATION.cff, which GitHub shows as "Cite this repository".

Developed by ÚFAL, Charles University, with AMČR (the Archaeological Map of the Czech Republic) as the pipeline partner. Funded by ATRIUM (GA 101132163), with the support of the LINDAT/CLARIAH-CZ research infrastructure and the AIS CR infrastructure (LM2023031). It translates with the LINDAT translation service, lemmatises with UDPipe, and builds its vocabulary from the AMČR OAI-PMH API and TEATER. © 2026 ÚFAL and ATRIUM. Contact: the issue tracker, or lutsai@ufal.mff.cuni.cz.

About

ATRIUM project in-place translation of XML (ALTO/AMCR) files into English language

Topics

Resources

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages