compiler: text keys (phase 26.2) - k"pause.resume" / kn"catch.count" of the builtin type Key (not a string, and no string one; == compares), its run-time value the key after a marker byte (1, or 2 for a plural); package.ludic's lang "assets/lang" en and a gettext .po reader; every key literal checked against en.po (a kn"" one wants a msgid_plural), trf / trn and a template's t('key', ...) against the English's holes, a component's template words and a @Text row's English as "English left" (warnings, english_left in ludic deps), a @Text Key field a compiled row leaves out filled with its derived key <registry>.<row>.<field> (or @TextKey's prefix; maps.<map>.<table>.<row>.<field> for a @PerMap row, checked), one English under several undescribed keys warned at en.po's line; the schema's "lang" (keys and their sites, unused, undescribed, split, and every other language's missing / fuzzy / extra); k"" in .lres data, @PerMap rows included (lres_key); ludic-fmt keeps a key literal whole; reseeded, bootstrap-cfree fixpoint holds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
82c652ca31
commit
cc1ea0f4ae
28 changed files with 139268 additions and 109746 deletions
224
selfhost/backend/emit_schema_lang.ludic
Normal file
224
selfhost/backend/emit_schema_lang.ludic
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
# emit_schema_lang.ludic — the schema's "lang" (emit_schema.ludic): what a language editor needs of
|
||||
# the keys (i18n.ludic). null when the program's package.ludic has no `lang` line; otherwise
|
||||
#
|
||||
# dir, source, file the languages' directory, the source language and its .po (null if missing)
|
||||
# keys every key used, sorted: its kind (code, template or data - the first site's, and
|
||||
# `kinds` all of them), every site as "file:line:col", whether en.po has it, its
|
||||
# English, whether it is a plural, and en.po's #. description (or null)
|
||||
# unused en.po's keys used nowhere
|
||||
# undescribed en.po's keys with no #. description
|
||||
# split one English under several keys: [{english, keys}] - each wants a description
|
||||
# languages every other .po in the directory: {code: {file, missing, fuzzy, extra}} - en.po's
|
||||
# keys it lacks (or leaves empty), its fuzzy ones, and what it has that en.po does not
|
||||
function sc_lang(b: Buf) -> void {
|
||||
if not g_i18n_on {
|
||||
buf_puts(b, "null")
|
||||
return
|
||||
}
|
||||
let en = g_i18n_en
|
||||
buf_puts(b, "{\n \"dir\": ")
|
||||
jq_put(b, g_i18n_dir)
|
||||
buf_puts(b, ",\n \"source\": ")
|
||||
jq_put(b, g_i18n_src)
|
||||
buf_puts(b, ",\n \"file\": ")
|
||||
if en == null { buf_puts(b, "null") } else { jq_put(b, g_i18n_en_path) }
|
||||
buf_puts(b, ",\n \"keys\": [")
|
||||
let used = ss_new(1024)
|
||||
sc_lang_keys(b, used)
|
||||
buf_puts(b, "\n ],\n \"unused\": [")
|
||||
if en != null { sc_lang_ids(b, en, used, 0) }
|
||||
buf_puts(b, "],\n \"undescribed\": [")
|
||||
if en != null { sc_lang_ids(b, en, used, 1) }
|
||||
buf_puts(b, "],\n \"split\": [")
|
||||
if en != null { sc_lang_split(b, en) }
|
||||
buf_puts(b, "\n ],\n \"languages\": {")
|
||||
sc_lang_others(b, en)
|
||||
buf_puts(b, "\n }\n }")
|
||||
}
|
||||
function sc_lang_keys(b: Buf, used: StrSet) -> void {
|
||||
let xs = new []int
|
||||
let keys = new []pointer
|
||||
var i = 0
|
||||
while i < len(g_ks_key) {
|
||||
push(xs, i)
|
||||
push(keys, `{g_ks_key[i]}\t{g_ks_file[i]}\t{itoa(1000000 + g_ks_line[i])}\t{itoa(1000000 + g_ks_col[i])}`)
|
||||
i += 1
|
||||
}
|
||||
sc_sort(xs, keys)
|
||||
var a = 0
|
||||
var first = true
|
||||
while a < len(xs) {
|
||||
let key = g_ks_key[xs[a]]
|
||||
var z = a + 1
|
||||
while z < len(xs) and (g_ks_key[xs[z]] == key) { z += 1 }
|
||||
let dummy = ss_add(used, key)
|
||||
sc_sep(b, first, " ")
|
||||
first = false
|
||||
buf_puts(b, "{\"key\": ")
|
||||
jq_put(b, key)
|
||||
buf_puts(b, ", \"kind\": ")
|
||||
jq_put(b, i18n_ks_name(g_ks_kind[xs[a]]))
|
||||
buf_puts(b, ", \"kinds\": [")
|
||||
var kd = 0
|
||||
var nk = 0
|
||||
while kd < 3 {
|
||||
var has = false
|
||||
var q = a
|
||||
while q < z {
|
||||
if g_ks_kind[xs[q]] == kd { has = true }
|
||||
q += 1
|
||||
}
|
||||
if has {
|
||||
if nk > 0 { buf_puts(b, ", ") }
|
||||
jq_put(b, i18n_ks_name(kd))
|
||||
nk += 1
|
||||
}
|
||||
kd += 1
|
||||
}
|
||||
buf_puts(b, "], \"sites\": [")
|
||||
var s = a
|
||||
while s < z {
|
||||
let j = xs[s]
|
||||
if s > a { buf_puts(b, ", ") }
|
||||
jq_put(b, `{g_ks_file[j]}:{itoa(g_ks_line[j])}:{itoa(g_ks_col[j])}`)
|
||||
s += 1
|
||||
}
|
||||
buf_puts(b, "]")
|
||||
let e = po_find(g_i18n_en, key)
|
||||
buf_puts(b, ", \"in_source\": ")
|
||||
sc_bool(b, e >= 0)
|
||||
buf_puts(b, ", \"english\": ")
|
||||
if e >= 0 { jq_put(b, g_i18n_en.first[e]) } else { buf_puts(b, "null") }
|
||||
buf_puts(b, ", \"plural\": ")
|
||||
sc_bool(b, e >= 0 and g_i18n_en.plural[e])
|
||||
buf_puts(b, ", \"description\": ")
|
||||
if e >= 0 { sc_str(b, g_i18n_en.desc[e]) } else { buf_puts(b, "null") }
|
||||
buf_puts(b, "}")
|
||||
a = z
|
||||
}
|
||||
}
|
||||
# en.po's ids, sorted: those used nowhere (what 0), or those with no description (what 1)
|
||||
function sc_lang_ids(b: Buf, en: PoFile, used: StrSet, what: int) -> void {
|
||||
let xs = new []int
|
||||
var i = 0
|
||||
while i < len(en.ids) {
|
||||
var take = false
|
||||
if what == 0 { take = not ss_has(used, en.ids[i]) }
|
||||
if what == 1 { take = en.desc[i] == null }
|
||||
if take { push(xs, i) }
|
||||
i += 1
|
||||
}
|
||||
sc_sort(xs, en.ids)
|
||||
i = 0
|
||||
while i < len(xs) {
|
||||
if i > 0 { buf_puts(b, ", ") }
|
||||
jq_put(b, en.ids[xs[i]])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
function sc_lang_split(b: Buf, en: PoFile) -> void {
|
||||
let xs = new []int
|
||||
let keys = new []pointer
|
||||
var i = 0
|
||||
while i < len(en.ids) {
|
||||
push(xs, i)
|
||||
push(keys, `{en.first[i]}\t{en.ids[i]}`)
|
||||
i += 1
|
||||
}
|
||||
sc_sort(xs, keys)
|
||||
var first = true
|
||||
var a = 0
|
||||
while a < len(xs) {
|
||||
var z = a + 1
|
||||
while z < len(xs) and (en.first[xs[z]] == en.first[xs[a]]) { z += 1 }
|
||||
if z - a > 1 and len(en.first[xs[a]]) > 0 {
|
||||
sc_sep(b, first, " ")
|
||||
first = false
|
||||
buf_puts(b, "{\"english\": ")
|
||||
jq_put(b, en.first[xs[a]])
|
||||
buf_puts(b, ", \"keys\": [")
|
||||
var k = a
|
||||
while k < z {
|
||||
if k > a { buf_puts(b, ", ") }
|
||||
jq_put(b, en.ids[xs[k]])
|
||||
k += 1
|
||||
}
|
||||
buf_puts(b, "]}")
|
||||
}
|
||||
a = z
|
||||
}
|
||||
}
|
||||
# every other language in the directory, against en.po
|
||||
function sc_lang_others(b: Buf, en: PoFile) -> void {
|
||||
let fs = pmk_list(g_i18n_dir, false)
|
||||
var first = true
|
||||
var i = 0
|
||||
while i < len(fs) {
|
||||
let f = fs[i]
|
||||
let n = len(f)
|
||||
if n > 3 and (f[n - 3 .. n] == ".po") and not (f[0 .. n - 3] == g_i18n_src) {
|
||||
let p = po_read(`{g_i18n_dir}/{f}`)
|
||||
if p != null {
|
||||
sc_sep(b, first, " ")
|
||||
first = false
|
||||
jq_put(b, f[0 .. n - 3])
|
||||
buf_puts(b, ": {\"file\": ")
|
||||
jq_put(b, p.path)
|
||||
buf_puts(b, ", \"missing\": [")
|
||||
sc_lang_diff(b, en, p, 0)
|
||||
buf_puts(b, "], \"fuzzy\": [")
|
||||
sc_lang_diff(b, en, p, 1)
|
||||
buf_puts(b, "], \"extra\": [")
|
||||
sc_lang_diff(b, en, p, 2)
|
||||
buf_puts(b, "]}")
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
# 0: en.po's keys p lacks or leaves empty; 1: p's fuzzy keys; 2: p's keys en.po does not have
|
||||
function sc_lang_diff(b: Buf, en: PoFile, p: PoFile, what: int) -> void {
|
||||
let names = new []pointer
|
||||
var i = 0
|
||||
if what == 0 and en != null {
|
||||
while i < len(en.ids) {
|
||||
let j = po_find(p, en.ids[i])
|
||||
if j < 0 or sc_lang_empty(p.strs[j]) { push(names, en.ids[i]) }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
if what == 1 {
|
||||
while i < len(p.ids) {
|
||||
if p.fuzzy[i] { push(names, p.ids[i]) }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
if what == 2 {
|
||||
while i < len(p.ids) {
|
||||
if po_find(en, p.ids[i]) < 0 { push(names, p.ids[i]) }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
let xs = new []int
|
||||
i = 0
|
||||
while i < len(names) {
|
||||
push(xs, i)
|
||||
i += 1
|
||||
}
|
||||
sc_sort(xs, names)
|
||||
i = 0
|
||||
while i < len(xs) {
|
||||
if i > 0 { buf_puts(b, ", ") }
|
||||
jq_put(b, names[xs[i]])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
# every form empty (the forms are joined by newlines)
|
||||
function sc_lang_empty(s: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(s) {
|
||||
if s[i] != '\n' { return false }
|
||||
i += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue