|
|
|
@@ -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))
|
|
|
|
|
}
|