Internationalization Playbook
This playbook documents the locale framework in builder-docs.
Russian is the first full implementation, but it is no longer a one-off rollout. The goal is that every later locale follows the same system:
- shared locale registry
- locale-owned glossary and policy files
- non-destructive bootstrap scaffolding
- wave-based editorial QA
- locale-safe routing, SEO, and discovery artifacts
- localized FastNear overlay catalogs that never mutate generated English source data
Design Goals
This framework is meant to keep future locale work mostly about content, not about infrastructure.
The non-negotiable rules are:
- English stays the default locale at
/ - localized docs publish at
/<locale>/... - canonical technical identifiers stay stable
- slugs
- endpoint paths
- payload keys
- schema property names
- operation IDs
- generated localization remains an overlay, never an in-place edit of vendored generated data
Core Files
Locale registry
Supported locales live in src/data/localeRegistry.json.
That registry is the shared source of truth for:
- Docusaurus locale config
- locale-aware route helpers
- bootstrap and audit tooling
- client-side hidden-section metadata
Locale-owned glossary
Each non-default locale owns i18n/<locale>/glossary.yml.
The glossary is the terminology contract for both humans and scripts. It keeps translation decisions out of scattered JS arrays and prose docs.
Current schema:
preserveTerms that must remain literal or canonical, such asRPC,API,JSON-RPC,GET,POST,FastNear,mainnet, and code-ish identifiers.translatePreferred exact and word-level mappings for recurring UI and docs phrases.transliteratePreferred transliterations for integrated jargon when that is better than keeping Latin script.notesHuman guidance that explains the editorial intent but is not required by scripts.
Locale-owned translation policy
Each non-default locale also owns i18n/<locale>/translation-policy.yml.
This file defines editorial scope and workflow policy:
waves.wave1Required-for-ship docs and page-model IDs. This is the CI-enforced editorial bar.waves.wave2Expanded public surface for follow-up editorial passes.hiddenSectionsRoute prefixes and doc path prefixes that are intentionally out of editorial scope until they become public.bootstrapLocale-owned route labels and translation JSON overrides used by the scaffold flow.
For Russian, /transaction-flow is the first hidden section tracked this way.
Shared Tooling
The locale framework now uses generic commands instead of Russian-only scripts.
yarn bootstrap:i18n --locale <code>
yarn bootstrap:i18n:reseed --locale <code>
yarn audit:i18n --locale <code> --wave <1|2|all>
yarn audit:i18n:all
What they do:
bootstrap:i18nSafe scaffold refresh. It fills in missing locale files and keys without overwriting curated content.bootstrap:i18n:reseedExplicit destructive path when you intentionally want to reseed a locale from the bootstrap heuristics.audit:i18nGlossary-aware editorial QA for a single locale and wave.audit:i18n:allCI-oriented wave-1 audit for every configured non-default locale.
Russian compatibility aliases still exist and remain supported:
yarn bootstrap:i18n:ru
yarn bootstrap:i18n:ru:reseed
yarn audit:i18n:ru
Those are convenience wrappers. The generic commands are now canonical.
Bootstrap Behavior
scripts/bootstrap-i18n.js is intentionally non-destructive by default.
For a locale such as ru, it:
- runs
write-translations --locale ru - scaffolds missing docs into
i18n/ru/docusaurus-plugin-content-docs/current - preserves existing curated locale docs instead of overwriting them
- merges missing runtime translation keys into locale JSON catalogs
- refreshes
src/data/fastnearTranslations.<locale>.jsonwithout discarding curated overlay entries - applies locale-owned route labels and JSON overrides from
translation-policy.yml
This keeps scaffold freshness and editorial curation compatible with each other.
Audit Behavior
scripts/audit-i18n.js is the lightweight editorial gate.
It reads:
- the locale glossary for allowed literal terms
- the locale translation policy for wave scope and hidden-section exclusions
- locale docs under
i18n/<locale>/... - locale runtime translation catalogs
- locale FastNear overlay catalogs
The audit flags suspicious English leftovers while respecting allowed literals such as:
- protocol names
- HTTP verbs
- product names
- code identifiers
- canonical path fragments
This is meant to be practical QA, not language-policing for every long-tail page on day one.
Wave Policy
Every locale should use the same editorial policy:
Wave 1
Wave 1 is the shipping bar.
It should include:
- homepage and primary decision pages
- top-level auth, API, RPC, and transaction entry points
- the most visible generated operation wrappers and overlay entries
- live runtime UI strings on those pages
Wave 1 is the only translation scope enforced in CI.
Wave 2
Wave 2 is the broader public-surface pass.
It should include:
- more leaf docs
- long-tail overview pages
- additional generated overlay entries
- lower-priority but still public runtime copy
Wave 2 is important, but it is intentionally non-blocking.
Long tail
Long-tail work is ongoing polish:
- maintainer docs
- obscure leaf pages
- rarely surfaced theme strings
- low-traffic generated pages
That work should keep improving, but it should not block shipping a healthy locale.
Hidden Sections
Hidden sections must be explicit so we do not confuse file coverage with editorial readiness.
The source of truth is translation-policy.yml.hiddenSections.
Those prefixes drive two things:
- they are excluded from wave-1 editorial requirements
- docs pages under those prefixes render a visible banner explaining that editorial and translation polish are intentionally deferred until the section becomes public
Today, /transaction-flow is the first section using this rule.
Runtime, Routing, And Discovery
The locale framework also covers the non-prose surfaces that future locales should inherit automatically.
Important files:
docusaurus.config.jssrc/utils/localizedRoutes.jssrc/utils/fastnearLocalization.jsscripts/generate-ai-surfaces.jsplugins/finalizeLocalizedStaticAssets.cjs
Together they ensure:
- locale dropdown and locale-aware routing work consistently
- root-relative links preserve the active locale
- generated FastNear overlays localize operation content without touching source page models
- localized Markdown mirrors,
llms.txt, and site-graph output ship from the correct locale root - structured data and SEO emit localized URLs and
inLanguage
Lean CI Gate
The locale-quality gate is intentionally small.
The required workflow runs:
yarn audit:i18n:all
yarn build
node scripts/audit-indexing-surface.js
That is enough to protect:
- wave-1 locale quality
- build correctness
- discovery/indexing correctness
It intentionally does not include Playwright, relevance scoring, or heavier editorial sweeps.
Adding A New Locale
Use this checklist when adding the next language:
- Add the locale to
src/data/localeRegistry.json. - Create
i18n/<locale>/glossary.yml. - Create
i18n/<locale>/translation-policy.yml. - Run
yarn bootstrap:i18n --locale <code>. - Curate the generated
i18n/<locale>/code.jsonand docs tree. - Add
src/data/fastnearTranslations.<locale>.jsonfor generated FastNear overlays. - Run
yarn audit:i18n --locale <code> --wave 1. - Run
yarn buildandnode scripts/audit-indexing-surface.js. - Add targeted browser checks only if the locale introduces new runtime behavior worth smoke-testing.
If those steps are followed, later locales should mostly be editorial work layered onto a stable framework.