feat(html): i18n#1591
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
0da2fa9 to
08a4f50
Compare
08a4f50 to
98c82df
Compare
98c82df to
66f0283
Compare
66f0283 to
33113b0
Compare
Compose i18n provider onto SkinElement, resolve playback-rate tooltips via getResolvedLabel, and normalize CDN i18n entry paths on Windows. Co-authored-by: Cursor <cursoragent@cursor.com>
Re-check lazy-load sequence after getBrowserTranslations resolves so a superseded locale cannot register browser translations globally. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep tooltip copy visible before media state attaches by translating the trigger label when getResolvedLabel returns undefined. Co-authored-by: Cursor <cursoragent@cursor.com>
Adds integration coverage that play button aria-label updates when document.documentElement.lang changes and the i18n provider has no explicit lang attribute. Co-authored-by: Cursor <cursoragent@cursor.com>
Use mergeLocaleOverlays loadedTags instead of merged overlay so English-only lazy merges no longer block browser translation fallback. Co-authored-by: Cursor <cursoragent@cursor.com>
| * via `adoptedStyleSheets` (or `<style>` fallback). | ||
| */ | ||
| export class SkinElement extends ReactiveElement { | ||
| export class SkinElement extends I18nProviderMixin(ReactiveElement) { |
There was a problem hiding this comment.
Rough thought but this is a possible smell to me in that it needs to be baked in and not an optional higher-level concern. Largely related to my comments on #1708.
In other words, I think i18n should be composed in rather than provided out of the box but this circles back to conversation around keys because a default english translation is required since keys have no meaning on their own.
| readonly #consumer: ContextConsumer<I18nContext, ReactiveControllerHost & HTMLElement>; | ||
| #unsubscribeRegistry: (() => void) | undefined; | ||
|
|
||
| constructor(host: ReactiveControllerHost & HTMLElement, context: I18nContext) { |
There was a problem hiding this comment.
(suggestion): pull ReactiveControllerHost & HTMLElement up into a I18nControllerHost type
| export namespace I18nController { | ||
| export type Constructor = new (host: ReactiveControllerHost & HTMLElement, context: I18nContext) => I18nController; | ||
| } |
There was a problem hiding this comment.
I think this can be Constructor = typeof I18nController, also good to add Host = I18nControllerHost to the namespace
| export function createI18n(options?: CreateI18nOptions): CreateI18nResult { | ||
| const ProviderMixin = createI18nProviderMixin({ | ||
| i18nContext, | ||
| loadLocale: options?.loadLocale, | ||
| }); | ||
| const TextMixin = createTextMixin({ i18nContext }); | ||
|
|
||
| return { | ||
| context: i18nContext, | ||
| I18nController, | ||
| ProviderMixin, | ||
| TextMixin, | ||
| }; | ||
| } |
| /** DOM `lang` values are untyped strings; align with core {@link Locale} at the boundary. */ | ||
| export function localeFromDomLang(raw: string | undefined): Locale | undefined { | ||
| return (domLangFromString(raw) as Locale) ?? undefined; | ||
| } |
There was a problem hiding this comment.
we could make the return type generic on domLangFromString, or we could move it out of from utils for now if it's only required in the html package.
| export function resolvePlayerLocale(explicit: Locale | undefined, inherited: Locale | undefined): Locale { | ||
| return effectiveLocale(explicit, inherited) as Locale; | ||
| } |
There was a problem hiding this comment.
same comments as localeFromDomLang
| /** Override lazy loading of shipped locale packs (tests or custom loaders). */ | ||
| loadLocale?: LocaleLoader | undefined; |
| import type { LocaleLoader, ReactiveElementMixinBase } from './types'; | ||
|
|
||
| export interface I18nProviderConfig { | ||
| i18nContext: I18nContext; |
| export type I18nTextMixin = <Base extends ReactiveElementMixinBase>(Base: Base) => Constructor<ReactiveElement> & Base; | ||
|
|
||
| export interface TextMixinConfig { | ||
| i18nContext: I18nContext; |
There was a problem hiding this comment.
Maybe it'd help if we had a base radio group element that the rest can extend and share basic logic on
Refs #222
Closes #1365
Closes #1367
Summary
Adds the HTML i18n layer with ambient
<html lang>inheritance, browser translation fallback, defaultloadLocale, and translated labels across web component controls.Changes
createI18n()—I18nController,ProviderMixin,TextMixin,<media-i18n-provider>,<media-text>aria-labels and tooltips viaI18nController@videojs/html/i18nand@videojs/html/cdn/i18nStack info
PR 3 of 5 — stacks on locale packs PR.
Testing
pnpm -F @videojs/html build pnpm -F @videojs/html test src/i18nMade with Cursor
Note
Medium Risk
Touches many player UI and accessibility strings across skins and controls; async locale/browser loading adds edge cases, though covered by new tests.
Overview
Adds an HTML i18n layer on top of
@videojs/core/i18n:createI18n()exposes Lit context,I18nController, provider/text mixins, and registers<media-i18n-provider>/<media-text>. Locale resolution follows explicitlang, ambient<html lang>, lazy shipped locale packs, registry overlays, and optional browser translation (with sequence guards on async loads).Players and
SkinElementare wrapped withI18nProviderMixinso skins and controls share a translator. UI elements consumeI18nControllerand useresolveControlAttrs/resolveControlLabelfor translatedaria-labels, tooltips, error-dialog copy, time remaining phrases, and input-indicator labels. Skin templates drop hardcoded error-dialog strings in favor of runtime-filled elements.Packaging & CDN: new
@videojs/html/i18nand locale export paths,build:cdnruns locale generation pluscdnI18nExternalPlugin(sharedi18n.json jsDelivr in prod, relative in dev), and the video CDN entry pulls indefine/i18n.Reviewed by Cursor Bugbot for commit 85e56ff. Bugbot is set up for automated code reviews on this repo. Configure here.