ts-video-player 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1026,8 +1026,9 @@ class VimeoProvider2 extends BaseProvider2 {
1026
1026
  return;
1027
1027
  }
1028
1028
  if (this.player) {
1029
- await this.player.destroy();
1030
- this.player = null;
1029
+ await this.player.loadVideo(parseInt(parsed.videoId, 10));
1030
+ await this.announceLoaded();
1031
+ return;
1031
1032
  }
1032
1033
  const playerOptions = {
1033
1034
  id: parseInt(parsed.videoId, 10),
@@ -1051,6 +1052,11 @@ class VimeoProvider2 extends BaseProvider2 {
1051
1052
  this.iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0";
1052
1053
  }
1053
1054
  this.attachVimeoEvents();
1055
+ await this.announceLoaded();
1056
+ }
1057
+ async announceLoaded() {
1058
+ if (!this.player)
1059
+ return;
1054
1060
  this.events.emit("loadedmetadata");
1055
1061
  this.events.emit("canplay");
1056
1062
  this._duration = await this.player.getDuration();
@@ -2042,6 +2048,630 @@ function embedCover(state, dismissed) {
2042
2048
  return { show: true, image: youtubeId ? `https://i.ytimg.com/vi/${youtubeId}/hqdefault.jpg` : "" };
2043
2049
  }
2044
2050
 
2051
+ // src/core/i18n.ts
2052
+ var englishStrings2 = {
2053
+ play: "Play",
2054
+ pause: "Pause",
2055
+ replay: "Replay",
2056
+ mute: "Mute",
2057
+ unmute: "Unmute",
2058
+ volume: "Volume",
2059
+ seekBackward: "Seek backward {seconds} seconds",
2060
+ seekForward: "Seek forward {seconds} seconds",
2061
+ seekTo: "Seek to {time}",
2062
+ duration: "Duration",
2063
+ currentTime: "Current time",
2064
+ played: "Played",
2065
+ buffered: "Buffered",
2066
+ enterFullscreen: "Enter fullscreen",
2067
+ exitFullscreen: "Exit fullscreen",
2068
+ enterPiP: "Enter picture-in-picture",
2069
+ exitPiP: "Exit picture-in-picture",
2070
+ enableCaptions: "Enable captions",
2071
+ disableCaptions: "Disable captions",
2072
+ captions: "Captions",
2073
+ captionsOff: "Off",
2074
+ settings: "Settings",
2075
+ quality: "Quality",
2076
+ auto: "Auto",
2077
+ speed: "Speed",
2078
+ normal: "Normal",
2079
+ loop: "Loop",
2080
+ loopEnabled: "Loop enabled",
2081
+ loopDisabled: "Loop disabled",
2082
+ audioBoost: "Audio boost",
2083
+ audioTrack: "Audio track",
2084
+ live: "LIVE",
2085
+ liveEdge: "At live edge",
2086
+ seekToLive: "Seek to live edge",
2087
+ behindLive: "{seconds}s behind",
2088
+ networkError: "A network error occurred",
2089
+ mediaError: "The media could not be loaded",
2090
+ unknownError: "An unknown error occurred",
2091
+ loading: "Loading",
2092
+ buffering: "Buffering",
2093
+ chapters: "Chapters",
2094
+ airPlay: "AirPlay",
2095
+ googleCast: "Cast",
2096
+ casting: "Casting to {device}",
2097
+ download: "Download",
2098
+ share: "Share",
2099
+ close: "Close",
2100
+ menu: "Menu",
2101
+ back: "Back",
2102
+ seek: "Seek",
2103
+ playbackRate: "Playback speed {rate}x",
2104
+ airPlayPlay: "Play on AirPlay",
2105
+ remotePlayback: "Play on another device",
2106
+ videoPlayer: "Video player",
2107
+ videoRole: "video player",
2108
+ playTitle: "Play {title}",
2109
+ audio: "Audio",
2110
+ defaultTrack: "Default",
2111
+ playing: "Playing",
2112
+ paused: "Paused",
2113
+ muted: "Muted",
2114
+ volumeLevel: "Volume {percent}%",
2115
+ ended: "Playback ended",
2116
+ enteredFullscreen: "Entered fullscreen",
2117
+ exitedFullscreen: "Exited fullscreen",
2118
+ errorMessage: "Error: {message}",
2119
+ timeOf: "{current} of {duration}",
2120
+ hourOne: "{n} hour",
2121
+ hourOther: "{n} hours",
2122
+ minuteOne: "{n} minute",
2123
+ minuteOther: "{n} minutes",
2124
+ secondOne: "{n} second",
2125
+ secondOther: "{n} seconds"
2126
+ };
2127
+ var spanishStrings2 = {
2128
+ play: "Reproducir",
2129
+ pause: "Pausar",
2130
+ replay: "Repetir",
2131
+ mute: "Silenciar",
2132
+ unmute: "Activar sonido",
2133
+ volume: "Volumen",
2134
+ seekBackward: "Retroceder {seconds} segundos",
2135
+ seekForward: "Avanzar {seconds} segundos",
2136
+ seekTo: "Ir a {time}",
2137
+ duration: "Duración",
2138
+ currentTime: "Tiempo actual",
2139
+ played: "Reproducido",
2140
+ buffered: "Almacenado",
2141
+ enterFullscreen: "Pantalla completa",
2142
+ exitFullscreen: "Salir de pantalla completa",
2143
+ enterPiP: "Imagen en imagen",
2144
+ exitPiP: "Salir de imagen en imagen",
2145
+ enableCaptions: "Activar subtítulos",
2146
+ disableCaptions: "Desactivar subtítulos",
2147
+ captions: "Subtítulos",
2148
+ captionsOff: "Desactivados",
2149
+ settings: "Ajustes",
2150
+ quality: "Calidad",
2151
+ auto: "Automático",
2152
+ speed: "Velocidad",
2153
+ normal: "Normal",
2154
+ loop: "Bucle",
2155
+ loopEnabled: "Bucle activado",
2156
+ loopDisabled: "Bucle desactivado",
2157
+ audioBoost: "Aumento de audio",
2158
+ audioTrack: "Pista de audio",
2159
+ live: "EN VIVO",
2160
+ liveEdge: "En vivo",
2161
+ seekToLive: "Ir al directo",
2162
+ behindLive: "{seconds}s de retraso",
2163
+ networkError: "Error de red",
2164
+ mediaError: "No se pudo cargar el medio",
2165
+ unknownError: "Error desconocido",
2166
+ loading: "Cargando",
2167
+ buffering: "Almacenando",
2168
+ chapters: "Capítulos",
2169
+ airPlay: "AirPlay",
2170
+ googleCast: "Enviar",
2171
+ casting: "Enviando a {device}",
2172
+ download: "Descargar",
2173
+ share: "Compartir",
2174
+ close: "Cerrar",
2175
+ menu: "Menú",
2176
+ back: "Volver",
2177
+ seek: "Buscar",
2178
+ playbackRate: "Velocidad de reproducción {rate}x",
2179
+ airPlayPlay: "Reproducir con AirPlay",
2180
+ remotePlayback: "Reproducir en otro dispositivo",
2181
+ videoPlayer: "Reproductor de vídeo",
2182
+ videoRole: "reproductor de vídeo",
2183
+ playTitle: "Reproducir {title}",
2184
+ audio: "Audio",
2185
+ defaultTrack: "Predeterminado",
2186
+ playing: "Reproduciendo",
2187
+ paused: "En pausa",
2188
+ muted: "Silenciado",
2189
+ volumeLevel: "Volumen {percent}%",
2190
+ ended: "Reproducción finalizada",
2191
+ enteredFullscreen: "Pantalla completa activada",
2192
+ exitedFullscreen: "Pantalla completa desactivada",
2193
+ errorMessage: "Error: {message}",
2194
+ timeOf: "{current} de {duration}",
2195
+ hourOne: "{n} hora",
2196
+ hourOther: "{n} horas",
2197
+ minuteOne: "{n} minuto",
2198
+ minuteOther: "{n} minutos",
2199
+ secondOne: "{n} segundo",
2200
+ secondOther: "{n} segundos"
2201
+ };
2202
+ var frenchStrings2 = {
2203
+ play: "Lecture",
2204
+ pause: "Pause",
2205
+ replay: "Relire",
2206
+ mute: "Muet",
2207
+ unmute: "Son activé",
2208
+ volume: "Volume",
2209
+ seekBackward: "Reculer de {seconds} secondes",
2210
+ seekForward: "Avancer de {seconds} secondes",
2211
+ seekTo: "Aller à {time}",
2212
+ duration: "Durée",
2213
+ currentTime: "Temps actuel",
2214
+ played: "Joué",
2215
+ buffered: "Mis en mémoire tampon",
2216
+ enterFullscreen: "Plein écran",
2217
+ exitFullscreen: "Quitter le plein écran",
2218
+ enterPiP: "Image dans l'image",
2219
+ exitPiP: "Quitter l'image dans l'image",
2220
+ enableCaptions: "Activer les sous-titres",
2221
+ disableCaptions: "Désactiver les sous-titres",
2222
+ captions: "Sous-titres",
2223
+ captionsOff: "Désactivés",
2224
+ settings: "Paramètres",
2225
+ quality: "Qualité",
2226
+ auto: "Auto",
2227
+ speed: "Vitesse",
2228
+ normal: "Normal",
2229
+ loop: "Boucle",
2230
+ loopEnabled: "Boucle activée",
2231
+ loopDisabled: "Boucle désactivée",
2232
+ audioBoost: "Amplification audio",
2233
+ audioTrack: "Piste audio",
2234
+ live: "EN DIRECT",
2235
+ liveEdge: "En direct",
2236
+ seekToLive: "Aller au direct",
2237
+ behindLive: "{seconds}s de retard",
2238
+ networkError: "Erreur réseau",
2239
+ mediaError: "Le média n'a pas pu être chargé",
2240
+ unknownError: "Erreur inconnue",
2241
+ loading: "Chargement",
2242
+ buffering: "Mise en mémoire tampon",
2243
+ chapters: "Chapitres",
2244
+ airPlay: "AirPlay",
2245
+ googleCast: "Caster",
2246
+ casting: "Diffusion sur {device}",
2247
+ download: "Télécharger",
2248
+ share: "Partager",
2249
+ close: "Fermer",
2250
+ menu: "Menu",
2251
+ back: "Retour",
2252
+ seek: "Rechercher",
2253
+ playbackRate: "Vitesse de lecture {rate}x",
2254
+ airPlayPlay: "Lire avec AirPlay",
2255
+ remotePlayback: "Lire sur un autre appareil",
2256
+ videoPlayer: "Lecteur vidéo",
2257
+ videoRole: "lecteur vidéo",
2258
+ playTitle: "Lire {title}",
2259
+ audio: "Audio",
2260
+ defaultTrack: "Par défaut",
2261
+ playing: "Lecture",
2262
+ paused: "En pause",
2263
+ muted: "Son coupé",
2264
+ volumeLevel: "Volume {percent} %",
2265
+ ended: "Lecture terminée",
2266
+ enteredFullscreen: "Plein écran activé",
2267
+ exitedFullscreen: "Plein écran désactivé",
2268
+ errorMessage: "Erreur : {message}",
2269
+ timeOf: "{current} sur {duration}",
2270
+ hourOne: "{n} heure",
2271
+ hourOther: "{n} heures",
2272
+ minuteOne: "{n} minute",
2273
+ minuteOther: "{n} minutes",
2274
+ secondOne: "{n} seconde",
2275
+ secondOther: "{n} secondes"
2276
+ };
2277
+ var germanStrings2 = {
2278
+ play: "Abspielen",
2279
+ pause: "Pause",
2280
+ replay: "Wiederholen",
2281
+ mute: "Stumm",
2282
+ unmute: "Ton an",
2283
+ volume: "Lautstärke",
2284
+ seekBackward: "{seconds} Sekunden zurück",
2285
+ seekForward: "{seconds} Sekunden vor",
2286
+ seekTo: "Zu {time} springen",
2287
+ duration: "Dauer",
2288
+ currentTime: "Aktuelle Zeit",
2289
+ played: "Abgespielt",
2290
+ buffered: "Gepuffert",
2291
+ enterFullscreen: "Vollbild",
2292
+ exitFullscreen: "Vollbild beenden",
2293
+ enterPiP: "Bild-in-Bild",
2294
+ exitPiP: "Bild-in-Bild beenden",
2295
+ enableCaptions: "Untertitel aktivieren",
2296
+ disableCaptions: "Untertitel deaktivieren",
2297
+ captions: "Untertitel",
2298
+ captionsOff: "Aus",
2299
+ settings: "Einstellungen",
2300
+ quality: "Qualität",
2301
+ auto: "Auto",
2302
+ speed: "Geschwindigkeit",
2303
+ normal: "Normal",
2304
+ loop: "Schleife",
2305
+ loopEnabled: "Schleife aktiviert",
2306
+ loopDisabled: "Schleife deaktiviert",
2307
+ audioBoost: "Audio-Verstärkung",
2308
+ audioTrack: "Audiospur",
2309
+ live: "LIVE",
2310
+ liveEdge: "Am Live-Punkt",
2311
+ seekToLive: "Zum Live-Punkt",
2312
+ behindLive: "{seconds}s hinter Live",
2313
+ networkError: "Netzwerkfehler",
2314
+ mediaError: "Medium konnte nicht geladen werden",
2315
+ unknownError: "Unbekannter Fehler",
2316
+ loading: "Wird geladen",
2317
+ buffering: "Puffern",
2318
+ chapters: "Kapitel",
2319
+ airPlay: "AirPlay",
2320
+ googleCast: "Streamen",
2321
+ casting: "Streame zu {device}",
2322
+ download: "Herunterladen",
2323
+ share: "Teilen",
2324
+ close: "Schließen",
2325
+ menu: "Menü",
2326
+ back: "Zurück",
2327
+ seek: "Spulen",
2328
+ playbackRate: "Wiedergabegeschwindigkeit {rate}x",
2329
+ airPlayPlay: "Über AirPlay abspielen",
2330
+ remotePlayback: "Auf einem anderen Gerät abspielen",
2331
+ videoPlayer: "Videoplayer",
2332
+ videoRole: "Videoplayer",
2333
+ playTitle: "{title} abspielen",
2334
+ audio: "Audio",
2335
+ defaultTrack: "Standard",
2336
+ playing: "Wird abgespielt",
2337
+ paused: "Pausiert",
2338
+ muted: "Stumm",
2339
+ volumeLevel: "Lautstärke {percent} %",
2340
+ ended: "Wiedergabe beendet",
2341
+ enteredFullscreen: "Vollbild an",
2342
+ exitedFullscreen: "Vollbild aus",
2343
+ errorMessage: "Fehler: {message}",
2344
+ timeOf: "{current} von {duration}",
2345
+ hourOne: "{n} Stunde",
2346
+ hourOther: "{n} Stunden",
2347
+ minuteOne: "{n} Minute",
2348
+ minuteOther: "{n} Minuten",
2349
+ secondOne: "{n} Sekunde",
2350
+ secondOther: "{n} Sekunden"
2351
+ };
2352
+ var japaneseStrings2 = {
2353
+ play: "再生",
2354
+ pause: "一時停止",
2355
+ replay: "リプレイ",
2356
+ mute: "ミュート",
2357
+ unmute: "ミュート解除",
2358
+ volume: "音量",
2359
+ seekBackward: "{seconds}秒戻る",
2360
+ seekForward: "{seconds}秒進む",
2361
+ seekTo: "{time}にシーク",
2362
+ duration: "長さ",
2363
+ currentTime: "現在の時間",
2364
+ played: "再生済み",
2365
+ buffered: "バッファ済み",
2366
+ enterFullscreen: "フルスクリーン",
2367
+ exitFullscreen: "フルスクリーン終了",
2368
+ enterPiP: "ピクチャーインピクチャー",
2369
+ exitPiP: "ピクチャーインピクチャー終了",
2370
+ enableCaptions: "字幕を有効化",
2371
+ disableCaptions: "字幕を無効化",
2372
+ captions: "字幕",
2373
+ captionsOff: "オフ",
2374
+ settings: "設定",
2375
+ quality: "画質",
2376
+ auto: "自動",
2377
+ speed: "再生速度",
2378
+ normal: "通常",
2379
+ loop: "ループ",
2380
+ loopEnabled: "ループ有効",
2381
+ loopDisabled: "ループ無効",
2382
+ audioBoost: "オーディオブースト",
2383
+ audioTrack: "オーディオトラック",
2384
+ live: "ライブ",
2385
+ liveEdge: "ライブ中",
2386
+ seekToLive: "ライブに移動",
2387
+ behindLive: "{seconds}秒遅れ",
2388
+ networkError: "ネットワークエラー",
2389
+ mediaError: "メディアを読み込めません",
2390
+ unknownError: "不明なエラー",
2391
+ loading: "読み込み中",
2392
+ buffering: "バッファリング",
2393
+ chapters: "チャプター",
2394
+ airPlay: "AirPlay",
2395
+ googleCast: "キャスト",
2396
+ casting: "{device}にキャスト中",
2397
+ download: "ダウンロード",
2398
+ share: "共有",
2399
+ close: "閉じる",
2400
+ menu: "メニュー",
2401
+ back: "戻る",
2402
+ seek: "シーク",
2403
+ playbackRate: "再生速度 {rate}x",
2404
+ airPlayPlay: "AirPlayで再生",
2405
+ remotePlayback: "別のデバイスで再生",
2406
+ videoPlayer: "動画プレーヤー",
2407
+ videoRole: "動画プレーヤー",
2408
+ playTitle: "{title}を再生",
2409
+ audio: "音声",
2410
+ defaultTrack: "デフォルト",
2411
+ playing: "再生中",
2412
+ paused: "一時停止中",
2413
+ muted: "ミュート",
2414
+ volumeLevel: "音量 {percent}%",
2415
+ ended: "再生終了",
2416
+ enteredFullscreen: "全画面表示",
2417
+ exitedFullscreen: "全画面表示を終了",
2418
+ errorMessage: "エラー: {message}",
2419
+ timeOf: "{current} / {duration}",
2420
+ hourOne: "{n}時間",
2421
+ hourOther: "{n}時間",
2422
+ minuteOne: "{n}分",
2423
+ minuteOther: "{n}分",
2424
+ secondOne: "{n}秒",
2425
+ secondOther: "{n}秒"
2426
+ };
2427
+ var chineseStrings2 = {
2428
+ play: "播放",
2429
+ pause: "暂停",
2430
+ replay: "重播",
2431
+ mute: "静音",
2432
+ unmute: "取消静音",
2433
+ volume: "音量",
2434
+ seekBackward: "后退{seconds}秒",
2435
+ seekForward: "前进{seconds}秒",
2436
+ seekTo: "跳转到{time}",
2437
+ duration: "时长",
2438
+ currentTime: "当前时间",
2439
+ played: "已播放",
2440
+ buffered: "已缓冲",
2441
+ enterFullscreen: "全屏",
2442
+ exitFullscreen: "退出全屏",
2443
+ enterPiP: "画中画",
2444
+ exitPiP: "退出画中画",
2445
+ enableCaptions: "开启字幕",
2446
+ disableCaptions: "关闭字幕",
2447
+ captions: "字幕",
2448
+ captionsOff: "关闭",
2449
+ settings: "设置",
2450
+ quality: "画质",
2451
+ auto: "自动",
2452
+ speed: "播放速度",
2453
+ normal: "正常",
2454
+ loop: "循环",
2455
+ loopEnabled: "循环已开启",
2456
+ loopDisabled: "循环已关闭",
2457
+ audioBoost: "音频增强",
2458
+ audioTrack: "音轨",
2459
+ live: "直播",
2460
+ liveEdge: "直播中",
2461
+ seekToLive: "跳转到直播",
2462
+ behindLive: "落后{seconds}秒",
2463
+ networkError: "网络错误",
2464
+ mediaError: "无法加载媒体",
2465
+ unknownError: "未知错误",
2466
+ loading: "加载中",
2467
+ buffering: "缓冲中",
2468
+ chapters: "章节",
2469
+ airPlay: "AirPlay",
2470
+ googleCast: "投射",
2471
+ casting: "正在投射到{device}",
2472
+ download: "下载",
2473
+ share: "分享",
2474
+ close: "关闭",
2475
+ menu: "菜单",
2476
+ back: "返回",
2477
+ seek: "跳转",
2478
+ playbackRate: "播放速度 {rate}x",
2479
+ airPlayPlay: "通过 AirPlay 播放",
2480
+ remotePlayback: "在其他设备上播放",
2481
+ videoPlayer: "视频播放器",
2482
+ videoRole: "视频播放器",
2483
+ playTitle: "播放{title}",
2484
+ audio: "音频",
2485
+ defaultTrack: "默认",
2486
+ playing: "正在播放",
2487
+ paused: "已暂停",
2488
+ muted: "已静音",
2489
+ volumeLevel: "音量 {percent}%",
2490
+ ended: "播放结束",
2491
+ enteredFullscreen: "已进入全屏",
2492
+ exitedFullscreen: "已退出全屏",
2493
+ errorMessage: "错误:{message}",
2494
+ timeOf: "{current} / {duration}",
2495
+ hourOne: "{n}小时",
2496
+ hourOther: "{n}小时",
2497
+ minuteOne: "{n}分钟",
2498
+ minuteOther: "{n}分钟",
2499
+ secondOne: "{n}秒",
2500
+ secondOther: "{n}秒"
2501
+ };
2502
+ var defaultTranslations2 = {
2503
+ en: englishStrings2,
2504
+ es: spanishStrings2,
2505
+ fr: frenchStrings2,
2506
+ de: germanStrings2,
2507
+ ja: japaneseStrings2,
2508
+ zh: chineseStrings2
2509
+ };
2510
+
2511
+ class I18n2 {
2512
+ language;
2513
+ fallbackLanguage;
2514
+ translations;
2515
+ interpolationPattern;
2516
+ constructor(config = {}) {
2517
+ this.translations = { ...defaultTranslations2 };
2518
+ this.language = config.language || this.detectLanguage() || "en";
2519
+ this.fallbackLanguage = config.fallbackLanguage || "en";
2520
+ if (config.translations) {
2521
+ for (const [lang, strings] of Object.entries(config.translations)) {
2522
+ if (strings) {
2523
+ this.translations[lang] = { ...this.translations[lang], ...strings };
2524
+ }
2525
+ }
2526
+ }
2527
+ this.interpolationPattern = config.interpolationPattern || /\{(\w+)\}/g;
2528
+ }
2529
+ detectLanguage() {
2530
+ if (typeof navigator === "undefined")
2531
+ return null;
2532
+ const lang = navigator.language || navigator.userLanguage;
2533
+ if (!lang)
2534
+ return null;
2535
+ if (this.translations[lang]) {
2536
+ return lang;
2537
+ }
2538
+ const baseLang = lang.split("-")[0];
2539
+ if (this.translations[baseLang]) {
2540
+ return baseLang;
2541
+ }
2542
+ return null;
2543
+ }
2544
+ getLanguage() {
2545
+ return this.language;
2546
+ }
2547
+ setLanguage(language) {
2548
+ this.language = language;
2549
+ }
2550
+ t(key, params) {
2551
+ let str = this.translations[this.language]?.[key];
2552
+ if (!str) {
2553
+ str = this.translations[this.fallbackLanguage]?.[key];
2554
+ }
2555
+ if (!str) {
2556
+ return key;
2557
+ }
2558
+ if (params) {
2559
+ str = str.replace(this.interpolationPattern, (match, paramKey) => {
2560
+ const value = params[paramKey];
2561
+ return value !== undefined ? String(value) : match;
2562
+ });
2563
+ }
2564
+ return str;
2565
+ }
2566
+ addTranslations(language, strings) {
2567
+ this.translations[language] = {
2568
+ ...this.translations[language],
2569
+ ...strings
2570
+ };
2571
+ }
2572
+ getTranslations(language) {
2573
+ return this.translations[language];
2574
+ }
2575
+ getAvailableLanguages() {
2576
+ return Object.keys(this.translations);
2577
+ }
2578
+ hasLanguage(language) {
2579
+ return language in this.translations;
2580
+ }
2581
+ }
2582
+ var globalI18n = null;
2583
+ function createI18n2(config) {
2584
+ return new I18n2(config);
2585
+ }
2586
+ function getI18n2(config) {
2587
+ if (!globalI18n) {
2588
+ globalI18n = createI18n2(config);
2589
+ }
2590
+ return globalI18n;
2591
+ }
2592
+ function setI18n2(i18n) {
2593
+ globalI18n = i18n;
2594
+ }
2595
+ function t2(key, params) {
2596
+ return getI18n2().t(key, params);
2597
+ }
2598
+
2599
+ // src/elements/utils.ts
2600
+ function languageOf(el) {
2601
+ let node = el;
2602
+ while (node) {
2603
+ const tagged = node.closest?.("[lang]")?.getAttribute("lang");
2604
+ if (tagged)
2605
+ return tagged.toLowerCase();
2606
+ const root = node.getRootNode?.();
2607
+ node = root && root.host ? root.host : null;
2608
+ }
2609
+ if (typeof navigator !== "undefined" && navigator.language)
2610
+ return navigator.language.toLowerCase();
2611
+ return "en";
2612
+ }
2613
+ function label(el, key, params) {
2614
+ const lang = languageOf(el);
2615
+ const i18n = getI18n2();
2616
+ const text = i18n.getTranslations(lang)?.[key] || i18n.getTranslations(lang.split("-")[0])?.[key] || englishStrings2[key] || key;
2617
+ return params ? text.replace(/\{(\w+)\}/g, (match, name) => params[name] !== undefined ? String(params[name]) : match) : text;
2618
+ }
2619
+ function formatTime2(seconds, guide = 0) {
2620
+ if (!isFinite(seconds) || isNaN(seconds))
2621
+ return "0:00";
2622
+ const s = Math.floor(seconds);
2623
+ const h = Math.floor(s / 3600);
2624
+ const m = Math.floor(s % 3600 / 60);
2625
+ const sec = s % 60;
2626
+ const showHours = h > 0 || guide >= 3600;
2627
+ if (showHours) {
2628
+ return `${h}:${m.toString().padStart(2, "0")}:${sec.toString().padStart(2, "0")}`;
2629
+ }
2630
+ return `${m}:${sec.toString().padStart(2, "0")}`;
2631
+ }
2632
+ function formatTimePhrase2(seconds, el) {
2633
+ const say = (n, one, other) => label(el, n === 1 ? one : other, { n });
2634
+ if (!isFinite(seconds) || isNaN(seconds))
2635
+ return say(0, "secondOne", "secondOther");
2636
+ const s = Math.floor(seconds);
2637
+ const h = Math.floor(s / 3600);
2638
+ const m = Math.floor(s % 3600 / 60);
2639
+ const sec = s % 60;
2640
+ const parts = [];
2641
+ if (h > 0)
2642
+ parts.push(say(h, "hourOne", "hourOther"));
2643
+ if (m > 0)
2644
+ parts.push(say(m, "minuteOne", "minuteOther"));
2645
+ if (sec > 0 || parts.length === 0)
2646
+ parts.push(say(sec, "secondOne", "secondOther"));
2647
+ return parts.join(", ");
2648
+ }
2649
+ function toISODuration2(seconds) {
2650
+ if (!isFinite(seconds) || isNaN(seconds))
2651
+ return "PT0S";
2652
+ const s = Math.floor(seconds);
2653
+ const h = Math.floor(s / 3600);
2654
+ const m = Math.floor(s % 3600 / 60);
2655
+ const sec = s % 60;
2656
+ let result = "PT";
2657
+ if (h > 0)
2658
+ result += `${h}H`;
2659
+ if (m > 0)
2660
+ result += `${m}M`;
2661
+ if (sec > 0 || result === "PT")
2662
+ result += `${sec}S`;
2663
+ return result;
2664
+ }
2665
+ function resolvePlayer2(el) {
2666
+ const forId = el.getAttribute("for");
2667
+ if (forId) {
2668
+ const target = document.getElementById(forId);
2669
+ return target?.player ?? null;
2670
+ }
2671
+ const host = el.closest("video-player");
2672
+ return host?.player ?? null;
2673
+ }
2674
+
2045
2675
  // src/player.ts
2046
2676
  class Player2 {
2047
2677
  _el;
@@ -2098,8 +2728,8 @@ class Player2 {
2098
2728
  this._el.classList.add("ts-video-player");
2099
2729
  this._el.setAttribute("tabindex", "0");
2100
2730
  this._el.setAttribute("role", "region");
2101
- this._el.setAttribute("aria-roledescription", "video player");
2102
- this._el.setAttribute("aria-label", this._options.title || "Video player");
2731
+ this._el.setAttribute("aria-roledescription", label(this._el, "videoRole"));
2732
+ this._el.setAttribute("aria-label", this._options.title || label(this._el, "videoPlayer"));
2103
2733
  this._el.setAttribute("aria-keyshortcuts", "Space K M F I C ArrowLeft ArrowRight J L ArrowUp ArrowDown Home End 0 1 2 3 4 5 6 7 8 9");
2104
2734
  const mediaContainer = document.createElement("div");
2105
2735
  mediaContainer.className = "ts-video-player__container";
@@ -2123,7 +2753,7 @@ class Player2 {
2123
2753
  const cover = document.createElement("button");
2124
2754
  cover.type = "button";
2125
2755
  cover.className = "ts-video-player__cover";
2126
- cover.setAttribute("aria-label", `Play ${this._options.title || "video"}`);
2756
+ cover.setAttribute("aria-label", this._options.title ? label(this._el, "playTitle", { title: this._options.title }) : label(this._el, "play"));
2127
2757
  cover.hidden = true;
2128
2758
  cover.style.cssText = "position:absolute;inset:0;z-index:2;display:none;align-items:center;justify-content:center;width:100%;height:100%;margin:0;padding:0;border:0;border-radius:inherit;background:#000 center/cover no-repeat;cursor:pointer;-webkit-tap-highlight-color:transparent";
2129
2759
  const button = document.createElement("span");
@@ -2348,7 +2978,7 @@ class Player2 {
2348
2978
  return;
2349
2979
  }
2350
2980
  this._coverDismissed = false;
2351
- this._playRequested = false;
2981
+ this._playRequested = !!this._options.autoplay;
2352
2982
  this._store.batch({
2353
2983
  sources,
2354
2984
  src: sources[0],
@@ -2408,7 +3038,7 @@ class Player2 {
2408
3038
  });
2409
3039
  if (this._playRequested) {
2410
3040
  this._playRequested = false;
2411
- this._provider.play().catch(() => {});
3041
+ this.startRequestedPlay(this._provider);
2412
3042
  }
2413
3043
  return;
2414
3044
  } catch (error) {
@@ -2455,12 +3085,12 @@ class Player2 {
2455
3085
  provider.on("play", () => {
2456
3086
  this._store.batch({ paused: false, ended: false });
2457
3087
  this._events.emit("play");
2458
- this.announce("Playing");
3088
+ this.announce(label(this._el, "playing"));
2459
3089
  });
2460
3090
  provider.on("pause", () => {
2461
3091
  this._store.set("paused", true);
2462
3092
  this._events.emit("pause");
2463
- this.announce("Paused");
3093
+ this.announce(label(this._el, "paused"));
2464
3094
  });
2465
3095
  provider.on("playing", () => {
2466
3096
  this._store.batch({ playing: true, started: true, waiting: false, playbackState: "playing" });
@@ -2490,9 +3120,9 @@ class Player2 {
2490
3120
  this._store.batch({ volume, muted });
2491
3121
  this._events.emit("volumechange", volume, muted);
2492
3122
  if (muted) {
2493
- this.announce("Muted");
3123
+ this.announce(label(this._el, "muted"));
2494
3124
  } else {
2495
- this.announce(`Volume ${Math.round(volume * 100)}%`);
3125
+ this.announce(label(this._el, "volumeLevel", { percent: Math.round(volume * 100) }));
2496
3126
  }
2497
3127
  });
2498
3128
  provider.on("ratechange", (rate) => {
@@ -2509,7 +3139,7 @@ class Player2 {
2509
3139
  provider.on("ended", () => {
2510
3140
  this._store.batch({ ended: true, playing: false, playbackState: "ended" });
2511
3141
  this._events.emit("ended");
2512
- this.announce("Playback ended");
3142
+ this.announce(label(this._el, "ended"));
2513
3143
  if (this.state.loop) {
2514
3144
  this.seekTo(0);
2515
3145
  this.play();
@@ -2518,7 +3148,7 @@ class Player2 {
2518
3148
  provider.on("error", (error) => {
2519
3149
  this._store.batch({ loadingState: "error", error });
2520
3150
  this._events.emit("error", error);
2521
- this.announce(`Error: ${error.message}`);
3151
+ this.announce(label(this._el, "errorMessage", { message: error.message }));
2522
3152
  });
2523
3153
  provider.on("statechange", (state) => {
2524
3154
  this._store.batch(state);
@@ -2531,7 +3161,7 @@ class Player2 {
2531
3161
  provider.on("fullscreenchange", (fullscreen) => {
2532
3162
  this._store.set("fullscreen", fullscreen);
2533
3163
  this._events.emit("fullscreenchange", fullscreen);
2534
- this.announce(fullscreen ? "Entered fullscreen" : "Exited fullscreen");
3164
+ this.announce(label(this._el, fullscreen ? "enteredFullscreen" : "exitedFullscreen"));
2535
3165
  });
2536
3166
  provider.on("pipchange", (pip) => {
2537
3167
  this._store.set("pictureInPicture", pip);
@@ -2547,6 +3177,14 @@ class Player2 {
2547
3177
  this._events.emit("availabilitychange", feature, availability);
2548
3178
  });
2549
3179
  }
3180
+ async startRequestedPlay(provider) {
3181
+ await provider.play().catch(() => {});
3182
+ if (this._provider !== provider || this.state.error?.code !== 1 || this.state.muted)
3183
+ return;
3184
+ this._store.set("error", null);
3185
+ this.setMuted(true);
3186
+ await provider.play().catch(() => {});
3187
+ }
2550
3188
  async play() {
2551
3189
  if (!this._provider) {
2552
3190
  this._playRequested = true;
@@ -2871,62 +3509,6 @@ class VideoPlayerElement2 extends HTMLElementBase {
2871
3509
  }
2872
3510
  }
2873
3511
  }
2874
- // src/elements/utils.ts
2875
- function formatTime2(seconds, guide = 0) {
2876
- if (!isFinite(seconds) || isNaN(seconds))
2877
- return "0:00";
2878
- const s = Math.floor(seconds);
2879
- const h = Math.floor(s / 3600);
2880
- const m = Math.floor(s % 3600 / 60);
2881
- const sec = s % 60;
2882
- const showHours = h > 0 || guide >= 3600;
2883
- if (showHours) {
2884
- return `${h}:${m.toString().padStart(2, "0")}:${sec.toString().padStart(2, "0")}`;
2885
- }
2886
- return `${m}:${sec.toString().padStart(2, "0")}`;
2887
- }
2888
- function formatTimePhrase2(seconds) {
2889
- if (!isFinite(seconds) || isNaN(seconds))
2890
- return "0 seconds";
2891
- const s = Math.floor(seconds);
2892
- const h = Math.floor(s / 3600);
2893
- const m = Math.floor(s % 3600 / 60);
2894
- const sec = s % 60;
2895
- const parts = [];
2896
- if (h > 0)
2897
- parts.push(`${h} ${h === 1 ? "hour" : "hours"}`);
2898
- if (m > 0)
2899
- parts.push(`${m} ${m === 1 ? "minute" : "minutes"}`);
2900
- if (sec > 0 || parts.length === 0)
2901
- parts.push(`${sec} ${sec === 1 ? "second" : "seconds"}`);
2902
- return parts.join(", ");
2903
- }
2904
- function toISODuration2(seconds) {
2905
- if (!isFinite(seconds) || isNaN(seconds))
2906
- return "PT0S";
2907
- const s = Math.floor(seconds);
2908
- const h = Math.floor(s / 3600);
2909
- const m = Math.floor(s % 3600 / 60);
2910
- const sec = s % 60;
2911
- let result = "PT";
2912
- if (h > 0)
2913
- result += `${h}H`;
2914
- if (m > 0)
2915
- result += `${m}M`;
2916
- if (sec > 0 || result === "PT")
2917
- result += `${sec}S`;
2918
- return result;
2919
- }
2920
- function resolvePlayer2(el) {
2921
- const forId = el.getAttribute("for");
2922
- if (forId) {
2923
- const target = document.getElementById(forId);
2924
- return target?.player ?? null;
2925
- }
2926
- const host = el.closest("video-player");
2927
- return host?.player ?? null;
2928
- }
2929
-
2930
3512
  // src/elements/video-skin.ts
2931
3513
  class VideoSkin2 extends HTMLElementBase {
2932
3514
  _cleanups = [];
@@ -3062,7 +3644,7 @@ class MediaPlayButton2 extends HTMLElementBase {
3062
3644
  }
3063
3645
  button:hover { opacity: 0.8; }
3064
3646
  </style>
3065
- <button part="button" type="button" aria-label="Play">
3647
+ <button part="button" type="button" aria-label="${label(this, "play")}">
3066
3648
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon">
3067
3649
  <path d="${PLAY_ICON}"/>
3068
3650
  </svg>
@@ -3088,7 +3670,7 @@ class MediaPlayButton2 extends HTMLElementBase {
3088
3670
  btn.addEventListener("click", this._onClick);
3089
3671
  const unsub = player.subscribe("paused", (state) => {
3090
3672
  const paused = state.paused;
3091
- btn.setAttribute("aria-label", paused ? "Play" : "Pause");
3673
+ btn.setAttribute("aria-label", label(this, paused ? "play" : "pause"));
3092
3674
  btn.setAttribute("aria-pressed", String(!paused));
3093
3675
  const path = this.shadowRoot.querySelector("path");
3094
3676
  path.setAttribute("d", paused ? PLAY_ICON : PAUSE_ICON);
@@ -3123,7 +3705,7 @@ class MediaMuteButton2 extends HTMLElementBase {
3123
3705
  }
3124
3706
  button:hover { opacity: 0.8; }
3125
3707
  </style>
3126
- <button part="button" type="button" aria-label="Mute">
3708
+ <button part="button" type="button" aria-label="${label(this, "mute")}">
3127
3709
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon">
3128
3710
  <path d="${ICON_HIGH}"/>
3129
3711
  </svg>
@@ -3144,7 +3726,7 @@ class MediaMuteButton2 extends HTMLElementBase {
3144
3726
  btn.addEventListener("click", () => player.toggleMute());
3145
3727
  const unsub = player.subscribe((state) => {
3146
3728
  const icon = getVolumeIcon(state.muted, state.volume);
3147
- btn.setAttribute("aria-label", state.muted ? "Unmute" : "Mute");
3729
+ btn.setAttribute("aria-label", label(this, state.muted ? "unmute" : "mute"));
3148
3730
  const path = this.shadowRoot.querySelector("path");
3149
3731
  path.setAttribute("d", icon);
3150
3732
  });
@@ -3172,7 +3754,7 @@ class MediaFullscreenButton2 extends HTMLElementBase {
3172
3754
  button:hover { opacity: 0.8; }
3173
3755
  button:disabled { opacity: 0.4; cursor: not-allowed; }
3174
3756
  </style>
3175
- <button part="button" type="button" aria-label="Enter fullscreen">
3757
+ <button part="button" type="button" aria-label="${label(this, "enterFullscreen")}">
3176
3758
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon">
3177
3759
  <path d="${ENTER_FS}"/>
3178
3760
  </svg>
@@ -3200,7 +3782,7 @@ class MediaFullscreenButton2 extends HTMLElementBase {
3200
3782
  }
3201
3783
  btn.disabled = avail !== "available";
3202
3784
  const fs = state.fullscreen;
3203
- btn.setAttribute("aria-label", fs ? "Exit fullscreen" : "Enter fullscreen");
3785
+ btn.setAttribute("aria-label", label(this, fs ? "exitFullscreen" : "enterFullscreen"));
3204
3786
  const path = this.shadowRoot.querySelector("path");
3205
3787
  path.setAttribute("d", fs ? EXIT_FS : ENTER_FS);
3206
3788
  });
@@ -3227,7 +3809,7 @@ class MediaPipButton2 extends HTMLElementBase {
3227
3809
  button:hover { opacity: 0.8; }
3228
3810
  button:disabled { opacity: 0.4; cursor: not-allowed; }
3229
3811
  </style>
3230
- <button part="button" type="button" aria-label="Picture in picture">
3812
+ <button part="button" type="button" aria-label="${label(this, "enterPiP")}">
3231
3813
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon">
3232
3814
  <path d="${PIP_ICON}"/>
3233
3815
  </svg>
@@ -3254,6 +3836,7 @@ class MediaPipButton2 extends HTMLElementBase {
3254
3836
  this.removeAttribute("hidden");
3255
3837
  }
3256
3838
  btn.disabled = avail !== "available";
3839
+ btn.setAttribute("aria-label", label(this, state.pictureInPicture ? "exitPiP" : "enterPiP"));
3257
3840
  });
3258
3841
  this._cleanup = unsub;
3259
3842
  }
@@ -3284,7 +3867,7 @@ class MediaTimeDisplay2 extends HTMLElementBase {
3284
3867
  time { color: inherit; }
3285
3868
  .separator { margin: 0 2px; }
3286
3869
  </style>
3287
- <time part="time" datetime="PT0S" aria-valuetext="0 seconds">0:00</time>
3870
+ <time part="time" datetime="PT0S" aria-valuetext="${formatTimePhrase2(0, this)}">0:00</time>
3288
3871
  `;
3289
3872
  }
3290
3873
  this.addEventListener("click", this.handleClick);
@@ -3330,7 +3913,7 @@ class MediaTimeDisplay2 extends HTMLElementBase {
3330
3913
  timeEl.textContent = formatTime2(seconds, state.duration);
3331
3914
  }
3332
3915
  timeEl.setAttribute("datetime", toISODuration2(seconds));
3333
- timeEl.setAttribute("aria-valuetext", formatTimePhrase2(seconds));
3916
+ timeEl.setAttribute("aria-valuetext", formatTimePhrase2(seconds, this));
3334
3917
  }
3335
3918
  }
3336
3919
  // src/elements/media-time-group.ts
@@ -3488,7 +4071,7 @@ class MediaProgressBar2 extends HTMLElementBase {
3488
4071
  </style>
3489
4072
  <div class="container" part="container" role="slider"
3490
4073
  aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"
3491
- aria-label="Seek" tabindex="0">
4074
+ aria-label="${label(this, "seek")}" tabindex="0">
3492
4075
  <div class="tooltip" part="tooltip">0:00</div>
3493
4076
  <div class="track" part="track">
3494
4077
  <div class="waveform" part="waveform" aria-hidden="true"></div>
@@ -3594,7 +4177,7 @@ class MediaProgressBar2 extends HTMLElementBase {
3594
4177
  fill.style.width = `${percent}%`;
3595
4178
  thumb.style.left = `${percent}%`;
3596
4179
  container.setAttribute("aria-valuenow", String(Math.round(percent)));
3597
- container.setAttribute("aria-valuetext", `${formatTimePhrase2(state.currentTime)} of ${formatTimePhrase2(duration)}`);
4180
+ container.setAttribute("aria-valuetext", label(this, "timeOf", { current: formatTimePhrase2(state.currentTime, this), duration: formatTimePhrase2(duration, this) }));
3598
4181
  const bufferedPct = (state.bufferedAmount || 0) * 100;
3599
4182
  buffered.style.width = `${bufferedPct}%`;
3600
4183
  });
@@ -3636,7 +4219,7 @@ class MediaVolumeSlider2 extends HTMLElementBase {
3636
4219
  </style>
3637
4220
  <div class="container" part="container" role="slider"
3638
4221
  aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"
3639
- aria-label="Volume" tabindex="0">
4222
+ aria-label="${label(this, "volume")}" tabindex="0">
3640
4223
  <div class="track" part="track">
3641
4224
  <div class="fill" part="fill"></div>
3642
4225
  <div class="thumb" part="thumb"></div>
@@ -3831,7 +4414,7 @@ class MediaSettingsMenu2 extends HTMLElementBase {
3831
4414
  .back { font-weight: bold; border-bottom: 1px solid rgba(255,255,255,0.2); }
3832
4415
  </style>
3833
4416
  <button class="trigger" part="trigger" type="button"
3834
- aria-label="Settings" aria-haspopup="true" aria-expanded="false">
4417
+ aria-label="${label(this, "settings")}" aria-haspopup="true" aria-expanded="false">
3835
4418
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon">
3836
4419
  <path d="${GEAR_ICON}"/>
3837
4420
  </svg>
@@ -3943,35 +4526,35 @@ class MediaSettingsMenu2 extends HTMLElementBase {
3943
4526
  }
3944
4527
  renderMain(menu, player) {
3945
4528
  const state = player.state;
3946
- this.addItem(menu, `Speed: ${state.playbackRate}x`, () => {
4529
+ this.addItem(menu, `${label(this, "speed")}: ${state.playbackRate}x`, () => {
3947
4530
  this._currentPanel = "speed";
3948
4531
  this.renderPanel();
3949
4532
  });
3950
4533
  if (state.qualities.length > 0) {
3951
4534
  const current = state.qualities.find((q) => q.selected);
3952
- const label = state.autoQuality ? "Auto" : current ? `${current.height}p` : "Auto";
3953
- this.addItem(menu, `Quality: ${label}`, () => {
4535
+ const label2 = state.autoQuality ? label(this, "auto") : current ? `${current.height}p` : label(this, "auto");
4536
+ this.addItem(menu, `${label(this, "quality")}: ${label2}`, () => {
3954
4537
  this._currentPanel = "quality";
3955
4538
  this.renderPanel();
3956
4539
  });
3957
4540
  }
3958
4541
  if (state.textTracks.length > 0) {
3959
4542
  const showing = state.textTracks.find((t) => t.mode === "showing");
3960
- this.addItem(menu, `Captions: ${showing?.label || "Off"}`, () => {
4543
+ this.addItem(menu, `${label(this, "captions")}: ${showing?.label || label(this, "captionsOff")}`, () => {
3961
4544
  this._currentPanel = "captions";
3962
4545
  this.renderPanel();
3963
4546
  });
3964
4547
  }
3965
4548
  if (state.audioTracks.length > 1) {
3966
4549
  const selected = state.audioTracks.find((track) => track.selected);
3967
- this.addItem(menu, `Audio: ${selected?.label || "Default"}`, () => {
4550
+ this.addItem(menu, `${label(this, "audio")}: ${selected?.label || label(this, "defaultTrack")}`, () => {
3968
4551
  this._currentPanel = "audio";
3969
4552
  this.renderPanel();
3970
4553
  });
3971
4554
  }
3972
4555
  }
3973
4556
  renderSpeed(menu, player) {
3974
- this.addItem(menu, "Back", () => {
4557
+ this.addItem(menu, label(this, "back"), () => {
3975
4558
  this._currentPanel = "main";
3976
4559
  this.renderPanel();
3977
4560
  }, false, true);
@@ -3984,11 +4567,11 @@ class MediaSettingsMenu2 extends HTMLElementBase {
3984
4567
  }
3985
4568
  }
3986
4569
  renderQuality(menu, player) {
3987
- this.addItem(menu, "Back", () => {
4570
+ this.addItem(menu, label(this, "back"), () => {
3988
4571
  this._currentPanel = "main";
3989
4572
  this.renderPanel();
3990
4573
  }, false, true);
3991
- this.addItem(menu, "Auto", () => {
4574
+ this.addItem(menu, label(this, "auto"), () => {
3992
4575
  player.setQuality("auto");
3993
4576
  this.close();
3994
4577
  }, player.state.autoQuality);
@@ -4000,12 +4583,12 @@ class MediaSettingsMenu2 extends HTMLElementBase {
4000
4583
  }
4001
4584
  }
4002
4585
  renderCaptions(menu, player) {
4003
- this.addItem(menu, "Back", () => {
4586
+ this.addItem(menu, label(this, "back"), () => {
4004
4587
  this._currentPanel = "main";
4005
4588
  this.renderPanel();
4006
4589
  }, false, true);
4007
4590
  const anyShowing = player.state.textTracks.some((t) => t.mode === "showing");
4008
- this.addItem(menu, "Off", () => {
4591
+ this.addItem(menu, label(this, "captionsOff"), () => {
4009
4592
  for (const t of player.state.textTracks) {
4010
4593
  if (t.mode === "showing")
4011
4594
  player.setTextTrack(t.id, "disabled");
@@ -4022,12 +4605,12 @@ class MediaSettingsMenu2 extends HTMLElementBase {
4022
4605
  }
4023
4606
  }
4024
4607
  renderAudio(menu, player) {
4025
- this.addItem(menu, "Back", () => {
4608
+ this.addItem(menu, label(this, "back"), () => {
4026
4609
  this._currentPanel = "main";
4027
4610
  this.renderPanel();
4028
4611
  }, false, true);
4029
4612
  for (const track of player.state.audioTracks) {
4030
- this.addItem(menu, track.label || track.language || "Audio track", () => {
4613
+ this.addItem(menu, track.label || track.language || label(this, "audioTrack"), () => {
4031
4614
  player.setAudioTrack(track.id);
4032
4615
  this.close();
4033
4616
  }, track.selected);
@@ -4079,9 +4662,8 @@ class MediaSeekButton2 extends HTMLElementBase {
4079
4662
  return;
4080
4663
  const seconds = Number.parseFloat(this.getAttribute("seconds") ?? "10");
4081
4664
  const offset = Number.isFinite(seconds) && seconds !== 0 ? seconds : 10;
4082
- const label = offset > 0 ? `Forward ${offset} seconds` : `Back ${Math.abs(offset)} seconds`;
4083
4665
  button.textContent = offset > 0 ? `+${offset}` : String(offset);
4084
- button.setAttribute("aria-label", label);
4666
+ button.setAttribute("aria-label", label(this, offset > 0 ? "seekForward" : "seekBackward", { seconds: Math.abs(offset) }));
4085
4667
  this.onClick = () => player.seekBy(offset);
4086
4668
  button.addEventListener("click", this.onClick);
4087
4669
  this.cleanup = player.subscribe((state) => {
@@ -4105,7 +4687,7 @@ class MediaPlaybackRateButton2 extends HTMLElementBase {
4105
4687
  button:hover { opacity: .8; }
4106
4688
  button:focus-visible { outline: 2px solid currentColor; outline-offset: -2px; }
4107
4689
  </style>
4108
- <button part="button" type="button" aria-label="Playback speed"></button>
4690
+ <button part="button" type="button" aria-label="${label(this, "speed")}"></button>
4109
4691
  `;
4110
4692
  }
4111
4693
  queueMicrotask(() => this.attach());
@@ -4135,7 +4717,7 @@ class MediaPlaybackRateButton2 extends HTMLElementBase {
4135
4717
  button.addEventListener("click", this.onClick);
4136
4718
  this.cleanup = player.subscribe("playbackRate", (state) => {
4137
4719
  button.textContent = `${state.playbackRate}x`;
4138
- button.setAttribute("aria-label", `Playback speed ${state.playbackRate}x`);
4720
+ button.setAttribute("aria-label", label(this, "playbackRate", { rate: state.playbackRate }));
4139
4721
  });
4140
4722
  }
4141
4723
  }
@@ -4155,7 +4737,7 @@ class MediaAirplayButton2 extends HTMLElementBase {
4155
4737
  button:hover { opacity: 0.8; }
4156
4738
  :host([connected]) button { color: var(--media-airplay-active-color, #38bdf8); }
4157
4739
  </style>
4158
- <button part="button" type="button" aria-label="Play on AirPlay">
4740
+ <button part="button" type="button" aria-label="${label(this, "airPlayPlay")}">
4159
4741
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon"><path d="${AIRPLAY_ICON}"/></svg>
4160
4742
  </button>
4161
4743
  `;
@@ -4214,7 +4796,7 @@ class MediaLiveButton2 extends HTMLElementBase {
4214
4796
  i { width: 8px; height: 8px; border-radius: 9999px; background: #ef4444; }
4215
4797
  :host(:not([at-live-edge])) button { opacity: 0.72; }
4216
4798
  </style>
4217
- <button part="button" type="button" aria-label="Go to live edge"><i aria-hidden="true"></i><span>LIVE</span></button>
4799
+ <button part="button" type="button" aria-label="${label(this, "seekToLive")}"><i aria-hidden="true"></i><span>${label(this, "live")}</span></button>
4218
4800
  `;
4219
4801
  }
4220
4802
  queueMicrotask(() => this.attach());
@@ -4310,7 +4892,7 @@ class MediaRemotePlaybackButton2 extends HTMLElementBase {
4310
4892
  button:hover { opacity: 0.8; }
4311
4893
  :host([connected]) button { color: var(--media-remote-active-color, #38bdf8); }
4312
4894
  </style>
4313
- <button part="button" type="button" aria-label="Play on another device">
4895
+ <button part="button" type="button" aria-label="${label(this, "remotePlayback")}">
4314
4896
  <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" part="icon"><path d="${REMOTE_ICON}"/></svg>
4315
4897
  </button>
4316
4898
  `;
@@ -4413,4 +4995,4 @@ function registerElements2() {
4413
4995
  }
4414
4996
  }
4415
4997
 
4416
- export { isMediaSessionSupported2, createMediaSession2, updateMetadata2, isOrientationLockSupported2, lockOrientation2, unlockOrientation2, BaseProvider2, isHTML5Source2, HTML5Provider2, html5Loader2, extractYouTubeId2, isYouTubeSource2, YouTubeProvider2, youtubeLoader2, extractVimeoId2, extractVimeoHash2, isVimeoSource2, VimeoProvider2, vimeoLoader2, isHLSSource2, isNativeHLSSupported2, HLSProvider2, hlsLoader2, isDASHSource2, DASHProvider2, dashLoader2, defaultLoaders2, findLoader2, detectMediaType2, getPreconnectHints2, Player2, createPlayer2, getPlayer2, VideoPlayerElement2, formatTime2, formatTimePhrase2, toISODuration2, resolvePlayer2, VideoSkin2, MediaPlayButton2, MediaMuteButton2, MediaFullscreenButton2, MediaPipButton2, MediaTimeDisplay2, MediaTimeGroup2, MediaTimeSeparator2, MediaPreviewTime2, MediaProgressBar2, MediaVolumeSlider2, computePosition2, applyPosition2, MediaSettingsMenu2, MediaSeekButton2, MediaPlaybackRateButton2, MediaAirplayButton2, MediaLiveButton2, MediaErrorDisplay2, MediaRemotePlaybackButton2, registerElements2 };
4998
+ export { isMediaSessionSupported2, createMediaSession2, updateMetadata2, isOrientationLockSupported2, lockOrientation2, unlockOrientation2, BaseProvider2, isHTML5Source2, HTML5Provider2, html5Loader2, extractYouTubeId2, isYouTubeSource2, YouTubeProvider2, youtubeLoader2, extractVimeoId2, extractVimeoHash2, isVimeoSource2, VimeoProvider2, vimeoLoader2, isHLSSource2, isNativeHLSSupported2, HLSProvider2, hlsLoader2, isDASHSource2, DASHProvider2, dashLoader2, defaultLoaders2, findLoader2, detectMediaType2, getPreconnectHints2, englishStrings2, spanishStrings2, frenchStrings2, germanStrings2, japaneseStrings2, chineseStrings2, defaultTranslations2, I18n2, createI18n2, getI18n2, setI18n2, t2, formatTime2, formatTimePhrase2, toISODuration2, resolvePlayer2, Player2, createPlayer2, getPlayer2, VideoPlayerElement2, VideoSkin2, MediaPlayButton2, MediaMuteButton2, MediaFullscreenButton2, MediaPipButton2, MediaTimeDisplay2, MediaTimeGroup2, MediaTimeSeparator2, MediaPreviewTime2, MediaProgressBar2, MediaVolumeSlider2, computePosition2, applyPosition2, MediaSettingsMenu2, MediaSeekButton2, MediaPlaybackRateButton2, MediaAirplayButton2, MediaLiveButton2, MediaErrorDisplay2, MediaRemotePlaybackButton2, registerElements2 };