# i18n — 30 locales, English default

## Architecture
- `src/ui/lang/strings.h` — string ids (`enum class S`) + `LangTable` registry API.
- `strings.cpp` — English base table (complete, ordered) + locale registry + resolver:
  `t(id)` = current locale entry → **falls back to English per string** when missing.
- `lang_<code>.cpp` — one TU per locale, sparse id-tagged entries via `TIG_E(Id, L"…")`
  macros (`langdef.h`); order-independent, link-safe (`extern const LangTable`).
- Coverage tiers: **ru** complete (all ids); zh/es/de/fr/pt/ja/ko/it complete core UI
  (~150 ids); the remaining locales ship the essential set (~50–75 ids) — everything else
  gracefully falls back to English. The Appearance page notes this.

## Locales (ordered by prevalence; en = default/fallback)
en, zh, hi, es, ar, fr, bn, ru, pt, ur, id, de, ja, tr, ko, vi, it, th, pl, nl, uk, fa,
el, cs, sv, hu, ro, he, fi, da

## Selection
- Setting `appearance.language`: `"auto"` (default) or a code.
- `auto` resolves via `GetUserDefaultUILanguage()` primary language (mapped in `core/os.cpp`);
  unmapped OS languages → `en`.
- Live switch: Appearance → Language list (native endonyms + English names + code,
  scrollable, check mark on the configured value, “Auto” row on top). No restart needed.

## Adding / improving a locale
1. Copy an existing `lang_xx.cpp`, rename, fill `TIG_E` entries (ids from `strings.h`).
2. Add `extern const LangTable kTable_xx;` + registry entry in `strings.cpp`,
   OS mapping in `core/os.cpp`, native/English names in the macro tail.
3. Build (`scripts/build.sh`) and screenshot-verify: settings `"language":"xx"` +
   `--ui-test dashboard`.
Rules: keep `%s`/`%d` placeholders; keep product names (TrayIconGuard, Windows, Explorer,
EnableAutoTray…) untranslated; ellipsis character `…`; RTL locales (ar/he/fa/ur) currently
render LTR layout — text is translated, mirroring is a tracked future item.
