docs(site): add i18n type reference pages and review fixes

Add Locale, BuiltInLocale, TranslationParams, Translations, and Translator
reference pages plus createTranslator. Cross-link types from concept and
guides; fix React examples and register-locale satisfies pattern.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sam Potts
2026-07-13 08:22:39 +10:00
co-authored by Cursor
parent fd54f36110
commit 266eb04407
12 changed files with 320 additions and 13 deletions
+14 -8
View File
@@ -23,13 +23,18 @@ Video.js translates control labels, ARIA text, tooltips, and error copy through
```
```tsx title="react"
<html lang="es">
<Provider>
<VideoSkin>
<Video src="..." playsInline />
</VideoSkin>
</Provider>
</html>
import { Provider, VideoSkin, Video } from '@videojs/react/video';
// Set `<html lang="es">` on the document (layout, _document, or index.html)
export function App() {
return (
<Provider>
<VideoSkin>
<Video src="..." playsInline />
</VideoSkin>
</Provider>
);
}
```
Register a locale once (or rely on lazy-loaded built-in packs), set `lang`, and skins pick up translated strings automatically.
@@ -38,7 +43,7 @@ Register a locale once (or rely on lazy-loaded built-in packs), set `lang`, and
Core controls expose keys, not visible labels. `PlayButtonCore.getLabel()` returns `'play'`; the translator turns that into `'Play'`, `'Reproducir'`, or your override.
Keys are typed in `TranslationParams` — TypeScript catches missing `{param}` placeholders and wrong argument names at compile time. See `packages/core/src/core/i18n/types.ts` for the full key list.
Keys are typed in <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink> — TypeScript catches missing `{param}` placeholders and wrong argument names at compile time.
Parametric strings use `{placeholder}` tokens, for example `seekForward: 'Seek forward {seconds} seconds'` and `timeRemainingPhrase: '{duration} remaining'`.
@@ -147,6 +152,7 @@ For zero flash-of-English on SSR or locale switches, pass `translations` directl
## See also
- <DocsLink slug="reference/locale">`Locale`</DocsLink>, <DocsLink slug="reference/translations">`Translations`</DocsLink>, <DocsLink slug="reference/translator">`Translator`</DocsLink> — core types
- <DocsLink slug="how-to/i18n-register-locale">Register a custom locale</DocsLink>
- <DocsLink slug="how-to/i18n-override-translations">Override individual keys</DocsLink>
- <DocsLink slug="how-to/i18n-switch-locale">Switch locale dynamically</DocsLink>
@@ -12,7 +12,7 @@ This guide is for **contributors** adding or updating shipped packs — app auth
### Prerequisites
- Familiarity with <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and `TranslationParams`
- Familiarity with <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>
- English defaults in `packages/core/src/core/i18n/locales/en.ts` as the source of keys
## 1. Add the locale file
@@ -71,12 +71,12 @@ Preset skins use `Container`, which includes <DocsLink slug="reference/i18n-prov
```ts title="my-es.ts"
import type { Translations } from '@videojs/core/i18n';
const es: Partial<Translations> = {
const es = {
play: 'Reproducir',
pause: 'Pausa',
mute: 'Silenciar',
unmute: 'Activar sonido',
};
} satisfies Partial<Translations>;
export default es;
```
@@ -127,5 +127,6 @@ Load your module after the player script, before playback starts.
## What's next?
- <DocsLink slug="how-to/i18n-override-translations">Override individual keys</DocsLink>
- <DocsLink slug="reference/translations">`Translations`</DocsLink> type
- <DocsLink slug="how-to/i18n-switch-locale">Switch locale at runtime</DocsLink>
- <DocsLink slug="reference/register-i18n">`registerI18n` reference</DocsLink>
@@ -0,0 +1,50 @@
---
title: BuiltInLocale
description: Union of shipped BCP 47 locale tags with autocomplete in TypeScript
---
import DocsLink from '@/components/docs/DocsLink.astro';
`BuiltInLocale` narrows <DocsLink slug="reference/locale">`Locale`</DocsLink> to tags with shipped translation packs in `@videojs/core/i18n/locales/*`. TypeScript suggests these tags when you call `registerI18n`, `loadLocale`, or import locale modules.
## Import
```ts
import type { BuiltInLocale } from '@videojs/core/i18n';
```
Runtime lists are also exported:
```ts
import { BUILT_IN_LOCALES, LOCALE_ALIAS_TAGS, SHIPPED_LOCALE_TAGS } from '@videojs/core/i18n';
```
## Definition
```ts
type BuiltInLocale =
| (typeof BUILT_IN_LOCALES)[number]
| (typeof LOCALE_ALIAS_TAGS)[number];
```
`BUILT_IN_LOCALES` lists regional packs (`es`, `pt-BR`, `zh-CN`, …). `LOCALE_ALIAS_TAGS` adds shorthand tags (`pt`, `zh`) that resolve to regional packs through the lookup chain.
## Lazy loading
Providers call `loadLocale(tag)` for tags in `SHIPPED_LOCALE_TAGS` when a pack is not already registered. Explicit `registerI18n` or CDN locale modules skip the async gap on first paint.
## Examples
```ts
import es from '@videojs/react/i18n/locales/es';
registerI18n('es', es); // 'es' autocompletes as BuiltInLocale
```
Custom app locales (`'xx'`) use `Locale` but are not members of `BuiltInLocale`.
## Related
- <DocsLink slug="reference/locale">`Locale`</DocsLink>
- <DocsLink slug="how-to/i18n-add-built-in-locale">Add a built-in locale (contributors)</DocsLink>
- <DocsLink slug="how-to/i18n-register-locale">Register a custom locale</DocsLink>
@@ -15,7 +15,10 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro";
import { createI18n } from '@videojs/react/i18n';
const { I18nProvider, useTranslator } = createI18n({
loadLocale: async (tag) => import(`./locales/${tag}.json`),
loadLocale: async (tag) => {
const mod = await import(`@videojs/react/i18n/locales/${tag}`);
return mod.default;
},
});
function App() {
@@ -0,0 +1,19 @@
---
title: createTranslator
description: Build a typed translator from a resolved translation map
---
import UtilReference from "@/components/docs/api-reference/UtilReference.astro";
import DocsLink from "@/components/docs/DocsLink.astro";
`createTranslator` wraps a <DocsLink slug="reference/translations">`Translations`</DocsLink> map and returns a <DocsLink slug="reference/translator">`Translator`</DocsLink>. Providers call this internally after merging registry, lazy, and prop layers — use it directly for custom UI outside built-in mixins.
```ts
import { createTranslator, getI18nTranslations } from '@videojs/core/i18n';
const t = createTranslator(getI18nTranslations('fr'), 'fr');
t('play');
t('seekForward', { seconds: 5 });
```
<UtilReference util="createTranslator" />
@@ -0,0 +1,41 @@
---
title: Locale
description: BCP 47 language tag type for i18n registry and provider APIs
---
import DocsLink from '@/components/docs/DocsLink.astro';
`Locale` is the BCP 47 tag type used by <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, providers, and <DocsLink slug="reference/translator">`Translator`</DocsLink>. Shipped packs autocomplete as <DocsLink slug="reference/built-in-locale">`BuiltInLocale`</DocsLink>; any other tag remains valid at runtime.
## Import
```ts
import type { Locale } from '@videojs/core/i18n';
// or @videojs/html/i18n, @videojs/react/i18n
```
## Definition
```ts
type Locale = BuiltInLocale | (string & {});
```
The `(string & {})` pattern keeps custom tags (`'xx'`, `'en-US'`) type-safe without losing autocomplete for built-ins.
## Resolution
Providers and `getI18nTranslations` normalize tags and walk the parent chain (`es-MX` → `es` → `en`) via `localeLookupChain`. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for explicit vs ambient resolution.
## Examples
```ts
const es: Locale = 'es';
const custom: Locale = 'en-US';
const regional: Locale = 'pt-BR';
```
## Related
- <DocsLink slug="reference/built-in-locale">`BuiltInLocale`</DocsLink>
- <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>
- <DocsLink slug="how-to/i18n-switch-locale">Switch locale dynamically</DocsLink>
@@ -8,7 +8,7 @@ import DocsLink from "@/components/docs/DocsLink.astro";
`registerI18n` merges a partial translation map into the process-wide registry for a locale tag. English defaults are registered when `@videojs/core/i18n` loads; call `registerI18n` for custom locales or to patch shipped packs before the player renders.
Import from `@videojs/core/i18n`, `@videojs/html/i18n`, or `@videojs/react/i18n`. Keys are opaque camelCase tokens (`play`, `pause`) — see <DocsLink slug="concepts/i18n">Internationalization</DocsLink>.
Import from `@videojs/core/i18n`, `@videojs/html/i18n`, or `@videojs/react/i18n`. Keys are opaque camelCase tokens (`play`, `pause`) — see <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and <DocsLink slug="reference/translations">`Translations`</DocsLink>.
```ts
import { registerI18n } from '@videojs/react/i18n';
@@ -0,0 +1,66 @@
---
title: TranslationParams
description: Typed contract for translation keys and their placeholder arguments
---
import DocsLink from '@/components/docs/DocsLink.astro';
`TranslationParams` maps every opaque translation key to its argument shape. Keys with `never` accept only `t('key')`. Keys with an object accept `t('key', { … })` with typed placeholder names.
## Import
```ts
import type { TranslationParams } from '@videojs/core/i18n';
```
## Definition
```ts
type TranslationParams = {
play: never;
pause: never;
seekForward: { seconds: number | string };
timeRemainingPhrase: { duration: string };
// …
};
```
English defaults and the full key list live in `packages/core/src/core/i18n/locales/en.ts`. The authoritative TypeScript map is `packages/core/src/core/i18n/types.ts`.
## Parametric keys
| Key | Placeholders | Example English value |
| --- | --- | --- |
| `seekForward` | `{seconds}` | `Seek forward {seconds} seconds` |
| `seekBackward` | `{seconds}` | `Seek backward {seconds} seconds` |
| `playbackRateAria` | `{rate}` | `Playback rate {rate}` |
| `timeSliderValueTextRange` | `{current}`, `{duration}` | `{current} of {duration}` |
| `timeRemainingPhrase` | `{duration}` | `{duration} remaining` |
| `volumeSliderValueTextMuted` | `{percent}` | `{percent}, muted` |
| `indicatorVolumeWithValue` | `{value}` | `Volume {value}` |
All other keys are plain strings with no parameters.
## Usage with Translator
```ts
const t: Translator = createTranslator(translations, 'es');
t('play');
t('seekForward', { seconds: 10 });
t('timeRemainingPhrase', { duration: '1 minute' });
```
TypeScript rejects missing placeholders when defining <DocsLink slug="reference/translations">`Translations`</DocsLink> overlays:
```ts
registerI18n('es', {
seekForward: 'Adelantar', // error: missing {seconds}
});
```
## Related
- <DocsLink slug="reference/translations">`Translations`</DocsLink>
- <DocsLink slug="reference/translator">`Translator`</DocsLink>
- <DocsLink slug="concepts/i18n">Internationalization</DocsLink> — opaque keys overview
@@ -0,0 +1,53 @@
---
title: Translations
description: Partial map of opaque translation keys to localized strings
---
import DocsLink from '@/components/docs/DocsLink.astro';
`Translations` is the shape for locale packs passed to <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, the React `translations` prop on <DocsLink slug="reference/i18n-provider">`I18nProvider`</DocsLink>, and <DocsLink slug="reference/create-translator">`createTranslator`</DocsLink>. Every entry is optional — missing keys fall back through the BCP 47 chain to English.
## Import
```ts
import type { Translations } from '@videojs/core/i18n';
```
## Definition
```ts
type Translations = {
[K in keyof TranslationParams]?: TranslationParams[K] extends never
? string
: /* parametric keys must include required {placeholder} tokens */
string;
};
```
Parametric values must include the same `{placeholder}` substrings as English (`{seconds}`, `{duration}`, …). TypeScript enforces this when you use `satisfies Partial<Translations>`.
## Examples
```ts
import type { Translations } from '@videojs/core/i18n';
const es = {
play: 'Reproducir',
pause: 'Pausa',
seekForward: 'Adelantar {seconds} segundos',
} satisfies Partial<Translations>;
registerI18n('es', es);
```
```tsx
<I18nProvider locale="de" translations={{ play: 'Abspielen' }} />
```
Only supplied keys override lower layers. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for merge priority.
## Related
- <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>
- <DocsLink slug="reference/get-i18n-translations">`getI18nTranslations`</DocsLink>
- <DocsLink slug="how-to/i18n-override-translations">Override translation keys</DocsLink>
@@ -0,0 +1,56 @@
---
title: Translator
description: Typed function that resolves opaque translation keys to localized strings
---
import DocsLink from '@/components/docs/DocsLink.astro';
`Translator` is the callable returned by <DocsLink slug="reference/create-translator">`createTranslator`</DocsLink> and <DocsLink slug="reference/use-translator">`useTranslator`</DocsLink>. It turns opaque keys from core controls into localized copy and interpolates `{placeholder}` tokens when params are required.
## Import
```ts
import type { Translator } from '@videojs/core/i18n';
```
## Definition
```ts
type Translator = <K extends keyof TranslationParams>(
key: K,
...args: TranslationParams[K] extends never ? [] : [params: TranslationParams[K]]
) => string;
```
- Plain keys: `t('play')`
- Parametric keys: `t('seekForward', { seconds: 10 })`
Missing keys in the active map resolve to the key string (`'play'`) so partial locale packs degrade visibly during development.
## Create manually
```ts
import { createTranslator, getI18nTranslations } from '@videojs/core/i18n';
const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR');
t('pause'); // localized or key fallback
```
## React hook
```tsx
import { useTranslator } from '@videojs/react/i18n';
function Label() {
const t = useTranslator();
return <span>{t('play')}</span>;
}
```
Control components resolve keys from core `getLabel()` through `resolveControlLabel` / `resolveControlAttrs` — you rarely call `t()` directly unless building custom UI.
## Related
- <DocsLink slug="reference/create-translator">`createTranslator`</DocsLink>
- <DocsLink slug="reference/use-translator">`useTranslator`</DocsLink>
- <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>
+12
View File
@@ -165,6 +165,18 @@ export const sidebar: Sidebar = [
{ slug: 'reference/get-i18n-translations', sidebarLabel: 'getI18nTranslations' },
{ slug: 'reference/has-registered-i18n', sidebarLabel: 'hasRegisteredI18n' },
{ slug: 'reference/on-i18n-registry-change', sidebarLabel: 'onI18nRegistryChange' },
{ slug: 'reference/create-translator', sidebarLabel: 'createTranslator' },
{
sidebarLabel: 'Types',
defaultOpen: false,
contents: [
{ slug: 'reference/built-in-locale', sidebarLabel: 'BuiltInLocale' },
{ slug: 'reference/locale', sidebarLabel: 'Locale' },
{ slug: 'reference/translation-params', sidebarLabel: 'TranslationParams' },
{ slug: 'reference/translations', sidebarLabel: 'Translations' },
{ slug: 'reference/translator', sidebarLabel: 'Translator' },
],
},
{ slug: 'reference/feature-buffer' },
{ slug: 'reference/feature-controls' },
{ slug: 'reference/feature-error' },