diff --git a/site/src/content/docs/concepts/i18n.mdx b/site/src/content/docs/concepts/i18n.mdx
index 386a9906..b36f3358 100644
--- a/site/src/content/docs/concepts/i18n.mdx
+++ b/site/src/content/docs/concepts/i18n.mdx
@@ -7,9 +7,11 @@ 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.
+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"
+
+
+```html
@@ -19,7 +21,11 @@ Video.js translates control labels, ARIA text, tooltips, and error copy through
```
-```tsx title="react"
+
+
+
+
+```tsx
import { Provider, VideoSkin, Video } from '@videojs/react/video';
// Set `` on the document (layout, _document, or index.html)
@@ -34,19 +40,21 @@ export function App() {
}
```
+
+
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.
+Keys are typed in `TranslationParams`. 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'`.
## Global registry
-`registerI18n(locale, translations)` merges strings into a process-wide map. English (`en`) is pre-registered when `@videojs/core/i18n` loads.
+`registerI18n(locale, translations)` merges strings into a process-wide map. English (`en`) is pre-registered when the i18n bundle loads.
| API | Purpose |
| --- | --- |
@@ -55,11 +63,11 @@ Parametric strings use `{placeholder}` tokens, for example `seekForward: 'Seek f
| `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.
+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:
+**You usually don't**. If the active locale is available and packs are registered (or lazy-loaded), built-in players already wire providers:
@@ -85,15 +93,15 @@ Wrap with an explicit `I18nProvider``` or `locale` on `I18nProvider`
-2. **Ambient** — nearest ancestor `[lang]` (HTML) or `langRootRef` / `` (React)
-3. **Fallback** — English defaults
+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.
+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.
## BCP 47 fallback
-Lookups walk a **parent chain**, not sibling locales. `es-MX` falls back to `es`, then `en` — not to `es-419`.
+Lookups walk a **parent chain**, not sibling locales. `es-MX` falls back to `es`, then `en`, not to `es-419`.
```
es-MX → es → en
@@ -116,7 +124,7 @@ Later layers win over earlier ones:
## 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.
+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.
CDN consumers load self-registering modules:
@@ -128,7 +136,7 @@ CDN consumers load self-registering modules:
## Common pitfalls
```tsx
-// ❌ Don't — key is the English word; keys are opaque tokens
+// ❌ Don't: key is the English word; keys are opaque tokens
registerI18n('es', { Play: 'Reproducir' });
// ✅ Do
@@ -136,7 +144,7 @@ registerI18n('es', { play: 'Reproducir' });
```
```tsx
-// ❌ Don't — parametric key without the placeholder
+// ❌ Don't: parametric key without the placeholder
registerI18n('es', { seekForward: 'Adelante 10 segundos' }); // TS error: missing {seconds}
// ✅ Do
@@ -149,10 +157,9 @@ For zero flash-of-English on SSR or locale switches, pass `translations` directl
## See also
-- `Locale`, `Translations`, `Translator` — core types
+- `Locale`, `Translations`, `Translator`: I18n types
- Register a custom locale
- Override individual keys
- Switch locale dynamically
- SSR and hydration
-- Add a built-in locale (contributors)
-- Accessibility — translated ARIA labels
+- 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
index 50d87a4c..983b531c 100644
--- 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
@@ -8,7 +8,7 @@ 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.
+This guide is for **contributors** adding or updating shipped packs. App authors should use Register a custom locale instead.
### Prerequisites
@@ -25,7 +25,7 @@ import type { Translations } from '../types';
export default {
play: '…',
pause: '…',
- // all keys from en.ts — each entry optional but completeness is preferred
+ // all keys from en.ts; completeness is preferred
} satisfies Partial;
```
@@ -33,10 +33,10 @@ Use **opaque keys** from `en.ts`, not English sentences as keys. Parametric stri
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
+- `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
@@ -76,7 +76,7 @@ pnpm -F @videojs/html build:cdn
```
## What's next?
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 977873b9..a24c6091 100644
--- a/site/src/content/docs/how-to/i18n-override-translations.mdx
+++ b/site/src/content/docs/how-to/i18n-override-translations.mdx
@@ -6,7 +6,7 @@ description: Patch individual i18n strings without replacing an entire locale pa
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.
+`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.
@@ -73,11 +73,11 @@ Keep required placeholders when overriding:
// ✅
registerI18n('es', { seekForward: 'Adelantar {seconds} segundos' });
-// ❌ TypeScript error — missing {seconds}
+// ❌ TypeScript error: missing {seconds}
registerI18n('es', { seekForward: 'Adelantar' });
```
## What's next?
- Switch locale dynamically
-- `getI18nTranslations` — inspect the merged map
+- `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
index 4ad0e77c..52bc2834 100644
--- a/site/src/content/docs/how-to/i18n-register-locale.mdx
+++ b/site/src/content/docs/how-to/i18n-register-locale.mdx
@@ -38,7 +38,7 @@ registerI18n('es', es);
```
-If you skip `registerI18n`, the built-in provider still **lazy-loads** `es` when `lang="es"` — explicit registration avoids the async gap on first paint.
+If you skip `registerI18n`, the built-in provider still **lazy-loads** `es` when `lang="es"`. Explicit registration avoids the async gap on first paint.
@@ -68,8 +68,10 @@ Preset skins use `Container`, which includes
+
```ts title="my-es.ts"
-import type { Translations } from '@videojs/core/i18n';
+import type { Translations } from '@videojs/html/i18n';
const es = {
play: 'Reproducir',
@@ -81,6 +83,25 @@ const es = {
export default es;
```
+
+
+
+
+```ts title="my-es.ts"
+import type { Translations } from '@videojs/react/i18n';
+
+const es = {
+ play: 'Reproducir',
+ pause: 'Pausa',
+ mute: 'Silenciar',
+ unmute: 'Activar sonido',
+} satisfies Partial;
+
+export default es;
+```
+
+
+
```ts
import { registerI18n } from '@videojs/html/i18n'; // or @videojs/react/i18n
import es from './my-es';
@@ -88,7 +109,7 @@ import es from './my-es';
registerI18n('es', es);
```
-All keys are optional — missing keys fall back through the BCP 47 chain to English.
+All keys are optional. Missing keys fall back through the BCP 47 chain to English.
## CDN
diff --git a/site/src/content/docs/how-to/i18n-ssr.mdx b/site/src/content/docs/how-to/i18n-ssr.mdx
index 6e6895c6..0756cb97 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.
+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.
@@ -46,7 +46,7 @@ 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. Labels match on server and client.
@@ -61,7 +61,7 @@ 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.
+The registry is module singleton state. Registration in server entry points carries into the client bundle when shared.
## next-intl and app routers
@@ -76,7 +76,7 @@ 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.
+Match your app's locale negotiation. Video.js does not replace framework i18n; it consumes the tag you pass.
## HTML custom elements
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 2f91ae6d..8a51a1c9 100644
--- a/site/src/content/docs/how-to/i18n-switch-locale.mdx
+++ b/site/src/content/docs/how-to/i18n-switch-locale.mdx
@@ -15,7 +15,7 @@ Video.js re-resolves translations when the active locale changes. How you trigge
## Ambient ``
-The simplest switch — update the document language and let providers pick it up:
+The simplest switch updates the document language and lets providers pick it up:
```ts
document.documentElement.lang = 'fr';
@@ -90,7 +90,7 @@ Or set `document.documentElement.lang` if the player inherits ambient language.
## Avoid flash while switching
-Async lazy loads can briefly show English. Preload copy before the active locale changes — three common patterns:
+Async lazy loads can briefly show English. Preload copy before the active locale changes with one of three common patterns:
### Pre-register at bootstrap (React and HTML)
@@ -106,7 +106,7 @@ import fr from '@videojs/react/i18n/locales/fr';
registerI18n('es', es);
registerI18n('fr', fr);
-// Switching locale is instant — no remount, no lazy-load gap
+// Switching locale is instant: no remount, no lazy-load gap
...
@@ -174,12 +174,12 @@ async function switchTo(next: string) {
| Approach | Scope | Best for |
| --- | --- | --- |
-| `registerI18n` at bootstrap | Global — all providers | Language picker with a fixed set of locales |
-| `registerI18n` before switch | Global — all providers | On-demand prefetch before changing `locale` |
+| `registerI18n` at bootstrap | Global, all providers | Language picker with a fixed set of locales |
+| `registerI18n` before switch | Global, all providers | On-demand prefetch before changing `locale` |
| `I18nProvider translations` | Single provider subtree | Scoped overrides, SSR first paint |