Case Study: A Self-Hosted Translation Platform for a Professional Language Agency

Source analysis showing word-count bands, machine translation, equivalent words, and the customer total.
Analysis, TM leverage, and pricing in the same workspace — no spreadsheet round-trip.


How we replaced expensive CAT-tool subscriptions, PDF conversion fees, and machine-translation gateways with one owned platform.

Services: Custom software · API integration · Document conversion · Cloud infrastructure
Industry: Translation / localisation (LSP)
Stack: Laravel, Vue 3, FastAPI, Docker, Azure Translator, Xero
Outcome: A production TMS the agency owns — quotes, files, translators, invoices, and CAT work in one workflow, with no per-seat CAT licence and no £99/month MT gateway.


At a glance

A translation agency needed professional CAT (computer-assisted translation) without:

  • Per-user Trados / MemoQ licences that grow as the team grows
  • Client files sitting on a vendor’s cloud
  • Paying Intento (~$99/month) just to reach Azure or DeepL
  • Paying a commercial filters service only to open PDFs

We designed and built Prime — a translation management system with four portals — and connected it to a self-hosted CAT environment. Project managers stay in Prime. Translators work in the editor. The agency keeps data, translation memories, and billing under its own control.


The problem

Typical agency work looks simple from the outside: a client sends files, the PM quotes, a translator works, an invoice goes out.

In practice it was fragmented:

  1. Quoting was slow. Word counts, TM matches, and client rates lived in spreadsheets. A quote that should take minutes took the better part of an hour.
  2. PDFs were a blocker. The CAT filters handled Word, Excel, XLIFF and similar formats well. They did not turn PDF into something the editor could segment. Upload a PDF and analysis failed — unless the agency paid a hosted conversion product.
  3. Machine translation was expensive or missing. The usual path to Azure ran through Intento. The agency’s MT volume sat around $0–$20/month. Azure’s free F0 tier already includes 2 million characters/month. Paying $99/month for a gateway wasted that allowance.
  4. Translation memories were the real asset. Large TMX files (years of EITI, UNICEF, GCF-style work) had to import without silent data loss. The CAT import API quietly failed above ~5,000 translation units.
  5. GDPR and client confidentiality required EU hosting, not a shared SaaS CAT.

The brief was not “install an open-source CAT tool.” It was: run a real agency on it — analysis, quoting, vendors, MT, PDF, invoices — without leaking data or adding monthly tax for every extra tool.


What we built

Prime is a Laravel 11 + Vue 3 (Inertia) application with four isolated portals:

Portal Who uses it What they do
Host Agency owner Customers, vendors, PMs, settings, Xero connection
PM Project managers Intake, source analysis, quotes, vendor assignment, billing
Customer Client companies Submit requests, approve/reject quote sections, view invoices
Vendor Translators Accept jobs, open the CAT editor (no extra login), submit work, invoices

Every project has a human ID (PRJ-0001). Quote lines are service sections that clients can approve independently (split-quote). Completed jobs can be re-requested as a new version: source files stay locked, new languages and new quotes start clean.

Sensitive actions write to an immutable audit log (project history + PM activity). That is a compliance feature, not a nice-to-have.


1. CAT integration as the control plane

The CAT tool remains the translation editor. Prime is the control plane. Project managers never configure the editor by hand for day-to-day jobs.

How a job actually starts

  1. The PM uploads source files in Prime (analysis workspace).
  2. Prime creates one CAT project per language pair.
  3. A background worker polls until analysis is usable.
  4. Word counts and TM match bands are stored in Prime and drive the quote.
  5. When a vendor is assigned, Prime generates a credential-less translator URL. The linguist clicks it from the vendor portal — no separate CAT account.

What makes it production-grade

  • Project creation is asynchronous. The API returns an ID immediately, but the project is not ready yet. Polling too early looks like a failure. Prime retries on a schedule instead of showing a false error.
  • Templates are applied in full. Passing a template ID was not enough. We copy MT engine, TM keys, and pre-translate flags into the create request ourselves.
  • TM is multi-select. Client organisations store several translation-memory keys. On send, Prime posts lookup / update / penalty per key in the format the editor expects.
  • Analysis and machine translation are separate steps. Word counts persist as soon as analysis is usable, so quoting is not blocked by MT. “Run translation” is an explicit PM action. If Azure or DeepL dies mid-run, Prime keeps the job and the segments already billed and offers Resume translation for leftover empty segments.
  • Workers survive restarts. After a Docker restart, stale process flags could leave the project queue with messages and zero consumers. We clear those flags on every container start so daemons spawn cleanly.

That is the difference between a demo CAT install and an API that a PM can trust on a Monday morning.


2. PDF conversion without a monthly filters bill

Why PDF failed

A PDF is a drawing of pages, not a flow of paragraphs. CAT tools segment sentences. Filters therefore need an editable format (typically DOCX).

Hosted CAT products often hide a commercial PDF converter behind the same upload button. A self-hosted stack does not get that converter. Paying RapidAPI Filters Pro or Intento for conversion did not fit a ~$0–$20/month MT budget.

What we built instead

A dedicated PDF → DOCX microservice (FastAPI + pdf2docx / PyMuPDF), called only from Laravel’s queue — never from the browser.

The PM experience

  1. Upload PDF in Prime.
  2. Request returns immediately; status is converting.
  3. The UI polls the server. The PM can leave the page. Conversion continues.
  4. Status becomes ready (or failed with a clear message and Retry).
  5. The CAT engine then receives DOCX, which it already knows how to segment.

The converter API

Endpoint Role
GET /health Docker healthcheck; queue workers wait until this is green
POST /inspect Fast preflight: page count, text layer, image coverage, risk
POST /compress Shrink image-heavy PDFs without destroying text
POST /extract-text Plain text for analysis-only paths
POST /convert Synchronous convert (small files)
POST /convert/async + job status / download Long jobs with progress (Opening PDFAnalyzing layout → pages)

What we refuse on purpose

  • Password-protected PDFs
  • Empty / not-a-PDF uploads
  • Scanned (image-only) PDFs — no OCR in v1, returned as 422 instead of a blank DOCX
  • Files over 50 MB

We sample pages for a text layer and measure image coverage so a “pretty brochure” does not silently produce garbage for translators.

Large files. Above ~40 pages we convert in page chunks, then stitch DOCX. Progress is written back to the database so the UI is honest.

Fallback engine. If pdf2docx is down or fails, the same Laravel job retries via Gotenberg (LibreOffice) so one flaky layout does not stop the project.

Ops

  • Separate Docker image (prime-pdf-converter); app deploys do not bounce conversion
  • Dedicated Redis queue (pdf-conversion)
  • Transient errors retry; stuck converting rows are failed by a scheduled artisan command
  • Status page in Prime lists app, database, Redis, and converter health

This is not pixel-perfect DTP. Complex magazines may still need layout cleanup. It is an editable, segmentable file in the CAT editor, with £0 extra SaaS for conversion.


3. Machine translation without Intento

There was no native Azure plugin on the CAT stack. Azure was documented through Intento.

The agency already had (or could use) Azure Translator F0: 2M characters/month free, 50,000 characters per request, hourly pacing. Routing that through Intento would:

  • Charge ~$99/month regardless of volume
  • Hide Azure’s own free tier
  • Add a third party in the data path

The MT proxy

CAT editor  →  mt-proxy (our service)  →  Azure Translator

The editor still speaks the gateway protocol it already knew. A guarded redirect points that URL at our internal proxy. We did not fork the CAT product, so upgrades stay possible.

  • Azure keys live in deployment environment only, never in the editor UI or database.
  • PMs do not configure engines per project in the CAT tool; Prime / templates own that.
  • If a run fails halfway, billed segments stay; leftover segments resume.

Live MT suggestions and pre-translate then work inside the self-hosted editor, billed to Azure (often $0 at current volume), not to a gateway.


4. Translation memory and glossary import

3D view of Prime’s TMX and glossary drawer, with TM segments for EN-GB to FR-FR.
Translation memory in the project workspace

Years of approved translations are the agency’s competitive advantage. Losing them in a “simple TMX upload” is not acceptable.

TMX

The CAT tool accepts a TMX and returns a job ID, then silently never processes files over ~5,000 translation units. No error. No progress.

We split TMX XML into ≤5,000 TU chunks, upload each, store every job ID, and poll until all chunks complete. Progress is the average of chunks (33% → 66% → 100%), not a fake 99% from the first chunk. The UI only shows 100% when every chunk is done.

Glossary

Same async pattern (upload job → poll job), single job ID — dropzone, progress bar, success/failure in the analysis workspace.

TM resources in quoting

PMs pick which TM keys apply to a client, import TMX/glossary into a chosen key, and those keys travel with project creation so analysis and leverage match how the agency actually works.


5. Quoting, production, and invoicing

CAT analysis is useless if the quote still lives in Excel.

Prime turns word counts into service sections with client-specific rates, TM-match bands, and custom line items. The PM sends a quote; the customer can approve sections independently. Email includes a generated quote PDF.

When work is done:

  • Vendors invoice through their portal
  • PMs create sales invoices from approved official sections (no double-billing the same section)
  • Xero: OAuth2 connection, invoice create/sync, HMAC-verified webhooks, encrypted tokens, “Pay now” via Xero’s hosted invoice URL

The commercial loop (analyse → quote → produce → invoice) stays in one system.


6. Infrastructure, security, and delivery

This is not a laptop Docker demo.

Layer Choice Why
App, queue, scheduler, Redis, PDF converter Docker images on GHCR Repeatable deploys; VPS never builds PHP
MySQL On the host, not in a disposable volume compose down -v cannot wipe the agency’s data
TLS Host Nginx + Let’s Encrypt Standard renewals
Backups Nightly mysqldump + Acronis off-site Files + SQL, not opaque VM snapshots
CI/CD GitHub Actions → pull tagged image Rollback is an image tag
Region EU VPS GDPR / client confidentiality

Bootstrap is scripted: a new Ubuntu box can be rebuilt from the same playbook. Disaster recovery is a documented drill, not a hope.


What this meant for the client

Before After
Spreadsheet quoting, slow word counts Analysis and quote in Prime, same afternoon
PDF only via a paid hosted filters product PDF in the agency portal → DOCX → CAT, no filters subscription
Intento or no Azure Direct Azure via MT proxy; free-tier volume usable
TMX uploads that silently fail Chunked import with real progress
Translators need CAT licences / logins Vendor portal link into the editor
Files on a vendor cloud Self-hosted EU stack, audit trail, backups

The agency owns the platform. Adding a translator does not add a CAT seat. Adding a PDF job does not add a conversion invoice. MT is billed at the provider, not through a markup gateway.


Why this is the work we sell

Open-source CAT is free. Making it behave like an LSP’s production system is not:

  • Mapping which APIs actually exist on a self-hosted install
  • Bridging PDF and Azure without monthly converters and gateways
  • Queueing, polling, retries, and honest UI status so PMs are not babysitting jobs
  • TM migration that does not eat the 5,001st translation unit
  • Hosting that survives a restart and a restore

That is custom software around a proven CAT core — faster and cheaper than rewriting a CAT tool from scratch, stricter than pointing a client at a public SaaS editor and walking away.


Is this you?

We build this class of system for agencies that:

  • Need GDPR-friendly, self-hosted translation workflow
  • Are tired of per-seat CAT pricing
  • Handle PDF-heavy public-sector or NGO work
  • Want Azure / DeepL without a $99 gateway
  • Need quoting and Xero in the same place as the CAT tool

Talk to Sana Infotech about a translation platform case like this — discovery, architecture, build, TM migration, and go-live on your own infrastructure.


Want this on the site as two posts? Split after “What we built” into (1) “Self-hosted CAT + TMS” and (2) “PDF conversion and MT without SaaS tax.”

Search