elp(translate): EN->ES/PT geometric-free translation faculty
Adds the missing middle of the ELP: a deterministic EN-content-lemma ->
target-lemma bridge (translate.el) on top of comprehend.el (parse) and
realizer.el (inflect). English-only engram geometry cannot route
cross-lingually and vocabulary-XX.el carries no en_translation glosses, so
the honest no-LLM bridge is a wired lexicon (poem coverage; OOV passes
through). SACRED polarity/neg_word are carried untouched: 'never' localizes
to a negator ('nunca'), never to a content lemma.
Additive realizer extensions: agent_person/agent_number recognize Romance
target pronouns; the non-EN negation branch surfaces a carried neg_word
instead of the generic negator.
Verified on the real toolchain (elc->cc->run):
'You never fought the ocean.' -> ES 'Tú nunca luchaste el océano.'
'I never saw the breaking.' -> ES 'Yo nunca vi la ruptura.'
nunca holds 3/3 negation lines. Known gaps: PT verb conjugation fallback
(lutarred), irregular EN lemma (broke->break), adjunct/subordinator passthrough.
This commit is contained in:
@@ -34,6 +34,13 @@ fn agent_person(agent: String) -> String {
|
||||
if str_eq(agent, "we") { return "first" }
|
||||
if str_eq(agent, "us") { return "first" }
|
||||
if str_eq(agent, "you") { return "second" }
|
||||
// Romance target-language subject pronouns (translate.el sets these).
|
||||
if str_eq(agent, "yo") { return "first" }
|
||||
if str_eq(agent, "eu") { return "first" }
|
||||
if str_eq(agent, "nosotros") { return "first" }
|
||||
if str_eq(agent, "nós") { return "first" }
|
||||
if str_eq(agent, "tú") { return "second" }
|
||||
if str_eq(agent, "tu") { return "second" }
|
||||
return "third"
|
||||
}
|
||||
|
||||
@@ -50,6 +57,19 @@ fn agent_number(agent: String) -> String {
|
||||
if str_eq(agent, "us") { return "plural" }
|
||||
if str_eq(agent, "they") { return "plural" }
|
||||
if str_eq(agent, "them") { return "plural" }
|
||||
// Romance target-language subject pronouns.
|
||||
if str_eq(agent, "yo") { return "singular" }
|
||||
if str_eq(agent, "eu") { return "singular" }
|
||||
if str_eq(agent, "tú") { return "singular" }
|
||||
if str_eq(agent, "tu") { return "singular" }
|
||||
if str_eq(agent, "él") { return "singular" }
|
||||
if str_eq(agent, "ella") { return "singular" }
|
||||
if str_eq(agent, "ele") { return "singular" }
|
||||
if str_eq(agent, "ela") { return "singular" }
|
||||
if str_eq(agent, "nosotros") { return "plural" }
|
||||
if str_eq(agent, "nós") { return "plural" }
|
||||
if str_eq(agent, "ellos") { return "plural" }
|
||||
if str_eq(agent, "eles") { return "plural" }
|
||||
return "singular"
|
||||
}
|
||||
|
||||
@@ -359,7 +379,11 @@ fn realize_lang(form: [String], profile: [String]) -> String {
|
||||
return add_punct(capitalize_first(sentence), "assert")
|
||||
}
|
||||
// Generic non-English: affirmative core with a preverbal negator particle.
|
||||
// SACRED: when a standalone negative adverb was carried (e.g. "nunca",
|
||||
// localized upstream from "never"), surface it rather than the generic
|
||||
// negator — the specific negation must never be flattened away.
|
||||
let neg_particle: String = realize_negator(code)
|
||||
if !str_eq(neg_word, "") { let neg_particle = neg_word }
|
||||
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
|
||||
let verb_surf: String = native_list_get(vp_pair, 0)
|
||||
let aux_surf: String = native_list_get(vp_pair, 1)
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// translate.el - ELP geometric-free translation faculty (EN -> ES/PT/IT).
|
||||
//
|
||||
// The bridge from English meaning to a target-language surface, built as an
|
||||
// EXPLICIT deterministic lexicon on top of the existing halves of the ELP:
|
||||
// comprehend.el parse_spec(text) : EN surface -> meaning-spec (SACRED polarity)
|
||||
// realizer.el realize(spec) : meaning-spec -> target surface (inflection)
|
||||
// translate.el supplies the missing middle: EN content lemma -> target lemma.
|
||||
//
|
||||
// WHY an explicit table and not geometry: the live engram embedder
|
||||
// (nomic-embed-text) is English-only — cross-lingual nearest-neighbour routing
|
||||
// is near-random (ocean~dog 0.51 > ocean~oceano 0.43), and vocabulary-XX.el
|
||||
// carries no en_translation glosses. So the honest, deterministic, no-LLM bridge
|
||||
// is a wired lexicon. Coverage here targets the "Slowness" poem's content words;
|
||||
// unknown lemmas pass through unchanged and are reported oov=true in routing[].
|
||||
//
|
||||
// SACRED: polarity and neg_word are NEVER routed through the content bridge.
|
||||
// "never" localizes to "nunca"/"nunca"/"mai" as a negator, never to a lemma.
|
||||
//
|
||||
// Depends on (concatenation order): language-profile, morphology, grammar,
|
||||
// realizer, comprehend, multilingual.
|
||||
|
||||
// ── EN verb lemma -> target infinitive ────────────────────────────────────────
|
||||
fn tr_pred(lemma: String, lang: String) -> String {
|
||||
if str_eq(lang, "en") { return lemma }
|
||||
if str_eq(lang, "es") {
|
||||
if str_eq(lemma, "fight") { return "luchar" }
|
||||
if str_eq(lemma, "touch") { return "tocar" }
|
||||
if str_eq(lemma, "wait") { return "esperar" }
|
||||
if str_eq(lemma, "see") { return "ver" }
|
||||
if str_eq(lemma, "break") { return "romper" }
|
||||
if str_eq(lemma, "stay") { return "quedar" }
|
||||
if str_eq(lemma, "call") { return "llamar" }
|
||||
if str_eq(lemma, "run") { return "correr" }
|
||||
if str_eq(lemma, "chase") { return "perseguir" }
|
||||
if str_eq(lemma, "take") { return "tomar" }
|
||||
if str_eq(lemma, "carry") { return "llevar" }
|
||||
return tr_pred_fallback(lemma, "es")
|
||||
}
|
||||
if str_eq(lang, "pt") {
|
||||
if str_eq(lemma, "fight") { return "lutar" }
|
||||
if str_eq(lemma, "touch") { return "tocar" }
|
||||
if str_eq(lemma, "wait") { return "esperar" }
|
||||
if str_eq(lemma, "see") { return "ver" }
|
||||
if str_eq(lemma, "break") { return "quebrar" }
|
||||
if str_eq(lemma, "stay") { return "ficar" }
|
||||
if str_eq(lemma, "call") { return "chamar" }
|
||||
if str_eq(lemma, "run") { return "correr" }
|
||||
if str_eq(lemma, "chase") { return "perseguir" }
|
||||
if str_eq(lemma, "take") { return "tomar" }
|
||||
if str_eq(lemma, "carry") { return "levar" }
|
||||
return tr_pred_fallback(lemma, "pt")
|
||||
}
|
||||
if str_eq(lang, "it") {
|
||||
if str_eq(lemma, "fight") { return "lottare" }
|
||||
if str_eq(lemma, "touch") { return "toccare" }
|
||||
if str_eq(lemma, "wait") { return "aspettare" }
|
||||
if str_eq(lemma, "see") { return "vedere" }
|
||||
if str_eq(lemma, "break") { return "rompere" }
|
||||
if str_eq(lemma, "stay") { return "restare" }
|
||||
return tr_pred_fallback(lemma, "it")
|
||||
}
|
||||
return lemma
|
||||
}
|
||||
|
||||
// Second-tier verbs already covered by the older multilingual table.
|
||||
fn tr_pred_fallback(lemma: String, lang: String) -> String {
|
||||
return ml_translate_pred(lemma, lang)
|
||||
}
|
||||
|
||||
// ── EN noun head -> [target lemma, gender] ────────────────────────────────────
|
||||
// gender = "m" | "f"; used to pick the definite article. Empty lemma => unknown.
|
||||
fn tr_noun_pair(head: String, lang: String) -> [String] {
|
||||
let out: [String] = native_list_empty()
|
||||
if str_eq(lang, "es") {
|
||||
if str_eq(head, "ocean") { let out = native_list_append(out, "océano"); let out = native_list_append(out, "m"); return out }
|
||||
if str_eq(head, "root") { let out = native_list_append(out, "raíz"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "roots") { let out = native_list_append(out, "raíces"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "breaking") { let out = native_list_append(out, "ruptura"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "shoreline") { let out = native_list_append(out, "orilla"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "patience") { let out = native_list_append(out, "paciencia"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "wave") { let out = native_list_append(out, "ola"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "truth") { let out = native_list_append(out, "verdad"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "silence") { let out = native_list_append(out, "silencio"); let out = native_list_append(out, "m"); return out }
|
||||
return out
|
||||
}
|
||||
if str_eq(lang, "pt") {
|
||||
if str_eq(head, "ocean") { let out = native_list_append(out, "oceano"); let out = native_list_append(out, "m"); return out }
|
||||
if str_eq(head, "root") { let out = native_list_append(out, "raiz"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "roots") { let out = native_list_append(out, "raízes"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "breaking") { let out = native_list_append(out, "ruptura"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "shoreline") { let out = native_list_append(out, "costa"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "patience") { let out = native_list_append(out, "paciência"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "wave") { let out = native_list_append(out, "onda"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "truth") { let out = native_list_append(out, "verdade"); let out = native_list_append(out, "f"); return out }
|
||||
if str_eq(head, "silence") { let out = native_list_append(out, "silêncio"); let out = native_list_append(out, "m"); return out }
|
||||
return out
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// definite article for a gender/lang.
|
||||
fn tr_def_article(gender: String, lang: String) -> String {
|
||||
if str_eq(lang, "es") { if str_eq(gender, "f") { return "la" } return "el" }
|
||||
if str_eq(lang, "pt") { if str_eq(gender, "f") { return "a" } return "o" }
|
||||
if str_eq(lang, "it") { if str_eq(gender, "f") { return "la" } return "il" }
|
||||
return "the"
|
||||
}
|
||||
|
||||
// strip a leading English determiner, return the bare head noun (lowercased).
|
||||
fn tr_strip_det(np: String) -> String {
|
||||
let s: String = str_to_lower(np)
|
||||
let dets: [String] = native_list_empty()
|
||||
let dets = native_list_append(dets, "the ")
|
||||
let dets = native_list_append(dets, "a ")
|
||||
let dets = native_list_append(dets, "an ")
|
||||
let dets = native_list_append(dets, "my ")
|
||||
let dets = native_list_append(dets, "your ")
|
||||
let dets = native_list_append(dets, "his ")
|
||||
let dets = native_list_append(dets, "her ")
|
||||
let dets = native_list_append(dets, "its ")
|
||||
let dets = native_list_append(dets, "our ")
|
||||
let dets = native_list_append(dets, "their ")
|
||||
let dets = native_list_append(dets, "every ")
|
||||
let i: Int = 0
|
||||
let n: Int = native_list_len(dets)
|
||||
while i < n {
|
||||
let d: String = native_list_get(dets, i)
|
||||
let dl: Int = str_len(d)
|
||||
if str_len(s) > dl {
|
||||
if str_eq(str_slice(s, 0, dl), d) {
|
||||
return str_slice(s, dl, str_len(s))
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// translate an English object NP into a target NP with definite article.
|
||||
// Unknown head => pass the English head through (honest OOV), no article.
|
||||
fn tr_np(np: String, lang: String) -> String {
|
||||
if str_eq(np, "") { return "" }
|
||||
let head: String = tr_strip_det(np)
|
||||
let pair: [String] = tr_noun_pair(head, lang)
|
||||
if native_list_len(pair) < 2 { return head }
|
||||
let lemma: String = native_list_get(pair, 0)
|
||||
let gender: String = native_list_get(pair, 1)
|
||||
let art: String = tr_def_article(gender, lang)
|
||||
return art + " " + lemma
|
||||
}
|
||||
|
||||
// translate an English subject pronoun to the target pronoun (keeps person).
|
||||
fn tr_pronoun(agent: String, lang: String) -> String {
|
||||
let a: String = str_to_lower(agent)
|
||||
if str_eq(lang, "es") {
|
||||
if str_eq(a, "i") { return "yo" }
|
||||
if str_eq(a, "you") { return "tú" }
|
||||
if str_eq(a, "he") { return "él" }
|
||||
if str_eq(a, "she") { return "ella" }
|
||||
if str_eq(a, "we") { return "nosotros" }
|
||||
if str_eq(a, "they") { return "ellos" }
|
||||
}
|
||||
if str_eq(lang, "pt") {
|
||||
if str_eq(a, "i") { return "eu" }
|
||||
if str_eq(a, "you") { return "tu" }
|
||||
if str_eq(a, "he") { return "ele" }
|
||||
if str_eq(a, "she") { return "ela" }
|
||||
if str_eq(a, "we") { return "nós" }
|
||||
if str_eq(a, "they") { return "eles" }
|
||||
}
|
||||
return agent
|
||||
}
|
||||
|
||||
// localize a standalone negative adverb (SACRED). "never" -> preverbal negator.
|
||||
fn tr_neg_word(neg_word: String, lang: String) -> String {
|
||||
let w: String = str_to_lower(neg_word)
|
||||
if str_eq(w, "never") {
|
||||
if str_eq(lang, "es") { return "nunca" }
|
||||
if str_eq(lang, "pt") { return "nunca" }
|
||||
if str_eq(lang, "it") { return "mai" }
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── the faculty: EN text -> target surface ────────────────────────────────────
|
||||
// Parses EN (mature path), swaps content lemmas via the wired bridge, carries
|
||||
// SACRED polarity/neg_word untouched, and hands a target-lang spec to realize().
|
||||
fn translate_spec(text: String, tgt: String) -> [String] {
|
||||
let spec: [String] = parse_spec(text)
|
||||
let pred: String = slots_get(spec, "predicate")
|
||||
let pat: String = slots_get(spec, "patient")
|
||||
let agent: String = slots_get(spec, "agent")
|
||||
let negw: String = slots_get(spec, "neg_word")
|
||||
|
||||
let spec = slots_set(spec, "predicate", tr_pred(pred, tgt))
|
||||
let spec = slots_set(spec, "patient", tr_np(pat, tgt))
|
||||
let spec = slots_set(spec, "agent", tr_pronoun(agent, tgt))
|
||||
// SACRED: neg_word localized to a negator, polarity left exactly as parsed.
|
||||
let tw: String = tr_neg_word(negw, tgt)
|
||||
if !str_eq(tw, "") { let spec = slots_set(spec, "neg_word", tw) }
|
||||
let spec = slots_set(spec, "lang", tgt)
|
||||
return spec
|
||||
}
|
||||
|
||||
fn translate_line(text: String, tgt: String) -> String {
|
||||
return realize(translate_spec(text, tgt))
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// translate_negation_gate.el - EN -> ES/PT translation of the poem's negation
|
||||
// lines. The bar (Aug-13 python reference):
|
||||
// l11 "You never fought the ocean." -> "Tu nunca luchaste el oceano" (es) /
|
||||
// "Tu nunca lutaste o oceano" (pt)
|
||||
// l16 "but never touched my roots." -> "...nunca toco los raices" /
|
||||
// "...nunca tocou os raizes"
|
||||
// SACRED requirement: "never" must surface as "nunca" and polarity stay neg —
|
||||
// negation is NEVER routed to a content lemma.
|
||||
|
||||
fn tg_line(text: String) -> String {
|
||||
let spec: [String] = parse_spec(text)
|
||||
let pol: String = slots_get(spec, "polarity")
|
||||
let negw: String = slots_get(spec, "neg_word")
|
||||
let pred: String = slots_get(spec, "predicate")
|
||||
let pat: String = slots_get(spec, "patient")
|
||||
let es: String = translate_line(text, "es")
|
||||
let pt: String = translate_line(text, "pt")
|
||||
let out: String = "EN: " + text + "\n"
|
||||
let out = out + " spec: pol=" + pol + " neg_word=" + negw + " pred=" + pred + " patient=" + pat + "\n"
|
||||
let out = out + " ES: " + es + "\n"
|
||||
let out = out + " PT: " + pt + "\n"
|
||||
// SACRED check: if source is negative, "nunca" must appear in both surfaces.
|
||||
let es_ok: String = "n/a"
|
||||
if str_eq(pol, "neg") {
|
||||
let es_ok = "NUNCA-LOST"
|
||||
if str_contains(es, "nunca") { let es_ok = "nunca-ok" }
|
||||
}
|
||||
let out = out + " negation[es]: " + es_ok + "\n"
|
||||
return out
|
||||
}
|
||||
|
||||
fn run_translate_negation_gate() -> String {
|
||||
let rep: String = "==== ELP EN->ES/PT translation — negation lines ====\n"
|
||||
// Negation lines (SACRED nunca must hold):
|
||||
let rep = rep + tg_line("You never fought the ocean.")
|
||||
let rep = rep + tg_line("but never touched my roots.")
|
||||
let rep = rep + tg_line("I never saw the breaking.")
|
||||
// Affirmative controls:
|
||||
let rep = rep + tg_line("You waited like the shoreline.")
|
||||
let rep = rep + tg_line("I broke against your truth.")
|
||||
return rep
|
||||
}
|
||||
|
||||
println(run_translate_negation_gate())
|
||||
Reference in New Issue
Block a user