feat(core): add i18n foundation with English locale and UI wiring (#1589)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Wesley Luyten <me@wesleyluyten.com>
This commit is contained in:
Sam Potts
2026-06-18 11:55:38 -07:00
committed by GitHub
co-authored by Cursor Wesley Luyten
parent fa768da757
commit 768bf09da0
79 changed files with 2237 additions and 455 deletions
@@ -1,5 +1,5 @@
import { PlaybackRateRadioGroupCore, PlaybackRateRadioGroupDataAttrs } from '@videojs/core';
import { applyStateDataAttrs, logMissingFeature, selectPlaybackRate } from '@videojs/core/dom';
import { applyElementProps, applyStateDataAttrs, logMissingFeature, selectPlaybackRate } from '@videojs/core/dom';
import type { PropertyDeclarationMap, PropertyValues } from '@videojs/element';
import { playerContext } from '../../player/context';
@@ -54,8 +54,11 @@ export class PlaybackRateRadioGroupElement extends MenuRadioGroupElement {
this.value = this.#core.getRateValue(state.rate);
if (!this.hasAttribute('aria-label') && !this.hasAttribute('aria-labelledby')) {
this.setAttribute('aria-label', 'Playback rate');
this.setAttribute('aria-label', this.#core.getLabel(state));
}
applyElementProps(this, {
'aria-disabled': state.disabled ? 'true' : undefined,
});
this.#syncContent(state);
}
@@ -155,7 +155,7 @@ afterEach(() => {
describe('PlaybackRateRadioGroupElement', () => {
it('renders radio items from the available playback rates', async () => {
const { menu, trigger } = setup({ playbackRates: [1, 1.25, 1.5], playbackRate: 1.25 });
const { menu, options, trigger } = setup({ playbackRates: [1, 1.25, 1.5], playbackRate: 1.25 });
await waitForMenu(menu, trigger);
@@ -165,6 +165,8 @@ describe('PlaybackRateRadioGroupElement', () => {
await waitForAssertion(() => {
expect(items.map((item) => item.getAttribute('aria-checked'))).toEqual(['false', 'true', 'false']);
});
expect(options.getAttribute('aria-label')).toBe('playbackRateAria');
expect(options.getAttribute('data-rate')).toBe('1.25');
});
it('renders radio items from a template', async () => {
@@ -206,7 +208,7 @@ describe('PlaybackRateButtonElement', () => {
await trigger.updateComplete;
expect(trigger.getAttribute('role')).toBe('button');
expect(trigger.getAttribute('aria-label')).toBe('Playback rate 2');
expect(trigger.getAttribute('aria-label')).toBe('playbackRateAria');
expect(trigger.getAttribute('data-rate')).toBe('2');
});
@@ -27,7 +27,7 @@ describe('TimeSliderElement', () => {
it('initializes with default property values', () => {
const slider = createElement(TimeSliderElement);
expect(slider.label).toBe('Seek');
expect(slider.label).toBe('');
expect(slider.changeThrottle).toBe(100);
expect(slider.step).toBe(1);
expect(slider.largeStep).toBe(10);
@@ -30,7 +30,7 @@ export class TimeSliderElement extends MediaElement {
orientation: { type: String },
disabled: { type: Boolean },
thumbAlignment: { type: String, attribute: 'thumb-alignment' },
} satisfies PropertyDeclarationMap<Exclude<keyof TimeSliderCore.Props, 'value' | 'min' | 'max'>>;
} satisfies PropertyDeclarationMap<Exclude<keyof TimeSliderCore.Props, 'value' | 'min' | 'max' | 'formatOptions'>>;
label = TimeSliderCore.defaultProps.label;
changeThrottle = TimeSliderCore.defaultProps.changeThrottle;
+1 -1
View File
@@ -13,7 +13,7 @@ export class TimeElement extends MediaElement {
type: { type: String },
negativeSign: { type: String, attribute: 'negative-sign' },
label: { type: String },
} satisfies PropertyDeclarationMap<keyof TimeCore.Props>;
} satisfies PropertyDeclarationMap<keyof Omit<TimeCore.Props, 'formatOptions'>>;
type: TimeType = TimeCore.defaultProps.type;
negativeSign = TimeCore.defaultProps.negativeSign;
@@ -26,7 +26,7 @@ describe('VolumeSliderElement', () => {
it('initializes with default property values', () => {
const slider = createElement(VolumeSliderElement);
expect(slider.label).toBe('Volume');
expect(slider.label).toBe('');
expect(slider.step).toBe(1);
expect(slider.largeStep).toBe(10);
expect(slider.orientation).toBe('horizontal');