From fd54f361107620cfc13fb556a61e0dfab60ea078 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Tue, 26 May 2026 10:47:10 +1000 Subject: [PATCH] docs(site): add i18n concept, guides, and API reference pages Documents opaque keys, registry APIs, React/HTML providers, and locale workflows for #1373. Extends api-docs-builder discovery for core/react i18n exports and adds matching reference MDX pages. Co-authored-by: Cursor --- site/src/content/docs/concepts/i18n.mdx | 155 ++++++++++++++++++ .../docs/how-to/i18n-add-built-in-locale.mdx | 85 ++++++++++ .../how-to/i18n-override-translations.mdx | 83 ++++++++++ .../docs/how-to/i18n-register-locale.mdx | 131 +++++++++++++++ site/src/content/docs/how-to/i18n-ssr.mdx | 100 +++++++++++ .../docs/how-to/i18n-switch-locale.mdx | 121 ++++++++++++++ .../content/docs/reference/create-i18n.mdx | 32 ++++ .../docs/reference/get-i18n-translations.mdx | 20 +++ .../docs/reference/has-registered-i18n.mdx | 19 +++ .../content/docs/reference/i18n-provider.mdx | 28 ++++ .../docs/reference/media-i18n-provider.mdx | 40 +++++ .../src/content/docs/reference/media-text.mdx | 27 +++ .../reference/on-i18n-registry-change.mdx | 12 ++ .../content/docs/reference/register-i18n.mdx | 22 +++ .../src/content/docs/reference/use-locale.mdx | 27 +++ .../content/docs/reference/use-translator.mdx | 27 +++ site/src/docs.config.ts | 16 ++ 17 files changed, 945 insertions(+) create mode 100644 site/src/content/docs/concepts/i18n.mdx create mode 100644 site/src/content/docs/how-to/i18n-add-built-in-locale.mdx create mode 100644 site/src/content/docs/how-to/i18n-override-translations.mdx create mode 100644 site/src/content/docs/how-to/i18n-register-locale.mdx create mode 100644 site/src/content/docs/how-to/i18n-ssr.mdx create mode 100644 site/src/content/docs/how-to/i18n-switch-locale.mdx create mode 100644 site/src/content/docs/reference/create-i18n.mdx create mode 100644 site/src/content/docs/reference/get-i18n-translations.mdx create mode 100644 site/src/content/docs/reference/has-registered-i18n.mdx create mode 100644 site/src/content/docs/reference/i18n-provider.mdx create mode 100644 site/src/content/docs/reference/media-i18n-provider.mdx create mode 100644 site/src/content/docs/reference/media-text.mdx create mode 100644 site/src/content/docs/reference/on-i18n-registry-change.mdx create mode 100644 site/src/content/docs/reference/register-i18n.mdx create mode 100644 site/src/content/docs/reference/use-locale.mdx create mode 100644 site/src/content/docs/reference/use-translator.mdx diff --git a/site/src/content/docs/concepts/i18n.mdx b/site/src/content/docs/concepts/i18n.mdx new file mode 100644 index 00000000..e0b5393d --- /dev/null +++ b/site/src/content/docs/concepts/i18n.mdx @@ -0,0 +1,155 @@ +--- +title: Internationalization (i18n) +description: How Video.js translates player UI copy with opaque keys, a global registry, and locale providers +frameworkTitle: + html: Internationalization in HTML + react: Internationalization in React +--- + +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; +import Aside from '@/components/Aside.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +Video.js translates control labels, ARIA text, tooltips, and error copy through a single **global registry**. Components ask for strings by **opaque key** (`play`, `pause`, `seekForward`) — not by English text — so locale files stay stable when copy changes. + +```html title="html" + + + + + + + +``` + +```tsx title="react" + + + + + + +``` + +Register a locale once (or rely on lazy-loaded built-in packs), set `lang`, and skins pick up translated strings automatically. + +## Opaque keys + +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. + +Parametric strings use `{placeholder}` tokens, for example `seekForward: 'Seek forward {seconds} seconds'` and `timeRemainingPhrase: '{duration} remaining'`. + +## Global registry + +`registerI18n(locale, translations)` merges strings into a process-wide map. English (`en`) is pre-registered when `@videojs/core/i18n` loads. + +| API | Purpose | +| --- | --- | +| `registerI18n` | Add or merge a locale layer | +| `getI18nTranslations` | Read the merged map for a locale | +| `hasRegisteredI18n` | Check whether a tag is in the registry | +| `onI18nRegistryChange` | Subscribe to registry updates | + +Import from `@videojs/html/i18n`, `@videojs/react/i18n`, or `@videojs/core/i18n` depending on your bundle. + +## When you need a provider + +**You usually don't** — if the active locale is available and packs are registered (or lazy-loaded), built-in players already wire providers: + + + +- ``, ``, and skins include an i18n provider mixin. +- Set `` (or `lang` on an ancestor) and ship/register Spanish strings. + +Use `` when you render **standalone** controls outside a player, or when one page hosts players in different languages. + + + + + +- Preset skins render inside `Container`, which mounts `I18nProvider` with `langRootRef` on the player shell. +- Set `` and register or lazy-load Spanish. + +Wrap with an explicit `I18nProvider` when you need a forced locale, per-render overrides, or custom controls outside `Container`. + + + +**You do need to register or lazy-load packs** before labels appear in the target language. `` alone does nothing if French strings were never registered and no built-in `fr` pack loads. + +## Locale resolution + +Providers resolve the active locale in order: + +1. **Explicit** — `lang` on `` or `locale` on `I18nProvider` +2. **Ambient** — nearest ancestor `[lang]` (HTML) or `langRootRef` / `` (React) +3. **Fallback** — English defaults + +Changing `` re-renders wired controls without remounting the player. An explicit `locale` / `lang` on a provider overrides ambient `` until you remove or update that override. + +## BCP 47 fallback + +Lookups walk a **parent chain**, not sibling locales. `es-MX` falls back to `es`, then `en` — not to `es-419`. + +``` +es-MX → es → en +zh-Hant-HK → zh-hant → zh → en +``` + +`getI18nTranslations`, lazy `loadLocale`, and providers all use the same chain via `localeLookupChain`. + +## Merge priority + +Later layers win over earlier ones: + +| Layer | Source | +| --- | --- | +| 1 (base) | English defaults (`en.ts`) | +| 2 | Browser Translation API (Chrome, pre-installed model only) | +| 3 | `registerI18n` / CDN locale modules | +| 4 | Lazy built-in packs (`loadLocale`) | +| 5 (top) | React `translations` prop on `I18nProvider` | + +## Built-in locale packs + +Video.js ships ~50 locale files under `@videojs/core/i18n/locales/*`, re-exported from `@videojs/html/i18n/locales/*` and `@videojs/react/i18n/locales/*`. Providers call `loadLocale` automatically when a pack is not already registered. + +CDN consumers load self-registering modules: + +```html + + +``` + +## Common pitfalls + +```tsx +// ❌ Don't — key is the English word; keys are opaque tokens +registerI18n('es', { Play: 'Reproducir' }); + +// ✅ Do +registerI18n('es', { play: 'Reproducir' }); +``` + +```tsx +// ❌ Don't — parametric key without the placeholder +registerI18n('es', { seekForward: 'Adelante 10 segundos' }); // TS error: missing {seconds} + +// ✅ Do +registerI18n('es', { seekForward: 'Adelantar {seconds} segundos' }); +``` + + + +## See also + +- Register a custom locale +- Override individual keys +- Switch locale dynamically +- SSR and hydration +- Add a built-in locale (contributors) +- Accessibility — translated ARIA labels diff --git a/site/src/content/docs/how-to/i18n-add-built-in-locale.mdx b/site/src/content/docs/how-to/i18n-add-built-in-locale.mdx new file mode 100644 index 00000000..3cb383ef --- /dev/null +++ b/site/src/content/docs/how-to/i18n-add-built-in-locale.mdx @@ -0,0 +1,85 @@ +--- +title: Add a built-in locale +description: Ship a new translation pack in @videojs/core for contributors +--- + +import Aside from '@/components/Aside.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +Built-in locales live in `packages/core/src/core/i18n/locales/`. The build generates lazy loaders, CDN chunks, and HTML/React re-exports from that directory. + +This guide is for **contributors** adding or updating shipped packs — app authors should use Register a custom locale instead. + +### Prerequisites + +- Familiarity with Internationalization and `TranslationParams` +- English defaults in `packages/core/src/core/i18n/locales/en.ts` as the source of keys + +## 1. Add the locale file + +Create `packages/core/src/core/i18n/locales/{tag}.ts` using the BCP 47 tag as the filename (`pt-BR.ts`, `zh-CN.ts`): + +```ts title="packages/core/src/core/i18n/locales/xx.ts" +import type { Translations } from '../types'; + +export default { + play: '…', + pause: '…', + // all keys from en.ts — each entry optional but completeness is preferred +} satisfies Partial; +``` + +Use **opaque keys** from `en.ts`, not English sentences as keys. Parametric strings must include the same `{placeholder}` tokens (TypeScript enforces this via `satisfies Partial`). + +Copy guidelines: + +- `liveBadge` — sentence case (`Live`); skins uppercase via CSS where needed +- `seek` — slider aria label (`Seek`), not legacy "Progress" wording +- `timeRemainingPhrase` — `'{duration} remaining'` pattern with `{duration}` placeholder +- Button and error strings — short aria-label style, aligned with V10 core label usage + +## 2. Register the tag in built-in metadata + +Add the tag to `BUILT_IN_LOCALES` in `packages/core/src/core/i18n/built-in-locales.ts`. Add alias tags (`pt`, `zh`) to `LOCALE_ALIAS_TAGS` only when intentional. + +## 3. Regenerate loaders + +From the repo root: + +```bash +pnpm -F @videojs/core build +``` + +This runs `generate:locales` and updates: + +- `packages/core/src/core/i18n/load-locale.ts` +- `packages/core/src/core/i18n/locales/all.ts` +- `packages/html/src/i18n/locales/*` and `packages/react/src/i18n/locales/*` re-exports +- CDN locale stubs via `packages/html/scripts/build-cdn-locales.ts` when building HTML CDN output + +Do not hand-edit generated files. + +## 4. Test + +```bash +pnpm -F @videojs/core test src/core/i18n +``` + +Add or extend locale tests if the tag introduces alias or loader edge cases. + +## 5. CDN locale chunk + +CDN builds emit `cdn/locales/{tag}.js` that import the pack and call `registerI18n`. Verify with: + +```bash +pnpm -F @videojs/html build:cdn +``` + + + +## What's next? + +- Open a PR with the locale file and `built-in-locales.ts` change only (generated outputs come from CI/build) +- Internationalization concept diff --git a/site/src/content/docs/how-to/i18n-override-translations.mdx b/site/src/content/docs/how-to/i18n-override-translations.mdx new file mode 100644 index 00000000..977873b9 --- /dev/null +++ b/site/src/content/docs/how-to/i18n-override-translations.mdx @@ -0,0 +1,83 @@ +--- +title: Override translation keys +description: Patch individual i18n strings without replacing an entire locale pack +--- + +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +`registerI18n` **merges** — each call adds or replaces keys for that locale tag without wiping prior registrations. Use this to tweak shipped packs or A/B test copy. + +Read Internationalization for merge order and key naming. + +## Override after a built-in pack + +```ts +import { registerI18n } from '@videojs/html/i18n'; +import es from '@videojs/html/i18n/locales/es'; + +registerI18n('es', es); +registerI18n('es', { play: 'Comenzar' }); // only `play` changes +``` + +Later registrations win for the same key. The rest of the `es` pack stays intact. + +## React provider overrides + +Pass `translations` on `I18nProvider` to scope overrides to one subtree. This layer sits **above** registry and lazy packs: + + + +```tsx +import { I18nProvider } from '@videojs/react/i18n'; +import { Provider, VideoSkin, Video } from '@videojs/react/video'; + + + + + + + +``` + +Nested providers inherit the parent locale when you only pass `translations`: + +```tsx + + + {/* locale stays `de`; only `play` differs */} + + +``` + + + +## HTML reflected text + +Use `` for static copy outside buttons: + +```html + + + +``` + +Override the key in the registry or set `lang` on the provider to a locale where you patched `play`. + +## Parametric keys + +Keep required placeholders when overriding: + +```ts +// ✅ +registerI18n('es', { seekForward: 'Adelantar {seconds} segundos' }); + +// ❌ TypeScript error — missing {seconds} +registerI18n('es', { seekForward: 'Adelantar' }); +``` + +## What's next? + +- Switch locale dynamically +- `getI18nTranslations` — inspect the merged map diff --git a/site/src/content/docs/how-to/i18n-register-locale.mdx b/site/src/content/docs/how-to/i18n-register-locale.mdx new file mode 100644 index 00000000..9e83c1d6 --- /dev/null +++ b/site/src/content/docs/how-to/i18n-register-locale.mdx @@ -0,0 +1,131 @@ +--- +title: Register a custom locale +description: Register translation packs for HTML, React, and CDN players +--- + +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; +import Aside from '@/components/Aside.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +Register strings once with `registerI18n`, then set `lang` on the page or pass `locale` to a provider. Read Internationalization first if you are new to opaque keys and the registry. + +### Prerequisites + +- A locale tag ([BCP 47](https://www.rfc-editor.org/rfc/rfc5646), e.g. `es`, `pt-BR`) +- A partial translation map keyed by camelCase tokens (`play`, `pause`, …) + +## Use a shipped pack + +Built-in packs live in `@videojs/*/i18n/locales/*`. Import and register before the player renders: + + + +```ts title="main.ts" +import '@videojs/html/video'; +import { registerI18n } from '@videojs/html/i18n'; +import es from '@videojs/html/i18n/locales/es'; + +registerI18n('es', es); +``` + +```html title="index.html" + + + + + + + +``` + +If you skip `registerI18n`, the built-in provider still **lazy-loads** `es` when `lang="es"` — explicit registration avoids the async gap on first paint. + + + + + +```tsx title="app.tsx" +import { registerI18n } from '@videojs/react/i18n'; +import es from '@videojs/react/i18n/locales/es'; +import { Provider, VideoSkin, Video } from '@videojs/react/video'; + +registerI18n('es', es); + +export function App() { + return ( + + + + + ); +} +``` + +Preset skins use `Container`, which includes `I18nProvider`. Set `` or wrap with ``. + + + +## Register your own pack + +```ts title="my-es.ts" +import type { Translations } from '@videojs/core/i18n'; + +const es: Partial = { + play: 'Reproducir', + pause: 'Pausa', + mute: 'Silenciar', + unmute: 'Activar sonido', +}; + +export default es; +``` + +```ts +import { registerI18n } from '@videojs/html/i18n'; // or @videojs/react/i18n +import es from './my-es'; + +registerI18n('es', es); +``` + +All keys are optional — missing keys fall back through the BCP 47 chain to English. + +## CDN + +Load the player and a locale chunk. Locale modules call `registerI18n` on import: + +```html + + + + + + + + + + +``` + + + +## Custom CDN locale + +```js title="my-locale.js" +import { registerI18n } from 'https://cdn.jsdelivr.net/npm/@videojs/html/cdn/i18n.js'; + +registerI18n('es', { + play: 'Reproducir', + pause: 'Pausa', +}); +``` + +Load your module after the player script, before playback starts. + +## What's next? + +- Override individual keys +- Switch locale at runtime +- `registerI18n` reference diff --git a/site/src/content/docs/how-to/i18n-ssr.mdx b/site/src/content/docs/how-to/i18n-ssr.mdx new file mode 100644 index 00000000..6e6895c6 --- /dev/null +++ b/site/src/content/docs/how-to/i18n-ssr.mdx @@ -0,0 +1,100 @@ +--- +title: SSR and locale +description: Render translated player UI on the server without a flash of English +--- + +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; +import Aside from '@/components/Aside.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +Server-rendered pages should output the correct `lang` **and** supply translations on the first client render. Lazy `loadLocale` runs after hydration — without preloaded copy, controls briefly show English. + +Read Internationalization for provider resolution and merge order. + +## Set `` + +Render the document language on the server: + +```html + +``` + +React providers use a server snapshot of `undefined` for ambient lang, then read `` on hydration. Passing `locale` explicitly avoids any mismatch. + +## Preload translations (React) + +Import the locale pack on the server (or in a server component) and pass it to `I18nProvider`: + + + +```tsx +import { I18nProvider } from '@videojs/react/i18n'; +import { Provider, VideoSkin, Video } from '@videojs/react/video'; + +export async function Player({ locale }: { locale: string }) { + const { default: translations } = await import(`@videojs/react/i18n/locales/${locale}`); + + return ( + + + + + + + ); +} +``` + +`translations` on the first render skips the async lazy layer — labels match on server and client. + + + +## Register on the server + +For custom packs, call `registerI18n` in server bootstrap code before rendering players: + +```ts +import { registerI18n } from '@videojs/react/i18n'; +import es from './locales/es'; + +registerI18n('es', es); +``` + +The registry is module singleton state — registration in server entry points carries into the client bundle when shared. + +## next-intl and app routers + +```tsx +import { getLocale } from 'next-intl/server'; + +const locale = await getLocale(); +const { default: translations } = await import(`@videojs/react/i18n/locales/${locale}`); + + + ... + +``` + +Match your app's locale negotiation — Video.js does not replace framework i18n; it consumes the tag you pass. + +## HTML custom elements + +SSR HTML players should emit `lang` on `` or on ``. Register packs in the entry module loaded before custom elements upgrade: + +```ts +import '@videojs/html/video'; +import { registerI18n } from '@videojs/html/i18n'; +import es from '@videojs/html/i18n/locales/es'; + +registerI18n('es', es); +``` + + + +## What's next? + +- Switch locale at runtime +- Register a custom locale diff --git a/site/src/content/docs/how-to/i18n-switch-locale.mdx b/site/src/content/docs/how-to/i18n-switch-locale.mdx new file mode 100644 index 00000000..0f9356b5 --- /dev/null +++ b/site/src/content/docs/how-to/i18n-switch-locale.mdx @@ -0,0 +1,121 @@ +--- +title: Switch locale dynamically +description: Change the player language at runtime in HTML and React +--- + +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; +import Aside from '@/components/Aside.astro'; +import DocsLink from '@/components/docs/DocsLink.astro'; + +Video.js re-resolves translations when the active locale changes. How you trigger that depends on the platform. + +### Prerequisites + +- Locale packs registered or lazy-loadable + +## Ambient `` + +The simplest switch — update the document language and let providers pick it up: + +```ts +document.documentElement.lang = 'fr'; +``` + + + + + +Player elements and skins observe `lang` on `` and ancestors. No remount required. + + + + + +`Container`'s `I18nProvider` reads `langRootRef` from the player shell. Updating `` is enough when you are not forcing `locale` on an outer provider. + + + +## Explicit provider locale + + + +```tsx +import { useState } from 'react'; +import { I18nProvider } from '@videojs/react/i18n'; +import { Provider, VideoSkin, Video } from '@videojs/react/video'; + +function App() { + const [locale, setLocale] = useState<'en' | 'es' | 'fr'>('en'); + + return ( + <> + + + + + + + + + ); +} +``` + +Changing `locale` triggers lazy `loadLocale` for built-in packs. + + + + + +```html + + ... + + + +``` + +Or set `document.documentElement.lang` if the player inherits ambient language. + + + +## Avoid flash while switching + +Async lazy loads can briefly show English. Pre-import the pack and pass `translations`: + + + +```tsx +async function switchTo(next: string) { + const { default: translations } = await import(`@videojs/react/i18n/locales/${next}`); + setState({ locale: next, translations }); +} + + + ... + +``` + + + + + +## CDN + +Reload or navigate with an updated `locale` query parameter, or load a different locale module before playback. CDN shells typically full-reload on locale change because locale script tags are not hot-swappable. + +## What's next? + +- SSR with locale +- `useLocale` — read the active locale in React diff --git a/site/src/content/docs/reference/create-i18n.mdx b/site/src/content/docs/reference/create-i18n.mdx new file mode 100644 index 00000000..9a18ea27 --- /dev/null +++ b/site/src/content/docs/reference/create-i18n.mdx @@ -0,0 +1,32 @@ +--- +title: createI18n +description: Factory that creates an isolated React i18n context stack with I18nProvider and hooks +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`createI18n` mirrors `createPlayer`: it returns a dedicated `I18nProvider`, `useTranslator`, and `useLocale` for apps that need an isolated i18n stack. Most apps import the default exports from `@videojs/react/i18n` instead. + +```tsx +import { createI18n } from '@videojs/react/i18n'; + +const { I18nProvider, useTranslator } = createI18n({ + loadLocale: async (tag) => import(`./locales/${tag}.json`), +}); + +function App() { + return ( + + + + ); +} +``` + + + + diff --git a/site/src/content/docs/reference/get-i18n-translations.mdx b/site/src/content/docs/reference/get-i18n-translations.mdx new file mode 100644 index 00000000..e7415086 --- /dev/null +++ b/site/src/content/docs/reference/get-i18n-translations.mdx @@ -0,0 +1,20 @@ +--- +title: getI18nTranslations +description: Read the merged translation map for a locale using BCP 47 parent-chain fallback +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; + +`getI18nTranslations` walks the BCP 47 lookup chain (`es-MX` → `es` → `en`) and merges registry layers into one map. Providers and `createTranslator` use this internally; call it directly when building custom UI outside the built-in mixins. + +See Internationalization for merge order and fallback rules. + +```ts +import { createTranslator, getI18nTranslations } from '@videojs/core/i18n'; + +const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR'); +t('play'); // merged Portuguese string +``` + + diff --git a/site/src/content/docs/reference/has-registered-i18n.mdx b/site/src/content/docs/reference/has-registered-i18n.mdx new file mode 100644 index 00000000..5bae5e58 --- /dev/null +++ b/site/src/content/docs/reference/has-registered-i18n.mdx @@ -0,0 +1,19 @@ +--- +title: hasRegisteredI18n +description: Check whether an exact locale tag exists in the global i18n registry +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; + +`hasRegisteredI18n` returns whether a normalized locale tag has an explicit registry layer from `registerI18n`. It does **not** indicate whether a lazy built-in pack exists — only registry entries count. + +```ts +import { hasRegisteredI18n, registerI18n } from '@videojs/html/i18n'; + +hasRegisteredI18n('fr'); // false until registered +registerI18n('fr', { play: 'Lecture' }); +hasRegisteredI18n('fr'); // true +``` + + diff --git a/site/src/content/docs/reference/i18n-provider.mdx b/site/src/content/docs/reference/i18n-provider.mdx new file mode 100644 index 00000000..a6b84242 --- /dev/null +++ b/site/src/content/docs/reference/i18n-provider.mdx @@ -0,0 +1,28 @@ +--- +title: I18nProvider +description: React provider that resolves locale and supplies a typed translator to descendants +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`I18nProvider` resolves the active locale, merges registry and lazy pack layers, and exposes a translator through context. Preset skins mount it inside `Container` with `langRootRef` on the player shell. + +Wrap custom controls or force a locale explicitly: + +```tsx +import { I18nProvider } from '@videojs/react/i18n'; + + + + +``` + +Omit `locale` to inherit the nearest `lang` attribute (via `langRootRef` or ``). See Internationalization for merge priority and SSR guidance. + + + + diff --git a/site/src/content/docs/reference/media-i18n-provider.mdx b/site/src/content/docs/reference/media-i18n-provider.mdx new file mode 100644 index 00000000..a8699c08 --- /dev/null +++ b/site/src/content/docs/reference/media-i18n-provider.mdx @@ -0,0 +1,40 @@ +--- +title: media-i18n-provider +description: HTML custom element that resolves locale and supplies translations to descendants +--- + +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`` applies the i18n provider mixin to a standalone subtree. Built-in `` and skins already include this mixin — use the element when rendering **controls outside a player** or when sibling players need different locales. + +Register locale strings with `registerI18n`, then set `lang` on the provider or an ancestor. See Internationalization for opaque keys and fallback behavior. + +```html + + + + + +``` + +## Attributes + +| Attribute | Description | +| --- | --- | +| `lang` | Forces the active BCP 47 locale for this subtree. Omit to inherit the nearest ancestor `[lang]`. | + +## Related + +- `` — renders a translated string by key +- Register a custom locale + + diff --git a/site/src/content/docs/reference/media-text.mdx b/site/src/content/docs/reference/media-text.mdx new file mode 100644 index 00000000..4aa357b9 --- /dev/null +++ b/site/src/content/docs/reference/media-text.mdx @@ -0,0 +1,27 @@ +--- +title: media-text +description: HTML custom element that renders a translated string by opaque key +--- + +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`` renders the translated value for a registry key inside a `` (or any ancestor with the i18n provider mixin). Use it for standalone labels, tooltips, or demos — skin controls resolve keys internally. + +```html + + + +``` + +## Attributes + +| Attribute | Description | +| --- | --- | +| `key` | Opaque translation key (`play`, `pause`, `timeRemainingPhrase`, …). | + +Parametric keys are not yet supported on `` — use `createTranslator` in script for interpolated strings. + + diff --git a/site/src/content/docs/reference/on-i18n-registry-change.mdx b/site/src/content/docs/reference/on-i18n-registry-change.mdx new file mode 100644 index 00000000..a339d7d2 --- /dev/null +++ b/site/src/content/docs/reference/on-i18n-registry-change.mdx @@ -0,0 +1,12 @@ +--- +title: onI18nRegistryChange +description: Subscribe to global i18n registry mutations +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; + +`onI18nRegistryChange` registers a callback that runs whenever any locale layer changes — for example after `registerI18n` or browser translation prefetch. Returns an unsubscribe function. + +React `I18nProvider` uses this to invalidate translators when the registry updates. + + diff --git a/site/src/content/docs/reference/register-i18n.mdx b/site/src/content/docs/reference/register-i18n.mdx new file mode 100644 index 00000000..c8edf5ca --- /dev/null +++ b/site/src/content/docs/reference/register-i18n.mdx @@ -0,0 +1,22 @@ +--- +title: registerI18n +description: Register or merge translation strings for a BCP 47 locale tag in the global i18n registry +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +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 Internationalization. + +```ts +import { registerI18n } from '@videojs/react/i18n'; + +registerI18n('es', { + play: 'Reproducir', + pause: 'Pausar', +}); +``` + + diff --git a/site/src/content/docs/reference/use-locale.mdx b/site/src/content/docs/reference/use-locale.mdx new file mode 100644 index 00000000..0ff72d05 --- /dev/null +++ b/site/src/content/docs/reference/use-locale.mdx @@ -0,0 +1,27 @@ +--- +title: useLocale +description: React hook that returns the active BCP 47 locale from the nearest I18nProvider +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`useLocale` returns the resolved BCP 47 tag from the nearest `I18nProvider`, or `'en'` when none is mounted. + +Use it when UI copy depends on the active locale outside the translator — for example formatting or caption language hooks. + +```tsx +import { useLocale } from '@videojs/react/i18n'; + +function LocaleBadge() { + const locale = useLocale(); + return {locale}; +} +``` + + + + diff --git a/site/src/content/docs/reference/use-translator.mdx b/site/src/content/docs/reference/use-translator.mdx new file mode 100644 index 00000000..6fa9e402 --- /dev/null +++ b/site/src/content/docs/reference/use-translator.mdx @@ -0,0 +1,27 @@ +--- +title: useTranslator +description: React hook that returns the typed translator for the nearest I18nProvider +--- + +import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + + +`useTranslator` returns the translator from the nearest `I18nProvider`. Control components pass opaque keys from core `getLabel()` — for example `t('play')` and `t('seekForward', { seconds: 10 })`. + +When no provider is mounted, the hook falls back to English registry strings so standalone demos do not throw. + +```tsx +import { useTranslator } from '@videojs/react/i18n'; + +function PlayLabel() { + const t = useTranslator(); + return {t('play')}; +} +``` + + + + diff --git a/site/src/docs.config.ts b/site/src/docs.config.ts index 6793fb41..ef8f4e43 100644 --- a/site/src/docs.config.ts +++ b/site/src/docs.config.ts @@ -42,6 +42,7 @@ export const sidebar: Sidebar = [ { slug: 'concepts/ui-components' }, { slug: 'concepts/accessibility' }, { slug: 'concepts/cast', sidebarLabel: 'Google Cast' }, + { slug: 'concepts/i18n', sidebarLabel: 'Internationalization' }, ], }, { @@ -52,6 +53,11 @@ export const sidebar: Sidebar = [ { slug: 'how-to/customize-skins' }, { slug: 'how-to/build-your-own-component' }, { slug: 'how-to/self-host-the-player', frameworks: ['html'] }, + { slug: 'how-to/i18n-register-locale', sidebarLabel: 'Register a locale' }, + { slug: 'how-to/i18n-override-translations', sidebarLabel: 'Override translations' }, + { slug: 'how-to/i18n-switch-locale', sidebarLabel: 'Switch locale' }, + { slug: 'how-to/i18n-ssr', sidebarLabel: 'SSR with locale' }, + { slug: 'how-to/i18n-add-built-in-locale', sidebarLabel: 'Add a built-in locale' }, ], }, { @@ -105,6 +111,10 @@ export const sidebar: Sidebar = [ frameworks: ['react'], contents: [ { slug: 'reference/create-player' }, + { slug: 'reference/create-i18n' }, + { slug: 'reference/i18n-provider' }, + { slug: 'reference/use-translator' }, + { slug: 'reference/use-locale' }, { slug: 'reference/use-player' }, { slug: 'reference/use-media' }, { slug: 'reference/use-store' }, @@ -129,6 +139,8 @@ export const sidebar: Sidebar = [ frameworks: ['html'], contents: [ { slug: 'reference/html-create-player', sidebarLabel: 'createPlayer' }, + { slug: 'reference/media-i18n-provider', sidebarLabel: 'media-i18n-provider' }, + { slug: 'reference/media-text', sidebarLabel: 'media-text' }, { slug: 'reference/player-controller' }, { sidebarLabel: 'Advanced', @@ -149,6 +161,10 @@ export const sidebar: Sidebar = [ llmsDescription: 'API reference for feature modules that provide player capabilities and state.', contents: [ { slug: 'reference/create-selector' }, + { slug: 'reference/register-i18n', sidebarLabel: 'registerI18n' }, + { slug: 'reference/get-i18n-translations', sidebarLabel: 'getI18nTranslations' }, + { slug: 'reference/has-registered-i18n', sidebarLabel: 'hasRegisteredI18n' }, + { slug: 'reference/on-i18n-registry-change', sidebarLabel: 'onI18nRegistryChange' }, { slug: 'reference/feature-buffer' }, { slug: 'reference/feature-controls' }, { slug: 'reference/feature-error' },