12 · Transfer-Based MT / SYSTRAN Lineage#
Dependencies: stdlib only
Runtime: < 1 min CPU
Chapter stub — implementation coming in a future commit.
Time-machine sentences#
Every chapter runs the same 20 held-out test sentences so the quality arc across chapters is directly comparable.
# This chapter is **stdlib only** — nothing to install.
# It runs on any Python 3.8+ with no pip dependencies.
12 · Transfer-Based MT — The SYSTRAN Lineage#
Transfer-based MT separates translation into three stages:
Analysis — parse source into a source-language representation
Transfer — map source structure to target structure
Generation — realise target string from target representation
SYSTRAN (1968) pioneered this approach and powered early Google Translate until 2007.
1 · The three-stage pipeline#
LEXICON = {
"a":{"fr":"un","pos":"DET"},"an":{"fr":"un","pos":"DET"},
"the":{"fr":"le","pos":"DET"},
"man":{"fr":"homme","pos":"N","g":"m"},"men":{"fr":"hommes","pos":"N","g":"m"},
"woman":{"fr":"femme","pos":"N","g":"f"},
"dog":{"fr":"chien","pos":"N","g":"m"},"cat":{"fr":"chat","pos":"N","g":"m"},
"park":{"fr":"parc","pos":"N","g":"m"},"book":{"fr":"livre","pos":"N","g":"m"},
"coat":{"fr":"manteau","pos":"N","g":"m"},"ball":{"fr":"balle","pos":"N","g":"f"},
"lake":{"fr":"lac","pos":"N","g":"m"},"boat":{"fr":"bateau","pos":"N","g":"m"},
"bench":{"fr":"banc","pos":"N","g":"m"},"musician":{"fr":"musicien","pos":"N","g":"m"},
"violin":{"fr":"violon","pos":"N","g":"m"},"road":{"fr":"route","pos":"N","g":"f"},
"ladder":{"fr":"echelle","pos":"N","g":"f"},"field":{"fr":"champ","pos":"N","g":"m"},
"newspaper":{"fr":"journal","pos":"N","g":"m"},"wall":{"fr":"mur","pos":"N","g":"m"},
"kitchen":{"fr":"cuisine","pos":"N","g":"f"},"windowsill":{"fr":"rebord","pos":"N","g":"m"},
"cafe":{"fr":"cafe","pos":"N","g":"m"},"pond":{"fr":"etang","pos":"N","g":"m"},
"beach":{"fr":"plage","pos":"N","g":"f"},"stairs":{"fr":"escaliers","pos":"N","g":"m"},
"fountain":{"fr":"fontaine","pos":"N","g":"f"},"street":{"fr":"rue","pos":"N","g":"f"},
"children":{"fr":"enfants","pos":"N","g":"m"},"tourists":{"fr":"touristes","pos":"N","g":"m"},
"workers":{"fr":"ouvriers","pos":"N","g":"m"},"firefighters":{"fr":"pompiers","pos":"N","g":"m"},
"flowers":{"fr":"fleurs","pos":"N","g":"f"},"groceries":{"fr":"courses","pos":"N","g":"f"},
"photographs":{"fr":"photos","pos":"N","g":"f"},"ducks":{"fr":"canards","pos":"N","g":"m"},
"people":{"fr":"personnes","pos":"N","g":"f"},"stop":{"fr":"arret","pos":"N","g":"m"},
"group":{"fr":"groupe","pos":"N","g":"m"},"couple":{"fr":"couple","pos":"N","g":"m"},
"chef":{"fr":"chef","pos":"N","g":"m"},"food":{"fr":"nourriture","pos":"N","g":"f"},
"boy":{"fr":"garcon","pos":"N","g":"m"},"girl":{"fr":"fille","pos":"N","g":"f"},
"football":{"fr":"ballon","pos":"N","g":"m"},"rain":{"fr":"pluie","pos":"N","g":"f"},
"chess":{"fr":"echecs","pos":"N","g":"m"},"flight":{"fr":"volee","pos":"N","g":"f"},
"bus":{"fr":"bus","pos":"N","g":"m"},"bicycle":{"fr":"velo","pos":"N","g":"m"},
"cyclist":{"fr":"cycliste","pos":"N","g":"m"},"staircase":{"fr":"escalier","pos":"N","g":"m"},
"walk":{"fr":"marche","pos":"V"},"walking":{"fr":"marche","pos":"V"},
"play":{"fr":"joue","pos":"V"},"playing":{"fr":"joue","pos":"V"},
"read":{"fr":"lit","pos":"V"},"reading":{"fr":"lit","pos":"V"},
"wait":{"fr":"attendent","pos":"V"},"waiting":{"fr":"attendent","pos":"V"},
"ride":{"fr":"roule","pos":"V"},"riding":{"fr":"roule","pos":"V"},
"chase":{"fr":"poursuit","pos":"V"},"chasing":{"fr":"poursuit","pos":"V"},
"take":{"fr":"prennent","pos":"V"},"taking":{"fr":"prennent","pos":"V"},
"feed":{"fr":"nourrit","pos":"V"},"feeding":{"fr":"nourrit","pos":"V"},
"dance":{"fr":"dansent","pos":"V"},"dancing":{"fr":"dansent","pos":"V"},
"sleep":{"fr":"dort","pos":"V"},"sleeping":{"fr":"dort","pos":"V"},
"sail":{"fr":"navigue","pos":"V"},"sailing":{"fr":"navigue","pos":"V"},
"climb":{"fr":"grimpent","pos":"V"},"climbing":{"fr":"grimpent","pos":"V"},
"repair":{"fr":"reparent","pos":"V"},"repairing":{"fr":"reparent","pos":"V"},
"carry":{"fr":"porte","pos":"V"},"carrying":{"fr":"porte","pos":"V"},
"kick":{"fr":"donne un coup","pos":"V"},"kicking":{"fr":"donne un coup","pos":"V"},
"sit":{"fr":"est assis","pos":"V"},"sitting":{"fr":"est assis","pos":"V"},
"run":{"fr":"courent","pos":"V"},"running":{"fr":"courent","pos":"V"},
"prepare":{"fr":"prepare","pos":"V"},"preparing":{"fr":"prepare","pos":"V"},
"is":{"fr":"est","pos":"AUX"},"are":{"fr":"sont","pos":"AUX"},
"red":{"fr":"rouge","pos":"ADJ"},"warm":{"fr":"chaud","pos":"ADJ"},
"small":{"fr":"petit","pos":"ADJ"},"tall":{"fr":"grand","pos":"ADJ"},
"outdoor":{"fr":"exterieur","pos":"ADJ"},"old":{"fr":"vieux","pos":"ADJ"},
"young":{"fr":"jeune","pos":"ADJ"},"crowded":{"fr":"animee","pos":"ADJ"},
"calm":{"fr":"calme","pos":"ADJ"},"empty":{"fr":"vide","pos":"ADJ"},
"in":{"fr":"dans","pos":"PREP"},"on":{"fr":"sur","pos":"PREP"},
"near":{"fr":"pres de","pos":"PREP"},"through":{"fr":"dans","pos":"PREP"},
"by":{"fr":"au bord de","pos":"PREP"},"at":{"fr":"a","pos":"PREP"},
"up":{"fr":"dans","pos":"PREP"},"against":{"fr":"contre","pos":"PREP"},
"of":{"fr":"de","pos":"PREP"},"his":{"fr":"son","pos":"DET"},
"her":{"fr":"ses","pos":"DET"},"their":{"fr":"leur","pos":"DET"},
"two":{"fr":"deux","pos":"NUM"},"several":{"fr":"plusieurs","pos":"DET"},
"an":{"fr":"un","pos":"DET"},"a":{"fr":"un","pos":"DET"},
}
def analyse(sentence):
tokens = sentence.rstrip(" .").lower().split()
return [{"src": tok, **LEXICON.get(tok, {"fr": tok, "pos": "UNK"})}
for tok in tokens]
def transfer(analysis):
# SYSTRAN key insight: EN DET+ADJ+N -> FR DET+N+ADJ
result, i = [], 0
while i < len(analysis):
tok = analysis[i]
if (tok["pos"] in ("DET","NUM")
and i+2 < len(analysis)
and analysis[i+1]["pos"] == "ADJ"
and analysis[i+2]["pos"] == "N"):
result += [analysis[i], analysis[i+2], analysis[i+1]]
i += 3
else:
result.append(tok)
i += 1
return result
def generate(transferred):
return " ".join(t["fr"] for t in transferred).capitalize() + "."
def translate_en_fr(sentence):
return generate(transfer(analyse(sentence)))
demo = [
"A man is walking his dog in the park .",
"A woman in a red coat is reading a book .",
"A small boat is sailing on a calm lake .",
]
print("Transfer-Based MT (EN->FR) -- SYSTRAN-style")
print("=" * 55)
for s in demo:
print(f" EN: {s}")
print(f" FR: {translate_en_fr(s)}")
print()
2 · Transfer rules — adjective inversion (EN DET+ADJ+N → FR DET+N+ADJ)#
adjective_tests = [
"A red coat .", "A warm windowsill .", "A calm lake .",
"A small boat .", "A crowded street .", "A tall ladder .",
]
print("EN (DET ADJ NOUN) -> FR (DET NOUN ADJ)")
print("-" * 50)
for en in adjective_tests:
print(f" {en:28s} -> {translate_en_fr(en)}")
3 · SYSTRAN’s modular architecture#
stages = [
("Morphological Analysis", "Decompose words; assign POS candidates"),
("Syntactic Analysis", "Build dependency tree; resolve POS ambiguity"),
("Lexical Transfer", "Replace src words with target equivalents"),
("Syntactic Transfer", "Reordering: adjective placement, verb structure"),
("Morphological Generation","Inflect: gender, number, tense agreement"),
("Orthographic Generation","Capitalization, spacing, punctuation"),
]
print("SYSTRAN Pipeline (circa 1970s)")
print("=" * 55)
for i, (s, d) in enumerate(stages, 1):
print(f" {i}. {s}\n {d}")
print("\nEach stage = thousands of hand-coded rules per language pair.")
print("Advantage: predictable, debuggable, no training data needed.")
print("Disadvantage: each new pair requires full re-engineering.")
4 · Time-machine#
TIME_MACHINE_EN = [
"A man is walking his dog in the park .",
"Two children are playing near a fountain .",
"A woman in a red coat is reading a book .",
"Several people are waiting at a bus stop .",
"A cyclist is riding through a crowded street .",
"A dog is chasing a ball on the beach .",
"A group of tourists is taking photographs .",
"A young girl is feeding ducks by a pond .",
"Two men are playing chess in a cafe .",
"A street musician is playing the violin .",
"Children are running through a field of flowers .",
"An old man is sitting on a bench reading a newspaper .",
"A woman is carrying groceries up a flight of stairs .",
"A boy is kicking a football against a wall .",
"A couple is dancing on an empty street .",
"A chef is preparing food in an outdoor kitchen .",
"A cat is sleeping on a warm windowsill .",
"Workers are repairing a road in the rain .",
"A small boat is sailing on a calm lake .",
"Firefighters are climbing a tall ladder .",
]
print("=== Chapter 12 * Time-Machine: Transfer-Based MT (EN->FR) ===")
print()
for s in TIME_MACHINE_EN:
print(f" EN: {s}")
print(f" FR: {translate_en_fr(s)}")
print()
print("Real SYSTRAN used 50k-word dictionaries + hundreds of grammar rules per pair.")