feat(core): add i18n foundation with English locale and UI wiring#1589
Conversation
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📦 Bundle Size Report🎨 @videojs/html
Presets (7)
Media (10)
Players (5)
Skins (30)
UI Components (38)
Sizes are marginal over the root entry point. ⚛️ @videojs/react
Presets (7)
Media (9)
Skins (27)
UI Components (32)
Sizes are marginal over the root entry point. 🧩 @videojs/core
Entries (15)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils
Entries (10)
📦 @videojs/spf — no changesEntries (4)
ℹ️ How to interpretAll sizes are standalone totals (minified + brotli).
Run |
There was a problem hiding this comment.
Happy to remove this if we don't think it's worthwhile. It's a bit of an edge case solution anyway. For it to work currently:
- the user has to be using Chrome; and
- they have to already have the language pack downloaded (we won't trigger it)
There was a problem hiding this comment.
I'm not sure, context matters sometimes with translations. That browser API might give some strange results?
There was a problem hiding this comment.
Yeah, that's valid. What I've done in the past is have a "Description" field alongside the key to set the context as translators often ask how it's being used (e.g. verb vs noun). Maybe we could add that somehow.
| import packageJson from './package.json' with { type: 'json' }; | ||
|
|
||
| const localeEntries = { | ||
| 'i18n/locales/en': './src/core/i18n/locales/en.ts', |
There was a problem hiding this comment.
More are added in later PRs 😅
| it('initializes with default property values', () => { | ||
| const slider = createElement(TimeSliderElement); | ||
| expect(slider.label).toBe('Seek'); | ||
| expect(slider.label).toBe(''); |
There was a problem hiding this comment.
Empty string means it'll be translated with the default key.
| /** | ||
| * Most-specific-first BCP 47 lookup tags (normalized). Always ends with `en` when missing from the truncated chain. | ||
| * | ||
| * @example `es-419-u-nu-latn` → `['es-419', 'es', 'en']` |
| unmute: 'Unmute', | ||
| seekForward: 'Seek forward {seconds} seconds', | ||
| seekBackward: 'Seek backward {seconds} seconds', | ||
| enterFullscreen: 'Enter fullscreen', |
There was a problem hiding this comment.
(blocking) I didn't expect this type of keys. Why not use both Enter fullscreen for key and value?
this will be a lot easier for translations because the key is the english value to translate.
in code this will read well too, maybe long in some cases.
(feedback is about all keys in this file)
There was a problem hiding this comment.
We came to the decision to use opaque keys rather than English:
https://github.com/videojs/v10/blob/main/internal/design/i18n.md#opaque-camelcase-keys-not-english-strings
Introduce the core i18n registry, translator, browser translation fallback, English copy, locale-aware formatting utilities, and opaque translation keys across core UI components. Co-authored-by: Cursor <cursoragent@cursor.com>
Use relative imports in core tests, exclude formatOptions from HTML element property maps, and align html/react tests with opaque translation keys until platform i18n wiring lands in later stack PRs. Co-authored-by: Cursor <cursoragent@cursor.com>
Only map known browser-specific strings to registry keys so app-supplied copy on standard error codes is shown instead of the generic translation. Co-authored-by: Cursor <cursoragent@cursor.com>
Skip Browser Translation API for en-US and en-GB so regional English UI locales keep the built-in en registry layer. Co-authored-by: Cursor <cursoragent@cursor.com>
Use a parametric timeRemainingPhrase key so locales control full remaining time word order; formatDuration accepts formatRemaining instead of translate. Co-authored-by: Cursor <cursoragent@cursor.com>
nearestLang now falls back to element.lang when the attribute is empty so document.documentElement.lang updates resolve consistently. Adds tests for lang property changes in subscribeAmbientLang and nearestLang. Co-authored-by: Cursor <cursoragent@cursor.com>
shouldAttemptBrowserTranslation now tracks which BCP 47 tags returned lazy packs instead of treating any merged overlay (including en) as a built-in locale. mergeLocaleOverlays returns loadedTags for callers. Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid mixed-language remaining-time phrases when callers omit formatRemaining but pass a non-English locale to formatDuration. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bf72f98. Configure here.

Refs #222
Closes #1363
Summary
Foundation PR for Video.js 10 i18n: registry, translator, English copy, browser Translation API fallback, locale-aware formatting utilities, and opaque translation keys wired through core UI components.
Changes
@videojs/core/i18n— registry,createTranslator, BCP 47 lookup chain,en.tsbase layer@videojs/utils— DOM locale helpers (subscribeAmbientLang,mergeLocaleOverlays) andformatDurationviaIntl.DurationFormatimplementedstatusStack info
This is PR 1 of 5 in the i18n stack. Merge order:
Testing
Made with Cursor
Note
Medium Risk
Touches accessibility strings and media error messaging across many UI cores; behavior depends on downstream HTML/React translators, but English defaults and tests limit user-visible regressions in this PR alone.
Overview
Adds
@videojs/core/i18n: a global registry with BCP 47 lookup, typedcreateTranslator, English defaults inen.ts, and optional Browser Translation API fallback (placeholder masking,shouldAttemptBrowserTranslation, pre-installed models only).Core UI now exposes opaque translation keys (and param hooks like
getLabelParams/getValueTextParams) instead of hardcoded English for play/mute/seek/sliders/time, live/cast/captions, playback rate, error dialog copy, and input-feedback indicators.resolveControlAttrs/resolveOptionalControlLabelbridge keys to resolved strings for platform layers.@videojs/utilsgains locale DOM helpers (effectiveLocale,mergeLocaleOverlays, ambientlangsubscription) andformatDuration/formatVolumePercentviaIntl. Native HLS errors for standard codes are normalized to canonical messages so dialog copy can map to registry keys.internal/design/i18n.mdis marked implemented and documents keys, merge order, and tooltip/media-textbehavior.@videojs/coreexports./i18nand./i18n/locales/*; registry is a declared side effect. Non-English locale packs and HTML/React providers are explicitly deferred to follow-up stack PRs.Reviewed by Cursor Bugbot for commit b58714d. Bugbot is set up for automated code reviews on this repo. Configure here.