為簽證申請建立文件流程:以程式處理認證翻譯的經驗教訓
Building a Document Pipeline for Visa Applications: Lessons from Handling Certified Translations Programmatically
作者分享如何把簽證文件管理視為工程流程,以狀態機追蹤文件,並讓 apostille 和翻譯可同步進行。流程可自動追蹤文件有效期、比對領事館要求及檢查掃描品質;判斷文件所需的翻譯類型仍須由人處理。
Most engineers never think about document workflows until they (or a teammate on a relocation package) have to deal with one personally. Then suddenly you're staring at a checklist of apostilles, sworn translations, and consulate-specific requirements with zero tooling to manage any of it.
I went through this recently while helping set up an internal process for a company relocating engineers to Spain. The legal and procedural side is well covered elsewhere — there's a solid breakdown of document requirements and translation types in this article on certified translation for Spanish work visas. What's missing from most of these guides is the operational side: how do you actually track, version, and validate a pile of multilingual legal documents without losing your mind or missing a deadline?
This post covers how to think about that as an engineering problem, not just an HR problem.
Why this is a pipeline problem, not a checklist problem
A visa document set has properties that should feel familiar if you've built any kind of data pipeline:
- Multiple source formats (PDF scans, photos, government portals exporting weird formats)
- Transformation steps with hard dependencies (apostille before or in parallel with translation, translation before submission)
- Validation gates (consulate-specific requirements that change the acceptance criteria)
- Expiry windows (criminal record certificates are often only valid for 90 days)
- Versioning (a translated contract might need updates if the original changes)
Treating this like a checklist in a spreadsheet works for one person, one time. It falls apart the moment you're coordinating five relocations at once, which is exactly when most companies start caring about it.
Modeling the document state machine
Each document in the pipeline goes through a predictable set of states. A simple state machine makes the tracking explicit instead of living in someone's head:
REQUESTED -> ISSUED -> APOSTILLE_PENDING -> APOSTILLED
\-> TRANSLATION_PENDING -> TRANSLATED -> SUBMITTED -> ACCEPTED/REJECTED
Note the branch: apostille and translation can happen in parallel, as the source article points out. Most teams serialize this unnecessarily and burn a week waiting for the apostille to come back before even starting the translation. If your tracking tool (even a Notion board or an Airtable) doesn't model that fork, you're adding latency for no reason.
A minimal schema for tracking this, if you're building something lightweight:
from dataclasses import dataclass, field
from datetime import date
from enum import Enum
class DocStatus(Enum):
REQUESTED = "requested"
ISSUED = "issued"
APOSTILLE_PENDING = "apostille_pending"
APOSTILLED = "apostilled"
TRANSLATION_PENDING = "translation_pending"
TRANSLATED = "translated"
SUBMITTED = "submitted"
ACCEPTED = "accepted"
REJECTED = "rejected"
@dataclass
class VisaDocument:
doc_type: str # e.g. "criminal_record", "employment_contract"
applicant_id: str
source_lang: str
target_lang: str
requires_sworn_translation: bool
issued_date: date | None = None
validity_days: int | None = None
status: DocStatus = DocStatus.REQUESTED
history: list[tuple[DocStatus, date]] = field(default_factory=list)
def is_expired(self, as_of: date) -> bool:
if not self.issued_date or not self.validity_days:
return False
return (as_of - self.issued_date).days > self.validity_days
The requires_sworn_translation flag matters more than it looks. As the source article notes, a certified translation from a non-appointed translator won't satisfy a requirement for traducción jurada. If you're building any kind of validation logic around document acceptance, this distinction needs to be a first-class field, not an afterthought in a comment.
Automating the parts that don't need a human
You can't automate the actual translation (nor should you try — machine translation has no legal standing here, full stop). But plenty of surrounding work is automatable:
Expiry tracking. A daily cron job that flags documents approaching their validity window is trivial to build and saves real pain:
from datetime import date, timedelta
def documents_expiring_soon(docs: list[VisaDocument], days_ahead: int = 14):
today = date.today()
return [
d for d in docs
if d.issued_date and d.validity_days
and 0 < (d.issued_date + timedelta(days=d.validity_days) - today).days <= days_ahead
]
Requirement diffing. Consulate requirements change and differ by jurisdiction. If you're handling more than a handful of applications, maintain a simple JSON config per consulate and diff it against your document checklist rather than relying on memory:
{
"consulate": "Madrid",
"visa_type": "work_visa_highly_qualified",
"required_docs": [
{"type": "criminal_record", "translation": "certified"},
{"type": "academic_degree", "translation": "sworn"},
{"type": "employment_contract", "translation": "certified"}
]
}
A quick script that cross-references this against your document tracker will catch mismatches (like a degree certificate with only a certified translation when sworn is required) before submission, not after rejection.
Scan quality gates. If your translation vendor works from scans, a bad scan is a silent failure mode. A basic check with something like pytesseract or opencv to validate resolution and completeness before handoff can prevent a translation redo cycle:
import cv2
def scan_is_usable(image_path: str, min_dpi_equiv: int = 1500) -> bool:
img = cv2.imread(image_path)
if img is None:
return False
height, width = img.shape[:2]
return min(height, width) >= min_dpi_equiv
This isn't rigorous DPI detection, but it catches the common case of someone uploading a blurry phone photo that will bounce back from the translator a day later.
Where humans still have to be in the loop
Don't try to automate:
- Determining whether a document needs sworn vs. certified translation — this depends on the receiving authority and changes over time
- Interpreting rejection reasons from a consulate
- Anything involving apostille submission to a foreign ministry or court, since these are manual processes with no API
The goal of tooling here isn't to remove the legal/administrative expertise, it's to remove the friction around tracking state, catching expiry windows, and preventing avoidable mismatches between document type and translation format.
Takeaway
If you're responsible for coordinating relocation or visa logistics at a company with any scale, treat it like the pipeline it actually is. A lightweight state machine, an expiry checker, and a requirements diff tool will save more time than any spreadsheet template, and they'll scale past the first relocation case without anyone having to remember which consulate wanted what.
For the legal and procedural details on document types, certified vs. sworn translation, and apostille requirements specifically for Spain, see the original breakdown here: Certified Translation for Work Visa Spain.
來源:DEV Community · dev.to