feat(packages): ludic.i18n - gettext languages from a shipped list and a player's folder, exact lines, patterns whose holes are translated in turn, paragraphs a sentence at a time, plurals by Plural-Forms, X-Font atlases through a port

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 08:20:46 +03:00
parent c446c07a4c
commit 9e713bc39c
15 changed files with 980 additions and 0 deletions

View file

@ -0,0 +1,95 @@
# match.ludic - which pattern produces a string, the best one being the one that pins down the most
# English, and the text that filled each hole translated in turn
# does pattern p produce s? The holes land in caps, in hole order
function try_pattern(p: int, s: string) -> bool {
let n = len(s)
let l0 = pat_l0[p]
let nl = pat_nl[p]
let first = lits[l0]
let f_n = len(first)
let last = lits[l0 + nl - 1]
let l_n = len(last)
if nl - 1 > CAPS { return false }
if n < f_n + l_n + nl - 1 { return false } # every hole takes at least a byte
for k in 0 .. f_n { if s[k] != first[k] { return false } }
for k in 0 .. l_n { if s[n - l_n + k] != last[k] { return false } }
let lim = n - l_n
var pos = f_n
for g in 0 .. nl - 1 {
var stop = lim
var adv = 0
if g < nl - 2 {
let mid = lits[l0 + g + 1]
stop = i18n_find(s, lim, pos + 1, mid, len(mid))
if stop < 0 { return false }
adv = len(mid)
}
if stop <= pos { return false }
caps[g] = s[pos..stop]
pos = stop + adv
}
return true
}
# the best of a chain that produces s, or `best` as it was
function best_of(head: int, next: words, s: string, best: int) -> int {
var b = best
var c = head
while c > 0 {
let p = c - 1
var sc = -1
if b >= 0 { sc = pat_sc[b] }
if pat_sc[p] > sc and try_pattern(p, s) { b = p }
c = next[p]
}
return b
}
function pattern(s: string, depth: int) -> string {
hit = false
if pat_n == 0 { return s }
let n = len(s)
var best = -1
if n >= 4 {
best = best_of(Dict.get_or(pfx, s[0..4], 0), nx_p, s, best)
best = best_of(Dict.get_or(sfx, s[n - 4..n], 0), nx_s, s, best)
}
if n < 80 { best = best_of(gen_head, nx_g, s, best) }
if best < 0 { return s }
try_pattern(best, s)
let l0 = pat_l0[best]
let nl = pat_nl[best]
# copied out first: translating a hole may match another pattern and reuse caps
let raw = new []string
for g in 0 .. nl - 1 { push(raw, caps[g]) }
let cs = new []string
for g in 0 .. nl - 1 { push(cs, piece(raw[g], depth + 1)) }
let r = fill_holes(pat_out[best], l0, nl, cs)
# a pattern that gives the English straight back has translated nothing, and must not stop
# the sentence-by-sentence pass
if r == s { return s }
hit = true
return r
}
# the text that filled a hole: known as it stands, known without the English article the game
# glued on ("a deer", "the Dark King"), or itself a pattern
function piece(c: string, depth: int) -> string {
let e = Dict.get_or(exact, c, -1)
if e >= 0 { return out[e] }
let n = len(c)
var bare = -1
if n > 4 and (i18n_starts(c, "the ") or i18n_starts(c, "The ")) { bare = 4 }
else if n > 3 and (i18n_starts(c, "an ") or i18n_starts(c, "An ")) { bare = 3 }
else if n > 2 and (i18n_starts(c, "a ") or i18n_starts(c, "A ")) { bare = 2 }
if bare > 0 {
let e2 = Dict.get_or(exact, c[bare..n], -1)
if e2 >= 0 { return out[e2] }
}
if depth < 3 {
let r = pattern(c, depth)
if hit { return r }
}
return c
}