open-standards/aac/aac-rs/tools/pagesets/pslib/langs.py
Transaction Science abbe4becb6 feat(aac): two open page sets — Growing Core and Fixed Paths — as Open Board Format
Our own vocabularies in the styles of a growing core and a motor-plan set,
built from cited open sources, never from a vendor's words, positions or names.

Growing Core: one core board through eleven sizes (1x1 to 8x10). Every item
lives at one point of the screen and is drawn, at every size, in the cell that
contains it, so a word learned at 3x3 is in the same region at 8x10. The board
is full at every size and nothing that appeared ever goes. The first boards are
designed by hand; a solver places the rest by Fitzgerald region. Ten pages of
more words, each with Back in the top-left corner; social messages with
repairs; a Word forms page; hide-and-reveal at a fixed size from each word's
stage.

Fixed Paths: one meaning per location and one path per word, of 1 to 3 hits,
at 30, 60 and 84 locations. The heaviest words (spoken frequency, more for
Universal Core and Banajee 2003 words) take one hit in the easiest places for
a blend of touch, scanning and gaze; a page is closed by the place of the key
that opened it. No multi-meaning icon sequences. A vocabulary filter
(starter) hides the rest in place.

tools/pagesets: design sources (YAML), the solver (run once, committed as
layouts), and a reproducible build that writes 19 .obz packages, an index,
metrics and a per-language report to data/pagesets. aac-core tests/pagesets.rs
imports every package with the app's own importer and checks positions,
actions, word classes, that every symbol is the one the app draws, that each
button says what the app's own export would, the growth and path rules, and
language honesty.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 11:23:41 -04:00

110 lines
5 KiB
Python

"""What each of the app's 45 languages gets from the page sets, said honestly.
The sets are English. Every other language sees the app's glosses of the
English words (SPEC §4.1: GlossOnly), which are never that language's core
vocabulary: the words people use most differ from language to language, and a
word like "is" or "the" may have no gloss at all because the language has no
such word. This report says, per language, how much of each set has a gloss,
which words do not, and what native core-vocabulary work is needed.
"""
from __future__ import annotations
from .boards import Package
from .data import Data
# Published core-vocabulary studies the app already cites for these languages
# (aac-core vocabulary.rs CORE_COVERAGE_EVIDENCE): the shortest road from
# GlossOnly to a validated core is to adopt the language's own list, licence
# permitting.
PUBLISHED_CORE = {
"ko": "219 morphemes, 12 adults (Shin & Hill)",
"zh": "100 words, 28 speakers of mixed ages (Tsai)",
"he": "200 lexemes, 12 speakers",
"ar": "239 words, 161 speakers (Saudi Arabic)",
}
def words_of(pkgs: list[Package]) -> dict[str, set[str]]:
"""Speak cells by set (full variants), and the page keys and controls."""
out: dict[str, set[str]] = {}
for p in pkgs:
if p.variant != "full":
continue
s = out.setdefault(p.set, set())
for b in p.boards:
for btn in b.buttons:
if btn.action == "speak":
s.add(btn.key)
return out
def report(data: Data, pkgs: list[Package]) -> dict:
sets = words_of(pkgs)
keys = {
p.set: sorted({btn.key for b in p.boards for btn in b.buttons if btn.action == "navigate" and not btn.control})
for p in pkgs
if p.variant == "full"
}
counts = dict(data.spoken_ranking)
sense_words = sorted(w for s in sets.values() for w in s if data.lexicon[w].sense)
langs = []
for lang in data.languages:
code = lang["code"]
entry = {
"code": code,
"name": lang["english"],
"native_name": lang["native"],
"core_status": lang["core_status"],
"prediction_pack": lang["pack_quality"],
}
if code == "en":
entry["note"] = "The source language: the sets' words are chosen for English (ValidatedNativeCorpus)."
langs.append(entry)
continue
nav = data.nav_labels["languages"].get(code, {})
agreed = nav.get("review", {}).get("status") == "agreed"
disputed = nav.get("review", {}).get("disputed", [])
entry["controls"] = {
"back": nav.get("back") if agreed and "back" not in disputed else "English (no agreed word)",
"home": nav.get("home") if agreed and "home" not in disputed else "English (no agreed word)",
}
per_set = {}
for set_name, words in sets.items():
single = [w for w in words if " " not in w]
phrases = [w for w in words if " " in w]
has = [w for w in words if code in data.glosses(w)]
missing = sorted(w for w in single if code not in data.glosses(w))
weight_all = sum(counts.get(w.lower(), 0) for w in single)
weight_has = sum(counts.get(w.lower(), 0) for w in single if code in data.glosses(w))
keys_missing = sorted(k for k in keys[set_name] if code not in data.glosses(k))
per_set[set_name] = {
"words": len(words),
"glossed": len(has),
"glossed_share": round(len(has) / len(words), 3),
"glossed_share_by_spoken_frequency": round(weight_has / weight_all, 3) if weight_all else None,
"words_without_gloss": missing,
"phrases_without_gloss": sorted(w for w in phrases if code not in data.glosses(w)),
"page_keys_in_english": keys_missing,
}
entry["sets"] = per_set
work = ["a core vocabulary from speakers of the language (SPEC §4.1: until then GlossOnly)"]
if code in PUBLISHED_CORE:
work.append(f"adopt the published core list ({PUBLISHED_CORE[code]}) where its licence allows")
if not lang["pack_quality"]:
work.append("a prediction pack (none yet)")
elif lang["pack_quality"] == "sparse":
work.append("a larger frequency source (its pack is sparse)")
work.append("native review of the glosses (model-read only)")
work.append("whole-phrase translations of the social messages (glossed word by word today)")
entry["needs"] = work
langs.append(entry)
return {
"about": (
"The page sets are English. Every other language shows the app's glosses of the English "
"words: GlossOnly (SPEC §4.1), never that language's core vocabulary. A word with no gloss "
"shows in English. Every language listed needs native core-vocabulary work."
),
"words_with_a_sense_note": sense_words,
"languages": langs,
}