Skip to content

Release 1.0.0-preview.6 - #20

Merged
soyeyalon merged 62 commits into
mainfrom
candidate
Nov 15, 2026
Merged

soyeyalon merged 62 commits into
mainfrom
candidate

Conversation

@soyeyalon

Copy link
Copy Markdown
Collaborator

Promotes the candidate 1514985, whose full gate passed, as Forma 1.0.0-preview.6: locale files are read in any layout, a culture folder among them, into one entry each, and two files of one source giving an entry different texts are a conflict the runtime settles and logs, while an application's strings for other assemblies need no reserved prefix; every input takes a Description and a FocusAsync; a FormaSelect, a FormaDropDownMenu and a FormaDataTree move to what a reader types, a label's space included; a file store can refuse a file, which the uploader and the explorer show; culture routing gives a page an address of its own in each culture; LocalizableText carries text to localize when it is shown; and a component taken away during its first render, a menu closing before its command runs, tabs inside a form and an explorer's asynchronous first page are fixed, each change listed in the changelog.

🤖 Generated with Claude Code

The fast gate builds every project in the solution and every task program,
and runs the unit and bUnit suites from that build, so a commit that breaks
the Showcase, a check host or a task program fails it.
BREAKING CHANGE: HandleInputActionAsync is gone from FormaToolbar,
FormaComposer, FormaPlainTextEditor and FormaRichTextEditor; a host runs a
command through HandleCommandAsync.
BREAKING CHANGE: IComposerInteraction gains JoinFocus. An implementation of its own adds it, holding the focus an editor hands it until the registration it answers with is disposed.
A store answers SaveAsync with the resource it saved or a refusal
naming its reason and the limit the file went past. The uploader
words the refusal for the reader beneath the file and reports it
through OnRefused, apart from a failure, and an explorer's upload
panel stays open to show a file the set left out.

BREAKING CHANGE: IFileStore.SaveAsync returns a FileSaveResult. A
store wraps the resource it returned in FileSaveResult.Saved, and
answers FileSaveResult.Refused where it threw to refuse a file.
A FormaExplorer refuses a file picked for upload past
CatalogConstraints.MaxFileBytes, raising OnRejected with
CatalogRejectionReason.FileBytes, and the composer's explorer plugin
forwards one.
A host's ICultureDestinationResolver names where a page leads in
another culture, its own address there or a fallback, and every
switch by address, every link built from the switcher's destination
and every web view following its application's culture goes there.
LocalizableText carries a registry's label as its source type, key
and values, and IStringLocaleFactory.Localize looks it up in the
cultures in force when it is shown.
A filtered browser check republished the WebView check host only when
the filter chose a check row run in the web view, so a filter choosing
the web view's own check classes alone ran them against whatever host
the last run had published.
A check that clicked through bUnit's synchronous click and asserted at
once raced the data view's state notification, whose render can land
after the click returns, as a group's fold did under load. Each check
now awaits the handler its click runs.
A typing binding matches a space typed within the type-ahead's pause
of the last character a typing binding matched, and the input router
leaves that space to the typing route rather than to the button
holding the focus. A select's or a data tree's type-ahead goes on
through a label such as "On Hold", where the space chose the option
the focus was on.
The plan's first step holds what a lookup costs before the runtime
reads files into translations: the first lookup in a culture, the
first of every type, and a later one, over Forma's packs and an
application's own files.
A source reads every file it places in a culture once into the
translations it holds for an assembly's types, and a type's lookup
takes its keys from them rather than walking its name level by level.
Within a source a type's own entry wins over a shared one, and two
files giving a key different texts are a conflict, settled by the file
whose name sorts last and logged once as a warning naming both.

BREAKING CHANGE: a file closer to a type no longer wins over a
shallower one of the same source; the file whose name sorts last does,
and LocaleSource.Read answers one entry for each key.
A file's culture is a folder named for it or the last part of its
name, a folder and a dot separate its name alike, and a file outside a
folder of locale files is read too, so .locales/es/Accounts.Admin.json
and Accounts/Admin/.locales/es.json read the same. A pack's and a
host's names start with the assembly's name, before the culture or
after it. An assembly's stated paths still replace the default reading.

BREAKING CHANGE: LocalePath.Defaults and LocalePath.HostDefaults are
gone, LocaleSource.DefaultPaths gives way to NamesStartWithAssemblyName,
and a file outside a folder of locale files whose name carries a
culture is read.
A source keeps, for each name, the sections of its files that give it,
and copies a type's translations out only when the type is read, so a
culture's first lookup no longer builds every type's strings. A file's
name is parsed once for a source, and only where it holds the culture
asked for, and the full-name reading leaves out the files no type it
answers can reach. A conflict is logged the first time a type it
touches is read.
A culture-bound locale keeps what it read for each of its cultures, and
a factory answers a type it has made a locale for from the type alone,
so neither a later lookup nor a component's locale allocates. A key's
holes and a text's plural expressions are read once, and text renders
into a stack buffer, each value formatted in the culture through its
hole's format, so a lookup with a value allocates little beyond the
text it returns.
A locale file is read in one pass, and still reports what it writes
wrongly as it did and in the same order. A type's entries are gathered
without splitting its name or asking its assembly for its name again,
so a culture's first lookup costs less in time and in memory, for an
application's own files as for a pack's.
Forma is a localization library, and "translation" stood for four
things it already has words for: a key with its text is an entry, what
a key maps to is its text, what a locale serves is a string, and
whether a key has text in a culture is its localization. The API, the
messages, the Showcase's localization page and the documents now use
those words alone.
A tool can read a locale file's name as the runtime does, into the
culture it is for and the parts of the name it stands for, and list
every section a type reads, its own and shared, whether their entries
win or not. A conflict says whether it is between shared entries, so a
linter can tell which types it reaches.
The design introduces what it reads every file into as locales, a
culture's text for an assembly's type and key, and the plan's build
step says what the README's account of where locale files live covers.
Every project embeds every locale file under its root namespace and
its path, so a culture folder such as es-419 keeps its name and a
project's root .locales may hold a folder per culture. An application's
files for another assembly are read from beneath its root, and a pack's
culture check reads a culture folder as the runtime does.

BREAKING CHANGE: the reserved prefix .locales. is gone, with
LocalePackSource.HostResourcePrefix, its resourcePrefix parameter and
LocaleAssemblyLoader.EmbedsHostFiles; a file a project embedded under
.locales. itself is embedded under its root namespace instead.
The localization README sets out the model every file is read into, the
layouts that read the same and the rule behind them, another assembly's
strings, and which text wins by culture, source and file; the plan
records the build step as landed.
A key is written in the source language, which SourceLanguage names and
which is English only by default, so the design, the READMEs and the
summaries say source text where they said English. The design names
what a type's full name leaves out: the assembly it lives in.
The plan records that no source Forma ships gives an entry two texts in
any culture it carries, so Forma's own files need no change and the
step waits only for the linter's release to confirm it.
A conflict among the root's shared entries names the root namespace
itself, where it named it with a trailing dot, in the conflict a source
lists and in the warning the runtime logs.
The JSON backend's README lists every source a lookup reads, the
applications' files among them, and how a conflict within one is
settled; the test project's README and config say its paths read in
place of the default reading, as stated paths do.
The localization README shows the one form every locale file is read
into, a culture, an assembly, a type and a key with its text, through
an entry that files in six layouts, a pack, an application and a stated
path all read into, and tests prove each of them does.
The rooted fixture's two files for the root no longer give one key two
texts, which is a conflict now that the file whose name sorts last wins
rather than the closer one; each still proves its file is read.
The four fixtures proving two files for one anchor are both read are
left out of the lint rather than excused from a rule, so the linter
version that retires that rule and the one before it both pass.
The features status records what remains for the gate's Android checks
to stop depending on the JDK and device a machine happens to have: one
declared environment, a script provisioning it, and a check before the
build, with running them nightly on a free runner left open.
@soyeyalon
soyeyalon merged commit 1514985 into main Nov 15, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant