Skip to content

Localize the GUI chrome - #25

Merged
BiosSystem merged 1 commit into
masterfrom
feature/localize-gui-chrome
Sep 7, 2026
Merged

Localize the GUI chrome#25
BiosSystem merged 1 commit into
masterfrom
feature/localize-gui-chrome

Conversation

@BiosSystem

Copy link
Copy Markdown
Owner

Second half of localization for v3.5.0. Covers the 194 literal strings across the nine XAML schemas that #24 left untouched.

Substitution before parse, not a tree walk

My first instinct was to walk the loaded window and set properties. Measuring killed it: a logical tree does not contain text inside a Style or a template, and MainWindow carries 113 literal strings but exposes only 99 as logical tree nodes. A tree walk would have silently missed fourteen. Substituting on the markup before XamlReader parses it reaches all of them.

Keying

Source-string keys, i.e. the key is the English text.

  • Positional keys (Content_01) break the moment anyone reorders markup. I built that first and threw it away.
  • x:Name keys would work, but only 46% of the strings sit on a named element, so it meant adding identifiers to 103 elements across nine files.
  • Source-string keys are stable under reordering, show the translator the original, and resolve to themselves when untranslated.

Values are read XML-decoded and written back XML-encoded, so a translation containing & or < cannot corrupt the markup. Attribute values opening with a brace are skipped as bindings.

All nine load sites now call Get-LocalizedXaml rather than reading the schema directly, keeping this to one integration point.

Two extraction problems

  • 41 values are icon glyph character references (&#xE70D;), not text. Excluded. This is why SharedStyles.xaml gets zero substitutions - all three of its strings are glyphs or a TemplateBinding.
  • Three phrases differ only in capitalization (Restore backup/Restore Backup, Current User/Current user). ConvertFrom-Json cannot hold those as distinct PSCustomObject properties and threw. They now share one entry and resolve through case-insensitive property lookup, which I verified rather than assumed.

Verification

Loaded every schema through XamlReader headlessly with all 123 entries translated:

AboutWindow.xaml                  10 substituted  parse=OK
ApplyChangesWindow.xaml           10 substituted  parse=OK
AppSelectionWindow.xaml            9 substituted  parse=OK
BubbleHint.xaml                    1 substituted  parse=OK
ImportExportConfigWindow.xaml      3 substituted  parse=OK
MainWindow.xaml                   85 substituted  parse=OK
MessageBox.xaml                    5 substituted  parse=OK
RestoreBackupWindow.xaml          30 substituted  parse=OK
SharedStyles.xaml                  0 substituted  parse=OK

Also confirmed a translation containing &, < and a double quote is encoded correctly, and that MainWindow's 192 bindings are identical before and after.

A test asserts every translatable schema string has a catalogue entry, so adding a string without one fails the build. 23 localization tests total.

Static validation passes (112 files), standalone rebuilt and parse-checked.

Not verified: the GUI has not been displayed. Windows are constructed and parsed, which is the step substitution could break, but nothing has been rendered on screen.

Second half of localization. Covers the 194 literal strings across the
nine XAML schemas, which the first pass left untouched.

Substitution happens on the markup before it is parsed rather than by
walking the loaded window. A logical tree does not contain text that
sits inside a Style or a template: MainWindow carries 113 literal
strings but exposes only 99 as logical tree nodes, so a tree walk would
have silently missed fourteen of them.

Chrome entries are keyed by their English text. Keying by position broke
the moment markup was reordered, and only 46 percent of the strings sit
on an x:Name element, so keying by element name would have meant adding
identifiers to 103 elements across nine files. A source-string key is
stable under reordering, shows the translator the original, and resolves
to itself when untranslated.

Values are read XML-decoded and written back XML-encoded, so a
translation containing an ampersand or an angle bracket cannot corrupt
the markup. Attribute values that open with a brace are skipped because
they are bindings rather than text.

All nine load sites now call Get-LocalizedXaml instead of reading the
schema directly, which keeps this to one integration point.

Two things the extraction had to handle. Forty-one values are icon glyph
character references rather than text and are excluded. Three phrases
differ only in capitalization, which ConvertFrom-Json cannot represent
as distinct properties, so they share one entry and resolve through
case-insensitive property lookup.

Verified by loading every schema through XamlReader with all 123 entries
translated: all nine parse, 153 substitutions land, and the 192 bindings
in MainWindow are untouched. A test asserts every translatable schema
string has a catalogue entry, so a new string without one fails the build.
@BiosSystem
BiosSystem merged commit 67a97e2 into master Sep 7, 2026
1 check passed
@BiosSystem
BiosSystem deleted the feature/localize-gui-chrome branch September 7, 2026 17:04
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