# Internationalization (i18n)

VS Engine serves the same underlying data to clients in different
languages. The i18n layer resolves a locale from the authenticated
`clientId` and rewrites display strings (titles, subtitles, labels)
at serve time.

## Locale resolution

Locale is an attribute of the client, not of the request. The
request never sends `Accept-Language`; instead:

```
authenticated clientId → client config → language → Locale
```

Defined in `src/i18n/index.ts`:

| Client        | Language | Locale code |
| ------------- | -------- | ----------- |
| Hemmaklubben  | Swedish  | `sv`        |
| Junibet       | English  | `en`        |

## String catalog

All display strings live in `src/i18n/strings.ts` under nested keys
grouped by feature (`junibet.accumulators.*`, `leaders.title.*`,
`ligaBast.*`, `standalone.*`, …). Each key has a `sv` and an `en`
translation, enforced by the types.

Example:

```ts
t('junibet.accumulators.safe', 'en') // → "Safe"
t('junibet.accumulators.safe', 'sv') // → "Säker"
```

## Cached-payload rewrite path

Some league-scoped widgets (`/leaders/:leagueId`,
`/liga-bast/:leagueId`, `/standalone/:leagueId`) cache ONE payload
per league and serve it to every client that focuses on that league.
Because the cache is language-agnostic, those endpoints run a small
*rewrite* step at serve time:

- `localizeLeaderBoards(data, locale)`
- `localizeLigaBast(data, locale)`
- `localizeStandaloneHighlights(data, locale)`

These functions walk the cached payload and replace display strings
in-place. They are pure (no disk writes) so the cache remains
locale-neutral.

## Shape-function locale threading

For non-cached widgets, the locale is threaded all the way down to
the shape functions in `src/assemble/widgetShaper.ts`, which call
`t(key, locale)` directly. Never hardcode Swedish strings in a
shape function.

## Adding a new translated string

1. Add the new key to `src/i18n/strings.ts` with both `sv` and `en`
   values.
2. Call `t('your.key', locale)` in the shape or rewrite function.
3. Run `npm run docs:generate` — the OpenAPI spec harvests any
   schema changes you made while wiring the key in.
