# Oshun V1 Translation Workflow

> Companion to `docs/launch/locale-coverage.md`. Defines the workflow, glossary
> ownership, language fallback, RTL handling, and text-expansion QA that govern
> customer-facing strings in `libs/oshun/i18n/` and domain-owned catalogs.

## Catalog ownership

| Surface or domain        | Source-of-truth catalog                                       | Owner                      |
| ------------------------ | ------------------------------------------------------------- | -------------------------- |
| Shared shell strings     | `libs/oshun/i18n/src/index.ts` (`CUSTOMER_MESSAGES`)          | Platform UI + localization |
| Assistant disclosures    | `libs/oshun/i18n/src/index.ts`                                | T&S + assistant product    |
| Privacy disclosures      | `libs/oshun/i18n/src/index.ts`                                | Privacy + legal            |
| Tara content             | `libs/oshun/i18n/` shared keys + Tara seed/content records    | Tara editorial             |
| Veritas content          | `libs/oshun/i18n/` shared keys + Veritas seed/content records | Veritas editorial          |
| Nyx event copy           | `libs/oshun/i18n/` shared keys + Nyx seed/content records     | Nyx editorial              |
| Arete prompts            | `libs/oshun/i18n/` shared keys + Arete seed/content records   | Arete product              |
| Nisaba metadata          | `libs/oshun/i18n/` shared keys + canonical passage metadata   | Nisaba editorial           |
| Metis course frames      | `libs/oshun/i18n/` shared keys + Metis course seed records    | Metis product              |
| Persona disclosures      | persona records inside `libs/oshun/persona-policy-lilith/`    | T&S persona ops            |
| Operator (admin) strings | `apps/oshun/admin/src/i18n/`                                  | Admin product              |

Shared shell strings — anything that appears in the customer shell chrome or in
the assistant compliance/disclosure copy — live in the canonical catalog at
`libs/oshun/i18n/`. Domain-owned content records may carry source text, but
their customer-visible shared labels, disclosures, status copy, formatting
rules, fallback rules, and launch-locale metadata depend on this package and may
not redefine its keys.

## Glossary

A single glossary, owned by the localization lead, governs proper nouns, persona
names, domain names (Tara, Veritas, Nyx, Arete, Nisaba, Metis), and
trust-and-safety terminology that must read identically across surfaces.

- **Glossary location**: `libs/oshun/i18n/glossary/`. Each glossary entry is
  JSON: source term, allowed translations per locale, do-not- translate flag,
  gender/animacy hints, disclosure context.
- **Glossary review**: localization lead + T&S lead sign-off for terms that
  touch persona identity or refusal copy.
- **Glossary lint**: a glossary-coverage check runs against every PR that adds
  or modifies a translation: terms in the glossary must translate to the allowed
  values per locale; do-not-translate terms must remain in source language.
  Failures block merge.

## Fallback chain

The runtime fallback for a missing key is:

1. Requested locale (e.g., `es-US`).
2. Language-only locale (e.g., `es`).
3. `en-US`.

The chain is implemented in `resolveFallbackChain` and `translate` in
`libs/oshun/i18n/src/index.ts`. The same package also exposes the mapping from
legacy customer language preferences (`en`, `es`, etc.) to launch locales
(`en-US`, `es-US`, etc.) so web and mobile selectors cannot advertise languages
outside the V1 launch coverage list. A fallback hit emits an analytics event so
that gaps surface in the launch dashboards.

A fallback gap discovered on a critical surface (disclosure, privacy, crisis) is
a sev2 — it does not block GA, but it blocks the locale from being marked
launch-quality for that surface.

## RTL handling

`ar` and `he` are RTL launch locales. Every shell, domain, and admin surface
must:

1. Mirror the layout: the primary visual flow runs right-to-left.
2. Mirror icons that imply direction (back/forward, next/previous, chevrons,
   sliders).
3. Preserve bidi safety: customer-controlled strings appear inside bidi-isolated
   runs.
4. Number and digit policy: western numerals by default; Arabic-Indic digits
   opt-in per content piece for `ar`.
5. Punctuation: locale-correct quotation marks, dashes, ellipsis.

The cross-cutting RTL test suite lives at `tests/locale/rtl-smoke.spec.ts` and
runs against every locale-tagged view in Playwright.

## Text-expansion QA

Every shell layout is verified against the `LOCALE_EXPANSION_BUDGET` table in
the i18n library:

- `en-US`: 1.0 baseline.
- `es-US`: 1.25.
- `fr-FR`: 1.3.
- `de-DE`: 1.4.
- `ar`: 1.2 (script width plus mirroring).
- `he`: 1.15.
- `ja-JP`: 0.7 (denser script).
- `pt-BR`: 1.3.

The customer preference aliases exposed in profile settings and the language
switcher are launch-scoped: `en`, `es`, `fr`, `de`, `ar`, `he`, `ja`, and `pt`.
Unsupported prelaunch aliases such as `zh`, `ko`, and `hi` are not surfaced to
customers until they receive a launch-locale coverage row and full catalog
coverage.

QA generates synthetic strings at each locale's budget against every catalog
key, drops them into the shell, and verifies no truncation, no unintended wrap,
no overflow into the next region. Failures are filed against the shell owner;
layout fixes are required before the locale can be marked launch-quality.

## Translation workflow

1. **Author the source string** in `en-US` in the matching catalog. PRs must
   include a one-sentence translator note when the meaning is non-obvious (e.g.,
   "disclosure copy — must read calmly, not alarming").
2. **Run the catalog-lint check** locally; this verifies the key is declared in
   the type union and the placeholder convention is correct.
3. **Trigger the localization pipeline**: the localization lead routes the new
   key to the contracted translators with context (surface, character budget,
   tone, glossary references).
4. **Review the returned translations**: locale leads + glossary lint + visual
   review against the actual surface (Storybook or staging) at the
   expansion-budget length.
5. **Land the translations**: PR merges only when every launch locale has a
   translation (the
   `every customer message key has a translation in every launch locale` test
   enforces this).
6. **Run the runtime smoke**: in staging, render the surface in every locale;
   verify no fallback hits, no truncation, no RTL break.

## Audit and provenance

- Every change to `CUSTOMER_MESSAGES` lands with the i18n maintainer + matching
  domain maintainer review.
- Trust-and-safety disclosure copy changes require T&S sign-off.
- Privacy disclosure copy changes require privacy + legal sign-off.
- The translation pipeline records translator identity (team or contractor),
  timestamp, and source-language hash so customer-facing disclosures have a
  clean provenance audit trail.

## Adding a new launch locale

Adding a locale is a launch-class change:

1. Add the locale to `OSHUN_LAUNCH_LOCALES`.
2. Add a row to `LOCALE_EXPANSION_BUDGET`, `LOCALE_CALENDAR`, and `RTL_LOCALES`
   if applicable.
3. Translate every key in `CUSTOMER_MESSAGES`.
4. Run the RTL smoke and the expansion-budget QA across every shell.
5. Update `docs/launch/locale-coverage.md` to reflect the locale's coverage
   tier.
6. Run the locale-coverage gate at the next release-readiness review.

Until every catalog entry passes the test suite, the locale stays in the
`staging-only` tier and is not surfaced to customers.

## Owner

Localization lead owns this workflow. Platform UI lead, T&S lead, privacy lead,
and legal counsel co-review changes that affect disclosure or trust-touching
copy. Updates require sign-off from localization plus any affected co-owner.
