# Internationalization (i18n)

Mark It Down ships with translations for **English** (default) plus
**Spanish**, **French**, **German**, and **Arabic** (RTL). VSCode picks
the locale from its own UI language setting; you don't need to configure
the extension separately.

## What's localised

* **Manifest strings** — command titles, view names, settings descriptions,
  the activity-bar container, the "no notes yet" welcome message.
  Source of truth: `package.nls.json` (English fallback) + per-locale
  `package.nls.<lang>.json`.
* **Runtime strings** — status-bar labels, empty-state hints, exporter
  notifications. Source of truth: `l10n/bundle.l10n.json` + per-locale
  `l10n/bundle.l10n.<lang>.json`.

## Locales shipped

| Code | Language | Notes |
| --- | --- | --- |
| (default) | English | Source of truth |
| es | Spanish | Full coverage |
| fr | French | Full coverage |
| de | German | Full coverage |
| ar | Arabic | Full coverage; RTL handled natively by VSCode |

For RTL: VSCode's locale loader sets `dir="rtl"` on the workbench
automatically when the active language is Arabic. Sidebar, modals, and
status bar mirror without code changes. The custom markdown editor
webview inherits the workbench direction so headings, lists, and tables
render right-to-left.

## How VSCode resolves locales

1. The user picks a language via `Configure Display Language` (it
   downloads the matching VSCode language pack).
2. On extension load, VSCode looks for `package.nls.<lang>.json`
   alongside `package.json` and substitutes `%key%` references in the
   manifest.
3. Runtime strings resolve via `vscode.l10n.t(key, …)` by reading
   `l10n/bundle.l10n.<lang>.json`. The `l10n` field in `package.json`
   points the resolver at this directory.
4. Missing keys fall back to the English source — there's no
   `[Untranslated]` marker shown to the user.

## Calling `t()` in code

```typescript
import { t } from './i18n/t';

const label = t('warehouse.statusBar.idle.multi', count);
```

The wrapper around `vscode.l10n.t` keeps call sites stable across
runtime changes and makes the bundle key easy to grep.

Positional placeholders use `{0}`, `{1}` … in the bundle string and the
extra args fill them in order. Don't concatenate translated strings —
build a single bundle key per sentence so locales can re-order it.

## Parity check

`scripts/check-i18n.mjs` enforces that every `package.nls.<lang>.json`
and `l10n/bundle.l10n.<lang>.json` has **exactly** the same keys as the
English source — no missing keys, no extras. CI runs it on every PR via
`.github/workflows/ci.yml`.

Run locally:

```bash
npm run check-i18n
```

A clean output reads `i18n parity OK — checked 8 locale file(s).`

## Adding a new locale

1. Copy `package.nls.json` to `package.nls.<lang>.json` and translate
   each value.
2. Copy `l10n/bundle.l10n.json` to `l10n/bundle.l10n.<lang>.json` and
   translate each value.
3. Run `npm run check-i18n` — fix any reported discrepancies.
4. Run `npm test` — the parity vitest in `tests/unit/i18n/parity.test.ts`
   asserts the same invariant.
5. Update this doc's "Locales shipped" table.

VSCode auto-discovers the new locale on the next launch — no manifest
declaration needed.

## Adding a new key

1. Add it to `package.nls.json` (or `l10n/bundle.l10n.json`) first.
2. Add the matching key to **every** `<lang>` file in that family.
3. Reference it in the manifest as `%key%` or in code via `t('key')`.
4. The parity check + vitest will catch any locale you forgot.

## Testing

```bash
npx vitest run tests/unit/i18n
```

11 tests cover: at-least-4 locales beyond English (per family), per-file
exact-key parity for each locale, every value is a non-empty string.

## Limitations

- Webview content (the custom markdown editor's toolbar buttons,
  table-export labels, mermaid controls) is currently English-only.
  Plumbing the bundle into the webview means either (a) shipping the
  bundle as JSON and resolving on the renderer side, or (b) sending the
  resolved strings via the existing `update` postMessage. Either is a
  natural follow-up.
- Slideshow preview (reveal.js client) and the published static site
  also remain English-only — they don't run in a VSCode locale context.
- Translations beyond English were drafted in a single pass and not
  reviewed by native speakers; PRs to refine es/fr/de/ar idiom are very
  welcome.
