fix(i18n): align stack after phrase key rollout

This commit is contained in:
Sam Potts
2026-07-13 08:22:40 +10:00
parent 1080f5fb19
commit cfb20fb134
21 changed files with 126 additions and 128 deletions
+13 -13
View File
@@ -1,13 +1,13 @@
---
title: Internationalization
description: How Video.js translates player UI copy with opaque keys, a global registry, and locale providers
description: How Video.js translates player UI copy with English phrase keys, a global registry, and locale providers
---
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 current English phrase (`Play`, `Pause`, `Seek forward {seconds} seconds`), so missing translations stay readable.
<FrameworkCase frameworks={["html"]}>
@@ -44,13 +44,13 @@ 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
## Phrase keys
Core controls expose keys, not visible labels. `PlayButtonCore.getLabel()` returns `'play'`; the translator turns that into `'Play'`, `'Reproducir'`, or your override.
Core controls expose current English phrases. `PlayButtonCore.getLabel()` returns `'Play'`; the translator turns that into `'Play'`, `'Reproducir'`, or your override.
Keys are typed in <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>. TypeScript catches missing `{param}` placeholders and wrong argument names at compile time.
Phrase params are typed in <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>. TypeScript catches missing `{param}` placeholders and wrong argument names at compile time.
Parametric strings use `{placeholder}` tokens, for example `seekForward: 'Seek forward {seconds} seconds'` and `timeRemainingPhrase: '{duration} remaining'`.
Parametric strings use `{placeholder}` tokens, for example `'Seek forward {seconds} seconds'` and `'{duration} remaining'`.
## Global registry
@@ -60,7 +60,7 @@ Parametric strings use `{placeholder}` tokens, for example `seekForward: 'Seek f
| --- | --- |
| <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink> | Add or merge a locale layer |
| <DocsLink slug="reference/get-i18n-translations">`getI18nTranslations`</DocsLink> | Read the merged map for a locale |
| <DocsLink slug="reference/has-registered-i18n">`hasRegisteredI18n`</DocsLink> | Check whether a tag is in the registry |
| <DocsLink slug="reference/has-registered-locale">`hasRegisteredLocale`</DocsLink> | Check whether a tag is in the registry |
| <DocsLink slug="reference/on-i18n-registry-change">`onI18nRegistryChange`</DocsLink> | Subscribe to registry updates |
Import from `@videojs/html/i18n` or `@videojs/react/i18n` depending on your framework.
@@ -108,7 +108,7 @@ es-MX → es → en
zh-Hant-HK → zh-hant → zh → en
```
`getI18nTranslations`, lazy `loadLocale`, and providers all use the same chain via `localeLookupChain`.
`getI18nTranslations`, lazy `loadLocale`, and providers all use the same chain via `findLocaleKeys`.
## Merge priority
@@ -136,19 +136,19 @@ CDN consumers load self-registering modules:
## Common pitfalls
```tsx
// ❌ Don't: key is the English word; keys are opaque tokens
registerI18n('es', { Play: 'Reproducir' });
// ❌ Don't: old camelCase keys are ignored
registerI18n('es', { play: 'Reproducir' });
// ✅ Do
registerI18n('es', { play: 'Reproducir' });
registerI18n('es', { Play: 'Reproducir' });
```
```tsx
// ❌ Don't: parametric key without the placeholder
registerI18n('es', { seekForward: 'Adelante 10 segundos' }); // TS error: missing {seconds}
registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelante 10 segundos' }); // TS error: missing {seconds}
// ✅ Do
registerI18n('es', { seekForward: 'Adelantar {seconds} segundos' });
registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelantar {seconds} segundos' });
```
<Aside type="tip">
@@ -13,7 +13,7 @@ This guide is for **contributors** adding or updating shipped packs. App authors
### Prerequisites
- Familiarity with <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and <DocsLink slug="reference/translation-params">`TranslationParams`</DocsLink>
- English defaults in `packages/core/src/core/i18n/locales/en.ts` as the source of keys
- English defaults in `packages/core/src/core/i18n/locales/en.ts` as the source of phrases
## 1. Add the locale file
@@ -23,24 +23,24 @@ Create `packages/core/src/core/i18n/locales/{tag}.ts` using the BCP 47 tag as th
import type { Translations } from '../types';
export default {
play: '…',
pause: '…',
// all keys from en.ts; completeness is preferred
Play: '…',
Pause: '…',
// all phrases from en.ts; completeness is preferred
} satisfies Partial<Translations>;
```
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<Translations>`).
Use the current English phrases from `en.ts`. Parametric strings must include the same `{placeholder}` tokens (TypeScript enforces this via `satisfies Partial<Translations>`).
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
- `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 `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.
Add the tag to `LOCALES` in `packages/core/src/core/i18n/locales.ts`.
## 3. Regenerate loaders
@@ -1,14 +1,14 @@
---
title: Override translation keys
title: Override translations
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.
`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 <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for merge order and key naming.
Read <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for merge order and phrase naming.
## Override after a built-in pack
@@ -17,10 +17,10 @@ import { registerI18n } from '@videojs/html/i18n';
import es from '@videojs/html/i18n/locales/es';
registerI18n('es', es);
registerI18n('es', { play: 'Comenzar' }); // only `play` changes
registerI18n('es', { Play: 'Comenzar' }); // only `Play` changes
```
Later registrations win for the same key. The rest of the `es` pack stays intact.
Later registrations win for the same phrase. The rest of the `es` pack stays intact.
## React provider overrides
@@ -33,7 +33,7 @@ import { I18nProvider } from '@videojs/react/i18n';
import { Provider, VideoSkin, Video } from '@videojs/react/video';
<Provider>
<I18nProvider locale="es" translations={{ play: 'Comenzar' }}>
<I18nProvider locale="es" translations={{ Play: 'Comenzar' }}>
<VideoSkin>
<Video src="..." playsInline />
</VideoSkin>
@@ -45,8 +45,8 @@ Nested providers inherit the parent locale when you only pass `translations`:
```tsx
<I18nProvider locale="de">
<I18nProvider translations={{ play: 'Override' }}>
{/* locale stays `de`; only `play` differs */}
<I18nProvider translations={{ Play: 'Override' }}>
{/* locale stays `de`; only `Play` differs */}
</I18nProvider>
</I18nProvider>
```
@@ -59,22 +59,22 @@ Use <DocsLink slug="reference/media-text">`<media-text>`</DocsLink> for static c
```html
<media-i18n-provider lang="es">
<media-text key="play"></media-text>
<media-text key="Play"></media-text>
</media-i18n-provider>
```
Override the key in the registry or set `lang` on the provider to a locale where you patched `play`.
Override the phrase in the registry or set `lang` on the provider to a locale where you patched `Play`.
## Parametric keys
## Parametric phrases
Keep required placeholders when overriding:
```ts
// ✅
registerI18n('es', { seekForward: 'Adelantar {seconds} segundos' });
registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelantar {seconds} segundos' });
// ❌ TypeScript error: missing {seconds}
registerI18n('es', { seekForward: 'Adelantar' });
registerI18n('es', { 'Seek forward {seconds} seconds': 'Adelantar' });
```
## What's next?
@@ -7,12 +7,12 @@ import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Aside from '@/components/Aside.astro';
import DocsLink from '@/components/docs/DocsLink.astro';
Register strings once with <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, then set `lang` on the page or pass `locale` to a provider. Read <DocsLink slug="concepts/i18n">Internationalization</DocsLink> first if you are new to opaque keys and the registry.
Register strings once with <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, then set `lang` on the page or pass `locale` to a provider. Read <DocsLink slug="concepts/i18n">Internationalization</DocsLink> 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 camelCase tokens (`play`, `pause`, …)
- A partial translation map keyed by current English phrases (`Play`, `Pause`, …)
## Use a shipped pack
@@ -74,10 +74,10 @@ Preset skins use `Container`, which includes <DocsLink slug="reference/i18n-prov
import type { Translations } from '@videojs/html/i18n';
const es = {
play: 'Reproducir',
pause: 'Pausa',
mute: 'Silenciar',
unmute: 'Activar sonido',
Play: 'Reproducir',
Pause: 'Pausa',
Mute: 'Silenciar',
Unmute: 'Activar sonido',
} satisfies Partial<Translations>;
export default es;
@@ -91,10 +91,10 @@ export default es;
import type { Translations } from '@videojs/react/i18n';
const es = {
play: 'Reproducir',
pause: 'Pausa',
mute: 'Silenciar',
unmute: 'Activar sonido',
Play: 'Reproducir',
Pause: 'Pausa',
Mute: 'Silenciar',
Unmute: 'Activar sonido',
} satisfies Partial<Translations>;
export default es;
@@ -138,8 +138,8 @@ CDN locale files import `registerI18n` from the shared `cdn/i18n.js` bundle so e
import { registerI18n } from 'https://cdn.jsdelivr.net/npm/@videojs/html/cdn/i18n.js';
registerI18n('es', {
play: 'Reproducir',
pause: 'Pausa',
Play: 'Reproducir',
Pause: 'Pausa',
});
```
@@ -17,16 +17,14 @@ import es from '@videojs/html/i18n/locales/es';
## Definition
```ts
type BuiltInLocale =
| (typeof BUILT_IN_LOCALES)[number]
| (typeof LOCALE_ALIAS_TAGS)[number];
type BuiltInLocale = (typeof LOCALES)[number];
```
`BUILT_IN_LOCALES` lists regional packs (`es`, `pt-BR`, `zh-CN`, …). `LOCALE_ALIAS_TAGS` adds shorthand tags (`pt`, `zh`) that resolve to regional packs through the lookup chain.
`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 `SHIPPED_LOCALE_TAGS` when a pack is not already registered. Explicit `registerI18n` or CDN locale modules skip the async gap on first paint.
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
@@ -13,8 +13,8 @@ import { createTranslator, getI18nTranslations } from '@videojs/html/i18n';
// or @videojs/react/i18n
const t = createTranslator(getI18nTranslations('fr'), 'fr');
t('play');
t('seekForward', { seconds: 5 });
t('Play');
t('Seek forward {seconds} seconds', { seconds: 5 });
```
<UtilReference util="createTranslator" />
@@ -15,7 +15,7 @@ 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'); // merged Portuguese string
```
<UtilReference util="getI18nTranslations" />
@@ -1,19 +0,0 @@
---
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
```
<UtilReference util="hasRegisteredI18n" />
@@ -0,0 +1,19 @@
---
title: hasRegisteredLocale
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";
`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';
hasRegisteredLocale('fr'); // false until registered
registerI18n('fr', { Play: 'Lecture' });
hasRegisteredLocale('fr'); // true
```
<UtilReference util="hasRegisteredLocale" />
@@ -16,7 +16,7 @@ Wrap custom controls or force a locale explicitly:
```tsx
import { I18nProvider } from '@videojs/react/i18n';
<I18nProvider locale="de" translations={{ play: 'Abspielen' }}>
<I18nProvider locale="de" translations={{ Play: 'Abspielen' }}>
<MyControls />
</I18nProvider>
```
+1 -1
View File
@@ -24,7 +24,7 @@ The `(string & {})` pattern keeps custom tags (`'xx'`, `'en-US'`) type-safe with
## Resolution
Providers and `getI18nTranslations` normalize tags and walk the parent chain (`es-MX` → `es` → `en`) via `localeLookupChain`. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for explicit vs ambient resolution.
Providers and `getI18nTranslations` normalize tags and walk the parent chain (`es-MX` → `es` → `en`) via `findLocaleKeys`. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for explicit vs ambient resolution.
## Examples
@@ -10,7 +10,7 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro";
`<media-i18n-provider>` applies the i18n provider mixin to a standalone subtree. Built-in `<video-player>` 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 <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, then set `lang` on the provider or an ancestor. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for opaque keys and fallback behavior.
Register locale strings with <DocsLink slug="reference/register-i18n">`registerI18n`</DocsLink>, then set `lang` on the provider or an ancestor. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for phrase keys and fallback behavior.
```html
<script type="module">
@@ -22,7 +22,7 @@ Register locale strings with <DocsLink slug="reference/register-i18n">`registerI
</script>
<media-i18n-provider lang="es">
<media-text key="play"></media-text>
<media-text key="Play"></media-text>
</media-i18n-provider>
```
@@ -1,6 +1,6 @@
---
title: media-text
description: HTML custom element that renders a translated string by opaque key
description: HTML custom element that renders a translated string by phrase
---
import DocsLink from "@/components/docs/DocsLink.astro";
@@ -8,11 +8,11 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro";
<FrameworkCase frameworks={["html"]}>
`<media-text>` renders the translated value for a registry key inside a <DocsLink slug="reference/media-i18n-provider">`<media-i18n-provider>`</DocsLink> (or any ancestor with the i18n provider mixin). Use it for standalone labels, tooltips, or demos — skin controls resolve keys internally.
`<media-text>` renders the translated value for a registry phrase inside a <DocsLink slug="reference/media-i18n-provider">`<media-i18n-provider>`</DocsLink> (or any ancestor with the i18n provider mixin). Use it for standalone labels, tooltips, or demos — skin controls resolve phrases internally.
```html
<media-i18n-provider lang="ja">
<media-text key="play"></media-text>
<media-text key="Play"></media-text>
</media-i18n-provider>
```
@@ -20,8 +20,8 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro";
| Attribute | Description |
| --- | --- |
| `key` | Opaque translation key (`play`, `pause`, `timeRemainingPhrase`, …). |
| `key` | English translation phrase (`Play`, `Pause`, `{duration} remaining`, …). |
Parametric keys are not yet supported on `<media-text>` — use `createTranslator` in script for interpolated strings.
Parametric phrases are not yet supported on `<media-text>` — use `createTranslator` in script for interpolated strings.
</FrameworkCase>
@@ -8,14 +8,14 @@ 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 by the i18n bundle. Call `registerI18n` for custom locales or to patch shipped packs before the player renders.
Import from `@videojs/html/i18n` or `@videojs/react/i18n`. Keys are opaque camelCase tokens (`play`, `pause`). See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and <DocsLink slug="reference/translations">`Translations`</DocsLink>.
Import from `@videojs/html/i18n` or `@videojs/react/i18n`. Keys are the current English phrases (`Play`, `Pause`). See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> and <DocsLink slug="reference/translations">`Translations`</DocsLink>.
```ts
import { registerI18n } from '@videojs/react/i18n';
registerI18n('es', {
play: 'Reproducir',
pause: 'Pausar',
Play: 'Reproducir',
Pause: 'Pausar',
});
```
@@ -1,11 +1,11 @@
---
title: TranslationParams
description: Typed contract for translation keys and their placeholder arguments
description: Typed contract for translation phrases and their placeholder arguments
---
import DocsLink from '@/components/docs/DocsLink.astro';
`TranslationParams` maps every opaque translation key to its argument shape. Keys with `never` accept only `t('key')`. Keys with an object accept `t('key', { … })` with typed placeholder names.
`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.
## Import
@@ -18,45 +18,45 @@ import type { TranslationParams } from '@videojs/html/i18n';
```ts
type TranslationParams = {
play: never;
pause: never;
seekForward: { seconds: number | string };
timeRemainingPhrase: { duration: string };
Play: never;
Pause: never;
'Seek forward {seconds} seconds': { seconds: number | string };
'{duration} remaining': { duration: string };
// …
};
```
English defaults and the full key list live in `packages/core/src/core/i18n/locales/en.ts`. The authoritative TypeScript map is `packages/core/src/core/i18n/types.ts`.
English defaults and the full phrase list live in `packages/core/src/core/i18n/locales/en.ts`. The authoritative TypeScript map is `packages/core/src/core/i18n/types.ts`.
## Parametric keys
## Parametric phrases
| Key | Placeholders | Example English value |
| --- | --- | --- |
| `seekForward` | `{seconds}` | `Seek forward {seconds} seconds` |
| `seekBackward` | `{seconds}` | `Seek backward {seconds} seconds` |
| `playbackRateAria` | `{rate}` | `Playback rate {rate}` |
| `timeSliderValueTextRange` | `{current}`, `{duration}` | `{current} of {duration}` |
| `timeRemainingPhrase` | `{duration}` | `{duration} remaining` |
| `volumeSliderValueTextMuted` | `{percent}` | `{percent}, muted` |
| `indicatorVolumeWithValue` | `{value}` | `Volume {value}` |
| Phrase | Placeholders |
| --- | --- |
| `Seek forward {seconds} seconds` | `{seconds}` |
| `Seek backward {seconds} seconds` | `{seconds}` |
| `Playback rate {rate}` | `{rate}` |
| `{current} of {duration}` | `{current}`, `{duration}` |
| `{duration} remaining` | `{duration}` |
| `{percent}, muted` | `{percent}` |
| `Volume {value}` | `{value}` |
All other keys are plain strings with no parameters.
All other phrases are plain strings with no parameters.
## Usage with Translator
```ts
const t: Translator = createTranslator(translations, 'es');
t('play');
t('seekForward', { seconds: 10 });
t('timeRemainingPhrase', { duration: '1 minute' });
t('Play');
t('Seek forward {seconds} seconds', { seconds: 10 });
t('{duration} remaining', { duration: '1 minute' });
```
TypeScript rejects missing placeholders when defining <DocsLink slug="reference/translations">`Translations`</DocsLink> overlays:
```ts
registerI18n('es', {
seekForward: 'Adelantar', // error: missing {seconds}
'Seek forward {seconds} seconds': 'Adelantar', // error: missing {seconds}
});
```
@@ -64,4 +64,4 @@ registerI18n('es', {
- <DocsLink slug="reference/translations">`Translations`</DocsLink>
- <DocsLink slug="reference/translator">`Translator`</DocsLink>
- <DocsLink slug="concepts/i18n">Internationalization</DocsLink>: Opaque keys overview
- <DocsLink slug="concepts/i18n">Internationalization</DocsLink>: Phrase keys overview
@@ -1,6 +1,6 @@
---
title: Translations
description: Partial map of opaque translation keys to localized strings
description: Partial map of English translation phrases to localized strings
---
import DocsLink from '@/components/docs/DocsLink.astro';
@@ -34,16 +34,16 @@ import type { Translations } from '@videojs/html/i18n';
// or @videojs/react/i18n
const es = {
play: 'Reproducir',
pause: 'Pausa',
seekForward: 'Adelantar {seconds} segundos',
Play: 'Reproducir',
Pause: 'Pausa',
'Seek forward {seconds} seconds': 'Adelantar {seconds} segundos',
} satisfies Partial<Translations>;
registerI18n('es', es);
```
```tsx
<I18nProvider locale="de" translations={{ play: 'Abspielen' }} />
<I18nProvider locale="de" translations={{ Play: 'Abspielen' }} />
```
Only supplied keys override lower layers. See <DocsLink slug="concepts/i18n">Internationalization</DocsLink> for merge priority.
@@ -1,11 +1,11 @@
---
title: Translator
description: Typed function that resolves opaque translation keys to localized strings
description: Typed function that resolves English translation phrases to localized strings
---
import DocsLink from '@/components/docs/DocsLink.astro';
`Translator` is the callable returned by <DocsLink slug="reference/create-translator">`createTranslator`</DocsLink> and <DocsLink slug="reference/use-translator">`useTranslator`</DocsLink>. It turns opaque keys from core controls into localized copy and interpolates `{placeholder}` tokens when params are required.
`Translator` is the callable returned by <DocsLink slug="reference/create-translator">`createTranslator`</DocsLink> and <DocsLink slug="reference/use-translator">`useTranslator`</DocsLink>. It turns current English phrases from core controls into localized copy and interpolates `{placeholder}` tokens when params are required.
## Import
@@ -23,10 +23,10 @@ type Translator = <K extends keyof TranslationParams>(
) => string;
```
- Plain keys: `t('play')`
- Parametric keys: `t('seekForward', { seconds: 10 })`
- Plain phrases: `t('Play')`
- Parametric phrases: `t('Seek forward {seconds} seconds', { seconds: 10 })`
Missing keys in the active map resolve to the key string (`'play'`) so partial locale packs degrade visibly during development.
Missing phrases in the active map resolve to the source English (`'Play'`) so partial locale packs stay readable.
## Create manually
@@ -35,7 +35,7 @@ import { createTranslator, getI18nTranslations } from '@videojs/html/i18n';
// or @videojs/react/i18n
const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR');
t('pause'); // localized or key fallback
t('Pause'); // localized or English fallback
```
## React hook
@@ -45,11 +45,11 @@ import { useTranslator } from '@videojs/react/i18n';
function Label() {
const t = useTranslator();
return <span>{t('play')}</span>;
return <span>{t('Play')}</span>;
}
```
Control components resolve keys from core `getLabel()` through `resolveControlLabel` / `resolveControlAttrs`. You rarely call `t()` directly unless building custom UI.
Control components resolve phrases from core `getLabel()` through their framework adapters. You rarely call `t()` directly unless building custom UI.
## Related
@@ -9,7 +9,7 @@ import FrameworkCase from "@/components/docs/FrameworkCase.astro";
<FrameworkCase frameworks={["react"]}>
`useTranslator` returns the translator from the nearest <DocsLink slug="reference/i18n-provider">`I18nProvider`</DocsLink>. Control components pass opaque keys from core `getLabel()` — for example `t('play')` and `t('seekForward', { seconds: 10 })`.
`useTranslator` returns the translator from the nearest <DocsLink slug="reference/i18n-provider">`I18nProvider`</DocsLink>. Control components pass current English phrases from core `getLabel()` — for example `t('Play')` and `t('Seek forward {seconds} seconds', { seconds: 10 })`.
When no provider is mounted, the hook falls back to English registry strings so standalone demos do not throw.
+1 -1
View File
@@ -195,7 +195,7 @@ export const sidebar: Sidebar = [
contents: [
{ slug: 'reference/register-i18n', sidebarLabel: 'registerI18n' },
{ slug: 'reference/get-i18n-translations', sidebarLabel: 'getI18nTranslations' },
{ slug: 'reference/has-registered-i18n', sidebarLabel: 'hasRegisteredI18n' },
{ slug: 'reference/has-registered-locale', sidebarLabel: 'hasRegisteredLocale' },
{ slug: 'reference/on-i18n-registry-change', sidebarLabel: 'onI18nRegistryChange' },
{ slug: 'reference/create-translator', sidebarLabel: 'createTranslator' },
{
+1 -1
View File
@@ -3,7 +3,7 @@ import { kebabCase } from 'es-toolkit/string';
const UTIL_SLUG_OVERRIDES: Record<string, string> = {
registerI18n: 'register-i18n',
getI18nTranslations: 'get-i18n-translations',
hasRegisteredI18n: 'has-registered-i18n',
hasRegisteredLocale: 'has-registered-locale',
onI18nRegistryChange: 'on-i18n-registry-change',
createI18n: 'create-i18n',
createTranslator: 'create-translator',