diff --git a/packages/core/src/core/i18n/README.md b/packages/core/src/core/i18n/README.md new file mode 100644 index 00000000..5735423b --- /dev/null +++ b/packages/core/src/core/i18n/README.md @@ -0,0 +1,26 @@ +# Adding a built-in locale + +Built-in locales live in `locales/`. The locale build generates lazy loaders, CDN chunks, and HTML/React re-exports from that directory. + +1. Add `locales/.ts` using a BCP 47 filename such as `pt-BR.ts` or `zh-CN.ts`. + + ```ts + import type { Translations } from '../types'; + + export default { + Play: '...', + Pause: '...', + } satisfies Partial; + ``` + + Use `locales/en.ts` as the source of phrases. Parametric strings must keep their placeholders. + +2. Add the tag to `LOCALES` in `locales.ts`. + +3. Run `pnpm -F @videojs/core build` to regenerate locale loaders and HTML/React re-exports. Do not edit generated files. + +4. Run `pnpm -F @videojs/core test src/core/i18n` and add coverage for locale aliases or loader behavior when needed. + +5. Run `pnpm -F @videojs/html build:cdn` to verify the generated CDN locale chunk. + +Do not copy Video.js v8 locale JSON blindly. V10 uses different phrase keys and ARIA-label semantics. diff --git a/site/src/content/docs/concepts/i18n.mdx b/site/src/content/docs/concepts/i18n.mdx index 46f1a21b..3e0f7ab9 100644 --- a/site/src/content/docs/concepts/i18n.mdx +++ b/site/src/content/docs/concepts/i18n.mdx @@ -7,17 +7,23 @@ 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 current English phrase (`Play`, `Pause`, `Seek forward {seconds} seconds`), so missing translations stay readable. +Video.js ships in English by default. Non-English UI is opt-in: mount an i18n provider, then configure its active language. Video.js resolves the matching strings for control labels, ARIA text, tooltips, and error copy. + +The default i18n path is automatic after you opt in. Built-in locale packs lazy-load on demand, and Chrome can fill missing languages or missing keys through the [Browser Translation API](https://developer.mozilla.org/en-US/docs/Web/API/Translator) when an on-device translation model is already available. + +Components ask for strings by current English phrase (`Play`, `Pause`, `Seek forward {seconds} seconds`), so missing translations stay readable. ```html - - - - - + + + + + + + ``` @@ -26,15 +32,18 @@ Video.js translates control labels, ARIA text, tooltips, and error copy through ```tsx +import { I18nProvider } from '@videojs/react/i18n'; import { Provider, VideoSkin, Video } from '@videojs/react/video'; // Set `` on the document (layout, _document, or index.html) export function App() { return ( - - + + + + ); } @@ -42,7 +51,19 @@ export function App() { -Register a locale once (or rely on lazy-loaded built-in packs), set `lang`, and skins pick up translated strings automatically. + + +Mount `` and set its `lang` attribute, or let it inherit the nearest ancestor `lang`. Built-in packs then load automatically. + + + + + +Mount `I18nProvider` and pass its `locale` prop to force a language, or omit it to inherit `lang`. Built-in packs then load automatically. + + + +Registering packs yourself is only needed when you want custom copy, synchronous first paint, SSR, or CDN loading. ## Phrase keys @@ -52,9 +73,11 @@ Phrase params are typed in `Transl Parametric strings use `{placeholder}` tokens, for example `'Seek forward {seconds} seconds'` and `'{duration} remaining'`. +See Translation phrases for every current key and the player UI that uses it. + ## Global registry -`registerI18n(locale, translations)` merges strings into a process-wide map. English (`en`) is pre-registered when the i18n bundle loads. +`registerI18n(locale, translations)` merges strings into a process-wide map. English (`en`) is pre-registered when the i18n bundle loads, which is why the default player works without setup. | API | Purpose | | --- | --- | @@ -63,41 +86,69 @@ Parametric strings use `{placeholder}` tokens, for example `'Seek forward {secon | `hasRegisteredLocale` | Check whether a tag is in the registry | | `onI18nRegistryChange` | Subscribe to registry updates | -Import from `@videojs/html/i18n` or `@videojs/react/i18n` depending on your framework. - -## 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. +Import from `@videojs/html/i18n`. -- 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`. +Import from `@videojs/react/i18n`. -**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. +## Opt into a language + +Leave your app in English by default. To opt into another language, mount the framework provider around the translated player subtree: + + + +- Wrap the player in ``. +- Set `` or `lang="es"` on `` to opt into Spanish. +- Video.js lazy-loads the shipped Spanish pack when it is not already registered. + +Use a separate `` for standalone controls or players that need different languages on the same page. + + + + + +- Mount `I18nProvider` inside `Provider`, around the preset skin. +- Omit the `locale` prop to inherit ``, or pass `locale="es"` directly. +- Video.js lazy-loads the shipped Spanish pack when it is not already registered. + +Pass the `locale` prop to force a language or `translations` for per-render overrides. + + + +If no pack exists, the provider keeps English until a registered pack, lazy-loaded pack, provider override, or supported browser translation layer supplies strings. ## 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) +`` resolves its active language in order: + +1. **Explicit**: its `lang` attribute +2. **Ambient**: nearest ancestor `[lang]` 3. **Fallback**: English defaults -Changing `` re-renders wired controls without remounting the player. An explicit `locale` or `lang` on a provider overrides ambient `` until you remove or update that override. +Changing `` re-renders wired controls without remounting the player. An explicit provider `lang` overrides ambient language until you remove or update it. + + + + + +`I18nProvider` resolves its active language in order: + +1. **Explicit**: its `locale` prop +2. **Ambient**: `langRootRef` or `` +3. **Fallback**: English defaults + +Changing `` re-renders wired controls without remounting the player. An explicit `locale` prop overrides ambient language until you remove or update it. + + ## BCP 47 fallback @@ -114,17 +165,66 @@ zh-Hant-HK → zh-hant → zh → en 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` | + + + + + +| 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) | `translations` prop on `I18nProvider` | + + ## Built-in locale packs -Video.js ships locale files under `@videojs/html/i18n/locales/*` and `@videojs/react/i18n/locales/*`. Providers call `loadLocale` automatically when a pack is not already registered. +Providers call `loadLocale` automatically for the active locale chain when a pack is not already registered, so app bundles can split locale packs into async chunks. + + + +HTML locale files live under `@videojs/html/i18n/locales/*`. + + + + + +React locale files live under `@videojs/react/i18n/locales/*`. + + + +To preload a shipped language without writing a `registerI18n` call, import its side-effect module: + + + +```ts +import '@videojs/html/i18n/locales/es/register'; +``` + + + + + +```ts +import '@videojs/react/i18n/locales/es/register'; +``` + + + +Import and register a pack manually when you need custom copy or want to preload a fixed language picker. See Register a locale. + + CDN consumers load self-registering modules: @@ -133,9 +233,19 @@ CDN consumers load self-registering modules: ``` + + +## Browser translation + +After lazy loading runs, Video.js can ask the browser to translate the English registry through the [Browser Translation API](https://developer.mozilla.org/en-US/docs/Web/API/Translator). This currently works in Chrome when `globalThis.Translator` is available and the matching on-device model is already installed. + +Video.js does not download translation models during normal provider resolution. If Chrome reports the model as unavailable, downloadable, or still downloading, controls keep using the registered, lazy-loaded, or English fallback strings. + +Browser translation is a fallback, not a replacement for reviewed locale packs. Use shipped packs or your own registered strings for production-critical copy and SSR. + ## Common pitfalls -```tsx +```ts // ❌ Don't: old camelCase keys are ignored registerI18n('es', { play: 'Reproducir' }); @@ -143,7 +253,7 @@ registerI18n('es', { play: 'Reproducir' }); registerI18n('es', { Play: 'Reproducir' }); ``` -```tsx +```ts // ❌ Don't: parametric key without the placeholder registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelante 10 segundos' }); // TS error: missing {seconds} @@ -151,15 +261,42 @@ registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelante 10 segundos' }) registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelantar {seconds} segundos' }); ``` + + + + + + + + + + ## See also -- `Locale`, `Translations`, `Translator`: I18n types + + +- ``, `Locale`, `Translations`, `Translator`: I18n APIs - Register a custom locale - Override individual keys - Switch locale dynamically - SSR and hydration - Accessibility: Translated ARIA labels + + + + + +- `I18nProvider`, `Locale`, `Translations`, `Translator`: I18n APIs +- Register a custom locale +- Override individual keys +- Switch locale dynamically +- SSR and hydration +- 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 deleted file mode 100644 index a75d394c..00000000 --- a/site/src/content/docs/how-to/i18n-add-built-in-locale.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -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 phrases - -## 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 phrases from en.ts; completeness is preferred -} satisfies Partial; -``` - -Use the current English phrases from `en.ts`. Parametric strings must include the same `{placeholder}` tokens (TypeScript enforces this via `satisfies Partial`). - -Copy guidelines: - -- `Live`: sentence case; skins uppercase via CSS where needed -- `Seek`: slider aria label, not legacy "Progress" wording -- `'{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 `LOCALES` in `packages/core/src/core/i18n/locales.ts`. - -## 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 index ce3b4bbd..56cbce74 100644 --- a/site/src/content/docs/how-to/i18n-override-translations.mdx +++ b/site/src/content/docs/how-to/i18n-override-translations.mdx @@ -8,26 +8,40 @@ import DocsLink from '@/components/docs/DocsLink.astro'; `registerI18n` **merges**. Each call adds or replaces phrases 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 phrase naming. +Read Internationalization for merge order and Translation phrases for the supported keys. ## Override after a built-in pack + + ```ts import { registerI18n } from '@videojs/html/i18n'; -import es from '@videojs/html/i18n/locales/es'; +import '@videojs/html/i18n/locales/es/register'; -registerI18n('es', es); registerI18n('es', { Play: 'Comenzar' }); // only `Play` changes ``` + + + + +```ts +import { registerI18n } from '@videojs/react/i18n'; +import '@videojs/react/i18n/locales/es/register'; + +registerI18n('es', { Play: 'Comenzar' }); // only `Play` changes +``` + + + Later registrations win for the same phrase. 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'; @@ -53,6 +67,8 @@ Nested providers inherit the parent locale when you only pass `translations`: + + ## HTML reflected text Use `` for static copy outside buttons: @@ -65,6 +81,8 @@ Use `` for static c Override the phrase in the registry or set `lang` on the provider to a locale where you patched `Play`. + + ## Parametric phrases Keep required placeholders when overriding: diff --git a/site/src/content/docs/how-to/i18n-register-locale.mdx b/site/src/content/docs/how-to/i18n-register-locale.mdx index 386274af..1a80d06a 100644 --- a/site/src/content/docs/how-to/i18n-register-locale.mdx +++ b/site/src/content/docs/how-to/i18n-register-locale.mdx @@ -7,62 +7,75 @@ 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 phrase keys and the registry. +Video.js is English by default. Built-in packs lazy-load automatically for app bundles; use `registerI18n` when you have custom strings, need synchronous first paint, or load locales from the CDN. + + + +To opt into another language, mount `` and set its `lang` attribute or an ancestor `lang` attribute. + + + + + +To opt into another language, mount `I18nProvider`. Pass its `locale` prop to force a tag, or omit it to inherit `lang`. + + + +Read Internationalization first if you are new to phrase 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 current English phrases (`Play`, `Pause`, …) -## Use a shipped pack +## Use a shipped pack synchronously -Built-in packs live in `@videojs/*/i18n/locales/*`. Import and register before the player renders: +Built-in packs include side-effect modules that register a language 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); +import '@videojs/html/i18n/locales/es/register'; ``` ```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. +Without the side-effect import, `` lazy-loads `es` when it resolves `lang="es"`. The import makes the first paint synchronous. ```tsx title="app.tsx" -import { registerI18n } from '@videojs/react/i18n'; -import es from '@videojs/react/i18n/locales/es'; +import { I18nProvider } from '@videojs/react/i18n'; +import '@videojs/react/i18n/locales/es/register'; 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 ``. +Wrap the player subtree with `I18nProvider`. Omit `locale` to inherit ``. @@ -102,29 +115,48 @@ export default es; + + ```ts -import { registerI18n } from '@videojs/html/i18n'; // or @videojs/react/i18n +import { registerI18n } from '@videojs/html/i18n'; import es from './my-es'; registerI18n('es', es); ``` -All keys are optional. Missing keys fall back through the BCP 47 chain to English. + + + + +```ts +import { registerI18n } from '@videojs/react/i18n'; +import es from './my-es'; + +registerI18n('es', es); +``` + + + +All keys are optional. Missing keys follow the locale fallback chain through a built-in pack, browser-translated copy in supported Chrome builds, or English. + + ## CDN -Load the player and a locale chunk. Locale modules call `registerI18n` on import: +Load the player and a locale chunk. CDN locale modules call `registerI18n` on import: ```html - - - - - + + + + + + + ``` @@ -143,7 +175,9 @@ registerI18n('es', { }); ``` -Load your module after the player script, before playback starts. +Load your module after the player script and before playback starts. + + ## What's next? diff --git a/site/src/content/docs/how-to/i18n-ssr.mdx b/site/src/content/docs/how-to/i18n-ssr.mdx index 0ac518a7..5ec13466 100644 --- a/site/src/content/docs/how-to/i18n-ssr.mdx +++ b/site/src/content/docs/how-to/i18n-ssr.mdx @@ -7,7 +7,7 @@ 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. +Video.js is English by default. Server-rendered pages that opt into another language should mount the relevant provider, output the correct `lang`, and supply translations on the first client render. Lazy `loadLocale` and browser translation fallback run after hydration, so controls briefly show English without preloaded copy. Read Internationalization for provider resolution and merge order. @@ -19,14 +19,18 @@ Render the document language on the server: ``` -React providers use a server snapshot of `undefined` for ambient lang, then read `` on hydration. Passing `locale` explicitly avoids any mismatch. + -## Preload translations (React) +React providers use a server snapshot of `undefined` for ambient lang, then read `` on hydration. Passing the `locale` prop explicitly avoids any mismatch. -Import the locale pack on the server (or in a server component) and pass it to `I18nProvider`: + +## Preload translations + +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'; @@ -46,13 +50,44 @@ export async function Player({ locale }: { locale: string }) { } ``` -`translations` on the first render skips the async lazy layer. Labels match on server and client. +`translations` on the first render skips the async lazy layer and Chrome-only browser translation fallback. Labels match on server and client. ## Register on the server -For custom packs, call `registerI18n` in server bootstrap code before rendering players: +For a shipped pack, import its side-effect module in server bootstrap code before rendering players: + + + +```ts +import '@videojs/html/i18n/locales/es/register'; +``` + + + + + +```ts +import '@videojs/react/i18n/locales/es/register'; +``` + + + +For a custom pack, call `registerI18n` instead: + + + +```ts +import { registerI18n } from '@videojs/html/i18n'; +import es from './locales/es'; + +registerI18n('es', es); +``` + + + + ```ts import { registerI18n } from '@videojs/react/i18n'; @@ -61,8 +96,12 @@ 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 @@ -78,22 +117,25 @@ const { default: translations } = await import(`@videojs/react/i18n/locales/${lo 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: +SSR HTML players should emit `lang` and wrap the player with ``. 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); +import '@videojs/html/i18n/locales/es/register'; ``` + + ## What's next? - Switch locale at runtime diff --git a/site/src/content/docs/how-to/i18n-switch-locale.mdx b/site/src/content/docs/how-to/i18n-switch-locale.mdx index 1e88cd92..879a35f1 100644 --- a/site/src/content/docs/how-to/i18n-switch-locale.mdx +++ b/site/src/content/docs/how-to/i18n-switch-locale.mdx @@ -7,11 +7,11 @@ 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. +Video.js starts in English and re-resolves translations when you opt into another active locale. How you trigger that depends on the platform. ### Prerequisites -- Locale packs registered or lazy-loadable +- A target locale from a shipped lazy-loaded pack, a registered custom pack, or a Chrome browser translation fallback ## Ambient `` @@ -22,22 +22,32 @@ document.documentElement.lang = 'fr'; ``` - -Player elements and skins observe `lang` on `` and ancestors. No remount required. +Ambient switching only applies when `` has no explicit `lang` attribute. -`Container`'s `I18nProvider` reads `langRootRef` from the player shell. Updating `` is enough when you are not forcing `locale` on an outer provider. +Ambient switching only applies when `I18nProvider` has no explicit `locale` prop. + + + + + + +Mounted `` providers observe `lang` on `` and ancestors. No remount required. -## Explicit provider locale + + +An `I18nProvider` without `locale` reads ``. Updating it is enough when you are not forcing an explicit locale. + + + +## Explicit provider language @@ -84,27 +94,23 @@ Changing `locale` triggers lazy `loadLocale` for built-in packs. ``` -Or set `document.documentElement.lang` if the player inherits ambient language. +Or set `document.documentElement.lang` when the mounted `` inherits ambient language. ## Avoid flash while switching -Async lazy loads can briefly show English. Preload copy before the active locale changes with one of three common patterns: +Async lazy loads and browser translation fallback can briefly show English. Preload copy before the active locale changes with one of three common patterns: -### Pre-register at bootstrap (React and HTML) +### Preload at bootstrap -`registerI18n` writes to a global registry that providers read synchronously. Register every locale you plan to switch between once at startup; `loadLocale` skips tags already in the registry. +Side-effect locale modules register shipped packs before the provider renders. Preload every language in a fixed picker to avoid the async gap. ```tsx -import { registerI18n } from '@videojs/react/i18n'; -import es from '@videojs/react/i18n/locales/es'; -import fr from '@videojs/react/i18n/locales/fr'; - -registerI18n('es', es); -registerI18n('fr', fr); +import '@videojs/react/i18n/locales/es/register'; +import '@videojs/react/i18n/locales/fr/register'; // Switching locale is instant: no remount, no lazy-load gap @@ -117,12 +123,8 @@ registerI18n('fr', fr); ```ts -import { registerI18n } from '@videojs/html/i18n'; -import es from '@videojs/html/i18n/locales/es'; -import fr from '@videojs/html/i18n/locales/fr'; - -registerI18n('es', es); -registerI18n('fr', fr); +import '@videojs/html/i18n/locales/es/register'; +import '@videojs/html/i18n/locales/fr/register'; ``` ```html @@ -137,7 +139,9 @@ registerI18n('fr', fr); -### Prefetch before switching (React) + + +### Prefetch before switching When you cannot register every locale up front, import and register immediately before updating `locale`: @@ -174,7 +178,7 @@ async function switchTo(next: string) { | Approach | Scope | Best for | | --- | --- | --- | -| `registerI18n` at bootstrap | Global, all providers | Language picker with a fixed set of locales | +| Side-effect locale imports | Global, all providers | Language picker with a fixed set of shipped locales | | `registerI18n` before switch | Global, all providers | On-demand prefetch before changing `locale` | | `I18nProvider translations` | Single provider subtree | Scoped overrides, SSR first paint | @@ -186,9 +190,13 @@ async function switchTo(next: string) { For server-rendered first paint, see SSR and hydration. -## 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. +## Browser translation fallback + +If a switched locale has no registered or shipped pack, Video.js can use Chrome's [Browser Translation API](https://developer.mozilla.org/en-US/docs/Web/API/Translator) when the browser exposes `globalThis.Translator` and already has the target model installed. This is automatic after lazy loading is attempted. + +Do not rely on it for instant switching. Preload or register reviewed packs when the language picker should update without a flash of English. ## What's next? diff --git a/site/src/content/docs/reference/built-in-locale.mdx b/site/src/content/docs/reference/built-in-locale.mdx deleted file mode 100644 index 428e5aa7..00000000 --- a/site/src/content/docs/reference/built-in-locale.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: BuiltInLocale -description: Union of shipped BCP 47 locale tags with autocomplete in TypeScript ---- - -import DocsLink from '@/components/docs/DocsLink.astro'; - -`BuiltInLocale` narrows `Locale` to tags with shipped translation packs. TypeScript suggests these tags when you call `registerI18n`, `loadLocale`, or import locale modules. - -## App imports - -```ts -import es from '@videojs/html/i18n/locales/es'; -// or @videojs/react/i18n/locales/es -``` - -## Definition - -```ts -type BuiltInLocale = (typeof LOCALES)[number]; -``` - -`LOCALES` lists shipped packs (`es`, `pt-BR`, `zh-CN`, …). Bare language tags such as `pt` and `zh` are handled by the normal locale lookup chain when there is a matching registered or loaded layer. - -## Lazy loading - -Providers call `loadLocale(tag)` for tags in the resolved locale chain when a pack is not already registered. Explicit `registerI18n` or CDN locale modules skip the async gap on first paint. - -## Examples - -```ts -import { registerI18n } from '@videojs/react/i18n'; -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 - -- `Locale` -- Add a built-in locale (contributors) -- Register a custom locale diff --git a/site/src/content/docs/reference/create-i18n.mdx b/site/src/content/docs/reference/create-i18n.mdx index 76e5cfdf..ab088a57 100644 --- a/site/src/content/docs/reference/create-i18n.mdx +++ b/site/src/content/docs/reference/create-i18n.mdx @@ -1,21 +1,38 @@ --- title: createI18n -description: Factory that creates a React i18n provider with custom loading options +description: Factory that creates framework i18n helpers with custom loading options --- import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; -import DocsLink from "@/components/docs/DocsLink.astro"; import FrameworkCase from "@/components/docs/FrameworkCase.astro"; + + +`createI18n` returns a context-bound `ProviderMixin`, `TextMixin`, and `I18nController` for custom elements. Use it when you need a custom locale loader or custom i18n elements. Most apps use `` and `` from `@videojs/html/i18n`. + +```ts +import { ReactiveElement } from '@videojs/element'; +import { createI18n } from '@videojs/html/i18n'; + +const { ProviderMixin, TextMixin } = createI18n(); + +class LocalizedRoot extends ProviderMixin(ReactiveElement) {} +class LocalizedText extends TextMixin(ReactiveElement) {} +``` + + + + + -`createI18n` returns an `I18nProvider`, `useTranslator`, and `useLocale` wired to the shared React i18n context used by the stock skins and controls. Use it when you need options such as a custom locale loader. Most apps import the default exports from `@videojs/react/i18n` instead. +`createI18n` returns an `I18nProvider`, `useTranslator`, and `useLocale` wired to the shared React i18n context used by the stock skins and controls. Use it when you need options such as a custom locale loader. Most apps use the default exports from `@videojs/react/i18n`. ```tsx import { createI18n } from '@videojs/react/i18n'; const { I18nProvider, useTranslator } = createI18n({ - loadLocale: async (tag) => { + loader: async (tag) => { const mod = await import(`@videojs/react/i18n/locales/${tag}`); return mod.default; }, diff --git a/site/src/content/docs/reference/create-translator.mdx b/site/src/content/docs/reference/create-translator.mdx index f073a95d..80ba42c5 100644 --- a/site/src/content/docs/reference/create-translator.mdx +++ b/site/src/content/docs/reference/create-translator.mdx @@ -5,16 +5,32 @@ 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"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; -`createTranslator` wraps a `Translations` map and returns a `Translator`. Providers call this internally after merging registry, lazy, and prop layers. Use it directly for custom UI outside built-in mixins. +`createTranslator` wraps a `Translations` map and returns a `Translator`. Providers call this internally after merging registry, lazy-loaded built-in packs, browser-translated fallback copy, and provider layers. Use it directly for custom UI outside built-in controls. + + ```ts import { createTranslator, getI18nTranslations } from '@videojs/html/i18n'; -// or @videojs/react/i18n const t = createTranslator(getI18nTranslations('fr'), 'fr'); t('Play'); t('Seek forward {seconds} seconds', { seconds: 5 }); ``` + + + + +```ts +import { createTranslator, getI18nTranslations } from '@videojs/react/i18n'; + +const t = createTranslator(getI18nTranslations('fr'), 'fr'); +t('Play'); +t('Seek forward {seconds} seconds', { seconds: 5 }); +``` + + + diff --git a/site/src/content/docs/reference/get-i18n-translations.mdx b/site/src/content/docs/reference/get-i18n-translations.mdx index 28e3f553..a0d20619 100644 --- a/site/src/content/docs/reference/get-i18n-translations.mdx +++ b/site/src/content/docs/reference/get-i18n-translations.mdx @@ -5,17 +5,32 @@ description: Read the merged translation map for a locale using BCP 47 parent-ch import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.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. +`getI18nTranslations` walks the BCP 47 lookup chain (`es-MX` → `es` → `en`) and merges registry layers into one map. Providers combine this map with lazy-loaded built-in packs and provider overrides. Call it directly when building custom UI outside built-in controls. See Internationalization for merge order and fallback rules. + + ```ts import { createTranslator, getI18nTranslations } from '@videojs/html/i18n'; -// or @videojs/react/i18n const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR'); -t('Play'); // merged Portuguese string +t('Play'); ``` + + + + +```ts +import { createTranslator, getI18nTranslations } from '@videojs/react/i18n'; + +const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR'); +t('Play'); +``` + + + diff --git a/site/src/content/docs/reference/has-registered-locale.mdx b/site/src/content/docs/reference/has-registered-locale.mdx index ee18fe9d..e212f979 100644 --- a/site/src/content/docs/reference/has-registered-locale.mdx +++ b/site/src/content/docs/reference/has-registered-locale.mdx @@ -5,8 +5,11 @@ description: Check whether an exact locale tag exists in the global i18n registr import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; -`hasRegisteredLocale` 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. +`hasRegisteredLocale` 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 { hasRegisteredLocale, registerI18n } from '@videojs/html/i18n'; @@ -16,4 +19,18 @@ registerI18n('fr', { Play: 'Lecture' }); hasRegisteredLocale('fr'); // true ``` + + + + +```ts +import { hasRegisteredLocale, registerI18n } from '@videojs/react/i18n'; + +hasRegisteredLocale('fr'); // false until registered +registerI18n('fr', { Play: 'Lecture' }); +hasRegisteredLocale('fr'); // true +``` + + + diff --git a/site/src/content/docs/reference/i18n-provider.mdx b/site/src/content/docs/reference/i18n-provider.mdx index 566e749e..d54a87c9 100644 --- a/site/src/content/docs/reference/i18n-provider.mdx +++ b/site/src/content/docs/reference/i18n-provider.mdx @@ -9,7 +9,7 @@ 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. +`I18nProvider` resolves the active locale, lazy-loads built-in packs, merges registry and provider layers, and exposes a translator through context. Wrap it around a player or custom controls that should translate. Wrap custom controls or force a locale explicitly: @@ -21,7 +21,7 @@ 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. +Omit `locale` to inherit the nearest `lang` attribute (via `langRootRef` or ``). English is the fallback when no non-English locale is active or no translation layer supplies a string. See Internationalization for merge priority, browser translation fallback, and SSR guidance. diff --git a/site/src/content/docs/reference/locale.mdx b/site/src/content/docs/reference/locale.mdx index 96351b86..03abf316 100644 --- a/site/src/content/docs/reference/locale.mdx +++ b/site/src/content/docs/reference/locale.mdx @@ -4,38 +4,55 @@ description: BCP 47 language tag type for i18n registry and provider APIs --- import DocsLink from '@/components/docs/DocsLink.astro'; +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; -`Locale` is the BCP 47 tag type used by `registerI18n`, providers, and `Translator`. Shipped packs autocomplete as `BuiltInLocale`; any other tag remains valid at runtime. +`Locale` is the BCP 47 tag type used by `registerI18n` and provider APIs. Shipped tags autocomplete, and any other BCP 47 tag remains valid at runtime. ## Import + + ```ts import type { Locale } from '@videojs/html/i18n'; -// or @videojs/react/i18n ``` + + + + +```ts +import type { Locale } from '@videojs/react/i18n'; +``` + + + ## Definition ```ts -type Locale = BuiltInLocale | (string & {}); +type Locale = (typeof LOCALES)[number] | (string & {}); ``` -The `(string & {})` pattern keeps custom tags (`'xx'`, `'en-US'`) type-safe without losing autocomplete for built-ins. +The `(string & {})` pattern keeps custom tags (`'xx'`, `'en-US'`) type-safe without losing autocomplete for shipped tags. + +## Provider use + + + +Set a `lang` attribute on ``; HTML providers do not have a `locale` attribute. + + + + + +Pass a `Locale` to the `locale` prop on `I18nProvider`, or omit the prop to inherit `lang`. + + ## Resolution -Providers and `getI18nTranslations` normalize tags and walk the parent chain (`es-MX` → `es` → `en`) via `findLocaleKeys`. See Internationalization for explicit vs ambient resolution. - -## Examples - -```ts -const es: Locale = 'es'; -const custom: Locale = 'en-US'; -const regional: Locale = 'pt-BR'; -``` +Providers and `getI18nTranslations` normalize tags and walk the parent chain (`es-MX` → `es` → `en`) via `findLocaleKeys`. See Internationalization for explicit and ambient resolution. ## Related -- `BuiltInLocale` - `registerI18n` - Switch locale dynamically diff --git a/site/src/content/docs/reference/media-i18n.mdx b/site/src/content/docs/reference/media-i18n.mdx index 9a25caaa..f538ad97 100644 --- a/site/src/content/docs/reference/media-i18n.mdx +++ b/site/src/content/docs/reference/media-i18n.mdx @@ -8,17 +8,14 @@ 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. +`` applies the i18n provider mixin to a subtree. Wrap a player, standalone controls, or one of several players that need different locales with this element. Without it, HTML controls use English defaults. -Register locale strings with `registerI18n`, then set `lang` on the provider or an ancestor. See Internationalization for phrase keys and fallback behavior. +Set `lang` on the provider or an ancestor. Built-in packs lazy-load automatically; register locale strings with `registerI18n` when you need custom copy or synchronous first paint. See Translation phrases for supported keys. ```html diff --git a/site/src/content/docs/reference/media-text.mdx b/site/src/content/docs/reference/media-text.mdx index 48c7e3de..9cc899e4 100644 --- a/site/src/content/docs/reference/media-text.mdx +++ b/site/src/content/docs/reference/media-text.mdx @@ -18,7 +18,7 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro"; ## Text content -The initial text content is the English translation phrase (`Play`, `Pause`, `{duration} remaining`, …). If the phrase is not registered for the active locale, `` keeps the original text. +The initial text content is the English translation phrase (`Play`, `Pause`, `{duration} remaining`, …). See Translation phrases for supported keys. If the phrase is not registered for the active locale, `` keeps the original text. Parametric phrases 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 index ae0fcb22..54d9c0ec 100644 --- a/site/src/content/docs/reference/on-i18n-registry-change.mdx +++ b/site/src/content/docs/reference/on-i18n-registry-change.mdx @@ -4,9 +4,14 @@ description: Subscribe to global i18n registry mutations --- import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.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 index acd0a71a..67be3fc7 100644 --- a/site/src/content/docs/reference/register-i18n.mdx +++ b/site/src/content/docs/reference/register-i18n.mdx @@ -5,10 +5,26 @@ description: Register or merge translation strings for a BCP 47 locale tag in th import UtilReference from "@/components/docs/api-reference/UtilReference.astro"; import DocsLink from "@/components/docs/DocsLink.astro"; +import FrameworkCase from "@/components/docs/FrameworkCase.astro"; -`registerI18n` merges a partial translation map into the process-wide registry for a locale tag. English defaults are registered by the i18n bundle. Call `registerI18n` for custom locales or to patch shipped packs before the player renders. +`registerI18n` merges a partial translation map into the process-wide registry for a locale tag. English defaults are registered by the i18n bundle. Built-in non-English packs lazy-load automatically; call `registerI18n` for custom locales, CDN locale modules, or patched shipped packs before the provider renders. -Import from `@videojs/html/i18n` or `@videojs/react/i18n`. Keys are the current English phrases (`Play`, `Pause`). See Internationalization and `Translations`. +Keys are the current English phrases (`Play`, `Pause`). See Translation phrases and `Translations`. + + + +```ts +import { registerI18n } from '@videojs/html/i18n'; + +registerI18n('es', { + Play: 'Reproducir', + Pause: 'Pausar', +}); +``` + + + + ```ts import { registerI18n } from '@videojs/react/i18n'; @@ -19,4 +35,6 @@ registerI18n('es', { }); ``` + + diff --git a/site/src/content/docs/reference/translation-params.mdx b/site/src/content/docs/reference/translation-params.mdx index a4847b33..e3bb8493 100644 --- a/site/src/content/docs/reference/translation-params.mdx +++ b/site/src/content/docs/reference/translation-params.mdx @@ -4,16 +4,30 @@ description: Typed contract for translation phrases and their placeholder argume --- import DocsLink from '@/components/docs/DocsLink.astro'; +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; -`TranslationParams` maps every current English translation phrase to its argument shape. Phrases with `never` accept only `t('Phrase')`. Phrases with an object accept `t('Phrase {value}', { … })` with typed placeholder names. +`TranslationParams` maps every current English translation phrase to its argument shape. Phrases with `never` accept only `t('Phrase')`. Phrases with an object accept `t('Phrase {value}', { ... })` with typed placeholder names. + +See Translation phrases for the complete key catalog and where each phrase appears. ## Import + + ```ts import type { TranslationParams } from '@videojs/html/i18n'; -// or @videojs/react/i18n ``` + + + + +```ts +import type { TranslationParams } from '@videojs/react/i18n'; +``` + + + ## Definition ```ts @@ -22,7 +36,7 @@ type TranslationParams = { Pause: never; 'Seek forward {seconds} seconds': { seconds: number | string }; '{duration} remaining': { duration: string }; - // … + // ... }; ``` @@ -39,10 +53,11 @@ English defaults and the full phrase list live in `packages/core/src/core/i18n/l | `{duration} remaining` | `{duration}` | | `{percent}, muted` | `{percent}` | | `Volume {value}` | `{value}` | +| `Auto ({label})` | `{label}` | All other phrases are plain strings with no parameters. -## Usage with Translator +## Usage ```ts const t: Translator = createTranslator(translations, 'es'); @@ -52,16 +67,10 @@ t('Seek forward {seconds} seconds', { seconds: 10 }); t('{duration} remaining', { duration: '1 minute' }); ``` -TypeScript rejects missing placeholders when defining `Translations` overlays: - -```ts -registerI18n('es', { - 'Seek forward {seconds} seconds': 'Adelantar', // error: missing {seconds} -}); -``` +TypeScript rejects missing placeholders when defining `Translations` overlays. ## Related - `Translations` - `Translator` -- Internationalization: Phrase keys overview +- Translation phrases diff --git a/site/src/content/docs/reference/translation-phrases.mdx b/site/src/content/docs/reference/translation-phrases.mdx new file mode 100644 index 00000000..4d02174a --- /dev/null +++ b/site/src/content/docs/reference/translation-phrases.mdx @@ -0,0 +1,76 @@ +--- +title: Translation phrases +description: English i18n phrase keys and the player UI that uses them +--- + +import DocsLink from '@/components/docs/DocsLink.astro'; + +Video.js uses the English phrases below as translation keys. They are defined in `packages/core/src/core/i18n/locales/en.ts` and typed by `TranslationParams`. Both HTML and React controls use the same keys. + +Use these keys with `registerI18n` or React `I18nProvider` `translations` overrides. + +## Playback controls + +| Phrase | Used by | +| --- | --- | +| `Play`, `Pause`, `Replay` | Play button and its tooltip | +| `Mute`, `Unmute` | Mute button and its tooltip | +| `Seek forward {seconds} seconds`, `Seek backward {seconds} seconds` | Seek buttons and their tooltips | +| `Enter fullscreen`, `Exit fullscreen` | Fullscreen button and input feedback | +| `Enter picture-in-picture`, `Exit picture-in-picture` | Picture-in-picture button | +| `Enable captions`, `Disable captions` | Captions button and captions selection control | +| `Playing live`, `Seek to live edge`, `Live` | Live button | +| `Start casting`, `Stop casting`, `Connecting` | Cast button; `Connecting` also appears on the AirPlay button | + +## Time and volume + +| Phrase | Used by | +| --- | --- | +| `Seek` | Time slider aria label | +| `Volume` | Volume slider aria label and input feedback | +| `Current time`, `Duration`, `Remaining` | Time display aria labels | +| `{duration} remaining` | Remaining time display | +| `{duration}. Show elapsed time.`, `{duration}. Show duration.`, `{duration}. Show remaining time.` | Toggleable time display aria labels | +| `Playback rate {rate}` | Playback-rate button and radio group | +| `{current} of {duration}` | Time slider value text | +| `{percent}, muted` | Muted volume slider value text | +| `Muted`, `Volume {value}` | Volume input feedback | + +## Playback feedback + +| Phrase | Used by | +| --- | --- | +| `Captions on`, `Captions off` | Input feedback after captions changes | +| `Paused`, `Playing` | Input feedback after playback changes | +| `Fullscreen`, `Exit fullscreen` | Input feedback after fullscreen changes | +| `Picture in picture`, `Exit picture in picture` | Input feedback after picture-in-picture changes | + +## Errors + +| Phrase | Used by | +| --- | --- | +| `You stopped media playback before it finished.` | Media error dialog for `MEDIA_ERR_ABORTED` | +| `This media could not be loaded due to a network or server issue.` | Media error dialog for `MEDIA_ERR_NETWORK` | +| `This media could not be played. It may be corrupted, or your browser may not support its format.` | Media error dialog for `MEDIA_ERR_DECODE` | +| `This media could not be loaded. It may be unavailable, or your browser may not support its format.` | Media error dialog for `MEDIA_ERR_SRC_NOT_SUPPORTED` | +| `This media could not be played because it could not be decrypted.` | Media error dialog for `MEDIA_ERR_ENCRYPTED` | +| `Something went wrong.`, `An unexpected error occurred.` | Generic media error dialog title and fallback description | +| `OK` | Error dialog confirmation button | + +## Settings and track menus + +| Phrase | Used by | +| --- | --- | +| `Settings` | Video settings menu trigger | +| `Quality`, `Auto`, `Auto ({label})` | Quality menu and automatic-quality option | +| `Audio` | Audio-track menu and fallback track label | +| `Default` | Reserved default-option label; no stock control currently emits it | +| `Speed`, `Playback rate` | Playback-rate menu and radio group | +| `Captions`, `Subtitles`, `Off` | Captions menu and track options | +| `Back` | Nested settings-menu back button | + +## Related + +- `TranslationParams` +- `Translations` +- Override translations diff --git a/site/src/content/docs/reference/translations.mdx b/site/src/content/docs/reference/translations.mdx index cb175d1b..5f5d5ea2 100644 --- a/site/src/content/docs/reference/translations.mdx +++ b/site/src/content/docs/reference/translations.mdx @@ -4,16 +4,36 @@ description: Partial map of English translation phrases to localized strings --- import DocsLink from '@/components/docs/DocsLink.astro'; +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; -`Translations` is the shape for locale packs passed to `registerI18n`, the React `translations` prop on `I18nProvider`, and `createTranslator`. Every entry is optional. Missing keys fall back through the BCP 47 chain to English. +`Translations` is the shape for locale packs passed to `registerI18n` and `createTranslator`. Every entry is optional. Missing keys fall back through the BCP 47 chain, lazy-loaded built-in packs, supported browser translation fallback, and English. + + + +`Translations` is also the type for the `translations` prop on `I18nProvider`. + + + +See Translation phrases for every supported key. ## Import + + ```ts import type { Translations } from '@videojs/html/i18n'; -// or @videojs/react/i18n ``` + + + + +```ts +import type { Translations } from '@videojs/react/i18n'; +``` + + + ## Definition ```ts @@ -25,13 +45,14 @@ type Translations = { }; ``` -Parametric values must include the same `{placeholder}` substrings as English (`{seconds}`, `{duration}`, …). TypeScript enforces this when you use `satisfies Partial`. +Parametric values must include the same `{placeholder}` substrings as English (`{seconds}`, `{duration}`, and so on). TypeScript enforces this when you use `satisfies Partial`. ## Examples + + ```ts -import type { Translations } from '@videojs/html/i18n'; -// or @videojs/react/i18n +import { registerI18n, type Translations } from '@videojs/html/i18n'; const es = { Play: 'Reproducir', @@ -42,10 +63,24 @@ const es = { registerI18n('es', es); ``` + + + + ```tsx - +import { I18nProvider, type Translations } from '@videojs/react/i18n'; + +const translations = { + Play: 'Abspielen', +} satisfies Partial; + + + +; ``` + + Only supplied keys override lower layers. See Internationalization for merge priority. ## Related diff --git a/site/src/content/docs/reference/translator.mdx b/site/src/content/docs/reference/translator.mdx index ec76dff1..3570abe8 100644 --- a/site/src/content/docs/reference/translator.mdx +++ b/site/src/content/docs/reference/translator.mdx @@ -4,16 +4,34 @@ description: Typed function that resolves English translation phrases to localiz --- import DocsLink from '@/components/docs/DocsLink.astro'; +import FrameworkCase from '@/components/docs/FrameworkCase.astro'; -`Translator` is the callable returned by `createTranslator` and `useTranslator`. It turns current English phrases from core controls into localized copy and interpolates `{placeholder}` tokens when params are required. +`Translator` is the callable returned by `createTranslator`. It turns current English phrases from core controls into localized copy and interpolates `{placeholder}` tokens when params are required. + + + +`useTranslator` also returns a `Translator` from the nearest `I18nProvider`. + + ## Import + + ```ts import type { Translator } from '@videojs/html/i18n'; -// or @videojs/react/i18n ``` + + + + +```ts +import type { Translator } from '@videojs/react/i18n'; +``` + + + ## Definition ```ts @@ -30,14 +48,30 @@ Missing phrases in the active map resolve to the source English (`'Play'`) so pa ## Create manually + + ```ts import { createTranslator, getI18nTranslations } from '@videojs/html/i18n'; -// or @videojs/react/i18n const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR'); -t('Pause'); // localized or English fallback +t('Pause'); ``` + + + + +```ts +import { createTranslator, getI18nTranslations } from '@videojs/react/i18n'; + +const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR'); +t('Pause'); +``` + + + + + ## React hook ```tsx @@ -49,6 +83,8 @@ function Label() { } ``` + + Control components resolve phrases from core `getLabel()` through their framework adapters. You rarely call `t()` directly unless building custom UI. ## Related @@ -56,3 +92,4 @@ Control components resolve phrases from core `getLabel()` through their framewor - `createTranslator` - `useTranslator` - `TranslationParams` +- Translation phrases diff --git a/site/src/content/docs/reference/use-translator.mdx b/site/src/content/docs/reference/use-translator.mdx index 793a225b..520ed5e2 100644 --- a/site/src/content/docs/reference/use-translator.mdx +++ b/site/src/content/docs/reference/use-translator.mdx @@ -18,7 +18,7 @@ import { useTranslator } from '@videojs/react/i18n'; function PlayLabel() { const t = useTranslator(); - return {t('play')}; + return {t('Play')}; } ``` diff --git a/site/src/docs.config.ts b/site/src/docs.config.ts index ae031f44..c4381694 100644 --- a/site/src/docs.config.ts +++ b/site/src/docs.config.ts @@ -17,11 +17,6 @@ export const sidebar: Sidebar = [ }, ], }, - { - sidebarLabel: 'Contributing', - devOnly: true, - contents: [{ slug: 'how-to/i18n-add-built-in-locale', sidebarLabel: 'Add a built-in locale' }], - }, { sidebarLabel: 'Getting started', // May change when we revisit this section's boundary with Concepts (#1105) @@ -198,11 +193,11 @@ export const sidebar: Sidebar = [ { slug: 'reference/has-registered-locale', sidebarLabel: 'hasRegisteredLocale' }, { slug: 'reference/on-i18n-registry-change', sidebarLabel: 'onI18nRegistryChange' }, { slug: 'reference/create-translator', sidebarLabel: 'createTranslator' }, + { slug: 'reference/translation-phrases', sidebarLabel: 'Translation phrases' }, { 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' }, diff --git a/site/src/styles/shiki-transformers.css b/site/src/styles/shiki-transformers.css index cfd392e4..99ffdef1 100644 --- a/site/src/styles/shiki-transformers.css +++ b/site/src/styles/shiki-transformers.css @@ -3,6 +3,7 @@ .astro-code { font-family: var(--font-mono), monospace; font-variant-ligatures: none; + color: var(--color-manila-light); } /* reinforcing shared.codeBlock */ diff --git a/site/src/utils/shikiStripPreStyle.ts b/site/src/utils/shikiStripPreStyle.ts index 798dcc9c..5f1a7fc2 100644 --- a/site/src/utils/shikiStripPreStyle.ts +++ b/site/src/utils/shikiStripPreStyle.ts @@ -6,7 +6,7 @@ import type { ShikiTransformer } from 'shiki'; * * Shiki should only highlight the text; the code container's background and * scrolling are owned by `CodeFrame` and the `.astro-code` rules. Token colors - * live on the inner spans, so removing the pre's style leaves them untouched. + * live on the inner spans, while `.astro-code` supplies the plaintext color. * * Astro adds its built-in `pre` transformer before user transformers, so this * one runs last and sees the fully-assembled style to remove.