Renderingen-US

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#

LayerIn the HIR?Behaviour
Keywords, types, paired phrasesYesLossless across all renderings
Glyphs ← → ∴ ≡ ∪ ⇥Yes (invariant)Identical in every rendering
Type-first structureYesIdentical in every rendering
NumeralsASCII only in all locales
CommentsNoOut of compiler scope; LLM-mediated, opt-in
Identifier namesNoPreserved byte-for-byte
Stdlib spellingsNoPer-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:

CodeLanguageScriptStatus
laLatina (Latin)LatinCanonical
enEnglishLatinBase surface
th-THไทยThaiReference proof
zh-Hans简体中文Simplified ChineseCoverage proof
zh-Hant繁體中文Traditional ChineseCoverage proof
arالعربيةArabicCoverage proof
hiहिन्दीDevanagariCoverage proof
viTiếng ViệtVietnamese (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:

LocaleAccessArchitectural stress
th-THHighSpaceless script — the tokeniser stress test
zh-Hans / zh-HantVery highPaired keywords; sibling pack inheritance; NFKC width collapse
arHighRight-to-left; bidi isolation in diagnostics
hiVery highMatra/virama clusters; Indic numerals
viHighHeavy 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 lacanonical

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-THreference 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.fab
  • examples/reader-locale/vi/src/undefined-variable.fab
  • examples/reader-locale/vi/src/non-ascii-number.fab
  • examples/reader-locale/vi/src/keyword-suggestion.fab
  • examples/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#

LayerStatus
Pack schema, aliases, inheritance, validation, diagnostics, LLM artifactsShipped
Pack-aware lexing, type resolution, manifest/CLI selection, visible fallbackShipped
Pack-owned diagnostic rendering, faber explain, bidi-isolated displayShipped
Canonical Faber formattingShipped
Localised Faber re-emission (format --locale <locale>)Shipped
Stdlib gloss overlays, measured LLM emission fidelity, complete locale coverageDeferred
Multilingual documentation generationProposed

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#

  1. radix/docs/design/reader-locale.md — full design document (69 KB)
  2. examples/reader-locale/ — 6 locale packages with localised source
  3. stdlib/locale/*/pack.toml — 8 installed pack definitions
  4. radix/crates/radix/src/locale.rs — runtime implementation
  5. radix/docs/design/faber-canonical-surface.md — canonical mode and faber format
  6. radix/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#

LocalePathSource example
th-THexamples/reader-locale/th-TH/ฟังก์ชัน salve(ข้อความ nomen) → ข้อความ
zh-Hansexamples/reader-locale/zh-Hans/函数 问候(文本 名字) → 文本
zh-Hantexamples/reader-locale/zh-Hant/函式 問候(文字 名字) → 文字
arexamples/reader-locale/ar/دالة تحية(نص اسم) → نص
hiexamples/reader-locale/hi/फलन नमस्कार(पाठ नाम) → पाठ
viexamples/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 diagnostics
  • undefined-variable.fab — localised name resolution errors
  • non-ascii-number.fab — NFKC handling
  • keyword-suggestion.fab — localised "did you mean?" hints