Skip to content

Resolve a $strings. reference against a real dictionary, at runtime - #130

Merged
einari merged 1 commit into
mainfrom
feature/runtime-strings-resolution
Sep 24, 2026
Merged

einari merged 1 commit into
mainfrom
feature/runtime-strings-resolution

Conversation

@einari

@einari einari commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Why

The frontend rendered a $strings.<key> reference literally - nothing served the dictionary a .strings file actually holds. Resolution has to happen at runtime (not compile time) so a locale switch or an edited translation takes effect without recompiling the model.

What changed

  • StageStrings discovers every .strings file under the mounted model root via Cratis.Screenplay.Strings' existing IStringsFiles/StringsFile.Parse, merging every file declaring a given locale into one flat dictionary.
  • Two new endpoints, /stage/locales and /stage/strings/{locale}, served alongside /stage/scene and /stage/routes rather than folded into either - a locale switch is a question about what a running Stage offers, not something the scene itself carries.
  • useStrings fetches both, defaulting to the first reported locale. resolveStringsInElement (Give a $strings. reference somewhere to resolve against, at runtime Scene#47) resolves every reference in a screen's element tree before it reaches the registry - the same seam SceneContent already had for handing an element to SceneElementView.
  • A locale switcher only appears once more than one locale exists.
  • Authored .strings files for every $strings. reference the Billing sample already declared, next to whichever .play file names each key - the established <base>.<locale>.strings convention.

Verification

  • npx tsc -b, npx vite build, npx vitest run: 10/10 passing (2 new specs).
  • dotnet test (Debug) on Host: 130/130. On Contracts: 399/399.
  • dotnet build -c Release: clean.
  • Rebuilt the real Docker image and ran it against the Billing sample: /stage/locales returns ["en"], /stage/strings/en returns the full merged dictionary, and InvoiceList's title renders as "Invoices" rather than the literal $strings.invoice.listTitle token.

The frontend rendered a $strings.<key> reference literally - nothing served
the dictionary a .strings file actually holds, so a screen author had a
localization mechanism that compiled and validated but never showed
anything but the token itself in a running application.

StageStrings discovers every .strings file under the mounted model root
via Cratis.Screenplay.Strings' existing IStringsFiles/StringsFile.Parse,
merging every file that declares a given locale into one flat dictionary.
Two new endpoints - /stage/locales and /stage/strings/{locale} - serve
that alongside /stage/scene and /stage/routes rather than folding it into
either: a locale switch is a question about what a running Stage offers,
not something the scene itself needs to carry.

The frontend's useStrings hook fetches both, defaulting to the first
locale reported, and resolveStringsInElement (Cratis/Scene#47) resolves
every reference in a screen's element tree before it reaches the
registry - the same seam SceneContent already had for handing an element
to SceneElementView. A locale switcher only appears once more than one
locale exists; one locale is not a choice worth a control for.

Authored .strings files for every $strings. reference the Billing sample
already declared, next to whichever .play file names each key - the
established <base>.<locale>.strings convention, not a new format. This is
what made the reference resolve to a real value in verification rather
than to an empty dictionary that happened to compile.

Verified end to end against a real running Stage container built from
these changes: /stage/locales and /stage/strings/en both serve real data,
and InvoiceList's title renders as "Invoices" - the value from the .strings
file - rather than the literal $strings.invoice.listTitle token it showed
before this.
@einari einari added the minor label Sep 24, 2026
@einari
einari merged commit 0867bd7 into main Sep 24, 2026
3 of 4 checks passed
@einari
einari deleted the feature/runtime-strings-resolution branch September 24, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant