Skip to content
react-wrapped

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.

Twój rok ukrywa 4 sceny.

Twoje osiągnięcia i kilka niespodzianek. Każda trafia do filmu, gdy skan ją znajdzie.

  • Wciąż ukryte
  • Wciąż ukryte
  • Wciąż ukryte
  • Wciąż ukryte

The year scan in Polish. Run it: the headlines count in Polish too.

scan-in-polish.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.
  • 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

GroupWhat it holds
playerThe transport under the video: play, pause, full screen, the clock.
timelineThe row of segments and the preview that opens over one.
editorThe scene editor: its title, its buttons, the transition picker.
fieldsA scene's editable fields, by their English name: { Headline: "Nagłówek" }.
settingsThe settings panel: colours, format, cells, speed, grain.
scanThe year scan: its headline for every state, its buttons, the card it turns over.
galleryThe graphics to keep: heading, locked tiles, download buttons.
shareCopy link, Download, and the line shown while the video file is made.
videoWhy a video file could not be made.
rarityCommon, Rare, Epic, Legendary.
scenes, chartsThe few words the ready-made scenes and charts print on their own.
palettes, transitions, formatsNames 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

PropTypeWhat it does
labels?WrappedLabelOverridesThe words to replace, in the shape of DEFAULT_LABELS. Anything left out keeps its English.
locale?stringHow the controls write numbers, such as "pl-PL". Left out, it is the provider's above, or "en-US".
classNames?SlotClassNamesA class per slot, such as "player-play", for every part under the provider. See styling.
children?ReactNodeEverything 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.

Last updated . Read this page as Markdown.