"Express" is also TD Snap's page-set name in the same field. The standard's reference app is now called the AAC Reference App: READMEs, crate docs, tool docs and the page-set design notes (which now say it was renamed). Its default title, without AAC_APP_TITLE, and its manifest name are "AAC — reference app". Code identifiers are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
492 lines
31 KiB
Markdown
492 lines
31 KiB
Markdown
# Phrase page sets: what they share
|
||
|
||
Four page sets for aac.aurasense.ai/app (the AAC Reference App, built on the
|
||
open AAC standard) and for any app that reads Open Board Format (OBF):
|
||
|
||
| Set | File | For | Design |
|
||
|---|---|---|---|
|
||
| Adult conversation | `adult.obz` | adults whose talking has changed: aphasia, ALS/MND, primary progressive aphasia, brain injury, Parkinson's | [DESIGN-adult.md](DESIGN-adult.md) |
|
||
| Intent first (12, 20, 40) | `intent-12.obz`, `intent-20.obz`, `intent-40.obz` | anyone who starts from what they want to do; also a printed book a partner reads from | [DESIGN-intent.md](DESIGN-intent.md) |
|
||
| Themed phrases | `themes.obz` | quick everyday messages, one 6 × 3 page per theme | [DESIGN-themes.md](DESIGN-themes.md) |
|
||
| First switches and messages | `emergent.obz` | first communication and first switch use | [DESIGN-emergent.md](DESIGN-emergent.md) |
|
||
|
||
This page covers what the four have in common: how they are written and
|
||
built, the OBF they produce (and the `ext_aurasense_*` extensions they use or
|
||
propose), how their translations were checked, their licences, how the app
|
||
should offer them, and the decisions they need from the founder.
|
||
|
||
The design follows the gap analysis of 2026-10-04 (`REPORT.md` on the
|
||
model-library drive, Part 1 and §1.1, §2.2, §2.7, §2.10–2.12, §4 items 8, 10,
|
||
13, 14 and §5). Every word, layout and name here is ours. No proprietary
|
||
page set was copied, and none is named on any board: the generator refuses
|
||
a board that names one (see *Guards* below).
|
||
|
||
## Sources and build
|
||
|
||
```
|
||
aac-rs/tools/pagesets/phrases/
|
||
sources/adult.yaml intent.yaml themes.yaml emergent.yaml the sets, by hand
|
||
build.py validate the sources; build the packages and catalogue
|
||
review.py the two-reader translation check, round by round
|
||
translations/<lang>.json every text's translation, status and evidence
|
||
translations/review/<lang>/round<N>/ each round's record (see Languages)
|
||
reports/sets.md symbols.md translation-status.md review aids, generated, committed
|
||
LICENSE.md CC BY-SA 4.0 for the sources and every package built from them
|
||
aac-rs/crates/aac-core/tests/pagesets_phrases.rs the import test
|
||
```
|
||
|
||
The packages are **generated, not committed**. The site build makes them:
|
||
|
||
```bash
|
||
cd aac-rs/tools/pagesets/phrases
|
||
python3 build.py --check # fail on bad sources, a non-reproducible build or stale reports
|
||
python3 build.py --out "$SITE/pagesets/phrases" --hash-names
|
||
```
|
||
|
||
`--out DIR` writes `DIR/<set>.obz` (English, with the pictures and every
|
||
agreed translation), `DIR/<lang>/<set>.obz` (one language; `<set>.f.obz` and
|
||
`<set>.m.obz` where the speaker's gender changes the words), `DIR/index.json`
|
||
(the catalogue: families, sets, sizes, descriptions, licence, each file with
|
||
its size, sha256 and how many of its cells are still in English, and each
|
||
language's status) and `DIR/LICENSE.md`. `--hash-names` names packages
|
||
`<set>.<sha256-12>.obz` for a cache that keeps files for hours.
|
||
`--check [--out DIR]` builds twice in memory and fails if the bytes differ,
|
||
if `reports/` is stale, or (with `--out`) if DIR differs from a fresh build.
|
||
`--reports` rewrites `reports/`; `--strings` writes `work/strings.json` for
|
||
translators. The interface matches the core page sets' `tools/pagesets/build.py`
|
||
(`--check`, an output directory, deterministic output), so one catalogue can
|
||
list both; this generator's `index.json` is self-describing (`version`,
|
||
`generator`) for that.
|
||
|
||
Today a full build is 99 packages, 28 MB, of which the six English packages
|
||
(with pictures) are 5 MB and each language package 100–450 KB.
|
||
|
||
Python 3.11 or later with PyYAML. The build reads only the app's own symbol
|
||
index (`crates/aac-core/src/symbols/index.json`) and sprites
|
||
(`crates/aac-web/public/symbols/`); it never touches the network, and the
|
||
same sources always give the same bytes (UUIDv5 ids, a fixed zip date,
|
||
sorted output).
|
||
|
||
### The source format
|
||
|
||
A source file has `set` (name, description, version, licence, which symbol
|
||
sets it may draw from, the root board), a `palette` of named colours,
|
||
`cells` (named cells used as `$name`), optional `frames` (cells placed on
|
||
every board of the set), and `boards` (or, for the intent-first set,
|
||
`sizes` and `sections`). A board is laid out by `rows`, each a list of cells
|
||
from left to right, so the layout reads as it looks. A cell is:
|
||
|
||
| Written as | Means |
|
||
|---|---|
|
||
| `"I'm tired."` | a message: shown and said |
|
||
| `$yes` | the named cell `yes` |
|
||
| `~` | an empty place, kept empty (SPEC §2: a blank keeps the grid) |
|
||
| `{label, say}` | shown `label`, said `say` |
|
||
| `{add: "I want to"}` | words added to the message without being said (a sentence starter) |
|
||
| `{label, go: board}` | a link; with `say` too, the words are added and then the board opens |
|
||
| `{label, do: backspace \| clear \| speak}` | a message-bar command |
|
||
| `symbol: concept` | the picture: a concept of the app's symbol index (it must exist) |
|
||
| `want_symbol: concept` | a picture we want but the index does not have (listed in `reports/symbols.md`) |
|
||
| `fill: {words, photo, recording, hint}` | a template for the person to complete |
|
||
| `seq: {lines, order, at_end, reset_after_minutes}` | each press says the next line |
|
||
| `effect: sparkle \| bounce \| confetti` | a short animation with the words |
|
||
| `src: universal-core` | where the words came from, if not written for this project |
|
||
| `note:` | context for translators (not written into the packages) |
|
||
|
||
A board may say `popup: true` (it closes after a choice), `speak_only: true`
|
||
(its cells speak and never build a message), `parent:` (where Back goes;
|
||
otherwise the first board that links to it) and `description:` (a note to
|
||
the partner). YAML 1.1's other booleans are switched off: `yes`, `no`, `on`
|
||
and `off` are words.
|
||
|
||
### Guards
|
||
|
||
`build.py` refuses to build when:
|
||
|
||
- a cell is outside its grid, two cells share a place, or a link leads to a board that does not exist;
|
||
- a board cannot be reached from the root;
|
||
- a symbol concept is not in the app's index, or not in a set the page set may use (the adult set uses pictograms only: Mulberry, then OpenMoji, no Bliss);
|
||
- a sequence has fewer than two lines, names a cell that does not speak, or has an unknown order or ending;
|
||
- any text names a proprietary product (`PROPRIETARY_NAMES`: PODD, TD Snap, Proloquo, LAMP, Unity, Minspeak, Gateway, WordPower, TouchChat, Lingraphica, Grid 3, BIGmack, Step-by-Step, PECS, Boardmaker, PCS, …);
|
||
- any text reads as a medical claim (`CLAIM_WORDS`: diagnose, treatment, therapy, cure, heal, rehabilitate, recover, clinically, prevents, …). The sets are for wellness, research and clinical-trial use; they are not a medical device.
|
||
|
||
## The OBF the generator writes
|
||
|
||
Each package is a `.obz`: a zip, stored without compression (what the app's
|
||
own importer reads directly), of `manifest.json`, `boards/<uuid>.obf` and,
|
||
in the English package, `images/<symbol>.svg`. Ids are UUIDv5 of the set,
|
||
board and position, so a rebuilt board keeps its ids, and every language's
|
||
package has the same board and cell ids in the same places (the test checks
|
||
this: changing language never moves a word, SPEC §3).
|
||
|
||
Standard OBF fields: `format`, `id`, `locale`, `name`, `description_html`,
|
||
`license`, `grid` (`rows`, `columns`, `order`), `buttons` (`id`, `label`,
|
||
`vocalization`, `label_locale`, `vocalization_locale`, `load_board`,
|
||
`action`, `image_id`, `background_color`, `border_color`, `translations`),
|
||
`images` (`id`, `content_type`, `width`, `height`, `path`, `symbol`,
|
||
`license`) and `sounds`.
|
||
|
||
### Extensions the app already reads or writes
|
||
|
||
| Field | Where | Meaning |
|
||
|---|---|---|
|
||
| `ext_aurasense_provenance` | board, button | where the words came from: `source_label` / `source_vocalization` (the stored words), `words_locale` (the words are in this language, kept exactly as written, never glossed: the app's own-words cell), `translation` (`"agreed by two model readers; native review pending"`, or `"pending: shown and said in English"`), `english` (the English source), `source` (file, board and position in the sources), `symbol_wanted`, `words_source` |
|
||
| `ext_aurasense_locked` | board, button | `false`: the person may change everything |
|
||
| `ext_aurasense_text_color` | button | the text colour |
|
||
| `ext_aurasense_action: "buffer"` | button | add the words to the message without saying them (sentence starters) |
|
||
| `ext_aurasense_symbol` | button | the picture, as a concept of the symbol index (the app draws it in the person's chosen set and skin tone; Track B reads and writes it) |
|
||
| `images[].symbol` + `license` | image | the symbol set and file, with the set's licence, attribution and conditions |
|
||
|
||
### Extensions these sets add
|
||
|
||
A client that does not know them still works: each falls back to plain OBF.
|
||
The **App** column is the state on `aac/pagesets-phrases`: *acts on it*
|
||
(`aac_core::behaviours`, `aac-web` `sequences.rs`, `behaviours.rs`,
|
||
`actions.rs`, `scan.rs`, `switches.rs`, `pointing.rs`), or *kept* (on the
|
||
cell or board, written back on export, and named once in the import's
|
||
warning: "These boards use features this version can't do yet: … They are
|
||
kept.").
|
||
|
||
| Field | Where | What the app does | Fallback | App |
|
||
|---|---|---|---|---|
|
||
| `ext_aurasense_sequence` | button | each press says the next line; full behaviour in [DESIGN-emergent.md](DESIGN-emergent.md#the-sequence-extension) | `vocalization` is the first line | acts on it |
|
||
| `ext_aurasense_effect` | button | a short visual effect with the words (DESIGN-emergent.md) | none | kept ("animations") |
|
||
| `ext_aurasense_behaviour.after_choice: "return"` | board | a popup: after a cell that says or adds words, go back to the board that opened this one | stays; a Back or Start cell is on the page | acts on it |
|
||
| `ext_aurasense_behaviour.message: "speak_only"` | board | its cells speak at once and are never added to the message | words are added too | acts on it |
|
||
| `ext_aurasense_link_says: "I want"` | button with `load_board` | add these words to the message as a speaking cell would, then open the board (the words themselves, as well as in `vocalization`: a reader that makes the button a link keeps no vocalization, but keeps this field) | the link opens; the words are not added | acts on it |
|
||
| `ext_aurasense_fill_in` | button | `{words, photo, recording, hint}`: a template cell; Edit mode marks it "to fill in" and lists every such cell after the set is added; the hint is shown in the cell editor; editing the cell drops the mark | the cell says its words (`…` is a pause) | kept ("cells to fill in") |
|
||
| `ext_aurasense_book.page` | board | page number in a printed book | printing numbers pages itself | kept quietly (the app prints books and numbers them itself) |
|
||
| `ext_aurasense_book_goto` | button with `load_board` | the page it links to, printed as "→ 12" | none | kept quietly (same) |
|
||
| `ext_aurasense_speaker_forms` | button, in a language's f/m packages | `{f: {label, vocalization, lines}, m: {…}}` for a word that changes with who says it: the app puts the person's form there, or leaves the word out while they would rather not say | the package's own form | acts on it |
|
||
| `translations.<lang>.ext_aurasense_speaker_forms` | button | `{f: {label, vocalization}, m: {…}}` where the speaker's gender changes the words | that language's words are absent: English |
|
||
| `translations.<lang>.ext_aurasense_sequence_lines` | button | a sequence's lines in that language | English lines |
|
||
| `manifest.ext_aurasense_page_set` | manifest | `key`, `name`, `version`, `locale`, `speaker_forms` | none |
|
||
|
||
**The importer defect, fixed.** `aac_core::obf` used to accept every
|
||
`ext_aurasense_*` field silently and keep none it did not know, so a
|
||
sequence or popup was lost without a word (SPEC §8 forbids it). It now
|
||
keeps every field it does not map — `ext_aurasense_*` or any other — on
|
||
`Cell::extra` / `Board::extra`, writes them back on export, and adds one
|
||
warning per import naming, in plain words, the features it keeps but does
|
||
not act on. A few fields with no meaning for the app (`url`, `data_url`,
|
||
`description`, `description_html`, `default_layout`, `default_locale`,
|
||
`background`, and the position fields `top`, `left`, `width`, `height`)
|
||
are kept without a warning. The test checks the warning, and that
|
||
sequences, links that say words and board behaviours survive an import.
|
||
|
||
### SPEC text (draft, after 0.2.0)
|
||
|
||
Approved and now in `aac/SPEC.md` (§2.2, §2.3, §2.4, §5.9 and the §8
|
||
addition, with the summary under "Proposed (draft, after 0.2.0)"):
|
||
|
||
> **§2.2 Sequences (proposed).** A cell **MAY** carry a sequence: an ordered
|
||
> list of two or more lines, an order (`in_order` or `shuffle`), what
|
||
> happens after the last line (`start_again` or `stay`), whether the lines
|
||
> are added to the message (default: not), and an optional idle time after
|
||
> which it starts again. Each activation of the cell says the next line. A
|
||
> line **MAY** name another cell of the same board, whose current words (and
|
||
> recording) it then says. Where the next line is kept is the person's
|
||
> state, not the board's: using a sequence **MUST NOT** change the board.
|
||
> A client that cannot play a sequence **MUST** say the cell's own words
|
||
> (its first line) and **MUST** keep the sequence when it saves the board.
|
||
> Undo after a line **MUST** step the sequence back. A press during a line
|
||
> **MUST** be honoured (the person sets the pace); the hold and release
|
||
> times of §5 still apply.
|
||
>
|
||
> **§2.3 Board behaviours (proposed).** A board **MAY** declare that it
|
||
> closes after a choice (a popup) or that it speaks without building a
|
||
> message. After a cell of a popup that says or adds words is chosen, a
|
||
> client **MUST** return to the board that opened it (a popup's continuation
|
||
> pages count as the popup). The person **MUST** be able to turn popups off
|
||
> for all boards; the choice travels with their settings. This is a
|
||
> property of the vocabulary's design and returns to the board that opened
|
||
> the popup, not home: it is not the "go home after each word" option of
|
||
> §5.6 item 6, which stays the person's alone and, where they have chosen
|
||
> it, wins over a popup's return.
|
||
>
|
||
> **§2.4 Links that say words (proposed).** A cell that goes to another
|
||
> board **MAY** also carry words; a client **MUST** add them to the message
|
||
> as a speaking cell would, then open the board.
|
||
>
|
||
> **§5.9 One target (proposed).** When a scan has a single target — a
|
||
> one-cell board, with the bar and the navigation not scanned (§5.5, §5.6
|
||
> let the person choose that) — every press of a switch **MUST** select it
|
||
> at once, and a touch or dwell anywhere on the board **MUST** select it.
|
||
>
|
||
> **§8 addition (proposed).** An extension field a reader does not act on
|
||
> **MUST** be kept with the board or cell (§2) and reported once as a
|
||
> warning, so that a sequence, popup or other behaviour is never lost
|
||
> without a word.
|
||
|
||
## Symbols
|
||
|
||
Pictures are chosen by concept from the app's own symbol index (472
|
||
concepts on 2026-10-05: Mulberry, OpenMoji, Bliss, all CC BY-SA 4.0), never
|
||
by inventing ids. Each set says which sets it may draw from: all four use
|
||
Mulberry, then OpenMoji (pictograms); none uses Bliss, which is a semantic
|
||
symbol language rather than a picture, and which a person can still choose
|
||
in the app (the app draws `ext_aurasense_symbol` in the person's own set).
|
||
|
||
Many adult concepts have no symbol yet (throat, chest, suction, frustrated,
|
||
appointment, …): those cells show their words alone, and an adult may add a
|
||
photo instead. `reports/symbols.md` lists every concept wanted and where.
|
||
Adding them is a job for `tools/symbols` (a confident match only: a wrong
|
||
picture is worse than none), after which the sources name them with
|
||
`symbol:`.
|
||
|
||
The English packages carry the pictures (each symbol once, as
|
||
`images/<id>.svg`, cut from the app's sprite with its set's attribution);
|
||
the language packages name each symbol by concept only, which the app draws
|
||
from its own sprites. That keeps each language package small (100–450 KB);
|
||
a language package opened in another OBF app shows words without pictures. `build.py` can embed
|
||
pictures in every package if that is wanted (one condition in `Package.button`).
|
||
|
||
## Languages
|
||
|
||
English is the source. A translation is used only where **two independent
|
||
readers agree**:
|
||
|
||
1. **Draft.** A drafter (reader 1) translates each text, seeing the English
|
||
and where and how it is used (`review.py prepare`): what a native speaker
|
||
would say, short and spoken; polite address in the adult set, familiar in
|
||
the emergent set; a wording that does not mark the speaker's gender
|
||
wherever one is natural, and otherwise both forms (`f`, `m`) — never
|
||
slash forms.
|
||
2. **Blind back-translation.** A second reader, who never sees the English
|
||
(`review.py blind` writes only the translations, with each text's role),
|
||
translates the drafter's words back into English and says how they read:
|
||
grammar, register, and whose gender they mark.
|
||
3. **Judge.** The English, the translation and the back-translation are
|
||
compared (`review.py judge`); "agree" only when the back-translation
|
||
means what the English means, with nothing added or lost, in natural
|
||
words.
|
||
4. **Merge.** Agreed texts become `"agreed"`; the rest `"disputed"`, with
|
||
the reason (`review.py merge`). A disputed text goes to a second round:
|
||
the drafter revises it with the reason, and a fresh blind reader checks
|
||
again. What is still disputed stays in English.
|
||
|
||
The status of every text in every language is data:
|
||
`translations/<lang>.json` (`status`, `round`, `text` or `forms`,
|
||
`back_translation`, `notes`), summarised in
|
||
`reports/translation-status.md` and in each build's `index.json`. Each
|
||
round's record is kept under `translations/review/<lang>/round<N>/`:
|
||
`draft-input.json` (what the drafter saw), `draft.json`, `blind-input.json`
|
||
(what the blind reader saw: no English), `back.json`, `verdict.json`.
|
||
Every reader was an AI model, so every agreed text says
|
||
**"agreed by two model readers; native review pending"**, in the
|
||
translation files and in the packages' provenance. A native speaker's review
|
||
is still wanted for every text.
|
||
|
||
**Result (2026-10-05).** 1,075 English texts; three rounds.
|
||
|
||
| | es | fr | de | pt | it | zh | ja | ar | hi | ru |
|
||
|---|---|---|---|---|---|---|---|---|---|---|
|
||
| Agreed | 1,075 | 1,075 | 1,075 | 1,075 | 1,075 | 1,075 | 1,074 | 1,074 | 1,075 | 1,075 |
|
||
| Disputed in round 1 | 10 | 17 | 9 | 9 | 6 | 15 | 29 | 21 | 11 | 6 |
|
||
| Texts with speaker forms | 29 | 20 | 0 | 30 | 24 | 0 | 0 | 0 | 14 | 15 |
|
||
|
||
Still in English after round 3: Japanese "OK" (a feelings word; every
|
||
wording offered could be heard as "so-so" or a polite "no thanks") and
|
||
Arabic "I did that for … years." (one cell of the adult set's work page).
|
||
What round 1 caught, among others: Spanish "Eso me pone" for the starter
|
||
"That makes me" (sexual slang in Spain), "Salir" for "Going out" (reads as
|
||
"Exit"), "Está demasiado alto" for "It's too loud" ("too high"); Hindi कल
|
||
for both "yesterday" and "tomorrow"; Japanese requests that could be heard as
|
||
about the listener ("brush/wash my…"); Russian «Мне очень неудобно» for "I'm
|
||
very uncomfortable" (usually "I'm embarrassed"); Arabic الحمام unvoweled
|
||
(bathroom or pigeons). The readers also showed three English texts were
|
||
unclear in every language, and they were changed: "Adult home" (a care
|
||
home) became "Home"; "My own card" (an ID card) became "In my own words";
|
||
three theme starters that no word on their page completed were rewritten.
|
||
|
||
Two blind readers (Spanish, Arabic) were stopped part-way by the model
|
||
provider's content filter; Spanish was rerun as it was, Arabic as four
|
||
readers of one part each. Neither saw the English.
|
||
|
||
A cell is shown and said in one language: if any of its texts (label,
|
||
message, sequence lines) is not agreed, the whole cell stays in English
|
||
(an empty translation is allowed only for a link's spoken words that the
|
||
language does not need, such as the English "to"; a label is never blank),
|
||
marked `label_locale: "en"` / `vocalization_locale: "en"`, and its board's
|
||
`description_html` carries a visible note, in the board's language: "Some
|
||
messages on this board are still in English: their translation has not yet
|
||
been agreed by two readers."
|
||
|
||
Done first: the ten largest app languages (es, fr, de, pt, it, zh, ja, ar,
|
||
hi, ru). **Pending** (English only, listed in `index.json`): nl, ko, he, ur,
|
||
fa, bn, ta, te, ml, mr, kn, gu, pa, th, vi, id, ms, tl, pl, uk, ro, cs, sv,
|
||
da, nb, fi, sw, am, ha, yo, zu, tr, el, hu. Adding one: add it to
|
||
`LANGUAGES` in `build.py` and `LANGUAGE_NOTES` in `review.py`, run the
|
||
four steps (`review.py prepare/blind/judge/merge`), and build.
|
||
|
||
**What translation cannot fix.** Sentence starters and words are combined
|
||
in the order the person picks them ("I want" + "to eat" + "an apple"). In
|
||
languages whose word order differs — Japanese and Hindi put the verb last,
|
||
German often does — the combined message is telegraphic; each language's
|
||
drafter shaped the fragments to survive it (DESIGN-intent.md lists how).
|
||
Phrase cells (whole messages) do not have the problem. Where a language
|
||
marks a referent's gender in a label ("Friend", "Doctor", "Nurse" on the
|
||
People templates), the drafters used the usual default and noted it: those
|
||
are templates the person fills in and may rename.
|
||
|
||
## Licences
|
||
|
||
- The page sets (words, layouts, packages): **CC BY-SA 4.0**, author
|
||
"AuraSense Tech Corporation and the open-standards AAC contributors"
|
||
(`tools/pagesets/phrases/LICENSE.md`, copied into every build). ShareAlike because the English
|
||
packages embed CC BY-SA symbols, and so that improvements stay open.
|
||
- Symbols: Mulberry Symbols, OpenMoji, CC BY-SA 4.0, each image carrying its
|
||
set's licence, attribution and conditions (Mulberry's symbols must stay
|
||
free of charge).
|
||
- Words: written for this project unless a cell says `src:`. The only
|
||
borrowed list is the Universal Core vocabulary (21 single words on the
|
||
40-cell intent-first start page; CC BY 4.0 per UNC CLDS, CC BY-SA 4.0 per
|
||
Project Core: treated as BY-SA, words only, no vendor symbols).
|
||
- Nothing from PCS, SymbolStix, Widgit, METACOM, ARASAAC or Sclera; no
|
||
proprietary vocabulary's words, positions, wording or names (§5 of the
|
||
report). The design notes name products only to say what was not copied.
|
||
|
||
## How the app should offer them
|
||
|
||
**What is built on this branch.** The app acts on the sets' behaviours:
|
||
sequences (by touch, switch scanning with one or two switches, pointer and
|
||
gaze dwell; "2 of 4" shown; Undo on the message bar's Delete; Modeling
|
||
shows the next line without moving on; the partner menu's "Start the lines
|
||
again" and "Back one line"; each line can be recorded in the person's own
|
||
voice from the cell editor, through message banking), popups that close
|
||
after a choice, speak-only boards, links that say words, one-target pages
|
||
(§5.9), a large scan highlight, and the profile setting "Words about
|
||
myself" (`AacProfile::speaker_form`; Settings → Language, shown for es, fr,
|
||
pt, it, hi and ru only, never assumed). The place in each sequence is the
|
||
person's (`localStorage` `aac_sequence_places`, per profile), never in the
|
||
board. The English cells of a phrase package — the English package taken
|
||
for a language without its own, and the cells still in English inside a
|
||
language's package — are marked as English words written as they are
|
||
(`lang` `"en"`, `pagesets::prepare`): shown in English, marked, said by an
|
||
English voice, never glossed word by word and never hidden. "Words about
|
||
myself" can be declined ("I'd rather not say"): each word that changes with
|
||
who says it is then left out, hidden in place and marked in Edit mode
|
||
"Write this your way", until a form is chosen in Settings, when those words
|
||
appear in it. A board's description (its note to the people around the
|
||
person: what it is for, that nothing is sent, that it does not replace
|
||
alarms or monitoring) is shown as a line above the board. Pictures: when a person exports boards for another app, the export
|
||
already embeds each cell's symbol as a `data:` URI from the chosen set's
|
||
sprite (`Cell::symbol` → `symbols::symbol_for_cell` →
|
||
`ObfExportOptions::sprites`), so nothing more was needed there.
|
||
|
||
**In the shared chooser.** The sets are offered by the core page-set
|
||
catalogue and chooser (`tools/pagesets/catalog.py`, `aac_core::pagesets`,
|
||
`aac-web` `pagesets.rs` and `catalogue_sets.rs`), beside Growing Core and
|
||
Fixed Paths, at setup ("Words to start with") and in Settings → Words and
|
||
boards. There is no second chooser.
|
||
|
||
1. **The generator.** `build.py --out DIR` writes, beside the packages,
|
||
`DIR/families.json` in the catalogue's generator contract: four
|
||
families (`phrases-adult`, `phrases-intent`, `phrases-themes`,
|
||
`phrases-emergent`, `chooser: "phrases"`), one package per set,
|
||
language and speaker form (99), each with `lang`, `gender` (`"f"` /
|
||
`"m"` / null), `size` (`"12"`, `"20"`, `"40"` for intent first, else
|
||
`""`), `rows`, `cols`, `home`, `label`, `description`, `boards`, `cells`
|
||
and `cells_in_english`; `languages[code]` is `GlossOnly` with the share
|
||
agreed (a translation of English phrases is a gloss, SPEC §4.1, however
|
||
good). `tools/pagesets/generators.json` lists it, and
|
||
`data/pagesets/index.json` (the catalogue compiled into the app) lists
|
||
all six families, 118 packages; `catalog.py --check` fails a build whose
|
||
packages differ. The site's build puts the packages in
|
||
`/app/models/pagesets/`.
|
||
2. **By language and speaker form.** The chooser takes the package with
|
||
`Family::for_person(size, variant, locale, speaker_form)`: the person's
|
||
language (exact tag, then base), in their form where that language's
|
||
packages differ by it, else the English one. Where a set's words in the
|
||
person's language change with who says them (`Family::
|
||
speaker_form_matters`) and `AacProfile::speaker_form` is not set, setup
|
||
shows "Words about myself" with the set and will not start until it is
|
||
answered; Settings asks it when Add is pressed and adds the set once it
|
||
is answered. Never assumed; the answer is kept on the profile and can be
|
||
changed in Settings → Language, which shows the question only for a
|
||
language where some set's words differ (`phrase_sets::
|
||
speaker_form_matters`, from the catalogue).
|
||
3. **How far the language goes.** Each family says what it is, its
|
||
sources and licence, and, for a language other than English, its
|
||
`language_note` and, for the chosen size, "All of it is in Spanish." or
|
||
"1 of its 366 cells is still in English, and says so." from the
|
||
package's `cells_in_english`; a language with no package of its own
|
||
gets the note alone (in English for now).
|
||
4. **Adding.** `pagesets::add` / `start_with` → `prepare` →
|
||
`obf::import_obz`, `give_new_ids`, `editing::add_boards_as_page_set`;
|
||
the import's warning is shown with the result (each package keeps, and
|
||
names, page numbers for a printed book; all but Themed phrases, cells to
|
||
fill in; First switches, animations). Each board keeps its licence
|
||
(CC BY-SA), so re-sharing keeps the attribution.
|
||
5. **After adding.** If the set has `ext_aurasense_fill_in` cells (adult:
|
||
names, photos, scripts, own voice; emergent: own message, news, photo
|
||
choices), open Edit mode's list of them: "Fill in your details". For the
|
||
adult set, offer later "Make *Simple page* my home" (the staged plan in
|
||
DESIGN-adult.md).
|
||
6. **Setup.** The setup wizard can offer the matching set by the path the
|
||
person picks: an adult whose talking has changed → Adult conversation; a
|
||
first switch user → First switches and messages; someone starting out →
|
||
Intent first (12); a reader who wants quick phrases → Themed phrases.
|
||
7. **Behaviours.** Built (see above), except fill-in badges and effects,
|
||
which are kept and named in the import's warning. In a client without
|
||
them the sets still work: sequences say their first line, popups stay
|
||
open (every page has Back or Start), speak-only boards also add to the
|
||
message, links that carry words only open the page. Track A's
|
||
per-person setting "After a word on another board, go back to Home"
|
||
(`AccessPrefs::return_home`) wins over a popup's return to its opener;
|
||
the person can also keep popups open (`AccessPrefs::popups_close`,
|
||
Access settings → Going back).
|
||
|
||
## Tests
|
||
|
||
`crates/aac-core/tests/pagesets_phrases.rs` runs `build.py --out` into
|
||
Cargo's per-test temporary directory (it needs Python 3.11+ with PyYAML;
|
||
`AAC_PYTHON` names the interpreter), then checks every package in the
|
||
catalogue, English and every language and speaker form:
|
||
|
||
- the catalogue lists all six sets in English and ten languages, its files
|
||
match the directory, and its sizes are the files';
|
||
- every package validates with the independent `open_board_format` crate
|
||
(no errors), and imports with the app's own `obf::import_obz` with no
|
||
warnings, every board present and the root first;
|
||
- every cell sits inside its grid, one per place; every link lands on a
|
||
board of the package; every board is reachable from the root;
|
||
- every `ext_aurasense_symbol` is a concept of the app's symbol index; every
|
||
picture names a symbol its set has, is in the package, and carries its
|
||
CC BY-SA licence;
|
||
- every language's package has the same boards with the same cell ids in
|
||
the same places as the English one;
|
||
- every cell of a language package is either wholly in that language (and
|
||
says it was agreed by two readers) or marked as English, and a board with
|
||
English left on it says so;
|
||
- imported with the app's importer, a translated cell is the person's own
|
||
words in that language (`Cell::lang`), an English cell is an English key,
|
||
and every chosen symbol is kept (`Cell::symbol`);
|
||
- sequences have two lines or more, valid order and ending, lines that name
|
||
cells of the same board, and say their first line to a client without
|
||
the extension; board behaviours are only the two defined;
|
||
- in the intent-first sets, each operational command (Oops, I'm finished,
|
||
Back to the start, Go back, Turn the page) is in one place on every page.
|
||
|
||
`build.py --check` adds: the sources are valid (every guard above), two
|
||
builds are byte-identical, and `reports/` is current.
|
||
|
||
## Decisions for the founder
|
||
|
||
1. **Speaker gender.** *Decided:* "Words about myself: feminine /
|
||
masculine", shown only for es, fr, pt, it, hi, ru, asked when a set in
|
||
one of them is added, no default, changeable in Settings. Built.
|
||
2. **Address.** Adult set: polite address (usted, vous, Sie, Lei, Вы, です/ます);
|
||
emergent set: familiar; intent and themes: familiar with family and
|
||
helpers. An informal adult variant (to family) would double the adult
|
||
set's translations; not proposed now.
|
||
3. **Pictures in language packages.** *Decided:* language packages name
|
||
symbols; the app embeds the pictures when a person exports boards for
|
||
another app (it already does).
|
||
4. **The importer defect.** *Decided and fixed* (above).
|
||
5. **The SPEC text.** *Approved* as draft text after 0.2.0; in SPEC.md.
|
||
6. **Native review.** Every translation is model-read only. Which languages
|
||
get native reviewers first (the AAC site's largest audiences), and who.
|
||
7. **Name collision.** *Decided.* TD Snap has an "Express" page set, and the
|
||
app was previously called Express too; it is now the AAC Reference App.
|
||
Our page set is called "Themed phrases", and nothing here uses the word.
|