Reader locales
Reader locale#
Reader locale is Faber's system for rendering source code, compiler diagnostics,
and language keywords in the reader's human language — without forking the
semantics. A Thai programmer can read and write Faber source with Thai keywords,
receive compiler errors in Thai, and collaborate through the same HIR as a Latin
or Chinese user. The mechanism that localises code from Latin to Thai is the
same mechanism that emits code to Rust: HIR → surface — neither is
privileged.
Problem#
Large language models have localised the conversation around programming — a Thai computer scientist can ask an LLM for help in Thai — but not the durable artifact. The generated code, APIs, compiler errors, and documentation remain English-shaped. English proficiency becomes a gate to computer science, not merely to conversation.
Reader locale is the design response: the language a human uses to understand Faber — source, diagnostics, and optionally stdlib spellings — without English as a prerequisite. It is not application internationalisation (complete string-matrix coverage). It is reader dialect support: opt-in, partial packs over a semantic core that does not fork.
Product thesis: English should not be the required review language for software intent. LLMs have localised the conversation around programming; reader locale localises the durable artifact.
How it works#
A reader-locale pack maps Faber keywords, primitive type spellings, and diagnostic templates into a target language. Packs are TOML files with three tables:
[keywords]— maps keyword names to their localised spellings[types]— maps primitive type names to localised spellings[diagnostics.*]— maps diagnostic codes to localised message templates[llm]— system prompt snippets and exemplars for LLM code generation
The compiler validates packs against a generated Latin scaffold — every keyword and type must have a defined spelling or explicitly inherit from Latin. Missing rows produce visible fallback rather than silent gaps.
Select a locale at the command line or in faber.toml:
faber check --locale th-TH program.fab# faber.toml
[locale]
locale = "zh-Hans"The legacy [reader] table name and --reader-locale flag are still accepted
as aliases during the rename sweep; [locale] / --locale are canonical. The
message language for diagnostics is independent of the code locale and is
selected with --diagnostic-locale <locale>.
What localises and what does not#
| Layer | In the HIR? | Behaviour |
|---|---|---|
| Keywords, types, paired phrases | Yes | Lossless across all renderings |
Glyphs ← → ∴ ≡ ∪ ⇥ | Yes (invariant) | Identical in every rendering |
| Type-first structure | Yes | Identical in every rendering |
| Numerals | — | ASCII only in all locales |
| Comments | No | Out of compiler scope; LLM-mediated, opt-in |
| Identifier names | No | Preserved byte-for-byte |
| Stdlib spellings | No | Per-locale overlay |
The critical architectural guarantee: any locale surface can become any other,
including Latin, at any time. A localised Faber file is never a trap because
it is never the only form the code can take. faber format --locale la
reproduces the former --canonical re-emit surface.
Shipped packs#
Eight packs ship with Radix today:
| Code | Language | Script | Status |
|---|---|---|---|
la | Latina (Latin) | Latin | Canonical |
en | English | Latin | Base surface |
th-TH | ไทย | Thai | Reference proof |
zh-Hans | 简体中文 | Simplified Chinese | Coverage proof |
zh-Hant | 繁體中文 | Traditional Chinese | Coverage proof |
ar | العربية | Arabic | Coverage proof |
hi | हिन्दी | Devanagari | Coverage proof |
vi | Tiếng Việt | Vietnamese (Latin) | Coverage proof |
en is the English reader surface — English keywords map to themselves,
like la maps Latin to itself — and was renamed from the old llm pack in
the 2026-08 locale rename. The reference locales below remain the
collective architectural stress set; en and la do not stress the
substrate because they are the base surfaces:
| Locale | Access | Architectural stress |
|---|---|---|
th-TH | High | Spaceless script — the tokeniser stress test |
zh-Hans / zh-Hant | Very high | Paired keywords; sibling pack inheritance; NFKC width collapse |
ar | High | Right-to-left; bidi isolation in diagnostics |
hi | Very high | Matra/virama clusters; Indic numerals |
vi | High | Heavy diacritics on Latin script; NFKC edge cases |
The reference set is chosen for architectural coverage, not population. Population alone would prove nothing the substrate did not already handle.
Localised source examples#
Each of the six non-canonical locales has a complete Faber package under
examples/reader-locale/ with localised source, diagnostic test
cases, and a faber.toml manifest. The same greet
program rendered in Latin and the six packaged locale surfaces below:
Latin la — canonical
fn salve(string nomen) → string {
const string msg ← "Salve, §!"(nomen)
return msg
}
main {
const string m ← salve("munde")
print m
}The canonical rendering. faber format --locale la reproduces the former
--canonical re-emit surface. Latin keywords map to themselves;
type names are the canonical spellings.
ไทย th-TH — reference proof
ฟังก์ชัน salve(ข้อความ nomen) → ข้อความ {
ค่าคงที่ ข้อความ msg ← "Salve, §!"(nomen)
คืนค่า msg
}
เริ่มต้น {
ค่าคงที่ ข้อความ m ← salve("มุนเด")
แจ้ง m
}The access-wedge proof. Thai is a spaceless script — no inter-word boundaries — making it the tokeniser stress test and the original architectural driver for the reader-locale system. Every token boundary must be resolved by the lexer through keyword matching alone.
简体中文 zh-Hans
函数 问候(文本 名字) → 文本 {
常量 文本 问候语 ← "你好,§!"(名字)
返回 问候语
}
入口 {
常量 文本 消息 ← 问候("世界")
显示 消息
}Paired keywords (如果/否则 for si/secus) requiring pack keyword groups; full/half-width punctuation; NFKC width collapse at lex entry. The hardest LLM emission case due to CJK tokeniser boundaries. A sibling pack for Traditional Chinese (zh-Hant) inherits and overrides zh-Hans roots.
العربية ar
دالة salve(نص nomen) → نص {
ثابت نص msg ← "مرحبا، §!"(nomen)
أعد msg
}
بداية {
ثابت نص m ← salve("عالم")
اعرض m
}Right-to-left script embedded inside a logical-order LTR code block. Keywords
are wrapped in <bdi> (bidirectional isolation) in the
compiler's HTML diagnostic output to prevent RFO (right-follows-left)
distortion. The raw source uses Arabic script in logical order; the display
layer handles bidi presentation.
हिन्दी hi
फलन salve(पाठ nomen) → पाठ {
स्थिर पाठ msg ← "Salve, §!"(nomen)
लौटा msg
}
आरंभ {
स्थिर पाठ m ← salve("जगत")
दिखा m
}Devanagari script with matra/virama consonant clusters. Proves the path for the wider Indic family — Bengali, Tamil, Telugu inherit the same shaping infrastructure — though pack authoring for each remains separate work. Indic numeral glyphs (०-९) are not accepted in numeric literals; ASCII digits are preserved across all locales.
Tiếng Việt vi
hàm chào(vănbản tên) → vănbản {
hằng vănbản lời_chào ← "Xin chào, §!"(tên)
trả lời_chào
}
bắtđầu {
hằng vănbản thông_điệp ← chào("thế giới")
in thông_điệp
}The control case: Latin-script but not English. Heavy diacritic load (ế, ệ, ả) stresses NFKC edge cases in the lexer. Prevents an architecture that works on exotic scripts but is unproven on Latin-with-diacritics. Identifiers use Vietnamese words (chào, tên, lời_chào, thông_điệp), preserved byte-for-byte by the compiler.
The glyphs (
← → ∴ ≡ ∪ ⇥), structural positions, and identifier names are identical across all six renderings above. Only the keywords and type names change. The HIR is exactly the same program — the compiler treats all six as equivalent. Rendering Faber to Thai is the same compiler operation as rendering it to Rust:HIR → surface, with neither privileged.
Localised diagnostics#
Diagnostics are structured facts before prose. Each diagnostic carries a
stable code (LEX###, LOCALE###, PARSE###, SEM###,
WARN###) and named arguments; the pack owns the rendered template
text. This means the diagnostic renderer can emit messages in any locale without
changing the diagnostic infrastructure.
The reader-locale example packages include diagnostic test cases — type mismatches, undefined variables, non-ASCII numbers — proving the full pipeline is locale-aware:
examples/reader-locale/vi/src/type-mismatch.fabexamples/reader-locale/vi/src/undefined-variable.fabexamples/reader-locale/vi/src/non-ascii-number.fabexamples/reader-locale/vi/src/keyword-suggestion.fabexamples/reader-locale/vi/src/keyword-edit-distance.fab
Bidi isolation is built in: Arabic keywords inside logical-order LTR code blocks
are wrapped in <bdi> elements in HTML output, preventing the
RFO (right-follows-left) distortion that would otherwise make RTL runs unreadable.
Status#
| Layer | Status |
|---|---|
| Pack schema, aliases, inheritance, validation, diagnostics, LLM artifacts | Shipped |
| Pack-aware lexing, type resolution, manifest/CLI selection, visible fallback | Shipped |
Pack-owned diagnostic rendering, faber explain, bidi-isolated display | Shipped |
| Canonical Faber formatting | Shipped |
Localised Faber re-emission (format --locale <locale>) | Shipped |
| Stdlib gloss overlays, measured LLM emission fidelity, complete locale coverage | Deferred |
| Multilingual documentation generation | Proposed |
The substrate prerequisite — NFKC normalisation at lex entry — has landed. Keyword tables, diagnostic named-args, the renderer, and pack delivery are shipped. The north-star layers (localised re-emission, stdlib glosses, LLM emission benchmarks, generated multilingual docs) remain explicitly partial or deferred.
References#
radix/docs/design/reader-locale.md— full design document (69 KB)examples/reader-locale/— 6 locale packages with localised sourcestdlib/locale/*/pack.toml— 8 installed pack definitionsradix/crates/radix/src/locale.rs— runtime implementationradix/docs/design/faber-canonical-surface.md— canonical mode andfaber formatradix/docs/factory/lex-nfkc-normalization/— NFKC prerequisite delivery
Reader-locale packages#
Each non-Latin reader locale has a complete Faber package under
examples/reader-locale/ with localised source, diagnostic test cases,
and a faber.toml manifest.
Available packages#
| Locale | Path | Source example |
|---|---|---|
| th-TH | examples/reader-locale/th-TH/ | ฟังก์ชัน salve(ข้อความ nomen) → ข้อความ |
| zh-Hans | examples/reader-locale/zh-Hans/ | 函数 问候(文本 名字) → 文本 |
| zh-Hant | examples/reader-locale/zh-Hant/ | 函式 問候(文字 名字) → 文字 |
| ar | examples/reader-locale/ar/ | دالة تحية(نص اسم) → نص |
| hi | examples/reader-locale/hi/ | फलन नमस्कार(पाठ नाम) → पाठ |
| vi | examples/reader-locale/vi/ | hàm chào(vănbản tên) → vănbản |
Diagnostic test cases#
Each package includes test cases proving the full compiler pipeline is locale-aware:
type-mismatch.fab— localised type error diagnosticsundefined-variable.fab— localised name resolution errorsnon-ascii-number.fab— NFKC handlingkeyword-suggestion.fab— localised "did you mean?" hints