Forge
markdowndeeb25a2
1# ADR 0033: Internationalization (i18n) — .NET resources, Avalonia, orthogonal to TOML
2
3**Status:** Proposed (direction fixed; language scope and string migration — per plan).
4**Date:** 2026-04-11
5
6## Related ADRs
7
8| ADR | Role |
9|-----|------|
10| [0028](0028-user-settings-toml-localappdata-and-secrets.md) | user `settings.toml` |
11| [0029](0029-configuration-toml-canonical-ui-facade.md) | configuration canon vs UI |
12| [0032](0032-hud-banner-configuration-and-grammar.md) | HUD: templates and keys — not a substitute for i18n |
13| [0027](0027-small-team-focus-vs-public-maturity.md) | documentation and discoverability |
14
15---
16## Context
17
18UI and message text is still mostly **hardcoded** in code (including Russian strings). For **multiple languages**, runtime language switch, and a proper translation pipeline we need **stable rules**, not duplicating long phrases in **`settings.toml`** or only in VMs.
19
20Confusion to remove early:
21
22- **Configuration** ([0028](0028-user-settings-toml-localappdata-and-secrets.md), [0029](0029-configuration-toml-canonical-ui-facade.md)) — canonical keys, numbers, flags; UI language is a **setting**, but **not** storage for all translated product strings.
23- **HUD and templates** ([0032](0032-hud-banner-configuration-and-grammar.md)) — semantics of “what to show” may live in TOML; **user-facing wording** should by default come from the **localization layer**, not an optional wall of text in config.
24
25## Solution (intent)
26
27<a id="adr0033-p1"></a>
28
291. **Base stack:** **.NET embedded resources (`.resx`)** and an Avalonia-aligned scheme: resources as string source for XAML and code, setting **`Culture`** / `CurrentUICulture` at startup and on user language change; resource generator (e.g. `PublicResXFileCodeGenerator`) — per repo convention so keys are available from markup and tests.
30
31<a id="adr0033-p2"></a>
32
332. **Role separation:**
34 - **Translatable UI strings** — in **resources** (per culture: `Resources.resx`, `Resources.ru.resx`, …). Key catalog stable for translators and CI.
35 - **`settings.toml`** — **not** the main translation file for the whole UI; **selection keys** allowed (e.g. preferred culture), optional **overrides** of individual texts for power users — only if explicitly designed and without breaking the unified translation catalog (see [§4](#adr0033-p4)).
36
37<a id="adr0033-p3"></a>
38
393. **Code and MVVM:** strings built in **ViewModel** / services via the **same resource layer** or an abstraction like **`IStringLocalizer<T>`** / `ResourceManager` wrapper — to avoid literal sprawl and ease tests (culture substitution in tests).
40
41<a id="adr0033-p4"></a>
42
434. **Plural, case, gender:** do not hand-code “Russian rules” in every VM. Prefer **separate resource keys** for variants (`*_one`, `*_few`, `*_many` / language-specific scheme) or a **proven library** with CLDR/ICU rules — choice when implementing the first non-English language with non-trivial plural rules; until then simple keys suffice.
44
45<a id="adr0033-p5"></a>
46
475. **Boundaries:** this ADR **does not** require immediate migration of all existing strings; **does not** replace configuration ADRs; **does not** describe the product User Guide ([0027](0027-small-team-focus-vs-public-maturity.md)) — only **in-app UI**.
48
49## Consequences
50
51- `*.resx` structure, key naming policy, possible CI step “no missing keys”.
52- Runtime language change will require binding/VM notifications where text is not from `{x:Static}`.
53
54## Rejected / deferred alternatives
55
56- **All strings only in TOML** — poor for translation, duplication, risk of editing “in config” instead of catalog; contradicts [0029](0029-configuration-toml-canonical-ui-facade.md) spirit as “settings”, not full product dictionary.
57- **Third-party format only (gettext, Fluent)** without evaluation — deferred; migration possible if ResX stops fitting the team or translation tools.
58
59## Open questions
60
61- **Target languages** for v1 and order (infrastructure + English first, then Russian etc.).
62- Single **key prefix** or split by assemblies/features (`Features/*`).
63- Integration with **command palette** and XML docs [0018](0018-ide-commands-canonical-xml-documentation.md) — how not to diverge hint languages.
64
View only · write via MCP/CIDE