# CLAUDE.md — Ripple Payments Direct 2.0 Documentation Localization (pt-BR)

Standing instructions for all localization work in this repository (`ripplenet-docs`, a
multi-product Redocly Realm portal maintained in GitLab). The goal of this project is a
**semi-automatic pipeline for delivering and maintaining** the Brazilian Portuguese
(`pt-BR`) localization of the **Ripple Payments Direct 2.0** documentation.

> Bootstrap completed 2026-07-21: all `TODO`s resolved and `[VERIFY]` assumptions
empirically confirmed (see `.l10n-sync/BOOTSTRAP-REPORT.md`). Sections marked
`[EXTEND]` are seeded and expected to grow as translation work proceeds.


## 1. Scope — read this first

- **In scope (the ONLY translatable source):**
`products/payments-direct-2/@v2026.03/` — all Markdown/Markdoc content, translatable
front matter under this path. **The OpenAPI spec is NOT in scope** (decided 2026-09-14,
§11). Also in scope:
  - Shared partials/snippets **only if included by in-scope pages** (root `partials/`,
any product-level snippet folders) — identify the exact inclusion set at bootstrap.
  - `translations.yaml` keys that render on Payments Direct 2.0 pages (see §6).
- **Explicitly OUT of scope — never create translations for:**
  - Other products (`custody/`, `payments-odl/`, `stablecoin/`, `wallet/`, `collections/`),
the archived `products/_archive/payments-direct/`, and root portal pages
(`index.md`, `faq.md`, `contact.md`).
  - Other Payments Direct 2.0 versions: `@v2025.11` and `@v2026.04`. Do not touch them.
(Version carry-forward is a planned pipeline capability — §8 — not a translation task.)
- **Never modify English *content* anywhere in the repo** (see §9 for the
defect-reporting path). The only files localization tasks may create/edit: this file,
the mirrored `@l10n/pt-BR/` tree, `.l10n-sync/` pipeline files, termbase files, and —
with explicit approval — `redocly.yaml` (locale config), `.vale.ini` (l10n exclusion),
`@theme/` components, and the in-scope `sidebars.yaml`.
  - The last two are how component UI and sidebar labels get localized at all: hardcoded
strings are routed through `translate(key, englishDefault)`, and sidebar entries get
a `labelTranslationKey` / `groupTranslationKey` / `separatorTranslationKey` beside
the existing label. **Both must leave the English rendering byte-identical** — the
English default is the previously hardcoded string, and a key missing from a locale
falls back to it. Verify that before opening the MR; do not take it on trust.


## 2. Project context

- **Platform:** Redocly Realm; file-based routing; versioned content in `@vX` folders.
- **Target locale:** `pt-BR` only. `pt-PT` is never a reference for spelling or register.
- **Program context:** this localization is a workstream of the BR VASP readiness
initiative (Slack: #tmp-brvasp), driven by the **October 2026 grandfathering window**
for PSAV authorization. It is the "API & Technical docs" track of four parallel
translation workstreams (marketing website, RNH UI, onboarding/Persona, technical
docs). Program coordination: Raji Sreehari (succeeding Christina Luah).
- **Mirror rule (VERIFIED 2026-07-21, local preview build):** translated files live under
`@l10n/pt-BR/` at the repo root, mirroring the full relative source path, including the
version folder:
  - `products/payments-direct-2/@v2026.03/introduction/index.md`
→ `@l10n/pt-BR/products/payments-direct-2/@v2026.03/introduction/index.md`
  - Root partials: `partials/<name>.md` → `@l10n/pt-BR/partials/<name>.md`
  - Verified mechanics: locale codes are lowercased in URLs (`/pt-br/...`); the default
version (v2026.03) serves at the **versionless** URL in both locales; non-default
versions serve at `/products/payments-direct-2/v2025.11/...` (no `@` in URL);
untranslated pages render English at the pt-br URL (fallback, no 404s); a translated
@v2026.03 file does NOT leak into other versions' pt-br URLs (version isolation).
- **Fallback behavior:** a missing pt-BR file falls back to English. Partial coverage is
safe. NOTE the stakeholder expectation: the language picker is portal-wide, so once
`pt-BR` is enabled, users can select it anywhere — only Payments Direct 2.0 @v2026.03
content will render in Portuguese; everything else falls back to English. Confirm this
staging posture is acceptable before enabling the locale in production (§7.2).
- **Termbase:** `.l10n-sync/termbase/ptbr_payments_termbase_seed.xlsx` is the human-edited
source of record. The pipeline loads the generated CSVs alongside it (`termbase.csv`,
`do-not-translate.csv`, `idiom-traps.csv`) — regenerate with
`python3 .l10n-sync/termbase/export_csv.py`; never hand-edit the CSVs.
**Load before translating anything.** Its Do-Not-Translate and Idiom Traps sheets are
binding. (153 term pairs, 26 DNT entries, 22 idiom traps as of 2026-07-21.)
- **Sync manifest:** `.l10n-sync/manifest.json` (confirmed). Pipeline scripts live inside
`.l10n-sync/` — the root `scripts/` dir is outside the file set localization work may
touch (§1), and keeping pipeline code self-contained simplifies review.
- **Docs syntax:** Markdown + Markdoc tags (heavy `table`/`admonition`/`tab` usage; product
names injected via `{% $env.PUBLIC_VAR_* %}` variables defined in `.env.*` — preserve
byte-for-byte); React landing pages in `@theme/markdoc/pages/` (see §6). **OpenAPI
(confirmed):** the @v2026.03 reference binds to the in-version, git-tracked spec
`products/payments-direct-2/@v2026.03/api-docs/payments-direct-api/payments-direct-2-api.yml`
(via `sidebars.yaml`). Root `api-docs/` (incl. `dist/`) is an empty, config-ignored husk;
there is no root `openapi/` dir. See §11.


## 3. Human review gate

- Every AI-produced translation is a **draft** until approved by a native pt-BR reviewer.
- All pt-BR changes are delivered as a **GitLab merge request**, never pushed to the
default branch. Branch naming: `l10n/pt-br/<short-description>`.
- MR description must include: source files/sections changed, the English diff (or link),
new termbase entries proposed, and items flagged for reviewer attention.
- Do not self-approve, merge, or resolve review discussions.
- Proposed native reviewers for this track (per the initiative's translation-review
assignments, June 2026): **Tiago Leite and Luis Pain** — pending confirmation. Route
review requests to them once confirmed; the Brazil corridor page goes to them with
extra lead time.


## 4. What is translated — and what is not

Translate:

- Prose: paragraphs, headings, admonition text, table cells, list items, image alt text,
link text, and front matter fields that render — confirmed set (2026-07-21): `title` and
`description` only. (`toc`, `tocMaxDepth`, `excludeFromSearch` are config — never
translate.)
- Code comments inside samples, ONLY where they explain something to the reader.
- Diagram/Mermaid labels where they are prose.


Never translate:

- Code identifiers, API endpoints, HTTP methods, parameter/field names, enum values,
header names, payload keys, CLI commands, file paths, environment variable names.
- Markdoc tag names/attributes, HTML tags, front matter keys, anchor IDs, YAML config keys.
- Country/corridor codes and folder-derived labels (`br`, `us-usd`, `apac`, ISO codes).
- Product names and everything on the termbase Do-Not-Translate sheet (Pix, TED, boleto,
CPF/CNPJ, IOF, Selic, CDI, Open Finance, Drex, blockchain/token/stablecoin/staking,
XRP/RLUSD/Ripple product names, "Payments Direct", "RippleNet" `[EXTEND]`).
- URLs. Internal links keep the same relative path — Realm resolves them within the
locale; never prefix `/pt-BR/` or rewrite into `@l10n/` paths.


## 5. Structural preservation rules

1. **Mirror or nothing.** Exact mirrored path under `@l10n/pt-BR/`, version folder
included. Never rename files or folders as part of translation.
2. **Preserve structure exactly:** heading hierarchy, section count, Markdoc tags in
place, code blocks byte-for-byte (except translatable comments), front matter keys in
the same order.
3. **Anchors (decided 2026-07-21):** translated headings change auto-generated slugs.
Policy: translate headings normally and **rewrite every `#fragment` link inside the
pt-BR copy to the slug of the translated heading** (85 in-scope fragment links, all
auto-slug, most same-page). No explicit anchor IDs are added to English source. The
sync pipeline includes a fragment-link check on translated files to catch mismatches.
4. **Partials first.** Translate included partials/snippets before the pages that include
them.
5. **Images:** `@v2026.03/images/` contains English UI screenshots (~60+ under
`ripplenet-home/`). Never edit images; log every English-text image referenced by a
translated page as a localization gap in the MR description.


## 6. Portal chrome, landing pages, and UI labels

- The Payments Direct 2.0 landing/navigation pages are **React components** in
`@theme/markdoc/pages/` (`PaymentsDirect2Landing/`, `Direct2*Landing/`, etc.). Their
visible text localizes via **translation keys** in `@l10n/pt-BR/translations.yaml`,
not mirrored files — and only if keys exist in the components.
- Scope decision (2026-07-21): **landing pages are IN pilot scope, as a docs-team
dependency.** Bootstrap inventory found all 9 in-scope components
(`PaymentsDirect2Landing3`, `Direct2ConceptsLanding3`, `Direct2PayoutNetworkLanding4`,
`Direct2FeaturesLanding3`, `Direct2UserGuidesLanding3`,
`Direct2IntegrationResourcesLanding3`, `Direct2MonitorLanding3`,
`Direct2DeveloperGuidesLanding3`, `Direct2CombinedDataRequirementsPicker`) are 100%
hardcoded English JSX with zero translation-key usage. This is reported as a source
defect (§9) with file:line evidence; the docs team must add translation keys to the
components. Until keys exist, landing-page localization is **blocked on that
dependency** — translate the keys in `@l10n/pt-BR/translations.yaml` as soon as they
land; landing tiles fall back to English meanwhile.
- Sidebar labels (finding 2026-07-21): Realm's `translate` command collects only
`*TranslationKey` entries from config/sidebars — and `@v2026.03/sidebars.yaml` has
none, so sidebar labels are currently **not localizable**. Adding
`labelTranslationKey` entries to the English `sidebars.yaml` is a **docs-team
dependency** (reported per §9). Once keys exist, regenerate with
`echo y | npx @redocly/cli translate pt-BR` (the command prompts interactively) and
translate only keys rendering on Payments Direct 2.0 pages; leave other products'
keys untranslated. NOTE: the generated defaults for Portuguese chrome look pt-PT
("Transferir", "separador") — review/override them per §10; pt-PT is never a
reference.


## 7. Configuration and CI guardrails

1. **Vale:** the repo lints prose with Vale (`.vale.ini`, `ci/vale/styles/` — Microsoft,
Ripple, Readability, proselint). These are English rulesets and will flood errors on
pt-BR content. Before the first content MR, propose a `.vale.ini` change excluding
`@l10n/**` (or a pt-BR-specific minimal ruleset) — as its own reviewed change.
2. **Locale enablement — DONE.** `pt-BR` is in the `redocly.yaml` `l10n` block on the
default branch and the locale is live. The staged path agreed 2026-07-21 was followed:
the block was held out of merged config until the pilot was approved, verified in the
preview environment, then landed as its own small MR. Adding a locale makes the
language picker visible **portal-wide**, so the mixed-language experience (localized
chrome plus English content on out-of-scope products) is the accepted state.
**No picker suppression was adopted**: overriding Redocly's internal picker is
high-maintenance and pt-br fallback URLs stay reachable regardless. Scoping the picker
to Payments Direct 2.0 only is a known, deferred option; it needs three surfaces
(navbar, footer, mobile menu) and `useCurrentProduct()`, not a config flag.
3. **CI — landed.** The drift-check job (§8.4) is live in `.gitlab-ci.yml` as
`l10n-drift-check`: `allow_failure: true`, warn-only, and scoped by a `changes:` rule
to `products/payments-direct-2/@v2026.03/**/*`. It runs
`node .l10n-sync/generate-manifest.mjs --check` against the working tree.
`.l10n-sync/` lives on the default branch, so there is no runtime fetch; if the job
reports STALE, retranslate the affected sections and commit a regenerated
`manifest.json` in the same MR. The route to the default branch that DOC-6065 entry 7
said did not exist now does: pt-BR content, the locale config and this tooling are all
merged. CI config is still shared repo state, so changes to this job belong in their
own MR rather than bundled with translation content.


## 8. Maintenance pipeline (the core of this prototype)

1. **Manifest.** `.l10n-sync/manifest.json` maps each in-scope source file → translated
file, with a hash per heading-delimited section recorded at translation time.
2. **Sync task.** On "sync"/"update" requests:
a. Diff current English section hashes against the manifest.
b. A stale flag is a signal to verify, not an instruction to re-translate:
confirm the English actually changed before touching the pt-BR. Sections
whose English is unchanged keep their approved translation byte-identical.
c. For light edits, revise the previous approved pt-BR text minimally — never
re-translate from scratch.
d. Update manifest; add new source pages; flag (never delete) orphaned pt-BR files.
A source page withdrawn from the release is declared in `scope.json` under
`withdrawnSources` and drops out of `files`. `generate-manifest.mjs` then reports
it as an orphan on every run and keeps the pt-BR page on disk. An in-scope file
that goes missing *without* being declared is still a hard error, so a silent
deletion cannot pass unnoticed. If the corridor returns, the generator says so;
move the entry back into `files` and re-check it for drift.
**Retained does not mean reachable** (Harold, 2026-09-30): a withdrawn corridor
must not be readable in pt-BR just because its translation still exists. Add the
`@l10n/<locale>/<source>` path to the `ignore` list in `redocly.yaml` so the page
is excluded from the build, and remove that line if the corridor returns. Keeping
the file and excluding the route are complementary, not alternatives.
e. Open an MR per §3 containing only what changed.
3. **Translation memory.** Append approved segment pairs to `.l10n-sync/tm.jsonl` on
native-reviewer approval, and consult it on every pass. Harvest on **approval**, not
on merge: approval is what makes a pair authoritative, and that ordering is unchanged
now that these branches do merge to the default branch. Scope every harvest to the
pages a reviewer actually approved (`harvest-tm.mjs --pages`); a whole-corpus harvest
is refused by design, because it would record unreviewed output as approved.
4. **Drift check.** GitLab CI job that recomputes hashes and warns/fails when pt-BR pages
are stale.
5. **Version carry-forward (planned, not yet active).** `@v2026.04` already exists and is
structurally near-identical to `@v2026.03`. When localization moves to a newer version,
the pipeline seeds it by matching sections against the TM and manifest — expect high
reuse. Do not translate `@v2026.04` until explicitly instructed. **Confirmed
(2026-07-20): `@v2026.03` is the current published version of the spec and docs;
the `@v2026.04` release is delayed pending an engineering fix.** When its release
is rescheduled, plan the carry-forward pass to land alongside it.
6. pt-BR files are generated artifacts: reviewer corrections land via MR review changes
(which feed the TM on approval), never as untracked hand edits made outside MR review.


## 9. Reporting, not fixing, source defects

While translating, report (in the MR description or a linked issue) — do not silently
fix: broken links, outdated screenshots, stale English content, hardcoded strings in
landing components, inconsistent source terminology. English fixes are the docs team's
call.

## 10. Voice, register, and variant

- Professional, plain technical Portuguese; address the reader as **você**; gerund forms
("estamos processando", never pt-PT "estamos a processar").
- Developer-docs conventions: "request" = **requisição** (never "pedido"); "response" =
**resposta**; **endpoint, payload, webhook, deploy, sandbox** stay in English
(established BR dev anglicisms) `[EXTEND]`.
- pt-BR only: "extrato" not "extracto", "registro" not "registo", "celular" not
"telemóvel", "cheque especial" not "descoberto bancário". EU corpora (DGT-TM, IATE,
EUR-Lex) are concept references only — never copy pt-PT surface forms.
- Ambiguity traps (full list in termbase): saldo≠balanço, extrato≠declaração,
tarifa/imposto/taxa, reembolso≠estorno, solicitar≠aplicar, "token" (crypto asset vs.
OTP), "chave" (chave Pix vs. chave privada vs. **chave de API** = API key).
- Formats in prose: `R$ 1.234,56`; dates `DD/MM/AAAA`; numbers `1.234.567,89`. Never
reformat literal values inside API request/response samples — wire formats are code.
- Brazil terminology precedent: the corridor pages (`integration-resources/latam/br/`)
are small enum-table pages; the highest Brazil-terminology-density pages in scope are
`introduction/concepts/financial-instruments.md` and
`introduction/concepts/payment-identities.md` (Pix, CPF/CNPJ, PII fields). Route these
and the corridor pages to the native reviewers with extra care and harvest approved
pairs into the termbase.


## 11. OpenAPI / API reference handling

- **DECISION 2026-09-14: the OpenAPI spec is not translated.** The API reference renders in
English at the pt-br URL, like any untranslated page (§2 fallback). Do not create, restore,
or translate a mirrored copy of the spec, and do not re-add it to `.l10n-sync/scope.json`.
- It was removed from `scope.json` and `manifest.json` on that date. It had never actually
been translated: the mirrored path contained only a `.gitkeep`, no TM records were
harvested from it (`harvest-tm.mjs` processes `.md` only), so nothing was discarded.
- Reversing this decision means re-adding the path to `scope.json`, regenerating the
manifest, and reinstating the handling below, which is retained as the record of what was
decided in July and superseded in September:
> *Superseded (2026-07-21).* Translate ONLY `summary` and `description` fields
(info/path/operation/parameter/schema level), tag descriptions, and example prose, in the
mirrored copy of whichever spec the @v2026.03 reference pages bind to. Never translate
`operationId`, paths, parameter names, schema property names, enum values, security
scheme names, server URLs, or `x-` extension keys. The bound spec is the in-version,
git-tracked file `@v2026.03/api-docs/payments-direct-api/payments-direct-2-api.yml`
(6,449 lines, 675 `description:` fields, Markdoc `$env` vars inside descriptions),
hand-maintained in this repo rather than generator output. The root
`api-docs/payments-direct-api/dist/` directory is empty and the whole root `api-docs/`
tree is ignored in `redocly.yaml`: a legacy husk, not a pipeline.
- **Known defect in the superseded handling, relevant only if this is ever reversed.** The
spec was hashed as a single `_file` section, so any change anywhere across 6,449 lines
marked the whole spec stale. The July note claimed release-time spec refreshes would be
"detected as stale sections by the manifest hash check like any other page". They were
not; they were detected as one all-or-nothing flag. Sectioning by path or operation would
be a prerequisite for reversing the decision.


## 12. Pilot scope

- Pilot set (decided 2026-07-21), all under `products/payments-direct-2/@v2026.03/`:
  1. `introduction/concepts/payment-identities.md` — conceptual page AND the real Brazil
terminology stress test (4,036 words; CPF/CNPJ/PII field terminology).
  2. `api-docs/developer-guides/create-a-payment.md` — developer guide, core API flow.
  3. `api-docs/payments-direct-api/payments-direct-2-api.yml` (mirrored copy) —
**dropped 2026-09-14, see §11.** It was never translated; the pilot set is effectively
the four Markdown pages.
  4. `api-docs/integration-resources/latam/br.md` — Brazil corridor landing (41 words).
  5. `api-docs/integration-resources/latam/br/brl.md` — BRL data requirements
(139 words; purpose-code enum tables; sets corridor-page precedent).
Inventory note: the corridor pages are tiny; the terminology-density expectation in §10
is carried by `payment-identities.md` (and later `financial-instruments.md`, 6,781
words, the densest Pix/Brazil page in scope).
- Demo script: translate pilot → a controlled fixture edit on a scratch branch → run sync →
targeted MR → approval, showing only the affected pt-BR sections updated. No merge step.
- Metrics: reviewer edit rate, turnaround per page, stale-section detection accuracy.