# Labels and translation

Every word the library shows on its own, from Play to the scan's headlines, lives in one object: `DEFAULT_LABELS`. Wrap any part of your page in `WrappedProvider` and hand it the words you want instead. Use it to translate the interface, or just to make it sound like your product.

> Live preview: try it at https://bettertyped.github.io/react-wrapped/docs/guides/labels/
>
> The year scan in Polish. Run it: the headlines count in Polish too.

`scan-in-polish.tsx`

```tsx
import {
  type CalendarLevels,
  resolvePalette,
  type WrappedConfig,
  type WrappedLabelOverrides,
  WrappedProvider,
} from "@react-wrapped/core";
import { type HiddenDrop, useOpenedDrops, YearScan } from "@react-wrapped/scan";

/** Polish needs three forms of a counted noun: 1 scena, 2 sceny, 5 scen. A label is a function, so it can have them. */
const scenes = (count: number) => {
  const few = count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 12 || count % 100 > 14);
  return `${count} ${count === 1 ? "scenę" : few ? "sceny" : "scen"}`;
};

// Only what this page shows is translated. Everything left out stays in English.
const POLISH: WrappedLabelOverrides = {
  scan: {
    hiding: (count) => `Twój rok ukrywa ${scenes(count)}.`,
    about: "Twoje osiągnięcia i kilka niespodzianek. Każda trafia do filmu, gdy skan ją znajdzie.",
    scanning: (left) => `Skanuję twój rok. Zostało jeszcze: ${left}.`,
    scanningLast: "Szukam ostatniej.",
    foundAll: (count) => `Wszystkie znalezione: ${count}.`,
    moreHiding: (count) => `W twoim roku kryje się jeszcze: ${count}.`,
    done: "Są już w twoim filmie. Uruchom skan ponownie, żeby zobaczyć je jeszcze raz.",
    start: "Skanuj mój rok",
    resume: "Skanuj dalej",
    findAll: "Znajdź wszystkie",
    again: "Skanuj ponownie",
    finds: "Twoje znaleziska",
    stillHidden: "Wciąż ukryte",
    foundOn: (day) => `Znalezione: ${day}`,
  },
  rarity: { common: "Pospolite", rare: "Rzadkie", epic: "Epickie", legendary: "Legendarne" },
};

export function ScanInPolish({
  calendar,
  drops,
  describeDay,
  config,
}: {
  calendar: CalendarLevels;
  drops: readonly HiddenDrop[];
  describeDay: (day: number) => string;
  config: WrappedConfig;
}) {
  const { opened, open } = useOpenedDrops(null);
  return (
    // Everything inside the provider reads its words from it. Numbers are written the way `locale` writes them.
    <WrappedProvider labels={POLISH} locale="pl-PL">
      <YearScan
        calendar={calendar}
        palette={resolvePalette(config)}
        drops={drops}
        found={opened}
        onOpen={open}
        describeDay={describeDay}
      />
    </WrappedProvider>
  );
}
```

## How it works

- **Replace only what you need.** `labels` is a partial of `DEFAULT_LABELS`. Anything you leave out stays as it is.
- **A sentence with a number in it is a function.** `scan.hiding` gets the count and returns the sentence, so a language with three plural forms can have all three.
- **Providers nest.** One around your app can set the language, and one around a single player can reword that player. The inner one is merged over the outer one.
- **No provider is fine.** Without one, every part reads the English defaults.
- **It restyles too.** The same provider takes `classNames`, a class per part for everything under it. That is covered in [styling](https://bettertyped.github.io/react-wrapped/docs/guides/styling/).
- **Your scenes are yours.** The text you pass to a scene is never touched. Labels cover only the words the library adds itself.

## The groups

| Group | What it holds |
| --- | --- |
| `player` | The transport under the video: play, pause, full screen, the clock. |
| `timeline` | The row of segments and the preview that opens over one. |
| `editor` | The scene editor: its title, its buttons, the transition picker. |
| `fields` | A scene's editable fields, by their English name: `{ Headline: "Nagłówek" }`. |
| `settings` | The settings panel: colours, format, cells, speed, grain. |
| `scan` | The year scan: its headline for every state, its buttons, the card it turns over. |
| `gallery` | The graphics to keep: heading, locked tiles, download buttons. |
| `share` | Copy link, Download, and the line shown while the video file is made. |
| `video` | Why a video file could not be made. |
| `rarity` | Common, Rare, Epic, Legendary. |
| `scenes`, `charts` | The few words the ready-made scenes and charts print on their own. |
| `palettes`, `transitions`, `formats` | Names by id. Empty by default: a missing id keeps its built-in name. |

The object is typed, so your editor lists every key and what each function is given. Read `DEFAULT_LABELS` in `@react-wrapped/core` for the full English text.

## Props

| Prop | Type | What it does |
| --- | --- | --- |
| `labels?` | `WrappedLabelOverrides` | The words to replace, in the shape of `DEFAULT_LABELS`. Anything left out keeps its English. |
| `locale?` | `string` | How the controls write numbers, such as `"pl-PL"`. Left out, it is the provider's above, or `"en-US"`. |
| `classNames?` | `SlotClassNames` | A class per slot, such as `"player-play"`, for every part under the provider. See [styling](https://bettertyped.github.io/react-wrapped/docs/guides/styling/). |
| `children?` | `ReactNode` | Everything that should read its words from this provider. |

## Outside React

Three functions draw without React and take their words as an argument: `renderShareCard(spec, palette, { labels, locale })`, `renderInsightCard(spec, palette, { labels, locale })` and `renderVideo(take, size, { labels })`. Build the full table for them with `mergeLabels(DEFAULT_LABELS, yours)`. Inside a component, `useLabels()` hands you the merged table of the nearest provider.

```tsx
import { DEFAULT_LABELS, mergeLabels, resolvePalette } from "@react-wrapped/core";
import { renderShareCard } from "@react-wrapped/graphics";

const labels = mergeLabels(DEFAULT_LABELS, { rarity: { legendary: "Legendarne" } });
const png = await renderShareCard(card, resolvePalette(config), { labels, locale: "pl-PL" });
```

## What is not covered yet

- Numbers drawn inside the video itself (a counter, a chart's pill) are written the `en-US` way: 1,687. Only the controls around the video follow `locale`.
- Hour names in the charts (`hourName`) are English. Pass your own labels in the chart's data where it takes them.

## Related

- [Custom palette and chrome colours](https://bettertyped.github.io/react-wrapped/docs/guides/palette-and-chrome/)
- [Styling](https://bettertyped.github.io/react-wrapped/docs/guides/styling/)
- [YearScan](https://bettertyped.github.io/react-wrapped/docs/scanner/year-scan/)
- [WrappedPlayer](https://bettertyped.github.io/react-wrapped/docs/player/wrapped-player/)
- [Build the page from parts](https://bettertyped.github.io/react-wrapped/docs/guides/from-parts/)

Source: https://bettertyped.github.io/react-wrapped/docs/guides/labels/
